]> www.fi.muni.cz Git - evince.git/blob - libdocument/ev-document.c
[libdocument] Remove redundant macro definition
[evince.git] / libdocument / ev-document.c
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
29 #define EV_DOCUMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_DOCUMENT, EvDocumentPrivate))
30
31 typedef struct _EvPageSize
32 {
33         gdouble width;
34         gdouble height;
35 } EvPageSize;
36
37 struct _EvDocumentPrivate
38 {
39         gchar          *uri;
40
41         gint            n_pages;
42
43         gboolean        uniform;
44         gdouble         uniform_width;
45         gdouble         uniform_height;
46
47         gdouble         max_width;
48         gdouble         max_height;
49         gint            max_label;
50
51         gchar         **page_labels;
52         EvPageSize     *page_sizes;
53         EvDocumentInfo *info;
54 };
55
56 static gint            _ev_document_get_n_pages   (EvDocument  *document);
57 static void            _ev_document_get_page_size (EvDocument *document,
58                                                    EvPage     *page,
59                                                    double     *width,
60                                                    double     *height);
61 static gchar          *_ev_document_get_page_label (EvDocument *document,
62                                                     EvPage     *page);
63 static EvDocumentInfo *_ev_document_get_info       (EvDocument *document);
64
65 GMutex *ev_doc_mutex = NULL;
66 GMutex *ev_fc_mutex = NULL;
67
68 G_DEFINE_ABSTRACT_TYPE (EvDocument, ev_document, G_TYPE_OBJECT)
69
70 GQuark
71 ev_document_error_quark (void)
72 {
73   static GQuark q = 0;
74   if (q == 0)
75     q = g_quark_from_static_string ("ev-document-error-quark");
76
77   return q;
78 }
79
80 static EvPage *
81 ev_document_impl_get_page (EvDocument *document,
82                            gint        index)
83 {
84         return ev_page_new (index);
85 }
86
87 static EvDocumentInfo *
88 ev_document_impl_get_info (EvDocument *document)
89 {
90         return g_new0 (EvDocumentInfo, 1);
91 }
92
93 static void
94 ev_document_finalize (GObject *object)
95 {
96         EvDocument *document = EV_DOCUMENT (object);
97
98         if (document->priv->uri) {
99                 g_free (document->priv->uri);
100                 document->priv->uri = NULL;
101         }
102
103         if (document->priv->page_sizes) {
104                 g_free (document->priv->page_sizes);
105                 document->priv->page_sizes = NULL;
106         }
107
108         if (document->priv->page_labels) {
109                 gint i;
110
111                 for (i = 0; i < document->priv->n_pages; i++) {
112                         g_free (document->priv->page_labels[i]);
113                 }
114                 g_free (document->priv->page_labels);
115                 document->priv->page_labels = NULL;
116         }
117
118         if (document->priv->info) {
119                 ev_document_info_free (document->priv->info);
120                 document->priv->info = NULL;
121         }
122
123         G_OBJECT_CLASS (ev_document_parent_class)->finalize (object);
124 }
125
126 static void
127 ev_document_init (EvDocument *document)
128 {
129         document->priv = EV_DOCUMENT_GET_PRIVATE (document);
130
131         /* Assume all pages are the same size until proven otherwise */
132         document->priv->uniform = TRUE;
133 }
134
135 static void
136 ev_document_class_init (EvDocumentClass *klass)
137 {
138         GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
139
140         g_type_class_add_private (g_object_class, sizeof (EvDocumentPrivate));
141
142         klass->get_page = ev_document_impl_get_page;
143         klass->get_info = ev_document_impl_get_info;
144         klass->get_backend_info = NULL;
145
146         g_object_class->finalize = ev_document_finalize;
147 }
148
149 GMutex *
150 ev_document_get_doc_mutex (void)
151 {
152         if (ev_doc_mutex == NULL) {
153                 ev_doc_mutex = g_mutex_new ();
154         }
155         return ev_doc_mutex;
156 }
157
158 void
159 ev_document_doc_mutex_lock (void)
160 {
161         g_mutex_lock (ev_document_get_doc_mutex ());
162 }
163
164 void
165 ev_document_doc_mutex_unlock (void)
166 {
167         g_mutex_unlock (ev_document_get_doc_mutex ());
168 }
169
170 gboolean
171 ev_document_doc_mutex_trylock (void)
172 {
173         return g_mutex_trylock (ev_document_get_doc_mutex ());
174 }
175
176 GMutex *
177 ev_document_get_fc_mutex (void)
178 {
179         if (ev_fc_mutex == NULL) {
180                 ev_fc_mutex = g_mutex_new ();
181         }
182         return ev_fc_mutex;
183 }
184
185 void
186 ev_document_fc_mutex_lock (void)
187 {
188         g_mutex_lock (ev_document_get_fc_mutex ());
189 }
190
191 void
192 ev_document_fc_mutex_unlock (void)
193 {
194         g_mutex_unlock (ev_document_get_fc_mutex ());
195 }
196
197 gboolean
198 ev_document_fc_mutex_trylock (void)
199 {
200         return g_mutex_trylock (ev_document_get_fc_mutex ());
201 }
202
203 /**
204  * ev_document_load:
205  * @document: a #EvDocument
206  * @uri: the document's URI
207  * @error: a #GError location to store an error, or %NULL
208  *
209  * Loads @document from @uri.
210  * 
211  * On failure, %FALSE is returned and @error is filled in.
212  * If the document is encrypted, EV_DEFINE_ERROR_ENCRYPTED is returned.
213  * If the backend cannot load the specific document, EV_DOCUMENT_ERROR_INVALID
214  * is returned. Other errors are possible too, depending on the backend
215  * used to load the document and the URI, e.g. #GIOError, #GFileError, and
216  * #GConvertError.
217  *
218  * Returns: %TRUE on success, or %FALSE on failure.
219  */
220 gboolean
221 ev_document_load (EvDocument  *document,
222                   const char  *uri,
223                   GError     **error)
224 {
225         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
226         gboolean retval;
227         GError *err = NULL;
228
229         retval = klass->load (document, uri, &err);
230         if (!retval) {
231                 if (err) {
232                         g_propagate_error (error, err);
233                 } else {
234                         g_warning ("%s::EvDocument::load returned FALSE but did not fill in @error; fix the backend!\n",
235                                    G_OBJECT_TYPE_NAME (document));
236
237                         /* So upper layers don't crash */
238                         g_set_error_literal (error,
239                                              EV_DOCUMENT_ERROR,
240                                              EV_DOCUMENT_ERROR_INVALID,
241                                              "Internal error in backend");
242                 }
243         } else {
244                 gint i;
245                 EvDocumentPrivate *priv = document->priv;
246
247                 /* Cache some info about the document to avoid
248                  * going to the backends since it requires locks
249                  */
250                 priv->uri = g_strdup (uri);
251                 priv->n_pages = _ev_document_get_n_pages (document);
252
253                 for (i = 0; i < priv->n_pages; i++) {
254                         EvPage     *page = ev_document_get_page (document, i);
255                         gdouble     page_width = 0;
256                         gdouble     page_height = 0;
257                         EvPageSize *page_size;
258                         gchar      *page_label;
259
260                         _ev_document_get_page_size (document, page, &page_width, &page_height);
261
262                         if (i == 0) {
263                                 priv->uniform_width = page_width;
264                                 priv->uniform_height = page_height;
265                                 priv->max_width = priv->uniform_width;
266                                 priv->max_height = priv->uniform_height;
267                         } else if (priv->uniform &&
268                                    (priv->uniform_width != page_width ||
269                                     priv->uniform_height != page_height)) {
270                                 /* It's a different page size.  Backfill the array. */
271                                 int j;
272
273                                 priv->page_sizes = g_new0 (EvPageSize, priv->n_pages);
274
275                                 for (j = 0; j < i; j++) {
276                                         page_size = &(priv->page_sizes[j]);
277                                         page_size->width = priv->uniform_width;
278                                         page_size->height = priv->uniform_height;
279                                 }
280                                 priv->uniform = FALSE;
281                         }
282                         if (!priv->uniform) {
283                                 page_size = &(priv->page_sizes[i]);
284
285                                 page_size->width = page_width;
286                                 page_size->height = page_height;
287
288                                 if (page_width > priv->max_width)
289                                         priv->max_width = page_width;
290
291                                 if (page_height > priv->max_height)
292                                         priv->max_height = page_height;
293                         }
294
295                         page_label = _ev_document_get_page_label (document, page);
296                         if (page_label) {
297                                 if (!priv->page_labels)
298                                         priv->page_labels = g_new0 (gchar *, priv->n_pages);
299
300                                 priv->page_labels[i] = page_label;
301                                 priv->max_label = MAX (priv->max_label,
302                                                        g_utf8_strlen (page_label, 256));
303                         }
304
305                         g_object_unref (page);
306                 }
307
308                 priv->info = _ev_document_get_info (document);
309         }
310
311         return retval;
312 }
313
314 /**
315  * ev_document_save:
316  * @document:
317  * @uri: the target URI
318  * @error: a #GError location to store an error, or %NULL
319  *
320  * Saves @document to @uri.
321  * 
322  * Returns: %TRUE on success, or %FALSE on error with @error filled in
323  */
324 gboolean
325 ev_document_save (EvDocument  *document,
326                   const char  *uri,
327                   GError     **error)
328 {
329         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
330
331         return klass->save (document, uri, error);
332 }
333
334 EvPage *
335 ev_document_get_page (EvDocument *document,
336                       gint        index)
337 {
338         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
339
340         return klass->get_page (document, index);
341 }
342
343 static gint
344 _ev_document_get_n_pages (EvDocument  *document)
345 {
346         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
347
348         return klass->get_n_pages (document);
349 }
350
351 gint
352 ev_document_get_n_pages (EvDocument  *document)
353 {
354         g_return_val_if_fail (EV_IS_DOCUMENT (document), 0);
355
356         return document->priv->n_pages;
357 }
358
359 static void
360 _ev_document_get_page_size (EvDocument *document,
361                             EvPage     *page,
362                             double     *width,
363                             double     *height)
364 {
365         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
366
367         klass->get_page_size (document, page, width, height);
368 }
369
370 void
371 ev_document_get_page_size (EvDocument *document,
372                            gint        page_index,
373                            double     *width,
374                            double     *height)
375 {
376         g_return_if_fail (EV_IS_DOCUMENT (document));
377         g_return_if_fail (page_index >= 0 || page_index < document->priv->n_pages);
378
379         if (width)
380                 *width = document->priv->uniform ?
381                         document->priv->uniform_width :
382                         document->priv->page_sizes[page_index].width;
383         if (height)
384                 *height = document->priv->uniform ?
385                         document->priv->uniform_height :
386                         document->priv->page_sizes[page_index].height;
387 }
388
389 static gchar *
390 _ev_document_get_page_label (EvDocument *document,
391                              EvPage     *page)
392 {
393         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
394
395         return klass->get_page_label ?
396                 klass->get_page_label (document, page) : NULL;
397 }
398
399 gchar *
400 ev_document_get_page_label (EvDocument *document,
401                             gint        page_index)
402 {
403         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
404         g_return_val_if_fail (page_index >= 0 || page_index < document->priv->n_pages, NULL);
405
406         return (document->priv->page_labels && document->priv->page_labels[page_index]) ?
407                 g_strdup (document->priv->page_labels[page_index]) :
408                 g_strdup_printf ("%d", page_index + 1);
409 }
410
411 static EvDocumentInfo *
412 _ev_document_get_info (EvDocument *document)
413 {
414         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
415
416         return klass->get_info (document);
417 }
418
419 EvDocumentInfo *
420 ev_document_get_info (EvDocument *document)
421 {
422         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
423
424         return document->priv->info;
425 }
426
427 gboolean
428 ev_document_get_backend_info (EvDocument *document, EvDocumentBackendInfo *info)
429 {
430         g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
431
432         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
433         if (klass->get_backend_info == NULL)
434                 return FALSE;
435
436         return klass->get_backend_info (document, info);
437 }
438
439 cairo_surface_t *
440 ev_document_render (EvDocument      *document,
441                     EvRenderContext *rc)
442 {
443         EvDocumentClass *klass = EV_DOCUMENT_GET_CLASS (document);
444
445         return klass->render (document, rc);
446 }
447
448 const gchar *
449 ev_document_get_uri (EvDocument *document)
450 {
451         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
452
453         return document->priv->uri;
454 }
455
456 const gchar *
457 ev_document_get_title (EvDocument *document)
458 {
459         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
460
461         return (document->priv->info->fields_mask & EV_DOCUMENT_INFO_TITLE) ?
462                 document->priv->info->title : NULL;
463 }
464
465 gboolean
466 ev_document_is_page_size_uniform (EvDocument *document)
467 {
468         g_return_val_if_fail (EV_IS_DOCUMENT (document), TRUE);
469
470         return document->priv->uniform;
471 }
472
473 void
474 ev_document_get_max_page_size (EvDocument *document,
475                                gdouble    *width,
476                                gdouble    *height)
477 {
478         g_return_if_fail (EV_IS_DOCUMENT (document));
479
480         if (width)
481                 *width = document->priv->max_width;
482         if (height)
483                 *height = document->priv->max_height;
484 }
485
486 gboolean
487 ev_document_check_dimensions (EvDocument *document)
488 {
489         g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
490
491         return (document->priv->max_width > 0 && document->priv->max_height > 0);
492 }
493
494 gint
495 ev_document_get_max_label_len (EvDocument *document)
496 {
497         g_return_val_if_fail (EV_IS_DOCUMENT (document), -1);
498
499         return document->priv->max_label;
500 }
501
502 gboolean
503 ev_document_has_text_page_labels (EvDocument *document)
504 {
505         g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
506
507         return document->priv->page_labels != NULL;
508 }
509
510 gboolean
511 ev_document_find_page_by_label (EvDocument  *document,
512                                 const gchar *page_label,
513                                 gint        *page_index)
514 {
515         gint i, page;
516         glong value;
517         gchar *endptr = NULL;
518         EvDocumentPrivate *priv = document->priv;
519
520         g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
521         g_return_val_if_fail (page_label != NULL, FALSE);
522         g_return_val_if_fail (page_index != NULL, FALSE);
523
524         /* First, look for a literal label match */
525         for (i = 0; priv->page_labels && i < priv->n_pages; i ++) {
526                 if (priv->page_labels[i] != NULL &&
527                     ! strcmp (page_label, priv->page_labels[i])) {
528                         *page_index = i;
529                         return TRUE;
530                 }
531         }
532
533         /* Second, look for a match with case insensitively */
534         for (i = 0; priv->page_labels && i < priv->n_pages; i++) {
535                 if (priv->page_labels[i] != NULL &&
536                     ! strcasecmp (page_label, priv->page_labels[i])) {
537                         *page_index = i;
538                         return TRUE;
539                 }
540         }
541
542         /* Next, parse the label, and see if the number fits */
543         value = strtol (page_label, &endptr, 10);
544         if (endptr[0] == '\0') {
545                 /* Page number is an integer */
546                 page = MIN (G_MAXINT, value);
547
548                 /* convert from a page label to a page offset */
549                 page --;
550                 if (page >= 0 && page < priv->n_pages) {
551                         *page_index = page;
552                         return TRUE;
553                 }
554         }
555
556         return FALSE;
557 }
558
559 /* EvDocumentInfo */
560 EV_DEFINE_BOXED_TYPE (EvDocumentInfo, ev_document_info, ev_document_info_copy, ev_document_info_free)
561
562 EvDocumentInfo *
563 ev_document_info_copy (EvDocumentInfo *info)
564 {
565         EvDocumentInfo *copy;
566         
567         g_return_val_if_fail (info != NULL, NULL);
568
569         copy = g_new0 (EvDocumentInfo, 1);
570         copy->title = g_strdup (info->title);
571         copy->format = g_strdup (info->format);
572         copy->author = g_strdup (info->author);
573         copy->subject = g_strdup (info->subject);
574         copy->keywords = g_strdup (info->keywords);
575         copy->security = g_strdup (info->security);
576         copy->creator = g_strdup (info->creator);
577         copy->producer = g_strdup (info->producer);
578         copy->linearized = g_strdup (info->linearized);
579         
580         copy->creation_date = info->creation_date;
581         copy->modified_date = info->modified_date;
582         copy->layout = info->layout;
583         copy->mode = info->mode;
584         copy->ui_hints = info->ui_hints;
585         copy->permissions = info->permissions;
586         copy->n_pages = info->n_pages;
587         copy->license = ev_document_license_copy (info->license);
588
589         copy->fields_mask = info->fields_mask;
590
591         return copy;
592 }
593
594 void
595 ev_document_info_free (EvDocumentInfo *info)
596 {
597         if (info == NULL)
598                 return;
599
600         g_free (info->title);
601         g_free (info->format);
602         g_free (info->author);
603         g_free (info->subject);
604         g_free (info->keywords);
605         g_free (info->creator);
606         g_free (info->producer);
607         g_free (info->linearized);
608         g_free (info->security);
609         ev_document_license_free (info->license);
610
611         g_free (info);
612 }
613
614 /* EvDocumentLicense */
615 EV_DEFINE_BOXED_TYPE (EvDocumentLicense, ev_document_license, ev_document_license_copy, ev_document_license_free)
616
617 EvDocumentLicense *
618 ev_document_license_new (void)
619 {
620         return g_new0 (EvDocumentLicense, 1);
621 }
622
623 EvDocumentLicense *
624 ev_document_license_copy (EvDocumentLicense *license)
625 {
626         EvDocumentLicense *new_license;
627
628         if (!license)
629                 return NULL;
630
631         new_license = ev_document_license_new ();
632
633         if (license->text)
634                 new_license->text = g_strdup (license->text);
635         if (license->uri)
636                 new_license->uri = g_strdup (license->uri);
637         if (license->web_statement)
638                 new_license->web_statement = g_strdup (license->web_statement);
639
640         return new_license;
641 }
642
643 void
644 ev_document_license_free (EvDocumentLicense *license)
645 {
646         if (!license)
647                 return;
648
649         g_free (license->text);
650         g_free (license->uri);
651         g_free (license->web_statement);
652
653         g_free (license);
654 }
655
656 const gchar *
657 ev_document_license_get_text (EvDocumentLicense *license)
658 {
659         return license->text;
660 }
661
662 const gchar *
663 ev_document_license_get_uri (EvDocumentLicense *license)
664 {
665         return license->uri;
666 }
667
668 const gchar *
669 ev_document_license_get_web_statement (EvDocumentLicense *license)
670 {
671         return license->web_statement;
672 }
673
674 /* EvRectangle */
675 EV_DEFINE_BOXED_TYPE (EvRectangle, ev_rectangle, ev_rectangle_copy, ev_rectangle_free)
676
677 EvRectangle *
678 ev_rectangle_new (void)
679 {
680         return g_new0 (EvRectangle, 1);
681 }
682
683 EvRectangle *
684 ev_rectangle_copy (EvRectangle *rectangle)
685 {
686         EvRectangle *new_rectangle;
687
688         g_return_val_if_fail (rectangle != NULL, NULL);
689
690         new_rectangle = g_new (EvRectangle, 1);
691         *new_rectangle = *rectangle;
692
693         return new_rectangle;
694 }
695
696 void
697 ev_rectangle_free (EvRectangle *rectangle)
698 {
699         g_free (rectangle);
700 }
701
702 /* Compares two rects.  returns 0 if they're equal */
703 #define EPSILON 0.0000001
704
705 gint
706 ev_rect_cmp (EvRectangle *a,
707              EvRectangle *b)
708 {
709         if (a == b)
710                 return 0;
711         if (a == NULL || b == NULL)
712                 return 1;
713
714         return ! ((ABS (a->x1 - b->x1) < EPSILON) &&
715                   (ABS (a->y1 - b->y1) < EPSILON) &&
716                   (ABS (a->x2 - b->x2) < EPSILON) &&
717                   (ABS (a->y2 - b->y2) < EPSILON));
718 }