]> www.fi.muni.cz Git - evince.git/blob - pdf/ev-poppler.cc
bd3151a6bc9d5e3c8b399ff7821ce351443a73fe
[evince.git] / pdf / ev-poppler.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 <math.h>
21 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <poppler.h>
24 #include <poppler-document.h>
25 #include <poppler-page.h>
26
27 #include "ev-poppler.h"
28 #include "ev-ps-exporter.h"
29 #include "ev-document-find.h"
30 #include "ev-document-misc.h"
31 #include "ev-document-links.h"
32 #include "ev-document-security.h"
33 #include "ev-document-thumbnails.h"
34
35 typedef struct {
36         PdfDocument *document;
37         char *text;
38         GList **pages;
39         guint idle;
40         int start_page;
41         int search_page;
42 } PdfDocumentSearch;
43
44 struct _PdfDocumentClass
45 {
46         GObjectClass parent_class;
47 };
48
49 struct _PdfDocument
50 {
51         GObject parent_instance;
52
53         PopplerDocument *document;
54         PopplerPSFile *ps_file;
55         gchar *password;
56
57         PdfDocumentSearch *search;
58 };
59
60 static void pdf_document_document_iface_init            (EvDocumentIface           *iface);
61 static void pdf_document_security_iface_init            (EvDocumentSecurityIface   *iface);
62 static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
63 static void pdf_document_document_links_iface_init      (EvDocumentLinksIface      *iface);
64 static void pdf_document_find_iface_init                (EvDocumentFindIface       *iface);
65 static void pdf_document_ps_exporter_iface_init         (EvPSExporterIface         *iface);
66 static void pdf_document_thumbnails_get_dimensions      (EvDocumentThumbnails      *document_thumbnails,
67                                                          gint                       page,
68                                                          gint                       size,
69                                                          gint                      *width,
70                                                          gint                      *height);
71 static EvLink * ev_link_from_action (PopplerAction *action);
72
73
74 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
75                          {
76                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
77                                                         pdf_document_document_iface_init);
78                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_SECURITY,
79                                                         pdf_document_security_iface_init);
80                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
81                                                         pdf_document_document_thumbnails_iface_init);
82                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
83                                                         pdf_document_document_links_iface_init);
84                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
85                                                         pdf_document_find_iface_init);
86                                  G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
87                                                         pdf_document_ps_exporter_iface_init);
88                          });
89
90 static void
91 pdf_document_class_init (PdfDocumentClass *klass)
92 {
93 }
94
95 static void
96 pdf_document_init (PdfDocument *pdf_document)
97 {
98         pdf_document->password = NULL;
99 }
100
101 static void
102 convert_error (GError  *poppler_error,
103                GError **error)
104 {
105         if (poppler_error == NULL)
106                 return;
107
108         if (poppler_error->domain == POPPLER_ERROR) {
109                 /* convert poppler errors into EvDocument errors */
110                 gint code = EV_DOCUMENT_ERROR_INVALID;
111                 if (poppler_error->code == POPPLER_ERROR_INVALID)
112                         code = EV_DOCUMENT_ERROR_INVALID;
113                 else if (poppler_error->code == POPPLER_ERROR_ENCRYPTED)
114                         code = EV_DOCUMENT_ERROR_ENCRYPTED;
115                         
116
117                 g_set_error (error,
118                              EV_DOCUMENT_ERROR,
119                              code,
120                              poppler_error->message,
121                              NULL);
122         } else {
123                 g_propagate_error (error, poppler_error);
124         }
125 }
126
127
128 /* EvDocument */
129 static gboolean
130 pdf_document_save (EvDocument  *document,
131                    const char  *uri,
132                    GError     **error)
133 {
134         gboolean retval;
135         GError *poppler_error = NULL;
136
137         retval = poppler_document_save (PDF_DOCUMENT (document)->document,
138                                         uri,
139                                         &poppler_error);
140         if (! retval)
141                 convert_error (poppler_error, error);
142
143         return retval;
144 }
145
146 static gboolean
147 pdf_document_load (EvDocument   *document,
148                    const char   *uri,
149                    GError      **error)
150 {
151         GError *poppler_error = NULL;
152         PdfDocument *pdf_document = PDF_DOCUMENT (document);
153
154         pdf_document->document =
155                 poppler_document_new_from_file (uri, pdf_document->password, &poppler_error);
156
157         if (pdf_document->document == NULL) {
158                 convert_error (poppler_error, error);
159                 return FALSE;
160         }
161
162         return TRUE;
163 }
164
165 static int
166 pdf_document_get_n_pages (EvDocument *document)
167 {
168         return poppler_document_get_n_pages (PDF_DOCUMENT (document)->document);
169 }
170
171 static void
172 pdf_document_get_page_size (EvDocument   *document,
173                             int           page,
174                             double       *width,
175                             double       *height)
176 {
177         PopplerPage *poppler_page;
178
179         poppler_page = poppler_document_get_page (PDF_DOCUMENT (document)->document,
180                                                   page);
181
182         poppler_page_get_size (poppler_page, width, height);
183 }
184
185 static char *
186 pdf_document_get_page_label (EvDocument *document,
187                              int         page)
188 {
189         PopplerPage *poppler_page;
190         char *label = NULL;
191
192         poppler_page = poppler_document_get_page (PDF_DOCUMENT (document)->document,
193                                                   page);
194
195         g_object_get (G_OBJECT (poppler_page),
196                       "label", &label,
197                       NULL);
198
199         return label;
200 }
201
202 static GList *
203 pdf_document_get_links (EvDocument *document,
204                         int         page)
205 {
206         PdfDocument *pdf_document;
207         PopplerPage *poppler_page;
208         GList *retval = NULL;
209         GList *mapping_list;
210         GList *list;
211         double height;
212
213         pdf_document = PDF_DOCUMENT (document);
214         poppler_page = poppler_document_get_page (pdf_document->document,
215                                                   page);
216         mapping_list = poppler_page_get_link_mapping (poppler_page);
217         poppler_page_get_size (poppler_page, NULL, &height);
218
219         for (list = mapping_list; list; list = list->next) {
220                 PopplerLinkMapping *link_mapping;
221                 EvLinkMapping *ev_link_mapping;
222
223                 link_mapping = (PopplerLinkMapping *)list->data;
224                 ev_link_mapping = g_new (EvLinkMapping, 1);
225                 ev_link_mapping->link = ev_link_from_action (link_mapping->action);
226                 ev_link_mapping->x1 = link_mapping->area.x1;
227                 ev_link_mapping->x2 = link_mapping->area.x2;
228                 /* Invert this for X-style coordinates */
229                 ev_link_mapping->y1 = height - link_mapping->area.y2;
230                 ev_link_mapping->y2 = height - link_mapping->area.y1;
231
232                 retval = g_list_prepend (retval, ev_link_mapping);
233         }
234
235         poppler_page_free_link_mapping (mapping_list);
236
237         return g_list_reverse (retval);
238 }
239                         
240
241 static GdkPixbuf *
242 pdf_document_render_pixbuf (EvDocument   *document,
243                             int           page,
244                             double        scale)
245 {
246         PdfDocument *pdf_document;
247         PopplerPage *poppler_page;
248         GdkPixbuf *pixbuf;
249         double width_points, height_points;
250         gint width, height;
251
252         pdf_document = PDF_DOCUMENT (document);
253         poppler_page = poppler_document_get_page (pdf_document->document,
254                                                   page);
255
256         poppler_page_get_size (poppler_page, &width_points, &height_points);
257         width = (int) ((width_points * scale) + 0.5);
258         height = (int) ((height_points * scale) + 0.5);
259
260         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
261                                  FALSE, 8,
262                                  width, height);
263
264         poppler_page_render_to_pixbuf (poppler_page,
265                                        0, 0,
266                                        width, height,
267                                        scale,
268                                        pixbuf,
269                                        0, 0);
270
271         return pixbuf;
272 }
273
274 /* EvDocumentSecurity */
275
276 static gboolean
277 pdf_document_has_document_security (EvDocumentSecurity *document_security)
278 {
279         /* FIXME: do we really need to have this? */
280         return FALSE;
281 }
282
283 static void
284 pdf_document_set_password (EvDocumentSecurity *document_security,
285                            const char         *password)
286 {
287         PdfDocument *document = PDF_DOCUMENT (document_security);
288
289         if (document->password)
290                 g_free (document->password);
291
292         document->password = g_strdup (password);
293 }
294
295 static gboolean
296 pdf_document_can_get_text (EvDocument *document)
297 {
298         return TRUE;
299 }
300
301 static EvDocumentInfo *
302 pdf_document_get_info (EvDocument *document)
303 {
304         EvDocumentInfo *info;
305         PopplerPageLayout layout;
306         PopplerPageMode mode;
307         PopplerViewerPreferences view_prefs;
308
309         info = g_new0 (EvDocumentInfo, 1);
310
311         info->fields_mask = EV_DOCUMENT_INFO_TITLE |
312                             EV_DOCUMENT_INFO_FORMAT |
313                             EV_DOCUMENT_INFO_AUTHOR |
314                             EV_DOCUMENT_INFO_SUBJECT |
315                             EV_DOCUMENT_INFO_KEYWORDS |
316                             EV_DOCUMENT_INFO_LAYOUT |
317                             EV_DOCUMENT_INFO_START_MODE |
318                             /* Missing EV_DOCUMENT_INFO_CREATION_DATE | */
319                             EV_DOCUMENT_INFO_UI_HINTS;
320
321
322         g_object_get (PDF_DOCUMENT (document)->document,
323                       "title", &(info->title),
324                       "format", &(info->format),
325                       "author", &(info->author),
326                       "subject", &(info->subject),
327                       "keywords", &(info->keywords),
328                       "page-mode", &mode,
329                       "page-layout", &layout,
330                       "viewer-preferences", &view_prefs,
331                       NULL);
332
333         switch (layout) {
334                 case POPPLER_PAGE_LAYOUT_SINGLE_PAGE:
335                         info->layout = EV_DOCUMENT_LAYOUT_SINGLE_PAGE;
336                         break;
337                 case POPPLER_PAGE_LAYOUT_ONE_COLUMN:
338                         info->layout = EV_DOCUMENT_LAYOUT_ONE_COLUMN;
339                         break;
340                 case POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT:
341                         info->layout = EV_DOCUMENT_LAYOUT_TWO_COLUMN_LEFT;
342                         break;
343                 case POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT:
344                         info->layout = EV_DOCUMENT_LAYOUT_TWO_COLUMN_RIGHT;
345                 case POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT:
346                         info->layout = EV_DOCUMENT_LAYOUT_TWO_PAGE_LEFT;
347                         break;
348                 case POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT:
349                         info->layout = EV_DOCUMENT_LAYOUT_TWO_PAGE_RIGHT;
350                         break;
351                 default:
352                         break;
353         }
354
355         switch (mode) {
356                 case POPPLER_PAGE_MODE_NONE:
357                         info->mode = EV_DOCUMENT_MODE_NONE;
358                         break;
359                 case POPPLER_PAGE_MODE_USE_THUMBS:
360                         info->mode = EV_DOCUMENT_MODE_USE_THUMBS;
361                         break;
362                 case POPPLER_PAGE_MODE_USE_OC:
363                         info->mode = EV_DOCUMENT_MODE_USE_OC;
364                         break;
365                 case POPPLER_PAGE_MODE_FULL_SCREEN:
366                         info->mode = EV_DOCUMENT_MODE_FULL_SCREEN;
367                         break;
368                 case POPPLER_PAGE_MODE_USE_ATTACHMENTS:
369                         info->mode = EV_DOCUMENT_MODE_USE_ATTACHMENTS;
370                 default:
371                         break;
372         }
373
374         info->ui_hints = 0;
375         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_TOOLBAR) {
376                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_TOOLBAR;
377         }
378         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_MENUBAR) {
379                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_MENUBAR;
380         }
381         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_WINDOWUI) {
382                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_WINDOWUI;
383         }
384         if (view_prefs & POPPLER_VIEWER_PREFERENCES_FIT_WINDOW) {
385                 info->ui_hints |= EV_DOCUMENT_UI_HINT_FIT_WINDOW;
386         }
387         if (view_prefs & POPPLER_VIEWER_PREFERENCES_CENTER_WINDOW) {
388                 info->ui_hints |= EV_DOCUMENT_UI_HINT_CENTER_WINDOW;
389         }
390         if (view_prefs & POPPLER_VIEWER_PREFERENCES_DISPLAY_DOC_TITLE) {
391                 info->ui_hints |= EV_DOCUMENT_UI_HINT_DISPLAY_DOC_TITLE;
392         }
393         if (view_prefs & POPPLER_VIEWER_PREFERENCES_DIRECTION_RTL) {
394                 info->ui_hints |=  EV_DOCUMENT_UI_HINT_DIRECTION_RTL;
395         }
396
397         return info;
398 }
399
400 static char *
401 pdf_document_get_text (EvDocument *document, int page, EvRectangle *rect)
402 {
403         PdfDocument *pdf_document = PDF_DOCUMENT (document);
404         PopplerPage *poppler_page;
405         PopplerRectangle r;
406         double height;
407         
408         poppler_page = poppler_document_get_page (pdf_document->document, page);
409         g_return_val_if_fail (poppler_page != NULL, NULL);
410
411         poppler_page_get_size (poppler_page, NULL, &height);
412         r.x1 = rect->x1;
413         r.y1 = height - rect->y2;
414         r.x2 = rect->x2;
415         r.y2 = height - rect->y1;
416
417         return poppler_page_get_text (poppler_page, &r);
418 }
419
420 static void
421 pdf_document_document_iface_init (EvDocumentIface *iface)
422 {
423         iface->save = pdf_document_save;
424         iface->load = pdf_document_load;
425         iface->get_n_pages = pdf_document_get_n_pages;
426         iface->get_page_size = pdf_document_get_page_size;
427         iface->get_page_label = pdf_document_get_page_label;
428         iface->get_links = pdf_document_get_links;
429         iface->render_pixbuf = pdf_document_render_pixbuf;
430         iface->get_text = pdf_document_get_text;
431         iface->can_get_text = pdf_document_can_get_text;
432         iface->get_info = pdf_document_get_info;
433 };
434
435 static void
436 pdf_document_security_iface_init (EvDocumentSecurityIface *iface)
437 {
438         iface->has_document_security = pdf_document_has_document_security;
439         iface->set_password = pdf_document_set_password;
440 }
441
442 static gboolean
443 pdf_document_links_has_document_links (EvDocumentLinks *document_links)
444 {
445         PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
446         PopplerIndexIter *iter;
447
448         g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
449
450         iter = poppler_index_iter_new (pdf_document->document);
451         if (iter == NULL)
452                 return FALSE;
453         poppler_index_iter_free (iter);
454
455         return TRUE;
456 }
457
458 static EvLink *
459 ev_link_from_action (PopplerAction *action)
460 {
461         EvLink *link;
462         const char *title;
463
464         title = action->any.title;
465         
466         if (action->type == POPPLER_ACTION_GOTO_DEST) {
467                 link = ev_link_new_page (title, action->goto_dest.dest->page_num - 1);
468         } else if (action->type == POPPLER_ACTION_URI) {
469                 link = ev_link_new_external (title, action->uri.uri);
470         } else {
471                 link = ev_link_new_title (title);
472         }
473
474         return link;    
475 }
476
477
478 static void
479 build_tree (PdfDocument      *pdf_document,
480             GtkTreeModel     *model,
481             GtkTreeIter      *parent,
482             PopplerIndexIter *iter)
483 {
484
485         do {
486                 GtkTreeIter tree_iter;
487                 PopplerIndexIter *child;
488                 PopplerAction *action;
489                 EvLink *link;
490                 
491                 action = poppler_index_iter_get_action (iter);
492                 if (action) {
493                         gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
494                         link = ev_link_from_action (action);
495                         poppler_action_free (action);
496
497                         gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
498                                             EV_DOCUMENT_LINKS_COLUMN_MARKUP, ev_link_get_title (link),
499                                             EV_DOCUMENT_LINKS_COLUMN_LINK, link,
500                                             -1);
501                         child = poppler_index_iter_get_child (iter);
502                         if (child)
503                                 build_tree (pdf_document, model, &tree_iter, child);
504                         poppler_index_iter_free (child);
505                 }
506         } while (poppler_index_iter_next (iter));
507 }
508
509
510 static GtkTreeModel *
511 pdf_document_links_get_links_model (EvDocumentLinks *document_links)
512 {
513         PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
514         GtkTreeModel *model = NULL;
515         PopplerIndexIter *iter;
516
517         g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL);
518
519         iter = poppler_index_iter_new (pdf_document->document);
520         /* Create the model iff we have items*/
521         if (iter != NULL) {
522                 model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
523                                                              G_TYPE_STRING,
524                                                              G_TYPE_POINTER);
525                 build_tree (pdf_document, model, NULL, iter);
526                 poppler_index_iter_free (iter);
527         }
528         
529
530         return model;
531 }
532
533
534 static void
535 pdf_document_document_links_iface_init (EvDocumentLinksIface *iface)
536 {
537         iface->has_document_links = pdf_document_links_has_document_links;
538         iface->get_links_model = pdf_document_links_get_links_model;
539 }
540
541
542 static GdkPixbuf *
543 make_thumbnail_for_size (PdfDocument *pdf_document,
544                          gint         page,
545                          gint         size,
546                          gboolean     border)
547 {
548         PopplerPage *poppler_page;
549         GdkPixbuf *pixbuf;
550         int width, height;
551         int x_offset, y_offset;
552         double scale;
553         gdouble unscaled_width, unscaled_height;
554
555         poppler_page = poppler_document_get_page (pdf_document->document, page);
556
557         g_return_val_if_fail (poppler_page != NULL, NULL);
558
559         pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), page, size, &width, &height);
560         poppler_page_get_size (poppler_page, &unscaled_width, &unscaled_height);
561         scale = width / unscaled_width;
562
563         if (border) {
564                 pixbuf = ev_document_misc_get_thumbnail_frame (width, height, NULL);
565                 x_offset = 1;
566                 y_offset = 1;
567         } else {
568                 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
569                                          width, height);
570                 gdk_pixbuf_fill (pixbuf, 0xffffffff);
571                 x_offset = 0;
572                 y_offset = 0;
573         }
574
575         poppler_page_render_to_pixbuf (poppler_page, 0, 0,
576                                        width, height,
577                                        scale, pixbuf,
578                                        x_offset, y_offset);
579
580         return pixbuf;
581 }
582
583 static GdkPixbuf *
584 pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
585                                        gint                  page,
586                                        gint                  size,
587                                        gboolean              border)
588 {
589         PdfDocument *pdf_document;
590         PopplerPage *poppler_page;
591         GdkPixbuf *pixbuf;
592
593         pdf_document = PDF_DOCUMENT (document_thumbnails);
594
595         poppler_page = poppler_document_get_page (pdf_document->document, page);
596         g_return_val_if_fail (poppler_page != NULL, NULL);
597
598         pixbuf = poppler_page_get_thumbnail (poppler_page);
599         if (pixbuf != NULL) {
600                 /* The document provides its own thumbnails. */
601                 if (border) {
602                         GdkPixbuf *real_pixbuf;
603
604                         real_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
605                         g_object_unref (pixbuf);
606                         pixbuf = real_pixbuf;
607                 }
608         } else {
609                 /* There is no provided thumbnail.  We need to make one. */
610                 pixbuf = make_thumbnail_for_size (pdf_document, page, size, border);
611         }
612         return pixbuf;
613 }
614
615 static void
616 pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
617                                         gint                  page,
618                                         gint                  size,
619                                         gint                 *width,
620                                         gint                 *height)
621 {
622         PdfDocument *pdf_document;
623         PopplerPage *poppler_page;
624         gint has_thumb;
625         
626         pdf_document = PDF_DOCUMENT (document_thumbnails);
627         poppler_page = poppler_document_get_page (pdf_document->document, page);
628
629         g_return_if_fail (width != NULL);
630         g_return_if_fail (height != NULL);
631         g_return_if_fail (poppler_page != NULL);
632
633         has_thumb = poppler_page_get_thumbnail_size (poppler_page, width, height);
634
635         if (!has_thumb) {
636                 double page_width, page_height;
637
638                 poppler_page_get_size (poppler_page, &page_width, &page_height);
639                 if (page_width > page_height) {
640                         *width = size;
641                         *height = (int) (size * page_height / page_width);
642                 } else {
643                         *width = (int) (size * page_width / page_height);
644                         *height = size;
645                 }
646         }
647 }
648
649 static void
650 pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
651 {
652         iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
653         iface->get_dimensions = pdf_document_thumbnails_get_dimensions;
654 }
655
656
657 static gboolean
658 pdf_document_search_idle_callback (void *data)
659 {
660         PdfDocumentSearch *search = (PdfDocumentSearch*) data;
661         PdfDocument *pdf_document = search->document;
662         int n_pages;
663         GList *matches;
664         PopplerPage *page;
665
666         page = poppler_document_get_page (search->document->document,
667                                           search->search_page);
668
669         ev_document_doc_mutex_lock ();
670         matches = poppler_page_find_text (page, search->text);
671         ev_document_doc_mutex_unlock ();
672
673         search->pages[search->search_page] = matches;
674         ev_document_find_changed (EV_DOCUMENT_FIND (pdf_document),
675                                   search->search_page);
676
677         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (search->document));
678         search->search_page += 1;
679         if (search->search_page == n_pages) {
680                 /* wrap around */
681                 search->search_page = 0;
682         }
683
684         if (search->search_page != search->start_page) {
685                 return TRUE;
686         }
687
688         /* We're done. */
689         search->idle = 0; /* will return FALSE to remove */
690         return FALSE;
691 }
692
693
694 static PdfDocumentSearch *
695 pdf_document_search_new (PdfDocument *pdf_document,
696                          int          start_page,
697                          const char  *text)
698 {
699         PdfDocumentSearch *search;
700         int n_pages;
701         int i;
702
703         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
704
705         search = g_new0 (PdfDocumentSearch, 1);
706
707         search->text = g_strdup (text);
708         search->pages = g_new0 (GList *, n_pages);
709         for (i = 0; i < n_pages; i++) {
710                 search->pages[i] = NULL;
711         }
712
713         search->document = pdf_document;
714
715         /* We add at low priority so the progress bar repaints */
716         search->idle = g_idle_add_full (G_PRIORITY_LOW,
717                                         pdf_document_search_idle_callback,
718                                         search,
719                                         NULL);
720
721         search->start_page = start_page;
722         search->search_page = start_page;
723
724         return search;
725 }
726
727 static void
728 pdf_document_search_free (PdfDocumentSearch   *search)
729 {
730         PdfDocument *pdf_document = search->document;
731         int n_pages;
732         int i;
733
734         if (search->idle != 0)
735                 g_source_remove (search->idle);
736
737         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
738         for (i = 0; i < n_pages; i++) {
739                 g_list_foreach (search->pages[i], (GFunc) g_free, NULL);
740                 g_list_free (search->pages[i]);
741         }
742         
743         g_free (search->text);
744 }
745
746 static void
747 pdf_document_find_begin (EvDocumentFind   *document,
748                          int               page,
749                          const char       *search_string,
750                          gboolean          case_sensitive)
751 {
752         PdfDocument *pdf_document = PDF_DOCUMENT (document);
753
754         /* FIXME handle case_sensitive (right now XPDF
755          * code is always case insensitive for ASCII
756          * and case sensitive for all other languaages)
757          */
758
759         if (pdf_document->search &&
760             strcmp (search_string, pdf_document->search->text) == 0)
761                 return;
762
763         if (pdf_document->search)
764                 pdf_document_search_free (pdf_document->search);
765
766         pdf_document->search = pdf_document_search_new (pdf_document,
767                                                         page,
768                                                         search_string);
769 }
770
771 int
772 pdf_document_find_get_n_results (EvDocumentFind *document_find, int page)
773 {
774         PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
775
776         if (search) {
777                 return g_list_length (search->pages[page]);
778         } else {
779                 return 0;
780         }
781 }
782
783 gboolean
784 pdf_document_find_get_result (EvDocumentFind *document_find,
785                               int             page,
786                               int             n_result,
787                               EvRectangle    *rectangle)
788 {
789         PdfDocument *pdf_document = PDF_DOCUMENT (document_find);
790         PdfDocumentSearch *search = pdf_document->search;
791         PopplerPage *poppler_page;
792         PopplerRectangle *r;
793         double height;
794
795         if (search == NULL)
796                 return FALSE;
797
798         r = (PopplerRectangle *) g_list_nth_data (search->pages[page],
799                                                   n_result);
800         if (r == NULL)
801                 return FALSE;
802
803         poppler_page = poppler_document_get_page (pdf_document->document, page);
804         poppler_page_get_size (poppler_page, NULL, &height);
805         rectangle->x1 = r->x1;
806         rectangle->y1 = height - r->y2;
807         rectangle->x2 = r->x2;
808         rectangle->y2 = height - r->y1;
809         
810         return TRUE;
811 }
812
813 int
814 pdf_document_find_page_has_results (EvDocumentFind *document_find,
815                                     int             page)
816 {
817         PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
818
819         g_return_val_if_fail (search != NULL, FALSE);
820
821         return search->pages[page] != NULL;
822 }
823
824 double
825 pdf_document_find_get_progress (EvDocumentFind *document_find)
826 {
827         PdfDocumentSearch *search;
828         int n_pages, pages_done;
829
830         search = PDF_DOCUMENT (document_find)->search;
831
832         if (search == NULL) {
833                 return 0;
834         }
835
836         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (document_find));
837         if (search->search_page > search->start_page) {
838                 pages_done = search->search_page - search->start_page + 1;
839         } else if (search->search_page == search->start_page) {
840                 pages_done = n_pages;
841         } else {
842                 pages_done = n_pages - search->start_page + search->search_page;
843         }
844
845         return pages_done / (double) n_pages;
846 }
847
848 static void
849 pdf_document_find_cancel (EvDocumentFind *document)
850 {
851         PdfDocument *pdf_document = PDF_DOCUMENT (document);
852
853         if (pdf_document->search) {
854                 pdf_document_search_free (pdf_document->search);
855                 pdf_document->search = NULL;
856         }
857 }
858
859 static void
860 pdf_document_find_iface_init (EvDocumentFindIface *iface)
861 {
862         iface->begin = pdf_document_find_begin;
863         iface->get_n_results = pdf_document_find_get_n_results;
864         iface->get_result = pdf_document_find_get_result;
865         iface->page_has_results = pdf_document_find_page_has_results;
866         iface->get_progress = pdf_document_find_get_progress;
867         iface->cancel = pdf_document_find_cancel;
868 }
869
870 static void
871 pdf_document_ps_exporter_begin (EvPSExporter *exporter, const char *filename,
872                                 int first_page, int last_page)
873 {
874         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
875         
876         pdf_document->ps_file = poppler_ps_file_new (pdf_document->document, filename,
877                                                      first_page,
878                                                      last_page - first_page + 1);
879 }
880
881 static void
882 pdf_document_ps_exporter_do_page (EvPSExporter *exporter, int page)
883 {
884         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
885         PopplerPage *poppler_page;
886
887         g_return_if_fail (pdf_document->ps_file != NULL);
888
889         poppler_page = poppler_document_get_page (pdf_document->document, page);
890         poppler_page_render_to_ps (poppler_page, pdf_document->ps_file);
891 }
892
893 static void
894 pdf_document_ps_exporter_end (EvPSExporter *exporter)
895 {
896         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
897
898         poppler_ps_file_free (pdf_document->ps_file);
899         pdf_document->ps_file = NULL;
900 }
901
902 static void
903 pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
904 {
905         iface->begin = pdf_document_ps_exporter_begin;
906         iface->do_page = pdf_document_ps_exporter_do_page;
907         iface->end = pdf_document_ps_exporter_end;
908 }
909
910 PdfDocument *
911 pdf_document_new (void)
912 {
913         return PDF_DOCUMENT (g_object_new (PDF_TYPE_DOCUMENT, NULL));
914 }