postext 1.24.0 → 1.25.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 (331) hide show
  1. package/dist/__tests__/bundle/cjkRulesPin.test.d.ts +2 -0
  2. package/dist/__tests__/bundle/cjkRulesPin.test.d.ts.map +1 -0
  3. package/dist/__tests__/bundle/cjkRulesPin.test.js +78 -0
  4. package/dist/__tests__/bundle/cjkRulesPin.test.js.map +1 -0
  5. package/dist/__tests__/bundle/designOverflowPin.test.js +4 -2
  6. package/dist/__tests__/bundle/designOverflowPin.test.js.map +1 -1
  7. package/dist/__tests__/bundle/flowColumnsPin.test.d.ts +2 -0
  8. package/dist/__tests__/bundle/flowColumnsPin.test.d.ts.map +1 -0
  9. package/dist/__tests__/bundle/flowColumnsPin.test.js +40 -0
  10. package/dist/__tests__/bundle/flowColumnsPin.test.js.map +1 -0
  11. package/dist/__tests__/bundle/gridBalancingPin.test.d.ts +2 -0
  12. package/dist/__tests__/bundle/gridBalancingPin.test.d.ts.map +1 -0
  13. package/dist/__tests__/bundle/gridBalancingPin.test.js +53 -0
  14. package/dist/__tests__/bundle/gridBalancingPin.test.js.map +1 -0
  15. package/dist/__tests__/bundle/inlineTableSplitPin.test.d.ts +2 -0
  16. package/dist/__tests__/bundle/inlineTableSplitPin.test.d.ts.map +1 -0
  17. package/dist/__tests__/bundle/inlineTableSplitPin.test.js +43 -0
  18. package/dist/__tests__/bundle/inlineTableSplitPin.test.js.map +1 -0
  19. package/dist/__tests__/bundle/openerHeadFloatsPin.test.d.ts +2 -0
  20. package/dist/__tests__/bundle/openerHeadFloatsPin.test.d.ts.map +1 -0
  21. package/dist/__tests__/bundle/openerHeadFloatsPin.test.js +50 -0
  22. package/dist/__tests__/bundle/openerHeadFloatsPin.test.js.map +1 -0
  23. package/dist/__tests__/bundle.test.js +11 -1
  24. package/dist/__tests__/bundle.test.js.map +1 -1
  25. package/dist/__tests__/cjk/annotationsRender.test.js +5 -2
  26. package/dist/__tests__/cjk/annotationsRender.test.js.map +1 -1
  27. package/dist/__tests__/cjk/circledNumbers.test.d.ts +2 -0
  28. package/dist/__tests__/cjk/circledNumbers.test.d.ts.map +1 -0
  29. package/dist/__tests__/cjk/circledNumbers.test.js +83 -0
  30. package/dist/__tests__/cjk/circledNumbers.test.js.map +1 -0
  31. package/dist/__tests__/cjk/gridBalancing.test.d.ts +2 -0
  32. package/dist/__tests__/cjk/gridBalancing.test.d.ts.map +1 -0
  33. package/dist/__tests__/cjk/gridBalancing.test.js +227 -0
  34. package/dist/__tests__/cjk/gridBalancing.test.js.map +1 -0
  35. package/dist/__tests__/cjk/justifyRender.test.js +2 -1
  36. package/dist/__tests__/cjk/justifyRender.test.js.map +1 -1
  37. package/dist/__tests__/cjk/titleBreaks.test.d.ts +2 -0
  38. package/dist/__tests__/cjk/titleBreaks.test.d.ts.map +1 -0
  39. package/dist/__tests__/cjk/titleBreaks.test.js +76 -0
  40. package/dist/__tests__/cjk/titleBreaks.test.js.map +1 -0
  41. package/dist/__tests__/cjk/wordJoiner.test.js +13 -4
  42. package/dist/__tests__/cjk/wordJoiner.test.js.map +1 -1
  43. package/dist/__tests__/codeBlocks.test.js +1 -1
  44. package/dist/__tests__/design/cjkDesignText.test.d.ts +2 -0
  45. package/dist/__tests__/design/cjkDesignText.test.d.ts.map +1 -0
  46. package/dist/__tests__/design/cjkDesignText.test.js +130 -0
  47. package/dist/__tests__/design/cjkDesignText.test.js.map +1 -0
  48. package/dist/__tests__/design/cjkDesignTextRender.test.d.ts +2 -0
  49. package/dist/__tests__/design/cjkDesignTextRender.test.d.ts.map +1 -0
  50. package/dist/__tests__/design/cjkDesignTextRender.test.js +100 -0
  51. package/dist/__tests__/design/cjkDesignTextRender.test.js.map +1 -0
  52. package/dist/__tests__/exports.test.js +49 -0
  53. package/dist/__tests__/exports.test.js.map +1 -1
  54. package/dist/__tests__/hardBreaks.test.js +1 -1
  55. package/dist/__tests__/headingInlineMarks.test.js +1 -1
  56. package/dist/__tests__/headingNumberSpans.test.d.ts +2 -0
  57. package/dist/__tests__/headingNumberSpans.test.d.ts.map +1 -0
  58. package/dist/__tests__/headingNumberSpans.test.js +96 -0
  59. package/dist/__tests__/headingNumberSpans.test.js.map +1 -0
  60. package/dist/__tests__/mathSizeMigration.test.js +2 -2
  61. package/dist/__tests__/multipleColumns.test.js +207 -0
  62. package/dist/__tests__/multipleColumns.test.js.map +1 -1
  63. package/dist/__tests__/parse/citations.test.js +29 -0
  64. package/dist/__tests__/parse/citations.test.js.map +1 -1
  65. package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +10 -6
  66. package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js.map +1 -1
  67. package/dist/__tests__/pipeline/calloutOverflow.test.js +4 -2
  68. package/dist/__tests__/pipeline/calloutOverflow.test.js.map +1 -1
  69. package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +1 -1
  70. package/dist/__tests__/pipeline/calloutTallKeepTogether.test.js +6 -5
  71. package/dist/__tests__/pipeline/calloutTallKeepTogether.test.js.map +1 -1
  72. package/dist/__tests__/pipeline/colonListRoom.test.js +1 -1
  73. package/dist/__tests__/pipeline/compoundBreaks.test.js +1 -1
  74. package/dist/__tests__/pipeline/dashBreaks.test.js +1 -1
  75. package/dist/__tests__/pipeline/floatCitingPage.test.d.ts +2 -0
  76. package/dist/__tests__/pipeline/floatCitingPage.test.d.ts.map +1 -0
  77. package/dist/__tests__/pipeline/floatCitingPage.test.js +241 -0
  78. package/dist/__tests__/pipeline/floatCitingPage.test.js.map +1 -0
  79. package/dist/__tests__/pipeline/floatOverInlineResource.test.js +18 -6
  80. package/dist/__tests__/pipeline/floatOverInlineResource.test.js.map +1 -1
  81. package/dist/__tests__/pipeline/floatPlacement.test.js +3 -3
  82. package/dist/__tests__/pipeline/floatPlacement.test.js.map +1 -1
  83. package/dist/__tests__/pipeline/flowColumns.test.d.ts +2 -0
  84. package/dist/__tests__/pipeline/flowColumns.test.d.ts.map +1 -0
  85. package/dist/__tests__/pipeline/flowColumns.test.js +261 -0
  86. package/dist/__tests__/pipeline/flowColumns.test.js.map +1 -0
  87. package/dist/__tests__/pipeline/headingKeepFitEps.test.d.ts +2 -0
  88. package/dist/__tests__/pipeline/headingKeepFitEps.test.d.ts.map +1 -0
  89. package/dist/__tests__/pipeline/headingKeepFitEps.test.js +83 -0
  90. package/dist/__tests__/pipeline/headingKeepFitEps.test.js.map +1 -0
  91. package/dist/__tests__/pipeline/headingSplitOrphans.test.js +1 -1
  92. package/dist/__tests__/pipeline/inlineFigureSpace.test.js +1 -1
  93. package/dist/__tests__/pipeline/inlineTableSplit.test.d.ts +2 -0
  94. package/dist/__tests__/pipeline/inlineTableSplit.test.d.ts.map +1 -0
  95. package/dist/__tests__/pipeline/inlineTableSplit.test.js +238 -0
  96. package/dist/__tests__/pipeline/inlineTableSplit.test.js.map +1 -0
  97. package/dist/__tests__/pipeline/paragraphContainerTail.test.js +1 -1
  98. package/dist/__tests__/pipeline/raggedOptimal.test.js +1 -1
  99. package/dist/__tests__/pipeline/resolvedPaletteCopy.test.js +3 -2
  100. package/dist/__tests__/pipeline/resolvedPaletteCopy.test.js.map +1 -1
  101. package/dist/__tests__/pipeline/sideBoxAfterText.test.d.ts +2 -0
  102. package/dist/__tests__/pipeline/sideBoxAfterText.test.d.ts.map +1 -0
  103. package/dist/__tests__/pipeline/sideBoxAfterText.test.js +163 -0
  104. package/dist/__tests__/pipeline/sideBoxAfterText.test.js.map +1 -0
  105. package/dist/__tests__/prepareFonts.test.d.ts +2 -0
  106. package/dist/__tests__/prepareFonts.test.d.ts.map +1 -0
  107. package/dist/__tests__/prepareFonts.test.js +358 -0
  108. package/dist/__tests__/prepareFonts.test.js.map +1 -0
  109. package/dist/__tests__/svgFonts.test.d.ts +2 -0
  110. package/dist/__tests__/svgFonts.test.d.ts.map +1 -0
  111. package/dist/__tests__/svgFonts.test.js +286 -0
  112. package/dist/__tests__/svgFonts.test.js.map +1 -0
  113. package/dist/__tests__/verseLines.test.js +1 -1
  114. package/dist/__tests__/vertical/measurePaint.test.js +16 -0
  115. package/dist/__tests__/vertical/measurePaint.test.js.map +1 -1
  116. package/dist/bundle/adapters.d.ts +31 -7
  117. package/dist/bundle/adapters.d.ts.map +1 -1
  118. package/dist/bundle/adapters.js +113 -34
  119. package/dist/bundle/adapters.js.map +1 -1
  120. package/dist/bundle/codec.d.ts.map +1 -1
  121. package/dist/bundle/codec.js +1 -0
  122. package/dist/bundle/codec.js.map +1 -1
  123. package/dist/bundle/configVersion.d.ts +127 -10
  124. package/dist/bundle/configVersion.d.ts.map +1 -1
  125. package/dist/bundle/configVersion.js +260 -9
  126. package/dist/bundle/configVersion.js.map +1 -1
  127. package/dist/bundle/index.d.ts +3 -3
  128. package/dist/bundle/index.d.ts.map +1 -1
  129. package/dist/bundle/index.js +2 -2
  130. package/dist/bundle/index.js.map +1 -1
  131. package/dist/bundle/manifest.d.ts.map +1 -1
  132. package/dist/bundle/manifest.js +3 -2
  133. package/dist/bundle/manifest.js.map +1 -1
  134. package/dist/bundle/types.d.ts +4 -0
  135. package/dist/bundle/types.d.ts.map +1 -1
  136. package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
  137. package/dist/canvas-backend/headerFooter.js +3 -1
  138. package/dist/canvas-backend/headerFooter.js.map +1 -1
  139. package/dist/columnRule.d.ts.map +1 -1
  140. package/dist/columnRule.js +5 -2
  141. package/dist/columnRule.js.map +1 -1
  142. package/dist/configWarnings.js +1 -1
  143. package/dist/configWarnings.js.map +1 -1
  144. package/dist/defaults/cjk.d.ts +2 -1
  145. package/dist/defaults/cjk.d.ts.map +1 -1
  146. package/dist/defaults/cjk.js +17 -1
  147. package/dist/defaults/cjk.js.map +1 -1
  148. package/dist/defaults/diagramStyle.d.ts.map +1 -1
  149. package/dist/defaults/diagramStyle.js +6 -0
  150. package/dist/defaults/diagramStyle.js.map +1 -1
  151. package/dist/defaults/headings.d.ts.map +1 -1
  152. package/dist/defaults/headings.js +3 -0
  153. package/dist/defaults/headings.js.map +1 -1
  154. package/dist/defaults/index.d.ts +1 -1
  155. package/dist/defaults/index.d.ts.map +1 -1
  156. package/dist/defaults/index.js +1 -1
  157. package/dist/defaults/index.js.map +1 -1
  158. package/dist/defaults/layout.d.ts +5 -0
  159. package/dist/defaults/layout.d.ts.map +1 -1
  160. package/dist/defaults/layout.js +32 -0
  161. package/dist/defaults/layout.js.map +1 -1
  162. package/dist/defaults/tableStyle.d.ts.map +1 -1
  163. package/dist/defaults/tableStyle.js +6 -0
  164. package/dist/defaults/tableStyle.js.map +1 -1
  165. package/dist/design/cjkText.d.ts +34 -0
  166. package/dist/design/cjkText.d.ts.map +1 -0
  167. package/dist/design/cjkText.js +82 -0
  168. package/dist/design/cjkText.js.map +1 -0
  169. package/dist/design/layout.d.ts.map +1 -1
  170. package/dist/design/layout.js +103 -5
  171. package/dist/design/layout.js.map +1 -1
  172. package/dist/design/richText.d.ts +24 -0
  173. package/dist/design/richText.d.ts.map +1 -1
  174. package/dist/design/richText.js +37 -1
  175. package/dist/design/richText.js.map +1 -1
  176. package/dist/fonts/buildWithFonts.d.ts +19 -0
  177. package/dist/fonts/buildWithFonts.d.ts.map +1 -0
  178. package/dist/fonts/buildWithFonts.js +16 -0
  179. package/dist/fonts/buildWithFonts.js.map +1 -0
  180. package/dist/fonts/faces.d.ts +88 -0
  181. package/dist/fonts/faces.d.ts.map +1 -0
  182. package/dist/fonts/faces.js +264 -0
  183. package/dist/fonts/faces.js.map +1 -0
  184. package/dist/fonts/fontSet.d.ts +34 -0
  185. package/dist/fonts/fontSet.d.ts.map +1 -0
  186. package/dist/fonts/fontSet.js +173 -0
  187. package/dist/fonts/fontSet.js.map +1 -0
  188. package/dist/fonts/prepare.d.ts +120 -0
  189. package/dist/fonts/prepare.d.ts.map +1 -0
  190. package/dist/fonts/prepare.js +375 -0
  191. package/dist/fonts/prepare.js.map +1 -0
  192. package/dist/html-backend.d.ts +17 -0
  193. package/dist/html-backend.d.ts.map +1 -1
  194. package/dist/html-backend.js +87 -3
  195. package/dist/html-backend.js.map +1 -1
  196. package/dist/index.d.ts +25 -5
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +18 -2
  199. package/dist/index.js.map +1 -1
  200. package/dist/measure/cache.d.ts.map +1 -1
  201. package/dist/measure/cache.js +32 -2
  202. package/dist/measure/cache.js.map +1 -1
  203. package/dist/measure/canvas.d.ts +15 -0
  204. package/dist/measure/canvas.d.ts.map +1 -1
  205. package/dist/measure/canvas.js +68 -4
  206. package/dist/measure/canvas.js.map +1 -1
  207. package/dist/measure/cjk.d.ts.map +1 -1
  208. package/dist/measure/cjk.js +4 -1
  209. package/dist/measure/cjk.js.map +1 -1
  210. package/dist/measure/cjkClasses.d.ts +17 -0
  211. package/dist/measure/cjkClasses.d.ts.map +1 -1
  212. package/dist/measure/cjkClasses.js +41 -1
  213. package/dist/measure/cjkClasses.js.map +1 -1
  214. package/dist/measure/cjkCompose.d.ts.map +1 -1
  215. package/dist/measure/cjkCompose.js +119 -7
  216. package/dist/measure/cjkCompose.js.map +1 -1
  217. package/dist/measure/cjkPunctuation.d.ts +18 -1
  218. package/dist/measure/cjkPunctuation.d.ts.map +1 -1
  219. package/dist/measure/cjkPunctuation.js +13 -1
  220. package/dist/measure/cjkPunctuation.js.map +1 -1
  221. package/dist/measure/font.d.ts +8 -0
  222. package/dist/measure/font.d.ts.map +1 -1
  223. package/dist/measure/font.js +13 -2
  224. package/dist/measure/font.js.map +1 -1
  225. package/dist/measure/fontString.d.ts +14 -0
  226. package/dist/measure/fontString.d.ts.map +1 -0
  227. package/dist/measure/fontString.js +55 -0
  228. package/dist/measure/fontString.js.map +1 -0
  229. package/dist/measure/index.d.ts +4 -1
  230. package/dist/measure/index.d.ts.map +1 -1
  231. package/dist/measure/index.js +3 -1
  232. package/dist/measure/index.js.map +1 -1
  233. package/dist/measure/types.d.ts +4 -0
  234. package/dist/measure/types.d.ts.map +1 -1
  235. package/dist/measure/types.js.map +1 -1
  236. package/dist/parse/citations.d.ts.map +1 -1
  237. package/dist/parse/citations.js +7 -3
  238. package/dist/parse/citations.js.map +1 -1
  239. package/dist/pipeline/build.d.ts +36 -0
  240. package/dist/pipeline/build.d.ts.map +1 -1
  241. package/dist/pipeline/build.js +1065 -193
  242. package/dist/pipeline/build.js.map +1 -1
  243. package/dist/pipeline/buildBlockKind.d.ts.map +1 -1
  244. package/dist/pipeline/buildBlockKind.js +20 -6
  245. package/dist/pipeline/buildBlockKind.js.map +1 -1
  246. package/dist/pipeline/buildMeasurement.d.ts +6 -0
  247. package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
  248. package/dist/pipeline/buildMeasurement.js +3 -2
  249. package/dist/pipeline/buildMeasurement.js.map +1 -1
  250. package/dist/pipeline/calloutLayout.d.ts +59 -0
  251. package/dist/pipeline/calloutLayout.d.ts.map +1 -1
  252. package/dist/pipeline/calloutLayout.js +287 -65
  253. package/dist/pipeline/calloutLayout.js.map +1 -1
  254. package/dist/pipeline/columnBalancing.d.ts +20 -0
  255. package/dist/pipeline/columnBalancing.d.ts.map +1 -1
  256. package/dist/pipeline/columnBalancing.js +22 -4
  257. package/dist/pipeline/columnBalancing.js.map +1 -1
  258. package/dist/pipeline/columnsGroup.d.ts +61 -0
  259. package/dist/pipeline/columnsGroup.d.ts.map +1 -0
  260. package/dist/pipeline/columnsGroup.js +88 -0
  261. package/dist/pipeline/columnsGroup.js.map +1 -0
  262. package/dist/pipeline/config.d.ts +28 -0
  263. package/dist/pipeline/config.d.ts.map +1 -1
  264. package/dist/pipeline/config.js +67 -8
  265. package/dist/pipeline/config.js.map +1 -1
  266. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  267. package/dist/pipeline/contentWarnings.js +28 -0
  268. package/dist/pipeline/contentWarnings.js.map +1 -1
  269. package/dist/pipeline/floatPlacement.d.ts +16 -2
  270. package/dist/pipeline/floatPlacement.d.ts.map +1 -1
  271. package/dist/pipeline/floatPlacement.js +12 -4
  272. package/dist/pipeline/floatPlacement.js.map +1 -1
  273. package/dist/pipeline/floatSlots.d.ts +9 -3
  274. package/dist/pipeline/floatSlots.d.ts.map +1 -1
  275. package/dist/pipeline/floatSlots.js +15 -6
  276. package/dist/pipeline/floatSlots.js.map +1 -1
  277. package/dist/pipeline/flowColumns.d.ts +41 -0
  278. package/dist/pipeline/flowColumns.d.ts.map +1 -0
  279. package/dist/pipeline/flowColumns.js +116 -0
  280. package/dist/pipeline/flowColumns.js.map +1 -0
  281. package/dist/pipeline/measureContentBlock.d.ts +13 -1
  282. package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
  283. package/dist/pipeline/measureContentBlock.js +20 -5
  284. package/dist/pipeline/measureContentBlock.js.map +1 -1
  285. package/dist/pipeline/partPalette.d.ts +3 -0
  286. package/dist/pipeline/partPalette.d.ts.map +1 -1
  287. package/dist/pipeline/partPalette.js +5 -0
  288. package/dist/pipeline/partPalette.js.map +1 -1
  289. package/dist/pipeline/placement.d.ts +19 -0
  290. package/dist/pipeline/placement.d.ts.map +1 -1
  291. package/dist/pipeline/placement.js +5 -2
  292. package/dist/pipeline/placement.js.map +1 -1
  293. package/dist/pipeline/resourceLayout.d.ts +26 -0
  294. package/dist/pipeline/resourceLayout.d.ts.map +1 -1
  295. package/dist/pipeline/resourceLayout.js +53 -3
  296. package/dist/pipeline/resourceLayout.js.map +1 -1
  297. package/dist/pipeline/toc.d.ts.map +1 -1
  298. package/dist/pipeline/toc.js +6 -1
  299. package/dist/pipeline/toc.js.map +1 -1
  300. package/dist/svg/fontRegistry.d.ts +66 -0
  301. package/dist/svg/fontRegistry.d.ts.map +1 -0
  302. package/dist/svg/fontRegistry.js +294 -0
  303. package/dist/svg/fontRegistry.js.map +1 -0
  304. package/dist/svg/fonts.d.ts +140 -0
  305. package/dist/svg/fonts.d.ts.map +1 -0
  306. package/dist/svg/fonts.js +642 -0
  307. package/dist/svg/fonts.js.map +1 -0
  308. package/dist/svg/image.d.ts +44 -0
  309. package/dist/svg/image.d.ts.map +1 -0
  310. package/dist/svg/image.js +67 -0
  311. package/dist/svg/image.js.map +1 -0
  312. package/dist/types.d.ts +138 -2
  313. package/dist/types.d.ts.map +1 -1
  314. package/dist/util/stableHash.d.ts +20 -0
  315. package/dist/util/stableHash.d.ts.map +1 -0
  316. package/dist/util/stableHash.js +59 -0
  317. package/dist/util/stableHash.js.map +1 -0
  318. package/dist/vdt.d.ts +118 -3
  319. package/dist/vdt.d.ts.map +1 -1
  320. package/dist/vdt.js.map +1 -1
  321. package/dist/worker/client.d.ts +8 -0
  322. package/dist/worker/client.d.ts.map +1 -1
  323. package/dist/worker/client.js +22 -0
  324. package/dist/worker/client.js.map +1 -1
  325. package/dist/worker/fonts.d.ts +1 -13
  326. package/dist/worker/fonts.d.ts.map +1 -1
  327. package/dist/worker/fonts.js +3 -61
  328. package/dist/worker/fonts.js.map +1 -1
  329. package/dist/worker/layout.worker.js +23 -16
  330. package/dist/worker/layout.worker.js.map +1 -1
  331. package/package.json +1 -1
@@ -39,6 +39,7 @@
39
39
  * children.
40
40
  */
41
41
  import { flowTextWidth, lineBaselineOffset } from '../measure/vertical';
42
+ import { onTextWidthCacheClear } from '../measure/canvas';
42
43
  import { suffixJoiner } from '../parse/inlineFormatting';
43
44
  import { spaceDirectiveLines } from '../parse/attrs';
44
45
  import { dimensionToPx } from '../units';
@@ -58,6 +59,9 @@ import { containerStyle, paragraphStyleIdOf } from './paragraphContainers';
58
59
  import { joiningScriptIn } from '../measure/joining';
59
60
  import { getMeasureDirection, shiftLineX } from '../measure/bidiLines';
60
61
  import { resolvedCodeStyle } from '../defaults/codeStyle';
62
+ import { parseLengthText } from '../defaults/tabStops';
63
+ import { isFlowColumnsStyle } from './flowColumns';
64
+ import { fillGalley, galleyBreaks, minFillHeight } from './columnsGroup';
61
65
  /** Map every `containerStart{callout}` index to its range. Resolved up front
62
66
  * (like `:::paragraphs`) because keep-with-next rollbacks rewind the
63
67
  * placement loop and replay blocks. Unclosed containers are auto-closed by
@@ -108,6 +112,10 @@ export function pickCalloutStyle(styles, type) {
108
112
  * body's `italic` / `smallCaps` ride on `bodyText` for that). Child
109
113
  * headings (rare) keep the normal heading styles. */
110
114
  export function deriveCalloutResolvedConfig(resolved, style) {
115
+ // A main-flow group's box (#634) lends its blocks nothing: they are set
116
+ // as the text around them.
117
+ if (isFlowColumnsStyle(style.id))
118
+ return resolved;
111
119
  const { body, lists } = style;
112
120
  const ul = resolved.unorderedLists;
113
121
  const ol = resolved.orderedLists;
@@ -177,6 +185,8 @@ export function deriveCalloutResolvedConfig(resolved, style) {
177
185
  * metrics (number widths) are kept from the document context — they were
178
186
  * measured with the document's list font, a close-enough approximation. */
179
187
  export function deriveCalloutMeasureContext(ctx, style) {
188
+ if (isFlowColumnsStyle(style.id))
189
+ return ctx;
180
190
  const resolved = deriveCalloutResolvedConfig(ctx.resolved, style);
181
191
  const bodyStyle = resolveBodyStyle(resolved);
182
192
  return {
@@ -191,7 +201,30 @@ export function deriveCalloutMeasureContext(ctx, style) {
191
201
  /** The measures of `layoutCallout`'s first child across widths (see
192
202
  * `CalloutLayoutInput.lineWidths`), per resolved configuration and
193
203
  * content block: a build lays a split box out again on every pass. */
194
- const acrossWidthsMemo = new WeakMap();
204
+ let acrossWidthsMemo = new WeakMap();
205
+ // Measures: dropped with the widths when faces change (#629).
206
+ onTextWidthCacheClear(() => {
207
+ acrossWidthsMemo = new WeakMap();
208
+ });
209
+ /** Read a `:::columns` fence: `count` (2 by default, at most 6), `gap` (a
210
+ * length; else `defaultGapPx`), `rule` (a rule down each gutter), `breaks`
211
+ * (the children that open each column after the first) and `flow`
212
+ * (`'parallel'` by default with `breaks`, `'snake'` without; an unknown
213
+ * value takes the default). */
214
+ export function columnsSpecOf(attrs, defaultGapPx, emPx, dpi) {
215
+ const count = Number.parseInt(attrs.count ?? '2', 10);
216
+ const breaks = (attrs.breaks ?? '').split(',').map((v) => Number.parseInt(v.trim(), 10)).filter((v) => Number.isFinite(v) && v > 1);
217
+ const gap = attrs.gap !== undefined ? parseLengthText(attrs.gap) : undefined;
218
+ const gapPx = gap ? dimensionToPx(gap, dpi, emPx) : defaultGapPx;
219
+ const flow = attrs.flow === 'snake' || attrs.flow === 'parallel' ? attrs.flow : breaks.length > 0 ? 'parallel' : 'snake';
220
+ return {
221
+ count: Number.isFinite(count) ? count : 2,
222
+ gapPx: Number.isFinite(gapPx) && gapPx >= 0 ? gapPx : defaultGapPx,
223
+ rule: attrs.rule !== undefined && attrs.rule !== 'false',
224
+ flow,
225
+ ...(breaks.length > 0 ? { breaks } : {}),
226
+ };
227
+ }
195
228
  const VALID_SPANS = new Set(['column', 'page', 'side']);
196
229
  const VALID_PLACEMENTS = new Set(['here', 'auto', 'top', 'bottom', 'fixed']);
197
230
  /** Per-instance span / placement: fence attribute when valid, else the style. */
@@ -465,6 +498,10 @@ export function layoutCallout(input) {
465
498
  // of the tallest column.
466
499
  const childBlocks = [];
467
500
  const units = [];
501
+ /** The `:::columns` groups among the box's own children, as laid out
502
+ * (#634), and the rules down their gutters. */
503
+ const groupLayouts = [];
504
+ const groupRules = [];
468
505
  /** The lines of the box's code listing a fence highlights (#624),
469
506
  * box-relative. */
470
507
  const highlights = [];
@@ -509,6 +546,21 @@ export function layoutCallout(input) {
509
546
  return undefined;
510
547
  if (history.every((h) => Math.abs(h.width - input.width) <= 0.5))
511
548
  return undefined;
549
+ // Every fragment's measure: the head's, then each one after it, which
550
+ // keeps the lines the fragments before it placed and breaks the rest
551
+ // for its own width. The one this fragment is set from is the last.
552
+ const last = history[history.length - 1];
553
+ const widths = Math.abs(last.width - input.width) <= 0.5
554
+ ? history
555
+ : [...history, { fromLine: lineFrom, width: input.width }];
556
+ return measureAcrossSteps(raw, blockIdx, styleOverride, lineFrom, widths.map((h) => ({ fromLine: h.fromLine, columnWidth: childWidthAt(h.width, width) })));
557
+ };
558
+ /** A child set from line `lineFrom` on, its lines measured at the widths
559
+ * of `steps` (the child's own measures, in line order), the lines placed
560
+ * before kept as they were: see `measureFirstChildAcrossWidths`. A
561
+ * paragraph of a `:::columns` group that goes on in sub-columns of
562
+ * another width uses it too (#634). */
563
+ const measureAcrossSteps = (raw, blockIdx, styleOverride, lineFrom, steps) => {
512
564
  // A split tries many cuts from the same start, and these measures are
513
565
  // not cached by the measurement cache (they break at several widths).
514
566
  let perBlock = acrossWidthsMemo.get(input.resolved);
@@ -518,14 +570,6 @@ export function layoutCallout(input) {
518
570
  if (!memo)
519
571
  perBlock.set(raw, (memo = new Map()));
520
572
  const styleAt = input.resolved.calloutStyles.indexOf(style);
521
- // Every fragment's measure: the head's, then each one after it, which
522
- // keeps the lines the fragments before it placed and breaks the rest
523
- // for its own width. The one this fragment is set from is the last.
524
- const last = history[history.length - 1];
525
- const widths = Math.abs(last.width - input.width) <= 0.5
526
- ? history
527
- : [...history, { fromLine: lineFrom, width: input.width }];
528
- const steps = widths.map((h) => ({ fromLine: h.fromLine, columnWidth: childWidthAt(h.width, width) }));
529
573
  const base = steps[0].columnWidth;
530
574
  let prev;
531
575
  for (let i = 0; i < steps.length; i++) {
@@ -598,8 +642,11 @@ export function layoutCallout(input) {
598
642
  return blk;
599
643
  };
600
644
  /** Lay out one child at `width` from `x`, appending it to `into` and
601
- * advancing the stack. */
602
- const placeChild = (raw, k, width, x, st, into, unitsInto) => {
645
+ * advancing the stack. `lineCut`: the lines of the child to keep, in
646
+ * place of the box's own line cuts — a paragraph of a `:::columns` group
647
+ * a fragment opens (`from`) or ends (`to`) inside (#634); `widths` are
648
+ * the sub-column widths its lines before `from` were set at. */
649
+ const placeChild = (raw, k, width, x, st, into, unitsInto, lineCut) => {
603
650
  if (isStripChild(raw))
604
651
  return placeStripChild(raw, k, width, x, st, into, unitsInto);
605
652
  const blockIdx = childStartIdx + k;
@@ -609,7 +656,15 @@ export function layoutCallout(input) {
609
656
  const next = children[k + 1];
610
657
  const isContainerTail = container !== undefined && next?.type === 'containerEnd' && next.containerId === container.id;
611
658
  const styleOverride = container ? containerStyle(container, isContainerTail, bodyStyle, derivedCtx.resolved.page.dpi) : undefined;
612
- const measuredBlock = measureFirstChildAcrossWidths(raw, blockIdx, k, width, styleOverride)
659
+ const groupWidths = lineCut?.widths;
660
+ const acrossGroup = lineCut && lineCut.from > 0 && groupWidths && groupWidths.some((h) => Math.abs(h.width - width) > 0.5)
661
+ ? measureAcrossSteps(raw, blockIdx, styleOverride, lineCut.from, [
662
+ ...groupWidths.map((h) => ({ fromLine: h.fromLine, columnWidth: h.width })),
663
+ ...(Math.abs(groupWidths[groupWidths.length - 1].width - width) > 0.5 ? [{ fromLine: lineCut.from, columnWidth: width }] : []),
664
+ ])
665
+ : undefined;
666
+ const measuredBlock = acrossGroup
667
+ ?? (lineCut ? undefined : measureFirstChildAcrossWidths(raw, blockIdx, k, width, styleOverride))
613
668
  ?? measureContentBlock(raw, blockIdx, width, derivedCtx, { styleOverride });
614
669
  if (!measuredBlock)
615
670
  return undefined;
@@ -681,9 +736,10 @@ export function layoutCallout(input) {
681
736
  const isTextRun = !(vdtType === 'resource' && resourceBlock) && !(vdtType === 'mathDisplay' && mathDisplayRender);
682
737
  const isFirstReal = isFirstRealOf(k);
683
738
  const isLastReal = isLastRealOf(k);
684
- const lineFrom = isTextRun && isFirstReal ? Math.max(0, input.lineFrom ?? 0) : 0;
685
- const lineTo = isTextRun && isLastReal && input.lineTo !== undefined
686
- ? Math.max(lineFrom + 1, Math.min(input.lineTo, measured.lines.length))
739
+ const lineFrom = !isTextRun ? 0 : lineCut ? Math.max(0, lineCut.from) : isFirstReal ? Math.max(0, input.lineFrom ?? 0) : 0;
740
+ const cutTo = lineCut ? lineCut.to : isLastReal ? input.lineTo : undefined;
741
+ const lineTo = isTextRun && cutTo !== undefined
742
+ ? Math.max(lineFrom + 1, Math.min(cutTo, measured.lines.length))
687
743
  : measured.lines.length;
688
744
  // A justified line the breaker could not fill is set ragged, as in the
689
745
  // running text (EF-104): a box's narrow measure is where it happens most.
@@ -919,25 +975,21 @@ export function layoutCallout(input) {
919
975
  tail.sourceStart = tail.lines[0]?.sourceStart ?? blk.sourceStart;
920
976
  return tail;
921
977
  };
922
- /** A `:::columns{count=N}` group: children `k0 … k1 - 1` in N columns.
923
- * A nested box among them is one item: never cut, moved whole. */
924
- const placeColumnsGroup = (k0, k1, count, st, breaks) => {
925
- const cols = Math.max(1, Math.min(6, count));
926
- const colW = Math.max(1, (innerWidth - columnGapPx * (cols - 1)) / cols);
927
- // Spacing above the group: as a block with no margin of its own.
928
- const spacing = st.first ? st.prevMarginBottom : st.prevMarginBottom;
929
- const groupTop = st.cursorY + spacing;
978
+ /** Lay children `kFrom … kTo - 1` of a group out in one galley at the
979
+ * sub-column `width` (a nested box is one item: never cut, moved
980
+ * whole). `open` / `close`: the position a fragment opens / ends at,
981
+ * inside a paragraph when its `line` is past 0 (#634). */
982
+ const stackGroup = (kFrom, kTo, kEnd, width, open, close) => {
930
983
  const gst = { cursorY: 0, prevMarginBottom: 0, pull: false, prevWasListItem: false, first: true, keepLeadingSpace: false };
931
- /** The group's items: a child block, or a nested box's blocks (frame first). */
932
984
  const stack = [];
933
- for (let k = k0; k < k1; k++) {
985
+ for (let k = kFrom; k < kTo; k++) {
934
986
  const raw = children[k];
935
987
  const into = [];
936
988
  const unitsInto = [];
937
989
  if (raw.type === 'containerStart' && raw.containerName === 'callout') {
938
990
  const e = closeOf(raw.containerId, k + 1);
939
- if (placeNested(raw, childStartIdx + k, k + 1, Math.min(e, k1), colW, innerX, gst, into, unitsInto)) {
940
- stack.push({ blocks: into, unit: unitsInto[0] });
991
+ if (placeNested(raw, childStartIdx + k, k + 1, Math.min(e, kEnd), width, innerX, gst, into, unitsInto)) {
992
+ stack.push({ blocks: into, unit: unitsInto[0], k, lineBase: 0 });
941
993
  k = e;
942
994
  continue;
943
995
  }
@@ -946,12 +998,36 @@ export function layoutCallout(input) {
946
998
  containerMargin(raw, k, gst);
947
999
  if ((raw.type === 'directive' && !isStripChild(raw)) || isMarkerBlock(raw))
948
1000
  continue;
949
- if (placeChild(raw, k, colW, innerX, gst, into, unitsInto))
950
- stack.push({ blocks: into, unit: unitsInto[0] });
1001
+ const from = open && open.child === k && open.line > 0 ? open.line : 0;
1002
+ const to = close && close.child === k && close.line > 0 ? close.line : undefined;
1003
+ const lineCut = from > 0 || to !== undefined
1004
+ ? { from, ...(to !== undefined ? { to } : {}), ...(from > 0 && open?.widths ? { widths: open.widths } : {}) }
1005
+ : undefined;
1006
+ if (placeChild(raw, k, width, innerX, gst, into, unitsInto, lineCut))
1007
+ stack.push({ blocks: into, unit: unitsInto[0], k, lineBase: from });
951
1008
  }
1009
+ // Where the stack ends, a `:::space` after its last item included.
1010
+ return Object.assign(stack, { end: gst.cursorY });
1011
+ };
1012
+ /** The galley of stacked items, for the split code (see `columnsGroup.ts`). */
1013
+ const galleyOf = (stack) => stack.map((it) => {
1014
+ const blk = it.blocks[0];
1015
+ const cuttable = it.unit.kind === 'block' && blk.type !== 'resource' && blk.type !== 'mathDisplay' && blk.lines.length > 1 && !blk.mathRender;
1016
+ return {
1017
+ child: it.k,
1018
+ lineBase: it.lineBase,
1019
+ y: blk.bbox.y,
1020
+ height: blk.bbox.height,
1021
+ ...(cuttable ? { lineBottoms: blk.lines.map((l) => l.bbox.y + l.bbox.height - blk.bbox.y) } : {}),
1022
+ };
1023
+ });
1024
+ /** Balanced cuts: each column start at the item or line boundary nearest
1025
+ * an even share of the stack's height. */
1026
+ const balancedCuts = (stack, cols) => {
952
1027
  if (stack.length === 0)
953
- return;
954
- const total = gst.cursorY;
1028
+ return [];
1029
+ const total = stack.end ?? 0;
1030
+ // Cut candidates: item boundaries and the line boundaries of text blocks.
955
1031
  const candidates = [];
956
1032
  stack.forEach((item, i) => {
957
1033
  const blk = item.blocks[0];
@@ -965,41 +1041,46 @@ export function layoutCallout(input) {
965
1041
  });
966
1042
  const cuts = [];
967
1043
  let prevY = 0;
968
- if (breaks && breaks.length > 0) {
969
- // Explicit column starts (`breaks="3"`: the third child opens the
970
- // second column): block-level cuts, no balancing.
971
- for (const b of breaks.slice(0, cols - 1)) {
972
- const i = b - 1;
973
- if (i <= 0 || i >= stack.length)
974
- continue;
975
- const y = stack[i].blocks[0].bbox.y;
976
- if (y <= prevY + 0.01)
1044
+ for (let c = 1; c < cols; c++) {
1045
+ const target = (total * c) / cols;
1046
+ let best;
1047
+ for (const cand of candidates) {
1048
+ if (cand.y <= prevY + 0.01)
977
1049
  continue;
978
- cuts.push({ y, block: i, line: 0 });
979
- prevY = y;
1050
+ if (!best || Math.abs(cand.y - target) < Math.abs(best.y - target))
1051
+ best = cand;
980
1052
  }
1053
+ if (!best)
1054
+ break;
1055
+ cuts.push(best);
1056
+ prevY = best.y;
981
1057
  }
982
- else {
983
- for (let c = 1; c < cols; c++) {
984
- const target = (total * c) / cols;
985
- let best;
986
- for (const cand of candidates) {
987
- if (cand.y <= prevY + 0.01)
988
- continue;
989
- if (!best || Math.abs(cand.y - target) < Math.abs(best.y - target))
990
- best = cand;
991
- }
992
- if (!best)
993
- break;
994
- cuts.push(best);
995
- prevY = best.y;
996
- }
1058
+ return cuts;
1059
+ };
1060
+ /** The stack indices `breaks` starts columns at (`breaks="3"`: the third
1061
+ * child opens the second column), at most `cols - 1` of them, rising. */
1062
+ const breakStarts = (stack, cols, breaks) => {
1063
+ const out = [];
1064
+ let prevY = 0;
1065
+ for (const b of breaks.slice(0, cols - 1)) {
1066
+ const i = b - 1;
1067
+ if (i <= 0 || i >= stack.length)
1068
+ continue;
1069
+ const y = stack[i].blocks[0].bbox.y;
1070
+ if (y <= prevY + 0.01)
1071
+ continue;
1072
+ out.push(i);
1073
+ prevY = y;
997
1074
  }
998
- // Split the stack at the cuts into columns of items; a cut inside a
999
- // text block leaves its head behind and opens the next column with the
1000
- // tail (a block of its own, bullet-less).
1075
+ return out;
1076
+ };
1077
+ /** Set a stack in columns at its cuts, the first at `groupTop`; a cut
1078
+ * inside a text block leaves its head behind and opens the next column
1079
+ * with the tail (a block of its own, bullet-less). Returns the height
1080
+ * the columns take. */
1081
+ const distributeGroup = (stack, cuts, colW, gap, groupTop) => {
1001
1082
  const columns = [[]];
1002
- const columnStarts = [0];
1083
+ const columnStarts = [stack[0]?.blocks[0]?.bbox.y ?? 0];
1003
1084
  let cutIdx = 0;
1004
1085
  for (let i = 0; i < stack.length; i++) {
1005
1086
  let item = stack[i];
@@ -1031,7 +1112,7 @@ export function layoutCallout(input) {
1031
1112
  }
1032
1113
  let groupHeight = 0;
1033
1114
  columns.forEach((items, c) => {
1034
- const shiftX = c * (colW + columnGapPx);
1115
+ const shiftX = c * (colW + gap);
1035
1116
  const shiftY = groupTop - (columnStarts[c] ?? 0);
1036
1117
  for (const item of items) {
1037
1118
  for (const blk of item.blocks) {
@@ -1042,6 +1123,134 @@ export function layoutCallout(input) {
1042
1123
  units.push(item.unit);
1043
1124
  }
1044
1125
  });
1126
+ return groupHeight;
1127
+ };
1128
+ /** A `:::columns{count=N}` group: children `k0 … k1 - 1` in N columns
1129
+ * (`spec`). Set whole, its stack is cut where the columns balance best,
1130
+ * or at its `breaks`. A fragment that opens or ends inside it (#634,
1131
+ * `cut`) sets the part of the galley between its positions: a
1132
+ * `'snake'` group that goes on fills its columns as level as the cuts
1133
+ * allow, the closing part balances as a whole group does; a
1134
+ * `'parallel'` group sets each stream from its own position in its own
1135
+ * column, top-aligned. The galley this layout starts with is recorded
1136
+ * in `groupLayouts` for the split code. */
1137
+ const placeColumnsGroup = (k0, k1, spec, st, cut = {}) => {
1138
+ const cols = Math.max(1, Math.min(6, spec.count));
1139
+ const gap = spec.gapPx;
1140
+ const colW = Math.max(1, (innerWidth - gap * (cols - 1)) / cols);
1141
+ // Spacing above the group: as a block with no margin of its own.
1142
+ const groupTop = st.cursorY + st.prevMarginBottom;
1143
+ const childMin = Math.min(Math.max(1, style.splitMinLines), input.resolved.layout.boxChildSplitMinLines);
1144
+ const parallel = spec.flow === 'parallel' && (spec.breaks?.length ?? 0) > 0;
1145
+ let groupHeight = 0;
1146
+ let streams = [];
1147
+ /** Where each stream ends: the next one's first child, the last at the
1148
+ * closing marker. */
1149
+ let ends = [k1];
1150
+ if (!cut.from && !cut.to) {
1151
+ // The whole group (as before #634).
1152
+ const stack = stackGroup(k0, k1, k1, colW);
1153
+ if (stack.length === 0)
1154
+ return;
1155
+ const starts = spec.breaks && spec.breaks.length > 0 ? breakStarts(stack, cols, spec.breaks) : undefined;
1156
+ const galley = galleyOf(stack);
1157
+ if (parallel && starts) {
1158
+ const bounds = [0, ...starts, stack.length];
1159
+ ends = [...starts.map((i) => stack[i].k), k1];
1160
+ streams = bounds.slice(0, -1).map((b, s) => {
1161
+ const part = galley.slice(b, bounds[s + 1]);
1162
+ const y0 = part[0]?.y ?? 0;
1163
+ return part.map((g) => ({ ...g, y: g.y - y0 }));
1164
+ });
1165
+ }
1166
+ else {
1167
+ streams = [galley];
1168
+ }
1169
+ const cuts = starts
1170
+ ? starts.map((i) => ({ y: stack[i].blocks[0].bbox.y, block: i, line: 0 }))
1171
+ : balancedCuts(stack, cols);
1172
+ groupHeight = distributeGroup(stack, cuts, colW, gap, groupTop);
1173
+ }
1174
+ else if (!parallel) {
1175
+ // A snake group cut across fragments: the part of its galley between
1176
+ // the fragment's positions.
1177
+ const a = cut.from?.[0];
1178
+ const b = cut.to?.[0];
1179
+ const kTo = b ? Math.min(k1, b.line > 0 ? b.child + 1 : b.child) : k1;
1180
+ const stack = stackGroup(a ? Math.max(k0, a.child) : k0, kTo, k1, colW, a, b);
1181
+ if (stack.length === 0)
1182
+ return;
1183
+ const galley = galleyOf(stack);
1184
+ streams = [galley];
1185
+ let cuts;
1186
+ if (b) {
1187
+ const breaks = galleyBreaks(galley, childMin);
1188
+ const fill = fillGalley(galley, breaks, cols, minFillHeight(galley, breaks, cols));
1189
+ cuts = fill.cuts.map((j) => ({ y: breaks[j].startY, block: breaks[j].item, line: breaks[j].line }));
1190
+ }
1191
+ else {
1192
+ cuts = balancedCuts(stack, cols);
1193
+ }
1194
+ groupHeight = distributeGroup(stack, cuts, colW, gap, groupTop);
1195
+ }
1196
+ else {
1197
+ // A parallel group cut across fragments: its streams are the runs
1198
+ // its breaks start in the whole group; each goes on from its own
1199
+ // position, in its own column.
1200
+ const whole = stackGroup(k0, k1, k1, colW);
1201
+ if (whole.length === 0)
1202
+ return;
1203
+ const starts = [0, ...breakStarts(whole, cols, spec.breaks)];
1204
+ const startChild = starts.map((i) => whole[i].k);
1205
+ ends = [...startChild.slice(1), k1];
1206
+ startChild.forEach((sk, s) => {
1207
+ const ek = startChild[s + 1] ?? k1;
1208
+ const a = cut.from?.[s];
1209
+ const b = cut.to?.[s];
1210
+ const kFrom = Math.max(sk, a?.child ?? sk);
1211
+ const kTo = Math.min(ek, b ? (b.line > 0 ? b.child + 1 : b.child) : ek);
1212
+ const stack = kFrom < kTo ? stackGroup(kFrom, kTo, k1, colW, a, b) : [];
1213
+ streams.push(galleyOf(stack));
1214
+ if (stack.length === 0)
1215
+ return;
1216
+ const y0 = stack[0].blocks[0].bbox.y;
1217
+ for (const item of stack) {
1218
+ for (const blk of item.blocks) {
1219
+ offsetBlock(blk, s * (colW + gap), groupTop - y0);
1220
+ childBlocks.push(blk);
1221
+ groupHeight = Math.max(groupHeight, blk.bbox.y + blk.bbox.height - groupTop);
1222
+ }
1223
+ units.push(item.unit);
1224
+ }
1225
+ });
1226
+ }
1227
+ groupLayouts.push({
1228
+ open: k0 - 1,
1229
+ close: k1,
1230
+ flow: parallel ? 'parallel' : 'snake',
1231
+ columns: cols,
1232
+ columnWidth: colW,
1233
+ top: groupTop,
1234
+ streams,
1235
+ ends,
1236
+ contentIndex: childStartIdx + k0 - 1,
1237
+ ...(colW < 6 * em ? { narrow: true } : {}),
1238
+ });
1239
+ // A rule down each gutter (`rule`), the page's column rule.
1240
+ if (spec.rule && cols > 1 && groupHeight > 0) {
1241
+ const ruleSpec = input.resolved.layout.columnRule;
1242
+ const w = Math.max(0.25, px(ruleSpec.lineWidth));
1243
+ for (let c = 1; c < cols; c++) {
1244
+ const x = innerX + c * (colW + gap) - gap / 2;
1245
+ groupRules.push({
1246
+ kind: 'rule',
1247
+ bbox: createBoundingBox(x - w / 2, groupTop, w, groupHeight),
1248
+ color: ruleSpec.color.hex,
1249
+ thicknessPx: w,
1250
+ direction: 'vertical',
1251
+ });
1252
+ }
1253
+ }
1045
1254
  st.cursorY = groupTop + groupHeight;
1046
1255
  st.prevMarginBottom = 0;
1047
1256
  st.pull = false;
@@ -1080,9 +1289,12 @@ export function layoutCallout(input) {
1080
1289
  let e = k + 1;
1081
1290
  while (e < children.length && !(children[e].type === 'containerEnd' && children[e].containerName === 'columns' && children[e].containerId === raw.containerId))
1082
1291
  e++;
1083
- const count = Number.parseInt(raw.containerAttrs?.count ?? '2', 10);
1084
- const breaks = (raw.containerAttrs?.breaks ?? '').split(',').map((v) => Number.parseInt(v.trim(), 10)).filter((v) => Number.isFinite(v) && v > 1);
1085
- placeColumnsGroup(k + 1, e, Number.isFinite(count) ? count : 2, st, breaks.length > 0 ? breaks : undefined);
1292
+ // A fragment that opens inside the group (it is then the first
1293
+ // child) or ends inside it (the last) sets its part (#634).
1294
+ placeColumnsGroup(k + 1, e, columnsSpecOf(raw.containerAttrs ?? {}, columnGapPx, em, dpi), st, {
1295
+ ...(k === 0 && input.groupFrom ? { from: input.groupFrom } : {}),
1296
+ ...(e === children.length - 1 && input.groupTo ? { to: input.groupTo } : {}),
1297
+ });
1086
1298
  k = e;
1087
1299
  continue;
1088
1300
  }
@@ -1142,6 +1354,10 @@ export function layoutCallout(input) {
1142
1354
  offsetDesignBlock(continuesBlock, 0, extra / 2);
1143
1355
  for (const blk of childBlocks)
1144
1356
  offsetBlock(blk, 0, extra / 2);
1357
+ for (const r of groupRules)
1358
+ offsetDesignBlock(r, 0, extra / 2);
1359
+ for (const g of groupLayouts)
1360
+ g.top += extra / 2;
1145
1361
  }
1146
1362
  contentBottom += extra;
1147
1363
  }
@@ -1216,6 +1432,7 @@ export function layoutCallout(input) {
1216
1432
  iconFormat = built.format;
1217
1433
  }
1218
1434
  }
1435
+ overlayBlocks.push(...groupRules);
1219
1436
  if (titleBlock)
1220
1437
  overlayBlocks.push(titleBlock);
1221
1438
  if (continuesBlock)
@@ -1302,6 +1519,8 @@ export function layoutCallout(input) {
1302
1519
  offsetDesignBlock(b, markerColumn, boxDy);
1303
1520
  for (const blk of childBlocks)
1304
1521
  offsetBlock(blk, markerColumn, boxDy);
1522
+ for (const g of groupLayouts)
1523
+ g.top += boxDy;
1305
1524
  innerRect.x += markerColumn;
1306
1525
  innerRect.y += boxDy;
1307
1526
  }
@@ -1339,6 +1558,8 @@ export function layoutCallout(input) {
1339
1558
  offsetDesignBlock(b, 0, labelRisePx);
1340
1559
  for (const blk of childBlocks)
1341
1560
  offsetBlock(blk, 0, labelRisePx);
1561
+ for (const g of groupLayouts)
1562
+ g.top += labelRisePx;
1342
1563
  innerRect.y += labelRisePx;
1343
1564
  }
1344
1565
  const frameHeight = totalHeight + labelRisePx;
@@ -1376,6 +1597,7 @@ export function layoutCallout(input) {
1376
1597
  marginTopPx: px(style.marginTop),
1377
1598
  marginBottomPx: px(style.marginBottom),
1378
1599
  continuesMarkerPx,
1600
+ ...(groupLayouts.length > 0 ? { groups: groupLayouts } : {}),
1379
1601
  };
1380
1602
  }
1381
1603
  /** Whether an icon / marker spec draws anything. */