#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 "SplashBitmap.h"
#include "PDFDoc.h"
#include "Outline.h"
+#include "ErrorCodes.h"
#include "UnicodeMap.h"
#include "GlobalParams.h"
#include "GfxState.h"
GDKSplashOutputDev *out;
PSOutputDev *ps_out;
PDFDoc *doc;
+ Links *links;
UnicodeMap *umap;
+ gchar *password;
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_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_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_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)
{
72 * pdf_document->scale,
0, gTrue, gTrue);
+ document_init_links (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
*/
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;
}
pdf_document->page = page;
pdf_document->page_valid = FALSE;
}
-
- ev_document_changed (document);
}
static int
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)
{
}
-/* EvDocumentBookmarks Implementation */
+/* EvDocumentLinks Implementation */
typedef struct
{
/* goo GList, not glib */
GList *items;
int index;
int level;
-} BookmarksIter;
+} LinksIter;
static gchar *
unicode_to_char (OutlineItem *outline_item,
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;
- 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 &&
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;
- BookmarksIter *iter;
+ LinksIter *iter;
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;
- iter = g_new0 (BookmarksIter, 1);
+ iter = g_new0 (LinksIter, 1);
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;
- LinkDest *link_dest = NULL;
- LinkURI *link_uri = NULL;
- LinkGoTo *link_goto = NULL;
- GString *named_dest;
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 (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 ();
+ 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;
- 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;
- 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);
- return (EvDocumentBookmarksIter *) child_iter;
+ return (EvDocumentLinksIter *) child_iter;
}
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())
}
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? */
{
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 (!info_dict->lookup ((gchar *)key, &obj)->isString ()) {
obj.free ();
- return g_strdup (_("Unknown"));
+ return NULL;
}
value = obj.getString ();
char *title = NULL;
Object info;
+ if (pdf_document->doc == NULL)
+ return NULL;
pdf_document->doc->getDocInfo (&info);
if (info.isDict ()) {
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 ();
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;
+
+ 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 {
+ return NULL;
+ }
+}
+
static void
pdf_document_get_property (GObject *object,
guint prop_id,
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;
}
static void
-pdf_document_document_bookmarks_iface_init (EvDocumentBookmarksIface *iface)
+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)
{
- 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_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 */
+
+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,
gint width,
gint height)
{
- GdkPixmap *pixmap;
- GDKSplashOutputDev *output;
+ SplashOutputDev *output;
GdkPixbuf *pixbuf;
- GdkPixbuf *shadow;
+ 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->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;
- shadow = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
- g_object_unref (pixbuf);
+ return pixbuf;
+}
- return shadow;
+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_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
{
iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = pdf_document_thumbnails_get_dimensions;
}
pdf_document->scale = 1.;
pdf_document->page_valid = FALSE;
+ pdf_document->password = NULL;
}