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