diff options
Diffstat (limited to 'apps/plugins/puzzles/src/printing.c')
-rw-r--r-- | apps/plugins/puzzles/src/printing.c | 247 |
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 | |||
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 | } | ||