From 881746789a489fad85aae8317555f73dbe261556 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 29 Apr 2017 18:21:56 -0400 Subject: puzzles: refactor and resync with upstream This brings puzzles up-to-date with upstream revision 2d333750272c3967cfd5cd3677572cddeaad5932, though certain changes made by me, including cursor-only Untangle and some compilation fixes remain. Upstream code has been moved to its separate subdirectory and future syncs can be done by simply copying over the new sources. Change-Id: Ia6506ca5f78c3627165ea6791d38db414ace0804 --- apps/plugins/puzzles/src/ps.c | 433 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 apps/plugins/puzzles/src/ps.c (limited to 'apps/plugins/puzzles/src/ps.c') diff --git a/apps/plugins/puzzles/src/ps.c b/apps/plugins/puzzles/src/ps.c new file mode 100644 index 0000000000..2394cc5e8e --- /dev/null +++ b/apps/plugins/puzzles/src/ps.c @@ -0,0 +1,433 @@ +/* + * ps.c: PostScript printing functions. + */ + +#include +#include +#include +#include + +#include "puzzles.h" + +#define ROOT2 1.414213562 + +struct psdata { + FILE *fp; + int colour; + int ytop; + int clipped; + float hatchthick, hatchspace; + int gamewidth, gameheight; + drawing *drawing; +}; + +static void ps_printf(psdata *ps, 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, 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, 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, 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, int 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, int 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; +} -- cgit v1.2.3