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=4b262900427080c48589811524244f2a8dea3b98;hb=18d2af9bce80392407fae997c8dfa029f5a54123;hp=2d838ea2ff1abe94fc4b252d86c8fdc7fdd20866;hpb=8dbd7b00f71be8b78198b2c713ae071c2dd77d11;p=evince.git diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc index 2d838ea2..4b262900 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" @@ -48,6 +50,8 @@ #include "ev-document-layers.h" #include "ev-document-print.h" #include "ev-document-annotations.h" +#include "ev-document-attachments.h" +#include "ev-document-text.h" #include "ev-selection.h" #include "ev-transition-effect.h" #include "ev-attachment.h" @@ -58,18 +62,16 @@ #include #include -#if (defined (HAVE_POPPLER_PAGE_RENDER)) && (defined (HAVE_CAIRO_PDF) || defined (HAVE_CAIRO_PS)) +#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; @@ -91,12 +93,12 @@ typedef struct { struct _PdfDocumentClass { - GObjectClass parent_class; + EvDocumentClass parent_class; }; struct _PdfDocument { - GObject parent_instance; + EvDocument parent_instance; PopplerDocument *document; gchar *password; @@ -106,40 +108,41 @@ 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); -#ifdef HAVE_POPPLER_PAGE_RENDER -static void pdf_document_document_print_iface_init (EvDocumentPrintIface *iface); -#endif -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_text_iface_init (EvDocumentTextInterface *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, { @@ -157,12 +160,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); -#ifdef HAVE_POPPLER_PAGE_RENDER EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_PRINT, pdf_document_document_print_iface_init); -#endif 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, @@ -171,29 +174,10 @@ EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, pdf_selection_iface_init); EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TRANSITION, pdf_document_page_transition_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TEXT, + pdf_document_text_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) { @@ -203,11 +187,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); @@ -229,14 +208,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) { @@ -361,122 +332,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, @@ -484,17 +339,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); @@ -511,25 +361,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 * @@ -558,51 +397,18 @@ pdf_document_render (EvDocument *document, width, height, rc); } -/* 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); -} - - /* 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 (const char *metadata) +pdf_document_get_format_from_metadata (xmlDocPtr doc, + xmlXPathContextPtr xpathCtx) { - xmlDocPtr doc; - xmlXPathContextPtr xpathCtx; xmlXPathObjectPtr xpathObj; xmlChar *part = NULL; xmlChar *conf = NULL; char *result = NULL; int i; - doc = xmlParseMemory (metadata, strlen (metadata)); - if (doc == NULL) - return NULL; /* invalid xml metadata */ - - xpathCtx = xmlXPathNewContext (doc); - if (xpathCtx == NULL) { - xmlFreeDoc (doc); - return NULL; /* invalid xpath context */ - } - /* 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#"); @@ -660,12 +466,159 @@ pdf_document_get_format_from_metadata (const char *metadata) /* Cleanup */ xmlFree (part); xmlFree (conf); - xmlXPathFreeContext (xpathCtx); - xmlFreeDoc (doc); 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_parse_metadata (const gchar *metadata, + EvDocumentInfo *info) +{ + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + gchar *fmt; + + doc = xmlParseMemory (metadata, strlen (metadata)); + if (doc == NULL) + return; /* invalid xml metadata */ + + 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) @@ -677,7 +630,6 @@ pdf_document_get_info (EvDocument *document) PopplerPermissions permissions; EvPage *page; char *metadata; - char *fmt; info = g_new0 (EvDocumentInfo, 1); @@ -697,7 +649,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), @@ -718,24 +671,16 @@ pdf_document_get_info (EvDocument *document) NULL); if (metadata != NULL) { - fmt = pdf_document_get_format_from_metadata(metadata); - if (fmt != NULL) { - g_free (info->format); - info->format = fmt; - } + 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; @@ -830,23 +775,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_security_iface_init (EvDocumentSecurityIface *iface) +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 (EvDocumentSecurityInterface *iface) { iface->has_document_security = pdf_document_has_document_security; iface->set_password = pdf_document_set_password; @@ -966,7 +958,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; @@ -1116,6 +1108,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"; } @@ -1237,7 +1237,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; @@ -1247,8 +1247,7 @@ 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); @@ -1270,7 +1269,6 @@ pdf_document_links_get_links (EvDocumentLinks *document_links, } poppler_page_free_link_mapping (mapping_list); - g_object_unref (poppler_page); return g_list_reverse (retval); } @@ -1295,7 +1293,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; @@ -1305,7 +1303,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; @@ -1314,7 +1312,7 @@ 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) { @@ -1325,7 +1323,7 @@ pdf_document_images_get_image_mapping (EvDocumentImages *document_images, ev_image_mapping = g_new (EvMapping, 1); - ev_image_mapping->data = ev_image_new (page, image_mapping->image_id); + 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; @@ -1335,7 +1333,6 @@ pdf_document_images_get_image_mapping (EvDocumentImages *document_images, } poppler_page_free_image_mapping (mapping_list); - g_object_unref (poppler_page); return g_list_reverse (retval); } @@ -1345,7 +1342,6 @@ pdf_document_images_get_image (EvDocumentImages *document_images, EvImage *image) { GdkPixbuf *retval = NULL; -#ifdef HAVE_POPPLER_PAGE_GET_IMAGE PdfDocument *pdf_document; PopplerPage *poppler_page; cairo_surface_t *surface; @@ -1361,12 +1357,12 @@ pdf_document_images_get_image (EvDocumentImages *document_images, } g_object_unref (poppler_page); -#endif + return retval; } 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; @@ -1487,7 +1483,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; @@ -1503,7 +1499,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); @@ -1516,18 +1513,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; } @@ -1797,21 +1802,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; @@ -1821,7 +1822,6 @@ pdf_document_file_exporter_iface_init (EvFileExporterIface *iface) iface->get_capabilities = pdf_document_file_exporter_get_capabilities; } -#ifdef HAVE_POPPLER_PAGE_RENDER /* EvDocumentPrint */ static void pdf_document_print_print_page (EvDocumentPrint *document, @@ -1834,11 +1834,10 @@ pdf_document_print_print_page (EvDocumentPrint *document, } static void -pdf_document_document_print_iface_init (EvDocumentPrintIface *iface) +pdf_document_document_print_iface_init (EvDocumentPrintInterface *iface) { iface->print_page = pdf_document_print_print_page; } -#endif /* HAVE_POPPLER_PAGE_RENDER */ static void pdf_selection_render_selection (EvSelection *selection, @@ -1851,6 +1850,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; @@ -1861,10 +1862,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; @@ -1893,30 +1890,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) { @@ -1925,7 +1903,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; @@ -1988,21 +1966,33 @@ pdf_selection_get_selection_region (EvSelection *selection, return retval; } +static void +pdf_selection_iface_init (EvSelectionInterface *iface) +{ + iface->render_selection = pdf_selection_render_selection; + iface->get_selected_text = pdf_selection_get_selected_text; + iface->get_selection_region = pdf_selection_get_selection_region; +} + + +/* EvDocumentText */ static GdkRegion * -pdf_selection_get_selection_map (EvSelection *selection, - EvRenderContext *rc) +pdf_document_text_get_text_mapping (EvDocumentText *document_text, + EvPage *page) { PopplerPage *poppler_page; PopplerRectangle points; GList *region; GdkRegion *retval; - poppler_page = POPPLER_PAGE (rc->page->backend_page); + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); points.x1 = 0.0; points.y1 = 0.0; poppler_page_get_size (poppler_page, &(points.x2), &(points.y2)); - + region = poppler_page_get_selection_region (poppler_page, 1.0, POPPLER_SELECTION_GLYPH, &points); @@ -2012,13 +2002,49 @@ pdf_selection_get_selection_map (EvSelection *selection, return retval; } +static gchar * +pdf_document_text_get_text (EvDocumentText *selection, + EvPage *page) +{ + PopplerPage *poppler_page; + PopplerRectangle r; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + r.x1 = 0; + r.y1 = 0; + poppler_page_get_size (poppler_page, &(r.x2), &(r.y2)); + + return poppler_page_get_text (poppler_page, + POPPLER_SELECTION_WORD, + &r); +} + +static gboolean +pdf_document_text_get_text_layout (EvDocumentText *selection, + EvPage *page, + EvRectangle **areas, + guint *n_areas) +{ + PopplerPage *poppler_page; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + return poppler_page_get_text_layout (poppler_page, (PopplerRectangle **)areas, n_areas); +} + static void -pdf_selection_iface_init (EvSelectionIface *iface) +pdf_document_text_iface_init (EvDocumentTextInterface *iface) { - iface->render_selection = pdf_selection_render_selection; - iface->get_selected_text = pdf_selection_get_selected_text; - iface->get_selection_region = pdf_selection_get_selection_region; - iface->get_selection_map = pdf_selection_get_selection_map; + iface->get_text_mapping = pdf_document_text_get_text_mapping; + iface->get_text = pdf_document_text_get_text; +#ifdef HAVE_POPPLER_PAGE_GET_TEXT_LAYOUT + iface->get_text_layout = pdf_document_text_get_text_layout; +#endif } /* Page Transitions */ @@ -2080,7 +2106,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; @@ -2446,7 +2472,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; @@ -2488,7 +2514,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; @@ -2498,7 +2524,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: @@ -2530,13 +2587,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), @@ -2546,17 +2603,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); @@ -2636,12 +2704,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) @@ -2766,7 +2951,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;