]> www.fi.muni.cz Git - evince.git/blobdiff - backend/pdf/ev-poppler.cc
[shell] Do not link to poppler directly
[evince.git] / backend / pdf / ev-poppler.cc
index 0333d715564810d5209bc9856e0c2386fc8b6733..389b436e9388a9db3f246b7b059a7aca893aa1be 100644 (file)
@@ -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 <juanj.marin@juntadeandalucia.es>
  * Copyright (C) 2004, Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
 #define HAVE_CAIRO_PRINT
 #endif
 
+/* 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 {
        PdfDocument *document;
        char *text;
@@ -92,12 +101,12 @@ typedef struct {
 
 struct _PdfDocumentClass
 {
-       GObjectClass parent_class;
+       EvDocumentClass parent_class;
 };
 
 struct _PdfDocument
 {
-       GObject parent_instance;
+       EvDocument parent_instance;
 
        PopplerDocument *document;
        gchar *password;
@@ -113,7 +122,6 @@ struct _PdfDocument
        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);
@@ -136,12 +144,16 @@ static void pdf_document_thumbnails_get_dimensions       (EvDocumentThumbnails
                                                          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 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 gboolean    attachment_save_to_buffer (PopplerAttachment *attachment,
+                                             gchar            **buffer,
+                                             gsize             *buffer_size,
+                                             GError           **error);
 
 EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document,
                         {
@@ -233,14 +245,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)
 {
@@ -448,51 +452,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#");
@@ -550,12 +521,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)
@@ -567,7 +685,6 @@ pdf_document_get_info (EvDocument *document)
        PopplerPermissions permissions;
        EvPage *page;
        char *metadata;
-       char *fmt;
 
        info = g_new0 (EvDocumentInfo, 1);
 
@@ -587,7 +704,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),
@@ -608,24 +726,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;
@@ -720,18 +830,67 @@ 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->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)
@@ -1125,7 +1284,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;
@@ -1135,8 +1294,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);
 
@@ -1158,7 +1316,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);
 }
@@ -1193,7 +1350,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;
@@ -1202,7 +1359,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) {
@@ -1213,7 +1370,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;
@@ -1223,7 +1380,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);
 }
@@ -1877,15 +2033,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;
@@ -2376,7 +2532,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;
 
@@ -2386,8 +2542,41 @@ 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;
+#ifdef HAVE_POPPLER_ANNOT_FILE_ATTACHMENT_GET_ATTACHMENT
+               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;
+#endif /* HAVE_POPPLER_ANNOT_FILE_ATTACHMENT_GET_ATTACHMENT */
                case POPPLER_ANNOT_LINK:
                case POPPLER_ANNOT_WIDGET:
                        /* Ignore link and widgets annots since they are already handled */