@stll/folio-core 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +2 -1
- package/dist/ai-edits/apply.js +693 -63
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +68 -2
- package/dist/ai-edits/clean-text.js +2 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/snapshot.js +21 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +238 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +62 -1
- package/dist/ai-edits/word-diff.js +21 -0
- package/dist/compat/eigenpal.d.ts +3 -3
- package/dist/compat/eigenpal.js +3 -3
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +2 -2
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +44 -12
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +17 -2
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +43 -11
- package/dist/document-operations.js +186 -18
- package/dist/docx/blockContentParser.js +21 -7
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/commentRangeIntegrity.d.ts +6 -0
- package/dist/docx/commentRangeIntegrity.js +186 -0
- package/dist/docx/conformance.js +62 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/encryption/agileDecryption.js +12 -2
- package/dist/docx/encryption/compoundFile.js +6 -1
- package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
- package/dist/docx/encryption/encryptionInfo.js +18 -2
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.js +8 -7
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/groupDrawingParser.js +10 -5
- package/dist/docx/hyperlinkParser.js +1 -1
- package/dist/docx/imageParser.js +10 -6
- package/dist/docx/metadataPrivacy.js +17 -3
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +70 -61
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +20 -9
- package/dist/docx/runParser.js +17 -6
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +18 -5
- package/dist/docx/selectiveSave.js +5 -1
- package/dist/docx/selectiveXmlPatch.d.ts +22 -1
- package/dist/docx/selectiveXmlPatch.js +61 -1
- package/dist/docx/serializer/blockSdtSerializer.js +3 -2
- package/dist/docx/serializer/commentSerializer.js +5 -6
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +23 -15
- package/dist/docx/serializer/runSerializer.js +25 -21
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +26 -20
- package/dist/docx/serializer/xmlUtils.d.ts +13 -1
- package/dist/docx/serializer/xmlUtils.js +27 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/extractDocxText.js +55 -8
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +26 -3
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.d.ts +8 -1
- package/dist/docx/tableParser.js +31 -10
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/unzip.js +1 -0
- package/dist/docx/vmlImageParser.js +11 -3
- package/dist/docx/xmlParser.js +11 -1
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/fonts/embeddedFonts.d.ts +33 -5
- package/dist/fonts/embeddedFonts.js +37 -8
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +58 -30
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +23 -5
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
- package/dist/layout-engine/measure/measureBlocks.js +15 -4
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
- package/dist/layout-engine/measure/measureHelpers.js +15 -4
- package/dist/layout-engine/measure/measureParagraph.js +85 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +48 -15
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderImage.d.ts +12 -1
- package/dist/layout-painter/renderImage.js +21 -4
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +59 -14
- package/dist/layout-painter/renderParagraph.js +55 -15
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +7 -3
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/layout-painter/renderWatermark.d.ts +1 -1
- package/dist/layout-painter/renderWatermark.js +2 -1
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/markdown/fromMarkdown.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +175 -18
- package/dist/prosemirror/commands/comments.d.ts +79 -4
- package/dist/prosemirror/commands/comments.js +622 -13
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +15 -1
- package/dist/prosemirror/commands/image.js +7 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/commentIdAllocator.js +13 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
- package/dist/prosemirror/conversion/index.js +1 -1
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +364 -103
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/index.d.ts +1 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
- package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
- package/dist/prosemirror/plugins/revisionIds.js +57 -0
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +5 -4
- package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
- package/dist/prosemirror/runFormattingMarkNames.js +40 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +30 -3
- package/dist/prosemirror/schema/marks.js +15 -0
- package/dist/prosemirror/schema/nodes.d.ts +111 -6
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
- package/dist/server.d.ts +7 -3
- package/dist/server.js +7 -3
- package/dist/utils/clipboard.d.ts +10 -1
- package/dist/utils/clipboard.js +33 -6
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +35 -5
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.d.ts +2 -1
- package/dist/utils/fontResolver.js +31 -7
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/hexId.d.ts +11 -1
- package/dist/utils/hexId.js +11 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/dist/utils/sanitizeImageSrc.d.ts +21 -0
- package/dist/utils/sanitizeImageSrc.js +30 -0
- package/dist/utils/stripXmlDeclarations.d.ts +11 -0
- package/dist/utils/stripXmlDeclarations.js +35 -0
- package/dist/version-comparison.d.ts +22 -2
- package/dist/version-comparison.js +41 -12
- package/dist/watermark/index.d.ts +11 -1
- package/dist/watermark/index.js +22 -1
- package/package.json +3 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toPaintedText } from "./textMeasurementPolicy.js";
|
|
1
2
|
import { panic } from "better-result";
|
|
2
3
|
//#region src/layout-engine/measure/measureProvider.ts
|
|
3
4
|
/**
|
|
@@ -28,8 +29,8 @@ const resetMeasureProvider = () => {
|
|
|
28
29
|
activeMeasureProvider = throwingMeasureProvider;
|
|
29
30
|
};
|
|
30
31
|
const getFontMetrics = (style) => activeMeasureProvider.getFontMetrics(style);
|
|
31
|
-
const measureTextWidth = (text, style) => activeMeasureProvider.measureTextWidth(text, style);
|
|
32
|
-
const measureText = (text, style) => activeMeasureProvider.measureText(text, style);
|
|
33
|
-
const measureRun = (text, style) => activeMeasureProvider.measureRun(text, style);
|
|
32
|
+
const measureTextWidth = (text, style) => activeMeasureProvider.measureTextWidth(toPaintedText(text), style);
|
|
33
|
+
const measureText = (text, style) => activeMeasureProvider.measureText(toPaintedText(text), style);
|
|
34
|
+
const measureRun = (text, style) => activeMeasureProvider.measureRun(toPaintedText(text), style);
|
|
34
35
|
//#endregion
|
|
35
36
|
export { getFontMetrics, getMeasureProvider, measureRun, measureText, measureTextWidth, resetMeasureProvider, setMeasureProvider };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { measuredLineRangeHeight } from "../lineFlow.js";
|
|
2
2
|
import { isFloatingTextBoxBlock } from "../types.js";
|
|
3
|
-
import { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph } from "../paragraphSpacing.js";
|
|
3
|
+
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph } from "../paragraphSpacing.js";
|
|
4
4
|
//#region src/layout-engine/measure/tableCellFlow.ts
|
|
5
5
|
const createTableCellFlowState = () => ({
|
|
6
6
|
height: 0,
|
|
@@ -57,7 +57,10 @@ const placeTableCellBlock = (state, block, measure) => {
|
|
|
57
57
|
}
|
|
58
58
|
const spacing = paragraphSpacing(block, measure);
|
|
59
59
|
const empty = isEmptyParagraph(block);
|
|
60
|
-
const leadingSpacing = empty && state.previousParagraphWasEmpty ? spacing.before + state.trailingSpacing :
|
|
60
|
+
const leadingSpacing = empty && state.previousParagraphWasEmpty ? spacing.before + state.trailingSpacing : collapseParagraphSpacing({
|
|
61
|
+
before: spacing.before,
|
|
62
|
+
after: state.trailingSpacing
|
|
63
|
+
});
|
|
61
64
|
const contentTop = top + leadingSpacing;
|
|
62
65
|
state.height = contentTop + contentHeight;
|
|
63
66
|
state.previousParagraphWasEmpty = empty;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { ParagraphBlock, ParagraphMeasure } from "../types.js";
|
|
1
|
+
import { ParagraphBlock, ParagraphMeasure, TableBlock, TableMeasure } from "../types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/layout-engine/measure/textBoxParagraphLayout.d.ts
|
|
4
|
-
type
|
|
4
|
+
type TextBoxContentPlacement = {
|
|
5
5
|
leadingSpacing: number;
|
|
6
6
|
contentHeight: number;
|
|
7
7
|
};
|
|
8
|
-
type
|
|
9
|
-
placements:
|
|
8
|
+
type TextBoxContentLayout = {
|
|
9
|
+
placements: TextBoxContentPlacement[];
|
|
10
10
|
totalHeight: number;
|
|
11
11
|
};
|
|
12
|
+
type TextBoxContentBlock = ParagraphBlock | TableBlock;
|
|
13
|
+
type TextBoxContentMeasure = ParagraphMeasure | TableMeasure;
|
|
12
14
|
/**
|
|
13
|
-
* Lay out a text box's
|
|
15
|
+
* Lay out a text box's block story as one unpaginated flow. Adjacent paragraph
|
|
14
16
|
* before/after spacing collapses to the larger value, matching body flow.
|
|
15
17
|
*/
|
|
16
|
-
declare function
|
|
18
|
+
declare function layoutTextBoxContent(blocks: readonly TextBoxContentBlock[], measures: readonly TextBoxContentMeasure[]): TextBoxContentLayout;
|
|
17
19
|
//#endregion
|
|
18
|
-
export {
|
|
20
|
+
export { TextBoxContentLayout, TextBoxContentPlacement, layoutTextBoxContent };
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import { measuredLineRangeHeight } from "../lineFlow.js";
|
|
2
|
-
import { getParagraphSpacingAfter, getParagraphSpacingBefore } from "../paragraphSpacing.js";
|
|
2
|
+
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore } from "../paragraphSpacing.js";
|
|
3
3
|
import { panic } from "better-result";
|
|
4
4
|
//#region src/layout-engine/measure/textBoxParagraphLayout.ts
|
|
5
5
|
/**
|
|
6
|
-
* Lay out a text box's
|
|
6
|
+
* Lay out a text box's block story as one unpaginated flow. Adjacent paragraph
|
|
7
7
|
* before/after spacing collapses to the larger value, matching body flow.
|
|
8
8
|
*/
|
|
9
|
-
function
|
|
10
|
-
if (blocks.length !== measures.length) panic("
|
|
9
|
+
function layoutTextBoxContent(blocks, measures) {
|
|
10
|
+
if (blocks.length !== measures.length) panic("layoutTextBoxContent: block and measure counts must match");
|
|
11
11
|
const placements = [];
|
|
12
12
|
let totalHeight = 0;
|
|
13
13
|
let trailingSpacing = 0;
|
|
14
14
|
for (let index = 0; index < blocks.length; index += 1) {
|
|
15
15
|
const block = blocks[index];
|
|
16
16
|
const measure = measures[index];
|
|
17
|
-
|
|
17
|
+
if (block.kind === "table") {
|
|
18
|
+
if (measure.kind !== "table") panic("layoutTextBoxContent: block and measure kinds must match");
|
|
19
|
+
const leadingSpacing = trailingSpacing;
|
|
20
|
+
const contentHeight = measure.totalHeight;
|
|
21
|
+
placements.push({
|
|
22
|
+
leadingSpacing,
|
|
23
|
+
contentHeight
|
|
24
|
+
});
|
|
25
|
+
totalHeight += leadingSpacing + contentHeight;
|
|
26
|
+
trailingSpacing = 0;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (measure.kind !== "paragraph") panic("layoutTextBoxContent: block and measure kinds must match");
|
|
30
|
+
const leadingSpacing = collapseParagraphSpacing({
|
|
31
|
+
before: getParagraphSpacingBefore(block),
|
|
32
|
+
after: trailingSpacing
|
|
33
|
+
});
|
|
18
34
|
const contentHeight = measuredLineRangeHeight(measure.lines, 0, measure.lines.length);
|
|
19
35
|
placements.push({
|
|
20
36
|
leadingSpacing,
|
|
@@ -29,4 +45,4 @@ function layoutTextBoxParagraphs(blocks, measures) {
|
|
|
29
45
|
};
|
|
30
46
|
}
|
|
31
47
|
//#endregion
|
|
32
|
-
export {
|
|
48
|
+
export { layoutTextBoxContent };
|
|
@@ -14,5 +14,7 @@ declare function getRunFontKerningMode(run: RunKerningInput, fallbackFontSize: n
|
|
|
14
14
|
declare function getFontKerningMode(style: Pick<FontStyle, "kerning">): FontKerningMode;
|
|
15
15
|
/** Count ordinary ASCII spaces that justification may compress. */
|
|
16
16
|
declare function countCompressibleSpaces(text: string): number;
|
|
17
|
+
/** Paint OOXML no-break hyphens with the ordinary hyphen glyph. */
|
|
18
|
+
declare function toPaintedText(text: string): string;
|
|
17
19
|
//#endregion
|
|
18
|
-
export { FONT_KERNING_MODE, FontKerningMode, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode };
|
|
20
|
+
export { FONT_KERNING_MODE, FontKerningMode, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText };
|
|
@@ -3,6 +3,8 @@ const FONT_KERNING_MODE = {
|
|
|
3
3
|
enabled: "normal",
|
|
4
4
|
disabled: "none"
|
|
5
5
|
};
|
|
6
|
+
const NO_BREAK_HYPHEN = "‑";
|
|
7
|
+
const PAINTED_HYPHEN = "-";
|
|
6
8
|
/** Resolve an authored kerning threshold against the effective run size. */
|
|
7
9
|
function getRunFontKerningMode(run, fallbackFontSize) {
|
|
8
10
|
const threshold = run.kerningMinPt;
|
|
@@ -20,5 +22,9 @@ function countCompressibleSpaces(text) {
|
|
|
20
22
|
for (const char of text) if (char === " ") count += 1;
|
|
21
23
|
return count;
|
|
22
24
|
}
|
|
25
|
+
/** Paint OOXML no-break hyphens with the ordinary hyphen glyph. */
|
|
26
|
+
function toPaintedText(text) {
|
|
27
|
+
return text.replaceAll(NO_BREAK_HYPHEN, PAINTED_HYPHEN);
|
|
28
|
+
}
|
|
23
29
|
//#endregion
|
|
24
|
-
export { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode };
|
|
30
|
+
export { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode, toPaintedText };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FOOTNOTE_SEPARATOR_HEIGHT } from "./types.js";
|
|
2
|
+
import { collapseParagraphSpacing } from "./paragraphSpacing.js";
|
|
2
3
|
import { panic } from "better-result";
|
|
3
4
|
//#region src/layout-engine/paginator.ts
|
|
4
5
|
/**
|
|
@@ -178,7 +179,10 @@ function createPaginator(options) {
|
|
|
178
179
|
function addFragment(fragment, height, spaceBefore = 0, spaceAfter = 0) {
|
|
179
180
|
const initialState = getCurrentState();
|
|
180
181
|
const initialColumnIndex = initialState.columnIndex;
|
|
181
|
-
const effectiveSpaceBefore =
|
|
182
|
+
const effectiveSpaceBefore = collapseParagraphSpacing({
|
|
183
|
+
before: spaceBefore,
|
|
184
|
+
after: initialState.trailingSpacing
|
|
185
|
+
});
|
|
182
186
|
const state = ensureFits(effectiveSpaceBefore + height);
|
|
183
187
|
const actualSpaceBefore = state === initialState && state.columnIndex === initialColumnIndex ? effectiveSpaceBefore : spaceBefore;
|
|
184
188
|
const x = getColumnX(state.columnIndex);
|
|
@@ -1,17 +1,41 @@
|
|
|
1
|
-
import { ParagraphBlock } from "./types.js";
|
|
1
|
+
import { FlowBlock, ParagraphBlock } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/layout-engine/paragraphSpacing.d.ts
|
|
4
|
+
type EffectiveParagraphSpacing = Readonly<{
|
|
5
|
+
before: number;
|
|
6
|
+
after: number;
|
|
7
|
+
}>;
|
|
8
|
+
type CollapseParagraphSpacingOptions = {
|
|
9
|
+
before: number;
|
|
10
|
+
after: number;
|
|
11
|
+
};
|
|
12
|
+
/** Collapse adjacent paragraph spacing to the larger authored side. */
|
|
13
|
+
declare const collapseParagraphSpacing: ({
|
|
14
|
+
before,
|
|
15
|
+
after
|
|
16
|
+
}: CollapseParagraphSpacingOptions) => number;
|
|
4
17
|
/** Whether a paragraph has no visible run content. */
|
|
5
18
|
declare function isEmptyParagraph(block: ParagraphBlock): boolean;
|
|
6
19
|
/**
|
|
7
20
|
* Resolve leading paragraph spacing after applying the empty-paragraph
|
|
8
21
|
* inherited-spacing collapse rule.
|
|
9
22
|
*/
|
|
10
|
-
declare
|
|
23
|
+
declare const getParagraphSpacingBefore: (block: ParagraphBlock) => number;
|
|
11
24
|
/**
|
|
12
25
|
* Resolve trailing paragraph spacing after applying the empty-paragraph
|
|
13
26
|
* inherited-spacing collapse rule.
|
|
14
27
|
*/
|
|
15
|
-
declare
|
|
28
|
+
declare const getParagraphSpacingAfter: (block: ParagraphBlock) => number;
|
|
29
|
+
/** Resolve empty-paragraph spacing without modifying the authored block. */
|
|
30
|
+
declare const resolveEffectiveParagraphSpacing: (block: ParagraphBlock) => EffectiveParagraphSpacing;
|
|
31
|
+
/**
|
|
32
|
+
* Derive sequence-level spacing suppression with structural sharing.
|
|
33
|
+
*
|
|
34
|
+
* Contextual and automatic list spacing affect layout values, not authored
|
|
35
|
+
* OOXML formatting. Only changed branches are cloned; the input tree remains
|
|
36
|
+
* untouched and repeated resolution is idempotent.
|
|
37
|
+
*/
|
|
38
|
+
declare const resolveEffectiveParagraphSpacingTree: (blocks: FlowBlock[]) => FlowBlock[];
|
|
39
|
+
declare const paragraphsShareStyle: (previous: ParagraphBlock, current: ParagraphBlock) => boolean;
|
|
16
40
|
//#endregion
|
|
17
|
-
export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
|
|
41
|
+
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { continuesNumberedSequence } from "./paragraphSequence.js";
|
|
1
2
|
//#region src/layout-engine/paragraphSpacing.ts
|
|
3
|
+
/** Collapse adjacent paragraph spacing to the larger authored side. */
|
|
4
|
+
const collapseParagraphSpacing = ({ before, after }) => Math.max(before, after);
|
|
2
5
|
/** Whether a paragraph has no visible run content. */
|
|
3
6
|
function isEmptyParagraph(block) {
|
|
4
7
|
if (block.runs.length === 0) return true;
|
|
@@ -10,21 +13,112 @@ function isEmptyParagraph(block) {
|
|
|
10
13
|
* Resolve leading paragraph spacing after applying the empty-paragraph
|
|
11
14
|
* inherited-spacing collapse rule.
|
|
12
15
|
*/
|
|
13
|
-
|
|
14
|
-
const value = block.attrs?.spacing?.before ?? 0;
|
|
15
|
-
if (value === 0) return 0;
|
|
16
|
-
if (isEmptyParagraph(block) && !block.attrs?.styleId && !block.attrs?.hasDirectParagraphFormatting && !block.attrs?.hasDirectParagraphMarkFormatting && !block.attrs?.spacingExplicit?.before) return 0;
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
16
|
+
const getParagraphSpacingBefore = (block) => resolveEffectiveParagraphSpacing(block).before;
|
|
19
17
|
/**
|
|
20
18
|
* Resolve trailing paragraph spacing after applying the empty-paragraph
|
|
21
19
|
* inherited-spacing collapse rule.
|
|
22
20
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
const getParagraphSpacingAfter = (block) => resolveEffectiveParagraphSpacing(block).after;
|
|
22
|
+
/** Resolve empty-paragraph spacing without modifying the authored block. */
|
|
23
|
+
const resolveEffectiveParagraphSpacing = (block) => {
|
|
24
|
+
const before = block.attrs?.spacing?.before ?? 0;
|
|
25
|
+
const after = block.attrs?.spacing?.after ?? 0;
|
|
26
|
+
if (!isEmptyParagraph(block) || preservesInheritedEmptySpacing(block)) return {
|
|
27
|
+
before,
|
|
28
|
+
after
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
before: block.attrs?.spacingExplicit?.before ? before : 0,
|
|
32
|
+
after: block.attrs?.spacingExplicit?.after ? after : 0
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Derive sequence-level spacing suppression with structural sharing.
|
|
37
|
+
*
|
|
38
|
+
* Contextual and automatic list spacing affect layout values, not authored
|
|
39
|
+
* OOXML formatting. Only changed branches are cloned; the input tree remains
|
|
40
|
+
* untouched and repeated resolution is idempotent.
|
|
41
|
+
*/
|
|
42
|
+
const resolveEffectiveParagraphSpacingTree = (blocks) => {
|
|
43
|
+
let changed = false;
|
|
44
|
+
const resolved = blocks.map((block, index) => {
|
|
45
|
+
const next = resolveBlockSpacing(blocks, index);
|
|
46
|
+
if (next !== block) changed = true;
|
|
47
|
+
return next;
|
|
48
|
+
});
|
|
49
|
+
return changed ? resolved : blocks;
|
|
50
|
+
};
|
|
51
|
+
const paragraphsShareStyle = (previous, current) => previous.attrs?.styleId === current.attrs?.styleId;
|
|
52
|
+
const preservesInheritedEmptySpacing = (block) => Boolean(block.attrs?.styleId || block.attrs?.hasDirectParagraphFormatting || block.attrs?.hasDirectParagraphMarkFormatting);
|
|
53
|
+
const resolveBlockSpacing = (blocks, index) => {
|
|
54
|
+
const block = blocks[index];
|
|
55
|
+
if (block.kind === "paragraph") return resolveParagraphSequenceSpacing(blocks, index, block);
|
|
56
|
+
if (block.kind === "table") return resolveTableSpacing(block);
|
|
57
|
+
if (block.kind === "textBox") return resolveTextBoxSpacing(block);
|
|
58
|
+
return block;
|
|
59
|
+
};
|
|
60
|
+
const resolveParagraphSequenceSpacing = (blocks, index, block) => {
|
|
61
|
+
const spacing = block.attrs?.spacing;
|
|
62
|
+
if (!spacing) return block;
|
|
63
|
+
const previous = blocks[index - 1];
|
|
64
|
+
const next = blocks[index + 1];
|
|
65
|
+
const suppressBefore = previous?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(previous, block) || block.attrs?.automaticSpacing?.before === true && continuesNumberedSequence(previous, block));
|
|
66
|
+
const suppressAfter = next?.kind === "paragraph" && (block.attrs?.contextualSpacing === true && paragraphsShareStyle(block, next) || block.attrs?.automaticSpacing?.after === true && continuesNumberedSequence(block, next));
|
|
67
|
+
const beforeChanged = suppressBefore && spacing.before !== 0;
|
|
68
|
+
const afterChanged = suppressAfter && spacing.after !== 0;
|
|
69
|
+
if (!beforeChanged && !afterChanged) return block;
|
|
70
|
+
const effectiveSpacing = { ...spacing };
|
|
71
|
+
if (beforeChanged) effectiveSpacing.before = 0;
|
|
72
|
+
if (afterChanged) effectiveSpacing.after = 0;
|
|
73
|
+
return {
|
|
74
|
+
...block,
|
|
75
|
+
attrs: {
|
|
76
|
+
...block.attrs,
|
|
77
|
+
spacing: effectiveSpacing
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const resolveTextBoxContentSpacing = (blocks) => {
|
|
82
|
+
let changed = false;
|
|
83
|
+
const resolved = blocks.map((block, index) => {
|
|
84
|
+
const next = block.kind === "table" ? resolveTableSpacing(block) : resolveParagraphSequenceSpacing(blocks, index, block);
|
|
85
|
+
if (next !== block) changed = true;
|
|
86
|
+
return next;
|
|
87
|
+
});
|
|
88
|
+
return changed ? resolved : blocks;
|
|
89
|
+
};
|
|
90
|
+
const resolveTableSpacing = (block) => {
|
|
91
|
+
let changed = false;
|
|
92
|
+
const rows = block.rows.map((row) => {
|
|
93
|
+
let rowChanged = false;
|
|
94
|
+
const cells = row.cells.map((cell) => {
|
|
95
|
+
const blocks = resolveEffectiveParagraphSpacingTree(cell.blocks);
|
|
96
|
+
if (blocks === cell.blocks) return cell;
|
|
97
|
+
rowChanged = true;
|
|
98
|
+
return {
|
|
99
|
+
...cell,
|
|
100
|
+
blocks
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
if (!rowChanged) return row;
|
|
104
|
+
changed = true;
|
|
105
|
+
return {
|
|
106
|
+
...row,
|
|
107
|
+
cells
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return changed ? {
|
|
111
|
+
...block,
|
|
112
|
+
rows
|
|
113
|
+
} : block;
|
|
114
|
+
};
|
|
115
|
+
const resolveTextBoxSpacing = (block) => {
|
|
116
|
+
const content = resolveTextBoxContentSpacing(block.content);
|
|
117
|
+
if (content === block.content) return block;
|
|
118
|
+
return {
|
|
119
|
+
...block,
|
|
120
|
+
content
|
|
121
|
+
};
|
|
122
|
+
};
|
|
29
123
|
//#endregion
|
|
30
|
-
export { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph };
|
|
124
|
+
export { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacing, resolveEffectiveParagraphSpacingTree };
|
|
@@ -21,7 +21,7 @@ const reconcileBreakBeforeBlock = ({ state, block, previousBlock, page, blocksBy
|
|
|
21
21
|
state
|
|
22
22
|
};
|
|
23
23
|
const markerAlreadySatisfied = pageStartsWithPreviousParagraphContinuation(page, previousBlock) || state.type === "pageAdvance" && state.reason === "authoredBoundary" || state.type === "pageAdvance" && state.reason === "reflowBoundary" || state.type === "pageAdvance" && isPaginationEmptyParagraph(block) || state.type === "pageAdvance" && (continuesNumberedSequence(previousBlock, block) || continuesTabbedParagraphSequence(previousBlock, block));
|
|
24
|
-
const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true) && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
|
|
24
|
+
const forcePageBreak = (renderedBreakNeedsSnap || block.attrs?.keepNext === true || (block.attrs?.spacing?.before ?? 0) > 0 && previousBlock?.kind !== "sectionBreak" || previousBlock?.kind === "table") && !markerAlreadySatisfied && pageHasVisibleBodyContent(page, blocksById);
|
|
25
25
|
const followsAuthoredPageBreak = previousBlock?.kind === "pageBreak";
|
|
26
26
|
const followsSectionBreak = previousBlock?.kind === "sectionBreak";
|
|
27
27
|
const followsPageBreakingSection = followsSectionBreak && previousBlock.type !== "continuous";
|
|
@@ -3,7 +3,7 @@ import { ImagePosition, ImageWrap, SdtProperties, SdtType, ShapeTextBody, TableC
|
|
|
3
3
|
|
|
4
4
|
//#region src/layout-engine/types.d.ts
|
|
5
5
|
declare namespace types_d_exports {
|
|
6
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
6
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Unique identifier for a block in the document.
|
|
@@ -87,6 +87,14 @@ type RunFormatting = {
|
|
|
87
87
|
changeAuthor?: string; /** Date of the tracked change */
|
|
88
88
|
changeDate?: string; /** Revision ID of the tracked change (for sidebar matching) */
|
|
89
89
|
changeRevisionId?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Whether this tracked insertion/deletion is an AI-proposed *suggestion*
|
|
92
|
+
* rather than a user change. Suggestions render with the same grammar but a
|
|
93
|
+
* dotted stroke and a dedicated hue; they are stripped from serialized DOCX
|
|
94
|
+
* until accepted.
|
|
95
|
+
*/
|
|
96
|
+
isSuggestion?: boolean; /** Groups a suggestion's runs for accept/reject and scroll-to. */
|
|
97
|
+
suggestionId?: string;
|
|
90
98
|
};
|
|
91
99
|
/**
|
|
92
100
|
* Hyperlink information for a run.
|
|
@@ -179,7 +187,10 @@ type ImageRun = {
|
|
|
179
187
|
cropTop?: number;
|
|
180
188
|
cropRight?: number;
|
|
181
189
|
cropBottom?: number;
|
|
182
|
-
cropLeft?: number; /**
|
|
190
|
+
cropLeft?: number; /** CSS border width in pixels. eigenpal #1096. */
|
|
191
|
+
borderWidth?: number; /** CSS border color. eigenpal #1096. */
|
|
192
|
+
borderColor?: string; /** CSS border style. eigenpal #1096. */
|
|
193
|
+
borderStyle?: string; /** Whether this picture is itself a tracked insertion (`<w:ins>`). eigenpal #641. */
|
|
183
194
|
isInsertion?: boolean; /** Whether this picture is itself a tracked deletion (`<w:del>`). eigenpal #641. */
|
|
184
195
|
isDeletion?: boolean; /** Author of the tracked change wrapping the picture. eigenpal #641. */
|
|
185
196
|
changeAuthor?: string; /** Date of the tracked change wrapping the picture. eigenpal #641. */
|
|
@@ -196,6 +207,12 @@ type LineBreakRun = {
|
|
|
196
207
|
pmStart?: number;
|
|
197
208
|
pmEnd?: number;
|
|
198
209
|
};
|
|
210
|
+
/** Zero-width cached page boundary inside a paragraph. */
|
|
211
|
+
type RenderedPageBreakRun = RunFormatting & {
|
|
212
|
+
kind: "renderedPageBreak";
|
|
213
|
+
pmStart?: number;
|
|
214
|
+
pmEnd?: number;
|
|
215
|
+
};
|
|
199
216
|
/**
|
|
200
217
|
* A field run (PAGE, NUMPAGES, etc.) that gets substituted at render time.
|
|
201
218
|
*/
|
|
@@ -230,7 +247,7 @@ type MathRun = RunFormatting & {
|
|
|
230
247
|
/**
|
|
231
248
|
* Union of all run types.
|
|
232
249
|
*/
|
|
233
|
-
type Run = TextRun | TabRun | ImageRun | LineBreakRun | FieldRun | MathRun;
|
|
250
|
+
type Run = TextRun | TabRun | ImageRun | LineBreakRun | RenderedPageBreakRun | FieldRun | MathRun;
|
|
234
251
|
/**
|
|
235
252
|
* Paragraph spacing configuration.
|
|
236
253
|
*/
|
|
@@ -293,7 +310,10 @@ type ListNumPr = {
|
|
|
293
310
|
* Paragraph block attributes.
|
|
294
311
|
*/
|
|
295
312
|
type ParagraphAttrs = {
|
|
296
|
-
alignment?: "left" | "center" | "right" | "justify"; /**
|
|
313
|
+
alignment?: "left" | "center" | "right" | "justify"; /** Document-generation policy for justified line fitting. */
|
|
314
|
+
justificationCompatibility?: {
|
|
315
|
+
type: "legacy";
|
|
316
|
+
}; /** East Asian first/last-character restrictions (`w:kinsoku`). */
|
|
297
317
|
kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`); defaults to enabled when omitted. */
|
|
298
318
|
overflowPunctuation?: boolean; /** Exempt this paragraph from document automatic hyphenation. */
|
|
299
319
|
suppressAutoHyphens?: boolean; /** Document-wide automatic hyphenation controls retained for line layout. */
|
|
@@ -315,7 +335,9 @@ type ParagraphAttrs = {
|
|
|
315
335
|
useLegacyEthiopicAmharicRules?: boolean;
|
|
316
336
|
}; /** OOXML outline level (`w:outlineLvl`), where zero is the top level. */
|
|
317
337
|
outlineLevel?: number;
|
|
318
|
-
spacing?: ParagraphSpacing; /**
|
|
338
|
+
spacing?: ParagraphSpacing; /** Whether this paragraph participates in the active section line grid. */
|
|
339
|
+
snapToGrid?: boolean; /** Active section line pitch in pixels; present only on top-level body paragraphs. */
|
|
340
|
+
documentGridLinePitch?: number; /** Spacing sides resolved from OOXML automatic paragraph spacing. */
|
|
319
341
|
automaticSpacing?: {
|
|
320
342
|
before?: boolean;
|
|
321
343
|
after?: boolean;
|
|
@@ -343,7 +365,7 @@ type ParagraphAttrs = {
|
|
|
343
365
|
keepNext?: boolean;
|
|
344
366
|
keepLines?: boolean;
|
|
345
367
|
widowControl?: boolean;
|
|
346
|
-
pageBreakBefore?: boolean; /**
|
|
368
|
+
pageBreakBefore?: boolean; /** Cached OOXML pagination hint (`w:lastRenderedPageBreak`). */
|
|
347
369
|
renderedPageBreakBefore?: boolean;
|
|
348
370
|
styleId?: string;
|
|
349
371
|
contextualSpacing?: boolean;
|
|
@@ -366,6 +388,12 @@ type ParagraphAttrs = {
|
|
|
366
388
|
listMarkerHidden?: boolean;
|
|
367
389
|
listMarkerFontFamily?: string;
|
|
368
390
|
listMarkerFontSize?: number;
|
|
391
|
+
/**
|
|
392
|
+
* Effective paragraph-mark size used only to measure a visible list
|
|
393
|
+
* paragraph's final line. Word lets the inherited complex-script size raise
|
|
394
|
+
* that line box without enlarging the marker or Western text glyphs.
|
|
395
|
+
*/
|
|
396
|
+
listParagraphMarkFontSize?: number;
|
|
369
397
|
listMarkerBold?: boolean; /** Horizontal alignment of the marker around the paragraph's list anchor. */
|
|
370
398
|
listMarkerAlignment?: "left" | "center" | "right";
|
|
371
399
|
/**
|
|
@@ -555,7 +583,10 @@ type ImageBlock = {
|
|
|
555
583
|
cropTop?: number;
|
|
556
584
|
cropRight?: number;
|
|
557
585
|
cropBottom?: number;
|
|
558
|
-
cropLeft?: number;
|
|
586
|
+
cropLeft?: number; /** CSS border width in pixels. eigenpal #1096. */
|
|
587
|
+
borderWidth?: number; /** CSS border color. eigenpal #1096. */
|
|
588
|
+
borderColor?: string; /** CSS border style. eigenpal #1096. */
|
|
589
|
+
borderStyle?: string;
|
|
559
590
|
pmStart?: number;
|
|
560
591
|
pmEnd?: number;
|
|
561
592
|
};
|
|
@@ -572,7 +603,8 @@ type SectionBreakBlock = {
|
|
|
572
603
|
};
|
|
573
604
|
orientation?: "portrait" | "landscape";
|
|
574
605
|
margins?: PageMargins;
|
|
575
|
-
columns?: ColumnLayout;
|
|
606
|
+
columns?: ColumnLayout; /** Line pitch authored by the section properties that end at this boundary, in twips. */
|
|
607
|
+
documentGridLinePitchTwips?: number;
|
|
576
608
|
};
|
|
577
609
|
type PageHeaderFooterRefs = {
|
|
578
610
|
titlePg?: boolean;
|
|
@@ -612,7 +644,7 @@ declare const DEFAULT_TEXTBOX_MARGINS: {
|
|
|
612
644
|
/** Default text box width in pixels when no width is specified */
|
|
613
645
|
declare const DEFAULT_TEXTBOX_WIDTH = 200;
|
|
614
646
|
/**
|
|
615
|
-
* Text box block — positioned container with
|
|
647
|
+
* Text box block — positioned container with block content.
|
|
616
648
|
*/
|
|
617
649
|
type TextBoxBlock = {
|
|
618
650
|
kind: "textBox";
|
|
@@ -629,8 +661,8 @@ type TextBoxBlock = {
|
|
|
629
661
|
bottom: number;
|
|
630
662
|
left: number;
|
|
631
663
|
right: number;
|
|
632
|
-
}; /**
|
|
633
|
-
content: ParagraphBlock[]; /** Display mode copied from the ProseMirror text box node. */
|
|
664
|
+
}; /** Flow blocks inside the text box */
|
|
665
|
+
content: (ParagraphBlock | TableBlock)[]; /** Display mode copied from the ProseMirror text box node. */
|
|
634
666
|
displayMode?: "inline" | "float" | "block"; /** CSS float direction copied from the ProseMirror text box node. */
|
|
635
667
|
cssFloat?: "left" | "right" | "none"; /** OOXML wrap type for anchored text boxes. */
|
|
636
668
|
wrapType?: ImageWrap["type"]; /** OOXML wrapText direction for anchored text boxes. */
|
|
@@ -704,7 +736,8 @@ type MeasuredLine = {
|
|
|
704
736
|
floatSkipBefore?: number; /** Decorative hyphen inserted at a dictionary break without changing source text. */
|
|
705
737
|
discretionaryHyphen?: {
|
|
706
738
|
runIndex: number;
|
|
707
|
-
};
|
|
739
|
+
}; /** A cached pagination boundary occurs immediately before this line. */
|
|
740
|
+
renderedPageBreakBefore?: boolean;
|
|
708
741
|
};
|
|
709
742
|
/**
|
|
710
743
|
* Measurement result for a paragraph block.
|
|
@@ -782,8 +815,8 @@ type ColumnBreakMeasure = {
|
|
|
782
815
|
type TextBoxMeasure = {
|
|
783
816
|
kind: "textBox";
|
|
784
817
|
width: number;
|
|
785
|
-
height: number; /** Pre-measured inner
|
|
786
|
-
innerMeasures: ParagraphMeasure[];
|
|
818
|
+
height: number; /** Pre-measured inner content (avoids re-measuring during render) */
|
|
819
|
+
innerMeasures: (ParagraphMeasure | TableMeasure)[];
|
|
787
820
|
};
|
|
788
821
|
/**
|
|
789
822
|
* Union of all measurement types.
|
|
@@ -1112,4 +1145,4 @@ declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
|
1112
1145
|
/** Return the leading visual offset for a row with omitted grid columns. */
|
|
1113
1146
|
declare const getTableRowLeadingWidth: (row: TableRow, columnWidths: readonly number[]) => number;
|
|
1114
1147
|
//#endregion
|
|
1115
|
-
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
1148
|
+
export { BlockId, BorderStyle, CellBorderSpec, CellBorders, ColumnBreakBlock, ColumnBreakMeasure, ColumnLayout, DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, DocumentPosition, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FieldRun, FloatingTablePosition, FlowBlock, FootnoteContent, Fragment, FragmentBase, HeaderFooterContent, HeaderFooterContentHeights, HeaderFooterLayout, HitTestResult, HyperlinkInfo, ImageBlock, ImageFragment, ImageMeasure, ImageRun, ImageRunPosition, Layout, LayoutOptions, LineBreakRun, ListNumPr, MathRun, Measure, MeasuredLine, Page, PageBreakBlock, PageBreakMeasure, PageHeaderFooterRefs, PageMargins, ParagraphAttrs, ParagraphBlock, ParagraphBorders, ParagraphFragment, ParagraphIndent, ParagraphMeasure, ParagraphSpacing, RenderedPageBreakRun, Run, RunFormatting, SdtGroup, SectionBreakBlock, SectionBreakMeasure, TabAlignment, TabRun, TabStop, TableBlock, TableCell, TableCellMeasure, TableFragment, TableMeasure, TableRow, TableRowMeasure, TextBoxBlock, TextBoxFlowAttrs, TextBoxFragment, TextBoxMeasure, TextRun, floatingTextBoxReservesBand, floatingTextBoxWrapsText, getTableRowLeadingWidth, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { renderTextBoxFragment } from "../../renderTextBox.js";
|
|
2
|
+
import { renderNestedTable } from "../../renderTable.js";
|
|
2
3
|
//#region src/layout-painter/registry/modules/textBox.ts
|
|
3
4
|
/**
|
|
4
5
|
* Text-box feature module — renders `TextBoxFragment`.
|
|
@@ -6,7 +7,10 @@ import { renderTextBoxFragment } from "../../renderTextBox.js";
|
|
|
6
7
|
const textBoxModule = {
|
|
7
8
|
kind: "textBox",
|
|
8
9
|
render({ fragment, block, measure, context, doc }) {
|
|
9
|
-
return renderTextBoxFragment(fragment, block, measure, context, {
|
|
10
|
+
return renderTextBoxFragment(fragment, block, measure, context, {
|
|
11
|
+
document: doc,
|
|
12
|
+
renderTable: renderNestedTable
|
|
13
|
+
});
|
|
10
14
|
}
|
|
11
15
|
};
|
|
12
16
|
//#endregion
|
|
@@ -21,6 +21,17 @@ type ImageVisualAttrs = {
|
|
|
21
21
|
cropBottom?: number;
|
|
22
22
|
cropLeft?: number;
|
|
23
23
|
};
|
|
24
|
+
type ImageBorderAttrs = {
|
|
25
|
+
borderWidth?: number;
|
|
26
|
+
borderColor?: string;
|
|
27
|
+
borderStyle?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Paint image borders carried through the layout model. Folio's PM schema
|
|
31
|
+
* uses `borderStyle` (not upstream's `borderKind`); the painter mirrors the
|
|
32
|
+
* editor DOM serialization while keeping the authored image box size stable.
|
|
33
|
+
*/
|
|
34
|
+
declare function applyImageBorder(element: HTMLElement, border: ImageBorderAttrs): void;
|
|
24
35
|
/**
|
|
25
36
|
* True when any visual attribute is set. Cheap call-site guard so the no-op
|
|
26
37
|
* common case skips the helper call.
|
|
@@ -86,4 +97,4 @@ type RenderImageFragmentOptions = {
|
|
|
86
97
|
*/
|
|
87
98
|
declare function renderImageFragment(fragment: ImageFragment, block: ImageBlock, _measure: ImageMeasure, _context: RenderContext, options?: RenderImageFragmentOptions): HTMLElement;
|
|
88
99
|
//#endregion
|
|
89
|
-
export { IMAGE_CLASS_NAMES, ImageVisualAttrs, RenderImageFragmentOptions, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, renderImageFragment, wrapImageWithCrop };
|
|
100
|
+
export { IMAGE_CLASS_NAMES, ImageBorderAttrs, ImageVisualAttrs, RenderImageFragmentOptions, applyImageBorder, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, renderImageFragment, wrapImageWithCrop };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
|
|
2
|
+
import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
|
|
1
3
|
//#region src/layout-painter/renderImage.ts
|
|
2
4
|
/**
|
|
3
5
|
* CSS class names for image elements
|
|
@@ -7,6 +9,18 @@ const IMAGE_CLASS_NAMES = {
|
|
|
7
9
|
imageAnchored: "layout-image-anchored"
|
|
8
10
|
};
|
|
9
11
|
/**
|
|
12
|
+
* Paint image borders carried through the layout model. Folio's PM schema
|
|
13
|
+
* uses `borderStyle` (not upstream's `borderKind`); the painter mirrors the
|
|
14
|
+
* editor DOM serialization while keeping the authored image box size stable.
|
|
15
|
+
*/
|
|
16
|
+
function applyImageBorder(element, border) {
|
|
17
|
+
if (border.borderWidth == null || border.borderWidth <= 0) return;
|
|
18
|
+
const borderStyle = border.borderStyle || "solid";
|
|
19
|
+
const borderColor = border.borderColor || "#000000";
|
|
20
|
+
element.style.border = `${border.borderWidth}px ${borderStyle} ${borderColor}`;
|
|
21
|
+
element.style.boxSizing = "border-box";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
10
24
|
* True when any visual attribute is set. Cheap call-site guard so the no-op
|
|
11
25
|
* common case skips the helper call.
|
|
12
26
|
*
|
|
@@ -118,7 +132,7 @@ function renderImageFragment(fragment, block, _measure, _context, options = {})
|
|
|
118
132
|
if (fragment.pmStart !== void 0) containerEl.dataset["pmStart"] = String(fragment.pmStart);
|
|
119
133
|
if (fragment.pmEnd !== void 0) containerEl.dataset["pmEnd"] = String(fragment.pmEnd);
|
|
120
134
|
const imgEl = doc.createElement("img");
|
|
121
|
-
imgEl
|
|
135
|
+
applySanitizedImageSrc(imgEl, block.src);
|
|
122
136
|
imgEl.alt = block.alt ?? "";
|
|
123
137
|
imgEl.style.width = "100%";
|
|
124
138
|
imgEl.style.height = "100%";
|
|
@@ -127,9 +141,10 @@ function renderImageFragment(fragment, block, _measure, _context, options = {})
|
|
|
127
141
|
if (block.transform) imgEl.style.transform = block.transform;
|
|
128
142
|
if (hasImageVisualAttrs(block)) applyImageVisualAttrs(imgEl, block);
|
|
129
143
|
imgEl.draggable = false;
|
|
130
|
-
|
|
144
|
+
const hlinkHref = sanitizeExternalUrl(block.hlinkHref);
|
|
145
|
+
if (hlinkHref) {
|
|
131
146
|
const linkEl = doc.createElement("a");
|
|
132
|
-
linkEl.href =
|
|
147
|
+
linkEl.href = hlinkHref;
|
|
133
148
|
linkEl.target = "_blank";
|
|
134
149
|
linkEl.rel = "noopener noreferrer";
|
|
135
150
|
linkEl.style.display = "block";
|
|
@@ -138,7 +153,9 @@ function renderImageFragment(fragment, block, _measure, _context, options = {})
|
|
|
138
153
|
linkEl.append(imgEl);
|
|
139
154
|
containerEl.append(linkEl);
|
|
140
155
|
} else containerEl.append(imgEl);
|
|
156
|
+
if (hasImageCrop(block)) applyImageBorder(containerEl, block);
|
|
157
|
+
else applyImageBorder(imgEl, block);
|
|
141
158
|
return containerEl;
|
|
142
159
|
}
|
|
143
160
|
//#endregion
|
|
144
|
-
export { IMAGE_CLASS_NAMES, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, renderImageFragment, wrapImageWithCrop };
|
|
161
|
+
export { IMAGE_CLASS_NAMES, applyImageBorder, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, renderImageFragment, wrapImageWithCrop };
|