1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        wielemak@science.uva.nl
5     WWW:           http://www.swi-prolog.org/projects/xpce/
6     Copyright (c)  1985-2005, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 #include <h/unix.h>
38 #include <h/text.h>
39 
40 static void	ps_put_string(PceString);
41 static int	postscriptImage(Image, Int, int iscolor);
42 static int	header(Any, Area, BoolObj);
43 static int	footer(void);
44 static status	fill(Any, Name);
45 static void	ps_colour(Colour c, int grey);
46 static status	draw_postscript_image(Image image, Int x, Int y, Name hb);
47 
48 static struct
49 { Colour colour;			/* current colour */
50   struct
51   { Name	family;			/* family of current PostScript font */
52     Int		points;			/* points in current PostScript font */
53   } currentFont;
54 } psstatus;
55 
56 static Chain documentFonts = NIL;	/* chain holding fonts in document */
57 static Chain documentDefs  = NIL;	/* chain holding defs in document */
58 static IOSTREAM *psoutput;		/* PostScript sink */
59 
60 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61 Create a postscript image of a  graphical  and  make  it  fit  into  the
62 specified  are.   If  `ls'  is  ON  the image will be rotated 90 degrees
63 resulting in a landscaped image.
64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
65 
66 StringObj
getPostscriptObject(Any obj,BoolObj ls,Area a)67 getPostscriptObject(Any obj, BoolObj ls, Area a)
68 { StringObj result;
69   char *PostScript = NULL;
70   size_t size = 0;
71 
72   if ( isNil(documentFonts) )
73     documentFonts = globalObject(NAME_DocumentFonts, ClassChain, EAV);
74   else
75     clearChain(documentFonts);
76 
77   if ( isNil(documentDefs) )
78     documentDefs = globalObject(NAME_DocumentDefs, ClassChain, EAV);
79   else
80     clearChain(documentDefs);
81 
82   psstatus.colour             = BLACK_COLOUR;
83   psstatus.currentFont.family = NIL;
84 
85   psoutput = Sopenmem(&PostScript, &size, "w");
86 
87   if ( hasSendMethodObject(obj, NAME_compute) )
88     send(obj, NAME_compute, EAV);
89   if ( !header(obj, a, ls) )
90   { Sclose(psoutput);
91     psoutput = NULL;
92     free(PostScript);
93     fail;
94   }
95   send(obj, NAME_Postscript, NAME_body, EAV);
96   footer();
97 
98   Sclose(psoutput);
99   psoutput = NULL;
100   result = CtoString(PostScript);	/* TBD: avoid duplication! */
101   free(PostScript);
102 
103   answer(result);
104 }
105 
106 
107 __pce_export void *
pcePostScriptStream(void)108 pcePostScriptStream(void)
109 { return psoutput;
110 }
111 
112 
113 void
ps_put_char(int c)114 ps_put_char(int c)
115 { Sputc(c, psoutput);
116 }
117 
118 
119 static void
putString(const char * s)120 putString(const char *s)
121 { Sfputs(s, psoutput);
122 }
123 
124 
125 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126 Output() is a special version of  printf.   The  following  options  are
127 recognised:
128 
129   Option   Argument	Description
130     ~~        -		Output a tilde
131     ~s	    char *	Output literal string
132     ~a	    PceString	Output PceString as PostScript string
133     ~c	    Point	Output <-x and <-y of the point
134     ~d	    Int		Output an integer
135     ~D	    int		Output an integer
136     ~f	    float	Output a float (passed as integer * 100)
137     ~m	    Graphical	Move to XY of graphical
138     ~t	    Figure	Translate to XY of figure
139     ~T	    Graphical	Set texture to texture of graphical
140     ~C	    Graphical	Output colour of the graphical
141     ~N	    Name	print text of name
142     ~S	    StringObj	Output text of StringObj with postscript escapes
143     ~O	    Object	Output comment to start O
144     ~P	    Int, Image	Output pattern of grayscale image with depth Int
145     ~I	    Int, Image  Output pattern of colour image with depth Int
146     ~p	    Graphical   Output pen of graphical
147     ~x	    Graphical	Output X of graphical
148     ~y	    Graphical	Output Y of graphical
149     ~w	    Graphical	Output W of graphical
150     ~h	    Graphical	Output H of graphical
151 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
152 
153 static void
_output(char * fm,va_list args)154 _output(char *fm, va_list args)
155 { char tmp[LINESIZE];
156 
157   for( ;*fm ; fm++)
158   { switch(*fm)
159     { case '\\':
160 	switch(*++fm)				/* \ escapes */
161 	{ case 'n':	ps_put_char('\n');
162 			continue;
163 	  case '\\':	ps_put_char('\\');
164 			continue;
165 	  case '\0':	ps_put_char('\\');
166 			return;
167 	}
168       case '~':
169 	switch(*++fm)				/* ~ escapes */
170 	{ case '~':	ps_put_char('~');
171 			continue;
172 	  case '\0':	ps_put_char('~');
173 			return;
174 	  case 's':	putString(va_arg(args, char *));
175 			continue;
176 	  case 'd':	sprintf(tmp, INTPTR_FORMAT, valInt(va_arg(args, Int)));
177 			putString(tmp);
178 			continue;
179 	  case 'D':	sprintf(tmp, "%d", va_arg(args, int));
180 			putString(tmp);
181 			continue;
182 	  case 'f':   { char *s;
183 
184 			sprintf(tmp, "%.2f", va_arg(args, double));
185 			for(s=tmp; *s; s++)
186 			  if ( *s != '-' && !isalnum(*s) )
187 			    *s = '.';
188 			putString(tmp);
189 			continue;
190 		      }
191 	  case 'm':   { Graphical gr = va_arg(args, Graphical);
192 			sprintf(tmp, INTPTR_FORMAT " " INTPTR_FORMAT " moveto",
193 				valInt(gr->area->x),
194 				valInt(gr->area->y));
195 			putString(tmp);
196 			continue;
197 		      }
198 	  case 'c':   { Point p = va_arg(args, Point);
199 			sprintf(tmp, INTPTR_FORMAT " " INTPTR_FORMAT,
200 				valInt(p->x), valInt(p->y));
201 			putString(tmp);
202 			continue;
203 		      }
204 	  case 't':   { Figure f = va_arg(args, Figure);
205 			sprintf(tmp, INTPTR_FORMAT " " INTPTR_FORMAT " translate",
206 				valInt(f->offset->x), valInt(f->offset->y));
207 			putString(tmp);
208 			continue;
209 		      }
210 	  case 'p':   { Graphical gr = va_arg(args, Graphical);
211 			sprintf(tmp, INTPTR_FORMAT, valInt(gr->pen));
212 			putString(tmp);
213 			continue;
214 		      }
215 
216 	  case 'T':   { Name texture = get(va_arg(args, Any), NAME_texture, EAV);
217 
218 			if ( texture == NAME_none )
219 			{ putString("nodash");
220 			} else if ( instanceOfObject(texture, ClassCharArray) )
221 			{ CharArray ca = (CharArray) texture;
222 			  ca = getDowncaseCharArray(ca);
223 
224 			  putString(strName(ca));
225 			}
226 
227 			continue;
228 		      }
229 	  case 'C':   { Graphical gr = va_arg(args, Graphical);
230 			Colour c = get(gr, NAME_colour, EAV);
231 
232 			ps_colour(c, 100);
233 
234 			continue;
235 		      }
236 	  case 'a':	ps_put_string(va_arg(args, PceString));
237 			continue;
238 	  case 'N':	putString(strName(va_arg(args, Name)));
239 			continue;
240 	  case 'S':   { StringObj s = va_arg(args, StringObj);
241 			ps_put_string(&s->data);
242 			continue;
243 		      }
244 	  case 'O':   { Any obj = va_arg(args, Any);
245 			char *s = pp(obj);
246 
247 			putString(s);
248 
249 			continue;
250 		      }
251 	  case 'P':   { Int depth = va_arg(args, Int);
252 			Image image = va_arg(args, Image);
253 
254 			postscriptImage(image, depth, FALSE);
255 			continue;
256 		      }
257 	  case 'I':   { Int depth = va_arg(args, Int);
258 			Image image = va_arg(args, Image);
259 
260 			postscriptImage(image, depth, TRUE);
261 			continue;
262 		      }
263 	  case 'x':   { Graphical gr = va_arg(args, Graphical);
264 
265 			sprintf(tmp, INTPTR_FORMAT, valInt(gr->area->x));
266 			putString(tmp);
267 			continue;
268 		      }
269 	  case 'y':   { Graphical gr = va_arg(args, Graphical);
270 
271 			sprintf(tmp, INTPTR_FORMAT, valInt(gr->area->y));
272 			putString(tmp);
273 			continue;
274 		      }
275 	  case 'w':   { Graphical gr = va_arg(args, Graphical);
276 
277 			sprintf(tmp, INTPTR_FORMAT, valInt(gr->area->w));
278 			putString(tmp);
279 			continue;
280 		      }
281 	  case 'h':   { Graphical gr = va_arg(args, Graphical);
282 
283 			sprintf(tmp, INTPTR_FORMAT, valInt(gr->area->h));
284 			putString(tmp);
285 			continue;
286 		      }
287 	  default:    { errorPce(NIL, NAME_unknownEscape,
288 				 CtoName("~"), toInt(*fm));
289 			ps_put_char('~'), ps_put_char(*fm);
290 			continue;
291 		      }
292 	}
293       default:
294       { ps_put_char(*fm);
295 	continue;
296       }
297     }
298   }
299 }
300 
301 
302 void
ps_output(char * fm,...)303 ps_output(char *fm, ...)
304 { va_list args;
305 
306   va_start(args, fm);
307   _output(fm, args);
308   va_end(args);
309 }
310 
311 
312 static void
ps_put_string(PceString s)313 ps_put_string(PceString s)
314 { wint_t c;
315   int i, size = s->s_size;
316 
317   ps_put_char('(');
318 
319   for(i=0; i<size; i++)
320   { switch( (c=str_fetch(s, i)) )
321     { case '\b':	putString("\\b");	break;
322       case '\t':	putString("\\t");	break;
323       case '\n':	putString("\\n");	break;
324       case '\r':	putString("\\r");	break;
325       case '\\':	putString("\\\\");	break;
326       case '(':		putString("\\(");	break;
327       case ')':		putString("\\)");	break;
328       default:		if ( c >= ' ' && c <= '~' )
329 			{ ps_put_char(c);
330 			} else
331 			{ char tmp[8];
332 			  sprintf(tmp, "\\%03o", (unsigned int)c);
333 			  putString(tmp);
334 			}
335     }
336   }
337 
338   ps_put_char(')');
339 }
340 
341 
342 status
ps_font(FontObj font)343 ps_font(FontObj font)
344 { Name family = (Name) get(font, NAME_postscriptFont, EAV);
345   Int  points = (Int)  get(font, NAME_postscriptSize, EAV);
346 
347   if ( !family ) family = CtoName("Courier");
348   if ( !points ) points = font->points;
349 
350   if ( psstatus.currentFont.family == family &&
351        psstatus.currentFont.points == points )
352     succeed;
353 
354   if ( memberChain(documentFonts, family) )
355     appendChain(documentFonts, family);
356 
357   ps_output("/~N findfont ~d scalefont setfont\n", family, points);
358 
359   succeed;
360 }
361 
362 
363 static void
ps_colour(Colour c,int grey)364 ps_colour(Colour c, int grey)
365 { if ( notDefault(c) && notNil(c) )
366   { float r = (float) valInt(getRedColour(c))   / (float) valInt(BRIGHT);
367     float g = (float) valInt(getGreenColour(c)) / (float) valInt(BRIGHT);
368     float b = (float) valInt(getBlueColour(c))  / (float) valInt(BRIGHT);
369 
370     if ( grey != 100 )
371     { r = 1 - (1-r) * grey / 100.0;
372       g = 1 - (1-g) * grey / 100.0;
373       b = 1 - (1-b) * grey / 100.0;
374     }
375 
376     ps_output("~f ~f ~f setrgbcolor ", r, g, b);
377   }
378 }
379 
380 
381 static Int
getPostScriptGreyPattern(Any pattern)382 getPostScriptGreyPattern(Any pattern)
383 { Int rval;
384 
385   if ( hasGetMethodObject(pattern, NAME_postscriptGrey) &&
386        (rval = get(pattern, NAME_postscriptGrey, EAV)) &&
387        (rval = toInteger(rval)) &&
388        valInt(rval) >= 0 &&
389        valInt(rval) <= 100 )
390     return rval;
391 
392   fail;
393 }
394 
395 
396 static status
fill(Any gr,Name sel)397 fill(Any gr, Name sel)
398 { Image pattern = get(gr, sel, EAV);
399   Int greyLevel;
400 
401   if ( instanceOfObject(pattern, ClassColour) )
402   { Colour c = (Colour) pattern;
403 
404     ps_output("gsave ");
405     ps_colour(c, 100);
406     ps_output(" fill grestore\n");
407   } else if ( instanceOfObject(pattern, ClassImage) )
408   { if ( (greyLevel = getPostScriptGreyPattern(pattern)) )
409     { Colour c = get(gr, NAME_displayColour, EAV);
410 
411       if ( c )
412       { ps_output("gsave ");
413 	ps_colour(c, valInt(greyLevel));
414 	ps_output(" fill grestore\n");
415       } else
416       { ps_output("gsave ~f setgray fill grestore\n",
417 		  (float) (100 - valInt(greyLevel)) / 100.0 );
418       }
419     } else
420     { ps_output("~x ~y ~w ~h ~d ~d \n<~P>\nfillwithmask\n",
421 		gr, gr, gr, gr,
422 		pattern->size->w, pattern->size->h, ONE, pattern);
423     }
424   }
425 
426   succeed;
427 }
428 
429 
430 #define putByte(b) { ps_put_char(print[(b >> 4) & 0xf]); \
431 		     ps_put_char(print[b & 0xf]); \
432 		     if ( (++bytes % 32) == 0 ) ps_put_char('\n'); \
433 		     bits = 8; c = 0; \
434 		   }
435 
436 
437 
438 status
postscriptDrawable(int ox,int oy,int w,int h,int depth,int iscolor)439 postscriptDrawable(int ox, int oy, int w, int h, int depth, int iscolor)
440 { static char print[] = {'0', '1', '2', '3', '4', '5', '6', '7',
441 			 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
442   int x, y;
443   int bits, bytes;
444   int c;
445 
446   DEBUG(NAME_postscript,
447 	Cprintf("postscriptDrawable(%d %d %d %d) ...", ox, oy, w, h));
448 
449   for(bytes = y = c = 0, bits = 8; y < h; y++)
450   { for(x=0; x < w; x++)
451     { c |= ((TRUE-(r_get_mono_pixel(x+ox, y+oy))) << --bits);
452       if ( bits == 0 )
453         putByte(c);
454     }
455     if ( bits != 8 )
456       putByte(c);
457   }
458 
459   DEBUG(NAME_postscript, Cprintf("ok\n"));
460 
461   succeed;
462 }
463 
464 
465 static int
postscriptImage(Image image,Int depth,int iscolor)466 postscriptImage(Image image, Int depth, int iscolor)
467 { ws_postscript_image(image, depth, iscolor);
468 
469   succeed;
470 }
471 
472 
473 		/********************************
474 		*        HEADER & FOOTER        *
475 		*********************************/
476 
477 static int
header(Any gr,Area area,BoolObj ls)478 header(Any gr, Area area, BoolObj ls)
479 { int x, y, w, h;
480   int xgr, ygr, wgr, hgr;
481   int paperH, paperW;
482   float scale;
483 
484   if ( isDefault(ls) )
485     ls = OFF;
486 
487   if ( isDefault(area) )
488   { x = 70, y = 70, w = 500, h = 700;
489   } else
490   { x = valInt(area->x);
491     y = valInt(area->y);
492     w = valInt(area->w);
493     h = valInt(area->h);
494   }
495 
496   ps_output("%!PS-Adobe-3.0 EPSF-3.0\n");
497   ps_output("%%Creator: PCE ~N\n", get(PCE, NAME_version, EAV));
498   ps_output("%%CreationDate: ~S\n", get(PCE, NAME_date, EAV));
499   ps_output("%%Pages: 1\n");
500   ps_output("%%DocumentFonts: (atend)\n");
501 
502   { Area bb = get(gr, NAME_boundingBox, EAV);
503 
504 					/* Hack */
505     if ( instanceOfObject(gr, ClassFrame) )
506     { assign(bb, x, ZERO);
507       assign(bb, y, ZERO);
508     }
509 
510     xgr = valInt(bb->x);
511     ygr = valInt(bb->y);
512     wgr = valInt(bb->w);
513     hgr = valInt(bb->h);
514   }
515 
516   if ( ls == ON )
517     paperH = wgr, paperW = hgr;
518   else
519     paperW = wgr, paperH = hgr;
520 
521   if ( wgr <= w && hgr <= h )
522   { scale = 1.0;
523   } else
524   { scale = min( (float)w / (float)wgr, (float)h/(float)hgr );
525     paperW = (int)((float)paperW * scale + 0.999);
526     paperH = (int)((float)paperH * scale + 0.999);
527   }
528 
529   if ( ls == ON )
530   { ps_output("%%BoundingBox: ~D ~D ~D ~D\n", x+w-paperW, y, x+w, y+paperH);
531   } else
532   { ps_output("%%BoundingBox: ~D ~D ~D ~D\n", x, y, x+paperW, y+paperH);
533   }
534 
535   ps_output("%%Object: ~O\n", gr);
536   ps_output("%%EndComments\n\n");
537 
538   TRY(send(gr, NAME_Postscript, NAME_head, EAV));
539 
540   ps_output("gsave\n\n");
541 
542   if ( ls == ON )
543     ps_output("~D ~D translate 90 rotate\n", x + w, y);
544   else
545     ps_output("~D ~D translate\n", x, y);
546 
547   ps_output("~f ~f scale\n", scale, -scale);
548   ps_output("~D ~D translate\n", -xgr, -hgr - ygr);
549   ps_output("%%EndProlog\n");
550   ps_output("%%Page 0 1\n");
551 
552   succeed;
553 }
554 
555 
556 static int
footer(void)557 footer(void)
558 { Cell cell;
559 
560   ps_output("\n%%Trailer\n");
561   ps_output("grestore\n");
562 /*  ps_output("/pce restore\n"); */
563   ps_output("%%DocumentFonts:");
564 
565   for_cell(cell, documentFonts)
566     ps_output(" ~N", cell->value);
567 
568   ps_output("\n");
569   succeed;
570 }
571 
572 		 /*******************************
573 		 *	      MACROS		*
574 		 *******************************/
575 
576 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
577 Definition of PostScript macros used by the real code. First is the name
578 of the Macro, followed by the   definion,  and finally a comma-separated
579 list of required definitions.
580 
581 A two pass process through  the  actual   objects  for  which  to create
582 PostScript will first generate the  required   header,  followed  by the
583 actual object.
584 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
585 
586 #define PSMACRO(a, b, r) { a, b, r }
587 
588 typedef struct
589 { Name		name;
590   const char   *def;
591   const char   *required;
592 } psmacro;
593 
594 static psmacro macrodefs[] =
595 { PSMACRO(NAME_dotted,
596 	  "\t{ [1 5] 0 setdash\n"
597 	  "\t} def",
598 	  NULL),
599   PSMACRO(NAME_dashed,
600 	  "\t{ [5] 0 setdash\n"
601 	  "\t} def",
602 	  NULL),
603   PSMACRO(NAME_dashdot,
604 	  "\t{ [5 2 1 2] 0 setdash\n"
605 	  "\t} def",
606 	  NULL),
607   PSMACRO(NAME_dashdotted,
608 	  "\t{ [5 2 1 2 1 2 1 2] 0 setdash\n"
609 	  "\t} def",
610 	  NULL),
611   PSMACRO(NAME_longdash,
612 	  "\t{ [8 5] 0 setdash\n"
613 	  "\t} def",
614 	  NULL),
615   PSMACRO(NAME_nodash,
616 	  "\t{ [] 0 setdash\n"
617 	  "\t} def",
618 	  NULL),
619   PSMACRO(NAME_pspen,
620 	  "\t{ 2 div\n"
621 	  "\t} def",
622 	  NULL),
623   PSMACRO(NAME_pen,
624 	  "\t{ pspen\n"
625 	  "\t  setlinewidth\n"
626 	  "\t} def",
627 	  "pspen"),
628 
629 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
630 %	Draw a path according to the current  texture  and  pen.   Draws
631 %	only  if  the  pen  is not 0.  If a dash pattern is provided the
632 %	path is first cleared.
633 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
634 
635   PSMACRO(NAME_draw,
636 	  "\t{ 0 currentlinewidth ne\n"
637 	  "\t  { currentdash 0 eq exch [] eq and not\n"
638 	  "\t    { gsave nodash 1 setgray stroke grestore\n"
639 	  "\t    } if\n"
640 	  "\t    stroke\n"
641 	  "\t  } if\n"
642 	  "\t} def",
643 	  NULL),
644 
645 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
646 % Create a rectangular path from x, y, w, h in the current dict
647 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
648 
649   PSMACRO(NAME_region,
650 	  "\t{ newpath\n"
651 	  "\t    x y moveto\n"
652 	  "\t    w 0 rlineto\n"
653 	  "\t    0 h rlineto\n"
654 	  "\t    w neg 0 rlineto\n"
655 	  "\t  closepath\n"
656 	  "\t} def",
657 	  NULL),
658 
659 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
660 %	Paint inverted
661 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
662 
663   PSMACRO(NAME_invert,
664 	  "\t{ {1 exch sub} settransfer\n"
665 	  "\t} def",
666 	  NULL),
667 
668 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
669 %	Create a path for a PCE box without rounded corners. Stack:
670 %
671 %	pen x y w h radius BOXPATH path
672 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
673 
674   PSMACRO(NAME_boxpath,
675 	  "\t{ /r exch def\n"
676 	  "\t  /h exch def\n"
677 	  "\t  /w exch def\n"
678 	  "\t  /y exch def\n"
679 	  "\t  /x exch def\n"
680 	  "\t  /p exch def\n"
681 	  "\t  \n"
682 	  "\t  r 0 eq\n"
683 	  "\t  { 2 setlinecap newpath\n"
684 	  "\t    x p pspen add y p pspen add moveto\n"
685 	  "\t    w p sub 0 rlineto\n"
686 	  "\t    0 h p sub rlineto\n"
687 	  "\t    w p sub neg 0 rlineto\n"
688 	  "\t    0 h p sub neg rlineto\n"
689 	  "\t  }\n"
690 	  "\t  { newpath\n"
691 	  "\t    /pr r p pspen add def\n"
692 	  "\t    x pr add y p pspen add moveto\n"
693 	  "\t    x w add pr sub	y pr add	r 270 360 arc\n"
694 	  "\t    x w add pr sub	y h add pr sub	r 0 90 arc\n"
695 	  "\t    x pr add		y h add pr sub  r 90 180 arc\n"
696 	  "\t    x pr add		y pr add	r 180 270 arc\n"
697 	  "\t  } ifelse\n"
698 	  "\t  \n"
699 	  "\t  p pen\n"
700 	  "\t} def",
701 	  "pen,pspen"),
702 
703 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
704 %	Create a path for a PCE circle.
705 %	pen x y radius CIRCLEPATH
706 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
707 
708   PSMACRO(NAME_circlepath,
709 	  "\t{ /r exch def\n"
710 	  "\t  /y exch def\n"
711 	  "\t  /x exch def\n"
712 	  "\t  /p exch def\n"
713 
714 	  "\t  newpath\n"
715 	  "\t  x r add y r add r p pspen sub 0 360 arc\n"
716 	  "\t  \n"
717 	  "\t  p pen\n"
718 	  "\t} def",
719 	  "pen,pspen"),
720 
721 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
722 %	Create a path for a PCE ellipse
723 %	pen x y w h ELLIPSEPATH
724 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
725 
726   PSMACRO(NAME_ellipsepath,
727 	  "\t{ /h exch 2 div def\n"
728 	  "\t  /w exch 2 div def\n"
729 	  "\t  /y exch def\n"
730 	  "\t  /x exch def\n"
731 	  "\t  /p exch def\n"
732 
733 	  "\t  matrix currentmatrix\n"
734 	  "\t  x w add y h add translate\n"
735 	  "\t  w p pspen sub h p pspen sub scale\n"
736 	  "\t  newpath\n"
737 	  "\t  0 0 1 0 360 arc\n"
738 	  "\t  setmatrix\n"
739 
740 	  "\t  p pen\n"
741 	  "\t} def",
742 	  "pen,pspen"),
743 
744 
745 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
746 %	pen close x y w h start size ARCPATH
747 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
748 
749   PSMACRO(NAME_arcpath,
750 	  "\t{ /size exch neg def\n"
751 	  "\t  /start exch neg 360 add def\n"
752 	  "\t  /h exch def\n"
753 	  "\t  /w exch def\n"
754 	  "\t  /y exch def\n"
755 	  "\t  /x exch def\n"
756 	  "\t  /close exch def\n"
757 	  "\t  /p exch def\n"
758 
759 	  "\t  matrix currentmatrix\n"
760 	  "\t  x y translate\n"
761 	  "\t  w p pspen sub h p pspen sub scale\n"
762 	  "\t  newpath\n"
763 
764 	  "\t  close 2 eq { 0 0 moveto } if\n"
765 	  "\t  size 0 le\n"
766 	  "\t  { 0 0 1 start start size add arcn\n"
767 	  "\t  }\n"
768 	  "\t  { 0 0 1 start start size add arc\n"
769 	  "\t  } ifelse\n"
770 	  "\t  close 0 ne { closepath } if\n"
771 	  "\t  setmatrix\n"
772 
773 	  "\t  p pen\n"
774 	  "\t} def",
775 	  "pen,pspen"),
776 
777 
778 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
779 %	pen x y STARTPATH
780 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
781 
782   PSMACRO(NAME_startpath,
783 	  "\t{ newpath moveto pen\n"
784 	  "\t} def",
785 	  "pen"),
786 
787 
788 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
789 %	Linepath: create a path for a pce line
790 %	pen x y w h LINEPATH
791 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
792 
793   PSMACRO(NAME_linepath,
794 	  "\t{ /h exch def\n"
795 	  "\t  /w exch def\n"
796 
797 	  "\t  newpath moveto w h rlineto\n"
798 	  "\t  pen\n"
799 	  "\t} def",
800 	  "pen"),
801 
802 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
803 %	drawline: draw a line using current pen/texture
804 %	x1 y1 x2 y2 DRAWLINE
805 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
806 
807   PSMACRO(NAME_drawline,
808 	  "\t{ /y2 exch def\n"
809 	  "\t  /x2 exch def\n"
810 
811 	  "\t  newpath moveto x2 y2 lineto\n"
812 	  "\t  draw\n"
813 	  "\t} def",
814 	  "draw"),
815 
816 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
817 %	x y w h CLEAR
818 %	Clear a region
819 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
820 
821   PSMACRO(NAME_clear,
822 	  "\t{ /h exch def\n"
823 	  "\t  /w exch def\n"
824 	  "\t  /y exch def\n"
825 	  "\t  /x exch def\n"
826 
827 	  "\t  gsave\n"
828 	  "\t    region 1 setgray fill\n"
829 	  "\t  grestore\n"
830 	  "\t} def",
831 	  "region"),
832 
833 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
834 %	text. (x,y) is the xy coordinate of the baselinestart of the first
835 %	character. w is the width to which the text is scaled.
836 %	x y <invert> w string TEXT
837 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
838 
839   PSMACRO(NAME_text,
840 	  "\t{ /s exch def\n"
841 	  "\t  /w exch def\n"
842 	  "\t  \n"
843 	  "\t  gsave\n"
844 	  "\t    1 eq { invert } if\n"
845 	  "\t    moveto s stringwidth pop w exch div -1 scale s show\n"
846 	  "\t  grestore\n"
847 	  "\t} def",
848 	  NULL),
849 
850 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
851 %	x y w h BITMAP hexdata
852 
853 Draw a transparent image.
854 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
855 
856   PSMACRO(NAME_bitmap,
857 	  "\t{ /h exch def\n"
858 	  "\t  /w exch def\n"
859 	  "\t  /y exch def\n"
860 	  "\t  /x exch def\n"
861 
862 	  "\t  gsave\n"
863 /*	  "\t  {1 exch sub} settransfer\n" */
864 /*	  "  region clip" */
865 	  "\t  x y h add translate\n"
866 	  "\t  /w8 w 7 add 8 idiv 8 mul def\n"
867 	  "\t  /picstr w8 8 idiv string def\n"
868 	  "\t  w8 h neg scale\n"
869 	  "\t  w8 h false [w 0 0 h neg 0 h]\n"
870 	  "\t  {currentfile picstr readhexstring pop} imagemask\n"
871 	  "\t  grestore\n"
872 	  "\t} def",
873 	  NULL),
874 
875 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
876 %	x y w h depth GREYMAP hexdata
877 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
878 
879   PSMACRO(NAME_greymap,
880 	  "\t{ /d exch def\n"
881 	  "\t  /h exch def\n"
882 	  "\t  /w exch def\n"
883 	  "\t  /y exch def\n"
884 	  "\t  /x exch def\n"
885 
886 	  "\t  gsave\n"
887 	  "  region clip"
888 	  "\t  x y h add translate\n"
889 	  "\t  /w8 w 7 add 8 idiv 8 mul def\n"
890 	  "\t  /picstr w8 8 idiv string def\n"
891 	  "\t  w h neg scale\n"
892 	  "\t  w8 h d [w 0 0 h neg 0 h]\n"
893 	  "\t  {currentfile picstr readhexstring pop} image\n"
894 	  "\t  grestore\n"
895 	  "\t} def",
896 	  "region"),
897 
898 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
899 %	x y w h depth RGBMAP hexdata
900 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
901 
902   PSMACRO(NAME_rgbimage,
903 	  "\t{ /d exch def\n"
904 	  "\t  /h exch def\n"
905 	  "\t  /w exch def\n"
906 	  "\t  /y exch def\n"
907 	  "\t  /x exch def\n"
908 
909 	  "\t  gsave\n"
910 	  "  region clip"
911 	  "\t  x y h add translate\n"
912 	  "\t  /w8 w 7 add 8 idiv 8 mul def\n"
913 	  "\t  /picstr w8 8 idiv string def\n"
914 	  "\t  w h neg scale\n"
915 	  "\t  w8 h d [w 0 0 h neg 0 h]\n"
916 	  "\t  {currentfile picstr readhexstring pop} false 3 colorimage\n"
917 	  "\t  grestore\n"
918 	  "\t} def",
919 	  "region"),
920 
921 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
922 %	x y w h pw ph pattern FILLWITHMASK
923 %	Fill path with a repetitive pattern <pattern> which is sized (pw, ph).
924 %	<x,y,w,h> discribe an area that encapsulates the path. <pattern> is a
925 %	1-deep image.  1's of the image are painted in the current colour.
926 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
927 
928   PSMACRO(NAME_fillwithmask,
929 	  "\t{ /patternstring exch def\n"
930 	  "\t  /ph exch def\n"
931 	  "\t  /pw exch def\n"
932 	  "\t  /h exch def\n"
933 	  "\t  /w exch def\n"
934 	  "\t  /y exch def\n"
935 	  "\t  /x exch def\n"
936 	  "\t  \n"
937 	  "\t  /pw8 pw 7 add 8 idiv 8 mul def\n"
938 	  "\t  \n"
939 	  "\t  /putpattern\n"
940 	  "\t  { translate 1 -1 scale\n"
941 	  "\t    newpath 0 0 moveto 1 0 rlineto 0 1 rlineto -1 0 rlineto closepath\n"
942 	  "\t    clip\n"
943 	  "\t    pw8 ph false [pw 0 0 ph neg 0 ph] patternstring imagemask\n"
944 	  "\t  } def\n"
945 
946 	  "\t  gsave\n"
947 	  "\t  clip\n"
948 	  "\t  gsave 1 setgray fill grestore\n"
949 	  "\t  x y translate pw ph scale\n"
950 
951 	  "\t  0 1 w pw div ceiling 1 sub\n"
952 	  "\t  { 1 1 h ph div ceiling\n"
953 	  "\t    { gsave 2 copy putpattern pop grestore\n"
954 	  "\t    } for\n"
955 	  "\t  } for\n"
956 	  "\t  grestore\n"
957 	  "\t} def",
958 	  NULL),
959 
960   PSMACRO(NAME_, NULL, NULL)		/* closes the list */
961 };
962 
963 
964 Sheet
makePSDefinitions()965 makePSDefinitions()
966 { Sheet sh = globalObject(NAME_postscriptDefs, ClassSheet, EAV);
967   psmacro *m;
968 
969   for(m=macrodefs; m->def; m++)
970     send(sh, NAME_value, m->name, CtoString(m->def), EAV);
971 
972   return sh;
973 }
974 
975 
976 static void
psdef(Name macro)977 psdef(Name macro)
978 { if ( !memberChain(documentDefs, macro) )
979   { Sheet psdefs = findGlobal(NAME_postscriptDefs);
980     StringObj def;
981     psmacro *m;
982 
983     if ( !psdefs )
984       psdefs = makePSDefinitions();
985 
986     for(m=macrodefs; m->def; m++)
987     { if ( m->name == macro )
988       { if ( m->required )
989 	{ const char *r = m->required;
990 	  const char *q;
991 	  char name[100];
992 
993 	  while((q = strchr(r, ',')))
994 	  { strncpy(name, r, q-r);
995 	    name[q-r] = EOS;
996 	    psdef(CtoName(name));
997 	    r = q+1;
998 	  }
999 	  if ( r[0] )
1000 	  { psdef(CtoName(r));
1001 	  }
1002 	}
1003 
1004 	break;
1005       }
1006     }
1007 
1008     if ( psdefs && (def = getValueSheet(psdefs, macro)) )
1009     { ps_output("/~s\n~s\n\n", strName(macro), strName(def));
1010       appendChain(documentDefs, macro);
1011     }
1012   }
1013 }
1014 
1015 
1016 static void
psdef_texture(Any obj)1017 psdef_texture(Any obj)
1018 { Name texture = get(obj, NAME_texture, EAV);
1019 
1020   if ( texture == NAME_none )
1021     psdef(NAME_nodash);
1022   else
1023     psdef(texture);
1024 }
1025 
1026 
1027 static void
psdef_fill(Any gr,Name sel)1028 psdef_fill(Any gr, Name sel)
1029 { Any pattern = get(gr, sel, EAV);
1030 
1031   if ( instanceOfObject(pattern, ClassImage) &&
1032        !getPostScriptGreyPattern(pattern) )
1033     psdef(NAME_fillwithmask);
1034 }
1035 
1036 
1037 static void
psdef_arrows(Any obj)1038 psdef_arrows(Any obj)
1039 { Joint j = obj;
1040 
1041   if ( notNil(j->first_arrow) )
1042     postscriptGraphical(j->first_arrow, NAME_head);
1043   if ( notNil(j->second_arrow) )
1044     postscriptGraphical(j->second_arrow, NAME_head);
1045 }
1046 
1047 
1048 		/********************************
1049 		*          POSTSCRIPT           *
1050 		*********************************/
1051 
1052 status
postscriptGraphical(Any obj,Name hb)1053 postscriptGraphical(Any obj, Name hb)
1054 { if ( hb == NAME_body )
1055     ps_output("\n%%Object: ~O\n", obj);
1056 
1057   return send(obj, NAME_DrawPostScript, hb, EAV);
1058 }
1059 
1060 
1061 status
drawPostScriptDevice(Device dev,Name hb)1062 drawPostScriptDevice(Device dev, Name hb)
1063 { Cell cell;
1064 
1065   if ( hb == NAME_body )
1066     ps_output("gsave ~t ~C\n", dev, dev);
1067 
1068   for_cell(cell, dev->graphicals)
1069   { Graphical gr = cell->value;
1070 
1071     if ( gr->displayed == ON )
1072       send(gr, NAME_Postscript, hb, EAV);
1073   }
1074 
1075   if ( hb == NAME_body )
1076     ps_output("grestore\n");
1077 
1078   succeed;
1079 }
1080 
1081 
1082 status
drawPostScriptFigure(Figure f,Name hb)1083 drawPostScriptFigure(Figure f, Name hb)
1084 { if ( f->pen != ZERO || notNil(f->background) )
1085   { if ( hb == NAME_head )
1086     { psdef(NAME_boxpath);
1087       psdef(NAME_draw);
1088       psdef_texture(f);
1089       psdef_fill(f, NAME_background);
1090     } else
1091     { ps_output("gsave ~C ~T ~p ~x ~y ~w ~h ~d boxpath\n",
1092 		f, f, f, f, f, f, f, f->radius);
1093       fill(f, NAME_background);
1094       ps_output("draw grestore\n");
1095     }
1096   }
1097 
1098   return drawPostScriptDevice((Device) f, hb);
1099 }
1100 
1101 
1102 static void
ps_line(int x1,int y1,int x2,int y2)1103 ps_line(int x1, int y1, int x2, int y2)
1104 { ps_output("~D ~D ~D ~D drawline\n", x1, y1, x2, y2);
1105 }
1106 
1107 
1108 static void
ps_image(Image img,int sx,int sy,int x,int y,int w,int h,BoolObj transparent,Name hb)1109 ps_image(Image img,
1110 	 int sx, int sy,
1111 	 int x, int y, int w, int h,
1112 	 BoolObj transparent, Name hb)
1113 { if ( sx || sy )
1114     Cprintf("ps_image(): sx/sy parameters currently ignored\n");
1115 
1116   if ( hb == NAME_head )
1117   { psdef(NAME_greymap);
1118   } else
1119   { Int depth = get(img, NAME_postscriptDepth, EAV);
1120 
1121     ps_output("~D ~D ~D ~D ~d greymap\n~P\n",
1122 	      x, y, w, h, depth, depth, img);
1123   }
1124 }
1125 
1126 static void
drawPostScriptNode(Node node,Image cimg,Image eimg)1127 drawPostScriptNode(Node node, Image cimg, Image eimg)
1128 { Graphical img = node->image;
1129   Tree t = node->tree;
1130   int lg = valInt(t->levelGap)/2;
1131   Node lastnode;
1132   int ly = valInt(img->area->y) + valInt(img->area->h)/2;
1133   int lx = valInt(img->area->x);
1134   Image i = NULL;
1135 
1136   if ( node->collapsed == OFF && eimg )
1137     i = eimg;
1138   else if ( node->collapsed == ON && cimg )
1139     i = cimg;
1140 
1141   if ( i || node != t->displayRoot )
1142     ps_line(lx-lg, ly, lx, ly);	/* line to parent */
1143 
1144   if ( i )
1145   { int iw = valInt(i->size->w);
1146     int ih = valInt(i->size->h);
1147 
1148     ps_image(i, 0, 0, lx-lg-(iw+1)/2, ly-(ih+1)/2, iw, ih, OFF, NAME_body);
1149   }
1150 
1151   if ( notNil(node->sons) && node->collapsed != ON &&
1152        (lastnode = getTailChain(node->sons)) )	/* I have sons */
1153   { int fy	   = valInt(getBottomSideGraphical(img));
1154     Graphical last = lastnode->image;
1155     int	ty	   = valInt(last->area->y) + valInt(last->area->h)/2;
1156     int lx	   = valInt(img->area->x) + lg;
1157     Cell cell;
1158 
1159     ps_line(lx, fy, lx, ty);
1160 
1161     for_cell(cell, node->sons)
1162       drawPostScriptNode(cell->value, cimg, eimg);
1163   }
1164 }
1165 
1166 
1167 status
drawPostScriptTree(Tree tree,Name hb)1168 drawPostScriptTree(Tree tree, Name hb)
1169 { if ( tree->direction == NAME_list && notNil(tree->displayRoot) )
1170   { Line proto = tree->link->line;
1171 
1172     if ( hb == NAME_head )
1173     { psdef(NAME_greymap);
1174       psdef(NAME_drawline);
1175       psdef_texture(proto);
1176       psdef(NAME_pen);
1177     } else
1178     { if ( proto->pen != ZERO )
1179       { Image cimg = getClassVariableValueObject(tree, NAME_collapsedImage);
1180 	Image eimg = getClassVariableValueObject(tree, NAME_expandedImage);
1181 
1182 	ps_output("gsave\n~t ~C ~T ~p pen\n", tree, proto, proto, proto);
1183 	drawPostScriptNode(tree->displayRoot, cimg, eimg);
1184 	ps_output("grestore\n");
1185       }
1186     }
1187   }
1188 
1189   return drawPostScriptFigure((Figure)tree, hb);
1190 }
1191 
1192 
1193 status
drawPostScriptBox(Box b,Name hb)1194 drawPostScriptBox(Box b, Name hb)
1195 { if ( hb == NAME_head )
1196   { psdef(NAME_draw);
1197     psdef(NAME_boxpath);
1198     psdef_texture(b);
1199     psdef_fill(b, NAME_fillPattern);
1200   } else
1201   { Area a = b->area;
1202     int x = valInt(a->x);
1203     int y = valInt(a->y);
1204     int w = valInt(a->w);
1205     int h = valInt(a->h);
1206     int r = valInt(b->radius);
1207     int rmax;
1208 
1209     NormaliseArea(x, y, w, h);
1210     rmax = min(w, h)/2;
1211     if ( r > rmax )
1212       r = rmax;
1213 
1214     if ( b->shadow != ZERO )
1215     { int s = valInt(b->shadow);
1216 
1217       ps_output("gsave nodash 0 ~D ~D ~D ~D ~D boxpath\n",
1218 		x+s, y+s, w-s, h-s, r);
1219       ps_output("0.0 setgray fill grestore\n");
1220       ps_output("gsave ~C ~T ~p ~x ~y ~d ~d ~d boxpath\n", b, b,
1221 		b, b, b, toInt(w-s), toInt(h-s), toInt(r));
1222       if ( notNil(b->fill_pattern) )
1223 	fill(b, NAME_fillPattern);
1224       else
1225 	ps_output("gsave 1.0 setgray fill grestore\n");
1226       ps_output("draw grestore\n");
1227     } else
1228     { ps_output("gsave ~C ~T ~p ~D ~D ~D ~D ~D boxpath\n",
1229 		b, b, b, x, y, w, h, r);
1230       fill(b, NAME_fillPattern);
1231       ps_output("draw grestore\n");
1232     }
1233   }
1234 
1235   succeed;
1236 }
1237 
1238 
1239 status
drawPostScriptCircle(Circle c,Name hb)1240 drawPostScriptCircle(Circle c, Name hb)
1241 { if ( hb == NAME_head )
1242   { psdef(NAME_draw);
1243     psdef(NAME_circlepath);
1244     psdef_texture(c);
1245     psdef_fill(c, NAME_fillPattern);
1246   } else
1247   { ps_output("gsave ~C ~T ~p ~x ~y ~d circlepath\n",
1248 	      c, c, c, c, c, div(c->area->w, TWO));
1249     fill(c, NAME_fillPattern);
1250     ps_output("draw grestore\n");
1251   }
1252 
1253   succeed;
1254 }
1255 
1256 
1257 status
drawPostScriptEllipse(EllipseObj e,Name hb)1258 drawPostScriptEllipse(EllipseObj e, Name hb)
1259 { if ( hb == NAME_head )
1260   { psdef(NAME_draw);
1261     psdef(NAME_nodash);
1262     psdef_texture(e);
1263     psdef(NAME_ellipsepath);
1264     psdef_fill(e, NAME_fillPattern);
1265   } else
1266   { if ( e->shadow != ZERO )
1267     { Area a = e->area;
1268       Int s = e->shadow;
1269 
1270       ps_output("gsave nodash 0 ~d ~d ~d ~d ellipsepath\n",
1271 		add(a->x, s), add(a->y, s), sub(a->w, s), sub(a->h, s));
1272       ps_output("0.0 setgray fill grestore\n");
1273       ps_output("gsave ~C ~T ~p ~x ~y ~d ~d ellipsepath\n",
1274 		e, e, e, e, e, sub(a->w, s), sub(a->h, s));
1275       if ( notNil(e->fill_pattern) )
1276 	fill(e, NAME_fillPattern);
1277       else
1278 	ps_output("gsave 1.0 setgray fill grestore\n");
1279       ps_output("draw grestore\n");
1280     } else
1281     { ps_output("gsave ~C ~T ~p ~x ~y ~w ~h ellipsepath\n",
1282 		e, e, e, e, e, e, e);
1283       fill(e, NAME_fillPattern);
1284       ps_output("draw grestore\n");
1285     }
1286   }
1287 
1288   succeed;
1289 }
1290 
1291 
1292 status
drawPostScriptPath(Path p,Name hb)1293 drawPostScriptPath(Path p, Name hb)
1294 { if ( hb == NAME_head )
1295   { psdef(NAME_draw);
1296     psdef(NAME_startpath);
1297     psdef_texture(p);
1298     psdef_fill(p, NAME_fillPattern);
1299     psdef_arrows(p);
1300 
1301     if ( notNil(p->mark) )
1302       draw_postscript_image(p->mark, ZERO, ZERO, hb);
1303   } else
1304   { if ( valInt(getSizeChain(p->points)) >= 2 )
1305     { Chain points = (p->kind == NAME_smooth ? p->interpolation : p->points);
1306 
1307       if ( p->kind == NAME_smooth )	/* Smooth path */
1308       { Cell cell;
1309 	int i = -1;			/* skip first */
1310 	int px, py, x0, y0;
1311 	Point pt = getHeadChain(points);
1312 
1313 	x0 = valInt(pt->x);
1314 	y0 = valInt(pt->y);
1315 
1316 	if ( p->closed == ON )
1317 	{ Point end = getTailChain(points);
1318 
1319 	  px = valInt(end->x);
1320 	  py = valInt(end->y);
1321 	} else
1322 	{ Point pn = getNth1Chain(points, TWO);
1323 
1324 	  px = x0 - (valInt(pn->x) - x0);
1325 	  py = y0 - (valInt(pn->y) - y0);
1326 	}
1327 
1328 	ps_output("gsave ~d ~d translate ~C ~T ~p ~c startpath\n",
1329 		  p->offset->x, p->offset->y, p, p, p, pt);
1330 
1331 	for_cell(cell, points)
1332 	{ if ( i >= 0 )
1333 	  { Point pt = cell->value;
1334 	    int x3 = valInt(pt->x);
1335 	    int y3 = valInt(pt->y);
1336 	    int nx, ny;
1337 	    float x1, y1, x2, y2;
1338 
1339 	    if ( notNil(cell->next) )
1340 	    { Point np = cell->next->value;
1341 	      nx = valInt(np->x);
1342 	      ny = valInt(np->y);
1343 	    } else if ( p->closed == ON )
1344 	    { Point np = getHeadChain(points);
1345 	      nx = valInt(np->x);
1346 	      ny = valInt(np->y);
1347 	    } else
1348 	    { nx = x3 + x3 - x0;
1349 	      ny = y3 + y3 -y0;
1350 	    }
1351 
1352 	    x1 = (float) x0 + (float) ((x0-px) + (x3-x0) + 4) / 8.0;
1353 	    y1 = (float) y0 + (float) ((y0-py) + (y3-y0) + 4) / 8.0;
1354 
1355 	    x2 = (float) x3 - (float) ((nx-x3) + (x3-x0) + 4) / 8.0;
1356 	    y2 = (float) y3 - (float) ((ny-y3) + (y3-y0) + 4) / 8.0;
1357 
1358 	    ps_output("~f ~f ~f ~f ~D ~D curveto\n", x1, y1, x2, y2, x3, y3);
1359 
1360 	    px = x0; py = y0;
1361 	    x0 = x3; y0 = y3;
1362 	  }
1363 
1364 	  i++;
1365 	}
1366 
1367 	if ( notNil(p->fill_pattern) || p->closed == ON )
1368 	  ps_output(" closepath");
1369 	ps_output("\n");
1370 
1371 	fill(p, NAME_fillPattern);
1372 	ps_output("draw\n");
1373       } else				/* poly-path */
1374       { Cell cell;
1375 	int i = -1;			/* skip first */
1376 
1377 	ps_output("gsave ~d ~d translate ~C ~T ~p ~c startpath\n",
1378 		  p->offset->x, p->offset->y,
1379 		  p, p, p, getHeadChain(points));
1380 	for_cell(cell, p->points)
1381 	{ if ( i >= 0 )
1382 	  { ps_output(" ~c lineto", cell->value);
1383 	    if ( i % 6 == 0 )
1384 	      ps_output("\n");
1385 	  }
1386 
1387 	  i++;
1388 	}
1389 
1390 	if ( notNil(p->fill_pattern) || p->closed == ON )
1391 	  ps_output(" closepath");
1392 	ps_output("\n");
1393 
1394 	fill(p, NAME_fillPattern);
1395 	ps_output("draw\n");
1396       }
1397 
1398       if ( notNil(p->mark) )
1399       { Cell cell;
1400 	Image i = p->mark;
1401 	int iw = valInt(i->size->w);
1402 	int ih = valInt(i->size->h);
1403 	int iw2 = (iw+1)/2;
1404 	int ih2 = (ih+1)/2;
1405 	int ox = valInt(p->offset->x);
1406 	int oy = valInt(p->offset->y);
1407 
1408 	for_cell(cell, p->points)
1409 	{ Point pt = cell->value;
1410 
1411 	  draw_postscript_image(i,
1412 				toInt(valInt(pt->x) - iw2 + ox),
1413 				toInt(valInt(pt->y) - ih2 + oy), hb);
1414 	}
1415       }
1416 
1417       if ( adjustFirstArrowPath(p) )
1418 	postscriptGraphical(p->first_arrow, hb);
1419       if ( adjustSecondArrowPath(p) )
1420 	postscriptGraphical(p->second_arrow, hb);
1421 
1422       ps_output("grestore\n");
1423     }
1424   }
1425 
1426 
1427   succeed;
1428 }
1429 
1430 
1431 status
drawPostScriptBezier(Bezier b,Name hb)1432 drawPostScriptBezier(Bezier b, Name hb)
1433 { if ( hb == NAME_head )
1434   { psdef(NAME_draw);
1435     psdef(NAME_startpath);
1436     psdef_texture(b);
1437     psdef_arrows(b);
1438   } else
1439   { ps_output("gsave ~C\n", b);
1440 
1441     if ( b->pen != ZERO )
1442     { ps_output("newpath ~d ~d moveto\n", b->start->x, b->start->y);
1443       ps_output("~T ~p pen\n", b, b);
1444       if ( isNil(b->control2) )
1445       { /* TBD: This is not correct! */
1446 	ps_output("~d ~d ~d ~d ~d ~d curveto draw\n",
1447 		  b->control1->x, b->control1->y,
1448 		  b->control1->x, b->control1->y,
1449 		  b->end->x, b->end->y);
1450       } else
1451       { ps_output("~d ~d ~d ~d ~d ~d curveto draw\n",
1452 		  b->control1->x, b->control1->y,
1453 		  b->control2->x, b->control2->y,
1454 		  b->end->x, b->end->y);
1455       }
1456     }
1457 
1458     if ( adjustFirstArrowBezier(b) )
1459       postscriptGraphical(b->first_arrow, hb);
1460     if ( adjustSecondArrowBezier(b) )
1461       postscriptGraphical(b->second_arrow, hb);
1462 
1463     ps_output("grestore\n");
1464   }
1465 
1466   succeed;
1467 }
1468 
1469 
1470 status
drawPostScriptLine(Line ln,Name hb)1471 drawPostScriptLine(Line ln, Name hb)
1472 { if ( hb == NAME_head )
1473   { if ( ln->pen != ZERO )
1474     { if ( ln->pen != ZERO )
1475       { psdef(NAME_draw);
1476 	psdef(NAME_linepath);
1477 	psdef_texture(ln);
1478       }
1479       psdef_arrows(ln);
1480     }
1481   } else
1482   { int x1 = valInt(ln->start_x);
1483     int x2 = valInt(ln->end_x);
1484     int y1 = valInt(ln->start_y);
1485     int y2 = valInt(ln->end_y);
1486 
1487     ps_output("gsave ~C\n", ln);
1488     if ( ln->pen != ZERO )
1489       ps_output("~T ~p ~D ~D ~D ~D linepath draw\n",
1490 		ln, ln, x1, y1, x2-x1, y2-y1);
1491 
1492     if ( adjustFirstArrowLine(ln) )
1493     { Colour old = ln->first_arrow->colour;
1494       ln->first_arrow->colour = ln->colour;
1495       postscriptGraphical(ln->first_arrow, hb);
1496       ln->first_arrow->colour = old;
1497     }
1498     if ( adjustSecondArrowLine(ln) )
1499     { Colour old = ln->second_arrow->colour;
1500       ln->second_arrow->colour = ln->colour;
1501       postscriptGraphical(ln->second_arrow, hb);
1502       ln->second_arrow->colour = old;
1503     }
1504 
1505     ps_output("grestore\n");
1506   }
1507 
1508   succeed;
1509 }
1510 
1511 
1512 status
drawPostScriptArrow(Arrow a,Name hb)1513 drawPostScriptArrow(Arrow a, Name hb)
1514 { if ( hb == NAME_head )
1515   { psdef(NAME_draw);
1516     psdef_texture(a);
1517     psdef(NAME_pen);
1518   } else
1519   { ps_output("gsave ~C ~T ~p pen ", a, a, a);
1520     ps_output("newpath ~d ~d moveto ~d ~d lineto ~d ~d lineto",
1521 	      a->left->x, a->left->y,
1522 	      a->tip->x, a->tip->y,
1523 	      a->right->x, a->right->y);
1524 
1525     if ( a->style == NAME_closed || notNil(a->fill_pattern) )
1526       ps_output(" closepath ");
1527 
1528     if ( notNil(a->fill_pattern) )
1529       fill(a, NAME_fillPattern);
1530     if ( a->pen != ZERO )
1531       ps_output(" ~T draw\n", a);
1532 
1533     ps_output(" grestore\n");
1534   }
1535 
1536   succeed;
1537 }
1538 
1539 
1540 status
drawPostScriptArc(ArcObj a,Name hb)1541 drawPostScriptArc(ArcObj a, Name hb)
1542 { if ( hb == NAME_head )
1543   { psdef(NAME_draw);
1544     psdef(NAME_arcpath);
1545     psdef_fill(a, NAME_fillPattern);
1546     psdef_arrows(a);
1547   } else
1548   { ps_output("gsave ~C ~T ~p ~D ~d ~d ~d ~d ~f ~f arcpath\n",
1549 	      a, a, a,
1550 	      a->close == NAME_none ? 0 : a->close == NAME_chord ? 1 : 2,
1551 	      a->position->x, a->position->y,
1552 	      a->size->w, a->size->h,
1553 	      valReal(a->start_angle), valReal(a->size_angle));
1554 
1555     fill(a, NAME_fillPattern);
1556     ps_output("draw\n");
1557 
1558     if ( notNil(a->first_arrow) ||  notNil(a->second_arrow) )
1559     { int sx, sy, cx, cy, ex, ey;
1560 
1561       points_arc(a, &sx, &sy, &ex, &ey);
1562       cx = valInt(a->position->x);
1563       cy = valInt(a->position->y);
1564 
1565       if (notNil(a->first_arrow))	/* should be merged from arc.c */
1566       { Any av[4];
1567 
1568 	av[0] = toInt(sx);
1569 	av[1] = toInt(sy);
1570 
1571 	if ( valReal(a->size_angle) >= 0.0 )
1572 	{ av[2] = toInt(sx+(sy-cy));
1573 	  av[3] = toInt(sy-(sx-cx));
1574 	} else
1575 	{ av[2] = toInt(sx-(sy-cy));
1576 	  av[3] = toInt(sy+(sx-cx));
1577 	}
1578 
1579 	if ( qadSendv(a->first_arrow, NAME_points, 4, av) )
1580 	{ ComputeGraphical(a->first_arrow);
1581 	  postscriptGraphical(a->first_arrow, hb);
1582 	}
1583       }
1584       if (notNil(a->second_arrow))
1585       { Any av[4];
1586 
1587 	av[0] = toInt(ex);
1588 	av[1] = toInt(ey);
1589 
1590 	if ( valReal(a->size_angle) >= 0.0 )
1591 	{ av[2] = toInt(ex-(ey-cy));
1592 	  av[3] = toInt(ey+(ex-cx));
1593 	} else
1594 	{ av[2] = toInt(ex+(ey-cy));
1595 	  av[3] = toInt(ey-(ex-cx));
1596 	}
1597 
1598 	if ( qadSendv(a->second_arrow, NAME_points, 4, av) )
1599 	{ ComputeGraphical(a->second_arrow);
1600 	  postscriptGraphical(a->second_arrow, hb);
1601 	}
1602       }
1603     }
1604 
1605     ps_output("grestore\n");
1606   }
1607 
1608   succeed;
1609 }
1610 
1611 
1612 static status
draw_postscript_image(Image image,Int x,Int y,Name hb)1613 draw_postscript_image(Image image, Int x, Int y, Name hb)
1614 { if ( image->depth == ONE /* && image->transparent == ON */ )
1615   { if ( hb == NAME_head )
1616     { psdef(NAME_bitmap);
1617     } else
1618     { Int iw = image->size->w;
1619       Int ih = image->size->h;
1620 
1621       ps_output("~d ~d ~d ~d bitmap\n~I\n",
1622 		x, y, iw, ih, ONE, image);
1623     }
1624   } else
1625   { Name format = get(image, NAME_postscriptFormat, EAV);
1626 
1627     if ( format == NAME_colour )
1628     { if ( hb == NAME_head )
1629       { psdef(NAME_rgbimage);
1630       } else
1631       { Int depth = get(image, NAME_postscriptDepth, EAV);
1632 
1633 	ps_output("~d ~d ~d ~d ~d rgbimage\n~I\n",
1634 		  x, y, image->size->w, image->size->h, depth, depth, image);
1635       }
1636     } else
1637     { if ( hb == NAME_head )
1638       { psdef(NAME_greymap);
1639       } else
1640       { Int depth = get(image, NAME_postscriptDepth, EAV);
1641 
1642 	ps_output("~d ~d ~d ~d ~d greymap\n~P\n",
1643 		  x, y, image->size->w, image->size->h, depth, depth, image);
1644       }
1645     }
1646   }
1647 
1648   succeed;
1649 }
1650 
1651 
1652 status
drawPostScriptBitmap(BitmapObj bm,Name hb)1653 drawPostScriptBitmap(BitmapObj bm, Name hb)
1654 { return draw_postscript_image(bm->image, bm->area->x, bm->area->y, hb);
1655 }
1656 
1657 
1658 status
drawPostScriptImage(Image image,Name hb)1659 drawPostScriptImage(Image image, Name hb)
1660 { return draw_postscript_image(image, ZERO, ZERO, hb);
1661 }
1662 
1663 
1664 #define Wrapped(t)      ((t)->wrap == NAME_wrap || \
1665 			 (t)->wrap == NAME_wrapFixedWidth)
1666 
1667 status
drawPostScriptText(TextObj t,Name hb)1668 drawPostScriptText(TextObj t, Name hb)
1669 { PceString s = &t->string->data;
1670 
1671   if ( s[0].s_size > 0 )		/* i.e. non-empty */
1672   { int x, y, w;
1673     int b = valInt(t->border);
1674 
1675     x = valInt(t->area->x);
1676     y = valInt(t->area->y);
1677     w = valInt(t->area->w);
1678 
1679     if ( isDefault(t->background) )
1680     { if ( hb == NAME_head )
1681 	psdef(NAME_clear);
1682       else
1683 	ps_output("~x ~y ~w ~h clear\n", t, t, t, t);
1684     }
1685 
1686     if ( hb == NAME_body )
1687       ps_output("gsave ~C", t);
1688     else
1689       psdef(NAME_text);
1690 
1691     if ( t->pen != ZERO || notNil(t->background) )
1692     { if ( hb == NAME_head )
1693       { psdef_fill(t, NAME_background);
1694 	psdef_texture(t);
1695 	psdef(NAME_boxpath);
1696 	if ( t->pen != ZERO )
1697 	  psdef(NAME_draw);
1698       } else
1699       { ps_output("~T ~p ~x ~y ~w ~h 0 boxpath\n",
1700 		  t, t, t, t, t, t);
1701 	fill(t, NAME_background);
1702 	if ( t->pen != ZERO )
1703 	  ps_output("draw\n");
1704       }
1705     }
1706 
1707     if ( hb == NAME_head )
1708     { if ( t->wrap == NAME_clip )
1709       { psdef(NAME_boxpath);
1710 	psdef_texture(t);
1711       }
1712       if ( t->underline == ON )
1713       { psdef(NAME_nodash);
1714 	psdef(NAME_linepath);
1715 	psdef(NAME_draw);
1716       }
1717     } else
1718     { int flags = 0;
1719 
1720       if ( t->underline == ON )
1721 	flags |= TXT_UNDERLINED;
1722 
1723       if ( Wrapped(t) )
1724       { LocalString(buf, s->s_iswide, s->s_size + MAX_WRAP_LINES);
1725 
1726 	str_format(buf, s, valInt(t->margin), t->font);
1727 	ps_string(buf, t->font, x+b, y+b, w-2*b, t->format, flags);
1728       } else if ( t->wrap == NAME_clip )
1729       { ps_output("gsave 0 ~x ~y ~w ~h 0 boxpath clip\n", t, t, t, t);
1730 	ps_string(s, t->font, x+b+valInt(t->x_offset), y+b, w-2*b,
1731 		  t->format, flags);
1732 	ps_output("grestore\n");
1733       } else
1734 	ps_string(s, t->font, x+b, y+b, w-2*b, t->format, flags);
1735 
1736       ps_output("grestore\n", t);
1737     }
1738   }
1739 
1740   succeed;
1741 }
1742 
1743 		/********************************
1744 		*       WINDOWS AND DISPLAY	*
1745 		********************************/
1746 
1747 
1748 status
postscriptFrame(FrameObj fr,Name hb)1749 postscriptFrame(FrameObj fr, Name hb)
1750 { if ( hb == NAME_head )
1751   { psdef(NAME_rgbimage);
1752     succeed;
1753   } else
1754     return ws_postscript_frame(fr, TRUE);
1755 }
1756 
1757 
1758 status
postscriptDisplay(DisplayObj d,Name hb)1759 postscriptDisplay(DisplayObj d, Name hb)
1760 { if ( hb == NAME_head )
1761   { psdef(NAME_rgbimage);
1762     succeed;
1763   } else
1764     return ws_postscript_display(d, TRUE);
1765 }
1766