@stll/folio-core 0.0.1-placeholder.0 → 0.1.1
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/LICENSE +202 -0
- package/NOTICE.md +29 -0
- package/README.md +41 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/ai-edits/apply.d.ts +35 -0
- package/dist/ai-edits/apply.js +683 -0
- package/dist/ai-edits/clean-text.d.ts +28 -0
- package/dist/ai-edits/clean-text.js +23 -0
- package/dist/ai-edits/headless.d.ts +225 -0
- package/dist/ai-edits/headless.js +513 -0
- package/dist/ai-edits/index.d.ts +6 -0
- package/dist/ai-edits/index.js +5 -0
- package/dist/ai-edits/inline-emphasis.d.ts +38 -0
- package/dist/ai-edits/inline-emphasis.js +78 -0
- package/dist/ai-edits/snapshot.d.ts +9 -0
- package/dist/ai-edits/snapshot.js +195 -0
- package/dist/ai-edits/types.d.ts +156 -0
- package/dist/ai-edits/types.js +0 -0
- package/dist/ai-edits/word-diff.d.ts +20 -0
- package/dist/ai-edits/word-diff.js +77 -0
- package/dist/ai-suggestions/apply.d.ts +17 -0
- package/dist/ai-suggestions/apply.js +64 -0
- package/dist/ai-suggestions/conflict.d.ts +22 -0
- package/dist/ai-suggestions/conflict.js +67 -0
- package/dist/ai-suggestions/text-positions.d.ts +21 -0
- package/dist/ai-suggestions/text-positions.js +48 -0
- package/dist/ai-suggestions/types.d.ts +199 -0
- package/dist/ai-suggestions/types.js +59 -0
- package/dist/content-controls/errors.d.ts +52 -0
- package/dist/content-controls/errors.js +36 -0
- package/dist/content-controls/findContentControls.d.ts +55 -0
- package/dist/content-controls/findContentControls.js +75 -0
- package/dist/content-controls/index.d.ts +4 -0
- package/dist/content-controls/index.js +4 -0
- package/dist/content-controls/mutateContentControls.d.ts +51 -0
- package/dist/content-controls/mutateContentControls.js +377 -0
- package/dist/controller/folioEditor.d.ts +21 -0
- package/dist/controller/folioEditor.js +29 -0
- package/dist/controller/folioEditorEvents.d.ts +23 -0
- package/dist/controller/folioEditorEvents.js +24 -0
- package/dist/controller/hiddenEditorApi.d.ts +35 -0
- package/dist/controller/hiddenEditorApi.js +112 -0
- package/dist/controller/hiddenEditorManager.d.ts +93 -0
- package/dist/controller/hiddenEditorManager.js +308 -0
- package/dist/controller/layoutPipeline.d.ts +63 -0
- package/dist/controller/layoutPipeline.js +406 -0
- package/dist/controller/layoutScheduler.d.ts +41 -0
- package/dist/controller/layoutScheduler.js +59 -0
- package/dist/controller/layoutSession.d.ts +24 -0
- package/dist/controller/layoutSession.js +13 -0
- package/dist/docx/blockContentParser.d.ts +13 -0
- package/dist/docx/blockContentParser.js +289 -0
- package/dist/docx/bookmarkParser.d.ts +178 -0
- package/dist/docx/bookmarkParser.js +246 -0
- package/dist/docx/bookmarkPlacement.d.ts +10 -0
- package/dist/docx/bookmarkPlacement.js +50 -0
- package/dist/docx/bulletMarkers.d.ts +4 -0
- package/dist/docx/bulletMarkers.js +42 -0
- package/dist/docx/commentParser.d.ts +34 -0
- package/dist/docx/commentParser.js +127 -0
- package/dist/docx/commentReferenceNormalization.d.ts +25 -0
- package/dist/docx/commentReferenceNormalization.js +97 -0
- package/dist/docx/commentReplyMarkers.d.ts +20 -0
- package/dist/docx/commentReplyMarkers.js +123 -0
- package/dist/docx/compatibility.d.ts +12 -0
- package/dist/docx/compatibility.js +77 -0
- package/dist/docx/documentParser.d.ts +65 -0
- package/dist/docx/documentParser.js +209 -0
- package/dist/docx/drawingUtils.d.ts +56 -0
- package/dist/docx/drawingUtils.js +305 -0
- package/dist/docx/fieldParser.d.ts +228 -0
- package/dist/docx/fieldParser.js +559 -0
- package/dist/docx/footnoteParser.d.ts +109 -0
- package/dist/docx/footnoteParser.js +270 -0
- package/dist/docx/headerFooterParser.d.ts +119 -0
- package/dist/docx/headerFooterParser.js +266 -0
- package/dist/docx/headerFooterRefParser.d.ts +22 -0
- package/dist/docx/headerFooterRefParser.js +52 -0
- package/dist/docx/headerFooterReferenceNormalization.d.ts +19 -0
- package/dist/docx/headerFooterReferenceNormalization.js +64 -0
- package/dist/docx/hyperlinkParser.d.ts +108 -0
- package/dist/docx/hyperlinkParser.js +230 -0
- package/dist/docx/imageParser.d.ts +93 -0
- package/dist/docx/imageParser.js +522 -0
- package/dist/docx/mathToMathml.d.ts +15 -0
- package/dist/docx/mathToMathml.js +357 -0
- package/dist/docx/modelValidation.d.ts +13 -0
- package/dist/docx/modelValidation.js +21 -0
- package/dist/docx/normalizeBaseDirection.d.ts +12 -0
- package/dist/docx/normalizeBaseDirection.js +81 -0
- package/dist/docx/notePropertiesParser.d.ts +16 -0
- package/dist/docx/notePropertiesParser.js +99 -0
- package/dist/docx/numberingParser.d.ts +86 -0
- package/dist/docx/numberingParser.js +735 -0
- package/dist/docx/numberingReferenceNormalization.d.ts +25 -0
- package/dist/docx/numberingReferenceNormalization.js +22 -0
- package/dist/docx/paragraphParser.d.ts +86 -0
- package/dist/docx/paragraphParser.js +1132 -0
- package/dist/docx/paragraphTraversal.d.ts +19 -0
- package/dist/docx/paragraphTraversal.js +76 -0
- package/dist/docx/parser.d.ts +67 -0
- package/dist/docx/parser.js +391 -0
- package/dist/docx/parserEnums.d.ts +45 -0
- package/dist/docx/parserEnums.js +70 -0
- package/dist/docx/relsParser.d.ts +160 -0
- package/dist/docx/relsParser.js +235 -0
- package/dist/docx/replyToComment.d.ts +29 -0
- package/dist/docx/replyToComment.js +73 -0
- package/dist/docx/rezip.d.ts +166 -0
- package/dist/docx/rezip.js +1194 -0
- package/dist/docx/runConsolidator.d.ts +57 -0
- package/dist/docx/runConsolidator.js +224 -0
- package/dist/docx/runParser.d.ts +78 -0
- package/dist/docx/runParser.js +661 -0
- package/dist/docx/sdtProperties.d.ts +15 -0
- package/dist/docx/sdtProperties.js +290 -0
- package/dist/docx/sdtPropertiesPatch.d.ts +16 -0
- package/dist/docx/sdtPropertiesPatch.js +144 -0
- package/dist/docx/sectionParser.d.ts +95 -0
- package/dist/docx/sectionParser.js +534 -0
- package/dist/docx/selectiveSave.d.ts +25 -0
- package/dist/docx/selectiveSave.js +247 -0
- package/dist/docx/selectiveSaveFlags.d.ts +33 -0
- package/dist/docx/selectiveSaveFlags.js +11 -0
- package/dist/docx/selectiveSaveTripwire.d.ts +33 -0
- package/dist/docx/selectiveSaveTripwire.js +79 -0
- package/dist/docx/selectiveXmlPatch.d.ts +103 -0
- package/dist/docx/selectiveXmlPatch.js +493 -0
- package/dist/docx/serializer/blockSdtSerializer.d.ts +6 -0
- package/dist/docx/serializer/blockSdtSerializer.js +88 -0
- package/dist/docx/serializer/borderSerializer.d.ts +25 -0
- package/dist/docx/serializer/borderSerializer.js +44 -0
- package/dist/docx/serializer/commentSerializer.d.ts +31 -0
- package/dist/docx/serializer/commentSerializer.js +184 -0
- package/dist/docx/serializer/documentSerializer.d.ts +65 -0
- package/dist/docx/serializer/documentSerializer.js +199 -0
- package/dist/docx/serializer/headerFooterSerializer.d.ts +12 -0
- package/dist/docx/serializer/headerFooterSerializer.js +84 -0
- package/dist/docx/serializer/noteSerializer.d.ts +19 -0
- package/dist/docx/serializer/noteSerializer.js +70 -0
- package/dist/docx/serializer/numberingSerializer.d.ts +12 -0
- package/dist/docx/serializer/numberingSerializer.js +93 -0
- package/dist/docx/serializer/paragraphSerializer.d.ts +48 -0
- package/dist/docx/serializer/paragraphSerializer.js +559 -0
- package/dist/docx/serializer/runSerializer.d.ts +56 -0
- package/dist/docx/serializer/runSerializer.js +621 -0
- package/dist/docx/serializer/sectionPropertiesSerializer.d.ts +6 -0
- package/dist/docx/serializer/sectionPropertiesSerializer.js +187 -0
- package/dist/docx/serializer/tableSerializer.d.ts +79 -0
- package/dist/docx/serializer/tableSerializer.js +422 -0
- package/dist/docx/serializer/xmlUtils.d.ts +20 -0
- package/dist/docx/serializer/xmlUtils.js +25 -0
- package/dist/docx/settingsParser.d.ts +8 -0
- package/dist/docx/settingsParser.js +36 -0
- package/dist/docx/shapeParser.d.ts +20 -0
- package/dist/docx/shapeParser.js +272 -0
- package/dist/docx/styleParser.d.ts +50 -0
- package/dist/docx/styleParser.js +905 -0
- package/dist/docx/tableParser.d.ts +202 -0
- package/dist/docx/tableParser.js +811 -0
- package/dist/docx/textBoxParser.d.ts +117 -0
- package/dist/docx/textBoxParser.js +262 -0
- package/dist/docx/themeParser.d.ts +77 -0
- package/dist/docx/themeParser.js +330 -0
- package/dist/docx/trackedMoveRangeNormalization.d.ts +22 -0
- package/dist/docx/trackedMoveRangeNormalization.js +100 -0
- package/dist/docx/unzip.d.ts +118 -0
- package/dist/docx/unzip.js +383 -0
- package/dist/docx/vmlImageParser.d.ts +16 -0
- package/dist/docx/vmlImageParser.js +100 -0
- package/dist/docx/watermarkParser.d.ts +45 -0
- package/dist/docx/watermarkParser.js +348 -0
- package/dist/docx/wrapTypes.d.ts +29 -0
- package/dist/docx/wrapTypes.js +48 -0
- package/dist/docx/xmlParser.d.ts +297 -0
- package/dist/docx/xmlParser.js +527 -0
- package/dist/fields/bookmarkPages.d.ts +13 -0
- package/dist/fields/bookmarkPages.js +66 -0
- package/dist/fields/bookmarkText.d.ts +16 -0
- package/dist/fields/bookmarkText.js +61 -0
- package/dist/fields/evaluateField.d.ts +23 -0
- package/dist/fields/evaluateField.js +77 -0
- package/dist/fields/fieldContext.d.ts +21 -0
- package/dist/fields/fieldContext.js +0 -0
- package/dist/fields/resolveFieldValues.d.ts +44 -0
- package/dist/fields/resolveFieldValues.js +204 -0
- package/dist/fields/sectionPageCounts.d.ts +11 -0
- package/dist/fields/sectionPageCounts.js +16 -0
- package/dist/fields/seqValues.d.ts +14 -0
- package/dist/fields/seqValues.js +36 -0
- package/dist/fonts/embeddedFontTable.d.ts +42 -0
- package/dist/fonts/embeddedFontTable.js +80 -0
- package/dist/fonts/embeddedFonts.d.ts +46 -0
- package/dist/fonts/embeddedFonts.js +112 -0
- package/dist/fonts/fontDeobfuscation.d.ts +30 -0
- package/dist/fonts/fontDeobfuscation.js +57 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +23 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +57 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +316 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +74 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +432 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.d.ts +36 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +176 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +65 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +1309 -0
- package/dist/layout-bridge/convert/wrapEditorViewAsSurface.d.ts +17 -0
- package/dist/layout-bridge/convert/wrapEditorViewAsSurface.js +36 -0
- package/dist/layout-bridge/dom/clickToPositionDom.d.ts +66 -0
- package/dist/layout-bridge/dom/clickToPositionDom.js +304 -0
- package/dist/layout-bridge/dom/findBodyPmSpans.d.ts +7 -0
- package/dist/layout-bridge/dom/findBodyPmSpans.js +17 -0
- package/dist/layout-bridge/dom/findHfPmSpans.d.ts +62 -0
- package/dist/layout-bridge/dom/findHfPmSpans.js +197 -0
- package/dist/layout-bridge/engine/clickToPosition.d.ts +66 -0
- package/dist/layout-bridge/engine/clickToPosition.js +403 -0
- package/dist/layout-bridge/engine/hitTest.d.ts +135 -0
- package/dist/layout-bridge/engine/hitTest.js +375 -0
- package/dist/layout-bridge/engine/measuring/index.d.ts +10 -0
- package/dist/layout-bridge/engine/measuring/index.js +9 -0
- package/dist/layout-bridge/engine/selectionRects.d.ts +59 -0
- package/dist/layout-bridge/engine/selectionRects.js +485 -0
- package/dist/layout-bridge/engine/tableWidthUtils.d.ts +7 -0
- package/dist/layout-bridge/engine/tableWidthUtils.js +25 -0
- package/dist/layout-engine/index.d.ts +44 -0
- package/dist/layout-engine/index.js +691 -0
- package/dist/layout-engine/keep-together.d.ts +43 -0
- package/dist/layout-engine/keep-together.js +98 -0
- package/dist/layout-engine/layoutInstrumentation.d.ts +50 -0
- package/dist/layout-engine/layoutInstrumentation.js +43 -0
- package/dist/layout-engine/lineFlow.d.ts +8 -0
- package/dist/layout-engine/lineFlow.js +17 -0
- package/dist/layout-engine/measure/cache.d.ts +93 -0
- package/dist/layout-engine/measure/cache.js +245 -0
- package/dist/layout-engine/measure/clampFloatingWrapMargins.d.ts +20 -0
- package/dist/layout-engine/measure/clampFloatingWrapMargins.js +29 -0
- package/dist/layout-engine/measure/featureFlags.d.ts +44 -0
- package/dist/layout-engine/measure/featureFlags.js +19 -0
- package/dist/layout-engine/measure/floatingZones.d.ts +89 -0
- package/dist/layout-engine/measure/floatingZones.js +152 -0
- package/dist/layout-engine/measure/font-metrics.worker.d.ts +17 -0
- package/dist/layout-engine/measure/font-metrics.worker.js +104 -0
- package/dist/layout-engine/measure/index.d.ts +9 -0
- package/dist/layout-engine/measure/index.js +8 -0
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +40 -0
- package/dist/layout-engine/measure/listMarkerWidth.js +76 -0
- package/dist/layout-engine/measure/measureBlocks.d.ts +25 -0
- package/dist/layout-engine/measure/measureBlocks.js +450 -0
- package/dist/layout-engine/measure/measureContainer.d.ts +24 -0
- package/dist/layout-engine/measure/measureContainer.js +280 -0
- package/dist/layout-engine/measure/measureHelpers.d.ts +82 -0
- package/dist/layout-engine/measure/measureHelpers.js +156 -0
- package/dist/layout-engine/measure/measureParagraph.d.ts +61 -0
- package/dist/layout-engine/measure/measureParagraph.js +724 -0
- package/dist/layout-engine/measure/measureProvider.d.ts +23 -0
- package/dist/layout-engine/measure/measureProvider.js +35 -0
- package/dist/layout-engine/measure/measureTypes.d.ts +61 -0
- package/dist/layout-engine/measure/measureTypes.js +0 -0
- package/dist/layout-engine/measure/measureWorker.d.ts +54 -0
- package/dist/layout-engine/measure/measureWorker.js +249 -0
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +88 -0
- package/dist/layout-engine/measure/measureWorkerProtocol.js +20 -0
- package/dist/layout-engine/measure/tableCellFloating.d.ts +26 -0
- package/dist/layout-engine/measure/tableCellFloating.js +99 -0
- package/dist/layout-engine/paginator.d.ts +84 -0
- package/dist/layout-engine/paginator.js +348 -0
- package/dist/layout-engine/paragraphFragmentRange.d.ts +9 -0
- package/dist/layout-engine/paragraphFragmentRange.js +40 -0
- package/dist/layout-engine/section-breaks.d.ts +78 -0
- package/dist/layout-engine/section-breaks.js +182 -0
- package/dist/layout-engine/tableRowBreak.d.ts +23 -0
- package/dist/layout-engine/tableRowBreak.js +147 -0
- package/dist/layout-engine/textBoxFlow.d.ts +50 -0
- package/dist/layout-engine/textBoxFlow.js +160 -0
- package/dist/layout-engine/types.d.ts +1031 -0
- package/dist/layout-engine/types.js +100 -0
- package/dist/layout-painter/documentColors.d.ts +4 -0
- package/dist/layout-painter/documentColors.js +40 -0
- package/dist/layout-painter/index.d.ts +110 -0
- package/dist/layout-painter/index.js +182 -0
- package/dist/layout-painter/registry/modules/image.d.ts +6 -0
- package/dist/layout-painter/registry/modules/image.js +17 -0
- package/dist/layout-painter/registry/modules/paragraph.d.ts +6 -0
- package/dist/layout-painter/registry/modules/paragraph.js +18 -0
- package/dist/layout-painter/registry/modules/table.d.ts +6 -0
- package/dist/layout-painter/registry/modules/table.js +16 -0
- package/dist/layout-painter/registry/modules/textBox.d.ts +6 -0
- package/dist/layout-painter/registry/modules/textBox.js +13 -0
- package/dist/layout-painter/registry/modules.d.ts +6 -0
- package/dist/layout-painter/registry/modules.js +26 -0
- package/dist/layout-painter/registry/registry.d.ts +24 -0
- package/dist/layout-painter/registry/registry.js +53 -0
- package/dist/layout-painter/registry/types.d.ts +61 -0
- package/dist/layout-painter/registry/types.js +0 -0
- package/dist/layout-painter/renderFragment.d.ts +32 -0
- package/dist/layout-painter/renderFragment.js +114 -0
- package/dist/layout-painter/renderImage.d.ts +88 -0
- package/dist/layout-painter/renderImage.js +130 -0
- package/dist/layout-painter/renderPage.d.ts +264 -0
- package/dist/layout-painter/renderPage.js +1795 -0
- package/dist/layout-painter/renderParagraph.d.ts +93 -0
- package/dist/layout-painter/renderParagraph.js +1300 -0
- package/dist/layout-painter/renderTable.d.ts +36 -0
- package/dist/layout-painter/renderTable.js +432 -0
- package/dist/layout-painter/renderTextBox.d.ts +22 -0
- package/dist/layout-painter/renderTextBox.js +67 -0
- package/dist/layout-painter/renderUtils.d.ts +42 -0
- package/dist/layout-painter/renderUtils.js +30 -0
- package/dist/layout-painter/renderWatermark.d.ts +16 -0
- package/dist/layout-painter/renderWatermark.js +61 -0
- package/dist/layout-painter/sdtBoundary.d.ts +6 -0
- package/dist/layout-painter/sdtBoundary.js +28 -0
- package/dist/managers/ContextMenuManager.d.ts +28 -0
- package/dist/managers/ContextMenuManager.js +48 -0
- package/dist/managers/DocumentLoaderManager.d.ts +47 -0
- package/dist/managers/DocumentLoaderManager.js +69 -0
- package/dist/managers/EditorModeManager.d.ts +28 -0
- package/dist/managers/EditorModeManager.js +50 -0
- package/dist/managers/ErrorManager.d.ts +26 -0
- package/dist/managers/ErrorManager.js +85 -0
- package/dist/managers/FindReplaceManager.d.ts +54 -0
- package/dist/managers/FindReplaceManager.js +104 -0
- package/dist/managers/HistoryManager.d.ts +92 -0
- package/dist/managers/HistoryManager.js +193 -0
- package/dist/managers/Subscribable.d.ts +34 -0
- package/dist/managers/Subscribable.js +46 -0
- package/dist/managers/TableSelectionManager.d.ts +74 -0
- package/dist/managers/TableSelectionManager.js +225 -0
- package/dist/managers/editorShortcuts.d.ts +42 -0
- package/dist/managers/editorShortcuts.js +60 -0
- package/dist/managers/types.d.ts +41 -0
- package/dist/managers/types.js +0 -0
- package/dist/markdown/annotations.d.ts +14 -0
- package/dist/markdown/annotations.js +42 -0
- package/dist/markdown/escape.d.ts +36 -0
- package/dist/markdown/escape.js +59 -0
- package/dist/markdown/fromMarkdown.d.ts +11 -0
- package/dist/markdown/fromMarkdown.js +223 -0
- package/dist/markdown/headings.d.ts +13 -0
- package/dist/markdown/headings.js +20 -0
- package/dist/markdown/images.d.ts +13 -0
- package/dist/markdown/images.js +63 -0
- package/dist/markdown/index.d.ts +18 -0
- package/dist/markdown/index.js +33 -0
- package/dist/markdown/internals.d.ts +15 -0
- package/dist/markdown/internals.js +35 -0
- package/dist/markdown/renderBlock.d.ts +7 -0
- package/dist/markdown/renderBlock.js +68 -0
- package/dist/markdown/renderParagraph.d.ts +11 -0
- package/dist/markdown/renderParagraph.js +64 -0
- package/dist/markdown/renderRuns.d.ts +11 -0
- package/dist/markdown/renderRuns.js +265 -0
- package/dist/markdown/renderTable.d.ts +8 -0
- package/dist/markdown/renderTable.js +229 -0
- package/dist/markdown/trailers.d.ts +13 -0
- package/dist/markdown/trailers.js +54 -0
- package/dist/markdown/types.d.ts +118 -0
- package/dist/markdown/types.js +0 -0
- package/dist/model.d.ts +13 -0
- package/dist/model.js +4 -0
- package/dist/paged-layout/LayoutSelectionGate.d.ts +80 -0
- package/dist/paged-layout/LayoutSelectionGate.js +131 -0
- package/dist/paged-layout/headerFooterMargins.d.ts +73 -0
- package/dist/paged-layout/headerFooterMargins.js +120 -0
- package/dist/paged-layout/hiddenEditorScroll.d.ts +4 -0
- package/dist/paged-layout/hiddenEditorScroll.js +4 -0
- package/dist/paged-layout/incrementalMeasure.d.ts +33 -0
- package/dist/paged-layout/incrementalMeasure.js +56 -0
- package/dist/paged-layout/rangeProjection.d.ts +51 -0
- package/dist/paged-layout/rangeProjection.js +153 -0
- package/dist/paged-layout/readOnlyEditAttempt.d.ts +5 -0
- package/dist/paged-layout/readOnlyEditAttempt.js +22 -0
- package/dist/paged-layout/scrollNavigation.d.ts +23 -0
- package/dist/paged-layout/scrollNavigation.js +31 -0
- package/dist/paged-layout/scrollPageInfo.d.ts +41 -0
- package/dist/paged-layout/scrollPageInfo.js +34 -0
- package/dist/paged-layout/scrollToPmPosition.d.ts +17 -0
- package/dist/paged-layout/scrollToPmPosition.js +95 -0
- package/dist/paged-layout/sectionBlockWidths.d.ts +35 -0
- package/dist/paged-layout/sectionBlockWidths.js +52 -0
- package/dist/paged-layout/sectionGeometry.d.ts +21 -0
- package/dist/paged-layout/sectionGeometry.js +36 -0
- package/dist/paged-layout/selectionViewportRect.d.ts +29 -0
- package/dist/paged-layout/selectionViewportRect.js +55 -0
- package/dist/paged-layout/tableColumnResize.d.ts +19 -0
- package/dist/paged-layout/tableColumnResize.js +28 -0
- package/dist/paged-layout/tableInsertButtonGeometry.d.ts +15 -0
- package/dist/paged-layout/tableInsertButtonGeometry.js +15 -0
- package/dist/paged-layout/transactionDirtyRange.d.ts +7 -0
- package/dist/paged-layout/transactionDirtyRange.js +27 -0
- package/dist/paged-layout/zoomScrollAnchor.d.ts +40 -0
- package/dist/paged-layout/zoomScrollAnchor.js +25 -0
- package/dist/prosemirror/attrs/index.d.ts +80 -0
- package/dist/prosemirror/attrs/index.js +1218 -0
- package/dist/prosemirror/autospacingBase.d.ts +11 -0
- package/dist/prosemirror/autospacingBase.js +16 -0
- package/dist/prosemirror/commands/comments.d.ts +93 -0
- package/dist/prosemirror/commands/comments.js +435 -0
- package/dist/prosemirror/commands/contentControls.d.ts +45 -0
- package/dist/prosemirror/commands/contentControls.js +294 -0
- package/dist/prosemirror/commands/contentControlsBlockFill.d.ts +15 -0
- package/dist/prosemirror/commands/contentControlsBlockFill.js +19 -0
- package/dist/prosemirror/commands/formatPainter.d.ts +31 -0
- package/dist/prosemirror/commands/formatPainter.js +130 -0
- package/dist/prosemirror/commands/formatting.d.ts +32 -0
- package/dist/prosemirror/commands/formatting.js +45 -0
- package/dist/prosemirror/commands/hyperlink.d.ts +31 -0
- package/dist/prosemirror/commands/hyperlink.js +79 -0
- package/dist/prosemirror/commands/image.d.ts +59 -0
- package/dist/prosemirror/commands/image.js +219 -0
- package/dist/prosemirror/commands/index.d.ts +13 -0
- package/dist/prosemirror/commands/index.js +13 -0
- package/dist/prosemirror/commands/pageBreak.d.ts +10 -0
- package/dist/prosemirror/commands/pageBreak.js +47 -0
- package/dist/prosemirror/commands/paragraph.d.ts +39 -0
- package/dist/prosemirror/commands/paragraph.js +64 -0
- package/dist/prosemirror/commands/pastePlainText.d.ts +39 -0
- package/dist/prosemirror/commands/pastePlainText.js +67 -0
- package/dist/prosemirror/commands/table.d.ts +83 -0
- package/dist/prosemirror/commands/table.js +105 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +27 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +1550 -0
- package/dist/prosemirror/conversion/index.d.ts +4 -0
- package/dist/prosemirror/conversion/index.js +4 -0
- package/dist/prosemirror/conversion/runShadingMark.d.ts +16 -0
- package/dist/prosemirror/conversion/runShadingMark.js +39 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +39 -0
- package/dist/prosemirror/conversion/toProseDoc.js +1404 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +43 -0
- package/dist/prosemirror/extensions/ExtensionManager.js +86 -0
- package/dist/prosemirror/extensions/StarterKit.d.ts +16 -0
- package/dist/prosemirror/extensions/StarterKit.js +132 -0
- package/dist/prosemirror/extensions/core/DocExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/core/DocExtension.js +12 -0
- package/dist/prosemirror/extensions/core/HistoryExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/core/HistoryExtension.js +28 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +25 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +619 -0
- package/dist/prosemirror/extensions/core/TextExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/core/TextExtension.js +12 -0
- package/dist/prosemirror/extensions/create.d.ts +17 -0
- package/dist/prosemirror/extensions/create.js +129 -0
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +23 -0
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +106 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.d.ts +8 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +172 -0
- package/dist/prosemirror/extensions/features/BidiShortcutExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/features/BidiShortcutExtension.js +38 -0
- package/dist/prosemirror/extensions/features/ContentControlWidgetsExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/features/ContentControlWidgetsExtension.js +21 -0
- package/dist/prosemirror/extensions/features/DropCursorExtension.d.ts +12 -0
- package/dist/prosemirror/extensions/features/DropCursorExtension.js +20 -0
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.d.ts +8 -0
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +44 -0
- package/dist/prosemirror/extensions/features/GapCursorExtension.d.ts +19 -0
- package/dist/prosemirror/extensions/features/GapCursorExtension.js +24 -0
- package/dist/prosemirror/extensions/features/ImageDragExtension.d.ts +15 -0
- package/dist/prosemirror/extensions/features/ImageDragExtension.js +66 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.d.ts +12 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +91 -0
- package/dist/prosemirror/extensions/features/ListExtension.d.ts +14 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +340 -0
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +60 -0
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +35 -0
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +188 -0
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +20 -0
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +34 -0
- package/dist/prosemirror/extensions/features/PasteStyleInlinerExtension.d.ts +20 -0
- package/dist/prosemirror/extensions/features/PasteStyleInlinerExtension.js +145 -0
- package/dist/prosemirror/extensions/features/SelectionTrackerExtension.d.ts +10 -0
- package/dist/prosemirror/extensions/features/SelectionTrackerExtension.js +17 -0
- package/dist/prosemirror/extensions/features/bidiShortcutController.d.ts +31 -0
- package/dist/prosemirror/extensions/features/bidiShortcutController.js +27 -0
- package/dist/prosemirror/extensions/features/pasteCleanup.d.ts +31 -0
- package/dist/prosemirror/extensions/features/pasteCleanup.js +159 -0
- package/dist/prosemirror/extensions/marks/AllCapsExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/AllCapsExtension.js +24 -0
- package/dist/prosemirror/extensions/marks/BoldExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/BoldExtension.js +41 -0
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.d.ts +14 -0
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +64 -0
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +23 -0
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +47 -0
- package/dist/prosemirror/extensions/marks/CommentExtension.d.ts +12 -0
- package/dist/prosemirror/extensions/marks/CommentExtension.js +38 -0
- package/dist/prosemirror/extensions/marks/FontFamilyExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/FontFamilyExtension.js +55 -0
- package/dist/prosemirror/extensions/marks/FontSizeExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/FontSizeExtension.js +47 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +92 -0
- package/dist/prosemirror/extensions/marks/HiddenTextExtension.d.ts +23 -0
- package/dist/prosemirror/extensions/marks/HiddenTextExtension.js +35 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.d.ts +10 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +115 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +146 -0
- package/dist/prosemirror/extensions/marks/ItalicExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/ItalicExtension.js +34 -0
- package/dist/prosemirror/extensions/marks/RtlExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/marks/RtlExtension.js +21 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +75 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +38 -0
- package/dist/prosemirror/extensions/marks/SmallCapsExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/SmallCapsExtension.js +24 -0
- package/dist/prosemirror/extensions/marks/StrikeExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/StrikeExtension.js +33 -0
- package/dist/prosemirror/extensions/marks/SubscriptExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/SubscriptExtension.js +25 -0
- package/dist/prosemirror/extensions/marks/SuperscriptExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +25 -0
- package/dist/prosemirror/extensions/marks/TextColorExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +50 -0
- package/dist/prosemirror/extensions/marks/TextEffectExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/marks/TextEffectExtension.js +35 -0
- package/dist/prosemirror/extensions/marks/TextEffectsExtensions.d.ts +31 -0
- package/dist/prosemirror/extensions/marks/TextEffectsExtensions.js +134 -0
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +22 -0
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +113 -0
- package/dist/prosemirror/extensions/marks/UnderlineExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/marks/UnderlineExtension.js +60 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +34 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +233 -0
- package/dist/prosemirror/extensions/nodes/BlockSdtExtension.d.ts +22 -0
- package/dist/prosemirror/extensions/nodes/BlockSdtExtension.js +122 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +12 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +87 -0
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +38 -0
- package/dist/prosemirror/extensions/nodes/HorizontalRuleExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/HorizontalRuleExtension.js +18 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +132 -0
- package/dist/prosemirror/extensions/nodes/MathExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/MathExtension.js +56 -0
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +20 -0
- package/dist/prosemirror/extensions/nodes/SdtExtension.d.ts +12 -0
- package/dist/prosemirror/extensions/nodes/SdtExtension.js +107 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.d.ts +30 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +502 -0
- package/dist/prosemirror/extensions/nodes/TabExtension.d.ts +9 -0
- package/dist/prosemirror/extensions/nodes/TabExtension.js +27 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +34 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +1972 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +34 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +150 -0
- package/dist/prosemirror/extensions/types.d.ts +101 -0
- package/dist/prosemirror/extensions/types.js +10 -0
- package/dist/prosemirror/findReplaceSelection.d.ts +23 -0
- package/dist/prosemirror/findReplaceSelection.js +86 -0
- package/dist/prosemirror/index.d.ts +25 -0
- package/dist/prosemirror/index.js +23 -0
- package/dist/prosemirror/insertOperations.d.ts +12 -0
- package/dist/prosemirror/insertOperations.js +19 -0
- package/dist/prosemirror/paragraphDirection.d.ts +42 -0
- package/dist/prosemirror/paragraphDirection.js +33 -0
- package/dist/prosemirror/plugins/aiCitationDecorations.d.ts +40 -0
- package/dist/prosemirror/plugins/aiCitationDecorations.js +90 -0
- package/dist/prosemirror/plugins/aiSuggestionDecorations.d.ts +42 -0
- package/dist/prosemirror/plugins/aiSuggestionDecorations.js +135 -0
- package/dist/prosemirror/plugins/anonymizationDecorations.d.ts +60 -0
- package/dist/prosemirror/plugins/anonymizationDecorations.js +120 -0
- package/dist/prosemirror/plugins/autocompleteSuggestion.d.ts +123 -0
- package/dist/prosemirror/plugins/autocompleteSuggestion.js +283 -0
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +56 -0
- package/dist/prosemirror/plugins/contentControlWidgets.js +192 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +37 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.js +55 -0
- package/dist/prosemirror/plugins/documentStyles.d.ts +18 -0
- package/dist/prosemirror/plugins/documentStyles.js +46 -0
- package/dist/prosemirror/plugins/index.d.ts +3 -0
- package/dist/prosemirror/plugins/index.js +3 -0
- package/dist/prosemirror/plugins/pmTextScan.d.ts +19 -0
- package/dist/prosemirror/plugins/pmTextScan.js +39 -0
- package/dist/prosemirror/plugins/selectionTracker.d.ts +43 -0
- package/dist/prosemirror/plugins/selectionTracker.js +179 -0
- package/dist/prosemirror/plugins/suggestionMode.d.ts +70 -0
- package/dist/prosemirror/plugins/suggestionMode.js +489 -0
- package/dist/prosemirror/plugins/templateDirectives.d.ts +25 -0
- package/dist/prosemirror/plugins/templateDirectives.js +67 -0
- package/dist/prosemirror/plugins/templatePreviewValues.d.ts +49 -0
- package/dist/prosemirror/plugins/templatePreviewValues.js +117 -0
- package/dist/prosemirror/plugins/templateSlashMenu.d.ts +57 -0
- package/dist/prosemirror/plugins/templateSlashMenu.js +184 -0
- package/dist/prosemirror/schema/index.d.ts +15 -0
- package/dist/prosemirror/schema/index.js +20 -0
- package/dist/prosemirror/schema/marks.d.ts +118 -0
- package/dist/prosemirror/schema/marks.js +0 -0
- package/dist/prosemirror/schema/nodes.d.ts +389 -0
- package/dist/prosemirror/schema/nodes.js +0 -0
- package/dist/prosemirror/selectionState.d.ts +23 -0
- package/dist/prosemirror/selectionState.js +111 -0
- package/dist/prosemirror/styles/index.d.ts +2 -0
- package/dist/prosemirror/styles/index.js +2 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +32 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +108 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +94 -0
- package/dist/prosemirror/styles/styleResolver.js +200 -0
- package/dist/prosemirror/utils/findParagraphByParaId.d.ts +26 -0
- package/dist/prosemirror/utils/findParagraphByParaId.js +37 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +110 -0
- package/dist/prosemirror/utils/tabCalculator.js +137 -0
- package/dist/prosemirror/validation.d.ts +20 -0
- package/dist/prosemirror/validation.js +177 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +6 -0
- package/dist/style-engine/index.d.ts +3 -0
- package/dist/style-engine/index.js +2 -0
- package/dist/style-engine/styleEngine.d.ts +67 -0
- package/dist/style-engine/styleEngine.js +70 -0
- package/dist/types/block-id.d.ts +66 -0
- package/dist/types/block-id.js +73 -0
- package/dist/types/colors.d.ts +2 -0
- package/dist/types/colors.js +0 -0
- package/dist/types/content.d.ts +2 -0
- package/dist/types/content.js +0 -0
- package/dist/types/document.d.ts +7 -0
- package/dist/types/document.js +0 -0
- package/dist/types/documentEnumValues.d.ts +89 -0
- package/dist/types/documentEnumValues.js +693 -0
- package/dist/types/formatting.d.ts +2 -0
- package/dist/types/formatting.js +0 -0
- package/dist/types/index.d.ts +107 -0
- package/dist/types/index.js +0 -0
- package/dist/utils/authorColors.d.ts +16 -0
- package/dist/utils/authorColors.js +30 -0
- package/dist/utils/baseDirection.d.ts +23 -0
- package/dist/utils/baseDirection.js +34 -0
- package/dist/utils/clipboard.d.ts +126 -0
- package/dist/utils/clipboard.js +622 -0
- package/dist/utils/colorResolver.d.ts +158 -0
- package/dist/utils/colorResolver.js +557 -0
- package/dist/utils/createDocument.d.ts +50 -0
- package/dist/utils/createDocument.js +262 -0
- package/dist/utils/documentLoaderBehavior.d.ts +22 -0
- package/dist/utils/documentLoaderBehavior.js +14 -0
- package/dist/utils/docxInput.d.ts +22 -0
- package/dist/utils/docxInput.js +16 -0
- package/dist/utils/domGuards.d.ts +59 -0
- package/dist/utils/domGuards.js +82 -0
- package/dist/utils/fontFamilyMerge.d.ts +7 -0
- package/dist/utils/fontFamilyMerge.js +31 -0
- package/dist/utils/fontLoader.d.ts +50 -0
- package/dist/utils/fontLoader.js +177 -0
- package/dist/utils/fontResolver.d.ts +61 -0
- package/dist/utils/fontResolver.js +651 -0
- package/dist/utils/fontWeights.d.ts +4 -0
- package/dist/utils/fontWeights.js +4 -0
- package/dist/utils/formatToStyle.d.ts +103 -0
- package/dist/utils/formatToStyle.js +289 -0
- package/dist/utils/headerFooter.d.ts +74 -0
- package/dist/utils/headerFooter.js +257 -0
- package/dist/utils/headingCollector.d.ts +22 -0
- package/dist/utils/headingCollector.js +36 -0
- package/dist/utils/hexId.d.ts +33 -0
- package/dist/utils/hexId.js +37 -0
- package/dist/utils/imageValidation.d.ts +5 -0
- package/dist/utils/imageValidation.js +31 -0
- package/dist/utils/paragraphFormattingMerge.d.ts +14 -0
- package/dist/utils/paragraphFormattingMerge.js +68 -0
- package/dist/utils/replaceText.d.ts +17 -0
- package/dist/utils/replaceText.js +161 -0
- package/dist/utils/rotationBoundingBox.d.ts +12 -0
- package/dist/utils/rotationBoundingBox.js +36 -0
- package/dist/utils/scriptSegments.d.ts +36 -0
- package/dist/utils/scriptSegments.js +52 -0
- package/dist/utils/tableOperations.d.ts +87 -0
- package/dist/utils/tableOperations.js +259 -0
- package/dist/utils/textFormattingMerge.d.ts +6 -0
- package/dist/utils/textFormattingMerge.js +31 -0
- package/dist/utils/tiffConverter.d.ts +18 -0
- package/dist/utils/tiffConverter.js +83 -0
- package/dist/utils/units.d.ts +99 -0
- package/dist/utils/units.js +139 -0
- package/dist/utils/urlSecurity.d.ts +7 -0
- package/dist/utils/urlSecurity.js +65 -0
- package/dist/watermark/index.d.ts +39 -0
- package/dist/watermark/index.js +149 -0
- package/package.json +122 -2
|
@@ -0,0 +1,1795 @@
|
|
|
1
|
+
import { eighthsToPixels, pointsToPixels } from "../utils/units.js";
|
|
2
|
+
import { resolveFontFamily } from "../utils/fontResolver.js";
|
|
3
|
+
import { floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun } from "../layout-engine/types.js";
|
|
4
|
+
import { rectsToFloatingZones } from "../layout-engine/measure/floatingZones.js";
|
|
5
|
+
import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
|
|
6
|
+
import { borderToStyle } from "../utils/formatToStyle.js";
|
|
7
|
+
import { renderFragment } from "./renderFragment.js";
|
|
8
|
+
import { applyImageVisualAttrs, hasImageVisualAttrs, renderImageFragment } from "./renderImage.js";
|
|
9
|
+
import { emuToPixels } from "./renderUtils.js";
|
|
10
|
+
import { renderParagraphFragment } from "./renderParagraph.js";
|
|
11
|
+
import { renderTableFragment } from "./renderTable.js";
|
|
12
|
+
import { renderTextBoxFragment } from "./renderTextBox.js";
|
|
13
|
+
import { renderWatermarkLayer } from "./renderWatermark.js";
|
|
14
|
+
import { panic } from "better-result";
|
|
15
|
+
//#region src/layout-painter/renderPage.ts
|
|
16
|
+
/**
|
|
17
|
+
* Page Renderer
|
|
18
|
+
*
|
|
19
|
+
* Renders a single page from Layout data to DOM elements.
|
|
20
|
+
* Each page contains positioned fragments within a content area.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* CSS class names for page elements
|
|
24
|
+
*/
|
|
25
|
+
const PAGE_CLASS_NAMES = {
|
|
26
|
+
page: "layout-page",
|
|
27
|
+
content: "layout-page-content",
|
|
28
|
+
header: "layout-page-header",
|
|
29
|
+
footer: "layout-page-footer"
|
|
30
|
+
};
|
|
31
|
+
const getDefaultPageFontFamily = () => resolveFontFamily("Calibri").cssFallback;
|
|
32
|
+
/**
|
|
33
|
+
* Apply page styles to an element
|
|
34
|
+
*/
|
|
35
|
+
function applyPageStyles(element, width, height, options) {
|
|
36
|
+
element.style.position = "relative";
|
|
37
|
+
element.style.width = `${width}px`;
|
|
38
|
+
element.style.height = `${height}px`;
|
|
39
|
+
element.style.backgroundColor = options.backgroundColor ?? "var(--doc-canvas, #ffffff)";
|
|
40
|
+
element.style.color = "var(--doc-canvas-text, #1f2937)";
|
|
41
|
+
element.style.overflow = "hidden";
|
|
42
|
+
element.style.border = "none";
|
|
43
|
+
element.style.boxShadow = "none";
|
|
44
|
+
element.style.outline = "none";
|
|
45
|
+
element.style.fontFamily = getDefaultPageFontFamily();
|
|
46
|
+
element.style.fontSize = `${1056 / 72}px`;
|
|
47
|
+
element.style.color = "var(--doc-canvas-text, #000)";
|
|
48
|
+
}
|
|
49
|
+
function pageBorderShouldRender(pageNumber, display) {
|
|
50
|
+
switch (display ?? "allPages") {
|
|
51
|
+
case "firstPage": return pageNumber === 1;
|
|
52
|
+
case "notFirstPage": return pageNumber !== 1;
|
|
53
|
+
case "allPages": return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function pageBorderSpacePx(border) {
|
|
57
|
+
return border?.space !== void 0 ? pointsToPixels(border.space) : 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Effective rendered border width in pixels. Mirrors `borderToStyle` (1px
|
|
61
|
+
* floor for hairlines) and the 3px floor applied to double borders in
|
|
62
|
+
* `applyPageBorderSide`, so callers can shift the overlay outward by the
|
|
63
|
+
* exact stroke they will see on screen.
|
|
64
|
+
*/
|
|
65
|
+
function pageBorderWidthPx(border) {
|
|
66
|
+
if (!border || border.style === "none" || border.style === "nil") return 0;
|
|
67
|
+
const widthPx = border.size !== void 0 && border.size !== 0 ? eighthsToPixels(border.size) : 1;
|
|
68
|
+
const floored = Math.max(1, widthPx);
|
|
69
|
+
return border.style === "double" ? Math.max(3, floored) : floored;
|
|
70
|
+
}
|
|
71
|
+
function applyPageBorderSide(element, border, side, theme) {
|
|
72
|
+
if (!border || border.style === "none" || border.style === "nil") return;
|
|
73
|
+
const styles = borderToStyle(border, side, theme);
|
|
74
|
+
for (const [key, value] of Object.entries(styles)) element.style[key] = String(value);
|
|
75
|
+
const styleKey = `border${side}Style`;
|
|
76
|
+
const widthKey = `border${side}Width`;
|
|
77
|
+
if (element.style[styleKey] === "double") {
|
|
78
|
+
const widthValue = Number.parseFloat(element.style[widthKey] ?? "");
|
|
79
|
+
if (!Number.isFinite(widthValue) || widthValue < 3) element.style[widthKey] = "3px";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function renderPageBorderOverlay(page, options, doc) {
|
|
83
|
+
const pb = options.pageBorders;
|
|
84
|
+
if (!pb || !pageBorderShouldRender(page.number, pb.display)) return null;
|
|
85
|
+
if (![
|
|
86
|
+
pb.top,
|
|
87
|
+
pb.bottom,
|
|
88
|
+
pb.left,
|
|
89
|
+
pb.right
|
|
90
|
+
].some((border) => border && border.style !== "none" && border.style !== "nil")) return null;
|
|
91
|
+
const offsetFrom = pb.offsetFrom ?? "text";
|
|
92
|
+
const topOffset = pageBorderSpacePx(pb.top);
|
|
93
|
+
const rightOffset = pageBorderSpacePx(pb.right);
|
|
94
|
+
const bottomOffset = pageBorderSpacePx(pb.bottom);
|
|
95
|
+
const leftOffset = pageBorderSpacePx(pb.left);
|
|
96
|
+
const overlay = doc.createElement("div");
|
|
97
|
+
overlay.className = "layout-page-border";
|
|
98
|
+
overlay.style.position = "absolute";
|
|
99
|
+
overlay.style.pointerEvents = "none";
|
|
100
|
+
overlay.style.boxSizing = "border-box";
|
|
101
|
+
overlay.style.zIndex = pb.zOrder === "back" ? "0" : "20";
|
|
102
|
+
if (offsetFrom === "page") {
|
|
103
|
+
overlay.style.top = `${topOffset}px`;
|
|
104
|
+
overlay.style.right = `${rightOffset}px`;
|
|
105
|
+
overlay.style.bottom = `${bottomOffset}px`;
|
|
106
|
+
overlay.style.left = `${leftOffset}px`;
|
|
107
|
+
} else {
|
|
108
|
+
const topWidth = pageBorderWidthPx(pb.top);
|
|
109
|
+
const rightWidth = pageBorderWidthPx(pb.right);
|
|
110
|
+
const bottomWidth = pageBorderWidthPx(pb.bottom);
|
|
111
|
+
const leftWidth = pageBorderWidthPx(pb.left);
|
|
112
|
+
overlay.style.top = `${Math.max(0, page.margins.top - topOffset - topWidth)}px`;
|
|
113
|
+
overlay.style.right = `${Math.max(0, page.margins.right - rightOffset - rightWidth)}px`;
|
|
114
|
+
overlay.style.bottom = `${Math.max(0, page.margins.bottom - bottomOffset - bottomWidth)}px`;
|
|
115
|
+
overlay.style.left = `${Math.max(0, page.margins.left - leftOffset - leftWidth)}px`;
|
|
116
|
+
}
|
|
117
|
+
applyPageBorderSide(overlay, pb.top, "Top", options.theme);
|
|
118
|
+
applyPageBorderSide(overlay, pb.bottom, "Bottom", options.theme);
|
|
119
|
+
applyPageBorderSide(overlay, pb.left, "Left", options.theme);
|
|
120
|
+
applyPageBorderSide(overlay, pb.right, "Right", options.theme);
|
|
121
|
+
return overlay;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Refresh the page-border overlay on an existing page shell so incremental
|
|
125
|
+
* rerenders pick up changes to size, margins, page number, or pageBorders
|
|
126
|
+
* options. Removes any stale `.layout-page-border` child before re-attaching
|
|
127
|
+
* with the current z-order (back → first child, front → last child).
|
|
128
|
+
*/
|
|
129
|
+
function syncPageBorderOverlay(pageEl, page, options, doc) {
|
|
130
|
+
for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-border"))) stale.remove();
|
|
131
|
+
const overlay = renderPageBorderOverlay(page, options, doc);
|
|
132
|
+
if (!overlay) return;
|
|
133
|
+
if (options.pageBorders?.zOrder === "back") pageEl.prepend(overlay);
|
|
134
|
+
else pageEl.append(overlay);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Refresh the watermark overlay on a rendered page shell. Incremental
|
|
138
|
+
* rerenders replace only the body content area, so the watermark sibling
|
|
139
|
+
* must be reconciled separately when per-section header resolution changes.
|
|
140
|
+
*/
|
|
141
|
+
function syncPageWatermarkOverlay(pageEl, page, options, doc) {
|
|
142
|
+
for (const stale of Array.from(pageEl.querySelectorAll(":scope > .layout-page-watermark"))) stale.remove();
|
|
143
|
+
const watermark = options.watermark;
|
|
144
|
+
if (!watermark) return;
|
|
145
|
+
const overlay = renderWatermarkLayer(watermark, page, doc, options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {});
|
|
146
|
+
if (!overlay) return;
|
|
147
|
+
const contentEl = pageEl.querySelector(`:scope > .${PAGE_CLASS_NAMES.content}`);
|
|
148
|
+
if (contentEl) {
|
|
149
|
+
contentEl.before(overlay);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
pageEl.append(overlay);
|
|
153
|
+
}
|
|
154
|
+
function floatingZonesForFragment(pageWideZones, scopedZones, fragmentIndex) {
|
|
155
|
+
if (scopedZones.length === 0) return pageWideZones;
|
|
156
|
+
let activeZones;
|
|
157
|
+
for (const scopedZone of scopedZones) {
|
|
158
|
+
if (scopedZone.startsAtFragmentIndex > fragmentIndex) continue;
|
|
159
|
+
activeZones ??= [...pageWideZones];
|
|
160
|
+
activeZones.push(scopedZone.zone);
|
|
161
|
+
}
|
|
162
|
+
return activeZones ?? pageWideZones;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Apply content area styles to an element
|
|
166
|
+
*/
|
|
167
|
+
function applyContentAreaStyles(element, page) {
|
|
168
|
+
const margins = page.margins;
|
|
169
|
+
element.style.position = "absolute";
|
|
170
|
+
element.style.top = `${margins.top}px`;
|
|
171
|
+
element.style.left = `${margins.left}px`;
|
|
172
|
+
element.style.right = `${margins.right}px`;
|
|
173
|
+
element.style.bottom = `${margins.bottom}px`;
|
|
174
|
+
element.style.overflow = "visible";
|
|
175
|
+
}
|
|
176
|
+
function getPositionAlignment(position) {
|
|
177
|
+
return position?.align ?? position?.alignment;
|
|
178
|
+
}
|
|
179
|
+
function resolveHeaderFooterFloatTop(floatImg, layout) {
|
|
180
|
+
const v = floatImg.position.vertical;
|
|
181
|
+
if (!v) return floatImg.paragraphY;
|
|
182
|
+
const align = getPositionAlignment(v);
|
|
183
|
+
const offsetPx = v.posOffset !== void 0 ? emuToPixels(v.posOffset) : void 0;
|
|
184
|
+
if (v.relativeTo === "page") {
|
|
185
|
+
if (offsetPx !== void 0) return offsetPx - layout.flowTop;
|
|
186
|
+
if (align === "top") return -layout.flowTop;
|
|
187
|
+
if (align === "bottom") return layout.pageHeight - floatImg.height - layout.flowTop;
|
|
188
|
+
if (align === "center") return (layout.pageHeight - floatImg.height) / 2 - layout.flowTop;
|
|
189
|
+
}
|
|
190
|
+
if (v.relativeTo === "margin") {
|
|
191
|
+
const marginTop = layout.margins.top;
|
|
192
|
+
const marginHeight = layout.pageHeight - layout.margins.top - layout.margins.bottom;
|
|
193
|
+
if (offsetPx !== void 0) return marginTop + offsetPx - layout.flowTop;
|
|
194
|
+
if (align === "top") return marginTop - layout.flowTop;
|
|
195
|
+
if (align === "bottom") return marginTop + marginHeight - floatImg.height - layout.flowTop;
|
|
196
|
+
if (align === "center") return marginTop + (marginHeight - floatImg.height) / 2 - layout.flowTop;
|
|
197
|
+
}
|
|
198
|
+
if (offsetPx !== void 0) return floatImg.paragraphY + offsetPx;
|
|
199
|
+
return floatImg.paragraphY;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Resolve the on-page position of a floating header/footer table
|
|
203
|
+
* (`<w:tbl><w:tblpPr ...>`). ECMA-376 §17.4.57. Returns coordinates
|
|
204
|
+
* relative to the HF container (which itself is positioned at the page's
|
|
205
|
+
* header or footer slot).
|
|
206
|
+
*/
|
|
207
|
+
function resolveHeaderFooterFloatingTablePosition(floating, measure, layout, sourceY) {
|
|
208
|
+
const horzAnchor = floating.horzAnchor ?? "margin";
|
|
209
|
+
const vertAnchor = floating.vertAnchor ?? "margin";
|
|
210
|
+
const horzFrameWidth = horzAnchor === "page" ? layout.pageWidth : layout.contentWidth;
|
|
211
|
+
const horzFrameOffset = horzAnchor === "page" ? -layout.flowLeft : 0;
|
|
212
|
+
const vertFrameHeight = vertAnchor === "page" ? layout.pageHeight : layout.pageHeight - layout.margins.top - layout.margins.bottom;
|
|
213
|
+
const vertFrameOffset = vertAnchor === "page" ? -layout.flowTop : layout.margins.top - layout.flowTop;
|
|
214
|
+
let left = 0;
|
|
215
|
+
const xSpec = floating.tblpXSpec;
|
|
216
|
+
if (xSpec === "left" || xSpec === "inside") left = horzFrameOffset;
|
|
217
|
+
else if (xSpec === "right" || xSpec === "outside") left = horzFrameOffset + horzFrameWidth - measure.totalWidth;
|
|
218
|
+
else if (xSpec === "center") left = horzFrameOffset + (horzFrameWidth - measure.totalWidth) / 2;
|
|
219
|
+
else if (floating.tblpX !== void 0) left = horzFrameOffset + floating.tblpX;
|
|
220
|
+
let top = sourceY;
|
|
221
|
+
if (floating.tblpYSpec === "top") top = vertFrameOffset;
|
|
222
|
+
else if (floating.tblpYSpec === "bottom") top = vertFrameOffset + vertFrameHeight - measure.totalHeight;
|
|
223
|
+
else if (floating.tblpYSpec === "center") top = vertFrameOffset + (vertFrameHeight - measure.totalHeight) / 2;
|
|
224
|
+
else if (floating.tblpY !== void 0) top = vertFrameOffset + floating.tblpY;
|
|
225
|
+
return {
|
|
226
|
+
left,
|
|
227
|
+
top
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Resolve the CSS `left` (px string) for an anchored object (image or text box)
|
|
232
|
+
* in a header/footer, honoring `wp:positionH` (relativeTo page/margin, align
|
|
233
|
+
* left/center/right, or posOffset). Shared by floating images and text boxes so
|
|
234
|
+
* a page-centered banner in the header lands centered like Word, not pinned to
|
|
235
|
+
* the left. Ported from eigenpal/docx-editor#700.
|
|
236
|
+
*/
|
|
237
|
+
function resolveHeaderFooterFloatLeft(width, h, layout) {
|
|
238
|
+
if (!h) return "0";
|
|
239
|
+
let align = getPositionAlignment(h);
|
|
240
|
+
if (align === "inside") align = "left";
|
|
241
|
+
else if (align === "outside") align = "right";
|
|
242
|
+
if (h.relativeTo === "page") {
|
|
243
|
+
if (h.posOffset !== void 0) return `${emuToPixels(h.posOffset) - layout.flowLeft}px`;
|
|
244
|
+
if (align === "right") return `${layout.pageWidth - width - layout.flowLeft}px`;
|
|
245
|
+
if (align === "center") return `${(layout.pageWidth - width) / 2 - layout.flowLeft}px`;
|
|
246
|
+
if (align === "left") return `${-layout.flowLeft}px`;
|
|
247
|
+
}
|
|
248
|
+
if (h.posOffset !== void 0) return `${emuToPixels(h.posOffset)}px`;
|
|
249
|
+
if (align === "right") return `${layout.contentWidth - width}px`;
|
|
250
|
+
if (align === "center") return `${(layout.contentWidth - width) / 2}px`;
|
|
251
|
+
return "0";
|
|
252
|
+
}
|
|
253
|
+
function applyHeaderFooterFloatHorizontalPosition(img, floatImg, layout) {
|
|
254
|
+
img.style.left = resolveHeaderFooterFloatLeft(floatImg.width, floatImg.position.horizontal, layout);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Apply fragment positioning styles
|
|
258
|
+
* Note: Fragment x/y include page margins, but fragments are positioned
|
|
259
|
+
* inside the content area which already has margin offsets applied.
|
|
260
|
+
* So we subtract the margins to get content-area-relative positions.
|
|
261
|
+
*/
|
|
262
|
+
function applyFragmentStyles(element, fragment, margins) {
|
|
263
|
+
element.style.position = "absolute";
|
|
264
|
+
element.style.left = `${fragment.x - margins.left}px`;
|
|
265
|
+
element.style.top = `${fragment.y - margins.top}px`;
|
|
266
|
+
element.style.width = `${fragment.width}px`;
|
|
267
|
+
if ("height" in fragment) element.style.height = `${fragment.height}px`;
|
|
268
|
+
}
|
|
269
|
+
function resolveHorizontalBand(relativeTo, geometry) {
|
|
270
|
+
switch (relativeTo) {
|
|
271
|
+
case "page": return {
|
|
272
|
+
baseX: -geometry.marginLeft,
|
|
273
|
+
bandWidth: geometry.pageWidth
|
|
274
|
+
};
|
|
275
|
+
case "leftMargin":
|
|
276
|
+
case "insideMargin": return {
|
|
277
|
+
baseX: -geometry.marginLeft,
|
|
278
|
+
bandWidth: geometry.marginLeft
|
|
279
|
+
};
|
|
280
|
+
case "rightMargin":
|
|
281
|
+
case "outsideMargin": return {
|
|
282
|
+
baseX: geometry.contentWidth,
|
|
283
|
+
bandWidth: geometry.marginRight
|
|
284
|
+
};
|
|
285
|
+
case "character": return {
|
|
286
|
+
baseX: 0,
|
|
287
|
+
bandWidth: 0
|
|
288
|
+
};
|
|
289
|
+
default: return {
|
|
290
|
+
baseX: 0,
|
|
291
|
+
bandWidth: geometry.contentWidth
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function resolveVerticalBand(relativeTo, fragmentY, geometry) {
|
|
296
|
+
switch (relativeTo) {
|
|
297
|
+
case "page": return {
|
|
298
|
+
baseY: -geometry.marginTop,
|
|
299
|
+
bandHeight: geometry.pageHeight
|
|
300
|
+
};
|
|
301
|
+
case "topMargin": return {
|
|
302
|
+
baseY: -geometry.marginTop,
|
|
303
|
+
bandHeight: geometry.marginTop
|
|
304
|
+
};
|
|
305
|
+
case "bottomMargin": return {
|
|
306
|
+
baseY: geometry.contentHeight,
|
|
307
|
+
bandHeight: geometry.marginBottom
|
|
308
|
+
};
|
|
309
|
+
case "paragraph":
|
|
310
|
+
case "line": return {
|
|
311
|
+
baseY: fragmentY,
|
|
312
|
+
bandHeight: 0
|
|
313
|
+
};
|
|
314
|
+
default: return {
|
|
315
|
+
baseY: 0,
|
|
316
|
+
bandHeight: geometry.contentHeight
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Resolve the on-page coordinates of an anchored floating image.
|
|
322
|
+
*
|
|
323
|
+
* Pure function — no DOM, no side effects — so the math is unit-testable
|
|
324
|
+
* without spinning up a full page render. Used by
|
|
325
|
+
* `extractFloatingImagesFromParagraph` for body anchors; mirrors the
|
|
326
|
+
* upstream painter helper introduced in eigenpal #424.
|
|
327
|
+
*
|
|
328
|
+
* The OOXML position is `posOffset` (EMUs from the relativeFrom origin)
|
|
329
|
+
* XOR `align` (symbolic top|center|bottom / left|center|right). When
|
|
330
|
+
* neither is present, the spec means "anchor at the band origin"; for
|
|
331
|
+
* paragraph/line that's the paragraph itself, otherwise the band origin
|
|
332
|
+
* picked by `relativeFrom`.
|
|
333
|
+
*/
|
|
334
|
+
function resolveAnchoredImagePosition(imgRun, fragmentY, geometry) {
|
|
335
|
+
const position = imgRun.position;
|
|
336
|
+
const contentWidth = geometry.contentWidth;
|
|
337
|
+
let side = "left";
|
|
338
|
+
let x = 0;
|
|
339
|
+
if (position?.horizontal) {
|
|
340
|
+
const h = position.horizontal;
|
|
341
|
+
const { baseX, bandWidth } = resolveHorizontalBand(h.relativeTo, geometry);
|
|
342
|
+
const horizontalHasBand = h.relativeTo !== "character";
|
|
343
|
+
if (h.align === "right" || h.align === "outside") {
|
|
344
|
+
side = "right";
|
|
345
|
+
x = horizontalHasBand ? baseX + bandWidth - imgRun.width : 0;
|
|
346
|
+
} else if (h.align === "left" || h.align === "inside") {
|
|
347
|
+
side = "left";
|
|
348
|
+
x = baseX;
|
|
349
|
+
} else if (h.align === "center") {
|
|
350
|
+
side = "left";
|
|
351
|
+
x = horizontalHasBand ? baseX + (bandWidth - imgRun.width) / 2 : 0;
|
|
352
|
+
} else if (h.posOffset !== void 0) {
|
|
353
|
+
x = baseX + emuToPixels(h.posOffset);
|
|
354
|
+
side = x > contentWidth / 2 ? "right" : "left";
|
|
355
|
+
} else x = baseX;
|
|
356
|
+
} else if (imgRun.cssFloat === "right") {
|
|
357
|
+
side = "right";
|
|
358
|
+
x = contentWidth - imgRun.width;
|
|
359
|
+
}
|
|
360
|
+
let y;
|
|
361
|
+
if (position?.vertical) {
|
|
362
|
+
const v = position.vertical;
|
|
363
|
+
const { baseY, bandHeight } = resolveVerticalBand(v.relativeTo, fragmentY, geometry);
|
|
364
|
+
const verticalHasBand = v.relativeTo !== "paragraph" && v.relativeTo !== "line";
|
|
365
|
+
if (v.align === "top" || v.align === "inside") y = baseY;
|
|
366
|
+
else if (v.align === "center") y = verticalHasBand ? baseY + (bandHeight - imgRun.height) / 2 : fragmentY;
|
|
367
|
+
else if (v.align === "bottom" || v.align === "outside") y = verticalHasBand ? baseY + bandHeight - imgRun.height : fragmentY;
|
|
368
|
+
else if (v.posOffset !== void 0) y = baseY + emuToPixels(v.posOffset);
|
|
369
|
+
else y = verticalHasBand ? baseY : fragmentY;
|
|
370
|
+
} else y = fragmentY;
|
|
371
|
+
return {
|
|
372
|
+
x,
|
|
373
|
+
y,
|
|
374
|
+
side
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Extract floating images from a paragraph block and determine their page-level positions.
|
|
379
|
+
* Returns extracted images and info for the paragraph about space reserved.
|
|
380
|
+
*/
|
|
381
|
+
function extractFloatingImagesFromParagraph(block, fragmentY, geometry) {
|
|
382
|
+
const floatingImages = [];
|
|
383
|
+
for (const run of block.runs) {
|
|
384
|
+
if (run.kind !== "image") continue;
|
|
385
|
+
const imgRun = run;
|
|
386
|
+
if (!isFloatingImageRun(imgRun)) continue;
|
|
387
|
+
const distTop = imgRun.distTop ?? 0;
|
|
388
|
+
const distBottom = imgRun.distBottom ?? 0;
|
|
389
|
+
const distLeft = imgRun.distLeft ?? 12;
|
|
390
|
+
const distRight = imgRun.distRight ?? 12;
|
|
391
|
+
const { x, y, side } = resolveAnchoredImagePosition(imgRun, fragmentY, geometry);
|
|
392
|
+
let wrapText;
|
|
393
|
+
if (imgRun.cssFloat === "left") wrapText = "right";
|
|
394
|
+
else if (imgRun.cssFloat === "right") wrapText = "left";
|
|
395
|
+
floatingImages.push({
|
|
396
|
+
src: imgRun.src,
|
|
397
|
+
width: imgRun.width,
|
|
398
|
+
height: imgRun.height,
|
|
399
|
+
...imgRun.alt !== void 0 ? { alt: imgRun.alt } : {},
|
|
400
|
+
...imgRun.transform !== void 0 ? { transform: imgRun.transform } : {},
|
|
401
|
+
...imgRun.opacity != null ? { opacity: imgRun.opacity } : {},
|
|
402
|
+
side,
|
|
403
|
+
x,
|
|
404
|
+
y,
|
|
405
|
+
distTop,
|
|
406
|
+
distBottom,
|
|
407
|
+
distLeft,
|
|
408
|
+
distRight,
|
|
409
|
+
...imgRun.pmStart !== void 0 ? { pmStart: imgRun.pmStart } : {},
|
|
410
|
+
...imgRun.pmEnd !== void 0 ? { pmEnd: imgRun.pmEnd } : {},
|
|
411
|
+
...wrapText !== void 0 ? { wrapText } : {},
|
|
412
|
+
...imgRun.wrapType !== void 0 ? { wrapType: imgRun.wrapType } : {},
|
|
413
|
+
affectsTextWrap: isTextWrappingFloatingImageRun(imgRun),
|
|
414
|
+
behindDoc: imgRun.wrapType === "behind"
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return floatingImages;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Render floating images into a page-level layer.
|
|
421
|
+
*
|
|
422
|
+
* `layerMode === "behind"` paints below body text (used for `behindDoc`
|
|
423
|
+
* wrapNone images, e.g. full-page letterhead backgrounds). `"front"` paints
|
|
424
|
+
* above text (default for `inFront` wrapNone and side-wrapping images).
|
|
425
|
+
*/
|
|
426
|
+
function renderFloatingImagesLayer(floatingImages, doc, layerMode = "front") {
|
|
427
|
+
const layer = doc.createElement("div");
|
|
428
|
+
layer.className = layerMode === "behind" ? "layout-floating-images-layer layout-floating-images-layer-behind" : "layout-floating-images-layer";
|
|
429
|
+
layer.style.position = "absolute";
|
|
430
|
+
layer.style.top = "0";
|
|
431
|
+
layer.style.left = "0";
|
|
432
|
+
layer.style.right = "0";
|
|
433
|
+
layer.style.bottom = "0";
|
|
434
|
+
layer.style.pointerEvents = "none";
|
|
435
|
+
if (layerMode === "front") layer.style.zIndex = "10";
|
|
436
|
+
for (const floatImg of floatingImages) {
|
|
437
|
+
const container = doc.createElement("div");
|
|
438
|
+
container.className = "layout-page-floating-image";
|
|
439
|
+
container.style.position = "absolute";
|
|
440
|
+
container.style.pointerEvents = "auto";
|
|
441
|
+
container.style.top = `${floatImg.y}px`;
|
|
442
|
+
container.style.left = `${floatImg.x}px`;
|
|
443
|
+
if (floatImg.pmStart !== void 0) container.dataset["pmStart"] = String(floatImg.pmStart);
|
|
444
|
+
if (floatImg.pmEnd !== void 0) container.dataset["pmEnd"] = String(floatImg.pmEnd);
|
|
445
|
+
const img = doc.createElement("img");
|
|
446
|
+
img.src = floatImg.src;
|
|
447
|
+
img.style.width = `${floatImg.width}px`;
|
|
448
|
+
img.style.height = `${floatImg.height}px`;
|
|
449
|
+
img.style.display = "block";
|
|
450
|
+
img.style.maxWidth = "none";
|
|
451
|
+
img.style.maxHeight = "none";
|
|
452
|
+
if (floatImg.alt) img.alt = floatImg.alt;
|
|
453
|
+
if (floatImg.transform) img.style.transform = floatImg.transform;
|
|
454
|
+
if (hasImageVisualAttrs(floatImg)) applyImageVisualAttrs(img, floatImg);
|
|
455
|
+
container.append(img);
|
|
456
|
+
layer.append(container);
|
|
457
|
+
}
|
|
458
|
+
return layer;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Render header or footer content
|
|
462
|
+
*/
|
|
463
|
+
function renderHeaderFooterContent(content, context, options, layout) {
|
|
464
|
+
const doc = options.document ?? document;
|
|
465
|
+
const containerEl = doc.createElement("div");
|
|
466
|
+
containerEl.style.position = "relative";
|
|
467
|
+
const contentWidth = context.contentWidth ?? 600;
|
|
468
|
+
const floatingImages = [];
|
|
469
|
+
let cursorY = 0;
|
|
470
|
+
const hfContext = {
|
|
471
|
+
...context,
|
|
472
|
+
positioning: "absolute"
|
|
473
|
+
};
|
|
474
|
+
for (let i = 0; i < content.blocks.length; i++) {
|
|
475
|
+
const block = content.blocks[i];
|
|
476
|
+
const measure = content.measures[i];
|
|
477
|
+
if (!block || !measure) continue;
|
|
478
|
+
if (block.kind === "paragraph" && measure.kind === "paragraph") {
|
|
479
|
+
const paragraphBlock = block;
|
|
480
|
+
const paragraphMeasure = measure;
|
|
481
|
+
const paragraphStartY = cursorY;
|
|
482
|
+
const inlineRuns = [];
|
|
483
|
+
for (const run of paragraphBlock.runs) if (run.kind === "image" && (isFloatingImageRun(run) || run.position)) floatingImages.push({
|
|
484
|
+
src: run.src,
|
|
485
|
+
width: run.width,
|
|
486
|
+
height: run.height,
|
|
487
|
+
...run.alt !== void 0 ? { alt: run.alt } : {},
|
|
488
|
+
...run.opacity != null ? { opacity: run.opacity } : {},
|
|
489
|
+
...run.pmStart !== void 0 ? { pmStart: run.pmStart } : {},
|
|
490
|
+
...run.pmEnd !== void 0 ? { pmEnd: run.pmEnd } : {},
|
|
491
|
+
paragraphY: paragraphStartY,
|
|
492
|
+
behindDoc: run.wrapType === "behind",
|
|
493
|
+
position: run.position ?? {}
|
|
494
|
+
});
|
|
495
|
+
else inlineRuns.push(run);
|
|
496
|
+
const inlineBlock = {
|
|
497
|
+
...paragraphBlock,
|
|
498
|
+
runs: inlineRuns
|
|
499
|
+
};
|
|
500
|
+
const fragEl = renderParagraphFragment({
|
|
501
|
+
kind: "paragraph",
|
|
502
|
+
blockId: paragraphBlock.id,
|
|
503
|
+
x: 0,
|
|
504
|
+
y: cursorY,
|
|
505
|
+
width: contentWidth,
|
|
506
|
+
height: paragraphMeasure.totalHeight,
|
|
507
|
+
fromLine: 0,
|
|
508
|
+
toLine: paragraphMeasure.lines.length
|
|
509
|
+
}, inlineBlock, paragraphMeasure, hfContext, { document: doc });
|
|
510
|
+
const spaceBefore = paragraphBlock.attrs?.spacingExplicit?.before === true ? paragraphBlock.attrs?.spacing?.before ?? 0 : 0;
|
|
511
|
+
fragEl.style.position = "absolute";
|
|
512
|
+
fragEl.style.top = `${cursorY + spaceBefore}px`;
|
|
513
|
+
fragEl.style.left = "0";
|
|
514
|
+
fragEl.style.width = `${contentWidth}px`;
|
|
515
|
+
containerEl.append(fragEl);
|
|
516
|
+
cursorY += paragraphMeasure.totalHeight;
|
|
517
|
+
} else if (block.kind === "table" && measure.kind === "table") {
|
|
518
|
+
const fragEl = renderTableFragment({
|
|
519
|
+
kind: "table",
|
|
520
|
+
blockId: block.id,
|
|
521
|
+
x: 0,
|
|
522
|
+
y: cursorY,
|
|
523
|
+
width: measure.totalWidth,
|
|
524
|
+
height: measure.totalHeight,
|
|
525
|
+
fromRow: 0,
|
|
526
|
+
toRow: measure.rows.length,
|
|
527
|
+
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
528
|
+
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
529
|
+
}, block, measure, hfContext, { document: doc });
|
|
530
|
+
if (block.floating) {
|
|
531
|
+
const { left, top } = resolveHeaderFooterFloatingTablePosition(block.floating, measure, layout, cursorY);
|
|
532
|
+
fragEl.style.position = "absolute";
|
|
533
|
+
fragEl.style.top = `${top}px`;
|
|
534
|
+
fragEl.style.left = `${left}px`;
|
|
535
|
+
containerEl.append(fragEl);
|
|
536
|
+
} else {
|
|
537
|
+
let inlineLeft = 0;
|
|
538
|
+
if (block.justification === "center") inlineLeft = (contentWidth - measure.totalWidth) / 2;
|
|
539
|
+
else if (block.justification === "right") inlineLeft = contentWidth - measure.totalWidth;
|
|
540
|
+
else if (block.indent) inlineLeft = block.indent;
|
|
541
|
+
fragEl.style.position = "absolute";
|
|
542
|
+
fragEl.style.top = `${cursorY}px`;
|
|
543
|
+
fragEl.style.left = `${inlineLeft}px`;
|
|
544
|
+
containerEl.append(fragEl);
|
|
545
|
+
cursorY += measure.totalHeight;
|
|
546
|
+
}
|
|
547
|
+
} else if (block.kind === "textBox" && measure.kind === "textBox") {
|
|
548
|
+
const fragEl = renderTextBoxFragment({
|
|
549
|
+
kind: "textBox",
|
|
550
|
+
blockId: block.id,
|
|
551
|
+
x: 0,
|
|
552
|
+
y: cursorY,
|
|
553
|
+
width: measure.width,
|
|
554
|
+
height: measure.height,
|
|
555
|
+
...block.pmStart !== void 0 ? { pmStart: block.pmStart } : {},
|
|
556
|
+
...block.pmEnd !== void 0 ? { pmEnd: block.pmEnd } : {}
|
|
557
|
+
}, block, measure, hfContext, { document: doc });
|
|
558
|
+
fragEl.style.top = `${cursorY}px`;
|
|
559
|
+
fragEl.style.left = resolveHeaderFooterFloatLeft(measure.width, block.position?.horizontal, layout);
|
|
560
|
+
if (block.wrapType === "behind") fragEl.style.zIndex = "-1";
|
|
561
|
+
containerEl.append(fragEl);
|
|
562
|
+
if (!isPositionedHeaderFooterTextBoxBlock(block)) cursorY += measure.height;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
for (const floatImg of floatingImages) {
|
|
566
|
+
const img = doc.createElement("img");
|
|
567
|
+
img.src = floatImg.src;
|
|
568
|
+
img.width = floatImg.width;
|
|
569
|
+
img.height = floatImg.height;
|
|
570
|
+
if (floatImg.alt) img.alt = floatImg.alt;
|
|
571
|
+
img.classList.add("layout-run", "layout-run-image");
|
|
572
|
+
if (floatImg.pmStart !== void 0) img.dataset["pmStart"] = String(floatImg.pmStart);
|
|
573
|
+
if (floatImg.pmEnd !== void 0) img.dataset["pmEnd"] = String(floatImg.pmEnd);
|
|
574
|
+
img.style.position = "absolute";
|
|
575
|
+
img.style.display = "block";
|
|
576
|
+
img.style.width = `${floatImg.width}px`;
|
|
577
|
+
img.style.height = `${floatImg.height}px`;
|
|
578
|
+
img.style.maxWidth = "none";
|
|
579
|
+
img.style.maxHeight = "none";
|
|
580
|
+
if (floatImg.behindDoc) img.style.zIndex = "-1";
|
|
581
|
+
if (hasImageVisualAttrs(floatImg)) applyImageVisualAttrs(img, floatImg);
|
|
582
|
+
applyHeaderFooterFloatHorizontalPosition(img, floatImg, layout);
|
|
583
|
+
img.style.top = `${resolveHeaderFooterFloatTop(floatImg, layout)}px`;
|
|
584
|
+
containerEl.append(img);
|
|
585
|
+
}
|
|
586
|
+
return containerEl;
|
|
587
|
+
}
|
|
588
|
+
function isPositionedHeaderFooterTextBoxBlock(block) {
|
|
589
|
+
if (block.displayMode === "block" || block.displayMode === "inline") return false;
|
|
590
|
+
return isFloatingTextBoxBlock(block);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Calculate the painter's actual footnote-area height in pixels:
|
|
594
|
+
* separator slot + per-footnote content (or fallback line) + per-footnote
|
|
595
|
+
* `marginBottom` spacing the painter applies in `renderFootnoteArea`. Used
|
|
596
|
+
* to clamp the paginator's reservation if it under-estimated, so dense
|
|
597
|
+
* stacks never overflow past the page bottom. Mirrors the upstream helper
|
|
598
|
+
* from eigenpal/docx-editor#485, extended for folio's fallback rendering
|
|
599
|
+
* and any wrapper margin so the helper matches the painted stack.
|
|
600
|
+
*/
|
|
601
|
+
function calculateFootnoteAreaRenderHeight(footnotes) {
|
|
602
|
+
let height = 12;
|
|
603
|
+
for (const fn of footnotes) {
|
|
604
|
+
const entryHeight = fn.content ? fn.content.height : 13;
|
|
605
|
+
height += entryHeight + 0;
|
|
606
|
+
}
|
|
607
|
+
return height;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Render the footnote area at the bottom of a page.
|
|
611
|
+
* Includes a separator line (33% width) and footnote entries.
|
|
612
|
+
*/
|
|
613
|
+
function renderFootnoteArea(footnotes, contentWidth, doc, context) {
|
|
614
|
+
const container = doc.createElement("div");
|
|
615
|
+
container.className = "layout-footnote-area";
|
|
616
|
+
container.style.width = `${contentWidth}px`;
|
|
617
|
+
const separator = doc.createElement("div");
|
|
618
|
+
const separatorRuleHeight = .5;
|
|
619
|
+
const separatorMargin = (12 - separatorRuleHeight) / 2;
|
|
620
|
+
separator.style.width = "33%";
|
|
621
|
+
separator.style.height = `${separatorRuleHeight}px`;
|
|
622
|
+
separator.style.backgroundColor = "var(--doc-canvas-text, #000)";
|
|
623
|
+
separator.style.marginTop = `${separatorMargin}px`;
|
|
624
|
+
separator.style.marginBottom = `${separatorMargin}px`;
|
|
625
|
+
container.append(separator);
|
|
626
|
+
for (const fn of footnotes) {
|
|
627
|
+
const fnEl = doc.createElement("div");
|
|
628
|
+
fnEl.style.marginBottom = `0px`;
|
|
629
|
+
fnEl.style.color = "var(--doc-canvas-text, #000)";
|
|
630
|
+
if (fn.content) {
|
|
631
|
+
const contentEl = renderFootnoteContent(fn, contentWidth, doc, context);
|
|
632
|
+
fnEl.append(contentEl);
|
|
633
|
+
} else {
|
|
634
|
+
fnEl.style.fontSize = "10px";
|
|
635
|
+
fnEl.style.lineHeight = `${13 / 10}`;
|
|
636
|
+
const sup = doc.createElement("sup");
|
|
637
|
+
sup.textContent = fn.displayNumber;
|
|
638
|
+
sup.style.fontSize = "7px";
|
|
639
|
+
sup.style.marginRight = "2px";
|
|
640
|
+
fnEl.append(sup);
|
|
641
|
+
const textNode = doc.createTextNode(` ${fn.text ?? ""}`);
|
|
642
|
+
fnEl.append(textNode);
|
|
643
|
+
}
|
|
644
|
+
container.append(fnEl);
|
|
645
|
+
}
|
|
646
|
+
return container;
|
|
647
|
+
}
|
|
648
|
+
function renderFootnoteContent(footnote, contentWidth, doc, context) {
|
|
649
|
+
const content = footnote.content;
|
|
650
|
+
if (!content) panic("Missing structured footnote content");
|
|
651
|
+
const wrapper = doc.createElement("div");
|
|
652
|
+
wrapper.className = "layout-footnote-content";
|
|
653
|
+
wrapper.style.position = "relative";
|
|
654
|
+
wrapper.style.width = `${contentWidth}px`;
|
|
655
|
+
wrapper.style.height = `${content.height}px`;
|
|
656
|
+
const renderContext = {
|
|
657
|
+
pageNumber: context?.pageNumber ?? 0,
|
|
658
|
+
totalPages: context?.totalPages ?? 0,
|
|
659
|
+
section: context?.section ?? "body",
|
|
660
|
+
contentWidth
|
|
661
|
+
};
|
|
662
|
+
let y = 0;
|
|
663
|
+
for (let index = 0; index < content.blocks.length; index++) {
|
|
664
|
+
const block = content.blocks[index];
|
|
665
|
+
const measure = content.measures[index];
|
|
666
|
+
if (!block || !measure) continue;
|
|
667
|
+
const blockEl = renderFootnoteBlock(block, measure, contentWidth, y, renderContext, doc);
|
|
668
|
+
if (!blockEl) continue;
|
|
669
|
+
wrapper.append(blockEl);
|
|
670
|
+
y += getFootnoteMeasureHeight(measure);
|
|
671
|
+
}
|
|
672
|
+
return wrapper;
|
|
673
|
+
}
|
|
674
|
+
function renderFootnoteBlock(block, measure, contentWidth, y, context, doc) {
|
|
675
|
+
const renderBlock = stripFootnotePmAnchors(block);
|
|
676
|
+
if (renderBlock.kind === "paragraph" && measure.kind === "paragraph") {
|
|
677
|
+
const element = renderParagraphFragment({
|
|
678
|
+
kind: "paragraph",
|
|
679
|
+
blockId: renderBlock.id,
|
|
680
|
+
x: 0,
|
|
681
|
+
y,
|
|
682
|
+
width: contentWidth,
|
|
683
|
+
height: measure.totalHeight,
|
|
684
|
+
fromLine: 0,
|
|
685
|
+
toLine: measure.lines.length
|
|
686
|
+
}, renderBlock, measure, context, { document: doc });
|
|
687
|
+
positionFootnoteBlock(element, y, contentWidth, measure.totalHeight);
|
|
688
|
+
return element;
|
|
689
|
+
}
|
|
690
|
+
if (renderBlock.kind === "table" && measure.kind === "table") {
|
|
691
|
+
const element = renderTableFragment({
|
|
692
|
+
kind: "table",
|
|
693
|
+
blockId: renderBlock.id,
|
|
694
|
+
x: 0,
|
|
695
|
+
y,
|
|
696
|
+
width: measure.totalWidth,
|
|
697
|
+
height: measure.totalHeight,
|
|
698
|
+
fromRow: 0,
|
|
699
|
+
toRow: renderBlock.rows.length
|
|
700
|
+
}, renderBlock, measure, context, { document: doc });
|
|
701
|
+
positionFootnoteBlock(element, y, measure.totalWidth, measure.totalHeight);
|
|
702
|
+
return element;
|
|
703
|
+
}
|
|
704
|
+
if (renderBlock.kind === "image" && measure.kind === "image") {
|
|
705
|
+
const element = renderImageFragment({
|
|
706
|
+
kind: "image",
|
|
707
|
+
blockId: renderBlock.id,
|
|
708
|
+
x: 0,
|
|
709
|
+
y,
|
|
710
|
+
width: measure.width,
|
|
711
|
+
height: measure.height
|
|
712
|
+
}, renderBlock, measure, context, { document: doc });
|
|
713
|
+
positionFootnoteBlock(element, y, measure.width, measure.height);
|
|
714
|
+
return element;
|
|
715
|
+
}
|
|
716
|
+
if (renderBlock.kind === "textBox" && measure.kind === "textBox") {
|
|
717
|
+
const element = renderTextBoxFragment({
|
|
718
|
+
kind: "textBox",
|
|
719
|
+
blockId: renderBlock.id,
|
|
720
|
+
x: 0,
|
|
721
|
+
y,
|
|
722
|
+
width: measure.width,
|
|
723
|
+
height: measure.height
|
|
724
|
+
}, renderBlock, measure, context, { document: doc });
|
|
725
|
+
positionFootnoteBlock(element, y, measure.width, measure.height);
|
|
726
|
+
return element;
|
|
727
|
+
}
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
function stripFootnotePmAnchors(block) {
|
|
731
|
+
switch (block.kind) {
|
|
732
|
+
case "paragraph": return stripFootnoteParagraphPmAnchors(block);
|
|
733
|
+
case "table": {
|
|
734
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...table } = block;
|
|
735
|
+
return {
|
|
736
|
+
...table,
|
|
737
|
+
rows: table.rows.map(stripFootnoteTableRowPmAnchors)
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
case "image": {
|
|
741
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...image } = block;
|
|
742
|
+
return image;
|
|
743
|
+
}
|
|
744
|
+
case "textBox": {
|
|
745
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...textBox } = block;
|
|
746
|
+
return {
|
|
747
|
+
...textBox,
|
|
748
|
+
content: textBox.content.map(stripFootnoteParagraphPmAnchors)
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
case "pageBreak":
|
|
752
|
+
case "columnBreak": {
|
|
753
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...breakBlock } = block;
|
|
754
|
+
return breakBlock;
|
|
755
|
+
}
|
|
756
|
+
case "sectionBreak": return block;
|
|
757
|
+
default: return block;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function stripFootnoteTableRowPmAnchors(row) {
|
|
761
|
+
return {
|
|
762
|
+
...row,
|
|
763
|
+
cells: row.cells.map(stripFootnoteTableCellPmAnchors)
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function stripFootnoteTableCellPmAnchors(cell) {
|
|
767
|
+
return {
|
|
768
|
+
...cell,
|
|
769
|
+
blocks: cell.blocks.map(stripFootnotePmAnchors)
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function stripFootnoteParagraphPmAnchors(block) {
|
|
773
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...paragraph } = block;
|
|
774
|
+
return {
|
|
775
|
+
...paragraph,
|
|
776
|
+
runs: paragraph.runs.map(stripFootnoteRunPmAnchors)
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function stripFootnoteRunPmAnchors(run) {
|
|
780
|
+
switch (run.kind) {
|
|
781
|
+
case "text": {
|
|
782
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...textRun } = run;
|
|
783
|
+
return textRun;
|
|
784
|
+
}
|
|
785
|
+
case "tab": {
|
|
786
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...tabRun } = run;
|
|
787
|
+
return tabRun;
|
|
788
|
+
}
|
|
789
|
+
case "image": {
|
|
790
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...imageRun } = run;
|
|
791
|
+
return imageRun;
|
|
792
|
+
}
|
|
793
|
+
case "lineBreak": {
|
|
794
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...lineBreakRun } = run;
|
|
795
|
+
return lineBreakRun;
|
|
796
|
+
}
|
|
797
|
+
case "field": {
|
|
798
|
+
const { pmStart: _pmStart, pmEnd: _pmEnd, ...fieldRun } = run;
|
|
799
|
+
return fieldRun;
|
|
800
|
+
}
|
|
801
|
+
default: return run;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function positionFootnoteBlock(element, top, width, height) {
|
|
805
|
+
element.style.position = "absolute";
|
|
806
|
+
element.style.left = "0";
|
|
807
|
+
element.style.top = `${top}px`;
|
|
808
|
+
element.style.width = `${width}px`;
|
|
809
|
+
element.style.height = `${height}px`;
|
|
810
|
+
}
|
|
811
|
+
function getFootnoteMeasureHeight(measure) {
|
|
812
|
+
if (measure.kind === "paragraph" || measure.kind === "table") return measure.totalHeight;
|
|
813
|
+
if (measure.kind === "image" || measure.kind === "textBox") return measure.height;
|
|
814
|
+
return 0;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Render a single page to DOM
|
|
818
|
+
*
|
|
819
|
+
* @param page - The page to render
|
|
820
|
+
* @param context - Rendering context
|
|
821
|
+
* @param options - Rendering options
|
|
822
|
+
* @returns The page DOM element
|
|
823
|
+
*/
|
|
824
|
+
function renderPage(page, context, options = {}) {
|
|
825
|
+
const doc = options.document ?? document;
|
|
826
|
+
const pageEl = doc.createElement("div");
|
|
827
|
+
pageEl.className = options.pageClassName ?? PAGE_CLASS_NAMES.page;
|
|
828
|
+
pageEl.dataset["pageNumber"] = String(page.number);
|
|
829
|
+
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
830
|
+
const pageBorderEl = renderPageBorderOverlay(page, options, doc);
|
|
831
|
+
if (pageBorderEl && options.pageBorders?.zOrder === "back") pageEl.append(pageBorderEl);
|
|
832
|
+
if (options.watermark) {
|
|
833
|
+
const watermarkEl = renderWatermarkLayer(options.watermark, page, doc, options.watermarkImageSrc !== void 0 ? { imageSrc: options.watermarkImageSrc } : {});
|
|
834
|
+
if (watermarkEl) pageEl.append(watermarkEl);
|
|
835
|
+
}
|
|
836
|
+
const contentEl = doc.createElement("div");
|
|
837
|
+
contentEl.className = PAGE_CLASS_NAMES.content;
|
|
838
|
+
applyContentAreaStyles(contentEl, page);
|
|
839
|
+
const contentWidth = page.size.w - page.margins.left - page.margins.right;
|
|
840
|
+
const allFloatingImages = [];
|
|
841
|
+
const floatingRects = [];
|
|
842
|
+
const scopedFloatingRects = [];
|
|
843
|
+
const pageGeometry = {
|
|
844
|
+
pageWidth: page.size.w,
|
|
845
|
+
pageHeight: page.size.h,
|
|
846
|
+
marginLeft: page.margins.left,
|
|
847
|
+
marginTop: page.margins.top,
|
|
848
|
+
marginRight: page.margins.right,
|
|
849
|
+
marginBottom: page.margins.bottom,
|
|
850
|
+
contentWidth,
|
|
851
|
+
contentHeight: page.size.h - page.margins.top - page.margins.bottom
|
|
852
|
+
};
|
|
853
|
+
for (const fragment of page.fragments) if (fragment.kind === "paragraph" && options.blockLookup) {
|
|
854
|
+
const blockData = options.blockLookup.get(String(fragment.blockId));
|
|
855
|
+
if (blockData?.block.kind === "paragraph") {
|
|
856
|
+
const paragraphBlock = blockData.block;
|
|
857
|
+
const extracted = extractFloatingImagesFromParagraph(paragraphBlock, fragment.y - page.margins.top, pageGeometry);
|
|
858
|
+
allFloatingImages.push(...extracted);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
for (const img of allFloatingImages) {
|
|
862
|
+
if (!img.affectsTextWrap) continue;
|
|
863
|
+
floatingRects.push({
|
|
864
|
+
side: img.side,
|
|
865
|
+
x: img.x,
|
|
866
|
+
y: img.y,
|
|
867
|
+
width: img.width,
|
|
868
|
+
height: img.height,
|
|
869
|
+
distTop: img.distTop,
|
|
870
|
+
distBottom: img.distBottom,
|
|
871
|
+
distLeft: img.distLeft,
|
|
872
|
+
distRight: img.distRight,
|
|
873
|
+
...img.wrapText !== void 0 ? { wrapText: img.wrapText } : {},
|
|
874
|
+
...img.wrapType !== void 0 ? { wrapType: img.wrapType } : {}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
if (options.blockLookup) for (const fragment of page.fragments) {
|
|
878
|
+
if (fragment.kind !== "table") continue;
|
|
879
|
+
const blockData = options.blockLookup.get(String(fragment.blockId));
|
|
880
|
+
if (blockData?.block.kind !== "table") continue;
|
|
881
|
+
const floating = blockData.block.floating;
|
|
882
|
+
if (!floating) continue;
|
|
883
|
+
const contentX = fragment.x - page.margins.left;
|
|
884
|
+
const contentY = fragment.y - page.margins.top;
|
|
885
|
+
const distTop = floating.topFromText ?? 0;
|
|
886
|
+
const distBottom = floating.bottomFromText ?? 0;
|
|
887
|
+
const distLeft = floating.leftFromText ?? 12;
|
|
888
|
+
const distRight = floating.rightFromText ?? 12;
|
|
889
|
+
const side = contentX < contentWidth / 2 ? "left" : "right";
|
|
890
|
+
floatingRects.push({
|
|
891
|
+
side,
|
|
892
|
+
x: contentX,
|
|
893
|
+
y: contentY,
|
|
894
|
+
width: fragment.width,
|
|
895
|
+
height: fragment.height,
|
|
896
|
+
distTop,
|
|
897
|
+
distBottom,
|
|
898
|
+
distLeft,
|
|
899
|
+
distRight
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
if (options.blockLookup) for (let fragmentIndex = 0; fragmentIndex < page.fragments.length; fragmentIndex++) {
|
|
903
|
+
const fragment = page.fragments[fragmentIndex];
|
|
904
|
+
if (fragment.kind !== "textBox") continue;
|
|
905
|
+
const blockData = options.blockLookup.get(String(fragment.blockId));
|
|
906
|
+
if (blockData?.block.kind !== "textBox") continue;
|
|
907
|
+
const textBoxBlock = blockData.block;
|
|
908
|
+
if (!isFloatingTextBoxBlock(textBoxBlock)) continue;
|
|
909
|
+
const reservesBand = floatingTextBoxReservesBand(textBoxBlock);
|
|
910
|
+
if (!reservesBand && !floatingTextBoxWrapsText(textBoxBlock)) continue;
|
|
911
|
+
const contentX = fragment.x - page.margins.left;
|
|
912
|
+
const contentY = fragment.y - page.margins.top;
|
|
913
|
+
const distTop = textBoxBlock.distTop ?? 0;
|
|
914
|
+
const distBottom = textBoxBlock.distBottom ?? 0;
|
|
915
|
+
const distLeft = textBoxBlock.distLeft ?? 12;
|
|
916
|
+
const distRight = textBoxBlock.distRight ?? 12;
|
|
917
|
+
let side = contentX < contentWidth / 2 ? "left" : "right";
|
|
918
|
+
if (textBoxBlock.cssFloat === "left") side = "left";
|
|
919
|
+
else if (textBoxBlock.cssFloat === "right") side = "right";
|
|
920
|
+
const rect = {
|
|
921
|
+
side,
|
|
922
|
+
x: contentX,
|
|
923
|
+
y: contentY,
|
|
924
|
+
width: fragment.width,
|
|
925
|
+
height: fragment.height,
|
|
926
|
+
distTop,
|
|
927
|
+
distBottom,
|
|
928
|
+
distLeft,
|
|
929
|
+
distRight
|
|
930
|
+
};
|
|
931
|
+
if (textBoxBlock.wrapText !== void 0) rect.wrapText = textBoxBlock.wrapText;
|
|
932
|
+
if (textBoxBlock.wrapType !== void 0) rect.wrapType = textBoxBlock.wrapType;
|
|
933
|
+
if (reservesBand) scopedFloatingRects.push({
|
|
934
|
+
startsAtFragmentIndex: fragmentIndex,
|
|
935
|
+
rect
|
|
936
|
+
});
|
|
937
|
+
else floatingRects.push(rect);
|
|
938
|
+
}
|
|
939
|
+
const floatingZones = floatingRects.length > 0 ? rectsToFloatingZones(floatingRects, contentWidth) : [];
|
|
940
|
+
const scopedFloatingZones = scopedFloatingRects.flatMap(({ startsAtFragmentIndex, rect }) => rectsToFloatingZones([rect], contentWidth).map((zone) => ({
|
|
941
|
+
startsAtFragmentIndex,
|
|
942
|
+
zone
|
|
943
|
+
})));
|
|
944
|
+
const behindFloatingImages = allFloatingImages.filter((img) => img.behindDoc);
|
|
945
|
+
const frontFloatingImages = allFloatingImages.filter((img) => !img.behindDoc);
|
|
946
|
+
if (behindFloatingImages.length > 0) {
|
|
947
|
+
const behindLayer = renderFloatingImagesLayer(behindFloatingImages, doc, "behind");
|
|
948
|
+
contentEl.append(behindLayer);
|
|
949
|
+
}
|
|
950
|
+
const getParaBorders = (frag) => {
|
|
951
|
+
if (frag.kind !== "paragraph" || !options.blockLookup || !frag.blockId) return;
|
|
952
|
+
const blockData = options.blockLookup.get(String(frag.blockId));
|
|
953
|
+
if (blockData?.block.kind === "paragraph") return blockData.block.attrs?.borders;
|
|
954
|
+
};
|
|
955
|
+
let prevParagraphBorders;
|
|
956
|
+
const renderedInlineImageKeysByBlock = /* @__PURE__ */ new Map();
|
|
957
|
+
for (let i = 0; i < page.fragments.length; i++) {
|
|
958
|
+
const fragment = page.fragments[i];
|
|
959
|
+
let fragmentEl;
|
|
960
|
+
const fragmentContext = {
|
|
961
|
+
...context,
|
|
962
|
+
section: "body",
|
|
963
|
+
contentWidth
|
|
964
|
+
};
|
|
965
|
+
const fragmentContentY = fragment.y - page.margins.top;
|
|
966
|
+
if (options.blockLookup && fragment.blockId) {
|
|
967
|
+
const blockData = options.blockLookup.get(String(fragment.blockId));
|
|
968
|
+
if (fragment.kind === "paragraph" && blockData?.block.kind === "paragraph" && blockData.measure.kind === "paragraph") {
|
|
969
|
+
const paragraphBlock = blockData.block;
|
|
970
|
+
const nextBorders = i + 1 < page.fragments.length ? getParaBorders(page.fragments[i + 1]) : void 0;
|
|
971
|
+
const blockKey = String(fragment.blockId);
|
|
972
|
+
let renderedInlineImageKeys = renderedInlineImageKeysByBlock.get(blockKey);
|
|
973
|
+
if (!renderedInlineImageKeys) {
|
|
974
|
+
renderedInlineImageKeys = /* @__PURE__ */ new Set();
|
|
975
|
+
renderedInlineImageKeysByBlock.set(blockKey, renderedInlineImageKeys);
|
|
976
|
+
}
|
|
977
|
+
let paragraphMeasure = blockData.measure;
|
|
978
|
+
const fragmentFloatingZones = floatingZonesForFragment(floatingZones, scopedFloatingZones, i);
|
|
979
|
+
if (fragmentFloatingZones.length > 0) paragraphMeasure = measureParagraph(paragraphBlock, contentWidth, {
|
|
980
|
+
floatingZones: fragmentFloatingZones,
|
|
981
|
+
paragraphYOffset: fragmentContentY
|
|
982
|
+
});
|
|
983
|
+
fragmentEl = renderParagraphFragment(fragment, paragraphBlock, paragraphMeasure, fragmentContext, {
|
|
984
|
+
document: doc,
|
|
985
|
+
fragmentContentY,
|
|
986
|
+
...prevParagraphBorders !== void 0 ? { prevBorders: prevParagraphBorders } : {},
|
|
987
|
+
...nextBorders !== void 0 ? { nextBorders } : {},
|
|
988
|
+
renderedInlineImageKeys
|
|
989
|
+
});
|
|
990
|
+
prevParagraphBorders = paragraphBlock.attrs?.borders;
|
|
991
|
+
} else if (fragment.kind === "table" && blockData?.block.kind === "table" && blockData.measure.kind === "table") {
|
|
992
|
+
fragmentEl = renderTableFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
|
|
993
|
+
prevParagraphBorders = void 0;
|
|
994
|
+
} else if (fragment.kind === "image" && blockData?.block.kind === "image" && blockData.measure.kind === "image") {
|
|
995
|
+
fragmentEl = renderImageFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
|
|
996
|
+
prevParagraphBorders = void 0;
|
|
997
|
+
} else if (fragment.kind === "textBox" && blockData?.block.kind === "textBox" && blockData.measure.kind === "textBox") {
|
|
998
|
+
fragmentEl = renderTextBoxFragment(fragment, blockData.block, blockData.measure, fragmentContext, { document: doc });
|
|
999
|
+
prevParagraphBorders = void 0;
|
|
1000
|
+
} else {
|
|
1001
|
+
fragmentEl = renderFragment(fragment, fragmentContext, { document: doc });
|
|
1002
|
+
prevParagraphBorders = void 0;
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
fragmentEl = renderFragment(fragment, fragmentContext, { document: doc });
|
|
1006
|
+
prevParagraphBorders = void 0;
|
|
1007
|
+
}
|
|
1008
|
+
applyFragmentStyles(fragmentEl, fragment, {
|
|
1009
|
+
left: page.margins.left,
|
|
1010
|
+
top: page.margins.top
|
|
1011
|
+
});
|
|
1012
|
+
contentEl.append(fragmentEl);
|
|
1013
|
+
}
|
|
1014
|
+
if (frontFloatingImages.length > 0) {
|
|
1015
|
+
const frontLayer = renderFloatingImagesLayer(frontFloatingImages, doc, "front");
|
|
1016
|
+
contentEl.append(frontLayer);
|
|
1017
|
+
}
|
|
1018
|
+
if (page.columns && page.columns.separator && page.columns.count > 1) {
|
|
1019
|
+
const colCount = page.columns.count;
|
|
1020
|
+
const colGap = page.columns.gap;
|
|
1021
|
+
const colWidth = (contentWidth - (colCount - 1) * colGap) / colCount;
|
|
1022
|
+
const contentHeight = page.size.h - page.margins.top - page.margins.bottom;
|
|
1023
|
+
for (let col = 0; col < colCount - 1; col++) {
|
|
1024
|
+
const lineX = (col + 1) * colWidth + col * colGap + colGap / 2;
|
|
1025
|
+
const line = doc.createElement("div");
|
|
1026
|
+
line.style.position = "absolute";
|
|
1027
|
+
line.style.left = `${lineX}px`;
|
|
1028
|
+
line.style.top = "0";
|
|
1029
|
+
line.style.height = `${contentHeight}px`;
|
|
1030
|
+
line.style.width = "0.5px";
|
|
1031
|
+
line.style.backgroundColor = "var(--doc-canvas-text, #000)";
|
|
1032
|
+
line.style.pointerEvents = "none";
|
|
1033
|
+
contentEl.append(line);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (options.footnoteArea && options.footnoteArea.length > 0) {
|
|
1037
|
+
const fnAreaEl = renderFootnoteArea(options.footnoteArea, contentWidth, doc, context);
|
|
1038
|
+
fnAreaEl.style.position = "absolute";
|
|
1039
|
+
const reservedHeight = Math.max(page.footnoteReservedHeight ?? 0, calculateFootnoteAreaRenderHeight(options.footnoteArea));
|
|
1040
|
+
const contentAreaBottom = page.size.h - page.margins.bottom - page.margins.top;
|
|
1041
|
+
fnAreaEl.style.top = `${Math.max(-page.margins.top, contentAreaBottom - reservedHeight)}px`;
|
|
1042
|
+
fnAreaEl.style.left = "0";
|
|
1043
|
+
fnAreaEl.style.right = "0";
|
|
1044
|
+
contentEl.append(fnAreaEl);
|
|
1045
|
+
}
|
|
1046
|
+
pageEl.append(contentEl);
|
|
1047
|
+
{
|
|
1048
|
+
const headerDistance = options.headerDistance ?? page.margins.header ?? 48;
|
|
1049
|
+
const headerContentWidth = page.size.w - page.margins.left - page.margins.right;
|
|
1050
|
+
const availableHeaderHeight = Math.max(page.margins.top - headerDistance, 48);
|
|
1051
|
+
const headerVisualTop = options.headerContent?.visualTop ?? 0;
|
|
1052
|
+
const headerVisualBottom = options.headerContent?.visualBottom ?? options.headerContent?.height ?? 0;
|
|
1053
|
+
const headerFlowHeight = options.headerContent?.height ?? 0;
|
|
1054
|
+
const interactiveHeaderHeight = Math.max(headerFlowHeight - Math.min(0, headerVisualTop), 24);
|
|
1055
|
+
const headerOverflows = headerVisualBottom > availableHeaderHeight;
|
|
1056
|
+
const headerContentFitsBox = headerVisualBottom - headerVisualTop <= interactiveHeaderHeight;
|
|
1057
|
+
const headerEl = doc.createElement("div");
|
|
1058
|
+
headerEl.className = PAGE_CLASS_NAMES.header;
|
|
1059
|
+
if (options.headerContent?.rId) headerEl.dataset["rid"] = options.headerContent.rId;
|
|
1060
|
+
headerEl.style.position = "absolute";
|
|
1061
|
+
headerEl.style.top = `${headerDistance + headerVisualTop}px`;
|
|
1062
|
+
headerEl.style.left = `${page.margins.left}px`;
|
|
1063
|
+
headerEl.style.right = `${page.margins.right}px`;
|
|
1064
|
+
headerEl.style.width = `${headerContentWidth}px`;
|
|
1065
|
+
headerEl.style.height = `${interactiveHeaderHeight}px`;
|
|
1066
|
+
headerEl.style.minHeight = `${interactiveHeaderHeight}px`;
|
|
1067
|
+
headerEl.style.opacity = "0.62";
|
|
1068
|
+
let shouldClipHeader = !headerOverflows && headerContentFitsBox;
|
|
1069
|
+
if (options.headerContent && options.headerContent.blocks.length > 0) {
|
|
1070
|
+
const headerContentEl = renderHeaderFooterContent(options.headerContent, {
|
|
1071
|
+
...context,
|
|
1072
|
+
section: "header",
|
|
1073
|
+
contentWidth: headerContentWidth
|
|
1074
|
+
}, options, {
|
|
1075
|
+
flowTop: headerDistance,
|
|
1076
|
+
flowLeft: page.margins.left,
|
|
1077
|
+
contentWidth: headerContentWidth,
|
|
1078
|
+
pageWidth: page.size.w,
|
|
1079
|
+
pageHeight: page.size.h,
|
|
1080
|
+
margins: page.margins
|
|
1081
|
+
});
|
|
1082
|
+
headerContentEl.style.top = `${-headerVisualTop}px`;
|
|
1083
|
+
if (headerContentEl.querySelector("img")) shouldClipHeader = false;
|
|
1084
|
+
headerEl.append(headerContentEl);
|
|
1085
|
+
}
|
|
1086
|
+
if (shouldClipHeader) {
|
|
1087
|
+
headerEl.style.maxHeight = `${availableHeaderHeight}px`;
|
|
1088
|
+
headerEl.style.overflow = "hidden";
|
|
1089
|
+
}
|
|
1090
|
+
pageEl.append(headerEl);
|
|
1091
|
+
moveBehindHeaderFooterElementsToPage(headerEl, pageEl, headerDistance, page.margins.left);
|
|
1092
|
+
}
|
|
1093
|
+
{
|
|
1094
|
+
const footerDistance = options.footerDistance ?? page.margins.footer ?? 48;
|
|
1095
|
+
const footerContentWidth = page.size.w - page.margins.left - page.margins.right;
|
|
1096
|
+
const availableFooterHeight = Math.max(page.margins.bottom - footerDistance, 48);
|
|
1097
|
+
const footerFlowHeight = options.footerContent?.height ?? 0;
|
|
1098
|
+
const footerVisualTop = options.footerContent?.visualTop ?? 0;
|
|
1099
|
+
const footerVisualBottom = options.footerContent?.visualBottom ?? footerFlowHeight;
|
|
1100
|
+
const footerOverflows = Math.max(footerVisualBottom - footerVisualTop, 24) > availableFooterHeight;
|
|
1101
|
+
const interactiveFooterHeight = Math.max(footerFlowHeight, 24);
|
|
1102
|
+
const footerNaturalTop = page.size.h - footerDistance - footerFlowHeight;
|
|
1103
|
+
const footerEl = doc.createElement("div");
|
|
1104
|
+
footerEl.className = PAGE_CLASS_NAMES.footer;
|
|
1105
|
+
if (options.footerContent?.rId) footerEl.dataset["rid"] = options.footerContent.rId;
|
|
1106
|
+
footerEl.style.position = "absolute";
|
|
1107
|
+
footerEl.style.top = `${footerNaturalTop}px`;
|
|
1108
|
+
footerEl.style.left = `${page.margins.left}px`;
|
|
1109
|
+
footerEl.style.right = `${page.margins.right}px`;
|
|
1110
|
+
footerEl.style.width = `${footerContentWidth}px`;
|
|
1111
|
+
footerEl.style.height = `${interactiveFooterHeight}px`;
|
|
1112
|
+
footerEl.style.minHeight = `${interactiveFooterHeight}px`;
|
|
1113
|
+
footerEl.style.opacity = "0.62";
|
|
1114
|
+
const footerContentFitsBox = footerVisualBottom - footerVisualTop <= interactiveFooterHeight;
|
|
1115
|
+
let shouldClipFooter = !footerOverflows && footerContentFitsBox;
|
|
1116
|
+
if (options.footerContent && options.footerContent.blocks.length > 0) {
|
|
1117
|
+
const footerContentEl = renderHeaderFooterContent(options.footerContent, {
|
|
1118
|
+
...context,
|
|
1119
|
+
section: "footer",
|
|
1120
|
+
contentWidth: footerContentWidth
|
|
1121
|
+
}, options, {
|
|
1122
|
+
flowTop: footerNaturalTop,
|
|
1123
|
+
flowLeft: page.margins.left,
|
|
1124
|
+
contentWidth: footerContentWidth,
|
|
1125
|
+
pageWidth: page.size.w,
|
|
1126
|
+
pageHeight: page.size.h,
|
|
1127
|
+
margins: page.margins
|
|
1128
|
+
});
|
|
1129
|
+
footerContentEl.style.top = "0px";
|
|
1130
|
+
if (footerContentEl.querySelector("img")) shouldClipFooter = false;
|
|
1131
|
+
footerEl.append(footerContentEl);
|
|
1132
|
+
}
|
|
1133
|
+
if (shouldClipFooter) {
|
|
1134
|
+
footerEl.style.maxHeight = `${availableFooterHeight}px`;
|
|
1135
|
+
footerEl.style.overflow = "hidden";
|
|
1136
|
+
}
|
|
1137
|
+
pageEl.append(footerEl);
|
|
1138
|
+
moveBehindHeaderFooterElementsToPage(footerEl, pageEl, footerNaturalTop, page.margins.left);
|
|
1139
|
+
}
|
|
1140
|
+
if (pageBorderEl && options.pageBorders?.zOrder !== "back") pageEl.append(pageBorderEl);
|
|
1141
|
+
return pageEl;
|
|
1142
|
+
}
|
|
1143
|
+
function moveBehindHeaderFooterElementsToPage(slotEl, pageEl, contentTop, contentLeft) {
|
|
1144
|
+
const slotKind = slotEl.className.split(" ").includes(PAGE_CLASS_NAMES.header) ? "header" : "footer";
|
|
1145
|
+
const slotRId = slotEl.dataset["rid"];
|
|
1146
|
+
for (const element of Array.from(slotEl.querySelectorAll("img[style*=\"z-index\"], .layout-textbox[style*=\"z-index\"]"))) {
|
|
1147
|
+
if (element.style.zIndex !== "-1") continue;
|
|
1148
|
+
const currentTop = Number.parseFloat(element.style.top) || 0;
|
|
1149
|
+
const currentLeft = Number.parseFloat(element.style.left) || 0;
|
|
1150
|
+
element.style.top = `${currentTop + contentTop}px`;
|
|
1151
|
+
element.style.left = `${currentLeft + contentLeft}px`;
|
|
1152
|
+
if (slotRId) {
|
|
1153
|
+
element.dataset["hfSlotKind"] = slotKind;
|
|
1154
|
+
element.dataset["hfRid"] = slotRId;
|
|
1155
|
+
}
|
|
1156
|
+
element.style.zIndex = "0";
|
|
1157
|
+
pageEl.prepend(element);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function applySectionHeaderFooterOptions(page, pageOptions, options) {
|
|
1161
|
+
const refs = page.headerFooterRefs;
|
|
1162
|
+
if (!refs) return false;
|
|
1163
|
+
const isFirstSectionPage = page.sectionPageNumber === 1;
|
|
1164
|
+
const useFirst = refs.titlePg === true && isFirstSectionPage;
|
|
1165
|
+
const useEven = (page.sectionPageNumber ?? page.number) % 2 === 0;
|
|
1166
|
+
const headerRId = (() => {
|
|
1167
|
+
if (useFirst) return refs.headerFirst;
|
|
1168
|
+
if (useEven && refs.headerEven) return refs.headerEven;
|
|
1169
|
+
return refs.headerDefault;
|
|
1170
|
+
})();
|
|
1171
|
+
const footerRId = (() => {
|
|
1172
|
+
if (useFirst) return refs.footerFirst;
|
|
1173
|
+
if (useEven && refs.footerEven) return refs.footerEven;
|
|
1174
|
+
return refs.footerDefault;
|
|
1175
|
+
})();
|
|
1176
|
+
if (headerRId) {
|
|
1177
|
+
const content = options.headerContentByRId?.get(headerRId);
|
|
1178
|
+
if (content) pageOptions.headerContent = content;
|
|
1179
|
+
else delete pageOptions.headerContent;
|
|
1180
|
+
} else delete pageOptions.headerContent;
|
|
1181
|
+
if (footerRId) {
|
|
1182
|
+
const content = options.footerContentByRId?.get(footerRId);
|
|
1183
|
+
if (content) pageOptions.footerContent = content;
|
|
1184
|
+
else delete pageOptions.footerContent;
|
|
1185
|
+
} else delete pageOptions.footerContent;
|
|
1186
|
+
const watermarkByRId = options.watermarkByHeaderRId;
|
|
1187
|
+
if (watermarkByRId) {
|
|
1188
|
+
const pageWatermark = headerRId ? watermarkByRId.get(headerRId) : void 0;
|
|
1189
|
+
if (pageWatermark) pageOptions.watermark = pageWatermark;
|
|
1190
|
+
else delete pageOptions.watermark;
|
|
1191
|
+
}
|
|
1192
|
+
return true;
|
|
1193
|
+
}
|
|
1194
|
+
/** SECTIONPAGES context fragment for `page`: the page count of its section, or
|
|
1195
|
+
* nothing when section counts were not supplied (field then falls back). */
|
|
1196
|
+
function sectionPagesContext(page, counts) {
|
|
1197
|
+
if (!counts) return {};
|
|
1198
|
+
const value = counts.get(page.sectionIndex ?? 0);
|
|
1199
|
+
return value === void 0 ? {} : { sectionPages: value };
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Build a RenderContext and resolved page options (with footnotes) for a page.
|
|
1203
|
+
* Centralises logic shared by populatePageShell, repopulatePageContent, and the eager render path.
|
|
1204
|
+
*/
|
|
1205
|
+
function buildPageRenderArgs(page, totalPages, options) {
|
|
1206
|
+
const context = {
|
|
1207
|
+
pageNumber: page.number,
|
|
1208
|
+
totalPages,
|
|
1209
|
+
section: "body",
|
|
1210
|
+
...options.bookmarkPages ? { bookmarkPages: options.bookmarkPages } : {},
|
|
1211
|
+
...options.bookmarkText ? { bookmarkText: options.bookmarkText } : {},
|
|
1212
|
+
...options.seqValues ? { seqValues: options.seqValues } : {},
|
|
1213
|
+
...sectionPagesContext(page, options.sectionPageCounts)
|
|
1214
|
+
};
|
|
1215
|
+
const pageOptions = { ...options };
|
|
1216
|
+
if (!applySectionHeaderFooterOptions(page, pageOptions, options) && options.titlePg && page.number === 1) {
|
|
1217
|
+
if (options.firstPageHeaderContent !== void 0) pageOptions.headerContent = options.firstPageHeaderContent;
|
|
1218
|
+
else delete pageOptions.headerContent;
|
|
1219
|
+
if (options.firstPageFooterContent !== void 0) pageOptions.footerContent = options.firstPageFooterContent;
|
|
1220
|
+
else delete pageOptions.footerContent;
|
|
1221
|
+
}
|
|
1222
|
+
if (options.footnotesByPage) {
|
|
1223
|
+
const fns = options.footnotesByPage.get(page.number);
|
|
1224
|
+
if (fns && fns.length > 0) pageOptions.footnoteArea = fns;
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
context,
|
|
1228
|
+
pageOptions
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Compute a fingerprint string for a page that changes when its content changes.
|
|
1233
|
+
* Used to detect which pages need re-rendering on incremental updates.
|
|
1234
|
+
*/
|
|
1235
|
+
function computePageFingerprint(page, blockLookup) {
|
|
1236
|
+
return computePageFingerprintInternal(page, blockLookup, { includePmPositions: true });
|
|
1237
|
+
}
|
|
1238
|
+
function computePageRenderFingerprint(page, blockLookup) {
|
|
1239
|
+
return computePageFingerprintInternal(page, blockLookup, { includePmPositions: false });
|
|
1240
|
+
}
|
|
1241
|
+
function computePageFingerprintInternal(page, blockLookup, options) {
|
|
1242
|
+
const parts = [];
|
|
1243
|
+
parts.push(`s:${page.size.w},${page.size.h}`);
|
|
1244
|
+
parts.push(`m:${page.margins.top},${page.margins.right},${page.margins.bottom},${page.margins.left}`);
|
|
1245
|
+
parts.push(`n:${page.number}`);
|
|
1246
|
+
if (page.sectionIndex !== void 0) parts.push(`si:${page.sectionIndex}`);
|
|
1247
|
+
if (page.sectionPageNumber !== void 0) parts.push(`sp:${page.sectionPageNumber}`);
|
|
1248
|
+
if (page.headerFooterRefs) parts.push(`hf:${JSON.stringify(page.headerFooterRefs)}`);
|
|
1249
|
+
if (page.footnoteReservedHeight) parts.push(`fn:${page.footnoteReservedHeight}`);
|
|
1250
|
+
for (const frag of page.fragments) {
|
|
1251
|
+
let fp = `${frag.kind}:${frag.blockId},${frag.x},${frag.y},${frag.width},${frag.height}`;
|
|
1252
|
+
if (options.includePmPositions && frag.pmStart !== void 0) fp += `,ps:${frag.pmStart}`;
|
|
1253
|
+
if (options.includePmPositions && frag.pmEnd !== void 0) fp += `,pe:${frag.pmEnd}`;
|
|
1254
|
+
if (frag.kind === "paragraph") fp += `,fl:${frag.fromLine},tl:${frag.toLine}`;
|
|
1255
|
+
else if (frag.kind === "table") {
|
|
1256
|
+
fp += `,fr:${frag.fromRow},tr:${frag.toRow}`;
|
|
1257
|
+
if (frag.headerRowCount !== void 0) fp += `,hr:${frag.headerRowCount}`;
|
|
1258
|
+
if (frag.topClip !== void 0) fp += `,tc:${frag.topClip}`;
|
|
1259
|
+
if (frag.bottomClip !== void 0) fp += `,bc:${frag.bottomClip}`;
|
|
1260
|
+
if (frag.continuesFromPrev) fp += ",cfp";
|
|
1261
|
+
if (frag.continuesOnNext) fp += ",con";
|
|
1262
|
+
}
|
|
1263
|
+
if (blockLookup) {
|
|
1264
|
+
const block = blockLookup.get(String(frag.blockId))?.block;
|
|
1265
|
+
const annotationFingerprint = block ? computeAnnotationFingerprint(block, options.includePmPositions) : "";
|
|
1266
|
+
if (annotationFingerprint) fp += `,ann:${annotationFingerprint}`;
|
|
1267
|
+
const contentFingerprint = block ? computeContentFingerprint(block) : "";
|
|
1268
|
+
if (contentFingerprint) fp += `,c:${contentFingerprint}`;
|
|
1269
|
+
}
|
|
1270
|
+
parts.push(fp);
|
|
1271
|
+
}
|
|
1272
|
+
return parts.join("|");
|
|
1273
|
+
}
|
|
1274
|
+
function computeAnnotationFingerprint(block, includePmPositions) {
|
|
1275
|
+
const parts = [];
|
|
1276
|
+
collectAnnotationFingerprint(block, parts, includePmPositions);
|
|
1277
|
+
return parts.join(";");
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Fingerprint of run-level content + formatting that the painter applies via
|
|
1281
|
+
* `applyRunStyles`. Geometry alone is insufficient: a mark-only edit (toggle
|
|
1282
|
+
* bold/italic/color/etc.) can leave fragment width/height/line counts
|
|
1283
|
+
* identical, in which case the geometry fingerprint matches and the
|
|
1284
|
+
* incremental render path would skip repopulating the page, leaving the new
|
|
1285
|
+
* formatting unpainted.
|
|
1286
|
+
*/
|
|
1287
|
+
function computeContentFingerprint(block) {
|
|
1288
|
+
const parts = [];
|
|
1289
|
+
collectContentFingerprint(block, parts);
|
|
1290
|
+
return parts.join(";");
|
|
1291
|
+
}
|
|
1292
|
+
function collectAnnotationFingerprint(block, parts, includePmPositions) {
|
|
1293
|
+
if (block.kind === "paragraph") {
|
|
1294
|
+
for (let index = 0; index < block.runs.length; index++) {
|
|
1295
|
+
const run = block.runs[index];
|
|
1296
|
+
if (!run) continue;
|
|
1297
|
+
if (run.kind !== "text" && run.kind !== "tab" && run.kind !== "field") continue;
|
|
1298
|
+
const runParts = [];
|
|
1299
|
+
if (run.commentIds?.length) runParts.push(`c:${run.commentIds.join(",")}`);
|
|
1300
|
+
if (run.isInsertion) runParts.push(`ins:${run.changeRevisionId ?? ""}`);
|
|
1301
|
+
if (run.isDeletion) runParts.push(`del:${run.changeRevisionId ?? ""}`);
|
|
1302
|
+
if (runParts.length > 0) parts.push(includePmPositions ? `${index}:${run.pmStart ?? ""}-${run.pmEnd ?? ""}:${runParts.join("|")}` : `${index}:${runParts.join("|")}`);
|
|
1303
|
+
}
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
if (block.kind === "table") {
|
|
1307
|
+
for (const row of block.rows) for (const cell of row.cells) for (const child of cell.blocks) collectAnnotationFingerprint(child, parts, includePmPositions);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
if (block.kind === "textBox") for (const child of block.content) collectAnnotationFingerprint(child, parts, includePmPositions);
|
|
1311
|
+
}
|
|
1312
|
+
function collectContentFingerprint(block, parts) {
|
|
1313
|
+
if (block.kind === "paragraph") {
|
|
1314
|
+
if (block.attrs?.styleId) parts.push(`p:st:${block.attrs.styleId}`);
|
|
1315
|
+
if (block.attrs?.alignment) parts.push(`p:al:${block.attrs.alignment}`);
|
|
1316
|
+
for (let index = 0; index < block.runs.length; index++) {
|
|
1317
|
+
const run = block.runs[index];
|
|
1318
|
+
if (!run) continue;
|
|
1319
|
+
parts.push(`${index}:${runContentKey(run)}`);
|
|
1320
|
+
}
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
if (block.kind === "table") {
|
|
1324
|
+
for (const row of block.rows) for (const cell of row.cells) for (const child of cell.blocks) collectContentFingerprint(child, parts);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (block.kind === "textBox") for (const child of block.content) collectContentFingerprint(child, parts);
|
|
1328
|
+
}
|
|
1329
|
+
function runContentKey(run) {
|
|
1330
|
+
if (run.kind === "lineBreak") return "lb";
|
|
1331
|
+
if (run.kind === "image") return `img:${run.src}|${run.width}x${run.height}${run.transform ? `|tr:${run.transform}` : ""}`;
|
|
1332
|
+
const parts = [run.kind];
|
|
1333
|
+
if (run.kind === "text") {
|
|
1334
|
+
parts.push(`t:${run.text}`);
|
|
1335
|
+
if (run.templatePreview) parts.push(`tp:${run.templatePreview}`);
|
|
1336
|
+
} else if (run.kind === "tab") {
|
|
1337
|
+
if (run.width !== void 0) parts.push(`w:${run.width}`);
|
|
1338
|
+
} else if (run.kind === "math") {
|
|
1339
|
+
parts.push(`md:${run.display}`);
|
|
1340
|
+
parts.push(`mx:${run.ommlXml}`);
|
|
1341
|
+
} else {
|
|
1342
|
+
parts.push(`ft:${run.fieldType}`);
|
|
1343
|
+
if (run.instruction !== void 0) parts.push(`fi:${run.instruction}`);
|
|
1344
|
+
if (run.fallback !== void 0) parts.push(`fb:${run.fallback}`);
|
|
1345
|
+
if (run.fldLock) parts.push("fl");
|
|
1346
|
+
}
|
|
1347
|
+
if (run.bold) parts.push("b");
|
|
1348
|
+
if (run.italic) parts.push("i");
|
|
1349
|
+
if (run.underline) parts.push(typeof run.underline === "boolean" ? "u" : `u:${run.underline.style ?? ""}:${run.underline.color ?? ""}`);
|
|
1350
|
+
if (run.strike) parts.push("s");
|
|
1351
|
+
if (run.color) parts.push(`c:${run.color}`);
|
|
1352
|
+
if (run.highlight) parts.push(`hi:${run.highlight}`);
|
|
1353
|
+
if (run.fontFamily) parts.push(`ff:${run.fontFamily}`);
|
|
1354
|
+
if (run.eastAsiaFontFamily) parts.push(`ea:${run.eastAsiaFontFamily}`);
|
|
1355
|
+
if (run.fontSize !== void 0) parts.push(`fs:${run.fontSize}`);
|
|
1356
|
+
if (run.letterSpacing !== void 0) parts.push(`ls:${run.letterSpacing}`);
|
|
1357
|
+
if (run.superscript) parts.push("sup");
|
|
1358
|
+
if (run.subscript) parts.push("sub");
|
|
1359
|
+
if (run.allCaps) parts.push("ac");
|
|
1360
|
+
if (run.smallCaps) parts.push("sc");
|
|
1361
|
+
if (run.positionPx !== void 0) parts.push(`pp:${run.positionPx}`);
|
|
1362
|
+
if (run.horizontalScale !== void 0 && run.horizontalScale !== 100) parts.push(`hs:${run.horizontalScale}`);
|
|
1363
|
+
if (run.imprint) parts.push("imp");
|
|
1364
|
+
if (run.emboss) parts.push("emb");
|
|
1365
|
+
if (run.textShadow) parts.push("sh");
|
|
1366
|
+
if (run.textOutline) parts.push("ol");
|
|
1367
|
+
if (run.emphasisMark) parts.push(`emk:${run.emphasisMark}`);
|
|
1368
|
+
if (run.hyperlink) parts.push(`hl:${run.hyperlink.href}`);
|
|
1369
|
+
return parts.join("|");
|
|
1370
|
+
}
|
|
1371
|
+
function headerFooterContentFingerprint(prefix, entries) {
|
|
1372
|
+
if (!entries) return [];
|
|
1373
|
+
return Array.from(entries.entries()).toSorted(([left], [right]) => left.localeCompare(right)).map(([rId, content]) => `${prefix}:${rId},${content.blocks.length},${content.height},${content.visualTop ?? 0},${content.visualBottom ?? content.height},${content.textSig ?? ""}`);
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Compute a hash for render options that affect all pages globally.
|
|
1377
|
+
* When this changes, all pages need a full re-render.
|
|
1378
|
+
*/
|
|
1379
|
+
function computeOptionsHash(options) {
|
|
1380
|
+
const parts = [];
|
|
1381
|
+
if (options.headerContent) parts.push(`hdr:${options.headerContent.blocks.length},${options.headerContent.height},${options.headerContent.visualTop ?? 0},${options.headerContent.visualBottom ?? options.headerContent.height},${options.headerContent.rId ?? ""},${options.headerContent.textSig ?? ""}`);
|
|
1382
|
+
if (options.footerContent) parts.push(`ftr:${options.footerContent.blocks.length},${options.footerContent.height},${options.footerContent.visualTop ?? 0},${options.footerContent.visualBottom ?? options.footerContent.height},${options.footerContent.rId ?? ""},${options.footerContent.textSig ?? ""}`);
|
|
1383
|
+
parts.push(...headerFooterContentFingerprint("hdr-map", options.headerContentByRId));
|
|
1384
|
+
parts.push(...headerFooterContentFingerprint("ftr-map", options.footerContentByRId));
|
|
1385
|
+
if (options.firstPageHeaderContent) parts.push(`fp-hdr:${options.firstPageHeaderContent.blocks.length},${options.firstPageHeaderContent.height},${options.firstPageHeaderContent.rId ?? ""},${options.firstPageHeaderContent.textSig ?? ""}`);
|
|
1386
|
+
if (options.firstPageFooterContent) parts.push(`fp-ftr:${options.firstPageFooterContent.blocks.length},${options.firstPageFooterContent.height},${options.firstPageFooterContent.rId ?? ""},${options.firstPageFooterContent.textSig ?? ""}`);
|
|
1387
|
+
if (options.titlePg) parts.push("titlePg");
|
|
1388
|
+
if (options.theme) parts.push(`thm:${options.theme.name ?? "default"}`);
|
|
1389
|
+
if (options.pageBorders) parts.push(`pb:${JSON.stringify(options.pageBorders)}`);
|
|
1390
|
+
if (options.headerDistance !== void 0) parts.push(`hd:${options.headerDistance}`);
|
|
1391
|
+
if (options.footerDistance !== void 0) parts.push(`fd:${options.footerDistance}`);
|
|
1392
|
+
if (options.watermark) parts.push(`wm:${JSON.stringify(options.watermark)}`);
|
|
1393
|
+
if (options.watermarkImageSrc !== void 0) parts.push(`wmsrc:${options.watermarkImageSrc}`);
|
|
1394
|
+
if (options.watermarkByHeaderRId) {
|
|
1395
|
+
const entries = [];
|
|
1396
|
+
for (const [rId, wm] of options.watermarkByHeaderRId) entries.push([rId, wm]);
|
|
1397
|
+
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
1398
|
+
parts.push(`wm-map:${JSON.stringify(entries)}`);
|
|
1399
|
+
}
|
|
1400
|
+
return parts.join("|");
|
|
1401
|
+
}
|
|
1402
|
+
function sortedMapFingerprint(prefix, map) {
|
|
1403
|
+
if (!map || map.size === 0) return [];
|
|
1404
|
+
const entries = [];
|
|
1405
|
+
for (const [key, value] of map) entries.push(`${String(key)}=${String(value)}`);
|
|
1406
|
+
entries.sort();
|
|
1407
|
+
return [`${prefix}:${entries.join(",")}`];
|
|
1408
|
+
}
|
|
1409
|
+
function computeFieldContextHash(options) {
|
|
1410
|
+
const parts = [];
|
|
1411
|
+
parts.push(...sortedMapFingerprint("bm-page", options.bookmarkPages));
|
|
1412
|
+
parts.push(...sortedMapFingerprint("bm-text", options.bookmarkText));
|
|
1413
|
+
parts.push(...sortedMapFingerprint("seq", options.seqValues));
|
|
1414
|
+
parts.push(...sortedMapFingerprint("section-pages", options.sectionPageCounts));
|
|
1415
|
+
return parts.join("|");
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Apply standard container styles for the pages wrapper.
|
|
1419
|
+
*/
|
|
1420
|
+
function applyContainerStyles(container, pageGap) {
|
|
1421
|
+
container.style.display = "flex";
|
|
1422
|
+
container.style.flexDirection = "column";
|
|
1423
|
+
container.style.alignItems = "center";
|
|
1424
|
+
container.style.gap = `${pageGap}px`;
|
|
1425
|
+
container.style.padding = "0";
|
|
1426
|
+
container.style.backgroundColor = "transparent";
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Number of pages to render above and below the visible area.
|
|
1430
|
+
* Keeps nearby pages ready for smooth scrolling.
|
|
1431
|
+
*/
|
|
1432
|
+
const VIRTUALIZATION_BUFFER = 1;
|
|
1433
|
+
const VIRTUALIZATION_ROOT_MARGIN_PX = 1e3;
|
|
1434
|
+
const INITIAL_EAGER_RENDER_PAGES = 3;
|
|
1435
|
+
/**
|
|
1436
|
+
* Minimum page count before virtualization kicks in.
|
|
1437
|
+
* Small documents render all pages eagerly for simplicity.
|
|
1438
|
+
*/
|
|
1439
|
+
const VIRTUALIZATION_THRESHOLD = 8;
|
|
1440
|
+
/**
|
|
1441
|
+
* Render multiple pages to a container with virtualization for large documents.
|
|
1442
|
+
*
|
|
1443
|
+
* For documents with fewer than VIRTUALIZATION_THRESHOLD pages, all pages
|
|
1444
|
+
* are rendered eagerly. For larger documents, only pages near the visible
|
|
1445
|
+
* viewport are fully rendered — off-screen pages are lightweight shells
|
|
1446
|
+
* with correct dimensions to preserve scroll position.
|
|
1447
|
+
*
|
|
1448
|
+
* An IntersectionObserver watches page elements and populates/clears
|
|
1449
|
+
* content as pages scroll into and out of view.
|
|
1450
|
+
*/
|
|
1451
|
+
function renderPages(pages, container, options = {}) {
|
|
1452
|
+
const totalPages = pages.length;
|
|
1453
|
+
const pageGap = options.pageGap ?? 24;
|
|
1454
|
+
const pc = container;
|
|
1455
|
+
const prevState = pc.__pageRenderState;
|
|
1456
|
+
const currentOptionsHash = computeOptionsHash(options);
|
|
1457
|
+
const currentFieldContextHash = computeFieldContextHash(options);
|
|
1458
|
+
const useVirtualization = totalPages >= VIRTUALIZATION_THRESHOLD;
|
|
1459
|
+
if (prevState && prevState.optionsHash === currentOptionsHash && useVirtualization) {
|
|
1460
|
+
const prevShells = prevState.pageStates;
|
|
1461
|
+
const prevDataMap = prevState.pageDataMap;
|
|
1462
|
+
const observer = pc.__pageObserver;
|
|
1463
|
+
const totalPagesChanged = prevState.totalPages !== totalPages;
|
|
1464
|
+
const fieldContextChanged = prevState.fieldContextHash !== currentFieldContextHash;
|
|
1465
|
+
const commonCount = Math.min(prevShells.length, pages.length);
|
|
1466
|
+
for (let i = 0; i < commonCount; i++) {
|
|
1467
|
+
const prev = prevShells[i];
|
|
1468
|
+
const page = pages[i];
|
|
1469
|
+
const data = prevDataMap.get(prev.element);
|
|
1470
|
+
if (!data) continue;
|
|
1471
|
+
const oldPage = data.page;
|
|
1472
|
+
data.page = page;
|
|
1473
|
+
if (!data.rendered) {
|
|
1474
|
+
prev.fingerprint = null;
|
|
1475
|
+
prev.renderFingerprint = null;
|
|
1476
|
+
applyPageStyles(prev.element, page.size.w, page.size.h, options);
|
|
1477
|
+
syncPageBorderOverlay(prev.element, page, options, options.document ?? document);
|
|
1478
|
+
prev.element.dataset["pageNumber"] = String(page.number);
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
const newFp = computePageFingerprint(page, options.blockLookup);
|
|
1482
|
+
if (prev.fingerprint === newFp && !totalPagesChanged && !fieldContextChanged) continue;
|
|
1483
|
+
const shell = prev.element;
|
|
1484
|
+
if (totalPagesChanged || fieldContextChanged) {
|
|
1485
|
+
shell.innerHTML = "";
|
|
1486
|
+
data.rendered = false;
|
|
1487
|
+
populatePageShell(shell, prevDataMap, totalPages, options);
|
|
1488
|
+
applyPageStyles(shell, page.size.w, page.size.h, options);
|
|
1489
|
+
syncPageBorderOverlay(shell, page, options, options.document ?? document);
|
|
1490
|
+
shell.dataset["pageNumber"] = String(page.number);
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
const newRenderFp = computePageRenderFingerprint(page, options.blockLookup);
|
|
1494
|
+
if (!(!(prev.renderFingerprint !== newRenderFp) && syncRenderedPmPositionData(shell, oldPage, data.page))) repopulatePageContent(shell, prevDataMap, totalPages, options);
|
|
1495
|
+
prev.fingerprint = newFp;
|
|
1496
|
+
prev.renderFingerprint = newRenderFp;
|
|
1497
|
+
applyPageStyles(shell, page.size.w, page.size.h, options);
|
|
1498
|
+
syncPageBorderOverlay(shell, page, options, options.document ?? document);
|
|
1499
|
+
shell.dataset["pageNumber"] = String(page.number);
|
|
1500
|
+
}
|
|
1501
|
+
if (pages.length > prevShells.length) {
|
|
1502
|
+
const doc = options.document ?? document;
|
|
1503
|
+
for (let i = prevShells.length; i < pages.length; i++) {
|
|
1504
|
+
const page = pages[i];
|
|
1505
|
+
const pageEl = doc.createElement("div");
|
|
1506
|
+
pageEl.className = options.pageClassName ?? PAGE_CLASS_NAMES.page;
|
|
1507
|
+
pageEl.dataset["pageNumber"] = String(page.number);
|
|
1508
|
+
pageEl.dataset["pageIndex"] = String(i);
|
|
1509
|
+
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
1510
|
+
syncPageBorderOverlay(pageEl, page, options, doc);
|
|
1511
|
+
container.append(pageEl);
|
|
1512
|
+
prevShells.push({
|
|
1513
|
+
element: pageEl,
|
|
1514
|
+
fingerprint: null,
|
|
1515
|
+
renderFingerprint: null
|
|
1516
|
+
});
|
|
1517
|
+
prevDataMap.set(pageEl, {
|
|
1518
|
+
page,
|
|
1519
|
+
index: i,
|
|
1520
|
+
rendered: false
|
|
1521
|
+
});
|
|
1522
|
+
if (observer) observer.observe(pageEl);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
if (pages.length < prevShells.length) {
|
|
1526
|
+
for (let i = prevShells.length - 1; i >= pages.length; i--) {
|
|
1527
|
+
const shell = prevShells[i].element;
|
|
1528
|
+
if (observer) observer.unobserve(shell);
|
|
1529
|
+
prevDataMap.delete(shell);
|
|
1530
|
+
shell.remove();
|
|
1531
|
+
}
|
|
1532
|
+
prevShells.length = pages.length;
|
|
1533
|
+
}
|
|
1534
|
+
for (let i = 0; i < prevShells.length; i++) {
|
|
1535
|
+
const data = prevDataMap.get(prevShells[i].element);
|
|
1536
|
+
if (data) data.index = i;
|
|
1537
|
+
}
|
|
1538
|
+
prevState.totalPages = totalPages;
|
|
1539
|
+
prevState.fieldContextHash = currentFieldContextHash;
|
|
1540
|
+
prevState.currentOptions = options;
|
|
1541
|
+
emitPainterPainted(container);
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
const prevObserver = pc.__pageObserver;
|
|
1545
|
+
if (prevObserver) {
|
|
1546
|
+
prevObserver.disconnect();
|
|
1547
|
+
delete pc.__pageObserver;
|
|
1548
|
+
}
|
|
1549
|
+
const pageShellClass = options.pageClassName ?? PAGE_CLASS_NAMES.page;
|
|
1550
|
+
const isPageShell = (el) => el.className.split(/\s+/u).includes(pageShellClass);
|
|
1551
|
+
const existingChildren = Array.from(container.children);
|
|
1552
|
+
const overlayBefore = existingChildren.find((el) => !isPageShell(el)) ?? null;
|
|
1553
|
+
for (const el of existingChildren) if (isPageShell(el)) el.remove();
|
|
1554
|
+
delete pc.__pageRenderState;
|
|
1555
|
+
applyContainerStyles(container, pageGap);
|
|
1556
|
+
const pageShells = [];
|
|
1557
|
+
for (let i = 0; i < pages.length; i++) {
|
|
1558
|
+
const page = pages[i];
|
|
1559
|
+
if (!useVirtualization) {
|
|
1560
|
+
const { context, pageOptions } = buildPageRenderArgs(page, totalPages, options);
|
|
1561
|
+
const pageEl = renderPage(page, context, pageOptions);
|
|
1562
|
+
container.insertBefore(pageEl, overlayBefore);
|
|
1563
|
+
pageShells.push(pageEl);
|
|
1564
|
+
} else {
|
|
1565
|
+
const doc = options.document ?? document;
|
|
1566
|
+
const pageEl = doc.createElement("div");
|
|
1567
|
+
pageEl.className = options.pageClassName ?? PAGE_CLASS_NAMES.page;
|
|
1568
|
+
pageEl.dataset["pageNumber"] = String(page.number);
|
|
1569
|
+
pageEl.dataset["pageIndex"] = String(i);
|
|
1570
|
+
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
1571
|
+
syncPageBorderOverlay(pageEl, page, options, doc);
|
|
1572
|
+
container.insertBefore(pageEl, overlayBefore);
|
|
1573
|
+
pageShells.push(pageEl);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
if (!useVirtualization) {
|
|
1577
|
+
emitPainterPainted(container);
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
const pageDataMap = /* @__PURE__ */ new Map();
|
|
1581
|
+
for (let i = 0; i < pages.length; i++) pageDataMap.set(pageShells[i], {
|
|
1582
|
+
page: pages[i],
|
|
1583
|
+
index: i,
|
|
1584
|
+
rendered: false
|
|
1585
|
+
});
|
|
1586
|
+
const observer = new IntersectionObserver((entries) => {
|
|
1587
|
+
const renderState = pc.__pageRenderState;
|
|
1588
|
+
if (!renderState) return;
|
|
1589
|
+
const { currentOptions: liveOptions, totalPages: liveTotalPages, pageDataMap: liveDataMap } = renderState;
|
|
1590
|
+
for (const entry of entries) {
|
|
1591
|
+
if (!(entry.target instanceof HTMLElement)) continue;
|
|
1592
|
+
const shell = entry.target;
|
|
1593
|
+
const data = liveDataMap.get(shell);
|
|
1594
|
+
if (!data) continue;
|
|
1595
|
+
if (entry.isIntersecting) {
|
|
1596
|
+
populatePageShell(shell, liveDataMap, liveTotalPages, liveOptions);
|
|
1597
|
+
for (let offset = -1; offset <= VIRTUALIZATION_BUFFER; offset++) {
|
|
1598
|
+
const neighborIdx = data.index + offset;
|
|
1599
|
+
if (neighborIdx >= 0 && neighborIdx < renderState.pageStates.length && neighborIdx !== data.index) populatePageShell(renderState.pageStates[neighborIdx].element, liveDataMap, liveTotalPages, liveOptions);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
const viewportHeight = window.innerHeight;
|
|
1604
|
+
const nearThreshold = viewportHeight * 3;
|
|
1605
|
+
const nearIndices = /* @__PURE__ */ new Set();
|
|
1606
|
+
for (const [el, data] of liveDataMap) {
|
|
1607
|
+
if (!data.rendered) continue;
|
|
1608
|
+
const rect = el.getBoundingClientRect();
|
|
1609
|
+
if (rect.bottom > -nearThreshold && rect.top < viewportHeight + nearThreshold) nearIndices.add(data.index);
|
|
1610
|
+
}
|
|
1611
|
+
for (const [el, data] of liveDataMap) {
|
|
1612
|
+
if (!data.rendered) continue;
|
|
1613
|
+
let keepRendered = false;
|
|
1614
|
+
for (const nearIdx of nearIndices) if (Math.abs(data.index - nearIdx) <= 2) {
|
|
1615
|
+
keepRendered = true;
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
if (!keepRendered && nearIndices.size > 0) depopulatePageShell(el, liveDataMap);
|
|
1619
|
+
}
|
|
1620
|
+
}, {
|
|
1621
|
+
root: null,
|
|
1622
|
+
rootMargin: `${VIRTUALIZATION_ROOT_MARGIN_PX}px 0px ${VIRTUALIZATION_ROOT_MARGIN_PX}px 0px`
|
|
1623
|
+
});
|
|
1624
|
+
for (const shell of pageShells) observer.observe(shell);
|
|
1625
|
+
pc.__pageObserver = observer;
|
|
1626
|
+
pc.__pageRenderState = {
|
|
1627
|
+
pageStates: pageShells.map((el) => ({
|
|
1628
|
+
element: el,
|
|
1629
|
+
fingerprint: null,
|
|
1630
|
+
renderFingerprint: null
|
|
1631
|
+
})),
|
|
1632
|
+
totalPages,
|
|
1633
|
+
optionsHash: currentOptionsHash,
|
|
1634
|
+
fieldContextHash: currentFieldContextHash,
|
|
1635
|
+
pageDataMap,
|
|
1636
|
+
currentOptions: options
|
|
1637
|
+
};
|
|
1638
|
+
const initialRenderCount = Math.min(pages.length, INITIAL_EAGER_RENDER_PAGES);
|
|
1639
|
+
for (let i = 0; i < initialRenderCount; i++) populatePageShell(pageShells[i], pageDataMap, totalPages, options);
|
|
1640
|
+
emitPainterPainted(container);
|
|
1641
|
+
}
|
|
1642
|
+
const PAINTER_PAINTED_EVENT = "painter:painted";
|
|
1643
|
+
function emitPainterPainted(container) {
|
|
1644
|
+
const pageCount = container.querySelectorAll(`.${PAGE_CLASS_NAMES.page}`).length;
|
|
1645
|
+
const event = new CustomEvent(PAINTER_PAINTED_EVENT, {
|
|
1646
|
+
detail: {
|
|
1647
|
+
container,
|
|
1648
|
+
pageCount
|
|
1649
|
+
},
|
|
1650
|
+
bubbles: true,
|
|
1651
|
+
cancelable: false
|
|
1652
|
+
});
|
|
1653
|
+
container.dispatchEvent(event);
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Populate a page shell with full rendered content.
|
|
1657
|
+
*/
|
|
1658
|
+
function populatePageShell(shell, pageDataMap, totalPages, options) {
|
|
1659
|
+
const data = pageDataMap.get(shell);
|
|
1660
|
+
if (!data || data.rendered) return;
|
|
1661
|
+
const { context, pageOptions } = buildPageRenderArgs(data.page, totalPages, options);
|
|
1662
|
+
const fullPageEl = renderPage(data.page, context, pageOptions);
|
|
1663
|
+
for (const stale of Array.from(shell.querySelectorAll(":scope > .layout-page-border"))) stale.remove();
|
|
1664
|
+
while (fullPageEl.firstChild) shell.append(fullPageEl.firstChild);
|
|
1665
|
+
data.rendered = true;
|
|
1666
|
+
syncPageShellFingerprints(shell, data, options);
|
|
1667
|
+
const container = shell.parentElement;
|
|
1668
|
+
if (container instanceof HTMLElement) emitPainterPainted(container);
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Surgically replace only the content area of a rendered page shell.
|
|
1672
|
+
* Preserves header/footer elements to avoid blinking.
|
|
1673
|
+
*/
|
|
1674
|
+
function repopulatePageContent(shell, pageDataMap, totalPages, options) {
|
|
1675
|
+
const data = pageDataMap.get(shell);
|
|
1676
|
+
if (!data) return;
|
|
1677
|
+
const { context, pageOptions } = buildPageRenderArgs(data.page, totalPages, options);
|
|
1678
|
+
const newContentEl = renderPage(data.page, context, pageOptions).querySelector(`.${PAGE_CLASS_NAMES.content}`);
|
|
1679
|
+
const oldContentEl = shell.querySelector(`.${PAGE_CLASS_NAMES.content}`);
|
|
1680
|
+
if (newContentEl && oldContentEl) {
|
|
1681
|
+
oldContentEl.replaceWith(newContentEl);
|
|
1682
|
+
syncPageWatermarkOverlay(shell, data.page, pageOptions, options.document ?? document);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
shell.innerHTML = "";
|
|
1686
|
+
data.rendered = false;
|
|
1687
|
+
populatePageShell(shell, pageDataMap, totalPages, options);
|
|
1688
|
+
}
|
|
1689
|
+
function syncRenderedPmPositionData(shell, oldPage, newPage) {
|
|
1690
|
+
const delta = getUniformPagePositionDelta(oldPage, newPage);
|
|
1691
|
+
if (delta === null) return false;
|
|
1692
|
+
if (delta === 0) return true;
|
|
1693
|
+
for (const element of shell.querySelectorAll("[data-pm-start], [data-pm-end], [data-table-pm-start]")) {
|
|
1694
|
+
shiftNumericDatasetValue(element, "pmStart", delta);
|
|
1695
|
+
shiftNumericDatasetValue(element, "pmEnd", delta);
|
|
1696
|
+
shiftNumericDatasetValue(element, "tablePmStart", delta);
|
|
1697
|
+
}
|
|
1698
|
+
return true;
|
|
1699
|
+
}
|
|
1700
|
+
function getUniformPagePositionDelta(oldPage, newPage) {
|
|
1701
|
+
if (oldPage.fragments.length !== newPage.fragments.length) return null;
|
|
1702
|
+
let delta = null;
|
|
1703
|
+
for (let i = 0; i < oldPage.fragments.length; i++) {
|
|
1704
|
+
const oldFragment = oldPage.fragments[i];
|
|
1705
|
+
const newFragment = newPage.fragments[i];
|
|
1706
|
+
if (!oldFragment || !newFragment) return null;
|
|
1707
|
+
if (oldFragment.kind !== newFragment.kind || oldFragment.blockId !== newFragment.blockId) return null;
|
|
1708
|
+
const startDelta = readPositionDelta(oldFragment.pmStart, newFragment.pmStart);
|
|
1709
|
+
if (startDelta !== null) {
|
|
1710
|
+
if (delta !== null && delta !== startDelta) return null;
|
|
1711
|
+
delta = startDelta;
|
|
1712
|
+
}
|
|
1713
|
+
const endDelta = readPositionDelta(oldFragment.pmEnd, newFragment.pmEnd);
|
|
1714
|
+
if (endDelta !== null) {
|
|
1715
|
+
if (delta !== null && delta !== endDelta) return null;
|
|
1716
|
+
delta = endDelta;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
return delta;
|
|
1720
|
+
}
|
|
1721
|
+
function readPositionDelta(previous, next) {
|
|
1722
|
+
if (previous === void 0 && next === void 0) return null;
|
|
1723
|
+
if (previous === void 0 || next === void 0) return null;
|
|
1724
|
+
return next - previous;
|
|
1725
|
+
}
|
|
1726
|
+
function shiftNumericDatasetValue(element, key, delta) {
|
|
1727
|
+
const value = element.dataset[key];
|
|
1728
|
+
if (value === void 0) return;
|
|
1729
|
+
const numeric = Number(value);
|
|
1730
|
+
if (!Number.isFinite(numeric)) return;
|
|
1731
|
+
element.dataset[key] = String(numeric + delta);
|
|
1732
|
+
}
|
|
1733
|
+
function syncPageShellFingerprints(shell, data, options) {
|
|
1734
|
+
const pageState = shell.parentElement?.__pageRenderState?.pageStates[data.index];
|
|
1735
|
+
if (!pageState || pageState.element !== shell) return;
|
|
1736
|
+
pageState.fingerprint = computePageFingerprint(data.page, options.blockLookup);
|
|
1737
|
+
pageState.renderFingerprint = computePageRenderFingerprint(data.page, options.blockLookup);
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Find the page shell whose layout fragments cover (or are nearest
|
|
1741
|
+
* before) a given ProseMirror position. Returns null if the
|
|
1742
|
+
* container has no virtualization state — callers should fall back
|
|
1743
|
+
* to the cheaper run-level DOM query in that case.
|
|
1744
|
+
*
|
|
1745
|
+
* Uses the layout-side Page records (always present even when a
|
|
1746
|
+
* page's content hasn't rendered yet under virtualization), so the
|
|
1747
|
+
* caller can scroll to the correct page shell and let the
|
|
1748
|
+
* IntersectionObserver populate it on demand. This avoids the
|
|
1749
|
+
* "many clicks to arrive" failure mode where a per-run DOM query
|
|
1750
|
+
* only finds runs in the currently-rendered buffer and steps the
|
|
1751
|
+
* viewport one buffer at a time.
|
|
1752
|
+
*/
|
|
1753
|
+
function findPageShellForPmPos(container, pmPos) {
|
|
1754
|
+
const dataMap = container.__pageRenderState?.pageDataMap;
|
|
1755
|
+
if (!dataMap || dataMap.size === 0) return null;
|
|
1756
|
+
let bestStartShell = null;
|
|
1757
|
+
let bestStart = Number.NEGATIVE_INFINITY;
|
|
1758
|
+
for (const [shell, entry] of dataMap) {
|
|
1759
|
+
let pageStart = Number.POSITIVE_INFINITY;
|
|
1760
|
+
let pageEnd = Number.NEGATIVE_INFINITY;
|
|
1761
|
+
for (const fragment of entry.page.fragments) {
|
|
1762
|
+
if (fragment.pmStart !== void 0 && fragment.pmStart < pageStart) pageStart = fragment.pmStart;
|
|
1763
|
+
if (fragment.pmEnd !== void 0 && fragment.pmEnd > pageEnd) pageEnd = fragment.pmEnd;
|
|
1764
|
+
}
|
|
1765
|
+
if (pageStart === Number.POSITIVE_INFINITY) continue;
|
|
1766
|
+
if (pageStart <= pmPos && pmPos <= pageEnd) return {
|
|
1767
|
+
element: shell,
|
|
1768
|
+
isExact: true
|
|
1769
|
+
};
|
|
1770
|
+
if (pageStart <= pmPos && pageStart > bestStart) {
|
|
1771
|
+
bestStart = pageStart;
|
|
1772
|
+
bestStartShell = shell;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
return bestStartShell ? {
|
|
1776
|
+
element: bestStartShell,
|
|
1777
|
+
isExact: false
|
|
1778
|
+
} : null;
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Clear a page shell's content (keep shell dimensions for scroll).
|
|
1782
|
+
*/
|
|
1783
|
+
function depopulatePageShell(shell, pageDataMap) {
|
|
1784
|
+
const data = pageDataMap.get(shell);
|
|
1785
|
+
if (!data || !data.rendered) return;
|
|
1786
|
+
shell.innerHTML = "";
|
|
1787
|
+
data.rendered = false;
|
|
1788
|
+
const pageState = shell.parentElement?.__pageRenderState?.pageStates[data.index];
|
|
1789
|
+
if (pageState?.element === shell) {
|
|
1790
|
+
pageState.fingerprint = null;
|
|
1791
|
+
pageState.renderFingerprint = null;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
//#endregion
|
|
1795
|
+
export { PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, getDefaultPageFontFamily, isFloatingImageRun, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|