postext 1.22.3 → 1.24.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.
- package/dist/__tests__/bundle/bitmapInfo.test.d.ts +2 -0
- package/dist/__tests__/bundle/bitmapInfo.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/bitmapInfo.test.js +78 -0
- package/dist/__tests__/bundle/bitmapInfo.test.js.map +1 -0
- package/dist/__tests__/bundle/designOverflowPin.test.d.ts +2 -0
- package/dist/__tests__/bundle/designOverflowPin.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/designOverflowPin.test.js +87 -0
- package/dist/__tests__/bundle/designOverflowPin.test.js.map +1 -0
- package/dist/__tests__/bundle.test.js +1 -1
- package/dist/__tests__/bundle.test.js.map +1 -1
- package/dist/__tests__/codeBlocks.test.d.ts +2 -0
- package/dist/__tests__/codeBlocks.test.d.ts.map +1 -0
- package/dist/__tests__/codeBlocks.test.js +550 -0
- package/dist/__tests__/codeBlocks.test.js.map +1 -0
- package/dist/__tests__/design/designTextOverflow.test.d.ts +2 -0
- package/dist/__tests__/design/designTextOverflow.test.d.ts.map +1 -0
- package/dist/__tests__/design/designTextOverflow.test.js +102 -0
- package/dist/__tests__/design/designTextOverflow.test.js.map +1 -0
- package/dist/__tests__/exports.test.js +30 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/frontmatterInvalid.test.d.ts +2 -0
- package/dist/__tests__/frontmatterInvalid.test.d.ts.map +1 -0
- package/dist/__tests__/frontmatterInvalid.test.js +101 -0
- package/dist/__tests__/frontmatterInvalid.test.js.map +1 -0
- package/dist/__tests__/hardBreaks.test.d.ts +2 -0
- package/dist/__tests__/hardBreaks.test.d.ts.map +1 -0
- package/dist/__tests__/hardBreaks.test.js +226 -0
- package/dist/__tests__/hardBreaks.test.js.map +1 -0
- package/dist/__tests__/headingFirstLineIndent.test.d.ts +2 -0
- package/dist/__tests__/headingFirstLineIndent.test.d.ts.map +1 -0
- package/dist/__tests__/headingFirstLineIndent.test.js +185 -0
- package/dist/__tests__/headingFirstLineIndent.test.js.map +1 -0
- package/dist/__tests__/headingInlineMarks.test.js +1 -1
- package/dist/__tests__/headingInlineMarks.test.js.map +1 -1
- package/dist/__tests__/headingLetterSpacing.test.js +1 -1
- package/dist/__tests__/headingLetterSpacing.test.js.map +1 -1
- package/dist/__tests__/lineNumbers.test.d.ts +2 -0
- package/dist/__tests__/lineNumbers.test.d.ts.map +1 -0
- package/dist/__tests__/lineNumbers.test.js +304 -0
- package/dist/__tests__/lineNumbers.test.js.map +1 -0
- package/dist/__tests__/mathSizeMigration.test.js +2 -2
- package/dist/__tests__/mathSizeMigration.test.js.map +1 -1
- package/dist/__tests__/paragraphDropCap.test.d.ts +2 -0
- package/dist/__tests__/paragraphDropCap.test.d.ts.map +1 -0
- package/dist/__tests__/paragraphDropCap.test.js +442 -0
- package/dist/__tests__/paragraphDropCap.test.js.map +1 -0
- package/dist/__tests__/paragraphStyleIndent.test.js +26 -2
- package/dist/__tests__/paragraphStyleIndent.test.js.map +1 -1
- package/dist/__tests__/pipeline/bitmapResolution.test.d.ts +2 -0
- package/dist/__tests__/pipeline/bitmapResolution.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline/bitmapResolution.test.js +139 -0
- package/dist/__tests__/pipeline/bitmapResolution.test.js.map +1 -0
- package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +1 -1
- package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js.map +1 -1
- package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +1 -1
- package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js.map +1 -1
- package/dist/__tests__/pipeline/colonListRoom.test.js +1 -1
- package/dist/__tests__/pipeline/colonListRoom.test.js.map +1 -1
- package/dist/__tests__/pipeline/compoundBreaks.test.js +1 -1
- package/dist/__tests__/pipeline/compoundBreaks.test.js.map +1 -1
- package/dist/__tests__/pipeline/dashBreaks.test.js +1 -1
- package/dist/__tests__/pipeline/dashBreaks.test.js.map +1 -1
- package/dist/__tests__/pipeline/designTextTruncated.test.d.ts +2 -0
- package/dist/__tests__/pipeline/designTextTruncated.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline/designTextTruncated.test.js +116 -0
- package/dist/__tests__/pipeline/designTextTruncated.test.js.map +1 -0
- package/dist/__tests__/pipeline/floatPlacement.test.js +3 -3
- package/dist/__tests__/pipeline/floatPlacement.test.js.map +1 -1
- package/dist/__tests__/pipeline/floatShrink.test.d.ts +2 -0
- package/dist/__tests__/pipeline/floatShrink.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline/floatShrink.test.js +298 -0
- package/dist/__tests__/pipeline/floatShrink.test.js.map +1 -0
- package/dist/__tests__/pipeline/headingSplitOrphans.test.js +1 -1
- package/dist/__tests__/pipeline/headingSplitOrphans.test.js.map +1 -1
- package/dist/__tests__/pipeline/inlineFigureSpace.test.js +1 -1
- package/dist/__tests__/pipeline/inlineFigureSpace.test.js.map +1 -1
- package/dist/__tests__/pipeline/paragraphContainerTail.test.js +1 -1
- package/dist/__tests__/pipeline/paragraphContainerTail.test.js.map +1 -1
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js +7 -1
- package/dist/__tests__/pipeline/paragraphStyleFaces.test.js.map +1 -1
- package/dist/__tests__/pipeline/raggedOptimal.test.js +1 -1
- package/dist/__tests__/pipeline/raggedOptimal.test.js.map +1 -1
- package/dist/__tests__/pipeline/textWrap.test.d.ts +2 -0
- package/dist/__tests__/pipeline/textWrap.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline/textWrap.test.js +453 -0
- package/dist/__tests__/pipeline/textWrap.test.js.map +1 -0
- package/dist/__tests__/preflight.test.js +30 -1
- package/dist/__tests__/preflight.test.js.map +1 -1
- package/dist/__tests__/presets/showcaseDesignOverflow.test.d.ts +2 -0
- package/dist/__tests__/presets/showcaseDesignOverflow.test.d.ts.map +1 -0
- package/dist/__tests__/presets/showcaseDesignOverflow.test.js +58 -0
- package/dist/__tests__/presets/showcaseDesignOverflow.test.js.map +1 -0
- package/dist/__tests__/tabStops.test.d.ts +2 -0
- package/dist/__tests__/tabStops.test.d.ts.map +1 -0
- package/dist/__tests__/tabStops.test.js +394 -0
- package/dist/__tests__/tabStops.test.js.map +1 -0
- package/dist/__tests__/table/booktabs.test.d.ts +2 -0
- package/dist/__tests__/table/booktabs.test.d.ts.map +1 -0
- package/dist/__tests__/table/booktabs.test.js +290 -0
- package/dist/__tests__/table/booktabs.test.js.map +1 -0
- package/dist/__tests__/textWrapMeasure.test.d.ts +2 -0
- package/dist/__tests__/textWrapMeasure.test.d.ts.map +1 -0
- package/dist/__tests__/textWrapMeasure.test.js +162 -0
- package/dist/__tests__/textWrapMeasure.test.js.map +1 -0
- package/dist/__tests__/verseLines.test.d.ts +2 -0
- package/dist/__tests__/verseLines.test.d.ts.map +1 -0
- package/dist/__tests__/verseLines.test.js +505 -0
- package/dist/__tests__/verseLines.test.js.map +1 -0
- package/dist/bitmapResolution.d.ts +42 -0
- package/dist/bitmapResolution.d.ts.map +1 -0
- package/dist/bitmapResolution.js +82 -0
- package/dist/bitmapResolution.js.map +1 -0
- package/dist/bundle/codec.d.ts +9 -2
- package/dist/bundle/codec.d.ts.map +1 -1
- package/dist/bundle/codec.js +24 -2
- package/dist/bundle/codec.js.map +1 -1
- package/dist/bundle/configVersion.d.ts +119 -8
- package/dist/bundle/configVersion.d.ts.map +1 -1
- package/dist/bundle/configVersion.js +395 -6
- package/dist/bundle/configVersion.js.map +1 -1
- package/dist/bundle/index.d.ts +3 -3
- package/dist/bundle/index.d.ts.map +1 -1
- package/dist/bundle/index.js +2 -2
- package/dist/bundle/index.js.map +1 -1
- package/dist/bundle/manifest.d.ts +12 -2
- package/dist/bundle/manifest.d.ts.map +1 -1
- package/dist/bundle/manifest.js +132 -6
- package/dist/bundle/manifest.js.map +1 -1
- package/dist/bundle/types.d.ts +28 -2
- package/dist/bundle/types.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.js +45 -9
- package/dist/canvas-backend/blockRender.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +3 -0
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.js +15 -1
- package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
- package/dist/cjkMarks.js +1 -1
- package/dist/cjkMarks.js.map +1 -1
- package/dist/code/highlight.d.ts +34 -0
- package/dist/code/highlight.d.ts.map +1 -0
- package/dist/code/highlight.js +406 -0
- package/dist/code/highlight.js.map +1 -0
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +220 -4
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/bodyText.d.ts.map +1 -1
- package/dist/defaults/bodyText.js +25 -0
- package/dist/defaults/bodyText.js.map +1 -1
- package/dist/defaults/calloutStyles.d.ts.map +1 -1
- package/dist/defaults/calloutStyles.js +7 -0
- package/dist/defaults/calloutStyles.js.map +1 -1
- package/dist/defaults/codeStyle.d.ts +29 -0
- package/dist/defaults/codeStyle.d.ts.map +1 -0
- package/dist/defaults/codeStyle.js +284 -0
- package/dist/defaults/codeStyle.js.map +1 -0
- package/dist/defaults/dropCap.d.ts +49 -0
- package/dist/defaults/dropCap.d.ts.map +1 -0
- package/dist/defaults/dropCap.js +66 -0
- package/dist/defaults/dropCap.js.map +1 -0
- package/dist/defaults/headerFooter.d.ts +21 -6
- package/dist/defaults/headerFooter.d.ts.map +1 -1
- package/dist/defaults/headerFooter.js +37 -14
- package/dist/defaults/headerFooter.js.map +1 -1
- package/dist/defaults/headings.d.ts +5 -4
- package/dist/defaults/headings.d.ts.map +1 -1
- package/dist/defaults/headings.js +26 -4
- package/dist/defaults/headings.js.map +1 -1
- package/dist/defaults/index.d.ts +4 -1
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +20 -1
- package/dist/defaults/index.js.map +1 -1
- package/dist/defaults/layout.d.ts +30 -1
- package/dist/defaults/layout.d.ts.map +1 -1
- package/dist/defaults/layout.js +105 -1
- package/dist/defaults/layout.js.map +1 -1
- package/dist/defaults/lineNumbers.d.ts +19 -0
- package/dist/defaults/lineNumbers.d.ts.map +1 -0
- package/dist/defaults/lineNumbers.js +101 -0
- package/dist/defaults/lineNumbers.js.map +1 -0
- package/dist/defaults/paragraphStyles.d.ts.map +1 -1
- package/dist/defaults/paragraphStyles.js +20 -0
- package/dist/defaults/paragraphStyles.js.map +1 -1
- package/dist/defaults/parts.js +4 -4
- package/dist/defaults/parts.js.map +1 -1
- package/dist/defaults/tabStops.d.ts +29 -0
- package/dist/defaults/tabStops.d.ts.map +1 -0
- package/dist/defaults/tabStops.js +94 -0
- package/dist/defaults/tabStops.js.map +1 -0
- package/dist/defaults/tableStyle.d.ts.map +1 -1
- package/dist/defaults/tableStyle.js +44 -0
- package/dist/defaults/tableStyle.js.map +1 -1
- package/dist/defaults/toc.js +2 -2
- package/dist/defaults/verse.d.ts +11 -0
- package/dist/defaults/verse.d.ts.map +1 -0
- package/dist/defaults/verse.js +57 -0
- package/dist/defaults/verse.js.map +1 -0
- package/dist/design/layout.d.ts +15 -1
- package/dist/design/layout.d.ts.map +1 -1
- package/dist/design/layout.js +28 -4
- package/dist/design/layout.js.map +1 -1
- package/dist/fonts/usage.d.ts +7 -0
- package/dist/fonts/usage.d.ts.map +1 -1
- package/dist/fonts/usage.js +71 -0
- package/dist/fonts/usage.js.map +1 -1
- package/dist/frontmatter.d.ts +20 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +69 -3
- package/dist/frontmatter.js.map +1 -1
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +107 -15
- package/dist/html-backend.js.map +1 -1
- package/dist/index.d.ts +18 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/measure/bidiLines.d.ts +4 -3
- package/dist/measure/bidiLines.d.ts.map +1 -1
- package/dist/measure/bidiLines.js +14 -9
- package/dist/measure/bidiLines.js.map +1 -1
- package/dist/measure/cache.js +2 -2
- package/dist/measure/cache.js.map +1 -1
- package/dist/measure/cjkCompose.d.ts.map +1 -1
- package/dist/measure/cjkCompose.js +10 -6
- package/dist/measure/cjkCompose.js.map +1 -1
- package/dist/measure/hardBreaks.d.ts +46 -0
- package/dist/measure/hardBreaks.d.ts.map +1 -0
- package/dist/measure/hardBreaks.js +228 -0
- package/dist/measure/hardBreaks.js.map +1 -0
- package/dist/measure/index.d.ts +4 -3
- package/dist/measure/index.d.ts.map +1 -1
- package/dist/measure/index.js +5 -4
- package/dist/measure/index.js.map +1 -1
- package/dist/measure/leader.d.ts +20 -0
- package/dist/measure/leader.d.ts.map +1 -0
- package/dist/measure/leader.js +46 -0
- package/dist/measure/leader.js.map +1 -0
- package/dist/measure/links.js +1 -1
- package/dist/measure/links.js.map +1 -1
- package/dist/measure/plain.d.ts.map +1 -1
- package/dist/measure/plain.js +23 -26
- package/dist/measure/plain.js.map +1 -1
- package/dist/measure/rich.d.ts +19 -0
- package/dist/measure/rich.d.ts.map +1 -1
- package/dist/measure/rich.js +215 -39
- package/dist/measure/rich.js.map +1 -1
- package/dist/measure/tabs.d.ts +103 -0
- package/dist/measure/tabs.d.ts.map +1 -0
- package/dist/measure/tabs.js +123 -0
- package/dist/measure/tabs.js.map +1 -0
- package/dist/measure/types.d.ts +72 -2
- package/dist/measure/types.d.ts.map +1 -1
- package/dist/measure/types.js +87 -3
- package/dist/measure/types.js.map +1 -1
- package/dist/parse/blockParser.d.ts +13 -6
- package/dist/parse/blockParser.d.ts.map +1 -1
- package/dist/parse/blockParser.js +457 -87
- package/dist/parse/blockParser.js.map +1 -1
- package/dist/parse/codeFence.d.ts +18 -0
- package/dist/parse/codeFence.d.ts.map +1 -0
- package/dist/parse/codeFence.js +52 -0
- package/dist/parse/codeFence.js.map +1 -0
- package/dist/parse/index.d.ts +1 -1
- package/dist/parse/index.d.ts.map +1 -1
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/indexMarks.d.ts +3 -1
- package/dist/parse/indexMarks.d.ts.map +1 -1
- package/dist/parse/indexMarks.js +22 -2
- package/dist/parse/indexMarks.js.map +1 -1
- package/dist/parse/inlineFormatting.d.ts +50 -0
- package/dist/parse/inlineFormatting.d.ts.map +1 -1
- package/dist/parse/inlineFormatting.js +94 -0
- package/dist/parse/inlineFormatting.js.map +1 -1
- package/dist/parse/sourceMapping.d.ts.map +1 -1
- package/dist/parse/sourceMapping.js +65 -1
- package/dist/parse/sourceMapping.js.map +1 -1
- package/dist/parse/types.d.ts +144 -9
- package/dist/parse/types.d.ts.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +629 -47
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/buildBlockKind.d.ts +5 -0
- package/dist/pipeline/buildBlockKind.d.ts.map +1 -1
- package/dist/pipeline/buildBlockKind.js +74 -2
- package/dist/pipeline/buildBlockKind.js.map +1 -1
- package/dist/pipeline/buildHelpers.d.ts +3 -0
- package/dist/pipeline/buildHelpers.d.ts.map +1 -1
- package/dist/pipeline/buildHelpers.js +7 -2
- package/dist/pipeline/buildHelpers.js.map +1 -1
- package/dist/pipeline/buildMeasurement.js +1 -1
- package/dist/pipeline/buildMeasurement.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +33 -2
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/chips.d.ts.map +1 -1
- package/dist/pipeline/chips.js +4 -0
- package/dist/pipeline/chips.js.map +1 -1
- package/dist/pipeline/codeBlocks.d.ts +38 -0
- package/dist/pipeline/codeBlocks.d.ts.map +1 -0
- package/dist/pipeline/codeBlocks.js +146 -0
- package/dist/pipeline/codeBlocks.js.map +1 -0
- package/dist/pipeline/codeInline.d.ts +22 -0
- package/dist/pipeline/codeInline.d.ts.map +1 -0
- package/dist/pipeline/codeInline.js +93 -0
- package/dist/pipeline/codeInline.js.map +1 -0
- package/dist/pipeline/codeLines.d.ts +91 -0
- package/dist/pipeline/codeLines.d.ts.map +1 -0
- package/dist/pipeline/codeLines.js +399 -0
- package/dist/pipeline/codeLines.js.map +1 -0
- package/dist/pipeline/columnBalancing.d.ts.map +1 -1
- package/dist/pipeline/columnBalancing.js +5 -0
- package/dist/pipeline/columnBalancing.js.map +1 -1
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +8 -1
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts +25 -0
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +173 -19
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/continuation.d.ts.map +1 -1
- package/dist/pipeline/continuation.js +9 -2
- package/dist/pipeline/continuation.js.map +1 -1
- package/dist/pipeline/dropCap.d.ts +128 -0
- package/dist/pipeline/dropCap.d.ts.map +1 -0
- package/dist/pipeline/dropCap.js +412 -0
- package/dist/pipeline/dropCap.js.map +1 -0
- package/dist/pipeline/floatPlacement.d.ts +34 -4
- package/dist/pipeline/floatPlacement.d.ts.map +1 -1
- package/dist/pipeline/floatPlacement.js +35 -5
- package/dist/pipeline/floatPlacement.js.map +1 -1
- package/dist/pipeline/floatSlots.d.ts +7 -0
- package/dist/pipeline/floatSlots.d.ts.map +1 -1
- package/dist/pipeline/floatSlots.js.map +1 -1
- package/dist/pipeline/hardBreaks.d.ts +19 -0
- package/dist/pipeline/hardBreaks.d.ts.map +1 -0
- package/dist/pipeline/hardBreaks.js +36 -0
- package/dist/pipeline/hardBreaks.js.map +1 -0
- package/dist/pipeline/headerFooter.d.ts +4 -1
- package/dist/pipeline/headerFooter.d.ts.map +1 -1
- package/dist/pipeline/headerFooter.js +23 -6
- package/dist/pipeline/headerFooter.js.map +1 -1
- package/dist/pipeline/lineNumbers.d.ts +37 -0
- package/dist/pipeline/lineNumbers.d.ts.map +1 -0
- package/dist/pipeline/lineNumbers.js +441 -0
- package/dist/pipeline/lineNumbers.js.map +1 -0
- package/dist/pipeline/measureContentBlock.d.ts +38 -5
- package/dist/pipeline/measureContentBlock.d.ts.map +1 -1
- package/dist/pipeline/measureContentBlock.js +284 -69
- package/dist/pipeline/measureContentBlock.js.map +1 -1
- package/dist/pipeline/partPalette.d.ts +1 -1
- package/dist/pipeline/partPalette.d.ts.map +1 -1
- package/dist/pipeline/partPalette.js +24 -2
- package/dist/pipeline/partPalette.js.map +1 -1
- package/dist/pipeline/placement.d.ts.map +1 -1
- package/dist/pipeline/placement.js +7 -0
- package/dist/pipeline/placement.js.map +1 -1
- package/dist/pipeline/resourceLayout.d.ts +15 -0
- package/dist/pipeline/resourceLayout.d.ts.map +1 -1
- package/dist/pipeline/resourceLayout.js +95 -15
- package/dist/pipeline/resourceLayout.js.map +1 -1
- package/dist/pipeline/styles.d.ts +18 -2
- package/dist/pipeline/styles.d.ts.map +1 -1
- package/dist/pipeline/styles.js +23 -8
- package/dist/pipeline/styles.js.map +1 -1
- package/dist/pipeline/tabs.d.ts +35 -0
- package/dist/pipeline/tabs.d.ts.map +1 -0
- package/dist/pipeline/tabs.js +99 -0
- package/dist/pipeline/tabs.js.map +1 -0
- package/dist/pipeline/textWrap.d.ts +94 -0
- package/dist/pipeline/textWrap.d.ts.map +1 -0
- package/dist/pipeline/textWrap.js +188 -0
- package/dist/pipeline/textWrap.js.map +1 -0
- package/dist/pipeline/toc.d.ts.map +1 -1
- package/dist/pipeline/toc.js +3 -38
- package/dist/pipeline/toc.js.map +1 -1
- package/dist/pipeline/verseLines.d.ts +98 -0
- package/dist/pipeline/verseLines.d.ts.map +1 -0
- package/dist/pipeline/verseLines.js +293 -0
- package/dist/pipeline/verseLines.js.map +1 -0
- package/dist/preflight.d.ts +46 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +43 -7
- package/dist/preflight.js.map +1 -1
- package/dist/table/booktabs.d.ts +37 -0
- package/dist/table/booktabs.d.ts.map +1 -0
- package/dist/table/booktabs.js +82 -0
- package/dist/table/booktabs.js.map +1 -0
- package/dist/types.d.ts +787 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +367 -9
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js +9 -2
- package/dist/vdt.js.map +1 -1
- package/dist/word/docxRead.d.ts.map +1 -1
- package/dist/word/docxRead.js +15 -0
- package/dist/word/docxRead.js.map +1 -1
- package/dist/word/index.d.ts +1 -1
- package/dist/word/index.d.ts.map +1 -1
- package/dist/word/index.js +1 -1
- package/dist/word/index.js.map +1 -1
- package/dist/word/inline.d.ts +6 -1
- package/dist/word/inline.d.ts.map +1 -1
- package/dist/word/inline.js +35 -1
- package/dist/word/inline.js.map +1 -1
- package/dist/word/model.d.ts +14 -0
- package/dist/word/model.d.ts.map +1 -1
- package/dist/word/model.js.map +1 -1
- package/dist/word/template.d.ts +12 -3
- package/dist/word/template.d.ts.map +1 -1
- package/dist/word/template.js +13 -3
- package/dist/word/template.js.map +1 -1
- package/dist/word/toDocx.d.ts.map +1 -1
- package/dist/word/toDocx.js +52 -8
- package/dist/word/toDocx.js.map +1 -1
- package/dist/word/toMarkdown.d.ts +15 -1
- package/dist/word/toMarkdown.d.ts.map +1 -1
- package/dist/word/toMarkdown.js +133 -7
- package/dist/word/toMarkdown.js.map +1 -1
- package/dist/word/word.test.js +124 -2
- package/dist/word/word.test.js.map +1 -1
- package/dist/worker/fonts.d.ts +2 -1
- package/dist/worker/fonts.d.ts.map +1 -1
- package/dist/worker/fonts.js +6 -2
- package/dist/worker/fonts.js.map +1 -1
- package/package.json +1 -1
package/dist/pipeline/build.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withBitmapResolutions } from '../bitmapResolution';
|
|
1
2
|
import { plainTitleBreaks } from '../parse/inlineFormatting';
|
|
2
3
|
import { dimensionToPx } from '../units';
|
|
3
4
|
import { createVDTDocument, createVDTBlock, createBoundingBox, createVDTColumn, pageIsMirrored, pageIsVertical, flowRectToPage, } from '../vdt';
|
|
@@ -13,6 +14,7 @@ import { getMeasureRegion, getMeasureUprightDigits, getMeasureWritingMode, setMe
|
|
|
13
14
|
import { stampCentralBaselines } from './verticalMetrics';
|
|
14
15
|
import { orientLinesToFrames } from './mirrorFrame';
|
|
15
16
|
import { verseMarginTopPx } from './buildBlockKind';
|
|
17
|
+
import { verseLinesSettings, versesLineByLine } from './verseLines';
|
|
16
18
|
import { resolveAllConfig, computeBaselineGrid, resolvedDirection, resolvedLocale } from './config';
|
|
17
19
|
import { asciiDigits } from '../arabicNumerals';
|
|
18
20
|
import { getMeasureDirection, setMeasureDirection, shiftLineX } from '../measure/bidiLines';
|
|
@@ -41,26 +43,34 @@ import { planParts, derivePartMeasureContext } from './parts';
|
|
|
41
43
|
import { layoutCallout, offsetBlock, offsetCalloutToAbsolute, pickCalloutStyle, planCallouts, resolveCalloutAttrs, } from './calloutLayout';
|
|
42
44
|
import { layoutCaptionText, layoutResourceBlock, planTableSlice } from './resourceLayout';
|
|
43
45
|
import { computeFloatPlan, floatedResourceIds, floatColumnCount, } from './floatPlacement';
|
|
44
|
-
import { enumerateCurrentPageSlots, measureFloatBand, floatGapAbove, measureSideStack, clearSideObstacles, columnHasFloatBand, fitsStrict, trueBottom, } from './floatSlots';
|
|
46
|
+
import { enumerateCurrentPageSlots, measureFloatBand, floatGapAbove, floatGapBelow, measureSideStack, clearSideObstacles, columnHasFloatBand, fitsStrict, trueBottom, } from './floatSlots';
|
|
45
47
|
import { computeHeadingContext, computeResourceNumberingState, } from './resourceNumbering';
|
|
46
48
|
import { documentLocale, effectiveResourceTypes } from '../defaults/resourceTypes';
|
|
47
49
|
import { resolveCalloutStylesConfig } from '../defaults/calloutStyles';
|
|
50
|
+
import { codeBoxStyle, codeParseOptions, wrapCodeBlocks } from './codeBlocks';
|
|
51
|
+
import { buildCodeLineNumbers, codeOverflowWarnings } from './codeLines';
|
|
48
52
|
import { pickTableStyle } from '../defaults/tableStyle';
|
|
53
|
+
import { DEFAULT_FLOAT_MIN_SCALE } from '../defaults/layout';
|
|
54
|
+
import { buildLineNumbers, lineNumberWarnings } from './lineNumbers';
|
|
49
55
|
import { buildHeadersAndFooters, defaultOpenerTitle, headingDesignBoxes, measureDefaultOpenerHeight, measureHeadingDesign } from './headerFooter';
|
|
50
56
|
import { flowColorValues } from './partPalette';
|
|
51
57
|
import { chapterNumberCounter, leadingBoldText } from './placeholders';
|
|
52
58
|
import { proposeBalanceLines, collectColumnGaps, firstDivergentColumn, gapLinesIn, boxRoomIn, boxLeverKeys, pageSegments, MAX_BALANCING_PASSES, MAX_BALANCING_PASSES_PER_DOCUMENT, balanceKey, candidateKey, flexFigureKey, flexFloatKey } from './columnBalancing';
|
|
53
59
|
import { applyBandCap, uncapBand, columnBottom, bandCapLines, bandTop, bareBandTop, resolveBandCapsGen, drainPasses, resolveTrailingCapsGen, bandCapLinesAroundZone, } from './bandCaps';
|
|
54
60
|
import { raggedLooseLines } from './raggedLines';
|
|
55
|
-
import { cjkLooseLineWarnings, collectContentWarnings, joiningLetterSpacingWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
|
|
61
|
+
import { cjkLooseLineWarnings, collectContentWarnings, designTruncationWarnings, dropCapWarnings, floatShrinkWarnings, joiningLetterSpacingWarnings, locateContentWarnings, wordOverflowWarnings } from './contentWarnings';
|
|
62
|
+
import { dropCapSinkAfter } from './dropCap';
|
|
56
63
|
import { mostlyJoiningScript } from '../measure/joining';
|
|
57
64
|
import { annotateDocument } from '../cjkMarks';
|
|
58
65
|
import { annotateArabicMarks } from '../arabicMarks';
|
|
66
|
+
import { literalBreaksFor } from './hardBreaks';
|
|
59
67
|
import { tashkilFor } from './tashkil';
|
|
60
68
|
import { overlineEmphasis } from '../emphasisOverline';
|
|
61
69
|
import { withBookTitleBrackets } from './annotations';
|
|
62
70
|
import { paperByBlock, stampPagePaper } from './paper';
|
|
63
71
|
import { headingLineSpanBand } from './lineSpan';
|
|
72
|
+
import { columnCursorY, insetsBefore, minTextWidthPx, openColumnWrap, resolveCalloutWrap, resolveResourceWrap, runsBesideWrap, sameInsetsFrom, settleColumnWraps, transparentToWrap, wrapGapPx, wrapInsetSteps } from './textWrap';
|
|
73
|
+
import { lineInsetsAt } from '../measure/types';
|
|
64
74
|
/** Tolerance for "does this block fit" checks against a column's free
|
|
65
75
|
* height, absorbing floating-point drift between grid multiples. */
|
|
66
76
|
const FIT_EPS = 0.01;
|
|
@@ -142,6 +152,12 @@ function measureLooseParagraph(rawBlock, blockIdx, columnWidth, ctx, styleOverri
|
|
|
142
152
|
const base = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, { styleOverride });
|
|
143
153
|
if (!base)
|
|
144
154
|
return null;
|
|
155
|
+
// A paragraph with a tab at a stop (#622) is set line by line, as
|
|
156
|
+
// measured: neither looseness nor tracking moves its stops' text.
|
|
157
|
+
if (base.measured.lines.some((l) => l.tabbed)) {
|
|
158
|
+
looseOutcome.set(blockIdx, null);
|
|
159
|
+
return base;
|
|
160
|
+
}
|
|
145
161
|
const target = base.measured.lines.length + extraLines;
|
|
146
162
|
// The breaker's optimum, before any runt fix (a looseness of 0 skips it).
|
|
147
163
|
const optimum = measureContentBlock(rawBlock, blockIdx, columnWidth, ctx, { styleOverride, looseness: 0 });
|
|
@@ -213,6 +229,12 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
213
229
|
* shortened it, so what does not fit it may still fit a full column. */
|
|
214
230
|
const shortSideColumn = (page, col) => col.bbox.height < page.contentArea.height - 0.5;
|
|
215
231
|
const resolved = resolveAllConfig(config);
|
|
232
|
+
// Bitmaps sized by the document's `layout.bitmapResolution` carry it as
|
|
233
|
+
// their own `resolution` from here on (#631); under the default policy
|
|
234
|
+
// the content is untouched.
|
|
235
|
+
const stampedResources = content.resources && withBitmapResolutions(content.resources, resolved.layout.bitmapResolution);
|
|
236
|
+
if (stampedResources && stampedResources !== content.resources)
|
|
237
|
+
content = { ...content, resources: stampedResources };
|
|
216
238
|
// Tracking rungs a loose paragraph may climb: none, then 5‰ steps up to
|
|
217
239
|
// the cap (`headings.balancing.maxTracking`, thousandths of an em), the
|
|
218
240
|
// cap itself always included. Only the smallest rung that gains the line
|
|
@@ -320,7 +342,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
320
342
|
// bibliography takes `:::bibliography`'s place or follows the text.
|
|
321
343
|
// Arabic vowel marks out of the text when `bodyText.tashkil` says so
|
|
322
344
|
// (#376), for the outline and the layout alike.
|
|
323
|
-
|
|
345
|
+
// A configuration stored before #620 prints the backslashes of forced
|
|
346
|
+
// line breaks (`bodyText.hardLineBreaks: false`).
|
|
347
|
+
// Code fences (#624) as `codeStyle` reads them: a configuration stored
|
|
348
|
+
// before #624 reads their lines as Markdown (`codeStyle.blocks: false`).
|
|
349
|
+
const parsedBody = numberTitlesFor(headingMarksFor(tashkilFor(literalBreaksFor(parseMarkdownMemo(markdownBody, codeParseOptions(resolved)), resolved), resolved.bodyText.tashkil), resolved), resolved);
|
|
324
350
|
const citationContext = content.citations
|
|
325
351
|
?? (needsCitationContext(parsedBody, frontmatterMeta) ? bookCitationContexts([{ metadata: frontmatterMeta, blocks: parsedBody }], content.resources)[0] : undefined);
|
|
326
352
|
const citationsApplied = citationContext
|
|
@@ -375,7 +401,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
375
401
|
// page where it stands (#535): each goes in a frameless page-span box,
|
|
376
402
|
// set by the span-block path like any other.
|
|
377
403
|
const spanEmbeds = wrapPageSpanEmbeds(numberedBlocks, content.resources ?? [], effectiveResourceTypes(config, content.resources ?? []));
|
|
378
|
-
|
|
404
|
+
// Code listings (#624): each in a box of the code style, which the
|
|
405
|
+
// callout machinery places and splits.
|
|
406
|
+
const codeBoxes = wrapCodeBlocks(spanEmbeds.blocks, resolved);
|
|
407
|
+
const contentBlocks = codeBoxes.blocks;
|
|
379
408
|
if (indexExpanded.warnings.length > 0)
|
|
380
409
|
indexWarnings.set(doc, indexExpanded.warnings);
|
|
381
410
|
const isNumbered = (b) => headingIsNumbered(b, resolved);
|
|
@@ -412,9 +441,17 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
412
441
|
// The callout styles, with the frameless box of the page-span embeds
|
|
413
442
|
// when there are any: the float gap (a line) above and below it, as an
|
|
414
443
|
// inline resource keeps in a column.
|
|
415
|
-
|
|
416
|
-
|
|
444
|
+
// The listings' box (#624) likewise.
|
|
445
|
+
const syntheticBoxes = [
|
|
446
|
+
...(spanEmbeds.wrapped ? [spanEmbedStyle(bodyStyle.lineHeightPx, resolved.layout.inlineResourceGap !== 'above')] : []),
|
|
447
|
+
...(codeBoxes.wrapped ? [codeBoxStyle(resolved)] : []),
|
|
448
|
+
];
|
|
449
|
+
const calloutStyles = syntheticBoxes.length > 0
|
|
450
|
+
? [...resolved.calloutStyles, ...resolveCalloutStylesConfig(syntheticBoxes, resolved.bodyText, resolved.headings, resolved.unorderedLists, config?.locale)]
|
|
417
451
|
: resolved.calloutStyles;
|
|
452
|
+
// A box nested in another looks its style up in the configuration it is
|
|
453
|
+
// laid out with: the listings' box among them.
|
|
454
|
+
const calloutResolved = codeBoxes.wrapped ? { ...resolved, calloutStyles } : resolved;
|
|
418
455
|
const blockquoteStyle = resolveBlockquoteStyle(resolved);
|
|
419
456
|
const listLevelIndentsPx = computeLevelIndentsPx(resolved, bodyStyle.fontSizePx);
|
|
420
457
|
const orderedMetrics = computeOrderedListRunMetrics(contentBlocks, resolved, bodyStyle.fontSizePx);
|
|
@@ -445,7 +482,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
445
482
|
// it asks for is not applied there. Decided per block, by the writing
|
|
446
483
|
// mode of the styled section the reference sits in (a horizontal
|
|
447
484
|
// appendix of a vertical book turns its figures as asked).
|
|
448
|
-
const floatPlan = computeFloatPlan(contentBlocks, resources, resourceTypes, incorporated, (blockIdx) => sectionWritingMode(sectionPlan, resolved, blockIdx) === 'vertical-rl');
|
|
485
|
+
const floatPlan = computeFloatPlan(contentBlocks, resources, resourceTypes, incorporated, (blockIdx) => sectionWritingMode(sectionPlan, resolved, blockIdx) === 'vertical-rl', resolved.layout.floatShrink, resolved.layout.wrap);
|
|
449
486
|
const floatedIds = floatedResourceIds(floatPlan, incorporated, resources, resourceTypes);
|
|
450
487
|
const floatsByFirstBlock = new Map();
|
|
451
488
|
for (const f of floatPlan) {
|
|
@@ -808,7 +845,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
808
845
|
/** Pages whose head or foot a floated box took (see the gallery rule in
|
|
809
846
|
* `placeFloatInColumns`). */
|
|
810
847
|
const calloutBandPages = new Set();
|
|
811
|
-
const enqueueCalloutFloat = (startIdx, plan, style, L, placement, span
|
|
848
|
+
const enqueueCalloutFloat = (startIdx, plan, style, L, placement, span,
|
|
849
|
+
/** Text wraps round the box (#627): a one-column float at its side. */
|
|
850
|
+
wrap) => {
|
|
812
851
|
const key = `${CALLOUT_FLOAT_PREFIX}${startIdx}`;
|
|
813
852
|
const columns = plan.attrs.columns !== undefined ? floatColumnCount(plan.attrs.columns) : style.columns;
|
|
814
853
|
// A keep-with-next replay passes the fence again: enqueue once.
|
|
@@ -824,6 +863,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
824
863
|
span: span === 'page' ? 'page' : 'column',
|
|
825
864
|
...(span === 'column' && columns > 1 ? { columns } : {}),
|
|
826
865
|
callout: { startIdx },
|
|
866
|
+
...(wrap && span === 'column' && columns === 1
|
|
867
|
+
? { widthFraction: wrap.width, align: wrap.side, wrap: wrap.side, ...(wrap.gap ? { wrapGap: wrap.gap } : {}) }
|
|
868
|
+
: {}),
|
|
827
869
|
...(col ? { refPageIndex: page.index, refY: col.bbox.y + (col.bbox.height - col.availableHeight) } : {}),
|
|
828
870
|
});
|
|
829
871
|
}
|
|
@@ -891,7 +933,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
891
933
|
};
|
|
892
934
|
const rotationKey = (rotated) => rotated ? `:${rotated.direction}${rotated.length.toFixed(2)}${rotated.upright ? 'u' : ''}` : '';
|
|
893
935
|
const asideKey = (a) => (a ? `:aside${a.dx.toFixed(1)}x${a.width.toFixed(1)}${a.alignBottom ? 'b' : 't'}${(a.offsetY ?? 0).toFixed(1)}` : '');
|
|
894
|
-
const
|
|
936
|
+
const capKey = (cap) => (cap ? `:cap${cap.maxBodyHeight.toFixed(2)}@${cap.minScale}` : '');
|
|
937
|
+
const layoutFloat = (resourceId, width, slice, rotated, aside, cap) => {
|
|
895
938
|
const resource = resourceById.get(resourceId);
|
|
896
939
|
if (!resource)
|
|
897
940
|
return null;
|
|
@@ -912,6 +955,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
912
955
|
...(rotated ? (rotated.upright ? { upright: { maxLength: rotated.length } } : { rotate: rotated.direction, rotatedLength: rotated.length }) : {}),
|
|
913
956
|
...(aside ? { captionAside: aside } : {}),
|
|
914
957
|
...(flexPx > 0 ? { bodyHeightDelta: flexPx } : {}),
|
|
958
|
+
...(cap && !rotated ? { maxBodyHeight: cap.maxBodyHeight, minBodyScale: cap.minScale } : {}),
|
|
915
959
|
});
|
|
916
960
|
};
|
|
917
961
|
/** The rotation of a pending float on a band whose columns keep `avail`
|
|
@@ -962,8 +1006,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
962
1006
|
/** Height (and caption baseline) of a float at a given width, memoised —
|
|
963
1007
|
* fit checks run for every pending float on every loop iteration. */
|
|
964
1008
|
const floatMeasureMemo = new Map();
|
|
965
|
-
const measureFloat = (resourceId, width, slice, rotated, aside) => {
|
|
966
|
-
const key = `${resourceId}:${width.toFixed(2)}${sliceKey(slice)}${rotationKey(rotated)}${asideKey(aside)}`;
|
|
1009
|
+
const measureFloat = (resourceId, width, slice, rotated, aside, cap) => {
|
|
1010
|
+
const key = `${resourceId}:${width.toFixed(2)}${sliceKey(slice)}${rotationKey(rotated)}${asideKey(aside)}${capKey(cap)}`;
|
|
967
1011
|
const memo = floatMeasureMemo.get(key);
|
|
968
1012
|
if (memo !== undefined)
|
|
969
1013
|
return memo;
|
|
@@ -997,7 +1041,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
997
1041
|
floatMeasureMemo.set(key, mc);
|
|
998
1042
|
return mc;
|
|
999
1043
|
}
|
|
1000
|
-
const laid = layoutFloat(resourceId, width, slice, rotated, aside);
|
|
1044
|
+
const laid = layoutFloat(resourceId, width, slice, rotated, aside, cap);
|
|
1001
1045
|
let m = null;
|
|
1002
1046
|
if (laid && laid.block.rotation) {
|
|
1003
1047
|
// A rotated block takes its band whole: no caption baseline to align,
|
|
@@ -1018,10 +1062,12 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1018
1062
|
if (lastBaseline === undefined || ln.baseline > lastBaseline)
|
|
1019
1063
|
lastBaseline = ln.baseline;
|
|
1020
1064
|
}
|
|
1065
|
+
const picture = rb.kind === 'bitmap' || rb.kind === 'svg' || rb.kind === 'video';
|
|
1021
1066
|
m = {
|
|
1022
1067
|
height: laid.totalHeight,
|
|
1023
1068
|
...(lastBaseline !== undefined ? { lastCaptionBaseline: lastBaseline } : {}),
|
|
1024
1069
|
...(laid.asideHeight !== undefined ? { asideHeight: laid.asideHeight } : {}),
|
|
1070
|
+
...(picture ? { body: { height: rb.bodyRect.height, scale: rb.shrinkScale ?? 1 } } : {}),
|
|
1025
1071
|
};
|
|
1026
1072
|
}
|
|
1027
1073
|
floatMeasureMemo.set(key, m);
|
|
@@ -1037,7 +1083,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1037
1083
|
flushEnd = false, aside,
|
|
1038
1084
|
/** The page is a verso of mirrored margins (a floated box's `'outer'`
|
|
1039
1085
|
* corner icon hangs on the left there). */
|
|
1040
|
-
mirrored = false
|
|
1086
|
+
mirrored = false,
|
|
1087
|
+
/** A picture scaled to its slot (`placement.shrink`). */
|
|
1088
|
+
cap) => {
|
|
1041
1089
|
const strip = comicStripOf(resourceId);
|
|
1042
1090
|
if (strip) {
|
|
1043
1091
|
const block = buildComicStripBlock(strip.idx, strip.source, x, width, contentArea.height - 2 * floatGapPx, cursor.pageIndex);
|
|
@@ -1050,7 +1098,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1050
1098
|
calloutFloatResults.set(result.frame, { result, startIdx, plan: cf.plan });
|
|
1051
1099
|
return { block: result.frame, height: result.totalHeight };
|
|
1052
1100
|
}
|
|
1053
|
-
const laid = layoutFloat(resourceId, width, slice, rotated, aside);
|
|
1101
|
+
const laid = layoutFloat(resourceId, width, slice, rotated, aside, cap);
|
|
1054
1102
|
if (!laid)
|
|
1055
1103
|
return null;
|
|
1056
1104
|
const { block: rb, totalHeight } = laid;
|
|
@@ -1089,6 +1137,52 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1089
1137
|
/** Pages a `:::pagebreak{center}` opened (#424). */
|
|
1090
1138
|
const centredPages = new Set();
|
|
1091
1139
|
const reservedOf = (col) => floatReserved.get(col) ?? { top: 0, bottom: 0 };
|
|
1140
|
+
/** Text wrap (#627): floats set in a band at the head or foot of one
|
|
1141
|
+
* column that text may run beside, per column. The band stays a band
|
|
1142
|
+
* until running text comes to take it back (`openFloatWraps`). */
|
|
1143
|
+
/** The room a wrapped item takes from the lines beside it (#627): its
|
|
1144
|
+
* width and gap, in whole characters of the body under a CJK character
|
|
1145
|
+
* grid (`cjk.grid`), so the lines beside it keep to the grid. */
|
|
1146
|
+
const wrapExclusionWidth = (columnWidth, itemWidth, gap) => {
|
|
1147
|
+
const w = itemWidth + gap;
|
|
1148
|
+
const em = bodyStyle.fontSizePx;
|
|
1149
|
+
return Math.min(columnWidth, resolved.cjk.grid.enabled && em > 0 ? Math.ceil((w - 0.01) / em) * em : w);
|
|
1150
|
+
};
|
|
1151
|
+
/** The `textWrap` warnings of this pass (#627). */
|
|
1152
|
+
const textWrapNotes = [];
|
|
1153
|
+
const floatWraps = new Map();
|
|
1154
|
+
/** Give the bands of the wrapped floats of `col` back to the flow, as
|
|
1155
|
+
* exclusions the text runs beside: a float at the head of a column the
|
|
1156
|
+
* text has not entered yet, one at its foot the text has not reached. */
|
|
1157
|
+
const openFloatWraps = (col) => {
|
|
1158
|
+
const list = floatWraps.get(col);
|
|
1159
|
+
if (!list)
|
|
1160
|
+
return;
|
|
1161
|
+
floatWraps.delete(col);
|
|
1162
|
+
for (const w of list) {
|
|
1163
|
+
const r = { ...reservedOf(col) };
|
|
1164
|
+
if (w.position === 'top') {
|
|
1165
|
+
// A block already stands under it, or another band between: it
|
|
1166
|
+
// keeps its band.
|
|
1167
|
+
if (col.blocks.length > 0 || Math.abs(w.ex.y + w.need - col.bbox.y) > 0.5)
|
|
1168
|
+
continue;
|
|
1169
|
+
col.bbox.y -= w.need;
|
|
1170
|
+
col.bbox.height += w.need;
|
|
1171
|
+
col.availableHeight += w.need;
|
|
1172
|
+
r.top = Math.max(0, r.top - w.need);
|
|
1173
|
+
openColumnWrap(col, w.ex, 'top');
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
if (uncappedBottoms.has(col) || Math.abs(col.bbox.y + col.bbox.height - w.ex.y) > 0.5 || columnCursorY(col) > w.ex.y + 0.01)
|
|
1177
|
+
continue;
|
|
1178
|
+
col.bbox.height += w.need;
|
|
1179
|
+
col.availableHeight += w.need;
|
|
1180
|
+
r.bottom = Math.max(0, r.bottom - w.need);
|
|
1181
|
+
openColumnWrap(col, w.ex, 'bottom');
|
|
1182
|
+
}
|
|
1183
|
+
floatReserved.set(col, r);
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1092
1186
|
const floatHeadOf = (col) => reservedOf(col).top;
|
|
1093
1187
|
/** `BandCap.headPx` for a cap proposed on `cols`: how far the float bands
|
|
1094
1188
|
* every column holds at its head put the band's top below its bare one. */
|
|
@@ -1342,6 +1436,50 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1342
1436
|
built.block.contentIndex = anchorIdx;
|
|
1343
1437
|
(page.floats ??= []).push(built.block);
|
|
1344
1438
|
};
|
|
1439
|
+
/**
|
|
1440
|
+
* A picture float scaled to `room` px of a slot instead of moving on
|
|
1441
|
+
* (`placement.shrink`, #626): the tallest body that keeps its band
|
|
1442
|
+
* within the room. A top band rounds up to the grid, so the float may
|
|
1443
|
+
* stand as tall as the grid multiple within the room, less its gap; a
|
|
1444
|
+
* bottom band (its caption's last baseline on the grid) and a side
|
|
1445
|
+
* stack give the room back px for px. The caption's height may change
|
|
1446
|
+
* with the picture's width (`captionMeasure: 'body'`) and a safe area
|
|
1447
|
+
* crops before it scales, so the cap is refined over a few rounds. The
|
|
1448
|
+
* scale never goes below the float's `minScale`; `fits` is false when
|
|
1449
|
+
* even that does not hold the room. Null for a float that does not
|
|
1450
|
+
* scale (no `shrink`, not an upright picture, the rest of a table) or
|
|
1451
|
+
* already fits.
|
|
1452
|
+
*/
|
|
1453
|
+
const shrinkToRoom = (f, width, aside, full, room, topBand, bandOf) => {
|
|
1454
|
+
if (!f.shrink || !full.body || f.rotate || f.startRow)
|
|
1455
|
+
return null;
|
|
1456
|
+
const minScale = f.minScale ?? DEFAULT_FLOAT_MIN_SCALE;
|
|
1457
|
+
const excessOf = (m, band) => topBand
|
|
1458
|
+
? m.height + floatGapBelow(m, floatGapPx) - Math.floor((room + 0.01) / baselineGrid) * baselineGrid
|
|
1459
|
+
: band.need - room;
|
|
1460
|
+
let measure = full;
|
|
1461
|
+
let band = bandOf(full);
|
|
1462
|
+
let cap;
|
|
1463
|
+
for (let round = 0; round < 4; round++) {
|
|
1464
|
+
const excess = excessOf(measure, band);
|
|
1465
|
+
if (excess <= 0.01)
|
|
1466
|
+
break;
|
|
1467
|
+
const next = { maxBodyHeight: Math.max(0, measure.body.height - excess), minScale };
|
|
1468
|
+
if (cap && next.maxBodyHeight >= cap.maxBodyHeight - 0.01)
|
|
1469
|
+
break;
|
|
1470
|
+
cap = next;
|
|
1471
|
+
const m = measureFloat(f.resourceId, width, undefined, undefined, aside, cap);
|
|
1472
|
+
if (!m?.body)
|
|
1473
|
+
return null;
|
|
1474
|
+
measure = m;
|
|
1475
|
+
band = bandOf(m);
|
|
1476
|
+
if (m.body.scale <= minScale + 1e-6)
|
|
1477
|
+
break;
|
|
1478
|
+
}
|
|
1479
|
+
if (!cap)
|
|
1480
|
+
return null;
|
|
1481
|
+
return { measure, ...band, cap, fits: excessOf(measure, band) <= 0.01 };
|
|
1482
|
+
};
|
|
1345
1483
|
const placeFloatInColumns = (page, f, targetCols, position, pageSpan, mode, anchorToCap = false, side = false, refY) => {
|
|
1346
1484
|
const first = targetCols[0];
|
|
1347
1485
|
const probe = probeFloatBand(page, f, targetCols, position, pageSpan, anchorToCap, side, refY);
|
|
@@ -1350,6 +1488,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1350
1488
|
const { width, xLeft, rotated, aside, sideCol } = probe;
|
|
1351
1489
|
let { slice, measure, need, y } = probe;
|
|
1352
1490
|
let rest;
|
|
1491
|
+
/** A picture scaled to this slot (`placement.shrink`, #626). */
|
|
1492
|
+
let cap;
|
|
1493
|
+
const bandOf = (m) => measureFloatBand(position, m, targetCols, page.contentArea, baselineGrid, floatGapPx, (c) => (anchorToCap ? c.bbox.y + c.bbox.height : trueBottom(c, uncappedBottoms)));
|
|
1353
1494
|
/** The float was cut to this slot (a table slice). */
|
|
1354
1495
|
let cut = false;
|
|
1355
1496
|
/** A rotated block wider than the band (its upright height). */
|
|
@@ -1359,24 +1500,36 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1359
1500
|
// when the rest of the column takes it; otherwise it waits for the
|
|
1360
1501
|
// side column of the next page — where it is set anyway, overflowing,
|
|
1361
1502
|
// when even an empty column cannot hold it (a dominating figure).
|
|
1503
|
+
let cap;
|
|
1362
1504
|
if (need > first.availableHeight + 0.01) {
|
|
1363
1505
|
// A column that already holds something — a float, a box, or a
|
|
1364
1506
|
// heading design standing in it — is no empty column to overflow.
|
|
1365
1507
|
const holds = sideUsedBottom(first) > first.bbox.y + 0.5 || (sideObstacles.get(first)?.length ?? 0) > 0;
|
|
1366
|
-
if (mode === 'strict' || holds)
|
|
1367
|
-
return 'defer';
|
|
1368
1508
|
// An empty column shortened by a band above it (a page-span box,
|
|
1369
1509
|
// a top float) is no measure of the float: when a full column
|
|
1370
1510
|
// would hold it, it waits for one instead of overflowing this one.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
//
|
|
1374
|
-
//
|
|
1375
|
-
|
|
1376
|
-
if
|
|
1377
|
-
|
|
1511
|
+
const waitsForFull = shortSideColumn(page, first) && measure.height <= page.contentArea.height + 0.01;
|
|
1512
|
+
const lastResort = mode === 'fresh' && !holds && !waitsForFull;
|
|
1513
|
+
// A picture scaled to the room the column keeps (#626): any slot
|
|
1514
|
+
// with `'slot'`, when it holds it at its smallest scale or more;
|
|
1515
|
+
// the empty column it would overflow with `'page'` too, at its
|
|
1516
|
+
// smallest scale if need be.
|
|
1517
|
+
if (f.shrink === 'slot' || (f.shrink && lastResort)) {
|
|
1518
|
+
const s = shrinkToRoom(f, width, undefined, measure, first.availableHeight, false, (m) => measureSideStack(m, first, refY, page.contentArea, baselineGrid, floatGapPx, sideObstacles.get(first)));
|
|
1519
|
+
if (s && (s.fits || lastResort))
|
|
1520
|
+
({ measure, need, y, cap } = s);
|
|
1521
|
+
}
|
|
1522
|
+
if (need > first.availableHeight + 0.01) {
|
|
1523
|
+
if (!lastResort)
|
|
1524
|
+
return 'defer';
|
|
1525
|
+
// Overflowing a column a side caption has shortened: the next pass
|
|
1526
|
+
// sets that caption under its figure and gives the column back.
|
|
1527
|
+
const cutBy = asideCutBy.get(first);
|
|
1528
|
+
if (cutBy !== undefined)
|
|
1529
|
+
captionUnderProposals.add(cutBy);
|
|
1530
|
+
}
|
|
1378
1531
|
}
|
|
1379
|
-
const built = buildFloatBlock(f.resourceId, xLeft, width, slice, rotated);
|
|
1532
|
+
const built = buildFloatBlock(f.resourceId, xLeft, width, slice, rotated, false, undefined, false, cap);
|
|
1380
1533
|
if (!built)
|
|
1381
1534
|
return 'skip';
|
|
1382
1535
|
first.availableHeight = Math.max(0, first.availableHeight - need);
|
|
@@ -1404,9 +1557,26 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1404
1557
|
const gallery = need <= minAvail + 0.01
|
|
1405
1558
|
&& f.refPageIndex !== undefined && f.refPageIndex < page.index
|
|
1406
1559
|
&& calloutBandPages.has(page) && !calloutFloatOf(f.resourceId);
|
|
1407
|
-
if (
|
|
1408
|
-
|
|
1409
|
-
|
|
1560
|
+
if (gallery) {
|
|
1561
|
+
galleryFill = true;
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
// A picture that scales to any slot (#626) takes the room left
|
|
1565
|
+
// beside the other band, the text room kept, when it holds it
|
|
1566
|
+
// at its smallest scale or more.
|
|
1567
|
+
const s = f.shrink === 'slot' ? shrinkToRoom(f, width, aside, measure, minAvail - minTextPx, position === 'top', bandOf) : null;
|
|
1568
|
+
if (!s?.fits)
|
|
1569
|
+
return 'defer';
|
|
1570
|
+
({ measure, need, y, cap } = s);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
// Dominating the band: a picture that scales (`'page'` or `'slot'`,
|
|
1574
|
+
// #626) takes the band's room, at its smallest scale if need be —
|
|
1575
|
+
// then it runs past the band, as any dominating float does.
|
|
1576
|
+
if (need > minAvail + 0.01 && f.shrink && !rotated) {
|
|
1577
|
+
const s = shrinkToRoom(f, width, aside, measure, minAvail, position === 'top', bandOf);
|
|
1578
|
+
if (s)
|
|
1579
|
+
({ measure, need, y, cap } = s);
|
|
1410
1580
|
}
|
|
1411
1581
|
if (need > minAvail + 0.01 || tooWide(measure)) {
|
|
1412
1582
|
// Dominating the band: a table is cut to it (a rotated table, to
|
|
@@ -1432,6 +1602,19 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1432
1602
|
// too wide.
|
|
1433
1603
|
if (f.rotate || tooWide(measure))
|
|
1434
1604
|
return 'defer';
|
|
1605
|
+
// A picture that scales to any slot (#626) takes this one when it
|
|
1606
|
+
// holds it at its smallest scale or more (the text room kept next
|
|
1607
|
+
// to another band, as `fitsStrict` keeps it); the loop below checks
|
|
1608
|
+
// the band as for any float.
|
|
1609
|
+
if (f.shrink === 'slot' && measure.body && !rotated) {
|
|
1610
|
+
const room = Math.min(...targetCols.map((c) => c.availableHeight - (columnHasFloatBand(page, c) ? minTextPx : 0)));
|
|
1611
|
+
if (need > room + 0.01) {
|
|
1612
|
+
const s = shrinkToRoom(f, width, aside, measure, room, position === 'top', bandOf);
|
|
1613
|
+
if (!s?.fits)
|
|
1614
|
+
return 'defer';
|
|
1615
|
+
({ measure, need, y, cap } = s);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1435
1618
|
for (const c of targetCols) {
|
|
1436
1619
|
if (position === 'bottom' && uncappedBottoms.has(c) && !anchorToCap) {
|
|
1437
1620
|
// Trailing cap: the band must lie entirely below the level cut.
|
|
@@ -1489,7 +1672,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1489
1672
|
// An upright figure of a vertical page stands at the head of its tier
|
|
1490
1673
|
// (`placement.align`: `left` the top, `center`, `right` the foot).
|
|
1491
1674
|
const flush = rotated?.upright ? (f.align === 'center' ? 0.5 : f.align === 'right' ? 1 : 0) : rotated ? rotatedFlushEnd(page) : false;
|
|
1492
|
-
const built = buildFloatBlock(f.resourceId, xLeft, width, slice, rotated, flush, aside, mirroredOf(page));
|
|
1675
|
+
const built = buildFloatBlock(f.resourceId, xLeft, width, slice, rotated, flush, aside, mirroredOf(page), cap);
|
|
1493
1676
|
if (!built)
|
|
1494
1677
|
return 'skip';
|
|
1495
1678
|
// The caption beside the figure takes its band of the side column: a
|
|
@@ -1544,6 +1727,40 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1544
1727
|
if (galleryFill)
|
|
1545
1728
|
for (const col of targetCols)
|
|
1546
1729
|
col.availableHeight = 0;
|
|
1730
|
+
// Text wrap (#627): a one-column float at the head or foot of its
|
|
1731
|
+
// column, which running text may take back to run beside it — unless
|
|
1732
|
+
// the text left beside it would be too narrow, or the float too short
|
|
1733
|
+
// for lines beside it.
|
|
1734
|
+
if (f.wrap && !pageSpan && !side && targetCols.length === 1 && !rotated && !aside && !cut && !galleryFill
|
|
1735
|
+
&& !(position === 'bottom' && uncappedBottoms.has(first))) {
|
|
1736
|
+
const gap = wrapGapPx(f.wrapGap, dpi, bodyStyle.fontSizePx, floatGapPx);
|
|
1737
|
+
const settings = resolved.layout.wrap;
|
|
1738
|
+
const reason = first.bbox.width - width - gap + 0.01 < minTextWidthPx(settings.minTextWidth, first.bbox.width, dpi, bodyStyle.fontSizePx)
|
|
1739
|
+
? 'tooNarrow'
|
|
1740
|
+
: Math.round(need / bodyStyle.lineHeightPx) < settings.minLinesBeside ? 'fewLines' : undefined;
|
|
1741
|
+
if (reason) {
|
|
1742
|
+
textWrapNotes.push({ kind: 'textWrap', reason, resourceId: f.resourceId, pageIndex: page.index });
|
|
1743
|
+
}
|
|
1744
|
+
else {
|
|
1745
|
+
const exWidth = wrapExclusionWidth(first.bbox.width, width, gap);
|
|
1746
|
+
const exY = position === 'top' ? first.bbox.y - need : first.bbox.y + first.bbox.height;
|
|
1747
|
+
const list = floatWraps.get(first) ?? [];
|
|
1748
|
+
list.push({
|
|
1749
|
+
position,
|
|
1750
|
+
need,
|
|
1751
|
+
ex: {
|
|
1752
|
+
x: f.wrap === 'left' ? first.bbox.x : first.bbox.x + first.bbox.width - exWidth,
|
|
1753
|
+
y: exY,
|
|
1754
|
+
width: exWidth,
|
|
1755
|
+
height: need,
|
|
1756
|
+
side: f.wrap,
|
|
1757
|
+
gap: exWidth - width,
|
|
1758
|
+
ownerId: built.block.id,
|
|
1759
|
+
},
|
|
1760
|
+
});
|
|
1761
|
+
floatWraps.set(first, list);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1547
1764
|
commitFloatBlock(page, first, built, xLeft, y, width, f.firstBlockIdx);
|
|
1548
1765
|
floatsPlaced++;
|
|
1549
1766
|
return rest ? { rest } : 'placed';
|
|
@@ -1898,6 +2115,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1898
2115
|
/** Blocks whose style tracks text of a joining script, which is set
|
|
1899
2116
|
* untracked (see `BlockMeasureContext.joiningLetterSpacing`). */
|
|
1900
2117
|
const joiningLetterSpacing = new Set();
|
|
2118
|
+
/** Paragraphs whose drop cap could not be set as configured (#623). */
|
|
2119
|
+
const dropCapNotes = new Map();
|
|
1901
2120
|
// Everything per-block measurement needs that is constant for this pass.
|
|
1902
2121
|
const measureCtx = {
|
|
1903
2122
|
resolved,
|
|
@@ -1923,6 +2142,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
1923
2142
|
floatedIds,
|
|
1924
2143
|
leftFlow: calloutsOutOfFlow,
|
|
1925
2144
|
joiningLetterSpacing,
|
|
2145
|
+
dropCapNotes,
|
|
1926
2146
|
...(footnoteNumbering.numbers.size > 0 ? { footnoteNumbers: footnoteNumbering.numbers } : {}),
|
|
1927
2147
|
...(anchorRefContext ? { anchorRefs: anchorRefContext } : {}),
|
|
1928
2148
|
};
|
|
@@ -2283,7 +2503,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
2283
2503
|
blk.lines = resetLinePositions(raggedLooseLines(m.measured.lines, noteStyle.textAlign), noteStyle.lineHeightPx).map((line) => ({
|
|
2284
2504
|
...line,
|
|
2285
2505
|
bbox: createBoundingBox(line.bbox.x + x, line.bbox.y + y, line.bbox.width, line.bbox.height),
|
|
2286
|
-
...(line.measure ? { measure: { x: line.measure.x + x
|
|
2506
|
+
...(line.measure ? { measure: { ...line.measure, x: line.measure.x + x } } : {}),
|
|
2287
2507
|
baseline: line.baseline + y,
|
|
2288
2508
|
}));
|
|
2289
2509
|
const h = blk.lines.length * noteStyle.lineHeightPx;
|
|
@@ -2745,7 +2965,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
2745
2965
|
return;
|
|
2746
2966
|
const band = currentBand(page, cursor);
|
|
2747
2967
|
const cols = bandColumns(page, band).filter((c) => c.bbox.height > 0.5);
|
|
2748
|
-
|
|
2968
|
+
// A band where text wraps round something (#627) is not levelled.
|
|
2969
|
+
if (cols.length < 2 || cols.some((c) => c.forcedBreak || (c.exclusions?.length ?? 0) > 0))
|
|
2749
2970
|
return;
|
|
2750
2971
|
if (activeCap && activeCap.spanIndex === boundaryIndex
|
|
2751
2972
|
&& activeCap.pageIndex === page.index && activeCap.band === band) {
|
|
@@ -2815,7 +3036,15 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
2815
3036
|
* page the boundary opens: they sit the drain out and stay pending (with
|
|
2816
3037
|
* no reference stamp, which the citing line sets), so they land after
|
|
2817
3038
|
* the line that cites them, in the new segment (EF-69). */
|
|
3039
|
+
/** Text wrap (#627): an inline figure that wraps, placed as a band, whose
|
|
3040
|
+
* band the next block takes back to run beside it when it can (see
|
|
3041
|
+
* `pipeline/textWrap.ts`). */
|
|
3042
|
+
let pendingWrap = null;
|
|
2818
3043
|
const closeFlowSegment = (boundaryIndex) => {
|
|
3044
|
+
// What text wrapped round in the column the segment ends in keeps its
|
|
3045
|
+
// room (#627).
|
|
3046
|
+
pendingWrap = null;
|
|
3047
|
+
settleColumnWraps(currentColumn(doc, cursor));
|
|
2819
3048
|
settleAwaitingSideBoxes(true);
|
|
2820
3049
|
releaseStaleCitationGates();
|
|
2821
3050
|
const ownFloats = [];
|
|
@@ -3081,7 +3310,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3081
3310
|
childStartIdx: startIdx + 1 + from.child,
|
|
3082
3311
|
width,
|
|
3083
3312
|
ctx: measureCtx,
|
|
3084
|
-
resolved,
|
|
3313
|
+
resolved: calloutResolved,
|
|
3085
3314
|
containerId: plan.containerId,
|
|
3086
3315
|
frameId,
|
|
3087
3316
|
nextChildId: () => `${frameId}-c${n++}`,
|
|
@@ -3154,7 +3383,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3154
3383
|
return null;
|
|
3155
3384
|
// A head that goes on also carries the style's continuation marker.
|
|
3156
3385
|
const tail = tailOf(full) + full.continuesMarkerPx;
|
|
3157
|
-
/** Candidate cuts with the head's content bottom.
|
|
3386
|
+
/** Candidate cuts with the head's content bottom. A `soft` cut parts a
|
|
3387
|
+
* wrapped code line from its continuation (#624): taken only when no
|
|
3388
|
+
* other cut fits. */
|
|
3158
3389
|
const candidates = [];
|
|
3159
3390
|
/** Collect the cuts among `units` (one box's items; `below` = the tails
|
|
3160
3391
|
* of the nested boxes around them), each side of a cut holding enough
|
|
@@ -3197,6 +3428,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3197
3428
|
candidates.push({
|
|
3198
3429
|
cut: { child: k, line: lineBase + l, widths: k === from.child ? cutWidths(from, width) : [{ fromLine: 0, width }] },
|
|
3199
3430
|
bottom: line.bbox.y + line.bbox.height + below,
|
|
3431
|
+
...(c.lines[l]?.codeLine?.continued ? { soft: true } : {}),
|
|
3200
3432
|
});
|
|
3201
3433
|
}
|
|
3202
3434
|
}
|
|
@@ -3223,6 +3455,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
3223
3455
|
const viable = candidates
|
|
3224
3456
|
.filter((c) => !insideGroup(c.cut))
|
|
3225
3457
|
.sort((a, b) => b.bottom - a.bottom);
|
|
3458
|
+
// A wrapped code line stays with its continuations when a cut
|
|
3459
|
+
// elsewhere fits (#624).
|
|
3460
|
+
viable.sort((a, b) => (a.soft ? 1 : 0) - (b.soft ? 1 : 0));
|
|
3226
3461
|
for (const c of viable) {
|
|
3227
3462
|
if (c.bottom + tail > roomPx + 0.01)
|
|
3228
3463
|
continue;
|
|
@@ -4066,6 +4301,83 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4066
4301
|
* boxes) still fall back to this inline placement for v1 — the frame's
|
|
4067
4302
|
* `callout.placement` records the request.
|
|
4068
4303
|
*/
|
|
4304
|
+
/**
|
|
4305
|
+
* A box set `here` that text wraps round (#627): laid out whole at its
|
|
4306
|
+
* share of the column, at its side, level with the next line's top, then
|
|
4307
|
+
* set as a band the next running text takes back to run beside it (see
|
|
4308
|
+
* `pendingWrap`). It never splits: too tall for the room left, it moves
|
|
4309
|
+
* on to the next column (a `textWrap` warning says so). Frame and
|
|
4310
|
+
* children go to the page's floats, where a floated box goes. Returns
|
|
4311
|
+
* false, the box set in the flow as any, when the text beside it would
|
|
4312
|
+
* be too narrow or the box too short for lines beside it.
|
|
4313
|
+
*/
|
|
4314
|
+
const placeCalloutWrapped = (startIdx, plan, style, L, frameId, wrap) => {
|
|
4315
|
+
const settings = resolved.layout.wrap;
|
|
4316
|
+
const gap = wrapGapPx(wrap.gap, dpi, bodyStyle.fontSizePx, floatGapPx);
|
|
4317
|
+
const note = (reason) => {
|
|
4318
|
+
const start = contentBlocks[startIdx];
|
|
4319
|
+
textWrapNotes.push({ kind: 'textWrap', reason, box: style.id, sourceStart: start.sourceStart + bodyOffset, sourceEnd: start.sourceEnd + bodyOffset, pageIndex: cursor.pageIndex });
|
|
4320
|
+
};
|
|
4321
|
+
let col = currentColumn(doc, cursor);
|
|
4322
|
+
if (col.bbox.width * (1 - wrap.width) - gap + 0.01 < minTextWidthPx(settings.minTextWidth, col.bbox.width, dpi, bodyStyle.fontSizePx)) {
|
|
4323
|
+
note('tooNarrow');
|
|
4324
|
+
return false;
|
|
4325
|
+
}
|
|
4326
|
+
for (let attempt = 0;; attempt++) {
|
|
4327
|
+
col = currentColumn(doc, cursor);
|
|
4328
|
+
const page = doc.pages[cursor.pageIndex];
|
|
4329
|
+
const width = col.bbox.width * wrap.width;
|
|
4330
|
+
const result = L.layoutRange(CUT_START, L.end, width, frameId, false, mirroredOf(page));
|
|
4331
|
+
if (attempt === 0 && Math.ceil((result.totalHeight + gap - 0.01) / bodyStyle.lineHeightPx) < settings.minLinesBeside) {
|
|
4332
|
+
note('fewLines');
|
|
4333
|
+
return false;
|
|
4334
|
+
}
|
|
4335
|
+
const spacing = col.blocks.length === 0 ? 0 : pendingSpacing;
|
|
4336
|
+
if (result.totalHeight > col.availableHeight - spacing + 0.01 && col.blocks.length > 0 && attempt < 4) {
|
|
4337
|
+
if (attempt === 0)
|
|
4338
|
+
note('moved');
|
|
4339
|
+
pendingSpacing = 0;
|
|
4340
|
+
advanceToNextColumn(doc, cursor, geomResolved, contentArea, pageWidthPx, pageHeightPx, onNewPage);
|
|
4341
|
+
continue;
|
|
4342
|
+
}
|
|
4343
|
+
const top = columnCursorY(col) + spacing;
|
|
4344
|
+
const x = wrap.side === 'left' ? col.bbox.x : col.bbox.x + col.bbox.width - width;
|
|
4345
|
+
const frame = result.frame;
|
|
4346
|
+
stampCalloutSource(frame, startIdx, plan);
|
|
4347
|
+
frame.pageIndex = page.index;
|
|
4348
|
+
frame.columnIndex = col.index;
|
|
4349
|
+
offsetCalloutToAbsolute(result, x, top);
|
|
4350
|
+
const floats = (page.floats ??= []);
|
|
4351
|
+
doc.blocks.push(frame);
|
|
4352
|
+
floats.push(frame);
|
|
4353
|
+
for (const child of result.children) {
|
|
4354
|
+
child.pageIndex = page.index;
|
|
4355
|
+
child.columnIndex = col.index;
|
|
4356
|
+
doc.blocks.push(child);
|
|
4357
|
+
floats.push(child);
|
|
4358
|
+
}
|
|
4359
|
+
const bottomRel = Math.ceil((top - col.bbox.y + result.totalHeight + gap - 0.01) / baselineGrid) * baselineGrid;
|
|
4360
|
+
col.availableHeight = Math.max(0, Math.min(col.availableHeight, col.bbox.height - bottomRel));
|
|
4361
|
+
pendingSpacing = 0;
|
|
4362
|
+
inlineGapOwed = null;
|
|
4363
|
+
const exWidth = wrapExclusionWidth(col.bbox.width, width, gap);
|
|
4364
|
+
pendingWrap = {
|
|
4365
|
+
col,
|
|
4366
|
+
blocks: col.blocks.length,
|
|
4367
|
+
cursorY: columnCursorY(col),
|
|
4368
|
+
ex: {
|
|
4369
|
+
x: wrap.side === 'left' ? col.bbox.x : col.bbox.x + col.bbox.width - exWidth,
|
|
4370
|
+
y: top,
|
|
4371
|
+
width: exWidth,
|
|
4372
|
+
height: col.bbox.y + bottomRel - top,
|
|
4373
|
+
side: wrap.side,
|
|
4374
|
+
gap: exWidth - width,
|
|
4375
|
+
ownerId: frame.id,
|
|
4376
|
+
},
|
|
4377
|
+
};
|
|
4378
|
+
return true;
|
|
4379
|
+
}
|
|
4380
|
+
};
|
|
4069
4381
|
const placeCalloutInline = (startIdx, plan) => {
|
|
4070
4382
|
const style = pickCalloutStyle(calloutStyles, plan.attrs.type);
|
|
4071
4383
|
const firstFrameId = `block-${blockIdCounter++}`;
|
|
@@ -4094,7 +4406,17 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4094
4406
|
const inFlow = tallerThanColumn(L.layoutRange(CUT_START, L.end, width, 'float-probe', false))
|
|
4095
4407
|
&& splitCalloutFragment(L, CUT_START, width, contentArea.height, 'float-probe', false, style.splitMinLines) !== null;
|
|
4096
4408
|
if (!inFlow) {
|
|
4097
|
-
|
|
4409
|
+
const wrap = pageIsVertical(page) ? undefined : resolveCalloutWrap(plan.attrs, resolved.layout.wrap);
|
|
4410
|
+
enqueueCalloutFloat(startIdx, plan, style, L, placement, span, wrap);
|
|
4411
|
+
calloutsOutOfFlow.add(startIdx);
|
|
4412
|
+
return undefined;
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
// A box text wraps round (#627): at its side of the column, the text
|
|
4416
|
+
// after it beside it.
|
|
4417
|
+
if (placement === 'here' && span === 'column' && !pageIsVertical(doc.pages[cursor.pageIndex])) {
|
|
4418
|
+
const wrap = resolveCalloutWrap(plan.attrs, resolved.layout.wrap);
|
|
4419
|
+
if (wrap && placeCalloutWrapped(startIdx, plan, style, L, firstFrameId, wrap)) {
|
|
4098
4420
|
calloutsOutOfFlow.add(startIdx);
|
|
4099
4421
|
return undefined;
|
|
4100
4422
|
}
|
|
@@ -4433,6 +4755,29 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4433
4755
|
releaseStaleCitationGates(blockIdx);
|
|
4434
4756
|
tryPlacePendingFloatsOnCurrentPage(spanBoxAt(blockIdx), blockIdx);
|
|
4435
4757
|
enqueueFloatsFor(blockIdx);
|
|
4758
|
+
// Text wrap (#627): running text after a wrapped figure takes the band
|
|
4759
|
+
// the figure was set in back, and runs beside it; anything else that
|
|
4760
|
+
// takes room from the flow goes under what text wraps round first.
|
|
4761
|
+
// Markers and the `::resource` line of a floated resource leave it.
|
|
4762
|
+
if (!transparentToWrap(rawBlock) && !(rawBlock.type === 'resourceBlock' && rawBlock.resourceId !== undefined && floatedIds.has(rawBlock.resourceId))) {
|
|
4763
|
+
const wrapCol = currentColumn(doc, cursor);
|
|
4764
|
+
const beside = runsBesideWrap(rawBlock);
|
|
4765
|
+
if (pendingWrap) {
|
|
4766
|
+
const { col, blocks, cursorY, ex } = pendingWrap;
|
|
4767
|
+
pendingWrap = null;
|
|
4768
|
+
if (beside && col === wrapCol && col.blocks.length === blocks && Math.abs(columnCursorY(col) - cursorY) < 0.01) {
|
|
4769
|
+
// The cursor goes back to the figure's top; the text starts there.
|
|
4770
|
+
col.availableHeight += columnCursorY(col) - ex.y;
|
|
4771
|
+
pendingSpacing = 0;
|
|
4772
|
+
inlineGapOwed = null;
|
|
4773
|
+
openColumnWrap(col, ex, 'top');
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
if (!beside && settleColumnWraps(wrapCol)) {
|
|
4777
|
+
pendingSpacing = 0;
|
|
4778
|
+
inlineGapOwed = null;
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4436
4781
|
// --- Directives ----------------------------------------------------
|
|
4437
4782
|
// (A comic page, `:::page`, and a strip, `:::strip`, are placed below,
|
|
4438
4783
|
// once a closed part or paper run has taken its page break.)
|
|
@@ -4881,6 +5226,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4881
5226
|
if (!measuredBlock)
|
|
4882
5227
|
continue;
|
|
4883
5228
|
const { kind, contentBlock, measured, prefixLen, absoluteSourceMap, mathDisplayRender } = measuredBlock;
|
|
5229
|
+
/** The drop cap opening the paragraph (#623): stamped on the fragment
|
|
5230
|
+
* that holds its first line. A paragraph broken again for a column of
|
|
5231
|
+
* another width takes the one of that setting. */
|
|
5232
|
+
let capInfo = measuredBlock.dropCap;
|
|
4884
5233
|
/** Tracking the block's lines are set with; a paragraph moved whole into
|
|
4885
5234
|
* a column of another width is measured again, and may change it. */
|
|
4886
5235
|
let { letterSpacingPx } = measuredBlock;
|
|
@@ -4922,12 +5271,55 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4922
5271
|
segments: [],
|
|
4923
5272
|
isLastLine: true,
|
|
4924
5273
|
}];
|
|
5274
|
+
// Text wrap (#627): a picture the text after it may run beside, at
|
|
5275
|
+
// its side of the column — unless the text left beside it would be
|
|
5276
|
+
// too narrow, or the picture too short to set lines beside.
|
|
5277
|
+
const wrapSettings = blockMeasureCtx.resolved.layout.wrap;
|
|
5278
|
+
const wrapSpec = kind.resource && !pageIsVertical(blockPage)
|
|
5279
|
+
? resolveResourceWrap(kind.resource, kind.resourceType, wrapSettings)
|
|
5280
|
+
: undefined;
|
|
5281
|
+
// Vertical text keeps its figures' bands whole (horizontal wrap only).
|
|
5282
|
+
if (kind.resource && pageIsVertical(blockPage) && resolveResourceWrap(kind.resource, kind.resourceType, wrapSettings)) {
|
|
5283
|
+
textWrapNotes.push({
|
|
5284
|
+
kind: 'textWrap', reason: 'verticalText', resourceId: kind.resource.id,
|
|
5285
|
+
sourceStart: rawBlock.sourceStart + bodyOffset, sourceEnd: rawBlock.sourceEnd + bodyOffset, pageIndex: cursor.pageIndex,
|
|
5286
|
+
});
|
|
5287
|
+
}
|
|
5288
|
+
let wrapGap = 0;
|
|
5289
|
+
let wraps = false;
|
|
5290
|
+
if (wrapSpec) {
|
|
5291
|
+
wrapGap = wrapGapPx(wrapSpec.gap, dpi, bodyStyle.fontSizePx, floatGapPx);
|
|
5292
|
+
const textWidth = col.bbox.width * (1 - wrapSpec.width) - wrapGap;
|
|
5293
|
+
const linesBeside = Math.ceil((groupHeight + wrapGap - 0.01) / bodyStyle.lineHeightPx);
|
|
5294
|
+
const reason = textWidth + 0.01 < minTextWidthPx(wrapSettings.minTextWidth, col.bbox.width, dpi, bodyStyle.fontSizePx)
|
|
5295
|
+
? 'tooNarrow'
|
|
5296
|
+
: linesBeside < wrapSettings.minLinesBeside ? 'fewLines' : undefined;
|
|
5297
|
+
if (reason) {
|
|
5298
|
+
textWrapNotes.push({
|
|
5299
|
+
kind: 'textWrap', reason, resourceId: kind.resource.id,
|
|
5300
|
+
sourceStart: rawBlock.sourceStart + bodyOffset, sourceEnd: rawBlock.sourceEnd + bodyOffset, pageIndex: cursor.pageIndex,
|
|
5301
|
+
});
|
|
5302
|
+
}
|
|
5303
|
+
else {
|
|
5304
|
+
wraps = true;
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
4925
5307
|
// An inline resource keeps the float gap (a line) above it, as a
|
|
4926
|
-
// float would, unless the block before asked for more.
|
|
4927
|
-
|
|
5308
|
+
// float would, unless the block before asked for more. A wrapped one
|
|
5309
|
+
// stands level with the next line's top instead.
|
|
5310
|
+
const spacingBefore = wraps ? pendingSpacing : Math.max(pendingSpacing, floatGapPx);
|
|
5311
|
+
const columnBefore = col;
|
|
4928
5312
|
enterBand(blockIdx, 0);
|
|
4929
5313
|
placeAtomicBlock(blk, groupHeight, spacingBefore, cursor, doc, geomResolved, contentArea, pageWidthPx, pageHeightPx, onAtomicNewPage(groupHeight));
|
|
4930
5314
|
enterBand(blockIdx, 0);
|
|
5315
|
+
// Too tall for the room left in its column, a wrapped picture moved
|
|
5316
|
+
// on with its anchor: say so.
|
|
5317
|
+
if (wraps && currentColumn(doc, cursor) !== columnBefore && columnBefore.blocks.length > 0) {
|
|
5318
|
+
textWrapNotes.push({
|
|
5319
|
+
kind: 'textWrap', reason: 'moved', resourceId: kind.resource.id,
|
|
5320
|
+
sourceStart: rawBlock.sourceStart + bodyOffset, sourceEnd: rawBlock.sourceEnd + bodyOffset, pageIndex: cursor.pageIndex,
|
|
5321
|
+
});
|
|
5322
|
+
}
|
|
4931
5323
|
// `placeBlockInColumn` (inside placeAtomicBlock) shifts `blk.lines`; the
|
|
4932
5324
|
// resource's own caption/table lines live on `resourceBlock` and must be
|
|
4933
5325
|
// offset to absolute page coordinates here using the placed bbox origin.
|
|
@@ -4938,6 +5330,34 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4938
5330
|
addBalanceExtra(currentColumn(doc, cursor), grown);
|
|
4939
5331
|
}
|
|
4940
5332
|
doc.blocks.push(blk);
|
|
5333
|
+
// A wrapped picture (#627): the flow goes on under it and its gap, on
|
|
5334
|
+
// a grid line, unless the next block takes the band back to run
|
|
5335
|
+
// beside it (see `pendingWrap`).
|
|
5336
|
+
if (wraps && wrapSpec) {
|
|
5337
|
+
const rCol = currentColumn(doc, cursor);
|
|
5338
|
+
const top = blk.bbox.y;
|
|
5339
|
+
const bottomRel = Math.ceil((top - rCol.bbox.y + groupHeight + wrapGap - 0.01) / baselineGrid) * baselineGrid;
|
|
5340
|
+
rCol.availableHeight = Math.max(0, Math.min(rCol.availableHeight, rCol.bbox.height - bottomRel));
|
|
5341
|
+
pendingSpacing = 0;
|
|
5342
|
+
inlineGapOwed = null;
|
|
5343
|
+
const exWidth = wrapExclusionWidth(rCol.bbox.width, rCol.bbox.width * wrapSpec.width, wrapGap);
|
|
5344
|
+
pendingWrap = {
|
|
5345
|
+
col: rCol,
|
|
5346
|
+
blocks: rCol.blocks.length,
|
|
5347
|
+
cursorY: columnCursorY(rCol),
|
|
5348
|
+
ex: {
|
|
5349
|
+
x: wrapSpec.side === 'left' ? rCol.bbox.x : rCol.bbox.x + rCol.bbox.width - exWidth,
|
|
5350
|
+
y: top,
|
|
5351
|
+
width: exWidth,
|
|
5352
|
+
height: rCol.bbox.y + bottomRel - top,
|
|
5353
|
+
side: wrapSpec.side,
|
|
5354
|
+
gap: exWidth - rCol.bbox.width * wrapSpec.width,
|
|
5355
|
+
ownerId: blk.id,
|
|
5356
|
+
},
|
|
5357
|
+
};
|
|
5358
|
+
flushPendingNumberingAtBoundary();
|
|
5359
|
+
continue;
|
|
5360
|
+
}
|
|
4941
5361
|
// Snap the flow position after the resource to the baseline grid (the
|
|
4942
5362
|
// group height is arbitrary), baking in at least marginBottom — same
|
|
4943
5363
|
// convention as snapped headings — so the following text lands back on
|
|
@@ -4965,6 +5385,32 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4965
5385
|
continue;
|
|
4966
5386
|
}
|
|
4967
5387
|
const finalizeListItem = (blk, isFirstPart) => {
|
|
5388
|
+
// The drop cap (#623) stands on the baseline of the line it sinks to,
|
|
5389
|
+
// beside the first fragment only.
|
|
5390
|
+
const capLine = blk.lines[0];
|
|
5391
|
+
if (isFirstPart && capInfo && capLine) {
|
|
5392
|
+
const cap = capInfo;
|
|
5393
|
+
blk.dropCap = {
|
|
5394
|
+
text: cap.text,
|
|
5395
|
+
fontString: cap.fontString,
|
|
5396
|
+
color: cap.color,
|
|
5397
|
+
x: blk.bbox.x + cap.x,
|
|
5398
|
+
baselineY: capLine.baseline + (cap.sink - 1) * style.lineHeightPx,
|
|
5399
|
+
width: cap.width,
|
|
5400
|
+
fontSizePx: cap.fontSizePx,
|
|
5401
|
+
lines: cap.lines,
|
|
5402
|
+
sink: cap.sink,
|
|
5403
|
+
...(cap.sourceStart !== undefined ? { sourceStart: cap.sourceStart, sourceEnd: cap.sourceEnd } : {}),
|
|
5404
|
+
plainStart: cap.plainStart,
|
|
5405
|
+
plainEnd: cap.plainEnd,
|
|
5406
|
+
word: cap.word,
|
|
5407
|
+
wordRest: cap.wordRest,
|
|
5408
|
+
...(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 } } : {}),
|
|
5409
|
+
};
|
|
5410
|
+
// The block's source range starts at the initial.
|
|
5411
|
+
if (cap.sourceStart !== undefined && (blk.sourceStart === undefined || cap.sourceStart < blk.sourceStart))
|
|
5412
|
+
blk.sourceStart = cap.sourceStart;
|
|
5413
|
+
}
|
|
4968
5414
|
if (!listBullet)
|
|
4969
5415
|
return;
|
|
4970
5416
|
blk.listDepth = listDepth;
|
|
@@ -4979,7 +5425,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
4979
5425
|
// `bulletXOffsetInColumn` is `indentPx` for unordered/task, and
|
|
4980
5426
|
// `indentPx + (maxNumberWidth - thisNumberWidth)` for ordered — giving
|
|
4981
5427
|
// the right-aligned separator.
|
|
4982
|
-
|
|
5428
|
+
// Beside a wrapped picture on the left (#627) the marker stands past
|
|
5429
|
+
// it, with its text.
|
|
5430
|
+
blk.bulletOffsetX = blk.bbox.x + bulletXOffsetInColumn + lineInsetsAt(wrapInsets, 0).start;
|
|
4983
5431
|
if (listBullet.separatorText !== undefined) {
|
|
4984
5432
|
blk.separatorText = listBullet.separatorText;
|
|
4985
5433
|
blk.separatorFontString = listBullet.separatorFontString;
|
|
@@ -5035,7 +5483,12 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5035
5483
|
(vdtType === 'paragraph' && isContainerTail && paragraphContainer?.snapToGrid !== false) ||
|
|
5036
5484
|
// A poem, like a container of one block: its own leading and the
|
|
5037
5485
|
// margins of its style are off the grid, the text after it is not.
|
|
5038
|
-
|
|
5486
|
+
// A stanza between two of a poem in the line layout snaps too, so the
|
|
5487
|
+
// space between stanzas is whole grid lines (#620); one of a poem set
|
|
5488
|
+
// as single hemistichs (a configuration stored before #620) does not,
|
|
5489
|
+
// as the one block postext 1.22 set it in did not.
|
|
5490
|
+
(vdtType === 'paragraph' && rawBlock.verse !== undefined && verseSnaps(rawBlock.verse.attrs.style, resolved)
|
|
5491
|
+
&& (rawBlock.verse.stanza === undefined || rawBlock.verse.stanza.last || versesLineByLine(rawBlock, resolved))) ||
|
|
5039
5492
|
vdtType === 'mathDisplay');
|
|
5040
5493
|
// Place block, splitting across columns/pages if needed.
|
|
5041
5494
|
// List items may split too — orphan/widow protection per-list is gated by
|
|
@@ -5072,6 +5525,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5072
5525
|
* default opener paints across the page. */
|
|
5073
5526
|
let brokenForWidth = measureWidth;
|
|
5074
5527
|
let laterWidths = [];
|
|
5528
|
+
/** Text wrap (#627): the lines of the block set short beside what text
|
|
5529
|
+
* wraps round, as its lines are now set (paragraph line numbers). */
|
|
5530
|
+
let wrapInsets = [];
|
|
5531
|
+
const wrapsBeside = canSplit && runsBesideWrap(rawBlock);
|
|
5075
5532
|
const rebreaksForColumn = !tryLoose && rawBlock.toc === undefined && rawBlock.index === undefined
|
|
5076
5533
|
&& (canSplit || (vdtType === 'heading' && !opensDefaultOpener(rawBlock)));
|
|
5077
5534
|
/** Measure the block again for the columns it is set in (see
|
|
@@ -5079,6 +5536,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5079
5536
|
const measureForColumns = (extra) => measureContentBlock(rawBlock, blockIdx, brokenForWidth, blockMeasureCtx, {
|
|
5080
5537
|
styleOverride,
|
|
5081
5538
|
...(laterWidths.length > 0 ? { restColumnWidths: laterWidths } : {}),
|
|
5539
|
+
...(wrapInsets.length > 0 ? { lineInsets: wrapInsets } : {}),
|
|
5082
5540
|
...extra,
|
|
5083
5541
|
});
|
|
5084
5542
|
// "Keep with next" for colon-introduced lists: a paragraph ending in `:`
|
|
@@ -5098,6 +5556,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5098
5556
|
while (remainingLines.length > 0) {
|
|
5099
5557
|
enterBand(blockIdx, partIndex);
|
|
5100
5558
|
const curCol = currentColumn(doc, cursor);
|
|
5559
|
+
// A wrapped float at the head or foot of this column (#627): running
|
|
5560
|
+
// text takes its band back to run beside it.
|
|
5561
|
+
if (wrapsBeside)
|
|
5562
|
+
openFloatWraps(curCol);
|
|
5101
5563
|
// The rest of the block goes into a column of another width than the
|
|
5102
5564
|
// one it was broken for (EF-157): break it again for this one. Up to
|
|
5103
5565
|
// postext 1.4 it kept the lines of the column it started in, and a
|
|
@@ -5109,8 +5571,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5109
5571
|
// Nothing placed yet: the whole block, for this column.
|
|
5110
5572
|
const again = measureContentBlock(rawBlock, blockIdx, curCol.bbox.width, blockMeasureCtx, { styleOverride });
|
|
5111
5573
|
if (again && again.measured.lines.length > 0) {
|
|
5574
|
+
capInfo = again.dropCap;
|
|
5112
5575
|
brokenForWidth = curCol.bbox.width;
|
|
5113
5576
|
laterWidths = [];
|
|
5577
|
+
wrapInsets = [];
|
|
5114
5578
|
letterSpacingPx = again.letterSpacingPx;
|
|
5115
5579
|
remainingLines = [...raggedLooseLines(again.measured.lines, style.textAlign)];
|
|
5116
5580
|
paragraphLines = remainingLines;
|
|
@@ -5129,8 +5593,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5129
5593
|
const keepBreaks = paragraphBreaks && paragraphBreaks.at.length >= placed
|
|
5130
5594
|
? { path: paragraphBreaks.path, at: paragraphBreaks.at.slice(0, placed) }
|
|
5131
5595
|
: undefined;
|
|
5596
|
+
const keptInsets = insetsBefore(wrapInsets, placed);
|
|
5132
5597
|
const again = measureContentBlock(rawBlock, blockIdx, brokenForWidth, blockMeasureCtx, {
|
|
5133
5598
|
styleOverride, restColumnWidths: steps, ...(keepBreaks ? { keepBreaks } : {}),
|
|
5599
|
+
...(keptInsets.length > 0 ? { lineInsets: keptInsets } : {}),
|
|
5134
5600
|
});
|
|
5135
5601
|
const lines = again && (again.letterSpacingPx ?? 0) === (letterSpacingPx ?? 0)
|
|
5136
5602
|
? [...raggedLooseLines(again.measured.lines, style.textAlign)]
|
|
@@ -5138,6 +5604,7 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5138
5604
|
if (lines && lines.length > placed
|
|
5139
5605
|
&& paragraphLines.slice(0, placed).every((l, i) => lines[i].text === l.text && lines[i].hyphenated === l.hyphenated)) {
|
|
5140
5606
|
laterWidths = steps;
|
|
5607
|
+
wrapInsets = keptInsets;
|
|
5141
5608
|
paragraphLines = lines;
|
|
5142
5609
|
paragraphBreaks = again.measured.breaks;
|
|
5143
5610
|
remainingLines = lines.slice(placed);
|
|
@@ -5244,6 +5711,58 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5244
5711
|
balancing = { levers: [vdtType === 'heading' ? 'heading' : 'afterFloat'], spaceAbove: extraPx };
|
|
5245
5712
|
}
|
|
5246
5713
|
}
|
|
5714
|
+
// A raised initial (#623) keeps its rise clear above the paragraph,
|
|
5715
|
+
// in whole grid lines.
|
|
5716
|
+
if (partIndex === 0 && capInfo && capInfo.rise > 0.5) {
|
|
5717
|
+
spacingBefore += Math.ceil((capInfo.rise - 0.5) / baselineGrid) * baselineGrid;
|
|
5718
|
+
}
|
|
5719
|
+
// Text wrap (#627): the lines this block would set beside what text
|
|
5720
|
+
// wraps round in this column are broken short of it; the lines it
|
|
5721
|
+
// already placed elsewhere keep their breaks. A block that cannot
|
|
5722
|
+
// keep them (a break trace of the other measuring path) goes under
|
|
5723
|
+
// what it would have run beside instead.
|
|
5724
|
+
if (wrapsBeside) {
|
|
5725
|
+
const placed = paragraphLines.length - remainingLines.length;
|
|
5726
|
+
const y0 = columnCursorY(curCol) + spacingBefore;
|
|
5727
|
+
const wanted = wrapInsetSteps(curCol, y0, style.lineHeightPx, placed);
|
|
5728
|
+
if (!sameInsetsFrom(wrapInsets, wanted, placed)) {
|
|
5729
|
+
const steps = [...insetsBefore(wrapInsets, placed), ...wanted];
|
|
5730
|
+
const keepBreaks = placed > 0 && paragraphBreaks && paragraphBreaks.at.length >= placed
|
|
5731
|
+
? { path: paragraphBreaks.path, at: paragraphBreaks.at.slice(0, placed) }
|
|
5732
|
+
: undefined;
|
|
5733
|
+
const again = placed > 0 && !keepBreaks ? null : measureContentBlock(rawBlock, blockIdx, brokenForWidth, blockMeasureCtx, {
|
|
5734
|
+
styleOverride,
|
|
5735
|
+
...(laterWidths.length > 0 ? { restColumnWidths: laterWidths } : {}),
|
|
5736
|
+
...(steps.length > 0 ? { lineInsets: steps } : {}),
|
|
5737
|
+
...(keepBreaks ? { keepBreaks } : {}),
|
|
5738
|
+
});
|
|
5739
|
+
const lines = again && (placed === 0 || (again.letterSpacingPx ?? 0) === (letterSpacingPx ?? 0))
|
|
5740
|
+
? [...raggedLooseLines(again.measured.lines, style.textAlign)]
|
|
5741
|
+
: undefined;
|
|
5742
|
+
if (lines && lines.length > placed
|
|
5743
|
+
&& paragraphLines.slice(0, placed).every((l, i) => lines[i].text === l.text && lines[i].hyphenated === l.hyphenated)) {
|
|
5744
|
+
wrapInsets = steps;
|
|
5745
|
+
paragraphLines = lines;
|
|
5746
|
+
paragraphBreaks = again.measured.breaks;
|
|
5747
|
+
remainingLines = lines.slice(placed);
|
|
5748
|
+
if (placed === 0) {
|
|
5749
|
+
capInfo = again.dropCap;
|
|
5750
|
+
letterSpacingPx = again.letterSpacingPx;
|
|
5751
|
+
guardedEnds = [];
|
|
5752
|
+
columnEndHyphenRetried = false;
|
|
5753
|
+
}
|
|
5754
|
+
regateCitedFloats(blockIdx, lines);
|
|
5755
|
+
}
|
|
5756
|
+
else if (wanted.length > 0) {
|
|
5757
|
+
if (balancing && balancing.spaceAbove > 0) {
|
|
5758
|
+
balanceExtraInColumn.set(curCol, (balanceExtraInColumn.get(curCol) ?? 0) - balancing.spaceAbove);
|
|
5759
|
+
}
|
|
5760
|
+
settleColumnWraps(curCol);
|
|
5761
|
+
pendingSpacing = 0;
|
|
5762
|
+
continue;
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5247
5766
|
// A loose paragraph's extra line is balancing height too (it lands
|
|
5248
5767
|
// whole in this column — loose candidates are never split parts).
|
|
5249
5768
|
const looseTracking = looseOutcome.get(blockIdx);
|
|
@@ -5281,6 +5800,11 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5281
5800
|
// actual content bottom so the reserved block height (and the
|
|
5282
5801
|
// subsequent marginBottom + grid snap) starts from there.
|
|
5283
5802
|
let effectiveRemainHeight = totalRemainHeight;
|
|
5803
|
+
// A paragraph shorter than its drop cap sinks (#623, `'reserve'`):
|
|
5804
|
+
// the block keeps the room under the initial.
|
|
5805
|
+
if (partIndex === 0 && capInfo && capInfo.minLines > remainingLines.length) {
|
|
5806
|
+
effectiveRemainHeight = capInfo.minLines * style.lineHeightPx;
|
|
5807
|
+
}
|
|
5284
5808
|
/** How far the lines of a 行取り heading go down its band (#424). */
|
|
5285
5809
|
let lineSpanShift = 0;
|
|
5286
5810
|
if (lineSpan !== undefined && partIndex === 0) {
|
|
@@ -5557,9 +6081,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5557
6081
|
? Math.ceil((naturalBottom - 0.01) / baselineGrid) * baselineGrid
|
|
5558
6082
|
: naturalBottom;
|
|
5559
6083
|
const remainAfterHeading = curCol.bbox.height - snappedBottom;
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
: 1;
|
|
6084
|
+
// A drop cap opening the text (#623) needs its lines under it.
|
|
6085
|
+
const minLinesNeeded = Math.max(resolved.bodyText.avoidWidows ? Math.max(1, resolved.bodyText.widowMinLines) : 1, dropCapSinkAfter(blockIdx, blockMeasureCtx));
|
|
5563
6086
|
// A `:::space` between the heading and its text needs room too.
|
|
5564
6087
|
const minSpaceAfter = minLinesNeeded * bodyStyle.lineHeightPx
|
|
5565
6088
|
+ spaceLinesAfter(contentBlocks, blockIdx) * baselineGrid;
|
|
@@ -5725,7 +6248,8 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5725
6248
|
// (the no-fit branch below rolls it back).
|
|
5726
6249
|
const headingRun = partIndex === 0 ? trailingHeadingRun(curCol) : 0;
|
|
5727
6250
|
if (choice.splitAt === 0 && headingRun > 0 && headingRun < curCol.blocks.length) {
|
|
5728
|
-
|
|
6251
|
+
// A drop cap's lines stay with it (#623).
|
|
6252
|
+
const minKeep = Math.max(effectiveAvoidWidows ? Math.max(1, resolved.bodyText.widowMinLines) : 1, partIndex === 0 && capInfo ? capInfo.sink : 1);
|
|
5729
6253
|
const minTail = effectiveAvoidOrphans && resolved.headings.keepWithNextSplit === 'rules'
|
|
5730
6254
|
? Math.max(1, resolved.bodyText.orphanMinLines)
|
|
5731
6255
|
: 1;
|
|
@@ -5848,11 +6372,27 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
5848
6372
|
}
|
|
5849
6373
|
}
|
|
5850
6374
|
// A poem's bayt set staggered over two lines (or more) stays in one
|
|
5851
|
-
// column (#378)
|
|
5852
|
-
// the
|
|
5853
|
-
|
|
6375
|
+
// column (#378), and so does a line of verse with its turnovers
|
|
6376
|
+
// (#620): the cut goes before it — in an empty column, which the
|
|
6377
|
+
// poem cannot leave, after its first line at least.
|
|
6378
|
+
while (choice.splitAt > (curCol.blocks.length === 0 ? 1 : 0) && sameVerseLine(remainingLines[choice.splitAt - 1], remainingLines[choice.splitAt])) {
|
|
5854
6379
|
choice = { splitAt: choice.splitAt - 1, demerit: choice.demerit };
|
|
5855
6380
|
}
|
|
6381
|
+
// A paragraph a drop cap opens never breaks before the initial's
|
|
6382
|
+
// last line (#623): it moves on whole, unless it is alone in an
|
|
6383
|
+
// empty column too short for those lines, where it breaks all the
|
|
6384
|
+
// same (and says so).
|
|
6385
|
+
if (partIndex === 0 && capInfo && choice.splitAt > 0 && choice.splitAt < Math.min(capInfo.sink, remainingLines.length)) {
|
|
6386
|
+
if (curCol.blocks.length > 0)
|
|
6387
|
+
choice = { splitAt: 0, demerit: choice.demerit };
|
|
6388
|
+
else
|
|
6389
|
+
blockMeasureCtx.dropCapNotes?.set(blockIdx, { reason: 'split' });
|
|
6390
|
+
}
|
|
6391
|
+
// A stanza `keepStanzas` covers (a haiku, a tanka) moves on whole,
|
|
6392
|
+
// unless the column is empty: it cannot do better anywhere.
|
|
6393
|
+
if (choice.splitAt > 0 && partIndex === 0 && curCol.blocks.length > 0 && keepsStanzaWhole(rawBlock, resolved)) {
|
|
6394
|
+
choice = { splitAt: 0, demerit: choice.demerit };
|
|
6395
|
+
}
|
|
5856
6396
|
if (choice.splitAt > 0) {
|
|
5857
6397
|
// Consume spacing (negative: a container margin pulling the block up)
|
|
5858
6398
|
if (spacingBefore !== 0) {
|
|
@@ -6121,6 +6661,9 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
6121
6661
|
.map((s) => s.startPageIndex);
|
|
6122
6662
|
if (restarts.length > 0)
|
|
6123
6663
|
doc.pageNumberRestarts = restarts;
|
|
6664
|
+
// Line numbers (#621): the lines of this pass counted where they landed,
|
|
6665
|
+
// before the running heads (whose palettes recolour them).
|
|
6666
|
+
buildLineNumbers(doc, contentBlocks, continuation);
|
|
6124
6667
|
buildHeadersAndFooters(doc, resourceById, {
|
|
6125
6668
|
// Where two palette entries share a base value, each kind of flow
|
|
6126
6669
|
// colour follows its own settings under a part or section palette.
|
|
@@ -6137,6 +6680,10 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
6137
6680
|
return text.length > 0 ? { styleId, text } : undefined;
|
|
6138
6681
|
},
|
|
6139
6682
|
});
|
|
6683
|
+
// The line numbers of code listings (#624), in the same slot, after the
|
|
6684
|
+
// margin numbers' palette pass: they keep `codeStyle.lineNumberColor`.
|
|
6685
|
+
if (codeBoxes.wrapped)
|
|
6686
|
+
buildCodeLineNumbers(doc);
|
|
6140
6687
|
// Vertical pages: the axis each font's upright characters turn about.
|
|
6141
6688
|
if (doc.pages.some((p) => pageIsVertical(p)))
|
|
6142
6689
|
stampCentralBaselines(doc);
|
|
@@ -6148,6 +6695,17 @@ function placeDocumentPass(content, config, cache, options, hints) {
|
|
|
6148
6695
|
doc.iterationCount = 1;
|
|
6149
6696
|
if (joiningLetterSpacing.size > 0)
|
|
6150
6697
|
joiningSpacingWarnings.set(doc, joiningLetterSpacingWarnings(doc, joiningLetterSpacing));
|
|
6698
|
+
if (dropCapNotes.size > 0)
|
|
6699
|
+
dropCapWarningsByDoc.set(doc, dropCapWarnings(doc, dropCapNotes));
|
|
6700
|
+
// Pictures scaled to the room of their slot (#626), at the block citing them.
|
|
6701
|
+
const shrunk = floatShrinkWarnings(doc, (idx) => {
|
|
6702
|
+
const b = contentBlocks[idx];
|
|
6703
|
+
return b ? { start: b.sourceStart + bodyOffset, end: b.sourceEnd + bodyOffset } : undefined;
|
|
6704
|
+
});
|
|
6705
|
+
if (shrunk.length > 0)
|
|
6706
|
+
floatShrinkWarningsByDoc.set(doc, shrunk);
|
|
6707
|
+
if (textWrapNotes.length > 0)
|
|
6708
|
+
textWrapWarningsByDoc.set(doc, textWrapNotes);
|
|
6151
6709
|
return { doc, forcedBreakPages, bandCapProposals, spanPlacedInBand, bandCapsApplied, looseOutcome, captionUnderProposals };
|
|
6152
6710
|
}
|
|
6153
6711
|
/** Passes a document printing its own contents gets at most, beyond the
|
|
@@ -6207,7 +6765,10 @@ export function* buildDocumentGen(content, config, cache, options) {
|
|
|
6207
6765
|
// Justified CJK lines the composer could not fill within its tracking
|
|
6208
6766
|
// cap; words of a joining script that run past their line, and styles
|
|
6209
6767
|
// whose letter-spacing such words do not take.
|
|
6210
|
-
|
|
6768
|
+
// Line numbers set over a float of the side column (#621).
|
|
6769
|
+
const loose = [...cjkLooseLineWarnings(doc), ...wordOverflowWarnings(doc), ...(joiningSpacingWarnings.get(doc) ?? []), ...(dropCapWarningsByDoc.get(doc) ?? []), ...lineNumberWarnings(doc), ...codeOverflowWarnings(doc), ...(floatShrinkWarningsByDoc.get(doc) ?? []), ...(textWrapWarningsByDoc.get(doc) ?? [])];
|
|
6770
|
+
// Design texts cut to fit their width (#628).
|
|
6771
|
+
loose.push(...designTruncationWarnings(doc));
|
|
6211
6772
|
// Comic panels whose cell cannot hold their picture's safe area (#556).
|
|
6212
6773
|
for (const page of doc.pages) {
|
|
6213
6774
|
if (page.comic)
|
|
@@ -6250,10 +6811,16 @@ const indexWarnings = new WeakMap();
|
|
|
6250
6811
|
/** The `joiningScriptLetterSpacing` warnings of the last pass, per
|
|
6251
6812
|
* document. */
|
|
6252
6813
|
const joiningSpacingWarnings = new WeakMap();
|
|
6814
|
+
/** The `dropCap` warnings of the last pass, per document (#623). */
|
|
6815
|
+
const dropCapWarningsByDoc = new WeakMap();
|
|
6816
|
+
/** The `floatShrunk` warnings of a build (#626), found by its last pass. */
|
|
6817
|
+
const floatShrinkWarningsByDoc = new WeakMap();
|
|
6818
|
+
/** The `textWrap` warnings of a build (#627), found by its last pass. */
|
|
6819
|
+
const textWrapWarningsByDoc = new WeakMap();
|
|
6253
6820
|
/** `doc` with the page of each of its index marks (`doc.indexMarks`). */
|
|
6254
6821
|
function withIndexMarks(doc, content) {
|
|
6255
6822
|
const { content: body, contentOffset } = extractFrontmatter(content.markdown);
|
|
6256
|
-
const blocks = parseMarkdownMemo(body);
|
|
6823
|
+
const blocks = parseMarkdownMemo(body, codeParseOptions(doc.config));
|
|
6257
6824
|
if (content.markdown.includes(':index')) {
|
|
6258
6825
|
const marks = locateIndexMarks(doc, blocks, contentOffset);
|
|
6259
6826
|
if (marks)
|
|
@@ -6274,7 +6841,7 @@ function* buildDocumentRounds(content, config, cache, options) {
|
|
|
6274
6841
|
// follows, and a numbering restart after the front matter usually settles
|
|
6275
6842
|
// it in one extra round.
|
|
6276
6843
|
if (content.outline === undefined) {
|
|
6277
|
-
const parsed = parseMarkdownMemo(extractFrontmatter(content.markdown).content);
|
|
6844
|
+
const parsed = parseMarkdownMemo(extractFrontmatter(content.markdown).content, codeParseOptions(resolveAllConfig(config)));
|
|
6278
6845
|
// A reference printing an anchor's page (#263) settles the same way.
|
|
6279
6846
|
const pageRefs = () => {
|
|
6280
6847
|
if (!hasAnchorRefs(parsed, new Set((content.resources ?? []).map((r) => r.id))))
|
|
@@ -6804,6 +7371,21 @@ function* buildDocumentBalanced(content, config, cache, options, tocRound = 0, f
|
|
|
6804
7371
|
function sameBayt(a, b) {
|
|
6805
7372
|
return a?.verse !== undefined && b?.verse !== undefined && a.verse.bayt === b.verse.bayt;
|
|
6806
7373
|
}
|
|
7374
|
+
/** Whether a column may not break between two lines of verse: they set one
|
|
7375
|
+
* bayt (#378), or `b` is a turnover of the line of verse `a` sets or
|
|
7376
|
+
* turns over (#620). */
|
|
7377
|
+
function sameVerseLine(a, b) {
|
|
7378
|
+
return sameBayt(a, b) || (a?.verseLine !== undefined && b?.verseLine?.turnover === true);
|
|
7379
|
+
}
|
|
7380
|
+
/** Whether a stanza of the line layout is kept whole (#620): it has no
|
|
7381
|
+
* more lines of verse than `keepStanzas` asks for. */
|
|
7382
|
+
function keepsStanzaWhole(raw, resolved) {
|
|
7383
|
+
const stanza = raw.verse?.stanza;
|
|
7384
|
+
if (!stanza || !versesLineByLine(raw, resolved))
|
|
7385
|
+
return false;
|
|
7386
|
+
const keep = verseLinesSettings(raw.verse.attrs, { fontSizePx: 0, lineHeightPx: 0 }, resolved, false).keepStanzas;
|
|
7387
|
+
return keep > 0 && stanza.lines.length <= keep;
|
|
7388
|
+
}
|
|
6807
7389
|
/** Whether the text after a poem goes back to the baseline grid: unless
|
|
6808
7390
|
* the paragraph style its fence names says `snapToGrid: false`. */
|
|
6809
7391
|
function verseSnaps(styleId, resolved) {
|