Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
gvrender_pango.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12#include <errno.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17#include <common/const.h>
18#include <gvc/gvplugin_render.h>
19#include <cgraph/agxbuf.h>
20#include <common/utils.h>
21#include <gvc/gvplugin_device.h>
22#include <gvc/gvio.h>
23#include <math.h>
24
25#include "gvplugin_pango.h"
26
27#include <pango/pangocairo.h>
28
37
38#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
39
40static double dashed[] = {6.};
42
43static double dotted[] = {2., 6.};
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
58static void cairogen_polyline(GVJ_t *job, pointf *A, size_t n);
59
60static 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
66static 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
73static cairo_status_t
74writer (void *closure, const unsigned char *data, unsigned int length)
75{
76 if (length == gvwrite(closure, (const char*)data, length))
77 return CAIRO_STATUS_SUCCESS;
78 return CAIRO_STATUS_WRITE_ERROR;
79}
80
81static void cairogen_begin_job(GVJ_t * job)
82{
83 if (job->external_context && job->context)
84 cairo_save(job->context);
85}
86
87static void cairogen_end_job(GVJ_t * job)
88{
89 cairo_t *cr = 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
99static const double CAIRO_XMAX = 32767;
100static const double CAIRO_YMAX = 32767;
101
102static void cairogen_begin_page(GVJ_t * job)
103{
104 cairo_t *cr = 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 case FORMAT_EPS:
112#ifdef CAIRO_HAS_PS_SURFACE
113 surface = cairo_ps_surface_create_for_stream (writer,
114 job, job->width, job->height);
115 if (job->render.id == FORMAT_EPS)
116 cairo_ps_surface_set_eps (surface, TRUE);
117#endif
118 break;
119 case FORMAT_PDF:
120#ifdef CAIRO_HAS_PDF_SURFACE
121 surface = cairo_pdf_surface_create_for_stream (writer,
122 job, job->width, job->height);
123
124 {
125 const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
126 if (source_date_epoch != NULL) {
127 char *end = NULL;
128 errno = 0;
129 long epoch = strtol(source_date_epoch, &end, 10);
130 // from https://reproducible-builds.org/specs/source-date-epoch/
131 //
132 // If the value is malformed, the build process SHOULD
133 // exit with a non-zero error code.
134 if ((epoch == LONG_MAX && errno != 0) || epoch < 0
135 || *end != '\0') {
136 fprintf(stderr,
137 "malformed value %s for $SOURCE_DATE_EPOCH\n",
138 source_date_epoch);
139 exit(EXIT_FAILURE);
140 }
141 time_t tepoch = (time_t)epoch;
142 struct tm *tm = gmtime(&tepoch);
143 if (tm == NULL) {
144 fprintf(stderr,
145 "malformed value %s for $SOURCE_DATE_EPOCH\n",
146 source_date_epoch);
147 exit(EXIT_FAILURE);
148 }
149#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
150 char iso8601[sizeof("YYYY-MM-DDThh:mm:ss")] = {0};
151 (void)strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%SZ", tm);
152 cairo_pdf_surface_set_metadata(surface,
153 CAIRO_PDF_METADATA_CREATE_DATE,
154 iso8601);
155 cairo_pdf_surface_set_metadata(surface,
156 CAIRO_PDF_METADATA_MOD_DATE,
157 iso8601);
158#endif
159 }
160 }
161#endif
162 break;
163 case FORMAT_SVG:
164#ifdef CAIRO_HAS_SVG_SURFACE
165 surface = cairo_svg_surface_create_for_stream (writer,
166 job, job->width, job->height);
167#endif
168 break;
169 case FORMAT_CAIRO:
170 case FORMAT_PNG:
171 default:
172 if (job->width >= CAIRO_XMAX || job->height >= CAIRO_YMAX) {
173 double scale = fmin(CAIRO_XMAX / job->width, CAIRO_YMAX / job->height);
174 job->width *= scale;
175 job->height *= scale;
176 job->scale.x *= scale;
177 job->scale.y *= scale;
178 fprintf(stderr,
179 "%s: graph is too large for cairo-renderer bitmaps. Scaling by %g to fit\n",
180 job->common->cmdname, scale);
181 }
182 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
183 job->width, job->height);
184 if (job->common->verbose)
185 fprintf(stderr,
186 "%s: allocating a %.0fK cairo image surface (%d x %d pixels)\n",
187 job->common->cmdname,
188 round(job->width * job->height * 4 / 1024.),
189 job->width, job->height);
190 break;
191 }
192 status = cairo_surface_status(surface);
193 if (status != CAIRO_STATUS_SUCCESS) {
194 fprintf(stderr, "%s: failure to create cairo surface: %s\n",
195 job->common->cmdname,
196 cairo_status_to_string(status));
197 cairo_surface_destroy (surface);
198 return;
199 }
200 cr = cairo_create(surface);
201 cairo_surface_destroy (surface);
202 job->context = cr;
203 }
204
205 cairo_scale(cr, job->scale.x, job->scale.y);
206 cairo_rotate(cr, -job->rotation * M_PI / 180.);
207 cairo_translate(cr, job->translation.x, -job->translation.y);
208
209 cairo_rectangle(cr, job->clip.LL.x, - job->clip.LL.y,
210 job->clip.UR.x - job->clip.LL.x, - (job->clip.UR.y - job->clip.LL.y));
211 cairo_clip(cr);
212}
213
214static void cairogen_end_page(GVJ_t * job)
215{
216 cairo_t *cr = job->context;
217 cairo_surface_t *surface;
218 cairo_status_t status;
219
220 switch (job->render.id) {
221
222#ifdef CAIRO_HAS_PNG_FUNCTIONS
223 case FORMAT_PNG:
224 surface = cairo_get_target(cr);
225 cairo_surface_write_to_png_stream(surface, writer, job);
226 break;
227#endif
228
229 case FORMAT_PS:
230 case FORMAT_PDF:
231 case FORMAT_SVG:
232 cairo_show_page(cr);
233 surface = cairo_surface_reference(cairo_get_target(cr));
234 cairo_surface_finish(surface);
235 status = cairo_surface_status(surface);
236 cairo_surface_destroy(surface);
237 if (status != CAIRO_STATUS_SUCCESS)
238 fprintf(stderr, "cairo: %s\n", cairo_status_to_string(status));
239 break;
240
241 case FORMAT_CAIRO:
242 default:
243 surface = cairo_get_target(cr);
244 if (cairo_image_surface_get_width(surface) == 0 || cairo_image_surface_get_height(surface) == 0) {
245 /* apparently cairo never allocates a surface if nothing was ever written to it */
246/* but suppress this error message since a zero area surface seems to happen during normal operations, particular in -Tx11
247 fprintf(stderr, "ERROR: cairo surface has zero area, this may indicate some problem during rendering shapes.\n");
248 - jce */
249 }
250 job->imagedata = (char *)(cairo_image_surface_get_data(surface));
251 break;
252 /* formatting will be done by gvdevice_format() */
253 }
254}
255
256static void cairogen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
257{
258 obj_state_t *obj = job->obj;
259 cairo_t *cr = job->context;
260 double p0x, p0y, p1x, p1y;
261 char *buf;
262 size_t buf_len;
263
264 // suppress unused parameter warnings
265 (void)tooltip;
266 (void)target;
267 (void)id;
268
269 if (url && obj->url_map_p) {
270 p0x = obj->url_map_p[0].x;
271 p0y = -obj->url_map_p[0].y;
272 cairo_user_to_device (cr, &p0x, &p0y);
273 p1x = obj->url_map_p[1].x;
274 p1y = -obj->url_map_p[1].y;
275 cairo_user_to_device (cr, &p1x, &p1y);
276 buf_len = strlen(url) + 200;
277 buf = malloc(buf_len);
278 snprintf(buf, buf_len, "rect=[%f %f %f %f] uri='%s'",
279 p0x,
280 p0y,
281 p1x - p0x,
282 p1y - p0y,
283 url);
284#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
285 cairo_tag_begin (cr, CAIRO_TAG_LINK, buf);
286 cairo_tag_end (cr, CAIRO_TAG_LINK);
287#endif
288 free(buf);
289 }
290}
291
292static void cairogen_textspan(GVJ_t * job, pointf p, textspan_t * span)
293{
294 obj_state_t *obj = job->obj;
295 cairo_t *cr = job->context;
296 pointf A[2];
297
298 cairo_set_dash (cr, dashed, 0, 0.0); /* clear any dashing */
299 cairogen_set_color(cr, &obj->pencolor);
300
301 switch (span->just) {
302 case 'r':
303 p.x -= span->size.x;
304 break;
305 case 'l':
306 p.x -= 0.0;
307 break;
308 case 'n':
309 default:
310 p.x -= span->size.x / 2.0;
311 break;
312 }
313 p.y += span->yoffset_centerline + span->yoffset_layout;
314
315 cairo_move_to (cr, p.x, -p.y);
316 cairo_save(cr);
317 cairo_scale(cr, POINTS_PER_INCH / FONT_DPI, POINTS_PER_INCH / FONT_DPI);
318 pango_cairo_show_layout(cr, (PangoLayout*)span->layout);
319 cairo_restore(cr);
320
321 if (span->font && (span->font->flags & HTML_OL)) {
322 A[0].x = p.x;
323 A[1].x = p.x + span->size.x;
324 A[1].y = A[0].y = p.y;
325 cairogen_polyline(job, A, 2);
326 }
327}
328
329static void cairogen_set_penstyle(GVJ_t *job, cairo_t *cr)
330{
331 obj_state_t *obj = job->obj;
332
333 if (obj->pen == PEN_DASHED) {
334 cairo_set_dash (cr, dashed, dashed_len, 0.0);
335 } else if (obj->pen == PEN_DOTTED) {
336 cairo_set_dash (cr, dotted, dotted_len, 0.0);
337 } else {
338 cairo_set_dash (cr, dashed, 0, 0.0);
339 }
340 cairo_set_line_width (cr, obj->penwidth);
341}
342
343static void cairo_gradient_fill(cairo_t *cr, obj_state_t *obj, int filled,
344 pointf *A, size_t n) {
345 cairo_pattern_t* pat;
346 double angle = obj->gradient_angle * M_PI / 180;
347 float r1,r2;
348 pointf G[2],c1;
349
350 if (filled == GRADIENT) {
351 get_gradient_points(A, G, n, angle, 0);
352 pat = cairo_pattern_create_linear (G[0].x,G[0].y,G[1].x,G[1].y);
353 }
354 else {
355 get_gradient_points(A, G, n, 0, 1);
356 //r1 is inner radius, r2 is outer radius
357 r1 = G[1].x; /* Set a r2/4 in get_gradient_points */
358 r2 = G[1].y;
359 if (obj->gradient_angle == 0) {
360 c1.x = G[0].x;
361 c1.y = G[0].y;
362 }
363 else {
364 c1.x = G[0].x + r1 * cos(angle);
365 c1.y = G[0].y - r1 * sin(angle);
366 }
367 pat = cairo_pattern_create_radial(c1.x,c1.y,r1,G[0].x,G[0].y,r2);
368 }
369 if (obj->gradient_frac > 0) {
370 cairogen_add_color_stop_rgba(pat,obj->gradient_frac - 0.001,&(obj->fillcolor));
372 }
373 else {
376 }
377 cairo_set_source (cr, pat);
378 cairo_fill_preserve (cr);
379 cairo_pattern_destroy (pat);
380}
381
382static void cairogen_ellipse(GVJ_t * job, pointf * A, int filled)
383{
384 obj_state_t *obj = job->obj;
385 cairo_t *cr = job->context;
386 cairo_matrix_t matrix;
387 double rx, ry;
388
389 cairogen_set_penstyle(job, cr);
390
391 cairo_get_matrix(cr, &matrix);
392
393 rx = A[1].x - A[0].x;
394 ry = A[1].y - A[0].y;
395
396#define RMIN 0.01
397 rx = fmax(rx, RMIN);
398 ry = fmax(ry, RMIN);
399
400 cairo_translate(cr, A[0].x, -A[0].y);
401 cairo_scale(cr, rx, ry);
402 cairo_move_to(cr, 1., 0.);
403 cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
404
405 cairo_set_matrix(cr, &matrix);
406
407 if (filled == GRADIENT || filled == RGRADIENT) {
408 cairo_gradient_fill (cr, obj, filled, A, 2);
409 }
410 else if (filled) {
411 cairogen_set_color(cr, &obj->fillcolor);
412 cairo_fill_preserve(cr);
413 }
414 cairogen_set_color(cr, &obj->pencolor);
415 cairo_stroke(cr);
416}
417
418static void cairogen_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
419 obj_state_t *obj = job->obj;
420 cairo_t *cr = job->context;
421
422 cairogen_set_penstyle(job, cr);
423
424 cairo_move_to(cr, A[0].x, -A[0].y);
425 for (size_t i = 1; i < n; i++)
426 cairo_line_to(cr, A[i].x, -A[i].y);
427 cairo_close_path(cr);
428 if (filled == GRADIENT || filled == RGRADIENT) {
429 cairo_gradient_fill(cr, obj, filled, A, n);
430 }
431 else if (filled) {
432 cairogen_set_color(cr, &obj->fillcolor);
433 cairo_fill_preserve(cr);
434 }
435 cairogen_set_color(cr, &obj->pencolor);
436 cairo_stroke(cr);
437}
438
439static void cairogen_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
440 obj_state_t *obj = job->obj;
441 cairo_t *cr = job->context;
442
443 cairogen_set_penstyle(job, cr);
444
445 cairo_move_to(cr, A[0].x, -A[0].y);
446 for (size_t i = 1; i < n; i += 3)
447 cairo_curve_to(cr, A[i].x, -A[i].y, A[i + 1].x, -A[i + 1].y,
448 A[i + 2].x, -A[i + 2].y);
449 if (filled == GRADIENT || filled == RGRADIENT) {
450 cairo_gradient_fill(cr, obj, filled, A, n);
451 }
452 else if (filled) {
453 cairogen_set_color(cr, &obj->fillcolor);
454 cairo_fill_preserve(cr);
455 }
456 cairogen_set_color(cr, &obj->pencolor);
457 cairo_stroke(cr);
458}
459
460static void cairogen_polyline(GVJ_t *job, pointf *A, size_t n) {
461 obj_state_t *obj = job->obj;
462 cairo_t *cr = job->context;
463
464 cairogen_set_penstyle(job, cr);
465
466 cairo_move_to(cr, A[0].x, -A[0].y);
467 for (size_t i = 1; i < n; i++)
468 cairo_line_to(cr, A[i].x, -A[i].y);
469 cairogen_set_color(cr, &obj->pencolor);
470 cairo_stroke(cr);
471}
472
476 0, /* cairogen_begin_graph */
477 0, /* cairogen_end_graph */
478 0, /* cairogen_begin_layer */
479 0, /* cairogen_end_layer */
482 0, /* cairogen_begin_cluster */
483 0, /* cairogen_end_cluster */
484 0, /* cairogen_begin_nodes */
485 0, /* cairogen_end_nodes */
486 0, /* cairogen_begin_edges */
487 0, /* cairogen_end_edges */
488 0, /* cairogen_begin_node */
489 0, /* cairogen_end_node */
490 0, /* cairogen_begin_edge */
491 0, /* cairogen_end_edge */
492 cairogen_begin_anchor, /* cairogen_begin_anchor */
493 0, /* cairogen_end_anchor */
494 0, /* cairogen_begin_label */
495 0, /* cairogen_end_label */
497 0, /* cairogen_resolve_color */
502 0, /* cairogen_comment */
503 0, /* cairogen_library_shape */
504};
505
508 | GVRENDER_DOES_TRANSFORM, /* flags */
509 4., /* default pad - graph units */
510 0, /* knowncolors */
511 0, /* sizeof knowncolors */
512 RGBA_DOUBLE, /* color_type */
513};
514
517 | GVDEVICE_DOES_TRUECOLOR,/* flags */
518 {0.,0.}, /* default margin - points */
519 {0.,0.}, /* default page width, height - points */
520 {96.,96.}, /* typical monitor dpi */
521};
522
525 | GVDEVICE_DOES_TRUECOLOR, /* flags */
526 {36.,36.}, /* default margin - points */
527 {0.,0.}, /* default page width, height - points */
528 {72.,72.}, /* postscript 72 dpi */
529};
530
533 | GVDEVICE_DOES_TRUECOLOR, /* flags */
534 {36.,36.}, /* default margin - points */
535 {0.,0.}, /* default page width, height - points */
536 {72.,72.}, /* postscript 72 dpi */
537};
538
544 | GVDEVICE_DOES_TRUECOLOR,/* flags */
545 {36.,36.}, /* default margin - points */
546 {0.,0.}, /* default page width, height - points */
547 {72.,72.}, /* postscript 72 dpi */
548};
549
552 | GVDEVICE_DOES_TRUECOLOR, /* flags */
553 {0.,0.}, /* default margin - points */
554 {0.,0.}, /* default page width, height - points */
555 {72.,72.}, /* svg 72 dpi */
556};
557
562
564#ifdef CAIRO_HAS_PNG_FUNCTIONS
565 {FORMAT_PNG, "png:cairo", 10, NULL, &device_features_png},
566#endif
567#ifdef CAIRO_HAS_PS_SURFACE
568 {FORMAT_PS, "ps:cairo", -10, NULL, &device_features_ps},
569 {FORMAT_EPS, "eps:cairo", -10, NULL, &device_features_eps},
570#endif
571#ifdef CAIRO_HAS_PDF_SURFACE
572 {FORMAT_PDF, "pdf:cairo", 10, NULL, &device_features_pdf},
573#endif
574#ifdef CAIRO_HAS_SVG_SURFACE
575 {FORMAT_SVG, "svg:cairo", -10, NULL, &device_features_svg},
576#endif
577 {0, NULL, 0, NULL, NULL}
578};
#define M_PI
Definition arith.h:41
@ RGBA_DOUBLE
Definition color.h:27
void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags)
Definition utils.c:1466
#define GRADIENT
Definition const.h:232
#define RGRADIENT
Definition const.h:233
#define A(n, t)
Definition expr.h:76
#define G
Definition gdefs.h:7
#define POINTS_PER_INCH
Definition geom.h:64
static pointf scale(double c, pointf p)
Definition geomprocs.h:130
void * malloc(YYSIZE_T)
void free(void *)
node NULL
Definition grammar.y:149
@ PEN_DOTTED
Definition gvcjob.h:35
@ PEN_DASHED
Definition gvcjob.h:35
#define GVRENDER_NO_WHITE_BG
Definition gvcjob.h:106
#define GVDEVICE_DOES_TRUECOLOR
Definition gvcjob.h:90
#define GVDEVICE_BINARY_FORMAT
Definition gvcjob.h:91
#define GVRENDER_DOES_MAPS
Definition gvcjob.h:97
#define GVRENDER_DOES_TRANSFORM
Definition gvcjob.h:95
#define GVRENDER_Y_GOES_DOWN
Definition gvcjob.h:94
#define GVRENDER_DOES_MAP_RECTANGLE
Definition gvcjob.h:98
static void color(Agraph_t *g)
Definition gvcolor.c:128
size_t gvwrite(GVJ_t *job, const char *s, size_t len)
Definition gvdevice.c:179
#define FONT_DPI
static void cairogen_begin_page(GVJ_t *job)
static int dotted_len
gvplugin_installed_t gvrender_pango_types[]
static gvdevice_features_t device_features_eps
static void cairogen_end_page(GVJ_t *job)
static gvdevice_features_t device_features_pdf
static int dashed_len
#define RMIN
static void cairogen_textspan(GVJ_t *job, pointf p, textspan_t *span)
#define ARRAY_SIZE(A)
static gvdevice_features_t device_features_svg
static void cairogen_begin_job(GVJ_t *job)
static void cairogen_ellipse(GVJ_t *job, pointf *A, int filled)
static void cairogen_bezier(GVJ_t *job, pointf *A, size_t n, int filled)
static void cairogen_set_penstyle(GVJ_t *job, cairo_t *cr)
static const double CAIRO_XMAX
static const double CAIRO_YMAX
static void cairogen_polygon(GVJ_t *job, pointf *A, size_t n, int filled)
static gvdevice_features_t device_features_ps
static void cairogen_add_color_stop_rgba(cairo_pattern_t *pat, double stop, gvcolor_t *color)
static cairo_status_t writer(void *closure, const unsigned char *data, unsigned int length)
static void cairogen_polyline(GVJ_t *job, pointf *A, size_t n)
static double dashed[]
format_type
@ FORMAT_CAIRO
@ FORMAT_SVG
@ FORMAT_EPS
@ FORMAT_PDF
@ FORMAT_PS
@ FORMAT_PNG
static double dotted[]
static void cairogen_end_job(GVJ_t *job)
static gvrender_engine_t cairogen_engine
static void cairogen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
static void cairo_gradient_fill(cairo_t *cr, obj_state_t *obj, int filled, pointf *A, size_t n)
static gvrender_features_t render_features_cairo
gvplugin_installed_t gvdevice_pango_types[]
static gvdevice_features_t device_features_png
static void cairogen_set_color(cairo_t *cr, gvcolor_t *color)
char * cmdname
Definition gvcommon.h:21
int verbose
Definition gvcommon.h:22
int rotation
Definition gvcjob.h:319
char * imagedata
Definition gvcjob.h:297
boxf clip
Definition gvcjob.h:313
obj_state_t * obj
Definition gvcjob.h:269
void * context
Definition gvcjob.h:295
bool external_context
Definition gvcjob.h:296
gvplugin_active_render_t render
Definition gvcjob.h:285
GVCOMMON_t * common
Definition gvcjob.h:267
pointf scale
Definition gvcjob.h:332
unsigned int width
Definition gvcjob.h:327
pointf translation
Definition gvcjob.h:333
unsigned int height
Definition gvcjob.h:328
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition legal.c:50
ingroup plugin_api
Definition gvplugin.h:35
pointf * url_map_p
Definition gvcjob.h:240
gvcolor_t fillcolor
Definition gvcjob.h:194
double gradient_frac
Definition gvcjob.h:196
gvcolor_t stopcolor
Definition gvcjob.h:194
pen_type pen
Definition gvcjob.h:197
gvcolor_t pencolor
Definition gvcjob.h:194
int gradient_angle
Definition gvcjob.h:195
double penwidth
Definition gvcjob.h:199
double x
Definition geom.h:29
double y
Definition geom.h:29
unsigned int flags
Definition textspan.h:58
double yoffset_layout
Definition textspan.h:69
char just
'l' 'n' 'r'
Definition textspan.h:71
void * layout
Definition textspan.h:67
pointf size
Definition textspan.h:70
textfont_t * font
Definition textspan.h:66
double yoffset_centerline
Definition textspan.h:69
#define HTML_OL
Definition textspan.h:35