Fix severe security issues
[external/pango1.0.git] / pango-view / viewer-pangocairo.c
1 /* viewer-pangocairo.c: PangoCairo viewer backend.
2  *
3  * Copyright (C) 1999,2004,2005 Red Hat, Inc.
4  * Copyright (C) 2001 Sun Microsystems
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include "viewer-render.h"
25 #include "viewer-cairo.h"
26
27 #include <pango/pangocairo.h>
28
29 static int opt_annotate = 0;
30
31 typedef struct
32 {
33   const CairoViewerIface *iface;
34
35   gpointer backend;
36
37   PangoFontMap *fontmap;
38   cairo_font_options_t *font_options;
39 } CairoViewer;
40
41 static gpointer
42 pangocairo_view_create (const PangoViewer *klass G_GNUC_UNUSED)
43 {
44   CairoViewer *instance;
45
46   instance = g_slice_new (CairoViewer);
47
48   instance->backend = cairo_viewer_iface_create (&instance->iface);
49
50   instance->fontmap = pango_cairo_font_map_new ();
51   pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (instance->fontmap), opt_dpi);
52
53   instance->font_options = cairo_font_options_create ();
54   if (opt_hinting != HINT_DEFAULT)
55     {
56       if (opt_hinting == HINT_NONE)
57         cairo_font_options_set_hint_style (instance->font_options, CAIRO_HINT_STYLE_NONE);
58       else if (opt_hinting == HINT_FULL)
59         cairo_font_options_set_hint_style (instance->font_options, CAIRO_HINT_STYLE_FULL);
60     }
61
62   return instance;
63 }
64
65 static void
66 pangocairo_view_destroy (gpointer instance)
67 {
68   CairoViewer *c = (CairoViewer *) instance;
69
70   cairo_font_options_destroy (c->font_options);
71
72   g_object_unref (c->fontmap);
73
74   c->iface->backend_class->destroy (c->backend);
75
76   cairo_debug_reset_static_data ();
77
78   g_slice_free (CairoViewer, c);
79 }
80
81 static PangoContext *
82 pangocairo_view_get_context (gpointer instance)
83 {
84   CairoViewer *c = (CairoViewer *) instance;
85   PangoContext *context;
86
87   context = pango_font_map_create_context (c->fontmap);
88   pango_cairo_context_set_font_options (context, c->font_options);
89
90   return context;
91 }
92
93 typedef struct
94 {
95   gpointer backend;
96
97   cairo_surface_t *cairo;
98 } CairoSurface;
99
100 static gpointer
101 pangocairo_view_create_surface (gpointer instance,
102                                 int      width,
103                                 int      height)
104 {
105   CairoViewer *c = (CairoViewer *) instance;
106   CairoSurface *surface;
107
108   surface = g_slice_new (CairoSurface);
109
110   surface->backend = c->iface->backend_class->create_surface (c->backend,
111                                                               width, height);
112
113   surface->cairo = c->iface->create_surface (c->backend,
114                                              surface->backend,
115                                              width, height);
116
117   return surface;
118 }
119
120 static void
121 pangocairo_view_destroy_surface (gpointer instance,
122                                  gpointer surface)
123 {
124   CairoViewer *c = (CairoViewer *) instance;
125   CairoSurface *c_surface = (CairoSurface *) surface;
126
127   c->iface->backend_class->destroy_surface (c->backend, c_surface->backend);
128   cairo_surface_destroy (c_surface->cairo);
129
130   g_slice_free (CairoSurface, surface);
131 }
132
133 static void
134 render_callback (PangoLayout *layout,
135                  int          x,
136                  int          y,
137                  gpointer     context,
138                  gpointer     state)
139 {
140   cairo_t *cr = (cairo_t *) context;
141   int annotate = (GPOINTER_TO_INT (state) + opt_annotate) % 3;
142
143   cairo_save (cr);
144   cairo_translate (cr, x, y);
145
146   if (annotate)
147     {
148       cairo_pattern_t *pattern;
149       PangoRectangle ink, logical;
150       double lw = cairo_get_line_width (cr);
151       PangoLayoutIter* iter;
152
153       pango_layout_get_extents (layout, &ink, &logical);
154
155       if (annotate >= 2)
156         {
157           /* draw resolved gravity "roof" in blue */
158           cairo_save (cr);
159           cairo_translate (cr,
160                            (double)logical.x / PANGO_SCALE,
161                            (double)logical.y / PANGO_SCALE);
162           cairo_scale     (cr,
163                            (double)logical.width / PANGO_SCALE * 0.5,
164                            (double)logical.height / PANGO_SCALE * 0.5);
165           cairo_translate   (cr,  1.0,  1.0);
166           cairo_rotate (cr,
167             pango_gravity_to_rotation (
168               pango_context_get_gravity (
169                 pango_layout_get_context (layout))));
170           cairo_move_to     (cr, -1.0, -1.0);
171           cairo_rel_line_to (cr, +1.0, -0.2); /* /   */
172           cairo_rel_line_to (cr, +1.0, +0.2); /*   \ */
173           cairo_close_path  (cr);             /*  -  */
174           pattern = cairo_pattern_create_linear (0, -1.0, 0, -1.2);
175           cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 1.0, 0.0);
176           cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 1.0, 0.15);
177           cairo_set_source (cr, pattern);
178           cairo_fill (cr);
179           /* once more, without close_path this time */
180           cairo_move_to     (cr, -1.0, -1.0);
181           cairo_rel_line_to (cr, +1.0, -0.2); /* /   */
182           cairo_rel_line_to (cr, +1.0, +0.2); /*   \ */
183           /* silly line_width is not locked :(. get rid of scale. */
184           cairo_restore (cr);
185           cairo_save (cr);
186           cairo_set_source_rgba (cr, 0.0, 0.0, 0.7, 0.2);
187           cairo_stroke (cr);
188           cairo_restore (cr);
189
190
191           /* draw block progression arrow in green */
192           cairo_save (cr);
193           cairo_translate (cr,
194                            (double)logical.x / PANGO_SCALE,
195                            (double)logical.y / PANGO_SCALE);
196           cairo_scale     (cr,
197                            (double)logical.width / PANGO_SCALE * 0.5,
198                            (double)logical.height / PANGO_SCALE * 0.5);
199           cairo_translate   (cr,  1.0,  1.0);
200           cairo_move_to     (cr, -0.4, -0.7);
201           cairo_rel_line_to (cr, +0.8,  0.0); /*  --   */
202           cairo_rel_line_to (cr,  0.0, +0.9); /*    |  */
203           cairo_rel_line_to (cr, +0.4,  0.0); /*     - */
204           cairo_rel_line_to (cr, -0.8, +0.5); /*    /  */
205           cairo_rel_line_to (cr, -0.8, -0.5); /*  \    */
206           cairo_rel_line_to (cr, +0.4,  0.0); /* -     */
207           cairo_close_path  (cr);             /*  |    */
208           pattern = cairo_pattern_create_linear (0, -0.7, 0, 0.7);
209           cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 1.0, 0.0, 0.0);
210           cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 1.0, 0.0, 0.15);
211           cairo_set_source (cr, pattern);
212           cairo_fill_preserve (cr);
213           /* silly line_width is not locked :(. get rid of scale. */
214           cairo_restore (cr);
215           cairo_save (cr);
216           cairo_set_source_rgba (cr, 0.0, 0.7, 0.0, 0.2);
217           cairo_stroke (cr);
218           cairo_restore (cr);
219         }
220
221       /* draw baselines with line direction arrow in orange */
222       cairo_save (cr);
223       cairo_set_source_rgba (cr, 1.0, 0.5, 0.0, 0.5);
224       iter = pango_layout_get_iter (layout);
225       do
226         {
227           PangoLayoutLine *line = pango_layout_iter_get_line (iter);
228           double width = (double)logical.width / PANGO_SCALE;
229
230           y = pango_layout_iter_get_baseline (iter);
231           cairo_save (cr);
232           cairo_translate (cr,
233                          (double)logical.x / PANGO_SCALE + width * 0.5,
234                          (double)y / PANGO_SCALE);
235           if (line->resolved_dir)
236             cairo_scale (cr, -1, 1);
237           cairo_move_to     (cr, -width * .5, -lw*0.2);
238           cairo_rel_line_to (cr, +width * .9, -lw*0.3);
239           cairo_rel_line_to (cr,  0,          -lw);
240           cairo_rel_line_to (cr, +width * .1, +lw*1.5);
241           cairo_rel_line_to (cr, -width * .1, +lw*1.5);
242           cairo_rel_line_to (cr, 0,           -lw);
243           cairo_rel_line_to (cr, -width * .9, -lw*0.3);
244           cairo_close_path (cr);
245           cairo_fill (cr);
246           cairo_restore (cr);
247         }
248       while (pango_layout_iter_next_line (iter));
249       pango_layout_iter_free (iter);
250       cairo_restore (cr);
251
252       /* draw the logical rect in red */
253       cairo_save (cr);
254       cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5);
255       cairo_rectangle (cr,
256                        (double)logical.x / PANGO_SCALE - lw / 2,
257                        (double)logical.y / PANGO_SCALE - lw / 2,
258                        (double)logical.width / PANGO_SCALE + lw,
259                        (double)logical.height / PANGO_SCALE + lw);
260       cairo_stroke (cr);
261       cairo_restore (cr);
262
263       /* draw the ink rect in green */
264       cairo_save (cr);
265       cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 0.5);
266       cairo_rectangle (cr,
267                        (double)ink.x / PANGO_SCALE - lw / 2,
268                        (double)ink.y / PANGO_SCALE - lw / 2,
269                        (double)ink.width / PANGO_SCALE + lw,
270                        (double)ink.height / PANGO_SCALE + lw);
271       cairo_stroke (cr);
272       cairo_restore (cr);
273     }
274
275   cairo_move_to (cr, 0, 0);
276   pango_cairo_show_layout (cr, layout);
277
278   cairo_restore (cr);
279 }
280
281 static void
282 transform_callback (PangoContext *context,
283                     PangoMatrix  *matrix,
284                     gpointer      cr_context,
285                     gpointer      state G_GNUC_UNUSED)
286 {
287   cairo_t *cr = (cairo_t *)cr_context;
288   cairo_matrix_t cairo_matrix;
289
290   if (matrix)
291     {
292       cairo_matrix.xx = matrix->xx;
293       cairo_matrix.yx = matrix->yx;
294       cairo_matrix.xy = matrix->xy;
295       cairo_matrix.yy = matrix->yy;
296       cairo_matrix.x0 = matrix->x0;
297       cairo_matrix.y0 = matrix->y0;
298     }
299   else
300     {
301       cairo_matrix_init_identity (&cairo_matrix);
302     }
303
304   cairo_set_matrix (cr, &cairo_matrix);
305
306   pango_cairo_update_context (cr, context);
307 }
308
309 static void
310 pangocairo_view_render (gpointer      instance,
311                         gpointer      surface,
312                         PangoContext *context,
313                         int          *width,
314                         int          *height,
315                         gpointer      state)
316 {
317   CairoViewer *c = (CairoViewer *) instance;
318   cairo_t *cr;
319   CairoSurface *c_surface = (CairoSurface *) surface;
320
321   g_assert (surface);
322
323   cr = cairo_create (c_surface->cairo);
324
325   transform_callback (context, NULL, cr, state);
326
327   c->iface->paint_background (instance, cr);
328
329   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
330   cairo_set_source_rgba (cr,
331                          opt_fg_color.red / 65535.,
332                          opt_fg_color.green / 65535.,
333                          opt_fg_color.blue / 65535.,
334                          opt_fg_alpha / 65535.);
335
336   do_output (context, render_callback, transform_callback, cr, state, width, height);
337
338   cairo_destroy (cr);
339 }
340
341 #ifdef HAVE_CAIRO_PNG
342 static cairo_status_t
343 write_func (void                *closure,
344             const unsigned char *data,
345             unsigned int         length)
346 {
347   FILE *stream = (FILE *) closure;
348   unsigned int l;
349
350   l = fwrite (data, 1, length, stream);
351
352   return l == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
353 }
354
355 static void
356 pangocairo_view_write (gpointer instance G_GNUC_UNUSED,
357                        gpointer surface,
358                        FILE    *stream,
359                        int      width G_GNUC_UNUSED,
360                        int      height G_GNUC_UNUSED)
361 {
362   CairoSurface *c_surface = (CairoSurface *) surface;
363
364   cairo_surface_write_to_png_stream (c_surface->cairo, write_func, stream);
365 }
366 #endif
367
368 static gpointer
369 pangocairo_view_create_window (gpointer    instance,
370                                const char *title,
371                                int         width,
372                                int         height)
373 {
374   CairoViewer *c = (CairoViewer *) instance;
375
376   if (!c->iface->backend_class->create_window)
377     return NULL;
378
379   return c->iface->backend_class->create_window (c->backend,
380                                                  title,
381                                                  width, height);
382 }
383
384 static void
385 pangocairo_view_destroy_window (gpointer instance,
386                                 gpointer window)
387 {
388   CairoViewer *c = (CairoViewer *) instance;
389
390   c->iface->backend_class->destroy_window (c->backend,
391                                            window);
392 }
393
394 static gpointer
395 pangocairo_view_display (gpointer instance,
396                          gpointer surface,
397                          gpointer window,
398                          int width, int height,
399                          gpointer state)
400 {
401   CairoViewer *c = (CairoViewer *) instance;
402   CairoSurface *c_surface = (CairoSurface *) surface;
403
404   return c->iface->backend_class->display (c->backend,
405                                            c_surface->backend,
406                                            window,
407                                            width, height,
408                                            state);
409 }
410
411 static GOptionGroup *
412 pangocairo_view_get_option_group (const PangoViewer *klass G_GNUC_UNUSED)
413 {
414   GOptionEntry entries[] =
415   {
416     {"annotate",        0, 0, G_OPTION_ARG_INT, &opt_annotate,
417      "Annotate the output",                             "1 or 2"},
418     {NULL}
419   };
420   GOptionGroup *group;
421
422   group = g_option_group_new ("cairo",
423                               "Cairo backend options:",
424                               "Options understood by the cairo backend",
425                               NULL,
426                               NULL);
427
428   g_option_group_add_entries (group, entries);
429
430   cairo_viewer_add_options (group);
431
432   return group;
433 }
434
435 const PangoViewer pangocairo_viewer = {
436   "PangoCairo",
437   "cairo",
438 #ifdef HAVE_CAIRO_PNG
439   "png",
440 #else
441   NULL,
442 #endif
443   pangocairo_view_create,
444   pangocairo_view_destroy,
445   pangocairo_view_get_context,
446   pangocairo_view_create_surface,
447   pangocairo_view_destroy_surface,
448   pangocairo_view_render,
449 #ifdef HAVE_CAIRO_PNG
450   pangocairo_view_write,
451 #else
452   NULL,
453 #endif
454   pangocairo_view_create_window,
455   pangocairo_view_destroy_window,
456   pangocairo_view_display,
457   NULL,
458   NULL,
459   pangocairo_view_get_option_group
460 };