Graphviz 14.0.1~dev.20250923.2039
Loading...
Searching...
No Matches
gvrender_core_ps.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 <assert.h>
13#include <ctype.h>
14#include <stdbool.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <gvc/gvplugin_render.h>
19#include <gvc/gvplugin_device.h>
20#include <gvc/gvio.h>
21#include <cgraph/cgraph.h>
22#include <common/utils.h>
23#include <util/agxbuf.h>
24#include <util/gv_ctype.h>
25#include <util/prisize_t.h>
26#include "ps.h"
27
28/* for CHAR_LATIN1 */
29#include <common/const.h>
30
31/*
32 * J$: added `pdfmark' URL embedding. PostScript rendered from
33 * dot files with URL attributes will get active PDF links
34 * from Adobe's Distiller.
35 */
36#define PDFMAX 14400 /* Maximum size of PDF page */
37
39
40static int isLatin1;
41static bool setupLatin1;
42
43static void psgen_begin_job(GVJ_t * job)
44{
45 gvputs(job, "%!PS-Adobe-3.0");
46 if (job->render.id == FORMAT_EPS)
47 gvputs(job, " EPSF-3.0\n");
48 else
49 gvputs(job, "\n");
50 gvprintf(job, "%%%%Creator: %s version %s (%s)\n",
51 job->common->info[0], job->common->info[1], job->common->info[2]);
52}
53
54static void psgen_end_job(GVJ_t * job)
55{
56 gvputs(job, "%%Trailer\n");
57 if (job->render.id != FORMAT_EPS)
58 gvprintf(job, "%%%%Pages: %d\n", job->common->viewNum);
59 if (job->common->show_boxes == NULL)
60 if (job->render.id != FORMAT_EPS)
61 gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
62 job->boundingBox.LL.x, job->boundingBox.LL.y,
63 job->boundingBox.UR.x, job->boundingBox.UR.y);
64 gvputs(job, "end\nrestore\n");
65 gvputs(job, "%%EOF\n");
66}
67
68static void psgen_begin_graph(GVJ_t * job)
69{
70 obj_state_t *obj = job->obj;
71
72 setupLatin1 = false;
73
74 if (job->common->viewNum == 0) {
75 gvprintf(job, "%%%%Title: %s\n", agnameof(obj->u.g));
76 if (job->render.id != FORMAT_EPS)
77 gvputs(job, "%%Pages: (atend)\n");
78 else
79 gvputs(job, "%%Pages: 1\n");
80 if (job->common->show_boxes == NULL) {
81 if (job->render.id != FORMAT_EPS)
82 gvputs(job, "%%BoundingBox: (atend)\n");
83 else
84 gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n",
87 }
88 gvputs(job, "%%EndComments\nsave\n");
89 /* include shape library */
90 cat_libfile(job, job->common->lib, ps_txt);
91 /* include epsf */
92 epsf_define(job);
93 if (job->common->show_boxes) {
94 const char *args[2] = {job->common->show_boxes[0]};
95 cat_libfile(job, NULL, args);
96 }
97 }
98 isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1 ? CHAR_LATIN1 : -1);
99 /* We always setup Latin1. The charset info is always output,
100 * and installing it is cheap. With it installed, we can then
101 * rely on ps_string to convert UTF-8 characters whose encoding
102 * is in the range of Latin-1 into the Latin-1 equivalent and
103 * get the expected PostScript output.
104 */
105 if (!setupLatin1) {
106 gvputs(job, "setupLatin1\n"); /* as defined in ps header */
107 setupLatin1 = true;
108 }
109 /* Set base URL for relative links (for Distiller >= 3.0) */
110 if (obj->url) {
111 char *const ps = ps_string(obj->url, isLatin1);
112 gvprintf(job, "[ {Catalog} << /URI << /Base %s >> >>\n/PUT pdfmark\n", ps);
113 free(ps);
114 }
115}
116
117static void psgen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers)
118{
119 (void)layername;
120 gvprintf(job, "%d %d setlayer\n", layerNum, numLayers);
121}
122
123static void psgen_begin_page(GVJ_t * job)
124{
125 box pbr = job->pageBoundingBox;
126
127 gvprintf(job, "%%%%Page: %d %d\n",
128 job->common->viewNum + 1, job->common->viewNum + 1);
129 if (job->common->show_boxes == NULL)
130 gvprintf(job, "%%%%PageBoundingBox: %d %d %d %d\n",
131 pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
132 gvprintf(job, "%%%%PageOrientation: %s\n",
133 (job->rotation ? "Landscape" : "Portrait"));
134 if (job->render.id == FORMAT_PS2)
135 gvprintf(job, "<< /PageSize [%d %d] >> setpagedevice\n",
136 pbr.UR.x, pbr.UR.y);
137 gvprintf(job, "%d %d %d beginpage\n",
138 job->pagesArrayElem.x, job->pagesArrayElem.y, job->numPages);
139 if (job->common->show_boxes == NULL)
140 gvprintf(job, "gsave\n%d %d %d %d boxprim clip newpath\n",
141 pbr.LL.x, pbr.LL.y, pbr.UR.x-pbr.LL.x, pbr.UR.y-pbr.LL.y);
142 gvprintf(job, "%g %g set_scale %d rotate %g %g translate\n",
143 job->scale.x, job->scale.y,
144 job->rotation,
145 job->translation.x, job->translation.y);
146
147 /* Define the size of the PS canvas */
148 if (job->render.id == FORMAT_PS2) {
149 if (pbr.UR.x >= PDFMAX || pbr.UR.y >= PDFMAX)
150 job->common->errorfn("canvas size (%d,%d) exceeds PDF limit (%d)\n"
151 "\t(suggest setting a bounding box size, see dot(1))\n",
152 pbr.UR.x, pbr.UR.y, PDFMAX);
153 gvprintf(job, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n",
154 pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y);
155 }
156}
157
158static void psgen_end_page(GVJ_t * job)
159{
160 if (job->common->show_boxes) {
161 gvputs(job, "0 0 0 edgecolor\n");
162 cat_libfile(job, NULL, job->common->show_boxes + 1);
163 }
164 /* the showpage is really a no-op, but at least one PS processor
165 * out there needs to see this literal token. endpage does the real work.
166 */
167 gvputs(job, "endpage\nshowpage\ngrestore\n");
168 gvputs(job, "%%PageTrailer\n");
169 gvprintf(job, "%%%%EndPage: %d\n", job->common->viewNum);
170}
171
172static void psgen_begin_cluster(GVJ_t * job)
173{
174 obj_state_t *obj = job->obj;
175
176 gvprintf(job, "%% %s\n", agnameof(obj->u.g));
177
178 gvputs(job, "gsave\n");
179}
180
181static void psgen_end_cluster(GVJ_t * job)
182{
183 gvputs(job, "grestore\n");
184}
185
186static void psgen_begin_node(GVJ_t * job)
187{
188 gvputs(job, "gsave\n");
189}
190
191static void psgen_end_node(GVJ_t * job)
192{
193 gvputs(job, "grestore\n");
194}
195
196static void
198{
199 gvputs(job, "gsave\n");
200}
201
202static void psgen_end_edge(GVJ_t * job)
203{
204 gvputs(job, "grestore\n");
205}
206
207static void psgen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
208{
209 (void)tooltip;
210 (void)target;
211 (void)id;
212
213 obj_state_t *obj = job->obj;
214
215 if (url && obj->url_map_p) {
216 gvputs(job, "[ /Rect [ ");
217 gvprintpointflist(job, obj->url_map_p, 2);
218 gvputs(job, " ]\n");
219 char *const ps = ps_string(url, isLatin1);
220 gvprintf(job, " /Border [ 0 0 0 ]\n"
221 " /Action << /Subtype /URI /URI %s >>\n"
222 " /Subtype /Link\n"
223 "/ANN pdfmark\n",
224 ps);
225 free(ps);
226 }
227}
228
229static void
231{
232 double penwidth = job->obj->penwidth;
233 char *p, *line, **s = job->obj->rawstyle;
234
236 gvputs(job," setlinewidth\n");
237
238 while (s && (p = line = *s++)) {
239 if (strcmp(line, "setlinewidth") == 0)
240 continue;
241 while (*p)
242 p++;
243 p++;
244 while (*p) {
245 gvprintf(job,"%s ", p);
246 while (*p)
247 p++;
248 p++;
249 }
250 if (strcmp(line, "invis") == 0)
251 job->obj->penwidth = 0;
252 gvprintf(job, "%s\n", line);
253 }
254}
255
257{
258 char *objtype;
259
260 if (color) {
261 switch (job->obj->type) {
263 case CLUSTER_OBJTYPE:
264 objtype = "graph";
265 break;
266 case NODE_OBJTYPE:
267 objtype = "node";
268 break;
269 case EDGE_OBJTYPE:
270 objtype = "edge";
271 break;
272 default:
273 objtype = "sethsb";
274 break;
275 }
276 gvprintf(job, "%.5g %.5g %.5g %scolor\n",
277 color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype);
278 }
279}
280
294static void check_fontname(const char *fontname) {
295
296 if (strlen(fontname) > 29) {
298 "font name %s is longer than 29 characters which may be rejected by "
299 "some PS viewers\n",
300 fontname);
301 }
302
303 for (const char *p = fontname; *p != '\0'; ++p) {
304 if (!isascii((int)*p) || *p == ' ' || gv_iscntrl(*p)) {
306 "font name %s contains characters that may not be accepted by some "
307 "PS viewers\n",
308 fontname);
309 break;
310 }
311 }
312}
313
314static void psgen_textspan(GVJ_t * job, pointf p, textspan_t * span)
315{
316 char *str;
317
318 if (job->obj->pencolor.u.HSVA[3] < .5)
319 return; /* skip transparent text */
320
321 ps_set_color(job, &(job->obj->pencolor));
322 gvprintdouble(job, span->font->size);
323 check_fontname(span->font->name);
324 gvprintf(job, " /%s set_font\n", span->font->name);
325 str = ps_string(span->str,isLatin1);
326 switch (span->just) {
327 case 'r':
328 p.x -= span->size.x;
329 break;
330 case 'l':
331 p.x -= 0.0;
332 break;
333 case 'n':
334 default:
335 p.x -= span->size.x / 2.0;
336 break;
337 }
338 p.y += span->yoffset_centerline;
339 gvprintpointf(job, p);
340 gvputs(job, " moveto ");
341 gvprintdouble(job, span->size.x);
342 gvprintf(job, " %s alignedtext\n", str);
343 free(str);
344}
345
346static void psgen_ellipse(GVJ_t * job, pointf * A, int filled)
347{
348 /* A[] contains 2 points: the center and corner. */
349 pointf AA[2];
350
351 AA[0] = A[0];
352 AA[1].x = A[1].x - A[0].x;
353 AA[1].y = A[1].y - A[0].y;
354
355 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
356 ps_set_color(job, &(job->obj->fillcolor));
357 gvprintpointflist(job, AA, 2);
358 gvputs(job, " ellipse_path fill\n");
359 }
360 if (job->obj->pencolor.u.HSVA[3] > .5) {
361 ps_set_pen_style(job);
362 ps_set_color(job, &(job->obj->pencolor));
363 gvprintpointflist(job, AA, 2);
364 gvputs(job, " ellipse_path stroke\n");
365 }
366}
367
368static void psgen_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
369 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
370 ps_set_color(job, &(job->obj->fillcolor));
371 gvputs(job, "newpath ");
372 gvprintpointf(job, A[0]);
373 gvputs(job, " moveto\n");
374 for (size_t j = 1; j < n; j += 3) {
375 gvprintpointflist(job, &A[j], 3);
376 gvputs(job, " curveto\n");
377 }
378 gvputs(job, "closepath fill\n");
379 }
380 if (job->obj->pencolor.u.HSVA[3] > .5) {
381 ps_set_pen_style(job);
382 ps_set_color(job, &(job->obj->pencolor));
383 gvputs(job, "newpath ");
384 gvprintpointf(job, A[0]);
385 gvputs(job, " moveto\n");
386 for (size_t j = 1; j < n; j += 3) {
387 gvprintpointflist(job, &A[j], 3);
388 gvputs(job, " curveto\n");
389 }
390 gvputs(job, "stroke\n");
391 }
392}
393
394static void psgen_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
395 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
396 ps_set_color(job, &(job->obj->fillcolor));
397 gvputs(job, "newpath ");
398 gvprintpointf(job, A[0]);
399 gvputs(job, " moveto\n");
400 for (size_t j = 1; j < n; j++) {
401 gvprintpointf(job, A[j]);
402 gvputs(job, " lineto\n");
403 }
404 gvputs(job, "closepath fill\n");
405 }
406 if (job->obj->pencolor.u.HSVA[3] > .5) {
407 ps_set_pen_style(job);
408 ps_set_color(job, &(job->obj->pencolor));
409 gvputs(job, "newpath ");
410 gvprintpointf(job, A[0]);
411 gvputs(job, " moveto\n");
412 for (size_t j = 1; j < n; j++) {
413 gvprintpointf(job, A[j]);
414 gvputs(job, " lineto\n");
415 }
416 gvputs(job, "closepath stroke\n");
417 }
418}
419
420static void psgen_polyline(GVJ_t *job, pointf *A, size_t n) {
421 if (job->obj->pencolor.u.HSVA[3] > .5) {
422 ps_set_pen_style(job);
423 ps_set_color(job, &(job->obj->pencolor));
424 gvputs(job, "newpath ");
425 gvprintpointf(job, A[0]);
426 gvputs(job, " moveto\n");
427 for (size_t j = 1; j < n; j++) {
428 gvprintpointf(job, A[j]);
429 gvputs(job, " lineto\n");
430 }
431 gvputs(job, "stroke\n");
432 }
433}
434
435static void psgen_comment(GVJ_t * job, char *str)
436{
437 gvputs(job, "% ");
438 gvputs(job, str);
439 gvputs(job, "\n");
440}
441
442static void psgen_library_shape(GVJ_t *job, char *name, pointf *A, size_t n,
443 int filled) {
444 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
445 ps_set_color(job, &(job->obj->fillcolor));
446 gvputs(job, "[ ");
447 gvprintpointflist(job, A, n);
448 gvputs(job, " ");
449 gvprintpointf(job, A[0]);
450 gvprintf(job, " ] %" PRISIZE_T " true %s\n", n, name);
451 }
452 if (job->obj->pencolor.u.HSVA[3] > .5) {
453 ps_set_pen_style(job);
454 ps_set_color(job, &(job->obj->pencolor));
455 gvputs(job, "[ ");
456 gvprintpointflist(job, A, n);
457 gvputs(job, " ");
458 gvprintpointf(job, A[0]);
459 gvprintf(job, " ] %" PRISIZE_T " false %s\n", n, name);
460 }
461}
462
467 0, /* psgen_end_graph */
469 0, /* psgen_end_layer */
474 0, /* psgen_begin_nodes */
475 0, /* psgen_end_nodes */
476 0, /* psgen_begin_edges */
477 0, /* psgen_end_edges */
483 0, /* psgen_end_anchor */
484 0, /* psgen_begin_label */
485 0, /* psgen_end_label */
487 0, /* psgen_resolve_color */
494};
495
501 4., /* default pad - graph units */
502 NULL, /* knowncolors */
503 0, /* sizeof knowncolors */
504 HSVA_DOUBLE, /* color_type */
505};
506
509 | GVDEVICE_DOES_LAYERS, /* flags */
510 {36.,36.}, /* default margin - points */
511 {612.,792.}, /* default page width, height - points */
512 {72.,72.}, /* default dpi */
513};
514
516 0, /* flags */
517 {36.,36.}, /* default margin - points */
518 {612.,792.}, /* default page width, height - points */
519 {72.,72.}, /* default dpi */
520};
521
526
528 {FORMAT_PS, "ps:ps", 1, NULL, &device_features_ps},
529 {FORMAT_PS2, "ps2:ps", 1, NULL, &device_features_ps},
530 {FORMAT_EPS, "eps:ps", 1, NULL, &device_features_eps},
531 {0, NULL, 0, NULL, NULL}
532};
abstract graph C library, Cgraph API
@ HSVA_DOUBLE
Definition color.h:26
#define CHAR_LATIN1
Definition const.h:197
#define A(n, t)
Definition expr.h:76
void free(void *)
node NULL
Definition grammar.y:181
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define GD_charset(g)
Definition types.h:367
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
replacements for ctype.h functions
static bool gv_iscntrl(int c)
Definition gv_ctype.h:33
#define GVRENDER_NO_WHITE_BG
Definition gvcjob.h:106
#define GVDEVICE_DOES_PAGES
Definition gvcjob.h:87
#define GVDEVICE_DOES_LAYERS
Definition gvcjob.h:88
#define GVRENDER_DOES_MAPS
Definition gvcjob.h:97
@ CLUSTER_OBJTYPE
Definition gvcjob.h:168
@ EDGE_OBJTYPE
Definition gvcjob.h:168
@ ROOTGRAPH_OBJTYPE
Definition gvcjob.h:168
@ NODE_OBJTYPE
Definition gvcjob.h:168
#define GVRENDER_DOES_TRANSFORM
Definition gvcjob.h:95
#define GVRENDER_DOES_MAP_RECTANGLE
Definition gvcjob.h:98
static void color(Agraph_t *g)
Definition gvcolor.c:129
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:266
void gvprintpointflist(GVJ_t *job, pointf *p, size_t n)
Definition gvdevice.c:544
void gvprintpointf(GVJ_t *job, pointf p)
Definition gvdevice.c:530
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:402
void gvprintdouble(GVJ_t *job, double num)
Definition gvdevice.c:513
static double penwidth[]
static void psgen_library_shape(GVJ_t *job, char *name, pointf *A, size_t n, int filled)
static void psgen_textspan(GVJ_t *job, pointf p, textspan_t *span)
gvplugin_installed_t gvrender_ps_types[]
static void check_fontname(const char *fontname)
static gvdevice_features_t device_features_eps
static void psgen_end_job(GVJ_t *job)
static int isLatin1
static void psgen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
static void psgen_begin_page(GVJ_t *job)
static void psgen_begin_edge(GVJ_t *job)
static gvrender_engine_t psgen_engine
static void ps_set_color(GVJ_t *job, gvcolor_t *color)
gvplugin_installed_t gvdevice_ps_types[]
@ FORMAT_EPS
@ FORMAT_PS2
@ FORMAT_PS
static gvrender_features_t render_features_ps
static void psgen_begin_graph(GVJ_t *job)
static void psgen_end_edge(GVJ_t *job)
#define PDFMAX
static void psgen_end_node(GVJ_t *job)
static void psgen_begin_cluster(GVJ_t *job)
static gvdevice_features_t device_features_ps
static void psgen_begin_node(GVJ_t *job)
static void psgen_end_cluster(GVJ_t *job)
static void psgen_ellipse(GVJ_t *job, pointf *A, int filled)
static void psgen_comment(GVJ_t *job, char *str)
static void psgen_polyline(GVJ_t *job, pointf *A, size_t n)
static void psgen_begin_layer(GVJ_t *job, char *layername, int layerNum, int numLayers)
static bool setupLatin1
static void psgen_bezier(GVJ_t *job, pointf *A, size_t n, int filled)
static void psgen_begin_job(GVJ_t *job)
static void psgen_polygon(GVJ_t *job, pointf *A, size_t n, int filled)
static void ps_set_pen_style(GVJ_t *job)
static void psgen_end_page(GVJ_t *job)
textitem scanner parser str
Definition htmlparse.y:218
static int * ps
Definition lu.c:51
#define PRISIZE_T
Definition prisize_t.h:25
static const char * ps_txt[]
Definition ps.h:5
void epsf_define(GVJ_t *job)
char * ps_string(char *ins, int chset)
void cat_libfile(GVJ_t *job, const char **arglib, const char **stdlib)
const char ** lib
Definition gvcommon.h:26
char ** info
Definition gvcommon.h:20
void(* errorfn)(const char *fmt,...)
Definition gvcommon.h:24
const char ** show_boxes
emit code for correct box coordinates
Definition gvcommon.h:25
int viewNum
rendering state
Definition gvcommon.h:29
int rotation
Definition gvcjob.h:319
obj_state_t * obj
Definition gvcjob.h:269
point pagesArrayElem
Definition gvcjob.h:308
gvplugin_active_render_t render
Definition gvcjob.h:285
GVCOMMON_t * common
Definition gvcjob.h:267
box pageBoundingBox
Definition gvcjob.h:329
box boundingBox
Definition gvcjob.h:330
pointf scale
Definition gvcjob.h:332
int numPages
Definition gvcjob.h:309
pointf translation
Definition gvcjob.h:333
Definition geom.h:39
point LL
Definition geom.h:39
point UR
Definition geom.h:39
double HSVA[4]
Definition color.h:33
union color_s::@71 u
ingroup plugin_api
Definition gvplugin.h:35
graph_t * g
Definition gvcjob.h:186
pointf * url_map_p
Definition gvcjob.h:240
gvcolor_t fillcolor
Definition gvcjob.h:194
char * url
Definition gvcjob.h:210
obj_type type
Definition gvcjob.h:184
gvcolor_t pencolor
Definition gvcjob.h:194
char ** rawstyle
Definition gvcjob.h:200
union obj_state_s::@97 u
double penwidth
Definition gvcjob.h:199
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
char * name
Definition textspan.h:54
double size
Definition textspan.h:57
char * str
Definition textspan.h:65
char just
'l' 'n' 'r'
Definition textspan.h:71
pointf size
Definition textspan.h:70
textfont_t * font
Definition textspan.h:66
double yoffset_centerline
Definition textspan.h:69
Definition grammar.c:90