]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/pdf-document.cc
ede8f77e992309d1743c399a3067f9a50c2cb905
[evince.git] / pdf / xpdf / pdf-document.cc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* pdfdocument.h: Implementation of EvDocument for PDF
3  * Copyright (C) 2004, Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <glib/gi18n.h>
21
22 #include "gpdf-g-switch.h"
23 #include "pdf-document.h"
24 #include "ev-ps-exporter.h"
25 #include "ev-document-find.h"
26 #include "gpdf-g-switch.h"
27
28 #include "GlobalParams.h"
29 #include "GDKSplashOutputDev.h"
30 #include "PDFDoc.h"
31 #include "Outline.h"
32 #include "UnicodeMap.h"
33 #include "GlobalParams.h"
34 #include "goo/GList.h"
35 #include "PSOutputDev.h"
36
37 enum {
38         PROP_0,
39         PROP_TITLE
40 };
41
42 typedef struct
43 {
44         PdfDocument *document;
45         gunichar *ucs4;
46         glong ucs4_len;
47         guint idle;
48         /* full results are only possible for the rendered current page */
49         int current_page;
50         GArray *current_page_results;
51         guchar *other_page_flags; /* length n_pages + 1, first element ignored */
52         int start_page;   /* skip this one as we iterate, since we did it first */
53         int search_page;  /* the page we're searching now */
54         TextOutputDev *output_dev;
55 } PdfDocumentSearch;
56
57 typedef struct _PdfDocumentClass PdfDocumentClass;
58
59 #define PDF_DOCUMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), PDF_TYPE_DOCUMENT, PdfDocumentClass))
60 #define PDF_IS_DOCUMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), PDF_TYPE_DOCUMENT))
61 #define PDF_DOCUMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), PDF_TYPE_DOCUMENT, PdfDocumentClass))
62
63 struct _PdfDocumentClass
64 {
65         GObjectClass parent_class;
66 };
67
68 struct _PdfDocument
69 {
70         GObject parent_instance;
71
72         int page;
73         int page_x_offset;
74         int page_y_offset;
75         double scale;
76         GdkDrawable *target;
77
78         GDKSplashOutputDev *out;
79         PSOutputDev *ps_out;
80         PDFDoc *doc;
81         UnicodeMap *umap;
82
83         gboolean page_valid;
84
85         PdfDocumentSearch *search;
86 };
87
88 static void pdf_document_document_bookmarks_iface_init (EvDocumentBookmarksIface *iface);
89 static void pdf_document_document_iface_init           (EvDocumentIface          *iface);
90 static void pdf_document_ps_exporter_iface_init (EvPSExporterIface   *iface);
91 static void pdf_document_find_iface_init        (EvDocumentFindIface *iface);
92 static void pdf_document_search_free            (PdfDocumentSearch   *search);
93 static void pdf_document_search_page_changed    (PdfDocumentSearch   *search);
94
95 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
96                          {
97                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
98                                                         pdf_document_document_iface_init);
99                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_BOOKMARKS,
100                                                         pdf_document_document_bookmarks_iface_init);
101                                  G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
102                                                         pdf_document_ps_exporter_iface_init);
103                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
104                                                         pdf_document_find_iface_init);
105                          });
106
107 static gboolean
108 document_validate_page (PdfDocument *pdf_document)
109 {
110         if (!pdf_document->page_valid) {
111                 pdf_document->doc->displayPage (pdf_document->out, pdf_document->page,
112                                                 72 * pdf_document->scale,
113                                                 72 * pdf_document->scale,
114                                                 0, gTrue, gTrue);
115
116                 pdf_document->page_valid = TRUE;
117
118                 /* Update the search results available to the app since
119                  * we only provide full results on the current page
120                  */
121                 if (pdf_document->search)
122                         pdf_document_search_page_changed (pdf_document->search);
123         }
124         
125         return pdf_document->page_valid;
126 }
127
128 static gboolean
129 pdf_document_load (EvDocument  *document,
130                    const char  *uri,
131                    GError     **error)
132 {
133         PdfDocument *pdf_document = PDF_DOCUMENT (document);
134         PDFDoc *newDoc;
135         int err;
136         char *filename;
137         GString *filename_g;
138         GString *enc; 
139
140         if (!globalParams) {
141                 globalParams = new GlobalParams("/etc/xpdfrc");
142                 globalParams->setupBaseFontsFc(NULL);
143         }
144
145         if (! pdf_document->umap) {
146                 enc = new GString("UTF-8");
147                 pdf_document->umap = globalParams->getUnicodeMap(enc);
148                 pdf_document->umap->incRefCnt (); 
149                 delete enc;
150         }
151
152         filename = g_filename_from_uri (uri, NULL, error);
153         if (!filename)
154                 return FALSE;
155
156         filename_g = new GString (filename);
157         g_free (filename);
158
159         // open the PDF file, assumes ownership of filename_g
160         newDoc = new PDFDoc(filename_g, 0, 0);
161
162         if (!newDoc->isOk()) {
163                 err = newDoc->getErrorCode();
164                 delete newDoc;
165
166                 /* FIXME: Add a real error enum to EvDocument */
167                 g_set_error (error, G_FILE_ERROR,
168                              G_FILE_ERROR_FAILED,
169                              "Failed to load document (error %d) '%s'\n",
170                              err,
171                              uri);
172
173                 return FALSE;
174         }
175
176         if (pdf_document->doc)
177                 delete pdf_document->doc;
178         pdf_document->doc = newDoc;
179
180         pdf_document->page = 1;
181
182         if (pdf_document->out)
183                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
184
185         pdf_document->page_valid = FALSE;
186
187         g_object_notify (G_OBJECT (pdf_document), "title");
188
189         return TRUE;
190 }
191
192 static int
193 pdf_document_get_n_pages (EvDocument  *document)
194 {
195         PdfDocument *pdf_document = PDF_DOCUMENT (document);
196
197         if (pdf_document->doc)
198                 return pdf_document->doc->getNumPages();
199         else
200                 return 1;
201 }
202
203 static void
204 pdf_document_set_page (EvDocument  *document,
205                        int          page)
206 {
207         PdfDocument *pdf_document = PDF_DOCUMENT (document);
208
209         page = CLAMP (page, 1, ev_document_get_n_pages (document));
210
211         if (page != pdf_document->page) {
212                 pdf_document->page = page;
213                 pdf_document->page_valid = FALSE;
214         }
215
216         ev_document_changed (document);
217 }
218
219 static int
220 pdf_document_get_page (EvDocument  *document)
221 {
222         PdfDocument *pdf_document = PDF_DOCUMENT (document);
223
224         return pdf_document->page;
225 }
226
227 static void
228 redraw_callback (void *data)
229 {
230         /* Need to hook up through a EvDocument callback? */
231 }
232
233 static void
234 pdf_document_set_target (EvDocument  *document,
235                          GdkDrawable *target)
236 {
237         PdfDocument *pdf_document = PDF_DOCUMENT (document);
238
239         if (pdf_document->target != target) {
240                 if (pdf_document->target)
241                         g_object_unref (pdf_document->target);
242
243                 pdf_document->target = target;
244
245                 if (pdf_document->target)
246                         g_object_ref (pdf_document->target);
247
248                 if (pdf_document->out) {
249                         delete pdf_document->out;
250                         pdf_document->out = NULL;
251                 }
252
253                 if (pdf_document->target) {
254                         pdf_document->out = new GDKSplashOutputDev (gdk_drawable_get_screen (pdf_document->target),
255                                                          redraw_callback, (void*) document);
256
257                         if (pdf_document->doc)
258                                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
259
260                 }
261
262                 pdf_document->page_valid = FALSE;
263         }
264 }
265
266 static void
267 pdf_document_set_scale (EvDocument  *document,
268                         double       scale)
269 {
270         PdfDocument *pdf_document = PDF_DOCUMENT (document);
271
272         if (pdf_document->scale != scale) {
273                 pdf_document->scale = scale;
274                 pdf_document->page_valid = FALSE;
275         }
276 }
277
278 static void
279 pdf_document_set_page_offset (EvDocument  *document,
280                               int          x,
281                               int          y)
282 {
283         PdfDocument *pdf_document = PDF_DOCUMENT (document);
284
285         pdf_document->page_x_offset = x;
286         pdf_document->page_y_offset = y;
287 }
288
289 static void
290 pdf_document_get_page_size (EvDocument   *document,
291                             int          *width,
292                             int          *height)
293 {
294         PdfDocument *pdf_document = PDF_DOCUMENT (document);
295
296         if (document_validate_page (pdf_document)) {
297                 if (width)
298                         *width = pdf_document->out->getBitmapWidth();
299                 if (height)
300                         *height = pdf_document->out->getBitmapHeight();
301         } else {
302                 if (width)
303                         *width = 1;
304                 if (height)
305                         *height = 1;
306         }
307 }
308
309 static void
310 pdf_document_render (EvDocument  *document,
311                      int          clip_x,
312                      int          clip_y,
313                      int          clip_width,
314                      int          clip_height)
315 {
316         PdfDocument *pdf_document = PDF_DOCUMENT (document);
317         GdkRectangle page;
318         GdkRectangle draw;
319
320         if (!document_validate_page (pdf_document) || !pdf_document->target)
321                 return;
322
323         page.x = pdf_document->page_x_offset;
324         page.y = pdf_document->page_y_offset;
325         page.width = pdf_document->out->getBitmapWidth();
326         page.height = pdf_document->out->getBitmapHeight();
327
328         draw.x = clip_x;
329         draw.y = clip_y;
330         draw.width = clip_width;
331         draw.height = clip_height;
332
333         if (gdk_rectangle_intersect (&page, &draw, &draw))
334                 pdf_document->out->redraw (draw.x - page.x, draw.y - page.y,
335                                            pdf_document->target,
336                                            draw.x, draw.y,
337                                            draw.width, draw.height);
338 }
339
340 static void
341 pdf_document_search_emit_found (PdfDocumentSearch *search)
342 {
343         PdfDocument *pdf_document = search->document;
344         int n_pages;
345         int pages_done;
346         GArray *tmp_results;
347         int i;
348
349         n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
350         if (search->search_page > search->start_page) {
351                 pages_done = search->search_page - search->start_page;
352         } else if (search->search_page == search->start_page) {
353                 pages_done = n_pages;
354         } else {
355                 pages_done = n_pages - search->start_page + search->search_page;
356         }
357
358         tmp_results = g_array_new (FALSE, FALSE, sizeof (EvFindResult));
359         g_array_append_vals (tmp_results,
360                              search->current_page_results->data,
361                              search->current_page_results->len);
362
363         /* Now append a bogus element for each page that has a result in it,
364          * that is not the current page
365          */
366         i = 1;
367         while (i <= n_pages) {
368                 if (i != pdf_document->page &&
369                     search->other_page_flags[i]) {
370                         EvFindResult result;
371                         
372                         result.page_num = i;
373                         
374                         /* Use bogus coordinates, again we can't get coordinates
375                          * until this is the current page because TextOutputDev
376                          * isn't good enough
377                          */
378                         result.highlight_area.x = -1;
379                         result.highlight_area.y = -1;
380                         result.highlight_area.width = 1;
381                         result.highlight_area.height = 1;
382                         
383                         g_array_append_val (tmp_results, result);
384                 }
385
386                 ++i;
387         }
388         
389         ev_document_find_found (EV_DOCUMENT_FIND (pdf_document),
390                                 (EvFindResult*) tmp_results->data,
391                                 tmp_results->len,
392                                 pages_done / (double) n_pages);
393         
394         g_array_free (tmp_results, TRUE);
395 }
396
397 static void
398 pdf_document_search_page_changed (PdfDocumentSearch   *search)
399 {
400         PdfDocument *pdf_document = search->document;
401         int current_page;
402         EvFindResult result;
403         int xMin, yMin, xMax, yMax;
404
405         current_page = pdf_document->page;
406
407         if (!pdf_document->page_valid) {
408                 /* we can't do anything until displayPage() */
409                 search->current_page = -1;
410                 return;
411         }
412         
413         if (search->current_page == current_page)
414                 return;
415         
416         /* We need to create current_page_results for the new current page */
417         g_array_set_size (search->current_page_results, 0);
418         
419         if (pdf_document->out->findText (search->ucs4, search->ucs4_len,
420                                          gTrue, gTrue, // startAtTop, stopAtBottom
421                                          gFalse, gFalse, // startAtLast, stopAtLast
422                                          &xMin, &yMin, &xMax, &yMax)) {
423                 result.page_num = pdf_document->page;
424
425                 result.highlight_area.x = xMin;
426                 result.highlight_area.y = yMin;
427                 result.highlight_area.width = xMax - xMin;
428                 result.highlight_area.height = yMax - yMin;
429
430                 g_array_append_val (search->current_page_results, result);
431                 /* Now find further results */
432
433                 while (pdf_document->out->findText (search->ucs4, search->ucs4_len,
434                                                     gFalse, gTrue,
435                                                     gTrue, gFalse,
436                                                     &xMin, &yMin, &xMax, &yMax)) {
437
438                         result.page_num = pdf_document->page;
439
440                         result.highlight_area.x = xMin;
441                         result.highlight_area.y = yMin;
442                         result.highlight_area.width = xMax - xMin;
443                         result.highlight_area.height = yMax - yMin;
444                         
445                         g_array_append_val (search->current_page_results, result);
446                 }
447         }
448
449         /* needed for the initial current page since we don't search
450          * it in the idle
451          */
452         search->other_page_flags[current_page] =
453                 search->current_page_results->len > 0;
454         
455         pdf_document_search_emit_found (search);
456 }
457
458 static gboolean
459 pdf_document_search_idle_callback (void *data)
460 {
461         PdfDocumentSearch *search = (PdfDocumentSearch*) data;
462         PdfDocument *pdf_document = search->document;
463         int n_pages;
464         double xMin, yMin, xMax, yMax;
465
466         /* Note that PDF page count is 1 through n_pages INCLUSIVE
467          * like a real book. We are looking to add one result for each
468          * page with a match, because the coordinates are meaningless
469          * with TextOutputDev, so we just want to flag matching pages
470          * and then when the user switches to the current page, we
471          * will emit "found" again with the real results.
472          */
473         n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
474
475         if (search->search_page == search->start_page) {
476                 goto end_search;
477         }
478
479         if (search->output_dev == 0) {
480                 /* First time through here... */
481                 search->output_dev = new TextOutputDev (NULL, gTrue, gFalse, gFalse);
482                 if (!search->output_dev->isOk()) {
483                         goto end_search;
484                 }
485         }
486                                                   
487         pdf_document->doc->displayPage (search->output_dev,
488                                         search->search_page,
489                                         72, 72, 0, gTrue, gFalse);
490
491         if (search->output_dev->findText (search->ucs4,
492                                           search->ucs4_len,
493                                           gTrue, gTrue, // startAtTop, stopAtBottom
494                                           gFalse, gFalse, // startAtLast, stopAtLast
495                                           &xMin, &yMin, &xMax, &yMax)) {
496                 /* This page has results */
497                 search->other_page_flags[search->search_page] = TRUE;
498         }
499         
500         search->search_page += 1;
501         if (search->search_page > n_pages) {
502                 /* wrap around */
503                 search->search_page = 1;
504         }
505
506         /* We do this even if nothing was found, to update the percent complete */
507         pdf_document_search_emit_found (search);
508         
509         return TRUE;
510
511  end_search:
512         /* We're done. */
513         search->idle = 0; /* will return FALSE to remove */
514         return FALSE;
515 }
516
517 static void
518 pdf_document_find_begin (EvDocumentFind   *document,
519                          const char       *search_string,
520                          gboolean          case_sensitive)
521 {
522         PdfDocument *pdf_document = PDF_DOCUMENT (document);
523         PdfDocumentSearch *search;
524         int n_pages;
525         gunichar *ucs4;
526         glong ucs4_len;
527
528         /* FIXME handle case_sensitive (right now XPDF
529          * code is always case insensitive for ASCII
530          * and case sensitive for all other languaages)
531          */
532         
533         g_assert (sizeof (gunichar) == sizeof (Unicode));
534         ucs4 = g_utf8_to_ucs4_fast (search_string, -1,
535                                     &ucs4_len);
536
537         if (pdf_document->search &&
538             pdf_document->search->ucs4_len == ucs4_len &&
539             memcmp (pdf_document->search->ucs4,
540                     ucs4,
541                     sizeof (gunichar) * ucs4_len) == 0) {
542                 /* Search is unchanged */
543                 g_free (ucs4);
544                 return;
545         }
546
547         if (pdf_document->search) {
548                 pdf_document_search_free (pdf_document->search);
549                 pdf_document->search = NULL;
550         }
551         
552         search = g_new0 (PdfDocumentSearch, 1);
553
554         search->ucs4 = ucs4;
555         search->ucs4_len = ucs4_len;
556         
557         search->current_page_results = g_array_new (FALSE,
558                                                     FALSE,
559                                                     sizeof (EvFindResult));
560         n_pages = ev_document_get_n_pages (EV_DOCUMENT (document)); 
561
562         /* This is an array of bool; with the first value ignored
563          * so we can index by the based-at-1 page numbers
564          */
565         search->other_page_flags = g_new0 (guchar, n_pages + 1);
566         
567         search->document = pdf_document;
568
569         /* We add at low priority so the progress bar repaints */
570         search->idle = g_idle_add_full (G_PRIORITY_LOW,
571                                         pdf_document_search_idle_callback,
572                                         search,
573                                         NULL);
574
575         search->output_dev = 0;
576
577         search->start_page = pdf_document->page;
578         search->search_page = search->start_page + 1;
579         if (search->search_page > n_pages)
580                 search->search_page = 1;
581
582         search->current_page = -1;
583
584         pdf_document->search = search;
585         
586         /* Update for the current page right away */
587         pdf_document_search_page_changed (search);
588 }
589
590 static void
591 pdf_document_find_cancel (EvDocumentFind   *document)
592 {
593         PdfDocument *pdf_document = PDF_DOCUMENT (document);
594
595         if (pdf_document->search) {
596                 pdf_document_search_free (pdf_document->search);
597                 pdf_document->search = NULL;
598         }
599 }
600
601 static void
602 pdf_document_search_free (PdfDocumentSearch   *search)
603 {
604         if (search->idle != 0)
605                 g_source_remove (search->idle);
606
607         if (search->output_dev)
608                 delete search->output_dev;
609         
610         g_array_free (search->current_page_results, TRUE);
611         g_free (search->other_page_flags);
612         
613         g_free (search->ucs4);
614         g_free (search);
615 }
616
617 static void
618 pdf_document_ps_export_begin (EvPSExporter *exporter, const char *filename)
619 {
620         PdfDocument *document = PDF_DOCUMENT (exporter);
621
622         if (document->ps_out)
623                 delete document->ps_out;
624
625         document->ps_out = new PSOutputDev ((char *)filename, document->doc->getXRef(),
626                                             document->doc->getCatalog(), 1,
627                                             ev_document_get_n_pages (EV_DOCUMENT (document)),
628                                             psModePS);  
629 }
630
631 static void
632 pdf_document_ps_export_do_page (EvPSExporter *exporter, int page)
633 {
634         PdfDocument *document = PDF_DOCUMENT (exporter);
635
636         document->doc->displayPage (document->ps_out, page,
637                                     72.0, 72.0, 0, gTrue, gFalse);
638 }
639
640 static void
641 pdf_document_ps_export_end (EvPSExporter *exporter)
642 {
643         PdfDocument *document = PDF_DOCUMENT (exporter);
644
645         delete document->ps_out;
646         document->ps_out = NULL;
647 }
648
649
650 /* EvDocumentBookmarks Implementation */
651 typedef struct
652 {
653         /* goo GList, not glib */
654         GList *items;
655         int index;
656         int level;
657 } BookmarksIter;
658
659 static gchar *
660 unicode_to_char (OutlineItem *outline_item, 
661                  UnicodeMap *uMap)
662 {
663         GString gstr;
664         gchar buf[8]; /* 8 is enough for mapping an unicode char to a string */
665         int i, n;
666         
667         for (i = 0; i < outline_item->getTitleLength(); ++i) {
668                 n = uMap->mapUnicode(outline_item->getTitle()[i], buf, sizeof(buf));
669                 gstr.append(buf, n);
670         }
671
672         return g_strdup (gstr.getCString ());
673 }
674
675
676 static gboolean
677 pdf_document_bookmarks_has_document_bookmarks (EvDocumentBookmarks *document_bookmarks)
678 {
679         PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
680         Outline *outline;
681
682         g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
683
684         outline = pdf_document->doc->getOutline();
685         if (outline->getItems() != NULL &&
686             outline->getItems()->getLength() > 0)
687                 return TRUE;
688
689         return FALSE;
690 }
691
692 static EvDocumentBookmarksIter *
693 pdf_document_bookmarks_begin_read (EvDocumentBookmarks *document_bookmarks)
694 {
695         PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
696         Outline *outline;
697         BookmarksIter *iter;
698         GList *items;
699
700         g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), NULL);
701
702         outline = pdf_document->doc->getOutline();
703         items = outline->getItems();
704         if (! items)
705                 return NULL;
706
707         iter = g_new0 (BookmarksIter, 1);
708         iter->items = items;
709         iter->index = 0;
710         iter->level = 0;
711
712         return (EvDocumentBookmarksIter *) iter;
713 }
714
715 static gboolean
716 pdf_document_bookmarks_get_values (EvDocumentBookmarks      *document_bookmarks,
717                                    EvDocumentBookmarksIter  *bookmarks_iter,
718                                    char                    **title,
719                                    EvDocumentBookmarksType  *type,
720                                    gint                     *page)
721 {
722         PdfDocument *pdf_document = PDF_DOCUMENT (document_bookmarks);
723         BookmarksIter *iter = (BookmarksIter *)bookmarks_iter;
724         OutlineItem *anItem;
725         LinkAction *link_action;
726         LinkDest *link_dest = NULL;
727         LinkURI *link_uri = NULL;
728         LinkGoTo *link_goto = NULL;
729         GString *named_dest; 
730         Unicode *link_title;
731         Ref page_ref;
732         gint page_num = -1;
733
734         g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
735         g_return_val_if_fail (iter != NULL, FALSE);
736         g_return_val_if_fail (title != NULL, FALSE);
737         g_return_val_if_fail (type != NULL, FALSE);
738         g_return_val_if_fail (page != NULL, FALSE);
739
740         anItem = (OutlineItem *)iter->items->get(iter->index);
741         link_action = anItem->getAction ();
742         link_title = anItem->getTitle (); 
743
744         if (link_action) {
745                 switch (link_action->getKind ()) {
746
747                 case actionGoTo:
748                         link_goto = dynamic_cast <LinkGoTo *> (link_action);
749                         link_dest = link_goto->getDest ();
750                         named_dest = link_goto->getNamedDest ();
751
752                         /* Wow!  This seems excessively slow on large
753                          * documents. I need to investigate more... -jrb */
754                         if (link_dest != NULL) {
755                                 link_dest = link_dest->copy ();
756                         } else if (named_dest != NULL) {
757                                 named_dest = named_dest->copy ();
758                                 link_dest = pdf_document->doc->findDest (named_dest);
759                                 delete named_dest;
760                         }
761                         if (link_dest != NULL) {
762                                 if (link_dest->isPageRef ()) {
763                                         page_ref = link_dest->getPageRef ();
764                                         page_num = pdf_document->doc->findPage (page_ref.num, page_ref.gen);
765                                 } else {
766                                         page_num = link_dest->getPageNum ();
767                                 }
768                                 
769                                 delete link_dest;
770                         }
771                                 
772                         break;
773                 case actionURI:
774                         link_uri = dynamic_cast <LinkURI *> (link_action);
775                         break;
776                         
777                 case actionNamed:
778                         /*Skip, for now */
779                 default:
780                         g_warning ("Unknown link action type: %d", link_action->getKind ());
781                 }
782
783                 *title = g_strdup (unicode_to_char (anItem, pdf_document->umap));
784         } else if (link_title) {
785                 *title = g_strdup (unicode_to_char (anItem, pdf_document->umap));
786         }
787
788         *type = EV_DOCUMENT_BOOKMARKS_TYPE_LINK;
789         *page = page_num;
790
791         return TRUE;
792 }
793
794 static EvDocumentBookmarksIter *
795 pdf_document_bookmarks_get_child (EvDocumentBookmarks     *document_bookmarks,
796                                   EvDocumentBookmarksIter *bookmarks_iter)
797 {
798         BookmarksIter *iter = (BookmarksIter *)bookmarks_iter;
799         BookmarksIter *child_iter;
800         OutlineItem *anItem;
801
802         g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
803
804         anItem = (OutlineItem *)iter->items->get(iter->index);
805         anItem->open ();
806         if (! (anItem->hasKids() && anItem->getKids()) )
807                 return NULL;
808
809         child_iter = g_new0 (BookmarksIter, 1);
810         child_iter->index = 0;
811         child_iter->level = iter->level + 1;
812         child_iter->items = anItem->getKids ();
813         g_assert (child_iter->items);
814
815         return (EvDocumentBookmarksIter *) child_iter;
816 }
817
818 static gboolean
819 pdf_document_bookmarks_next (EvDocumentBookmarks     *document_bookmarks,
820                              EvDocumentBookmarksIter *bookmarks_iter)
821 {
822         BookmarksIter *iter = (BookmarksIter *) bookmarks_iter;
823
824         g_return_val_if_fail (PDF_IS_DOCUMENT (document_bookmarks), FALSE);
825
826         iter->index++;
827         if (iter->index >= iter->items->getLength())
828                 return FALSE;
829
830         return TRUE;
831 }
832
833 static void
834 pdf_document_bookmarks_free_iter (EvDocumentBookmarks     *document_bookmarks,
835                                   EvDocumentBookmarksIter *iter)
836 {
837         g_return_if_fail (PDF_IS_DOCUMENT (document_bookmarks));
838         g_return_if_fail (iter != NULL);
839
840         /* FIXME: Should I close all the nodes?? Free them? */
841         g_free (iter);
842 }
843
844 static void
845 pdf_document_finalize (GObject *object)
846 {
847         PdfDocument *pdf_document = PDF_DOCUMENT (object);
848
849         if (pdf_document->umap) {
850                 pdf_document->umap->decRefCnt ();
851                 pdf_document->umap = NULL;
852         }
853
854         if (pdf_document->search)
855                 pdf_document_search_free (pdf_document->search);
856         
857         if (pdf_document->target)
858                 g_object_unref (pdf_document->target);
859
860         if (pdf_document->out)
861                 delete pdf_document->out;
862         if (pdf_document->ps_out)
863                 delete pdf_document->ps_out;
864         if (pdf_document->doc)
865                 delete pdf_document->doc;
866
867 }
868
869 static void
870 pdf_document_set_property (GObject *object,
871                            guint prop_id,
872                            const GValue *value,
873                            GParamSpec *pspec)
874 {
875         switch (prop_id)
876
877         {
878                 case PROP_TITLE:
879                         /* read only */
880                         break;
881         }
882 }
883
884 static gboolean
885 has_unicode_marker (GString *string)
886 {
887         return ((string->getChar (0) & 0xff) == 0xfe &&
888                 (string->getChar (1) & 0xff) == 0xff);
889 }
890
891 static gchar *
892 pdf_info_dict_get_string (Dict *info_dict, const gchar *key) {
893         Object obj;
894         GString *value;
895         gchar *result;
896
897         g_return_val_if_fail (info_dict != NULL, NULL);
898         g_return_val_if_fail (key != NULL, NULL);
899
900         if (!info_dict->lookup ((gchar *)key, &obj)->isString ()) {
901                 obj.free ();
902                 return g_strdup (_("Unknown"));
903         }
904
905         value = obj.getString ();
906
907         if (has_unicode_marker (value)) {
908                 result = g_convert (value->getCString () + 2,
909                                     value->getLength () - 2,
910                                     "UTF-8", "UTF-16BE", NULL, NULL, NULL);
911         } else {
912                 result = g_strndup (value->getCString (), value->getLength ());
913         }
914
915         obj.free ();
916
917         return result;
918 }
919
920 static char *
921 pdf_document_get_title (PdfDocument *pdf_document)
922 {
923         char *title;
924         Object info;
925
926         pdf_document->doc->getDocInfo (&info);
927
928         if (info.isDict ()) {
929                 title = pdf_info_dict_get_string (info.getDict(), "Title");
930         }
931 }
932
933 static void
934 pdf_document_get_property (GObject *object,
935                            guint prop_id,
936                            GValue *value,
937                            GParamSpec *pspec)
938 {
939         PdfDocument *pdf_document = PDF_DOCUMENT (object);
940         char *title;
941
942         switch (prop_id)
943         {
944                 case PROP_TITLE:
945                         title = pdf_document_get_title (pdf_document);  
946                         g_value_set_string (value, title);
947                         g_free (title);
948                         break;
949         }
950 }
951
952 static void
953 pdf_document_class_init (PdfDocumentClass *klass)
954 {
955         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
956
957         gobject_class->finalize = pdf_document_finalize;
958         gobject_class->get_property = pdf_document_get_property;
959         gobject_class->set_property = pdf_document_set_property;
960
961         g_object_class_override_property (gobject_class, PROP_TITLE, "title");
962 }
963
964 static void
965 pdf_document_document_iface_init (EvDocumentIface *iface)
966 {
967         iface->load = pdf_document_load;
968         iface->get_n_pages = pdf_document_get_n_pages;
969         iface->set_page = pdf_document_set_page;
970         iface->get_page = pdf_document_get_page;
971         iface->set_scale = pdf_document_set_scale;
972         iface->set_target = pdf_document_set_target;
973         iface->set_page_offset = pdf_document_set_page_offset;
974         iface->get_page_size = pdf_document_get_page_size;
975         iface->render = pdf_document_render;
976 }
977
978 static void
979 pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
980 {
981         iface->begin = pdf_document_ps_export_begin;
982         iface->do_page = pdf_document_ps_export_do_page;
983         iface->end = pdf_document_ps_export_end;
984 }
985
986
987 static void
988 pdf_document_find_iface_init (EvDocumentFindIface *iface)
989 {
990         iface->begin = pdf_document_find_begin;
991         iface->cancel = pdf_document_find_cancel;
992 }
993
994 static void
995 pdf_document_document_bookmarks_iface_init (EvDocumentBookmarksIface *iface)
996 {
997         iface->has_document_bookmarks = pdf_document_bookmarks_has_document_bookmarks;
998         iface->begin_read = pdf_document_bookmarks_begin_read;
999         iface->get_values = pdf_document_bookmarks_get_values;
1000         iface->get_child = pdf_document_bookmarks_get_child;
1001         iface->next = pdf_document_bookmarks_next;
1002         iface->free_iter = pdf_document_bookmarks_free_iter;
1003 }
1004
1005
1006 static void
1007 pdf_document_init (PdfDocument *pdf_document)
1008 {
1009         pdf_document->page = 1;
1010         pdf_document->page_x_offset = 0;
1011         pdf_document->page_y_offset = 0;
1012         pdf_document->scale = 1.;
1013
1014         pdf_document->page_valid = FALSE;
1015 }
1016