]> www.fi.muni.cz Git - evince.git/blob - backend/impress/impress-document.c
0d58aaf61a344c36095fedceb128c1be19f00abb
[evince.git] / backend / impress / impress-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, Jonathan Blandford <jrb@gnome.org>
4  * Copyright (C) 2005, Bastien Nocera <hadess@hadess.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtk.h>
27
28 #include "imposter.h"
29 #include "impress-document.h"
30
31 #include "ev-document-misc.h"
32 #include "ev-document-thumbnails.h"
33
34 struct _ImpressDocumentClass
35 {
36   EvDocumentClass parent_class;
37 };
38
39 struct _ImpressDocument
40 {
41   EvDocument parent_instance;
42
43   ImpDoc *imp;
44   ImpRenderCtx *ctx;
45
46   GMutex *mutex;
47   GdkPixmap *pixmap;
48   GdkGC *gc;
49   PangoContext *pango_ctx;
50
51   /* Only used while rendering inside the mainloop */
52   int pagenum;
53   GdkPixbuf *pixbuf;
54   GCond *cond;
55 };
56
57 #define PAGE_WIDTH 1024
58 #define PAGE_HEIGHT 768
59
60 typedef struct _ImpressDocumentClass ImpressDocumentClass;
61
62 static void impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
63
64 EV_BACKEND_REGISTER_WITH_CODE (ImpressDocument, impress_document,
65                          {
66                            EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
67                                                            impress_document_document_thumbnails_iface_init);
68                          });
69
70 /* Renderer */
71 static void
72 imp_render_draw_bezier_real (GdkDrawable *d, GdkGC *gc, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
73 {
74   int x, y, nx, ny;
75   int ax, bx, cx, ay, by, cy;
76   double t, t2, t3;
77
78   x = x0;
79   y = y0;
80
81   cx = 3 * (x1 - x0);
82   bx = 3 * (x2 - x1) - cx;
83   ax = x3 - x0 - cx - bx;
84   cy = 3 * (y1 - y0);
85   by = 3 * (y2 - y1) - cy;
86   ay = y3 - y0 - cy - by;
87
88   for (t = 0; t < 1; t += 0.01) {
89     t2 = t * t;
90     t3 = t2 * t;
91     nx = ax * t3 + bx * t2 + cx * t + x0;
92     ny = ay * t3 + by * t2 + cy * t + y0;
93     gdk_draw_line (d, gc, x, y, nx, ny);
94     x = nx;
95     y = ny;
96   }
97 }
98
99 static void
100 imp_render_get_size(void *drw_data, int *w, int *h)
101 {
102   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
103
104   gdk_drawable_get_size(impress_document->pixmap, w, h);
105 }
106
107 static void
108 imp_render_set_fg_color(void *drw_data, ImpColor *color)
109 {
110   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
111   GdkColor c;
112
113   c.red = color->red;
114   c.green = color->green;
115   c.blue = color->blue;
116   gdk_gc_set_rgb_fg_color(impress_document->gc, &c);
117 }
118
119 static void
120 imp_render_draw_line(void *drw_data, int x1, int y1, int x2, int y2)
121 {
122   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
123
124   gdk_draw_line(impress_document->pixmap, impress_document->gc, x1, y1, x2, y2);
125 }
126
127 static void
128 imp_render_draw_rect(void *drw_data, int fill, int x, int y, int w, int h)
129 {
130   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
131
132   gdk_draw_rectangle(impress_document->pixmap, impress_document->gc, fill, x, y, w, h);
133 }
134
135 static void
136 imp_render_draw_polygon(void *drw_data, int fill, ImpPoint *pts, int nr_pts)
137 {
138   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
139
140   gdk_draw_polygon(impress_document->pixmap, impress_document->gc, fill, (GdkPoint *)pts, nr_pts);
141 }
142
143 static void
144 imp_render_draw_arc(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea)
145 {
146   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
147
148   gdk_draw_arc(impress_document->pixmap, impress_document->gc, fill, x, y, w, h, sa * 64, ea * 64);
149 }
150
151 static void
152 imp_render_draw_bezier(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
153 {
154   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
155
156   imp_render_draw_bezier_real (impress_document->pixmap, impress_document->gc, x0, y0, x1, y1, x2, y2, x3, y3);
157 }
158
159 static void *
160 imp_render_open_image(void *drw_data, const unsigned char *pix, size_t size)
161 {
162   GdkPixbufLoader *gpl;
163   GdkPixbuf *pb;
164
165   gpl = gdk_pixbuf_loader_new();
166   gdk_pixbuf_loader_write(gpl, pix, size, NULL);
167   gdk_pixbuf_loader_close(gpl, NULL);
168   pb = gdk_pixbuf_loader_get_pixbuf(gpl);
169   return pb;
170 }
171
172 static void
173 imp_render_get_image_size(void *drw_data, void *img_data, int *w, int *h)
174 {
175   GdkPixbuf *pb = (GdkPixbuf *) img_data;
176
177   *w = gdk_pixbuf_get_width(pb);
178   *h = gdk_pixbuf_get_height(pb);
179 }
180
181 static void *
182 imp_render_scale_image(void *drw_data, void *img_data, int w, int h)
183 {
184   GdkPixbuf *pb = (GdkPixbuf *) img_data;
185
186   return gdk_pixbuf_scale_simple(pb, w, h, GDK_INTERP_BILINEAR);
187 }
188
189 static void
190 imp_render_draw_image(void *drw_data, void *img_data, int x, int y, int w, int h)
191 {
192   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
193   GdkPixbuf *pb = (GdkPixbuf *) img_data;
194
195   gdk_draw_pixbuf(impress_document->pixmap, impress_document->gc, pb, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
196 }
197
198 static void
199 imp_render_close_image(void *drw_data, void *img_data)
200 {
201   GdkPixbuf *pb = (GdkPixbuf *) img_data;
202
203   g_object_unref(G_OBJECT(pb));
204 }
205
206 static char *
207 imp_render_markup(const char *text, size_t len, int styles, int size)
208 {
209   double scr_mm, scr_px, dpi;
210   char *esc;
211   char *ret;
212   int sz;
213
214   scr_mm = gdk_screen_get_height_mm(gdk_screen_get_default());
215   scr_px = gdk_screen_get_height(gdk_screen_get_default());
216   dpi = (scr_px / scr_mm) * 25.4; 
217   sz = (int) ((double) size * 72.0 * PANGO_SCALE / dpi);
218   esc = g_markup_escape_text(text, len);
219   ret = g_strdup_printf("<span size ='%d'>%s</span>", sz, esc);
220   g_free(esc);
221   return ret;
222 }
223
224 static void
225 imp_render_get_text_size(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h)
226 {
227   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
228   PangoLayout *lay;
229   int pw, ph;
230   char *m;
231
232   g_return_if_fail (impress_document->pango_ctx != NULL);
233
234   lay = pango_layout_new(impress_document->pango_ctx);
235   m = imp_render_markup(text, len, styles, size);
236   pango_layout_set_markup(lay, m, strlen(m));
237   pango_layout_get_size(lay, &pw, &ph);
238   g_object_unref(lay);
239   g_free(m);
240   *w = pw / PANGO_SCALE;
241   *h = ph / PANGO_SCALE;
242 }
243
244 static void
245 imp_render_draw_text(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles)
246 {
247   ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
248   PangoLayout *lay;
249   char *m;
250
251   g_return_if_fail (impress_document->pango_ctx != NULL);
252
253   lay = pango_layout_new(impress_document->pango_ctx);
254   m = imp_render_markup(text, len, styles, size);
255   pango_layout_set_markup(lay, m, strlen(m));
256   gdk_draw_layout(impress_document->pixmap, impress_document->gc, x, y, lay);
257   g_object_unref(lay);
258   g_free(m);
259 }
260
261 static const ImpDrawer imp_render_functions = {
262   imp_render_get_size,
263   imp_render_set_fg_color,
264   imp_render_draw_line,
265   imp_render_draw_rect,
266   imp_render_draw_polygon,
267   imp_render_draw_arc,
268   imp_render_draw_bezier,
269   imp_render_open_image,
270   imp_render_get_image_size,
271   imp_render_scale_image,
272   imp_render_draw_image,
273   imp_render_close_image,
274   imp_render_get_text_size,
275   imp_render_draw_text
276 };
277
278 /* Document interface */
279 static gboolean
280 impress_document_load (EvDocument  *document,
281                     const char  *uri,
282                     GError     **error)
283 {
284   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
285   gchar *filename;
286   ImpDoc *imp;
287   int err;
288
289   /* FIXME: Could we actually load uris ? */
290   filename = g_filename_from_uri (uri, NULL, error);
291   if (!filename)
292     return FALSE;
293
294   imp = imp_open (filename, &err);
295   g_free (filename);
296
297   if (!imp)
298     {
299       g_set_error_literal (error,
300                            EV_DOCUMENT_ERROR,
301                            EV_DOCUMENT_ERROR_INVALID,
302                            _("Invalid document"));
303       return FALSE;
304     }
305   impress_document->imp = imp;
306
307   return TRUE;
308 }
309
310 static gboolean
311 impress_document_save (EvDocument  *document,
312                        const char  *uri,
313                        GError     **error)
314 {
315         g_set_error_literal (error,
316                              EV_DOCUMENT_ERROR,
317                              EV_DOCUMENT_ERROR_INVALID,
318                              "Not supported");
319         return FALSE;
320 }
321
322 static int
323 impress_document_get_n_pages (EvDocument  *document)
324 {
325   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
326
327   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), 0);
328   g_return_val_if_fail (impress_document->imp != NULL, 0);
329
330   return imp_nr_pages (impress_document->imp);
331 }
332
333 static void
334 impress_document_get_page_size (EvDocument   *document,
335                                 EvPage       *page,
336                                 double       *width,
337                                 double       *height)
338 {
339   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
340
341   g_return_if_fail (IMPRESS_IS_DOCUMENT (document));
342   g_return_if_fail (impress_document->imp != NULL);
343
344   //FIXME
345   *width = PAGE_WIDTH;
346   *height = PAGE_HEIGHT;
347 }
348
349 static gboolean
350 imp_render_get_from_drawable (ImpressDocument *impress_document)
351 {
352   ImpPage *page;
353
354   page = imp_get_page (impress_document->imp, impress_document->pagenum);
355
356   g_return_val_if_fail (page != NULL, FALSE);
357
358   ev_document_doc_mutex_lock ();
359   imp_context_set_page (impress_document->ctx, page);
360   imp_render (impress_document->ctx, impress_document);
361   ev_document_doc_mutex_unlock ();
362
363   impress_document->pixbuf = gdk_pixbuf_get_from_drawable (NULL,
364                                          GDK_DRAWABLE (impress_document->pixmap),
365                                          NULL,
366                                          0, 0,
367                                          0, 0,
368                                          PAGE_WIDTH, PAGE_HEIGHT);
369
370   g_cond_broadcast (impress_document->cond);
371   return FALSE;
372 }
373
374 static GdkPixbuf *
375 impress_document_render_pixbuf (EvDocument      *document,
376                                 EvRenderContext *rc)
377 {
378   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
379   GdkPixbuf       *pixbuf;
380
381   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), NULL);
382   g_return_val_if_fail (impress_document->imp != NULL, NULL);
383   
384   impress_document->pagenum = rc->page->index;
385
386   g_mutex_lock (impress_document->mutex);
387   impress_document->cond = g_cond_new ();
388
389   ev_document_fc_mutex_unlock ();
390   ev_document_doc_mutex_unlock ();
391   g_idle_add ((GSourceFunc) imp_render_get_from_drawable, impress_document);
392   g_cond_wait (impress_document->cond, impress_document->mutex);
393   g_cond_free (impress_document->cond);
394   ev_document_doc_mutex_lock ();
395   ev_document_fc_mutex_lock ();
396   
397   g_mutex_unlock (impress_document->mutex);
398
399   pixbuf = impress_document->pixbuf;
400   impress_document->pixbuf = NULL;
401
402   return pixbuf;
403 }
404
405 static cairo_surface_t *
406 impress_document_render (EvDocument      *document,
407                          EvRenderContext *rc)
408 {
409   GdkPixbuf *pixbuf;
410   cairo_surface_t *surface, *scaled_surface;
411
412   pixbuf = impress_document_render_pixbuf (document, rc);
413   
414   /* FIXME: impress backend should be ported to cairo */
415   surface = ev_document_misc_surface_from_pixbuf (pixbuf);
416   g_object_unref (pixbuf);
417
418   scaled_surface = ev_document_misc_surface_rotate_and_scale (surface,
419                                                               (PAGE_WIDTH * rc->scale) + 0.5,
420                                                               (PAGE_HEIGHT * rc->scale) + 0.5,
421                                                               rc->rotation);
422   cairo_surface_destroy (surface);
423
424   return scaled_surface;
425 }
426
427 static EvDocumentInfo *
428 impress_document_get_info (EvDocument *document)
429 {
430   EvDocumentInfo *info;
431
432   info = g_new0 (EvDocumentInfo, 1);
433   info->fields_mask = 0;
434
435   return info;
436 }
437
438 static void
439 impress_document_finalize (GObject *object)
440 {
441   ImpressDocument *impress_document = IMPRESS_DOCUMENT (object);
442
443   if (impress_document->mutex)
444     g_mutex_free (impress_document->mutex);
445
446   if (impress_document->imp)
447     imp_close (impress_document->imp);
448
449   if (impress_document->ctx)
450     imp_delete_context (impress_document->ctx);
451
452   if (impress_document->pango_ctx)
453     g_object_unref (impress_document->pango_ctx);
454
455   if (impress_document->pixmap)
456     g_object_unref (G_OBJECT (impress_document->pixmap));
457
458   if (impress_document->gc)
459     g_object_unref (impress_document->gc);
460
461   G_OBJECT_CLASS (impress_document_parent_class)->finalize (object);
462 }
463
464 static void
465 impress_document_class_init (ImpressDocumentClass *klass)
466 {
467   GObjectClass    *gobject_class = G_OBJECT_CLASS (klass);
468   EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
469
470   gobject_class->finalize = impress_document_finalize;
471
472   ev_document_class->load = impress_document_load;
473   ev_document_class->save = impress_document_save;
474   ev_document_class->get_n_pages = impress_document_get_n_pages;
475   ev_document_class->get_page_size = impress_document_get_page_size;
476   ev_document_class->render = impress_document_render;
477   ev_document_class->get_info = impress_document_get_info;
478 }
479
480 static GdkPixbuf *
481 impress_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
482                                            EvRenderContext      *rc, 
483                                            gboolean              border)
484 {
485   GdkPixbuf *pixbuf;
486   GdkPixbuf *scaled_pixbuf;
487
488   pixbuf = impress_document_render_pixbuf (EV_DOCUMENT (document), rc);
489   scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
490                                            (PAGE_WIDTH * rc->scale),
491                                            (PAGE_HEIGHT * rc->scale),
492                                            GDK_INTERP_BILINEAR);
493   g_object_unref (pixbuf);
494
495   if (border)
496     {
497       GdkPixbuf *tmp_pixbuf = scaled_pixbuf;
498       
499       scaled_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
500       g_object_unref (tmp_pixbuf);
501     }
502
503   return scaled_pixbuf;
504 }
505
506 static void
507 impress_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
508                                             EvRenderContext      *rc,
509                                             gint                 *width,
510                                             gint                 *height)
511 {
512   gdouble page_width, page_height;
513
514   impress_document_get_page_size (EV_DOCUMENT (document),
515                                   rc->page,
516                                   &page_width, &page_height);
517   
518   if (rc->rotation == 90 || rc->rotation == 270)
519     {
520       *width = (gint) (page_height * rc->scale);
521       *height = (gint) (page_width * rc->scale);
522     }
523   else
524     {
525       *width = (gint) (page_width * rc->scale);
526       *height = (gint) (page_height * rc->scale);
527     }
528 }
529
530 static void
531 impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
532 {
533   iface->get_thumbnail = impress_document_thumbnails_get_thumbnail;
534   iface->get_dimensions = impress_document_thumbnails_get_dimensions;
535 }
536
537 static void
538 impress_document_init (ImpressDocument *impress_document)
539 {
540   GdkWindow *window;
541
542   impress_document->mutex = g_mutex_new ();
543   impress_document->ctx = imp_create_context(&imp_render_functions);
544
545   window = gdk_screen_get_root_window (gdk_screen_get_default ());
546
547   impress_document->pixmap = gdk_pixmap_new (window,
548                                              PAGE_WIDTH, PAGE_HEIGHT, -1);
549   impress_document->gc = gdk_gc_new (impress_document->pixmap);
550   impress_document->pango_ctx = gdk_pango_context_get_for_screen (gdk_screen_get_default ());
551 }
552
553 /*
554  * vim: sw=2 ts=8 cindent noai bs=2
555  */