-
Notifications
You must be signed in to change notification settings - Fork 0
/
printing.c
300 lines (258 loc) · 7.76 KB
/
printing.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* 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);
}