]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/dvi-document.c
Use cairo image surfaces instead of GDK pixbufs for drawing pages and
[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 "pixbuf-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         
53         double base_width;
54         double base_height;
55         
56         gchar *uri;
57 };
58
59 typedef struct _DviDocumentClass DviDocumentClass;
60
61 static void dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg);
62 static void dvi_document_document_iface_init (EvDocumentIface *iface);
63 static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
64 static void dvi_document_get_page_size                  (EvDocument   *document,
65                                                          int       page,
66                                                          double    *width,
67                                                          double    *height);
68
69 G_DEFINE_TYPE_WITH_CODE 
70     (DviDocument, dvi_document, G_TYPE_OBJECT, 
71     {
72       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT, dvi_document_document_iface_init);    
73       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_iface_init)
74      });
75
76 static gboolean
77 dvi_document_load (EvDocument  *document,
78                       const char  *uri,
79                       GError     **error)
80 {
81     gchar *filename;
82     DviDocument *dvi_document = DVI_DOCUMENT(document);
83     
84     filename = g_filename_from_uri (uri, NULL, error);
85     
86     if (!filename) {
87                 g_set_error (error,
88                              EV_DOCUMENT_ERROR,
89                              EV_DOCUMENT_ERROR_INVALID,
90                              _("File not available"));
91                 return FALSE;
92     }
93         
94     g_mutex_lock (dvi_context_mutex);
95     if (dvi_document->context)
96         mdvi_destroy_context (dvi_document->context);
97
98     dvi_document->context = mdvi_init_context(dvi_document->params, dvi_document->spec, filename);
99     g_mutex_unlock (dvi_context_mutex);
100
101     if (!dvi_document->context) {
102                 g_set_error (error,
103                              EV_DOCUMENT_ERROR,
104                              EV_DOCUMENT_ERROR_INVALID,
105                              _("DVI document has incorrect format"));
106                 return FALSE;
107     }
108
109     mdvi_pixbuf_device_init (&dvi_document->context->device);
110
111     dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv 
112                 + 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
113                 
114     dvi_document->base_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv 
115                 + 2 * unit2pix(dvi_document->params->vdpi, MDVI_VMARGIN) / dvi_document->params->vshrink;
116
117     g_free (dvi_document->uri);
118     dvi_document->uri = g_strdup (uri);
119
120     return TRUE;
121 }
122
123
124 static gboolean
125 dvi_document_save (EvDocument  *document,
126                       const char  *uri,
127                       GError     **error)
128 {
129         DviDocument *dvi_document = DVI_DOCUMENT (document);
130
131         return ev_xfer_uri_simple (dvi_document->uri, uri, error);
132 }
133
134 static int
135 dvi_document_get_n_pages (EvDocument  *document)
136 {
137     DviDocument *dvi_document = DVI_DOCUMENT (document);
138     return dvi_document->context->npages;
139 }
140
141 static void
142 dvi_document_get_page_size (EvDocument   *document,
143                             int       page,
144                             double    *width,
145                             double    *height)
146 {
147         DviDocument * dvi_document = DVI_DOCUMENT (document);   
148
149         *width = dvi_document->base_width;
150         *height = dvi_document->base_height;;
151                                     
152         return;
153 }
154
155 static cairo_surface_t *
156 dvi_document_render (EvDocument      *document,
157                      EvRenderContext *rc)
158 {
159         GdkPixbuf *pixbuf;
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_pixbuf_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
190
191         mdvi_pixbuf_device_render (dvi_document->context);
192         
193         pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
194
195         g_mutex_unlock (dvi_context_mutex);
196
197         /* FIXME: we should write a mdvi device based on cairo */
198         surface = ev_document_misc_surface_from_pixbuf (pixbuf);
199         g_object_unref (pixbuf);
200
201         rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
202                                                                      required_width,
203                                                                      required_height,
204                                                                      rc->rotation);
205         cairo_surface_destroy (surface);
206
207         return rotated_surface;
208 }
209
210 static void
211 dvi_document_finalize (GObject *object)
212 {       
213         DviDocument *dvi_document = DVI_DOCUMENT(object);
214         
215         g_mutex_lock (dvi_context_mutex);
216         if (dvi_document->context) {
217                 mdvi_pixbuf_device_free (&dvi_document->context->device);
218                 mdvi_destroy_context (dvi_document->context);
219         }
220         g_mutex_unlock (dvi_context_mutex);
221
222         if (dvi_document->params)
223                 g_free (dvi_document->params);
224
225         g_free (dvi_document->uri);
226                 
227         G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
228 }
229
230
231 static void
232 dvi_document_class_init (DviDocumentClass *klass)
233 {
234         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
235
236         gobject_class->finalize = dvi_document_finalize;
237
238         mdvi_init_kpathsea("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI);
239         mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1);
240         mdvi_register_fonts ();
241
242         dvi_context_mutex = g_mutex_new ();
243 }
244
245 static gboolean
246 dvi_document_can_get_text (EvDocument *document)
247 {
248         return FALSE;
249 }
250
251 static EvDocumentInfo *
252 dvi_document_get_info (EvDocument *document)
253 {
254         EvDocumentInfo *info;
255
256         info = g_new0 (EvDocumentInfo, 1);
257
258         return info;
259 }
260
261 static void
262 dvi_document_document_iface_init (EvDocumentIface *iface)
263 {
264         iface->load = dvi_document_load;
265         iface->save = dvi_document_save;
266         iface->can_get_text = dvi_document_can_get_text;
267         iface->get_n_pages = dvi_document_get_n_pages;
268         iface->get_page_size = dvi_document_get_page_size;
269         iface->render = dvi_document_render;
270         iface->get_info = dvi_document_get_info;
271 }
272
273 static void
274 dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
275                                         EvRenderContext      *rc, 
276                                         gint                  *width,
277                                         gint                  *height)
278 {       
279         DviDocument *dvi_document = DVI_DOCUMENT (document);
280         gdouble page_width = dvi_document->base_width;
281         gdouble page_height = dvi_document->base_height;
282
283         if (rc->rotation == 90 || rc->rotation == 270) {
284                 *width = (gint) (page_height * rc->scale);
285                 *height = (gint) (page_width * rc->scale);
286         } else {
287                 *width = (gint) (page_width * rc->scale);
288                 *height = (gint) (page_height * rc->scale);
289         }
290 }
291
292 static GdkPixbuf *
293 dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
294                                        EvRenderContext      *rc,   
295                                        gboolean              border)
296 {
297         DviDocument *dvi_document = DVI_DOCUMENT (document);
298         GdkPixbuf *pixbuf;
299         GdkPixbuf *border_pixbuf;
300         GdkPixbuf *rotated_pixbuf;
301         gint thumb_width, thumb_height;
302         gint proposed_width, proposed_height;
303         
304         thumb_width = (gint) (dvi_document->base_width * rc->scale);
305         thumb_height = (gint) (dvi_document->base_height * rc->scale);
306         
307         g_mutex_lock (dvi_context_mutex);
308
309         mdvi_setpage (dvi_document->context, rc->page);
310
311         mdvi_set_shrink (dvi_document->context, 
312                           (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width,
313                           (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height);
314
315         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
316         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
317                           
318         if (border) {
319                 mdvi_pixbuf_device_set_margins  (&dvi_document->context->device, 
320                                                  MAX (thumb_width - proposed_width, 0) / 2,
321                                                  MAX (thumb_height - proposed_height, 0) / 2);  
322         } else {
323                 mdvi_pixbuf_device_set_margins  (&dvi_document->context->device, 
324                                                  MAX (thumb_width - proposed_width - 2, 0) / 2,
325                                                  MAX (thumb_height - proposed_height - 2, 0) / 2);      
326         }
327         
328
329         mdvi_pixbuf_device_render (dvi_document->context);
330         pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
331
332         g_mutex_unlock (dvi_context_mutex);
333         
334         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
335         g_object_unref (pixbuf);
336         
337         if (border) {
338               GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
339               
340               rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
341               g_object_unref (tmp_pixbuf);
342         }
343
344         return rotated_pixbuf;
345 }
346
347 static void
348 dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
349 {
350         iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
351         iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
352 }
353
354 #define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b))
355
356 static gboolean
357 hsb2rgb (float h, float s, float v, char *red, char *green, char *blue)
358 {
359         float i, f, p, q, t, r, g, b;
360
361         if (h == 360)
362                 h = 0;
363         else if ((h > 360) || (h < 0))
364                 return FALSE;
365
366         s /= 100;
367         v /= 100;
368         h /= 60;
369         i = floor (h);
370         f = h - i;
371         p = v * (1 - s);
372         q = v * (1 - (s * f));
373         t = v * (1 - (s * (1 - f)));
374
375         if (i == 0) {
376                 r = v;
377                 g = t;
378                 b = p;
379         } else if (i == 1) {
380                 r = q;
381                 g = v;
382                 b = p;
383         } else if (i == 2) {
384                 r = p;
385                 g = v;
386                 b = t;
387         } else if (i == 3) {
388                 r = p;
389                 g = q;
390                 b = v;
391         } else if (i == 4) {
392                 r = t;
393                 g = p;
394                 b = v;
395         } else if (i == 5) {
396                 r = v;
397                 g = p;
398                 b = q;
399         }
400
401         (*red) = (char)floor(r * 255);
402         (*green) = (char)floor(g * 255);
403         (*blue) = (char)floor(b * 255);
404         
405         return TRUE;
406 }
407
408 static void
409 dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg)
410 {
411         char *op, *color;
412
413         if (strncmp (arg, "pop", 3) == 0) {
414                 mdvi_pop_color (dvi);
415         } else if (strncmp (arg, "push", 4) == 0) {
416                 /* Find color source : Named, CMYK or RGB */
417                 const char *tmp = arg+4;
418                 while (isspace (*tmp)) tmp++;
419
420                 if (!strncmp ("rgb", tmp, 3)) {
421                         float r, g, b;
422                         unsigned char red, green, blue;
423                         sscanf (tmp+4, "%f %f %f", &r, &g, &b);
424                         red = 255*r;
425                         green = 255*g;
426                         blue = 255*b;
427                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
428                 } else if (!strncmp ("hsb", tmp, 4)) {
429                         float h, s, b;
430                         char red, green, blue;
431                         sscanf (tmp+4, "%f %f %f", &h, &s, &b);
432
433                         if (hsb2rgb (h, s, b, &red, &green, &blue))
434                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
435                 } else if (!strncmp ("cmyk", tmp, 4)) {
436                         double r, g, b, c, m, y, k;
437                         
438                         sscanf (tmp+5, "%f %f %f %f", &c, &m, &y, &k);
439
440                         r = 1.0 - c - k;
441                         if (r < 0.0)
442                                 r = 0.0;
443                         g = 1.0 - m - k;
444                         if (g < 0.0)
445                                 g = 0.0;
446                         b = 1.0 - y - k;
447                         if (b < 0.0)
448                                 b = 0.0;
449                         mdvi_push_color (dvi, RGB2ULONG ((char)(r*255+0.5), (char)(r*255+0.5),
450                                                          (char)(b*255+0.5)), 0xFFFFFFFF);
451                 } else {
452                         GdkColor color;
453                         if (gdk_color_parse (tmp, &color))
454                                 mdvi_push_color (dvi, RGB2ULONG (color.red*255/65535,
455                                                                  color.green*255/65535,
456                                                                  color.blue*255/65535), 0xFFFFFFFF);
457                 }
458         }
459 }
460
461 static void
462 dvi_document_init_params (DviDocument *dvi_document)
463 {       
464         dvi_document->params = g_new0 (DviParams, 1);   
465
466         dvi_document->params->dpi      = MDVI_DPI;
467         dvi_document->params->vdpi     = MDVI_VDPI;
468         dvi_document->params->mag      = MDVI_MAGNIFICATION;
469         dvi_document->params->density  = MDVI_DEFAULT_DENSITY;
470         dvi_document->params->gamma    = MDVI_DEFAULT_GAMMA;
471         dvi_document->params->flags    = MDVI_PARAM_ANTIALIASED;
472         dvi_document->params->hdrift   = 0;
473         dvi_document->params->vdrift   = 0;
474         dvi_document->params->hshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi);
475         dvi_document->params->vshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi);
476         dvi_document->params->orientation = MDVI_ORIENT_TBLR;
477
478         dvi_document->spec = NULL;
479         
480         dvi_document->params->bg = 0xffffffff;
481         dvi_document->params->fg = 0xff000000;
482 }
483
484 static void
485 dvi_document_init (DviDocument *dvi_document)
486 {
487         dvi_document->context = NULL;
488         dvi_document_init_params (dvi_document);
489 }