From 5ad91699940909223c167ef40de03e9a55a5a9c3 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Mon, 3 Sep 2012 00:00:52 +0200 Subject: add xps backend xps backend is from evince ported to MATE by Leandro Vital --- backend/Makefile.am | 4 + backend/xps/Makefile.am | 31 ++ backend/xps/xps-document.c | 505 +++++++++++++++++++++++++++++++ backend/xps/xps-document.h | 45 +++ backend/xps/xpsdocument.atril-backend.in | 5 + configure.ac | 31 +- libdocument/ev-document-links.c | 14 + libdocument/ev-document-links.h | 4 + 8 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 backend/xps/Makefile.am create mode 100644 backend/xps/xps-document.c create mode 100644 backend/xps/xps-document.h create mode 100644 backend/xps/xpsdocument.atril-backend.in diff --git a/backend/Makefile.am b/backend/Makefile.am index a6dc6e58..ab1663d7 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -30,6 +30,10 @@ if ENABLE_COMICS SUBDIRS += comics endif +if ENABLE_XPS + SUBDIRS += xps +endif + if ENABLE_IMPRESS SUBDIRS += impress endif diff --git a/backend/xps/Makefile.am b/backend/xps/Makefile.am new file mode 100644 index 00000000..2e0453bf --- /dev/null +++ b/backend/xps/Makefile.am @@ -0,0 +1,31 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DATRIL_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libxpsdocument.la + +libxpsdocument_la_SOURCES = \ + xps-document.c \ + xps-document.h + +libxpsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libxpsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libatrildocument.la \ + $(BACKEND_LIBS) \ + $(GXPS_LIBS) + +backend_in_files = xpsdocument.atril-backend.in +backend_DATA = $(backend_in_files:.atril-backend.in=.atril-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_ATRIL_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/xps/xps-document.c b/backend/xps/xps-document.c new file mode 100644 index 00000000..891f2008 --- /dev/null +++ b/backend/xps/xps-document.c @@ -0,0 +1,505 @@ +/* this file is part of atril, a mate document viewer + * + * Copyright (C) 2010 Carlos Garcia Campos + * 2012 Leandro Vital + * + * Atril is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Atril is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include "xps-document.h" +#include "ev-document-links.h" +#include "ev-document-misc.h" +#include "ev-document-print.h" + +struct _XPSDocument { + EvDocument object; + + GFile *file; + GXPSFile *xps; + GXPSDocument *doc; +}; + +struct _XPSDocumentClass { + EvDocumentClass parent_class; +}; + +static void xps_document_document_links_iface_init (EvDocumentLinksInterface *iface); +static void xps_document_document_print_iface_init (EvDocumentPrintInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (XPSDocument, xps_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, + xps_document_document_links_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_PRINT, + xps_document_document_print_iface_init); + }) + +/* XPSDocument */ +static void +xps_document_init (XPSDocument *ps_document) +{ +} + +static void +xps_document_dispose (GObject *object) +{ + XPSDocument *xps = XPS_DOCUMENT (object); + + if (xps->file) { + g_object_unref (xps->file); + xps->file = NULL; + } + + if (xps->xps) { + g_object_unref (xps->xps); + xps->xps = NULL; + } + + if (xps->doc) { + g_object_unref (xps->doc); + xps->doc = NULL; + } + + G_OBJECT_CLASS (xps_document_parent_class)->dispose (object); +} + +/* EvDocumentIface */ +static gboolean +xps_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + XPSDocument *xps = XPS_DOCUMENT (document); + + xps->file = g_file_new_for_uri (uri); + xps->xps = gxps_file_new (xps->file, error); + + if (!xps->xps) + return FALSE; + + /* FIXME: what if there are multiple docs? */ + xps->doc = gxps_file_get_document (xps->xps, 0, error); + if (!xps->doc) { + g_object_unref (xps->xps); + xps->xps = NULL; + + return FALSE; + } + + return TRUE; +} + +static gboolean +xps_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + XPSDocument *xps = XPS_DOCUMENT (document); + GFile *dest; + gboolean retval; + + dest = g_file_new_for_uri (uri); + retval = g_file_copy (xps->file, dest, + G_FILE_COPY_TARGET_DEFAULT_PERMS | + G_FILE_COPY_OVERWRITE, + NULL, NULL, NULL, error); + g_object_unref (dest); + + return retval; +} + +static gint +xps_document_get_n_pages (EvDocument *document) +{ + XPSDocument *xps = XPS_DOCUMENT (document); + + return gxps_document_get_n_pages (xps->doc); +} + +static EvPage * +xps_document_get_page (EvDocument *document, + gint index) +{ + XPSDocument *xps = XPS_DOCUMENT (document); + GXPSPage *xps_page; + EvPage *page; + + xps_page = gxps_document_get_page (xps->doc, index, NULL); + page = ev_page_new (index); + if (xps_page) { + page->backend_page = (EvBackendPage)xps_page; + page->backend_destroy_func = (EvBackendPageDestroyFunc)g_object_unref; + } + + return page; +} + +static void +xps_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + gxps_page_get_size (GXPS_PAGE (page->backend_page), width, height); +} + +static EvDocumentInfo * +xps_document_get_info (EvDocument *document) +{ + XPSDocument *xps = XPS_DOCUMENT (document); + EvDocumentInfo *info; + + info = g_new0 (EvDocumentInfo, 1); + info->fields_mask = + EV_DOCUMENT_INFO_N_PAGES | + EV_DOCUMENT_INFO_PAPER_SIZE; + + + if (gxps_document_get_n_pages (xps->doc) > 0) { + ev_document_get_page_size (document, 0, + &(info->paper_width), + &(info->paper_height)); + info->paper_width = info->paper_width / 96.0f * 25.4f; + info->paper_height = info->paper_height / 96.0f * 25.4f; + } + + info->n_pages = gxps_document_get_n_pages (xps->doc); + + return info; +} + +static gboolean +xps_document_get_backend_info (EvDocument *document, + EvDocumentBackendInfo *info) +{ + info->name = "libgxps"; + /* FIXME */ + info->version = ""; + + return TRUE; +} + +static cairo_surface_t * +xps_document_render (EvDocument *document, + EvRenderContext *rc) +{ + GXPSPage *xps_page; + gdouble page_width, page_height; + guint width, height; + cairo_surface_t *surface; + cairo_t *cr; + GError *error = NULL; + + xps_page = GXPS_PAGE (rc->page->backend_page); + + gxps_page_get_size (xps_page, &page_width, &page_height); + if (rc->rotation == 90 || rc->rotation == 270) { + width = (guint) ((page_height * rc->scale) + 0.5); + height = (guint) ((page_width * rc->scale) + 0.5); + } else { + width = (guint) ((page_width * rc->scale) + 0.5); + height = (guint) ((page_height * rc->scale) + 0.5); + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create (surface); + + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + + switch (rc->rotation) { + case 90: + cairo_translate (cr, width, 0); + break; + case 180: + cairo_translate (cr, width, height); + break; + case 270: + cairo_translate (cr, 0, height); + break; + default: + cairo_translate (cr, 0, 0); + } + + cairo_scale (cr, rc->scale, rc->scale); + cairo_rotate (cr, rc->rotation * G_PI / 180.0); + gxps_page_render (xps_page, cr, &error); + cairo_destroy (cr); + + if (error) { + g_warning ("Error rendering page %d: %s\n", + rc->page->index, error->message); + g_error_free (error); + } + + return surface; +} + +static void +xps_document_class_init (XPSDocumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + object_class->dispose = xps_document_dispose; + + ev_document_class->load = xps_document_load; + ev_document_class->save = xps_document_save; + ev_document_class->get_n_pages = xps_document_get_n_pages; + ev_document_class->get_page = xps_document_get_page; + ev_document_class->get_page_size = xps_document_get_page_size; + ev_document_class->get_info = xps_document_get_info; + ev_document_class->get_backend_info = xps_document_get_backend_info; + ev_document_class->render = xps_document_render; +} + +/* EvDocumentLinks */ +static gboolean +xps_document_links_has_document_links (EvDocumentLinks *document_links) +{ + XPSDocument *xps_document = XPS_DOCUMENT (document_links); + GXPSDocumentStructure *structure; + gboolean retval; + + structure = gxps_document_get_structure (xps_document->doc); + if (!structure) + return FALSE; + + retval = gxps_document_structure_has_outline (structure); + g_object_unref (structure); + + return retval; +} + +static EvLink * +ev_link_from_target (XPSDocument *xps_document, + GXPSLinkTarget *target) +{ + EvLinkAction *ev_action; + + if (gxps_link_target_is_internal (target)) { + EvLinkDest *dest = NULL; + guint doc; + const gchar *anchor; + + anchor = gxps_link_target_get_anchor (target); + + /* FIXME: multidoc */ + doc = gxps_file_get_document_for_link_target (xps_document->xps, target); + if (doc == 0) { + if (!anchor) + return NULL; + + dest = ev_link_dest_new_named (anchor); + ev_action = ev_link_action_new_dest (dest); + } else if (doc == -1 && anchor && + gxps_document_get_page_for_anchor (xps_document->doc, anchor) >= 0) { + /* Internal, but source is not a doc, + * let's try with doc = 0 + */ + dest = ev_link_dest_new_named (anchor); + ev_action = ev_link_action_new_dest (dest); + } else { + gchar *filename; + + /* FIXME: remote uri? */ + filename = g_file_get_path (xps_document->file); + + if (anchor) + dest = ev_link_dest_new_named (anchor); + ev_action = ev_link_action_new_remote (dest, filename); + g_free (filename); + } + } else { + const gchar *uri; + + uri = gxps_link_target_get_uri (target); + ev_action = ev_link_action_new_external_uri (uri); + } + + return ev_link_new (NULL, ev_action); +} + +static void +build_tree (XPSDocument *xps_document, + GtkTreeModel *model, + GtkTreeIter *parent, + GXPSOutlineIter *iter) +{ + do { + GtkTreeIter tree_iter; + GXPSOutlineIter child_iter; + EvLink *link; + GXPSLinkTarget *target; + gchar *title; + + target = gxps_outline_iter_get_target (iter); + title = g_markup_escape_text (gxps_outline_iter_get_description (iter), -1); + link = ev_link_from_target (xps_document, target); + gxps_link_target_free (target); + + gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); + gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, + EV_DOCUMENT_LINKS_COLUMN_MARKUP, title, + EV_DOCUMENT_LINKS_COLUMN_LINK, link, + EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE, + -1); + g_object_unref (link); + g_free (title); + + if (gxps_outline_iter_children (&child_iter, iter)) + build_tree (xps_document, model, &tree_iter, &child_iter); + } while (gxps_outline_iter_next (iter)); +} + +static GtkTreeModel * +xps_document_links_get_links_model (EvDocumentLinks *document_links) +{ + XPSDocument *xps_document = XPS_DOCUMENT (document_links); + GXPSDocumentStructure *structure; + GXPSOutlineIter iter; + GtkTreeModel *model = NULL; + + structure = gxps_document_get_structure (xps_document->doc); + if (!structure) + return NULL; + + if (gxps_document_structure_outline_iter_init (&iter, structure)) { + model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_BOOLEAN, + G_TYPE_STRING); + build_tree (xps_document, model, NULL, &iter); + } + + g_object_unref (structure); + + return model; +} + +static EvMappingList * +xps_document_links_get_links (EvDocumentLinks *document_links, + EvPage *page) +{ + XPSDocument *xps_document = XPS_DOCUMENT (document_links); + GXPSPage *xps_page; + GList *retval = NULL; + GList *mapping_list; + GList *list; + + xps_page = GXPS_PAGE (page->backend_page); + mapping_list = gxps_page_get_links (xps_page, NULL); + + for (list = mapping_list; list; list = list->next) { + GXPSLink *xps_link; + GXPSLinkTarget *target; + EvMapping *ev_link_mapping; + cairo_rectangle_t area; + + xps_link = (GXPSLink *)list->data; + ev_link_mapping = g_new (EvMapping, 1); + gxps_link_get_area (xps_link, &area); + target = gxps_link_get_target (xps_link); + gxps_link_get_area (xps_link, &area); + ev_link_mapping->data = ev_link_from_target (xps_document, target); + + ev_link_mapping->area.x1 = area.x; + ev_link_mapping->area.x2 = area.x + area.width; + ev_link_mapping->area.y1 = area.y; + ev_link_mapping->area.y2 = area.y + area.height; + + retval = g_list_prepend (retval, ev_link_mapping); + gxps_link_free (xps_link); + } + + g_list_free (mapping_list); + + return ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref); +} + +static EvLinkDest * +xps_document_links_find_link_dest (EvDocumentLinks *document_links, const gchar *link_name) +{ + XPSDocument *xps_document = XPS_DOCUMENT (document_links); + GXPSPage *xps_page; + gint page; + cairo_rectangle_t area; + EvLinkDest *dest = NULL; + + page = gxps_document_get_page_for_anchor (xps_document->doc, link_name); + if (page == -1) + return NULL; + + xps_page = gxps_document_get_page (xps_document->doc, page, NULL); + if (!xps_page) + return NULL; + + if (gxps_page_get_anchor_destination (xps_page, link_name, &area, NULL)) + dest = ev_link_dest_new_xyz (page, area.x, area.y, 1., TRUE, TRUE, FALSE); + + g_object_unref (xps_page); + + return dest; +} + +static gint +xps_document_links_find_link_page (EvDocumentLinks *document_links, + const gchar *link_name) +{ + XPSDocument *xps_document = XPS_DOCUMENT (document_links); + + return gxps_document_get_page_for_anchor (xps_document->doc, link_name); +} + +static void +xps_document_document_links_iface_init (EvDocumentLinksInterface *iface) +{ + iface->has_document_links = xps_document_links_has_document_links; + iface->get_links_model = xps_document_links_get_links_model; + iface->get_links = xps_document_links_get_links; + iface->find_link_dest = xps_document_links_find_link_dest; + iface->find_link_page = xps_document_links_find_link_page; +} + + +/* EvDocumentPrint */ +static void +xps_document_print_print_page (EvDocumentPrint *document, + EvPage *page, + cairo_t *cr) +{ + GError *error = NULL; + + gxps_page_render (GXPS_PAGE (page->backend_page), cr, &error); + if (error) { + g_warning ("Error rendering page %d for printing: %s\n", + page->index, error->message); + g_error_free (error); + } +} + +static void +xps_document_document_print_iface_init (EvDocumentPrintInterface *iface) +{ + iface->print_page = xps_document_print_print_page; +} + diff --git a/backend/xps/xps-document.h b/backend/xps/xps-document.h new file mode 100644 index 00000000..c5cc0cd6 --- /dev/null +++ b/backend/xps/xps-document.h @@ -0,0 +1,45 @@ +/* this file is part of atril, a mate document viewer + * + * Copyright (C) 2010 Carlos Garcia Campos + * 2012 Leandro Vital + * + * Atril is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Atril is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __XPS_DOCUMENT_H__ +#define __XPS_DOCUMENT_H__ + +#include + +#include "ev-document.h" + +G_BEGIN_DECLS + +#define XPS_TYPE_DOCUMENT (xps_document_get_type()) +#define XPS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XPS_TYPE_DOCUMENT, XPSDocument)) +#define XPS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XPS_TYPE_DOCUMENT, XPSDocumentClass)) +#define XPS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XPS_TYPE_DOCUMENT)) +#define XPS_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XPS_TYPE_DOCUMENT, XPSDocumentClass)) + +typedef struct _XPSDocument XPSDocument; +typedef struct _XPSDocumentClass XPSDocumentClass; + +GType xps_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __XPS_DOCUMENT_H__ */ diff --git a/backend/xps/xpsdocument.atril-backend.in b/backend/xps/xpsdocument.atril-backend.in new file mode 100644 index 00000000..3fca5801 --- /dev/null +++ b/backend/xps/xpsdocument.atril-backend.in @@ -0,0 +1,5 @@ +[Atril Backend] +Module=xpsdocument +Resident=true +_TypeDescription=XPS Documents +MimeType=application/oxps;application/vnd.ms-xpsdocument diff --git a/configure.ac b/configure.ac index ab808986..112e7264 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # ***************************************************************************** m4_define([ev_major_version], [1]) -m4_define([ev_minor_version], [4]) +m4_define([ev_minor_version], [5]) m4_define([ev_micro_version], [0]) m4_define([ev_extra_version], []) m4_define([ev_version], [ev_major_version.ev_minor_version.ev_micro_version()ev_extra_version]) @@ -679,6 +679,30 @@ AM_CONDITIONAL(ENABLE_COMICS, test x$enable_comics = xyes) dnl ================== End of comic book checks ============================================ +dnl ================== XPS checks =================================================== + +AC_ARG_ENABLE(xps, + [AS_HELP_STRING([--enable-xps], + [Compile with support for XPS documents.])], + [enable_xps=$enableval], + [enable_xps=yes]) + +if test "x$enable_xps" = "xyes"; then + GXPS_REQUIRED=0.0.1 + PKG_CHECK_MODULES(GXPS, libgxps >= $GXPS_REQUIRED,enable_xps=yes,enable_xps=no) + + if test "x$enable_xps" = "xyes"; then + AC_DEFINE([ENABLE_XPS], [1], [Enable support for XPS documents.]) + else + enable_xps="no" + AC_MSG_WARN(["XPS support is disabled since libgxps (version >= $GXPS_REQUIRED) is needed"]) + fi +fi + +AM_CONDITIONAL(ENABLE_XPS, test x$enable_xps = xyes) + +dnl ================== End of XPS checks =================================================== + dnl ================== impress book checks =================================================== AC_ARG_ENABLE(impress, @@ -717,6 +741,9 @@ fi if test "x$enable_pixbuf" = "xyes"; then ATRIL_MIME_TYPES="${ATRIL_MIME_TYPES}image/*;" fi +if test "x$enable_xps" = "xyes"; then + EVINCE_MIME_TYPES="${EVINCE_MIME_TYPES}application/oxps;application/vnd.ms-xpsdocument;" +fi if test "x$enable_impress" = "xyes"; then ATRIL_MIME_TYPES="${ATRIL_MIME_TYPES}application/vnd.sun.xml.impress;application/vnd.oasis.opendocument.presentation;" fi @@ -799,6 +826,7 @@ backend/pdf/Makefile backend/pixbuf/Makefile backend/ps/Makefile backend/tiff/Makefile +backend/xps/Makefile cut-n-paste/Makefile cut-n-paste/gimpcellrenderertoggle/Makefile cut-n-paste/smclient/Makefile @@ -883,5 +911,6 @@ Configure summary: DVI Backend........: $enable_dvi Pixbuf Backend.....: $enable_pixbuf Comics Backend.....: $enable_comics + XPS Backend........: $enable_xps Impress Backend....: $enable_impress " diff --git a/libdocument/ev-document-links.c b/libdocument/ev-document-links.c index 3fa0b18b..1c8eb03e 100644 --- a/libdocument/ev-document-links.c +++ b/libdocument/ev-document-links.c @@ -77,6 +77,20 @@ ev_document_links_find_link_dest (EvDocumentLinks *document_links, return retval; } +gint +ev_document_links_find_link_page (EvDocumentLinks *document_links, + const gchar *link_name) +{ + EvDocumentLinksInterface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links); + gint retval; + + ev_document_doc_mutex_lock (); + retval = iface->find_link_page (document_links, link_name); + ev_document_doc_mutex_unlock (); + + return retval; +} + /* Helper functions */ gint ev_document_links_get_dest_page (EvDocumentLinks *document_links, diff --git a/libdocument/ev-document-links.h b/libdocument/ev-document-links.h index 286131d4..75cba6a1 100644 --- a/libdocument/ev-document-links.h +++ b/libdocument/ev-document-links.h @@ -67,6 +67,8 @@ struct _EvDocumentLinksInterface EvPage *page); EvLinkDest *(* find_link_dest) (EvDocumentLinks *document_links, const gchar *link_name); + gint (* find_link_page) (EvDocumentLinks *document_links, + const gchar *link_name); }; GType ev_document_links_get_type (void) G_GNUC_CONST; @@ -77,6 +79,8 @@ EvMappingList *ev_document_links_get_links (EvDocumentLinks *document_ EvPage *page); EvLinkDest *ev_document_links_find_link_dest (EvDocumentLinks *document_links, const gchar *link_name); +gint ev_document_links_find_link_page (EvDocumentLinks *document_links, + const gchar *link_name); gint ev_document_links_get_dest_page (EvDocumentLinks *document_links, EvLinkDest *dest); gchar *ev_document_links_get_dest_page_label (EvDocumentLinks *document_links, -- cgit v1.2.1