Graphviz  2.35.20130930.0449
gvrender_pango.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17 
18 #ifdef HAVE_STDLIB_H
19 #include <stdlib.h>
20 #endif
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
24 
25 #include "const.h"
26 #include "gvplugin_render.h"
27 #include "agxbuf.h"
28 #include "utils.h"
29 #include "gvplugin_device.h"
30 #include "gvio.h"
31 
32 #include "gvplugin_pango.h"
33 
34 #ifdef HAVE_PANGOCAIRO
35 #include <pango/pangocairo.h>
36 
37 typedef enum {
38  FORMAT_CAIRO,
39  FORMAT_PNG,
40  FORMAT_PS,
41  FORMAT_PDF,
42  FORMAT_SVG,
43  } format_type;
44 
45 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
46 
47 static double dashed[] = {6.};
48 static int dashed_len = ARRAY_SIZE(dashed);
49 
50 static double dotted[] = {2., 6.};
51 static int dotted_len = ARRAY_SIZE(dotted);
52 
53 #ifdef CAIRO_HAS_PS_SURFACE
54 #include <cairo-ps.h>
55 #endif
56 
57 #ifdef CAIRO_HAS_PDF_SURFACE
58 #include <cairo-pdf.h>
59 #endif
60 
61 #ifdef CAIRO_HAS_SVG_SURFACE
62 #include <cairo-svg.h>
63 #endif
64 
65 static void cairogen_set_color(cairo_t * cr, gvcolor_t * color)
66 {
67  cairo_set_source_rgba(cr, color->u.RGBA[0], color->u.RGBA[1],
68  color->u.RGBA[2], color->u.RGBA[3]);
69 }
70 
71 static void cairogen_add_color_stop_rgba(cairo_pattern_t *pat, double stop , gvcolor_t * color)
72 {
73  cairo_pattern_add_color_stop_rgba (pat, stop,color->u.RGBA[0], color->u.RGBA[1],
74  color->u.RGBA[2], color->u.RGBA[3]);
75 }
76 
77 
78 static cairo_status_t
79 writer (void *closure, const unsigned char *data, unsigned int length)
80 {
81  if (length == gvwrite((GVJ_t *)closure, (const char*)data, length))
82  return CAIRO_STATUS_SUCCESS;
83  return CAIRO_STATUS_WRITE_ERROR;
84 }
85 
86 static void cairogen_begin_job(GVJ_t * job)
87 {
88  if (job->external_context && job->context)
89  cairo_save((cairo_t *) job->context);
90 }
91 
92 static void cairogen_end_job(GVJ_t * job)
93 {
94  cairo_t *cr = (cairo_t *) job->context;
95 
96  if (job->external_context)
97  cairo_restore(cr);
98  else {
99  cairo_destroy(cr);
100  job->context = NULL;
101  }
102 }
103 
104 #define CAIRO_XMAX 32767
105 #define CAIRO_YMAX 32767
106 
107 static void cairogen_begin_page(GVJ_t * job)
108 {
109  cairo_t *cr = (cairo_t *) job->context;
110  cairo_surface_t *surface;
111  cairo_status_t status;
112 
113  if (cr == NULL) {
114  switch (job->render.id) {
115  case FORMAT_PS:
116 #ifdef CAIRO_HAS_PS_SURFACE
117  surface = cairo_ps_surface_create_for_stream (writer,
118  job, job->width, job->height);
119 #endif
120  break;
121  case FORMAT_PDF:
122 #ifdef CAIRO_HAS_PDF_SURFACE
123  surface = cairo_pdf_surface_create_for_stream (writer,
124  job, job->width, job->height);
125 #endif
126  break;
127  case FORMAT_SVG:
128 #ifdef CAIRO_HAS_SVG_SURFACE
129  surface = cairo_svg_surface_create_for_stream (writer,
130  job, job->width, job->height);
131 #endif
132  break;
133  case FORMAT_CAIRO:
134  case FORMAT_PNG:
135  default:
136  if (job->width >= CAIRO_XMAX || job->height >= CAIRO_YMAX) {
137  double scale = MIN((double)CAIRO_XMAX / job->width,
138  (double)CAIRO_YMAX / job->height);
139  job->width *= scale;
140  job->height *= scale;
141  job->scale.x *= scale;
142  job->scale.y *= scale;
143  fprintf(stderr,
144  "%s: graph is too large for cairo-renderer bitmaps. Scaling by %g to fit\n",
145  job->common->cmdname, scale);
146  }
147  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
148  job->width, job->height);
149  if (job->common->verbose)
150  fprintf(stderr,
151  "%s: allocating a %dK cairo image surface (%d x %d pixels)\n",
152  job->common->cmdname,
153  ROUND(job->width * job->height * 4 / 1024.),
154  job->width, job->height);
155  break;
156  }
157  status = cairo_surface_status(surface);
158  if (status != CAIRO_STATUS_SUCCESS) {
159  fprintf(stderr, "%s: failure to create cairo surface: %s\n",
160  job->common->cmdname,
161  cairo_status_to_string(status));
162  cairo_surface_destroy (surface);
163  return;
164  }
165  cr = cairo_create(surface);
166  cairo_surface_destroy (surface);
167  job->context = (void *) cr;
168  }
169 
170  cairo_scale(cr, job->scale.x, job->scale.y);
171  cairo_rotate(cr, -job->rotation * M_PI / 180.);
172  cairo_translate(cr, job->translation.x, -job->translation.y);
173 
174  cairo_rectangle(cr, job->clip.LL.x, - job->clip.LL.y,
175  job->clip.UR.x - job->clip.LL.x, - (job->clip.UR.y - job->clip.LL.y));
176  cairo_clip(cr);
177  cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
178 }
179 
180 static void cairogen_end_page(GVJ_t * job)
181 {
182  cairo_t *cr = (cairo_t *) job->context;
183  cairo_surface_t *surface;
184  cairo_status_t status;
185 
186  switch (job->render.id) {
187 
188 #ifdef CAIRO_HAS_PNG_FUNCTIONS
189  case FORMAT_PNG:
190  surface = cairo_get_target(cr);
191  cairo_surface_write_to_png_stream(surface, writer, job);
192  break;
193 #endif
194 
195  case FORMAT_PS:
196  case FORMAT_PDF:
197  case FORMAT_SVG:
198  cairo_show_page(cr);
199  surface = cairo_surface_reference(cairo_get_target(cr));
200  cairo_surface_finish(surface);
201  status = cairo_surface_status(surface);
202  cairo_surface_destroy(surface);
203  if (status != CAIRO_STATUS_SUCCESS)
204  fprintf(stderr, "cairo: %s\n", cairo_status_to_string(status));
205  break;
206 
207  case FORMAT_CAIRO:
208  default:
209  surface = cairo_get_target(cr);
210  if (cairo_image_surface_get_width(surface) == 0 || cairo_image_surface_get_height(surface) == 0) {
211  /* apparently cairo never allocates a surface if nothing was ever written to it */
212 /* but suppress this error message since a zero area surface seems to happen during normal operations, particular in -Tx11
213  fprintf(stderr, "ERROR: cairo surface has zero area, this may indicate some problem during rendering shapes.\n");
214  - jce */
215  }
216  job->imagedata = (char *)(cairo_image_surface_get_data(surface));
217  break;
218  /* formatting will be done by gvdevice_format() */
219  }
220 }
221 
222 static void cairogen_textpara(GVJ_t * job, pointf p, textpara_t * para)
223 {
224  obj_state_t *obj = job->obj;
225  cairo_t *cr = (cairo_t *) job->context;
226 
227  cairo_set_dash (cr, dashed, 0, 0.0); /* clear any dashing */
228  cairogen_set_color(cr, &(obj->pencolor));
229 
230  switch (para->just) {
231  case 'r':
232  p.x -= para->width;
233  break;
234  case 'l':
235  p.x -= 0.0;
236  break;
237  case 'n':
238  default:
239  p.x -= para->width / 2.0;
240  break;
241  }
242  p.y += para->yoffset_centerline + para->yoffset_layout;
243 
244  cairo_move_to (cr, p.x, -p.y);
245  cairo_save(cr);
246  cairo_scale(cr, POINTS_PER_INCH / FONT_DPI, POINTS_PER_INCH / FONT_DPI);
247  pango_cairo_show_layout(cr, (PangoLayout*)(para->layout));
248  cairo_restore(cr);
249 }
250 
251 static void cairogen_set_penstyle(GVJ_t *job, cairo_t *cr)
252 {
253  obj_state_t *obj = job->obj;
254 
255  if (obj->pen == PEN_DASHED) {
256  cairo_set_dash (cr, dashed, dashed_len, 0.0);
257  } else if (obj->pen == PEN_DOTTED) {
258  cairo_set_dash (cr, dotted, dotted_len, 0.0);
259  } else {
260  cairo_set_dash (cr, dashed, 0, 0.0);
261  }
262  cairo_set_line_width (cr, obj->penwidth);
263 
264 }
265 
266 static void cairo_gradient_fill (cairo_t* cr, obj_state_t* obj, int filled, pointf* A, int n)
267 {
268  cairo_pattern_t* pat;
269  float angle = obj->gradient_angle * M_PI / 180;
270  float r1,r2;
271  pointf G[2],c1;
272 
273  if (filled == GRADIENT) {
274  get_gradient_points(A, G, n, angle, 0);
275  pat = cairo_pattern_create_linear (G[0].x,G[0].y,G[1].x,G[1].y);
276  }
277  else {
278  get_gradient_points(A, G, n, 0, 1);
279  //r1 is inner radius, r2 is outer radius
280  r1 = G[1].x; /* Set a r2/4 in get_gradient_points */
281  r2 = G[1].y;
282  if (angle == 0) {
283  c1.x = G[0].x;
284  c1.y = G[0].y;
285  }
286  else {
287  c1.x = G[0].x + r1 * cos(angle);
288  c1.y = G[0].y - r1 * sin(angle);
289  }
290  pat = cairo_pattern_create_radial(c1.x,c1.y,r1,G[0].x,G[0].y,r2);
291  }
292  if (obj->gradient_frac > 0) {
293  cairogen_add_color_stop_rgba(pat,obj->gradient_frac - 0.001,&(obj->fillcolor));
294  cairogen_add_color_stop_rgba(pat,obj->gradient_frac,&(obj->stopcolor));
295  }
296  else {
297  cairogen_add_color_stop_rgba(pat,0,&(obj->fillcolor));
298  cairogen_add_color_stop_rgba(pat,1,&(obj->stopcolor));
299  }
300  cairo_set_source (cr, pat);
301  cairo_fill_preserve (cr);
302  cairo_pattern_destroy (pat);
303 }
304 
305 static void cairogen_ellipse(GVJ_t * job, pointf * A, int filled)
306 {
307  obj_state_t *obj = job->obj;
308  cairo_t *cr = (cairo_t *) job->context;
309  cairo_matrix_t matrix;
310  double rx, ry;
311 
312  cairogen_set_penstyle(job, cr);
313 
314  cairo_get_matrix(cr, &matrix);
315 
316  rx = A[1].x - A[0].x;
317  ry = A[1].y - A[0].y;
318 
319 #define RMIN 0.01
320 if (rx < RMIN) rx = RMIN;
321 if (ry < RMIN) ry = RMIN;
322 
323  cairo_translate(cr, A[0].x, -A[0].y);
324  cairo_scale(cr, rx, ry);
325  cairo_move_to(cr, 1., 0.);
326  cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
327 
328  cairo_set_matrix(cr, &matrix);
329 
330  if (filled == GRADIENT || filled == (RGRADIENT)) {
331  cairo_gradient_fill (cr, obj, filled, A, 2);
332  }
333  else if (filled) {
334  cairogen_set_color(cr, &(obj->fillcolor));
335  cairo_fill_preserve(cr);
336  }
337  cairogen_set_color(cr, &(obj->pencolor));
338  cairo_stroke(cr);
339 }
340 
341 static void
342 cairogen_polygon(GVJ_t * job, pointf * A, int n, int filled)
343 {
344  obj_state_t *obj = job->obj;
345  cairo_t *cr = (cairo_t *) job->context;
346  int i;
347 
348  cairogen_set_penstyle(job, cr);
349 
350  cairo_move_to(cr, A[0].x, -A[0].y);
351  for (i = 1; i < n; i++)
352  cairo_line_to(cr, A[i].x, -A[i].y);
353  cairo_close_path(cr);
354  if (filled == GRADIENT || filled == (RGRADIENT)) {
355  cairo_gradient_fill (cr, obj, filled, A, n);
356  }
357  else if (filled) {
358  cairogen_set_color(cr, &(obj->fillcolor));
359  cairo_fill_preserve(cr);
360  }
361  cairogen_set_color(cr, &(obj->pencolor));
362  cairo_stroke(cr);
363 }
364 
365 static void
366 cairogen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
367  int arrow_at_end, int filled)
368 {
369  obj_state_t *obj = job->obj;
370  cairo_t *cr = (cairo_t *) job->context;
371  int i;
372 
373  cairogen_set_penstyle(job, cr);
374 
375  cairo_move_to(cr, A[0].x, -A[0].y);
376  for (i = 1; i < n; i += 3)
377  cairo_curve_to(cr, A[i].x, -A[i].y, A[i + 1].x, -A[i + 1].y,
378  A[i + 2].x, -A[i + 2].y);
379  if (filled == GRADIENT || filled == (RGRADIENT)) {
380  cairo_gradient_fill (cr, obj, filled, A, n);
381  }
382  else if (filled) {
383  cairogen_set_color(cr, &(obj->fillcolor));
384  cairo_fill_preserve(cr);
385  }
386  cairogen_set_color(cr, &(obj->pencolor));
387  cairo_stroke(cr);
388 }
389 
390 static void
391 cairogen_polyline(GVJ_t * job, pointf * A, int n)
392 {
393  obj_state_t *obj = job->obj;
394  cairo_t *cr = (cairo_t *) job->context;
395  int i;
396 
397  cairogen_set_penstyle(job, cr);
398 
399  cairo_move_to(cr, A[0].x, -A[0].y);
400  for (i = 1; i < n; i++)
401  cairo_line_to(cr, A[i].x, -A[i].y);
402  cairogen_set_color(cr, &(obj->pencolor));
403  cairo_stroke(cr);
404 }
405 
406 static gvrender_engine_t cairogen_engine = {
407  cairogen_begin_job,
408  cairogen_end_job,
409  0, /* cairogen_begin_graph */
410  0, /* cairogen_end_graph */
411  0, /* cairogen_begin_layer */
412  0, /* cairogen_end_layer */
413  cairogen_begin_page,
414  cairogen_end_page,
415  0, /* cairogen_begin_cluster */
416  0, /* cairogen_end_cluster */
417  0, /* cairogen_begin_nodes */
418  0, /* cairogen_end_nodes */
419  0, /* cairogen_begin_edges */
420  0, /* cairogen_end_edges */
421  0, /* cairogen_begin_node */
422  0, /* cairogen_end_node */
423  0, /* cairogen_begin_edge */
424  0, /* cairogen_end_edge */
425  0, /* cairogen_begin_anchor */
426  0, /* cairogen_end_anchor */
427  0, /* cairogen_begin_label */
428  0, /* cairogen_end_label */
429  cairogen_textpara,
430  0, /* cairogen_resolve_color */
431  cairogen_ellipse,
432  cairogen_polygon,
433  cairogen_bezier,
434  cairogen_polyline,
435  0, /* cairogen_comment */
436  0, /* cairogen_library_shape */
437 };
438 
439 static gvrender_features_t render_features_cairo = {
441  | GVRENDER_DOES_TRANSFORM, /* flags */
442  4., /* default pad - graph units */
443  0, /* knowncolors */
444  0, /* sizeof knowncolors */
445  RGBA_DOUBLE, /* color_type */
446 };
447 
448 static gvdevice_features_t device_features_png = {
450  | GVDEVICE_DOES_TRUECOLOR,/* flags */
451  {0.,0.}, /* default margin - points */
452  {0.,0.}, /* default page width, height - points */
453  {96.,96.}, /* typical monitor dpi */
454 };
455 
456 static gvdevice_features_t device_features_ps = {
457  GVDEVICE_DOES_TRUECOLOR, /* flags */
458  {36.,36.}, /* default margin - points */
459  {0.,0.}, /* default page width, height - points */
460  {72.,72.}, /* postscript 72 dpi */
461 };
462 
463 static gvdevice_features_t device_features_pdf = {
465  | GVDEVICE_DOES_TRUECOLOR,/* flags */
466  {36.,36.}, /* default margin - points */
467  {0.,0.}, /* default page width, height - points */
468  {72.,72.}, /* postscript 72 dpi */
469 };
470 
472  GVDEVICE_DOES_TRUECOLOR, /* flags */
473  {0.,0.}, /* default margin - points */
474  {0.,0.}, /* default page width, height - points */
475  {72.,72.}, /* svg 72 dpi */
476 };
477 #endif
478 
480 #ifdef HAVE_PANGOCAIRO
481  {FORMAT_CAIRO, "cairo", 10, &cairogen_engine, &render_features_cairo},
482 #endif
483  {0, NULL, 0, NULL, NULL}
484 };
485 
487 #ifdef HAVE_PANGOCAIRO
488 #ifdef CAIRO_HAS_PNG_FUNCTIONS
489  {FORMAT_PNG, "png:cairo", 10, NULL, &device_features_png},
490 #endif
491 #ifdef CAIRO_HAS_PS_SURFACE
492  {FORMAT_PS, "ps:cairo", -10, NULL, &device_features_ps},
493 #endif
494 #ifdef CAIRO_HAS_PDF_SURFACE
495  {FORMAT_PDF, "pdf:cairo", 10, NULL, &device_features_pdf},
496 #endif
497 #ifdef CAIRO_HAS_SVG_SURFACE
498  {FORMAT_SVG, "svg:cairo", -10, NULL, &device_features_svg},
499 #endif
500 #endif
501  {0, NULL, 0, NULL, NULL}
502 };