Commit a94a0aa4 authored by Carlos Garcia Campos's avatar Carlos Garcia Campos

Add support for XPS Documents

New backend to read XPS documents using libgxps. Fixes bug #321868.
parent b6f2f6c3
......@@ -30,6 +30,10 @@ if ENABLE_COMICS
SUBDIRS += comics
endif
if ENABLE_XPS
SUBDIRS += xps
endif
EXTRA_DIST = \
backend.symbols
......
backend_LTLIBRARIES = libxpsdocument.la
libxpsdocument_la_SOURCES = \
xps-document.c \
xps-document.h
libxpsdocument_la_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/libdocument \
-DGNOMELOCALEDIR=\"$(datadir)/locale\" \
-DEVINCE_COMPILATION
libxpsdocument_la_CFLAGS = \
$(BACKEND_CFLAGS) \
$(GXPS_CFLAGS) \
$(WARN_CFLAGS) \
$(DISABLE_DEPRECATED)
libxpsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
libxpsdocument_la_LIBADD = \
$(top_builddir)/libdocument/libevdocument3.la \
$(BACKEND_LIBS) \
$(GXPS_LIBS)
backend_in_files = xpsdocument.evince-backend.in
backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend)
EXTRA_DIST = $(backend_in_files)
CLEANFILES = $(backend_DATA)
@EV_INTLTOOL_EVINCE_BACKEND_RULE@
-include $(top_srcdir)/git.mk
/* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
*
* Evince 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.
*
* Evince 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 <config.h>
#include <glib/gi18n-lib.h>
#include <libgxps/gxps.h>
#include "xps-document.h"
#include "ev-document-links.h"
#include "ev-document-misc.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);
EV_BACKEND_REGISTER_WITH_CODE (XPSDocument, xps_document,
{
EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
xps_document_document_links_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)
{
GXPSPage *xps_page;
guint w, h;
xps_page = GXPS_PAGE (page->backend_page);
gxps_page_get_size (xps_page, &w, &h);
if (width)
*width = (gdouble)w;
if (height)
*height = (gdouble)h;
}
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;
guint 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 {
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;
}
/* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
*
* Evince 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.
*
* Evince 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 <glib-object.h>
#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__ */
[Evince Backend]
Module=xpsdocument
Resident=true
_TypeDescription=XPS Documents
MimeType=application/vnd.ms-xpsdocument
......@@ -684,6 +684,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 =================== Mime types list ====================================================
if test "x$enable_pdf" = "xyes" ; then
......@@ -707,6 +731,9 @@ fi
if test "x$enable_pixbuf" = "xyes"; then
EVINCE_MIME_TYPES="${EVINCE_MIME_TYPES}image/*;"
fi
if test "x$enable_xps" = "xyes"; then
EVINCE_MIME_TYPES="${EVINCE_MIME_TYPES}application/vnd.ms-xpsdocument;"
fi
AC_SUBST(EVINCE_MIME_TYPES)
AC_CHECK_FUNC(localtime_r, AC_DEFINE(HAVE_LOCALTIME_R, 1, [Defines if localtime_r is available on your system]))
......@@ -785,6 +812,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
......@@ -868,4 +896,5 @@ Configure summary:
DVI Backend........: $enable_dvi
Pixbuf Backend.....: $enable_pixbuf
Comics Backend.....: $enable_comics
XPS Backend........: $enable_xps
"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment