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