123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /*
- * ps.c: PostScript printing functions.
- */
- #include <stdio.h>
- #include <stdarg.h>
- #include <string.h>
- #include <assert.h>
- #include "puzzles.h"
- struct psdata {
- FILE *fp;
- bool colour;
- int ytop;
- bool clipped;
- float hatchthick, hatchspace;
- int gamewidth, gameheight;
- drawing *drawing;
- };
- static void ps_printf(psdata *ps, const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(ps->fp, fmt, ap);
- va_end(ap);
- }
- static void ps_fill(psdata *ps, int colour)
- {
- int hatch;
- float r, g, b;
- print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
- if (hatch < 0) {
- if (ps->colour)
- ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b);
- else
- ps_printf(ps, "%g setgray fill\n", r);
- } else {
- /* Clip to the region. */
- ps_printf(ps, "gsave clip\n");
- /* Hatch the entire game printing area. */
- ps_printf(ps, "newpath\n");
- if (hatch == HATCH_VERT || hatch == HATCH_PLUS)
- ps_printf(ps, "0 %g %d {\n"
- " 0 moveto 0 %d rlineto\n"
- "} for\n", ps->hatchspace, ps->gamewidth,
- ps->gameheight);
- if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS)
- ps_printf(ps, "0 %g %d {\n"
- " 0 exch moveto %d 0 rlineto\n"
- "} for\n", ps->hatchspace, ps->gameheight,
- ps->gamewidth);
- if (hatch == HATCH_SLASH || hatch == HATCH_X)
- ps_printf(ps, "%d %g %d {\n"
- " 0 moveto %d dup rlineto\n"
- "} for\n", -ps->gameheight, ps->hatchspace * ROOT2,
- ps->gamewidth, max(ps->gamewidth, ps->gameheight));
- if (hatch == HATCH_BACKSLASH || hatch == HATCH_X)
- ps_printf(ps, "0 %g %d {\n"
- " 0 moveto %d neg dup neg rlineto\n"
- "} for\n", ps->hatchspace * ROOT2,
- ps->gamewidth+ps->gameheight,
- max(ps->gamewidth, ps->gameheight));
- ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n",
- ps->hatchthick);
- }
- }
- static void ps_setcolour_internal(psdata *ps, int colour, const char *suffix)
- {
- int hatch;
- float r, g, b;
- print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
- /*
- * Stroking in hatched colours is not permitted.
- */
- assert(hatch < 0);
-
- if (ps->colour)
- ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix);
- else
- ps_printf(ps, "%g setgray%s\n", r, suffix);
- }
- static void ps_setcolour(psdata *ps, int colour)
- {
- ps_setcolour_internal(ps, colour, "");
- }
- static void ps_stroke(psdata *ps, int colour)
- {
- ps_setcolour_internal(ps, colour, " stroke");
- }
- static void ps_draw_text(void *handle, int x, int y, int fonttype,
- int fontsize, int align, int colour,
- const char *text)
- {
- psdata *ps = (psdata *)handle;
- y = ps->ytop - y;
- ps_setcolour(ps, colour);
- ps_printf(ps, "/%s findfont %d scalefont setfont\n",
- fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1",
- fontsize);
- if (align & ALIGN_VCENTRE) {
- ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
- " pathbbox\n"
- "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
- y, x);
- } else {
- ps_printf(ps, "%d %d moveto\n", x, y);
- }
- ps_printf(ps, "(");
- while (*text) {
- if (*text == '\\' || *text == '(' || *text == ')')
- ps_printf(ps, "\\");
- ps_printf(ps, "%c", *text);
- text++;
- }
- ps_printf(ps, ") ");
- if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT))
- ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n",
- (align & ALIGN_HCENTRE) ? "2 div " : "");
- else
- ps_printf(ps, "show\n");
- }
- static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
- {
- psdata *ps = (psdata *)handle;
- y = ps->ytop - y;
- /*
- * Offset by half a pixel for the exactness requirement.
- */
- ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
- " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
- ps_fill(ps, colour);
- }
- static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
- int colour)
- {
- psdata *ps = (psdata *)handle;
- y1 = ps->ytop - y1;
- y2 = ps->ytop - y2;
- ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2);
- ps_stroke(ps, colour);
- }
- static void ps_draw_polygon(void *handle, const int *coords, int npoints,
- int fillcolour, int outlinecolour)
- {
- psdata *ps = (psdata *)handle;
- int i;
- ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]);
- for (i = 1; i < npoints; i++)
- ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]);
- ps_printf(ps, "closepath\n");
- if (fillcolour >= 0) {
- ps_printf(ps, "gsave\n");
- ps_fill(ps, fillcolour);
- ps_printf(ps, "grestore\n");
- }
- ps_stroke(ps, outlinecolour);
- }
- static void ps_draw_circle(void *handle, int cx, int cy, int radius,
- int fillcolour, int outlinecolour)
- {
- psdata *ps = (psdata *)handle;
- cy = ps->ytop - cy;
- ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius);
- if (fillcolour >= 0) {
- ps_printf(ps, "gsave\n");
- ps_fill(ps, fillcolour);
- ps_printf(ps, "grestore\n");
- }
- ps_stroke(ps, outlinecolour);
- }
- static void ps_unclip(void *handle)
- {
- psdata *ps = (psdata *)handle;
- assert(ps->clipped);
- ps_printf(ps, "grestore\n");
- ps->clipped = false;
- }
-
- static void ps_clip(void *handle, int x, int y, int w, int h)
- {
- psdata *ps = (psdata *)handle;
- if (ps->clipped)
- ps_unclip(ps);
- y = ps->ytop - y;
- /*
- * Offset by half a pixel for the exactness requirement.
- */
- ps_printf(ps, "gsave\n");
- ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
- " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
- ps_printf(ps, "clip\n");
- ps->clipped = true;
- }
- static void ps_line_width(void *handle, float width)
- {
- psdata *ps = (psdata *)handle;
- ps_printf(ps, "%g setlinewidth\n", width);
- }
- static void ps_line_dotted(void *handle, bool dotted)
- {
- psdata *ps = (psdata *)handle;
- if (dotted) {
- ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n");
- } else {
- ps_printf(ps, "[ ] 0 setdash\n");
- }
- }
- static char *ps_text_fallback(void *handle, const char *const *strings,
- int nstrings)
- {
- /*
- * We can handle anything in ISO 8859-1, and we'll manually
- * translate it out of UTF-8 for the purpose.
- */
- int i, maxlen;
- char *ret;
- maxlen = 0;
- for (i = 0; i < nstrings; i++) {
- int len = strlen(strings[i]);
- if (maxlen < len) maxlen = len;
- }
- ret = snewn(maxlen + 1, char);
- for (i = 0; i < nstrings; i++) {
- const char *p = strings[i];
- char *q = ret;
- while (*p) {
- int c = (unsigned char)*p++;
- if (c < 0x80) {
- *q++ = c; /* ASCII */
- } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) {
- *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */
- } else {
- break;
- }
- }
- if (!*p) {
- *q = '\0';
- return ret;
- }
- }
- assert(!"Should never reach here");
- return NULL;
- }
- static void ps_begin_doc(void *handle, int pages)
- {
- psdata *ps = (psdata *)handle;
- fputs("%!PS-Adobe-3.0\n", ps->fp);
- fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
- fputs("%%DocumentData: Clean7Bit\n", ps->fp);
- fputs("%%LanguageLevel: 1\n", ps->fp);
- fprintf(ps->fp, "%%%%Pages: %d\n", pages);
- fputs("%%DocumentNeededResources:\n", ps->fp);
- fputs("%%+ font Helvetica\n", ps->fp);
- fputs("%%+ font Courier\n", ps->fp);
- fputs("%%EndComments\n", ps->fp);
- fputs("%%BeginSetup\n", ps->fp);
- fputs("%%IncludeResource: font Helvetica\n", ps->fp);
- fputs("%%IncludeResource: font Courier\n", ps->fp);
- fputs("%%EndSetup\n", ps->fp);
- fputs("%%BeginProlog\n", ps->fp);
- /*
- * Re-encode Helvetica and Courier into ISO-8859-1, which gives
- * us times and divide signs - and also (according to the
- * Language Reference Manual) a bonus in that the ASCII '-' code
- * point now points to a minus sign instead of a hyphen.
- */
- fputs("/Helvetica findfont " /* get the font dictionary */
- "dup maxlength dict dup begin " /* create and open a new dict */
- "exch " /* move the original font to top of stack */
- "{1 index /FID ne {def} {pop pop} ifelse} forall "
- /* copy everything except FID */
- "/Encoding ISOLatin1Encoding def "
- /* set the thing we actually wanted to change */
- "/FontName /Helvetica-L1 def " /* set a new font name */
- "FontName end exch definefont" /* and define the font */
- "\n", ps->fp);
- fputs("/Courier findfont " /* get the font dictionary */
- "dup maxlength dict dup begin " /* create and open a new dict */
- "exch " /* move the original font to top of stack */
- "{1 index /FID ne {def} {pop pop} ifelse} forall "
- /* copy everything except FID */
- "/Encoding ISOLatin1Encoding def "
- /* set the thing we actually wanted to change */
- "/FontName /Courier-L1 def " /* set a new font name */
- "FontName end exch definefont" /* and define the font */
- "\n", ps->fp);
- fputs("%%EndProlog\n", ps->fp);
- }
- static void ps_begin_page(void *handle, int number)
- {
- psdata *ps = (psdata *)handle;
- fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
- number, number, 72.0 / 25.4);
- }
- static void ps_begin_puzzle(void *handle, float xm, float xc,
- float ym, float yc, int pw, int ph, float wmm)
- {
- psdata *ps = (psdata *)handle;
- fprintf(ps->fp, "gsave\n"
- "clippath flattenpath pathbbox pop pop translate\n"
- "clippath flattenpath pathbbox 4 2 roll pop pop\n"
- "exch %g mul %g add exch dup %g mul %g add sub translate\n"
- "%g dup scale\n"
- "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph);
- ps->ytop = ph;
- ps->clipped = false;
- ps->gamewidth = pw;
- ps->gameheight = ph;
- ps->hatchthick = 0.2 * pw / wmm;
- ps->hatchspace = 1.0 * pw / wmm;
- }
- static void ps_end_puzzle(void *handle)
- {
- psdata *ps = (psdata *)handle;
- fputs("grestore\n", ps->fp);
- }
- static void ps_end_page(void *handle, int number)
- {
- psdata *ps = (psdata *)handle;
- fputs("restore grestore showpage\n", ps->fp);
- }
- static void ps_end_doc(void *handle)
- {
- psdata *ps = (psdata *)handle;
- fputs("%%EOF\n", ps->fp);
- }
- static const struct drawing_api ps_drawing = {
- ps_draw_text,
- ps_draw_rect,
- ps_draw_line,
- ps_draw_polygon,
- ps_draw_circle,
- NULL /* draw_update */,
- ps_clip,
- ps_unclip,
- NULL /* start_draw */,
- NULL /* end_draw */,
- NULL /* status_bar */,
- NULL /* blitter_new */,
- NULL /* blitter_free */,
- NULL /* blitter_save */,
- NULL /* blitter_load */,
- ps_begin_doc,
- ps_begin_page,
- ps_begin_puzzle,
- ps_end_puzzle,
- ps_end_page,
- ps_end_doc,
- ps_line_width,
- ps_line_dotted,
- ps_text_fallback,
- };
- psdata *ps_init(FILE *outfile, bool colour)
- {
- psdata *ps = snew(psdata);
- ps->fp = outfile;
- ps->colour = colour;
- ps->ytop = 0;
- ps->clipped = false;
- ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0;
- ps->drawing = drawing_new(&ps_drawing, NULL, ps);
- return ps;
- }
- void ps_free(psdata *ps)
- {
- drawing_free(ps->drawing);
- sfree(ps);
- }
- drawing *ps_drawing_api(psdata *ps)
- {
- return ps->drawing;
- }
|