A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 300 lines 8.0 kB view raw
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}