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