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, const char *fmt, ...)
29 vfprintf(ps->fp, fmt, ap);
33 static void ps_fill(psdata *ps, int colour)
38 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
42 ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b);
44 ps_printf(ps, "%g setgray fill\n", r);
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,
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,
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",
76 static void ps_setcolour_internal(psdata *ps, int colour, const char *suffix)
81 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
84 * Stroking in hatched colours is not permitted.
89 ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix);
91 ps_printf(ps, "%g setgray%s\n", r, suffix);
94 static void ps_setcolour(psdata *ps, int colour)
96 ps_setcolour_internal(ps, colour, "");
99 static void ps_stroke(psdata *ps, int colour)
101 ps_setcolour_internal(ps, colour, " stroke");
104 static void ps_draw_text(void *handle, int x, int y, int fonttype,
105 int fontsize, int align, int colour,
108 psdata *ps = (psdata *)handle;
111 ps_setcolour(ps, colour);
112 ps_printf(ps, "/%s findfont %d scalefont setfont\n",
113 fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1",
115 if (align & ALIGN_VCENTRE) {
116 ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
118 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
121 ps_printf(ps, "%d %d moveto\n", x, y);
125 if (*text == '\\' || *text == '(' || *text == ')')
127 ps_printf(ps, "%c", *text);
131 if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT))
132 ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n",
133 (align & ALIGN_HCENTRE) ? "2 div " : "");
135 ps_printf(ps, "show\n");
138 static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
140 psdata *ps = (psdata *)handle;
144 * Offset by half a pixel for the exactness requirement.
146 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
147 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
151 static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
154 psdata *ps = (psdata *)handle;
158 ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2);
159 ps_stroke(ps, colour);
162 static void ps_draw_polygon(void *handle, int *coords, int npoints,
163 int fillcolour, int outlinecolour)
165 psdata *ps = (psdata *)handle;
169 ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]);
171 for (i = 1; i < npoints; i++)
172 ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]);
174 ps_printf(ps, "closepath\n");
176 if (fillcolour >= 0) {
177 ps_printf(ps, "gsave\n");
178 ps_fill(ps, fillcolour);
179 ps_printf(ps, "grestore\n");
181 ps_stroke(ps, outlinecolour);
184 static void ps_draw_circle(void *handle, int cx, int cy, int radius,
185 int fillcolour, int outlinecolour)
187 psdata *ps = (psdata *)handle;
191 ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius);
193 if (fillcolour >= 0) {
194 ps_printf(ps, "gsave\n");
195 ps_fill(ps, fillcolour);
196 ps_printf(ps, "grestore\n");
198 ps_stroke(ps, outlinecolour);
201 static void ps_unclip(void *handle)
203 psdata *ps = (psdata *)handle;
206 ps_printf(ps, "grestore\n");
210 static void ps_clip(void *handle, int x, int y, int w, int h)
212 psdata *ps = (psdata *)handle;
219 * Offset by half a pixel for the exactness requirement.
221 ps_printf(ps, "gsave\n");
222 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
223 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
224 ps_printf(ps, "clip\n");
228 static void ps_line_width(void *handle, float width)
230 psdata *ps = (psdata *)handle;
232 ps_printf(ps, "%g setlinewidth\n", width);
235 static void ps_line_dotted(void *handle, int dotted)
237 psdata *ps = (psdata *)handle;
240 ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n");
242 ps_printf(ps, "[ ] 0 setdash\n");
246 static char *ps_text_fallback(void *handle, const char *const *strings,
250 * We can handle anything in ISO 8859-1, and we'll manually
251 * translate it out of UTF-8 for the purpose.
257 for (i = 0; i < nstrings; i++) {
258 int len = strlen(strings[i]);
259 if (maxlen < len) maxlen = len;
262 ret = snewn(maxlen + 1, char);
264 for (i = 0; i < nstrings; i++) {
265 const char *p = strings[i];
269 int c = (unsigned char)*p++;
271 *q++ = c; /* ASCII */
272 } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) {
273 *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */
285 assert(!"Should never reach here");
289 static void ps_begin_doc(void *handle, int pages)
291 psdata *ps = (psdata *)handle;
293 fputs("%!PS-Adobe-3.0\n", ps->fp);
294 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
295 fputs("%%DocumentData: Clean7Bit\n", ps->fp);
296 fputs("%%LanguageLevel: 1\n", ps->fp);
297 fprintf(ps->fp, "%%%%Pages: %d\n", pages);
298 fputs("%%DocumentNeededResources:\n", ps->fp);
299 fputs("%%+ font Helvetica\n", ps->fp);
300 fputs("%%+ font Courier\n", ps->fp);
301 fputs("%%EndComments\n", ps->fp);
302 fputs("%%BeginSetup\n", ps->fp);
303 fputs("%%IncludeResource: font Helvetica\n", ps->fp);
304 fputs("%%IncludeResource: font Courier\n", ps->fp);
305 fputs("%%EndSetup\n", ps->fp);
306 fputs("%%BeginProlog\n", ps->fp);
308 * Re-encode Helvetica and Courier into ISO-8859-1, which gives
309 * us times and divide signs - and also (according to the
310 * Language Reference Manual) a bonus in that the ASCII '-' code
311 * point now points to a minus sign instead of a hyphen.
313 fputs("/Helvetica findfont " /* get the font dictionary */
314 "dup maxlength dict dup begin " /* create and open a new dict */
315 "exch " /* move the original font to top of stack */
316 "{1 index /FID ne {def} {pop pop} ifelse} forall "
317 /* copy everything except FID */
318 "/Encoding ISOLatin1Encoding def "
319 /* set the thing we actually wanted to change */
320 "/FontName /Helvetica-L1 def " /* set a new font name */
321 "FontName end exch definefont" /* and define the font */
323 fputs("/Courier findfont " /* get the font dictionary */
324 "dup maxlength dict dup begin " /* create and open a new dict */
325 "exch " /* move the original font to top of stack */
326 "{1 index /FID ne {def} {pop pop} ifelse} forall "
327 /* copy everything except FID */
328 "/Encoding ISOLatin1Encoding def "
329 /* set the thing we actually wanted to change */
330 "/FontName /Courier-L1 def " /* set a new font name */
331 "FontName end exch definefont" /* and define the font */
333 fputs("%%EndProlog\n", ps->fp);
336 static void ps_begin_page(void *handle, int number)
338 psdata *ps = (psdata *)handle;
340 fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
341 number, number, 72.0 / 25.4);
344 static void ps_begin_puzzle(void *handle, float xm, float xc,
345 float ym, float yc, int pw, int ph, float wmm)
347 psdata *ps = (psdata *)handle;
349 fprintf(ps->fp, "gsave\n"
350 "clippath flattenpath pathbbox pop pop translate\n"
351 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
352 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
354 "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph);
359 ps->hatchthick = 0.2 * pw / wmm;
360 ps->hatchspace = 1.0 * pw / wmm;
363 static void ps_end_puzzle(void *handle)
365 psdata *ps = (psdata *)handle;
367 fputs("grestore\n", ps->fp);
370 static void ps_end_page(void *handle, int number)
372 psdata *ps = (psdata *)handle;
374 fputs("restore grestore showpage\n", ps->fp);
377 static void ps_end_doc(void *handle)
379 psdata *ps = (psdata *)handle;
381 fputs("%%EOF\n", ps->fp);
384 static const struct drawing_api ps_drawing = {
390 NULL /* draw_update */,
393 NULL /* start_draw */,
395 NULL /* status_bar */,
396 NULL /* blitter_new */,
397 NULL /* blitter_free */,
398 NULL /* blitter_save */,
399 NULL /* blitter_load */,
411 psdata *ps_init(FILE *outfile, int colour)
413 psdata *ps = snew(psdata);
419 ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0;
420 ps->drawing = drawing_new(&ps_drawing, NULL, ps);
425 void ps_free(psdata *ps)
427 drawing_free(ps->drawing);
431 drawing *ps_drawing_api(psdata *ps)