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