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
xps_document_init(XPSDocument * ps_document)55 xps_document_init (XPSDocument *ps_document)
56 {
57 }
58
59 static void
xps_document_dispose(GObject * object)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
xps_document_load(EvDocument * document,const char * uri,GError ** error)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
xps_document_save(EvDocument * document,const char * uri,GError ** error)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
xps_document_get_n_pages(EvDocument * document)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 *
xps_document_get_page(EvDocument * document,gint index)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
xps_document_get_page_size(EvDocument * document,EvPage * page,double * width,double * height)154 xps_document_get_page_size (EvDocument *document,
155 EvPage *page,
156 double *width,
157 double *height)
158 {
159 gxps_page_get_size (GXPS_PAGE (page->backend_page), width, height);
160 }
161
162 static EvDocumentInfo *
xps_document_get_info(EvDocument * document)163 xps_document_get_info (EvDocument *document)
164 {
165 XPSDocument *xps = XPS_DOCUMENT (document);
166 EvDocumentInfo *info;
167
168 info = g_new0 (EvDocumentInfo, 1);
169 info->fields_mask =
170 EV_DOCUMENT_INFO_N_PAGES |
171 EV_DOCUMENT_INFO_PAPER_SIZE;
172
173
174 info->n_pages = gxps_document_get_n_pages (xps->doc);
175 if (info->n_pages > 0) {
176 GXPSPage *gxps_page;
177
178 gxps_page = gxps_document_get_page (xps->doc, 0, NULL);
179 gxps_page_get_size (gxps_page, &(info->paper_width), &(info->paper_height));
180 g_object_unref (gxps_page);
181
182 info->paper_width = info->paper_width / 96.0f * 25.4f;
183 info->paper_height = info->paper_height / 96.0f * 25.4f;
184 }
185
186 return info;
187 }
188
189 static gboolean
xps_document_get_backend_info(EvDocument * document,EvDocumentBackendInfo * info)190 xps_document_get_backend_info (EvDocument *document,
191 EvDocumentBackendInfo *info)
192 {
193 info->name = "libgxps";
194 info->version = GXPS_VERSION_STRING;
195
196 return TRUE;
197 }
198
199 static cairo_surface_t *
xps_document_render(EvDocument * document,EvRenderContext * rc)200 xps_document_render (EvDocument *document,
201 EvRenderContext *rc)
202 {
203 GXPSPage *xps_page;
204 gdouble page_width, page_height;
205 gint width, height;
206 double scale_x, scale_y;
207 cairo_surface_t *surface;
208 cairo_t *cr;
209 GError *error = NULL;
210
211 xps_page = GXPS_PAGE (rc->page->backend_page);
212
213 gxps_page_get_size (xps_page, &page_width, &page_height);
214 ev_render_context_compute_transformed_size (rc, page_width, page_height,
215 &width, &height);
216
217 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
218 width, height);
219 cr = cairo_create (surface);
220
221 cairo_set_source_rgb (cr, 1., 1., 1.);
222 cairo_paint (cr);
223
224 switch (rc->rotation) {
225 case 90:
226 cairo_translate (cr, width, 0);
227 break;
228 case 180:
229 cairo_translate (cr, width, height);
230 break;
231 case 270:
232 cairo_translate (cr, 0, height);
233 break;
234 default:
235 cairo_translate (cr, 0, 0);
236 }
237
238 ev_render_context_compute_scales (rc, page_width, page_height,
239 &scale_x, &scale_y);
240 cairo_scale (cr, scale_x, scale_y);
241
242 cairo_rotate (cr, rc->rotation * G_PI / 180.0);
243 gxps_page_render (xps_page, cr, &error);
244 cairo_destroy (cr);
245
246 if (error) {
247 g_warning ("Error rendering page %d: %s\n",
248 rc->page->index, error->message);
249 g_error_free (error);
250 }
251
252 return surface;
253 }
254
255 static void
xps_document_class_init(XPSDocumentClass * klass)256 xps_document_class_init (XPSDocumentClass *klass)
257 {
258 GObjectClass *object_class = G_OBJECT_CLASS (klass);
259 EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
260
261 object_class->dispose = xps_document_dispose;
262
263 ev_document_class->load = xps_document_load;
264 ev_document_class->save = xps_document_save;
265 ev_document_class->get_n_pages = xps_document_get_n_pages;
266 ev_document_class->get_page = xps_document_get_page;
267 ev_document_class->get_page_size = xps_document_get_page_size;
268 ev_document_class->get_info = xps_document_get_info;
269 ev_document_class->get_backend_info = xps_document_get_backend_info;
270 ev_document_class->render = xps_document_render;
271 }
272
273 /* EvDocumentLinks */
274 static gboolean
xps_document_links_has_document_links(EvDocumentLinks * document_links)275 xps_document_links_has_document_links (EvDocumentLinks *document_links)
276 {
277 XPSDocument *xps_document = XPS_DOCUMENT (document_links);
278 GXPSDocumentStructure *structure;
279 gboolean retval;
280
281 structure = gxps_document_get_structure (xps_document->doc);
282 if (!structure)
283 return FALSE;
284
285 retval = gxps_document_structure_has_outline (structure);
286 g_object_unref (structure);
287
288 return retval;
289 }
290
291 static EvLinkAction *
link_action_from_target(XPSDocument * xps_document,GXPSLinkTarget * target)292 link_action_from_target (XPSDocument *xps_document,
293 GXPSLinkTarget *target)
294 {
295 EvLinkAction *ev_action;
296
297 if (gxps_link_target_is_internal (target)) {
298 EvLinkDest *dest = NULL;
299 gint doc;
300 const gchar *anchor;
301
302 anchor = gxps_link_target_get_anchor (target);
303
304 /* FIXME: multidoc */
305 doc = gxps_file_get_document_for_link_target (xps_document->xps, target);
306 if (doc == 0) {
307 if (!anchor)
308 return NULL;
309
310 dest = ev_link_dest_new_named (anchor);
311 ev_action = ev_link_action_new_dest (dest);
312 g_object_unref (dest);
313 } else if (doc == -1 && anchor &&
314 gxps_document_get_page_for_anchor (xps_document->doc, anchor) >= 0) {
315 /* Internal, but source is not a doc,
316 * let's try with doc = 0
317 */
318 dest = ev_link_dest_new_named (anchor);
319 ev_action = ev_link_action_new_dest (dest);
320 g_object_unref (dest);
321 } else {
322 gchar *filename;
323
324 /* FIXME: remote uri? */
325 filename = g_file_get_path (xps_document->file);
326
327 if (anchor)
328 dest = ev_link_dest_new_named (anchor);
329 ev_action = ev_link_action_new_remote (dest, filename);
330 g_clear_object (&dest);
331 g_free (filename);
332 }
333 } else {
334 const gchar *uri;
335
336 uri = gxps_link_target_get_uri (target);
337 ev_action = ev_link_action_new_external_uri (uri);
338 }
339
340 return ev_action;
341 }
342
343 static void
build_tree(XPSDocument * xps_document,GtkTreeModel * model,GtkTreeIter * parent,GXPSOutlineIter * iter)344 build_tree (XPSDocument *xps_document,
345 GtkTreeModel *model,
346 GtkTreeIter *parent,
347 GXPSOutlineIter *iter)
348 {
349 do {
350 GtkTreeIter tree_iter;
351 GXPSOutlineIter child_iter;
352 EvLinkAction *action;
353 EvLink *link;
354 GXPSLinkTarget *target;
355 gchar *title;
356
357 target = gxps_outline_iter_get_target (iter);
358 title = g_markup_escape_text (gxps_outline_iter_get_description (iter), -1);
359 action = link_action_from_target (xps_document, target);
360 link = ev_link_new (title, action);
361 g_object_unref (action);
362 gxps_link_target_free (target);
363
364 gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
365 gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
366 EV_DOCUMENT_LINKS_COLUMN_MARKUP, title,
367 EV_DOCUMENT_LINKS_COLUMN_LINK, link,
368 EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
369 -1);
370 g_object_unref (link);
371 g_free (title);
372
373 if (gxps_outline_iter_children (&child_iter, iter))
374 build_tree (xps_document, model, &tree_iter, &child_iter);
375 } while (gxps_outline_iter_next (iter));
376 }
377
378 static GtkTreeModel *
xps_document_links_get_links_model(EvDocumentLinks * document_links)379 xps_document_links_get_links_model (EvDocumentLinks *document_links)
380 {
381 XPSDocument *xps_document = XPS_DOCUMENT (document_links);
382 GXPSDocumentStructure *structure;
383 GXPSOutlineIter iter;
384 GtkTreeModel *model = NULL;
385
386 structure = gxps_document_get_structure (xps_document->doc);
387 if (!structure)
388 return NULL;
389
390 if (gxps_document_structure_outline_iter_init (&iter, structure)) {
391 model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
392 G_TYPE_STRING,
393 G_TYPE_OBJECT,
394 G_TYPE_BOOLEAN,
395 G_TYPE_STRING);
396 build_tree (xps_document, model, NULL, &iter);
397 }
398
399 g_object_unref (structure);
400
401 return model;
402 }
403
404 static EvMappingList *
xps_document_links_get_links(EvDocumentLinks * document_links,EvPage * page)405 xps_document_links_get_links (EvDocumentLinks *document_links,
406 EvPage *page)
407 {
408 XPSDocument *xps_document = XPS_DOCUMENT (document_links);
409 GXPSPage *xps_page;
410 GList *retval = NULL;
411 GList *mapping_list;
412 GList *list;
413
414 xps_page = GXPS_PAGE (page->backend_page);
415 mapping_list = gxps_page_get_links (xps_page, NULL);
416
417 for (list = mapping_list; list; list = list->next) {
418 GXPSLink *xps_link;
419 GXPSLinkTarget *target;
420 EvLinkAction *action;
421 EvMapping *ev_link_mapping;
422 cairo_rectangle_t area;
423
424 xps_link = (GXPSLink *)list->data;
425 ev_link_mapping = g_new (EvMapping, 1);
426 gxps_link_get_area (xps_link, &area);
427 target = gxps_link_get_target (xps_link);
428 action = link_action_from_target (xps_document, target);
429
430 ev_link_mapping->data = ev_link_new (NULL, action);
431 ev_link_mapping->area.x1 = area.x;
432 ev_link_mapping->area.x2 = area.x + area.width;
433 ev_link_mapping->area.y1 = area.y;
434 ev_link_mapping->area.y2 = area.y + area.height;
435
436 retval = g_list_prepend (retval, ev_link_mapping);
437 gxps_link_free (xps_link);
438 g_object_unref (action);
439 }
440
441 g_list_free (mapping_list);
442
443 return ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref);
444 }
445
446 static EvLinkDest *
xps_document_links_find_link_dest(EvDocumentLinks * document_links,const gchar * link_name)447 xps_document_links_find_link_dest (EvDocumentLinks *document_links,
448 const gchar *link_name)
449 {
450 XPSDocument *xps_document = XPS_DOCUMENT (document_links);
451 GXPSPage *xps_page;
452 gint page;
453 cairo_rectangle_t area;
454 EvLinkDest *dest = NULL;
455
456 page = gxps_document_get_page_for_anchor (xps_document->doc, link_name);
457 if (page == -1)
458 return NULL;
459
460 xps_page = gxps_document_get_page (xps_document->doc, page, NULL);
461 if (!xps_page)
462 return NULL;
463
464 if (gxps_page_get_anchor_destination (xps_page, link_name, &area, NULL))
465 dest = ev_link_dest_new_xyz (page, area.x, area.y, 1., TRUE, TRUE, FALSE);
466
467 g_object_unref (xps_page);
468
469 return dest;
470 }
471
472 static gint
xps_document_links_find_link_page(EvDocumentLinks * document_links,const gchar * link_name)473 xps_document_links_find_link_page (EvDocumentLinks *document_links,
474 const gchar *link_name)
475 {
476 XPSDocument *xps_document = XPS_DOCUMENT (document_links);
477
478 return gxps_document_get_page_for_anchor (xps_document->doc, link_name);
479 }
480
481 static void
xps_document_document_links_iface_init(EvDocumentLinksInterface * iface)482 xps_document_document_links_iface_init (EvDocumentLinksInterface *iface)
483 {
484 iface->has_document_links = xps_document_links_has_document_links;
485 iface->get_links_model = xps_document_links_get_links_model;
486 iface->get_links = xps_document_links_get_links;
487 iface->find_link_dest = xps_document_links_find_link_dest;
488 iface->find_link_page = xps_document_links_find_link_page;
489 }
490
491 /* EvDocumentPrint */
492 static void
xps_document_print_print_page(EvDocumentPrint * document,EvPage * page,cairo_t * cr)493 xps_document_print_print_page (EvDocumentPrint *document,
494 EvPage *page,
495 cairo_t *cr)
496 {
497 GError *error = NULL;
498
499 gxps_page_render (GXPS_PAGE (page->backend_page), cr, &error);
500 if (error) {
501 g_warning ("Error rendering page %d for printing: %s\n",
502 page->index, error->message);
503 g_error_free (error);
504 }
505 }
506
507 static void
xps_document_document_print_iface_init(EvDocumentPrintInterface * iface)508 xps_document_document_print_iface_init (EvDocumentPrintInterface *iface)
509 {
510 iface->print_page = xps_document_print_print_page;
511 }
512