2 * ps.c: PostScript printing functions.
12 #define ROOT2 1.414213562
19 float hatchthick
, hatchspace
;
20 int gamewidth
, gameheight
;
24 static void ps_printf(psdata
*ps
, char *fmt
, ...)
29 vfprintf(ps
->fp
, fmt
, ap
);
33 static void ps_fill(psdata
*ps
, int colour
)
38 print_get_colour(ps
->drawing
, colour
, &hatch
, &r
, &g
, &b
);
41 ps_printf(ps
, "%g %g %g setrgbcolor fill\n", r
, g
, b
);
42 } else if (hatch
== HATCH_SOLID
|| hatch
== HATCH_CLEAR
) {
43 ps_printf(ps
, "%d setgray fill\n", hatch
== HATCH_CLEAR
);
45 /* Clip to the region. */
46 ps_printf(ps
, "gsave clip\n");
47 /* Hatch the entire game printing area. */
48 ps_printf(ps
, "newpath\n");
49 if (hatch
== HATCH_VERT
|| hatch
== HATCH_PLUS
)
50 ps_printf(ps
, "0 %g %d {\n"
51 " 0 moveto 0 %d rlineto\n"
52 "} for\n", ps
->hatchspace
, ps
->gamewidth
,
54 if (hatch
== HATCH_HORIZ
|| hatch
== HATCH_PLUS
)
55 ps_printf(ps
, "0 %g %d {\n"
56 " 0 exch moveto %d 0 rlineto\n"
57 "} for\n", ps
->hatchspace
, ps
->gameheight
,
59 if (hatch
== HATCH_SLASH
|| hatch
== HATCH_X
)
60 ps_printf(ps
, "%d %g %d {\n"
61 " 0 moveto %d dup rlineto\n"
62 "} for\n", -ps
->gameheight
, ps
->hatchspace
* ROOT2
,
63 ps
->gamewidth
, max(ps
->gamewidth
, ps
->gameheight
));
64 if (hatch
== HATCH_BACKSLASH
|| hatch
== HATCH_X
)
65 ps_printf(ps
, "0 %g %d {\n"
66 " 0 moveto %d neg dup neg rlineto\n"
67 "} for\n", ps
->hatchspace
* ROOT2
,
68 ps
->gamewidth
+ps
->gameheight
,
69 max(ps
->gamewidth
, ps
->gameheight
));
70 ps_printf(ps
, "0 setgray %g setlinewidth stroke grestore\n",
75 static void ps_setcolour_internal(psdata
*ps
, int colour
, char *suffix
)
80 print_get_colour(ps
->drawing
, colour
, &hatch
, &r
, &g
, &b
);
84 ps_printf(ps
, "%g %g %g setrgbcolor%s\n", r
, g
, b
, suffix
);
86 ps_printf(ps
, "%g setgray%s\n", r
, suffix
);
89 * Stroking in hatched colours is not permitted.
91 assert(hatch
== HATCH_SOLID
|| hatch
== HATCH_CLEAR
);
92 ps_printf(ps
, "%d setgray%s\n", hatch
== HATCH_CLEAR
, suffix
);
96 static void ps_setcolour(psdata
*ps
, int colour
)
98 ps_setcolour_internal(ps
, colour
, "");
101 static void ps_stroke(psdata
*ps
, int colour
)
103 ps_setcolour_internal(ps
, colour
, " stroke");
106 static void ps_draw_text(void *handle
, int x
, int y
, int fonttype
,
107 int fontsize
, int align
, int colour
, char *text
)
109 psdata
*ps
= (psdata
*)handle
;
112 ps_setcolour(ps
, colour
);
113 ps_printf(ps
, "/%s findfont %d scalefont setfont\n",
114 fonttype
== FONT_FIXED ?
"Courier" : "Helvetica",
116 if (align
& ALIGN_VCENTRE
) {
117 ps_printf(ps
, "newpath 0 0 moveto (X) true charpath flattenpath"
119 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
122 ps_printf(ps
, "%d %d moveto\n", x
, y
);
126 if (*text
== '\\' || *text
== '(' || *text
== ')')
128 ps_printf(ps
, "%c", *text
);
132 if (align
& (ALIGN_HCENTRE
| ALIGN_HRIGHT
))
133 ps_printf(ps
, "dup stringwidth pop %sneg 0 rmoveto show\n",
134 (align
& ALIGN_HCENTRE
) ?
"2 div " : "");
136 ps_printf(ps
, "show\n");
139 static void ps_draw_rect(void *handle
, int x
, int y
, int w
, int h
, int colour
)
141 psdata
*ps
= (psdata
*)handle
;
145 * Offset by half a pixel for the exactness requirement.
147 ps_printf(ps
, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
148 " %d 0 rlineto closepath\n", x
- 0.5, y
+ 0.5, w
, -h
, -w
);
152 static void ps_draw_line(void *handle
, int x1
, int y1
, int x2
, int y2
,
155 psdata
*ps
= (psdata
*)handle
;
159 ps_printf(ps
, "newpath %d %d moveto %d %d lineto\n", x1
, y1
, x2
, y2
);
160 ps_stroke(ps
, colour
);
163 static void ps_draw_polygon(void *handle
, int *coords
, int npoints
,
164 int fillcolour
, int outlinecolour
)
166 psdata
*ps
= (psdata
*)handle
;
170 ps_printf(ps
, "newpath %d %d moveto\n", coords
[0], ps
->ytop
- coords
[1]);
172 for (i
= 1; i
< npoints
; i
++)
173 ps_printf(ps
, "%d %d lineto\n", coords
[i
*2], ps
->ytop
- coords
[i
*2+1]);
175 ps_printf(ps
, "closepath\n");
177 if (fillcolour
>= 0) {
178 ps_printf(ps
, "gsave\n");
179 ps_fill(ps
, fillcolour
);
180 ps_printf(ps
, "grestore\n");
182 ps_stroke(ps
, outlinecolour
);
185 static void ps_draw_circle(void *handle
, int cx
, int cy
, int radius
,
186 int fillcolour
, int outlinecolour
)
188 psdata
*ps
= (psdata
*)handle
;
192 ps_printf(ps
, "newpath %d %d %d 0 360 arc closepath\n", cx
, cy
, radius
);
194 if (fillcolour
>= 0) {
195 ps_printf(ps
, "gsave\n");
196 ps_fill(ps
, fillcolour
);
197 ps_printf(ps
, "grestore\n");
199 ps_stroke(ps
, outlinecolour
);
202 static void ps_unclip(void *handle
)
204 psdata
*ps
= (psdata
*)handle
;
207 ps_printf(ps
, "grestore\n");
211 static void ps_clip(void *handle
, int x
, int y
, int w
, int h
)
213 psdata
*ps
= (psdata
*)handle
;
220 * Offset by half a pixel for the exactness requirement.
222 ps_printf(ps
, "gsave\n");
223 ps_printf(ps
, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
224 " %d 0 rlineto closepath\n", x
- 0.5, y
+ 0.5, w
, -h
, -w
);
225 ps_printf(ps
, "clip\n");
229 static void ps_line_width(void *handle
, float width
)
231 psdata
*ps
= (psdata
*)handle
;
233 ps_printf(ps
, "%g setlinewidth\n", width
);
236 static void ps_begin_doc(void *handle
, int pages
)
238 psdata
*ps
= (psdata
*)handle
;
240 fputs("%!PS-Adobe-3.0\n", ps
->fp
);
241 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps
->fp
);
242 fputs("%%DocumentData: Clean7Bit\n", ps
->fp
);
243 fputs("%%LanguageLevel: 1\n", ps
->fp
);
244 fprintf(ps
->fp
, "%%%%Pages: %d\n", pages
);
245 fputs("%%DocumentNeededResources:\n", ps
->fp
);
246 fputs("%%+ font Helvetica\n", ps
->fp
);
247 fputs("%%+ font Courier\n", ps
->fp
);
248 fputs("%%EndComments\n", ps
->fp
);
249 fputs("%%BeginSetup\n", ps
->fp
);
250 fputs("%%IncludeResource: font Helvetica\n", ps
->fp
);
251 fputs("%%IncludeResource: font Courier\n", ps
->fp
);
252 fputs("%%EndSetup\n", ps
->fp
);
255 static void ps_begin_page(void *handle
, int number
)
257 psdata
*ps
= (psdata
*)handle
;
259 fprintf(ps
->fp
, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
260 number
, number
, 72.0 / 25.4);
263 static void ps_begin_puzzle(void *handle
, float xm
, float xc
,
264 float ym
, float yc
, int pw
, int ph
, float wmm
)
266 psdata
*ps
= (psdata
*)handle
;
268 fprintf(ps
->fp
, "gsave\n"
269 "clippath flattenpath pathbbox pop pop translate\n"
270 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
271 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
273 "0 -%d translate\n", xm
, xc
, ym
, yc
, wmm
/pw
, ph
);
278 ps
->hatchthick
= 0.2 * pw
/ wmm
;
279 ps
->hatchspace
= 1.0 * pw
/ wmm
;
282 static void ps_end_puzzle(void *handle
)
284 psdata
*ps
= (psdata
*)handle
;
286 fputs("grestore\n", ps
->fp
);
289 static void ps_end_page(void *handle
, int number
)
291 psdata
*ps
= (psdata
*)handle
;
293 fputs("restore grestore showpage\n", ps
->fp
);
296 static void ps_end_doc(void *handle
)
298 psdata
*ps
= (psdata
*)handle
;
300 fputs("%%EOF\n", ps
->fp
);
303 static const struct drawing_api ps_drawing
= {
309 NULL
/* draw_update */,
312 NULL
/* start_draw */,
314 NULL
/* status_bar */,
315 NULL
/* blitter_new */,
316 NULL
/* blitter_free */,
317 NULL
/* blitter_save */,
318 NULL
/* blitter_load */,
328 psdata
*ps_init(FILE *outfile
, int colour
)
330 psdata
*ps
= snew(psdata
);
336 ps
->hatchthick
= ps
->hatchspace
= ps
->gamewidth
= ps
->gameheight
= 0;
337 ps
->drawing
= drawing_new(&ps_drawing
, NULL
, ps
);
342 void ps_free(psdata
*ps
)
344 drawing_free(ps
->drawing
);
348 drawing
*ps_drawing_api(psdata
*ps
)