123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /*
- * printing.c: Cross-platform printing manager. Handles document
- * setup and layout.
- */
- #include <assert.h>
- #include "puzzles.h"
- struct puzzle {
- const game *game;
- game_params *par;
- game_ui *ui;
- game_state *st;
- game_state *st2;
- };
- struct document {
- int pw, ph;
- int npuzzles;
- struct puzzle *puzzles;
- int puzzlesize;
- bool got_solns;
- float *colwid, *rowht;
- float userscale;
- };
- /*
- * Create a new print document. pw and ph are the layout
- * parameters: they state how many puzzles will be printed across
- * the page, and down the page.
- */
- document *document_new(int pw, int ph, float userscale)
- {
- document *doc = snew(document);
- doc->pw = pw;
- doc->ph = ph;
- doc->puzzles = NULL;
- doc->puzzlesize = doc->npuzzles = 0;
- doc->got_solns = false;
- doc->colwid = snewn(pw, float);
- doc->rowht = snewn(ph, float);
- doc->userscale = userscale;
- return doc;
- }
- /*
- * Free a document structure, whether it's been printed or not.
- */
- void document_free(document *doc)
- {
- int i;
- for (i = 0; i < doc->npuzzles; i++) {
- doc->puzzles[i].game->free_params(doc->puzzles[i].par);
- doc->puzzles[i].game->free_ui(doc->puzzles[i].ui);
- doc->puzzles[i].game->free_game(doc->puzzles[i].st);
- if (doc->puzzles[i].st2)
- doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
- }
- sfree(doc->colwid);
- sfree(doc->rowht);
- sfree(doc->puzzles);
- sfree(doc);
- }
- /*
- * Called from midend.c to add a puzzle to be printed. Provides a
- * game_params (for initial layout computation), a game_state, and
- * optionally a second game_state to be printed in parallel on
- * another sheet (typically the solution to the first game_state).
- */
- void document_add_puzzle(document *doc, const game *game, game_params *par,
- game_ui *ui, game_state *st, game_state *st2)
- {
- if (doc->npuzzles >= doc->puzzlesize) {
- doc->puzzlesize += 32;
- doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle);
- }
- doc->puzzles[doc->npuzzles].game = game;
- doc->puzzles[doc->npuzzles].par = par;
- doc->puzzles[doc->npuzzles].ui = ui;
- doc->puzzles[doc->npuzzles].st = st;
- doc->puzzles[doc->npuzzles].st2 = st2;
- doc->npuzzles++;
- if (st2)
- doc->got_solns = true;
- }
- static void get_puzzle_size(const document *doc, struct puzzle *pz,
- float *w, float *h, float *scale)
- {
- float ww, hh, ourscale;
- /* Get the preferred size of the game, in mm. */
- {
- game_ui *ui = pz->game->new_ui(pz->st);
- pz->game->print_size(pz->par, ui, &ww, &hh);
- pz->game->free_ui(ui);
- }
- /* Adjust for user-supplied scale factor. */
- ourscale = doc->userscale;
- /*
- * FIXME: scale it down here if it's too big for the page size.
- * Rather than do complicated things involving scaling all
- * columns down in proportion, the simplest approach seems to
- * me to be to scale down until the game fits within one evenly
- * divided cell of the page (i.e. width/pw by height/ph).
- *
- * In order to do this step we need the page size available.
- */
- *scale = ourscale;
- *w = ww * ourscale;
- *h = hh * ourscale;
- }
- /*
- * Calculate the the number of pages for a document.
- */
- int document_npages(const document *doc)
- {
- int ppp; /* puzzles per page */
- int pages, passes;
- ppp = doc->pw * doc->ph;
- pages = (doc->npuzzles + ppp - 1) / ppp;
- passes = (doc->got_solns ? 2 : 1);
- return pages * passes;
- }
- /*
- * Begin a document.
- */
- void document_begin(const document *doc, drawing *dr)
- {
- print_begin_doc(dr, document_npages(doc));
- }
- /*
- * End a document.
- */
- void document_end(const document *doc, drawing *dr)
- {
- print_end_doc(dr);
- }
- /*
- * Print a single page of a document.
- */
- void document_print_page(const document *doc, drawing *dr, int page_nr)
- {
- int ppp; /* puzzles per page */
- int pages;
- int page, pass;
- int pageno;
- int i, n, offset;
- float colsum, rowsum;
- ppp = doc->pw * doc->ph;
- pages = (doc->npuzzles + ppp - 1) / ppp;
- /* Get the current page, pass, and pageno based on page_nr. */
- if (page_nr < pages) {
- page = page_nr;
- pass = 0;
- }
- else {
- assert(doc->got_solns);
- page = page_nr - pages;
- pass = 1;
- }
- pageno = page_nr + 1;
- offset = page * ppp;
- n = min(ppp, doc->npuzzles - offset);
- print_begin_page(dr, pageno);
- for (i = 0; i < doc->pw; i++)
- doc->colwid[i] = 0;
- for (i = 0; i < doc->ph; i++)
- doc->rowht[i] = 0;
- /*
- * Lay the page out by computing all the puzzle sizes.
- */
- for (i = 0; i < n; i++) {
- struct puzzle *pz = doc->puzzles + offset + i;
- int x = i % doc->pw, y = i / doc->pw;
- float w, h, scale;
- get_puzzle_size(doc, pz, &w, &h, &scale);
- /* Update the maximum width/height of this column. */
- doc->colwid[x] = max(doc->colwid[x], w);
- doc->rowht[y] = max(doc->rowht[y], h);
- }
- /*
- * Add up the maximum column/row widths to get the
- * total amount of space used up by puzzles on the
- * page. We will use this to compute gutter widths.
- */
- colsum = 0.0;
- for (i = 0; i < doc->pw; i++)
- colsum += doc->colwid[i];
- rowsum = 0.0;
- for (i = 0; i < doc->ph; i++)
- rowsum += doc->rowht[i];
- /*
- * Now do the printing.
- */
- for (i = 0; i < n; i++) {
- struct puzzle *pz = doc->puzzles + offset + i;
- int x = i % doc->pw, y = i / doc->pw, j;
- float w, h, scale, xm, xc, ym, yc;
- int pixw, pixh, tilesize;
- if (pass == 1 && !pz->st2)
- continue; /* nothing to do */
- /*
- * The total amount of gutter space is the page
- * width minus colsum. This is divided into pw+1
- * gutters, so the amount of horizontal gutter
- * space appearing to the left of this puzzle
- * column is
- *
- * (width-colsum) * (x+1)/(pw+1)
- * = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1))
- */
- xm = (float)(x+1) / (doc->pw + 1);
- xc = -xm * colsum;
- /* And similarly for y. */
- ym = (float)(y+1) / (doc->ph + 1);
- yc = -ym * rowsum;
- /*
- * However, the amount of space to the left of this
- * puzzle isn't just gutter space: we must also
- * count the widths of all the previous columns.
- */
- for (j = 0; j < x; j++)
- xc += doc->colwid[j];
- /* And similarly for rows. */
- for (j = 0; j < y; j++)
- yc += doc->rowht[j];
- /*
- * Now we adjust for this _specific_ puzzle, which
- * means centring it within the cell we've just
- * computed.
- */
- get_puzzle_size(doc, pz, &w, &h, &scale);
- xc += (doc->colwid[x] - w) / 2;
- yc += (doc->rowht[y] - h) / 2;
- /*
- * And now we know where and how big we want to
- * print the puzzle, just go ahead and do so. For
- * the moment I'll pick a standard pixel tile size
- * of 512.
- *
- * (FIXME: would it be better to pick this value
- * with reference to the printer resolution? Or
- * permit each game to choose its own?)
- */
- tilesize = 512;
- pz->game->compute_size(pz->par, tilesize, pz->ui, &pixw, &pixh);
- print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
- pz->game->print(dr, pass == 0 ? pz->st : pz->st2, pz->ui, tilesize);
- print_end_puzzle(dr);
- }
- print_end_page(dr, pageno);
- }
- /*
- * Having accumulated a load of puzzles, actually do the printing.
- */
- void document_print(const document *doc, drawing *dr)
- {
- int page, pages;
- pages = document_npages(doc);
- print_begin_doc(dr, pages);
- for (page = 0; page < pages; page++)
- document_print_page(doc, dr, page);
- print_end_doc(dr);
- }
|