/********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1988 Jeffrey M. Hsu **********/ /* Postscript driver */ #include "ngspice/ngspice.h" #include "ngspice/cpdefs.h" #include "ngspice/graph.h" #include "ngspice/ftedbgra.h" #include "ngspice/ftedev.h" #include "ngspice/fteinput.h" #include "ngspice/fteext.h" #include "postsc.h" #include "variable.h" #include "plotting/graphdb.h" #define RAD_TO_DEG (180.0 / M_PI) #define DEVDEP(g) (*((PSdevdep *) (g)->devdep)) #define MAX_PS_LINES 1000 #define SOLID 0 #define DOTTED 1 #define gtype graph->grid.gridtype #define xoff dispdev->minx #define yoff dispdev->miny #define XOFF 48 /* printer left margin */ #define YOFF 48 /* printer bottom margin */ #define XTADJ 0 /* printer text adjustment x */ #define YTADJ 4 /* printer text adjustment y */ #define GRIDSIZE 420 /* printer gridsize divisible by 10, [7-2] */ #define GRIDSIZES 360 /* printer gridsize divisible by [10-8], [6-2] */ #define FONTSIZE 10 /* printer default fontsize */ #define FONTWIDTH 6 /* printer default fontwidth */ #define FONTHEIGHT 14 /* printer default fontheight */ typedef struct { int red, green, blue; } COLOR; /* duplicated colors from src/frontend/plotting/x11.c in rgb-style */ static const COLOR colors[] = {{ 0, 0, 0}, /*0: black */ {255, 255, 255}, /*1: white */ {255, 0, 0}, /*2: red */ { 0, 0, 255}, /*3: blue */ {255, 165, 0}, /*4: orange */ { 0, 255, 0}, /*5: green */ {255, 192, 203}, /*6: pink */ {165, 42, 42}, /*7: brown */ {240, 230, 140}, /*8: khaki */ {221, 160, 221}, /*9: plum */ {218, 112, 214}, /*10: orchid */ {238, 130, 238}, /*11: violet */ {176, 48, 96}, /*12: maroon */ { 64, 224, 208}, /*13: turqoise */ {160, 82, 45}, /*14: sienna */ {255, 127, 80}, /*15: coral */ { 0, 255, 255}, /*16: cyan */ {255, 0, 255}, /*17: magenta */ {255, 215, 0}, /*18: gold */ {255, 255, 0}, /*19: yello */ { 96, 96, 96}, /*20: gray for smith grid */ {150, 150, 150}, /*21: gray for smith grid */ {128, 128, 128}}; /*22: gray for normal grid */ typedef struct { int lastlinestyle, lastcolor; /* initial invalid value */ int lastx, lasty, linecount; } PSdevdep; static char *linestyle[] = { "[]", /* solid */ "[1 2]", /* dotted */ "[7 7]", /* longdashed */ "[3 3]", /* shortdashed */ "[7 2 2 2]", /* longdotdashed */ "[3 2 1 2]", /* shortdotdashed */ "[8 3 2 3]", "[14 2]", "[3 5 1 5]" /* dotdashed */ }; static FILE *plotfile; char psfont[128], psfontsize[32], psscale[32], pscolor[32]; static int fontsize = FONTSIZE; static int fontwidth = FONTWIDTH; static int fontheight = FONTHEIGHT; static int screenflag = 0; static int colorflag = 0; static int setbgcolor = 0; static int settxcolor = 1; static double scale; /* Used for fine tuning */ static int xtadj; /* text adjustment x */ static int ytadj; /* text adjustment y */ static int hcopygraphid; static double linewidth; static double gridlinewidth; static int maxcolor = 2; void PS_LinestyleColor(int linestyleid, int colorid); void PS_SelectColor(int colorid); void PS_Stroke(void); static size_t utf8_to_latin9(char * const output, const char *const input, const size_t length); /* Set scale, color and size of the plot */ int PS_Init(void) { char pswidth[30], psheight[30]; maxcolor = NUMELEMS(colors); if (!cp_getvar("hcopyscale", CP_STRING, psscale, sizeof(psscale))) { scale = 1.0; } else if (sscanf(psscale, "%lf", &scale) != 1) { (void) fprintf(cp_err, "Error getting scale value\n"); scale = 1.0; } else if ((scale <= 0.0) || (scale > 10.0)) { (void) fprintf(cp_err, "Scale value %lf is out of range\n", scale); scale = 1.0; } dispdev->numlinestyles = NUMELEMS(linestyle); /* plot color */ if (!cp_getvar("hcopypscolor", CP_NUM, &setbgcolor, 0)) { /* if not set, set plot to b&w and use line styles */ colorflag = 0; dispdev->numcolors = 2; } else { /* get text color and set plot to color */ colorflag = 1; dispdev->numcolors = maxcolor; cp_getvar("hcopypstxcolor", CP_NUM, &settxcolor, 0); } if (settxcolor > maxcolor || settxcolor < 0) { fprintf(stderr, "Bad PS text color selection %d\n", settxcolor); fprintf(stderr, " Maximum for hcopypstxcolor is %d\n\n", maxcolor - 1); colorflag = 0; dispdev->numcolors = 2; } if (setbgcolor > maxcolor || setbgcolor < 0) { fprintf(stderr, "Bad PS background color selection %d\n", setbgcolor); fprintf(stderr, " Maximum for hcopypscolor is %d\n", maxcolor - 1); fprintf(stderr, " Set to 1 (white)\n\n"); setbgcolor = 1; } /* plot size */ if (!cp_getvar("hcopywidth", CP_STRING, pswidth, sizeof( pswidth))) { dispdev->width = (int)(7.75 * 72.0 * scale); /* (8 1/2 - 3/4) * 72 */ } else { sscanf(pswidth, "%d", &(dispdev->width)); if (dispdev->width <= 100) dispdev->width = 100; if (dispdev->width >= 10000) dispdev->width = 10000; } if (!cp_getvar("hcopyheight", CP_STRING, psheight, sizeof(psheight))) { dispdev->height = dispdev->width; } else { sscanf(psheight, "%d", &(dispdev->height)); if (dispdev->height <= 100) dispdev->height = 100; if (dispdev->height >= 10000) dispdev->height = 10000; } /* get linewidth information from spinit */ if (!cp_getvar("xbrushwidth", CP_REAL, &linewidth, 0)) linewidth = 0; if (linewidth < 0) linewidth = 0; /* get linewidth for grid from spinit */ if (!cp_getvar("xgridwidth", CP_REAL, &gridlinewidth, 0)) gridlinewidth = linewidth; if (gridlinewidth < 0) gridlinewidth = 0; /* The following side effects have to be considered * when the printer is called by com_hardcopy ! * gr_init: * viewportxoff = 8 * fontwidth * viewportyoff = 4 * fontheight * gr_resize_internal: * viewport.width = absolute.width - 2 * viewportxoff * viewport.height = absolute.height - 2 * viewportyoff */ if (!cp_getvar("hcopyfont", CP_STRING, psfont, sizeof(psfont))) { strcpy(psfont, "Helvetica"); } if (!cp_getvar("hcopyfontsize", CP_STRING, psfontsize, sizeof(psfontsize))) { fontsize = 10; fontwidth = 6; fontheight = 14; xtadj = (int)(XTADJ * scale); ytadj = (int)(YTADJ * scale); } else { sscanf(psfontsize, "%d", &fontsize); if ((fontsize < 10) || (fontsize > 18)) fontsize = 10; fontwidth = (int)(0.5 + 0.6 * fontsize); fontheight = (int)(2.5 + 1.2 * fontsize); xtadj = (int)(XTADJ * scale * fontsize / 10); ytadj = (int)(YTADJ * scale * fontsize / 10); } screenflag = 0; dispdev->minx = (int)(XOFF / scale); dispdev->miny = (int)(YOFF / scale); return 0; } /* end of function PS_Init */ /* Plot and fill bounding box */ int PS_NewViewport(GRAPH *graph) { int x1, x2, y1, y2; hcopygraphid = graph->graphid; /* devdep initially contains name of output file */ if ((plotfile = fopen((char*)graph->devdep, "w")) == NULL) { perror((char *) graph->devdep); free(graph->devdep); graph->devdep = NULL; graph->n_byte_devdep = 0; return 1; } if (graph->absolute.width) { /* hardcopying from the screen */ screenflag = 1; } /* reasonable values, used in gr_ for placement */ graph->fontwidth = (int)(fontwidth * scale); /* was 12, p.w.h. */ graph->fontheight = (int)(fontheight * scale); /* was 24, p.w.h. */ graph->absolute.width = dispdev->width; graph->absolute.height = dispdev->height; /* Also done in gr_init, if called . . . */ graph->viewportxoff = 8 * fontwidth; graph->viewportyoff = 4 * fontheight; xoff = (int)(scale * XOFF); yoff = (int)(scale * YOFF); x1 = (int) (0.5 * 72 - fontheight); y1 = x1; x2 = (int)(graph->absolute.width + .5 * 72); y2 = (int)(graph->absolute.height + .75 * 72); /* start file off with a % */ fprintf(plotfile, "%%!PS-Adobe-3.0 EPSF-3.0\n"); fprintf(plotfile, "%%%%Creator: ngspice\n"); fprintf(plotfile, "%%%%BoundingBox: %d %d %d %d\n", x1, y1, x2, y2); /* Re-encoding to allow 'extended asccii' * thanks to http://apps.jcns.fz-juelich.de/doku/sc/ps-latin/ */ fprintf(plotfile, "/Re-encode { %% inFont outFont encoding | -\n"); fprintf(plotfile, " /MyEncoding exch def\n"); fprintf(plotfile, " exch findfont\n"); fprintf(plotfile, " dup length dict\n"); fprintf(plotfile, " begin\n"); fprintf(plotfile, " {def} forall\n"); fprintf(plotfile, " /Encoding MyEncoding def\n"); fprintf(plotfile, " currentdict\n"); fprintf(plotfile, " end\n"); fprintf(plotfile, " definefont\n"); fprintf(plotfile, "} def\n"); fprintf(plotfile, "/%s /%sLatin1 ISOLatin1Encoding Re-encode\n", psfont, psfont); fprintf(plotfile, "%g %g scale\n", 1.0 / scale, 1.0 / scale); if (colorflag == 1) { /* set the background to color given in spinit (or 0) */ PS_SelectColor(setbgcolor); fprintf(plotfile, "%s setrgbcolor\n", pscolor); fprintf(plotfile, "newpath\n"); fprintf(plotfile, "%d %d moveto %d %d lineto\n", x1, y1, x2, y1); fprintf(plotfile, "%d %d lineto %d %d lineto\n", x2, y2, x1, y2); fprintf(plotfile, "closepath fill\n"); } /* set up a reasonable font */ fprintf(plotfile, "/%sLatin1 findfont %d scalefont setfont\n\n", psfont, (int) (fontsize * scale)); graph->devdep = TMALLOC(PSdevdep, 1); graph->n_byte_devdep = sizeof(PSdevdep); DEVDEP(graph).lastlinestyle = -1; DEVDEP(graph).lastcolor = -1; DEVDEP(graph).lastx = -1; DEVDEP(graph).lasty = -1; DEVDEP(graph).linecount = 0; PS_SelectColor(0); graph->linestyle = -1; return 0; } int PS_Close(void) { /* in case PS_Close is called as part of an abort, w/o having reached PS_NewViewport */ if (plotfile) { PS_Stroke(); fprintf(plotfile, "showpage\n%%%%EOF\n"); fclose(plotfile); plotfile = NULL; } /* In case of hardcopy command destroy the hardcopy graph * and reset currentgraph to graphid 1, if possible */ if (!screenflag) { if (hcopygraphid > 0) DestroyGraph(hcopygraphid); currentgraph = FindGraph(1); } return 0; } int PS_Clear(void) { /* do nothing */ return 0; } int PS_DrawLine(int x1, int y1, int x2, int y2, bool isgrid) { /* note: this is not extendible to more than one graph => will have to give NewViewport a writeable graph XXX */ if (DEVDEP(currentgraph).linecount > MAX_PS_LINES || DEVDEP(currentgraph).linecount == 0 || x1 != DEVDEP(currentgraph).lastx || y1 != DEVDEP(currentgraph).lasty) { PS_Stroke(); fprintf(plotfile, "newpath\n"); fprintf(plotfile, "%d %d moveto\n", x1 + xoff, y1 + yoff); DEVDEP(currentgraph).linecount += 1; } if (x1 != x2 || y1 != y2) { fprintf(plotfile, "%d %d lineto\n", x2 + xoff, y2 + yoff); DEVDEP(currentgraph).linecount += 1; if(isgrid) fprintf(plotfile, "%f setlinewidth\n", gridlinewidth); else fprintf(plotfile, "%f setlinewidth\n", linewidth); } DEVDEP(currentgraph).lastx = x2; DEVDEP(currentgraph).lasty = y2; return 0; } int PS_Arc(int x0, int y0, int r, double theta, double delta_theta) { double x1, y1; double angle1, angle2; PS_Stroke(); angle1 = (double) (RAD_TO_DEG * theta); angle2 = (double) (RAD_TO_DEG * (theta + delta_theta)); x1 = (double) x0 + r * cos(theta); y1 = (double) y0 + r * sin(theta); fprintf(plotfile, "%f %f moveto ", x1+(double)xoff, y1+(double)yoff); fprintf(plotfile, "%d %d %d %f %f arc\n", x0+xoff, y0+yoff, r, angle1, angle2); fprintf(plotfile, "stroke\n"); DEVDEP(currentgraph).linecount = 0; return 0; } int PS_Text(const char *text_in, int x, int y, int angle) { int savedlstyle, savedcolor; char *text; #ifdef EXT_ASC text = text_in; #else { const size_t n_char_text = strlen(text_in); text = TMALLOC(char, n_char_text); utf8_to_latin9(text, text_in, n_char_text); } #endif /* set linestyle to solid or may get funny color text on some plotters */ savedlstyle = currentgraph->linestyle; savedcolor = currentgraph->currentcolor; PS_SetLinestyle(SOLID); /* set text color to black if background is not white */ if (setbgcolor > 0) PS_SetColor(0); else PS_SetColor(1); /* if color is given by set hcopytxpscolor=settxcolor, give it a try */ if (settxcolor >= 0) PS_SetColor(settxcolor); /* stroke the path if there's an open one */ PS_Stroke(); /* move to (x, y) */ fprintf(plotfile, "%d %d moveto\n", x + xoff + xtadj, y + yoff + ytadj); /* rotate the text counterclockwise by 'angle' degrees */ fprintf(plotfile, "%d rotate\n", angle); fprintf(plotfile, "(%s) show\n", text); /* rotate the text back clockwise by 'angle' degrees */ fprintf(plotfile, "-%d rotate\n", angle); DEVDEP(currentgraph).lastx = -1; DEVDEP(currentgraph).lasty = -1; /* restore old linestyle */ PS_SetColor(savedcolor); PS_SetLinestyle(savedlstyle); #ifndef EXT_ASC txfree(text); #endif return 0; } /* PS_DefineColor */ /* PS_DefineLinestyle */ int PS_SetLinestyle(int linestyleid) { /* special case get it when PS_Text restores a -1 linestyle */ if (linestyleid == -1) { currentgraph->linestyle = -1; return 0; } if (linestyleid < 0 || linestyleid > dispdev->numlinestyles) { internalerror("bad linestyleid inside PS_SetLinestyle"); fprintf(cp_err, "linestyleid is: %d\n", linestyleid); return 0; } PS_LinestyleColor(linestyleid, currentgraph->currentcolor); return 0; } int PS_SetColor(int colorid) { PS_LinestyleColor(currentgraph->linestyle, colorid); return 0; } int PS_Update(void) { fflush(plotfile); return 0; } /**************** PRIVAT FUNCTIONS OF PS FRONTEND *****************************/ void PS_SelectColor(int colorid) /* should be replaced by PS_DefineColor */ { char colorN[30] = "", colorstring[30] = ""; char rgb[30], s_red[30] = "0x", s_green[30] = "0x", s_blue[30] = "0x"; int red = 0, green = 0, blue = 0, maxval = 1; int i; /* Extract the rgbcolor, format is: "rgb://" */ sprintf(colorN, "color%d", colorid); if (cp_getvar(colorN, CP_STRING, colorstring, sizeof(colorstring))) { for (i = 0; colorstring[i]; i++) if (colorstring[i] == '/' || colorstring[i] == ':') colorstring[i] = ' '; sscanf(colorstring, "%s %s %s %s", rgb, &(s_red[2]), &(s_green[2]), &(s_blue[2])); if ((strlen(s_blue) == strlen(s_red) && strlen(s_green) == strlen(s_red)) && (strlen(s_blue) > 2) && (strlen(s_blue) < 7)) { sscanf(s_red, "%x", &red); sscanf(s_green, "%x", &green); sscanf(s_blue, "%x", &blue); maxval = (1 << (strlen(s_blue) - 2) * 4) - 1; sprintf(colorstring, "%1.3f %1.3f %1.3f", (double) red/maxval, (double) green/maxval, (double) blue/maxval); strcpy(pscolor, colorstring); } } if (colorid < 0 || colorid >= maxcolor) { internalerror("bad colorid inside PS_SelectColor"); } else if (maxval == 1) { /* colorN is not an rgbstring, use default color */ sprintf(colorstring, "%1.3f %1.3f %1.3f", colors[colorid].red/255.0, colors[colorid].green/255.0, colors[colorid].blue/255.0); strcpy(pscolor, colorstring); } } void PS_LinestyleColor(int linestyleid, int colorid) { /* we have some different linestyles and colors: - color and linestyle we got via function call - color and linestyle we used last time for drawing - generated color and linestyle we'll use for drawing this time */ /* these are the rules: DOTTED and colored ps -> color20 (used for grid) and SOLID color18 or 19 and black-white -> linestyle is DOTTED */ int gencolor = 0, genstyle = 0; if (colorflag == 1) { genstyle = SOLID; if (linestyleid == DOTTED) gencolor = 20; else gencolor = colorid; } else { /* colorflag == 0 -> mono*/ if ((colorid == 18) || (colorid == 19)) genstyle = DOTTED; else if (linestyleid == -1) genstyle = 0; else genstyle = linestyleid; } /* change color if nessecary */ if (colorflag == 1 && gencolor != DEVDEP(currentgraph).lastcolor) { /* if background is white, set all white line colors to black */ if ((setbgcolor == 1) && (gencolor == 1)) PS_SelectColor(0); else PS_SelectColor(gencolor); PS_Stroke(); fprintf(plotfile, "%s setrgbcolor\n", pscolor); DEVDEP(currentgraph).lastcolor = gencolor; } currentgraph->currentcolor = colorid; /* change linestyle if nessecary */ if (colorflag == 0 && genstyle != DEVDEP(currentgraph).lastlinestyle) { PS_Stroke(); fprintf(plotfile, "%s 0 setdash\n", linestyle[genstyle]); DEVDEP(currentgraph).lastlinestyle = genstyle; } currentgraph->linestyle = linestyleid; } void PS_Stroke(void) { /* strokes an open path */ if (DEVDEP(currentgraph).linecount > 0) { fprintf(plotfile, "stroke\n"); DEVDEP(currentgraph).linecount = 0; } } /* UTF-8 to ISO-8859-1/ISO-8859-15 mapper. * Return 0..255 for valid ISO-8859-15 code points, 256 otherwise. */ static inline unsigned int to_latin9(const unsigned int code) { /* Code points 0 to U+00FF are the same in both. */ if (code < 256U) return code; switch (code) { case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */ case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */ case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */ case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */ case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */ case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */ case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */ case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */ default: return 256U; } } /* Convert an UTF-8 string to ISO-8859-15. * All invalid sequences are ignored. * Note: output == input is allowed, * but input < output < input + length * is not. * Output has to have room for (length+1) chars, including the trailing NUL byte. from http://stackoverflow.com/questions/11156473/is-there-a-way-to-convert-from-utf8-to-iso-8859-1#11173493 */ size_t utf8_to_latin9(char * const output, const char *const input, const size_t length) { unsigned char *out = (unsigned char *)output; const unsigned char *in = (const unsigned char *)input; const unsigned char *const end = (const unsigned char *)input + length; unsigned int c; while (in < end) if (*in < 128) *(out++) = *(in++); /* Valid codepoint */ else if (*in < 192) in++; /* 10000000 .. 10111111 are invalid */ else if (*in < 224) { /* 110xxxxx 10xxxxxx */ if (in + 1 >= end) break; if ((in[1] & 192U) == 128U) { c = to_latin9((((unsigned int)(in[0] & 0x1FU)) << 6U) | ((unsigned int)(in[1] & 0x3FU))); if (c < 256) *(out++) = (unsigned char)c; } in += 2; } else if (*in < 240) { /* 1110xxxx 10xxxxxx 10xxxxxx */ if (in + 2 >= end) break; if ((in[1] & 192U) == 128U && (in[2] & 192U) == 128U) { c = to_latin9((((unsigned int)(in[0] & 0x0FU)) << 12U) | (((unsigned int)(in[1] & 0x3FU)) << 6U) | ((unsigned int)(in[2] & 0x3FU))); if (c < 256) *(out++) = (unsigned char)c; } in += 3; } else if (*in < 248) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (in + 3 >= end) break; if ((in[1] & 192U) == 128U && (in[2] & 192U) == 128U && (in[3] & 192U) == 128U) { c = to_latin9((((unsigned int)(in[0] & 0x07U)) << 18U) | (((unsigned int)(in[1] & 0x3FU)) << 12U) | (((unsigned int)(in[2] & 0x3FU)) << 6U) | ((unsigned int)(in[3] & 0x3FU))); if (c < 256) *(out++) = (unsigned char)c; } in += 4; } else if (*in < 252) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (in + 4 >= end) break; if ((in[1] & 192U) == 128U && (in[2] & 192U) == 128U && (in[3] & 192U) == 128U && (in[4] & 192U) == 128U) { c = to_latin9((((unsigned int)(in[0] & 0x03U)) << 24U) | (((unsigned int)(in[1] & 0x3FU)) << 18U) | (((unsigned int)(in[2] & 0x3FU)) << 12U) | (((unsigned int)(in[3] & 0x3FU)) << 6U) | ((unsigned int)(in[4] & 0x3FU))); if (c < 256) *(out++) = (unsigned char)c; } in += 5; } else if (*in < 254) { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (in + 5 >= end) break; if ((in[1] & 192U) == 128U && (in[2] & 192U) == 128U && (in[3] & 192U) == 128U && (in[4] & 192U) == 128U && (in[5] & 192U) == 128U) { c = to_latin9((((unsigned int)(in[0] & 0x01U)) << 30U) | (((unsigned int)(in[1] & 0x3FU)) << 24U) | (((unsigned int)(in[2] & 0x3FU)) << 18U) | (((unsigned int)(in[3] & 0x3FU)) << 12U) | (((unsigned int)(in[4] & 0x3FU)) << 6U) | ((unsigned int)(in[5] & 0x3FU))); if (c < 256) *(out++) = (unsigned char)c; } in += 6; } else in++; /* 11111110 and 11111111 are invalid */ /* Terminate the output string. */ *out = '\0'; return (size_t)(out - (unsigned char *)output); }