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