]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/dvi-document.c
Removed Added
[evince.git] / backend / dvi / dvi-document.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2005, Nickolay V. Shmyrev <nshmyrev@yandex.ru>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU 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 "dvi-document.h"
21 #include "ev-document-thumbnails.h"
22 #include "ev-document-misc.h"
23
24 #include "mdvi.h"
25 #include "fonts.h"
26 #include "cairo-device.h"
27
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30
31 GMutex *dvi_context_mutex = NULL;
32
33 enum {
34         PROP_0,
35         PROP_TITLE
36 };
37
38 struct _DviDocumentClass
39 {
40         GObjectClass parent_class;
41 };
42
43 struct _DviDocument
44 {
45         GObject parent_instance;
46
47         DviContext *context;
48         DviPageSpec *spec;
49         DviParams *params;
50         
51         /* To let document scale we should remember width and height */
52         double base_width;
53         double base_height;
54         
55         gchar *uri;
56 };
57
58 typedef struct _DviDocumentClass DviDocumentClass;
59
60 static void dvi_document_document_iface_init            (EvDocumentIface           *iface);
61 static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
62 static void dvi_document_get_page_size                  (EvDocument                *document,
63                                                          int                        page,
64                                                          double                    *width,
65                                                          double                    *height);
66 static void dvi_document_do_color_special               (DviContext                *dvi,
67                                                          const char                *prefix,
68                                                          const char                *arg);
69
70 G_DEFINE_TYPE_WITH_CODE 
71     (DviDocument, dvi_document, G_TYPE_OBJECT, 
72     {
73       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT, dvi_document_document_iface_init);    
74       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_iface_init)
75      });
76
77 static gboolean
78 dvi_document_load (EvDocument  *document,
79                    const char  *uri,
80                    GError     **error)
81 {
82         gchar *filename;
83         DviDocument *dvi_document = DVI_DOCUMENT(document);
84         
85         filename = g_filename_from_uri (uri, NULL, error);
86         
87         if (!filename) {
88                 g_set_error (error,
89                              EV_DOCUMENT_ERROR,
90                              EV_DOCUMENT_ERROR_INVALID,
91                              _("File not available"));
92                 return FALSE;
93         }
94         
95         g_mutex_lock (dvi_context_mutex);
96         if (dvi_document->context)
97                 mdvi_destroy_context (dvi_document->context);
98         
99         dvi_document->context = mdvi_init_context(dvi_document->params, dvi_document->spec, filename);
100         g_mutex_unlock (dvi_context_mutex);
101         
102         if (!dvi_document->context) {
103                 g_set_error (error,
104                              EV_DOCUMENT_ERROR,
105                              EV_DOCUMENT_ERROR_INVALID,
106                              _("DVI document has incorrect format"));
107                 return FALSE;
108         }
109         
110         mdvi_cairo_device_init (&dvi_document->context->device);
111         
112         
113         dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv 
114                 + 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
115         
116         dvi_document->base_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv 
117                 + 2 * unit2pix(dvi_document->params->vdpi, MDVI_VMARGIN) / dvi_document->params->vshrink;
118         
119         g_free (dvi_document->uri);
120         dvi_document->uri = g_strdup (uri);
121         
122         return TRUE;
123 }
124
125
126 static gboolean
127 dvi_document_save (EvDocument  *document,
128                       const char  *uri,
129                       GError     **error)
130 {
131         DviDocument *dvi_document = DVI_DOCUMENT (document);
132
133         return ev_xfer_uri_simple (dvi_document->uri, uri, error);
134 }
135
136 static int
137 dvi_document_get_n_pages (EvDocument *document)
138 {
139         DviDocument *dvi_document = DVI_DOCUMENT (document);
140         
141         return dvi_document->context->npages;
142 }
143
144 static void
145 dvi_document_get_page_size (EvDocument *document,
146                             int         page,
147                             double     *width,
148                             double     *height)
149 {
150         DviDocument *dvi_document = DVI_DOCUMENT (document);    
151
152         *width = dvi_document->base_width;
153         *height = dvi_document->base_height;;
154 }
155
156 static cairo_surface_t *
157 dvi_document_render (EvDocument      *document,
158                      EvRenderContext *rc)
159 {
160         cairo_surface_t *surface;
161         cairo_surface_t *rotated_surface;
162         DviDocument *dvi_document = DVI_DOCUMENT(document);
163         gint required_width, required_height;
164         gint proposed_width, proposed_height;
165         gint xmargin = 0, ymargin = 0;
166
167         /* We should protect our context since it's not 
168          * thread safe. The work to the future - 
169          * let context render page independently
170          */
171         g_mutex_lock (dvi_context_mutex);
172         
173         mdvi_setpage (dvi_document->context, rc->page);
174         
175         mdvi_set_shrink (dvi_document->context, 
176                          (int)((dvi_document->params->hshrink - 1) / rc->scale) + 1,
177                          (int)((dvi_document->params->vshrink - 1) / rc->scale) + 1);
178
179         required_width = dvi_document->base_width * rc->scale + 0.5;
180         required_height = dvi_document->base_height * rc->scale + 0.5;
181         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
182         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
183         
184         if (required_width >= proposed_width)
185             xmargin = (required_width - proposed_width) / 2;
186         if (required_height >= proposed_height)
187             ymargin = (required_height - proposed_height) / 2;
188             
189         mdvi_cairo_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
190         mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
191         mdvi_cairo_device_render (dvi_document->context);
192         surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
193
194         g_mutex_unlock (dvi_context_mutex);
195
196         rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
197                                                                      required_width,
198                                                                      required_height, 
199                                                                      rc->rotation);
200         cairo_surface_destroy (surface);
201         
202         return rotated_surface;
203 }
204
205 static void
206 dvi_document_finalize (GObject *object)
207 {       
208         DviDocument *dvi_document = DVI_DOCUMENT(object);
209         
210         g_mutex_lock (dvi_context_mutex);
211         if (dvi_document->context) {
212                 mdvi_cairo_device_free (&dvi_document->context->device);
213                 mdvi_destroy_context (dvi_document->context);
214         }
215         g_mutex_unlock (dvi_context_mutex);
216
217         if (dvi_document->params)
218                 g_free (dvi_document->params);
219
220         g_free (dvi_document->uri);
221                 
222         G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
223 }
224
225
226 static void
227 dvi_document_class_init (DviDocumentClass *klass)
228 {
229         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
230
231         gobject_class->finalize = dvi_document_finalize;
232
233         mdvi_init_kpathsea ("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI);
234         mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1);
235         mdvi_register_fonts ();
236
237         dvi_context_mutex = g_mutex_new ();
238 }
239
240 static gboolean
241 dvi_document_can_get_text (EvDocument *document)
242 {
243         return FALSE;
244 }
245
246 static EvDocumentInfo *
247 dvi_document_get_info (EvDocument *document)
248 {
249         EvDocumentInfo *info;
250
251         info = g_new0 (EvDocumentInfo, 1);
252
253         return info;
254 }
255
256 static void
257 dvi_document_document_iface_init (EvDocumentIface *iface)
258 {
259         iface->load = dvi_document_load;
260         iface->save = dvi_document_save;
261         iface->can_get_text = dvi_document_can_get_text;
262         iface->get_n_pages = dvi_document_get_n_pages;
263         iface->get_page_size = dvi_document_get_page_size;
264         iface->render = dvi_document_render;
265         iface->get_info = dvi_document_get_info;
266 }
267
268 static void
269 dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
270                                         EvRenderContext      *rc, 
271                                         gint                  *width,
272                                         gint                  *height)
273 {       
274         DviDocument *dvi_document = DVI_DOCUMENT (document);
275         gdouble page_width = dvi_document->base_width;
276         gdouble page_height = dvi_document->base_height;
277
278         if (rc->rotation == 90 || rc->rotation == 270) {
279                 *width = (gint) (page_height * rc->scale);
280                 *height = (gint) (page_width * rc->scale);
281         } else {
282                 *width = (gint) (page_width * rc->scale);
283                 *height = (gint) (page_height * rc->scale);
284         }
285 }
286
287 static GdkPixbuf *
288 dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
289                                        EvRenderContext      *rc,   
290                                        gboolean              border)
291 {
292         DviDocument *dvi_document = DVI_DOCUMENT (document);
293         GdkPixbuf *pixbuf;
294         GdkPixbuf *rotated_pixbuf;
295         cairo_surface_t *surface;
296         gint thumb_width, thumb_height;
297         gint proposed_width, proposed_height;
298
299         thumb_width = (gint) (dvi_document->base_width * rc->scale);
300         thumb_height = (gint) (dvi_document->base_height * rc->scale);
301
302         g_mutex_lock (dvi_context_mutex);
303         
304         mdvi_setpage (dvi_document->context, rc->page);
305
306         mdvi_set_shrink (dvi_document->context, 
307                           (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width,
308                           (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height);
309
310         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
311         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
312                           
313         if (border) {
314                 mdvi_cairo_device_set_margins (&dvi_document->context->device, 
315                                                MAX (thumb_width - proposed_width, 0) / 2,
316                                                MAX (thumb_height - proposed_height, 0) / 2);    
317         } else {
318                 mdvi_cairo_device_set_margins (&dvi_document->context->device, 
319                                                MAX (thumb_width - proposed_width - 2, 0) / 2,
320                                                MAX (thumb_height - proposed_height - 2, 0) / 2);        
321         }
322
323         mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
324         mdvi_cairo_device_render (dvi_document->context);
325         surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
326         g_mutex_unlock (dvi_context_mutex);
327
328         pixbuf = ev_document_misc_pixbuf_from_surface (surface);
329         cairo_surface_destroy (surface);
330
331         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
332         g_object_unref (pixbuf);
333
334         if (border) {
335                 GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
336
337                 rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
338                 g_object_unref (tmp_pixbuf);
339         }
340
341         return rotated_pixbuf;
342 }
343
344 static void
345 dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
346 {
347         iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
348         iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
349 }
350
351 #define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b))
352
353 static gboolean
354 hsb2rgb (float h, float s, float v, char *red, char *green, char *blue)
355 {
356         float i, f, p, q, t, r, g, b;
357
358         if (h == 360)
359                 h = 0;
360         else if ((h > 360) || (h < 0))
361                 return FALSE;
362
363         s /= 100;
364         v /= 100;
365         h /= 60;
366         i = floor (h);
367         f = h - i;
368         p = v * (1 - s);
369         q = v * (1 - (s * f));
370         t = v * (1 - (s * (1 - f)));
371
372         if (i == 0) {
373                 r = v;
374                 g = t;
375                 b = p;
376         } else if (i == 1) {
377                 r = q;
378                 g = v;
379                 b = p;
380         } else if (i == 2) {
381                 r = p;
382                 g = v;
383                 b = t;
384         } else if (i == 3) {
385                 r = p;
386                 g = q;
387                 b = v;
388         } else if (i == 4) {
389                 r = t;
390                 g = p;
391                 b = v;
392         } else if (i == 5) {
393                 r = v;
394                 g = p;
395                 b = q;
396         }
397
398         (*red) = (char)floor(r * 255);
399         (*green) = (char)floor(g * 255);
400         (*blue) = (char)floor(b * 255);
401         
402         return TRUE;
403 }
404
405 static void
406 parse_color (const gchar *ptr,
407              gdouble     *color,
408              gint         n_color)
409 {
410         gchar *p = (gchar *)ptr;
411         gint   i;
412
413         for (i = 0; i < n_color; i++) {
414                 while (isspace (*p)) p++;
415                 color[i] = g_ascii_strtod (p, NULL);
416                 while (!isspace (*p) && *p != '\0') p++;
417                 if (*p == '\0')
418                         break;
419         }
420 }
421
422 static void
423 dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg)
424 {
425         if (strncmp (arg, "pop", 3) == 0) {
426                 mdvi_pop_color (dvi);
427         } else if (strncmp (arg, "push", 4) == 0) {
428                 /* Find color source: Named, CMYK or RGB */
429                 const char *tmp = arg + 4;
430                 
431                 while (isspace (*tmp)) tmp++;
432
433                 if (!strncmp ("rgb", tmp, 3)) {
434                         gdouble rgb[3];
435                         guchar red, green, blue;
436
437                         parse_color (tmp + 4, rgb, 3);
438                         
439                         red = 255 * rgb[0];
440                         green = 255 * rgb[1];
441                         blue = 255 * rgb[2];
442
443                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
444                 } else if (!strncmp ("hsb", tmp, 4)) {
445                         gdouble hsb[3];
446                         guchar red, green, blue;
447
448                         parse_color (tmp + 4, hsb, 3);
449                         
450                         if (hsb2rgb (hsb[0], hsb[1], hsb[2], &red, &green, &blue))
451                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
452                 } else if (!strncmp ("cmyk", tmp, 4)) {
453                         gdouble cmyk[4];
454                         double r, g, b;
455                         guchar red, green, blue;
456                         
457                         parse_color (tmp + 5, cmyk, 4);
458
459                         r = 1.0 - cmyk[0] - cmyk[3];
460                         if (r < 0.0)
461                                 r = 0.0;
462                         g = 1.0 - cmyk[1] - cmyk[3];
463                         if (g < 0.0)
464                                 g = 0.0;
465                         b = 1.0 - cmyk[2] - cmyk[3];
466                         if (b < 0.0)
467                                 b = 0.0;
468
469                         red = r * 255 + 0.5;
470                         green = g * 255 + 0.5;
471                         blue = b * 255 + 0.5;
472                         
473                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
474                 } else if (!strncmp ("gray ", tmp, 5)) {
475                         gdouble gray;
476                         guchar rgb;
477
478                         parse_color (tmp + 5, &gray, 1);
479
480                         rgb = gray * 255 + 0.5;
481
482                         mdvi_push_color (dvi, RGB2ULONG (rgb, rgb, rgb), 0xFFFFFFFF);
483                 } else {
484                         GdkColor color;
485                         
486                         if (gdk_color_parse (tmp, &color)) {
487                                 guchar red, green, blue;
488
489                                 red = color.red * 255 / 65535.;
490                                 green = color.green * 255 / 65535.;
491                                 blue = color.blue * 255 / 65535.;
492
493                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
494                         }
495                 }
496         }
497 }
498
499 static void
500 dvi_document_init_params (DviDocument *dvi_document)
501 {       
502         dvi_document->params = g_new0 (DviParams, 1);   
503
504         dvi_document->params->dpi      = MDVI_DPI;
505         dvi_document->params->vdpi     = MDVI_VDPI;
506         dvi_document->params->mag      = MDVI_MAGNIFICATION;
507         dvi_document->params->density  = MDVI_DEFAULT_DENSITY;
508         dvi_document->params->gamma    = MDVI_DEFAULT_GAMMA;
509         dvi_document->params->flags    = MDVI_PARAM_ANTIALIASED;
510         dvi_document->params->hdrift   = 0;
511         dvi_document->params->vdrift   = 0;
512         dvi_document->params->hshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi);
513         dvi_document->params->vshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi);
514         dvi_document->params->orientation = MDVI_ORIENT_TBLR;
515
516         dvi_document->spec = NULL;
517         
518         dvi_document->params->bg = 0xffffffff;
519         dvi_document->params->fg = 0xff000000;
520 }
521
522 static void
523 dvi_document_init (DviDocument *dvi_document)
524 {
525         dvi_document->context = NULL;
526         dvi_document_init_params (dvi_document);
527 }