]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/dvi-document.c
7551f80343eeee247b269dee99945b9310c03851
[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 "config.h"
21
22 #include "dvi-document.h"
23 #include "ev-document-thumbnails.h"
24 #include "ev-document-misc.h"
25 #include "ev-file-exporter.h"
26 #include "ev-file-helpers.h"
27
28 #include "mdvi.h"
29 #include "fonts.h"
30 #include "color.h"
31 #include "cairo-device.h"
32
33 #include <glib/gi18n-lib.h>
34 #include <ctype.h>
35 #include <sys/wait.h>
36 #include <stdlib.h>
37
38 GMutex *dvi_context_mutex = NULL;
39
40 enum {
41         PROP_0,
42         PROP_TITLE
43 };
44
45 struct _DviDocumentClass
46 {
47         EvDocumentClass parent_class;
48 };
49
50 struct _DviDocument
51 {
52         EvDocument parent_instance;
53
54         DviContext *context;
55         DviPageSpec *spec;
56         DviParams *params;
57         
58         /* To let document scale we should remember width and height */
59         double base_width;
60         double base_height;
61         
62         gchar *uri;
63
64         /* PDF exporter */
65         gchar            *exporter_filename;
66         GString          *exporter_opts;
67 };
68
69 typedef struct _DviDocumentClass DviDocumentClass;
70
71 static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
72 static void dvi_document_file_exporter_iface_init       (EvFileExporterIface       *iface);
73 static void dvi_document_do_color_special               (DviContext                *dvi,
74                                                          const char                *prefix,
75                                                          const char                *arg);
76
77 EV_BACKEND_REGISTER_WITH_CODE (DviDocument, dvi_document,
78      {
79       EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_iface_init);
80       EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, dvi_document_file_exporter_iface_init);
81      });
82
83 static gboolean
84 dvi_document_load (EvDocument  *document,
85                    const char  *uri,
86                    GError     **error)
87 {
88         gchar *filename;
89         DviDocument *dvi_document = DVI_DOCUMENT(document);
90         
91         filename = g_filename_from_uri (uri, NULL, error);
92         if (!filename)
93                 return FALSE;
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         g_free (filename);
102         
103         if (!dvi_document->context) {
104                 g_set_error_literal (error,
105                                      EV_DOCUMENT_ERROR,
106                                      EV_DOCUMENT_ERROR_INVALID,
107                                      _("DVI document has incorrect format"));
108                 return FALSE;
109         }
110         
111         mdvi_cairo_device_init (&dvi_document->context->device);
112         
113         
114         dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv 
115                 + 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
116         
117         dvi_document->base_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv 
118                 + 2 * unit2pix(dvi_document->params->vdpi, MDVI_VMARGIN) / dvi_document->params->vshrink;
119         
120         g_free (dvi_document->uri);
121         dvi_document->uri = g_strdup (uri);
122         
123         return TRUE;
124 }
125
126
127 static gboolean
128 dvi_document_save (EvDocument  *document,
129                       const char  *uri,
130                       GError     **error)
131 {
132         DviDocument *dvi_document = DVI_DOCUMENT (document);
133
134         return ev_xfer_uri_simple (dvi_document->uri, uri, error);
135 }
136
137 static int
138 dvi_document_get_n_pages (EvDocument *document)
139 {
140         DviDocument *dvi_document = DVI_DOCUMENT (document);
141         
142         return dvi_document->context->npages;
143 }
144
145 static void
146 dvi_document_get_page_size (EvDocument *document,
147                             EvPage     *page,
148                             double     *width,
149                             double     *height)
150 {
151         DviDocument *dvi_document = DVI_DOCUMENT (document);    
152
153         *width = dvi_document->base_width;
154         *height = dvi_document->base_height;;
155 }
156
157 static cairo_surface_t *
158 dvi_document_render (EvDocument      *document,
159                      EvRenderContext *rc)
160 {
161         cairo_surface_t *surface;
162         cairo_surface_t *rotated_surface;
163         DviDocument *dvi_document = DVI_DOCUMENT(document);
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->index);
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 + 0.5;
181         required_height = dvi_document->base_height * rc->scale + 0.5;
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_cairo_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
191         mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
192         mdvi_cairo_device_render (dvi_document->context);
193         surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
194
195         g_mutex_unlock (dvi_context_mutex);
196
197         rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
198                                                                      required_width,
199                                                                      required_height, 
200                                                                      rc->rotation);
201         cairo_surface_destroy (surface);
202         
203         return rotated_surface;
204 }
205
206 static void
207 dvi_document_finalize (GObject *object)
208 {       
209         DviDocument *dvi_document = DVI_DOCUMENT(object);
210         
211         g_mutex_lock (dvi_context_mutex);
212         if (dvi_document->context) {
213                 mdvi_cairo_device_free (&dvi_document->context->device);
214                 mdvi_destroy_context (dvi_document->context);
215         }
216         g_mutex_unlock (dvi_context_mutex);
217
218         if (dvi_document->params)
219                 g_free (dvi_document->params);
220
221         if (dvi_document->exporter_filename)
222                 g_free (dvi_document->exporter_filename);
223         
224         if (dvi_document->exporter_opts)
225                 g_string_free (dvi_document->exporter_opts, TRUE);
226
227         g_free (dvi_document->uri);
228                 
229         G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
230 }
231
232 static EvDocumentInfo *
233 dvi_document_get_info (EvDocument *document)
234 {
235         EvDocumentInfo *info;
236
237         info = g_new0 (EvDocumentInfo, 1);
238
239         return info;
240 }
241
242 static void
243 dvi_document_class_init (DviDocumentClass *klass)
244 {
245         GObjectClass    *gobject_class = G_OBJECT_CLASS (klass);
246         EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
247
248         gobject_class->finalize = dvi_document_finalize;
249
250         mdvi_init_kpathsea ("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI);
251         mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1);
252         mdvi_register_fonts ();
253
254         dvi_context_mutex = g_mutex_new ();
255
256         ev_document_class->load = dvi_document_load;
257         ev_document_class->save = dvi_document_save;
258         ev_document_class->get_n_pages = dvi_document_get_n_pages;
259         ev_document_class->get_page_size = dvi_document_get_page_size;
260         ev_document_class->render = dvi_document_render;
261         ev_document_class->get_info = dvi_document_get_info;
262 }
263
264 static void
265 dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
266                                         EvRenderContext      *rc, 
267                                         gint                 *width,
268                                         gint                 *height)
269 {       
270         DviDocument *dvi_document = DVI_DOCUMENT (document);
271         gdouble page_width = dvi_document->base_width;
272         gdouble page_height = dvi_document->base_height;
273
274         if (rc->rotation == 90 || rc->rotation == 270) {
275                 *width = (gint) (page_height * rc->scale);
276                 *height = (gint) (page_width * rc->scale);
277         } else {
278                 *width = (gint) (page_width * rc->scale);
279                 *height = (gint) (page_height * rc->scale);
280         }
281 }
282
283 static GdkPixbuf *
284 dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
285                                        EvRenderContext      *rc,   
286                                        gboolean              border)
287 {
288         DviDocument *dvi_document = DVI_DOCUMENT (document);
289         GdkPixbuf *pixbuf;
290         GdkPixbuf *rotated_pixbuf;
291         cairo_surface_t *surface;
292         gint thumb_width, thumb_height;
293         gint proposed_width, proposed_height;
294
295         thumb_width = (gint) (dvi_document->base_width * rc->scale);
296         thumb_height = (gint) (dvi_document->base_height * rc->scale);
297
298         g_mutex_lock (dvi_context_mutex);
299         
300         mdvi_setpage (dvi_document->context, rc->page->index);
301
302         mdvi_set_shrink (dvi_document->context, 
303                           (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width,
304                           (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height);
305
306         proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
307         proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
308                           
309         if (border) {
310                 mdvi_cairo_device_set_margins (&dvi_document->context->device, 
311                                                MAX (thumb_width - proposed_width, 0) / 2,
312                                                MAX (thumb_height - proposed_height, 0) / 2);    
313         } else {
314                 mdvi_cairo_device_set_margins (&dvi_document->context->device, 
315                                                MAX (thumb_width - proposed_width - 2, 0) / 2,
316                                                MAX (thumb_height - proposed_height - 2, 0) / 2);        
317         }
318
319         mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
320         mdvi_cairo_device_render (dvi_document->context);
321         surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
322         g_mutex_unlock (dvi_context_mutex);
323
324         pixbuf = ev_document_misc_pixbuf_from_surface (surface);
325         cairo_surface_destroy (surface);
326
327         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
328         g_object_unref (pixbuf);
329
330         if (border) {
331                 GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
332
333                 rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
334                 g_object_unref (tmp_pixbuf);
335         }
336
337         return rotated_pixbuf;
338 }
339
340 static void
341 dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
342 {
343         iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
344         iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
345 }
346
347 /* EvFileExporterIface */
348 static void
349 dvi_document_file_exporter_begin (EvFileExporter        *exporter,
350                                   EvFileExporterContext *fc)
351 {
352         DviDocument *dvi_document = DVI_DOCUMENT(exporter);
353         
354         if (dvi_document->exporter_filename)
355                 g_free (dvi_document->exporter_filename);       
356         dvi_document->exporter_filename = g_strdup (fc->filename);
357         
358         if (dvi_document->exporter_opts) {
359                 g_string_free (dvi_document->exporter_opts, TRUE);
360         }
361         dvi_document->exporter_opts = g_string_new ("-s ");
362 }
363
364 static void
365 dvi_document_file_exporter_do_page (EvFileExporter  *exporter,
366                                     EvRenderContext *rc)
367 {
368        DviDocument *dvi_document = DVI_DOCUMENT(exporter);
369
370        g_string_append_printf (dvi_document->exporter_opts, "%d,", (rc->page->index) + 1);
371 }
372
373 static void
374 dvi_document_file_exporter_end (EvFileExporter *exporter)
375 {
376         gchar *command_line;
377         gint exit_stat;
378         GError *err = NULL;
379         gboolean success;
380         
381         DviDocument *dvi_document = DVI_DOCUMENT(exporter);
382         
383         command_line = g_strdup_printf ("dvipdfm %s -o %s \"%s\"", /* dvipdfm -s 1,2,.., -o exporter_filename dvi_filename */
384                                         dvi_document->exporter_opts->str,
385                                         dvi_document->exporter_filename,
386                                         dvi_document->context->filename);
387         
388         success = g_spawn_command_line_sync (command_line,
389                                              NULL,
390                                              NULL,
391                                              &exit_stat,
392                                              &err);
393
394         g_free (command_line);
395
396         if (success == FALSE) {
397                 g_warning ("Error: %s", err->message);
398         } else if (!WIFEXITED(exit_stat) || WEXITSTATUS(exit_stat) != EXIT_SUCCESS){
399                 g_warning ("Error: dvipdfm does not end normally or exit with a failure status.");
400         }
401
402         if (err)
403                 g_error_free (err);
404 }
405
406 static EvFileExporterCapabilities
407 dvi_document_file_exporter_get_capabilities (EvFileExporter *exporter)
408 {
409         return  EV_FILE_EXPORTER_CAN_PAGE_SET |
410                 EV_FILE_EXPORTER_CAN_COPIES |
411                 EV_FILE_EXPORTER_CAN_COLLATE |
412                 EV_FILE_EXPORTER_CAN_REVERSE |
413                 EV_FILE_EXPORTER_CAN_GENERATE_PDF;
414 }
415
416 static void
417 dvi_document_file_exporter_iface_init (EvFileExporterIface *iface)
418 {
419         iface->begin = dvi_document_file_exporter_begin;
420         iface->do_page = dvi_document_file_exporter_do_page;
421         iface->end = dvi_document_file_exporter_end;
422         iface->get_capabilities = dvi_document_file_exporter_get_capabilities;
423 }
424
425 #define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b))
426
427 static gboolean
428 hsb2rgb (float h, float s, float v, guchar *red, guchar *green, guchar *blue)
429 {
430         float i, f, p, q, t, r, g, b;
431
432         if (h == 360)
433                 h = 0;
434         else if ((h > 360) || (h < 0))
435                 return FALSE;
436
437         s /= 100;
438         v /= 100;
439         h /= 60;
440         i = floor (h);
441         f = h - i;
442         p = v * (1 - s);
443         q = v * (1 - (s * f));
444         t = v * (1 - (s * (1 - f)));
445
446         if (i == 0) {
447                 r = v;
448                 g = t;
449                 b = p;
450         } else if (i == 1) {
451                 r = q;
452                 g = v;
453                 b = p;
454         } else if (i == 2) {
455                 r = p;
456                 g = v;
457                 b = t;
458         } else if (i == 3) {
459                 r = p;
460                 g = q;
461                 b = v;
462         } else if (i == 4) {
463                 r = t;
464                 g = p;
465                 b = v;
466         } else if (i == 5) {
467                 r = v;
468                 g = p;
469                 b = q;
470         }
471
472         *red   = (guchar)floor(r * 255.0);
473         *green = (guchar)floor(g * 255.0);
474         *blue  = (guchar)floor(b * 255.0);
475         
476         return TRUE;
477 }
478
479 static void
480 parse_color (const gchar *ptr,
481              gdouble     *color,
482              gint         n_color)
483 {
484         gchar *p = (gchar *)ptr;
485         gint   i;
486
487         for (i = 0; i < n_color; i++) {
488                 while (isspace (*p)) p++;
489                 color[i] = g_ascii_strtod (p, NULL);
490                 while (!isspace (*p) && *p != '\0') p++;
491                 if (*p == '\0')
492                         break;
493         }
494 }
495
496 static void
497 dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg)
498 {
499         if (strncmp (arg, "pop", 3) == 0) {
500                 mdvi_pop_color (dvi);
501         } else if (strncmp (arg, "push", 4) == 0) {
502                 /* Find color source: Named, CMYK or RGB */
503                 const char *tmp = arg + 4;
504                 
505                 while (isspace (*tmp)) tmp++;
506
507                 if (!strncmp ("rgb", tmp, 3)) {
508                         gdouble rgb[3];
509                         guchar red, green, blue;
510
511                         parse_color (tmp + 4, rgb, 3);
512                         
513                         red = 255 * rgb[0];
514                         green = 255 * rgb[1];
515                         blue = 255 * rgb[2];
516
517                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
518                 } else if (!strncmp ("hsb", tmp, 4)) {
519                         gdouble hsb[3];
520                         guchar red, green, blue;
521
522                         parse_color (tmp + 4, hsb, 3);
523                         
524                         if (hsb2rgb (hsb[0], hsb[1], hsb[2], &red, &green, &blue))
525                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
526                 } else if (!strncmp ("cmyk", tmp, 4)) {
527                         gdouble cmyk[4];
528                         double r, g, b;
529                         guchar red, green, blue;
530                         
531                         parse_color (tmp + 5, cmyk, 4);
532
533                         r = 1.0 - cmyk[0] - cmyk[3];
534                         if (r < 0.0)
535                                 r = 0.0;
536                         g = 1.0 - cmyk[1] - cmyk[3];
537                         if (g < 0.0)
538                                 g = 0.0;
539                         b = 1.0 - cmyk[2] - cmyk[3];
540                         if (b < 0.0)
541                                 b = 0.0;
542
543                         red = r * 255 + 0.5;
544                         green = g * 255 + 0.5;
545                         blue = b * 255 + 0.5;
546                         
547                         mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
548                 } else if (!strncmp ("gray ", tmp, 5)) {
549                         gdouble gray;
550                         guchar rgb;
551
552                         parse_color (tmp + 5, &gray, 1);
553
554                         rgb = gray * 255 + 0.5;
555
556                         mdvi_push_color (dvi, RGB2ULONG (rgb, rgb, rgb), 0xFFFFFFFF);
557                 } else {
558                         GdkColor color;
559                         
560                         if (gdk_color_parse (tmp, &color)) {
561                                 guchar red, green, blue;
562
563                                 red = color.red * 255 / 65535.;
564                                 green = color.green * 255 / 65535.;
565                                 blue = color.blue * 255 / 65535.;
566
567                                 mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
568                         }
569                 }
570         }
571 }
572
573 static void
574 dvi_document_init_params (DviDocument *dvi_document)
575 {       
576         dvi_document->params = g_new0 (DviParams, 1);   
577
578         dvi_document->params->dpi      = MDVI_DPI;
579         dvi_document->params->vdpi     = MDVI_VDPI;
580         dvi_document->params->mag      = MDVI_MAGNIFICATION;
581         dvi_document->params->density  = MDVI_DEFAULT_DENSITY;
582         dvi_document->params->gamma    = MDVI_DEFAULT_GAMMA;
583         dvi_document->params->flags    = MDVI_PARAM_ANTIALIASED;
584         dvi_document->params->hdrift   = 0;
585         dvi_document->params->vdrift   = 0;
586         dvi_document->params->hshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi);
587         dvi_document->params->vshrink  =  MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi);
588         dvi_document->params->orientation = MDVI_ORIENT_TBLR;
589
590         dvi_document->spec = NULL;
591         
592         dvi_document->params->bg = 0xffffffff;
593         dvi_document->params->fg = 0xff000000;
594 }
595
596 static void
597 dvi_document_init (DviDocument *dvi_document)
598 {
599         dvi_document->context = NULL;
600         dvi_document_init_params (dvi_document);
601
602         dvi_document->exporter_filename = NULL;
603         dvi_document->exporter_opts = NULL;
604 }