]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/ev-poppler.cc
Add page transition support in presentation mode. At the moment only page
[evince.git] / pdf / ev-poppler.cc
index f6b1d64fa195387d2475d5039b84e8d42c3bfd28..479a741bb887e4bffb3de39df86f1151d75e3235 100644 (file)
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include "config.h"
+
 #include <math.h>
 #include <string.h>
 #include <gtk/gtk.h>
 #include <poppler.h>
 #include <poppler-document.h>
 #include <poppler-page.h>
+#ifdef HAVE_CAIRO_PDF
+#include <cairo-pdf.h>
+#endif
 #include <glib/gi18n.h>
 
 #include "ev-poppler.h"
-#include "ev-ps-exporter.h"
+#include "ev-file-exporter.h"
 #include "ev-document-find.h"
 #include "ev-document-misc.h"
 #include "ev-document-links.h"
 #include "ev-document-fonts.h"
 #include "ev-document-security.h"
 #include "ev-document-thumbnails.h"
+#include "ev-document-transition.h"
 #include "ev-selection.h"
 #include "ev-attachment.h"
 
@@ -45,6 +51,14 @@ typedef struct {
        int search_page;
 } PdfDocumentSearch;
 
+typedef struct {
+       EvFileExporterFormat format;
+       PopplerPSFile *ps_file;
+#ifdef HAVE_CAIRO_PDF
+       cairo_t *pdf_cairo;
+#endif
+} PdfPrintContext;
+
 struct _PdfDocumentClass
 {
        GObjectClass parent_class;
@@ -55,7 +69,6 @@ struct _PdfDocument
        GObject parent_instance;
 
        PopplerDocument *document;
-       PopplerPSFile *ps_file;
        gchar *password;
 
        PopplerFontInfo *font_info;
@@ -63,6 +76,7 @@ struct _PdfDocument
        int fonts_scanned_pages;
 
        PdfDocumentSearch *search;
+       PdfPrintContext *print_ctx;
 };
 
 static void pdf_document_document_iface_init            (EvDocumentIface           *iface);
@@ -71,8 +85,9 @@ static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIfa
 static void pdf_document_document_links_iface_init      (EvDocumentLinksIface      *iface);
 static void pdf_document_document_fonts_iface_init      (EvDocumentFontsIface      *iface);
 static void pdf_document_find_iface_init                (EvDocumentFindIface       *iface);
-static void pdf_document_ps_exporter_iface_init         (EvPSExporterIface         *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,
                                                         gint                       page,
                                                         gint                       size,
@@ -80,9 +95,12 @@ 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 (PopplerDest *dest);
-static EvLink *ev_link_from_action (PopplerAction *action);
-static void pdf_document_search_free (PdfDocumentSearch   *search);
+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);
 
 
 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
@@ -99,10 +117,12 @@ G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
                                                        pdf_document_document_fonts_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
                                                        pdf_document_find_iface_init);
-                                G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
-                                                       pdf_document_ps_exporter_iface_init);
+                                G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
+                                                       pdf_document_file_exporter_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION,
                                                        pdf_selection_iface_init);
+                                G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TRANSITION,
+                                                       pdf_document_page_transition_iface_init);
                         });
 
 
@@ -134,8 +154,10 @@ pdf_document_search_free (PdfDocumentSearch   *search)
                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->text);
+       g_free (search);
 }
 
 static void
@@ -143,6 +165,11 @@ pdf_document_dispose (GObject *object)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT(object);
 
+       if (pdf_document->print_ctx) {
+               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;
@@ -484,8 +511,8 @@ pdf_document_get_info (EvDocument *document)
                            EV_DOCUMENT_INFO_MOD_DATE |
                            EV_DOCUMENT_INFO_LINEARIZED |
                            EV_DOCUMENT_INFO_N_PAGES |
-                           EV_DOCUMENT_INFO_SECURITY;
-
+                           EV_DOCUMENT_INFO_SECURITY | 
+                           EV_DOCUMENT_INFO_PAPER_SIZE;
 
        g_object_get (PDF_DOCUMENT (document)->document,
                      "title", &(info->title),
@@ -504,6 +531,14 @@ pdf_document_get_info (EvDocument *document)
                      "linearized", &(info->linearized),
                      NULL);
 
+       pdf_document_get_page_size(document, 0,
+                                  &(info->paper_width),
+                                  &(info->paper_height));
+
+       // Convert to mm.
+       info->paper_width = info->paper_width / 72.0f * 25.4f;
+       info->paper_height = info->paper_height / 72.0f * 25.4f;
+
        switch (layout) {
                case POPPLER_PAGE_LAYOUT_SINGLE_PAGE:
                        info->layout = EV_DOCUMENT_LAYOUT_SINGLE_PAGE;
@@ -781,37 +816,62 @@ pdf_document_links_has_document_links (EvDocumentLinks *document_links)
 }
 
 static EvLinkDest *
-ev_link_dest_from_dest (PopplerDest *dest)
+ev_link_dest_from_dest (PdfDocument *pdf_document,
+                       PopplerDest *dest)
 {
        EvLinkDest *ev_dest = NULL;
        const char *unimplemented_dest = NULL;
 
        g_assert (dest != NULL);
-       
+
        switch (dest->type) {
-               case POPPLER_DEST_XYZ:
+               case POPPLER_DEST_XYZ: {
+                       PopplerPage *poppler_page;
+                       double height;
+
+                       poppler_page = poppler_document_get_page (pdf_document->document,
+                                                                 MAX (0, dest->page_num - 1));
+                       poppler_page_get_size (poppler_page, NULL, &height);
                        ev_dest = ev_link_dest_new_xyz (dest->page_num - 1,
                                                        dest->left,
-                                                       dest->top,
+                                                       height - dest->top,
                                                        dest->zoom);
+                       g_object_unref (poppler_page);
+               }
                        break;
                case POPPLER_DEST_FIT:
                        ev_dest = ev_link_dest_new_fit (dest->page_num - 1);
                        break;
-               case POPPLER_DEST_FITH:
+               case POPPLER_DEST_FITH: {
+                       PopplerPage *poppler_page;
+                       double height;
+
+                       poppler_page = poppler_document_get_page (pdf_document->document,
+                                                                 MAX (0, dest->page_num - 1));
+                       poppler_page_get_size (poppler_page, NULL, &height);
                        ev_dest = ev_link_dest_new_fith (dest->page_num - 1,
-                                                        dest->top);
+                                                        height - dest->top);
+                       g_object_unref (poppler_page);
+               }
                        break;
                case POPPLER_DEST_FITV:
                        ev_dest = ev_link_dest_new_fitv (dest->page_num - 1,
                                                         dest->left);
                        break;
-               case POPPLER_DEST_FITR:
+               case POPPLER_DEST_FITR: {
+                       PopplerPage *poppler_page;
+                       double height;
+
+                       poppler_page = poppler_document_get_page (pdf_document->document,
+                                                                 MAX (0, dest->page_num - 1));
+                       poppler_page_get_size (poppler_page, NULL, &height);
                        ev_dest = ev_link_dest_new_fitr (dest->page_num - 1,
                                                         dest->left,
-                                                        dest->bottom,
+                                                        height - dest->bottom,
                                                         dest->right,
-                                                        dest->top);
+                                                        height - dest->top);
+                       g_object_unref (poppler_page);
+               }
                        break;
                case POPPLER_DEST_FITB:
                        unimplemented_dest = "POPPLER_DEST_FITB";
@@ -829,7 +889,7 @@ ev_link_dest_from_dest (PopplerDest *dest)
                        unimplemented_dest = "POPPLER_DEST_UNKNOWN";
                        break;
        }
-       
+
        if (unimplemented_dest) {
                g_warning ("Unimplemented named action: %s, please post a "
                           "bug report in Evince bugzilla "
@@ -844,7 +904,8 @@ ev_link_dest_from_dest (PopplerDest *dest)
 }
 
 static EvLink *
-ev_link_from_action (PopplerAction *action)
+ev_link_from_action (PdfDocument   *pdf_document,
+                    PopplerAction *action)
 {
        EvLink       *link = NULL;
        EvLinkAction *ev_action = NULL;
@@ -854,14 +915,14 @@ ev_link_from_action (PopplerAction *action)
                case POPPLER_ACTION_GOTO_DEST: {
                        EvLinkDest *dest;
                        
-                       dest = ev_link_dest_from_dest (action->goto_dest.dest);
+                       dest = ev_link_dest_from_dest (pdf_document, action->goto_dest.dest);
                        ev_action = ev_link_action_new_dest (dest);
                }
                        break;
                case POPPLER_ACTION_GOTO_REMOTE: {
                        EvLinkDest *dest;
                        
-                       dest = ev_link_dest_from_dest (action->goto_remote.dest);
+                       dest = ev_link_dest_from_dest (pdf_document, action->goto_remote.dest);
                        ev_action = ev_link_action_new_remote (dest, 
                                                               action->goto_remote.file_name);
                        
@@ -926,22 +987,22 @@ build_tree (PdfDocument      *pdf_document,
                                        dest = poppler_document_find_dest (pdf_document->document,
                                                                           action->goto_dest.dest->named_dest);
                                        if (!dest) {
-                                               link = ev_link_from_action (action);
+                                               link = ev_link_from_action (pdf_document, action);
                                                break;
                                        }
                                        
-                                       ev_dest = ev_link_dest_from_dest (dest);
+                                       ev_dest = ev_link_dest_from_dest (pdf_document, dest);
                                        poppler_dest_free (dest);
                                        
                                        ev_action = ev_link_action_new_dest (ev_dest);
                                        link = ev_link_new (action->any.title, ev_action);
                                } else {
-                                       link = ev_link_from_action (action);
+                                       link = ev_link_from_action (pdf_document, action);
                                }
                        }
                                break;
                        default:
-                               link = ev_link_from_action (action);
+                               link = ev_link_from_action (pdf_document, action);
                                break;
                }
                
@@ -1017,7 +1078,8 @@ pdf_document_links_get_links (EvDocumentLinks *document_links,
 
                link_mapping = (PopplerLinkMapping *)list->data;
                ev_link_mapping = g_new (EvLinkMapping, 1);
-               ev_link_mapping->link = ev_link_from_action (link_mapping->action);
+               ev_link_mapping->link = 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;
                /* Invert this for X-style coordinates */
@@ -1045,7 +1107,7 @@ pdf_document_links_find_link_dest (EvDocumentLinks  *document_links,
        dest = poppler_document_find_dest (pdf_document->document,
                                           link_name);
        if (dest) {
-               ev_dest = ev_link_dest_from_dest (dest);
+               ev_dest = ev_link_dest_from_dest (pdf_document, dest);
                poppler_dest_free (dest);
        }
 
@@ -1093,9 +1155,11 @@ make_thumbnail_for_size (PdfDocument   *pdf_document,
                                 width, height);
        gdk_pixbuf_fill (pixbuf, 0xffffffff);
 
+       ev_document_fc_mutex_lock ();
        poppler_page_render_to_pixbuf (poppler_page, 0, 0,
                                       width, height,
                                       scale, rotation, pixbuf);
+       ev_document_fc_mutex_unlock ();
        
 
        g_object_unref (poppler_page);
@@ -1230,10 +1294,6 @@ pdf_document_search_new (PdfDocument *pdf_document,
 
        search->text = g_strdup (text);
         search->pages = g_new0 (GList *, n_pages);
-       for (i = 0; i < n_pages; i++) {
-               search->pages[i] = NULL;
-       }
-
         search->document = pdf_document;
 
         /* We add at low priority so the progress bar repaints */
@@ -1273,7 +1333,7 @@ pdf_document_find_begin (EvDocumentFind   *document,
                                                        search_string);
 }
 
-int
+static int
 pdf_document_find_get_n_results (EvDocumentFind *document_find, int page)
 {
        PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
@@ -1285,7 +1345,7 @@ pdf_document_find_get_n_results (EvDocumentFind *document_find, int page)
        }
 }
 
-gboolean
+static gboolean
 pdf_document_find_get_result (EvDocumentFind *document_find,
                              int             page,
                              int             n_result,
@@ -1316,7 +1376,7 @@ pdf_document_find_get_result (EvDocumentFind *document_find,
        return TRUE;
 }
 
-int
+static int
 pdf_document_find_page_has_results (EvDocumentFind *document_find,
                                    int             page)
 {
@@ -1325,7 +1385,7 @@ pdf_document_find_page_has_results (EvDocumentFind *document_find,
        return search && search->pages[page] != NULL;
 }
 
-double
+static double
 pdf_document_find_get_progress (EvDocumentFind *document_find)
 {
        PdfDocumentSearch *search;
@@ -1371,52 +1431,135 @@ pdf_document_find_iface_init (EvDocumentFindIface *iface)
         iface->cancel = pdf_document_find_cancel;
 }
 
+static const gboolean supported_formats[] = {
+       TRUE, /* EV_FILE_FORMAT_PS */
+#ifdef HAVE_CAIRO_PDF
+#ifdef HAVE_POPPLER_PAGE_RENDER
+       TRUE, /* EV_FILE_FORMAT_PDF */
+#else
+       FALSE, /* EV_FILE_FORMAT_PDF */
+#endif
+#endif
+};
+
 static void
-pdf_document_ps_exporter_begin (EvPSExporter *exporter, const char *filename,
-                               int first_page, int last_page,
-                               double width, double height, gboolean duplex)
+pdf_print_context_free (PdfPrintContext *ctx)
+{
+       if (!ctx)
+               return;
+
+       if (ctx->ps_file) {
+               poppler_ps_file_free (ctx->ps_file);
+               ctx->ps_file = NULL;
+       }
+#ifdef HAVE_CAIRO_PDF
+       if (ctx->pdf_cairo) {
+               cairo_destroy (ctx->pdf_cairo);
+               ctx->pdf_cairo = NULL;
+       }
+#endif
+       g_free (ctx);
+}
+
+static gboolean
+pdf_document_file_exporter_format_supported (EvFileExporter      *exporter,
+                                            EvFileExporterFormat format)
+{
+       return supported_formats[format];
+}
+
+static void
+pdf_document_file_exporter_begin (EvFileExporter      *exporter,
+                                 EvFileExporterFormat format,
+                                 const char          *filename,
+                                 int                  first_page,
+                                 int                  last_page,
+                                 double               width,
+                                 double               height,
+                                 gboolean             duplex)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
+       PdfPrintContext *ctx;
+
+       if (pdf_document->print_ctx)
+               pdf_print_context_free (pdf_document->print_ctx);
+       pdf_document->print_ctx = g_new0 (PdfPrintContext, 1);
+       ctx = pdf_document->print_ctx;
+       ctx->format = format;
        
-       pdf_document->ps_file = poppler_ps_file_new (pdf_document->document, filename,
-                                                    first_page,
-                                                    last_page - first_page + 1);
-       poppler_ps_file_set_paper_size (pdf_document->ps_file, width, height);
-       poppler_ps_file_set_duplex (pdf_document->ps_file, duplex);
+       switch (format) {
+               case EV_FILE_FORMAT_PS:
+                       ctx->ps_file = poppler_ps_file_new (pdf_document->document,
+                                                           filename, first_page,
+                                                           last_page - first_page + 1);
+                       poppler_ps_file_set_paper_size (ctx->ps_file, width, height);
+                       poppler_ps_file_set_duplex (ctx->ps_file, duplex);
+
+                       break;
+               case EV_FILE_FORMAT_PDF: {
+#ifdef HAVE_CAIRO_PDF
+                       cairo_surface_t *surface;
+                       
+                       surface = cairo_pdf_surface_create (filename, width, height);
+                       ctx->pdf_cairo = cairo_create (surface);
+                       cairo_surface_destroy (surface);
+#endif
+               }
+                       break;
+               default:
+                       g_assert_not_reached ();
+       }
 }
 
 static void
-pdf_document_ps_exporter_do_page (EvPSExporter *exporter, EvRenderContext *rc)
+pdf_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
+       PdfPrintContext *ctx = pdf_document->print_ctx;
        PopplerPage *poppler_page;
 
-       g_return_if_fail (pdf_document->ps_file != NULL);
+       g_return_if_fail (pdf_document->print_ctx != NULL);
 
        poppler_page = poppler_document_get_page (pdf_document->document, rc->page);
-       poppler_page_render_to_ps (poppler_page, pdf_document->ps_file);
+
+       switch (ctx->format) {
+               case EV_FILE_FORMAT_PS:
+                       poppler_page_render_to_ps (poppler_page, ctx->ps_file);
+                       break;
+               case EV_FILE_FORMAT_PDF:
+#ifdef HAVE_POPPLER_PAGE_RENDER
+                       poppler_page_render (poppler_page, ctx->pdf_cairo);
+#endif
+#ifdef HAVE_CAIRO_PDF
+                       cairo_show_page (ctx->pdf_cairo);
+#endif
+                       break;
+               default:
+                       g_assert_not_reached ();
+       }
+       
        g_object_unref (poppler_page);
 }
 
 static void
-pdf_document_ps_exporter_end (EvPSExporter *exporter)
+pdf_document_file_exporter_end (EvFileExporter *exporter)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
 
-       poppler_ps_file_free (pdf_document->ps_file);
-       pdf_document->ps_file = NULL;
+       pdf_print_context_free (pdf_document->print_ctx);
+       pdf_document->print_ctx = NULL;
 }
 
 static void
-pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
+pdf_document_file_exporter_iface_init (EvFileExporterIface *iface)
 {
-        iface->begin = pdf_document_ps_exporter_begin;
-        iface->do_page = pdf_document_ps_exporter_do_page;
-        iface->end = pdf_document_ps_exporter_end;
+       iface->format_supported = pdf_document_file_exporter_format_supported;
+        iface->begin = pdf_document_file_exporter_begin;
+        iface->do_page = pdf_document_file_exporter_do_page;
+        iface->end = pdf_document_file_exporter_end;
 }
 
-
-void
+static void
 pdf_selection_render_selection (EvSelection      *selection,
                                EvRenderContext  *rc,
                                GdkPixbuf       **pixbuf,
@@ -1451,7 +1594,7 @@ pdf_selection_render_selection (EvSelection      *selection,
 }
 
 
-GdkRegion *
+static GdkRegion *
 pdf_selection_get_selection_region (EvSelection     *selection,
                                    EvRenderContext *rc,
                                    EvRectangle     *points)
@@ -1468,7 +1611,7 @@ pdf_selection_get_selection_region (EvSelection     *selection,
        return retval;
 }
 
-GdkRegion *
+static GdkRegion *
 pdf_selection_get_selection_map (EvSelection     *selection,
                                 EvRenderContext *rc)
 {
@@ -1498,6 +1641,36 @@ pdf_selection_iface_init (EvSelectionIface *iface)
         iface->get_selection_map = pdf_selection_get_selection_map;
 }
 
+/* Page Transitions */
+static gdouble
+pdf_document_get_page_duration (EvDocumentTransition *trans,
+                               gint                  page)
+{
+#ifdef HAVE_POPPLER_PAGE_GET_DURATION  
+       PdfDocument *pdf_document;
+       PopplerPage *poppler_page;
+       gdouble      duration = -1;
+
+       pdf_document = PDF_DOCUMENT (trans);
+       poppler_page = poppler_document_get_page (pdf_document->document, page);
+       if (!poppler_page)
+               return -1;
+
+       duration = poppler_page_get_duration (poppler_page);
+       g_object_unref (poppler_page);
+
+       return duration;
+#else
+       return -1;
+#endif /* HAVE_POPPLER_PAGE_GET_DURATION */
+}
+
+static void
+pdf_document_page_transition_iface_init (EvDocumentTransitionIface *iface)
+{
+       iface->get_page_duration = pdf_document_get_page_duration;
+}
+
 PdfDocument *
 pdf_document_new (void)
 {