Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-document.c
Go to the documentation of this file.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2009 Carlos Garcia Campos
4  * Copyright (C) 2004 Marco Pesenti Gritti
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "ev-document.h"
28 #include "ev-document-misc.h"
29 #include "synctex_parser.h"
30 
31 #define EV_DOCUMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_DOCUMENT, EvDocumentPrivate))
32 
33 typedef struct _EvPageSize
34 {
35  gdouble width;
36  gdouble height;
37 } EvPageSize;
38 
40 {
41  gchar *uri;
42  guint64 file_size;
43 
44  gboolean cache_loaded;
45  gint n_pages;
46 
47  gboolean uniform;
48  gdouble uniform_width;
49  gdouble uniform_height;
50 
51  gdouble max_width;
52  gdouble max_height;
53  gdouble min_width;
54  gdouble min_height;
55  gint max_label;
56 
57  gchar **page_labels;
60 
62 };
63 
64 static guint64 _ev_document_get_size_gfile (GFile *file);
65 static guint64 _ev_document_get_size (const char *uri);
66 static gint _ev_document_get_n_pages (EvDocument *document);
67 static void _ev_document_get_page_size (EvDocument *document,
68  EvPage *page,
69  double *width,
70  double *height);
71 static gchar *_ev_document_get_page_label (EvDocument *document,
72  EvPage *page);
74 static gboolean _ev_document_support_synctex (EvDocument *document);
75 
76 static GMutex ev_doc_mutex;
77 static GMutex ev_fc_mutex;
78 
79 G_DEFINE_ABSTRACT_TYPE (EvDocument, ev_document, G_TYPE_OBJECT)
80 
81 GQuark
83 {
84  static GQuark q = 0;
85  if (q == 0)
86  q = g_quark_from_static_string ("ev-document-error-quark");
87 
88  return q;
89 }
90 
91 static EvPage *
93  gint index)
94 {
95  return ev_page_new (index);
96 }
97 
98 static EvDocumentInfo *
100 {
101  return g_new0 (EvDocumentInfo, 1);
102 }
103 
104 static void
105 ev_document_finalize (GObject *object)
106 {
107  EvDocument *document = EV_DOCUMENT (object);
108 
109  if (document->priv->uri) {
110  g_free (document->priv->uri);
111  document->priv->uri = NULL;
112  }
113 
114  if (document->priv->page_sizes) {
115  g_free (document->priv->page_sizes);
116  document->priv->page_sizes = NULL;
117  }
118 
119  g_clear_pointer (&document->priv->page_labels, g_strfreev);
120 
121  if (document->priv->info) {
122  ev_document_info_free (document->priv->info);
123  document->priv->info = NULL;
124  }
125 
126  if (document->priv->synctex_scanner) {
128  document->priv->synctex_scanner = NULL;
129  }
130 
131  G_OBJECT_CLASS (ev_document_parent_class)->finalize (object);
132 }
133 
134 static void
136 {
137  document->priv = EV_DOCUMENT_GET_PRIVATE (document);
138 
139  /* Assume all pages are the same size until proven otherwise */
140  document->priv->uniform = TRUE;
141 }
142 
143 static void
145 {
146  GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
147 
148  g_type_class_add_private (g_object_class, sizeof (EvDocumentPrivate));
149 
152  klass->get_backend_info = NULL;
153 
154  g_object_class->finalize = ev_document_finalize;
155 }
156 
157 void
159 {
160  g_mutex_lock (&ev_doc_mutex);
161 }
162 
163 void
165 {
166  g_mutex_unlock (&ev_doc_mutex);
167 }
168 
169 gboolean
171 {
172  return g_mutex_trylock (&ev_doc_mutex);
173 }
174 
175 void
177 {
178  g_mutex_lock (&ev_fc_mutex);
179 }
180 
181 void
183 {
184  g_mutex_unlock (&ev_fc_mutex);
185 }
186 
187 gboolean
189 {
190  return g_mutex_trylock (&ev_fc_mutex);
191 }
192 
193 static void
195 {
196  EvDocumentPrivate *priv = document->priv;
197  gboolean custom_page_labels = FALSE;
198  gint i;
199 
200  /* Cache some info about the document to avoid
201  * going to the backends since it requires locks
202  */
203  priv->cache_loaded = TRUE;
204 
205  for (i = 0; i < priv->n_pages; i++) {
206  EvPage *page = ev_document_get_page (document, i);
207  gdouble page_width = 0;
208  gdouble page_height = 0;
209  EvPageSize *page_size;
210  gchar *page_label;
211 
212  _ev_document_get_page_size (document, page, &page_width, &page_height);
213 
214  if (i == 0) {
215  priv->uniform_width = page_width;
216  priv->uniform_height = page_height;
217  priv->max_width = priv->uniform_width;
218  priv->max_height = priv->uniform_height;
219  priv->min_width = priv->uniform_width;
220  priv->min_height = priv->uniform_height;
221  } else if (priv->uniform &&
222  (priv->uniform_width != page_width ||
223  priv->uniform_height != page_height)) {
224  /* It's a different page size. Backfill the array. */
225  int j;
226 
227  priv->page_sizes = g_new0 (EvPageSize, priv->n_pages);
228 
229  for (j = 0; j < i; j++) {
230  page_size = &(priv->page_sizes[j]);
231  page_size->width = priv->uniform_width;
232  page_size->height = priv->uniform_height;
233  }
234  priv->uniform = FALSE;
235  }
236  if (!priv->uniform) {
237  page_size = &(priv->page_sizes[i]);
238 
239  page_size->width = page_width;
240  page_size->height = page_height;
241 
242  if (page_width > priv->max_width)
243  priv->max_width = page_width;
244  if (page_width < priv->min_width)
245  priv->min_width = page_width;
246 
247  if (page_height > priv->max_height)
248  priv->max_height = page_height;
249  if (page_height < priv->min_height)
250  priv->min_height = page_height;
251  }
252 
253  page_label = _ev_document_get_page_label (document, page);
254  if (page_label) {
255  if (!priv->page_labels)
256  priv->page_labels = g_new0 (gchar *, priv->n_pages + 1);
257 
258  if (!custom_page_labels) {
259  gchar *real_page_label;
260 
261  real_page_label = g_strdup_printf ("%d", i + 1);
262  custom_page_labels = g_strcmp0 (real_page_label, page_label) != 0;
263  g_free (real_page_label);
264  }
265 
266  priv->page_labels[i] = page_label;
267  priv->max_label = MAX (priv->max_label,
268  g_utf8_strlen (page_label, 256));
269  }
270 
271  g_object_unref (page);
272  }
273 
274  if (!custom_page_labels)
275  g_clear_pointer (&priv->page_labels, g_strfreev);
276 }
277 
278 static void
280  const gchar *uri)
281 {
282  EvDocumentPrivate *priv = document->priv;
283 
284  if (_ev_document_support_synctex (document)) {
285  gchar *filename;
286 
287  filename = g_filename_from_uri (uri, NULL, NULL);
288  if (filename != NULL) {
289  priv->synctex_scanner =
290  synctex_scanner_new_with_output_file (filename, NULL, 1);
291  g_free (filename);
292  }
293  }
294 }
295 
314 gboolean
316  const char *uri,
317  EvDocumentLoadFlags flags,
318  GError **error)
319 {
320  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
321  gboolean retval;
322  GError *err = NULL;
323 
324  retval = klass->load (document, uri, &err);
325  if (!retval) {
326  if (err) {
327  g_propagate_error (error, err);
328  } else {
329  g_warning ("%s::EvDocument::load returned FALSE but did not fill in @error; fix the backend!\n",
330  G_OBJECT_TYPE_NAME (document));
331 
332  /* So upper layers don't crash */
333  g_set_error_literal (error,
336  "Internal error in backend");
337  }
338  } else {
339  document->priv->info = _ev_document_get_info (document);
340  document->priv->n_pages = _ev_document_get_n_pages (document);
341  if (!(flags & EV_DOCUMENT_LOAD_FLAG_NO_CACHE))
342  ev_document_setup_cache (document);
343  document->priv->uri = g_strdup (uri);
344  document->priv->file_size = _ev_document_get_size (uri);
345  ev_document_initialize_synctex (document, uri);
346  }
347 
348  return retval;
349 }
350 
368 gboolean
370  const char *uri,
371  GError **error)
372 {
373  return ev_document_load_full (document, uri,
375 }
376 
392 gboolean
394  GInputStream *stream,
395  EvDocumentLoadFlags flags,
396  GCancellable *cancellable,
397  GError **error)
398 {
399  EvDocumentClass *klass;
400 
401  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
402  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
403  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
404  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
405 
406  klass = EV_DOCUMENT_GET_CLASS (document);
407  if (!klass->load_stream) {
408  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
409  "Backend does not support loading from stream");
410  return FALSE;
411  }
412 
413  if (!klass->load_stream (document, stream, flags, cancellable, error))
414  return FALSE;
415 
416  document->priv->info = _ev_document_get_info (document);
417  document->priv->n_pages = _ev_document_get_n_pages (document);
418 
419  if (!(flags & EV_DOCUMENT_LOAD_FLAG_NO_CACHE))
420  ev_document_setup_cache (document);
421 
422  return TRUE;
423 }
424 
440 gboolean
442  GFile *file,
443  EvDocumentLoadFlags flags,
444  GCancellable *cancellable,
445  GError **error)
446 {
447  EvDocumentClass *klass;
448 
449  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
450  g_return_val_if_fail (G_IS_FILE (file), FALSE);
451  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
452  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
453 
454  klass = EV_DOCUMENT_GET_CLASS (document);
455  if (!klass->load_gfile) {
456  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
457  "Backend does not support loading from GFile");
458  return FALSE;
459  }
460 
461  if (!klass->load_gfile (document, file, flags, cancellable, error))
462  return FALSE;
463 
464  document->priv->info = _ev_document_get_info (document);
465  document->priv->n_pages = _ev_document_get_n_pages (document);
466 
467  if (!(flags & EV_DOCUMENT_LOAD_FLAG_NO_CACHE))
468  ev_document_setup_cache (document);
469 
470  document->priv->uri = g_file_get_uri (file);
471  document->priv->file_size = _ev_document_get_size_gfile (file);
472  ev_document_initialize_synctex (document, document->priv->uri);
473 
474  return TRUE;
475 }
476 
487 gboolean
489  const char *uri,
490  GError **error)
491 {
492  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
493 
494  return klass->save (document, uri, error);
495 }
496 
504 EvPage *
506  gint index)
507 {
508  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
509 
510  return klass->get_page (document, index);
511 }
512 
513 static gboolean
515 {
516  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
517 
518  return klass->support_synctex ? klass->support_synctex (document) : FALSE;
519 }
520 
521 gboolean
523 {
524  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
525 
526  return document->priv->synctex_scanner != NULL;
527 }
528 
544 EvSourceLink *
546  gint page_index,
547  gfloat x,
548  gfloat y)
549 {
550  EvSourceLink *result = NULL;
551  synctex_scanner_t scanner;
552 
553  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
554 
555  scanner = document->priv->synctex_scanner;
556  if (!scanner)
557  return NULL;
558 
559  if (synctex_edit_query (scanner, page_index + 1, x, y) > 0) {
560  synctex_node_t node;
561 
562  /* We assume that a backward search returns either zero or one result_node */
563  node = synctex_next_result (scanner);
564  if (node != NULL) {
565  const gchar *filename;
566 
567  filename = synctex_scanner_get_name (scanner, synctex_node_tag (node));
568 
569  if (filename) {
570  result = ev_source_link_new (filename,
571  synctex_node_line (node),
572  synctex_node_column (node));
573  }
574  }
575  }
576 
577  return result;
578 }
579 
591 EvMapping *
593  EvSourceLink *link)
594 {
595  EvMapping *result = NULL;
596  synctex_scanner_t scanner;
597 
598  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
599 
600  scanner = document->priv->synctex_scanner;
601  if (!scanner)
602  return NULL;
603 
604  if (synctex_display_query (scanner, link->filename, link->line, link->col) > 0) {
605  synctex_node_t node;
606  gint page;
607 
608  if ((node = synctex_next_result (scanner))) {
609  result = g_new (EvMapping, 1);
610 
611  page = synctex_node_page (node) - 1;
612  result->data = GINT_TO_POINTER (page);
613 
614  result->area.x1 = synctex_node_box_visible_h (node);
615  result->area.y1 = synctex_node_box_visible_v (node) -
617  result->area.x2 = synctex_node_box_visible_width (node) + result->area.x1;
618  result->area.y2 = synctex_node_box_visible_depth (node) +
619  synctex_node_box_visible_height (node) + result->area.y1;
620  }
621  }
622 
623  return result;
624 }
625 
626 static guint64
628 {
629  goffset size = 0;
630  GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
631  G_FILE_QUERY_INFO_NONE, NULL, NULL);
632  if (info) {
633  size = g_file_info_get_size (info);
634 
635  g_object_unref (info);
636  }
637 
638  return size;
639 }
640 
641 static guint64
642 _ev_document_get_size (const char *uri)
643 {
644  GFile *file = g_file_new_for_uri (uri);
645  guint64 size = _ev_document_get_size_gfile (file);
646 
647  g_object_unref (file);
648 
649  return size;
650 }
651 
652 static gint
654 {
655  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
656 
657  return klass->get_n_pages (document);
658 }
659 
660 gint
662 {
663  g_return_val_if_fail (EV_IS_DOCUMENT (document), 0);
664 
665  return document->priv->n_pages;
666 }
667 
668 static void
670  EvPage *page,
671  double *width,
672  double *height)
673 {
674  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
675 
676  klass->get_page_size (document, page, width, height);
677 }
678 
686 void
688  gint page_index,
689  double *width,
690  double *height)
691 {
692  EvDocumentPrivate *priv;
693 
694  g_return_if_fail (EV_IS_DOCUMENT (document));
695  g_return_if_fail (page_index >= 0 || page_index < document->priv->n_pages);
696 
697  priv = document->priv;
698 
699  if (priv->cache_loaded) {
700  if (width)
701  *width = priv->uniform ?
702  priv->uniform_width :
703  priv->page_sizes[page_index].width;
704  if (height)
705  *height = priv->uniform ?
706  priv->uniform_height :
707  priv->page_sizes[page_index].height;
708  } else {
709  EvPage *page;
710 
711  g_mutex_lock (&ev_doc_mutex);
712  page = ev_document_get_page (document, page_index);
713  _ev_document_get_page_size (document, page, width, height);
714  g_object_unref (page);
715  g_mutex_unlock (&ev_doc_mutex);
716  }
717 }
718 
719 static gchar *
721  EvPage *page)
722 {
723  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
724 
725  return klass->get_page_label ?
726  klass->get_page_label (document, page) : NULL;
727 }
728 
729 gchar *
731  gint page_index)
732 {
733  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
734  g_return_val_if_fail (page_index >= 0 || page_index < document->priv->n_pages, NULL);
735 
736  if (!document->priv->cache_loaded) {
737  EvPage *page;
738  gchar *page_label;
739 
740  g_mutex_lock (&ev_doc_mutex);
741  page = ev_document_get_page (document, page_index);
742  page_label = _ev_document_get_page_label (document, page);
743  g_object_unref (page);
744  g_mutex_unlock (&ev_doc_mutex);
745 
746  return page_label ? page_label : g_strdup_printf ("%d", page_index + 1);
747  }
748 
749  return (document->priv->page_labels && document->priv->page_labels[page_index]) ?
750  g_strdup (document->priv->page_labels[page_index]) :
751  g_strdup_printf ("%d", page_index + 1);
752 }
753 
754 static EvDocumentInfo *
756 {
757  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
758 
759  return klass->get_info (document);
760 }
761 
764 {
765  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
766 
767  return document->priv->info;
768 }
769 
770 gboolean
772 {
773  EvDocumentClass *klass;
774 
775  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
776 
777  klass = EV_DOCUMENT_GET_CLASS (document);
778  if (klass->get_backend_info == NULL)
779  return FALSE;
780 
781  return klass->get_backend_info (document, info);
782 }
783 
784 cairo_surface_t *
786  EvRenderContext *rc)
787 {
788  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
789 
790  return klass->render (document, rc);
791 }
792 
793 static GdkPixbuf *
795  EvRenderContext *rc)
796 {
797  cairo_surface_t *surface;
798  GdkPixbuf *pixbuf = NULL;
799 
800  surface = ev_document_render (document, rc);
801  if (surface != NULL) {
802  pixbuf = ev_document_misc_pixbuf_from_surface (surface);
803  cairo_surface_destroy (surface);
804  }
805 
806  return pixbuf;
807 }
808 
816 GdkPixbuf *
818  EvRenderContext *rc)
819 {
820  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
821 
822  if (klass->get_thumbnail)
823  return klass->get_thumbnail (document, rc);
824 
825  return _ev_document_get_thumbnail (document, rc);
826 }
827 
837 cairo_surface_t *
839  EvRenderContext *rc)
840 {
841  EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
842 
843  if (klass->get_thumbnail_surface)
844  return klass->get_thumbnail_surface (document, rc);
845 
846  return ev_document_render (document, rc);
847 }
848 
849 
850 const gchar *
852 {
853  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
854 
855  return document->priv->uri;
856 }
857 
858 const gchar *
860 {
861  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
862 
863  return (document->priv->info->fields_mask & EV_DOCUMENT_INFO_TITLE) ?
864  document->priv->info->title : NULL;
865 }
866 
867 gboolean
869 {
870  g_return_val_if_fail (EV_IS_DOCUMENT (document), TRUE);
871 
872  if (!document->priv->cache_loaded) {
873  g_mutex_lock (&ev_doc_mutex);
874  ev_document_setup_cache (document);
875  g_mutex_unlock (&ev_doc_mutex);
876  }
877 
878  return document->priv->uniform;
879 }
880 
881 void
883  gdouble *width,
884  gdouble *height)
885 {
886  g_return_if_fail (EV_IS_DOCUMENT (document));
887 
888  if (!document->priv->cache_loaded) {
889  g_mutex_lock (&ev_doc_mutex);
890  ev_document_setup_cache (document);
891  g_mutex_unlock (&ev_doc_mutex);
892  }
893 
894  if (width)
895  *width = document->priv->max_width;
896  if (height)
897  *height = document->priv->max_height;
898 }
899 
900 void
902  gdouble *width,
903  gdouble *height)
904 {
905  g_return_if_fail (EV_IS_DOCUMENT (document));
906 
907  if (!document->priv->cache_loaded) {
908  g_mutex_lock (&ev_doc_mutex);
909  ev_document_setup_cache (document);
910  g_mutex_unlock (&ev_doc_mutex);
911  }
912 
913  if (width)
914  *width = document->priv->min_width;
915  if (height)
916  *height = document->priv->min_height;
917 }
918 
919 gboolean
921 {
922  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
923 
924  if (!document->priv->cache_loaded) {
925  g_mutex_lock (&ev_doc_mutex);
926  ev_document_setup_cache (document);
927  g_mutex_unlock (&ev_doc_mutex);
928  }
929 
930  return (document->priv->max_width > 0 && document->priv->max_height > 0);
931 }
932 
933 guint64
935 {
936  g_return_val_if_fail (EV_IS_DOCUMENT (document), 0);
937 
938  return document->priv->file_size;
939 }
940 
941 gint
943 {
944  g_return_val_if_fail (EV_IS_DOCUMENT (document), -1);
945 
946  if (!document->priv->cache_loaded) {
947  g_mutex_lock (&ev_doc_mutex);
948  ev_document_setup_cache (document);
949  g_mutex_unlock (&ev_doc_mutex);
950  }
951 
952  return document->priv->max_label;
953 }
954 
955 gboolean
957 {
958  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
959 
960  if (!document->priv->cache_loaded) {
961  g_mutex_lock (&ev_doc_mutex);
962  ev_document_setup_cache (document);
963  g_mutex_unlock (&ev_doc_mutex);
964  }
965 
966  return document->priv->page_labels != NULL;
967 }
968 
969 gboolean
971  const gchar *page_label,
972  gint *page_index)
973 {
974  gint i, page;
975  glong value;
976  gchar *endptr = NULL;
977  EvDocumentPrivate *priv = document->priv;
978 
979  g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
980  g_return_val_if_fail (page_label != NULL, FALSE);
981  g_return_val_if_fail (page_index != NULL, FALSE);
982 
983  if (!document->priv->cache_loaded) {
984  g_mutex_lock (&ev_doc_mutex);
985  ev_document_setup_cache (document);
986  g_mutex_unlock (&ev_doc_mutex);
987  }
988 
989  /* First, look for a literal label match */
990  for (i = 0; priv->page_labels && i < priv->n_pages; i ++) {
991  if (priv->page_labels[i] != NULL &&
992  ! strcmp (page_label, priv->page_labels[i])) {
993  *page_index = i;
994  return TRUE;
995  }
996  }
997 
998  /* Second, look for a match with case insensitively */
999  for (i = 0; priv->page_labels && i < priv->n_pages; i++) {
1000  if (priv->page_labels[i] != NULL &&
1001  ! strcasecmp (page_label, priv->page_labels[i])) {
1002  *page_index = i;
1003  return TRUE;
1004  }
1005  }
1006 
1007  /* Next, parse the label, and see if the number fits */
1008  value = strtol (page_label, &endptr, 10);
1009  if (endptr[0] == '\0') {
1010  /* Page number is an integer */
1011  page = MIN (G_MAXINT, value);
1012 
1013  /* convert from a page label to a page offset */
1014  page --;
1015  if (page >= 0 && page < priv->n_pages) {
1016  *page_index = page;
1017  return TRUE;
1018  }
1019  }
1020 
1021  return FALSE;
1022 }
1023 
1024 /* EvSourceLink */
1025 G_DEFINE_BOXED_TYPE (EvSourceLink, ev_source_link, ev_source_link_copy, ev_source_link_free)
1026 
1027 EvSourceLink *
1028 ev_source_link_new (const gchar *filename,
1029  gint line,
1030  gint col)
1031 {
1032  EvSourceLink *link = g_slice_new (EvSourceLink);
1033 
1034  link->filename = g_strdup (filename);
1035  link->line = line;
1036  link->col = col;
1037 
1038  return link;
1039 }
1040 
1041 EvSourceLink *
1043 {
1044  EvSourceLink *copy;
1045 
1046  g_return_val_if_fail (link != NULL, NULL);
1047 
1048  copy = g_slice_new (EvSourceLink);
1049 
1050  *copy = *link;
1051  copy->filename = g_strdup (link->filename);
1052 
1053  return copy;
1054 }
1055 
1056 void
1058 {
1059  if (link == NULL)
1060  return;
1061 
1062  g_free (link->filename);
1063  g_slice_free (EvSourceLink, link);
1064 }
1065 
1066 /* EvDocumentInfo */
1067 G_DEFINE_BOXED_TYPE (EvDocumentInfo, ev_document_info, ev_document_info_copy, ev_document_info_free)
1068 
1071 {
1072  EvDocumentInfo *copy;
1073 
1074  g_return_val_if_fail (info != NULL, NULL);
1075 
1076  copy = g_new0 (EvDocumentInfo, 1);
1077  copy->title = g_strdup (info->title);
1078  copy->format = g_strdup (info->format);
1079  copy->author = g_strdup (info->author);
1080  copy->subject = g_strdup (info->subject);
1081  copy->keywords = g_strdup (info->keywords);
1082  copy->security = g_strdup (info->security);
1083  copy->creator = g_strdup (info->creator);
1084  copy->producer = g_strdup (info->producer);
1085  copy->linearized = g_strdup (info->linearized);
1086 
1087  copy->creation_date = info->creation_date;
1088  copy->modified_date = info->modified_date;
1089  copy->layout = info->layout;
1090  copy->mode = info->mode;
1091  copy->ui_hints = info->ui_hints;
1092  copy->permissions = info->permissions;
1093  copy->n_pages = info->n_pages;
1094  copy->license = ev_document_license_copy (info->license);
1095 
1096  copy->fields_mask = info->fields_mask;
1097 
1098  return copy;
1099 }
1100 
1101 void
1103 {
1104  if (info == NULL)
1105  return;
1106 
1107  g_free (info->title);
1108  g_free (info->format);
1109  g_free (info->author);
1110  g_free (info->subject);
1111  g_free (info->keywords);
1112  g_free (info->creator);
1113  g_free (info->producer);
1114  g_free (info->linearized);
1115  g_free (info->security);
1116  ev_document_license_free (info->license);
1117 
1118  g_free (info);
1119 }
1120 
1121 /* EvDocumentLicense */
1122 G_DEFINE_BOXED_TYPE (EvDocumentLicense, ev_document_license, ev_document_license_copy, ev_document_license_free)
1123 
1126 {
1127  return g_new0 (EvDocumentLicense, 1);
1128 }
1129 
1132 {
1133  EvDocumentLicense *new_license;
1134 
1135  if (!license)
1136  return NULL;
1137 
1138  new_license = ev_document_license_new ();
1139 
1140  if (license->text)
1141  new_license->text = g_strdup (license->text);
1142  if (license->uri)
1143  new_license->uri = g_strdup (license->uri);
1144  if (license->web_statement)
1145  new_license->web_statement = g_strdup (license->web_statement);
1146 
1147  return new_license;
1148 }
1149 
1150 void
1152 {
1153  if (!license)
1154  return;
1155 
1156  g_free (license->text);
1157  g_free (license->uri);
1158  g_free (license->web_statement);
1159 
1160  g_free (license);
1161 }
1162 
1163 const gchar *
1165 {
1166  return license->text;
1167 }
1168 
1169 const gchar *
1171 {
1172  return license->uri;
1173 }
1174 
1175 const gchar *
1177 {
1178  return license->web_statement;
1179 }
1180 
1181 /* EvRectangle */
1182 G_DEFINE_BOXED_TYPE (EvRectangle, ev_rectangle, ev_rectangle_copy, ev_rectangle_free)
1183 
1184 EvRectangle *
1186 {
1187  return g_new0 (EvRectangle, 1);
1188 }
1189 
1190 EvRectangle *
1192 {
1193  EvRectangle *new_rectangle;
1194 
1195  g_return_val_if_fail (rectangle != NULL, NULL);
1196 
1197  new_rectangle = g_new (EvRectangle, 1);
1198  *new_rectangle = *rectangle;
1199 
1200  return new_rectangle;
1201 }
1202 
1203 void
1205 {
1206  g_free (rectangle);
1207 }
1208 
1209 /* Compares two rects. returns 0 if they're equal */
1210 #define EPSILON 0.0000001
1211 
1212 gint
1214  EvRectangle *b)
1215 {
1216  if (a == b)
1217  return 0;
1218  if (a == NULL || b == NULL)
1219  return 1;
1220 
1221  return ! ((ABS (a->x1 - b->x1) < EPSILON) &&
1222  (ABS (a->y1 - b->y1) < EPSILON) &&
1223  (ABS (a->x2 - b->x2) < EPSILON) &&
1224  (ABS (a->y2 - b->y2) < EPSILON));
1225 }