|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 #ifdef HAVE_CONFIG_H 00015 #include "config.h" 00016 #endif 00017 00018 #include <stdlib.h> 00019 #include <string.h> 00020 00021 #include "gvplugin_render.h" 00022 #include "gvplugin_device.h" 00023 #include "gvio.h" 00024 #include "agxbuf.h" 00025 #include "utils.h" 00026 #include "ps.h" 00027 00028 /* for CHAR_LATIN1 */ 00029 #include "const.h" 00030 00031 /* 00032 * J$: added `pdfmark' URL embedding. PostScript rendered from 00033 * dot files with URL attributes will get active PDF links 00034 * from Adobe's Distiller. 00035 */ 00036 #define PDFMAX 14400 /* Maximum size of PDF page */ 00037 00038 typedef enum { FORMAT_PS, FORMAT_PS2, FORMAT_EPS } format_type; 00039 00040 static int isLatin1; 00041 static char setupLatin1; 00042 00043 static void psgen_begin_job(GVJ_t * job) 00044 { 00045 gvputs(job, "%!PS-Adobe-3.0"); 00046 if (job->render.id == FORMAT_EPS) 00047 gvputs(job, " EPSF-3.0\n"); 00048 else 00049 gvputs(job, "\n"); 00050 gvprintf(job, "%%%%Creator: %s version %s (%s)\n", 00051 job->common->info[0], job->common->info[1], job->common->info[2]); 00052 } 00053 00054 static void psgen_end_job(GVJ_t * job) 00055 { 00056 gvputs(job, "%%Trailer\n"); 00057 if (job->render.id != FORMAT_EPS) 00058 gvprintf(job, "%%%%Pages: %d\n", job->common->viewNum); 00059 if (job->common->show_boxes == NULL) 00060 if (job->render.id != FORMAT_EPS) 00061 gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n", 00062 job->boundingBox.LL.x, job->boundingBox.LL.y, 00063 job->boundingBox.UR.x, job->boundingBox.UR.y); 00064 gvputs(job, "end\nrestore\n"); 00065 gvputs(job, "%%EOF\n"); 00066 } 00067 00068 static void psgen_begin_graph(GVJ_t * job) 00069 { 00070 obj_state_t *obj = job->obj; 00071 00072 setupLatin1 = FALSE; 00073 00074 if (job->common->viewNum == 0) { 00075 gvprintf(job, "%%%%Title: %s\n", agnameof(obj->u.g)); 00076 if (job->render.id != FORMAT_EPS) 00077 gvputs(job, "%%Pages: (atend)\n"); 00078 else 00079 gvputs(job, "%%Pages: 1\n"); 00080 if (job->common->show_boxes == NULL) { 00081 if (job->render.id != FORMAT_EPS) 00082 gvputs(job, "%%BoundingBox: (atend)\n"); 00083 else 00084 gvprintf(job, "%%%%BoundingBox: %d %d %d %d\n", 00085 job->pageBoundingBox.LL.x, job->pageBoundingBox.LL.y, 00086 job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.y); 00087 } 00088 gvputs(job, "%%EndComments\nsave\n"); 00089 /* include shape library */ 00090 cat_libfile(job, job->common->lib, ps_txt); 00091 /* include epsf */ 00092 epsf_define(job); 00093 if (job->common->show_boxes) { 00094 const char* args[2]; 00095 args[0] = job->common->show_boxes[0]; 00096 args[1] = NULL; 00097 cat_libfile(job, NULL, args); 00098 } 00099 } 00100 isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1); 00101 /* We always setup Latin1. The charset info is always output, 00102 * and installing it is cheap. With it installed, we can then 00103 * rely on ps_string to convert UTF-8 characters whose encoding 00104 * is in the range of Latin-1 into the Latin-1 equivalent and 00105 * get the expected PostScript output. 00106 */ 00107 if (!setupLatin1) { 00108 gvputs(job, "setupLatin1\n"); /* as defined in ps header */ 00109 setupLatin1 = TRUE; 00110 } 00111 /* Set base URL for relative links (for Distiller >= 3.0) */ 00112 if (obj->url) 00113 gvprintf(job, "[ {Catalog} << /URI << /Base (%s) >> >>\n" 00114 "/PUT pdfmark\n", obj->url); 00115 } 00116 00117 static void psgen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers) 00118 { 00119 gvprintf(job, "%d %d setlayer\n", layerNum, numLayers); 00120 } 00121 00122 static void psgen_begin_page(GVJ_t * job) 00123 { 00124 box pbr = job->pageBoundingBox; 00125 00126 gvprintf(job, "%%%%Page: %d %d\n", 00127 job->common->viewNum + 1, job->common->viewNum + 1); 00128 if (job->common->show_boxes == NULL) 00129 gvprintf(job, "%%%%PageBoundingBox: %d %d %d %d\n", 00130 pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); 00131 gvprintf(job, "%%%%PageOrientation: %s\n", 00132 (job->rotation ? "Landscape" : "Portrait")); 00133 if (job->render.id == FORMAT_PS2) 00134 gvprintf(job, "<< /PageSize [%d %d] >> setpagedevice\n", 00135 pbr.UR.x, pbr.UR.y); 00136 gvprintf(job, "%d %d %d beginpage\n", 00137 job->pagesArrayElem.x, job->pagesArrayElem.y, job->numPages); 00138 if (job->common->show_boxes == NULL) 00139 gvprintf(job, "gsave\n%d %d %d %d boxprim clip newpath\n", 00140 pbr.LL.x, pbr.LL.y, pbr.UR.x-pbr.LL.x, pbr.UR.y-pbr.LL.y); 00141 gvprintf(job, "%g %g set_scale %d rotate %g %g translate\n", 00142 job->scale.x, job->scale.y, 00143 job->rotation, 00144 job->translation.x, job->translation.y); 00145 00146 /* Define the size of the PS canvas */ 00147 if (job->render.id == FORMAT_PS2) { 00148 if (pbr.UR.x >= PDFMAX || pbr.UR.y >= PDFMAX) 00149 job->common->errorfn("canvas size (%d,%d) exceeds PDF limit (%d)\n" 00150 "\t(suggest setting a bounding box size, see dot(1))\n", 00151 pbr.UR.x, pbr.UR.y, PDFMAX); 00152 gvprintf(job, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n", 00153 pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); 00154 } 00155 } 00156 00157 static void psgen_end_page(GVJ_t * job) 00158 { 00159 if (job->common->show_boxes) { 00160 gvputs(job, "0 0 0 edgecolor\n"); 00161 cat_libfile(job, NULL, job->common->show_boxes + 1); 00162 } 00163 /* the showpage is really a no-op, but at least one PS processor 00164 * out there needs to see this literal token. endpage does the real work. 00165 */ 00166 gvputs(job, "endpage\nshowpage\ngrestore\n"); 00167 gvputs(job, "%%PageTrailer\n"); 00168 gvprintf(job, "%%%%EndPage: %d\n", job->common->viewNum); 00169 } 00170 00171 static void psgen_begin_cluster(GVJ_t * job) 00172 { 00173 obj_state_t *obj = job->obj; 00174 00175 gvprintf(job, "%% %s\n", agnameof(obj->u.g)); 00176 00177 gvputs(job, "gsave\n"); 00178 } 00179 00180 static void psgen_end_cluster(GVJ_t * job) 00181 { 00182 gvputs(job, "grestore\n"); 00183 } 00184 00185 static void psgen_begin_node(GVJ_t * job) 00186 { 00187 gvputs(job, "gsave\n"); 00188 } 00189 00190 static void psgen_end_node(GVJ_t * job) 00191 { 00192 gvputs(job, "grestore\n"); 00193 } 00194 00195 static void 00196 psgen_begin_edge(GVJ_t * job) 00197 { 00198 gvputs(job, "gsave\n"); 00199 } 00200 00201 static void psgen_end_edge(GVJ_t * job) 00202 { 00203 gvputs(job, "grestore\n"); 00204 } 00205 00206 static void psgen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id) 00207 { 00208 obj_state_t *obj = job->obj; 00209 00210 if (url && obj->url_map_p) { 00211 gvputs(job, "[ /Rect [ "); 00212 gvprintpointflist(job, obj->url_map_p, 2); 00213 gvputs(job, " ]\n"); 00214 gvprintf(job, " /Border [ 0 0 0 ]\n" 00215 " /Action << /Subtype /URI /URI %s >>\n" 00216 " /Subtype /Link\n" 00217 "/ANN pdfmark\n", 00218 ps_string(url, isLatin1)); 00219 } 00220 } 00221 00222 static void 00223 ps_set_pen_style(GVJ_t *job) 00224 { 00225 double penwidth = job->obj->penwidth; 00226 char *p, *line, **s = job->obj->rawstyle; 00227 00228 gvprintdouble(job, penwidth); 00229 gvputs(job," setlinewidth\n"); 00230 00231 while (s && (p = line = *s++)) { 00232 if (strcmp(line, "setlinewidth") == 0) 00233 continue; 00234 while (*p) 00235 p++; 00236 p++; 00237 while (*p) { 00238 gvprintf(job,"%s ", p); 00239 while (*p) 00240 p++; 00241 p++; 00242 } 00243 if (strcmp(line, "invis") == 0) 00244 job->obj->penwidth = 0; 00245 gvprintf(job, "%s\n", line); 00246 } 00247 } 00248 00249 static void ps_set_color(GVJ_t *job, gvcolor_t *color) 00250 { 00251 char *objtype; 00252 00253 if (color) { 00254 switch (job->obj->type) { 00255 case ROOTGRAPH_OBJTYPE: 00256 case CLUSTER_OBJTYPE: 00257 objtype = "graph"; 00258 break; 00259 case NODE_OBJTYPE: 00260 objtype = "node"; 00261 break; 00262 case EDGE_OBJTYPE: 00263 objtype = "edge"; 00264 break; 00265 default: 00266 objtype = "sethsb"; 00267 break; 00268 } 00269 gvprintf(job, "%.5g %.5g %.5g %scolor\n", 00270 color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype); 00271 } 00272 } 00273 00274 static void psgen_textpara(GVJ_t * job, pointf p, textpara_t * para) 00275 { 00276 char *str; 00277 00278 if (job->obj->pencolor.u.HSVA[3] < .5) 00279 return; /* skip transparent text */ 00280 00281 ps_set_color(job, &(job->obj->pencolor)); 00282 gvprintdouble(job, para->fontsize); 00283 gvprintf(job, " /%s set_font\n", para->fontname); 00284 str = ps_string(para->str,isLatin1); 00285 switch (para->just) { 00286 case 'r': 00287 p.x -= para->width; 00288 break; 00289 case 'l': 00290 p.x -= 0.0; 00291 break; 00292 case 'n': 00293 default: 00294 p.x -= para->width / 2.0; 00295 break; 00296 } 00297 p.y += para->yoffset_centerline; 00298 gvprintpointf(job, p); 00299 gvputs(job, " moveto "); 00300 gvprintdouble(job, para->width); 00301 gvprintf(job, " %s alignedtext\n", str); 00302 } 00303 00304 static void psgen_ellipse(GVJ_t * job, pointf * A, int filled) 00305 { 00306 /* A[] contains 2 points: the center and corner. */ 00307 pointf AA[2]; 00308 00309 AA[0] = A[0]; 00310 AA[1].x = A[1].x - A[0].x; 00311 AA[1].y = A[1].y - A[0].y; 00312 00313 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { 00314 ps_set_color(job, &(job->obj->fillcolor)); 00315 gvprintpointflist(job, AA, 2); 00316 gvputs(job, " ellipse_path fill\n"); 00317 } 00318 if (job->obj->pencolor.u.HSVA[3] > .5) { 00319 ps_set_pen_style(job); 00320 ps_set_color(job, &(job->obj->pencolor)); 00321 gvprintpointflist(job, AA, 2); 00322 gvputs(job, " ellipse_path stroke\n"); 00323 } 00324 } 00325 00326 static void 00327 psgen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, 00328 int arrow_at_end, int filled) 00329 { 00330 int j; 00331 00332 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { 00333 ps_set_color(job, &(job->obj->fillcolor)); 00334 gvputs(job, "newpath "); 00335 gvprintpointf(job, A[0]); 00336 gvputs(job, " moveto\n"); 00337 for (j = 1; j < n; j += 3) { 00338 gvprintpointflist(job, &A[j], 3); 00339 gvputs(job, " curveto\n"); 00340 } 00341 gvputs(job, "closepath fill\n"); 00342 } 00343 if (job->obj->pencolor.u.HSVA[3] > .5) { 00344 ps_set_pen_style(job); 00345 ps_set_color(job, &(job->obj->pencolor)); 00346 gvputs(job, "newpath "); 00347 gvprintpointf(job, A[0]); 00348 gvputs(job, " moveto\n"); 00349 for (j = 1; j < n; j += 3) { 00350 gvprintpointflist(job, &A[j], 3); 00351 gvputs(job, " curveto\n"); 00352 } 00353 gvputs(job, "stroke\n"); 00354 } 00355 } 00356 00357 static void psgen_polygon(GVJ_t * job, pointf * A, int n, int filled) 00358 { 00359 int j; 00360 00361 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { 00362 ps_set_color(job, &(job->obj->fillcolor)); 00363 gvputs(job, "newpath "); 00364 gvprintpointf(job, A[0]); 00365 gvputs(job, " moveto\n"); 00366 for (j = 1; j < n; j++) { 00367 gvprintpointf(job, A[j]); 00368 gvputs(job, " lineto\n"); 00369 } 00370 gvputs(job, "closepath fill\n"); 00371 } 00372 if (job->obj->pencolor.u.HSVA[3] > .5) { 00373 ps_set_pen_style(job); 00374 ps_set_color(job, &(job->obj->pencolor)); 00375 gvputs(job, "newpath "); 00376 gvprintpointf(job, A[0]); 00377 gvputs(job, " moveto\n"); 00378 for (j = 1; j < n; j++) { 00379 gvprintpointf(job, A[j]); 00380 gvputs(job, " lineto\n"); 00381 } 00382 gvputs(job, "closepath stroke\n"); 00383 } 00384 } 00385 00386 static void psgen_polyline(GVJ_t * job, pointf * A, int n) 00387 { 00388 int j; 00389 00390 if (job->obj->pencolor.u.HSVA[3] > .5) { 00391 ps_set_pen_style(job); 00392 ps_set_color(job, &(job->obj->pencolor)); 00393 gvputs(job, "newpath "); 00394 gvprintpointf(job, A[0]); 00395 gvputs(job, " moveto\n"); 00396 for (j = 1; j < n; j++) { 00397 gvprintpointf(job, A[j]); 00398 gvputs(job, " lineto\n"); 00399 } 00400 gvputs(job, "stroke\n"); 00401 } 00402 } 00403 00404 static void psgen_comment(GVJ_t * job, char *str) 00405 { 00406 gvputs(job, "% "); 00407 gvputs(job, str); 00408 gvputs(job, "\n"); 00409 } 00410 00411 static void psgen_library_shape(GVJ_t * job, char *name, pointf * A, int n, int filled) 00412 { 00413 if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { 00414 ps_set_color(job, &(job->obj->fillcolor)); 00415 gvputs(job, "[ "); 00416 gvprintpointflist(job, A, n); 00417 gvputs(job, " "); 00418 gvprintpointf(job, A[0]); 00419 gvprintf(job, " ] %d true %s\n", n, name); 00420 } 00421 if (job->obj->pencolor.u.HSVA[3] > .5) { 00422 ps_set_pen_style(job); 00423 ps_set_color(job, &(job->obj->pencolor)); 00424 gvputs(job, "[ "); 00425 gvprintpointflist(job, A, n); 00426 gvputs(job, " "); 00427 gvprintpointf(job, A[0]); 00428 gvprintf(job, " ] %d false %s\n", n, name); 00429 } 00430 } 00431 00432 static gvrender_engine_t psgen_engine = { 00433 psgen_begin_job, 00434 psgen_end_job, 00435 psgen_begin_graph, 00436 0, /* psgen_end_graph */ 00437 psgen_begin_layer, 00438 0, /* psgen_end_layer */ 00439 psgen_begin_page, 00440 psgen_end_page, 00441 psgen_begin_cluster, 00442 psgen_end_cluster, 00443 0, /* psgen_begin_nodes */ 00444 0, /* psgen_end_nodes */ 00445 0, /* psgen_begin_edges */ 00446 0, /* psgen_end_edges */ 00447 psgen_begin_node, 00448 psgen_end_node, 00449 psgen_begin_edge, 00450 psgen_end_edge, 00451 psgen_begin_anchor, 00452 0, /* psgen_end_anchor */ 00453 0, /* psgen_begin_label */ 00454 0, /* psgen_end_label */ 00455 psgen_textpara, 00456 0, /* psgen_resolve_color */ 00457 psgen_ellipse, 00458 psgen_polygon, 00459 psgen_bezier, 00460 psgen_polyline, 00461 psgen_comment, 00462 psgen_library_shape, 00463 }; 00464 00465 static gvrender_features_t render_features_ps = { 00466 GVRENDER_DOES_TRANSFORM 00467 | GVRENDER_DOES_MAPS 00468 | GVRENDER_NO_WHITE_BG 00469 | GVRENDER_DOES_MAP_RECTANGLE, 00470 4., /* default pad - graph units */ 00471 NULL, /* knowncolors */ 00472 0, /* sizeof knowncolors */ 00473 HSVA_DOUBLE, /* color_type */ 00474 }; 00475 00476 static gvdevice_features_t device_features_ps = { 00477 GVDEVICE_DOES_PAGES 00478 | GVDEVICE_DOES_LAYERS, /* flags */ 00479 {36.,36.}, /* default margin - points */ 00480 {612.,792.}, /* default page width, height - points */ 00481 {72.,72.}, /* default dpi */ 00482 }; 00483 00484 static gvdevice_features_t device_features_eps = { 00485 0, /* flags */ 00486 {36.,36.}, /* default margin - points */ 00487 {612.,792.}, /* default page width, height - points */ 00488 {72.,72.}, /* default dpi */ 00489 }; 00490 00491 gvplugin_installed_t gvrender_ps_types[] = { 00492 {FORMAT_PS, "ps", 1, &psgen_engine, &render_features_ps}, 00493 {0, NULL, 0, NULL, NULL} 00494 }; 00495 00496 gvplugin_installed_t gvdevice_ps_types[] = { 00497 {FORMAT_PS, "ps:ps", 1, NULL, &device_features_ps}, 00498 {FORMAT_PS2, "ps2:ps", 1, NULL, &device_features_ps}, 00499 {FORMAT_EPS, "eps:ps", 1, NULL, &device_features_eps}, 00500 {0, NULL, 0, NULL, NULL} 00501 };
1.7.5