]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/pdf-document.cc
Keep offset in consideration in a few places
[evince.git] / pdf / xpdf / pdf-document.cc
index 51ade189877dae2aefd5f1c8a98e62d7e60777cd..b9180bd9988b1e712c4c79a445401f5777e33e3e 100644 (file)
 #include "ev-ps-exporter.h"
 #include "ev-document-find.h"
 #include "gpdf-g-switch.h"
 #include "ev-ps-exporter.h"
 #include "ev-document-find.h"
 #include "gpdf-g-switch.h"
-#include "ev-document-bookmarks.h"
+#include "ev-document-links.h"
+#include "ev-document-misc.h"
+#include "ev-document-security.h"
 #include "ev-document-thumbnails.h"
 
 #include "GlobalParams.h"
 #include "GDKSplashOutputDev.h"
 #include "ev-document-thumbnails.h"
 
 #include "GlobalParams.h"
 #include "GDKSplashOutputDev.h"
+#include "SplashBitmap.h"
 #include "PDFDoc.h"
 #include "Outline.h"
 #include "PDFDoc.h"
 #include "Outline.h"
+#include "ErrorCodes.h"
 #include "UnicodeMap.h"
 #include "GlobalParams.h"
 #include "GfxState.h"
 #include "UnicodeMap.h"
 #include "GlobalParams.h"
 #include "GfxState.h"
@@ -82,35 +86,56 @@ struct _PdfDocument
        GDKSplashOutputDev *out;
        PSOutputDev *ps_out;
        PDFDoc *doc;
        GDKSplashOutputDev *out;
        PSOutputDev *ps_out;
        PDFDoc *doc;
+       Links *links;
        UnicodeMap *umap;
 
        UnicodeMap *umap;
 
+       gchar *password;
        gboolean page_valid;
 
        PdfDocumentSearch *search;
 };
 
        gboolean page_valid;
 
        PdfDocumentSearch *search;
 };
 
-static void pdf_document_document_bookmarks_iface_init  (EvDocumentBookmarksIface  *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_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,
                          {
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
                                                        pdf_document_document_iface_init);
 
 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
                          {
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
                                                        pdf_document_document_iface_init);
-                                G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_BOOKMARKS,
-                                                       pdf_document_document_bookmarks_iface_init);
+                                G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
+                                                       pdf_document_document_links_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
                                                        pdf_document_document_thumbnails_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
                                                        pdf_document_ps_exporter_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
                                                        pdf_document_find_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
                                                        pdf_document_document_thumbnails_iface_init);
                                 G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
                                                        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
+document_init_links (PdfDocument *pdf_document)
+{
+       Page *page;
+       Object obj;
+
+       if (pdf_document->links) {
+               delete pdf_document->links;
+       }
+       page = pdf_document->doc->getCatalog ()->getPage (pdf_document->page);
+       pdf_document->links = new Links (page->getAnnots (&obj),
+                                        pdf_document->doc->getCatalog ()->getBaseURI ());
+       obj.free ();
+}
+
 static gboolean
 document_validate_page (PdfDocument *pdf_document)
 {
 static gboolean
 document_validate_page (PdfDocument *pdf_document)
 {
@@ -120,8 +145,12 @@ document_validate_page (PdfDocument *pdf_document)
                                                72 * pdf_document->scale,
                                                0, gTrue, gTrue);
 
                                                72 * pdf_document->scale,
                                                0, gTrue, gTrue);
 
+               document_init_links (pdf_document);
+
                pdf_document->page_valid = TRUE;
 
                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
                  */
                 /* Update the search results available to the app since
                  * we only provide full results on the current page
                  */
@@ -164,18 +193,27 @@ pdf_document_load (EvDocument  *document,
        g_free (filename);
 
        // open the PDF file, assumes ownership of filename_g
        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;
 
        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;
        }
 
                return FALSE;
        }
@@ -196,6 +234,25 @@ pdf_document_load (EvDocument  *document,
        return TRUE;
 }
 
        return TRUE;
 }
 
+static gboolean
+pdf_document_save (EvDocument  *document,
+                  const char  *uri,
+                  GError     **error)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document);
+       char *filename;
+       gboolean retval = FALSE;
+
+       filename = g_filename_from_uri (uri, NULL, error);
+       if (filename != NULL) {
+               GString *fname = new GString (filename);
+
+               retval = pdf_document->doc->saveAs (fname);
+       }
+
+       return retval;
+}
+
 static int
 pdf_document_get_n_pages (EvDocument  *document)
 {
 static int
 pdf_document_get_n_pages (EvDocument  *document)
 {
@@ -219,8 +276,6 @@ pdf_document_set_page (EvDocument  *document,
                pdf_document->page = page;
                pdf_document->page_valid = FALSE;
        }
                pdf_document->page = page;
                pdf_document->page_valid = FALSE;
        }
-
-       ev_document_changed (document);
 }
 
 static int
 }
 
 static int
@@ -429,8 +484,8 @@ pdf_document_search_page_changed (PdfDocumentSearch   *search)
                                          &xMin, &yMin, &xMax, &yMax)) {
                 result.page_num = pdf_document->page;
 
                                          &xMin, &yMin, &xMax, &yMax)) {
                 result.page_num = pdf_document->page;
 
-                result.highlight_area.x = xMin;
-                result.highlight_area.y = yMin;
+                result.highlight_area.x = xMin + pdf_document->page_x_offset;
+                result.highlight_area.y = yMin + pdf_document->page_y_offset;
                 result.highlight_area.width = xMax - xMin;
                 result.highlight_area.height = yMax - yMin;
 
                 result.highlight_area.width = xMax - xMin;
                 result.highlight_area.height = yMax - yMin;
 
@@ -444,8 +499,8 @@ pdf_document_search_page_changed (PdfDocumentSearch   *search)
 
                         result.page_num = pdf_document->page;
 
 
                         result.page_num = pdf_document->page;
 
-                        result.highlight_area.x = xMin;
-                        result.highlight_area.y = yMin;
+                        result.highlight_area.x = xMin + pdf_document->page_x_offset;
+                        result.highlight_area.y = yMin + pdf_document->page_y_offset;
                         result.highlight_area.width = xMax - xMin;
                         result.highlight_area.height = yMax - yMin;
 
                         result.highlight_area.width = xMax - xMin;
                         result.highlight_area.height = yMax - yMin;
 
@@ -621,6 +676,25 @@ pdf_document_search_free (PdfDocumentSearch   *search)
        g_free (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)
 {
 static void
 pdf_document_ps_export_begin (EvPSExporter *exporter, const char *filename)
 {
@@ -654,14 +728,14 @@ pdf_document_ps_export_end (EvPSExporter *exporter)
 }
 
 
 }
 
 
-/* EvDocumentBookmarks Implementation */
+/* EvDocumentLinks Implementation */
 typedef struct
 {
        /* goo GList, not glib */
        GList *items;
        int index;
        int level;
 typedef struct
 {
        /* goo GList, not glib */
        GList *items;
        int index;
        int level;
-} BookmarksIter;
+} LinksIter;
 
 static gchar *
 unicode_to_char (OutlineItem *outline_item,
 
 static gchar *
 unicode_to_char (OutlineItem *outline_item,
@@ -681,12 +755,12 @@ unicode_to_char (OutlineItem *outline_item,
 
 
 static gboolean
 
 
 static gboolean
-pdf_document_bookmarks_has_document_bookmarks (EvDocumentBookmarks *document_bookmarks)
+pdf_document_links_has_document_links (EvDocumentLinks *document_links)
 {
 {
-       PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
+       PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
        Outline *outline;
 
        Outline *outline;
 
-       g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
+       g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
 
        outline = pdf_document->doc->getOutline();
        if (outline->getItems() != NULL &&
 
        outline = pdf_document->doc->getOutline();
        if (outline->getItems() != NULL &&
@@ -696,139 +770,137 @@ pdf_document_bookmarks_has_document_bookmarks (EvDocumentBookmarks *document_boo
        return FALSE;
 }
 
        return FALSE;
 }
 
-static EvDocumentBookmarksIter *
-pdf_document_bookmarks_begin_read (EvDocumentBookmarks *document_bookmarks)
+static EvDocumentLinksIter *
+pdf_document_links_begin_read (EvDocumentLinks *document_links)
 {
 {
-       PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
+       PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
        Outline *outline;
        Outline *outline;
-       BookmarksIter *iter;
+       LinksIter *iter;
        GList *items;
 
        GList *items;
 
-       g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), NULL);
+       g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL);
 
        outline = pdf_document->doc->getOutline();
        items = outline->getItems();
        if (! items)
                return NULL;
 
 
        outline = pdf_document->doc->getOutline();
        items = outline->getItems();
        if (! items)
                return NULL;
 
-       iter = g_new0 (BookmarksIter, 1);
+       iter = g_new0 (LinksIter, 1);
        iter->items = items;
        iter->index = 0;
        iter->level = 0;
 
        iter->items = items;
        iter->index = 0;
        iter->level = 0;
 
-       return (EvDocumentBookmarksIter *) iter;
+       return (EvDocumentLinksIter *) iter;
 }
 
 }
 
-static gboolean
-pdf_document_bookmarks_get_values (EvDocumentBookmarks      *document_bookmarks,
-                                  EvDocumentBookmarksIter  *bookmarks_iter,
-                                  char                    **title,
-                                  EvDocumentBookmarksType  *type,
-                                  gint                     *page)
+static EvLink *
+build_link_from_action (PdfDocument *pdf_document,
+                       LinkAction  *link_action,
+                       const char  *title)
 {
 {
-       PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
-       BookmarksIter *iter = (BookmarksIter *)bookmarks_iter;
+       EvLink *link = NULL;
+
+       if (link_action == NULL) {
+               link = ev_link_new_title (title);
+       } else if (link_action->getKind () == actionGoTo) {
+               LinkDest *link_dest;
+               LinkGoTo *link_goto;
+               Ref page_ref;
+               gint page_num = 0;
+               GString *named_dest;
+
+               link_goto = dynamic_cast <LinkGoTo *> (link_action);
+               link_dest = link_goto->getDest ();
+               named_dest = link_goto->getNamedDest ();
+
+               /* Wow!  This seems excessively slow on large
+                * documents. I need to investigate more... -jrb */
+               if (link_dest != NULL) {
+                       link_dest = link_dest->copy ();
+               } else if (named_dest != NULL) {
+                       named_dest = named_dest->copy ();
+                       link_dest = pdf_document->doc->findDest (named_dest);
+                       delete named_dest;
+               }
+               if (link_dest != NULL) {
+                       if (link_dest->isPageRef ()) {
+                               page_ref = link_dest->getPageRef ();
+                               page_num = pdf_document->doc->findPage (page_ref.num, page_ref.gen);
+                       } else {
+                               page_num = link_dest->getPageNum ();
+                       }
+                       delete link_dest;
+               }
+
+               link = ev_link_new_page (title, page_num);
+       } else if (link_action->getKind () == actionURI) {
+               LinkURI *link_uri;
+
+               link_uri = dynamic_cast <LinkURI *> (link_action);
+               link = ev_link_new_external
+                       (title, link_uri->getURI()->getCString());
+       } else if (link_action->getKind () == actionNamed) {
+                       /*Skip, for now */
+       }
+
+       return link;
+}
+
+/* FIXME This returns a new object every time, probably we should cache it
+   in the iter */
+static EvLink *
+pdf_document_links_get_link (EvDocumentLinks      *document_links,
+                            EvDocumentLinksIter  *links_iter)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
+       LinksIter *iter = (LinksIter *)links_iter;
        OutlineItem *anItem;
        LinkAction *link_action;
        OutlineItem *anItem;
        LinkAction *link_action;
-       LinkDest *link_dest = NULL;
-       LinkURI *link_uri = NULL;
-       LinkGoTo *link_goto = NULL;
-       GString *named_dest;
        Unicode *link_title;
        Unicode *link_title;
-       Ref page_ref;
-       gint page_num = -1;
+       const char *title;
 
 
-       g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
+       g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
        g_return_val_if_fail (iter != NULL, FALSE);
        g_return_val_if_fail (iter != NULL, FALSE);
-       g_return_val_if_fail (title != NULL, FALSE);
-       g_return_val_if_fail (type != NULL, FALSE);
-       g_return_val_if_fail (page != NULL, FALSE);
 
        anItem = (OutlineItem *)iter->items->get(iter->index);
        link_action = anItem->getAction ();
        link_title = anItem->getTitle ();
 
        anItem = (OutlineItem *)iter->items->get(iter->index);
        link_action = anItem->getAction ();
        link_title = anItem->getTitle ();
+       title = unicode_to_char (anItem, pdf_document->umap);
 
 
-       if (link_action) {
-               switch (link_action->getKind ()) {
-
-               case actionGoTo:
-                       link_goto = dynamic_cast <LinkGoTo *> (link_action);
-                       link_dest = link_goto->getDest ();
-                       named_dest = link_goto->getNamedDest ();
-
-                       /* Wow!  This seems excessively slow on large
-                        * documents. I need to investigate more... -jrb */
-                       if (link_dest != NULL) {
-                               link_dest = link_dest->copy ();
-                       } else if (named_dest != NULL) {
-                               named_dest = named_dest->copy ();
-                               link_dest = pdf_document->doc->findDest (named_dest);
-                               delete named_dest;
-                       }
-                       if (link_dest != NULL) {
-                               if (link_dest->isPageRef ()) {
-                                       page_ref = link_dest->getPageRef ();
-                                       page_num = pdf_document->doc->findPage (page_ref.num, page_ref.gen);
-                               } else {
-                                       page_num = link_dest->getPageNum ();
-                               }
-
-                               delete link_dest;
-                       }
-
-                       break;
-               case actionURI:
-                       link_uri = dynamic_cast <LinkURI *> (link_action);
-                       break;
-
-               case actionNamed:
-                       /*Skip, for now */
-               default:
-                       g_warning ("Unknown link action type: %d", link_action->getKind ());
-               }
-
-               *title = g_strdup (unicode_to_char (anItem, pdf_document->umap));
-       } else if (link_title) {
-               *title = g_strdup (unicode_to_char (anItem, pdf_document->umap));
-       }
-
-       *type = EV_DOCUMENT_BOOKMARKS_TYPE_LINK;
-       *page = page_num;
-
-       return TRUE;
+       return build_link_from_action (pdf_document, link_action, title);
 }
 
 }
 
-static EvDocumentBookmarksIter *
-pdf_document_bookmarks_get_child (EvDocumentBookmarks     *document_bookmarks,
-                                 EvDocumentBookmarksIter *bookmarks_iter)
+static EvDocumentLinksIter *
+pdf_document_links_get_child (EvDocumentLinks     *document_links,
+                             EvDocumentLinksIter *links_iter)
 {
 {
-       BookmarksIter *iter = (BookmarksIter *)bookmarks_iter;
-       BookmarksIter *child_iter;
+       LinksIter *iter = (LinksIter *)links_iter;
+       LinksIter *child_iter;
        OutlineItem *anItem;
 
        OutlineItem *anItem;
 
-       g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
+       g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
 
        anItem = (OutlineItem *)iter->items->get(iter->index);
        anItem->open ();
        if (! (anItem->hasKids() && anItem->getKids()) )
                return NULL;
 
 
        anItem = (OutlineItem *)iter->items->get(iter->index);
        anItem->open ();
        if (! (anItem->hasKids() && anItem->getKids()) )
                return NULL;
 
-       child_iter = g_new0 (BookmarksIter, 1);
+       child_iter = g_new0 (LinksIter, 1);
        child_iter->index = 0;
        child_iter->level = iter->level + 1;
        child_iter->items = anItem->getKids ();
        g_assert (child_iter->items);
 
        child_iter->index = 0;
        child_iter->level = iter->level + 1;
        child_iter->items = anItem->getKids ();
        g_assert (child_iter->items);
 
-       return (EvDocumentBookmarksIter *) child_iter;
+       return (EvDocumentLinksIter *) child_iter;
 }
 
 static gboolean
 }
 
 static gboolean
-pdf_document_bookmarks_next (EvDocumentBookmarks     *document_bookmarks,
-                            EvDocumentBookmarksIter *bookmarks_iter)
+pdf_document_links_next (EvDocumentLinks     *document_links,
+                        EvDocumentLinksIter *links_iter)
 {
 {
-       BookmarksIter *iter = (BookmarksIter *) bookmarks_iter;
+       LinksIter *iter = (LinksIter *) links_iter;
 
 
-       g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
+       g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
 
        iter->index++;
        if (iter->index >= iter->items->getLength())
 
        iter->index++;
        if (iter->index >= iter->items->getLength())
@@ -838,10 +910,10 @@ pdf_document_bookmarks_next (EvDocumentBookmarks     *document_bookmarks,
 }
 
 static void
 }
 
 static void
-pdf_document_bookmarks_free_iter (EvDocumentBookmarks     *document_bookmarks,
-                                 EvDocumentBookmarksIter *iter)
+pdf_document_links_free_iter (EvDocumentLinks     *document_links,
+                             EvDocumentLinksIter *iter)
 {
 {
-       g_return_if_fail (PDF_IS_DOCUMENT (document_bookmarks));
+       g_return_if_fail (PDF_IS_DOCUMENT (document_links));
        g_return_if_fail (iter != NULL);
 
        /* FIXME: Should I close all the nodes?? Free them? */
        g_return_if_fail (iter != NULL);
 
        /* FIXME: Should I close all the nodes?? Free them? */
@@ -853,6 +925,10 @@ pdf_document_finalize (GObject *object)
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (object);
 
 {
        PdfDocument *pdf_document = PDF_DOCUMENT (object);
 
+       if (pdf_document->links) {
+               delete pdf_document->links;
+       }
+
        if (pdf_document->umap) {
                pdf_document->umap->decRefCnt ();
                pdf_document->umap = NULL;
        if (pdf_document->umap) {
                pdf_document->umap->decRefCnt ();
                pdf_document->umap = NULL;
@@ -906,7 +982,7 @@ pdf_info_dict_get_string (Dict *info_dict, const gchar *key) {
 
        if (!info_dict->lookup ((gchar *)key, &obj)->isString ()) {
                obj.free ();
 
        if (!info_dict->lookup ((gchar *)key, &obj)->isString ()) {
                obj.free ();
-               return g_strdup (_("Unknown"));
+               return NULL;
        }
 
        value = obj.getString ();
        }
 
        value = obj.getString ();
@@ -939,6 +1015,52 @@ pdf_document_get_title (PdfDocument *pdf_document)
        return title;
 }
 
        return title;
 }
 
+static char *
+pdf_document_get_text (EvDocument *document, GdkRectangle *rect)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document);
+       GString *sel_text = new GString;
+       const char *text;
+       int x1, y1, x2, y2;
+
+       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 ();
+
+       return text ? g_strdup (text) : NULL;
+}
+
+static EvLink *
+pdf_document_get_link (EvDocument *document, int x, int y)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document);
+       LinkAction *action;
+       double link_x, link_y;
+
+       /* Zoom */
+       link_x = x / pdf_document->scale;
+       link_y = y / pdf_document->scale;
+
+       /* Offset */
+       link_x -= pdf_document->page_x_offset;
+       link_y -= pdf_document->page_y_offset;
+
+       /* Inverse y */
+       link_y = pdf_document->out->getBitmapHeight() - link_y;
+
+       action = pdf_document->links->find (link_x, link_y);
+       
+       if (action) {
+               return build_link_from_action (pdf_document, action, "");
+       } else {
+               return NULL;
+       }
+}
+
 static void
 pdf_document_get_property (GObject *object,
                           guint prop_id,
 static void
 pdf_document_get_property (GObject *object,
                           guint prop_id,
@@ -974,6 +1096,9 @@ static void
 pdf_document_document_iface_init (EvDocumentIface *iface)
 {
        iface->load = pdf_document_load;
 pdf_document_document_iface_init (EvDocumentIface *iface)
 {
        iface->load = pdf_document_load;
+       iface->save = pdf_document_save;
+       iface->get_text = pdf_document_get_text;
+       iface->get_link = pdf_document_get_link;
        iface->get_n_pages = pdf_document_get_n_pages;
        iface->set_page = pdf_document_set_page;
        iface->get_page = pdf_document_get_page;
        iface->get_n_pages = pdf_document_get_n_pages;
        iface->set_page = pdf_document_set_page;
        iface->get_page = pdf_document_get_page;
@@ -1001,17 +1126,78 @@ pdf_document_find_iface_init (EvDocumentFindIface *iface)
 }
 
 static void
 }
 
 static void
-pdf_document_document_bookmarks_iface_init (EvDocumentBookmarksIface *iface)
+pdf_document_security_iface_init (EvDocumentSecurityIface *iface)
 {
 {
-       iface->has_document_bookmarks = pdf_document_bookmarks_has_document_bookmarks;
-       iface->begin_read = pdf_document_bookmarks_begin_read;
-       iface->get_values = pdf_document_bookmarks_get_values;
-       iface->get_child = pdf_document_bookmarks_get_child;
-       iface->next = pdf_document_bookmarks_next;
-       iface->free_iter = pdf_document_bookmarks_free_iter;
+       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)
+{
+       iface->has_document_links = pdf_document_links_has_document_links;
+       iface->begin_read = pdf_document_links_begin_read;
+       iface->get_link = pdf_document_links_get_link;
+       iface->get_child = pdf_document_links_get_child;
+       iface->next = pdf_document_links_next;
+       iface->free_iter = pdf_document_links_free_iter;
 }
 
 /* Thumbnails */
 }
 
 /* Thumbnails */
+
+static GdkPixbuf *
+bitmap_to_pixbuf (SplashBitmap *bitmap,
+                 GdkPixbuf    *target,
+                 gint          x_offset,
+                 gint          y_offset)
+{
+       gint width;
+       gint height;
+       SplashColorPtr dataPtr;
+       int x, y;
+
+       gboolean target_has_alpha;
+       gint target_rowstride;
+       guchar *target_data;
+
+       width = bitmap->getWidth ();
+       height = bitmap->getHeight ();
+
+       if (width + x_offset > gdk_pixbuf_get_width (target))
+               width = gdk_pixbuf_get_width (target) - x_offset;
+       if (height + y_offset > gdk_pixbuf_get_height (target))
+               height = gdk_pixbuf_get_height (target) - x_offset;
+
+       target_has_alpha = gdk_pixbuf_get_has_alpha (target);
+       target_rowstride = gdk_pixbuf_get_rowstride (target);
+       target_data = gdk_pixbuf_get_pixels (target);
+
+       dataPtr = bitmap->getDataPtr ();
+
+       for (y = 0; y < height; y++) {
+               SplashRGB8 *p;
+               SplashRGB8 rgb;
+               guchar *q;
+
+               p = dataPtr.rgb8 + y * width;
+               q = target_data + ((y + y_offset) * target_rowstride + 
+                                  x_offset * (target_has_alpha?4:3));
+               for (x = 0; x < width; x++) {
+                       rgb = *p++;
+
+                       *q++ = splashRGB8R (rgb);
+                       *q++ = splashRGB8G (rgb);
+                       *q++ = splashRGB8B (rgb);
+
+                       if (target_has_alpha)
+                               q++;
+               }
+       }
+
+       return target;
+}
+
+
 static GdkPixbuf *
 pdf_document_thumbnails_get_page_pixbuf (PdfDocument *pdf_document,
                                         gdouble      scale_factor,
 static GdkPixbuf *
 pdf_document_thumbnails_get_page_pixbuf (PdfDocument *pdf_document,
                                         gdouble      scale_factor,
@@ -1019,36 +1205,60 @@ pdf_document_thumbnails_get_page_pixbuf (PdfDocument *pdf_document,
                                         gint         width,
                                         gint         height)
 {
                                         gint         width,
                                         gint         height)
 {
-       GdkPixmap *pixmap;
-       GDKSplashOutputDev *output;
+       SplashOutputDev *output;
        GdkPixbuf *pixbuf;
        GdkPixbuf *pixbuf;
+       SplashColor color;
 
 
-       pixmap = gdk_pixmap_new (pdf_document->target,
-                                width, height, -1);
+       color.rgb8 = splashMakeRGB8 (255, 255, 255);
 
 
-       output = new GDKSplashOutputDev (gdk_drawable_get_screen (pdf_document->target),
-                                        NULL, NULL);
+       output = new SplashOutputDev (splashModeRGB8, gFalse, color);
        output->startDoc (pdf_document->doc->getXRef());
        pdf_document->doc->displayPage (output,
                                        page_num + 1,
                                        72*scale_factor,
                                        72*scale_factor,
                                        0, gTrue, gFalse);
        output->startDoc (pdf_document->doc->getXRef());
        pdf_document->doc->displayPage (output,
                                        page_num + 1,
                                        72*scale_factor,
                                        72*scale_factor,
                                        0, gTrue, gFalse);
-       output->redraw (0, 0,
-                       pixmap,
-                       0, 0,
-                       width, height);
-       pixbuf = gdk_pixbuf_get_from_drawable (NULL,
-                                              pixmap,
-                                              NULL,
-                                              0, 0,
-                                              0, 0,
-                                              width, height);
-       gdk_drawable_unref (pixmap);
+
+       pixbuf = ev_document_misc_get_thumbnail_frame (output->getBitmap()->getWidth(),
+                                                      output->getBitmap()->getHeight(),
+                                                      NULL);
+       bitmap_to_pixbuf (output->getBitmap(), pixbuf, 1, 1);
        delete output;
        delete output;
+
        return pixbuf;
 }
 
        return pixbuf;
 }
 
+static void
+pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
+                                       gint                  page,
+                                       gint                  suggested_width,
+                                       gint                 *width,
+                                       gint                 *height)
+{
+       PdfDocument *pdf_document = PDF_DOCUMENT (document_thumbnails);
+       Page *the_page;
+       Object the_thumb;
+       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->getThumb (&the_thumb);
+
+       if (!(the_thumb.isNull () || the_thumb.isNone())) {
+               /* Build the thumbnail object */
+               thumb = new Thumb(pdf_document->doc->getXRef (),
+                                 &the_thumb);
+
+               *width = thumb->getWidth ();
+               *height = thumb->getHeight ();
+       } else {
+               page_ratio = the_page->getHeight () / the_page->getWidth ();
+               *width = suggested_width;
+               *height = (gint) (suggested_width * page_ratio);
+       }
+}
+
 static GdkPixbuf *
 pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
                                       gint                  page,
 static GdkPixbuf *
 pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
                                       gint                  page,
@@ -1060,11 +1270,17 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails
        Object the_thumb;
        Thumb *thumb = NULL;
        gboolean have_ethumbs = FALSE;
        Object the_thumb;
        Thumb *thumb = NULL;
        gboolean have_ethumbs = FALSE;
+       gdouble page_ratio;
+       gint dest_height;
 
        /* getPage seems to want page + 1 for some reason; */
        the_page = pdf_document->doc->getCatalog ()->getPage (page + 1);
        the_page->getThumb(&the_thumb);
 
 
        /* getPage seems to want page + 1 for some reason; */
        the_page = pdf_document->doc->getCatalog ()->getPage (page + 1);
        the_page->getThumb(&the_thumb);
 
+       page_ratio = the_page->getHeight () / the_page->getWidth ();
+       dest_height = (gint) (width * page_ratio);
+
+
        if (!(the_thumb.isNull () || the_thumb.isNone())) {
                /* Build the thumbnail object */
                thumb = new Thumb(pdf_document->doc->getXRef (),
        if (!(the_thumb.isNull () || the_thumb.isNone())) {
                /* Build the thumbnail object */
                thumb = new Thumb(pdf_document->doc->getXRef (),
@@ -1078,7 +1294,6 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails
                GdkPixbuf *tmp_pixbuf;
 
                data = thumb->getPixbufData();
                GdkPixbuf *tmp_pixbuf;
 
                data = thumb->getPixbufData();
-               /* FISME: scale the image if it's not an appropriate size */
                tmp_pixbuf = gdk_pixbuf_new_from_data (data,
                                                       GDK_COLORSPACE_RGB,
                                                       FALSE,
                tmp_pixbuf = gdk_pixbuf_new_from_data (data,
                                                       GDK_COLORSPACE_RGB,
                                                       FALSE,
@@ -1087,24 +1302,19 @@ pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails
                                                       thumb->getHeight (),
                                                       thumb->getWidth () * 3,
                                                       NULL, NULL);
                                                       thumb->getHeight (),
                                                       thumb->getWidth () * 3,
                                                       NULL, NULL);
-
-               thumbnail = tmp_pixbuf;
+               /* FIXME: do we want to check that the thumb's size isn't ridiculous?? */
+               thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
+               g_object_unref (tmp_pixbuf);
        } else {
        } else {
-               gdouble page_ratio;
                gdouble scale_factor;
                gdouble scale_factor;
-               gint dest_height;
 
 
-               page_ratio = the_page->getHeight () / the_page->getWidth ();
                scale_factor = (gdouble)width / the_page->getWidth ();
                scale_factor = (gdouble)width / the_page->getWidth ();
-               dest_height = (gint) (width * page_ratio);
 
                thumbnail = pdf_document_thumbnails_get_page_pixbuf (pdf_document,
                                                                     scale_factor,
                                                                     page,
                                                                     width,
                                                                     dest_height);
 
                thumbnail = pdf_document_thumbnails_get_page_pixbuf (pdf_document,
                                                                     scale_factor,
                                                                     page,
                                                                     width,
                                                                     dest_height);
-
-               /* FIXME: Actually get the image... */
        }
 
        return thumbnail;
        }
 
        return thumbnail;
@@ -1113,6 +1323,7 @@ static void
 pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
 {
        iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
 pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
 {
        iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
+       iface->get_dimensions = pdf_document_thumbnails_get_dimensions;
 }
 
 
 }
 
 
@@ -1125,5 +1336,6 @@ pdf_document_init (PdfDocument *pdf_document)
        pdf_document->scale = 1.;
 
        pdf_document->page_valid = FALSE;
        pdf_document->scale = 1.;
 
        pdf_document->page_valid = FALSE;
+       pdf_document->password = NULL;
 }
 
 }