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