]> www.fi.muni.cz Git - evince.git/blob - backend/xps/xps-document.c
[xps] Fix internal links that point to a source that is not a document
[evince.git] / backend / xps / xps-document.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
4  *
5  * Evince is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Evince is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <config.h>
21
22 #include <glib/gi18n-lib.h>
23 #include <libgxps/gxps.h>
24
25 #include "xps-document.h"
26 #include "ev-document-links.h"
27 #include "ev-document-print.h"
28 #include "ev-document-misc.h"
29
30 struct _XPSDocument {
31         EvDocument    object;
32
33         GFile        *file;
34         GXPSFile     *xps;
35         GXPSDocument *doc;
36 };
37
38 struct _XPSDocumentClass {
39         EvDocumentClass parent_class;
40 };
41
42 static void xps_document_document_links_iface_init (EvDocumentLinksInterface *iface);
43 static void xps_document_document_print_iface_init (EvDocumentPrintInterface *iface);
44
45 EV_BACKEND_REGISTER_WITH_CODE (XPSDocument, xps_document,
46                {
47                        EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
48                                                        xps_document_document_links_iface_init);
49                        EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_PRINT,
50                                                        xps_document_document_print_iface_init);
51                })
52
53 /* XPSDocument */
54 static void
55 xps_document_init (XPSDocument *ps_document)
56 {
57 }
58
59 static void
60 xps_document_dispose (GObject *object)
61 {
62         XPSDocument *xps = XPS_DOCUMENT (object);
63
64         if (xps->file) {
65                 g_object_unref (xps->file);
66                 xps->file = NULL;
67         }
68
69         if (xps->xps) {
70                 g_object_unref (xps->xps);
71                 xps->xps = NULL;
72         }
73
74         if (xps->doc) {
75                 g_object_unref (xps->doc);
76                 xps->doc = NULL;
77         }
78
79         G_OBJECT_CLASS (xps_document_parent_class)->dispose (object);
80 }
81
82 /* EvDocumentIface */
83 static gboolean
84 xps_document_load (EvDocument *document,
85                    const char *uri,
86                    GError    **error)
87 {
88         XPSDocument *xps = XPS_DOCUMENT (document);
89
90         xps->file = g_file_new_for_uri (uri);
91         xps->xps = gxps_file_new (xps->file, error);
92
93         if (!xps->xps)
94                 return FALSE;
95
96         /* FIXME: what if there are multiple docs? */
97         xps->doc = gxps_file_get_document (xps->xps, 0, error);
98         if (!xps->doc) {
99                 g_object_unref (xps->xps);
100                 xps->xps = NULL;
101
102                 return FALSE;
103         }
104
105         return TRUE;
106 }
107
108 static gboolean
109 xps_document_save (EvDocument *document,
110                    const char *uri,
111                    GError    **error)
112 {
113         XPSDocument *xps = XPS_DOCUMENT (document);
114         GFile       *dest;
115         gboolean     retval;
116
117         dest = g_file_new_for_uri (uri);
118         retval = g_file_copy (xps->file, dest,
119                               G_FILE_COPY_TARGET_DEFAULT_PERMS |
120                               G_FILE_COPY_OVERWRITE,
121                               NULL, NULL, NULL, error);
122         g_object_unref (dest);
123
124         return retval;
125 }
126
127 static gint
128 xps_document_get_n_pages (EvDocument *document)
129 {
130         XPSDocument *xps = XPS_DOCUMENT (document);
131
132         return gxps_document_get_n_pages (xps->doc);
133 }
134
135 static EvPage *
136 xps_document_get_page (EvDocument *document,
137                        gint        index)
138 {
139         XPSDocument *xps = XPS_DOCUMENT (document);
140         GXPSPage    *xps_page;
141         EvPage      *page;
142
143         xps_page = gxps_document_get_page (xps->doc, index, NULL);
144         page = ev_page_new (index);
145         if (xps_page) {
146                 page->backend_page = (EvBackendPage)xps_page;
147                 page->backend_destroy_func = (EvBackendPageDestroyFunc)g_object_unref;
148         }
149
150         return page;
151 }
152
153 static void
154 xps_document_get_page_size (EvDocument *document,
155                             EvPage     *page,
156                             double     *width,
157                             double     *height)
158 {
159         GXPSPage *xps_page;
160         guint     w, h;
161
162         xps_page = GXPS_PAGE (page->backend_page);
163
164         gxps_page_get_size (xps_page, &w, &h);
165
166         if (width)
167                 *width = (gdouble)w;
168         if (height)
169                 *height = (gdouble)h;
170 }
171
172 static EvDocumentInfo *
173 xps_document_get_info (EvDocument *document)
174 {
175         XPSDocument    *xps = XPS_DOCUMENT (document);
176         EvDocumentInfo *info;
177
178         info = g_new0 (EvDocumentInfo, 1);
179         info->fields_mask =
180                 EV_DOCUMENT_INFO_N_PAGES |
181                 EV_DOCUMENT_INFO_PAPER_SIZE;
182
183
184         if (gxps_document_get_n_pages (xps->doc) > 0) {
185                 ev_document_get_page_size (document, 0,
186                                            &(info->paper_width),
187                                            &(info->paper_height));
188                 info->paper_width  = info->paper_width / 96.0f * 25.4f;
189                 info->paper_height = info->paper_height / 96.0f * 25.4f;
190         }
191
192         info->n_pages = gxps_document_get_n_pages (xps->doc);
193
194         return info;
195 }
196
197 static gboolean
198 xps_document_get_backend_info (EvDocument            *document,
199                                EvDocumentBackendInfo *info)
200 {
201         info->name = "libgxps";
202         /* FIXME */
203         info->version = "";
204
205         return TRUE;
206 }
207
208 static cairo_surface_t *
209 xps_document_render (EvDocument      *document,
210                      EvRenderContext *rc)
211 {
212         GXPSPage        *xps_page;
213         guint            page_width, page_height;
214         guint            width, height;
215         cairo_surface_t *surface;
216         cairo_t         *cr;
217         GError          *error = NULL;
218
219         xps_page = GXPS_PAGE (rc->page->backend_page);
220
221         gxps_page_get_size (xps_page, &page_width, &page_height);
222         if (rc->rotation == 90 || rc->rotation == 270) {
223                 width = (guint) ((page_height * rc->scale) + 0.5);
224                 height = (guint) ((page_width * rc->scale) + 0.5);
225         } else {
226                 width = (guint) ((page_width * rc->scale) + 0.5);
227                 height = (guint) ((page_height * rc->scale) + 0.5);
228         }
229
230         surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
231                                               width, height);
232         cr = cairo_create (surface);
233
234         cairo_set_source_rgb (cr, 1., 1., 1.);
235         cairo_paint (cr);
236
237         switch (rc->rotation) {
238         case 90:
239                 cairo_translate (cr, width, 0);
240                 break;
241         case 180:
242                 cairo_translate (cr, width, height);
243                 break;
244         case 270:
245                 cairo_translate (cr, 0, height);
246                 break;
247         default:
248                 cairo_translate (cr, 0, 0);
249         }
250
251         cairo_scale (cr, rc->scale, rc->scale);
252         cairo_rotate (cr, rc->rotation * G_PI / 180.0);
253         gxps_page_render (xps_page, cr, &error);
254         cairo_destroy (cr);
255
256         if (error) {
257                 g_warning ("Error rendering page %d: %s\n",
258                            rc->page->index, error->message);
259                 g_error_free (error);
260         }
261
262         return surface;
263 }
264
265 static void
266 xps_document_class_init (XPSDocumentClass *klass)
267 {
268         GObjectClass    *object_class = G_OBJECT_CLASS (klass);
269         EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
270
271         object_class->dispose = xps_document_dispose;
272
273         ev_document_class->load = xps_document_load;
274         ev_document_class->save = xps_document_save;
275         ev_document_class->get_n_pages = xps_document_get_n_pages;
276         ev_document_class->get_page = xps_document_get_page;
277         ev_document_class->get_page_size = xps_document_get_page_size;
278         ev_document_class->get_info = xps_document_get_info;
279         ev_document_class->get_backend_info = xps_document_get_backend_info;
280         ev_document_class->render = xps_document_render;
281 }
282
283 /* EvDocumentLinks */
284 static gboolean
285 xps_document_links_has_document_links (EvDocumentLinks *document_links)
286 {
287         XPSDocument           *xps_document = XPS_DOCUMENT (document_links);
288         GXPSDocumentStructure *structure;
289         gboolean               retval;
290
291         structure = gxps_document_get_structure (xps_document->doc);
292         if (!structure)
293                 return FALSE;
294
295         retval = gxps_document_structure_has_outline (structure);
296         g_object_unref (structure);
297
298         return retval;
299 }
300
301 static EvLink *
302 ev_link_from_target (XPSDocument    *xps_document,
303                      GXPSLinkTarget *target)
304 {
305         EvLinkAction *ev_action;
306
307         if (gxps_link_target_is_internal (target)) {
308                 EvLinkDest  *dest = NULL;
309                 guint        doc;
310                 const gchar *anchor;
311
312                 anchor = gxps_link_target_get_anchor (target);
313
314                 /* FIXME: multidoc */
315                 doc = gxps_file_get_document_for_link_target (xps_document->xps, target);
316                 if (doc == 0) {
317                         if (!anchor)
318                                 return NULL;
319
320                         dest = ev_link_dest_new_named (anchor);
321                         ev_action = ev_link_action_new_dest (dest);
322                 } else if (doc == -1 && anchor &&
323                            gxps_document_get_page_for_anchor (xps_document->doc, anchor) >= 0) {
324                         /* Internal, but source is not a doc,
325                          * let's try with doc = 0
326                          */
327                         dest = ev_link_dest_new_named (anchor);
328                         ev_action = ev_link_action_new_dest (dest);
329                 } else {
330                         gchar *filename;
331
332                         /* FIXME: remote uri? */
333                         filename = g_file_get_path (xps_document->file);
334
335                         if (anchor)
336                                 dest = ev_link_dest_new_named (anchor);
337                         ev_action = ev_link_action_new_remote (dest, filename);
338                         g_free (filename);
339                 }
340         } else {
341                 const gchar *uri;
342
343                 uri = gxps_link_target_get_uri (target);
344                 ev_action = ev_link_action_new_external_uri (uri);
345         }
346
347         return ev_link_new (NULL, ev_action);
348 }
349
350 static void
351 build_tree (XPSDocument     *xps_document,
352             GtkTreeModel    *model,
353             GtkTreeIter     *parent,
354             GXPSOutlineIter *iter)
355 {
356         do {
357                 GtkTreeIter     tree_iter;
358                 GXPSOutlineIter child_iter;
359                 EvLink         *link;
360                 GXPSLinkTarget *target;
361                 gchar          *title;
362
363                 target = gxps_outline_iter_get_target (iter);
364                 title = g_markup_escape_text (gxps_outline_iter_get_description (iter), -1);
365                 link = ev_link_from_target (xps_document, target);
366                 gxps_link_target_free (target);
367
368                 gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
369                 gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
370                                     EV_DOCUMENT_LINKS_COLUMN_MARKUP, title,
371                                     EV_DOCUMENT_LINKS_COLUMN_LINK, link,
372                                     EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
373                                     -1);
374                 g_object_unref (link);
375                 g_free (title);
376
377                 if (gxps_outline_iter_children (&child_iter, iter))
378                         build_tree (xps_document, model, &tree_iter, &child_iter);
379         } while (gxps_outline_iter_next (iter));
380 }
381
382 static GtkTreeModel *
383 xps_document_links_get_links_model (EvDocumentLinks *document_links)
384 {
385         XPSDocument           *xps_document = XPS_DOCUMENT (document_links);
386         GXPSDocumentStructure *structure;
387         GXPSOutlineIter        iter;
388         GtkTreeModel          *model = NULL;
389
390         structure = gxps_document_get_structure (xps_document->doc);
391         if (!structure)
392                 return NULL;
393
394         if (gxps_document_structure_outline_iter_init (&iter, structure)) {
395                 model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
396                                                              G_TYPE_STRING,
397                                                              G_TYPE_OBJECT,
398                                                              G_TYPE_BOOLEAN,
399                                                              G_TYPE_STRING);
400                 build_tree (xps_document, model, NULL, &iter);
401         }
402
403         g_object_unref (structure);
404
405         return model;
406 }
407
408 static EvMappingList *
409 xps_document_links_get_links (EvDocumentLinks *document_links,
410                               EvPage          *page)
411 {
412         XPSDocument *xps_document = XPS_DOCUMENT (document_links);
413         GXPSPage    *xps_page;
414         GList       *retval = NULL;
415         GList       *mapping_list;
416         GList       *list;
417
418         xps_page = GXPS_PAGE (page->backend_page);
419         mapping_list = gxps_page_get_links (xps_page, NULL);
420
421         for (list = mapping_list; list; list = list->next) {
422                 GXPSLink *xps_link;
423                 GXPSLinkTarget *target;
424                 EvMapping *ev_link_mapping;
425                 cairo_rectangle_t area;
426
427                 xps_link = (GXPSLink *)list->data;
428                 ev_link_mapping = g_new (EvMapping, 1);
429                 gxps_link_get_area (xps_link, &area);
430                 target = gxps_link_get_target (xps_link);
431                 gxps_link_get_area (xps_link, &area);
432                 ev_link_mapping->data = ev_link_from_target (xps_document, target);
433
434                 ev_link_mapping->area.x1 = area.x;
435                 ev_link_mapping->area.x2 = area.x + area.width;
436                 ev_link_mapping->area.y1 = area.y;
437                 ev_link_mapping->area.y2 = area.y + area.height;
438
439                 retval = g_list_prepend (retval, ev_link_mapping);
440                 gxps_link_free (xps_link);
441         }
442
443         g_list_free (mapping_list);
444
445         return ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref);
446 }
447
448 static EvLinkDest *
449 xps_document_links_find_link_dest (EvDocumentLinks *document_links,
450                                    const gchar     *link_name)
451 {
452         XPSDocument       *xps_document = XPS_DOCUMENT (document_links);
453         GXPSPage          *xps_page;
454         gint               page;
455         cairo_rectangle_t  area;
456         EvLinkDest        *dest = NULL;
457
458         page = gxps_document_get_page_for_anchor (xps_document->doc, link_name);
459         if (page == -1)
460                 return NULL;
461
462         xps_page = gxps_document_get_page (xps_document->doc, page, NULL);
463         if (!xps_page)
464                 return NULL;
465
466         if (gxps_page_get_anchor_destination (xps_page, link_name, &area, NULL))
467                 dest = ev_link_dest_new_xyz (page, area.x, area.y, 1., TRUE, TRUE, FALSE);
468
469         g_object_unref (xps_page);
470
471         return dest;
472 }
473
474 static gint
475 xps_document_links_find_link_page (EvDocumentLinks *document_links,
476                                    const gchar     *link_name)
477 {
478         XPSDocument *xps_document = XPS_DOCUMENT (document_links);
479
480         return gxps_document_get_page_for_anchor (xps_document->doc, link_name);
481 }
482
483 static void
484 xps_document_document_links_iface_init (EvDocumentLinksInterface *iface)
485 {
486         iface->has_document_links = xps_document_links_has_document_links;
487         iface->get_links_model = xps_document_links_get_links_model;
488         iface->get_links = xps_document_links_get_links;
489         iface->find_link_dest = xps_document_links_find_link_dest;
490         iface->find_link_page = xps_document_links_find_link_page;
491 }
492
493 /* EvDocumentPrint */
494 static void
495 xps_document_print_print_page (EvDocumentPrint *document,
496                                EvPage          *page,
497                                cairo_t         *cr)
498 {
499         GError *error = NULL;
500
501         gxps_page_render (GXPS_PAGE (page->backend_page), cr, &error);
502         if (error) {
503                 g_warning ("Error rendering page %d for printing: %s\n",
504                            page->index, error->message);
505                 g_error_free (error);
506         }
507 }
508
509 static void
510 xps_document_document_print_iface_init (EvDocumentPrintInterface *iface)
511 {
512         iface->print_page = xps_document_print_print_page;
513 }