postext 1.46.4 → 1.48.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 (353) hide show
  1. package/dist/__tests__/bundle/designOverflowPin.test.js +1 -1
  2. package/dist/__tests__/bundle/gridBreakRulesPin.test.js +2 -2
  3. package/dist/__tests__/bundle/shapingLanguagePin.test.js +3 -3
  4. package/dist/__tests__/bundle/taskBoxPin.test.js +1 -1
  5. package/dist/__tests__/bundle.test.js +1 -1
  6. package/dist/__tests__/codeBlocks.test.js +1 -1
  7. package/dist/__tests__/comics/config.test.js +20 -0
  8. package/dist/__tests__/comics/config.test.js.map +1 -1
  9. package/dist/__tests__/composed/cardShapes.test.d.ts +2 -0
  10. package/dist/__tests__/composed/cardShapes.test.d.ts.map +1 -0
  11. package/dist/__tests__/composed/cardShapes.test.js +82 -0
  12. package/dist/__tests__/composed/cardShapes.test.js.map +1 -0
  13. package/dist/__tests__/composed/connectors.test.d.ts +2 -0
  14. package/dist/__tests__/composed/connectors.test.d.ts.map +1 -0
  15. package/dist/__tests__/composed/connectors.test.js +349 -0
  16. package/dist/__tests__/composed/connectors.test.js.map +1 -0
  17. package/dist/__tests__/composed/decorationSidePoints.test.d.ts +2 -0
  18. package/dist/__tests__/composed/decorationSidePoints.test.d.ts.map +1 -0
  19. package/dist/__tests__/composed/decorationSidePoints.test.js +164 -0
  20. package/dist/__tests__/composed/decorationSidePoints.test.js.map +1 -0
  21. package/dist/__tests__/composed/fit.test.js +2 -1
  22. package/dist/__tests__/composed/fit.test.js.map +1 -1
  23. package/dist/__tests__/composed/fixtures/connectors.d.ts +33 -0
  24. package/dist/__tests__/composed/fixtures/connectors.d.ts.map +1 -0
  25. package/dist/__tests__/composed/fixtures/connectors.js +142 -0
  26. package/dist/__tests__/composed/fixtures/connectors.js.map +1 -0
  27. package/dist/__tests__/composed/fixtures/grid.d.ts +32 -0
  28. package/dist/__tests__/composed/fixtures/grid.d.ts.map +1 -0
  29. package/dist/__tests__/composed/fixtures/grid.js +53 -0
  30. package/dist/__tests__/composed/fixtures/grid.js.map +1 -0
  31. package/dist/__tests__/composed/fixtures/legendMap.d.ts +24 -0
  32. package/dist/__tests__/composed/fixtures/legendMap.d.ts.map +1 -0
  33. package/dist/__tests__/composed/fixtures/legendMap.js +117 -0
  34. package/dist/__tests__/composed/fixtures/legendMap.js.map +1 -0
  35. package/dist/__tests__/composed/fixtures/mapParts.d.ts +58 -0
  36. package/dist/__tests__/composed/fixtures/mapParts.d.ts.map +1 -0
  37. package/dist/__tests__/composed/fixtures/mapParts.js +192 -0
  38. package/dist/__tests__/composed/fixtures/mapParts.js.map +1 -0
  39. package/dist/__tests__/composed/fixtures/modules.d.ts +55 -0
  40. package/dist/__tests__/composed/fixtures/modules.d.ts.map +1 -0
  41. package/dist/__tests__/composed/fixtures/modules.js +123 -0
  42. package/dist/__tests__/composed/fixtures/modules.js.map +1 -0
  43. package/dist/__tests__/composed/grid.test.d.ts +2 -0
  44. package/dist/__tests__/composed/grid.test.d.ts.map +1 -0
  45. package/dist/__tests__/composed/grid.test.js +357 -0
  46. package/dist/__tests__/composed/grid.test.js.map +1 -0
  47. package/dist/__tests__/composed/groups.test.js +6 -3
  48. package/dist/__tests__/composed/groups.test.js.map +1 -1
  49. package/dist/__tests__/composed/labelLegend.test.d.ts +2 -0
  50. package/dist/__tests__/composed/labelLegend.test.d.ts.map +1 -0
  51. package/dist/__tests__/composed/labelLegend.test.js +268 -0
  52. package/dist/__tests__/composed/labelLegend.test.js.map +1 -0
  53. package/dist/__tests__/composed/labelsChecks.test.js.map +1 -1
  54. package/dist/__tests__/composed/leaders.test.d.ts +2 -0
  55. package/dist/__tests__/composed/leaders.test.d.ts.map +1 -0
  56. package/dist/__tests__/composed/leaders.test.js +372 -0
  57. package/dist/__tests__/composed/leaders.test.js.map +1 -0
  58. package/dist/__tests__/composed/modules.test.d.ts +2 -0
  59. package/dist/__tests__/composed/modules.test.d.ts.map +1 -0
  60. package/dist/__tests__/composed/modules.test.js +352 -0
  61. package/dist/__tests__/composed/modules.test.js.map +1 -0
  62. package/dist/__tests__/composed/overConnectors.test.d.ts +2 -0
  63. package/dist/__tests__/composed/overConnectors.test.d.ts.map +1 -0
  64. package/dist/__tests__/composed/overConnectors.test.js +232 -0
  65. package/dist/__tests__/composed/overConnectors.test.js.map +1 -0
  66. package/dist/__tests__/composed/overlap.test.d.ts +2 -0
  67. package/dist/__tests__/composed/overlap.test.d.ts.map +1 -0
  68. package/dist/__tests__/composed/overlap.test.js +339 -0
  69. package/dist/__tests__/composed/overlap.test.js.map +1 -0
  70. package/dist/__tests__/composed/viewerLeaf.test.d.ts +2 -0
  71. package/dist/__tests__/composed/viewerLeaf.test.d.ts.map +1 -0
  72. package/dist/__tests__/composed/viewerLeaf.test.js +106 -0
  73. package/dist/__tests__/composed/viewerLeaf.test.js.map +1 -0
  74. package/dist/__tests__/composedTemplates.test.js +1 -1
  75. package/dist/__tests__/design/boxShape.test.d.ts +2 -0
  76. package/dist/__tests__/design/boxShape.test.d.ts.map +1 -0
  77. package/dist/__tests__/design/boxShape.test.js +126 -0
  78. package/dist/__tests__/design/boxShape.test.js.map +1 -0
  79. package/dist/__tests__/design/boxShapePaint.test.d.ts +2 -0
  80. package/dist/__tests__/design/boxShapePaint.test.d.ts.map +1 -0
  81. package/dist/__tests__/design/boxShapePaint.test.js +130 -0
  82. package/dist/__tests__/design/boxShapePaint.test.js.map +1 -0
  83. package/dist/__tests__/design/openerBottomAnchor.test.js.map +1 -1
  84. package/dist/__tests__/design/pathBlock.test.d.ts +2 -0
  85. package/dist/__tests__/design/pathBlock.test.d.ts.map +1 -0
  86. package/dist/__tests__/design/pathBlock.test.js +194 -0
  87. package/dist/__tests__/design/pathBlock.test.js.map +1 -0
  88. package/dist/__tests__/design/pathElement.test.d.ts +2 -0
  89. package/dist/__tests__/design/pathElement.test.d.ts.map +1 -0
  90. package/dist/__tests__/design/pathElement.test.js +257 -0
  91. package/dist/__tests__/design/pathElement.test.js.map +1 -0
  92. package/dist/__tests__/design/svgPath.test.d.ts +2 -0
  93. package/dist/__tests__/design/svgPath.test.d.ts.map +1 -0
  94. package/dist/__tests__/design/svgPath.test.js +144 -0
  95. package/dist/__tests__/design/svgPath.test.js.map +1 -0
  96. package/dist/__tests__/exports.test.js +53 -0
  97. package/dist/__tests__/exports.test.js.map +1 -1
  98. package/dist/__tests__/fontUsage.test.js +46 -0
  99. package/dist/__tests__/fontUsage.test.js.map +1 -1
  100. package/dist/__tests__/hardBreaks.test.js +1 -1
  101. package/dist/__tests__/headingInlineMarks.test.js +1 -1
  102. package/dist/__tests__/labelGlyphPaint.test.d.ts +2 -0
  103. package/dist/__tests__/labelGlyphPaint.test.d.ts.map +1 -0
  104. package/dist/__tests__/labelGlyphPaint.test.js +86 -0
  105. package/dist/__tests__/labelGlyphPaint.test.js.map +1 -0
  106. package/dist/__tests__/mathSizeMigration.test.js +2 -2
  107. package/dist/__tests__/migrate/labelsOnPictures.test.js +1 -1
  108. package/dist/__tests__/migrate/labelsOnPictures.test.js.map +1 -1
  109. package/dist/__tests__/parse/containers.test.js +1 -1
  110. package/dist/__tests__/parse/containers.test.js.map +1 -1
  111. package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +1 -1
  112. package/dist/__tests__/pipeline/calloutLabelGlyph.test.d.ts +2 -0
  113. package/dist/__tests__/pipeline/calloutLabelGlyph.test.d.ts.map +1 -0
  114. package/dist/__tests__/pipeline/calloutLabelGlyph.test.js +198 -0
  115. package/dist/__tests__/pipeline/calloutLabelGlyph.test.js.map +1 -0
  116. package/dist/__tests__/pipeline/calloutShapes.test.d.ts +2 -0
  117. package/dist/__tests__/pipeline/calloutShapes.test.d.ts.map +1 -0
  118. package/dist/__tests__/pipeline/calloutShapes.test.js +159 -0
  119. package/dist/__tests__/pipeline/calloutShapes.test.js.map +1 -0
  120. package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +1 -1
  121. package/dist/__tests__/pipeline/colonListRoom.test.js +1 -1
  122. package/dist/__tests__/pipeline/compoundBreaks.test.js +1 -1
  123. package/dist/__tests__/pipeline/dashBreaks.test.js +1 -1
  124. package/dist/__tests__/pipeline/headingSplitOrphans.test.js +1 -1
  125. package/dist/__tests__/pipeline/inlineFigureSpace.test.js +1 -1
  126. package/dist/__tests__/pipeline/paragraphContainerTail.test.js +1 -1
  127. package/dist/__tests__/pipeline/raggedOptimal.test.js +1 -1
  128. package/dist/__tests__/prepareFonts.test.js +8 -6
  129. package/dist/__tests__/prepareFonts.test.js.map +1 -1
  130. package/dist/__tests__/verseLines.test.js +1 -1
  131. package/dist/bundle/configVersion.d.ts +12 -3
  132. package/dist/bundle/configVersion.d.ts.map +1 -1
  133. package/dist/bundle/configVersion.js +16 -5
  134. package/dist/bundle/configVersion.js.map +1 -1
  135. package/dist/canvas-backend/blockRender.js +3 -3
  136. package/dist/canvas-backend/blockRender.js.map +1 -1
  137. package/dist/canvas-backend/composed.d.ts +4 -2
  138. package/dist/canvas-backend/composed.d.ts.map +1 -1
  139. package/dist/canvas-backend/composed.js +12 -7
  140. package/dist/canvas-backend/composed.js.map +1 -1
  141. package/dist/canvas-backend/headerFooter.d.ts +17 -1
  142. package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
  143. package/dist/canvas-backend/headerFooter.js +64 -1
  144. package/dist/canvas-backend/headerFooter.js.map +1 -1
  145. package/dist/canvas-backend/index.d.ts.map +1 -1
  146. package/dist/canvas-backend/index.js +7 -1
  147. package/dist/canvas-backend/index.js.map +1 -1
  148. package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
  149. package/dist/canvas-backend/renderResourceBlock.js +17 -6
  150. package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
  151. package/dist/canvas-backend/swatch.d.ts +20 -1
  152. package/dist/canvas-backend/swatch.d.ts.map +1 -1
  153. package/dist/canvas-backend/swatch.js +51 -1
  154. package/dist/canvas-backend/swatch.js.map +1 -1
  155. package/dist/comics/fonts.d.ts +19 -0
  156. package/dist/comics/fonts.d.ts.map +1 -1
  157. package/dist/comics/fonts.js +70 -17
  158. package/dist/comics/fonts.js.map +1 -1
  159. package/dist/comics/layoutPage.d.ts.map +1 -1
  160. package/dist/comics/layoutPage.js +6 -1
  161. package/dist/comics/layoutPage.js.map +1 -1
  162. package/dist/composed/continuation.d.ts +10 -2
  163. package/dist/composed/continuation.d.ts.map +1 -1
  164. package/dist/composed/continuation.js +10 -2
  165. package/dist/composed/continuation.js.map +1 -1
  166. package/dist/composed/document.d.ts +45 -1
  167. package/dist/composed/document.d.ts.map +1 -1
  168. package/dist/composed/document.js +76 -17
  169. package/dist/composed/document.js.map +1 -1
  170. package/dist/composed/labelText.d.ts +9 -1
  171. package/dist/composed/labelText.d.ts.map +1 -1
  172. package/dist/composed/labelText.js +23 -1
  173. package/dist/composed/labelText.js.map +1 -1
  174. package/dist/composed/labels.d.ts +57 -3
  175. package/dist/composed/labels.d.ts.map +1 -1
  176. package/dist/composed/labels.js +261 -12
  177. package/dist/composed/labels.js.map +1 -1
  178. package/dist/composed/modules.d.ts +30 -0
  179. package/dist/composed/modules.d.ts.map +1 -0
  180. package/dist/composed/modules.js +194 -0
  181. package/dist/composed/modules.js.map +1 -0
  182. package/dist/composed/overlap.d.ts +52 -0
  183. package/dist/composed/overlap.d.ts.map +1 -0
  184. package/dist/composed/overlap.js +611 -0
  185. package/dist/composed/overlap.js.map +1 -0
  186. package/dist/composed/paint.d.ts +28 -5
  187. package/dist/composed/paint.d.ts.map +1 -1
  188. package/dist/composed/paint.js +26 -5
  189. package/dist/composed/paint.js.map +1 -1
  190. package/dist/composed/reflowLog.d.ts +6 -1
  191. package/dist/composed/reflowLog.d.ts.map +1 -1
  192. package/dist/composed/reflowLog.js +43 -0
  193. package/dist/composed/reflowLog.js.map +1 -1
  194. package/dist/composed/repeat.d.ts +66 -0
  195. package/dist/composed/repeat.d.ts.map +1 -0
  196. package/dist/composed/repeat.js +217 -0
  197. package/dist/composed/repeat.js.map +1 -0
  198. package/dist/composed/slotChecks.d.ts +1 -1
  199. package/dist/composed/slotChecks.d.ts.map +1 -1
  200. package/dist/composed/slotChecks.js +114 -8
  201. package/dist/composed/slotChecks.js.map +1 -1
  202. package/dist/composed/sourceMap.d.ts.map +1 -1
  203. package/dist/composed/sourceMap.js +16 -4
  204. package/dist/composed/sourceMap.js.map +1 -1
  205. package/dist/composed/spread.d.ts.map +1 -1
  206. package/dist/composed/spread.js +6 -2
  207. package/dist/composed/spread.js.map +1 -1
  208. package/dist/composed/templates.d.ts +22 -2
  209. package/dist/composed/templates.d.ts.map +1 -1
  210. package/dist/composed/templates.js +159 -9
  211. package/dist/composed/templates.js.map +1 -1
  212. package/dist/composed/types.d.ts +140 -7
  213. package/dist/composed/types.d.ts.map +1 -1
  214. package/dist/composed/validate.d.ts +1 -1
  215. package/dist/composed/validate.d.ts.map +1 -1
  216. package/dist/composed/validate.js +101 -4
  217. package/dist/composed/validate.js.map +1 -1
  218. package/dist/composed/wrap.d.ts.map +1 -1
  219. package/dist/composed/wrap.js +95 -17
  220. package/dist/composed/wrap.js.map +1 -1
  221. package/dist/configWarnings.d.ts +7 -1
  222. package/dist/configWarnings.d.ts.map +1 -1
  223. package/dist/configWarnings.js +275 -4
  224. package/dist/configWarnings.js.map +1 -1
  225. package/dist/contrast.d.ts.map +1 -1
  226. package/dist/contrast.js +8 -0
  227. package/dist/contrast.js.map +1 -1
  228. package/dist/defaults/calloutStyles.d.ts +10 -1
  229. package/dist/defaults/calloutStyles.d.ts.map +1 -1
  230. package/dist/defaults/calloutStyles.js +46 -7
  231. package/dist/defaults/calloutStyles.js.map +1 -1
  232. package/dist/defaults/comics.d.ts +2 -1
  233. package/dist/defaults/comics.d.ts.map +1 -1
  234. package/dist/defaults/comics.js +18 -3
  235. package/dist/defaults/comics.js.map +1 -1
  236. package/dist/defaults/headerFooter.d.ts.map +1 -1
  237. package/dist/defaults/headerFooter.js +38 -0
  238. package/dist/defaults/headerFooter.js.map +1 -1
  239. package/dist/design/boxShape.d.ts +101 -0
  240. package/dist/design/boxShape.d.ts.map +1 -0
  241. package/dist/design/boxShape.js +398 -0
  242. package/dist/design/boxShape.js.map +1 -0
  243. package/dist/design/connector.d.ts +75 -0
  244. package/dist/design/connector.d.ts.map +1 -0
  245. package/dist/design/connector.js +325 -0
  246. package/dist/design/connector.js.map +1 -0
  247. package/dist/design/layout.d.ts +56 -3
  248. package/dist/design/layout.d.ts.map +1 -1
  249. package/dist/design/layout.js +214 -12
  250. package/dist/design/layout.js.map +1 -1
  251. package/dist/design/logicalEdges.d.ts +7 -0
  252. package/dist/design/logicalEdges.d.ts.map +1 -1
  253. package/dist/design/logicalEdges.js +28 -0
  254. package/dist/design/logicalEdges.js.map +1 -1
  255. package/dist/design/pathBlock.d.ts +69 -0
  256. package/dist/design/pathBlock.d.ts.map +1 -0
  257. package/dist/design/pathBlock.js +286 -0
  258. package/dist/design/pathBlock.js.map +1 -0
  259. package/dist/design/svgPath.d.ts +48 -0
  260. package/dist/design/svgPath.d.ts.map +1 -0
  261. package/dist/design/svgPath.js +402 -0
  262. package/dist/design/svgPath.js.map +1 -0
  263. package/dist/fonts/usage.d.ts +5 -0
  264. package/dist/fonts/usage.d.ts.map +1 -1
  265. package/dist/fonts/usage.js +81 -11
  266. package/dist/fonts/usage.js.map +1 -1
  267. package/dist/html-backend.d.ts.map +1 -1
  268. package/dist/html-backend.js +127 -22
  269. package/dist/html-backend.js.map +1 -1
  270. package/dist/index.d.ts +19 -9
  271. package/dist/index.d.ts.map +1 -1
  272. package/dist/index.js +13 -5
  273. package/dist/index.js.map +1 -1
  274. package/dist/measure/cache.js +1 -1
  275. package/dist/measure/cache.js.map +1 -1
  276. package/dist/measure/inlineIcon.d.ts +34 -0
  277. package/dist/measure/inlineIcon.d.ts.map +1 -0
  278. package/dist/measure/inlineIcon.js +89 -0
  279. package/dist/measure/inlineIcon.js.map +1 -0
  280. package/dist/measure/rich.d.ts +2 -1
  281. package/dist/measure/rich.d.ts.map +1 -1
  282. package/dist/measure/rich.js +6 -0
  283. package/dist/measure/rich.js.map +1 -1
  284. package/dist/migrate/pageDesign.d.ts.map +1 -1
  285. package/dist/migrate/pageDesign.js +4 -2
  286. package/dist/migrate/pageDesign.js.map +1 -1
  287. package/dist/migrate/textToMarkdown.js +2 -0
  288. package/dist/migrate/textToMarkdown.js.map +1 -1
  289. package/dist/parse/blockParser.d.ts.map +1 -1
  290. package/dist/parse/blockParser.js +171 -20
  291. package/dist/parse/blockParser.js.map +1 -1
  292. package/dist/parse/index.d.ts +1 -1
  293. package/dist/parse/index.d.ts.map +1 -1
  294. package/dist/parse/index.js.map +1 -1
  295. package/dist/parse/inlineFormatting.d.ts +11 -3
  296. package/dist/parse/inlineFormatting.d.ts.map +1 -1
  297. package/dist/parse/inlineFormatting.js +63 -5
  298. package/dist/parse/inlineFormatting.js.map +1 -1
  299. package/dist/parse/inlineSnippet.d.ts +2 -1
  300. package/dist/parse/inlineSnippet.d.ts.map +1 -1
  301. package/dist/parse/inlineSnippet.js +3 -2
  302. package/dist/parse/inlineSnippet.js.map +1 -1
  303. package/dist/parse/sourceMapping.d.ts.map +1 -1
  304. package/dist/parse/sourceMapping.js +8 -1
  305. package/dist/parse/sourceMapping.js.map +1 -1
  306. package/dist/parse/types.d.ts +71 -1
  307. package/dist/parse/types.d.ts.map +1 -1
  308. package/dist/pipeline/build.d.ts.map +1 -1
  309. package/dist/pipeline/build.js +67 -13
  310. package/dist/pipeline/build.js.map +1 -1
  311. package/dist/pipeline/buildHelpers.d.ts +10 -0
  312. package/dist/pipeline/buildHelpers.d.ts.map +1 -1
  313. package/dist/pipeline/buildHelpers.js +17 -0
  314. package/dist/pipeline/buildHelpers.js.map +1 -1
  315. package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
  316. package/dist/pipeline/buildMeasurement.js +23 -11
  317. package/dist/pipeline/buildMeasurement.js.map +1 -1
  318. package/dist/pipeline/calloutLayout.d.ts.map +1 -1
  319. package/dist/pipeline/calloutLayout.js +142 -32
  320. package/dist/pipeline/calloutLayout.js.map +1 -1
  321. package/dist/pipeline/composedLayout.d.ts +1 -33
  322. package/dist/pipeline/composedLayout.d.ts.map +1 -1
  323. package/dist/pipeline/composedLayout.js +526 -36
  324. package/dist/pipeline/composedLayout.js.map +1 -1
  325. package/dist/pipeline/config.d.ts.map +1 -1
  326. package/dist/pipeline/config.js +12 -0
  327. package/dist/pipeline/config.js.map +1 -1
  328. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  329. package/dist/pipeline/contentWarnings.js +46 -1
  330. package/dist/pipeline/contentWarnings.js.map +1 -1
  331. package/dist/pipeline/continuation.d.ts.map +1 -1
  332. package/dist/pipeline/continuation.js +3 -2
  333. package/dist/pipeline/continuation.js.map +1 -1
  334. package/dist/pipeline/headerFooter.d.ts +19 -1
  335. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  336. package/dist/pipeline/headerFooter.js +169 -17
  337. package/dist/pipeline/headerFooter.js.map +1 -1
  338. package/dist/pipeline/partPalette.d.ts.map +1 -1
  339. package/dist/pipeline/partPalette.js +40 -1
  340. package/dist/pipeline/partPalette.js.map +1 -1
  341. package/dist/pipeline/resourceLayout.d.ts +1 -1
  342. package/dist/pipeline/resourceLayout.d.ts.map +1 -1
  343. package/dist/pipeline/resourceLayout.js +44 -15
  344. package/dist/pipeline/resourceLayout.js.map +1 -1
  345. package/dist/preflight.d.ts.map +1 -1
  346. package/dist/preflight.js +36 -1
  347. package/dist/preflight.js.map +1 -1
  348. package/dist/types.d.ts +319 -22
  349. package/dist/types.d.ts.map +1 -1
  350. package/dist/vdt.d.ts +392 -12
  351. package/dist/vdt.d.ts.map +1 -1
  352. package/dist/vdt.js.map +1 -1
  353. package/package.json +1 -1
@@ -1,3 +1,36 @@
1
+ /**
2
+ * Lay out one composed page (#855, DESIGN §5.1–5.2): place the frames of
3
+ * its template, pour each story (`composed/wrap.ts`) into its frames with
4
+ * the box typesetter (`frameLayout.ts`), and hand back the blocks for the
5
+ * page's floats with the page's `VDTComposedPage`.
6
+ *
7
+ * Per frame, in {@link frameOrder}: its box ({@link placeBox}, an `'auto'`
8
+ * height measured with the content the frame will hold), then its content:
9
+ * the story from where the frame before it in the chain stopped, cut at the
10
+ * frame's foot when a `next` frame takes the rest, else laid whole and
11
+ * clipped at the foot. Text runs round the frames that push it (`exclude:
12
+ * 'push'`) already placed; groups negotiate their frames' space (#856).
13
+ *
14
+ * A frame whose content still does not fit climbs the reflow ladder (#858,
15
+ * DESIGN §5.4), frame by frame in template order, each step on top of the
16
+ * ones before it, stopping at the first that makes it fit: free space (a
17
+ * fixed height grows to its `maxHeight`), its group pushing and yielding
18
+ * (already part of every layout), smaller pictures in the frame and its
19
+ * group (`fit.imageMinScale`), the group's other frames down to their
20
+ * minimum or their content, smaller text (`fit.minScale`), continuation on
21
+ * another page (#859). Each step lays the whole page again, so a change
22
+ * stays on the page. What still does not fit is clipped and reported
23
+ * (`frameOverflow`) with the steps tried and proposals worked out on the
24
+ * page.
25
+ *
26
+ * A frame whose `overflow.continueIn` sends content to another page
27
+ * (`'next-page'`, `'template:<id>'`, #859) takes the ladder's last step
28
+ * before the warning: it is cut at its foot, with its mark, its text and
29
+ * pictures back at full size, and the rest is handed back
30
+ * (`ComposedPageLayout.continuations`) for `build.ts` to lay on pages it
31
+ * adds after this one, with this function (`ComposedPageInput.continuing`).
32
+ */
33
+ import { isCornerRadii } from '../design/boxShape';
1
34
  import { pageIsMirrored, pageIsVertical } from '../vdt';
2
35
  import { contourBounds, scaleContourAbout, shiftContour } from '../contour';
3
36
  import { frameContour } from '../composed/contour';
@@ -8,11 +41,14 @@ import { chainHead, FRAME_ATTR } from '../composed/wrap';
8
41
  import { framePlaceable, nextFrameOf, groupPlaceable, placeBox, unionRect } from '../composed/geometry';
9
42
  import { composedOrder, groupOfFrames, layoutGroup } from '../composed/groups';
10
43
  import { reflowEvents } from '../composed/reflowLog';
11
- import { anchorTarget, resolveContinueIn } from '../composed/templates';
44
+ import { anchorTarget, repeatInstance, repeatedFrames, resolveContinueIn } from '../composed/templates';
12
45
  import { collapsedOnAddedPage, collapseFrames, continuationTemplate, continuesCollapsed, continuesOffPage, trailingFrames } from '../composed/continuation';
13
- import { resolvePageTemplate } from '../composed/templates';
46
+ import { isModuleTemplate, resolvePageTemplate } from '../composed/templates';
14
47
  import { frameElementId } from '../composed/ids';
48
+ import { expandRepeats, instanceOf, repeatItemsOf } from '../composed/repeat';
49
+ import { moduleOfFrame } from '../composed/modules';
15
50
  import { frameLinePx, frameRuleBlocks } from '../composed/rules';
51
+ import { overlapChecks } from '../composed/overlap';
16
52
  import { offsetCalloutToAbsolute } from './calloutLayout';
17
53
  import { isMarkerBlock } from './buildHelpers';
18
54
  import { createLayoutMemo, layoutMemoSwitch } from './layoutMemo';
@@ -21,7 +57,7 @@ import { computeOrderedListRunMetrics } from './lists';
21
57
  import { planParagraphContainers } from './paragraphContainers';
22
58
  import { CUT_START, fragmentRange, frameCutRules, makeCalloutLayouter, partBreakCut, splitCalloutFragment } from './frameLayout';
23
59
  const MM_PER_IN = 25.4;
24
- const NO_FIT = { patches: new Map(), text: new Map(), pictures: new Map(), yieldAll: new Set(), goOn: new Set() };
60
+ const NO_FIT = { patches: new Map(), text: new Map(), pictures: new Map(), yieldAll: new Set(), goOn: new Set(), gridsOn: new Set() };
25
61
  /** Step of the text and picture scales the ladder tries (DESIGN §5.4). */
26
62
  const FIT_STEP = 0.025;
27
63
  /** Lowest text scale an `allowShrink` proposal goes to. */
@@ -66,7 +102,12 @@ function chainPrev(template, receivers) {
66
102
  return prev;
67
103
  }
68
104
  const sameCut = (a, b) => a.child === b.child && a.line === b.line && JSON.stringify(a.group ?? null) === JSON.stringify(b.group ?? null);
69
- export function layoutComposedPage(given) {
105
+ export function layoutComposedPage(asked) {
106
+ // The grids that repeat a frame set (#1061) are laid with one instance of
107
+ // the set per item the page's stories hold (`<frame>[n]`): from here on
108
+ // they are plain frames.
109
+ const expanded = expandRepeats(asked.template, repeatItemsOf(asked.template, asked.stories.keys()));
110
+ const given = expanded === asked.template ? asked : { ...asked, template: expanded };
70
111
  // A page added by continuation (#859) lays its template with the
71
112
  // continuation's rules (and, for the template laid again, its other
72
113
  // frames collapsed).
@@ -78,7 +119,9 @@ export function layoutComposedPage(given) {
78
119
  const fi = fence.fenceIndex;
79
120
  const mmPx = dpi / MM_PER_IN;
80
121
  const byName = new Map(template.frames.map((f) => [f.name, f]));
81
- const groupOf = groupOfFrames(template);
122
+ // (A modules group, #1062, holds its modules' frames to lay them as one
123
+ // step: what a frame shares room with is the group of its own module.)
124
+ const groupOf = groupOfFrames({ ...template, groups: template.groups.filter((g) => !g.modules) });
82
125
  const prev = chainPrev(template, input.continuing?.receivers);
83
126
  const headOf = (name) => {
84
127
  let at = name;
@@ -89,11 +132,17 @@ export function layoutComposedPage(given) {
89
132
  }
90
133
  return at;
91
134
  };
135
+ /** The module a frame belongs to (#1062), when its modules group goes on
136
+ * by whole modules (`overflow.continueIn` off the page). */
137
+ const movingModule = (name) => {
138
+ const m = moduleOfFrame(template, name);
139
+ return m && !input.continuing?.last && continuesOffPage(m.group.overflow?.continueIn) ? m : undefined;
140
+ };
92
141
  // Compositions by fit state: the ladder and its proposals ask for some
93
142
  // twice.
94
143
  const memo = new Map();
95
144
  let runs = 0;
96
- const keyOf = (tpl, fit) => JSON.stringify([tpl.id, [...fit.patches], [...fit.text], [...fit.pictures], [...fit.yieldAll], [...fit.goOn]]);
145
+ const keyOf = (tpl, fit) => JSON.stringify([tpl.id, [...fit.patches], [...fit.text], [...fit.pictures], [...fit.yieldAll], [...fit.goOn], [...fit.gridsOn]]);
97
146
  /** Frames a page added by continuation lays with no room (#859). */
98
147
  const collapsedHere = input.continuing ? collapsedOnAddedPage(given.template, input.continuing) : new Set();
99
148
  const holdsStory = (name) => input.stories.has(headOf(name));
@@ -234,9 +283,18 @@ export function layoutComposedPage(given) {
234
283
  };
235
284
  /** Story heads of the frame and of its group's frames whose content holds
236
285
  * a picture. */
286
+ /** The frames a frame shares its group's room with: its group's, or for
287
+ * an instance of a grid's set (#1061) its own item's. */
288
+ const peersOf = (name) => {
289
+ const inst = instanceOf(template, name);
290
+ if (inst)
291
+ return inst.group.items.find((c) => c.item === inst.item).frames;
292
+ // A module's frame (#1062) outside a group of its own: its module's.
293
+ return groupOf.get(name)?.frames ?? moduleOfFrame(template, name)?.frames;
294
+ };
237
295
  const pictureHeads = (frame) => {
238
- const g = groupOf.get(frame.name);
239
- const names = g ? [frame.name, ...g.frames.filter((m) => m !== frame.name)] : [frame.name];
296
+ const peers = peersOf(frame.name);
297
+ const names = peers ? [frame.name, ...peers.filter((m) => m !== frame.name)] : [frame.name];
240
298
  const heads = [];
241
299
  for (const n of names) {
242
300
  const head = headOf(n);
@@ -266,8 +324,8 @@ export function layoutComposedPage(given) {
266
324
  return false;
267
325
  };
268
326
  const movesOthers = (frame) => {
269
- const g = groupOf.get(frame.name);
270
- if (g && g.frames.filter((m) => byName.has(m)).length > 1)
327
+ const peers = peersOf(frame.name);
328
+ if (peers && peers.filter((m) => byName.has(m)).length > 1)
271
329
  return true;
272
330
  return template.frames.some((f) => f.name !== frame.name && !f.grid && anchorTarget(f.placement) === frame.name);
273
331
  };
@@ -309,7 +367,8 @@ export function layoutComposedPage(given) {
309
367
  // width) back, down to its minimum or its content.
310
368
  const redistribute = () => {
311
369
  const g = groupOf.get(name);
312
- if (!g || g.frames.filter((m) => byName.has(m)).length <= 1)
370
+ // (A grid's rows are as tall as their tallest cell: nothing to give.)
371
+ if (!g || g.kind === 'grid' || g.frames.filter((m) => byName.has(m)).length <= 1)
313
372
  return false;
314
373
  steps.push('redistribute');
315
374
  return attempt({ ...fit, yieldAll: new Set([...fit.yieldAll, g.name]) }, name);
@@ -325,15 +384,26 @@ export function layoutComposedPage(given) {
325
384
  // `'template:<id>'` or `'page:<id>'`, #859). On a page added by
326
385
  // continuation, a frame that would take none of what it receives
327
386
  // sends nothing on (it would go on for ever): it warns.
387
+ // An instance of a grid's set (#1061) goes on with its whole row, as
388
+ // the grid's main frame says (`overflow.continueIn`, `fit`).
389
+ // A frame of a module (#1062) whose modules group goes on goes on with
390
+ // its whole module, as the group says.
391
+ const moving = movingModule(name);
392
+ const grid = moving ? undefined : instanceOf(template, name)?.group;
393
+ const ruler = moving
394
+ ? { ...frame, overflow: { ...frame.overflow, continueIn: moving.group.overflow.continueIn } }
395
+ : grid ? byName.get(grid.items[0].frames[0]) ?? frame : frame;
328
396
  const goOn = () => {
329
- if (!continuesOffPage(frame.overflow.continueIn))
397
+ if (!continuesOffPage(ruler.overflow.continueIn))
330
398
  return false;
331
399
  steps.push('continue');
332
400
  const before = { fit, cur };
333
401
  // (Only for a story that came from another page: a page's own story
334
402
  // may go on whole.)
335
403
  const received = input.continuing?.receivers.has(headOf(name)) ?? false;
336
- if (attempt(goingOn(fit, frame), name) && (!received || cur.spills.get(name)?.advanced !== false))
404
+ const wholeIn = moving?.group ?? grid;
405
+ const next = wholeIn ? { ...fit, gridsOn: new Set([...fit.gridsOn, wholeIn.name]) } : goingOn(fit, frame);
406
+ if (attempt(next, name) && (!received || cur.spills.get(name)?.advanced !== false))
337
407
  return true;
338
408
  ({ fit, cur } = before);
339
409
  return false;
@@ -343,7 +413,7 @@ export function layoutComposedPage(given) {
343
413
  // one size from page to page. The pictures and the text shrink only
344
414
  // when it cannot go on (the last page continuation may add, or a
345
415
  // receiver that takes none of it).
346
- const order = continuesOffPage(frame.overflow.continueIn) && frame.fit.preferContinue
416
+ const order = continuesOffPage(ruler.overflow.continueIn) && ruler.fit.preferContinue
347
417
  ? [redistribute, goOn, scaleImages, shrinkText]
348
418
  : [scaleImages, redistribute, shrinkText, goOn];
349
419
  for (const step of order)
@@ -353,10 +423,21 @@ export function layoutComposedPage(given) {
353
423
  // 7. Warn, with what would make each frame that still overflows fit.
354
424
  const warnings = [...(cur.notes ?? [])];
355
425
  const templateAttr = fence.attrSources.template;
426
+ // The rows of a grid cut at its foot (#1061) warn once, on the last
427
+ // instance cut.
428
+ // (And so do the modules cut at the foot of their group, #1062.)
429
+ const cutIn = (name) => moduleOfFrame(template, name)?.group.name ?? instanceOf(template, name)?.group.name ?? '';
430
+ const lastCut = new Map();
431
+ for (const frame of template.frames) {
432
+ if (cur.clipped.has(frame.name) && overflows(cur, frame.name))
433
+ lastCut.set(cutIn(frame.name), frame.name);
434
+ }
356
435
  for (const frame of template.frames) {
357
436
  const vdt = frameIn(cur, frame.name);
358
437
  if (!vdt || vdt.overflowPx <= 0.5)
359
438
  continue;
439
+ if (cur.clipped.has(frame.name) && lastCut.get(cutIn(frame.name)) !== frame.name)
440
+ continue;
360
441
  const overflowMm = Math.round((vdt.overflowPx / mmPx) * 10) / 10;
361
442
  const proposals = overflowProposals({
362
443
  frame,
@@ -396,7 +477,23 @@ export function layoutComposedPage(given) {
396
477
  pageIndex: input.page.index,
397
478
  });
398
479
  }
399
- const { frames, blocks, log, laid, fitted } = cur;
480
+ // 8. What no frame's box says (#1053): ink under a frame painted over it,
481
+ // and ink past the trim, the bleed or the box it is anchored inside.
482
+ const overlaps = overlapChecks({
483
+ template: cur.template,
484
+ frames: cur.frames,
485
+ blocks: cur.blocks,
486
+ groupRects: cur.groupRects,
487
+ areas: input.areas,
488
+ dpi,
489
+ emPx: input.emPx,
490
+ fenceIndex: fi,
491
+ pageIndex: input.page.index,
492
+ ...(input.locale ? { locale: input.locale } : {}),
493
+ });
494
+ warnings.push(...overlaps.warnings);
495
+ const { frames, blocks, laid, fitted } = cur;
496
+ const log = overlaps.events.length > 0 ? mergeLog(cur.log, overlaps.events, template) : cur.log;
400
497
  // Template order on the page record.
401
498
  const order = new Map(template.frames.map((f, i) => [f.name, i]));
402
499
  frames.sort((a, b) => order.get(a.name) - order.get(b.name));
@@ -411,6 +508,10 @@ export function layoutComposedPage(given) {
411
508
  frames,
412
509
  log,
413
510
  configPath: template.configPath,
511
+ // The modules laid (#1062), each with its box.
512
+ ...(template.modules && template.modules.length > 0
513
+ ? { modules: template.modules.map((m) => ({ name: m.name, template: m.template, configPath: m.configPath, ...(cur.moduleRects.has(m.name) ? { rect: { ...cur.moduleRects.get(m.name) } } : {}) })) }
514
+ : {}),
414
515
  sourceStart: contentBlocks[input.startIdx].sourceStart + input.bodyOffset,
415
516
  sourceEnd: contentBlocks[input.endIdx].sourceEnd + input.bodyOffset,
416
517
  };
@@ -423,12 +524,26 @@ export function layoutComposedPage(given) {
423
524
  if (!spill || !story)
424
525
  continue;
425
526
  const came = input.continuing?.receivers.get(spill.head);
527
+ // An item of a grid (#1061) goes on whole, as its main frame says; the
528
+ // instances of its set that hold nothing go with it.
529
+ // A module (#1062) goes on whole as its group says, its frames that
530
+ // hold nothing along with the first of them that goes; any frame of a
531
+ // module goes on in that module on the page it goes to.
532
+ const inModule = moduleOfFrame(template, frame.name);
533
+ const wholeModule = spill.whole && inModule && continuesOffPage(inModule.group.overflow?.continueIn) ? inModule : undefined;
534
+ const cell = spill.whole && !wholeModule ? instanceOf(template, frame.name) : undefined;
535
+ const cellFrames = cell ? cell.group.items.find((c) => c.item === cell.item).frames : [];
536
+ const ruler = cell ? byName.get(cellFrames[0]) ?? frame : frame;
537
+ const firstGoing = wholeModule ? wholeModule.frames.find((n) => cur.spills.has(n)) : undefined;
538
+ const empty = cell?.main
539
+ ? cellFrames.filter((n) => n !== frame.name && !cur.spills.has(n))
540
+ : wholeModule && firstGoing === frame.name ? wholeModule.frames.filter((n) => n !== frame.name && !cur.spills.has(n)) : [];
426
541
  continuations.push({
427
542
  frame: frame.name,
428
543
  fromFrameId: spill.frameId,
429
544
  head: spill.head,
430
545
  // `{attr.x}` read from the fence (#917).
431
- rule: resolveContinueIn(frame.overflow.continueIn, fence.attrs),
546
+ rule: resolveContinueIn(wholeModule ? wholeModule.group.overflow.continueIn : ruler.overflow.continueIn, fence.attrs),
432
547
  template: template.id,
433
548
  collapsed: input.continuing?.collapse ?? false,
434
549
  story,
@@ -437,8 +552,10 @@ export function layoutComposedPage(given) {
437
552
  headBlockId: spill.headBlockId,
438
553
  // A story that jumped here (#917) goes on named by this page's frame.
439
554
  slot: (input.continuing?.jump ? undefined : came?.slot) ?? frame.name,
440
- ...(trailing.has(frame.name) ? { trailing: trailing.get(frame.name) } : {}),
555
+ ...(trailing.has(frame.name) ? { trailing: trailing.get(frame.name) } : empty.length > 0 ? { trailing: empty } : {}),
441
556
  ...(frame.overflow.fromMark !== undefined ? { fromMark: frame.overflow.fromMark } : {}),
557
+ ...(spill.whole ? { whole: true } : {}),
558
+ ...(inModule ? { module: { name: inModule.module.name, template: inModule.module.template } } : {}),
442
559
  });
443
560
  }
444
561
  return { composed, blocks, warnings, laid, ...(fitted.size > 0 ? { fitted } : {}), ...(continuations.length > 0 ? { continuations } : {}) };
@@ -575,9 +692,14 @@ function regroupStories(input, alt) {
575
692
  const flow = alt.frames.find((f) => f.flow);
576
693
  const flowHead = flow ? chainHead(alt, flow.name) : undefined;
577
694
  const current = input.template.frames;
695
+ const repeatedIn = repeatedFrames(alt);
578
696
  const headIn = (head) => {
579
697
  if (names.has(head))
580
698
  return chainHead(alt, head);
699
+ // An instance of a frame `alt` repeats too (#1061) stays an instance.
700
+ const inst = repeatInstance(head);
701
+ if (inst && repeatedIn.has(inst.frame))
702
+ return head;
581
703
  const takes = current.find((f) => f.name === head)?.takes;
582
704
  const taker = takes ? alt.frames.find((f) => f.takes === takes) : undefined;
583
705
  return taker ? chainHead(alt, taker.name) : flowHead;
@@ -759,16 +881,27 @@ export function keepChromeAt(style, scale) {
759
881
  return {
760
882
  ...style,
761
883
  padding: { top: d(p.top), right: d(p.right), bottom: d(p.bottom), left: d(p.left) },
762
- border: { ...style.border, width: d(style.border.width) },
763
- borderRadius: d(style.borderRadius),
884
+ border: {
885
+ ...style.border,
886
+ width: d(style.border.width),
887
+ ...(style.border.sides
888
+ ? { sides: Object.fromEntries(Object.entries(style.border.sides).map(([k, v]) => [k, { ...v, width: d(v.width) }])) }
889
+ : {}),
890
+ },
891
+ borderRadius: isCornerRadii(style.borderRadius)
892
+ ? Object.fromEntries(Object.entries(style.borderRadius).map(([k, v]) => [k, v ? d(v) : v]))
893
+ : d(style.borderRadius),
764
894
  stripe: { ...style.stripe, width: d(style.stripe.width) },
765
895
  ...(style.label
766
- ? { label: { ...style.label, height: d(style.label.height), paddingX: d(style.label.paddingX), offset: d(style.label.offset), inset: d(style.label.inset), rule: { ...style.label.rule, width: d(style.label.rule.width) } } }
896
+ ? { label: { ...style.label, height: d(style.label.height), paddingX: d(style.label.paddingX), offset: d(style.label.offset), inset: d(style.label.inset), rule: { ...style.label.rule, width: d(style.label.rule.width) }, ...(style.label.fillet ? { fillet: d(style.label.fillet) } : {}) } }
767
897
  : {}),
768
898
  };
769
899
  }
770
900
  /** Lay out every frame of the page once, with the reflow ladder's `fit`. */
771
- function composePage(input, template, fit, collapsed) {
901
+ function composePage(pageInput, template, fit, collapsed) {
902
+ /** What the frames are placed in: the page, or, while a module's frames
903
+ * are placed (#1062), the page with the module's box as its area. */
904
+ let input = pageInput;
772
905
  const { pass, fence, dpi } = input;
773
906
  const fi = fence.fenceIndex;
774
907
  const cont = input.continuing;
@@ -778,6 +911,8 @@ function composePage(input, template, fit, collapsed) {
778
911
  * the fence's page that first sent it on. */
779
912
  const frameIdOf = (name) => (cont && !cont.jump ? frameElementId(fi, cont.receivers.get(name)?.slot ?? name, cont.generation) : frameElementId(fi, name));
780
913
  const byName = new Map(template.frames.map((f) => [f.name, f]));
914
+ /** Frames a grid repeats (#1061): their instances are `<frame>[n]`. */
915
+ const repeated = new Set(template.groups.flatMap((g) => (g.items ? g.repeat?.frames ?? [] : [])));
781
916
  const prev = chainPrev(template, cont?.receivers);
782
917
  /** The head of the chain a frame belongs to. */
783
918
  const headOf = (name) => {
@@ -926,6 +1061,10 @@ function composePage(input, template, fit, collapsed) {
926
1061
  const laid = new Set();
927
1062
  const spills = new Map();
928
1063
  const wantsOn = new Set();
1064
+ const clipped = new Set();
1065
+ const moduleRects = new Map();
1066
+ /** The foot a module cut at its group's foot is clipped to (#1062). */
1067
+ let clipFoot;
929
1068
  const pushed = seed
930
1069
  ? [...seed].map(([name, box]) => ({ name, box, ...(seedShapes?.get(name) ? { contour: seedShapes.get(name) } : {}) }))
931
1070
  : [];
@@ -1207,7 +1346,7 @@ function composePage(input, template, fit, collapsed) {
1207
1346
  const start = firstFree?.own ?? input.areas.area;
1208
1347
  const box = ownBox ?? { x: start.x, y: start.y, width: 0, height: 0 };
1209
1348
  const out = layoutGroup({
1210
- kind: group.kind,
1349
+ kind: group.kind === 'row' ? 'row' : 'stack',
1211
1350
  box,
1212
1351
  elastic: !ownBox,
1213
1352
  limit: input.areas.area,
@@ -1258,6 +1397,170 @@ function composePage(input, template, fit, collapsed) {
1258
1397
  groupDeclared.set(group.name, decl.box);
1259
1398
  return fin.rects;
1260
1399
  };
1400
+ /** Lay a grid (#1061): its cells fill rows from the start side of the
1401
+ * page's lines (the flow frame is mirrored on a right-to-left page, so
1402
+ * its left is the start), `columns` to a row, `columnGap` and `rowGap`
1403
+ * between them. A cell is one item of a repeated frame set (or one
1404
+ * frame of a grid that repeats none): the main frame takes the cell's
1405
+ * width and its own height rule, the set's other frames are placed as
1406
+ * the template draws them, anchored to the main frame or to each
1407
+ * other (one that anchors to nothing in the set moves with the cell).
1408
+ * A row is as tall as its tallest cell, the set's frames counted;
1409
+ * `rowAlign` places the shorter ones (`'stretch'`: their main frame
1410
+ * grows to the row). The grid runs down its own box, or to the foot of
1411
+ * the area from where the template draws its main frame. A row that
1412
+ * does not fit is clipped at the foot, or, when the grid goes on to
1413
+ * another page (`FitState.gridsOn`), it and every row after it are
1414
+ * left for that page, whole (`spilled`). */
1415
+ const placeGrid = (group, members) => {
1416
+ const byMember = new Map(members.map((f) => [f.name, f]));
1417
+ const cells = (group.items ?? (group.repeat ? [] : members.map((f) => ({ item: 0, frames: [f.name] }))))
1418
+ .map((c) => c.frames.filter((n) => byMember.has(n)))
1419
+ .filter((c) => c.length > 0);
1420
+ const laidBoxes = new Map();
1421
+ const spilled = new Set();
1422
+ const cut = [];
1423
+ const own = groupPlaceable(group);
1424
+ const ownBox = own ? placeBox(own, template, input, refOf, () => 0) : undefined;
1425
+ const area = input.areas.area;
1426
+ const first = cells[0] ? byMember.get(cells[0][0]) : undefined;
1427
+ /** Where the template draws a cell's main frame. */
1428
+ const drawnOf = (main) => placeBox(framePlaceable(main), template, input, refOf, () => 0);
1429
+ const drawnFirst = first ? drawnOf(first) : area;
1430
+ const box = ownBox ?? { x: drawnFirst.x, y: drawnFirst.y, width: Math.max(0, area.x + area.width - drawnFirst.x), height: Math.max(0, area.y + area.height - drawnFirst.y) };
1431
+ const foot = box.y + box.height;
1432
+ const columns = Math.max(1, group.columns ?? 1);
1433
+ const colGap = dimensionToPx(group.columnGap ?? group.gap, dpi, input.emPx);
1434
+ const rowGap = dimensionToPx(group.rowGap ?? group.gap, dpi, input.emPx);
1435
+ const colW = Math.max(1, (box.width - colGap * (columns - 1)) / columns);
1436
+ const xOf = (c) => box.x + c * (colW + colGap);
1437
+ const px = (d) => (d ? dimensionToPx(d, dpi, input.emPx) : undefined);
1438
+ /** The main frame's height in a cell `w` wide: its own rule, held
1439
+ * between its minimum and maximum. */
1440
+ const mainHeight = (f, w) => {
1441
+ const h = f.height;
1442
+ let v;
1443
+ if (h === 'auto')
1444
+ v = naturalOf(prepare(f))(w);
1445
+ else if (h === 'fill')
1446
+ v = 0;
1447
+ else if ('percent' in h)
1448
+ v = (area.height * h.percent) / 100;
1449
+ else
1450
+ v = dimensionToPx(h, dpi, input.emPx);
1451
+ const min = px(f.minHeight) ?? 0;
1452
+ const max = px(f.maxHeight) ?? Infinity;
1453
+ return Math.max(min, Math.min(v, max));
1454
+ };
1455
+ /** The boxes of a cell's frames with its main frame at `(x, y)`,
1456
+ * `h` tall; and the frames that count for the cell's extent. */
1457
+ const layCell = (cell, x, y, h) => {
1458
+ const main = byMember.get(cell[0]);
1459
+ const boxes = new Map([[main.name, { x, y, width: colW, height: h }]]);
1460
+ const inCell = [main.name];
1461
+ const set = new Set(cell);
1462
+ const drawn = drawnOf(main);
1463
+ const refs = (n) => boxes.get(n) ?? refOf(n);
1464
+ // A frame anchored in the set fills (and takes its percentages of)
1465
+ // the cell's column, not the whole area.
1466
+ const column = { ...input, areas: { ...input.areas, area: { x, y: area.y, width: colW, height: area.height } } };
1467
+ let todo = cell.slice(1);
1468
+ for (let pass = 0; todo.length > 0 && pass <= cell.length; pass++) {
1469
+ const later = [];
1470
+ for (const name of todo) {
1471
+ const f = byMember.get(name);
1472
+ const target = f.grid ? undefined : anchorTarget(f.placement);
1473
+ // Anchored to a frame of the set not placed yet: next pass.
1474
+ if (target !== undefined && set.has(target) && !boxes.has(target) && pass < cell.length) {
1475
+ later.push(name);
1476
+ continue;
1477
+ }
1478
+ const p = prepare(f);
1479
+ const inSet = target !== undefined && set.has(target);
1480
+ const r = placeFitted(f, framePlaceable(f), inSet ? column : input, refs, naturalOf(p), naturalWidthOf(p));
1481
+ if (target !== undefined && !set.has(target)) {
1482
+ // Anchored to a frame outside the set: where it says.
1483
+ boxes.set(name, r);
1484
+ continue;
1485
+ }
1486
+ // Anchored in the set, or to nothing in it: with the cell.
1487
+ boxes.set(name, target !== undefined ? r : { ...r, x: r.x + x - drawn.x, y: r.y + y - drawn.y });
1488
+ inCell.push(name);
1489
+ }
1490
+ todo = later;
1491
+ }
1492
+ return { boxes, inCell };
1493
+ };
1494
+ const stretchable = (f) => f.height === 'auto' || f.height === 'fill';
1495
+ let cursor = box.y;
1496
+ let laidAny = false;
1497
+ let out = false;
1498
+ for (let r = 0; r * columns < cells.length; r++) {
1499
+ const row = cells.slice(r * columns, (r + 1) * columns);
1500
+ if (out) {
1501
+ for (const c of row)
1502
+ for (const n of c)
1503
+ spilled.add(n);
1504
+ continue;
1505
+ }
1506
+ // Each cell at the row's top, to see how far it reaches above and
1507
+ // below its main frame.
1508
+ const measured = row.map((cell, c) => {
1509
+ const main = byMember.get(cell[0]);
1510
+ const h = mainHeight(main, colW);
1511
+ const { boxes, inCell } = layCell(cell, xOf(c), 0, h);
1512
+ const u = unionRect(inCell.map((n) => boxes.get(n)));
1513
+ return { cell, main, h, above: Math.max(0, -u.y), below: u.y + u.height };
1514
+ });
1515
+ const above = Math.max(0, ...measured.map((m) => m.above));
1516
+ const below = Math.max(0, ...measured.map((m) => m.below));
1517
+ const rowH = above + below;
1518
+ const fits = cursor + rowH <= foot + 0.5;
1519
+ if (!fits && fit.gridsOn.has(group.name)) {
1520
+ // This row and the rest go on, whole.
1521
+ out = true;
1522
+ for (const c of row)
1523
+ for (const n of c)
1524
+ spilled.add(n);
1525
+ continue;
1526
+ }
1527
+ measured.forEach((m, c) => {
1528
+ const spare = below - m.below;
1529
+ let dy = 0;
1530
+ let h = m.h;
1531
+ if (spare > 0.01) {
1532
+ const align = group.rowAlign ?? 'stretch';
1533
+ if (align === 'end')
1534
+ dy = spare;
1535
+ else if (align === 'center')
1536
+ dy = spare / 2;
1537
+ else if (align === 'stretch' && stretchable(m.main))
1538
+ h = Math.min(m.h + spare, px(m.main.maxHeight) ?? Infinity);
1539
+ }
1540
+ const { boxes } = layCell(m.cell, xOf(c), cursor + above + dy, h);
1541
+ if (!fits)
1542
+ cut.push(...m.cell);
1543
+ for (const [n, b] of boxes) {
1544
+ // A row past the grid's foot is cut there: what is cut shows as
1545
+ // the frames' overflow.
1546
+ const y = Math.min(b.y, foot);
1547
+ laidBoxes.set(n, fits ? b : { ...b, y, height: Math.max(0, Math.min(b.y + b.height, foot) - y) });
1548
+ }
1549
+ });
1550
+ laidAny = true;
1551
+ cursor += rowH + rowGap;
1552
+ }
1553
+ for (const [n, b] of laidBoxes) {
1554
+ // A grid's cells are its template's drawing: no reflow events.
1555
+ declared.set(n, b);
1556
+ rects.set(n, b);
1557
+ }
1558
+ const laidRects = [...laidBoxes.values()];
1559
+ const union = ownBox ?? unionRect(laidRects) ?? { ...box, height: 0 };
1560
+ groupRects.set(group.name, union);
1561
+ groupDeclared.set(group.name, union);
1562
+ return { rects: laidBoxes, spilled, laidAny, clipped: cut };
1563
+ };
1261
1564
  /** Pour a frame's content into its box and record the frame. A frame
1262
1565
  * whose text the ladder scales (#858) is laid out in a box `1 / scale`
1263
1566
  * as large, painted at `scale` (`frameMatrix`). */
@@ -1347,9 +1650,11 @@ function composePage(input, template, fit, collapsed) {
1347
1650
  emptyRules = turned ? blocks.map((b) => ({ ...b, bbox: turnRectAbout(b.bbox, rect, frameModeTurn({ writingMode: turned })) })) : blocks;
1348
1651
  }
1349
1652
  }
1653
+ const instance = repeated.has(repeatInstance(frame.name)?.frame ?? '') ? repeatInstance(frame.name) : undefined;
1350
1654
  const vdt = {
1351
1655
  id: frameIdOf(frame.name),
1352
1656
  name: frame.name,
1657
+ ...(instance ? { repeat: instance } : {}),
1353
1658
  rect: { ...rect },
1354
1659
  rotate: frame.rotate + (turned ? frameModeTurn({ writingMode: turned }) : 0),
1355
1660
  ...(turned ? { writingMode: turned } : {}),
@@ -1375,8 +1680,9 @@ function composePage(input, template, fit, collapsed) {
1375
1680
  if (q)
1376
1681
  q.continuesTo = vdt.id;
1377
1682
  }
1378
- else if (holds && cont?.receivers.has(frame.name)) {
1379
- // Content from the page before (#859).
1683
+ else if (holds && cont?.receivers.has(frame.name) && !cont.receivers.get(frame.name).whole) {
1684
+ // Content from the page before (#859). (An item of a grid that
1685
+ // came whole, #1061, starts here.)
1380
1686
  const came = cont.receivers.get(frame.name);
1381
1687
  vdt.continuedFrom = came.fromFrameId;
1382
1688
  // From a page of an earlier chapter (#943).
@@ -1436,25 +1742,199 @@ function composePage(input, template, fit, collapsed) {
1436
1742
  frames.push(vdt);
1437
1743
  vdtByName.set(frame.name, vdt);
1438
1744
  };
1439
- for (const step of composedOrder(template)) {
1745
+ /** A frame's box cut at `clipFoot` (a module cut at its group's foot,
1746
+ * #1062): what is cut shows as the frame's overflow. */
1747
+ const clipAtFoot = (name, r) => {
1748
+ if (clipFoot === undefined || r.y + r.height <= clipFoot + 0.5)
1749
+ return r;
1750
+ clipped.add(name);
1751
+ const y = Math.min(r.y, clipFoot);
1752
+ const out = { ...r, y, height: Math.max(0, Math.min(r.y + r.height, clipFoot) - y) };
1753
+ rects.set(name, out);
1754
+ return out;
1755
+ };
1756
+ /** The frames of `names` whose content goes on whole on another page
1757
+ * (#1061, #1062): none of it is laid here. */
1758
+ const spillWhole = (names, advanced, lastLaid) => {
1759
+ for (const n of names) {
1760
+ const f = byName.get(n);
1761
+ const st = f ? prepare(f).st : undefined;
1762
+ if (!st)
1763
+ continue;
1764
+ laid.add(headOf(n));
1765
+ spills.set(n, { head: headOf(n), cut: st.cut, advanced, pieces: st.pieces, headBlockId: st.headBlockId, frameId: lastLaid ?? frameIdOf(n), whole: true });
1766
+ }
1767
+ };
1768
+ /** Place (and, `filling`, fill) one step of `composedOrder`. */
1769
+ const runStep = (step, filling) => {
1440
1770
  if (step.kind === 'frame') {
1441
1771
  const p = prepare(step.frame);
1442
- fill(p, placeFrame(p));
1443
- continue;
1772
+ const r = clipAtFoot(step.frame.name, placeFrame(p));
1773
+ if (filling)
1774
+ fill(p, r);
1775
+ return;
1776
+ }
1777
+ if (step.group.modules) {
1778
+ // A modules group (#1062): its modules, one under another.
1779
+ placeModules(step.group, step.frames);
1780
+ return;
1781
+ }
1782
+ if (step.group.kind === 'grid') {
1783
+ // A grid (#1061): its cells, and the items of the rows left for
1784
+ // another page, which go on whole.
1785
+ const grid = placeGrid(step.group, step.frames);
1786
+ if (!filling)
1787
+ return;
1788
+ for (const n of grid.clipped)
1789
+ clipped.add(n);
1790
+ let lastLaid;
1791
+ for (const f of step.frames) {
1792
+ if (!grid.spilled.has(f.name)) {
1793
+ fill(prepare(f), clipAtFoot(f.name, grid.rects.get(f.name)));
1794
+ lastLaid = frameIdOf(f.name);
1795
+ continue;
1796
+ }
1797
+ spillWhole([f.name], grid.laidAny, lastLaid);
1798
+ }
1799
+ return;
1444
1800
  }
1445
1801
  const solved = placeGroup(step.group, step.frames);
1446
1802
  // A frame of a chain inside the group is reached once the frame
1447
1803
  // before it was filled: each is prepared again as it is filled.
1448
- for (const f of step.frames)
1449
- fill(prepare(f), solved.get(f.name));
1450
- }
1804
+ for (const f of step.frames) {
1805
+ const r = clipAtFoot(f.name, solved.get(f.name));
1806
+ if (filling)
1807
+ fill(prepare(f), r);
1808
+ }
1809
+ };
1810
+ /** Lay a modules group (#1062): each module, in order, in a box as wide
1811
+ * as the group, from where the module before it ends (`gap` between
1812
+ * them), as tall as its frames. A module's frames are placed as its
1813
+ * template draws them in that box (`'container'` is the box) and its
1814
+ * groups laid in it: first in the room left, to find how tall it is
1815
+ * (the frames held by the box's foot or middle, or sized by its
1816
+ * height, left out), then in a box that tall. The group runs down its
1817
+ * own box, else the area. A module that does not fit is cut at the
1818
+ * group's foot (its frames clipped there), or, when the group goes on
1819
+ * to another page (`FitState.gridsOn`), it and every module after it
1820
+ * go on, whole. The modules' declared boxes follow each other at the
1821
+ * heights their frames declare, so the log tells a module moved by
1822
+ * the one before it. */
1823
+ const placeModules = (group, members) => {
1824
+ const byMember = new Map(members.map((f) => [f.name, f]));
1825
+ const own = groupPlaceable(group);
1826
+ const ownBox = own ? placeBox(own, template, pageInput, refOf, () => 0) : undefined;
1827
+ const box = ownBox ?? pageInput.areas.area;
1828
+ const foot = box.y + box.height;
1829
+ const gap = dimensionToPx(group.gap, dpi, pageInput.emPx);
1830
+ let cursor = box.y;
1831
+ let declCursor = box.y;
1832
+ let out = false;
1833
+ let laidAny = false;
1834
+ let lastLaid;
1835
+ const boxes = [];
1836
+ const declBoxes = [];
1837
+ const restore = (to, from) => {
1838
+ to.clear();
1839
+ for (const [k, v] of from)
1840
+ to.set(k, v);
1841
+ };
1842
+ for (const item of group.items ?? []) {
1843
+ const mod = template.modules?.find((m) => m.name === item.module);
1844
+ const names = item.frames.filter((n) => byMember.has(n));
1845
+ if (!mod || names.length === 0)
1846
+ continue;
1847
+ if (out) {
1848
+ spillWhole(names, laidAny, lastLaid);
1849
+ continue;
1850
+ }
1851
+ const sub = { frames: names.map((n) => byMember.get(n)), groups: template.groups.filter((g) => mod.groups.includes(g.name)) };
1852
+ const steps = composedOrder(sub);
1853
+ const lay = (height, filling) => {
1854
+ input = { ...pageInput, areas: { ...pageInput.areas, area: { x: box.x, y: cursor, width: box.width, height: Math.max(0, height) } } };
1855
+ try {
1856
+ for (const step of steps)
1857
+ runStep(step, filling);
1858
+ }
1859
+ finally {
1860
+ input = pageInput;
1861
+ }
1862
+ };
1863
+ // 1. How tall the module is: its frames placed in the room left,
1864
+ // then everything they set put back.
1865
+ const kept = { rects: new Map(rects), declared: new Map(declared), groupRects: new Map(groupRects), groupDeclared: new Map(groupDeclared), pinned: pinned.length, boxShort: new Map(boxShort), fitted: new Map(fitted) };
1866
+ lay(foot - cursor, false);
1867
+ const placed = names.filter((n) => rects.has(n));
1868
+ const measured = placed.filter((n) => !heldByFoot(byMember.get(n)));
1869
+ const reach = unionRect((measured.length > 0 ? measured : placed).map((n) => rects.get(n)));
1870
+ const height = reach ? Math.max(0, reach.y + reach.height - cursor) : 0;
1871
+ restore(rects, kept.rects);
1872
+ restore(declared, kept.declared);
1873
+ restore(groupRects, kept.groupRects);
1874
+ restore(groupDeclared, kept.groupDeclared);
1875
+ restore(boxShort, kept.boxShort);
1876
+ restore(fitted, kept.fitted);
1877
+ pinned.length = kept.pinned;
1878
+ const fits = cursor + height <= foot + 0.5;
1879
+ if (!fits && fit.gridsOn.has(group.name)) {
1880
+ // This module and the ones after it go on, whole.
1881
+ out = true;
1882
+ spillWhole(names, laidAny, lastLaid);
1883
+ continue;
1884
+ }
1885
+ // 2. The module laid in a box that tall (cut at the foot when it
1886
+ // does not fit).
1887
+ clipFoot = fits ? undefined : foot;
1888
+ try {
1889
+ lay(fits ? height : foot - cursor, true);
1890
+ }
1891
+ finally {
1892
+ clipFoot = undefined;
1893
+ }
1894
+ const u = unionRect(names.flatMap((n) => (rects.has(n) ? [rects.get(n)] : [])));
1895
+ const bottom = u ? Math.max(cursor, u.y + u.height) : cursor;
1896
+ const moduleBox = { x: box.x, y: cursor, width: box.width, height: bottom - cursor };
1897
+ moduleRects.set(mod.name, moduleBox);
1898
+ boxes.push(moduleBox);
1899
+ // Declared: after the modules before it as they declare themselves.
1900
+ const shift = declCursor - cursor;
1901
+ for (const n of names) {
1902
+ const d = declared.get(n);
1903
+ if (d && Math.abs(shift) > 0.001)
1904
+ declared.set(n, { ...d, y: d.y + shift });
1905
+ }
1906
+ const du = unionRect(names.flatMap((n) => (declared.has(n) ? [declared.get(n)] : [])));
1907
+ const declBottom = du ? Math.max(declCursor, du.y + du.height) : declCursor;
1908
+ declBoxes.push({ x: box.x, y: declCursor, width: box.width, height: declBottom - declCursor });
1909
+ laidAny = true;
1910
+ lastLaid = frameIdOf(names[names.length - 1]);
1911
+ cursor = bottom + gap;
1912
+ declCursor = declBottom + gap;
1913
+ }
1914
+ groupRects.set(group.name, ownBox ?? unionRect(boxes) ?? { ...box, height: 0 });
1915
+ groupDeclared.set(group.name, ownBox ?? unionRect(declBoxes) ?? { ...box, height: 0 });
1916
+ };
1917
+ for (const step of composedOrder(template))
1918
+ runStep(step, true);
1451
1919
  const log = mergeLog(reflowEvents(motionsOf(template, rects, declared), dpi / MM_PER_IN), [...fitLog, ...overflowLog], template);
1452
- return { frames, blocks, log, laid, needs, pushRects, pushShapes, fitted, notes, spills, wantsOn };
1920
+ return { frames, blocks, log, laid, needs, pushRects, pushShapes, fitted, notes, spills, wantsOn, clipped, groupRects, moduleRects };
1453
1921
  };
1454
1922
  const first = run();
1455
- const { frames, blocks, log, laid, needs, fitted, notes, spills, wantsOn } = first.pushRects.size > 0 ? run(first.pushRects, first.pushShapes) : first;
1456
- return { frames, blocks, log, laid, needs, template, fitted, spills, wantsOn, ...(notes.length > 0 ? { notes } : {}) };
1923
+ const { frames, blocks, log, laid, needs, fitted, notes, spills, wantsOn, clipped, groupRects, moduleRects } = first.pushRects.size > 0 ? run(first.pushRects, first.pushShapes) : first;
1924
+ return { frames, blocks, log, laid, needs, template, fitted, spills, wantsOn, clipped, groupRects, moduleRects, ...(notes.length > 0 ? { notes } : {}) };
1457
1925
  }
1926
+ /** A module's frame placed by its box's foot or middle, or sized by its
1927
+ * height (#1062): it does not say how tall the module is. */
1928
+ function heldByFoot(f) {
1929
+ if (f.height === 'fill' || (typeof f.height === 'object' && 'percent' in f.height))
1930
+ return true;
1931
+ const anchor = f.placement?.anchor;
1932
+ if (!anchor || anchor.to !== 'container')
1933
+ return false;
1934
+ return FOOT_EDGES.has(anchor.edge);
1935
+ }
1936
+ /** The nine-point edges below a box's top. */
1937
+ const FOOT_EDGES = new Set(['left', 'center', 'right', 'start', 'end', 'bottom-left', 'bottom', 'bottom-right', 'bottom-start', 'bottom-end']);
1458
1938
  /** `r` turned `deg` (±90) about the centre of `about`: an axis-aligned
1459
1939
  * box again, its width and height swapped. */
1460
1940
  function turnRectAbout(r, about, deg) {
@@ -1600,8 +2080,16 @@ export function styleWithLook(base, look) {
1600
2080
  out.background = look.background;
1601
2081
  out.backgroundEnabled = true;
1602
2082
  }
1603
- if (look.border?.color)
1604
- out.border = { ...base.border, color: look.border.color };
2083
+ if (look.border?.color) {
2084
+ // Every side takes the look's border colour too (#1059).
2085
+ const color = look.border.color;
2086
+ const sides = base.border.sides;
2087
+ out.border = {
2088
+ ...base.border,
2089
+ color,
2090
+ ...(sides ? { sides: { top: { ...sides.top, color }, right: { ...sides.right, color }, bottom: { ...sides.bottom, color }, left: { ...sides.left, color } } } : {}),
2091
+ };
2092
+ }
1605
2093
  if (look.stripe?.color)
1606
2094
  out.stripe = { ...base.stripe, color: look.stripe.color };
1607
2095
  if (look.icon?.color)
@@ -1667,7 +2155,9 @@ export function planComposedPages(blocks, plans, source) {
1667
2155
  while (end < blocks.length && !(blocks[end].type === 'containerEnd' && blocks[end].containerId === b.containerId))
1668
2156
  end++;
1669
2157
  end = Math.min(end, blocks.length - 1);
1670
- if (resolvePageTemplate(source, b.composedPage.template)) {
2158
+ // (A module, #1062, is never laid as a page.)
2159
+ const t = resolvePageTemplate(source, b.composedPage.template);
2160
+ if (t && !isModuleTemplate(t)) {
1671
2161
  const stories = composedStories(i, end, plans);
1672
2162
  const list = [...stories.values()];
1673
2163
  out.set(i, {