]> www.fi.muni.cz Git - evince.git/blob - backend/ps/ev-spectre.c
Convert EvDocument interface into an abstract class
[evince.git] / backend / ps / ev-spectre.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21
22 #include <config.h>
23 #include <glib/gi18n-lib.h>
24 #include <stdlib.h>
25 #include <libspectre/spectre.h>
26
27 #include "ev-spectre.h"
28
29 #include "ev-file-exporter.h"
30 #include "ev-document-thumbnails.h"
31 #include "ev-document-misc.h"
32
33 struct _PSDocument {
34         EvDocument object;
35
36         SpectreDocument *doc;
37         SpectreExporter *exporter;
38 };
39
40 struct _PSDocumentClass {
41         EvDocumentClass parent_class;
42 };
43
44 static void ps_document_file_exporter_iface_init       (EvFileExporterIface       *iface);
45 static void ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
46
47 EV_BACKEND_REGISTER_WITH_CODE (PSDocument, ps_document,
48                          {
49                                  EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
50                                                                  ps_document_document_thumbnails_iface_init);
51                                  EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
52                                                                  ps_document_file_exporter_iface_init);
53                          });
54
55 /* PSDocument */
56 static void
57 ps_document_init (PSDocument *ps_document)
58 {
59 }
60
61 static void
62 ps_document_dispose (GObject *object)
63 {
64         PSDocument *ps = PS_DOCUMENT (object);
65
66         if (ps->doc) {
67                 spectre_document_free (ps->doc);
68                 ps->doc = NULL;
69         }
70
71         if (ps->exporter) {
72                 spectre_exporter_free (ps->exporter);
73                 ps->exporter = NULL;
74         }
75
76         G_OBJECT_CLASS (ps_document_parent_class)->dispose (object);
77 }
78
79 /* EvDocumentIface */
80 static gboolean
81 ps_document_load (EvDocument *document,
82                   const char *uri,
83                   GError    **error)
84 {
85         PSDocument *ps = PS_DOCUMENT (document);
86         gchar      *filename;
87
88         filename = g_filename_from_uri (uri, NULL, error);
89         if (!filename)
90                 return FALSE;
91         
92         ps->doc = spectre_document_new ();
93
94         spectre_document_load (ps->doc, filename);
95         if (spectre_document_status (ps->doc)) {
96                 gchar *filename_dsp;
97                 
98                 filename_dsp = g_filename_display_name (filename);
99                 g_set_error (error,
100                              G_FILE_ERROR,
101                              G_FILE_ERROR_FAILED,
102                              _("Failed to load document ā€œ%sā€"),
103                              filename_dsp);
104                 g_free (filename_dsp);
105                 g_free (filename);
106
107                 return FALSE;
108         }
109
110         g_free (filename);
111
112         return TRUE;
113 }
114
115 static gboolean
116 ps_document_save (EvDocument *document,
117                   const char *uri,
118                   GError    **error)
119 {
120         PSDocument *ps = PS_DOCUMENT (document);
121         gchar      *filename;
122
123         filename = g_filename_from_uri (uri, NULL, error);
124         if (!filename)
125                 return FALSE;
126
127         spectre_document_save (ps->doc, filename);
128         if (spectre_document_status (ps->doc)) {
129                 gchar *filename_dsp;
130
131                 filename_dsp = g_filename_display_name (filename);
132                 g_set_error (error,
133                              G_FILE_ERROR,
134                              G_FILE_ERROR_FAILED,
135                              _("Failed to save document ā€œ%sā€"),
136                              filename_dsp);
137                 g_free (filename_dsp);
138                 g_free (filename);
139
140                 return FALSE;
141         }
142
143         g_free (filename);
144
145         return TRUE;
146 }
147
148 static int
149 ps_document_get_n_pages (EvDocument *document)
150 {
151         PSDocument *ps = PS_DOCUMENT (document);
152
153         return spectre_document_get_n_pages (ps->doc);
154 }
155
156 static EvPage *
157 ps_document_get_page (EvDocument *document,
158                       gint        index)
159 {
160         PSDocument  *ps = PS_DOCUMENT (document);
161         SpectrePage *ps_page;
162         EvPage      *page;
163
164         ps_page = spectre_document_get_page (ps->doc, index);
165         page = ev_page_new (index);
166         page->backend_page = (EvBackendPage)ps_page;
167         page->backend_destroy_func = (EvBackendPageDestroyFunc)spectre_page_free;
168
169         return page;
170 }
171
172 static gint
173 get_page_rotation (SpectrePage *page)
174 {
175         switch (spectre_page_get_orientation (page)) {
176                 default:
177                 case SPECTRE_ORIENTATION_PORTRAIT:
178                         return 0;
179                 case SPECTRE_ORIENTATION_LANDSCAPE:
180                         return 90;
181                 case SPECTRE_ORIENTATION_REVERSE_PORTRAIT:
182                         return 180;
183                 case SPECTRE_ORIENTATION_REVERSE_LANDSCAPE:
184                         return 270;
185         }
186
187         return 0;
188 }
189
190 static void
191 ps_document_get_page_size (EvDocument *document,
192                            EvPage     *page,
193                            double     *width,
194                            double     *height)
195 {
196         SpectrePage *ps_page;
197         gdouble      page_width, page_height;
198         gint         pwidth, pheight;
199         gint         rotate;
200
201         ps_page = (SpectrePage *)page->backend_page;
202
203         spectre_page_get_size (ps_page, &pwidth, &pheight);
204
205         rotate = get_page_rotation (ps_page);
206         if (rotate == 90 || rotate == 270) {
207                 page_height = pwidth;
208                 page_width = pheight;
209         } else {
210                 page_width = pwidth;
211                 page_height = pheight;
212         }
213
214         if (width) {
215                 *width = page_width;
216         }
217
218         if (height) {
219                 *height = page_height;
220         }
221 }
222
223 static char *
224 ps_document_get_page_label (EvDocument *document,
225                             EvPage     *page)
226 {
227         return g_strdup (spectre_page_get_label ((SpectrePage *)page->backend_page));
228 }
229
230 static EvDocumentInfo *
231 ps_document_get_info (EvDocument *document)
232 {
233         PSDocument     *ps = PS_DOCUMENT (document);
234         EvDocumentInfo *info;
235         const gchar    *creator;
236         SpectrePage    *ps_page;
237         gint            width, height;
238
239         info = g_new0 (EvDocumentInfo, 1);
240         info->fields_mask = EV_DOCUMENT_INFO_TITLE |
241                             EV_DOCUMENT_INFO_FORMAT |
242                             EV_DOCUMENT_INFO_CREATOR |
243                             EV_DOCUMENT_INFO_N_PAGES |
244                             EV_DOCUMENT_INFO_PAPER_SIZE;
245
246         creator = spectre_document_get_creator (ps->doc);
247
248         ps_page = spectre_document_get_page (ps->doc, 0);
249         spectre_page_get_size (ps_page, &width, &height);
250         spectre_page_free (ps_page);
251         
252         info->title = g_strdup (spectre_document_get_title (ps->doc));
253         info->format = g_strdup (spectre_document_get_format (ps->doc));
254         info->creator = g_strdup (creator ? creator : spectre_document_get_for (ps->doc));
255         info->n_pages = spectre_document_get_n_pages (ps->doc);
256         info->paper_width  = width / 72.0f * 25.4f;
257         info->paper_height = height / 72.0f * 25.4f;
258
259         return info;
260 }
261
262 static cairo_surface_t *
263 ps_document_render (EvDocument      *document,
264                     EvRenderContext *rc)
265 {
266         SpectrePage          *ps_page;
267         SpectreRenderContext *src;
268         gint                  width_points;
269         gint                  height_points;
270         gint                  width, height;
271         gint                  swidth, sheight;
272         guchar               *data = NULL;
273         gint                  stride;
274         gint                  rotation;
275         cairo_surface_t      *surface;
276         static const cairo_user_data_key_t key;
277
278         ps_page = (SpectrePage *)rc->page->backend_page;
279         
280         spectre_page_get_size (ps_page, &width_points, &height_points);
281
282         width = (gint) ((width_points * rc->scale) + 0.5);
283         height = (gint) ((height_points * rc->scale) + 0.5);
284         rotation = (rc->rotation + get_page_rotation (ps_page)) % 360;
285
286         src = spectre_render_context_new ();
287         spectre_render_context_set_scale (src,
288                                           (gdouble)width / width_points,
289                                           (gdouble)height / height_points);
290         spectre_render_context_set_rotation (src, rotation);
291         spectre_page_render (ps_page, src, &data, &stride);
292         spectre_render_context_free (src);
293
294         if (!data) {
295                 return NULL;
296         }
297
298         if (spectre_page_status (ps_page)) {
299                 g_warning ("%s", spectre_status_to_string (spectre_page_status (ps_page)));
300                 g_free (data);
301                 
302                 return NULL;
303         }
304
305         if (rotation == 90 || rotation == 270) {
306                 swidth = height;
307                 sheight = width;
308         } else {
309                 swidth = width;
310                 sheight = height;
311         }
312         
313         surface = cairo_image_surface_create_for_data (data,
314                                                        CAIRO_FORMAT_RGB24,
315                                                        swidth, sheight,
316                                                        stride);
317         cairo_surface_set_user_data (surface, &key,
318                                      data, (cairo_destroy_func_t)g_free);
319         return surface;
320 }
321
322 static void
323 ps_document_class_init (PSDocumentClass *klass)
324 {
325         GObjectClass    *object_class = G_OBJECT_CLASS (klass);
326         EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
327
328         object_class->dispose = ps_document_dispose;
329
330         ev_document_class->load = ps_document_load;
331         ev_document_class->save = ps_document_save;
332         ev_document_class->get_n_pages = ps_document_get_n_pages;
333         ev_document_class->get_page = ps_document_get_page;
334         ev_document_class->get_page_size = ps_document_get_page_size;
335         ev_document_class->get_page_label = ps_document_get_page_label;
336         ev_document_class->get_info = ps_document_get_info;
337         ev_document_class->render = ps_document_render;
338 }
339
340 /* EvDocumentThumbnailsIface */
341 static GdkPixbuf *
342 ps_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
343                                       EvRenderContext      *rc, 
344                                       gboolean              border)
345 {
346         PSDocument      *ps = PS_DOCUMENT (document_thumbnails);
347         cairo_surface_t *surface;
348         GdkPixbuf       *pixbuf = NULL;
349
350         surface = ps_document_render (EV_DOCUMENT (ps), rc);
351         if (!surface) {
352                 g_warning ("Error rendering thumbnail");
353                 return NULL;
354         }
355                 
356         pixbuf = ev_document_misc_pixbuf_from_surface (surface);
357         cairo_surface_destroy (surface);
358
359         if (border) {
360                 GdkPixbuf *border_pixbuf;
361                 
362                 border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
363                 g_object_unref (pixbuf);
364                 pixbuf = border_pixbuf;
365         }
366
367         return pixbuf;
368 }
369
370 static void
371 ps_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
372                                        EvRenderContext      *rc, 
373                                        gint                 *width,
374                                        gint                 *height)
375 {
376         PSDocument *ps = PS_DOCUMENT (document_thumbnails);
377         gdouble     page_width, page_height;
378
379         ps_document_get_page_size (EV_DOCUMENT (ps),
380                                    rc->page,
381                                    &page_width, &page_height);
382
383         if (rc->rotation == 90 || rc->rotation == 270) {
384                 *width = (gint) (page_height * rc->scale);
385                 *height = (gint) (page_width * rc->scale);
386         } else {
387                 *width = (gint) (page_width * rc->scale);
388                 *height = (gint) (page_height * rc->scale);
389         }
390 }
391
392 static void
393 ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
394 {
395         iface->get_thumbnail = ps_document_thumbnails_get_thumbnail;
396         iface->get_dimensions = ps_document_thumbnails_get_dimensions;
397 }
398         
399 /* EvFileExporterIface */
400 static void
401 ps_document_file_exporter_begin (EvFileExporter        *exporter,
402                                  EvFileExporterContext *fc)
403 {
404         PSDocument *ps = PS_DOCUMENT (exporter);
405
406         if (ps->exporter)
407                 spectre_exporter_free (ps->exporter);
408
409         switch (fc->format) {
410                 case EV_FILE_FORMAT_PS:
411                         ps->exporter =
412                                 spectre_exporter_new (ps->doc,
413                                                       SPECTRE_EXPORTER_FORMAT_PS);
414                         break;
415                 case EV_FILE_FORMAT_PDF:
416                         ps->exporter =
417                                 spectre_exporter_new (ps->doc,
418                                                       SPECTRE_EXPORTER_FORMAT_PDF);
419                         break;
420                 default:
421                         g_assert_not_reached ();
422         }
423
424         spectre_exporter_begin (ps->exporter, fc->filename);
425 }
426
427 static void
428 ps_document_file_exporter_do_page (EvFileExporter  *exporter,
429                                    EvRenderContext *rc)
430 {
431         PSDocument *ps = PS_DOCUMENT (exporter);
432
433         spectre_exporter_do_page (ps->exporter, rc->page->index);
434 }
435
436 static void
437 ps_document_file_exporter_end (EvFileExporter *exporter)
438 {
439         PSDocument *ps = PS_DOCUMENT (exporter);
440
441         spectre_exporter_end (ps->exporter);
442 }
443
444 static EvFileExporterCapabilities
445 ps_document_file_exporter_get_capabilities (EvFileExporter *exporter)
446 {
447         return  EV_FILE_EXPORTER_CAN_PAGE_SET |
448                 EV_FILE_EXPORTER_CAN_COPIES |
449                 EV_FILE_EXPORTER_CAN_COLLATE |
450                 EV_FILE_EXPORTER_CAN_REVERSE |
451                 EV_FILE_EXPORTER_CAN_GENERATE_PS |
452                 EV_FILE_EXPORTER_CAN_GENERATE_PDF;
453 }
454
455 static void
456 ps_document_file_exporter_iface_init (EvFileExporterIface *iface)
457 {
458         iface->begin = ps_document_file_exporter_begin;
459         iface->do_page = ps_document_file_exporter_do_page;
460         iface->end = ps_document_file_exporter_end;
461         iface->get_capabilities = ps_document_file_exporter_get_capabilities;
462 }