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