Graphviz  2.29.20120524.0446
plugin/core/gvrender_core_ps.c
Go to the documentation of this file.
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 };