4
* $Id: ps.c,v 1.2 2003/08/21 22:22:00 travo Exp $
6
* Implement the PostScript engine for GIST.
9
/* Copyright (c) 1994. The Regents of the University of California.
10
All rights reserved. */
16
extern p_file *GistOpen(const char *name); /* from gread.c */
23
#define CREATE_PS(name) p_fopen(name, "w")
26
static char *psType= "PostScript";
28
static char line[80]; /* no lines longer than 78 characters! */
30
#define PS_PER_POINT 20
31
#define NDC_TO_PS (20.0/ONE_POINT)
32
#define DEFAULT_PS_WIDTH (DEFAULT_LINE_WIDTH*NDC_TO_PS)
36
static int PutPrologLine(p_file *file);
37
static p_file *CopyProlog(const char *name, const char *title);
38
static void InitBB(GpsBBox *bb);
39
static void SetPageDefaults(PSEngine *psEngine);
40
static void SetPSTransform(GpTransform *toPixels, int landscape);
41
static void GetEPSFBox(int landscape, GpsBBox *bb,
42
int *xll, int *yll, int *xur, int *yur);
43
static int PutLine(PSEngine *psEngine);
44
static int Append(PSEngine *psEngine, const char *s);
45
static int BeginPage(PSEngine *psEngine);
46
static int EndClip(PSEngine *psEngine);
47
static int BeginClip(PSEngine *psEngine, GpTransform *trans);
48
static int EndPage(PSEngine *psEngine);
49
static int PutPoints(PSEngine *psEngine, GpPoint *points, long nPoints,
51
static int ChangePalette(Engine *engine);
52
static void Kill(Engine *engine);
53
static int Clear(Engine *engine, int always);
54
static int Flush(Engine *engine);
55
static int SetupColor(PSEngine *psEngine, unsigned long color);
56
static int SetupLine(PSEngine *psEngine, GpLineAttribs *gistAl);
57
static int CheckClip(PSEngine *psEngine);
58
static int DrawLines(Engine *engine, long n, const GpReal *px,
59
const GpReal *py, int closed, int smooth);
60
static int SetupFont(PSEngine *psEngine, GpReal height);
61
static int DrawMarkers(Engine *engine, long n, const GpReal *px,
63
static int SetupText(PSEngine *psEngine);
64
static int DrwText(Engine *engine, GpReal x0, GpReal y0, const char *text);
65
static int DrawFill(Engine *engine, long n, const GpReal *px,
67
static int DrawCells(Engine *engine, GpReal px, GpReal py, GpReal qx,
68
GpReal qy, long width, long height, long nColumns,
69
const GpColor *colors);
70
static int DrawDisjoint(Engine *engine, long n, const GpReal *px,
71
const GpReal *py, const GpReal *qx, const GpReal *qy);
73
static int ps_fputs(p_file *file, char *buf);
75
/* ------------------------------------------------------------------------ */
77
static const char *titleIs;
78
static int needUser, needDate;
79
static void *pf_stdout = &titleIs; /* any non-zero address will do */
82
ps_fputs(p_file *file, char *buf)
84
if (file != pf_stdout) return p_fputs(file, buf);
85
if (g_stdout) g_stdout(buf);
89
static int PutPrologLine(p_file *file)
91
if (titleIs && strncmp(line, "%%Title:", 8L)==0) {
94
strncat(line, titleIs, 60L);
97
} else if (needUser && strncmp(line, "%%For:", 6L)==0) {
100
strncat(line, p_getuser(), 60L);
103
} else if (needDate && strncmp(line, "%%CreationDate:", 15L)==0) {
104
time_t t= time((time_t *)0);
105
/* ctime returns 26 chars, e.g.- "Sun Jan 3 15:14:13 1988\n\0" */
106
char *st= (t==-1)? "\n" : ctime(&t);
109
strcat(line, st? st : "\n");
112
return ps_fputs(file, line);
115
static p_file *CopyProlog(const char *name, const char *title)
117
p_file *psps= GistOpen("ps.ps");
118
p_file *file= strcmp(name, "*stdout*")? CREATE_PS(name) : pf_stdout;
119
if (!psps) strcpy(gistError, "unable to open PostScript prolog ps.ps");
120
if (!file) strcpy(gistError, "unable to create PostScript output file");
124
needUser= needDate= 1;
126
if (!p_fgets(psps, line, 79) || PutPrologLine(file)<0) {
127
if (file!=pf_stdout) p_fclose(file);
129
strcpy(gistError, "bad PostScript prolog format in ps.ps??");
132
if (strncmp(line, "%%EndSetup", 10L)==0) break;
136
if (file!=pf_stdout) p_fclose(file);
140
if (psps) p_fclose(psps);
144
static void InitBB(GpsBBox *bb)
146
bb->xll= bb->yll= 0x7ff0;
150
static void SetPageDefaults(PSEngine *psEngine)
152
/* Set current state to match state set by GI procedure in ps.ps */
153
psEngine->curClip= 0;
154
psEngine->curColor= P_FG;
155
psEngine->curType= L_SOLID;
156
psEngine->curWidth= 1.0;
157
psEngine->curFont= T_COURIER;
158
psEngine->curHeight= 12.0*ONE_POINT;
159
psEngine->curAlignH= TH_LEFT;
160
psEngine->curAlignV= TV_BASE;
161
psEngine->curOpaque= 0;
162
InitBB(&psEngine->pageBB);
165
static void SetPSTransform(GpTransform *toPixels, int landscape)
167
/* PostScript thinks an 8.5-by-11 inch page is 612-by-792 points */
168
toPixels->window.xmin= toPixels->window.ymin= 0.0;
170
toPixels->window.xmax= 15840.0;
171
toPixels->window.ymax= 12240.0;
173
toPixels->window.xmax= 12240.0;
174
toPixels->window.ymax= 15840.0;
176
toPixels->viewport.xmin= toPixels->viewport.ymin= 0.0;
177
toPixels->viewport.xmax= toPixels->window.xmax*(1.0/NDC_TO_PS);
178
toPixels->viewport.ymax= toPixels->window.ymax*(1.0/NDC_TO_PS);
181
static void GetEPSFBox(int landscape, GpsBBox *bb,
182
int *xll, int *yll, int *xur, int *yur)
184
/* Transform bounding box to
185
Document Structure Comment coordinates for use as EPSF file */
188
if (bb->xll < bb->xur) {
189
/* this is a valid bounding box */
190
xl= bb->xll/PS_PER_POINT;
191
yl= bb->yll/PS_PER_POINT;
192
xu= 1+(bb->xur-1)/PS_PER_POINT;
193
yu= 1+(bb->yur-1)/PS_PER_POINT;
196
/* this is not a valid bounding box, return whole page */
198
xu= 612; /* In PostScript, 1 inch is exactly 72 points */
203
*xll= 612-yu; *yll= xl;
204
*xur= 612-yl; *yur= xu;
211
static int PutLine(PSEngine *psEngine)
213
p_file *file= psEngine->file;
214
char *line= psEngine->line;
215
long nchars= psEngine->nchars;
218
if (psEngine->closed) return 1;
219
file= psEngine->file= CopyProlog(psEngine->filename, psEngine->e.name);
220
if (!file) { psEngine->closed= 1; return 1; }
221
psEngine->currentPage= 1;
222
SetPageDefaults(psEngine);
223
InitBB(&psEngine->docBB);
226
line[nchars++]= '\n';
228
if (ps_fputs(file, line)<0) {
229
if (file!=pf_stdout) p_fclose(file);
232
strcpy(gistError, "p_fputs failed writing PostScript file");
240
static int Append(PSEngine *psEngine, const char *s)
242
long len= s? strlen(s) : 0;
243
long nchars= psEngine->nchars;
244
char *line= psEngine->line;
246
if (nchars+1+len>78) {
247
if (PutLine(psEngine)) return 1;
249
} else if (nchars>0) {
252
strcpy(line+nchars, s);
253
psEngine->nchars= nchars+len;
258
static int BeginPage(PSEngine *psEngine)
260
int currentPage= psEngine->currentPage;
262
psEngine->e.marked= 1;
264
/* A change in color table can take place only at the beginning
265
of a page. ChangePalette strobes the palette in the Engine base
266
class part into the PSEngine palette. */
267
psEngine->nColors= 0; /* reset to mono mode */
268
ChangePalette((Engine *)psEngine);
270
if (psEngine->nchars && PutLine(psEngine)) return 1;
271
if (PutLine(psEngine)) return 1;
273
sprintf(line, "%%%%Page: %d %d", currentPage, currentPage);
274
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
276
if (Append(psEngine, "%%PageBoundingBox: (atend)") ||
277
PutLine(psEngine)) return 1;
279
if (Append(psEngine, "GistPrimitives begin /PG save def GI") ||
280
PutLine(psEngine)) return 1;
282
/* Set transform viewport to reflect current page orientation */
283
if (psEngine->landscape != psEngine->e.landscape) {
284
SetPSTransform(&psEngine->e.transform, psEngine->e.landscape);
285
psEngine->landscape= psEngine->e.landscape;
287
if (psEngine->landscape) {
288
if (Append(psEngine, "LAND") ||
289
PutLine(psEngine)) return 1;
292
if (psEngine->e.colorMode && psEngine->e.palette &&
293
psEngine->e.nColors>0) {
294
int i, nColors= psEngine->e.nColors;
295
GpColorCell *palette= psEngine->e.palette;
297
sprintf(line, "%d CT", nColors);
298
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
299
for (i=0 ; i<nColors ; i++) {
300
color= P_R(palette[i])<<16 | P_G(palette[i])<<8 | P_B(palette[i]);
301
sprintf(line, "%06lx", color);
302
if (Append(psEngine, line)) return 1;
304
if (psEngine->nchars && PutLine(psEngine)) return 1;
305
psEngine->colorMode= 1; /* color table has been written */
306
psEngine->nColors= nColors;
308
psEngine->colorMode= 0; /* NO color table exists on this page */
309
/* But, if there is a palette, still want to use grays */
310
if (psEngine->e.palette && psEngine->e.nColors>0)
311
psEngine->nColors= psEngine->e.nColors;
314
if (Append(psEngine, "%%EndPageSetup") || PutLine(psEngine)) return 1;
318
static int EndClip(PSEngine *psEngine)
320
if (psEngine->curClip) {
321
if ((psEngine->nchars && PutLine(psEngine)) ||
322
Append(psEngine, "CLOF")) return 1;
323
psEngine->curClip= 0;
324
/* Restore state at time of CLON */
325
psEngine->curColor= psEngine->clipColor;
326
psEngine->curType= psEngine->clipType;
327
psEngine->curWidth= psEngine->clipWidth;
328
psEngine->curFont= psEngine->clipFont;
329
psEngine->curHeight= psEngine->clipHeight;
334
static int BeginClip(PSEngine *psEngine, GpTransform *trans)
338
int xll, yll, xur, yur;
339
GpBox *port= &trans->viewport;
340
GpBox *box= &psEngine->clipBox;
342
if (!psEngine->e.marked && BeginPage(psEngine)) return 1;
344
if (psEngine->curClip) {
345
if (port->xmin==box->xmin && port->xmax==box->xmax &&
346
port->ymin==box->ymin && port->ymax==box->ymax) return 0;
347
if (EndClip(psEngine)) return 1;
350
x[0]= trans->window.xmin; x[1]= trans->window.xmax;
351
y[0]= trans->window.ymin; y[1]= trans->window.ymax;
353
GpIntPoints(&psEngine->e.map, 3, 2, x, y, &points);
354
if (points[0].x > points[1].x) {
355
xll= points[1].x; xur= points[0].x;
357
xll= points[0].x; xur= points[1].x;
359
if (points[0].y > points[1].y) {
360
yll= points[1].y; yur= points[0].y;
362
yll= points[0].y; yur= points[1].y;
365
sprintf(line, "%d %d %d %d CLON", xur-xll, yur-yll, xll, yll);
366
if (Append(psEngine, line)) return 1;
367
psEngine->curClip= 1;
370
/* Must save state at time of CLON, since CLOF does grestore */
371
psEngine->clipColor= psEngine->curColor;
372
psEngine->clipType= psEngine->curType;
373
psEngine->clipWidth= psEngine->curWidth;
374
psEngine->clipFont= psEngine->curFont;
375
psEngine->clipHeight= psEngine->curHeight;
376
/* Note that text alignment/opacity is not affected by grestore */
378
/* Expand page boundary to include clip boundary */
379
if (xll < psEngine->pageBB.xll) psEngine->pageBB.xll= xll;
380
if (yll < psEngine->pageBB.yll) psEngine->pageBB.yll= yll;
381
if (xur > psEngine->pageBB.xur) psEngine->pageBB.xur= xur;
382
if (yur > psEngine->pageBB.yur) psEngine->pageBB.yur= yur;
386
static int EndPage(PSEngine *psEngine)
388
char *line= psEngine->line;
389
int xll, yll, xur, yur;
391
if (EndClip(psEngine)) return 1;
393
if ((psEngine->nchars && PutLine(psEngine)) ||
394
Append(psEngine, "PG restore") || PutLine(psEngine)) return 1;
395
if (Append(psEngine, "showpage") || PutLine(psEngine)) return 1;
396
if (Append(psEngine, "end") || PutLine(psEngine)) return 1;
397
if (Append(psEngine, "%%PageTrailer") || PutLine(psEngine)) return 1;
399
GetEPSFBox(psEngine->e.landscape, &psEngine->pageBB,
400
&xll, &yll, &xur, &yur);
401
if (xll < psEngine->docBB.xll) psEngine->docBB.xll= xll;
402
if (yll < psEngine->docBB.yll) psEngine->docBB.yll= yll;
403
if (xur > psEngine->docBB.xur) psEngine->docBB.xur= xur;
404
if (yur > psEngine->docBB.yur) psEngine->docBB.yur= yur;
405
sprintf(line, "%%%%PageBoundingBox: %d %d %d %d", xll, yll, xur, yur);
406
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
408
psEngine->currentPage++;
409
psEngine->e.marked= 0;
410
SetPageDefaults(psEngine);
411
if (psEngine->file!=pf_stdout) p_fflush(psEngine->file);
415
static char hexChar[17]= "0123456789abcdef";
417
static int PutPoints(PSEngine *psEngine, GpPoint *points, long nPoints,
421
int xll= 0x7ff0, yll= 0x7ff0, xur= 0, yur= 0;
422
char *now, *line= psEngine->line;
423
int perLine, nchars= psEngine->nchars;
425
if (!psEngine->e.marked && BeginPage(psEngine)) return 1;
430
} else if (nchars<70) {
432
perLine= (78-nchars)/8;
434
if (PutLine(psEngine)) return 1;
440
/* Dump the points in hex 9 (72 chars) to a line */
442
for (i=0 ; i<perLine ; i++) {
446
if (ix<0) ix= xll; /* be sure x in range, set xll, xur */
447
else if (ix>0x7ff0) ix= xur;
448
else if (ix<xll) xll= ix;
449
else if (ix>xur) xur= ix;
450
if (iy<0) iy= yll; /* be sure y in range, set yll, yur */
451
else if (iy>0x7ff0) iy= yur;
452
else if (iy<yll) yll= iy;
453
else if (iy>yur) yur= iy;
454
*now++= hexChar[ix>>12];
455
*now++= hexChar[(ix>>8)&0xf];
456
*now++= hexChar[(ix>>4)&0xf];
457
*now++= hexChar[ix&0xf];
458
*now++= hexChar[iy>>12];
459
*now++= hexChar[(iy>>8)&0xf];
460
*now++= hexChar[(iy>>4)&0xf];
461
*now++= hexChar[iy&0xf];
464
if (nPoints==0) break;
466
psEngine->nchars= nchars;
467
if (PutLine(psEngine)) return 1;
473
/* Adjust the bounding box for the current page */
474
if (xll<xur && !psEngine->curClip) {
479
if (xll<psEngine->pageBB.xll) psEngine->pageBB.xll= xll;
480
if (xur>psEngine->pageBB.xur) psEngine->pageBB.xur= xur;
481
if (yll<psEngine->pageBB.yll) psEngine->pageBB.yll= yll;
482
if (yur>psEngine->pageBB.yur) psEngine->pageBB.yur= yur;
488
/* ------------------------------------------------------------------------ */
490
static int ChangePalette(Engine *engine)
492
PSEngine *psEngine= (PSEngine *)engine;
493
int nColors= engine->nColors;
495
if (nColors<=0 || !engine->palette) {
496
/* new color table is void-- don't allow indexed colors */
497
psEngine->colorMode= 0;
498
psEngine->nColors= 0;
501
/* remember current color mode, then set to mono mode until
502
new color table can be written in BeginPage */
503
psEngine->colorMode= 0;
504
psEngine->nColors= 0; /* don't index into table before BeginPage */
507
engine->colorChange= 0;
511
/* ------------------------------------------------------------------------ */
513
static char *psFontNames[N_PSFONTS]= {
514
"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
515
"Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic",
516
"Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique",
517
"Symbol", "Symbol", "Symbol", "Symbol",
518
"NewCenturySchlbk-Roman", "NewCenturySchlbk-Bold",
519
"NewCenturySchlbk-Italic", "NewCenturySchlbk-BoldItalic" };
521
static void Kill(Engine *engine)
523
PSEngine *psEngine= (PSEngine *)engine;
524
long fonts= psEngine->fonts;
525
int i, xll, yll, xur, yur;
528
if (psEngine->e.marked) bad= EndPage(psEngine);
530
if (psEngine->file) {
531
if (!bad) bad= PutLine(psEngine);
532
if (!bad) bad= Append(psEngine, "%%Trailer");
533
if (!bad) bad= PutLine(psEngine);
535
sprintf(line, "%%%%Pages: %d", psEngine->currentPage-1);
536
if (!bad) bad= Append(psEngine, line);
537
if (!bad) bad= PutLine(psEngine);
539
xll= psEngine->docBB.xll;
540
xur= psEngine->docBB.xur;
542
yll= psEngine->docBB.yll;
543
yur= psEngine->docBB.yur;
549
sprintf(line, "%%%%BoundingBox: %d %d %d %d", xll, yll, xur, yur);
550
if (!bad) bad= Append(psEngine, line);
551
if (!bad) bad= PutLine(psEngine);
553
strcpy(line, "%%DocumentFonts: ");
554
for (i=0 ; i<N_PSFONTS ; i++) {
555
if ((1<<i) & fonts) {
556
strcat(line, psFontNames[i]);
557
if (!bad) bad= Append(psEngine, line);
558
if (!bad) bad= PutLine(psEngine);
559
strcpy(line, "%%+ ");
563
if (psEngine->file!=pf_stdout) p_fclose(psEngine->file);
568
static int Clear(Engine *engine, int always)
570
PSEngine *psEngine= (PSEngine *)engine;
571
if (always || engine->marked) EndPage(psEngine);
576
static int Flush(Engine *engine)
578
PSEngine *psEngine= (PSEngine *)engine;
579
if (psEngine->file && psEngine->file!=pf_stdout)
580
p_fflush(psEngine->file);
584
/* ------------------------------------------------------------------------ */
586
static char *colorCommands[14]= {
587
"BG", "FG", "BLK", "WHT", "RED", "GRN", "BLU", "CYA", "MAG", "YEL",
588
"GYD", "GYC", "GYB", "GYA" };
590
static int SetupColor(PSEngine *psEngine, unsigned long color)
592
int nColors= psEngine->nColors;
594
if (!psEngine->e.marked && BeginPage(psEngine)) return 1;
595
if (color==psEngine->curColor) return 0;
598
/* "CI index C"-- "CI" omitted if current color is indexed */
600
GpColorCell *palette= psEngine->e.palette;
602
if (color>=(unsigned long)nColors) color= nColors-1;
603
if (psEngine->colorMode) c= color; /* this page has a color table */
604
else c= (P_R(palette[color])+
605
P_G(palette[color])+P_B(palette[color]))/3; /* mono page */
607
if (color>255) color= 255;
608
c= color; /* No palette ==> no color table on page */
610
if (psEngine->curColor>=240UL) {
611
if (Append(psEngine, "CI")) return 1;
613
sprintf(line, "%ld C", c);
614
if (Append(psEngine, line)) return 1;
615
} else if (color<256UL) {
616
/* standard color command FG, RED, etc. */
617
if (color<P_GRAYA) color= P_FG;
618
if (Append(psEngine, colorCommands[255UL-color])) return 1;
620
sprintf(line, "16#%lx C", color);
621
if (Append(psEngine, line)) return 1;
623
psEngine->curColor= color;
628
static int SetupLine(PSEngine *psEngine, GpLineAttribs *gistAl)
630
int changeLW= (psEngine->curWidth!=gistAl->width);
631
if (SetupColor(psEngine, gistAl->color)) return 1;
636
lwidth= (int)(DEFAULT_PS_WIDTH*gistAl->width);
637
sprintf(line, "%d LW", lwidth);
638
if (Append(psEngine, line)) return 1;
640
psEngine->curWidth= gistAl->width;
644
the dash pattern is a function of the line width (see pscom.ps) */
645
if (psEngine->curType!=gistAl->type ||
646
(changeLW && gistAl->type!=L_SOLID)) {
647
int ltype= gistAl->type;
648
if (ltype==L_NONE) return 1;
650
if (ltype<0 || ltype>N_PSDASHES) ltype= L_SOLID;
651
sprintf(line, "%d DSH", ltype-1);
652
if (Append(psEngine, line)) return 1;
654
psEngine->curType= gistAl->type;
660
static int CheckClip(PSEngine *psEngine)
662
if (gistClip) return BeginClip(psEngine, &gistT);
663
else if (psEngine->curClip) return EndClip(psEngine);
667
static int DrawLines(Engine *engine, long n, const GpReal *px,
668
const GpReal *py, int closed, int smooth)
670
PSEngine *psEngine= (PSEngine *)engine;
671
GpXYMap *map= &engine->map;
672
long maxPoints= 4050, nPoints;
673
long np= n + (closed?1:0);
674
int firstPass= 1, markEnd= 0;
675
GpPoint firstPoint, *points;
678
if (CheckClip(psEngine)) return 1;
680
if (SetupLine(psEngine, &gistA.l)) return 1;
681
if (psEngine->curClip) size= 0;
682
else size= (int)(psEngine->curWidth*0.5*DEFAULT_PS_WIDTH);
685
long nLines= (np-1)/9 + 1; /* 9 points is 72 characters */
686
if (psEngine->nchars && PutLine(psEngine)) return 1;
687
sprintf(line, "%%%%BeginData: %ld ASCII Lines", nLines+1);
688
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
691
sprintf(line, smooth? "%ld LS" : "%ld L", np);
692
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
695
GpIntPoints(map, maxPoints, n, px, py, &points))) {
698
firstPoint= points[0];
703
points[nPoints++]= firstPoint;
706
if (PutPoints(psEngine, points, nPoints, size)) return 1;
707
if (n==nPoints) break;
714
if (Append(psEngine, "%%EndData") || PutLine(psEngine)) return 1;
720
/* ------------------------------------------------------------------------ */
722
static char *psFontCommands[N_PSFONTS]= {
723
"0 Cour", "1 Cour", "2 Cour", "3 Cour",
724
"0 Tims", "1 Tims", "2 Tims", "3 Tims",
725
"0 Helv", "1 Helv", "2 Helv", "3 Helv",
726
"0 Symb", "1 Symb", "2 Symb", "3 Symb",
727
"0 NCen", "1 NCen", "2 NCen", "3 NCen" };
729
static int SetupFont(PSEngine *psEngine, GpReal height)
731
int font= gistA.t.font;
732
if (font<0 || font>=N_PSFONTS) font= 0;
733
if (psEngine->curFont!=font ||
734
psEngine->curHeight!=height) {
735
int ptSz= (int)(gistA.t.height*NDC_TO_PS+0.5);
736
int lnSp= ptSz; /* LnSp same as PtSz for now */
737
if (Append(psEngine, psFontCommands[font])) return 1;
738
sprintf(line, "%d %d FNT", ptSz, lnSp);
739
if (Append(psEngine, line)) return 1;
740
psEngine->curFont= font;
741
psEngine->curHeight= height;
742
psEngine->fonts|= (1<<font);
747
static int DrawMarkers(Engine *engine, long n, const GpReal *px,
750
PSEngine *psEngine= (PSEngine *)engine;
751
GpXYMap *map= &engine->map;
752
long maxPoints= 4050, nPoints;
753
int type, markEnd= 0;
758
if (n<1 || gistA.m.type<=0) return 0;
759
if (CheckClip(psEngine)) return 1;
760
if (SetupColor(psEngine, gistA.m.color) ||
761
SetupFont(psEngine, gistA.m.size*DEFAULT_MARKER_SIZE)) return 1;
762
if (psEngine->curClip) size= 0;
763
else size= (int)(psEngine->curHeight*NDC_TO_PS);
765
if (gistA.m.type>32 && gistA.m.type<127) {
766
char *now= typeString;
768
if (gistA.m.type=='(' || gistA.m.type==')' || gistA.m.type=='\\')
770
*now++= type= gistA.m.type;
774
if (gistA.m.type<0 || gistA.m.type>M_CROSS) type= M_ASTERISK;
775
else type= gistA.m.type;
776
sprintf(typeString, "%d", type-1);
780
long nLines= (n-1)/9 + 1; /* 9 points is 72 characters */
781
if (psEngine->nchars && PutLine(psEngine)) return 1;
782
sprintf(line, "%%%%BeginData: %ld ASCII Lines", nLines+1);
783
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
786
if (Append(psEngine, typeString)) return 1; /* "(A)" or "1", e.g. */
787
sprintf(line, type<32? "%ld MS" : "%ld M", n);
788
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
791
GpIntPoints(map, maxPoints, n, px, py, &points))) {
792
if (PutPoints(psEngine, points, nPoints, size)) return 1;
793
if (n==nPoints) break;
800
if (Append(psEngine, "%%EndData") || PutLine(psEngine)) return 1;
806
/* ------------------------------------------------------------------------ */
808
static char *psHCommands[3]= { "/LF", "/CN", "/RT" };
809
static char *psVCommands[5]= { "/TP", "/CP", "/HF", "/BA", "/BT" };
811
static int SetupText(PSEngine *psEngine)
814
int h= gistA.t.alignH, v= gistA.t.alignV;
815
GtGetAlignment(&gistA.t, &h, &v);
817
if (SetupColor(psEngine, gistA.t.color)) return 1;
819
if (psEngine->curAlignH!=h || psEngine->curAlignV!=v) {
820
sprintf(line, "%s %s JUS", psHCommands[h-1], psVCommands[v-1]);
821
if (Append(psEngine, line)) return 1;
822
psEngine->curAlignH= h;
823
psEngine->curAlignV= v;
827
/* if (gistA.t.orient!=TX_RIGHT) opq= 1; let this be X only limitation */
828
if (psEngine->curOpaque != (opq!=0)) {
829
if (opq) Append(psEngine, "1 OPQ");
830
else if (Append(psEngine, "0 OPQ")) return 1;
831
psEngine->curOpaque= (opq!=0);
834
if (psEngine->curFont!=gistA.t.font ||
835
psEngine->curHeight!=gistA.t.height) {
836
if (SetupFont(psEngine, gistA.t.height)) return 1;
841
static int break_line_now(PSEngine *psEngine, char **nw, int *nchs);
843
static int DrwText(Engine *engine, GpReal x0, GpReal y0, const char *text)
845
PSEngine *psEngine= (PSEngine *)engine;
847
GpReal width, height, lineHeight;
848
GpXYMap *map= &engine->map;
849
GpBox *wind= &engine->transform.window;
850
GpReal xmin, xmax, ymin, ymax;
851
int xll, yll, xur, yur, alignH, alignV;
852
int count, nchars, firstPass;
857
if (CheckClip(psEngine) || SetupText(psEngine)) return 1;
858
alignH= psEngine->curAlignH;
859
alignV= psEngine->curAlignV;
860
/* Compute text location in PostScript coordinates. */
861
x0= map->x.scale*x0 + map->x.offset;
862
y0= map->y.scale*y0 + map->y.offset;
864
/* handle multi-line strings */
865
nlines= GtTextShape(text, &gistA.t, (WidthFunction)0, &width);
866
lineHeight= gistA.t.height * NDC_TO_PS;
867
width*= 0.6*lineHeight;
868
height= lineHeight*(GpReal)nlines;
870
/* Reject if and only if the specified point is off of the current
871
page by more than the approximate size of the text. Note that
872
the entire block of text is either accepted or rejected --
873
PostScript does the clipping. */
874
if (wind->xmax>wind->xmin) { xmin= wind->xmin; xmax= wind->xmax; }
875
else { xmin= wind->xmax; xmax= wind->xmin; }
876
if (wind->ymax>wind->ymin) { ymin= wind->ymin; ymax= wind->ymax; }
877
else { ymin= wind->ymax; ymax= wind->ymin; }
878
if (gistA.t.orient==TX_RIGHT || gistA.t.orient==TX_LEFT) {
879
if (x0<xmin-width || x0>xmax+width ||
880
y0<ymin-height || y0>ymax+height) return 0;
882
if (x0<xmin-height || x0>xmax+height ||
883
y0<ymin-width || y0>ymax+width) return 0;
886
/* Adjust y0 (or x0) to represent topmost line */
888
if (gistA.t.orient==TX_RIGHT) {
889
if (alignV==TV_BASE || alignV==TV_BOTTOM) y0+= height-lineHeight;
890
if (alignV==TV_HALF) y0+= 0.5*(height-lineHeight);
891
} else if (gistA.t.orient==TX_LEFT) {
892
if (alignV==TV_BASE || alignV==TV_BOTTOM) y0-= height-lineHeight;
893
if (alignV==TV_HALF) y0-= 0.5*(height-lineHeight);
894
} else if (gistA.t.orient==TX_UP) {
895
if (alignV==TV_BASE || alignV==TV_BOTTOM) x0-= height-lineHeight;
896
if (alignV==TV_HALF) x0-= 0.5*(height-lineHeight);
898
if (alignV==TV_BASE || alignV==TV_BOTTOM) x0+= height-lineHeight;
899
if (alignV==TV_HALF) x0+= 0.5*(height-lineHeight);
906
/* Guess at the bounding box for the text */
907
if (!psEngine->curClip) {
908
if (gistA.t.orient==TX_RIGHT) {
910
if (alignH==TH_CENTER) x0 -= 0.5*width;
911
else if (alignH==TH_RIGHT) x0 -= width;
913
if (alignV==TV_TOP || alignV==TV_CAP) y0 -= height;
914
else if (alignV==TV_HALF) y0 -= height-0.4*lineHeight;
915
else if (alignV==TV_BASE) y0 -= height-0.8*lineHeight;
916
else if (alignV==TV_BOTTOM) y0 -= height-lineHeight;
918
} else if (gistA.t.orient==TX_LEFT) {
920
if (alignH==TH_CENTER) x0 -= 0.5*width;
921
else if (alignH==TH_LEFT) x0 -= width;
923
if (alignV==TV_HALF) y0 -= .4*lineHeight;
924
else if (alignV==TV_BASE) y0 -= 0.8*lineHeight;
925
else if (alignV==TV_BOTTOM) y0 -= lineHeight;
927
} else if (gistA.t.orient==TX_UP) {
929
if (alignH==TH_CENTER) y0 -= 0.5*width;
930
else if (alignH==TH_RIGHT) y0 -= width;
932
if (alignV==TV_HALF) x0 -= 0.4*lineHeight;
933
else if (alignV==TV_BASE) x0 -= 0.8*lineHeight;
934
else if (alignV==TV_BOTTOM) x0 -= lineHeight;
936
} else { /* TX_DOWN */
938
if (alignH==TH_CENTER) y0 -= 0.5*width;
939
else if (alignH==TH_LEFT) y0 -= width;
941
if (alignV==TV_TOP || alignV==TV_CAP) x0 -= height;
942
else if (alignV==TV_HALF) x0 -= height-0.4*lineHeight;
943
else if (alignV==TV_BASE) x0 -= height-0.8*lineHeight;
944
else if (alignV==TV_BOTTOM) x0 -= height-lineHeight;
948
if (x0>xmin) xmin= x0;
949
if (x0+width<xmax) xmax= x0+width;
950
if (y0>ymin) ymin= y0;
951
if (y0+height<ymax) ymax= y0+height;
957
if (xll<psEngine->pageBB.xll) psEngine->pageBB.xll= xll;
958
if (xur>psEngine->pageBB.xur) psEngine->pageBB.xur= xur;
959
if (yll<psEngine->pageBB.yll) psEngine->pageBB.yll= yll;
960
if (yur>psEngine->pageBB.yur) psEngine->pageBB.yur= yur;
963
if (nlines>1 || gistA.t.orient!=TX_RIGHT) {
964
if (Append(psEngine, "[")) return 1;
967
nchars= psEngine->nchars;
968
now= psEngine->line+nchars;
970
while ((t= GtNextLine(text, &count, gistA.t.orient)) || firstPass) {
976
psEngine->nchars= nchars;
977
if (PutLine(psEngine)) return 1;
986
if (nchars>72 && break_line_now(psEngine, &now, &nchars)) return 1;
989
if (c>=32 && c<127) {
991
if (c=='!' && count) {
992
c= text[1]; /* peek at next char */
993
if (c=='!' || c=='^' || c=='_') {
999
break_line_now(psEngine, &now, &nchars)) return 1;
1000
strcpy(now, "\\024"); /* DC4 is ps.ps escape char */
1003
psEngine->fonts|= (1<<T_SYMBOL); /* symbol font used */
1005
*now++= '^'; /* !] means ^ (perp) in symbol font */
1012
} else if (c=='^') {
1014
/* terminate current super or subscript escape */
1015
strcpy(now, "\\021"); /* DC1 is ps.ps escape char */
1020
/* intialize superscript escape */
1022
break_line_now(psEngine, &now, &nchars)) return 1;
1023
strcpy(now, "\\021\\022"); /* DC1DC2 is ps.ps escape seq */
1031
} else if (c=='_') {
1033
/* terminate current super or subscript escape */
1035
break_line_now(psEngine, &now, &nchars)) return 1;
1036
strcpy(now, "\\021"); /* DC1 is ps.ps escape char */
1041
/* intialize subscript escape */
1043
break_line_now(psEngine, &now, &nchars)) return 1;
1044
strcpy(now, "\\021\\023"); /* DC1DC3 is ps.ps escape seq */
1054
/* ordinary printing character */
1055
if (c=='(' || c==')' || c=='\\') {
1063
/* non-printing characters rendered as \ooo */
1068
} else if (c<'\021' || c>'\024') {
1069
/* DC1 through DC4 have special meaning in ps.ps, skip them */
1070
sprintf(now, "\\%03o", (int)((unsigned char)c));
1079
/* terminate current super or subscript escape */
1080
strcpy(now, "\\021"); /* DC1 is ps.ps escape char */
1089
psEngine->nchars= nchars;
1091
if (gistA.t.orient==TX_RIGHT) {
1092
sprintf(line, nlines>1? "] %d %d TA" : "%d %d T", ix, iy);
1095
if (gistA.t.orient==TX_LEFT) angle= 180;
1096
else if (gistA.t.orient==TX_UP) angle= 90;
1098
sprintf(line, "] %d %d %d TR", angle, ix, iy);
1100
if (Append(psEngine, line)) return 1;
1105
static int break_line_now(PSEngine *psEngine, char **nw, int *nchs)
1110
psEngine->nchars= *nchs+1;
1111
if (PutLine(psEngine)) return 1;
1113
*nw= psEngine->line;
1117
/* ------------------------------------------------------------------------ */
1119
static int DrawFill(Engine *engine, long n, const GpReal *px,
1122
PSEngine *psEngine= (PSEngine *)engine;
1123
GpXYMap *map= &engine->map;
1124
long maxPoints= 4050, nPoints;
1129
/* For now, only FillSolid style supported */
1132
if (CheckClip(psEngine) || SetupColor(psEngine, gistA.f.color)) return 1;
1135
long nLines= (n-1)/9 + 1; /* 9 points is 72 characters */
1136
if (psEngine->nchars && PutLine(psEngine)) return 1;
1137
sprintf(line, "%%%%BeginData: %ld ASCII Lines", nLines+1);
1138
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
1141
if (gistA.e.type==L_NONE) sprintf(line, "%ld F", n);
1142
else sprintf(line, "%ld E", n);
1143
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
1146
GpIntPoints(map, maxPoints, n, px, py, &points))) {
1147
if (PutPoints(psEngine, points, nPoints, 0)) return 1;
1148
if (n==nPoints) break;
1152
value= 1; /* Polygons with >4050 sides won't be filled correctly */
1154
if (gistA.e.type!=L_NONE) {
1155
/* setup for edge (usually different color than fill), then draw it */
1156
if (SetupLine(psEngine, &gistA.e)) return 1;
1157
if (Append(psEngine, "0 E") || PutLine(psEngine)) return 1;
1161
if (Append(psEngine, "%%EndData") || PutLine(psEngine)) return 1;
1167
/* ------------------------------------------------------------------------ */
1169
static int DrawCells(Engine *engine, GpReal px, GpReal py, GpReal qx,
1170
GpReal qy, long width, long height, long nColumns,
1171
const GpColor *colors)
1173
PSEngine *psEngine= (PSEngine *)engine;
1174
GpXYMap *map= &psEngine->e.map;
1175
int nColors= psEngine->nColors;
1176
GpColorCell *palette;
1177
int ix, iy, idx, idy, depth;
1181
char *now= psEngine->line;
1182
int nc, ncmax, color, colorMode;
1184
if (!psEngine->e.marked && BeginPage(psEngine)) return 1;
1185
if (CheckClip(psEngine)) return 1;
1187
/* Transform corner coordinates, clipping and adjusting width,
1188
height, nColumns, and colors as necessary. */
1189
width = GpClipCells(&map->x, &px, &qx,
1190
gistT.window.xmin, gistT.window.xmax, width, &off);
1191
colors += gistA.rgb? 3*off : off;
1192
height = GpClipCells(&map->y, &py, &qy,
1193
gistT.window.ymin, gistT.window.ymax, height, &off);
1194
colors += gistA.rgb? 3*nColumns*off : nColumns*off;
1196
if (width<=0 || height<=0) return 0;
1202
/* Set bounding box for image if necessary */
1203
if (!psEngine->curClip) {
1204
GpBox *wind= &engine->transform.window;
1205
GpReal xmin, xmax, ymin, ymax;
1206
int xll, yll, xur, yur;
1207
if (wind->xmax>wind->xmin) { xmin= wind->xmin; xmax= wind->xmax; }
1208
else { xmin= wind->xmax; xmax= wind->xmin; }
1209
if (wind->ymax>wind->ymin) { ymin= wind->ymin; ymax= wind->ymax; }
1210
else { ymin= wind->ymax; ymax= wind->ymin; }
1213
if (px>xmin) xmin= px;
1214
if (qx<xmax) xmax= qx;
1216
if (qx>xmin) xmin= qx;
1217
if (px<xmax) xmax= px;
1220
if (py>ymin) ymin= py;
1221
if (qy<ymax) ymax= qy;
1223
if (qy>ymin) ymin= qy;
1224
if (py<ymax) ymax= py;
1231
if (xll<psEngine->pageBB.xll) psEngine->pageBB.xll= xll;
1232
if (xur>psEngine->pageBB.xur) psEngine->pageBB.xur= xur;
1233
if (yll<psEngine->pageBB.yll) psEngine->pageBB.yll= yll;
1234
if (yur>psEngine->pageBB.yur) psEngine->pageBB.yur= yur;
1237
/* Use 4 bit depth if the color table is small, otherwise use
1240
/* Image value will be either */
1241
colorMode= psEngine->colorMode;
1243
palette= 0; /* palette already written */
1244
if (nColors>16) depth= 8;
1247
palette= psEngine->e.palette; /* must lookup gray level now */
1251
/* Must assume image varies over maximum possible range */
1252
colorMode= 1; /* That is, use index without trying palette lookup */
1258
/* Write the 6 arguments to the J procedure */
1259
sprintf(line, "%d %d %d %d %d %d",
1260
(int)width, (int)height, idx, idy, ix, iy);
1262
/* Write the 7 arguments to the I procedure */
1263
sprintf(line, "%d %d %d %d %d %d %d",
1264
(int)width, (int)height, depth, idx, idy, ix, iy);
1266
if (Append(psEngine, line)) return 1;
1269
nLines= 6*width*height;
1270
ncmax = 72; /* 12 cells (72 chars) per line */
1272
nLines= width*height;
1274
if (depth) nLines*= 2;
1275
else if (nLines&1L) nLines++;
1276
ncmax = 76; /* Will put 76 or 38 cells per line */
1278
nLines= 1+(nLines-1)/ncmax;
1280
if (psEngine->nchars && PutLine(psEngine)) return 1;
1281
sprintf(line, "%%%%BeginData: %ld ASCII Lines", nLines+1);
1282
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
1285
if (Append(psEngine, gistA.rgb?"J":"I") || PutLine(psEngine)) return 1;
1289
for (nc=0 ; nc<ncmax ; ) {
1292
if (height<=0) break;
1297
const GpColor *ccell = &colors[3*(i+j)];
1298
now[nc++] = hexChar[ccell[0]>>4];
1299
now[nc++] = hexChar[ccell[0]&0xf];
1300
now[nc++] = hexChar[ccell[1]>>4];
1301
now[nc++] = hexChar[ccell[1]&0xf];
1302
now[nc++] = hexChar[ccell[2]>>4];
1303
now[nc++] = hexChar[ccell[2]&0xf];
1307
if (color>=nColors && nColors>0) color= nColors-1;
1308
if (!colorMode) color= (P_R(palette[color])+
1309
P_G(palette[color])+P_B(palette[color]))/3;
1310
if (depth) { /* 2 hex chars per cell */
1311
now[nc++]= hexChar[color>>4];
1312
now[nc++]= hexChar[color&0xf];
1313
} else { /* 1 hex char per cell */
1314
now[nc++]= hexChar[color];
1319
psEngine->nchars= nc;
1320
if (PutLine(psEngine)) return 1;
1324
if (Append(psEngine, "%%EndData") || PutLine(psEngine)) return 1;
1330
/* ------------------------------------------------------------------------ */
1332
static int DrawDisjoint(Engine *engine, long n, const GpReal *px,
1333
const GpReal *py, const GpReal *qx, const GpReal *qy)
1335
PSEngine *psEngine= (PSEngine *)engine;
1336
GpXYMap *map= &engine->map;
1337
long maxSegs= 2025, nSegs;
1342
if (CheckClip(psEngine)) return 1;
1343
if (n<1 || SetupLine(psEngine, &gistA.l)) return 0;
1344
if (psEngine->curClip) size= 0;
1345
else size= (int)(psEngine->curWidth*0.5*DEFAULT_PS_WIDTH);
1348
long nLines= (2*n-1)/9 + 1; /* 9 points is 72 characters */
1349
if (psEngine->nchars && PutLine(psEngine)) return 1;
1350
sprintf(line, "%%%%BeginData: %ld ASCII Lines", nLines+1);
1351
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
1354
sprintf(line, "%ld D", n);
1355
if (Append(psEngine, line) || PutLine(psEngine)) return 1;
1358
GpIntSegs(map, maxSegs, n, px, py, qx, qy, &segs))) {
1359
/* This cast from GpPoint to GpSegment is lazy programming,
1360
but I don't know of any platforms it will not work on... */
1361
if (PutPoints(psEngine, (GpPoint *)segs, 2*nSegs, size)) return 1;
1362
if (n==nSegs) break;
1371
if (Append(psEngine, "%%EndData") || PutLine(psEngine)) return 1;
1377
/* ------------------------------------------------------------------------ */
1379
Engine *GpPSEngine(char *name, int landscape, int mode, char *file)
1382
long flen= file? strlen(file) : 0;
1383
long engineSize= sizeof(PSEngine)+flen+1;
1384
GpTransform toPixels;
1386
if (flen<=0) return 0;
1388
SetPSTransform(&toPixels, landscape);
1391
(PSEngine *)GpNewEngine(engineSize, name, psType, &toPixels, landscape,
1392
&Kill, &Clear, &Flush, &GpComposeMap,
1393
&ChangePalette, &DrawLines, &DrawMarkers,
1394
&DrwText, &DrawFill, &DrawCells,
1398
strcpy(gistError, "memory manager failed in GpPSEngine");
1402
psEngine->filename= (char *)(psEngine+1);
1403
strcpy(psEngine->filename, file);
1405
psEngine->closed= 0;
1407
SetPageDefaults(psEngine);
1408
psEngine->e.colorMode= mode;
1409
psEngine->colorMode= 0;
1410
psEngine->nColors= 0;
1412
psEngine->landscape= landscape;
1413
psEngine->docBB.xll= psEngine->docBB.xur=
1414
psEngine->docBB.yll= psEngine->docBB.yur= 0;
1415
psEngine->currentPage= 1;
1416
psEngine->fonts|= (1<<T_COURIER);
1418
psEngine->clipColor= psEngine->curColor;
1419
psEngine->clipType= psEngine->curType;
1420
psEngine->clipWidth= psEngine->curWidth;
1421
psEngine->clipFont= psEngine->curFont;
1422
psEngine->clipHeight= psEngine->curHeight;
1424
psEngine->clipBox.xmin= psEngine->clipBox.xmax=
1425
psEngine->clipBox.ymin= psEngine->clipBox.ymax= 0.0;
1427
psEngine->line[0]= '\0';
1428
psEngine->nchars= 0;
1430
return (Engine *)psEngine;
1433
PSEngine *GisPSEngine(Engine *engine)
1435
return (engine && engine->type==psType)? (PSEngine *)engine : 0;
1438
/* ------------------------------------------------------------------------ */