]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/pdf-document.cc
Fix several bugs with find
[evince.git] / pdf / xpdf / pdf-document.cc
index a8824e8845036310d9cf57424155ec1a96a777fe..255c5abd17f1fa6c50cc94c35211c9826c5f71b1 100644 (file)
@@ -26,6 +26,7 @@
 #include "gpdf-g-switch.h"
 #include "ev-document-links.h"
 #include "ev-document-misc.h"
+#include "ev-document-security.h"
 #include "ev-document-thumbnails.h"
 
 #include "GlobalParams.h"
@@ -33,6 +34,7 @@
 #include "SplashBitmap.h"
 #include "PDFDoc.h"
 #include "Outline.h"
+#include "ErrorCodes.h"
 #include "UnicodeMap.h"
 #include "GlobalParams.h"
 #include "GfxState.h"
@@ -54,7 +56,7 @@ typedef struct
         /* full results are only possible for the rendered current page */
         int current_page;
         GArray *current_page_results;
-        guchar *other_page_flags; /* length n_pages + 1, first element ignored */
+        int *other_page_flags; /* length n_pages + 1, first element ignored */
         int start_page;   /* skip this one as we iterate, since we did it first */
         int search_page;  /* the page we're searching now */
         TextOutputDev *output_dev;
@@ -87,18 +89,21 @@ struct _PdfDocument
        Links *links;
        UnicodeMap *umap;
 
+       gchar *password;
        gboolean page_valid;
 
        PdfDocumentSearch *search;
 };
 
-static void pdf_document_document_links_iface_init      (EvDocumentLinksIface  *iface);
+static void pdf_document_document_links_iface_init      (EvDocumentLinksIface      *iface);
 static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
 static void pdf_document_document_iface_init            (EvDocumentIface           *iface);
-static void pdf_document_ps_exporter_iface_init (EvPSExporterIface   *iface);
-static void pdf_document_find_iface_init        (EvDocumentFindIface *iface);
-static void pdf_document_search_free            (PdfDocumentSearch   *search);
-static void pdf_document_search_page_changed    (PdfDocumentSearch   *search);
+static void pdf_document_ps_exporter_iface_init         (EvPSExporterIface         *iface);
+static void pdf_document_find_iface_init                (EvDocumentFindIface       *iface);
+static void pdf_document_security_iface_init            (EvDocumentSecurityIface   *iface);
+static void pdf_document_search_free                    (PdfDocumentSearch         *search);
+static void pdf_document_search_page_changed            (PdfDocumentSearch         *search);
+
 
 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
                          {
@@ -112,6 +117,8 @@ G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
                                                        pdf_document_ps_exporter_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
                                                        pdf_document_find_iface_init);
+                                G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_SECURITY,
+                                                       pdf_document_security_iface_init);
                         });
 
 static void
@@ -142,6 +149,8 @@ document_validate_page (PdfDocument *pdf_document)
 
                pdf_document->page_valid = TRUE;
 
+               ev_document_changed (EV_DOCUMENT (pdf_document));
+
                 /* Update the search results available to the app since
                  * we only provide full results on the current page
                  */
@@ -184,18 +193,27 @@ pdf_document_load (EvDocument  *document,
        g_free (filename);
 
        // open the PDF file, assumes ownership of filename_g
-       newDoc = new PDFDoc(filename_g, 0, 0);
+       GString *password = NULL;
+       if (pdf_document->password)
+               password = new GString (pdf_document->password);
+       newDoc = new PDFDoc(filename_g, password, password);
+       if (password)
+               delete password;
 
        if (!newDoc->isOk()) {
                err = newDoc->getErrorCode();
                delete newDoc;
-
-               /* FIXME: Add a real error enum to EvDocument */
-               g_set_error (error, G_FILE_ERROR,
-                            G_FILE_ERROR_FAILED,
-                            "Failed to load document (error %d) '%s'\n",
-                            err,
-                            uri);
+               if (err == errEncrypted) {
+                       g_set_error (error, EV_DOCUMENT_ERROR,
+                                    EV_DOCUMENT_ERROR_ENCRYPTED,
+                                    "Document is encrypted.");
+               } else {
+                       g_set_error (error, G_FILE_ERROR,
+                                    G_FILE_ERROR_FAILED,
+                                    "Failed to load document (error %d) '%s'\n",
+                                    err,
+                                    uri);
+               }
 
                return FALSE;
        }
@@ -258,8 +276,6 @@ pdf_document_set_page (EvDocument  *document,
                pdf_document->page = page;
                pdf_document->page_valid = FALSE;
        }
-
-       ev_document_changed (document);
 }
 
 static int
@@ -334,21 +350,32 @@ pdf_document_set_page_offset (EvDocument  *document,
 
 static void
 pdf_document_get_page_size (EvDocument   *document,
+                           int           page,
                            int          *width,
                            int          *height)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (document);
+       Page *the_page;
 
-       if (document_validate_page (pdf_document)) {
+       /* set some default values */
+       if (width)
+               *width = 1;
+       if (height)
+               *height = 1;
+
+               
+       if (page == -1 && document_validate_page (pdf_document)) {
                if (width)
                        *width = pdf_document->out->getBitmapWidth();
                if (height)
                        *height = pdf_document->out->getBitmapHeight();
-       } else {
-               if (width)
-                       *width = 1;
-               if (height)
-                       *height = 1;
+               return;
+       }
+
+       the_page = pdf_document->doc->getCatalog ()->getPage (page);
+       if (the_page) {
+               *width = (int) the_page->getWidth ();
+               *height = (int) the_page->getHeight ();
        }
 }
 
@@ -383,61 +410,76 @@ pdf_document_render (EvDocument  *document,
                                           draw.width, draw.height);
 }
 
-static void
-pdf_document_search_emit_found (PdfDocumentSearch *search)
+double
+pdf_document_find_get_progress (EvDocumentFind *document_find)
 {
-        PdfDocument *pdf_document = search->document;
-        int n_pages;
-        int pages_done;
-        GArray *tmp_results;
-        int i;
+       PdfDocumentSearch *search;
+       int n_pages, pages_done;
 
-        n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
-        if (search->search_page > search->start_page) {
-                pages_done = search->search_page - search->start_page;
-        } else if (search->search_page == search->start_page) {
-                pages_done = n_pages;
-        } else {
-                pages_done = n_pages - search->start_page + search->search_page;
-        }
+       search = PDF_DOCUMENT (document_find)->search;
 
-        tmp_results = g_array_new (FALSE, FALSE, sizeof (EvFindResult));
-        g_array_append_vals (tmp_results,
-                             search->current_page_results->data,
-                             search->current_page_results->len);
+       if (search == NULL) {
+               return 0;
+       }
 
-        /* Now append a bogus element for each page that has a result in it,
-         * that is not the current page
-         */
-        i = 1;
-        while (i <= n_pages) {
-                if (i != pdf_document->page &&
-                    search->other_page_flags[i]) {
-                        EvFindResult result;
-
-                        result.page_num = i;
-
-                        /* Use bogus coordinates, again we can't get coordinates
-                         * until this is the current page because TextOutputDev
-                         * isn't good enough
-                         */
-                        result.highlight_area.x = -1;
-                        result.highlight_area.y = -1;
-                        result.highlight_area.width = 1;
-                        result.highlight_area.height = 1;
-
-                        g_array_append_val (tmp_results, result);
-                }
+       n_pages = ev_document_get_n_pages (EV_DOCUMENT (document_find));
+       if (search->search_page > search->start_page) {
+               pages_done = search->search_page - search->start_page + 1;
+       } else if (search->search_page == search->start_page) {
+               pages_done = n_pages;
+       } else {
+               pages_done = n_pages - search->start_page + search->search_page;
+       }
 
-                ++i;
-        }
+       return pages_done / (double) n_pages;
+}
+
+int
+pdf_document_find_page_has_results (EvDocumentFind *document_find,
+                                   int             page)
+{
+       PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
 
-        ev_document_find_found (EV_DOCUMENT_FIND (pdf_document),
-                                (EvFindResult*) tmp_results->data,
-                                tmp_results->len,
-                                pages_done / (double) n_pages);
+       g_return_val_if_fail (search != NULL, FALSE);
 
-        g_array_free (tmp_results, TRUE);
+       return search->other_page_flags[page];
+}
+
+int
+pdf_document_find_get_n_results (EvDocumentFind *document_find)
+{
+       PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
+
+       if (search) {
+               return search->current_page_results->len;
+       } else {
+               return 0;
+       }
+}
+
+gboolean
+pdf_document_find_get_result (EvDocumentFind *document_find,
+                             int             n_result,
+                             GdkRectangle   *rectangle)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document_find);
+       PdfDocumentSearch *search = pdf_document->search;
+       GdkRectangle r;
+
+       if (search != NULL &&
+           n_result < search->current_page_results->len) {
+               r = g_array_index (search->current_page_results,
+                                  GdkRectangle, n_result);
+
+               rectangle->x = r.x + pdf_document->page_x_offset;
+               rectangle->y = r.y + pdf_document->page_y_offset;
+               rectangle->width = r.width;
+               rectangle->height = r.height;
+
+               return TRUE;
+       } else {
+               return FALSE;
+       }
 }
 
 static void
@@ -445,7 +487,7 @@ pdf_document_search_page_changed (PdfDocumentSearch   *search)
 {
         PdfDocument *pdf_document = search->document;
         int current_page;
-        EvFindResult result;
+        GdkRectangle result;
         int xMin, yMin, xMax, yMax;
 
         current_page = pdf_document->page;
@@ -466,12 +508,10 @@ pdf_document_search_page_changed (PdfDocumentSearch   *search)
                                          gTrue, gTrue, // startAtTop, stopAtBottom
                                          gFalse, gFalse, // startAtLast, stopAtLast
                                          &xMin, &yMin, &xMax, &yMax)) {
-                result.page_num = pdf_document->page;
-
-                result.highlight_area.x = xMin;
-                result.highlight_area.y = yMin;
-                result.highlight_area.width = xMax - xMin;
-                result.highlight_area.height = yMax - yMin;
+                result.x = xMin;
+                result.y = yMin;
+                result.width = xMax - xMin;
+                result.height = yMax - yMin;
 
                 g_array_append_val (search->current_page_results, result);
                 /* Now find further results */
@@ -480,25 +520,14 @@ pdf_document_search_page_changed (PdfDocumentSearch   *search)
                                                     gFalse, gTrue,
                                                     gTrue, gFalse,
                                                     &xMin, &yMin, &xMax, &yMax)) {
-
-                        result.page_num = pdf_document->page;
-
-                        result.highlight_area.x = xMin;
-                        result.highlight_area.y = yMin;
-                        result.highlight_area.width = xMax - xMin;
-                        result.highlight_area.height = yMax - yMin;
+                        result.x = xMin;
+                        result.y = yMin;
+                        result.width = xMax - xMin;
+                        result.height = yMax - yMin;
 
                         g_array_append_val (search->current_page_results, result);
                 }
         }
-
-        /* needed for the initial current page since we don't search
-         * it in the idle
-         */
-        search->other_page_flags[current_page] =
-                search->current_page_results->len > 0;
-
-        pdf_document_search_emit_found (search);
 }
 
 static gboolean
@@ -506,7 +535,7 @@ pdf_document_search_idle_callback (void *data)
 {
         PdfDocumentSearch *search = (PdfDocumentSearch*) data;
         PdfDocument *pdf_document = search->document;
-        int n_pages;
+        int n_pages, changed_page;
         double xMin, yMin, xMax, yMax;
 
         /* Note that PDF page count is 1 through n_pages INCLUSIVE
@@ -518,10 +547,6 @@ pdf_document_search_idle_callback (void *data)
          */
         n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
 
-        if (search->search_page == search->start_page) {
-                goto end_search;
-        }
-
         if (search->output_dev == 0) {
                 /* First time through here... */
                 search->output_dev = new TextOutputDev (NULL, gTrue, gFalse, gFalse);
@@ -540,8 +565,12 @@ pdf_document_search_idle_callback (void *data)
                                           gFalse, gFalse, // startAtLast, stopAtLast
                                           &xMin, &yMin, &xMax, &yMax)) {
                 /* This page has results */
-                search->other_page_flags[search->search_page] = TRUE;
-        }
+                search->other_page_flags[search->search_page] = 1;
+        } else {
+               search->other_page_flags[search->search_page] = 0;
+       }
+
+       changed_page = search->start_page;
 
         search->search_page += 1;
         if (search->search_page > n_pages) {
@@ -549,12 +578,13 @@ pdf_document_search_idle_callback (void *data)
                 search->search_page = 1;
         }
 
-        /* We do this even if nothing was found, to update the percent complete */
-        pdf_document_search_emit_found (search);
-
-        return TRUE;
+        if (search->search_page != search->start_page) {
+               ev_document_find_changed (EV_DOCUMENT_FIND (pdf_document),
+                                         changed_page);
+               return TRUE;
+       }
 
- end_search:
+end_search:
         /* We're done. */
         search->idle = 0; /* will return FALSE to remove */
         return FALSE;
@@ -567,7 +597,7 @@ pdf_document_find_begin (EvDocumentFind   *document,
 {
         PdfDocument *pdf_document = PDF_DOCUMENT (document);
         PdfDocumentSearch *search;
-        int n_pages;
+        int n_pages, i;
         gunichar *ucs4;
         glong ucs4_len;
 
@@ -602,13 +632,13 @@ pdf_document_find_begin (EvDocumentFind   *document,
 
         search->current_page_results = g_array_new (FALSE,
                                                     FALSE,
-                                                    sizeof (EvFindResult));
+                                                    sizeof (GdkRectangle));
         n_pages = ev_document_get_n_pages (EV_DOCUMENT (document));
 
-        /* This is an array of bool; with the first value ignored
-         * so we can index by the based-at-1 page numbers
-         */
-        search->other_page_flags = g_new0 (guchar, n_pages + 1);
+        search->other_page_flags = g_new0 (int, n_pages + 1);
+       for (i = 0; i <= n_pages; i++) {
+               search->other_page_flags[i] = -1;
+       }
 
         search->document = pdf_document;
 
@@ -621,9 +651,7 @@ pdf_document_find_begin (EvDocumentFind   *document,
         search->output_dev = 0;
 
         search->start_page = pdf_document->page;
-        search->search_page = search->start_page + 1;
-        if (search->search_page > n_pages)
-                search->search_page = 1;
+        search->search_page = search->start_page;
 
         search->current_page = -1;
 
@@ -634,7 +662,7 @@ pdf_document_find_begin (EvDocumentFind   *document,
 }
 
 static void
-pdf_document_find_cancel (EvDocumentFind   *document)
+pdf_document_find_cancel (EvDocumentFind *document)
 {
         PdfDocument *pdf_document = PDF_DOCUMENT (document);
 
@@ -660,6 +688,25 @@ pdf_document_search_free (PdfDocumentSearch   *search)
        g_free (search);
 }
 
+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_ps_export_begin (EvPSExporter *exporter, const char *filename)
 {
@@ -971,6 +1018,8 @@ pdf_document_get_title (PdfDocument *pdf_document)
        char *title = NULL;
        Object info;
 
+       if (pdf_document->doc == NULL)
+               return NULL;
        pdf_document->doc->getDocInfo (&info);
 
        if (info.isDict ()) {
@@ -988,10 +1037,10 @@ pdf_document_get_text (EvDocument *document, GdkRectangle *rect)
        const char *text;
        int x1, y1, x2, y2;
 
-       x1 = rect->x;
-       y1 = rect->y;
-       x2 = x1 + rect->width;
-       y2 = y1 + rect->height;
+       x1 = rect->x + pdf_document->page_x_offset;
+       y1 = rect->y + pdf_document->page_y_offset;
+       x2 = x1 + rect->width + pdf_document->page_x_offset;
+       y2 = y1 + rect->height + pdf_document->page_y_offset;
 
        sel_text = pdf_document->out->getText (x1, y1, x2, y2);
        text = sel_text->getCString ();
@@ -1004,8 +1053,25 @@ pdf_document_get_link (EvDocument *document, int x, int y)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (document);
        LinkAction *action;
+       double link_x, link_y;
 
-       action = pdf_document->links->find (x, y);
+       if (pdf_document->links == NULL) {
+               return NULL;
+       }
+
+       /* Offset */
+       link_x = x - pdf_document->page_x_offset;
+       link_y = y - pdf_document->page_y_offset;
+
+       /* Inverse y */
+       link_y = pdf_document->out->getBitmapHeight() - link_y;
+
+       /* Zoom */
+       link_x = link_x / pdf_document->scale;
+       link_y = link_y / pdf_document->scale;
+
+       action = pdf_document->links->find (link_x, link_y);
+       
        if (action) {
                return build_link_from_action (pdf_document, action, "");
        } else {
@@ -1074,9 +1140,20 @@ static void
 pdf_document_find_iface_init (EvDocumentFindIface *iface)
 {
         iface->begin = pdf_document_find_begin;
+       iface->get_n_results = pdf_document_find_get_n_results;
+       iface->get_result = pdf_document_find_get_result;
+       iface->page_has_results = pdf_document_find_page_has_results;
+       iface->get_progress = pdf_document_find_get_progress;
         iface->cancel = pdf_document_find_cancel;
 }
 
+static void
+pdf_document_security_iface_init (EvDocumentSecurityIface *iface)
+{
+       iface->has_document_security = pdf_document_has_document_security;
+       iface->set_password = pdf_document_set_password;
+}
+
 static void
 pdf_document_document_links_iface_init (EvDocumentLinksIface *iface)
 {
@@ -1186,8 +1263,7 @@ pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnail
        Thumb *thumb = NULL;
        gdouble page_ratio;
 
-       /* getPage seems to want page + 1 for some reason; */
-       the_page = pdf_document->doc->getCatalog ()->getPage (page + 1);
+       the_page = pdf_document->doc->getCatalog ()->getPage (page);
        the_page->getThumb (&the_thumb);
 
        if (!(the_thumb.isNull () || the_thumb.isNone())) {
@@ -1281,5 +1357,6 @@ pdf_document_init (PdfDocument *pdf_document)
        pdf_document->scale = 1.;
 
        pdf_document->page_valid = FALSE;
+       pdf_document->password = NULL;
 }