printing.c 7.8 KB

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