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