]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/dvi-document.c
Implement font color specials in the DVI backend. Fixes bug #303651.
[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 GdkPixbuf *
156 dvi_document_render_pixbuf (EvDocument  *document,
157                             EvRenderContext *rc)
158 {
159         GdkPixbuf *pixbuf;
160         GdkPixbuf *rotated_pixbuf;
161
162         DviDocument *dvi_document = DVI_DOCUMENT(document);
163
164         gint required_width, required_height;
165         gint proposed_width, proposed_height;
166         gint xmargin = 0, ymargin = 0;
167
168         /* We should protect our context since it's not 
169          * thread safe. The work to the future - 
170          * let context render page independently
171          */
172         g_mutex_lock (dvi_context_mutex);
173         
174         mdvi_setpage(dvi_document->context,  rc->page);
175         
176         mdvi_set_shrink (dvi_document->context, 
177                          (int)((dvi_document->params->hshrink - 1) / rc->scale) + 1,
178                          (int)((dvi_document->params->vshrink - 1) / rc->scale) + 1);
179
180         required_width = dvi_document->base_width * rc->scale;
181         required_height = dvi_document->base_height * rc->scale;
182         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
183         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
184         
185         if (required_width >= proposed_width)
186             xmargin = (required_width - proposed_width) / 2;
187         if (required_height >= proposed_height)
188             ymargin = (required_height - proposed_height) / 2;
189             
190         mdvi_pixbuf_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
191
192         mdvi_pixbuf_device_render (dvi_document->context);
193         
194         pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
195
196         g_mutex_unlock (dvi_context_mutex);
197
198         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
199         g_object_unref (pixbuf);
200
201         return rotated_pixbuf;
202 }
203
204 static void
205 dvi_document_finalize (GObject *object)
206 {       
207         DviDocument *dvi_document = DVI_DOCUMENT(object);
208         
209         g_mutex_lock (dvi_context_mutex);
210         if (dvi_document->context) {
211                 mdvi_pixbuf_device_free (&dvi_document->context->device);
212                 mdvi_destroy_context (dvi_document->context);
213         }
214         g_mutex_unlock (dvi_context_mutex);
215
216         if (dvi_document->params)
217                 g_free (dvi_document->params);
218
219         g_free (dvi_document->uri);
220                 
221         G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
222 }
223
224
225 static void
226 dvi_document_class_init (DviDocumentClass *klass)
227 {
228         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
229
230         gobject_class->finalize = dvi_document_finalize;
231
232         mdvi_init_kpathsea("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI);
233         mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1);
234         mdvi_register_fonts ();
235
236         dvi_context_mutex = g_mutex_new ();
237 }
238
239 static gboolean
240 dvi_document_can_get_text (EvDocument *document)
241 {
242         return FALSE;
243 }
244
245 static EvDocumentInfo *
246 dvi_document_get_info (EvDocument *document)
247 {
248         EvDocumentInfo *info;
249
250         info = g_new0 (EvDocumentInfo, 1);
251
252         return info;
253 }
254
255 static void
256 dvi_document_document_iface_init (EvDocumentIface *iface)
257 {
258         iface->load = dvi_document_load;
259         iface->save = dvi_document_save;
260         iface->can_get_text = dvi_document_can_get_text;
261         iface->get_n_pages = dvi_document_get_n_pages;
262         iface->get_page_size = dvi_document_get_page_size;
263         iface->render_pixbuf = dvi_document_render_pixbuf;
264         iface->get_info = dvi_document_get_info;
265 }
266
267 static void
268 dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
269                                         EvRenderContext      *rc, 
270                                         gint                  *width,
271                                         gint                  *height)
272 {       
273         DviDocument *dvi_document = DVI_DOCUMENT (document);
274         gdouble page_width = dvi_document->base_width;
275         gdouble page_height = dvi_document->base_height;
276
277         if (rc->rotation == 90 || rc->rotation == 270) {
278                 *width = (gint) (page_height * rc->scale);
279                 *height = (gint) (page_width * rc->scale);
280         } else {
281                 *width = (gint) (page_width * rc->scale);
282                 *height = (gint) (page_height * rc->scale);
283         }
284 }
285
286 static GdkPixbuf *
287 dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
288                                        EvRenderContext      *rc,   
289                                        gboolean              border)
290 {
291         DviDocument *dvi_document = DVI_DOCUMENT (document);
292         GdkPixbuf *pixbuf;
293         GdkPixbuf *border_pixbuf;
294         GdkPixbuf *rotated_pixbuf;
295         gint thumb_width, thumb_height;
296         gint proposed_width, proposed_height;
297         
298         thumb_width = (gint) (dvi_document->base_width * rc->scale);
299         thumb_height = (gint) (dvi_document->base_height * rc->scale);
300         
301         g_mutex_lock (dvi_context_mutex);
302
303         mdvi_setpage (dvi_document->context, rc->page);
304
305         mdvi_set_shrink (dvi_document->context, 
306                           (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width,
307                           (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height);
308
309         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
310         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
311                           
312         if (border) {
313                 mdvi_pixbuf_device_set_margins  (&dvi_document->context->device, 
314                                                  MAX (thumb_width - proposed_width, 0) / 2,
315                                                  MAX (thumb_height - proposed_height, 0) / 2);  
316         } else {
317                 mdvi_pixbuf_device_set_margins  (&dvi_document->context->device, 
318                                                  MAX (thumb_width - proposed_width - 2, 0) / 2,
319                                                  MAX (thumb_height - proposed_height - 2, 0) / 2);      
320         }
321         
322
323         mdvi_pixbuf_device_render (dvi_document->context);
324         pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
325
326         g_mutex_unlock (dvi_context_mutex);
327         
328         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
329         g_object_unref (pixbuf);
330         
331         if (border) {
332               GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
333               
334               rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
335               g_object_unref (tmp_pixbuf);
336         }
337
338         return rotated_pixbuf;
339 }
340
341 static void
342 dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
343 {
344         iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
345         iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
346 }
347
348 #define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b))
349
350 static gboolean
351 hsb2rgb (float h, float s, float v, char *red, char *green, char *blue)
352 {
353         float i, f, p, q, t, r, g, b;
354
355         if (h == 360)
356                 h = 0;
357         else if ((h > 360) || (h < 0))
358                 return FALSE;
359
360         s /= 100;
361         v /= 100;
362         h /= 60;
363         i = floor (h);
364         f = h - i;
365         p = v * (1 - s);
366         q = v * (1 - (s * f));
367         t = v * (1 - (s * (1 - f)));
368
369         if (i == 0) {
370                 r = v;
371                 g = t;
372                 b = p;
373         } else if (i == 1) {
374                 r = q;
375                 g = v;
376                 b = p;
377         } else if (i == 2) {
378                 r = p;
379                 g = v;
380                 b = t;
381         } else if (i == 3) {
382                 r = p;
383                 g = q;
384                 b = v;
385         } else if (i == 4) {
386                 r = t;
387                 g = p;
388                 b = v;
389         } else if (i == 5) {
390                 r = v;
391                 g = p;
392                 b = q;
393         }
394
395         (*red) = (char)floor(r * 255);
396         (*green) = (char)floor(g * 255);
397         (*blue) = (char)floor(b * 255);
398         
399         return TRUE;
400 }
401
402 static void
403 dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg)
404 {
405         char *op, *color;
406
407         if (strncmp (arg, "pop", 3) == 0) {
408                 mdvi_pop_color (dvi);
409         } else if (strncmp (arg, "push", 4) == 0) {
410                 /* Find color source : Named, CMYK or RGB */
411                 const char *tmp = arg+4;
412                 while (isspace (*tmp)) tmp++;
413
414                 if (!strncmp ("rgb", tmp, 3)) {
415                         float r, g, b;
416                         unsigned char red, green, blue;
417                         sscanf (tmp+4, "%f %f %f", &r, &g, &b);
418                         red = 255*r;
419                         green = 255*g;
420                         blue = 255*b;
421                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
422                 } else if (!strncmp ("hsb", tmp, 4)) {
423                         float h, s, b;
424                         char red, green, blue;
425                         sscanf (tmp+4, "%f %f %f", &h, &s, &b);
426
427                         if (hsb2rgb (h, s, b, &red, &green, &blue))
428                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
429                 } else if (!strncmp ("cmyk", tmp, 4)) {
430                         double r, g, b, c, m, y, k;
431                         
432                         sscanf (tmp+5, "%f %f %f %f", &c, &m, &y, &k);
433
434                         r = 1.0 - c - k;
435                         if (r < 0.0)
436                                 r = 0.0;
437                         g = 1.0 - m - k;
438                         if (g < 0.0)
439                                 g = 0.0;
440                         b = 1.0 - y - k;
441                         if (b < 0.0)
442                                 b = 0.0;
443                         mdvi_push_color (dvi, RGB2ULONG ((char)(r*255+0.5), (char)(r*255+0.5),
444                                                          (char)(b*255+0.5)), 0xFFFFFFFF);
445                 } else {
446                         GdkColor color;
447                         if (gdk_color_parse (tmp, &color))
448                                 mdvi_push_color (dvi, RGB2ULONG (color.red*255/65535,
449                                                                  color.green*255/65535,
450                                                                  color.blue*255/65535), 0xFFFFFFFF);
451                 }
452         }
453 }
454
455 static void
456 dvi_document_init_params (DviDocument *dvi_document)
457 {       
458         dvi_document->params = g_new0 (DviParams, 1);   
459
460         dvi_document->params->dpi      = MDVI_DPI;
461         dvi_document->params->vdpi     = MDVI_VDPI;
462         dvi_document->params->mag      = MDVI_MAGNIFICATION;
463         dvi_document->params->density  = MDVI_DEFAULT_DENSITY;
464         dvi_document->params->gamma    = MDVI_DEFAULT_GAMMA;
465         dvi_document->params->flags    = MDVI_PARAM_ANTIALIASED;
466         dvi_document->params->hdrift   = 0;
467         dvi_document->params->vdrift   = 0;
468         dvi_document->params->hshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi);
469         dvi_document->params->vshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi);
470         dvi_document->params->orientation = MDVI_ORIENT_TBLR;
471
472         dvi_document->spec = NULL;
473         
474         dvi_document->params->bg = 0xffffffff;
475         dvi_document->params->fg = 0xff000000;
476 }
477
478 static void
479 dvi_document_init (DviDocument *dvi_document)
480 {
481         dvi_document->context = NULL;
482         dvi_document_init_params (dvi_document);
483 }