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=38ab1d5a846cc20a344ca67bb1f7c8d72b875e3c;hpb=7f09d6fafebe4fcaee4878eea41dd23150a3d8e5;p=evince.git diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc index 38ab1d5a..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" @@ -35,6 +37,7 @@ #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" @@ -45,24 +48,29 @@ #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; @@ -84,12 +92,12 @@ typedef struct { struct _PdfDocumentClass { - GObjectClass parent_class; + EvDocumentClass parent_class; }; struct _PdfDocument { - GObject parent_instance; + EvDocument parent_instance; PopplerDocument *document; gchar *password; @@ -99,37 +107,40 @@ struct _PdfDocument 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_document_layers_iface_init (EvDocumentLayersIface *iface); -static void pdf_document_document_annotations_iface_init (EvDocumentAnnotationsIface *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, { @@ -147,8 +158,12 @@ EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, 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, @@ -159,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) { @@ -189,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); @@ -215,14 +204,6 @@ pdf_document_dispose (GObject *object) 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); - - g_object_class->dispose = pdf_document_dispose; -} - static void pdf_document_init (PdfDocument *pdf_document) { @@ -347,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, @@ -470,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); @@ -497,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); - return surface; + cairo_destroy (cr); + + return surface; } static cairo_surface_t * @@ -544,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) { @@ -574,6 +625,7 @@ pdf_document_get_info (EvDocument *document) PopplerViewerPreferences view_prefs; PopplerPermissions permissions; EvPage *page; + char *metadata; info = g_new0 (EvDocumentInfo, 1); @@ -593,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), @@ -610,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; @@ -716,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; @@ -852,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; @@ -902,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; @@ -918,6 +1022,7 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, 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, @@ -938,15 +1043,6 @@ ev_link_dest_from_dest (PdfDocument *pdf_document, 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; @@ -1008,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"; } @@ -1129,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; @@ -1139,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); } @@ -1187,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; @@ -1197,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; @@ -1206,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); } @@ -1236,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, @@ -1257,7 +1358,7 @@ 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; @@ -1365,9 +1466,9 @@ pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnail poppler_page_get_size (POPPLER_PAGE (rc->page->backend_page), &page_width, &page_height); - *width = (gint) MAX (page_width * rc->scale, 1); - *height = (gint) MAX (page_height * rc->scale, 1); - + *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; @@ -1378,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; @@ -1394,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); @@ -1407,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; } @@ -1688,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; @@ -1712,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, @@ -1723,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; @@ -1733,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; @@ -1765,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) { @@ -1797,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; @@ -1861,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; @@ -1885,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; @@ -1952,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; @@ -2099,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; @@ -2108,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", @@ -2318,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; @@ -2360,7 +2462,7 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, const gchar *unimplemented_annot = NULL; switch (poppler_annot_get_annot_type (poppler_annot)) { - case POPPLER_ANNOT_TEXT: + case POPPLER_ANNOT_TEXT: { PopplerAnnotText *poppler_text; EvAnnotationText *ev_annot_text; @@ -2370,7 +2472,38 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, 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: @@ -2402,13 +2535,13 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, PopplerAnnotMarkup *markup; gchar *label; gdouble opacity; - gboolean is_open; 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), @@ -2418,17 +2551,28 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, ev_rect.y1 = height - poppler_rect.y2; ev_rect.y2 = height - poppler_rect.y1; - g_object_set (ev_annot, "rectangle", &ev_rect, NULL); + 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); - is_open = poppler_annot_markup_get_popup_is_open (markup); g_object_set (ev_annot, "label", label, "opacity", opacity, - "is_open", is_open, NULL); g_free (label); @@ -2457,7 +2601,7 @@ pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annota for (list = annots; list; list = list->next) { PopplerAnnotMapping *mapping; - EvAnnotationMapping *annot_mapping; + EvMapping *annot_mapping; EvAnnotation *ev_annot; mapping = (PopplerAnnotMapping *)list->data; @@ -2472,12 +2616,12 @@ pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annota if (!ev_annot->name) ev_annot->name = g_strdup_printf ("annot-%d-%d", page->index, i); - annot_mapping = g_new0 (EvAnnotationMapping, 1); - annot_mapping->x1 = mapping->area.x1; - annot_mapping->x2 = mapping->area.x2; - annot_mapping->y1 = height - mapping->area.y2; - annot_mapping->y2 = height - mapping->area.y1; - annot_mapping->annotation = ev_annot; + 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", @@ -2508,12 +2652,129 @@ pdf_document_annotations_annotation_set_contents (EvDocumentAnnotations *documen } static void -pdf_document_document_annotations_iface_init (EvDocumentAnnotationsIface *iface) +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) @@ -2638,7 +2899,7 @@ pdf_document_layers_layer_is_visible (EvDocumentLayers *document, } static void -pdf_document_document_layers_iface_init (EvDocumentLayersIface *iface) +pdf_document_document_layers_iface_init (EvDocumentLayersInterface *iface) { iface->has_layers = pdf_document_layers_has_layers; iface->get_layers = pdf_document_layers_get_layers;