@kensio/colophon 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +62 -801
  2. package/dist/background.d.ts +7 -2
  3. package/dist/background.d.ts.map +1 -1
  4. package/dist/background.js +44 -1
  5. package/dist/background.js.map +1 -1
  6. package/dist/cli/args.d.ts +1 -1
  7. package/dist/cli/args.d.ts.map +1 -1
  8. package/dist/cli/args.js +1 -1
  9. package/dist/cli/config.d.ts +1 -1
  10. package/dist/cli/config.js +1 -1
  11. package/dist/config/defaults.d.ts +8 -7
  12. package/dist/config/defaults.d.ts.map +1 -1
  13. package/dist/config/defaults.js +8 -8
  14. package/dist/config/defaults.js.map +1 -1
  15. package/dist/config/index.d.ts.map +1 -1
  16. package/dist/config/index.js +5 -3
  17. package/dist/config/index.js.map +1 -1
  18. package/dist/config/resolve.d.ts +7 -0
  19. package/dist/config/resolve.d.ts.map +1 -1
  20. package/dist/config/resolve.js +16 -0
  21. package/dist/config/resolve.js.map +1 -1
  22. package/dist/config/size.d.ts +1 -1
  23. package/dist/config/size.d.ts.map +1 -1
  24. package/dist/config/size.js +3 -2
  25. package/dist/config/size.js.map +1 -1
  26. package/dist/content/props.d.ts +2 -2
  27. package/dist/content/props.js +2 -2
  28. package/dist/content/slug.d.ts +5 -5
  29. package/dist/content/slug.js +6 -6
  30. package/dist/content/slug.js.map +1 -1
  31. package/dist/content/walk.d.ts +1 -1
  32. package/dist/content/walk.js +1 -1
  33. package/dist/fonts/index.d.ts +3 -2
  34. package/dist/fonts/index.d.ts.map +1 -1
  35. package/dist/fonts/index.js +3 -2
  36. package/dist/fonts/index.js.map +1 -1
  37. package/dist/fonts/resolve.js +1 -1
  38. package/dist/generate/extra.js +4 -4
  39. package/dist/generate/extra.js.map +1 -1
  40. package/dist/generate/options.d.ts +1 -1
  41. package/dist/generate/outputs.d.ts +4 -4
  42. package/dist/generate/outputs.js +4 -4
  43. package/dist/highlight/language.d.ts +2 -3
  44. package/dist/highlight/language.d.ts.map +1 -1
  45. package/dist/highlight/language.js +3 -4
  46. package/dist/highlight/language.js.map +1 -1
  47. package/dist/highlight/normalise.d.ts +2 -2
  48. package/dist/highlight/normalise.js +2 -2
  49. package/dist/image/index.d.ts +18 -0
  50. package/dist/image/index.d.ts.map +1 -0
  51. package/dist/image/index.js +42 -0
  52. package/dist/image/index.js.map +1 -0
  53. package/dist/image/jpeg.d.ts +10 -0
  54. package/dist/image/jpeg.d.ts.map +1 -0
  55. package/dist/image/jpeg.js +57 -0
  56. package/dist/image/jpeg.js.map +1 -0
  57. package/dist/image/load.d.ts +4 -0
  58. package/dist/image/load.d.ts.map +1 -0
  59. package/dist/image/load.js +60 -0
  60. package/dist/image/load.js.map +1 -0
  61. package/dist/image/media.d.ts +10 -0
  62. package/dist/image/media.d.ts.map +1 -0
  63. package/dist/image/media.js +37 -0
  64. package/dist/image/media.js.map +1 -0
  65. package/dist/image/resolve.d.ts +13 -0
  66. package/dist/image/resolve.d.ts.map +1 -0
  67. package/dist/image/resolve.js +38 -0
  68. package/dist/image/resolve.js.map +1 -0
  69. package/dist/image/size.d.ts +22 -0
  70. package/dist/image/size.d.ts.map +1 -0
  71. package/dist/image/size.js +60 -0
  72. package/dist/image/size.js.map +1 -0
  73. package/dist/image/svg.d.ts +12 -0
  74. package/dist/image/svg.d.ts.map +1 -0
  75. package/dist/image/svg.js +61 -0
  76. package/dist/image/svg.js.map +1 -0
  77. package/dist/image/uri.d.ts +9 -0
  78. package/dist/image/uri.d.ts.map +1 -0
  79. package/dist/image/uri.js +22 -0
  80. package/dist/image/uri.js.map +1 -0
  81. package/dist/index.d.ts +6 -2
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +5 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/layout/block.d.ts +37 -0
  86. package/dist/layout/block.d.ts.map +1 -0
  87. package/dist/layout/block.js +26 -0
  88. package/dist/layout/block.js.map +1 -0
  89. package/dist/layout/box.d.ts +20 -0
  90. package/dist/layout/box.d.ts.map +1 -0
  91. package/dist/layout/box.js +61 -0
  92. package/dist/layout/box.js.map +1 -0
  93. package/dist/layout/distribute.d.ts +15 -0
  94. package/dist/layout/distribute.d.ts.map +1 -0
  95. package/dist/layout/distribute.js +30 -0
  96. package/dist/layout/distribute.js.map +1 -0
  97. package/dist/layout/image.d.ts +42 -0
  98. package/dist/layout/image.d.ts.map +1 -0
  99. package/dist/layout/image.js +34 -0
  100. package/dist/layout/image.js.map +1 -0
  101. package/dist/layout/index.d.ts +40 -0
  102. package/dist/layout/index.d.ts.map +1 -0
  103. package/dist/layout/index.js +30 -0
  104. package/dist/layout/index.js.map +1 -0
  105. package/dist/layout/lines.d.ts +45 -0
  106. package/dist/layout/lines.d.ts.map +1 -0
  107. package/dist/layout/lines.js +71 -0
  108. package/dist/layout/lines.js.map +1 -0
  109. package/dist/layout/measure.d.ts +11 -0
  110. package/dist/layout/measure.d.ts.map +1 -0
  111. package/dist/layout/measure.js +11 -0
  112. package/dist/layout/measure.js.map +1 -0
  113. package/dist/layout/panel.d.ts +20 -0
  114. package/dist/layout/panel.d.ts.map +1 -0
  115. package/dist/layout/panel.js +22 -0
  116. package/dist/layout/panel.js.map +1 -0
  117. package/dist/layout/row.d.ts +15 -0
  118. package/dist/layout/row.d.ts.map +1 -0
  119. package/dist/layout/row.js +20 -0
  120. package/dist/layout/row.js.map +1 -0
  121. package/dist/layout/scrim.d.ts +23 -0
  122. package/dist/layout/scrim.d.ts.map +1 -0
  123. package/dist/layout/scrim.js +38 -0
  124. package/dist/layout/scrim.js.map +1 -0
  125. package/dist/layout/stack.d.ts +14 -0
  126. package/dist/layout/stack.d.ts.map +1 -0
  127. package/dist/layout/stack.js +19 -0
  128. package/dist/layout/stack.js.map +1 -0
  129. package/dist/layout/types.d.ts +49 -0
  130. package/dist/layout/types.d.ts.map +1 -0
  131. package/dist/layout/types.js +2 -0
  132. package/dist/layout/types.js.map +1 -0
  133. package/dist/manifest/build.d.ts +1 -1
  134. package/dist/manifest/build.js +1 -1
  135. package/dist/manifest/index.d.ts +1 -1
  136. package/dist/manifest/index.js +1 -1
  137. package/dist/manifest/order.d.ts +1 -1
  138. package/dist/manifest/order.js +1 -1
  139. package/dist/manifest/page.d.ts +2 -2
  140. package/dist/manifest/page.js +3 -3
  141. package/dist/measure/estimate.d.ts +10 -0
  142. package/dist/measure/estimate.d.ts.map +1 -0
  143. package/dist/measure/estimate.js +58 -0
  144. package/dist/measure/estimate.js.map +1 -0
  145. package/dist/measure/faces.d.ts +15 -0
  146. package/dist/measure/faces.d.ts.map +1 -0
  147. package/dist/measure/faces.js +70 -0
  148. package/dist/measure/faces.js.map +1 -0
  149. package/dist/measure/index.d.ts +16 -0
  150. package/dist/measure/index.d.ts.map +1 -0
  151. package/dist/measure/index.js +0 -0
  152. package/dist/measure/index.js.map +1 -0
  153. package/dist/measure/runs.d.ts +17 -0
  154. package/dist/measure/runs.d.ts.map +1 -0
  155. package/dist/measure/runs.js +39 -0
  156. package/dist/measure/runs.js.map +1 -0
  157. package/dist/measure/select.d.ts +13 -0
  158. package/dist/measure/select.d.ts.map +1 -0
  159. package/dist/measure/select.js +47 -0
  160. package/dist/measure/select.js.map +1 -0
  161. package/dist/meta/tags.d.ts +6 -6
  162. package/dist/meta/tags.js +6 -6
  163. package/dist/meta/url.d.ts +4 -4
  164. package/dist/meta/url.js +4 -4
  165. package/dist/placement/check.d.ts +1 -1
  166. package/dist/placement/check.js +1 -1
  167. package/dist/placement/index.js +1 -1
  168. package/dist/placement/relative.d.ts +3 -3
  169. package/dist/placement/relative.js +3 -3
  170. package/dist/pool.d.ts +2 -2
  171. package/dist/pool.js +3 -3
  172. package/dist/pool.js.map +1 -1
  173. package/dist/props.d.ts +7 -0
  174. package/dist/props.d.ts.map +1 -0
  175. package/dist/props.js +17 -0
  176. package/dist/props.js.map +1 -0
  177. package/dist/render/svg.d.ts +4 -2
  178. package/dist/render/svg.d.ts.map +1 -1
  179. package/dist/render/svg.js +19 -4
  180. package/dist/render/svg.js.map +1 -1
  181. package/dist/stamp/config-digest.d.ts.map +1 -1
  182. package/dist/stamp/config-digest.js +20 -1
  183. package/dist/stamp/config-digest.js.map +1 -1
  184. package/dist/stamp/index.js +2 -2
  185. package/dist/stamp/index.js.map +1 -1
  186. package/dist/stamp/read.d.ts +1 -1
  187. package/dist/stamp/read.js +4 -4
  188. package/dist/templates/attribution.d.ts +17 -0
  189. package/dist/templates/attribution.d.ts.map +1 -0
  190. package/dist/templates/attribution.js +39 -0
  191. package/dist/templates/attribution.js.map +1 -0
  192. package/dist/templates/banner/badge.d.ts.map +1 -1
  193. package/dist/templates/banner/badge.js +3 -6
  194. package/dist/templates/banner/badge.js.map +1 -1
  195. package/dist/templates/banner/index.d.ts +3 -0
  196. package/dist/templates/banner/index.d.ts.map +1 -1
  197. package/dist/templates/banner/index.js +37 -35
  198. package/dist/templates/banner/index.js.map +1 -1
  199. package/dist/templates/banner/lines.d.ts +7 -12
  200. package/dist/templates/banner/lines.d.ts.map +1 -1
  201. package/dist/templates/banner/lines.js +23 -34
  202. package/dist/templates/banner/lines.js.map +1 -1
  203. package/dist/templates/banner/version.d.ts +9 -0
  204. package/dist/templates/banner/version.d.ts.map +1 -0
  205. package/dist/templates/banner/version.js +21 -0
  206. package/dist/templates/banner/version.js.map +1 -0
  207. package/dist/templates/card/index.d.ts +4 -1
  208. package/dist/templates/card/index.d.ts.map +1 -1
  209. package/dist/templates/card/index.js +37 -25
  210. package/dist/templates/card/index.js.map +1 -1
  211. package/dist/templates/card/lines.d.ts +17 -13
  212. package/dist/templates/card/lines.d.ts.map +1 -1
  213. package/dist/templates/card/lines.js +24 -20
  214. package/dist/templates/card/lines.js.map +1 -1
  215. package/dist/templates/code/chrome.d.ts +7 -3
  216. package/dist/templates/code/chrome.d.ts.map +1 -1
  217. package/dist/templates/code/chrome.js +12 -11
  218. package/dist/templates/code/chrome.js.map +1 -1
  219. package/dist/templates/code/clip.d.ts +1 -1
  220. package/dist/templates/code/clip.js +1 -1
  221. package/dist/templates/code/fit.d.ts +2 -2
  222. package/dist/templates/code/fit.d.ts.map +1 -1
  223. package/dist/templates/code/fit.js +4 -4
  224. package/dist/templates/code/fit.js.map +1 -1
  225. package/dist/templates/code/index.d.ts.map +1 -1
  226. package/dist/templates/code/index.js +7 -6
  227. package/dist/templates/code/index.js.map +1 -1
  228. package/dist/templates/code/layout.d.ts +1 -1
  229. package/dist/templates/code/layout.d.ts.map +1 -1
  230. package/dist/templates/code/layout.js +6 -6
  231. package/dist/templates/code/layout.js.map +1 -1
  232. package/dist/templates/code/panel.d.ts +13 -0
  233. package/dist/templates/code/panel.d.ts.map +1 -1
  234. package/dist/templates/code/panel.js +34 -2
  235. package/dist/templates/code/panel.js.map +1 -1
  236. package/dist/templates/code/plate.d.ts +3 -1
  237. package/dist/templates/code/plate.d.ts.map +1 -1
  238. package/dist/templates/code/plate.js +11 -9
  239. package/dist/templates/code/plate.js.map +1 -1
  240. package/dist/templates/code/spans.js +1 -1
  241. package/dist/templates/code/spans.js.map +1 -1
  242. package/dist/templates/code/width.d.ts +10 -0
  243. package/dist/templates/code/width.d.ts.map +1 -0
  244. package/dist/templates/code/width.js +26 -0
  245. package/dist/templates/code/width.js.map +1 -0
  246. package/dist/templates/footer.d.ts +8 -0
  247. package/dist/templates/footer.d.ts.map +1 -1
  248. package/dist/templates/footer.js +11 -1
  249. package/dist/templates/footer.js.map +1 -1
  250. package/dist/templates/index.d.ts +1 -1
  251. package/dist/templates/index.d.ts.map +1 -1
  252. package/dist/templates/index.js +1 -1
  253. package/dist/templates/index.js.map +1 -1
  254. package/dist/templates/logo.d.ts +15 -0
  255. package/dist/templates/logo.d.ts.map +1 -0
  256. package/dist/templates/logo.js +30 -0
  257. package/dist/templates/logo.js.map +1 -0
  258. package/dist/templates/measure.d.ts +11 -0
  259. package/dist/templates/measure.d.ts.map +1 -0
  260. package/dist/templates/measure.js +11 -0
  261. package/dist/templates/measure.js.map +1 -0
  262. package/dist/templates/props.d.ts +2 -2
  263. package/dist/templates/props.js +2 -2
  264. package/dist/templates/text.d.ts +37 -0
  265. package/dist/templates/text.d.ts.map +1 -0
  266. package/dist/templates/text.js +26 -0
  267. package/dist/templates/text.js.map +1 -0
  268. package/dist/text/break.d.ts +11 -0
  269. package/dist/text/break.d.ts.map +1 -0
  270. package/dist/text/break.js +30 -0
  271. package/dist/text/break.js.map +1 -0
  272. package/dist/text/fit.d.ts +30 -0
  273. package/dist/text/fit.d.ts.map +1 -0
  274. package/dist/text/fit.js +29 -0
  275. package/dist/text/fit.js.map +1 -0
  276. package/dist/text/index.d.ts +5 -3
  277. package/dist/text/index.d.ts.map +1 -1
  278. package/dist/text/index.js +3 -2
  279. package/dist/text/index.js.map +1 -1
  280. package/dist/text/wrap.d.ts +17 -7
  281. package/dist/text/wrap.d.ts.map +1 -1
  282. package/dist/text/wrap.js +25 -17
  283. package/dist/text/wrap.js.map +1 -1
  284. package/dist/types.d.ts +161 -45
  285. package/dist/types.d.ts.map +1 -1
  286. package/dist/validate/index.d.ts +1 -1
  287. package/dist/validate/index.d.ts.map +1 -1
  288. package/dist/validate/index.js +3 -2
  289. package/dist/validate/index.js.map +1 -1
  290. package/dist/validate/keys.d.ts +19 -2
  291. package/dist/validate/keys.d.ts.map +1 -1
  292. package/dist/validate/keys.js +40 -3
  293. package/dist/validate/keys.js.map +1 -1
  294. package/dist/validate/suggest.d.ts.map +1 -1
  295. package/dist/validate/suggest.js +21 -3
  296. package/dist/validate/suggest.js.map +1 -1
  297. package/dist/validate/values.d.ts +2 -2
  298. package/dist/validate/values.d.ts.map +1 -1
  299. package/dist/validate/values.js +17 -3
  300. package/dist/validate/values.js.map +1 -1
  301. package/package.json +7 -1
@@ -0,0 +1,71 @@
1
+ import { textElement } from "../text/index.js";
2
+ import { distribute } from "./distribute.js";
3
+ /** Vertical advance of a line, as a multiple of its font size. */
4
+ const defaultLineHeight = 1.2;
5
+ /**
6
+ * Where the baseline sits within a line's advance. A face puts its ascenders
7
+ * around here, and taking it as a fraction of the size rather than as a metric
8
+ * read from the font means a line lands in the same place whichever font the
9
+ * image is eventually drawn in.
10
+ */
11
+ const baselineRatio = 0.8;
12
+ /**
13
+ * The baseline of one line of text sitting in a band of its own font size.
14
+ *
15
+ * A template that has set room aside for a single line, rather than stacking
16
+ * several, wants this rather than a fraction of the font size chosen by eye:
17
+ * the ink then lands inside the band that was written down, so the clear space
18
+ * either side of it is the space that was reserved. The descender takes the
19
+ * rest of the band, which is why the room below a line is not the same as the
20
+ * room above it.
21
+ */
22
+ export function baselineFor(top, fontSize) {
23
+ return Math.round(top + fontSize * baselineRatio);
24
+ }
25
+ /**
26
+ * Place a block of lines within an area and return each baseline.
27
+ *
28
+ * The lines are placed as one block rather than each finding its own middle,
29
+ * which is what keeps a wrapped title reading as a paragraph. A block taller
30
+ * than its area starts at the top and runs over, since a template that has
31
+ * overflowed wants to see it.
32
+ */
33
+ export function placeLines(lines, area, lineHeight = defaultLineHeight, align = "centre") {
34
+ const placed = distribute(lines.map((line) => ({
35
+ size: line.fontSize * lineHeight,
36
+ ...(line.gapBefore !== undefined && { gapBefore: line.gapBefore }),
37
+ })), { start: area.y, end: area.y + area.height }, align);
38
+ return placed.map((placement, index) => ({
39
+ y: baselineFor(placement.start, lines[index]?.fontSize ?? 0),
40
+ index,
41
+ }));
42
+ }
43
+ /** The x a line is drawn from, which the anchor decides. */
44
+ function anchorX(area, anchor) {
45
+ if (anchor === "middle") {
46
+ return Math.round(area.x + area.width / 2);
47
+ }
48
+ return anchor === "end" ? area.x + area.width : area.x;
49
+ }
50
+ /**
51
+ * Draw a block of lines within an area: place them, then write the `<text>`
52
+ * elements. Once a template has fitted its words, this is the whole of what it
53
+ * does with them.
54
+ */
55
+ export function drawLines(lines, area, style) {
56
+ const placed = placeLines(lines, area, style.lineHeight ?? defaultLineHeight, style.align ?? "centre");
57
+ const x = anchorX(area, style.anchor);
58
+ return lines
59
+ .map((line, index) => textElement(line.text, {
60
+ x,
61
+ y: placed[index]?.y ?? area.y,
62
+ fontFamily: style.fontFamily,
63
+ fontSize: line.fontSize,
64
+ fontWeight: line.fontWeight,
65
+ fill: style.fill,
66
+ fillOpacity: line.opacity,
67
+ ...(style.anchor !== undefined && { anchor: style.anchor }),
68
+ }))
69
+ .join("");
70
+ }
71
+ //# sourceMappingURL=lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/layout/lines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,kEAAkE;AAClE,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,QAAgB;IACvD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,QAAQ,GAAG,aAAa,CAAC,CAAC;AACpD,CAAC;AAoBD;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CACxB,KAA0B,EAC1B,IAAU,EACV,UAAU,GAAG,iBAAiB,EAC9B,KAAK,GAAU,QAAQ;IAEvB,MAAM,MAAM,GAAG,UAAU,CACvB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnB,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,UAAU;QAChC,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;KACnE,CAAC,CAAC,EACH,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAC5C,KAAK,CACN,CAAC;IAEF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC;QAC5D,KAAK;KACN,CAAC,CAAC,CAAC;AACN,CAAC;AAED,4DAA4D;AAC5D,SAAS,OAAO,CAAC,IAAU,EAAE,MAA4B;IACvD,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,KAA0B,EAC1B,IAAU,EACV,KAAiB;IAEjB,MAAM,MAAM,GAAG,UAAU,CACvB,KAAK,EACL,IAAI,EACJ,KAAK,CAAC,UAAU,IAAI,iBAAiB,EACrC,KAAK,CAAC,KAAK,IAAI,QAAQ,CACxB,CAAC;IACF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtC,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACnB,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE;QACrB,CAAC;QACD,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,IAAI,CAAC,OAAO;QACzB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;KAC5D,CAAC,CACH;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { MeasureAt } from "../text/index.js";
2
+ import type { MeasureText } from "../types.js";
3
+ /**
4
+ * Bind a measurer to one family and weight, leaving only the size open.
5
+ *
6
+ * That is the shape fitting text wants, since choosing a size is the whole of
7
+ * what it varies, and it keeps a template from repeating the family and weight
8
+ * on every call.
9
+ */
10
+ export declare function measureIn(measure: MeasureText, fontFamily: string, fontWeight: number): MeasureAt;
11
+ //# sourceMappingURL=measure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../src/layout/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,SAAS,CAGX"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Bind a measurer to one family and weight, leaving only the size open.
3
+ *
4
+ * That is the shape fitting text wants, since choosing a size is the whole of
5
+ * what it varies, and it keeps a template from repeating the family and weight
6
+ * on every call.
7
+ */
8
+ export function measureIn(measure, fontFamily, fontWeight) {
9
+ return (text, fontSize) => measure(text, { fontFamily, fontSize, fontWeight });
10
+ }
11
+ //# sourceMappingURL=measure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure.js","sourceRoot":"","sources":["../../src/layout/measure.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CACvB,OAAoB,EACpB,UAAkB,EAClB,UAAkB;IAElB,OAAO,CAAC,IAAY,EAAE,QAAgB,EAAU,EAAE,CAChD,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { BoxStyle, Rect } from "./types.js";
2
+ /** A panel is a box that casts a shadow. */
3
+ export interface PanelStyle extends BoxStyle {
4
+ /**
5
+ * How far the shadow falls below the panel. Omit for none, which is right
6
+ * for a panel that is a region of the image rather than a card on top of it.
7
+ */
8
+ readonly shadow?: number;
9
+ readonly shadowColor?: string;
10
+ readonly shadowOpacity?: number;
11
+ }
12
+ /**
13
+ * A raised plate: a soft shadow, then the surface on top of it.
14
+ *
15
+ * The shadow is a second rectangle rather than a blur filter. It costs a
16
+ * rasteriser nothing, it survives every backend, and at these sizes a hard
17
+ * offset reads as depth just as well as a blurred one.
18
+ */
19
+ export declare function panel(rect: Rect, style?: PanelStyle): string;
20
+ //# sourceMappingURL=panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/layout/panel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEjD,4CAA4C;AAC5C,MAAM,WAAW,UAAW,SAAQ,QAAQ;IAC1C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAKD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAE,UAAe,GAAG,MAAM,CAiBhE"}
@@ -0,0 +1,22 @@
1
+ import { box } from "./box.js";
2
+ const defaultShadowColor = "#000000";
3
+ const defaultShadowOpacity = 0.22;
4
+ /**
5
+ * A raised plate: a soft shadow, then the surface on top of it.
6
+ *
7
+ * The shadow is a second rectangle rather than a blur filter. It costs a
8
+ * rasteriser nothing, it survives every backend, and at these sizes a hard
9
+ * offset reads as depth just as well as a blurred one.
10
+ */
11
+ export function panel(rect, style = {}) {
12
+ const { shadow, shadowColor, shadowOpacity, ...surface } = style;
13
+ if (shadow === undefined) {
14
+ return box(rect, surface);
15
+ }
16
+ return (box({ ...rect, y: rect.y + shadow }, {
17
+ ...(surface.radius !== undefined && { radius: surface.radius }),
18
+ fill: shadowColor ?? defaultShadowColor,
19
+ fillOpacity: shadowOpacity ?? defaultShadowOpacity,
20
+ }) + box(rect, surface));
21
+ }
22
+ //# sourceMappingURL=panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/layout/panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAc/B,MAAM,kBAAkB,GAAG,SAAS,CAAC;AACrC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,IAAU,EAAE,KAAK,GAAe,EAAE;IACtD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC;IAEjE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,CACL,GAAG,CACD,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,MAAM,EAAE,EAC/B;QACE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAC/D,IAAI,EAAE,WAAW,IAAI,kBAAkB;QACvC,WAAW,EAAE,aAAa,IAAI,oBAAoB;KACnD,CACF,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Align, Extent, Rect } from "./types.js";
2
+ /** A placed item's rectangle, and which item it belongs to. */
3
+ export interface RowRect extends Rect {
4
+ readonly index: number;
5
+ }
6
+ /**
7
+ * Set items across an area and give each one its rectangle: an avatar beside a
8
+ * name, a badge beside a title, a row of tags along the bottom.
9
+ *
10
+ * Each keeps the full height of the area, so an item shorter than the row
11
+ * centres itself within the rectangle it was given rather than being told
12
+ * where to sit.
13
+ */
14
+ export declare function row(items: readonly Extent[], area: Rect, align?: Align): readonly RowRect[];
15
+ //# sourceMappingURL=row.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row.d.ts","sourceRoot":"","sources":["../../src/layout/row.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEtD,+DAA+D;AAC/D,MAAM,WAAW,OAAQ,SAAQ,IAAI;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,KAAgB,GACtB,SAAS,OAAO,EAAE,CAcpB"}
@@ -0,0 +1,20 @@
1
+ import { distribute } from "./distribute.js";
2
+ /**
3
+ * Set items across an area and give each one its rectangle: an avatar beside a
4
+ * name, a badge beside a title, a row of tags along the bottom.
5
+ *
6
+ * Each keeps the full height of the area, so an item shorter than the row
7
+ * centres itself within the rectangle it was given rather than being told
8
+ * where to sit.
9
+ */
10
+ export function row(items, area, align = "centre") {
11
+ const placed = distribute(items, { start: area.x, end: area.x + area.width }, align);
12
+ return placed.map((placement, index) => ({
13
+ x: placement.start,
14
+ y: area.y,
15
+ width: items[index]?.size ?? 0,
16
+ height: area.height,
17
+ index: placement.index,
18
+ }));
19
+ }
20
+ //# sourceMappingURL=row.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row.js","sourceRoot":"","sources":["../../src/layout/row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ7C;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CACjB,KAAwB,EACxB,IAAU,EACV,KAAK,GAAU,QAAQ;IAEvB,MAAM,MAAM,GAAG,UAAU,CACvB,KAAK,EACL,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAC3C,KAAK,CACN,CAAC;IAEF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,EAAE,SAAS,CAAC,KAAK;QAClB,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,SAAS,CAAC,KAAK;KACvB,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Rect } from "./types.js";
2
+ /** How heavily a scrim shades what is under it, and from which edge. */
3
+ export interface ScrimOptions {
4
+ readonly color?: string;
5
+ /** Opacity at the top edge. Defaults to none, so the image shows through. */
6
+ readonly from?: number;
7
+ /** Opacity at the bottom edge. */
8
+ readonly to?: number;
9
+ }
10
+ /**
11
+ * A wash of colour over an image, so that text on top of it can be read.
12
+ *
13
+ * This is the difference between a designed image and text sitting on a photo.
14
+ * A photograph has light and dark in it wherever it likes, and white text over
15
+ * a bright sky is invisible; a gradient from clear at the top to dark at the
16
+ * bottom keeps a headline legible without hiding the picture.
17
+ *
18
+ * `id` names the gradient and has to be unique within the image. A flat wash
19
+ * needs no gradient, so it needs no `id` either: pass the same value for
20
+ * `from` and `to`.
21
+ */
22
+ export declare function scrim(rect: Rect, id: string, options?: ScrimOptions): string;
23
+ //# sourceMappingURL=scrim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrim.d.ts","sourceRoot":"","sources":["../../src/layout/scrim.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CACnB,IAAI,EAAE,IAAI,EACV,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,YAAiB,GACzB,MAAM,CA0BR"}
@@ -0,0 +1,38 @@
1
+ import { escapeXml } from "../text/index.js";
2
+ import { box } from "./box.js";
3
+ const defaultColor = "#000000";
4
+ /**
5
+ * A wash of colour over an image, so that text on top of it can be read.
6
+ *
7
+ * This is the difference between a designed image and text sitting on a photo.
8
+ * A photograph has light and dark in it wherever it likes, and white text over
9
+ * a bright sky is invisible; a gradient from clear at the top to dark at the
10
+ * bottom keeps a headline legible without hiding the picture.
11
+ *
12
+ * `id` names the gradient and has to be unique within the image. A flat wash
13
+ * needs no gradient, so it needs no `id` either: pass the same value for
14
+ * `from` and `to`.
15
+ */
16
+ export function scrim(rect, id, options = {}) {
17
+ const from = options.from ?? 0;
18
+ const to = options.to ?? 0.55;
19
+ if (from === to) {
20
+ return box(rect, {
21
+ fill: options.color ?? defaultColor,
22
+ fillOpacity: from,
23
+ });
24
+ }
25
+ // Escaped here for the reason `box` escapes what it is given: both of these
26
+ // may have come from a post's frontmatter by way of a template. The fill
27
+ // below takes the id raw, since `box` does its own escaping and the two
28
+ // attributes have to agree once parsed rather than as written.
29
+ const color = escapeXml(options.color ?? defaultColor);
30
+ const gradient = `<defs>` +
31
+ `<linearGradient id="${escapeXml(id)}" x1="0" y1="0" x2="0" y2="1">` +
32
+ `<stop offset="0%" stop-color="${color}" stop-opacity="${String(from)}"/>` +
33
+ `<stop offset="100%" stop-color="${color}" stop-opacity="${String(to)}"/>` +
34
+ `</linearGradient>` +
35
+ `</defs>`;
36
+ return gradient + box(rect, { fill: `url(#${id})` });
37
+ }
38
+ //# sourceMappingURL=scrim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrim.js","sourceRoot":"","sources":["../../src/layout/scrim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAY/B,MAAM,YAAY,GAAG,SAAS,CAAC;AAE/B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,KAAK,CACnB,IAAU,EACV,EAAU,EACV,OAAO,GAAiB,EAAE;IAE1B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC;IAE9B,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,IAAI,EAAE;YACf,IAAI,EAAE,OAAO,CAAC,KAAK,IAAI,YAAY;YACnC,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,wEAAwE;IACxE,+DAA+D;IAC/D,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC;IAEvD,MAAM,QAAQ,GACZ,QAAQ;QACR,uBAAuB,SAAS,CAAC,EAAE,CAAC,gCAAgC;QACpE,iCAAiC,KAAK,mBAAmB,MAAM,CAAC,IAAI,CAAC,KAAK;QAC1E,mCAAmC,KAAK,mBAAmB,MAAM,CAAC,EAAE,CAAC,KAAK;QAC1E,mBAAmB;QACnB,SAAS,CAAC;IAEZ,OAAO,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Align, Extent, Rect } from "./types.js";
2
+ /** A stacked item's rectangle, and which item it belongs to. */
3
+ export interface StackedRect extends Rect {
4
+ readonly index: number;
5
+ }
6
+ /**
7
+ * Stack items down an area and give each one its rectangle.
8
+ *
9
+ * Each keeps the full width of the area, because the vertical placement is the
10
+ * only decision being made here. An item that wants less width draws itself
11
+ * narrower, or puts a `row` inside the rectangle it was given.
12
+ */
13
+ export declare function stack(items: readonly Extent[], area: Rect, align?: Align): readonly StackedRect[];
14
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../src/layout/stack.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEtD,gEAAgE;AAChE,MAAM,WAAW,WAAY,SAAQ,IAAI;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CACnB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,IAAI,EACV,KAAK,GAAE,KAAgB,GACtB,SAAS,WAAW,EAAE,CAcxB"}
@@ -0,0 +1,19 @@
1
+ import { distribute } from "./distribute.js";
2
+ /**
3
+ * Stack items down an area and give each one its rectangle.
4
+ *
5
+ * Each keeps the full width of the area, because the vertical placement is the
6
+ * only decision being made here. An item that wants less width draws itself
7
+ * narrower, or puts a `row` inside the rectangle it was given.
8
+ */
9
+ export function stack(items, area, align = "centre") {
10
+ const placed = distribute(items, { start: area.y, end: area.y + area.height }, align);
11
+ return placed.map((placement, index) => ({
12
+ x: area.x,
13
+ y: placement.start,
14
+ width: area.width,
15
+ height: items[index]?.size ?? 0,
16
+ index: placement.index,
17
+ }));
18
+ }
19
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../../src/layout/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ7C;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CACnB,KAAwB,EACxB,IAAU,EACV,KAAK,GAAU,QAAQ;IAEvB,MAAM,MAAM,GAAG,UAAU,CACvB,KAAK,EACL,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAC5C,KAAK,CACN,CAAC;IAEF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,CAAC,EAAE,SAAS,CAAC,KAAK;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC;QAC/B,KAAK,EAAE,SAAS,CAAC,KAAK;KACvB,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A rectangle in image coordinates, which is what every primitive here takes
3
+ * and returns. Templates are handed the image's `dimensions` and work down
4
+ * from there, so a rectangle is the one shape they all have in common.
5
+ */
6
+ export interface Rect {
7
+ readonly x: number;
8
+ readonly y: number;
9
+ readonly width: number;
10
+ readonly height: number;
11
+ }
12
+ /**
13
+ * A stretch of one axis: top and bottom for a stack, left and right for a row.
14
+ * `start` is the smaller coordinate, so it is the top or the left edge.
15
+ */
16
+ export interface Span {
17
+ readonly start: number;
18
+ readonly end: number;
19
+ }
20
+ /**
21
+ * Where a group of items sits within the space it was given. `centre` is the
22
+ * default everywhere, because a share image is looked at rather than read down,
23
+ * and the middle is where the eye goes.
24
+ */
25
+ export type Align = "start" | "centre" | "end";
26
+ /**
27
+ * How much of an axis one item takes, and how much clear space goes before it.
28
+ * A gap on the first item is space at the head of the group.
29
+ */
30
+ export interface Extent {
31
+ readonly size: number;
32
+ readonly gapBefore?: number;
33
+ }
34
+ /** Where an item ended up: its start coordinate along the axis. */
35
+ export interface Placed {
36
+ readonly start: number;
37
+ readonly index: number;
38
+ }
39
+ /** Fill, corners and stroke for a rectangle. Every field is optional. */
40
+ export interface BoxStyle {
41
+ readonly fill?: string;
42
+ readonly fillOpacity?: number;
43
+ /** Corner radius. Omit for square corners. */
44
+ readonly radius?: number;
45
+ readonly stroke?: string;
46
+ readonly strokeOpacity?: number;
47
+ readonly strokeWidth?: number;
48
+ }
49
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/layout/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,mEAAmE;AACnE,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/layout/types.ts"],"names":[],"mappings":""}
@@ -4,7 +4,7 @@ import type { Manifest } from "../types.js";
4
4
  * Build the manifest for a planned build.
5
5
  *
6
6
  * It is built from the jobs rather than from what was written, so a rebuild
7
- * that skips every image still describes the whole site the manifest says
7
+ * that skips every image still describes the whole site. The manifest says
8
8
  * what exists, not what happened this time.
9
9
  *
10
10
  * Pages are sorted by slug, and each page's images by size name, so a manifest
@@ -27,7 +27,7 @@ function drafts(jobs) {
27
27
  * Build the manifest for a planned build.
28
28
  *
29
29
  * It is built from the jobs rather than from what was written, so a rebuild
30
- * that skips every image still describes the whole site the manifest says
30
+ * that skips every image still describes the whole site. The manifest says
31
31
  * what exists, not what happened this time.
32
32
  *
33
33
  * Pages are sorted by slug, and each page's images by size name, so a manifest
@@ -13,7 +13,7 @@ export interface PlannedManifest {
13
13
  *
14
14
  * Built while planning rather than from the results, so that a site with two
15
15
  * pages sharing a slug hears about it before rendering the tree rather than
16
- * after the same reason the output paths are checked up front.
16
+ * after. That is the same reason the output paths are checked up front.
17
17
  */
18
18
  export declare function planManifest(manifestPath: string | undefined, jobs: readonly RenderJob[]): PlannedManifest | undefined;
19
19
  /**
@@ -7,7 +7,7 @@ export { buildManifest } from "./build.js";
7
7
  *
8
8
  * Built while planning rather than from the results, so that a site with two
9
9
  * pages sharing a slug hears about it before rendering the tree rather than
10
- * after the same reason the output paths are checked up front.
10
+ * after. That is the same reason the output paths are checked up front.
11
11
  */
12
12
  export function planManifest(manifestPath, jobs) {
13
13
  return manifestPath === undefined
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * The manifest is written into a site's repository and committed, so the order
6
6
  * has to be the same on a laptop and on CI. `localeCompare` reads the default
7
- * locale, and locales disagree about case and accented letters a slug of
7
+ * locale, and locales disagree about case and accented letters, so a slug of
8
8
  * `Über` or `MyPost` could sort one way in development and another in the
9
9
  * build, leaving a diff that says nothing happened.
10
10
  */
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * The manifest is written into a site's repository and committed, so the order
6
6
  * has to be the same on a laptop and on CI. `localeCompare` reads the default
7
- * locale, and locales disagree about case and accented letters a slug of
7
+ * locale, and locales disagree about case and accented letters, so a slug of
8
8
  * `Über` or `MyPost` could sort one way in development and another in the
9
9
  * build, leaving a diff that says nothing happened.
10
10
  */
@@ -5,8 +5,8 @@ import type { ManifestPage } from "../types.js";
5
5
  * of them so far.
6
6
  *
7
7
  * A page is created from its first job rather than from an empty list, so
8
- * there is never a page without images to explain away `widest` has to name
9
- * one of them.
8
+ * there is never a page without images to explain away, since `widest` has to
9
+ * name one of them.
10
10
  */
11
11
  export interface PageDraft {
12
12
  readonly jobs: RenderJob[];
@@ -21,9 +21,9 @@ export function addToPage(page, job) {
21
21
  * Reject two pages sharing a slug.
22
22
  *
23
23
  * The manifest is keyed by slug, so a site looking a page up would find
24
- * whichever was written last an image quietly belonging to the wrong post,
25
- * which is worse than no manifest at all. Their images may well be distinct on
26
- * disk, so nothing else in the build has any reason to complain.
24
+ * whichever was written last. That is an image quietly belonging to the wrong
25
+ * post, which is worse than no manifest at all. Their images may well be
26
+ * distinct on disk, so nothing else in the build has any reason to complain.
27
27
  */
28
28
  function assertOnePage(slug, page) {
29
29
  const paths = [...new Set(page.jobs.map((job) => job.contentPath))];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Width of `text` when there is no font to measure it in, either because none
3
+ * were configured or because the stack names one the build did not load.
4
+ *
5
+ * It is a guess, and a build that wants text laid out to the pixel supplies
6
+ * the font as a file. What it is not is a guess a template has to make: the
7
+ * one ratio lives here, where the reason for it can be written down once.
8
+ */
9
+ export declare function estimateWidth(text: string, fontSize: number, weight: number, ratio?: number): number;
10
+ //# sourceMappingURL=estimate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.d.ts","sourceRoot":"","sources":["../../src/measure/estimate.ts"],"names":[],"mappings":"AA8CA;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,KAAK,SAAe,GACnB,MAAM,CAQR"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Advance width to assume for proportional text, as a fraction of the font
3
+ * size. It is the average over the sort of text a share image carries, mostly
4
+ * lower-case Latin with spaces, for the sans-serif faces a site is likely to
5
+ * use. Nothing here is exact, which is the point of measuring instead.
6
+ */
7
+ const defaultRatio = 0.52;
8
+ /**
9
+ * Characters that occupy a full em rather than a fraction of one: the CJK
10
+ * ranges, Hangul, kana, the fullwidth forms and emoji.
11
+ *
12
+ * Estimating these at the Latin ratio is what makes a Japanese title overflow
13
+ * by nearly half its width, and no per-template fudge factor can be right for
14
+ * both scripts at once. Widths of the scripts in between, such as Devanagari,
15
+ * are still guesses; those want a font file, which is what measuring is for.
16
+ */
17
+ const wideRanges = [
18
+ [0x11_00, 0x11_5f], // Hangul Jamo
19
+ [0x2e_80, 0x30_3e], // CJK radicals and punctuation
20
+ [0x30_41, 0x33_ff], // Kana through the CJK compatibility forms
21
+ [0x34_00, 0x4d_bf], // CJK extension A
22
+ [0x4e_00, 0x9f_ff], // CJK unified ideographs
23
+ [0xa0_00, 0xa4_cf], // Yi
24
+ [0xac_00, 0xd7_a3], // Hangul syllables
25
+ [0xf9_00, 0xfa_ff], // CJK compatibility ideographs
26
+ [0xfe_30, 0xfe_4f], // CJK compatibility forms
27
+ [0xff_00, 0xff_60], // Fullwidth forms, but not the halfwidth kana after them
28
+ [0xff_e0, 0xff_e6], // Fullwidth currency and bar symbols
29
+ [0x1_f3_00, 0x1_fa_ff], // Emoji
30
+ [0x2_00_00, 0x3_ff_fd], // CJK extensions B onwards
31
+ ];
32
+ function isWide(character) {
33
+ const point = character.codePointAt(0) ?? 0;
34
+ return wideRanges.some(([from, to]) => point >= from && point <= to);
35
+ }
36
+ /**
37
+ * How much wider a face gets as its weight rises. A bold cut of a family runs
38
+ * around a tenth wider than its regular; heavier than bold, little more.
39
+ */
40
+ function weightFactor(weight) {
41
+ return 1 + Math.min(Math.max(weight - 400, 0) / 400, 1) * 0.1;
42
+ }
43
+ /**
44
+ * Width of `text` when there is no font to measure it in, either because none
45
+ * were configured or because the stack names one the build did not load.
46
+ *
47
+ * It is a guess, and a build that wants text laid out to the pixel supplies
48
+ * the font as a file. What it is not is a guess a template has to make: the
49
+ * one ratio lives here, where the reason for it can be written down once.
50
+ */
51
+ export function estimateWidth(text, fontSize, weight, ratio = defaultRatio) {
52
+ let ems = 0;
53
+ for (const character of text) {
54
+ ems += isWide(character) ? 1 : ratio;
55
+ }
56
+ return ems * fontSize * weightFactor(weight);
57
+ }
58
+ //# sourceMappingURL=estimate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../src/measure/estimate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B;;;;;;;;GAQG;AACH,MAAM,UAAU,GAA2C;IACzD,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc;IAClC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,+BAA+B;IACnD,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,2CAA2C;IAC/D,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB;IACtC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,yBAAyB;IAC7C,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KAAK;IACzB,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB;IACvC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,+BAA+B;IACnD,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,0BAA0B;IAC9C,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,yDAAyD;IAC7E,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,qCAAqC;IACzD,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,QAAQ;IAChC,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,2BAA2B;CACpD,CAAC;AAEF,SAAS,MAAM,CAAC,SAAiB;IAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;AACvE,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,QAAgB,EAChB,MAAc,EACd,KAAK,GAAG,YAAY;IAEpB,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACvC,CAAC;IAED,OAAO,GAAG,GAAG,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Font } from "fontkit";
2
+ import type { FontSource, WarningHandler } from "../types.js";
3
+ /**
4
+ * One face loaded for measurement: the font itself, plus the family and weight
5
+ * it declares. Both are read from the file rather than from config, for the
6
+ * reason {@link FontSource} gives: the file is what the rasteriser matches on.
7
+ */
8
+ export interface Face {
9
+ readonly family: string;
10
+ readonly weight: number;
11
+ readonly font: Font;
12
+ }
13
+ /** Every face the configured fonts hold, in the order they were configured. */
14
+ export declare function loadFaces(fonts: readonly FontSource[], onWarning: WarningHandler): Promise<readonly Face[]>;
15
+ //# sourceMappingURL=faces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faces.d.ts","sourceRoot":"","sources":["../../src/measure/faces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB;AAuFD,+EAA+E;AAC/E,wBAAsB,SAAS,CAC7B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,SAAS,EAAE,cAAc,GACxB,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAM1B"}