Fix the Java front end's vertical text positioning when
[sgt/puzzles] / ps.c
CommitLineData
dafd6cf6 1/*
2 * ps.c: PostScript printing functions.
3 */
4
5#include <stdio.h>
6#include <stdarg.h>
7#include <string.h>
8#include <assert.h>
9
10#include "puzzles.h"
11
12#define ROOT2 1.414213562
13
14struct psdata {
15 FILE *fp;
16 int colour;
17 int ytop;
18 int clipped;
19 float hatchthick, hatchspace;
20 int gamewidth, gameheight;
21 drawing *drawing;
22};
23
24static void ps_printf(psdata *ps, char *fmt, ...)
25{
26 va_list ap;
27
28 va_start(ap, fmt);
29 vfprintf(ps->fp, fmt, ap);
30 va_end(ap);
31}
32
33static void ps_fill(psdata *ps, int colour)
34{
35 int hatch;
36 float r, g, b;
37
60aa1c74 38 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
dafd6cf6 39
60aa1c74 40 if (hatch < 0) {
41 if (ps->colour)
42 ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b);
43 else
44 ps_printf(ps, "%g setgray fill\n", r);
dafd6cf6 45 } else {
46 /* Clip to the region. */
47 ps_printf(ps, "gsave clip\n");
48 /* Hatch the entire game printing area. */
49 ps_printf(ps, "newpath\n");
50 if (hatch == HATCH_VERT || hatch == HATCH_PLUS)
51 ps_printf(ps, "0 %g %d {\n"
52 " 0 moveto 0 %d rlineto\n"
53 "} for\n", ps->hatchspace, ps->gamewidth,
54 ps->gameheight);
55 if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS)
56 ps_printf(ps, "0 %g %d {\n"
57 " 0 exch moveto %d 0 rlineto\n"
58 "} for\n", ps->hatchspace, ps->gameheight,
59 ps->gamewidth);
60 if (hatch == HATCH_SLASH || hatch == HATCH_X)
61 ps_printf(ps, "%d %g %d {\n"
62 " 0 moveto %d dup rlineto\n"
63 "} for\n", -ps->gameheight, ps->hatchspace * ROOT2,
64 ps->gamewidth, max(ps->gamewidth, ps->gameheight));
65 if (hatch == HATCH_BACKSLASH || hatch == HATCH_X)
66 ps_printf(ps, "0 %g %d {\n"
67 " 0 moveto %d neg dup neg rlineto\n"
68 "} for\n", ps->hatchspace * ROOT2,
69 ps->gamewidth+ps->gameheight,
70 max(ps->gamewidth, ps->gameheight));
71 ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n",
72 ps->hatchthick);
73 }
74}
75
76static void ps_setcolour_internal(psdata *ps, int colour, char *suffix)
77{
78 int hatch;
79 float r, g, b;
80
60aa1c74 81 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
dafd6cf6 82
60aa1c74 83 /*
84 * Stroking in hatched colours is not permitted.
85 */
86 assert(hatch < 0);
87
88 if (ps->colour)
89 ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix);
90 else
91 ps_printf(ps, "%g setgray%s\n", r, suffix);
dafd6cf6 92}
93
94static void ps_setcolour(psdata *ps, int colour)
95{
96 ps_setcolour_internal(ps, colour, "");
97}
98
99static void ps_stroke(psdata *ps, int colour)
100{
101 ps_setcolour_internal(ps, colour, " stroke");
102}
103
104static void ps_draw_text(void *handle, int x, int y, int fonttype,
105 int fontsize, int align, int colour, char *text)
106{
107 psdata *ps = (psdata *)handle;
108
109 y = ps->ytop - y;
110 ps_setcolour(ps, colour);
111 ps_printf(ps, "/%s findfont %d scalefont setfont\n",
112 fonttype == FONT_FIXED ? "Courier" : "Helvetica",
0d336b11 113 fontsize);
dafd6cf6 114 if (align & ALIGN_VCENTRE) {
115 ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
116 " pathbbox\n"
117 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
118 y, x);
119 } else {
120 ps_printf(ps, "%d %d moveto\n", x, y);
121 }
122 ps_printf(ps, "(");
123 while (*text) {
124 if (*text == '\\' || *text == '(' || *text == ')')
125 ps_printf(ps, "\\");
126 ps_printf(ps, "%c", *text);
127 text++;
128 }
129 ps_printf(ps, ") ");
130 if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT))
131 ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n",
132 (align & ALIGN_HCENTRE) ? "2 div " : "");
133 else
134 ps_printf(ps, "show\n");
135}
136
137static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
138{
139 psdata *ps = (psdata *)handle;
140
141 y = ps->ytop - y;
142 /*
143 * Offset by half a pixel for the exactness requirement.
144 */
145 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
146 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
147 ps_fill(ps, colour);
148}
149
150static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
151 int colour)
152{
153 psdata *ps = (psdata *)handle;
154
155 y1 = ps->ytop - y1;
156 y2 = ps->ytop - y2;
157 ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2);
158 ps_stroke(ps, colour);
159}
160
161static void ps_draw_polygon(void *handle, int *coords, int npoints,
162 int fillcolour, int outlinecolour)
163{
164 psdata *ps = (psdata *)handle;
165
166 int i;
167
168 ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]);
169
170 for (i = 1; i < npoints; i++)
171 ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]);
172
173 ps_printf(ps, "closepath\n");
174
175 if (fillcolour >= 0) {
176 ps_printf(ps, "gsave\n");
177 ps_fill(ps, fillcolour);
178 ps_printf(ps, "grestore\n");
179 }
180 ps_stroke(ps, outlinecolour);
181}
182
183static void ps_draw_circle(void *handle, int cx, int cy, int radius,
184 int fillcolour, int outlinecolour)
185{
186 psdata *ps = (psdata *)handle;
187
188 cy = ps->ytop - cy;
189
190 ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius);
191
192 if (fillcolour >= 0) {
193 ps_printf(ps, "gsave\n");
194 ps_fill(ps, fillcolour);
195 ps_printf(ps, "grestore\n");
196 }
197 ps_stroke(ps, outlinecolour);
198}
199
200static void ps_unclip(void *handle)
201{
202 psdata *ps = (psdata *)handle;
203
204 assert(ps->clipped);
205 ps_printf(ps, "grestore\n");
206 ps->clipped = FALSE;
207}
208
209static void ps_clip(void *handle, int x, int y, int w, int h)
210{
211 psdata *ps = (psdata *)handle;
212
213 if (ps->clipped)
214 ps_unclip(ps);
215
216 y = ps->ytop - y;
217 /*
218 * Offset by half a pixel for the exactness requirement.
219 */
220 ps_printf(ps, "gsave\n");
221 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
222 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
223 ps_printf(ps, "clip\n");
224 ps->clipped = TRUE;
225}
226
227static void ps_line_width(void *handle, float width)
228{
229 psdata *ps = (psdata *)handle;
230
231 ps_printf(ps, "%g setlinewidth\n", width);
232}
233
234static void ps_begin_doc(void *handle, int pages)
235{
236 psdata *ps = (psdata *)handle;
237
238 fputs("%!PS-Adobe-3.0\n", ps->fp);
239 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
240 fputs("%%DocumentData: Clean7Bit\n", ps->fp);
241 fputs("%%LanguageLevel: 1\n", ps->fp);
242 fprintf(ps->fp, "%%%%Pages: %d\n", pages);
243 fputs("%%DocumentNeededResources:\n", ps->fp);
244 fputs("%%+ font Helvetica\n", ps->fp);
245 fputs("%%+ font Courier\n", ps->fp);
246 fputs("%%EndComments\n", ps->fp);
247 fputs("%%BeginSetup\n", ps->fp);
248 fputs("%%IncludeResource: font Helvetica\n", ps->fp);
249 fputs("%%IncludeResource: font Courier\n", ps->fp);
250 fputs("%%EndSetup\n", ps->fp);
251}
252
253static void ps_begin_page(void *handle, int number)
254{
255 psdata *ps = (psdata *)handle;
256
257 fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
258 number, number, 72.0 / 25.4);
259}
260
261static void ps_begin_puzzle(void *handle, float xm, float xc,
262 float ym, float yc, int pw, int ph, float wmm)
263{
264 psdata *ps = (psdata *)handle;
265
266 fprintf(ps->fp, "gsave\n"
267 "clippath flattenpath pathbbox pop pop translate\n"
268 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
269 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
270 "%g dup scale\n"
271 "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph);
272 ps->ytop = ph;
273 ps->clipped = FALSE;
274 ps->gamewidth = pw;
275 ps->gameheight = ph;
276 ps->hatchthick = 0.2 * pw / wmm;
277 ps->hatchspace = 1.0 * pw / wmm;
278}
279
280static void ps_end_puzzle(void *handle)
281{
282 psdata *ps = (psdata *)handle;
283
284 fputs("grestore\n", ps->fp);
285}
286
287static void ps_end_page(void *handle, int number)
288{
289 psdata *ps = (psdata *)handle;
290
291 fputs("restore grestore showpage\n", ps->fp);
292}
293
294static void ps_end_doc(void *handle)
295{
296 psdata *ps = (psdata *)handle;
297
298 fputs("%%EOF\n", ps->fp);
299}
300
301static const struct drawing_api ps_drawing = {
302 ps_draw_text,
303 ps_draw_rect,
304 ps_draw_line,
305 ps_draw_polygon,
306 ps_draw_circle,
307 NULL /* draw_update */,
308 ps_clip,
309 ps_unclip,
310 NULL /* start_draw */,
311 NULL /* end_draw */,
312 NULL /* status_bar */,
313 NULL /* blitter_new */,
314 NULL /* blitter_free */,
315 NULL /* blitter_save */,
316 NULL /* blitter_load */,
317 ps_begin_doc,
318 ps_begin_page,
319 ps_begin_puzzle,
320 ps_end_puzzle,
321 ps_end_page,
322 ps_end_doc,
323 ps_line_width,
324};
325
326psdata *ps_init(FILE *outfile, int colour)
327{
328 psdata *ps = snew(psdata);
329
330 ps->fp = outfile;
331 ps->colour = colour;
332 ps->ytop = 0;
333 ps->clipped = FALSE;
334 ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0;
83c0438f 335 ps->drawing = drawing_new(&ps_drawing, NULL, ps);
dafd6cf6 336
337 return ps;
338}
339
340void ps_free(psdata *ps)
341{
342 drawing_free(ps->drawing);
343 sfree(ps);
344}
345
346drawing *ps_drawing_api(psdata *ps)
347{
348 return ps->drawing;
349}