Files
puzzles/printing.c
Simon Tatham 5f5b284c0b Use C99 bool within source modules.
This is the main bulk of this boolification work, but although it's
making the largest actual change, it should also be the least
disruptive to anyone interacting with this code base downstream of me,
because it doesn't modify any interface between modules: all the
inter-module APIs were updated one by one in the previous commits.
This just cleans up the code within each individual source file to use
bool in place of int where I think that makes things clearer.
2018-11-13 21:48:24 +00:00

248 lines
6.3 KiB
C

/*
* printing.c: Cross-platform printing manager. Handles document
* setup and layout.
*/
#include "puzzles.h"
struct puzzle {
const game *game;
game_params *par;
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_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_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].st = st;
doc->puzzles[doc->npuzzles].st2 = st2;
doc->npuzzles++;
if (st2)
doc->got_solns = true;
}
static void get_puzzle_size(document *doc, struct puzzle *pz,
float *w, float *h, float *scale)
{
float ww, hh, ourscale;
/* Get the preferred size of the game, in mm. */
pz->game->print_size(pz->par, &ww, &hh);
/* 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;
}
/*
* Having accumulated a load of puzzles, actually do the printing.
*/
void document_print(document *doc, drawing *dr)
{
int ppp; /* puzzles per page */
int pages, passes;
int page, pass;
int pageno;
ppp = doc->pw * doc->ph;
pages = (doc->npuzzles + ppp - 1) / ppp;
passes = (doc->got_solns ? 2 : 1);
print_begin_doc(dr, pages * passes);
pageno = 1;
for (pass = 0; pass < passes; pass++) {
for (page = 0; page < pages; page++) {
int i, n, offset;
float colsum, rowsum;
print_begin_page(dr, pageno);
offset = page * ppp;
n = min(ppp, doc->npuzzles - offset);
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, &pixw, &pixh);
print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize);
print_end_puzzle(dr);
}
print_end_page(dr, pageno);
pageno++;
}
}
print_end_doc(dr);
}