726 lines
25 KiB
C
726 lines
25 KiB
C
/**********
|
|
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:<red>/<green>/<blue>" */
|
|
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);
|
|
}
|