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