Graphviz 12.0.1~dev.20240715.2254
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/agxbuf.h>
22#include <cgraph/cgraph.h>
23#include <cgraph/gv_ctype.h>
24#include <cgraph/prisize_t.h>
25#include <common/utils.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];
95 args[0] = job->common->show_boxes[0];
96 args[1] = NULL;
97 cat_libfile(job, NULL, args);
98 }
99 }
100 isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1 ? CHAR_LATIN1 : -1);
101 /* We always setup Latin1. The charset info is always output,
102 * and installing it is cheap. With it installed, we can then
103 * rely on ps_string to convert UTF-8 characters whose encoding
104 * is in the range of Latin-1 into the Latin-1 equivalent and
105 * get the expected PostScript output.
106 */
107 if (!setupLatin1) {
108 gvputs(job, "setupLatin1\n"); /* as defined in ps header */
109 setupLatin1 = true;
110 }
111 /* Set base URL for relative links (for Distiller >= 3.0) */
112 if (obj->url)
113 gvprintf(job, "[ {Catalog} << /URI << /Base %s >> >>\n"
114 "/PUT pdfmark\n", ps_string(obj->url,isLatin1));
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 gvprintf(job, " /Border [ 0 0 0 ]\n"
220 " /Action << /Subtype /URI /URI %s >>\n"
221 " /Subtype /Link\n"
222 "/ANN pdfmark\n",
223 ps_string(url, isLatin1));
224 }
225}
226
227static void
229{
230 double penwidth = job->obj->penwidth;
231 char *p, *line, **s = job->obj->rawstyle;
232
234 gvputs(job," setlinewidth\n");
235
236 while (s && (p = line = *s++)) {
237 if (strcmp(line, "setlinewidth") == 0)
238 continue;
239 while (*p)
240 p++;
241 p++;
242 while (*p) {
243 gvprintf(job,"%s ", p);
244 while (*p)
245 p++;
246 p++;
247 }
248 if (strcmp(line, "invis") == 0)
249 job->obj->penwidth = 0;
250 gvprintf(job, "%s\n", line);
251 }
252}
253
255{
256 char *objtype;
257
258 if (color) {
259 switch (job->obj->type) {
261 case CLUSTER_OBJTYPE:
262 objtype = "graph";
263 break;
264 case NODE_OBJTYPE:
265 objtype = "node";
266 break;
267 case EDGE_OBJTYPE:
268 objtype = "edge";
269 break;
270 default:
271 objtype = "sethsb";
272 break;
273 }
274 gvprintf(job, "%.5g %.5g %.5g %scolor\n",
275 color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype);
276 }
277}
278
292static void check_fontname(const char *fontname) {
293
294 if (strlen(fontname) > 29) {
296 "font name %s is longer than 29 characters which may be rejected by "
297 "some PS viewers\n",
298 fontname);
299 }
300
301 for (const char *p = fontname; *p != '\0'; ++p) {
302 if (!isascii((int)*p) || *p == ' ' || gv_iscntrl(*p)) {
304 "font name %s contains characters that may not be accepted by some "
305 "PS viewers\n",
306 fontname);
307 break;
308 }
309 }
310}
311
312static void psgen_textspan(GVJ_t * job, pointf p, textspan_t * span)
313{
314 char *str;
315
316 if (job->obj->pencolor.u.HSVA[3] < .5)
317 return; /* skip transparent text */
318
319 ps_set_color(job, &(job->obj->pencolor));
320 gvprintdouble(job, span->font->size);
321 check_fontname(span->font->name);
322 gvprintf(job, " /%s set_font\n", span->font->name);
323 str = ps_string(span->str,isLatin1);
324 switch (span->just) {
325 case 'r':
326 p.x -= span->size.x;
327 break;
328 case 'l':
329 p.x -= 0.0;
330 break;
331 case 'n':
332 default:
333 p.x -= span->size.x / 2.0;
334 break;
335 }
336 p.y += span->yoffset_centerline;
337 gvprintpointf(job, p);
338 gvputs(job, " moveto ");
339 gvprintdouble(job, span->size.x);
340 gvprintf(job, " %s alignedtext\n", str);
341}
342
343static void psgen_ellipse(GVJ_t * job, pointf * A, int filled)
344{
345 /* A[] contains 2 points: the center and corner. */
346 pointf AA[2];
347
348 AA[0] = A[0];
349 AA[1].x = A[1].x - A[0].x;
350 AA[1].y = A[1].y - A[0].y;
351
352 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
353 ps_set_color(job, &(job->obj->fillcolor));
354 gvprintpointflist(job, AA, 2);
355 gvputs(job, " ellipse_path fill\n");
356 }
357 if (job->obj->pencolor.u.HSVA[3] > .5) {
358 ps_set_pen_style(job);
359 ps_set_color(job, &(job->obj->pencolor));
360 gvprintpointflist(job, AA, 2);
361 gvputs(job, " ellipse_path stroke\n");
362 }
363}
364
365static void psgen_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
366 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
367 ps_set_color(job, &(job->obj->fillcolor));
368 gvputs(job, "newpath ");
369 gvprintpointf(job, A[0]);
370 gvputs(job, " moveto\n");
371 for (size_t j = 1; j < n; j += 3) {
372 gvprintpointflist(job, &A[j], 3);
373 gvputs(job, " curveto\n");
374 }
375 gvputs(job, "closepath fill\n");
376 }
377 if (job->obj->pencolor.u.HSVA[3] > .5) {
378 ps_set_pen_style(job);
379 ps_set_color(job, &(job->obj->pencolor));
380 gvputs(job, "newpath ");
381 gvprintpointf(job, A[0]);
382 gvputs(job, " moveto\n");
383 for (size_t j = 1; j < n; j += 3) {
384 gvprintpointflist(job, &A[j], 3);
385 gvputs(job, " curveto\n");
386 }
387 gvputs(job, "stroke\n");
388 }
389}
390
391static void psgen_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
392 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
393 ps_set_color(job, &(job->obj->fillcolor));
394 gvputs(job, "newpath ");
395 gvprintpointf(job, A[0]);
396 gvputs(job, " moveto\n");
397 for (size_t j = 1; j < n; j++) {
398 gvprintpointf(job, A[j]);
399 gvputs(job, " lineto\n");
400 }
401 gvputs(job, "closepath fill\n");
402 }
403 if (job->obj->pencolor.u.HSVA[3] > .5) {
404 ps_set_pen_style(job);
405 ps_set_color(job, &(job->obj->pencolor));
406 gvputs(job, "newpath ");
407 gvprintpointf(job, A[0]);
408 gvputs(job, " moveto\n");
409 for (size_t j = 1; j < n; j++) {
410 gvprintpointf(job, A[j]);
411 gvputs(job, " lineto\n");
412 }
413 gvputs(job, "closepath stroke\n");
414 }
415}
416
417static void psgen_polyline(GVJ_t *job, pointf *A, size_t n) {
418 if (job->obj->pencolor.u.HSVA[3] > .5) {
419 ps_set_pen_style(job);
420 ps_set_color(job, &(job->obj->pencolor));
421 gvputs(job, "newpath ");
422 gvprintpointf(job, A[0]);
423 gvputs(job, " moveto\n");
424 for (size_t j = 1; j < n; j++) {
425 gvprintpointf(job, A[j]);
426 gvputs(job, " lineto\n");
427 }
428 gvputs(job, "stroke\n");
429 }
430}
431
432static void psgen_comment(GVJ_t * job, char *str)
433{
434 gvputs(job, "% ");
435 gvputs(job, str);
436 gvputs(job, "\n");
437}
438
439static void psgen_library_shape(GVJ_t *job, char *name, pointf *A, size_t n,
440 int filled) {
441 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) {
442 ps_set_color(job, &(job->obj->fillcolor));
443 gvputs(job, "[ ");
444 gvprintpointflist(job, A, n);
445 gvputs(job, " ");
446 gvprintpointf(job, A[0]);
447 gvprintf(job, " ] %" PRISIZE_T " true %s\n", n, name);
448 }
449 if (job->obj->pencolor.u.HSVA[3] > .5) {
450 ps_set_pen_style(job);
451 ps_set_color(job, &(job->obj->pencolor));
452 gvputs(job, "[ ");
453 gvprintpointflist(job, A, n);
454 gvputs(job, " ");
455 gvprintpointf(job, A[0]);
456 gvprintf(job, " ] %" PRISIZE_T " false %s\n", n, name);
457 }
458}
459
464 0, /* psgen_end_graph */
466 0, /* psgen_end_layer */
471 0, /* psgen_begin_nodes */
472 0, /* psgen_end_nodes */
473 0, /* psgen_begin_edges */
474 0, /* psgen_end_edges */
480 0, /* psgen_end_anchor */
481 0, /* psgen_begin_label */
482 0, /* psgen_end_label */
484 0, /* psgen_resolve_color */
491};
492
498 4., /* default pad - graph units */
499 NULL, /* knowncolors */
500 0, /* sizeof knowncolors */
501 HSVA_DOUBLE, /* color_type */
502};
503
506 | GVDEVICE_DOES_LAYERS, /* flags */
507 {36.,36.}, /* default margin - points */
508 {612.,792.}, /* default page width, height - points */
509 {72.,72.}, /* default dpi */
510};
511
513 0, /* flags */
514 {36.,36.}, /* default margin - points */
515 {612.,792.}, /* default page width, height - points */
516 {72.,72.}, /* default dpi */
517};
518
523
525 {FORMAT_PS, "ps:ps", 1, NULL, &device_features_ps},
526 {FORMAT_PS2, "ps2:ps", 1, NULL, &device_features_ps},
527 {FORMAT_EPS, "eps:ps", 1, NULL, &device_features_eps},
528 {0, NULL, 0, NULL, NULL}
529};
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
node NULL
Definition grammar.y:149
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:158
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:128
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:263
void gvprintpointflist(GVJ_t *job, pointf *p, size_t n)
Definition gvdevice.c:537
void gvprintpointf(GVJ_t *job, pointf p)
Definition gvdevice.c:523
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:394
void gvprintdouble(GVJ_t *job, double num)
Definition gvdevice.c:506
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[]
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)
format_type
@ FORMAT_EPS
@ FORMAT_PS2
@ FORMAT_PS
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)
agxbuf * str
Definition htmlparse.c:97
#define PRISIZE_T
PRIu64 alike for printing size_t
Definition prisize_t.h:27
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::@72 u
ingroup plugin_api
Definition gvplugin.h:35
union obj_state_s::@92 u
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
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:93