+ fe->drawstatus = NOTHING;
+}
+
+static void win_line_width(void *handle, float width)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ fe->linewidth = (int)(width * fe->printpixelscale);
+}
+
+static void win_begin_doc(void *handle, int pages)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ if (StartDoc(fe->hdc, &fe->di) <= 0) {
+ char *e = geterrstr();
+ MessageBox(fe->hwnd, e, "Error starting to print",
+ MB_ICONERROR | MB_OK);
+ sfree(e);
+ fe->drawstatus = NOTHING;
+ }
+
+ /*
+ * Push a marker on the font stack so that we won't use the
+ * same fonts for printing and drawing. (This is because
+ * drawing seems to look generally better in bold, but printing
+ * is better not in bold.)
+ */
+ fe->fontstart = fe->nfonts;
+}
+
+static void win_begin_page(void *handle, int number)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ if (StartPage(fe->hdc) <= 0) {
+ char *e = geterrstr();
+ MessageBox(fe->hwnd, e, "Error starting a page",
+ MB_ICONERROR | MB_OK);
+ sfree(e);
+ fe->drawstatus = NOTHING;
+ }
+}
+
+static void win_begin_puzzle(void *handle, float xm, float xc,
+ float ym, float yc, int pw, int ph, float wmm)
+{
+ frontend *fe = (frontend *)handle;
+ int ppw, pph, pox, poy;
+ float mmpw, mmph, mmox, mmoy;
+ float scale;
+
+ assert(fe->drawstatus != DRAWING);
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ ppw = GetDeviceCaps(fe->hdc, HORZRES);
+ pph = GetDeviceCaps(fe->hdc, VERTRES);
+ mmpw = (float)GetDeviceCaps(fe->hdc, HORZSIZE);
+ mmph = (float)GetDeviceCaps(fe->hdc, VERTSIZE);
+
+ /*
+ * Compute the puzzle's position on the logical page.
+ */
+ mmox = xm * mmpw + xc;
+ mmoy = ym * mmph + yc;
+
+ /*
+ * Work out what that comes to in pixels.
+ */
+ pox = (int)(mmox * (float)ppw / mmpw);
+ poy = (int)(mmoy * (float)ppw / mmpw);
+
+ /*
+ * And determine the scale.
+ *
+ * I need a scale such that the maximum puzzle-coordinate
+ * extent of the rectangle (pw * scale) is equal to the pixel
+ * equivalent of the puzzle's millimetre width (wmm * ppw /
+ * mmpw).
+ */
+ scale = (wmm * ppw) / (mmpw * pw);
+
+ /*
+ * Now store pox, poy and scale for use in the main drawing
+ * functions.
+ */
+ fe->printoffsetx = pox;
+ fe->printoffsety = poy;
+ fe->printpixelscale = scale;
+
+ fe->linewidth = 1;
+}
+
+static void win_end_puzzle(void *handle)
+{
+ /* Nothing needs to be done here. */
+}
+
+static void win_end_page(void *handle, int number)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ if (EndPage(fe->hdc) <= 0) {
+ char *e = geterrstr();
+ MessageBox(fe->hwnd, e, "Error finishing a page",
+ MB_ICONERROR | MB_OK);
+ sfree(e);
+ fe->drawstatus = NOTHING;
+ }
+}
+
+static void win_end_doc(void *handle)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+
+ /*
+ * Free all the fonts created since we began printing.
+ */
+ while (fe->nfonts > fe->fontstart) {
+ fe->nfonts--;
+ DeleteObject(fe->fonts[fe->nfonts].font);
+ }
+ fe->fontstart = 0;
+
+ /*
+ * The MSDN web site sample code doesn't bother to call EndDoc
+ * if an error occurs half way through printing. I expect doing
+ * so would cause the erroneous document to actually be
+ * printed, or something equally undesirable.
+ */
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ if (EndDoc(fe->hdc) <= 0) {
+ char *e = geterrstr();
+ MessageBox(fe->hwnd, e, "Error finishing printing",
+ MB_ICONERROR | MB_OK);
+ sfree(e);
+ fe->drawstatus = NOTHING;
+ }
+}
+
+const struct drawing_api win_drawing = {
+ win_draw_text,
+ win_draw_rect,
+ win_draw_line,
+ win_draw_polygon,
+ win_draw_circle,
+ win_draw_update,
+ win_clip,
+ win_unclip,
+ win_start_draw,
+ win_end_draw,
+ win_status_bar,
+ win_blitter_new,
+ win_blitter_free,
+ win_blitter_save,
+ win_blitter_load,
+ win_begin_doc,
+ win_begin_page,
+ win_begin_puzzle,
+ win_end_puzzle,
+ win_end_page,
+ win_end_doc,
+ win_line_width,
+};
+
+void print(frontend *fe)
+{
+ PRINTDLG pd;
+ char doctitle[256];
+ document *doc;
+ midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
+ int i;
+ char *err = NULL;
+
+ /*
+ * Create our document structure and fill it up with puzzles.
+ */
+ doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
+ for (i = 0; i < fe->printcount; i++) {
+ if (i == 0 && fe->printcurr) {
+ err = midend_print_puzzle(fe->me, doc, fe->printsolns);
+ } else {
+ if (!nme) {
+ game_params *params;
+
+ nme = midend_new(NULL, &thegame, NULL, NULL);
+
+ /*
+ * Set the non-interactive mid-end to have the same
+ * parameters as the standard one.
+ */
+ params = midend_get_params(fe->me);
+ midend_set_params(nme, params);
+ thegame.free_params(params);
+ }
+
+ midend_new_game(nme);
+ err = midend_print_puzzle(nme, doc, fe->printsolns);
+ }
+ if (err)
+ break;
+ }
+ if (nme)
+ midend_free(nme);
+
+ if (err) {
+ MessageBox(fe->hwnd, err, "Error preparing puzzles for printing",
+ MB_ICONERROR | MB_OK);
+ document_free(doc);
+ return;
+ }
+
+ memset(&pd, 0, sizeof(pd));
+ pd.lStructSize = sizeof(pd);
+ pd.hwndOwner = fe->hwnd;
+ pd.hDevMode = NULL;
+ pd.hDevNames = NULL;
+ pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC |
+ PD_NOPAGENUMS | PD_NOSELECTION;
+ pd.nCopies = 1;
+ pd.nFromPage = pd.nToPage = 0xFFFF;
+ pd.nMinPage = pd.nMaxPage = 1;
+
+ if (!PrintDlg(&pd)) {
+ document_free(doc);
+ return;
+ }
+
+ /*
+ * Now pd.hDC is a device context for the printer.
+ */
+
+ /*
+ * FIXME: IWBNI we put up an Abort box here.
+ */
+
+ memset(&fe->di, 0, sizeof(fe->di));
+ fe->di.cbSize = sizeof(fe->di);
+ sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's"
+ " Portable Puzzle Collection)", thegame.name);
+ fe->di.lpszDocName = doctitle;
+ fe->di.lpszOutput = NULL;
+ fe->di.lpszDatatype = NULL;
+ fe->di.fwType = 0;
+
+ fe->drawstatus = PRINTING;
+ fe->hdc = pd.hDC;
+
+ fe->dr = drawing_init(&win_drawing, fe);
+ document_print(doc, fe->dr);
+ drawing_free(fe->dr);
+ fe->dr = NULL;
+
+ fe->drawstatus = NOTHING;
+
+ DeleteDC(pd.hDC);
+ document_free(doc);