X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;ds=sidebyside;f=backend%2Fpdf%2Fev-poppler.cc;h=fb2a5ba5d8399697f08fa231e3692b39477894bc;hb=1535a8e52feeffa1ad445ca4f1b49b8e5f7b40dd;hp=3c39e5960b866b2050eb5bc5f40c596bc38d971d;hpb=dec10c13ca0e95a58e9d6eb537320505ca64d1f8;p=evince.git diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc index 3c39e596..fb2a5ba5 100644 --- a/backend/pdf/ev-poppler.cc +++ b/backend/pdf/ev-poppler.cc @@ -45,11 +45,18 @@ #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-selection.h" #include "ev-transition-effect.h" #include "ev-attachment.h" #include "ev-image.h" +#include +#include +#include +#include + #if (defined (HAVE_POPPLER_PAGE_RENDER)) && (defined (HAVE_CAIRO_PDF) || defined (HAVE_CAIRO_PS)) #define HAVE_CAIRO_PRINT #endif @@ -104,23 +111,27 @@ 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); -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_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 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); @@ -145,6 +156,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_FIND, pdf_document_find_iface_init); EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, @@ -561,6 +578,94 @@ pdf_document_set_password (EvDocumentSecurity *document_security, 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) +{ + 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#"); + 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); + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); + + return result; +} + + static EvDocumentInfo * pdf_document_get_info (EvDocument *document) { @@ -570,6 +675,8 @@ pdf_document_get_info (EvDocument *document) PopplerViewerPreferences view_prefs; PopplerPermissions permissions; EvPage *page; + char *metadata; + char *fmt; info = g_new0 (EvDocumentInfo, 1); @@ -606,8 +713,18 @@ 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) { + fmt = pdf_document_get_format_from_metadata(metadata); + if (fmt != NULL) { + g_free (info->format); + info->format = fmt; + } + g_free (metadata); + } + info->n_pages = ev_document_get_n_pages (document); if (info->n_pages > 0) { @@ -898,9 +1015,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; @@ -914,6 +1033,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, @@ -934,15 +1054,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; @@ -1232,10 +1343,11 @@ GdkPixbuf * 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; - GdkPixbuf *retval = NULL; pdf_document = PDF_DOCUMENT (document_images); poppler_page = poppler_document_get_page (pdf_document->document, @@ -1248,7 +1360,7 @@ pdf_document_images_get_image (EvDocumentImages *document_images, } g_object_unref (poppler_page); - +#endif return retval; } @@ -1341,7 +1453,7 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails pixbuf = make_thumbnail_for_page (poppler_page, rc, width, height); } - if (border) { + if (border && pixbuf) { border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf); g_object_unref (pixbuf); pixbuf = border_pixbuf; @@ -1361,9 +1473,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; @@ -1708,6 +1820,25 @@ 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, + 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 (EvDocumentPrintIface *iface) +{ + iface->print_page = pdf_document_print_print_page; +} +#endif /* HAVE_POPPLER_PAGE_RENDER */ + static void pdf_selection_render_selection (EvSelection *selection, EvRenderContext *rc, @@ -2331,11 +2462,189 @@ pdf_document_document_forms_iface_init (EvDocumentFormsIface *iface) iface->form_field_choice_get_text = pdf_document_forms_form_field_choice_get_text; } +/* Annotations */ +static void +poppler_annot_color_to_gdk_color (PopplerAnnot *poppler_annot, + GdkColor *color) +{ + PopplerColor *poppler_color; + + poppler_color = poppler_annot_get_color (poppler_annot); + if (poppler_color) { + color->red = poppler_color->red; + color->green = poppler_color->green; + color->blue = poppler_color->blue; + + g_free (poppler_color); + } /* TODO: else use a default color */ +} + +static EvAnnotation * +ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, + EvPage *page) +{ + EvAnnotation *ev_annot = NULL; + const gchar *unimplemented_annot = NULL; + + switch (poppler_annot_get_annot_type (poppler_annot)) { + case POPPLER_ANNOT_TEXT: + PopplerAnnotText *poppler_text; + EvAnnotationText *ev_annot_text; + + poppler_text = POPPLER_ANNOT_TEXT (poppler_annot); + + ev_annot = ev_annotation_text_new (page); + + ev_annot_text = EV_ANNOTATION_TEXT (ev_annot); + ev_annot_text->is_open = poppler_annot_text_get_is_open (poppler_text); + + break; + case POPPLER_ANNOT_LINK: + case POPPLER_ANNOT_WIDGET: + /* Ignore link and widgets annots since they are already handled */ + break; + default: { + GEnumValue *enum_value; + + enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_ANNOT_TYPE), + poppler_annot_get_annot_type (poppler_annot)); + unimplemented_annot = enum_value ? enum_value->value_name : "Unknown annotation"; + } + } + + if (unimplemented_annot) { + g_warning ("Unimplemented annotation: %s, please post a " + "bug report in Evince bugzilla " + "(http://bugzilla.gnome.org) with a testcase.", + unimplemented_annot); + } + + if (ev_annot) { + ev_annot->contents = poppler_annot_get_contents (poppler_annot); + ev_annot->name = poppler_annot_get_name (poppler_annot); + ev_annot->modified = poppler_annot_get_modified (poppler_annot); + poppler_annot_color_to_gdk_color (poppler_annot, &ev_annot->color); + + if (POPPLER_IS_ANNOT_MARKUP (poppler_annot)) { + PopplerAnnotMarkup *markup; + gchar *label; + gdouble opacity; + 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; + gdouble height; + + poppler_page_get_size (POPPLER_PAGE (page->backend_page), + NULL, &height); + ev_rect.x1 = poppler_rect.x1; + ev_rect.x2 = poppler_rect.x2; + ev_rect.y1 = height - poppler_rect.y2; + ev_rect.y2 = height - poppler_rect.y1; + + g_object_set (ev_annot, "rectangle", &ev_rect, 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); + } + } + + return ev_annot; +} + +static GList * +pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annotations, + EvPage *page) +{ + GList *retval = NULL; + PdfDocument *pdf_document; + PopplerPage *poppler_page; + GList *annots; + GList *list; + gdouble height; + gint i = 0; + + pdf_document = PDF_DOCUMENT (document_annotations); + poppler_page = POPPLER_PAGE (page->backend_page); + annots = poppler_page_get_annot_mapping (poppler_page); + poppler_page_get_size (poppler_page, NULL, &height); + + for (list = annots; list; list = list->next) { + PopplerAnnotMapping *mapping; + EvAnnotationMapping *annot_mapping; + EvAnnotation *ev_annot; + + mapping = (PopplerAnnotMapping *)list->data; + + ev_annot = ev_annot_from_poppler_annot (mapping->annot, page); + if (!ev_annot) + continue; + + i++; + + /* Make sure annot has a unique name */ + if (!ev_annot->name) + ev_annot->name = g_strdup_printf ("annot-%d-%d", page->index, i); + + annot_mapping = g_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; + + g_object_set_data_full (G_OBJECT (ev_annot), + "poppler-annot", + g_object_ref (mapping->annot), + (GDestroyNotify) g_object_unref); + + retval = g_list_prepend (retval, annot_mapping); + } + + poppler_page_free_annot_mapping (annots); + + return g_list_reverse (retval); +} + +static void +pdf_document_annotations_annotation_set_contents (EvDocumentAnnotations *document, + EvAnnotation *annot, + const gchar *contents) +{ + PopplerAnnot *poppler_annot; + + poppler_annot = POPPLER_ANNOT (g_object_get_data (G_OBJECT (annot), "poppler-annot")); + if (!poppler_annot) + return; + + poppler_annot_set_contents (poppler_annot, contents); + PDF_DOCUMENT (document)->modified = TRUE; +} + +static void +pdf_document_document_annotations_iface_init (EvDocumentAnnotationsIface *iface) +{ + iface->get_annotations = pdf_document_annotations_get_annotations; + iface->annotation_set_contents = pdf_document_annotations_annotation_set_contents; +} + /* Layers */ static gboolean pdf_document_layers_has_layers (EvDocumentLayers *document) { -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW PdfDocument *pdf_document = PDF_DOCUMENT (document); PopplerLayersIter *iter; @@ -2345,12 +2654,8 @@ pdf_document_layers_has_layers (EvDocumentLayers *document) poppler_layers_iter_free (iter); return TRUE; -#else - return FALSE; -#endif /* HAVE_POPPLER_LAYERS_ITER_NEW */ } -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW static void build_layers_tree (PdfDocument *pdf_document, GtkTreeModel *model, @@ -2406,13 +2711,11 @@ build_layers_tree (PdfDocument *pdf_document, poppler_layers_iter_free (child); } while (poppler_layers_iter_next (iter)); } -#endif /* HAVE_POPPLER_LAYERS_ITER_NEW */ static GtkTreeModel * pdf_document_layers_get_layers (EvDocumentLayers *document) { GtkTreeModel *model = NULL; -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW PdfDocument *pdf_document = PDF_DOCUMENT (document); PopplerLayersIter *iter; @@ -2428,8 +2731,6 @@ pdf_document_layers_get_layers (EvDocumentLayers *document) build_layers_tree (pdf_document, model, NULL, iter); poppler_layers_iter_free (iter); } - -#endif return model; } @@ -2437,42 +2738,30 @@ static void pdf_document_layers_show_layer (EvDocumentLayers *document, EvLayer *layer) { -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW PdfDocument *pdf_document = PDF_DOCUMENT (document); guint layer_id = ev_layer_get_id (layer); poppler_layer_show (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); -#else - return; -#endif /* HAVE_POPPLER_LAYERS_ITER_NEW */ } static void pdf_document_layers_hide_layer (EvDocumentLayers *document, EvLayer *layer) { -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW PdfDocument *pdf_document = PDF_DOCUMENT (document); guint layer_id = ev_layer_get_id (layer); poppler_layer_hide (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); -#else - return; -#endif /* HAVE_POPPLER_LAYERS_ITER_NEW */ } static gboolean pdf_document_layers_layer_is_visible (EvDocumentLayers *document, EvLayer *layer) { -#ifdef HAVE_POPPLER_LAYERS_ITER_NEW PdfDocument *pdf_document = PDF_DOCUMENT (document); guint layer_id = ev_layer_get_id (layer); return poppler_layer_is_visible (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); -#else - return FALSE; -#endif /* HAVE_POPPLER_LAYERS_ITER_NEW */ } static void