summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/printing.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/printing.c')
-rw-r--r--apps/plugins/puzzles/src/printing.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/printing.c b/apps/plugins/puzzles/src/printing.c
new file mode 100644
index 0000000000..e921a4d545
--- /dev/null
+++ b/apps/plugins/puzzles/src/printing.c
@@ -0,0 +1,247 @@
1/*
2 * printing.c: Cross-platform printing manager. Handles document
3 * setup and layout.
4 */
5
6#include "puzzles.h"
7
8struct puzzle {
9 const game *game;
10 game_params *par;
11 game_state *st;
12 game_state *st2;
13};
14
15struct document {
16 int pw, ph;
17 int npuzzles;
18 struct puzzle *puzzles;
19 int puzzlesize;
20 int got_solns;
21 float *colwid, *rowht;
22 float userscale;
23};
24
25/*
26 * Create a new print document. pw and ph are the layout
27 * parameters: they state how many puzzles will be printed across
28 * the page, and down the page.
29 */
30document *document_new(int pw, int ph, float userscale)
31{
32 document *doc = snew(document);
33
34 doc->pw = pw;
35 doc->ph = ph;
36 doc->puzzles = NULL;
37 doc->puzzlesize = doc->npuzzles = 0;
38 doc->got_solns = FALSE;
39
40 doc->colwid = snewn(pw, float);
41 doc->rowht = snewn(ph, float);
42
43 doc->userscale = userscale;
44
45 return doc;
46}
47
48/*
49 * Free a document structure, whether it's been printed or not.
50 */
51void document_free(document *doc)
52{
53 int i;
54
55 for (i = 0; i < doc->npuzzles; i++) {
56 doc->puzzles[i].game->free_params(doc->puzzles[i].par);
57 doc->puzzles[i].game->free_game(doc->puzzles[i].st);
58 if (doc->puzzles[i].st2)
59 doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
60 }
61
62 sfree(doc->colwid);
63 sfree(doc->rowht);
64
65 sfree(doc->puzzles);
66 sfree(doc);
67}
68
69/*
70 * Called from midend.c to add a puzzle to be printed. Provides a
71 * game_params (for initial layout computation), a game_state, and
72 * optionally a second game_state to be printed in parallel on
73 * another sheet (typically the solution to the first game_state).
74 */
75void document_add_puzzle(document *doc, const game *game, game_params *par,
76 game_state *st, game_state *st2)
77{
78 if (doc->npuzzles >= doc->puzzlesize) {
79 doc->puzzlesize += 32;
80 doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle);
81 }
82 doc->puzzles[doc->npuzzles].game = game;
83 doc->puzzles[doc->npuzzles].par = par;
84 doc->puzzles[doc->npuzzles].st = st;
85 doc->puzzles[doc->npuzzles].st2 = st2;
86 doc->npuzzles++;
87 if (st2)
88 doc->got_solns = TRUE;
89}
90
91static void get_puzzle_size(document *doc, struct puzzle *pz,
92 float *w, float *h, float *scale)
93{
94 float ww, hh, ourscale;
95
96 /* Get the preferred size of the game, in mm. */
97 pz->game->print_size(pz->par, &ww, &hh);
98
99 /* Adjust for user-supplied scale factor. */
100 ourscale = doc->userscale;
101
102 /*
103 * FIXME: scale it down here if it's too big for the page size.
104 * Rather than do complicated things involving scaling all
105 * columns down in proportion, the simplest approach seems to
106 * me to be to scale down until the game fits within one evenly
107 * divided cell of the page (i.e. width/pw by height/ph).
108 *
109 * In order to do this step we need the page size available.
110 */
111
112 *scale = ourscale;
113 *w = ww * ourscale;
114 *h = hh * ourscale;
115}
116
117/*
118 * Having accumulated a load of puzzles, actually do the printing.
119 */
120void document_print(document *doc, drawing *dr)
121{
122 int ppp; /* puzzles per page */
123 int pages, passes;
124 int page, pass;
125 int pageno;
126
127 ppp = doc->pw * doc->ph;
128 pages = (doc->npuzzles + ppp - 1) / ppp;
129 passes = (doc->got_solns ? 2 : 1);
130
131 print_begin_doc(dr, pages * passes);
132
133 pageno = 1;
134 for (pass = 0; pass < passes; pass++) {
135 for (page = 0; page < pages; page++) {
136 int i, n, offset;
137 float colsum, rowsum;
138
139 print_begin_page(dr, pageno);
140
141 offset = page * ppp;
142 n = min(ppp, doc->npuzzles - offset);
143
144 for (i = 0; i < doc->pw; i++)
145 doc->colwid[i] = 0;
146 for (i = 0; i < doc->ph; i++)
147 doc->rowht[i] = 0;
148
149 /*
150 * Lay the page out by computing all the puzzle sizes.
151 */
152 for (i = 0; i < n; i++) {
153 struct puzzle *pz = doc->puzzles + offset + i;
154 int x = i % doc->pw, y = i / doc->pw;
155 float w, h, scale;
156
157 get_puzzle_size(doc, pz, &w, &h, &scale);
158
159 /* Update the maximum width/height of this column. */
160 doc->colwid[x] = max(doc->colwid[x], w);
161 doc->rowht[y] = max(doc->rowht[y], h);
162 }
163
164 /*
165 * Add up the maximum column/row widths to get the
166 * total amount of space used up by puzzles on the
167 * page. We will use this to compute gutter widths.
168 */
169 colsum = 0.0;
170 for (i = 0; i < doc->pw; i++)
171 colsum += doc->colwid[i];
172 rowsum = 0.0;
173 for (i = 0; i < doc->ph; i++)
174 rowsum += doc->rowht[i];
175
176 /*
177 * Now do the printing.
178 */
179 for (i = 0; i < n; i++) {
180 struct puzzle *pz = doc->puzzles + offset + i;
181 int x = i % doc->pw, y = i / doc->pw, j;
182 float w, h, scale, xm, xc, ym, yc;
183 int pixw, pixh, tilesize;
184
185 if (pass == 1 && !pz->st2)
186 continue; /* nothing to do */
187
188 /*
189 * The total amount of gutter space is the page
190 * width minus colsum. This is divided into pw+1
191 * gutters, so the amount of horizontal gutter
192 * space appearing to the left of this puzzle
193 * column is
194 *
195 * (width-colsum) * (x+1)/(pw+1)
196 * = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1))
197 */
198 xm = (float)(x+1) / (doc->pw + 1);
199 xc = -xm * colsum;
200 /* And similarly for y. */
201 ym = (float)(y+1) / (doc->ph + 1);
202 yc = -ym * rowsum;
203
204 /*
205 * However, the amount of space to the left of this
206 * puzzle isn't just gutter space: we must also
207 * count the widths of all the previous columns.
208 */
209 for (j = 0; j < x; j++)
210 xc += doc->colwid[j];
211 /* And similarly for rows. */
212 for (j = 0; j < y; j++)
213 yc += doc->rowht[j];
214
215 /*
216 * Now we adjust for this _specific_ puzzle, which
217 * means centring it within the cell we've just
218 * computed.
219 */
220 get_puzzle_size(doc, pz, &w, &h, &scale);
221 xc += (doc->colwid[x] - w) / 2;
222 yc += (doc->rowht[y] - h) / 2;
223
224 /*
225 * And now we know where and how big we want to
226 * print the puzzle, just go ahead and do so. For
227 * the moment I'll pick a standard pixel tile size
228 * of 512.
229 *
230 * (FIXME: would it be better to pick this value
231 * with reference to the printer resolution? Or
232 * permit each game to choose its own?)
233 */
234 tilesize = 512;
235 pz->game->compute_size(pz->par, tilesize, &pixw, &pixh);
236 print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
237 pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize);
238 print_end_puzzle(dr);
239 }
240
241 print_end_page(dr, pageno);
242 pageno++;
243 }
244 }
245
246 print_end_doc(dr);
247}