]> www.fi.muni.cz Git - evince.git/blob - backend/impress/impress-document.c
Include config.h. Bug #504721.
[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 <config.h>
24 #include <gtk/gtk.h>
25 #include <glib/gi18n.h>
26 #include <string.h>
27
28 #include "imposter.h"
29 #include "impress-document.h"
30 #include "ev-document-thumbnails.h"
31 #include "ev-document-misc.h"
32
33 struct _ImpressDocumentClass
34 {
35   GObjectClass parent_class;
36 };
37
38 struct _ImpressDocument
39 {
40   GObject parent_instance;
41
42   ImpDoc *imp;
43   ImpRenderCtx *ctx;
44
45   GMutex *mutex;
46   GdkPixmap *pixmap;
47   GdkGC *gc;
48   PangoContext *pango_ctx;
49
50   /* Only used while rendering inside the mainloop */
51   int pagenum;
52   GdkPixbuf *pixbuf;
53   GCond *cond;
54 };
55
56 #define PAGE_WIDTH 1024
57 #define PAGE_HEIGHT 768
58
59 typedef struct _ImpressDocumentClass ImpressDocumentClass;
60
61 static void impress_document_document_iface_init (EvDocumentIface *iface);
62 static void impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
63
64 EV_BACKEND_REGISTER_WITH_CODE (ImpressDocument, impress_document,
65                          {
66                            G_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     {
293       g_set_error (error,
294                    EV_DOCUMENT_ERROR,
295                    EV_DOCUMENT_ERROR_INVALID,
296                    _("Remote files aren't supported"));
297       return FALSE;
298     }
299
300   imp = imp_open (filename, &err);
301
302   if (!imp)
303     {
304       g_set_error (error,
305                    EV_DOCUMENT_ERROR,
306                    EV_DOCUMENT_ERROR_INVALID,
307                    _("Invalid document"));
308       g_free (filename);
309       return FALSE;
310     }
311   impress_document->imp = imp;
312
313   return TRUE;
314 }
315
316 static gboolean
317 impress_document_save (EvDocument  *document,
318                       const char  *uri,
319                       GError     **error)
320 {
321         return FALSE;
322 }
323
324 static int
325 impress_document_get_n_pages (EvDocument  *document)
326 {
327   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
328
329   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), 0);
330   g_return_val_if_fail (impress_document->imp != NULL, 0);
331
332   return imp_nr_pages (impress_document->imp);
333 }
334
335 static void
336 impress_document_get_page_size (EvDocument   *document,
337                              int           page,
338                              double       *width,
339                              double       *height)
340 {
341   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
342
343   g_return_if_fail (IMPRESS_IS_DOCUMENT (document));
344   g_return_if_fail (impress_document->imp != NULL);
345
346   //FIXME
347   *width = PAGE_WIDTH;
348   *height = PAGE_HEIGHT;
349 }
350
351 static gboolean
352 imp_render_get_from_drawable (ImpressDocument *impress_document)
353 {
354   ImpPage *page;
355
356   page = imp_get_page (impress_document->imp, impress_document->pagenum);
357
358   g_return_val_if_fail (page != NULL, FALSE);
359
360   ev_document_doc_mutex_lock ();
361   imp_context_set_page (impress_document->ctx, page);
362   imp_render (impress_document->ctx, impress_document);
363   ev_document_doc_mutex_unlock ();
364
365   impress_document->pixbuf = gdk_pixbuf_get_from_drawable (NULL,
366                                          GDK_DRAWABLE (impress_document->pixmap),
367                                          NULL,
368                                          0, 0,
369                                          0, 0,
370                                          PAGE_WIDTH, PAGE_HEIGHT);
371
372   g_cond_broadcast (impress_document->cond);
373   return FALSE;
374 }
375
376 static GdkPixbuf *
377 impress_document_render_pixbuf (EvDocument      *document,
378                                 EvRenderContext *rc)
379 {
380   ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
381   GdkPixbuf       *pixbuf;
382
383   g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), NULL);
384   g_return_val_if_fail (impress_document->imp != NULL, NULL);
385   
386   impress_document->pagenum = rc->page;
387
388   g_mutex_lock (impress_document->mutex);
389   impress_document->cond = g_cond_new ();
390
391   ev_document_fc_mutex_unlock ();
392   ev_document_doc_mutex_unlock ();
393   g_idle_add ((GSourceFunc) imp_render_get_from_drawable, impress_document);
394   g_cond_wait (impress_document->cond, impress_document->mutex);
395   g_cond_free (impress_document->cond);
396   ev_document_doc_mutex_lock ();
397   ev_document_fc_mutex_lock ();
398   
399   g_mutex_unlock (impress_document->mutex);
400
401   pixbuf = impress_document->pixbuf;
402   impress_document->pixbuf = NULL;
403
404   return pixbuf;
405 }
406
407 static cairo_surface_t *
408 impress_document_render (EvDocument      *document,
409                          EvRenderContext *rc)
410 {
411   GdkPixbuf *pixbuf;
412   cairo_surface_t *surface, *scaled_surface;
413
414   pixbuf = impress_document_render_pixbuf (document, rc);
415   
416   /* FIXME: impress backend should be ported to cairo */
417   surface = ev_document_misc_surface_from_pixbuf (pixbuf);
418   g_object_unref (pixbuf);
419
420   scaled_surface = ev_document_misc_surface_rotate_and_scale (surface,
421                                                               (PAGE_WIDTH * rc->scale) + 0.5,
422                                                               (PAGE_HEIGHT * rc->scale) + 0.5,
423                                                               rc->rotation);
424   cairo_surface_destroy (surface);
425
426   return scaled_surface;
427 }
428
429 static void
430 impress_document_finalize (GObject *object)
431 {
432   ImpressDocument *impress_document = IMPRESS_DOCUMENT (object);
433
434   if (impress_document->mutex)
435     g_mutex_free (impress_document->mutex);
436
437   if (impress_document->imp)
438     imp_close (impress_document->imp);
439
440   if (impress_document->ctx)
441     imp_delete_context (impress_document->ctx);
442
443   if (impress_document->pango_ctx)
444     g_object_unref (impress_document->pango_ctx);
445
446   if (impress_document->pixmap)
447     g_object_unref (G_OBJECT (impress_document->pixmap));
448
449   if (impress_document->gc)
450     g_object_unref (impress_document->gc);
451
452   G_OBJECT_CLASS (impress_document_parent_class)->finalize (object);
453 }
454
455 static void
456 impress_document_class_init (ImpressDocumentClass *klass)
457 {
458   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
459
460   gobject_class->finalize = impress_document_finalize;
461 }
462
463 static EvDocumentInfo *
464 impress_document_get_info (EvDocument *document)
465 {
466   EvDocumentInfo *info;
467
468   info = g_new0 (EvDocumentInfo, 1);
469   info->fields_mask = 0;
470
471   return info;
472 }
473
474 static void
475 impress_document_document_iface_init (EvDocumentIface *iface)
476 {
477   iface->load = impress_document_load;
478   iface->save = impress_document_save;
479   iface->get_n_pages = impress_document_get_n_pages;
480   iface->get_page_size = impress_document_get_page_size;
481   iface->render = impress_document_render;
482   iface->get_info = impress_document_get_info;
483 }
484
485 static GdkPixbuf *
486 impress_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
487                                            EvRenderContext      *rc, 
488                                            gboolean              border)
489 {
490   GdkPixbuf *pixbuf;
491   GdkPixbuf *scaled_pixbuf;
492   gdouble w, h;
493
494   pixbuf = impress_document_render_pixbuf (EV_DOCUMENT (document), rc);
495   scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
496                                            (PAGE_WIDTH * rc->scale),
497                                            (PAGE_HEIGHT * rc->scale),
498                                            GDK_INTERP_BILINEAR);
499   g_object_unref (pixbuf);
500
501   if (border)
502     {
503       GdkPixbuf *tmp_pixbuf = scaled_pixbuf;
504       
505       scaled_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
506       g_object_unref (tmp_pixbuf);
507     }
508
509   return scaled_pixbuf;
510 }
511
512 static void
513 impress_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
514                                             EvRenderContext      *rc,
515                                             gint                 *width,
516                                             gint                 *height)
517 {
518   gdouble page_width, page_height;
519
520   impress_document_get_page_size (EV_DOCUMENT (document),
521                                   rc->page,
522                                   &page_width, &page_height);
523   
524   if (rc->rotation == 90 || rc->rotation == 270)
525     {
526       *width = (gint) (page_height * rc->scale);
527       *height = (gint) (page_width * rc->scale);
528     }
529   else
530     {
531       *width = (gint) (page_width * rc->scale);
532       *height = (gint) (page_height * rc->scale);
533     }
534 }
535
536 static void
537 impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
538 {
539   iface->get_thumbnail = impress_document_thumbnails_get_thumbnail;
540   iface->get_dimensions = impress_document_thumbnails_get_dimensions;
541 }
542
543 static void
544 impress_document_init (ImpressDocument *impress_document)
545 {
546   GdkWindow *window;
547
548   impress_document->mutex = g_mutex_new ();
549   impress_document->ctx = imp_create_context(&imp_render_functions);
550
551   window = gdk_screen_get_root_window (gdk_screen_get_default ());
552
553   impress_document->pixmap = gdk_pixmap_new (window,
554                                              PAGE_WIDTH, PAGE_HEIGHT, -1);
555   impress_document->gc = gdk_gc_new (impress_document->pixmap);
556   impress_document->pango_ctx = gdk_pango_context_get_for_screen (gdk_screen_get_default ());
557 }
558
559 /*
560  * vim: sw=2 ts=8 cindent noai bs=2
561  */