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
, 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
, 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
, char *text
)
107 psdata
*ps
= (psdata
*)handle
;
110 ps_setcolour(ps
, colour
);
111 ps_printf(ps
, "/%s findfont %d scalefont setfont\n",
112 fonttype
== FONT_FIXED
? "Courier-L1" : "Helvetica-L1",
114 if (align
& ALIGN_VCENTRE
) {
115 ps_printf(ps
, "newpath 0 0 moveto (X) true charpath flattenpath"
117 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
120 ps_printf(ps
, "%d %d moveto\n", x
, y
);
124 if (*text
== '\\' || *text
== '(' || *text
== ')')
126 ps_printf(ps
, "%c", *text
);
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 " : "");
134 ps_printf(ps
, "show\n");
137 static void ps_draw_rect(void *handle
, int x
, int y
, int w
, int h
, int colour
)
139 psdata
*ps
= (psdata
*)handle
;
143 * Offset by half a pixel for the exactness requirement.
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
);
150 static void ps_draw_line(void *handle
, int x1
, int y1
, int x2
, int y2
,
153 psdata
*ps
= (psdata
*)handle
;
157 ps_printf(ps
, "newpath %d %d moveto %d %d lineto\n", x1
, y1
, x2
, y2
);
158 ps_stroke(ps
, colour
);
161 static void ps_draw_polygon(void *handle
, int *coords
, int npoints
,
162 int fillcolour
, int outlinecolour
)
164 psdata
*ps
= (psdata
*)handle
;
168 ps_printf(ps
, "newpath %d %d moveto\n", coords
[0], ps
->ytop
- coords
[1]);
170 for (i
= 1; i
< npoints
; i
++)
171 ps_printf(ps
, "%d %d lineto\n", coords
[i
*2], ps
->ytop
- coords
[i
*2+1]);
173 ps_printf(ps
, "closepath\n");
175 if (fillcolour
>= 0) {
176 ps_printf(ps
, "gsave\n");
177 ps_fill(ps
, fillcolour
);
178 ps_printf(ps
, "grestore\n");
180 ps_stroke(ps
, outlinecolour
);
183 static void ps_draw_circle(void *handle
, int cx
, int cy
, int radius
,
184 int fillcolour
, int outlinecolour
)
186 psdata
*ps
= (psdata
*)handle
;
190 ps_printf(ps
, "newpath %d %d %d 0 360 arc closepath\n", cx
, cy
, radius
);
192 if (fillcolour
>= 0) {
193 ps_printf(ps
, "gsave\n");
194 ps_fill(ps
, fillcolour
);
195 ps_printf(ps
, "grestore\n");
197 ps_stroke(ps
, outlinecolour
);
200 static void ps_unclip(void *handle
)
202 psdata
*ps
= (psdata
*)handle
;
205 ps_printf(ps
, "grestore\n");
209 static void ps_clip(void *handle
, int x
, int y
, int w
, int h
)
211 psdata
*ps
= (psdata
*)handle
;
218 * Offset by half a pixel for the exactness requirement.
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");
227 static void ps_line_width(void *handle
, float width
)
229 psdata
*ps
= (psdata
*)handle
;
231 ps_printf(ps
, "%g setlinewidth\n", width
);
234 static void ps_line_dotted(void *handle
, int dotted
)
236 psdata
*ps
= (psdata
*)handle
;
239 ps_printf(ps
, "[ currentlinewidth 3 mul ] 0 setdash\n");
241 ps_printf(ps
, "[ ] 0 setdash\n");
245 static char *ps_text_fallback(void *handle
, const char *const *strings
,
249 * We can handle anything in ISO 8859-1, and we'll manually
250 * translate it out of UTF-8 for the purpose.
256 for (i
= 0; i
< nstrings
; i
++) {
257 int len
= strlen(strings
[i
]);
258 if (maxlen
< len
) maxlen
= len
;
261 ret
= snewn(maxlen
+ 1, char);
263 for (i
= 0; i
< nstrings
; i
++) {
264 const char *p
= strings
[i
];
268 int c
= (unsigned char)*p
++;
270 *q
++ = c
; /* ASCII */
271 } else if ((c
== 0xC2 || c
== 0xC3) && (*p
& 0xC0) == 0x80) {
272 *q
++ = (c
<< 6) | (*p
++ & 0x3F); /* top half of 8859-1 */
284 assert(!"Should never reach here");
288 static void ps_begin_doc(void *handle
, int pages
)
290 psdata
*ps
= (psdata
*)handle
;
292 fputs("%!PS-Adobe-3.0\n", ps
->fp
);
293 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps
->fp
);
294 fputs("%%DocumentData: Clean7Bit\n", ps
->fp
);
295 fputs("%%LanguageLevel: 1\n", ps
->fp
);
296 fprintf(ps
->fp
, "%%%%Pages: %d\n", pages
);
297 fputs("%%DocumentNeededResources:\n", ps
->fp
);
298 fputs("%%+ font Helvetica\n", ps
->fp
);
299 fputs("%%+ font Courier\n", ps
->fp
);
300 fputs("%%EndComments\n", ps
->fp
);
301 fputs("%%BeginSetup\n", ps
->fp
);
302 fputs("%%IncludeResource: font Helvetica\n", ps
->fp
);
303 fputs("%%IncludeResource: font Courier\n", ps
->fp
);
304 fputs("%%EndSetup\n", ps
->fp
);
305 fputs("%%BeginProlog\n", ps
->fp
);
307 * Re-encode Helvetica and Courier into ISO-8859-1, which gives
308 * us times and divide signs - and also (according to the
309 * Language Reference Manual) a bonus in that the ASCII '-' code
310 * point now points to a minus sign instead of a hyphen.
312 fputs("/Helvetica findfont " /* get the font dictionary */
313 "dup maxlength dict dup begin " /* create and open a new dict */
314 "exch " /* move the original font to top of stack */
315 "{1 index /FID ne {def} {pop pop} ifelse} forall "
316 /* copy everything except FID */
317 "/Encoding ISOLatin1Encoding def "
318 /* set the thing we actually wanted to change */
319 "/FontName /Helvetica-L1 def " /* set a new font name */
320 "FontName end exch definefont" /* and define the font */
322 fputs("/Courier findfont " /* get the font dictionary */
323 "dup maxlength dict dup begin " /* create and open a new dict */
324 "exch " /* move the original font to top of stack */
325 "{1 index /FID ne {def} {pop pop} ifelse} forall "
326 /* copy everything except FID */
327 "/Encoding ISOLatin1Encoding def "
328 /* set the thing we actually wanted to change */
329 "/FontName /Courier-L1 def " /* set a new font name */
330 "FontName end exch definefont" /* and define the font */
332 fputs("%%EndProlog\n", ps
->fp
);
335 static void ps_begin_page(void *handle
, int number
)
337 psdata
*ps
= (psdata
*)handle
;
339 fprintf(ps
->fp
, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
340 number
, number
, 72.0 / 25.4);
343 static void ps_begin_puzzle(void *handle
, float xm
, float xc
,
344 float ym
, float yc
, int pw
, int ph
, float wmm
)
346 psdata
*ps
= (psdata
*)handle
;
348 fprintf(ps
->fp
, "gsave\n"
349 "clippath flattenpath pathbbox pop pop translate\n"
350 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
351 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
353 "0 -%d translate\n", xm
, xc
, ym
, yc
, wmm
/pw
, ph
);
358 ps
->hatchthick
= 0.2 * pw
/ wmm
;
359 ps
->hatchspace
= 1.0 * pw
/ wmm
;
362 static void ps_end_puzzle(void *handle
)
364 psdata
*ps
= (psdata
*)handle
;
366 fputs("grestore\n", ps
->fp
);
369 static void ps_end_page(void *handle
, int number
)
371 psdata
*ps
= (psdata
*)handle
;
373 fputs("restore grestore showpage\n", ps
->fp
);
376 static void ps_end_doc(void *handle
)
378 psdata
*ps
= (psdata
*)handle
;
380 fputs("%%EOF\n", ps
->fp
);
383 static const struct drawing_api ps_drawing
= {
389 NULL
/* draw_update */,
392 NULL
/* start_draw */,
394 NULL
/* status_bar */,
395 NULL
/* blitter_new */,
396 NULL
/* blitter_free */,
397 NULL
/* blitter_save */,
398 NULL
/* blitter_load */,
410 psdata
*ps_init(FILE *outfile
, int colour
)
412 psdata
*ps
= snew(psdata
);
418 ps
->hatchthick
= ps
->hatchspace
= ps
->gamewidth
= ps
->gameheight
= 0;
419 ps
->drawing
= drawing_new(&ps_drawing
, NULL
, ps
);
424 void ps_free(psdata
*ps
)
426 drawing_free(ps
->drawing
);
430 drawing
*ps_drawing_api(psdata
*ps
)