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