postext 1.36.0 → 1.38.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 (300) hide show
  1. package/dist/__tests__/comics/frames.test.d.ts +2 -0
  2. package/dist/__tests__/comics/frames.test.d.ts.map +1 -0
  3. package/dist/__tests__/comics/frames.test.js +251 -0
  4. package/dist/__tests__/comics/frames.test.js.map +1 -0
  5. package/dist/__tests__/composed/continuation.test.d.ts +2 -0
  6. package/dist/__tests__/composed/continuation.test.d.ts.map +1 -0
  7. package/dist/__tests__/composed/continuation.test.js +352 -0
  8. package/dist/__tests__/composed/continuation.test.js.map +1 -0
  9. package/dist/__tests__/composed/continuationRules.test.d.ts +2 -0
  10. package/dist/__tests__/composed/continuationRules.test.d.ts.map +1 -0
  11. package/dist/__tests__/composed/continuationRules.test.js +389 -0
  12. package/dist/__tests__/composed/continuationRules.test.js.map +1 -0
  13. package/dist/__tests__/composed/contrast.test.d.ts +2 -0
  14. package/dist/__tests__/composed/contrast.test.d.ts.map +1 -0
  15. package/dist/__tests__/composed/contrast.test.js +124 -0
  16. package/dist/__tests__/composed/contrast.test.js.map +1 -0
  17. package/dist/__tests__/composed/corpus.test.js +7 -1
  18. package/dist/__tests__/composed/corpus.test.js.map +1 -1
  19. package/dist/__tests__/composed/coverImages.test.d.ts +2 -0
  20. package/dist/__tests__/composed/coverImages.test.d.ts.map +1 -0
  21. package/dist/__tests__/composed/coverImages.test.js +75 -0
  22. package/dist/__tests__/composed/coverImages.test.js.map +1 -0
  23. package/dist/__tests__/composed/decorationSize.test.d.ts +2 -0
  24. package/dist/__tests__/composed/decorationSize.test.d.ts.map +1 -0
  25. package/dist/__tests__/composed/decorationSize.test.js +79 -0
  26. package/dist/__tests__/composed/decorationSize.test.js.map +1 -0
  27. package/dist/__tests__/composed/emptyFrames.test.d.ts +2 -0
  28. package/dist/__tests__/composed/emptyFrames.test.d.ts.map +1 -0
  29. package/dist/__tests__/composed/emptyFrames.test.js +129 -0
  30. package/dist/__tests__/composed/emptyFrames.test.js.map +1 -0
  31. package/dist/__tests__/composed/fit.test.js +19 -10
  32. package/dist/__tests__/composed/fit.test.js.map +1 -1
  33. package/dist/__tests__/composed/groups.test.js +34 -0
  34. package/dist/__tests__/composed/groups.test.js.map +1 -1
  35. package/dist/__tests__/composed/headingDesigns.test.d.ts +2 -0
  36. package/dist/__tests__/composed/headingDesigns.test.d.ts.map +1 -0
  37. package/dist/__tests__/composed/headingDesigns.test.js +262 -0
  38. package/dist/__tests__/composed/headingDesigns.test.js.map +1 -0
  39. package/dist/__tests__/composed/labels.test.d.ts +2 -0
  40. package/dist/__tests__/composed/labels.test.d.ts.map +1 -0
  41. package/dist/__tests__/composed/labels.test.js +326 -0
  42. package/dist/__tests__/composed/labels.test.js.map +1 -0
  43. package/dist/__tests__/composed/outsidePrint.test.js +5 -2
  44. package/dist/__tests__/composed/outsidePrint.test.js.map +1 -1
  45. package/dist/__tests__/composed/ruledFrames.test.d.ts +2 -0
  46. package/dist/__tests__/composed/ruledFrames.test.d.ts.map +1 -0
  47. package/dist/__tests__/composed/ruledFrames.test.js +93 -0
  48. package/dist/__tests__/composed/ruledFrames.test.js.map +1 -0
  49. package/dist/__tests__/composed/sourceMap.test.js +26 -0
  50. package/dist/__tests__/composed/sourceMap.test.js.map +1 -1
  51. package/dist/__tests__/composed/spread.test.js +82 -0
  52. package/dist/__tests__/composed/spread.test.js.map +1 -1
  53. package/dist/__tests__/composed/spreadLabels.test.d.ts +2 -0
  54. package/dist/__tests__/composed/spreadLabels.test.d.ts.map +1 -0
  55. package/dist/__tests__/composed/spreadLabels.test.js +131 -0
  56. package/dist/__tests__/composed/spreadLabels.test.js.map +1 -0
  57. package/dist/__tests__/exports.test.js +23 -0
  58. package/dist/__tests__/exports.test.js.map +1 -1
  59. package/dist/__tests__/migrate/fixtures/textbookPort.d.ts +14 -0
  60. package/dist/__tests__/migrate/fixtures/textbookPort.d.ts.map +1 -0
  61. package/dist/__tests__/migrate/fixtures/textbookPort.js +116 -0
  62. package/dist/__tests__/migrate/fixtures/textbookPort.js.map +1 -0
  63. package/dist/__tests__/migrate/labelsOnPictures.test.d.ts +2 -0
  64. package/dist/__tests__/migrate/labelsOnPictures.test.d.ts.map +1 -0
  65. package/dist/__tests__/migrate/labelsOnPictures.test.js +114 -0
  66. package/dist/__tests__/migrate/labelsOnPictures.test.js.map +1 -0
  67. package/dist/__tests__/migrate/textToMarkdown.test.d.ts +2 -0
  68. package/dist/__tests__/migrate/textToMarkdown.test.d.ts.map +1 -0
  69. package/dist/__tests__/migrate/textToMarkdown.test.js +268 -0
  70. package/dist/__tests__/migrate/textToMarkdown.test.js.map +1 -0
  71. package/dist/__tests__/paragraphDropCap.test.js +38 -0
  72. package/dist/__tests__/paragraphDropCap.test.js.map +1 -1
  73. package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts +2 -0
  74. package/dist/__tests__/paragraphStyleLetterSpacing.test.d.ts.map +1 -0
  75. package/dist/__tests__/paragraphStyleLetterSpacing.test.js +89 -0
  76. package/dist/__tests__/paragraphStyleLetterSpacing.test.js.map +1 -0
  77. package/dist/__tests__/pictureBelowPrintSize.test.d.ts +2 -0
  78. package/dist/__tests__/pictureBelowPrintSize.test.d.ts.map +1 -0
  79. package/dist/__tests__/pictureBelowPrintSize.test.js +82 -0
  80. package/dist/__tests__/pictureBelowPrintSize.test.js.map +1 -0
  81. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +1 -1
  82. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
  83. package/dist/__tests__/resourceTypePartial.test.d.ts +2 -0
  84. package/dist/__tests__/resourceTypePartial.test.d.ts.map +1 -0
  85. package/dist/__tests__/resourceTypePartial.test.js +60 -0
  86. package/dist/__tests__/resourceTypePartial.test.js.map +1 -0
  87. package/dist/canvas-backend/composed.d.ts +8 -2
  88. package/dist/canvas-backend/composed.d.ts.map +1 -1
  89. package/dist/canvas-backend/composed.js +52 -29
  90. package/dist/canvas-backend/composed.js.map +1 -1
  91. package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
  92. package/dist/canvas-backend/headerFooter.js +13 -0
  93. package/dist/canvas-backend/headerFooter.js.map +1 -1
  94. package/dist/canvas-backend/index.d.ts.map +1 -1
  95. package/dist/canvas-backend/index.js +53 -24
  96. package/dist/canvas-backend/index.js.map +1 -1
  97. package/dist/canvas-backend/renderResourceBlock.d.ts +5 -1
  98. package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
  99. package/dist/canvas-backend/renderResourceBlock.js +60 -1
  100. package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
  101. package/dist/comics/frames.d.ts +82 -0
  102. package/dist/comics/frames.d.ts.map +1 -0
  103. package/dist/comics/frames.js +182 -0
  104. package/dist/comics/frames.js.map +1 -0
  105. package/dist/comics/index.d.ts +4 -1
  106. package/dist/comics/index.d.ts.map +1 -1
  107. package/dist/comics/index.js +3 -1
  108. package/dist/comics/index.js.map +1 -1
  109. package/dist/comics/layoutPage.d.ts +9 -0
  110. package/dist/comics/layoutPage.d.ts.map +1 -1
  111. package/dist/comics/layoutPage.js +53 -68
  112. package/dist/comics/layoutPage.js.map +1 -1
  113. package/dist/comics/page.d.ts.map +1 -1
  114. package/dist/comics/page.js +10 -1
  115. package/dist/comics/page.js.map +1 -1
  116. package/dist/comics/sourceMap.d.ts +60 -0
  117. package/dist/comics/sourceMap.d.ts.map +1 -0
  118. package/dist/comics/sourceMap.js +166 -0
  119. package/dist/comics/sourceMap.js.map +1 -0
  120. package/dist/comics/spread.d.ts.map +1 -1
  121. package/dist/comics/spread.js +6 -0
  122. package/dist/comics/spread.js.map +1 -1
  123. package/dist/comics/textPanels.d.ts +113 -0
  124. package/dist/comics/textPanels.d.ts.map +1 -0
  125. package/dist/comics/textPanels.js +327 -0
  126. package/dist/comics/textPanels.js.map +1 -0
  127. package/dist/comics/transform.d.ts +6 -2
  128. package/dist/comics/transform.d.ts.map +1 -1
  129. package/dist/comics/transform.js +14 -1
  130. package/dist/comics/transform.js.map +1 -1
  131. package/dist/comics/types.d.ts +10 -0
  132. package/dist/comics/types.d.ts.map +1 -1
  133. package/dist/composed/continuation.d.ts +152 -0
  134. package/dist/composed/continuation.d.ts.map +1 -0
  135. package/dist/composed/continuation.js +189 -0
  136. package/dist/composed/continuation.js.map +1 -0
  137. package/dist/composed/groups.d.ts +3 -0
  138. package/dist/composed/groups.d.ts.map +1 -1
  139. package/dist/composed/groups.js +34 -4
  140. package/dist/composed/groups.js.map +1 -1
  141. package/dist/composed/labels.d.ts +154 -0
  142. package/dist/composed/labels.d.ts.map +1 -0
  143. package/dist/composed/labels.js +524 -0
  144. package/dist/composed/labels.js.map +1 -0
  145. package/dist/composed/pageMemo.d.ts +3 -2
  146. package/dist/composed/pageMemo.d.ts.map +1 -1
  147. package/dist/composed/pageMemo.js +3 -1
  148. package/dist/composed/pageMemo.js.map +1 -1
  149. package/dist/composed/paint.d.ts +28 -3
  150. package/dist/composed/paint.d.ts.map +1 -1
  151. package/dist/composed/paint.js +48 -2
  152. package/dist/composed/paint.js.map +1 -1
  153. package/dist/composed/reflowLog.d.ts +3 -1
  154. package/dist/composed/reflowLog.d.ts.map +1 -1
  155. package/dist/composed/reflowLog.js +20 -6
  156. package/dist/composed/reflowLog.js.map +1 -1
  157. package/dist/composed/rules.d.ts +31 -0
  158. package/dist/composed/rules.d.ts.map +1 -0
  159. package/dist/composed/rules.js +45 -0
  160. package/dist/composed/rules.js.map +1 -0
  161. package/dist/composed/slotChecks.d.ts +2 -1
  162. package/dist/composed/slotChecks.d.ts.map +1 -1
  163. package/dist/composed/slotChecks.js +56 -2
  164. package/dist/composed/slotChecks.js.map +1 -1
  165. package/dist/composed/sourceMap.d.ts +0 -21
  166. package/dist/composed/sourceMap.d.ts.map +1 -1
  167. package/dist/composed/sourceMap.js +102 -8
  168. package/dist/composed/sourceMap.js.map +1 -1
  169. package/dist/composed/spread.d.ts +23 -5
  170. package/dist/composed/spread.d.ts.map +1 -1
  171. package/dist/composed/spread.js +52 -27
  172. package/dist/composed/spread.js.map +1 -1
  173. package/dist/composed/templates.d.ts +11 -0
  174. package/dist/composed/templates.d.ts.map +1 -1
  175. package/dist/composed/templates.js +51 -2
  176. package/dist/composed/templates.js.map +1 -1
  177. package/dist/composed/types.d.ts +43 -3
  178. package/dist/composed/types.d.ts.map +1 -1
  179. package/dist/composed/validate.d.ts.map +1 -1
  180. package/dist/composed/validate.js +5 -3
  181. package/dist/composed/validate.js.map +1 -1
  182. package/dist/configWarnings.d.ts +15 -0
  183. package/dist/configWarnings.d.ts.map +1 -1
  184. package/dist/configWarnings.js +40 -18
  185. package/dist/configWarnings.js.map +1 -1
  186. package/dist/contrast.d.ts +5 -3
  187. package/dist/contrast.d.ts.map +1 -1
  188. package/dist/contrast.js +46 -5
  189. package/dist/contrast.js.map +1 -1
  190. package/dist/defaults/headerFooter.d.ts.map +1 -1
  191. package/dist/defaults/headerFooter.js +4 -0
  192. package/dist/defaults/headerFooter.js.map +1 -1
  193. package/dist/defaults/paragraphStyles.d.ts +1 -1
  194. package/dist/defaults/paragraphStyles.d.ts.map +1 -1
  195. package/dist/defaults/paragraphStyles.js +5 -1
  196. package/dist/defaults/paragraphStyles.js.map +1 -1
  197. package/dist/defaults/resourceTypes.d.ts +15 -0
  198. package/dist/defaults/resourceTypes.d.ts.map +1 -1
  199. package/dist/defaults/resourceTypes.js +39 -1
  200. package/dist/defaults/resourceTypes.js.map +1 -1
  201. package/dist/design/layout.d.ts +7 -0
  202. package/dist/design/layout.d.ts.map +1 -1
  203. package/dist/design/layout.js +109 -16
  204. package/dist/design/layout.js.map +1 -1
  205. package/dist/design/slots.d.ts +12 -10
  206. package/dist/design/slots.d.ts.map +1 -1
  207. package/dist/design/slots.js +26 -2
  208. package/dist/design/slots.js.map +1 -1
  209. package/dist/fonts/faces.d.ts.map +1 -1
  210. package/dist/fonts/faces.js +6 -0
  211. package/dist/fonts/faces.js.map +1 -1
  212. package/dist/highlightMarks.d.ts.map +1 -1
  213. package/dist/highlightMarks.js +6 -0
  214. package/dist/highlightMarks.js.map +1 -1
  215. package/dist/html-backend.d.ts.map +1 -1
  216. package/dist/html-backend.js +83 -7
  217. package/dist/html-backend.js.map +1 -1
  218. package/dist/htmlComic.d.ts +3 -0
  219. package/dist/htmlComic.d.ts.map +1 -1
  220. package/dist/htmlComic.js +2 -0
  221. package/dist/htmlComic.js.map +1 -1
  222. package/dist/index.d.ts +12 -8
  223. package/dist/index.d.ts.map +1 -1
  224. package/dist/index.js +7 -3
  225. package/dist/index.js.map +1 -1
  226. package/dist/migrate/markdownRuns.d.ts +70 -0
  227. package/dist/migrate/markdownRuns.d.ts.map +1 -0
  228. package/dist/migrate/markdownRuns.js +215 -0
  229. package/dist/migrate/markdownRuns.js.map +1 -0
  230. package/dist/migrate/pageDesign.d.ts +152 -0
  231. package/dist/migrate/pageDesign.d.ts.map +1 -0
  232. package/dist/migrate/pageDesign.js +573 -0
  233. package/dist/migrate/pageDesign.js.map +1 -0
  234. package/dist/migrate/textToMarkdown.d.ts +156 -0
  235. package/dist/migrate/textToMarkdown.d.ts.map +1 -0
  236. package/dist/migrate/textToMarkdown.js +913 -0
  237. package/dist/migrate/textToMarkdown.js.map +1 -0
  238. package/dist/parse/blockParser.js +26 -0
  239. package/dist/parse/blockParser.js.map +1 -1
  240. package/dist/pipeline/build.d.ts.map +1 -1
  241. package/dist/pipeline/build.js +388 -41
  242. package/dist/pipeline/build.js.map +1 -1
  243. package/dist/pipeline/buildMeasurement.d.ts.map +1 -1
  244. package/dist/pipeline/buildMeasurement.js +12 -1
  245. package/dist/pipeline/buildMeasurement.js.map +1 -1
  246. package/dist/pipeline/calloutLayout.d.ts +25 -1
  247. package/dist/pipeline/calloutLayout.d.ts.map +1 -1
  248. package/dist/pipeline/calloutLayout.js +100 -2
  249. package/dist/pipeline/calloutLayout.js.map +1 -1
  250. package/dist/pipeline/composedLayout.d.ts +16 -2
  251. package/dist/pipeline/composedLayout.d.ts.map +1 -1
  252. package/dist/pipeline/composedLayout.js +337 -70
  253. package/dist/pipeline/composedLayout.js.map +1 -1
  254. package/dist/pipeline/contentWarnings.d.ts +15 -0
  255. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  256. package/dist/pipeline/contentWarnings.js +106 -1
  257. package/dist/pipeline/contentWarnings.js.map +1 -1
  258. package/dist/pipeline/dropCap.d.ts +5 -0
  259. package/dist/pipeline/dropCap.d.ts.map +1 -1
  260. package/dist/pipeline/dropCap.js +23 -2
  261. package/dist/pipeline/dropCap.js.map +1 -1
  262. package/dist/pipeline/frameLayout.d.ts +21 -3
  263. package/dist/pipeline/frameLayout.d.ts.map +1 -1
  264. package/dist/pipeline/frameLayout.js +52 -4
  265. package/dist/pipeline/frameLayout.js.map +1 -1
  266. package/dist/pipeline/headerFooter.d.ts +11 -1
  267. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  268. package/dist/pipeline/headerFooter.js +210 -68
  269. package/dist/pipeline/headerFooter.js.map +1 -1
  270. package/dist/pipeline/measureContentBlock.d.ts +1 -1
  271. package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
  272. package/dist/pipeline/measureContentBlock.js +4 -1
  273. package/dist/pipeline/measureContentBlock.js.map +1 -1
  274. package/dist/pipeline/mirrorFrame.d.ts.map +1 -1
  275. package/dist/pipeline/mirrorFrame.js +25 -2
  276. package/dist/pipeline/mirrorFrame.js.map +1 -1
  277. package/dist/pipeline/resourceLayout.d.ts +28 -0
  278. package/dist/pipeline/resourceLayout.d.ts.map +1 -1
  279. package/dist/pipeline/resourceLayout.js +21 -0
  280. package/dist/pipeline/resourceLayout.js.map +1 -1
  281. package/dist/pipeline/resourceNumbering.d.ts.map +1 -1
  282. package/dist/pipeline/resourceNumbering.js +13 -4
  283. package/dist/pipeline/resourceNumbering.js.map +1 -1
  284. package/dist/pipeline/styles.d.ts +1 -1
  285. package/dist/pipeline/styles.d.ts.map +1 -1
  286. package/dist/pipeline/styles.js +4 -0
  287. package/dist/pipeline/styles.js.map +1 -1
  288. package/dist/preflight.js +5 -1
  289. package/dist/preflight.js.map +1 -1
  290. package/dist/types.d.ts +45 -2
  291. package/dist/types.d.ts.map +1 -1
  292. package/dist/vdt.d.ts +228 -9
  293. package/dist/vdt.d.ts.map +1 -1
  294. package/dist/vdt.js.map +1 -1
  295. package/dist/word/toDocx.d.ts.map +1 -1
  296. package/dist/word/toDocx.js +14 -0
  297. package/dist/word/toDocx.js.map +1 -1
  298. package/dist/word/word.test.js +11 -1
  299. package/dist/word/word.test.js.map +1 -1
  300. package/package.json +1 -1
@@ -4,12 +4,13 @@ import { dimensionToPx } from '../units';
4
4
  import { createVDTDocument, createVDTBlock, createBoundingBox, createVDTColumn, pageIsMirrored, pageIsVertical, flowRectToPage, } from '../vdt';
5
5
  import { figureLabels } from './buildMeasurement';
6
6
  import { contourOnPage } from '../contour';
7
+ import { figureLabelWarnings, layOutFigureLabels, stampFigureLabelRects } from '../composed/labels';
7
8
  import { anchorBox } from '../design/layout';
8
9
  import { parseMarkdownMemo, spaceDirectiveLines } from '../parse';
9
10
  import { buildPageLabels, computeHeadingNumbering, parseNumberFormat, } from '../numbering';
10
11
  import { extractFrontmatter, normalizeMetadata } from '../frontmatter';
11
12
  import { collectConfigWarnings } from '../configWarnings';
12
- import { initHyphenator } from '../measure';
13
+ import { initHyphenator, measureRichBlock } from '../measure';
13
14
  import { setHyphenationRules } from '../hyphenate';
14
15
  import { citedNames } from '../citations/names';
15
16
  import { getCjkLineBreak, setCjkLineBreak } from '../measure/cjkClasses';
@@ -37,9 +38,12 @@ import { applyStatementNumbering, numberStatements, outlineLookup } from './stat
37
38
  import { SPAN_EMBED_STYLE_ID, isPageSpanStrip, spanEmbedStyle, wrapPageSpanEmbeds } from './spanEmbeds';
38
39
  import { flowColumnsStyles, isFlowColumnsStyle, wrapFlowColumns } from './flowColumns';
39
40
  import { FRAME_STYLE_ID, frameBoxStyle, wrapComposedPages } from '../composed/wrap';
40
- import { resolvePageTemplate } from '../composed/templates';
41
+ import { continueInPage, resolvePageTemplate } from '../composed/templates';
41
42
  import { layoutComposedSpread } from '../composed/spread';
42
- import { composedPageAt, frameStyleOf, planComposedPages } from './composedLayout';
43
+ import { assignJumpReceivers, assignReceivers, continuationTarget, MAX_CONTINUATION_PAGES } from '../composed/continuation';
44
+ import { frameElementId } from '../composed/ids';
45
+ import { nextFrameOf } from '../composed/geometry';
46
+ import { composedPageAt, frameStyleOf, layoutComposedPage, planComposedPages } from './composedLayout';
43
47
  import { configKeyOf, layoutComposedPageMemo } from '../composed/pageMemo';
44
48
  import { anchorOutline, computeOutline, hasIndexDirective, hasTocDirective, headingNumberingOptions, headingTemplatesOf, outlineFromDoc, sameOutline } from './outline';
45
49
  import { expandTocDirectives } from './toc';
@@ -51,6 +55,7 @@ import { chooseParagraphSplit } from './orphanWidow';
51
55
  import { layoutComicPage, comicPageLayoutWarnings } from '../comics/layoutPage';
52
56
  import { comicStripExtent, comicStripOffset, comicStripPlacement, comicStripWidth, layoutComicStrip } from '../comics/strip';
53
57
  import { isComicSpread, layoutComicSpread } from '../comics/spread';
58
+ import { comicTextPanelsSupported, layoutComicTextFrames, planComicTextPanels, wrapComicTextPanels } from '../comics/textPanels';
54
59
  import { applyStyleAttrs, computePageMetrics, flowPageMirrored, pageMirrored, sheetRectToFlow, isMarkerBlock, nextNonMarkerBlock, spaceLinesAfter, prevNonMarkerBlock, rollbackTrailingBlocks, } from './buildHelpers';
55
60
  import { measureContentBlock } from './measureContentBlock';
56
61
  import { containerStyle, paragraphStyleIdOf, planParagraphContainers } from './paragraphContainers';
@@ -75,7 +80,7 @@ import { chapterNumberCounter, leadingBoldText } from './placeholders';
75
80
  import { proposeBalanceLines, GRID_LINE_LEVERS, collectColumnGaps, firstDivergentColumn, gapLinesIn, boxRoomIn, boxLeverKeys, pageSegments, MAX_BALANCING_PASSES, MAX_BALANCING_PASSES_PER_DOCUMENT, balanceKey, candidateKey, flexFigureKey, flexFloatKey } from './columnBalancing';
76
81
  import { applyBandCap, uncapBand, columnBottom, bandCapLines, bandTop, bareBandTop, resolveBandCapsGen, drainPasses, resolveTrailingCapsGen, bandCapLinesAroundZone, bandLinePitch, capRetryLimit, } from './bandCaps';
77
82
  import { raggedLooseLines } from './raggedLines';
78
- import { cjkLooseLineWarnings, raggedLineWarnings, collectContentWarnings, designTruncationWarnings, dropCapWarnings, floatShrinkWarnings, joiningLetterSpacingWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
83
+ import { cjkLooseLineWarnings, raggedLineWarnings, collectContentWarnings, designTruncationWarnings, dropCapWarnings, floatShrinkWarnings, joiningLetterSpacingWarnings, pictureBelowPrintSizeWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
79
84
  import { dropCapSinkAfter } from './dropCap';
80
85
  import { mostlyJoiningScript } from '../measure/joining';
81
86
  import { composesAsCjk } from '../measure/cjkCompose';
@@ -279,6 +284,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
279
284
  // their own `resolution` from here on (#631); under the default policy
280
285
  // the content is untouched.
281
286
  const stampedResources = content.resources && withBitmapResolutions(content.resources, resolved.layout.bitmapResolution);
287
+ /** The resources as given: whose print size is the author's (#901). */
288
+ const givenResources = content.resources;
282
289
  if (stampedResources && stampedResources !== content.resources)
283
290
  content = { ...content, resources: stampedResources };
284
291
  // Tracking rungs a loose paragraph may climb: none, then 5‰ steps up to
@@ -493,7 +500,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
493
500
  // in a frame is a group of its box.
494
501
  const templateSource = { pageTemplates: resolved.pageTemplates, calloutStyles: resolved.calloutStyles };
495
502
  const composedWrap = wrapComposedPages(numberedBlocks, templateSource);
496
- const flowGroups = resolved.layout.flowColumns ? wrapFlowColumns(composedWrap.blocks) : { blocks: composedWrap.blocks, wrapped: false };
503
+ // A comic page's text panels (#885): each panel's Markdown in a story
504
+ // box of the same kind, set in the panel's frame (below).
505
+ const comicText = wrapComicTextPanels(composedWrap.blocks, comicTextPanelsSupported(resolved));
506
+ const flowGroups = resolved.layout.flowColumns ? wrapFlowColumns(comicText.blocks) : { blocks: comicText.blocks, wrapped: false };
497
507
  const spanEmbeds = wrapPageSpanEmbeds(flowGroups.blocks, content.resources ?? [], effectiveResourceTypes(config, content.resources ?? []));
498
508
  // Code listings (#624): each in a box of the code style, which the
499
509
  // callout machinery places and splits.
@@ -536,7 +546,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
536
546
  for (const b of contentBlocks) {
537
547
  const labels = b.resourceId !== undefined && !figureLabelsById.has(b.resourceId) ? figureLabels(b, bodyOffset) : undefined;
538
548
  if (labels)
539
- figureLabelsById.set(b.resourceId, labels);
549
+ figureLabelsById.set(b.resourceId, { labels, parsed: b.figure.labels });
540
550
  }
541
551
  // Resolve styles
542
552
  const bodyStyle = resolveBodyStyle(resolved);
@@ -548,7 +558,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
548
558
  ...(spanEmbeds.wrapped ? [spanEmbedStyle(bodyStyle.lineHeightPx, resolved.layout.inlineResourceGap !== 'above')] : []),
549
559
  ...(codeBoxes.wrapped ? [codeBoxStyle(resolved)] : []),
550
560
  // The frameless box of a composed page's story (#855).
551
- ...(composedWrap.wrapped ? [frameBoxStyle(Math.max(resolved.bodyText.avoidOrphans ? resolved.bodyText.orphanMinLines : 1, resolved.bodyText.avoidWidows ? resolved.bodyText.widowMinLines : 1))] : []),
561
+ ...(composedWrap.wrapped || comicText.wrapped ? [frameBoxStyle(Math.max(resolved.bodyText.avoidOrphans ? resolved.bodyText.orphanMinLines : 1, resolved.bodyText.avoidWidows ? resolved.bodyText.widowMinLines : 1))] : []),
552
562
  // The boxes of main-flow groups: sub-columns a body line apart in the
553
563
  // text column, a gutter apart across the page.
554
564
  ...(flowGroups.wrapped
@@ -560,7 +570,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
560
570
  : resolved.calloutStyles;
561
571
  // A box nested in another looks its style up in the configuration it is
562
572
  // laid out with: the listings' box among them.
563
- const calloutResolved = codeBoxes.wrapped || flowGroups.wrapped || composedWrap.wrapped ? { ...resolved, calloutStyles } : resolved;
573
+ const calloutResolved = codeBoxes.wrapped || flowGroups.wrapped || composedWrap.wrapped || comicText.wrapped ? { ...resolved, calloutStyles } : resolved;
564
574
  const blockquoteStyle = resolveBlockquoteStyle(resolved);
565
575
  const listLevelIndentsPx = computeLevelIndentsPx(resolved, bodyStyle.fontSizePx);
566
576
  const orderedMetrics = computeOrderedListRunMetrics(contentBlocks, resolved, bodyStyle.fontSizePx);
@@ -596,10 +606,12 @@ function placeDocumentPass(content, config, cache, options, hints) {
596
606
  // story holds does not float. A reference in a frame to a picture set
597
607
  // elsewhere floats from the page's end, to the pages after it.
598
608
  const composedPlans = planComposedPages(contentBlocks, calloutPlan, templateSource);
599
- if (composedPlans.size > 0) {
609
+ // A comic page's text panels (#885) likewise.
610
+ const comicTextPlans = comicText.wrapped ? planComicTextPanels(contentBlocks, calloutPlan) : new Map();
611
+ if (composedPlans.size > 0 || comicTextPlans.size > 0) {
600
612
  for (let k = floatPlan.length - 1; k >= 0; k--) {
601
613
  const f = floatPlan[k];
602
- const page = composedPageAt(composedPlans, f.firstBlockIdx);
614
+ const page = composedPageAt(composedPlans, f.firstBlockIdx) ?? composedPageAt(comicTextPlans, f.firstBlockIdx);
603
615
  if (!page)
604
616
  continue;
605
617
  const raw = contentBlocks[f.firstBlockIdx];
@@ -1257,7 +1269,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
1257
1269
  const { block: rb, totalHeight } = laid;
1258
1270
  const id = slice && slice.startRow > 0 ? `float-${resourceId}-cont-${slice.startRow}` : `float-${resourceId}`;
1259
1271
  const blk = createVDTBlock(id, 'resource', bodyStyle.fontString, bodyStyle.color, bodyStyle.textAlign);
1260
- const labels = figureLabelsById.get(resourceId);
1272
+ // A floated figure's labels, laid out on its picture as set (#860).
1273
+ const figure = figureLabelsById.get(resourceId);
1274
+ const labels = figure
1275
+ ? layOutFigureLabels(figure.labels, figure.parsed, rb, { resolved, resourceNumbering, resourceTypes, resources, ...(cache ? { cache } : {}) })
1276
+ : undefined;
1261
1277
  blk.resourceBlock = labels ? { ...rb, labels } : rb;
1262
1278
  blk.dirty = false;
1263
1279
  blk.snappedToGrid = false;
@@ -4000,6 +4016,18 @@ function placeDocumentPass(content, config, cache, options, hints) {
4000
4016
  withStripPlaceholder(blk, stripGeometry(idx, raw.comic, width, contentArea.height - 2 * floatGapPx, pageIsVertical(doc.pages[cursor.pageIndex])));
4001
4017
  return blk;
4002
4018
  };
4019
+ /** The room a heading's design takes in a box or a frame (#906): as in
4020
+ * a column (see the heading placement below), against the heading's
4021
+ * box; a `span: 'page'` heading is laid out at the box's width too. */
4022
+ const boxHeadingDesign = (raw, blockIdx, lines, pref, headingNumber, width) => {
4023
+ const lvl = headingLevels.forBlock(raw);
4024
+ if (!lvl?.advancedDesign.enabled)
4025
+ return undefined;
4026
+ const { titleText, marked } = defaultOpenerTitle(lines, pref, raw.titleBreaks, raw.text.length, lvl.numberSeparator);
4027
+ const info = { titleText, ...(marked !== undefined ? { titleMarked: marked } : {}), formattedNumber: pref, numericValue: headingNumber, locale: resolvedLocale(resolved), ...(resolved.numerals ? { numerals: resolved.numerals } : {}), chapterNumber: chapterNumberByBlock[blockIdx] ?? '', attrs: raw.attrs };
4028
+ const design = measureHeadingDesign(lvl, info, width, resolved.page.dpi, doc.metadata, cursor.pageIndex, undefined, undefined, resourceById);
4029
+ return Math.max(design.height, design.textFloor);
4030
+ };
4003
4031
  /** What the box typesetter (`frameLayout.ts`) reads of this pass. */
4004
4032
  const framePass = {
4005
4033
  contentBlocks,
@@ -4013,6 +4041,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
4013
4041
  : measureCtx),
4014
4042
  stripBlock: calloutStripBlock,
4015
4043
  columnHeight: () => contentArea.height,
4044
+ headingDesign: (raw, blockIdx, heading, width) => boxHeadingDesign(raw, blockIdx, heading.lines, heading.numberPrefix, heading.headingNumber, width),
4016
4045
  };
4017
4046
  const makeCalloutLayouter = (startIdx, plan, style) => makeCalloutLayouterIn(framePass, startIdx, plan, style);
4018
4047
  /** Whether a page swaps its mirrored margins (a verso of a left-bound
@@ -5932,6 +5961,19 @@ function placeDocumentPass(content, config, cache, options, hints) {
5932
5961
  return false;
5933
5962
  },
5934
5963
  };
5964
+ /** Every frame of the composed pages laid so far, by id: a story that
5965
+ * jumps to a later page (#917) links its pieces across pages. */
5966
+ const frameAt = new Map();
5967
+ /** The planned composed pages by their fence's `id` (#917): where a
5968
+ * `'page:<id>'` continuation lands. */
5969
+ const fenceById = new Map();
5970
+ for (const pl of composedPlans.values()) {
5971
+ const id = contentBlocks[pl.startIdx]?.composedPage?.attrs.id;
5972
+ if (id !== undefined && id !== '' && !fenceById.has(id))
5973
+ fenceById.set(id, pl);
5974
+ }
5975
+ /** Stories waiting for the later page fence they jump to, by its id. */
5976
+ const jumps = new Map();
5935
5977
  /** Set the composed page `planned` on a fresh page (#855), or a spread
5936
5978
  * template on the two facing pages from the next verso (#861). */
5937
5979
  const placeComposedPage = (planned, fence) => {
@@ -5967,10 +6009,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
5967
6009
  });
5968
6010
  const placed = [];
5969
6011
  let laid;
5970
- const sheet = resolvePageTemplate(templateSource, fence.template, { spread: true });
5971
- if (sheet?.spread) {
5972
- // A spread opens on a verso, a parity blank before it when needed,
5973
- // as a comic spread does; the recto after it takes the other half.
6012
+ /** Lay a spread template over the next verso and the recto after it
6013
+ * (`breakToEvenPage` opens the verso, a parity blank before it when
6014
+ * needed, as a comic spread does). */
6015
+ const laySpread = (sheet, extra) => {
5974
6016
  breakToEvenPage();
5975
6017
  const verso = doc.pages[cursor.pageIndex];
5976
6018
  verso.columns = [createVDTColumn(0, verso.contentArea)];
@@ -5982,50 +6024,262 @@ function placeDocumentPass(content, config, cache, options, hints) {
5982
6024
  recto.columns = [createVDTColumn(0, recto.contentArea)];
5983
6025
  cursor.columnIndex = 0;
5984
6026
  // The sheet is laid in the pages' flow: the left page first, or the
5985
- // right one where the flow is mirrored (right to left).
6027
+ // right one where the flow is mirrored (right to left) or turned (a
6028
+ // vertical page, #926: the flow's y runs from the sheet's right edge
6029
+ // to its left, and the right page comes first along it).
5986
6030
  const versoIsLeft = resolved.page.binding !== 'right';
5987
- const flowMirrored = pageIsMirrored(verso);
6031
+ const vertical = pageIsVertical(verso);
6032
+ const flowMirrored = pageIsMirrored(verso) || vertical;
5988
6033
  const left = versoIsLeft ? verso : recto;
5989
6034
  const right = versoIsLeft ? recto : verso;
6035
+ const frames = designFramesOn(verso);
5990
6036
  const out = layoutComposedSpread({
5991
6037
  ...common(flowMirrored ? right : left, { id: sheet.id, spread: true }),
6038
+ ...extra,
5992
6039
  template: sheet,
5993
6040
  first: flowMirrored ? right : left,
5994
6041
  second: flowMirrored ? left : right,
5995
6042
  firstSide: flowMirrored ? 'right' : 'left',
5996
- trim: pageMetrics.physical.trimBox,
5997
- bleed: pageMetrics.physical.bleedBox,
6043
+ trim: frames.page,
6044
+ bleed: frames.bleed,
6045
+ ...(vertical ? { axis: 'y' } : {}),
5998
6046
  });
6047
+ return { sides: out.sides, warnings: out.warnings, laid: out.laid, continuations: out.continuations };
6048
+ };
6049
+ /** The template `id` for the current page's side, and the boxes its
6050
+ * `area` reads. */
6051
+ const singleOn = (page, id) => {
6052
+ page.columns = [createVDTColumn(0, page.contentArea)];
6053
+ // The physical side: a recto has an odd folio (in either binding).
6054
+ const parity = (page.index + pageIndexOffset) % 2 === 0 ? 'recto' : 'verso';
6055
+ const template = resolvePageTemplate(templateSource, id, { parity });
6056
+ const frames = designFramesOn(page);
6057
+ const area = template.area === 'trim' ? frames.page : template.area === 'bleed' ? frames.bleed : page.contentArea;
6058
+ return { parity, template, areas: { area, page: frames.page, bleed: frames.bleed } };
6059
+ };
6060
+ let continuations;
6061
+ // Stories that jump to this page (#917): they go on in the frames its
6062
+ // own content leaves empty; those that find none go on after it.
6063
+ const incoming = fence.attrs.id !== undefined ? jumps.get(fence.attrs.id) ?? [] : [];
6064
+ if (fence.attrs.id !== undefined)
6065
+ jumps.delete(fence.attrs.id);
6066
+ let jumpReceivers = new Map();
6067
+ let jumpLeft = [];
6068
+ const receive = (template) => {
6069
+ if (incoming.length === 0)
6070
+ return {};
6071
+ const names = new Set(template.frames.map((f) => f.name));
6072
+ const prev = new Map();
6073
+ for (const f of template.frames) {
6074
+ const n = nextFrameOf(f);
6075
+ if (n && names.has(n) && !prev.has(n))
6076
+ prev.set(n, f.name);
6077
+ }
6078
+ const headOf = (n) => {
6079
+ const seen = new Set([n]);
6080
+ let at = n;
6081
+ for (let q = prev.get(at); q !== undefined && !seen.has(q); q = prev.get(at)) {
6082
+ seen.add(q);
6083
+ at = q;
6084
+ }
6085
+ return at;
6086
+ };
6087
+ const assigned = assignJumpReceivers(template, incoming, (n) => planned.stories.has(headOf(n)));
6088
+ jumpReceivers = assigned.receivers;
6089
+ jumpLeft = assigned.left;
6090
+ return {
6091
+ stories: new Map([...planned.stories, ...[...jumpReceivers].map(([n, c]) => [n, c.story])]),
6092
+ continuing: { receivers: jumpReceivers, generation: 0, collapse: false, last: false, jump: true },
6093
+ };
6094
+ };
6095
+ const sheet = resolvePageTemplate(templateSource, fence.template, { spread: true });
6096
+ if (sheet?.spread) {
6097
+ const out = laySpread(sheet, receive(sheet));
5999
6098
  placed.push(...out.sides);
6000
6099
  composedNotes.push(...out.warnings);
6001
6100
  laid = out.laid;
6101
+ continuations = out.continuations;
6002
6102
  }
6003
6103
  else {
6004
6104
  const page = doc.pages[cursor.pageIndex];
6005
- page.columns = [createVDTColumn(0, page.contentArea)];
6006
- // The physical side: a recto has an odd folio (in either binding).
6007
- const parity = (page.index + pageIndexOffset) % 2 === 0 ? 'recto' : 'verso';
6008
- const template = resolvePageTemplate(templateSource, fence.template, { parity });
6009
- const frames = designFramesOn(page);
6010
- const area = template.area === 'trim' ? frames.page : template.area === 'bleed' ? frames.bleed : page.contentArea;
6105
+ const { parity, template, areas } = singleOn(page, fence.template);
6106
+ const extra = receive(template);
6011
6107
  // Kept by what it reads (#857): a page met again is not set again.
6012
- // A spread is laid out afresh each time (its two sides are one sheet).
6013
- const out = layoutComposedPageMemo({
6014
- ...common(page, { id: template.id, spread: false, parity }),
6015
- page,
6016
- template,
6017
- areas: { area, page: frames.page, bleed: frames.bleed },
6018
- }, composedMemo);
6108
+ // A spread is laid out afresh each time (its two sides are one sheet),
6109
+ // and so is a page a story jumps to.
6110
+ const args = { ...common(page, { id: template.id, spread: false, parity }), page, template, areas };
6111
+ const out = extra.continuing ? layoutComposedPage({ ...args, ...extra }) : layoutComposedPageMemo(args, composedMemo);
6019
6112
  placed.push({ page, composed: out.composed, blocks: out.blocks });
6020
6113
  composedNotes.push(...out.warnings);
6021
6114
  laid = out.laid;
6115
+ continuations = out.continuations;
6022
6116
  }
6023
- for (const { page, composed, blocks } of placed) {
6024
- page.composed = composed;
6025
- const floats = (page.floats ??= []);
6026
- for (const b of blocks) {
6027
- floats.push(b);
6028
- doc.blocks.push(b);
6117
+ // A jumping story that found no free frame here goes on after it.
6118
+ if (jumpLeft.length > 0)
6119
+ continuations = [...(continuations ?? []), ...jumpLeft.map((c) => ({ ...c, rule: 'next-page', template: placed[0].composed.template, collapsed: false }))];
6120
+ const commit = (list) => {
6121
+ for (const { page, composed, blocks } of list) {
6122
+ page.composed = composed;
6123
+ const floats = (page.floats ??= []);
6124
+ for (const b of blocks) {
6125
+ floats.push(b);
6126
+ doc.blocks.push(b);
6127
+ }
6128
+ }
6129
+ };
6130
+ commit(placed);
6131
+ // Content that goes on on pages added after this one (#859): one page
6132
+ // (or spread) per template it goes on to, generation after generation,
6133
+ // each laid afresh after the pages before it. The added pages are
6134
+ // ordinary pages (numbered, with running heads, sides kept).
6135
+ const indexFrames = (list) => {
6136
+ for (const s of list)
6137
+ for (const f of s.composed.frames)
6138
+ if (!f.facingBlocks)
6139
+ frameAt.set(f.id, { page: s.page, frame: f });
6140
+ };
6141
+ indexFrames(placed);
6142
+ /** The sending frame of `c` points to the frame `to` that took it, and
6143
+ * its page's log says where it went. */
6144
+ const link = (c, to) => {
6145
+ const src = frameAt.get(c.fromFrameId);
6146
+ const dst = frameAt.get(to);
6147
+ if (!src || !dst)
6148
+ return;
6149
+ src.frame.continuesTo = to;
6150
+ const log = src.page.composed.log;
6151
+ let at = log.length;
6152
+ for (let i = log.length - 1; i >= 0; i--) {
6153
+ if (log[i].frame === c.frame) {
6154
+ at = i + 1;
6155
+ break;
6156
+ }
6157
+ }
6158
+ log.splice(at, 0, { frame: c.frame, kind: 'continued', toPage: dst.page.index, toFrame: to });
6159
+ };
6160
+ for (const [name, c] of jumpReceivers)
6161
+ link(c, frameElementId(fence.fenceIndex, name));
6162
+ /** Stories that jump to a later page fence (#917) wait for it; one
6163
+ * whose fence is not after this page goes on on the next page, and
6164
+ * says so. */
6165
+ const route = (list) => {
6166
+ const out = [];
6167
+ for (const c of list) {
6168
+ const target = continueInPage(c.rule);
6169
+ if (!target) {
6170
+ out.push(c);
6171
+ continue;
6172
+ }
6173
+ const to = fenceById.get(target.page);
6174
+ if (to && to.startIdx > planned.endIdx) {
6175
+ const waiting = jumps.get(target.page) ?? [];
6176
+ waiting.push(c);
6177
+ jumps.set(target.page, waiting);
6178
+ continue;
6179
+ }
6180
+ const from = frameAt.get(c.fromFrameId);
6181
+ composedNotes.push({
6182
+ kind: 'continueTargetMissing',
6183
+ template: c.template,
6184
+ frame: c.frame,
6185
+ fenceIndex: fence.fenceIndex,
6186
+ target: target.page,
6187
+ configPath: resolvePageTemplate(templateSource, c.template)?.frames.find((f) => f.name === c.frame)?.configPath ?? '',
6188
+ ...(to ? { before: true } : {}),
6189
+ ...(from?.frame.sourceStart !== undefined ? { sourceStart: from.frame.sourceStart, sourceEnd: from.frame.sourceEnd } : {}),
6190
+ ...(from ? { pageIndex: from.page.index } : {}),
6191
+ });
6192
+ out.push({ ...c, rule: 'next-page' });
6193
+ }
6194
+ return out;
6195
+ };
6196
+ const overflowTemplateOf = (id) => resolvePageTemplate(templateSource, id)?.overflowTemplate;
6197
+ const firstPage = placed[0]?.page.index ?? cursor.pageIndex;
6198
+ let pending = route(continuations ?? []);
6199
+ for (let generation = 1; pending.length > 0; generation++) {
6200
+ const last = generation >= MAX_CONTINUATION_PAGES;
6201
+ let queue = pending;
6202
+ pending = [];
6203
+ while (queue.length > 0) {
6204
+ const target = continuationTarget(queue[0], overflowTemplateOf);
6205
+ const same = (c) => {
6206
+ const t = continuationTarget(c, overflowTemplateOf);
6207
+ return t.template === target.template && t.collapse === target.collapse;
6208
+ };
6209
+ const batch = queue.filter(same);
6210
+ queue = queue.filter((c) => !same(c));
6211
+ const spreadSheetT = resolvePageTemplate(templateSource, target.template, { spread: true });
6212
+ // A template with no frames takes nothing.
6213
+ if (!spreadSheetT || spreadSheetT.frames.length === 0)
6214
+ continue;
6215
+ // A fresh page after the last one laid.
6216
+ const start = cursor.pageIndex;
6217
+ do {
6218
+ advanceToNextColumn(doc, cursor, geomResolved, contentArea, pageWidthPx, pageHeightPx);
6219
+ } while (cursor.pageIndex === start);
6220
+ cursor.columnIndex = 0;
6221
+ const storiesOf = (receivers) => new Map([...receivers].map(([name, c]) => [name, c.story]));
6222
+ let sides;
6223
+ let notes;
6224
+ let more;
6225
+ let receivers;
6226
+ let laidTemplate;
6227
+ if (spreadSheetT.spread) {
6228
+ const assigned = assignReceivers(spreadSheetT, batch);
6229
+ receivers = assigned.receivers;
6230
+ queue = [...assigned.left, ...queue];
6231
+ laidTemplate = spreadSheetT;
6232
+ const out = laySpread(spreadSheetT, { stories: storiesOf(receivers), continuing: { receivers, generation, collapse: target.collapse, last } });
6233
+ sides = out.sides;
6234
+ notes = out.warnings;
6235
+ more = out.continuations;
6236
+ }
6237
+ else {
6238
+ const page = doc.pages[cursor.pageIndex];
6239
+ const { parity, template, areas } = singleOn(page, target.template);
6240
+ const assigned = assignReceivers(template, batch);
6241
+ receivers = assigned.receivers;
6242
+ queue = [...assigned.left, ...queue];
6243
+ laidTemplate = template;
6244
+ const out = layoutComposedPage({
6245
+ ...common(page, { id: template.id, spread: false, parity }),
6246
+ page,
6247
+ template,
6248
+ areas,
6249
+ stories: storiesOf(receivers),
6250
+ continuing: { receivers, generation, collapse: target.collapse, last },
6251
+ });
6252
+ sides = [{ page, composed: out.composed, blocks: out.blocks }];
6253
+ notes = out.warnings;
6254
+ more = out.continuations;
6255
+ }
6256
+ for (const s of sides)
6257
+ s.composed.continuationOf = firstPage;
6258
+ // On the last page allowed, a frame whose content would still go on
6259
+ // says so (it is cut at its foot).
6260
+ if (last) {
6261
+ const chained = new Set();
6262
+ const nextOf = (n) => {
6263
+ const f = laidTemplate.frames.find((x) => x.name === n);
6264
+ return f ? nextFrameOf(f) : undefined;
6265
+ };
6266
+ for (const name of receivers.keys()) {
6267
+ for (let n = name; n !== undefined && !chained.has(n); n = nextOf(n))
6268
+ chained.add(n);
6269
+ }
6270
+ notes = notes.map((w) => (w.kind === 'frameOverflow' && chained.has(w.frame)
6271
+ ? { kind: 'frameContinuationLimit', template: w.template, frame: w.frame, fenceIndex: w.fenceIndex, pages: generation, overflowMm: w.overflowMm, configPath: w.configPath, ...(w.sourceStart !== undefined ? { sourceStart: w.sourceStart, sourceEnd: w.sourceEnd } : {}), ...(w.pageIndex !== undefined ? { pageIndex: w.pageIndex } : {}) }
6272
+ : w));
6273
+ }
6274
+ commit(sides);
6275
+ indexFrames(sides);
6276
+ composedNotes.push(...notes);
6277
+ // Each frame that sent content on points to the frame that took it,
6278
+ // and its page's log says where it went.
6279
+ for (const c of receivers.values())
6280
+ link(c, frameElementId(fence.fenceIndex, c.slot, generation));
6281
+ if (more)
6282
+ pending.push(...route(more));
6029
6283
  }
6030
6284
  }
6031
6285
  for (const [head, story] of planned.stories) {
@@ -6037,6 +6291,39 @@ function placeDocumentPass(content, config, cache, options, hints) {
6037
6291
  pendingComposedBreak = 'page';
6038
6292
  flushPendingNumberingAtBoundary();
6039
6293
  };
6294
+ /**
6295
+ * The text panels of a comic page (#885): each story set in its panel's
6296
+ * frame (`layoutComicTextFrames`), its blocks floats of the page; the
6297
+ * stories laid are done for the main loop.
6298
+ */
6299
+ const placeComicText = (page, comic, plan) => {
6300
+ const frameless = calloutStyles.find((st) => st.id === FRAME_STYLE_ID);
6301
+ const out = layoutComicTextFrames({
6302
+ pass: framePass,
6303
+ resolved,
6304
+ page,
6305
+ comic,
6306
+ plan,
6307
+ styleOf: (frame) => frameStyleOf((frame.style ? resolved.calloutStyles.find((st) => st.id === frame.style) : undefined) ?? frameless, frame),
6308
+ dpi,
6309
+ emPx: bodyStyle.fontSizePx,
6310
+ bodyOffset,
6311
+ mirrored: mirroredOf(page),
6312
+ pushGapPx: wrapGapPx(resolved.layout.wrap?.gap, dpi, bodyStyle.fontSizePx, floatGapPx),
6313
+ locale: resolvedLocale(resolved),
6314
+ });
6315
+ const floats = (page.floats ??= []);
6316
+ for (const b of out.blocks) {
6317
+ floats.push(b);
6318
+ doc.blocks.push(b);
6319
+ }
6320
+ for (const [key, story] of plan.stories) {
6321
+ if (!out.laid.has(key))
6322
+ continue;
6323
+ for (let i = story.startIdx; i <= story.plan.endIdx; i++)
6324
+ composedLaid.add(i);
6325
+ }
6326
+ };
6040
6327
  /** The first block of the document that sets something: a heading there
6041
6328
  * opens the document's first page (`openerPages`, #749). */
6042
6329
  const firstFlowBlockIdx = contentBlocks.findIndex((b) => !isMarkerBlock(b) && b.type !== 'directive');
@@ -6321,6 +6608,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
6321
6608
  sourceOffset: bodyOffset,
6322
6609
  mirrorMargins: pageMirrored(resolved, page.index, pageIndexOffset, true),
6323
6610
  });
6611
+ const textPlan = comicTextPlans.get(blockIdx);
6612
+ if (textPlan)
6613
+ placeComicText(page, page.comic, textPlan);
6324
6614
  }
6325
6615
  pendingComicBreak = true;
6326
6616
  flushPendingNumberingAtBoundary();
@@ -8277,6 +8567,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
8277
8567
  page.pageLabel = info.label;
8278
8568
  page.pageNumberFormat = info.format;
8279
8569
  }
8570
+ // Continuation marks with a page number (#917): the page the rest goes
8571
+ // on on, or comes from, now that pages are numbered.
8572
+ fillPageMarks(doc.pages);
8280
8573
  const restarts = pageNumberSegments
8281
8574
  .filter((s, i) => i > 0 && s.startAt !== undefined && s.startPageIndex < doc.pages.length)
8282
8575
  .map((s) => s.startPageIndex);
@@ -8312,6 +8605,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
8312
8605
  // reverse of their visual order.
8313
8606
  if (doc.pages.some((p) => pageIsMirrored(p)))
8314
8607
  orientLinesToFrames(doc);
8608
+ // Figure labels (#860): where each one is painted, for the source map.
8609
+ stampFigureLabelRects(doc);
8315
8610
  doc.converged = true;
8316
8611
  doc.iterationCount = 1;
8317
8612
  if (joiningLetterSpacing.size > 0)
@@ -8323,8 +8618,13 @@ function placeDocumentPass(content, config, cache, options, hints) {
8323
8618
  const b = contentBlocks[idx];
8324
8619
  return b ? { start: b.sourceStart + bodyOffset, end: b.sourceEnd + bodyOffset } : undefined;
8325
8620
  });
8326
- if (shrunk.length > 0)
8327
- floatShrinkWarningsByDoc.set(doc, shrunk);
8621
+ // Pictures whose column holds them under half their print size (#901).
8622
+ const belowPrint = pictureBelowPrintSizeWarnings(doc, givenResources, (idx) => {
8623
+ const b = contentBlocks[idx];
8624
+ return b ? { start: b.sourceStart + bodyOffset, end: b.sourceEnd + bodyOffset } : undefined;
8625
+ });
8626
+ if (shrunk.length > 0 || belowPrint.length > 0)
8627
+ floatShrinkWarningsByDoc.set(doc, [...shrunk, ...belowPrint]);
8328
8628
  if (textWrapNotes.length > 0 || columnsNotes.length > 0)
8329
8629
  textWrapWarningsByDoc.set(doc, [...textWrapNotes, ...columnsNotes]);
8330
8630
  // Composed pages (#855): frames whose content does not fit.
@@ -8410,6 +8710,9 @@ export function* buildDocumentGen(content, config, cache, options) {
8410
8710
  loose.push(...afterTextWarnings(doc), ...(unplacedByDoc.get(doc) ?? []));
8411
8711
  // Design texts cut to fit their width (#628).
8412
8712
  loose.push(...designTruncationWarnings(doc));
8713
+ // Figure labels at anchors their picture lacks, anchors left without a
8714
+ // word in this locale (#860).
8715
+ loose.push(...figureLabelWarnings(doc, (b) => (b.sourceStart !== undefined && b.sourceEnd !== undefined ? { start: b.sourceStart, end: b.sourceEnd } : undefined)));
8413
8716
  // Comic panels whose cell cannot hold their picture's safe area (#556).
8414
8717
  for (const page of doc.pages) {
8415
8718
  if (page.comic)
@@ -8460,7 +8763,8 @@ const indexWarnings = new WeakMap();
8460
8763
  const joiningSpacingWarnings = new WeakMap();
8461
8764
  /** The `dropCap` warnings of the last pass, per document (#623). */
8462
8765
  const dropCapWarningsByDoc = new WeakMap();
8463
- /** The `floatShrunk` warnings of a build (#626), found by its last pass. */
8766
+ /** The `floatShrunk` (#626) and `pictureBelowPrintSize` (#901) warnings
8767
+ * of a build, found by its last pass. */
8464
8768
  const floatShrinkWarningsByDoc = new WeakMap();
8465
8769
  /** The `textWrap` warnings of a build (#627) and its `columnsTooNarrow`
8466
8770
  * ones (#634), found by its last pass. */
@@ -8494,6 +8798,49 @@ function afterTextWarnings(doc) {
8494
8798
  }
8495
8799
  return out;
8496
8800
  }
8801
+ /**
8802
+ * Write the page numbers into the continuation marks of composed pages'
8803
+ * frames (#917, `VDTDesignTextBlock.pageMark`): `{page}` becomes the label
8804
+ * of the page the frame's rest goes on on (`continuesTo`) or comes from
8805
+ * (`continuedFrom`). The mark was laid out with a guess of the number; its
8806
+ * lines are measured again with the true one and keep their alignment.
8807
+ */
8808
+ function fillPageMarks(pages) {
8809
+ const frames = new Map();
8810
+ for (const p of pages)
8811
+ for (const f of p.composed?.frames ?? [])
8812
+ if (!f.facingBlocks)
8813
+ frames.set(f.id, p);
8814
+ for (const p of pages) {
8815
+ for (const f of p.composed?.frames ?? []) {
8816
+ for (const id of f.blockIds) {
8817
+ const box = p.floats?.find((b) => b.id === id);
8818
+ for (const d of box?.designOverlay?.blocks ?? []) {
8819
+ if (d.kind !== 'text' || !d.pageMark)
8820
+ continue;
8821
+ const other = d.pageMark.page === 'to' ? f.continuesTo : f.continuedFrom;
8822
+ const at = other !== undefined ? frames.get(other) : undefined;
8823
+ if (!at)
8824
+ continue;
8825
+ const label = at.pageLabel ?? String(at.pageNumberValue ?? at.index + 1);
8826
+ const text = d.pageMark.text.split('{page}').join(label);
8827
+ const lh = d.lines.length > 0 ? d.bbox.height / d.lines.length : d.bbox.height;
8828
+ const first = d.lines[0];
8829
+ const slack0 = first ? d.bbox.width - first.width : 0;
8830
+ const align = !first || first.xOffset < 0.5 ? 'left' : Math.abs(first.xOffset - slack0) < 0.5 ? 'right' : 'center';
8831
+ const base = first ? first.baselineY - d.bbox.y : lh * 0.8;
8832
+ const { lines } = measureRichBlock([{ text, bold: false, italic: false }], d.fontString, d.fontString, d.fontString, d.fontString, d.bbox.width, lh, { textAlign: 'left' });
8833
+ if (lines.length === 0)
8834
+ continue;
8835
+ d.lines = lines.map((ln, i) => {
8836
+ const slack = Math.max(0, d.bbox.width - ln.bbox.width);
8837
+ return { text: ln.text, xOffset: align === 'right' ? slack : align === 'center' ? slack / 2 : 0, baselineY: d.bbox.y + base + i * lh, width: ln.bbox.width };
8838
+ });
8839
+ }
8840
+ }
8841
+ }
8842
+ }
8843
+ }
8497
8844
  /** `doc` with the page of each of its index marks (`doc.indexMarks`). */
8498
8845
  function withIndexMarks(doc, content) {
8499
8846
  const { content: body, contentOffset } = extractFrontmatter(content.markdown);