]> www.fi.muni.cz Git - evince.git/blob - pdf/ev-poppler.cc
Updated Canadian English translation.
[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 (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) ceil (width_points * scale);
258         height = (int) ceil (height_points * scale);
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         }
352
353         switch (mode) {
354                 case POPPLER_PAGE_MODE_NONE:
355                         info->mode = EV_DOCUMENT_MODE_NONE;
356                         break;
357                 case POPPLER_PAGE_MODE_USE_THUMBS:
358                         info->mode = EV_DOCUMENT_MODE_USE_THUMBS;
359                         break;
360                 case POPPLER_PAGE_MODE_USE_OC:
361                         info->mode = EV_DOCUMENT_MODE_USE_OC;
362                         break;
363                 case POPPLER_PAGE_MODE_FULL_SCREEN:
364                         info->mode = EV_DOCUMENT_MODE_FULL_SCREEN;
365                         break;
366                 case POPPLER_PAGE_MODE_USE_ATTACHMENTS:
367                         info->mode = EV_DOCUMENT_MODE_USE_ATTACHMENTS;
368         }
369
370         info->ui_hints = 0;
371         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_TOOLBAR) {
372                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_TOOLBAR;
373         }
374         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_MENUBAR) {
375                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_MENUBAR;
376         }
377         if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_WINDOWUI) {
378                 info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_WINDOWUI;
379         }
380         if (view_prefs & POPPLER_VIEWER_PREFERENCES_FIT_WINDOW) {
381                 info->ui_hints |= EV_DOCUMENT_UI_HINT_FIT_WINDOW;
382         }
383         if (view_prefs & POPPLER_VIEWER_PREFERENCES_CENTER_WINDOW) {
384                 info->ui_hints |= EV_DOCUMENT_UI_HINT_CENTER_WINDOW;
385         }
386         if (view_prefs & POPPLER_VIEWER_PREFERENCES_DISPLAY_DOC_TITLE) {
387                 info->ui_hints |= EV_DOCUMENT_UI_HINT_DISPLAY_DOC_TITLE;
388         }
389         if (view_prefs & POPPLER_VIEWER_PREFERENCES_DIRECTION_RTL) {
390                 info->ui_hints |=  EV_DOCUMENT_UI_HINT_DIRECTION_RTL;
391         }
392
393         return info;
394 }
395
396 static char *
397 pdf_document_get_text (EvDocument *document, int page, EvRectangle *rect)
398 {
399         PdfDocument *pdf_document = PDF_DOCUMENT (document);
400         PopplerPage *poppler_page;
401         PopplerRectangle r;
402         double height;
403         
404         poppler_page = poppler_document_get_page (pdf_document->document, page);
405         g_return_val_if_fail (poppler_page != NULL, NULL);
406
407         poppler_page_get_size (poppler_page, NULL, &height);
408         r.x1 = rect->x1;
409         r.y1 = height - rect->y2;
410         r.x2 = rect->x2;
411         r.y2 = height - rect->y1;
412
413         return poppler_page_get_text (poppler_page, &r);
414 }
415
416 static void
417 pdf_document_document_iface_init (EvDocumentIface *iface)
418 {
419         iface->save = pdf_document_save;
420         iface->load = pdf_document_load;
421         iface->get_n_pages = pdf_document_get_n_pages;
422         iface->get_page_size = pdf_document_get_page_size;
423         iface->get_page_label = pdf_document_get_page_label;
424         iface->get_links = pdf_document_get_links;
425         iface->render_pixbuf = pdf_document_render_pixbuf;
426         iface->get_text = pdf_document_get_text;
427         iface->can_get_text = pdf_document_can_get_text;
428         iface->get_info = pdf_document_get_info;
429 };
430
431 static void
432 pdf_document_security_iface_init (EvDocumentSecurityIface *iface)
433 {
434         iface->has_document_security = pdf_document_has_document_security;
435         iface->set_password = pdf_document_set_password;
436 }
437
438 static gboolean
439 pdf_document_links_has_document_links (EvDocumentLinks *document_links)
440 {
441         PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
442         PopplerIndexIter *iter;
443
444         g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
445
446         iter = poppler_index_iter_new (pdf_document->document);
447         if (iter == NULL)
448                 return FALSE;
449         poppler_index_iter_free (iter);
450
451         return TRUE;
452 }
453
454 static EvLink *
455 ev_link_from_action (PopplerAction *action)
456 {
457         EvLink *link;
458         const char *title;
459
460         title = action->any.title;
461         
462         if (action->type == POPPLER_ACTION_GOTO_DEST) {
463                 link = ev_link_new_page (title, action->goto_dest.dest->page_num - 1);
464         } else if (action->type == POPPLER_ACTION_URI) {
465                 link = ev_link_new_external (title, action->uri.uri);
466         } else {
467                 link = ev_link_new_title (title);
468         }
469
470         return link;    
471 }
472
473
474 static void
475 build_tree (PdfDocument      *pdf_document,
476             GtkTreeModel     *model,
477             GtkTreeIter      *parent,
478             PopplerIndexIter *iter)
479 {
480
481         do {
482                 GtkTreeIter tree_iter;
483                 PopplerIndexIter *child;
484                 PopplerAction *action;
485                 EvLink *link;
486                 
487                 action = poppler_index_iter_get_action (iter);
488                 if (action) {
489                         gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
490                         link = ev_link_from_action (action);
491                         poppler_action_free (action);
492
493                         gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
494                                             EV_DOCUMENT_LINKS_COLUMN_MARKUP, ev_link_get_title (link),
495                                             EV_DOCUMENT_LINKS_COLUMN_LINK, link,
496                                             -1);
497                         child = poppler_index_iter_get_child (iter);
498                         if (child)
499                                 build_tree (pdf_document, model, &tree_iter, child);
500                         poppler_index_iter_free (child);
501                 }
502         } while (poppler_index_iter_next (iter));
503 }
504
505
506 static GtkTreeModel *
507 pdf_document_links_get_links_model (EvDocumentLinks *document_links)
508 {
509         PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
510         GtkTreeModel *model = NULL;
511         PopplerIndexIter *iter;
512
513         g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL);
514
515         iter = poppler_index_iter_new (pdf_document->document);
516         /* Create the model iff we have items*/
517         if (iter != NULL) {
518                 model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
519                                                              G_TYPE_STRING,
520                                                              G_TYPE_POINTER);
521                 build_tree (pdf_document, model, NULL, iter);
522                 poppler_index_iter_free (iter);
523         }
524         
525
526         return model;
527 }
528
529
530 static void
531 pdf_document_document_links_iface_init (EvDocumentLinksIface *iface)
532 {
533         iface->has_document_links = pdf_document_links_has_document_links;
534         iface->get_links_model = pdf_document_links_get_links_model;
535 }
536
537
538 static GdkPixbuf *
539 make_thumbnail_for_size (PdfDocument *pdf_document,
540                          gint         page,
541                          gint         size,
542                          gboolean     border)
543 {
544         PopplerPage *poppler_page;
545         GdkPixbuf *pixbuf;
546         int width, height;
547         int x_offset, y_offset;
548         double scale;
549         gdouble unscaled_width, unscaled_height;
550
551         poppler_page = poppler_document_get_page (pdf_document->document, page);
552
553         g_return_val_if_fail (poppler_page != NULL, NULL);
554
555         pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), page, size, &width, &height);
556         poppler_page_get_size (poppler_page, &unscaled_width, &unscaled_height);
557         scale = width / unscaled_width;
558
559         if (border) {
560                 pixbuf = ev_document_misc_get_thumbnail_frame (width, height, NULL);
561                 x_offset = 1;
562                 y_offset = 1;
563         } else {
564                 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
565                                          width, height);
566                 gdk_pixbuf_fill (pixbuf, 0xffffffff);
567                 x_offset = 0;
568                 y_offset = 0;
569         }
570
571         poppler_page_render_to_pixbuf (poppler_page, 0, 0,
572                                        width, height,
573                                        scale, pixbuf,
574                                        x_offset, y_offset);
575
576         return pixbuf;
577 }
578
579 static GdkPixbuf *
580 pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
581                                        gint                  page,
582                                        gint                  size,
583                                        gboolean              border)
584 {
585         PdfDocument *pdf_document;
586         PopplerPage *poppler_page;
587         GdkPixbuf *pixbuf;
588
589         pdf_document = PDF_DOCUMENT (document_thumbnails);
590
591         poppler_page = poppler_document_get_page (pdf_document->document, page);
592         g_return_val_if_fail (poppler_page != NULL, NULL);
593
594         pixbuf = poppler_page_get_thumbnail (poppler_page);
595         if (pixbuf != NULL) {
596                 /* The document provides its own thumbnails. */
597                 if (border) {
598                         GdkPixbuf *real_pixbuf;
599
600                         real_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
601                         g_object_unref (pixbuf);
602                         pixbuf = real_pixbuf;
603                 }
604         } else {
605                 /* There is no provided thumbnail.  We need to make one. */
606                 pixbuf = make_thumbnail_for_size (pdf_document, page, size, border);
607         }
608         return pixbuf;
609 }
610
611 static void
612 pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
613                                         gint                  page,
614                                         gint                  size,
615                                         gint                 *width,
616                                         gint                 *height)
617 {
618         PdfDocument *pdf_document;
619         PopplerPage *poppler_page;
620         gint has_thumb;
621         
622         pdf_document = PDF_DOCUMENT (document_thumbnails);
623         poppler_page = poppler_document_get_page (pdf_document->document, page);
624
625         g_return_if_fail (width != NULL);
626         g_return_if_fail (height != NULL);
627         g_return_if_fail (poppler_page != NULL);
628
629         has_thumb = poppler_page_get_thumbnail_size (poppler_page, width, height);
630
631         if (!has_thumb) {
632                 double page_width, page_height;
633
634                 poppler_page_get_size (poppler_page, &page_width, &page_height);
635                 if (page_width > page_height) {
636                         *width = size;
637                         *height = (int) (size * page_height / page_width);
638                 } else {
639                         *width = (int) (size * page_width / page_height);
640                         *height = size;
641                 }
642         }
643 }
644
645 static void
646 pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
647 {
648         iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
649         iface->get_dimensions = pdf_document_thumbnails_get_dimensions;
650 }
651
652
653 static gboolean
654 pdf_document_search_idle_callback (void *data)
655 {
656         PdfDocumentSearch *search = (PdfDocumentSearch*) data;
657         PdfDocument *pdf_document = search->document;
658         int n_pages, changed_page;
659         GList *matches;
660         PopplerPage *page;
661
662         page = poppler_document_get_page (search->document->document,
663                                           search->search_page);
664
665         g_mutex_lock (EV_DOC_MUTEX);
666         matches = poppler_page_find_text (page, search->text);
667         g_mutex_unlock (EV_DOC_MUTEX);
668
669         search->pages[search->search_page] = matches;
670         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (search->document));
671
672         changed_page = search->search_page;
673         search->search_page += 1;
674         if (search->search_page == n_pages) {
675                 /* wrap around */
676                 search->search_page = 0;
677         }
678
679         if (search->search_page != search->start_page) {
680                 ev_document_find_changed (EV_DOCUMENT_FIND (pdf_document),
681                                           changed_page);
682                 return TRUE;
683         }
684
685         /* We're done. */
686         search->idle = 0; /* will return FALSE to remove */
687         return FALSE;
688 }
689
690
691 static PdfDocumentSearch *
692 pdf_document_search_new (PdfDocument *pdf_document,
693                          int          start_page,
694                          const char  *text)
695 {
696         PdfDocumentSearch *search;
697         int n_pages;
698         int i;
699
700         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
701
702         search = g_new0 (PdfDocumentSearch, 1);
703
704         search->text = g_strdup (text);
705         search->pages = g_new0 (GList *, n_pages);
706         for (i = 0; i < n_pages; i++) {
707                 search->pages[i] = NULL;
708         }
709
710         search->document = pdf_document;
711
712         /* We add at low priority so the progress bar repaints */
713         search->idle = g_idle_add_full (G_PRIORITY_LOW,
714                                         pdf_document_search_idle_callback,
715                                         search,
716                                         NULL);
717
718         search->start_page = start_page;
719         search->search_page = start_page;
720
721         return search;
722 }
723
724 static void
725 pdf_document_search_free (PdfDocumentSearch   *search)
726 {
727         PdfDocument *pdf_document = search->document;
728         int n_pages;
729         int i;
730
731         if (search->idle != 0)
732                 g_source_remove (search->idle);
733
734         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
735         for (i = 0; i < n_pages; i++) {
736                 g_list_foreach (search->pages[i], (GFunc) g_free, NULL);
737                 g_list_free (search->pages[i]);
738         }
739         
740         g_free (search->text);
741 }
742
743 static void
744 pdf_document_find_begin (EvDocumentFind   *document,
745                          int               page,
746                          const char       *search_string,
747                          gboolean          case_sensitive)
748 {
749         PdfDocument *pdf_document = PDF_DOCUMENT (document);
750
751         /* FIXME handle case_sensitive (right now XPDF
752          * code is always case insensitive for ASCII
753          * and case sensitive for all other languaages)
754          */
755
756         if (pdf_document->search &&
757             strcmp (search_string, pdf_document->search->text) == 0)
758                 return;
759
760         if (pdf_document->search)
761                 pdf_document_search_free (pdf_document->search);
762
763         pdf_document->search = pdf_document_search_new (pdf_document,
764                                                         page,
765                                                         search_string);
766 }
767
768 int
769 pdf_document_find_get_n_results (EvDocumentFind *document_find, int page)
770 {
771         PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
772         int current_page;
773
774         if (search) {
775                 return g_list_length (search->pages[page]);
776         } else {
777                 return 0;
778         }
779 }
780
781 gboolean
782 pdf_document_find_get_result (EvDocumentFind *document_find,
783                               int             page,
784                               int             n_result,
785                               EvRectangle    *rectangle)
786 {
787         PdfDocument *pdf_document = PDF_DOCUMENT (document_find);
788         PdfDocumentSearch *search = pdf_document->search;
789         PopplerPage *poppler_page;
790         PopplerRectangle *r;
791         int current_page;
792         double height;
793
794         if (search == NULL)
795                 return FALSE;
796
797         r = (PopplerRectangle *) g_list_nth_data (search->pages[page],
798                                                   n_result);
799         if (r == NULL)
800                 return FALSE;
801
802         poppler_page = poppler_document_get_page (pdf_document->document, page);
803         poppler_page_get_size (poppler_page, NULL, &height);
804         rectangle->x1 = r->x1;
805         rectangle->y1 = height - r->y2;
806         rectangle->x2 = r->x2;
807         rectangle->y2 = height - r->y1;
808         
809         return TRUE;
810 }
811
812 int
813 pdf_document_find_page_has_results (EvDocumentFind *document_find,
814                                     int             page)
815 {
816         PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
817
818         g_return_val_if_fail (search != NULL, FALSE);
819
820         return search->pages[page] != NULL;
821 }
822
823 double
824 pdf_document_find_get_progress (EvDocumentFind *document_find)
825 {
826         PdfDocumentSearch *search;
827         int n_pages, pages_done;
828
829         search = PDF_DOCUMENT (document_find)->search;
830
831         if (search == NULL) {
832                 return 0;
833         }
834
835         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (document_find));
836         if (search->search_page > search->start_page) {
837                 pages_done = search->search_page - search->start_page + 1;
838         } else if (search->search_page == search->start_page) {
839                 pages_done = n_pages;
840         } else {
841                 pages_done = n_pages - search->start_page + search->search_page;
842         }
843
844         return pages_done / (double) n_pages;
845 }
846
847 static void
848 pdf_document_find_cancel (EvDocumentFind *document)
849 {
850         PdfDocument *pdf_document = PDF_DOCUMENT (document);
851
852         if (pdf_document->search) {
853                 pdf_document_search_free (pdf_document->search);
854                 pdf_document->search = NULL;
855         }
856 }
857
858 static void
859 pdf_document_find_iface_init (EvDocumentFindIface *iface)
860 {
861         iface->begin = pdf_document_find_begin;
862         iface->get_n_results = pdf_document_find_get_n_results;
863         iface->get_result = pdf_document_find_get_result;
864         iface->page_has_results = pdf_document_find_page_has_results;
865         iface->get_progress = pdf_document_find_get_progress;
866         iface->cancel = pdf_document_find_cancel;
867 }
868
869 static void
870 pdf_document_ps_exporter_begin (EvPSExporter *exporter, const char *filename)
871 {
872         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
873         int n_pages;
874         
875         n_pages = pdf_document_get_n_pages (EV_DOCUMENT (exporter));
876         pdf_document->ps_file = poppler_ps_file_new (pdf_document->document,
877                                                      filename, n_pages);
878 }
879
880 static void
881 pdf_document_ps_exporter_do_page (EvPSExporter *exporter, int page)
882 {
883         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
884         PopplerPage *poppler_page;
885
886         g_return_if_fail (pdf_document->ps_file != NULL);
887
888         poppler_page = poppler_document_get_page (pdf_document->document, page);
889         poppler_page_render_to_ps (poppler_page, pdf_document->ps_file);
890 }
891
892 static void
893 pdf_document_ps_exporter_end (EvPSExporter *exporter)
894 {
895         PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
896
897         poppler_ps_file_free (pdf_document->ps_file);
898         pdf_document->ps_file = NULL;
899 }
900
901 static void
902 pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
903 {
904         iface->begin = pdf_document_ps_exporter_begin;
905         iface->do_page = pdf_document_ps_exporter_do_page;
906         iface->end = pdf_document_ps_exporter_end;
907 }
908
909 PdfDocument *
910 pdf_document_new (void)
911 {
912         return PDF_DOCUMENT (g_object_new (PDF_TYPE_DOCUMENT, NULL));
913 }