postext 1.22.2 → 1.23.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 (288) hide show
  1. package/dist/__tests__/bundle.test.js +1 -1
  2. package/dist/__tests__/codeBlocks.test.d.ts +2 -0
  3. package/dist/__tests__/codeBlocks.test.d.ts.map +1 -0
  4. package/dist/__tests__/codeBlocks.test.js +550 -0
  5. package/dist/__tests__/codeBlocks.test.js.map +1 -0
  6. package/dist/__tests__/exports.test.js +19 -0
  7. package/dist/__tests__/exports.test.js.map +1 -1
  8. package/dist/__tests__/hardBreaks.test.d.ts +2 -0
  9. package/dist/__tests__/hardBreaks.test.d.ts.map +1 -0
  10. package/dist/__tests__/hardBreaks.test.js +226 -0
  11. package/dist/__tests__/hardBreaks.test.js.map +1 -0
  12. package/dist/__tests__/headingInlineMarks.test.js +1 -1
  13. package/dist/__tests__/headingLetterSpacing.test.js +1 -1
  14. package/dist/__tests__/headingLetterSpacing.test.js.map +1 -1
  15. package/dist/__tests__/lineNumbers.test.d.ts +2 -0
  16. package/dist/__tests__/lineNumbers.test.d.ts.map +1 -0
  17. package/dist/__tests__/lineNumbers.test.js +304 -0
  18. package/dist/__tests__/lineNumbers.test.js.map +1 -0
  19. package/dist/__tests__/mathSizeMigration.test.js +2 -2
  20. package/dist/__tests__/paragraphDropCap.test.d.ts +2 -0
  21. package/dist/__tests__/paragraphDropCap.test.d.ts.map +1 -0
  22. package/dist/__tests__/paragraphDropCap.test.js +442 -0
  23. package/dist/__tests__/paragraphDropCap.test.js.map +1 -0
  24. package/dist/__tests__/paragraphStyleIndent.test.js +26 -2
  25. package/dist/__tests__/paragraphStyleIndent.test.js.map +1 -1
  26. package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +1 -1
  27. package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +1 -1
  28. package/dist/__tests__/pipeline/colonListRoom.test.js +1 -1
  29. package/dist/__tests__/pipeline/compoundBreaks.test.js +1 -1
  30. package/dist/__tests__/pipeline/dashBreaks.test.js +1 -1
  31. package/dist/__tests__/pipeline/headingSplitOrphans.test.js +1 -1
  32. package/dist/__tests__/pipeline/inlineFigureSpace.test.js +1 -1
  33. package/dist/__tests__/pipeline/paragraphContainerTail.test.js +1 -1
  34. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +7 -1
  35. package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
  36. package/dist/__tests__/pipeline/raggedOptimal.test.js +1 -1
  37. package/dist/__tests__/tabStops.test.d.ts +2 -0
  38. package/dist/__tests__/tabStops.test.d.ts.map +1 -0
  39. package/dist/__tests__/tabStops.test.js +394 -0
  40. package/dist/__tests__/tabStops.test.js.map +1 -0
  41. package/dist/__tests__/verseLines.test.d.ts +2 -0
  42. package/dist/__tests__/verseLines.test.d.ts.map +1 -0
  43. package/dist/__tests__/verseLines.test.js +375 -0
  44. package/dist/__tests__/verseLines.test.js.map +1 -0
  45. package/dist/bundle/configVersion.d.ts +77 -7
  46. package/dist/bundle/configVersion.d.ts.map +1 -1
  47. package/dist/bundle/configVersion.js +239 -5
  48. package/dist/bundle/configVersion.js.map +1 -1
  49. package/dist/bundle/index.d.ts +1 -1
  50. package/dist/bundle/index.d.ts.map +1 -1
  51. package/dist/bundle/index.js +1 -1
  52. package/dist/bundle/index.js.map +1 -1
  53. package/dist/canvas-backend/blockRender.d.ts.map +1 -1
  54. package/dist/canvas-backend/blockRender.js +39 -5
  55. package/dist/canvas-backend/blockRender.js.map +1 -1
  56. package/dist/canvas-backend/index.d.ts.map +1 -1
  57. package/dist/canvas-backend/index.js +3 -0
  58. package/dist/canvas-backend/index.js.map +1 -1
  59. package/dist/cjkMarks.js +1 -1
  60. package/dist/cjkMarks.js.map +1 -1
  61. package/dist/code/highlight.d.ts +34 -0
  62. package/dist/code/highlight.d.ts.map +1 -0
  63. package/dist/code/highlight.js +406 -0
  64. package/dist/code/highlight.js.map +1 -0
  65. package/dist/configWarnings.d.ts.map +1 -1
  66. package/dist/configWarnings.js +192 -3
  67. package/dist/configWarnings.js.map +1 -1
  68. package/dist/defaults/bodyText.d.ts.map +1 -1
  69. package/dist/defaults/bodyText.js +25 -0
  70. package/dist/defaults/bodyText.js.map +1 -1
  71. package/dist/defaults/calloutStyles.d.ts.map +1 -1
  72. package/dist/defaults/calloutStyles.js +7 -0
  73. package/dist/defaults/calloutStyles.js.map +1 -1
  74. package/dist/defaults/codeStyle.d.ts +29 -0
  75. package/dist/defaults/codeStyle.d.ts.map +1 -0
  76. package/dist/defaults/codeStyle.js +284 -0
  77. package/dist/defaults/codeStyle.js.map +1 -0
  78. package/dist/defaults/dropCap.d.ts +49 -0
  79. package/dist/defaults/dropCap.d.ts.map +1 -0
  80. package/dist/defaults/dropCap.js +66 -0
  81. package/dist/defaults/dropCap.js.map +1 -0
  82. package/dist/defaults/headings.d.ts +1 -1
  83. package/dist/defaults/headings.d.ts.map +1 -1
  84. package/dist/defaults/headings.js +11 -0
  85. package/dist/defaults/headings.js.map +1 -1
  86. package/dist/defaults/index.d.ts +3 -0
  87. package/dist/defaults/index.d.ts.map +1 -1
  88. package/dist/defaults/index.js +19 -0
  89. package/dist/defaults/index.js.map +1 -1
  90. package/dist/defaults/lineNumbers.d.ts +19 -0
  91. package/dist/defaults/lineNumbers.d.ts.map +1 -0
  92. package/dist/defaults/lineNumbers.js +101 -0
  93. package/dist/defaults/lineNumbers.js.map +1 -0
  94. package/dist/defaults/paragraphStyles.d.ts.map +1 -1
  95. package/dist/defaults/paragraphStyles.js +20 -0
  96. package/dist/defaults/paragraphStyles.js.map +1 -1
  97. package/dist/defaults/tabStops.d.ts +29 -0
  98. package/dist/defaults/tabStops.d.ts.map +1 -0
  99. package/dist/defaults/tabStops.js +94 -0
  100. package/dist/defaults/tabStops.js.map +1 -0
  101. package/dist/defaults/verse.d.ts +10 -0
  102. package/dist/defaults/verse.d.ts.map +1 -0
  103. package/dist/defaults/verse.js +52 -0
  104. package/dist/defaults/verse.js.map +1 -0
  105. package/dist/fonts/usage.d.ts +7 -0
  106. package/dist/fonts/usage.d.ts.map +1 -1
  107. package/dist/fonts/usage.js +71 -0
  108. package/dist/fonts/usage.js.map +1 -1
  109. package/dist/html-backend.d.ts.map +1 -1
  110. package/dist/html-backend.js +85 -11
  111. package/dist/html-backend.js.map +1 -1
  112. package/dist/index.d.ts +8 -5
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +4 -2
  115. package/dist/index.js.map +1 -1
  116. package/dist/measure/bidiLines.js +2 -2
  117. package/dist/measure/bidiLines.js.map +1 -1
  118. package/dist/measure/cache.js +2 -2
  119. package/dist/measure/cache.js.map +1 -1
  120. package/dist/measure/cjkCompose.d.ts.map +1 -1
  121. package/dist/measure/cjkCompose.js +5 -5
  122. package/dist/measure/cjkCompose.js.map +1 -1
  123. package/dist/measure/hardBreaks.d.ts +46 -0
  124. package/dist/measure/hardBreaks.d.ts.map +1 -0
  125. package/dist/measure/hardBreaks.js +216 -0
  126. package/dist/measure/hardBreaks.js.map +1 -0
  127. package/dist/measure/index.d.ts +1 -0
  128. package/dist/measure/index.d.ts.map +1 -1
  129. package/dist/measure/index.js +1 -0
  130. package/dist/measure/index.js.map +1 -1
  131. package/dist/measure/leader.d.ts +20 -0
  132. package/dist/measure/leader.d.ts.map +1 -0
  133. package/dist/measure/leader.js +46 -0
  134. package/dist/measure/leader.js.map +1 -0
  135. package/dist/measure/links.js +1 -1
  136. package/dist/measure/links.js.map +1 -1
  137. package/dist/measure/plain.d.ts.map +1 -1
  138. package/dist/measure/plain.js +18 -25
  139. package/dist/measure/plain.js.map +1 -1
  140. package/dist/measure/rich.d.ts +19 -0
  141. package/dist/measure/rich.d.ts.map +1 -1
  142. package/dist/measure/rich.js +211 -38
  143. package/dist/measure/rich.js.map +1 -1
  144. package/dist/measure/tabs.d.ts +103 -0
  145. package/dist/measure/tabs.d.ts.map +1 -0
  146. package/dist/measure/tabs.js +123 -0
  147. package/dist/measure/tabs.js.map +1 -0
  148. package/dist/measure/types.d.ts +26 -2
  149. package/dist/measure/types.d.ts.map +1 -1
  150. package/dist/measure/types.js +23 -3
  151. package/dist/measure/types.js.map +1 -1
  152. package/dist/parse/blockParser.d.ts +13 -6
  153. package/dist/parse/blockParser.d.ts.map +1 -1
  154. package/dist/parse/blockParser.js +457 -87
  155. package/dist/parse/blockParser.js.map +1 -1
  156. package/dist/parse/codeFence.d.ts +18 -0
  157. package/dist/parse/codeFence.d.ts.map +1 -0
  158. package/dist/parse/codeFence.js +52 -0
  159. package/dist/parse/codeFence.js.map +1 -0
  160. package/dist/parse/index.d.ts +1 -1
  161. package/dist/parse/index.d.ts.map +1 -1
  162. package/dist/parse/index.js.map +1 -1
  163. package/dist/parse/indexMarks.d.ts +3 -1
  164. package/dist/parse/indexMarks.d.ts.map +1 -1
  165. package/dist/parse/indexMarks.js +22 -2
  166. package/dist/parse/indexMarks.js.map +1 -1
  167. package/dist/parse/inlineFormatting.d.ts +50 -0
  168. package/dist/parse/inlineFormatting.d.ts.map +1 -1
  169. package/dist/parse/inlineFormatting.js +94 -0
  170. package/dist/parse/inlineFormatting.js.map +1 -1
  171. package/dist/parse/sourceMapping.d.ts.map +1 -1
  172. package/dist/parse/sourceMapping.js +65 -1
  173. package/dist/parse/sourceMapping.js.map +1 -1
  174. package/dist/parse/types.d.ts +144 -9
  175. package/dist/parse/types.d.ts.map +1 -1
  176. package/dist/pipeline/build.d.ts.map +1 -1
  177. package/dist/pipeline/build.js +143 -18
  178. package/dist/pipeline/build.js.map +1 -1
  179. package/dist/pipeline/buildBlockKind.d.ts.map +1 -1
  180. package/dist/pipeline/buildBlockKind.js +60 -1
  181. package/dist/pipeline/buildBlockKind.js.map +1 -1
  182. package/dist/pipeline/buildHelpers.d.ts +3 -0
  183. package/dist/pipeline/buildHelpers.d.ts.map +1 -1
  184. package/dist/pipeline/buildHelpers.js +7 -2
  185. package/dist/pipeline/buildHelpers.js.map +1 -1
  186. package/dist/pipeline/calloutLayout.d.ts.map +1 -1
  187. package/dist/pipeline/calloutLayout.js +33 -2
  188. package/dist/pipeline/calloutLayout.js.map +1 -1
  189. package/dist/pipeline/chips.d.ts.map +1 -1
  190. package/dist/pipeline/chips.js +4 -0
  191. package/dist/pipeline/chips.js.map +1 -1
  192. package/dist/pipeline/codeBlocks.d.ts +38 -0
  193. package/dist/pipeline/codeBlocks.d.ts.map +1 -0
  194. package/dist/pipeline/codeBlocks.js +146 -0
  195. package/dist/pipeline/codeBlocks.js.map +1 -0
  196. package/dist/pipeline/codeInline.d.ts +22 -0
  197. package/dist/pipeline/codeInline.d.ts.map +1 -0
  198. package/dist/pipeline/codeInline.js +93 -0
  199. package/dist/pipeline/codeInline.js.map +1 -0
  200. package/dist/pipeline/codeLines.d.ts +91 -0
  201. package/dist/pipeline/codeLines.d.ts.map +1 -0
  202. package/dist/pipeline/codeLines.js +399 -0
  203. package/dist/pipeline/codeLines.js.map +1 -0
  204. package/dist/pipeline/config.d.ts.map +1 -1
  205. package/dist/pipeline/config.js +8 -1
  206. package/dist/pipeline/config.js.map +1 -1
  207. package/dist/pipeline/contentWarnings.d.ts +9 -0
  208. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  209. package/dist/pipeline/contentWarnings.js +60 -2
  210. package/dist/pipeline/contentWarnings.js.map +1 -1
  211. package/dist/pipeline/continuation.d.ts.map +1 -1
  212. package/dist/pipeline/continuation.js +9 -2
  213. package/dist/pipeline/continuation.js.map +1 -1
  214. package/dist/pipeline/dropCap.d.ts +128 -0
  215. package/dist/pipeline/dropCap.d.ts.map +1 -0
  216. package/dist/pipeline/dropCap.js +412 -0
  217. package/dist/pipeline/dropCap.js.map +1 -0
  218. package/dist/pipeline/hardBreaks.d.ts +19 -0
  219. package/dist/pipeline/hardBreaks.d.ts.map +1 -0
  220. package/dist/pipeline/hardBreaks.js +36 -0
  221. package/dist/pipeline/hardBreaks.js.map +1 -0
  222. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  223. package/dist/pipeline/headerFooter.js +4 -1
  224. package/dist/pipeline/headerFooter.js.map +1 -1
  225. package/dist/pipeline/lineNumbers.d.ts +37 -0
  226. package/dist/pipeline/lineNumbers.d.ts.map +1 -0
  227. package/dist/pipeline/lineNumbers.js +441 -0
  228. package/dist/pipeline/lineNumbers.js.map +1 -0
  229. package/dist/pipeline/measureContentBlock.d.ts +31 -5
  230. package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
  231. package/dist/pipeline/measureContentBlock.js +274 -68
  232. package/dist/pipeline/measureContentBlock.js.map +1 -1
  233. package/dist/pipeline/partPalette.d.ts +1 -1
  234. package/dist/pipeline/partPalette.d.ts.map +1 -1
  235. package/dist/pipeline/partPalette.js +24 -2
  236. package/dist/pipeline/partPalette.js.map +1 -1
  237. package/dist/pipeline/styles.d.ts +18 -2
  238. package/dist/pipeline/styles.d.ts.map +1 -1
  239. package/dist/pipeline/styles.js +15 -3
  240. package/dist/pipeline/styles.js.map +1 -1
  241. package/dist/pipeline/tabs.d.ts +35 -0
  242. package/dist/pipeline/tabs.d.ts.map +1 -0
  243. package/dist/pipeline/tabs.js +99 -0
  244. package/dist/pipeline/tabs.js.map +1 -0
  245. package/dist/pipeline/toc.d.ts.map +1 -1
  246. package/dist/pipeline/toc.js +3 -38
  247. package/dist/pipeline/toc.js.map +1 -1
  248. package/dist/pipeline/verseLines.d.ts +78 -0
  249. package/dist/pipeline/verseLines.d.ts.map +1 -0
  250. package/dist/pipeline/verseLines.js +245 -0
  251. package/dist/pipeline/verseLines.js.map +1 -0
  252. package/dist/preflight.d.ts.map +1 -1
  253. package/dist/preflight.js +1 -0
  254. package/dist/preflight.js.map +1 -1
  255. package/dist/types.d.ts +560 -4
  256. package/dist/types.d.ts.map +1 -1
  257. package/dist/vdt.d.ts +240 -7
  258. package/dist/vdt.d.ts.map +1 -1
  259. package/dist/vdt.js +6 -0
  260. package/dist/vdt.js.map +1 -1
  261. package/dist/word/docxRead.d.ts.map +1 -1
  262. package/dist/word/docxRead.js +15 -0
  263. package/dist/word/docxRead.js.map +1 -1
  264. package/dist/word/inline.d.ts +6 -1
  265. package/dist/word/inline.d.ts.map +1 -1
  266. package/dist/word/inline.js +35 -1
  267. package/dist/word/inline.js.map +1 -1
  268. package/dist/word/model.d.ts +14 -0
  269. package/dist/word/model.d.ts.map +1 -1
  270. package/dist/word/model.js.map +1 -1
  271. package/dist/word/template.d.ts +12 -3
  272. package/dist/word/template.d.ts.map +1 -1
  273. package/dist/word/template.js +13 -3
  274. package/dist/word/template.js.map +1 -1
  275. package/dist/word/toDocx.d.ts.map +1 -1
  276. package/dist/word/toDocx.js +52 -8
  277. package/dist/word/toDocx.js.map +1 -1
  278. package/dist/word/toMarkdown.d.ts +5 -1
  279. package/dist/word/toMarkdown.d.ts.map +1 -1
  280. package/dist/word/toMarkdown.js +117 -6
  281. package/dist/word/toMarkdown.js.map +1 -1
  282. package/dist/word/word.test.js +124 -2
  283. package/dist/word/word.test.js.map +1 -1
  284. package/dist/worker/fonts.d.ts +2 -1
  285. package/dist/worker/fonts.d.ts.map +1 -1
  286. package/dist/worker/fonts.js +6 -2
  287. package/dist/worker/fonts.js.map +1 -1
  288. package/package.json +1 -1
@@ -13,6 +13,7 @@ import { getMeasureRegion, getMeasureUprightDigits, getMeasureWritingMode, setMe
13
13
  import { stampCentralBaselines } from './verticalMetrics';
14
14
  import { orientLinesToFrames } from './mirrorFrame';
15
15
  import { verseMarginTopPx } from './buildBlockKind';
16
+ import { verseLinesSettings, versesLineByLine } from './verseLines';
16
17
  import { resolveAllConfig, computeBaselineGrid, resolvedDirection, resolvedLocale } from './config';
17
18
  import { asciiDigits } from '../arabicNumerals';
18
19
  import { getMeasureDirection, setMeasureDirection, shiftLineX } from '../measure/bidiLines';
@@ -45,17 +46,22 @@ import { enumerateCurrentPageSlots, measureFloatBand, floatGapAbove, measureSide
45
46
  import { computeHeadingContext, computeResourceNumberingState, } from './resourceNumbering';
46
47
  import { documentLocale, effectiveResourceTypes } from '../defaults/resourceTypes';
47
48
  import { resolveCalloutStylesConfig } from '../defaults/calloutStyles';
49
+ import { codeBoxStyle, codeParseOptions, wrapCodeBlocks } from './codeBlocks';
50
+ import { buildCodeLineNumbers, codeOverflowWarnings } from './codeLines';
48
51
  import { pickTableStyle } from '../defaults/tableStyle';
52
+ import { buildLineNumbers, lineNumberWarnings } from './lineNumbers';
49
53
  import { buildHeadersAndFooters, defaultOpenerTitle, headingDesignBoxes, measureDefaultOpenerHeight, measureHeadingDesign } from './headerFooter';
50
54
  import { flowColorValues } from './partPalette';
51
55
  import { chapterNumberCounter, leadingBoldText } from './placeholders';
52
56
  import { proposeBalanceLines, collectColumnGaps, firstDivergentColumn, gapLinesIn, boxRoomIn, boxLeverKeys, pageSegments, MAX_BALANCING_PASSES, MAX_BALANCING_PASSES_PER_DOCUMENT, balanceKey, candidateKey, flexFigureKey, flexFloatKey } from './columnBalancing';
53
57
  import { applyBandCap, uncapBand, columnBottom, bandCapLines, bandTop, bareBandTop, resolveBandCapsGen, drainPasses, resolveTrailingCapsGen, bandCapLinesAroundZone, } from './bandCaps';
54
58
  import { raggedLooseLines } from './raggedLines';
55
- import { cjkLooseLineWarnings, collectContentWarnings, joiningLetterSpacingWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
59
+ import { cjkLooseLineWarnings, collectContentWarnings, dropCapWarnings, joiningLetterSpacingWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
60
+ import { dropCapSinkAfter } from './dropCap';
56
61
  import { mostlyJoiningScript } from '../measure/joining';
57
62
  import { annotateDocument } from '../cjkMarks';
58
63
  import { annotateArabicMarks } from '../arabicMarks';
64
+ import { literalBreaksFor } from './hardBreaks';
59
65
  import { tashkilFor } from './tashkil';
60
66
  import { overlineEmphasis } from '../emphasisOverline';
61
67
  import { withBookTitleBrackets } from './annotations';
@@ -142,6 +148,12 @@ function measureLooseParagraph(rawBlock, blockIdx, columnWidth, ctx, styleOverri
142
148
  const base = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, { styleOverride });
143
149
  if (!base)
144
150
  return null;
151
+ // A paragraph with a tab at a stop (#622) is set line by line, as
152
+ // measured: neither looseness nor tracking moves its stops' text.
153
+ if (base.measured.lines.some((l) => l.tabbed)) {
154
+ looseOutcome.set(blockIdx, null);
155
+ return base;
156
+ }
145
157
  const target = base.measured.lines.length + extraLines;
146
158
  // The breaker's optimum, before any runt fix (a looseness of 0 skips it).
147
159
  const optimum = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, { styleOverride, looseness: 0 });
@@ -320,7 +332,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
320
332
  // bibliography takes `:::bibliography`'s place or follows the text.
321
333
  // Arabic vowel marks out of the text when `bodyText.tashkil` says so
322
334
  // (#376), for the outline and the layout alike.
323
- const parsedBody = numberTitlesFor(headingMarksFor(tashkilFor(parseMarkdownMemo(markdownBody), resolved.bodyText.tashkil), resolved), resolved);
335
+ // A configuration stored before #620 prints the backslashes of forced
336
+ // line breaks (`bodyText.hardLineBreaks: false`).
337
+ // Code fences (#624) as `codeStyle` reads them: a configuration stored
338
+ // before #624 reads their lines as Markdown (`codeStyle.blocks: false`).
339
+ const parsedBody = numberTitlesFor(headingMarksFor(tashkilFor(literalBreaksFor(parseMarkdownMemo(markdownBody, codeParseOptions(resolved)), resolved), resolved.bodyText.tashkil), resolved), resolved);
324
340
  const citationContext = content.citations
325
341
  ?? (needsCitationContext(parsedBody, frontmatterMeta) ? bookCitationContexts([{ metadata: frontmatterMeta, blocks: parsedBody }], content.resources)[0] : undefined);
326
342
  const citationsApplied = citationContext
@@ -375,7 +391,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
375
391
  // page where it stands (#535): each goes in a frameless page-span box,
376
392
  // set by the span-block path like any other.
377
393
  const spanEmbeds = wrapPageSpanEmbeds(numberedBlocks, content.resources ?? [], effectiveResourceTypes(config, content.resources ?? []));
378
- const contentBlocks = spanEmbeds.blocks;
394
+ // Code listings (#624): each in a box of the code style, which the
395
+ // callout machinery places and splits.
396
+ const codeBoxes = wrapCodeBlocks(spanEmbeds.blocks, resolved);
397
+ const contentBlocks = codeBoxes.blocks;
379
398
  if (indexExpanded.warnings.length > 0)
380
399
  indexWarnings.set(doc, indexExpanded.warnings);
381
400
  const isNumbered = (b) => headingIsNumbered(b, resolved);
@@ -412,9 +431,17 @@ function placeDocumentPass(content, config, cache, options, hints) {
412
431
  // The callout styles, with the frameless box of the page-span embeds
413
432
  // when there are any: the float gap (a line) above and below it, as an
414
433
  // inline resource keeps in a column.
415
- const calloutStyles = spanEmbeds.wrapped
416
- ? [...resolved.calloutStyles, ...resolveCalloutStylesConfig([spanEmbedStyle(bodyStyle.lineHeightPx, resolved.layout.inlineResourceGap !== 'above')], resolved.bodyText, resolved.headings, resolved.unorderedLists, config?.locale)]
434
+ // The listings' box (#624) likewise.
435
+ const syntheticBoxes = [
436
+ ...(spanEmbeds.wrapped ? [spanEmbedStyle(bodyStyle.lineHeightPx, resolved.layout.inlineResourceGap !== 'above')] : []),
437
+ ...(codeBoxes.wrapped ? [codeBoxStyle(resolved)] : []),
438
+ ];
439
+ const calloutStyles = syntheticBoxes.length > 0
440
+ ? [...resolved.calloutStyles, ...resolveCalloutStylesConfig(syntheticBoxes, resolved.bodyText, resolved.headings, resolved.unorderedLists, config?.locale)]
417
441
  : resolved.calloutStyles;
442
+ // A box nested in another looks its style up in the configuration it is
443
+ // laid out with: the listings' box among them.
444
+ const calloutResolved = codeBoxes.wrapped ? { ...resolved, calloutStyles } : resolved;
418
445
  const blockquoteStyle = resolveBlockquoteStyle(resolved);
419
446
  const listLevelIndentsPx = computeLevelIndentsPx(resolved, bodyStyle.fontSizePx);
420
447
  const orderedMetrics = computeOrderedListRunMetrics(contentBlocks, resolved, bodyStyle.fontSizePx);
@@ -1898,6 +1925,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
1898
1925
  /** Blocks whose style tracks text of a joining script, which is set
1899
1926
  * untracked (see `BlockMeasureContext.joiningLetterSpacing`). */
1900
1927
  const joiningLetterSpacing = new Set();
1928
+ /** Paragraphs whose drop cap could not be set as configured (#623). */
1929
+ const dropCapNotes = new Map();
1901
1930
  // Everything per-block measurement needs that is constant for this pass.
1902
1931
  const measureCtx = {
1903
1932
  resolved,
@@ -1923,6 +1952,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
1923
1952
  floatedIds,
1924
1953
  leftFlow: calloutsOutOfFlow,
1925
1954
  joiningLetterSpacing,
1955
+ dropCapNotes,
1926
1956
  ...(footnoteNumbering.numbers.size > 0 ? { footnoteNumbers: footnoteNumbering.numbers } : {}),
1927
1957
  ...(anchorRefContext ? { anchorRefs: anchorRefContext } : {}),
1928
1958
  };
@@ -3081,7 +3111,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
3081
3111
  childStartIdx: startIdx + 1 + from.child,
3082
3112
  width,
3083
3113
  ctx: measureCtx,
3084
- resolved,
3114
+ resolved: calloutResolved,
3085
3115
  containerId: plan.containerId,
3086
3116
  frameId,
3087
3117
  nextChildId: () => `${frameId}-c${n++}`,
@@ -3154,7 +3184,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
3154
3184
  return null;
3155
3185
  // A head that goes on also carries the style's continuation marker.
3156
3186
  const tail = tailOf(full) + full.continuesMarkerPx;
3157
- /** Candidate cuts with the head's content bottom. */
3187
+ /** Candidate cuts with the head's content bottom. A `soft` cut parts a
3188
+ * wrapped code line from its continuation (#624): taken only when no
3189
+ * other cut fits. */
3158
3190
  const candidates = [];
3159
3191
  /** Collect the cuts among `units` (one box's items; `below` = the tails
3160
3192
  * of the nested boxes around them), each side of a cut holding enough
@@ -3197,6 +3229,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
3197
3229
  candidates.push({
3198
3230
  cut: { child: k, line: lineBase + l, widths: k === from.child ? cutWidths(from, width) : [{ fromLine: 0, width }] },
3199
3231
  bottom: line.bbox.y + line.bbox.height + below,
3232
+ ...(c.lines[l]?.codeLine?.continued ? { soft: true } : {}),
3200
3233
  });
3201
3234
  }
3202
3235
  }
@@ -3223,6 +3256,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
3223
3256
  const viable = candidates
3224
3257
  .filter((c) => !insideGroup(c.cut))
3225
3258
  .sort((a, b) => b.bottom - a.bottom);
3259
+ // A wrapped code line stays with its continuations when a cut
3260
+ // elsewhere fits (#624).
3261
+ viable.sort((a, b) => (a.soft ? 1 : 0) - (b.soft ? 1 : 0));
3226
3262
  for (const c of viable) {
3227
3263
  if (c.bottom + tail > roomPx + 0.01)
3228
3264
  continue;
@@ -4881,6 +4917,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
4881
4917
  if (!measuredBlock)
4882
4918
  continue;
4883
4919
  const { kind, contentBlock, measured, prefixLen, absoluteSourceMap, mathDisplayRender } = measuredBlock;
4920
+ /** The drop cap opening the paragraph (#623): stamped on the fragment
4921
+ * that holds its first line. A paragraph broken again for a column of
4922
+ * another width takes the one of that setting. */
4923
+ let capInfo = measuredBlock.dropCap;
4884
4924
  /** Tracking the block's lines are set with; a paragraph moved whole into
4885
4925
  * a column of another width is measured again, and may change it. */
4886
4926
  let { letterSpacingPx } = measuredBlock;
@@ -4965,6 +5005,32 @@ function placeDocumentPass(content, config, cache, options, hints) {
4965
5005
  continue;
4966
5006
  }
4967
5007
  const finalizeListItem = (blk, isFirstPart) => {
5008
+ // The drop cap (#623) stands on the baseline of the line it sinks to,
5009
+ // beside the first fragment only.
5010
+ const capLine = blk.lines[0];
5011
+ if (isFirstPart && capInfo && capLine) {
5012
+ const cap = capInfo;
5013
+ blk.dropCap = {
5014
+ text: cap.text,
5015
+ fontString: cap.fontString,
5016
+ color: cap.color,
5017
+ x: blk.bbox.x + cap.x,
5018
+ baselineY: capLine.baseline + (cap.sink - 1) * style.lineHeightPx,
5019
+ width: cap.width,
5020
+ fontSizePx: cap.fontSizePx,
5021
+ lines: cap.lines,
5022
+ sink: cap.sink,
5023
+ ...(cap.sourceStart !== undefined ? { sourceStart: cap.sourceStart, sourceEnd: cap.sourceEnd } : {}),
5024
+ plainStart: cap.plainStart,
5025
+ plainEnd: cap.plainEnd,
5026
+ word: cap.word,
5027
+ wordRest: cap.wordRest,
5028
+ ...(cap.hang ? { hang: { text: cap.hang.text, fontString: cap.hang.fontString, x: blk.bbox.x + cap.hang.x, baselineY: capLine.baseline, width: cap.hang.width } } : {}),
5029
+ };
5030
+ // The block's source range starts at the initial.
5031
+ if (cap.sourceStart !== undefined && (blk.sourceStart === undefined || cap.sourceStart < blk.sourceStart))
5032
+ blk.sourceStart = cap.sourceStart;
5033
+ }
4968
5034
  if (!listBullet)
4969
5035
  return;
4970
5036
  blk.listDepth = listDepth;
@@ -5035,7 +5101,12 @@ function placeDocumentPass(content, config, cache, options, hints) {
5035
5101
  (vdtType === 'paragraph' && isContainerTail && paragraphContainer?.snapToGrid !== false) ||
5036
5102
  // A poem, like a container of one block: its own leading and the
5037
5103
  // margins of its style are off the grid, the text after it is not.
5038
- (vdtType === 'paragraph' && rawBlock.verse !== undefined && verseSnaps(rawBlock.verse.attrs.style, resolved)) ||
5104
+ // A stanza between two of a poem in the line layout snaps too, so the
5105
+ // space between stanzas is whole grid lines (#620); one of a poem set
5106
+ // as single hemistichs (a configuration stored before #620) does not,
5107
+ // as the one block postext 1.22 set it in did not.
5108
+ (vdtType === 'paragraph' && rawBlock.verse !== undefined && verseSnaps(rawBlock.verse.attrs.style, resolved)
5109
+ && (rawBlock.verse.stanza === undefined || rawBlock.verse.stanza.last || versesLineByLine(rawBlock, resolved))) ||
5039
5110
  vdtType === 'mathDisplay');
5040
5111
  // Place block, splitting across columns/pages if needed.
5041
5112
  // List items may split too — orphan/widow protection per-list is gated by
@@ -5109,6 +5180,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
5109
5180
  // Nothing placed yet: the whole block, for this column.
5110
5181
  const again = measureContentBlock(rawBlock, blockIdx, curCol.bbox.width, blockMeasureCtx, { styleOverride });
5111
5182
  if (again && again.measured.lines.length > 0) {
5183
+ capInfo = again.dropCap;
5112
5184
  brokenForWidth = curCol.bbox.width;
5113
5185
  laterWidths = [];
5114
5186
  letterSpacingPx = again.letterSpacingPx;
@@ -5244,6 +5316,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
5244
5316
  balancing = { levers: [vdtType === 'heading' ? 'heading' : 'afterFloat'], spaceAbove: extraPx };
5245
5317
  }
5246
5318
  }
5319
+ // A raised initial (#623) keeps its rise clear above the paragraph,
5320
+ // in whole grid lines.
5321
+ if (partIndex === 0 && capInfo && capInfo.rise > 0.5) {
5322
+ spacingBefore += Math.ceil((capInfo.rise - 0.5) / baselineGrid) * baselineGrid;
5323
+ }
5247
5324
  // A loose paragraph's extra line is balancing height too (it lands
5248
5325
  // whole in this column — loose candidates are never split parts).
5249
5326
  const looseTracking = looseOutcome.get(blockIdx);
@@ -5281,6 +5358,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
5281
5358
  // actual content bottom so the reserved block height (and the
5282
5359
  // subsequent marginBottom + grid snap) starts from there.
5283
5360
  let effectiveRemainHeight = totalRemainHeight;
5361
+ // A paragraph shorter than its drop cap sinks (#623, `'reserve'`):
5362
+ // the block keeps the room under the initial.
5363
+ if (partIndex === 0 && capInfo && capInfo.minLines > remainingLines.length) {
5364
+ effectiveRemainHeight = capInfo.minLines * style.lineHeightPx;
5365
+ }
5284
5366
  /** How far the lines of a 行取り heading go down its band (#424). */
5285
5367
  let lineSpanShift = 0;
5286
5368
  if (lineSpan !== undefined && partIndex === 0) {
@@ -5557,9 +5639,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
5557
5639
  ? Math.ceil((naturalBottom - 0.01) / baselineGrid) * baselineGrid
5558
5640
  : naturalBottom;
5559
5641
  const remainAfterHeading = curCol.bbox.height - snappedBottom;
5560
- const minLinesNeeded = resolved.bodyText.avoidWidows
5561
- ? Math.max(1, resolved.bodyText.widowMinLines)
5562
- : 1;
5642
+ // A drop cap opening the text (#623) needs its lines under it.
5643
+ const minLinesNeeded = Math.max(resolved.bodyText.avoidWidows ? Math.max(1, resolved.bodyText.widowMinLines) : 1, dropCapSinkAfter(blockIdx, blockMeasureCtx));
5563
5644
  // A `:::space` between the heading and its text needs room too.
5564
5645
  const minSpaceAfter = minLinesNeeded * bodyStyle.lineHeightPx
5565
5646
  + spaceLinesAfter(contentBlocks, blockIdx) * baselineGrid;
@@ -5725,7 +5806,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
5725
5806
  // (the no-fit branch below rolls it back).
5726
5807
  const headingRun = partIndex === 0 ? trailingHeadingRun(curCol) : 0;
5727
5808
  if (choice.splitAt === 0 && headingRun > 0 && headingRun < curCol.blocks.length) {
5728
- const minKeep = effectiveAvoidWidows ? Math.max(1, resolved.bodyText.widowMinLines) : 1;
5809
+ // A drop cap's lines stay with it (#623).
5810
+ const minKeep = Math.max(effectiveAvoidWidows ? Math.max(1, resolved.bodyText.widowMinLines) : 1, partIndex === 0 && capInfo ? capInfo.sink : 1);
5729
5811
  const minTail = effectiveAvoidOrphans && resolved.headings.keepWithNextSplit === 'rules'
5730
5812
  ? Math.max(1, resolved.bodyText.orphanMinLines)
5731
5813
  : 1;
@@ -5848,11 +5930,27 @@ function placeDocumentPass(content, config, cache, options, hints) {
5848
5930
  }
5849
5931
  }
5850
5932
  // A poem's bayt set staggered over two lines (or more) stays in one
5851
- // column (#378): the cut goes before it — in an empty column, which
5852
- // the poem cannot leave, after its first line at least.
5853
- while (choice.splitAt > (curCol.blocks.length === 0 ? 1 : 0) && sameBayt(remainingLines[choice.splitAt - 1], remainingLines[choice.splitAt])) {
5933
+ // column (#378), and so does a line of verse with its turnovers
5934
+ // (#620): the cut goes before it — in an empty column, which the
5935
+ // poem cannot leave, after its first line at least.
5936
+ while (choice.splitAt > (curCol.blocks.length === 0 ? 1 : 0) && sameVerseLine(remainingLines[choice.splitAt - 1], remainingLines[choice.splitAt])) {
5854
5937
  choice = { splitAt: choice.splitAt - 1, demerit: choice.demerit };
5855
5938
  }
5939
+ // A paragraph a drop cap opens never breaks before the initial's
5940
+ // last line (#623): it moves on whole, unless it is alone in an
5941
+ // empty column too short for those lines, where it breaks all the
5942
+ // same (and says so).
5943
+ if (partIndex === 0 && capInfo && choice.splitAt > 0 && choice.splitAt < Math.min(capInfo.sink, remainingLines.length)) {
5944
+ if (curCol.blocks.length > 0)
5945
+ choice = { splitAt: 0, demerit: choice.demerit };
5946
+ else
5947
+ blockMeasureCtx.dropCapNotes?.set(blockIdx, { reason: 'split' });
5948
+ }
5949
+ // A stanza `keepStanzas` covers (a haiku, a tanka) moves on whole,
5950
+ // unless the column is empty: it cannot do better anywhere.
5951
+ if (choice.splitAt > 0 && partIndex === 0 && curCol.blocks.length > 0 && keepsStanzaWhole(rawBlock, resolved)) {
5952
+ choice = { splitAt: 0, demerit: choice.demerit };
5953
+ }
5856
5954
  if (choice.splitAt > 0) {
5857
5955
  // Consume spacing (negative: a container margin pulling the block up)
5858
5956
  if (spacingBefore !== 0) {
@@ -6121,6 +6219,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
6121
6219
  .map((s) => s.startPageIndex);
6122
6220
  if (restarts.length > 0)
6123
6221
  doc.pageNumberRestarts = restarts;
6222
+ // Line numbers (#621): the lines of this pass counted where they landed,
6223
+ // before the running heads (whose palettes recolour them).
6224
+ buildLineNumbers(doc, contentBlocks, continuation);
6124
6225
  buildHeadersAndFooters(doc, resourceById, {
6125
6226
  // Where two palette entries share a base value, each kind of flow
6126
6227
  // colour follows its own settings under a part or section palette.
@@ -6137,6 +6238,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
6137
6238
  return text.length > 0 ? { styleId, text } : undefined;
6138
6239
  },
6139
6240
  });
6241
+ // The line numbers of code listings (#624), in the same slot, after the
6242
+ // margin numbers' palette pass: they keep `codeStyle.lineNumberColor`.
6243
+ if (codeBoxes.wrapped)
6244
+ buildCodeLineNumbers(doc);
6140
6245
  // Vertical pages: the axis each font's upright characters turn about.
6141
6246
  if (doc.pages.some((p) => pageIsVertical(p)))
6142
6247
  stampCentralBaselines(doc);
@@ -6148,6 +6253,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
6148
6253
  doc.iterationCount = 1;
6149
6254
  if (joiningLetterSpacing.size > 0)
6150
6255
  joiningSpacingWarnings.set(doc, joiningLetterSpacingWarnings(doc, joiningLetterSpacing));
6256
+ if (dropCapNotes.size > 0)
6257
+ dropCapWarningsByDoc.set(doc, dropCapWarnings(doc, dropCapNotes));
6151
6258
  return { doc, forcedBreakPages, bandCapProposals, spanPlacedInBand, bandCapsApplied, looseOutcome, captionUnderProposals };
6152
6259
  }
6153
6260
  /** Passes a document printing its own contents gets at most, beyond the
@@ -6207,7 +6314,8 @@ export function* buildDocumentGen(content, config, cache, options) {
6207
6314
  // Justified CJK lines the composer could not fill within its tracking
6208
6315
  // cap; words of a joining script that run past their line, and styles
6209
6316
  // whose letter-spacing such words do not take.
6210
- const loose = [...cjkLooseLineWarnings(doc), ...wordOverflowWarnings(doc), ...(joiningSpacingWarnings.get(doc) ?? [])];
6317
+ // Line numbers set over a float of the side column (#621).
6318
+ const loose = [...cjkLooseLineWarnings(doc), ...wordOverflowWarnings(doc), ...(joiningSpacingWarnings.get(doc) ?? []), ...(dropCapWarningsByDoc.get(doc) ?? []), ...lineNumberWarnings(doc), ...codeOverflowWarnings(doc)];
6211
6319
  // Comic panels whose cell cannot hold their picture's safe area (#556).
6212
6320
  for (const page of doc.pages) {
6213
6321
  if (page.comic)
@@ -6250,10 +6358,12 @@ const indexWarnings = new WeakMap();
6250
6358
  /** The `joiningScriptLetterSpacing` warnings of the last pass, per
6251
6359
  * document. */
6252
6360
  const joiningSpacingWarnings = new WeakMap();
6361
+ /** The `dropCap` warnings of the last pass, per document (#623). */
6362
+ const dropCapWarningsByDoc = new WeakMap();
6253
6363
  /** `doc` with the page of each of its index marks (`doc.indexMarks`). */
6254
6364
  function withIndexMarks(doc, content) {
6255
6365
  const { content: body, contentOffset } = extractFrontmatter(content.markdown);
6256
- const blocks = parseMarkdownMemo(body);
6366
+ const blocks = parseMarkdownMemo(body, codeParseOptions(doc.config));
6257
6367
  if (content.markdown.includes(':index')) {
6258
6368
  const marks = locateIndexMarks(doc, blocks, contentOffset);
6259
6369
  if (marks)
@@ -6274,7 +6384,7 @@ function* buildDocumentRounds(content, config, cache, options) {
6274
6384
  // follows, and a numbering restart after the front matter usually settles
6275
6385
  // it in one extra round.
6276
6386
  if (content.outline === undefined) {
6277
- const parsed = parseMarkdownMemo(extractFrontmatter(content.markdown).content);
6387
+ const parsed = parseMarkdownMemo(extractFrontmatter(content.markdown).content, codeParseOptions(resolveAllConfig(config)));
6278
6388
  // A reference printing an anchor's page (#263) settles the same way.
6279
6389
  const pageRefs = () => {
6280
6390
  if (!hasAnchorRefs(parsed, new Set((content.resources ?? []).map((r) => r.id))))
@@ -6804,6 +6914,21 @@ function* buildDocumentBalanced(content, config, cache, options, tocRound = 0, f
6804
6914
  function sameBayt(a, b) {
6805
6915
  return a?.verse !== undefined && b?.verse !== undefined && a.verse.bayt === b.verse.bayt;
6806
6916
  }
6917
+ /** Whether a column may not break between two lines of verse: they set one
6918
+ * bayt (#378), or `b` is a turnover of the line of verse `a` sets or
6919
+ * turns over (#620). */
6920
+ function sameVerseLine(a, b) {
6921
+ return sameBayt(a, b) || (a?.verseLine !== undefined && b?.verseLine?.turnover === true);
6922
+ }
6923
+ /** Whether a stanza of the line layout is kept whole (#620): it has no
6924
+ * more lines of verse than `keepStanzas` asks for. */
6925
+ function keepsStanzaWhole(raw, resolved) {
6926
+ const stanza = raw.verse?.stanza;
6927
+ if (!stanza || !versesLineByLine(raw, resolved))
6928
+ return false;
6929
+ const keep = verseLinesSettings(raw.verse.attrs, { fontSizePx: 0, lineHeightPx: 0 }, resolved, false).keepStanzas;
6930
+ return keep > 0 && stanza.lines.length <= keep;
6931
+ }
6807
6932
  /** Whether the text after a poem goes back to the baseline grid: unless
6808
6933
  * the paragraph style its fence names says `snapToGrid: false`. */
6809
6934
  function verseSnaps(styleId, resolved) {