dafd6cf6 |
1 | /* |
2 | * printing.c: Cross-platform printing manager. Handles document |
3 | * setup and layout. |
4 | */ |
5 | |
6 | #include "puzzles.h" |
7 | |
8 | struct puzzle { |
9 | const game *game; |
10 | game_params *par; |
11 | game_state *st; |
12 | game_state *st2; |
13 | }; |
14 | |
15 | struct 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 | */ |
30 | document *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 | */ |
51 | void 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 | */ |
75 | void 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 | |
91 | static 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 | */ |
120 | void 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 | } |