X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=backend%2Fpdf%2Fev-poppler.cc;h=8659d082db5803e15de872accd155f470ae75c3c;hb=2297bff1e7d745f0f837d44feeda03244368d7f1;hp=a765cb4cd2d56e1cf62c3ec7a5bd5384c8c09031;hpb=81c134286178c1b09b7505c1a9e8fba0c612fe1f;p=evince.git diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc index a765cb4c..8659d082 100644 --- a/backend/pdf/ev-poppler.cc +++ b/backend/pdf/ev-poppler.cc @@ -1,5 +1,7 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ -/* pdfdocument.h: Implementation of EvDocument for PDF +/* this file is part of evince, a gnome document viewer + * + * Copyright (C) 2009, Juanjo Marín * Copyright (C) 2004, Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify @@ -14,7 +16,7 @@ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" @@ -31,10 +33,11 @@ #ifdef HAVE_CAIRO_PS #include #endif -#include +#include #include "ev-poppler.h" #include "ev-file-exporter.h" +#include "ev-mapping.h" #include "ev-document-find.h" #include "ev-document-misc.h" #include "ev-document-links.h" @@ -44,23 +47,30 @@ #include "ev-document-thumbnails.h" #include "ev-document-transition.h" #include "ev-document-forms.h" +#include "ev-document-layers.h" +#include "ev-document-print.h" +#include "ev-document-annotations.h" +#include "ev-document-attachments.h" #include "ev-selection.h" #include "ev-transition-effect.h" #include "ev-attachment.h" #include "ev-image.h" -#if (defined (HAVE_POPPLER_PAGE_RENDER)) && (defined (HAVE_CAIRO_PDF) || defined (HAVE_CAIRO_PS)) +#include +#include +#include +#include + +#if (defined (HAVE_CAIRO_PDF) || defined (HAVE_CAIRO_PS)) #define HAVE_CAIRO_PRINT #endif -typedef struct { - PdfDocument *document; - char *text; - GList **pages; - guint idle; - int start_page; - int search_page; -} PdfDocumentSearch; +/* fields from the XMP Rights Management Schema, XMP Specification Sept 2005, pag. 45 */ +#define LICENSE_MARKED "/x:xmpmeta/rdf:RDF/rdf:Description/xmpRights:Marked" +#define LICENSE_TEXT "/x:xmpmeta/rdf:RDF/rdf:Description/dc:rights/rdf:Alt/rdf:li[lang('%s')]" +#define LICENSE_WEB_STATEMENT "/x:xmpmeta/rdf:RDF/rdf:Description/xmpRights:WebStatement" +/* license field from Creative Commons schema, http://creativecommons.org/ns */ +#define LICENSE_URI "/x:xmpmeta/rdf:RDF/rdf:Description/cc:license/@rdf:resource" typedef struct { EvFileExporterFormat format; @@ -82,47 +92,55 @@ typedef struct { struct _PdfDocumentClass { - GObjectClass parent_class; + EvDocumentClass parent_class; }; struct _PdfDocument { - GObject parent_instance; + EvDocument parent_instance; PopplerDocument *document; gchar *password; + gboolean modified; PopplerFontInfo *font_info; PopplerFontsIter *fonts_iter; int fonts_scanned_pages; - PdfDocumentSearch *search; PdfPrintContext *print_ctx; + + GList *layers; }; -static void pdf_document_document_iface_init (EvDocumentIface *iface); -static void pdf_document_security_iface_init (EvDocumentSecurityIface *iface); -static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface); -static void pdf_document_document_links_iface_init (EvDocumentLinksIface *iface); -static void pdf_document_document_images_iface_init (EvDocumentImagesIface *iface); -static void pdf_document_document_forms_iface_init (EvDocumentFormsIface *iface); -static void pdf_document_document_fonts_iface_init (EvDocumentFontsIface *iface); -static void pdf_document_find_iface_init (EvDocumentFindIface *iface); -static void pdf_document_file_exporter_iface_init (EvFileExporterIface *iface); -static void pdf_selection_iface_init (EvSelectionIface *iface); -static void pdf_document_page_transition_iface_init (EvDocumentTransitionIface *iface); -static void pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, - EvRenderContext *rc, - gint *width, - gint *height); -static int pdf_document_get_n_pages (EvDocument *document); - -static EvLinkDest *ev_link_dest_from_dest (PdfDocument *pdf_document, - PopplerDest *dest); -static EvLink *ev_link_from_action (PdfDocument *pdf_document, - PopplerAction *action); -static void pdf_document_search_free (PdfDocumentSearch *search); -static void pdf_print_context_free (PdfPrintContext *ctx); +static void pdf_document_security_iface_init (EvDocumentSecurityInterface *iface); +static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); +static void pdf_document_document_links_iface_init (EvDocumentLinksInterface *iface); +static void pdf_document_document_images_iface_init (EvDocumentImagesInterface *iface); +static void pdf_document_document_forms_iface_init (EvDocumentFormsInterface *iface); +static void pdf_document_document_fonts_iface_init (EvDocumentFontsInterface *iface); +static void pdf_document_document_layers_iface_init (EvDocumentLayersInterface *iface); +static void pdf_document_document_print_iface_init (EvDocumentPrintInterface *iface); +static void pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface); +static void pdf_document_document_attachments_iface_init (EvDocumentAttachmentsInterface *iface); +static void pdf_document_find_iface_init (EvDocumentFindInterface *iface); +static void pdf_document_file_exporter_iface_init (EvFileExporterInterface *iface); +static void pdf_selection_iface_init (EvSelectionInterface *iface); +static void pdf_document_page_transition_iface_init (EvDocumentTransitionInterface *iface); +static void pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + gint *width, + gint *height); +static int pdf_document_get_n_pages (EvDocument *document); + +static EvLinkDest *ev_link_dest_from_dest (PdfDocument *pdf_document, + PopplerDest *dest); +static EvLink *ev_link_from_action (PdfDocument *pdf_document, + PopplerAction *action); +static void pdf_print_context_free (PdfPrintContext *ctx); +static gboolean attachment_save_to_buffer (PopplerAttachment *attachment, + gchar **buffer, + gsize *buffer_size, + GError **error); EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, { @@ -138,6 +156,14 @@ EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, pdf_document_document_forms_iface_init); EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FONTS, pdf_document_document_fonts_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LAYERS, + pdf_document_document_layers_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_PRINT, + pdf_document_document_print_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_ANNOTATIONS, + pdf_document_document_annotations_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_ATTACHMENTS, + pdf_document_document_attachments_iface_init); EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, pdf_document_find_iface_init); EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, @@ -148,27 +174,6 @@ EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, pdf_document_page_transition_iface_init); }); -static void -pdf_document_search_free (PdfDocumentSearch *search) -{ - PdfDocument *pdf_document = search->document; - int n_pages; - int i; - - if (search->idle != 0) - g_source_remove (search->idle); - - n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document)); - for (i = 0; i < n_pages; i++) { - g_list_foreach (search->pages[i], (GFunc) g_free, NULL); - g_list_free (search->pages[i]); - } - g_free (search->pages); - - g_free (search->text); - g_free (search); -} - static void pdf_document_dispose (GObject *object) { @@ -178,11 +183,6 @@ pdf_document_dispose (GObject *object) pdf_print_context_free (pdf_document->print_ctx); pdf_document->print_ctx = NULL; } - - if (pdf_document->search) { - pdf_document_search_free (pdf_document->search); - pdf_document->search = NULL; - } if (pdf_document->document) { g_object_unref (pdf_document->document); @@ -196,15 +196,12 @@ pdf_document_dispose (GObject *object) poppler_fonts_iter_free (pdf_document->fonts_iter); } - G_OBJECT_CLASS (pdf_document_parent_class)->dispose (object); -} - -static void -pdf_document_class_init (PdfDocumentClass *klass) -{ - GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + if (pdf_document->layers) { + g_list_foreach (pdf_document->layers, (GFunc)g_object_unref, NULL); + g_list_free (pdf_document->layers); + } - g_object_class->dispose = pdf_document_dispose; + G_OBJECT_CLASS (pdf_document_parent_class)->dispose (object); } static void @@ -228,12 +225,12 @@ convert_error (GError *poppler_error, else if (poppler_error->code == POPPLER_ERROR_ENCRYPTED) code = EV_DOCUMENT_ERROR_ENCRYPTED; + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + code, + poppler_error->message); - g_set_error (error, - EV_DOCUMENT_ERROR, - code, - poppler_error->message, - NULL); + g_error_free (poppler_error); } else { g_propagate_error (error, poppler_error); } @@ -246,12 +243,18 @@ pdf_document_save (EvDocument *document, const char *uri, GError **error) { + PdfDocument *pdf_document = PDF_DOCUMENT (document); gboolean retval; GError *poppler_error = NULL; - retval = poppler_document_save (PDF_DOCUMENT (document)->document, - uri, - &poppler_error); + if (pdf_document->modified) { + retval = poppler_document_save (pdf_document->document, + uri, &poppler_error); + } else { + retval = poppler_document_save_a_copy (pdf_document->document, + uri, &poppler_error); + } + if (! retval) convert_error (poppler_error, error); @@ -325,122 +328,6 @@ pdf_document_get_page_label (EvDocument *document, return label; } -static gboolean -pdf_document_has_attachments (EvDocument *document) -{ - PdfDocument *pdf_document; - - pdf_document = PDF_DOCUMENT (document); - - return poppler_document_has_attachments (pdf_document->document); -} - -struct SaveToBufferData { - gchar *buffer; - gsize len, max; -}; - -static gboolean -attachment_save_to_buffer_callback (const gchar *buf, - gsize count, - gpointer user_data, - GError **error) -{ - struct SaveToBufferData *sdata = (SaveToBufferData *)user_data; - gchar *new_buffer; - gsize new_max; - - if (sdata->len + count > sdata->max) { - new_max = MAX (sdata->max * 2, sdata->len + count); - new_buffer = (gchar *)g_realloc (sdata->buffer, new_max); - - sdata->buffer = new_buffer; - sdata->max = new_max; - } - - memcpy (sdata->buffer + sdata->len, buf, count); - sdata->len += count; - - return TRUE; -} - -static gboolean -attachment_save_to_buffer (PopplerAttachment *attachment, - gchar **buffer, - gsize *buffer_size, - GError **error) -{ - static const gint initial_max = 1024; - struct SaveToBufferData sdata; - - *buffer = NULL; - *buffer_size = 0; - - sdata.buffer = (gchar *) g_malloc (initial_max); - sdata.max = initial_max; - sdata.len = 0; - - if (! poppler_attachment_save_to_callback (attachment, - attachment_save_to_buffer_callback, - &sdata, - error)) { - g_free (sdata.buffer); - return FALSE; - } - - *buffer = sdata.buffer; - *buffer_size = sdata.len; - - return TRUE; -} - -static GList * -pdf_document_get_attachments (EvDocument *document) -{ - PdfDocument *pdf_document; - GList *attachments; - GList *list; - GList *retval = NULL; - - pdf_document = PDF_DOCUMENT (document); - - if (!pdf_document_has_attachments (document)) - return NULL; - - attachments = poppler_document_get_attachments (pdf_document->document); - - for (list = attachments; list; list = list->next) { - PopplerAttachment *attachment; - EvAttachment *ev_attachment; - gchar *data = NULL; - gsize size; - GError *error = NULL; - - attachment = (PopplerAttachment *) list->data; - - if (attachment_save_to_buffer (attachment, &data, &size, &error)) { - ev_attachment = ev_attachment_new (attachment->name, - attachment->description, - attachment->mtime, - attachment->ctime, - size, data); - - retval = g_list_prepend (retval, ev_attachment); - } else { - if (error) { - g_warning ("%s", error->message); - g_error_free (error); - - g_free (data); - } - } - - g_object_unref (attachment); - } - - return g_list_reverse (retval); -} - static cairo_surface_t * pdf_page_render (PopplerPage *page, gint width, @@ -448,17 +335,12 @@ pdf_page_render (PopplerPage *page, EvRenderContext *rc) { cairo_surface_t *surface; - -#ifdef HAVE_POPPLER_PAGE_RENDER cairo_t *cr; - - surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - memset (cairo_image_surface_get_data (surface), 0xff, - cairo_image_surface_get_height (surface) * - cairo_image_surface_get_stride (surface)); - cr = cairo_create (surface); + switch (rc->rotation) { case 90: cairo_translate (cr, width, 0); @@ -475,25 +357,14 @@ pdf_page_render (PopplerPage *page, cairo_scale (cr, rc->scale, rc->scale); cairo_rotate (cr, rc->rotation * G_PI / 180.0); poppler_page_render (page, cr); - cairo_destroy (cr); -#else /* HAVE_POPPLER_PAGE_RENDER */ - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - FALSE, 8, - width, height); - poppler_page_render_to_pixbuf (page, - 0, 0, - width, height, - rc->scale, - rc->rotation, - pixbuf); - surface = ev_document_misc_surface_from_pixbuf (pixbuf); - g_object_unref (pixbuf); -#endif /* HAVE_POPPLER_PAGE_RENDER */ + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + + cairo_destroy (cr); - return surface; + return surface; } static cairo_surface_t * @@ -522,27 +393,229 @@ pdf_document_render (EvDocument *document, width, height, rc); } -/* EvDocumentSecurity */ - -static gboolean -pdf_document_has_document_security (EvDocumentSecurity *document_security) +/* reference: +http://www.pdfa.org/lib/exe/fetch.php?id=pdfa%3Aen%3Atechdoc&cache=cache&media=pdfa:techdoc:tn0001_pdfa-1_and_namespaces_2008-03-18.pdf */ +static char * +pdf_document_get_format_from_metadata (xmlDocPtr doc, + xmlXPathContextPtr xpathCtx) { - /* FIXME: do we really need to have this? */ - return FALSE; + xmlXPathObjectPtr xpathObj; + xmlChar *part = NULL; + xmlChar *conf = NULL; + char *result = NULL; + int i; + + /* add pdf/a namespaces */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "x", BAD_CAST "adobe:ns:meta/"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "rdf", BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "pdfaid", BAD_CAST "http://www.aiim.org/pdfa/ns/id/"); + + /* reads pdf/a part */ + /* first syntax: child node */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/pdfaid:part", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + part = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + if (part == NULL) { + /* second syntax: attribute */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/@pdfaid:part", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + part = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + } + + /* reads pdf/a conformance */ + /* first syntax: child node */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/pdfaid:conformance", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + conf = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + if (conf == NULL) { + /* second syntax: attribute */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/@pdfaid:conformance", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + conf = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + } + + if (part != NULL && conf != NULL) { + /* makes conf lowercase */ + for (i = 0; conf[i]; i++) + conf[i] = g_ascii_tolower (conf[i]); + + /* return buffer */ + result = g_strdup_printf ("PDF/A - %s%s", part, conf); + } + + /* Cleanup */ + xmlFree (part); + xmlFree (conf); + + return result; +} + +static EvDocumentLicense * +pdf_document_get_license_from_metadata (xmlDocPtr doc, + xmlXPathContextPtr xpathCtx) +{ + xmlXPathObjectPtr xpathObj; + xmlChar *marked = NULL; + const char *language_string; + char *aux; + gchar **tags; + gchar *tag, *tag_aux; + int i, j; + EvDocumentLicense *license; + + /* register namespaces */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "x", BAD_CAST "adobe:ns:meta/"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "rdf", BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "dc", BAD_CAST "http://purl.org/dc/elements/1.1/"); + /* XMP Rights Management Schema */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "xmpRights", BAD_CAST "http://ns.adobe.com/xap/1.0/rights/"); + /* Creative Commons Schema */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "cc", BAD_CAST "http://creativecommons.org/ns#"); + + /* checking if the document has been marked as defined on the XMP Rights + * Management Schema */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_MARKED, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + marked = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + + /* a) Not marked => No XMP Rights information */ + if (!marked) { + xmlFree (marked); + return NULL; + } + + license = ev_document_license_new (); + + /* b) Marked False => Public Domain, no copyrighted material and no + * license needed */ + if (g_strrstr ((char *) marked, "False") != NULL) { + license->text = g_strdup (_("This work is in the Public Domain")); + /* c) Marked True => Copyrighted material */ + } else { + /* Checking usage terms as defined by the XMP Rights Management + * Schema. This field is recomended to be checked by Creative + * Commons */ + /* 1) checking for a suitable localized string */ + language_string = pango_language_to_string (gtk_get_default_language ()); + tags = g_strsplit (language_string, "-", -1); + i = g_strv_length (tags); + while (i-- && !license->text) { + tag = g_strdup (tags[0]); + for (j = 1; j <= i; j++) { + tag_aux = g_strdup_printf ("%s-%s", tag, tags[j]); + g_free (tag); + tag = tag_aux; + } + aux = g_strdup_printf (LICENSE_TEXT, tag); + xpathObj = xmlXPathEvalExpression (BAD_CAST aux, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->text = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + g_free (tag); + g_free (aux); + } + g_strfreev(tags); + + /* 2) if not, use the default string */ + if (!license->text) { + aux = g_strdup_printf (LICENSE_TEXT, "x-default"); + xpathObj = xmlXPathEvalExpression (BAD_CAST aux, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->text = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + g_free (aux); + } + + /* Checking the license URI as defined by the Creative Commons + * Schema. This field is recomended to be checked by Creative + * Commons */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_URI, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->uri = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + + /* Checking the web statement as defined by the XMP Rights + * Management Schema. Checking it out is a sort of above-and-beyond + * the basic recommendations by Creative Commons. It can be + * considered as a "reinforcement" approach to add certainty. */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_WEB_STATEMENT, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->web_statement = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + } + xmlFree (marked); + + if (!license->text && !license->uri && !license->web_statement) { + ev_document_license_free (license); + return NULL; + } + + return license; } static void -pdf_document_set_password (EvDocumentSecurity *document_security, - const char *password) +pdf_document_parse_metadata (const gchar *metadata, + EvDocumentInfo *info) { - PdfDocument *document = PDF_DOCUMENT (document_security); + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + gchar *fmt; - if (document->password) - g_free (document->password); + doc = xmlParseMemory (metadata, strlen (metadata)); + if (doc == NULL) + return; /* invalid xml metadata */ - document->password = g_strdup (password); + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + xmlFreeDoc (doc); + return; /* invalid xpath context */ + } + + fmt = pdf_document_get_format_from_metadata (doc, xpathCtx); + if (fmt != NULL) { + g_free (info->format); + info->format = fmt; + } + + info->license = pdf_document_get_license_from_metadata (doc, xpathCtx); + + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); } + static EvDocumentInfo * pdf_document_get_info (EvDocument *document) { @@ -552,6 +625,7 @@ pdf_document_get_info (EvDocument *document) PopplerViewerPreferences view_prefs; PopplerPermissions permissions; EvPage *page; + char *metadata; info = g_new0 (EvDocumentInfo, 1); @@ -571,7 +645,8 @@ pdf_document_get_info (EvDocument *document) EV_DOCUMENT_INFO_LINEARIZED | EV_DOCUMENT_INFO_N_PAGES | EV_DOCUMENT_INFO_SECURITY | - EV_DOCUMENT_INFO_PAPER_SIZE; + EV_DOCUMENT_INFO_PAPER_SIZE | + EV_DOCUMENT_INFO_LICENSE; g_object_get (PDF_DOCUMENT (document)->document, "title", &(info->title), @@ -588,18 +663,20 @@ pdf_document_get_info (EvDocument *document) "creation-date", &(info->creation_date), "mod-date", &(info->modified_date), "linearized", &(info->linearized), + "metadata", &metadata, NULL); + if (metadata != NULL) { + pdf_document_parse_metadata (metadata, info); + g_free (metadata); + } + info->n_pages = ev_document_get_n_pages (document); if (info->n_pages > 0) { - page = ev_document_get_page (document, 0); - ev_document_get_page_size (document, page, + ev_document_get_page_size (document, 0, &(info->paper_width), &(info->paper_height)); - g_object_unref (page); - - // Convert to mm. info->paper_width = info->paper_width / 72.0f * 25.4f; info->paper_height = info->paper_height / 72.0f * 25.4f; @@ -694,23 +771,70 @@ pdf_document_get_info (EvDocument *document) return info; } +static gboolean +pdf_document_get_backend_info (EvDocument *document, EvDocumentBackendInfo *info) +{ + PopplerBackend backend; + + backend = poppler_get_backend (); + switch (backend) { + case POPPLER_BACKEND_CAIRO: + info->name = "poppler/cairo"; + break; + case POPPLER_BACKEND_SPLASH: + info->name = "poppler/splash"; + break; + default: + info->name = "poppler/unknown"; + break; + } + + info->version = poppler_get_version (); + + return TRUE; +} + static void -pdf_document_document_iface_init (EvDocumentIface *iface) -{ - iface->save = pdf_document_save; - iface->load = pdf_document_load; - iface->get_n_pages = pdf_document_get_n_pages; - iface->get_page = pdf_document_get_page; - iface->get_page_size = pdf_document_get_page_size; - iface->get_page_label = pdf_document_get_page_label; - iface->has_attachments = pdf_document_has_attachments; - iface->get_attachments = pdf_document_get_attachments; - iface->render = pdf_document_render; - iface->get_info = pdf_document_get_info; -}; +pdf_document_class_init (PdfDocumentClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + g_object_class->dispose = pdf_document_dispose; + + ev_document_class->save = pdf_document_save; + ev_document_class->load = pdf_document_load; + ev_document_class->get_n_pages = pdf_document_get_n_pages; + ev_document_class->get_page = pdf_document_get_page; + ev_document_class->get_page_size = pdf_document_get_page_size; + ev_document_class->get_page_label = pdf_document_get_page_label; + ev_document_class->render = pdf_document_render; + ev_document_class->get_info = pdf_document_get_info; + ev_document_class->get_backend_info = pdf_document_get_backend_info; +} + +/* EvDocumentSecurity */ +static gboolean +pdf_document_has_document_security (EvDocumentSecurity *document_security) +{ + /* FIXME: do we really need to have this? */ + return FALSE; +} + +static void +pdf_document_set_password (EvDocumentSecurity *document_security, + const char *password) +{ + PdfDocument *document = PDF_DOCUMENT (document_security); + + if (document->password) + g_free (document->password); + + document->password = g_strdup (password); +} static void -pdf_document_security_iface_init (EvDocumentSecurityIface *iface) +pdf_document_security_iface_init (EvDocumentSecurityInterface *iface) { iface->has_document_security = pdf_document_has_document_security; iface->set_password = pdf_document_set_password; @@ -830,7 +954,7 @@ pdf_document_fonts_fill_model (EvDocumentFonts *document_fonts, } static void -pdf_document_document_fonts_iface_init (EvDocumentFontsIface *iface) +pdf_document_document_fonts_iface_init (EvDocumentFontsInterface *iface) { iface->fill_model = pdf_document_fonts_fill_model; iface->scan = pdf_document_fonts_scan; @@ -872,7 +996,7 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, poppler_page_get_size (poppler_page, NULL, &height); ev_dest = ev_link_dest_new_xyz (dest->page_num - 1, dest->left, - height - dest->top, + height - MIN (height, dest->top), dest->zoom, dest->change_left, dest->change_top, @@ -880,9 +1004,11 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, g_object_unref (poppler_page); } break; - case POPPLER_DEST_FIT: + case POPPLER_DEST_FITB: + case POPPLER_DEST_FIT: ev_dest = ev_link_dest_new_fit (dest->page_num - 1); break; + case POPPLER_DEST_FITBH: case POPPLER_DEST_FITH: { PopplerPage *poppler_page; double height; @@ -891,11 +1017,12 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, MAX (0, dest->page_num - 1)); poppler_page_get_size (poppler_page, NULL, &height); ev_dest = ev_link_dest_new_fith (dest->page_num - 1, - height - dest->top, + height - MIN (height, dest->top), dest->change_top); g_object_unref (poppler_page); } break; + case POPPLER_DEST_FITBV: case POPPLER_DEST_FITV: ev_dest = ev_link_dest_new_fitv (dest->page_num - 1, dest->left, @@ -910,21 +1037,12 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, poppler_page_get_size (poppler_page, NULL, &height); ev_dest = ev_link_dest_new_fitr (dest->page_num - 1, dest->left, - height - dest->bottom, + height - MIN (height, dest->bottom), dest->right, - height - dest->top); + height - MIN (height, dest->top)); g_object_unref (poppler_page); } break; - case POPPLER_DEST_FITB: - unimplemented_dest = "POPPLER_DEST_FITB"; - break; - case POPPLER_DEST_FITBH: - unimplemented_dest = "POPPLER_DEST_FITBH"; - break; - case POPPLER_DEST_FITBV: - unimplemented_dest = "POPPLER_DEST_FITBV"; - break; case POPPLER_DEST_NAMED: ev_dest = ev_link_dest_new_named (dest->named_dest); break; @@ -986,6 +1104,14 @@ ev_link_from_action (PdfDocument *pdf_document, case POPPLER_ACTION_MOVIE: unimplemented_action = "POPPLER_ACTION_MOVIE"; break; +#if POPPLER_CHECK_VERSION (0, 13, 2) + case POPPLER_ACTION_RENDITION: + unimplemented_action = "POPPLER_ACTION_RENDITION"; + break; + case POPPLER_ACTION_OCG_STATE: + unimplemented_action = "POPPLER_ACTION_OCG_STATE"; + break; +#endif case POPPLER_ACTION_UNKNOWN: unimplemented_action = "POPPLER_ACTION_UNKNOWN"; } @@ -1107,7 +1233,7 @@ pdf_document_links_get_links_model (EvDocumentLinks *document_links) static GList * pdf_document_links_get_links (EvDocumentLinks *document_links, - gint page) + EvPage *page) { PdfDocument *pdf_document; PopplerPage *poppler_page; @@ -1117,30 +1243,28 @@ pdf_document_links_get_links (EvDocumentLinks *document_links, double height; pdf_document = PDF_DOCUMENT (document_links); - poppler_page = poppler_document_get_page (pdf_document->document, - page); + poppler_page = POPPLER_PAGE (page->backend_page); mapping_list = poppler_page_get_link_mapping (poppler_page); poppler_page_get_size (poppler_page, NULL, &height); for (list = mapping_list; list; list = list->next) { PopplerLinkMapping *link_mapping; - EvLinkMapping *ev_link_mapping; + EvMapping *ev_link_mapping; link_mapping = (PopplerLinkMapping *)list->data; - ev_link_mapping = g_new (EvLinkMapping, 1); - ev_link_mapping->link = ev_link_from_action (pdf_document, + ev_link_mapping = g_new (EvMapping, 1); + ev_link_mapping->data = ev_link_from_action (pdf_document, link_mapping->action); - ev_link_mapping->x1 = link_mapping->area.x1; - ev_link_mapping->x2 = link_mapping->area.x2; + ev_link_mapping->area.x1 = link_mapping->area.x1; + ev_link_mapping->area.x2 = link_mapping->area.x2; /* Invert this for X-style coordinates */ - ev_link_mapping->y1 = height - link_mapping->area.y2; - ev_link_mapping->y2 = height - link_mapping->area.y1; + ev_link_mapping->area.y1 = height - link_mapping->area.y2; + ev_link_mapping->area.y2 = height - link_mapping->area.y1; retval = g_list_prepend (retval, ev_link_mapping); } poppler_page_free_link_mapping (mapping_list); - g_object_unref (poppler_page); return g_list_reverse (retval); } @@ -1165,7 +1289,7 @@ pdf_document_links_find_link_dest (EvDocumentLinks *document_links, } static void -pdf_document_document_links_iface_init (EvDocumentLinksIface *iface) +pdf_document_document_links_iface_init (EvDocumentLinksInterface *iface) { iface->has_document_links = pdf_document_links_has_document_links; iface->get_links_model = pdf_document_links_get_links_model; @@ -1175,7 +1299,7 @@ pdf_document_document_links_iface_init (EvDocumentLinksIface *iface) static GList * pdf_document_images_get_image_mapping (EvDocumentImages *document_images, - gint page) + EvPage *page) { GList *retval = NULL; PdfDocument *pdf_document; @@ -1184,28 +1308,27 @@ pdf_document_images_get_image_mapping (EvDocumentImages *document_images, GList *list; pdf_document = PDF_DOCUMENT (document_images); - poppler_page = poppler_document_get_page (pdf_document->document, page); + poppler_page = POPPLER_PAGE (page->backend_page); mapping_list = poppler_page_get_image_mapping (poppler_page); for (list = mapping_list; list; list = list->next) { PopplerImageMapping *image_mapping; - EvImageMapping *ev_image_mapping; + EvMapping *ev_image_mapping; image_mapping = (PopplerImageMapping *)list->data; - ev_image_mapping = g_new (EvImageMapping, 1); + ev_image_mapping = g_new (EvMapping, 1); - ev_image_mapping->image = ev_image_new (page, image_mapping->image_id); - ev_image_mapping->x1 = image_mapping->area.x1; - ev_image_mapping->x2 = image_mapping->area.x2; - ev_image_mapping->y1 = image_mapping->area.y1; - ev_image_mapping->y2 = image_mapping->area.y2; + ev_image_mapping->data = ev_image_new (page->index, image_mapping->image_id); + ev_image_mapping->area.x1 = image_mapping->area.x1; + ev_image_mapping->area.y1 = image_mapping->area.y1; + ev_image_mapping->area.x2 = image_mapping->area.x2; + ev_image_mapping->area.y2 = image_mapping->area.y2; retval = g_list_prepend (retval, ev_image_mapping); } poppler_page_free_image_mapping (mapping_list); - g_object_unref (poppler_page); return g_list_reverse (retval); } @@ -1214,10 +1337,10 @@ GdkPixbuf * pdf_document_images_get_image (EvDocumentImages *document_images, EvImage *image) { + GdkPixbuf *retval = NULL; PdfDocument *pdf_document; PopplerPage *poppler_page; cairo_surface_t *surface; - GdkPixbuf *retval = NULL; pdf_document = PDF_DOCUMENT (document_images); poppler_page = poppler_document_get_page (pdf_document->document, @@ -1235,22 +1358,20 @@ pdf_document_images_get_image (EvDocumentImages *document_images, } static void -pdf_document_document_images_iface_init (EvDocumentImagesIface *iface) +pdf_document_document_images_iface_init (EvDocumentImagesInterface *iface) { iface->get_image_mapping = pdf_document_images_get_image_mapping; iface->get_image = pdf_document_images_get_image; } static GdkPixbuf * -make_thumbnail_for_page (PdfDocument *pdf_document, - PopplerPage *poppler_page, - EvRenderContext *rc) +make_thumbnail_for_page (PopplerPage *poppler_page, + EvRenderContext *rc, + gint width, + gint height) { GdkPixbuf *pixbuf; - int width, height; - pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), - rc, &width, &height); #ifdef POPPLER_WITH_GDK pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height); @@ -1284,9 +1405,13 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails PopplerPage *poppler_page; GdkPixbuf *pixbuf = NULL; GdkPixbuf *border_pixbuf; + gint width, height; poppler_page = POPPLER_PAGE (rc->page->backend_page); + pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), + rc, &width, &height); + #ifdef POPPLER_WITH_GDK pixbuf = poppler_page_get_thumbnail_pixbuf (poppler_page); #else @@ -1299,20 +1424,29 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails } #endif /* POPPLER_WITH_GDK */ - if (pixbuf) { - /* Rotate provided thumbnail if needed */ - GdkPixbuf *rotated_pixbuf; + if (pixbuf != NULL) { + int thumb_width = (rc->rotation == 90 || rc->rotation == 270) ? + gdk_pixbuf_get_height (pixbuf) : + gdk_pixbuf_get_width (pixbuf); - rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, - (GdkPixbufRotation) (360 - rc->rotation)); - g_object_unref (pixbuf); - pixbuf = rotated_pixbuf; + if (thumb_width == width) { + GdkPixbuf *rotated_pixbuf; + + rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, + (GdkPixbufRotation) (360 - rc->rotation)); + g_object_unref (pixbuf); + pixbuf = rotated_pixbuf; + } else { + /* The provided thumbnail has a different size */ + g_object_unref (pixbuf); + pixbuf = make_thumbnail_for_page (poppler_page, rc, width, height); + } } else { - /* There is no provided thumbnail. We need to make one. */ - pixbuf = make_thumbnail_for_page (pdf_document, poppler_page, rc); + /* There is no provided thumbnail. We need to make one. */ + pixbuf = make_thumbnail_for_page (poppler_page, rc, width, height); } - if (border) { + if (border && pixbuf) { border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf); g_object_unref (pixbuf); pixbuf = border_pixbuf; @@ -1327,22 +1461,14 @@ pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnail gint *width, gint *height) { - PopplerPage *poppler_page; - gint has_thumb; + double page_width, page_height; - poppler_page = POPPLER_PAGE (rc->page->backend_page); - - has_thumb = poppler_page_get_thumbnail_size (poppler_page, width, height); - - if (!has_thumb || *width <= 0 || *height <= 0) { - double page_width, page_height; - - poppler_page_get_size (poppler_page, &page_width, &page_height); - - *width = (gint) MAX (page_width * rc->scale, 1); - *height = (gint) MAX (page_height * rc->scale, 1); - } + poppler_page_get_size (POPPLER_PAGE (rc->page->backend_page), + &page_width, &page_height); + *width = MAX ((gint)(page_width * rc->scale + 0.5), 1); + *height = MAX ((gint)(page_height * rc->scale + 0.5), 1); + if (rc->rotation == 90 || rc->rotation == 270) { gint temp; @@ -1353,7 +1479,7 @@ pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnail } static void -pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface) +pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) { iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail; iface->get_dimensions = pdf_document_thumbnails_get_dimensions; @@ -1369,7 +1495,8 @@ pdf_document_find_find_text (EvDocumentFind *document_find, GList *matches, *l; PopplerPage *poppler_page; gdouble height; - + GList *retval = NULL; + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); g_return_val_if_fail (text != NULL, NULL); @@ -1382,18 +1509,26 @@ pdf_document_find_find_text (EvDocumentFind *document_find, poppler_page_get_size (poppler_page, NULL, &height); for (l = matches; l && l->data; l = g_list_next (l)) { PopplerRectangle *rect = (PopplerRectangle *)l->data; - gdouble tmp; + EvRectangle *ev_rect; + + ev_rect = ev_rectangle_new (); + ev_rect->x1 = rect->x1; + ev_rect->x2 = rect->x2; + /* Invert this for X-style coordinates */ + ev_rect->y1 = height - rect->y2; + ev_rect->y2 = height - rect->y1; - tmp = rect->y1; - rect->y1 = height - rect->y2; - rect->y2 = height - tmp; + retval = g_list_prepend (retval, ev_rect); } - - return matches; + + g_list_foreach (matches, (GFunc)poppler_rectangle_free, NULL); + g_list_free (matches); + + return g_list_reverse (retval); } static void -pdf_document_find_iface_init (EvDocumentFindIface *iface) +pdf_document_find_iface_init (EvDocumentFindInterface *iface) { iface->find_text = pdf_document_find_find_text; } @@ -1663,21 +1798,17 @@ pdf_document_file_exporter_get_capabilities (EvFileExporter *exporter) EV_FILE_EXPORTER_CAN_REVERSE | EV_FILE_EXPORTER_CAN_SCALE | #ifdef HAVE_CAIRO_PRINT -#ifdef HAVE_POPPLER_PAGE_RENDER EV_FILE_EXPORTER_CAN_NUMBER_UP | #endif -#endif #ifdef HAVE_CAIRO_PDF -#ifdef HAVE_POPPLER_PAGE_RENDER EV_FILE_EXPORTER_CAN_GENERATE_PDF | -#endif #endif EV_FILE_EXPORTER_CAN_GENERATE_PS); } static void -pdf_document_file_exporter_iface_init (EvFileExporterIface *iface) +pdf_document_file_exporter_iface_init (EvFileExporterInterface *iface) { iface->begin = pdf_document_file_exporter_begin; iface->begin_page = pdf_document_file_exporter_begin_page; @@ -1687,6 +1818,23 @@ pdf_document_file_exporter_iface_init (EvFileExporterIface *iface) iface->get_capabilities = pdf_document_file_exporter_get_capabilities; } +/* EvDocumentPrint */ +static void +pdf_document_print_print_page (EvDocumentPrint *document, + EvPage *page, + cairo_t *cr) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + + poppler_page_render_for_printing (POPPLER_PAGE (page->backend_page), cr); +} + +static void +pdf_document_document_print_iface_init (EvDocumentPrintInterface *iface) +{ + iface->print_page = pdf_document_print_print_page; +} + static void pdf_selection_render_selection (EvSelection *selection, EvRenderContext *rc, @@ -1698,6 +1846,8 @@ pdf_selection_render_selection (EvSelection *selection, GdkColor *base) { PopplerPage *poppler_page; + cairo_t *cr; + PopplerColor text_color, base_color; double width_points, height_points; gint width, height; @@ -1708,10 +1858,6 @@ pdf_selection_render_selection (EvSelection *selection, width = (int) ((width_points * rc->scale) + 0.5); height = (int) ((height_points * rc->scale) + 0.5); -#ifdef HAVE_POPPLER_PAGE_RENDER - cairo_t *cr; - PopplerColor text_color, base_color; - text_color.red = text->red; text_color.green = text->green; text_color.blue = text->blue; @@ -1740,30 +1886,11 @@ pdf_selection_render_selection (EvSelection *selection, &text_color, &base_color); cairo_destroy (cr); -#else /* HAVE_POPPLER_PAGE_RENDER */ - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - TRUE, 8, - width, height); - - poppler_page_render_selection_to_pixbuf (poppler_page, - rc->scale, rc->rotation, pixbuf, - (PopplerRectangle *)points, - (PopplerRectangle *)old_points, - (PopplerSelectionStyle)style, - text, - base); - if (*surface) - cairo_surface_destroy (*surface); - *surface = ev_document_misc_surface_from_pixbuf (pixbuf); - g_object_unref (pixbuf); -#endif /* HAVE_POPPLER_PAGE_RENDER */ } static gchar * pdf_selection_get_selected_text (EvSelection *selection, - EvRenderContext *rc, + EvPage *page, EvSelectionStyle style, EvRectangle *points) { @@ -1772,7 +1899,7 @@ pdf_selection_get_selected_text (EvSelection *selection, double height; char *retval; - poppler_page = POPPLER_PAGE (rc->page->backend_page); + poppler_page = POPPLER_PAGE (page->backend_page); poppler_page_get_size (poppler_page, NULL, &height); r.x1 = points->x1; @@ -1836,15 +1963,15 @@ pdf_selection_get_selection_region (EvSelection *selection, } static GdkRegion * -pdf_selection_get_selection_map (EvSelection *selection, - EvRenderContext *rc) +pdf_selection_get_selection_map (EvSelection *selection, + EvPage *page) { PopplerPage *poppler_page; PopplerRectangle points; GList *region; GdkRegion *retval; - poppler_page = POPPLER_PAGE (rc->page->backend_page); + poppler_page = POPPLER_PAGE (page->backend_page); points.x1 = 0.0; points.y1 = 0.0; @@ -1860,7 +1987,7 @@ pdf_selection_get_selection_map (EvSelection *selection, } static void -pdf_selection_iface_init (EvSelectionIface *iface) +pdf_selection_iface_init (EvSelectionInterface *iface) { iface->render_selection = pdf_selection_render_selection; iface->get_selected_text = pdf_selection_get_selected_text; @@ -1927,7 +2054,7 @@ pdf_document_get_effect (EvDocumentTransition *trans, } static void -pdf_document_page_transition_iface_init (EvDocumentTransitionIface *iface) +pdf_document_page_transition_iface_init (EvDocumentTransitionInterface *iface) { iface->get_page_duration = pdf_document_get_page_duration; iface->get_effect = pdf_document_get_effect; @@ -2074,7 +2201,7 @@ pdf_document_forms_get_form_fields (EvDocumentForms *document, for (list = fields; list; list = list->next) { PopplerFormFieldMapping *mapping; - EvFormFieldMapping *field_mapping; + EvMapping *field_mapping; EvFormField *ev_field; mapping = (PopplerFormFieldMapping *)list->data; @@ -2083,13 +2210,13 @@ pdf_document_forms_get_form_fields (EvDocumentForms *document, if (!ev_field) continue; - field_mapping = g_new0 (EvFormFieldMapping, 1); - field_mapping->x1 = mapping->area.x1; - field_mapping->x2 = mapping->area.x2; - field_mapping->y1 = height - mapping->area.y2; - field_mapping->y2 = height - mapping->area.y1; - field_mapping->field = ev_field; - field_mapping->field->page = EV_PAGE (g_object_ref (page)); + field_mapping = g_new0 (EvMapping, 1); + field_mapping->area.x1 = mapping->area.x1; + field_mapping->area.x2 = mapping->area.x2; + field_mapping->area.y1 = height - mapping->area.y2; + field_mapping->area.y2 = height - mapping->area.y1; + field_mapping->data = ev_field; + ev_field->page = EV_PAGE (g_object_ref (page)); g_object_set_data_full (G_OBJECT (ev_field), "poppler-field", @@ -2131,7 +2258,9 @@ pdf_document_forms_form_field_text_set_text (EvDocumentForms *document, poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); if (!poppler_field) return; + poppler_form_field_text_set_text (poppler_field, text); + PDF_DOCUMENT (document)->modified = TRUE; } static void @@ -2146,6 +2275,7 @@ pdf_document_forms_form_field_button_set_state (EvDocumentForms *document, return; poppler_form_field_button_set_state (poppler_field, state); + PDF_DOCUMENT (document)->modified = TRUE; } static gboolean @@ -2226,6 +2356,7 @@ pdf_document_forms_form_field_choice_select_item (EvDocumentForms *document, return; poppler_form_field_choice_select_item (poppler_field, index); + PDF_DOCUMENT (document)->modified = TRUE; } static void @@ -2240,6 +2371,7 @@ pdf_document_forms_form_field_choice_toggle_item (EvDocumentForms *document, return; poppler_form_field_choice_toggle_item (poppler_field, index); + PDF_DOCUMENT (document)->modified = TRUE; } static void @@ -2253,6 +2385,7 @@ pdf_document_forms_form_field_choice_unselect_all (EvDocumentForms *document, return; poppler_form_field_choice_unselect_all (poppler_field); + PDF_DOCUMENT (document)->modified = TRUE; } static void @@ -2267,6 +2400,7 @@ pdf_document_forms_form_field_choice_set_text (EvDocumentForms *document, return; poppler_form_field_choice_set_text (poppler_field, text); + PDF_DOCUMENT (document)->modified = TRUE; } static gchar * @@ -2286,7 +2420,7 @@ pdf_document_forms_form_field_choice_get_text (EvDocumentForms *document, } static void -pdf_document_document_forms_iface_init (EvDocumentFormsIface *iface) +pdf_document_document_forms_iface_init (EvDocumentFormsInterface *iface) { iface->get_form_fields = pdf_document_forms_get_form_fields; iface->form_field_text_get_text = pdf_document_forms_form_field_text_get_text; @@ -2303,3 +2437,473 @@ pdf_document_document_forms_iface_init (EvDocumentFormsIface *iface) iface->form_field_choice_get_text = pdf_document_forms_form_field_choice_get_text; } +/* Annotations */ +static void +poppler_annot_color_to_gdk_color (PopplerAnnot *poppler_annot, + GdkColor *color) +{ + PopplerColor *poppler_color; + + poppler_color = poppler_annot_get_color (poppler_annot); + if (poppler_color) { + color->red = poppler_color->red; + color->green = poppler_color->green; + color->blue = poppler_color->blue; + + g_free (poppler_color); + } /* TODO: else use a default color */ +} + +static EvAnnotation * +ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, + EvPage *page) +{ + EvAnnotation *ev_annot = NULL; + const gchar *unimplemented_annot = NULL; + + switch (poppler_annot_get_annot_type (poppler_annot)) { + case POPPLER_ANNOT_TEXT: { + PopplerAnnotText *poppler_text; + EvAnnotationText *ev_annot_text; + + poppler_text = POPPLER_ANNOT_TEXT (poppler_annot); + + ev_annot = ev_annotation_text_new (page); + + ev_annot_text = EV_ANNOTATION_TEXT (ev_annot); + ev_annot_text->is_open = poppler_annot_text_get_is_open (poppler_text); + } + break; + case POPPLER_ANNOT_FILE_ATTACHMENT: { + PopplerAnnotFileAttachment *poppler_annot_attachment; + EvAnnotationAttachment *ev_annot_attachment; + PopplerAttachment *poppler_attachment; + gchar *data = NULL; + gsize size; + GError *error = NULL; + + poppler_annot_attachment = POPPLER_ANNOT_FILE_ATTACHMENT (poppler_annot); + poppler_attachment = poppler_annot_file_attachment_get_attachment (poppler_annot_attachment); + + if (poppler_attachment && + attachment_save_to_buffer (poppler_attachment, &data, &size, &error)) { + EvAttachment *ev_attachment; + + ev_attachment = ev_attachment_new (poppler_attachment->name, + poppler_attachment->description, + poppler_attachment->mtime, + poppler_attachment->ctime, + size, data); + ev_annot = ev_annotation_attachment_new (page, ev_attachment); + g_object_unref (ev_attachment); + } else if (error) { + g_warning ("%s", error->message); + g_error_free (error); + } + + if (poppler_attachment) + g_object_unref (poppler_attachment); + } + break; + case POPPLER_ANNOT_LINK: + case POPPLER_ANNOT_WIDGET: + /* Ignore link and widgets annots since they are already handled */ + break; + default: { + GEnumValue *enum_value; + + enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_ANNOT_TYPE), + poppler_annot_get_annot_type (poppler_annot)); + unimplemented_annot = enum_value ? enum_value->value_name : "Unknown annotation"; + } + } + + if (unimplemented_annot) { + g_warning ("Unimplemented annotation: %s, please post a " + "bug report in Evince bugzilla " + "(http://bugzilla.gnome.org) with a testcase.", + unimplemented_annot); + } + + if (ev_annot) { + ev_annot->contents = poppler_annot_get_contents (poppler_annot); + ev_annot->name = poppler_annot_get_name (poppler_annot); + ev_annot->modified = poppler_annot_get_modified (poppler_annot); + poppler_annot_color_to_gdk_color (poppler_annot, &ev_annot->color); + + if (POPPLER_IS_ANNOT_MARKUP (poppler_annot)) { + PopplerAnnotMarkup *markup; + gchar *label; + gdouble opacity; + PopplerRectangle poppler_rect; + + markup = POPPLER_ANNOT_MARKUP (poppler_annot); + + if (poppler_annot_markup_get_popup_rectangle (markup, &poppler_rect)) { + EvRectangle ev_rect; + gboolean is_open; + gdouble height; + + poppler_page_get_size (POPPLER_PAGE (page->backend_page), + NULL, &height); + ev_rect.x1 = poppler_rect.x1; + ev_rect.x2 = poppler_rect.x2; + ev_rect.y1 = height - poppler_rect.y2; + ev_rect.y2 = height - poppler_rect.y1; + + is_open = poppler_annot_markup_get_popup_is_open (markup); + + g_object_set (ev_annot, + "rectangle", &ev_rect, + "is_open", is_open, + "has_popup", TRUE, + NULL); + } else { + /* FIXME: Use poppler_annot_markup_has_popup() when + * new poppler is released. + */ + g_object_set (ev_annot, + "has_popup", FALSE, + NULL); + } + + label = poppler_annot_markup_get_label (markup); + opacity = poppler_annot_markup_get_opacity (markup); + + g_object_set (ev_annot, + "label", label, + "opacity", opacity, + NULL); + + g_free (label); + } + } + + return ev_annot; +} + +static GList * +pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annotations, + EvPage *page) +{ + GList *retval = NULL; + PdfDocument *pdf_document; + PopplerPage *poppler_page; + GList *annots; + GList *list; + gdouble height; + gint i = 0; + + pdf_document = PDF_DOCUMENT (document_annotations); + poppler_page = POPPLER_PAGE (page->backend_page); + annots = poppler_page_get_annot_mapping (poppler_page); + poppler_page_get_size (poppler_page, NULL, &height); + + for (list = annots; list; list = list->next) { + PopplerAnnotMapping *mapping; + EvMapping *annot_mapping; + EvAnnotation *ev_annot; + + mapping = (PopplerAnnotMapping *)list->data; + + ev_annot = ev_annot_from_poppler_annot (mapping->annot, page); + if (!ev_annot) + continue; + + i++; + + /* Make sure annot has a unique name */ + if (!ev_annot->name) + ev_annot->name = g_strdup_printf ("annot-%d-%d", page->index, i); + + annot_mapping = g_new (EvMapping, 1); + annot_mapping->area.x1 = mapping->area.x1; + annot_mapping->area.x2 = mapping->area.x2; + annot_mapping->area.y1 = height - mapping->area.y2; + annot_mapping->area.y2 = height - mapping->area.y1; + annot_mapping->data = ev_annot; + + g_object_set_data_full (G_OBJECT (ev_annot), + "poppler-annot", + g_object_ref (mapping->annot), + (GDestroyNotify) g_object_unref); + + retval = g_list_prepend (retval, annot_mapping); + } + + poppler_page_free_annot_mapping (annots); + + return g_list_reverse (retval); +} + +static void +pdf_document_annotations_annotation_set_contents (EvDocumentAnnotations *document, + EvAnnotation *annot, + const gchar *contents) +{ + PopplerAnnot *poppler_annot; + + poppler_annot = POPPLER_ANNOT (g_object_get_data (G_OBJECT (annot), "poppler-annot")); + if (!poppler_annot) + return; + + poppler_annot_set_contents (poppler_annot, contents); + PDF_DOCUMENT (document)->modified = TRUE; +} + +static void +pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface) +{ + iface->get_annotations = pdf_document_annotations_get_annotations; + iface->annotation_set_contents = pdf_document_annotations_annotation_set_contents; +} + +/* Attachments */ +struct SaveToBufferData { + gchar *buffer; + gsize len, max; +}; + +static gboolean +attachment_save_to_buffer_callback (const gchar *buf, + gsize count, + gpointer user_data, + GError **error) +{ + struct SaveToBufferData *sdata = (SaveToBufferData *)user_data; + gchar *new_buffer; + gsize new_max; + + if (sdata->len + count > sdata->max) { + new_max = MAX (sdata->max * 2, sdata->len + count); + new_buffer = (gchar *)g_realloc (sdata->buffer, new_max); + + sdata->buffer = new_buffer; + sdata->max = new_max; + } + + memcpy (sdata->buffer + sdata->len, buf, count); + sdata->len += count; + + return TRUE; +} + +static gboolean +attachment_save_to_buffer (PopplerAttachment *attachment, + gchar **buffer, + gsize *buffer_size, + GError **error) +{ + static const gint initial_max = 1024; + struct SaveToBufferData sdata; + + *buffer = NULL; + *buffer_size = 0; + + sdata.buffer = (gchar *) g_malloc (initial_max); + sdata.max = initial_max; + sdata.len = 0; + + if (! poppler_attachment_save_to_callback (attachment, + attachment_save_to_buffer_callback, + &sdata, + error)) { + g_free (sdata.buffer); + return FALSE; + } + + *buffer = sdata.buffer; + *buffer_size = sdata.len; + + return TRUE; +} + +static GList * +pdf_document_attachments_get_attachments (EvDocumentAttachments *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + GList *attachments; + GList *list; + GList *retval = NULL; + + attachments = poppler_document_get_attachments (pdf_document->document); + + for (list = attachments; list; list = list->next) { + PopplerAttachment *attachment; + EvAttachment *ev_attachment; + gchar *data = NULL; + gsize size; + GError *error = NULL; + + attachment = (PopplerAttachment *) list->data; + + if (attachment_save_to_buffer (attachment, &data, &size, &error)) { + ev_attachment = ev_attachment_new (attachment->name, + attachment->description, + attachment->mtime, + attachment->ctime, + size, data); + + retval = g_list_prepend (retval, ev_attachment); + } else { + if (error) { + g_warning ("%s", error->message); + g_error_free (error); + + g_free (data); + } + } + + g_object_unref (attachment); + } + + return g_list_reverse (retval); +} + +static gboolean +pdf_document_attachments_has_attachments (EvDocumentAttachments *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + + return poppler_document_has_attachments (pdf_document->document); +} + +static void +pdf_document_document_attachments_iface_init (EvDocumentAttachmentsInterface *iface) +{ + iface->has_attachments = pdf_document_attachments_has_attachments; + iface->get_attachments = pdf_document_attachments_get_attachments; +} + +/* Layers */ +static gboolean +pdf_document_layers_has_layers (EvDocumentLayers *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + PopplerLayersIter *iter; + + iter = poppler_layers_iter_new (pdf_document->document); + if (!iter) + return FALSE; + poppler_layers_iter_free (iter); + + return TRUE; +} + +static void +build_layers_tree (PdfDocument *pdf_document, + GtkTreeModel *model, + GtkTreeIter *parent, + PopplerLayersIter *iter) +{ + do { + GtkTreeIter tree_iter; + PopplerLayersIter *child; + PopplerLayer *layer; + EvLayer *ev_layer = NULL; + gboolean visible; + gchar *markup; + gint rb_group = 0; + + layer = poppler_layers_iter_get_layer (iter); + if (layer) { + markup = g_markup_escape_text (poppler_layer_get_title (layer), -1); + visible = poppler_layer_is_visible (layer); + rb_group = poppler_layer_get_radio_button_group_id (layer); + pdf_document->layers = g_list_append (pdf_document->layers, + g_object_ref (layer)); + ev_layer = ev_layer_new (g_list_length (pdf_document->layers) - 1, + poppler_layer_is_parent (layer), + rb_group); + } else { + gchar *title; + + title = poppler_layers_iter_get_title (iter); + markup = g_markup_escape_text (title, -1); + g_free (title); + + visible = FALSE; + layer = NULL; + } + + gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); + gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, + EV_DOCUMENT_LAYERS_COLUMN_TITLE, markup, + EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, visible, + EV_DOCUMENT_LAYERS_COLUMN_ENABLED, TRUE, /* FIXME */ + EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE, (layer != NULL), + EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, rb_group, + EV_DOCUMENT_LAYERS_COLUMN_LAYER, ev_layer, + -1); + if (ev_layer) + g_object_unref (ev_layer); + g_free (markup); + + child = poppler_layers_iter_get_child (iter); + if (child) + build_layers_tree (pdf_document, model, &tree_iter, child); + poppler_layers_iter_free (child); + } while (poppler_layers_iter_next (iter)); +} + +static GtkTreeModel * +pdf_document_layers_get_layers (EvDocumentLayers *document) +{ + GtkTreeModel *model = NULL; + PdfDocument *pdf_document = PDF_DOCUMENT (document); + PopplerLayersIter *iter; + + iter = poppler_layers_iter_new (pdf_document->document); + if (iter) { + model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LAYERS_N_COLUMNS, + G_TYPE_STRING, /* TITLE */ + G_TYPE_OBJECT, /* LAYER */ + G_TYPE_BOOLEAN, /* VISIBLE */ + G_TYPE_BOOLEAN, /* ENABLED */ + G_TYPE_BOOLEAN, /* SHOWTOGGLE */ + G_TYPE_INT); /* RBGROUP */ + build_layers_tree (pdf_document, model, NULL, iter); + poppler_layers_iter_free (iter); + } + return model; +} + +static void +pdf_document_layers_show_layer (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + poppler_layer_show (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static void +pdf_document_layers_hide_layer (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + poppler_layer_hide (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static gboolean +pdf_document_layers_layer_is_visible (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + return poppler_layer_is_visible (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static void +pdf_document_document_layers_iface_init (EvDocumentLayersInterface *iface) +{ + iface->has_layers = pdf_document_layers_has_layers; + iface->get_layers = pdf_document_layers_get_layers; + iface->show_layer = pdf_document_layers_show_layer; + iface->hide_layer = pdf_document_layers_hide_layer; + iface->layer_is_visible = pdf_document_layers_layer_is_visible; +}