@stll/folio-core 0.4.0 → 0.6.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 +3 -9
- package/dist/ai-edits/apply.js +94 -14
- package/dist/ai-edits/blockRange.d.ts +33 -0
- package/dist/ai-edits/blockRange.js +58 -0
- package/dist/ai-edits/headless.d.ts +37 -4
- package/dist/ai-edits/headless.js +134 -16
- package/dist/ai-edits/index.d.ts +5 -3
- package/dist/ai-edits/index.js +3 -2
- package/dist/ai-edits/snapshot.d.ts +14 -2
- package/dist/ai-edits/snapshot.js +12 -1
- package/dist/ai-edits/types.d.ts +42 -3
- package/dist/compat/eigenpal.d.ts +3 -2
- package/dist/compat/eigenpal.js +6 -5
- package/dist/controller/layoutPipeline.d.ts +1 -0
- package/dist/controller/layoutPipeline.js +35 -137
- package/dist/controller/layoutScheduler.d.ts +2 -1
- package/dist/controller/layoutScheduler.js +6 -0
- package/dist/document-operations.d.ts +139 -0
- package/dist/document-operations.js +578 -0
- package/dist/docx/blockContentParser.js +17 -7
- package/dist/docx/encryption/agileDecryption.js +2 -1
- package/dist/docx/encryption/compoundFile.js +4 -2
- package/dist/docx/footnoteParser.d.ts +1 -17
- package/dist/docx/footnoteParser.js +1 -27
- package/dist/docx/groupDrawingParser.d.ts +8 -0
- package/dist/docx/groupDrawingParser.js +131 -0
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +8 -2
- package/dist/docx/paragraphParser.js +9 -3
- package/dist/docx/runParser.js +8 -1
- package/dist/docx/selectiveXmlPatch.js +4 -10
- package/dist/docx/serializer/numberingSerializer.js +3 -1
- package/dist/docx/settingsParser.d.ts +6 -3
- package/dist/docx/settingsParser.js +2 -0
- package/dist/docx/xmlParser.js +11 -4
- package/dist/i18n/messages/catalogs.gen.d.ts +1054 -34
- package/dist/i18n/messages/catalogs.gen.js +1122 -68
- package/dist/i18n/messages/messages.gen.d.ts +62 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -5
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +37 -1
- package/dist/layout-bridge/convert/footnoteLayout.js +138 -14
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -7
- package/dist/layout-bridge/convert/toFlowBlocks.js +56 -19
- package/dist/layout-engine/index.d.ts +6 -5
- package/dist/layout-engine/index.js +143 -30
- package/dist/layout-engine/keep-together.d.ts +13 -8
- package/dist/layout-engine/keep-together.js +40 -25
- package/dist/layout-engine/layoutInstrumentation.d.ts +11 -1
- package/dist/layout-engine/layoutInstrumentation.js +10 -1
- package/dist/layout-engine/measure/measureBlocks.js +50 -18
- package/dist/layout-engine/measure/measureParagraph.js +61 -15
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +9 -3
- package/dist/layout-engine/textBoxGroup.d.ts +7 -0
- package/dist/layout-engine/textBoxGroup.js +8 -0
- package/dist/layout-engine/types.d.ts +30 -9
- package/dist/layout-engine/types.js +20 -2
- package/dist/layout-painter/renderPage.d.ts +15 -1
- package/dist/layout-painter/renderPage.js +27 -11
- package/dist/layout-painter/renderParagraph.js +18 -8
- package/dist/layout-painter/renderTable.js +10 -7
- package/dist/managers/DocumentLoaderManager.js +12 -5
- package/dist/model.js +1 -1
- package/dist/paged-layout/pageText.d.ts +14 -0
- package/dist/paged-layout/pageText.js +21 -0
- package/dist/prosemirror/attrs/index.js +2 -0
- package/dist/prosemirror/commands/comments.js +75 -5
- package/dist/prosemirror/commands/hyperlink.js +4 -2
- package/dist/prosemirror/commands/image.js +2 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +68 -0
- package/dist/prosemirror/commands/propertyChangeScope.js +242 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +14 -3
- package/dist/prosemirror/conversion/toProseDoc.js +29 -8
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +10 -11
- package/dist/prosemirror/findReplaceSelection.js +2 -1
- package/dist/prosemirror/schema/nodes.d.ts +10 -5
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/utils/tabCalculator.js +4 -2
- package/dist/redline.d.ts +20 -0
- package/dist/redline.js +127 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +6 -2
- package/dist/symbols.d.ts +35 -0
- package/dist/symbols.js +512 -0
- package/dist/utils/clipboard.js +3 -3
- package/dist/utils/fontLoader.js +3 -0
- package/dist/utils/fontResolver.js +48 -1
- package/dist/utils/zoom.d.ts +21 -0
- package/dist/utils/zoom.js +21 -0
- package/dist/version-comparison.d.ts +92 -0
- package/dist/version-comparison.js +436 -0
- package/package.json +3 -3
- package/dist/paged-layout/headerFooterMargins.d.ts +0 -75
- package/dist/paged-layout/headerFooterMargins.js +0 -122
|
@@ -3,7 +3,7 @@ import { ImagePosition, ImageWrap, SdtProperties, SdtType, TableWidthType } from
|
|
|
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, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun };
|
|
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, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned };
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Unique identifier for a block in the document.
|
|
@@ -304,7 +304,8 @@ type ParagraphAttrs = {
|
|
|
304
304
|
keepNext?: boolean;
|
|
305
305
|
keepLines?: boolean;
|
|
306
306
|
widowControl?: boolean;
|
|
307
|
-
pageBreakBefore?: boolean;
|
|
307
|
+
pageBreakBefore?: boolean; /** Word's cached pagination hint (`w:lastRenderedPageBreak`). */
|
|
308
|
+
renderedPageBreakBefore?: boolean;
|
|
308
309
|
styleId?: string;
|
|
309
310
|
contextualSpacing?: boolean;
|
|
310
311
|
/**
|
|
@@ -459,7 +460,14 @@ type TableBlock = {
|
|
|
459
460
|
rows: TableRow[];
|
|
460
461
|
columnWidths?: number[]; /** Table width value (twips for dxa, 50ths of percent for pct). */
|
|
461
462
|
width?: number; /** Table width type ('auto', 'pct', 'dxa', 'nil'). */
|
|
462
|
-
widthType?: TableWidthType;
|
|
463
|
+
widthType?: TableWidthType;
|
|
464
|
+
/**
|
|
465
|
+
* Table layout algorithm (`w:tblLayout`). "fixed" pins columns to their grid
|
|
466
|
+
* widths; "autofit" (the default when absent) lets Word size columns to
|
|
467
|
+
* content. Consulted alongside `widthType` to decide whether `w:noWrap` cells
|
|
468
|
+
* can be honored (see `tableColumnsArePinned`).
|
|
469
|
+
*/
|
|
470
|
+
layout?: "fixed" | "autofit"; /** Table horizontal alignment */
|
|
463
471
|
justification?: "left" | "center" | "right"; /** Table indent from left margin (in pixels, from w:tblInd) */
|
|
464
472
|
indent?: number; /** Right-to-left column order (w:bidiVisual): logical column 0 paints on the right. */
|
|
465
473
|
bidi?: boolean; /** Floating table properties (pixel values). */
|
|
@@ -903,7 +911,8 @@ type LayoutOptions = {
|
|
|
903
911
|
headerContentHeights?: HeaderFooterContentHeights; /** Footer content heights by variant. */
|
|
904
912
|
footerContentHeights?: HeaderFooterContentHeights; /** Whether section has different first page header/footer. */
|
|
905
913
|
titlePage?: boolean; /** Whether section has different even/odd headers/footers. */
|
|
906
|
-
evenAndOddHeaders?: boolean; /**
|
|
914
|
+
evenAndOddHeaders?: boolean; /** Swap left/right margins on even physical pages. */
|
|
915
|
+
mirrorMargins?: boolean; /** Per-page footnote reserved heights (pageNumber → height in pixels). */
|
|
907
916
|
footnoteReservedHeights?: Map<number, number>;
|
|
908
917
|
/**
|
|
909
918
|
* Footnote content heights keyed by internal footnote id (the OOXML
|
|
@@ -970,10 +979,8 @@ type HeaderFooterContent = {
|
|
|
970
979
|
visualTop?: number; /** Bottom-most visual extent relative to the nominal flow origin. */
|
|
971
980
|
visualBottom?: number;
|
|
972
981
|
/**
|
|
973
|
-
* Flow-only bounds
|
|
974
|
-
*
|
|
975
|
-
* the renderer paints behind body content; including them would reserve
|
|
976
|
-
* a full-page letterhead as body push-down.
|
|
982
|
+
* Flow-only bounds for consumers that need to distinguish normal
|
|
983
|
+
* header/footer flow from floating or `behindDoc` artwork.
|
|
977
984
|
*/
|
|
978
985
|
marginPushTop?: number;
|
|
979
986
|
marginPushBottom?: number;
|
|
@@ -1031,5 +1038,19 @@ declare function floatingTextBoxWrapsText(block: TextBoxFlowAttrs): boolean;
|
|
|
1031
1038
|
* Ported from eigenpal #694.
|
|
1032
1039
|
*/
|
|
1033
1040
|
declare function floatingTextBoxReservesBand(block: TextBoxFlowAttrs): boolean;
|
|
1041
|
+
/**
|
|
1042
|
+
* A table's columns are pinned when the layout engine cannot widen a column to
|
|
1043
|
+
* fit content that overflows it: either the layout is explicitly fixed
|
|
1044
|
+
* (`w:tblLayout w:type="fixed"`) or an explicit total width (`w:tblW` `dxa`/
|
|
1045
|
+
* `pct`) is fully consumed by the grid.
|
|
1046
|
+
*
|
|
1047
|
+
* In OOXML, `w:noWrap` (§17.4.30) asks Word not to soft-wrap a cell, but Word
|
|
1048
|
+
* can only honor it by widening the column. When the columns are pinned it
|
|
1049
|
+
* cannot, so overflowing content wraps despite `w:noWrap`. An auto-width table
|
|
1050
|
+
* (`w:tblW` `auto`/`nil`/absent with autofit layout) lets Word widen the column
|
|
1051
|
+
* instead, so there `w:noWrap` genuinely keeps content on one line. Measurement
|
|
1052
|
+
* and the painter both consult this so their line count and `white-space` agree.
|
|
1053
|
+
*/
|
|
1054
|
+
declare function tableColumnsArePinned(table: TableBlock): boolean;
|
|
1034
1055
|
//#endregion
|
|
1035
|
-
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, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, types_d_exports };
|
|
1056
|
+
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, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_d_exports };
|
|
@@ -11,7 +11,8 @@ var types_exports = /* @__PURE__ */ __exportAll({
|
|
|
11
11
|
floatingTextBoxWrapsText: () => floatingTextBoxWrapsText,
|
|
12
12
|
isFloatingImageRun: () => isFloatingImageRun,
|
|
13
13
|
isFloatingTextBoxBlock: () => isFloatingTextBoxBlock,
|
|
14
|
-
isTextWrappingFloatingImageRun: () => isTextWrappingFloatingImageRun
|
|
14
|
+
isTextWrappingFloatingImageRun: () => isTextWrappingFloatingImageRun,
|
|
15
|
+
tableColumnsArePinned: () => tableColumnsArePinned
|
|
15
16
|
});
|
|
16
17
|
/** Default internal margins for text boxes (OOXML defaults in pixels) */
|
|
17
18
|
const DEFAULT_TEXTBOX_MARGINS = {
|
|
@@ -96,5 +97,22 @@ function floatingTextBoxWrapsText(block) {
|
|
|
96
97
|
function floatingTextBoxReservesBand(block) {
|
|
97
98
|
return isFloatingTextBoxBlock(block) && block.wrapType === "topAndBottom";
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* A table's columns are pinned when the layout engine cannot widen a column to
|
|
102
|
+
* fit content that overflows it: either the layout is explicitly fixed
|
|
103
|
+
* (`w:tblLayout w:type="fixed"`) or an explicit total width (`w:tblW` `dxa`/
|
|
104
|
+
* `pct`) is fully consumed by the grid.
|
|
105
|
+
*
|
|
106
|
+
* In OOXML, `w:noWrap` (§17.4.30) asks Word not to soft-wrap a cell, but Word
|
|
107
|
+
* can only honor it by widening the column. When the columns are pinned it
|
|
108
|
+
* cannot, so overflowing content wraps despite `w:noWrap`. An auto-width table
|
|
109
|
+
* (`w:tblW` `auto`/`nil`/absent with autofit layout) lets Word widen the column
|
|
110
|
+
* instead, so there `w:noWrap` genuinely keeps content on one line. Measurement
|
|
111
|
+
* and the painter both consult this so their line count and `white-space` agree.
|
|
112
|
+
*/
|
|
113
|
+
function tableColumnsArePinned(table) {
|
|
114
|
+
if (table.layout === "fixed") return true;
|
|
115
|
+
return table.widthType === "dxa" || table.widthType === "pct";
|
|
116
|
+
}
|
|
99
117
|
//#endregion
|
|
100
|
-
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, types_exports };
|
|
118
|
+
export { DEFAULT_TEXTBOX_MARGINS, DEFAULT_TEXTBOX_WIDTH, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, floatingTextBoxReservesBand, floatingTextBoxWrapsText, isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun, tableColumnsArePinned, types_exports };
|
|
@@ -42,6 +42,20 @@ type PageFloatingImage = {
|
|
|
42
42
|
*/
|
|
43
43
|
behindDoc: boolean;
|
|
44
44
|
};
|
|
45
|
+
type FloatingTableBandOptions = {
|
|
46
|
+
contentX: number;
|
|
47
|
+
tableWidth: number;
|
|
48
|
+
contentWidth: number;
|
|
49
|
+
distLeft: number;
|
|
50
|
+
distRight: number;
|
|
51
|
+
};
|
|
52
|
+
declare const floatingTableReservesBand: ({
|
|
53
|
+
contentX,
|
|
54
|
+
tableWidth,
|
|
55
|
+
contentWidth,
|
|
56
|
+
distLeft,
|
|
57
|
+
distRight
|
|
58
|
+
}: FloatingTableBandOptions) => boolean;
|
|
45
59
|
/**
|
|
46
60
|
* CSS class names for page elements
|
|
47
61
|
*/
|
|
@@ -276,4 +290,4 @@ declare function findPageShellForPmPos(container: HTMLElement, pmPos: number): {
|
|
|
276
290
|
isExact: boolean;
|
|
277
291
|
} | null;
|
|
278
292
|
//#endregion
|
|
279
|
-
export { AnchoredImagePosition, FootnoteRenderItem, type HeaderFooterContent, HeaderFooterLayoutInfo, PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, PageFloatingImage, PageGeometry, PainterPaintedDetail, type RenderContext, RenderPageOptions, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
293
|
+
export { AnchoredImagePosition, FootnoteRenderItem, type HeaderFooterContent, HeaderFooterLayoutInfo, PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, PageFloatingImage, PageGeometry, PainterPaintedDetail, type RenderContext, RenderPageOptions, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
@@ -19,6 +19,11 @@ import { panic } from "better-result";
|
|
|
19
19
|
* Renders a single page from Layout data to DOM elements.
|
|
20
20
|
* Each page contains positioned fragments within a content area.
|
|
21
21
|
*/
|
|
22
|
+
const floatingTableReservesBand = ({ contentX, tableWidth, contentWidth, distLeft, distRight }) => {
|
|
23
|
+
const leftClearance = Math.max(0, contentX - distLeft);
|
|
24
|
+
const rightClearance = Math.max(0, contentWidth - contentX - tableWidth - distRight);
|
|
25
|
+
return Math.max(leftClearance, rightClearance) < 24;
|
|
26
|
+
};
|
|
22
27
|
/**
|
|
23
28
|
* CSS class names for page elements
|
|
24
29
|
*/
|
|
@@ -874,7 +879,8 @@ function renderPage(page, context, options = {}) {
|
|
|
874
879
|
...img.wrapType !== void 0 ? { wrapType: img.wrapType } : {}
|
|
875
880
|
});
|
|
876
881
|
}
|
|
877
|
-
if (options.blockLookup) for (
|
|
882
|
+
if (options.blockLookup) for (let fragmentIndex = 0; fragmentIndex < page.fragments.length; fragmentIndex++) {
|
|
883
|
+
const fragment = page.fragments[fragmentIndex];
|
|
878
884
|
if (fragment.kind !== "table") continue;
|
|
879
885
|
const blockData = options.blockLookup.get(String(fragment.blockId));
|
|
880
886
|
if (blockData?.block.kind !== "table") continue;
|
|
@@ -886,9 +892,8 @@ function renderPage(page, context, options = {}) {
|
|
|
886
892
|
const distBottom = floating.bottomFromText ?? 0;
|
|
887
893
|
const distLeft = floating.leftFromText ?? 12;
|
|
888
894
|
const distRight = floating.rightFromText ?? 12;
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
side,
|
|
895
|
+
const rect = {
|
|
896
|
+
side: contentX < contentWidth / 2 ? "left" : "right",
|
|
892
897
|
x: contentX,
|
|
893
898
|
y: contentY,
|
|
894
899
|
width: fragment.width,
|
|
@@ -897,7 +902,19 @@ function renderPage(page, context, options = {}) {
|
|
|
897
902
|
distBottom,
|
|
898
903
|
distLeft,
|
|
899
904
|
distRight
|
|
905
|
+
};
|
|
906
|
+
if (floatingTableReservesBand({
|
|
907
|
+
contentX,
|
|
908
|
+
tableWidth: fragment.width,
|
|
909
|
+
contentWidth,
|
|
910
|
+
distLeft,
|
|
911
|
+
distRight
|
|
912
|
+
})) rect.wrapType = "topAndBottom";
|
|
913
|
+
if (floating.vertAnchor === "text") scopedFloatingRects.push({
|
|
914
|
+
startsAtFragmentIndex: fragmentIndex,
|
|
915
|
+
rect
|
|
900
916
|
});
|
|
917
|
+
else floatingRects.push(rect);
|
|
901
918
|
}
|
|
902
919
|
if (options.blockLookup) for (let fragmentIndex = 0; fragmentIndex < page.fragments.length; fragmentIndex++) {
|
|
903
920
|
const fragment = page.fragments[fragmentIndex];
|
|
@@ -1557,22 +1574,21 @@ function renderPages(pages, container, options = {}) {
|
|
|
1557
1574
|
const pageShells = [];
|
|
1558
1575
|
for (let i = 0; i < pages.length; i++) {
|
|
1559
1576
|
const page = pages[i];
|
|
1577
|
+
let pageEl;
|
|
1560
1578
|
if (!useVirtualization) {
|
|
1561
1579
|
const { context, pageOptions } = buildPageRenderArgs(page, totalPages, options);
|
|
1562
|
-
|
|
1563
|
-
container.insertBefore(pageEl, overlayBefore);
|
|
1564
|
-
pageShells.push(pageEl);
|
|
1580
|
+
pageEl = renderPage(page, context, pageOptions);
|
|
1565
1581
|
} else {
|
|
1566
1582
|
const doc = options.document ?? document;
|
|
1567
|
-
|
|
1583
|
+
pageEl = doc.createElement("div");
|
|
1568
1584
|
pageEl.className = options.pageClassName ?? PAGE_CLASS_NAMES.page;
|
|
1569
1585
|
pageEl.dataset["pageNumber"] = String(page.number);
|
|
1570
1586
|
pageEl.dataset["pageIndex"] = String(i);
|
|
1571
1587
|
applyPageStyles(pageEl, page.size.w, page.size.h, options);
|
|
1572
1588
|
syncPageBorderOverlay(pageEl, page, options, doc);
|
|
1573
|
-
container.insertBefore(pageEl, overlayBefore);
|
|
1574
|
-
pageShells.push(pageEl);
|
|
1575
1589
|
}
|
|
1590
|
+
container.insertBefore(pageEl, overlayBefore);
|
|
1591
|
+
pageShells.push(pageEl);
|
|
1576
1592
|
}
|
|
1577
1593
|
if (!useVirtualization) {
|
|
1578
1594
|
emitPainterPainted(container);
|
|
@@ -1820,4 +1836,4 @@ function depopulatePageShell(shell, pageDataMap) {
|
|
|
1820
1836
|
}
|
|
1821
1837
|
}
|
|
1822
1838
|
//#endregion
|
|
1823
|
-
export { PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
1839
|
+
export { PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
@@ -330,8 +330,8 @@ function renderTabRun(run, doc, width, leader) {
|
|
|
330
330
|
} else span.textContent = "\xA0";
|
|
331
331
|
return span;
|
|
332
332
|
}
|
|
333
|
-
function canClampTabToRightEdge(alignment, hasPriorRenderedContent) {
|
|
334
|
-
if (alignment === "start" || alignment === "default") return hasPriorRenderedContent;
|
|
333
|
+
function canClampTabToRightEdge(alignment, hasPriorRenderedContent, hasPriorTab, isLastLine) {
|
|
334
|
+
if (alignment === "start" || alignment === "default") return hasPriorRenderedContent && (hasPriorTab || isLastLine);
|
|
335
335
|
return true;
|
|
336
336
|
}
|
|
337
337
|
function applyTabUnderline(element, run) {
|
|
@@ -905,24 +905,32 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
905
905
|
lineEl.append(emptySpan);
|
|
906
906
|
return lineEl;
|
|
907
907
|
}
|
|
908
|
+
const hasTabRuns = runsForLine.some(isTabRun);
|
|
908
909
|
if (alignment === "justify" && options) {
|
|
909
910
|
if (!options.isLastLine || options.paragraphEndsWithLineBreak) {
|
|
910
|
-
const
|
|
911
|
+
const firstLineIndentPx = options.isFirstLine ? options.firstLineIndentPx ?? 0 : 0;
|
|
912
|
+
const firstLineHangingPx = Math.max(0, -firstLineIndentPx);
|
|
913
|
+
const justifyCapacityPx = options.availableWidth + firstLineHangingPx;
|
|
914
|
+
const overfullPx = line.width - justifyCapacityPx;
|
|
911
915
|
const shrinkableSpaces = countShrinkableSpaces(runsForLine);
|
|
912
916
|
if (overfullPx > RIGHT_EDGE_EPSILON_PX && shrinkableSpaces > 0) {
|
|
913
917
|
lineEl.style.textAlign = "left";
|
|
914
918
|
lineEl.style.textAlignLast = "auto";
|
|
915
919
|
lineEl.style.wordSpacing = `${-overfullPx / shrinkableSpaces}px`;
|
|
920
|
+
} else if (hasTabRuns && shrinkableSpaces > 0 && -overfullPx > RIGHT_EDGE_EPSILON_PX) {
|
|
921
|
+
lineEl.style.textAlign = "left";
|
|
922
|
+
lineEl.style.textAlignLast = "auto";
|
|
923
|
+
lineEl.style.wordSpacing = `${-overfullPx / shrinkableSpaces}px`;
|
|
916
924
|
} else {
|
|
917
925
|
lineEl.style.textAlign = "justify";
|
|
918
926
|
lineEl.style.textAlignLast = "justify";
|
|
919
927
|
}
|
|
920
|
-
|
|
928
|
+
const listFirstLineOffset = options.isFirstLine && block.attrs?.listMarker && !block.attrs.listMarkerHidden ? options.firstLineIndentPx ?? 0 : 0;
|
|
929
|
+
lineEl.style.width = `${options.availableWidth - listFirstLineOffset}px`;
|
|
921
930
|
}
|
|
922
931
|
}
|
|
923
932
|
lineEl.style.whiteSpace = "pre";
|
|
924
933
|
lineEl.style.overflow = "visible";
|
|
925
|
-
const hasTabRuns = runsForLine.some(isTabRun);
|
|
926
934
|
let tabContext;
|
|
927
935
|
const measureText = createTextMeasurer(doc);
|
|
928
936
|
if (hasTabRuns) {
|
|
@@ -930,6 +938,7 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
930
938
|
const leftIndentTwips = options?.leftIndentPx ? Math.round(options.leftIndentPx * 15) : 0;
|
|
931
939
|
tabContext = {
|
|
932
940
|
...explicitStops !== void 0 ? { explicitStops } : {},
|
|
941
|
+
...block.attrs?.defaultTabStopTwips !== void 0 ? { defaultTabInterval: block.attrs.defaultTabStopTwips } : {},
|
|
933
942
|
leftIndent: leftIndentTwips
|
|
934
943
|
};
|
|
935
944
|
}
|
|
@@ -992,7 +1001,7 @@ function renderLine(block, line, alignment, doc, options) {
|
|
|
992
1001
|
break;
|
|
993
1002
|
}
|
|
994
1003
|
let tabWidth = tabResult.width;
|
|
995
|
-
if (lineRightEdgeX !== void 0 && canClampTabToRightEdge(tabResult.alignment, i > 0) && currentX + tabWidth + followingWidthForCheck > lineRightEdgeX) tabWidth = Math.max(1, lineRightEdgeX - currentX - followingWidthForCheck);
|
|
1004
|
+
if (lineRightEdgeX !== void 0 && canClampTabToRightEdge(tabResult.alignment, i > 0, runsForLine.slice(0, i).some(isTabRun), options?.isLastLine === true) && currentX + tabWidth + followingWidthForCheck > lineRightEdgeX) tabWidth = Math.max(1, lineRightEdgeX - currentX - followingWidthForCheck);
|
|
996
1005
|
const tabEl = renderTabRun(run, doc, tabWidth, tabResult.leader);
|
|
997
1006
|
lineEl.append(tabEl);
|
|
998
1007
|
currentX += tabWidth;
|
|
@@ -1240,7 +1249,7 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
|
|
|
1240
1249
|
} else if (indentLeft > 0) lineEl.style.paddingLeft = `${indentLeft}px`;
|
|
1241
1250
|
else if (hasFirstLine && !isFlexLine) lineEl.style.textIndent = `${indent.firstLine ?? 0}px`;
|
|
1242
1251
|
} else if (indentLeft > 0) lineEl.style.paddingLeft = `${indentLeft}px`;
|
|
1243
|
-
else if (hasHanging) lineEl.style.paddingLeft = `${indent.hanging ?? 0}px`;
|
|
1252
|
+
else if (hasHanging && indentLeft === 0) lineEl.style.paddingLeft = `${indent.hanging ?? 0}px`;
|
|
1244
1253
|
if (indentRight > 0) lineEl.style.paddingRight = `${indentRight}px`;
|
|
1245
1254
|
if (isFirstLine && block.attrs?.listMarker && !block.attrs.listMarkerHidden) {
|
|
1246
1255
|
const hanging = indent?.hanging ?? 0;
|
|
@@ -1259,7 +1268,8 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
|
|
|
1259
1268
|
const markerFontFamily = block.attrs.listMarkerFontFamily ?? firstTextRun?.fontFamily ?? block.attrs.defaultFontFamily;
|
|
1260
1269
|
const markerFontSize = block.attrs.listMarkerFontSize ?? firstTextRun?.fontSize ?? block.attrs.defaultFontSize;
|
|
1261
1270
|
const marker = renderListMarker(block.attrs.listMarker, getListMarkerInlineWidth(block), doc, markerFontFamily, markerFontSize, block.attrs.listMarkerRevision, block.attrs.listMarkerSecondSlotOffsetTwips);
|
|
1262
|
-
|
|
1271
|
+
const markerMarginLeft = markerStart - Math.min(indentLeft, 0);
|
|
1272
|
+
if (markerMarginLeft < 0 && indentLeft !== 0) marker.style.marginLeft = `${markerMarginLeft}px`;
|
|
1263
1273
|
lineEl.prepend(marker);
|
|
1264
1274
|
}
|
|
1265
1275
|
fragmentEl.append(lineEl);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tableColumnsArePinned } from "../layout-engine/types.js";
|
|
1
2
|
import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
|
|
2
3
|
import { getAutomaticTextColorForBackground } from "./documentColors.js";
|
|
3
4
|
import { renderParagraphFragment } from "./renderParagraph.js";
|
|
@@ -137,6 +138,7 @@ function renderNestedTable(block, measure, context, doc) {
|
|
|
137
138
|
}
|
|
138
139
|
rowYPositions.push(yPos);
|
|
139
140
|
const spanningCells = /* @__PURE__ */ new Map();
|
|
141
|
+
const columnsPinned = tableColumnsArePinned(block);
|
|
140
142
|
let y = 0;
|
|
141
143
|
for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex++) {
|
|
142
144
|
const row = block.rows[rowIndex];
|
|
@@ -146,7 +148,7 @@ function renderNestedTable(block, measure, context, doc) {
|
|
|
146
148
|
y += rowMeasure.height;
|
|
147
149
|
continue;
|
|
148
150
|
}
|
|
149
|
-
const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, void 0, block.bidi);
|
|
151
|
+
const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, void 0, block.bidi, columnsPinned);
|
|
150
152
|
tableEl.append(rowEl);
|
|
151
153
|
y += rowMeasure.height;
|
|
152
154
|
}
|
|
@@ -169,7 +171,7 @@ function applyBorder(el, side, border) {
|
|
|
169
171
|
/**
|
|
170
172
|
* Render a single table cell
|
|
171
173
|
*/
|
|
172
|
-
function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, context, doc) {
|
|
174
|
+
function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, columnsPinned, context, doc) {
|
|
173
175
|
const cellEl = doc.createElement("div");
|
|
174
176
|
cellEl.className = TABLE_CLASS_NAMES.cell;
|
|
175
177
|
cellEl.style.position = "absolute";
|
|
@@ -195,7 +197,7 @@ function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, context,
|
|
|
195
197
|
const automaticTextColor = getAutomaticTextColorForBackground(cell.background);
|
|
196
198
|
if (automaticTextColor) cellEl.style.color = automaticTextColor;
|
|
197
199
|
}
|
|
198
|
-
if (cell.noWrap) cellEl.style.whiteSpace = "nowrap";
|
|
200
|
+
if (cell.noWrap && !columnsPinned) cellEl.style.whiteSpace = "nowrap";
|
|
199
201
|
if (cell.verticalAlign) {
|
|
200
202
|
cellEl.style.display = "flex";
|
|
201
203
|
cellEl.style.flexDirection = "column";
|
|
@@ -226,7 +228,7 @@ function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, context,
|
|
|
226
228
|
/**
|
|
227
229
|
* Render a table row with rowSpan support
|
|
228
230
|
*/
|
|
229
|
-
function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, bidi = false) {
|
|
231
|
+
function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, bidi = false, columnsPinned = false) {
|
|
230
232
|
const rowEl = doc.createElement("div");
|
|
231
233
|
rowEl.className = TABLE_CLASS_NAMES.row;
|
|
232
234
|
const tableWidth = bidi ? columnWidths.reduce((sum, columnWidth) => sum + columnWidth, 0) : 0;
|
|
@@ -270,7 +272,7 @@ function renderTableRow(row, rowMeasure, rowIndex, y, columnWidths, totalRows, c
|
|
|
270
272
|
isLastRow,
|
|
271
273
|
isFirstCol: bidi ? atLogicalEnd : atLogicalStart,
|
|
272
274
|
isLastCol: bidi ? atLogicalStart : atLogicalEnd
|
|
273
|
-
}, context, doc);
|
|
275
|
+
}, columnsPinned, context, doc);
|
|
274
276
|
cellEl.dataset["cellIndex"] = String(cellIndex);
|
|
275
277
|
cellEl.dataset["columnIndex"] = String(columnIndex);
|
|
276
278
|
if (rowSpan > 1) cellEl.dataset["rowSpan"] = String(rowSpan);
|
|
@@ -348,6 +350,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
348
350
|
}
|
|
349
351
|
rowYPositions.push(yPos);
|
|
350
352
|
const spanningCells = /* @__PURE__ */ new Map();
|
|
353
|
+
const columnsPinned = tableColumnsArePinned(block);
|
|
351
354
|
const headerRowCount = fragment.headerRowCount ?? 0;
|
|
352
355
|
let y = 0;
|
|
353
356
|
if (headerRowCount > 0 && fragment.continuesFromPrev) for (let hdrIdx = 0; hdrIdx < headerRowCount; hdrIdx++) {
|
|
@@ -358,7 +361,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
358
361
|
y += hdrRowMeasure.height;
|
|
359
362
|
continue;
|
|
360
363
|
}
|
|
361
|
-
const rowEl = renderTableRow(hdrRow, hdrRowMeasure, hdrIdx, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, hdrIdx === 0, block.bidi);
|
|
364
|
+
const rowEl = renderTableRow(hdrRow, hdrRowMeasure, hdrIdx, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, hdrIdx === 0, block.bidi, columnsPinned);
|
|
362
365
|
rowEl.dataset["repeatedHeader"] = "true";
|
|
363
366
|
tableEl.append(rowEl);
|
|
364
367
|
y += hdrRowMeasure.height;
|
|
@@ -386,7 +389,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
386
389
|
continue;
|
|
387
390
|
}
|
|
388
391
|
const isFirstRowInFragment = headerRowCount > 0 && fragment.continuesFromPrev ? false : fragment.continuesFromPrev && rowIndex === fragment.fromRow;
|
|
389
|
-
const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, block.bidi);
|
|
392
|
+
const rowEl = renderTableRow(row, rowMeasure, rowIndex, y, measure.columnWidths, block.rows.length, context, doc, spanningCells, rowYPositions, isFirstRowInFragment, block.bidi, columnsPinned);
|
|
390
393
|
contentParent.append(rowEl);
|
|
391
394
|
y += rowMeasure.height;
|
|
392
395
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resetAuthorColors } from "../utils/authorColors.js";
|
|
2
2
|
import { loadFontsWithMapping } from "../utils/fontLoader.js";
|
|
3
3
|
import { parseDocx } from "../docx/parser.js";
|
|
4
|
+
import { recordDocumentLoadPhase } from "../layout-engine/layoutInstrumentation.js";
|
|
4
5
|
import { inspectDocxCompatibility } from "../docx/compatibility.js";
|
|
5
6
|
//#region src/managers/DocumentLoaderManager.ts
|
|
6
7
|
/**
|
|
@@ -48,11 +49,17 @@ var DocumentLoaderManager = class {
|
|
|
48
49
|
const generation = ++this.loadGeneration;
|
|
49
50
|
if (!(history.state !== null)) setDocumentLoadState({ status: "loading" });
|
|
50
51
|
try {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const parseStartedAt = performance.now();
|
|
53
|
+
let doc;
|
|
54
|
+
try {
|
|
55
|
+
doc = await parseDocx(buffer, {
|
|
56
|
+
detectVariables: false,
|
|
57
|
+
preloadFonts: false,
|
|
58
|
+
password: options.password
|
|
59
|
+
});
|
|
60
|
+
} finally {
|
|
61
|
+
recordDocumentLoadPhase("docx-parse", performance.now() - parseStartedAt);
|
|
62
|
+
}
|
|
56
63
|
if (this.loadGeneration !== generation) return;
|
|
57
64
|
this.loadParsedDocument(doc);
|
|
58
65
|
} catch (error) {
|
package/dist/model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES } from "./types/documentEnumValues.js";
|
|
2
1
|
import { deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
|
|
2
|
+
import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES } from "./types/documentEnumValues.js";
|
|
3
3
|
import { types_exports } from "./layout-engine/types.js";
|
|
4
4
|
export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_exports as Layout, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isFolioBlockId, isSequentialFolioBlockId };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Layout } from "../layout-engine/types.js";
|
|
2
|
+
import { Node } from "prosemirror-model";
|
|
3
|
+
|
|
4
|
+
//#region src/paged-layout/pageText.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The plain text of a single rendered page (1-based), read by joining each of
|
|
7
|
+
* the page's layout fragments' `[pmStart, pmEnd)` document text with a
|
|
8
|
+
* newline. Fragments with no `pmStart`/`pmEnd` (e.g. a page-shell placeholder)
|
|
9
|
+
* are skipped. Returns `null` when the layout hasn't been computed yet or the
|
|
10
|
+
* page number is out of range.
|
|
11
|
+
*/
|
|
12
|
+
declare const getPageTextFromLayout: (layout: Layout | null, doc: Node, page: number) => string | null;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { getPageTextFromLayout };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/paged-layout/pageText.ts
|
|
2
|
+
/**
|
|
3
|
+
* The plain text of a single rendered page (1-based), read by joining each of
|
|
4
|
+
* the page's layout fragments' `[pmStart, pmEnd)` document text with a
|
|
5
|
+
* newline. Fragments with no `pmStart`/`pmEnd` (e.g. a page-shell placeholder)
|
|
6
|
+
* are skipped. Returns `null` when the layout hasn't been computed yet or the
|
|
7
|
+
* page number is out of range.
|
|
8
|
+
*/
|
|
9
|
+
const getPageTextFromLayout = (layout, doc, page) => {
|
|
10
|
+
if (!layout || !Number.isInteger(page) || page < 1) return null;
|
|
11
|
+
const layoutPage = layout.pages[page - 1];
|
|
12
|
+
if (!layoutPage) return null;
|
|
13
|
+
const fragmentTexts = [];
|
|
14
|
+
for (const fragment of layoutPage.fragments) {
|
|
15
|
+
if (typeof fragment.pmStart !== "number" || typeof fragment.pmEnd !== "number") continue;
|
|
16
|
+
fragmentTexts.push(doc.textBetween(fragment.pmStart, fragment.pmEnd, "\n"));
|
|
17
|
+
}
|
|
18
|
+
return fragmentTexts.join("\n");
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { getPageTextFromLayout };
|
|
@@ -174,6 +174,7 @@ const readParagraphAttrs = (node) => {
|
|
|
174
174
|
});
|
|
175
175
|
optionalOneOf(attrs, "sectionBreakType", "paragraph.attrs.sectionBreakType", issues, SECTION_BREAK_TYPES);
|
|
176
176
|
optionalStringArray(attrs, "listLevelNumFmts", "paragraph.attrs.listLevelNumFmts", issues);
|
|
177
|
+
optionalNumberArray(attrs, "listLevelStarts", "paragraph.attrs.listLevelStarts", issues);
|
|
177
178
|
optionalNumber(attrs, "listAbstractNumId", "paragraph.attrs.listAbstractNumId", issues);
|
|
178
179
|
optionalBorderMap(attrs, "borders", "paragraph.attrs.borders", issues, [
|
|
179
180
|
"top",
|
|
@@ -186,6 +187,7 @@ const readParagraphAttrs = (node) => {
|
|
|
186
187
|
optionalShading(attrs, "shading", "paragraph.attrs.shading", issues);
|
|
187
188
|
optionalTabStops(attrs, "tabs", "paragraph.attrs.tabs", issues);
|
|
188
189
|
optionalRecord(attrs, "spacingExplicit", "paragraph.attrs.spacingExplicit", issues);
|
|
190
|
+
optionalRecord(attrs, "spacingFromDocDefaults", "paragraph.attrs.spacingFromDocDefaults", issues);
|
|
189
191
|
optionalTextFormatting(attrs, "defaultTextFormatting", "paragraph.attrs.defaultTextFormatting", issues);
|
|
190
192
|
optionalRecord(attrs, "numPr", "paragraph.attrs.numPr", issues);
|
|
191
193
|
validateNumPr(attrs["numPr"], issues);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, sectionRejectProperties, tableCellRejectAttrPatch, tableRejectAttrPatch, tableRowRejectAttrPatch } from "./propertyChangeScope.js";
|
|
1
2
|
//#region src/prosemirror/commands/comments.ts
|
|
2
3
|
/**
|
|
3
4
|
* Add a comment mark to the current selection.
|
|
@@ -59,21 +60,47 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
59
60
|
if (node.type.name === "paragraph") {
|
|
60
61
|
const op = collectPPrMarkOp(node, pos, from, to, mode, revisionSet);
|
|
61
62
|
if (op) pPrMarkOps.push(op);
|
|
63
|
+
const boundaryCovered = rangeCoversParagraphBoundary(from, to, pos, node);
|
|
64
|
+
let nextAttrs = null;
|
|
62
65
|
const propertyChanges = node.attrs["_propertyChanges"];
|
|
63
|
-
if (Array.isArray(propertyChanges) && propertyChanges.length > 0 &&
|
|
66
|
+
if (Array.isArray(propertyChanges) && propertyChanges.length > 0 && boundaryCovered) {
|
|
64
67
|
const matches = propertyChanges.filter((c) => revisionSet === null || c.info && revisionSet.has(c.info.id));
|
|
65
68
|
if (matches.length > 0) {
|
|
66
69
|
const remaining = propertyChanges.filter((c) => revisionSet !== null && (!c.info || !revisionSet.has(c.info.id)));
|
|
67
|
-
|
|
70
|
+
nextAttrs = {
|
|
68
71
|
...node.attrs,
|
|
69
72
|
_propertyChanges: remaining.length > 0 ? remaining : null
|
|
70
73
|
};
|
|
71
|
-
if (mode === "reject") {
|
|
72
|
-
|
|
74
|
+
if (mode === "reject") for (const change of matches.toReversed()) {
|
|
75
|
+
Object.assign(nextAttrs, paragraphRejectAttrPatch(change.previousFormatting));
|
|
76
|
+
nextAttrs["_originalFormatting"] = paragraphRejectOriginalFormatting(change.previousFormatting, node.attrs["_originalFormatting"]);
|
|
73
77
|
}
|
|
74
|
-
tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
80
|
+
const sectionProperties = node.attrs["_sectionProperties"];
|
|
81
|
+
const sectionChanges = sectionProperties?.propertyChanges;
|
|
82
|
+
if (sectionProperties && Array.isArray(sectionChanges) && sectionChanges.length > 0 && boundaryCovered) {
|
|
83
|
+
const matches = sectionChanges.filter((c) => revisionSet === null || c.info && revisionSet.has(c.info.id));
|
|
84
|
+
if (matches.length > 0) {
|
|
85
|
+
const remaining = sectionChanges.filter((c) => revisionSet !== null && (!c.info || !revisionSet.has(c.info.id)));
|
|
86
|
+
let restored = { ...sectionProperties };
|
|
87
|
+
if (mode === "reject") for (const change of matches.toReversed()) restored = sectionRejectProperties(restored, change.previousProperties);
|
|
88
|
+
delete restored.propertyChanges;
|
|
89
|
+
if (remaining.length > 0) restored.propertyChanges = remaining;
|
|
90
|
+
nextAttrs = nextAttrs ?? { ...node.attrs };
|
|
91
|
+
nextAttrs["_sectionProperties"] = restored;
|
|
92
|
+
nextAttrs["sectionBreakType"] = sectionBreakTypeFromSectionStart(restored.sectionStart);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (nextAttrs) tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const tableChangeAttrName = TABLE_PROPERTY_CHANGE_ATTR_BY_NODE[node.type.name];
|
|
99
|
+
if (tableChangeAttrName !== void 0) {
|
|
100
|
+
if (rangeCoversNode(from, to, pos, node)) {
|
|
101
|
+
const nextAttrs = resolveTablePropertyChangeAttrs(node, tableChangeAttrName, mode, revisionSet);
|
|
102
|
+
if (nextAttrs) tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
103
|
+
}
|
|
77
104
|
return true;
|
|
78
105
|
}
|
|
79
106
|
if (!node.isInline) return true;
|
|
@@ -124,6 +151,49 @@ function rangeCoversParagraphBoundary(from, to, pos, node) {
|
|
|
124
151
|
const boundaryTo = pos + node.nodeSize;
|
|
125
152
|
return from <= boundaryFrom && to >= boundaryTo;
|
|
126
153
|
}
|
|
154
|
+
/** Whether [from, to] fully covers the node — the range property-change cards
|
|
155
|
+
* and accept-all / reject-all sweeps supply for table-level records. */
|
|
156
|
+
function rangeCoversNode(from, to, pos, node) {
|
|
157
|
+
return from <= pos && to >= pos + node.nodeSize;
|
|
158
|
+
}
|
|
159
|
+
const SECTION_BREAK_TYPE_VALUES = [
|
|
160
|
+
"nextPage",
|
|
161
|
+
"continuous",
|
|
162
|
+
"oddPage",
|
|
163
|
+
"evenPage"
|
|
164
|
+
];
|
|
165
|
+
function sectionBreakTypeFromSectionStart(sectionStart) {
|
|
166
|
+
return SECTION_BREAK_TYPE_VALUES.find((value) => value === sectionStart) ?? null;
|
|
167
|
+
}
|
|
168
|
+
/** PM node type name → the attr its tracked property-change records live on. */
|
|
169
|
+
const TABLE_PROPERTY_CHANGE_ATTR_BY_NODE = {
|
|
170
|
+
table: "tblPrChange",
|
|
171
|
+
tableRow: "trPrChange",
|
|
172
|
+
tableCell: "tcPrChange",
|
|
173
|
+
tableHeader: "tcPrChange"
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the tracked property-change records on one table / row / cell node.
|
|
177
|
+
* Accept keeps the live formatting and clears the matched records; reject
|
|
178
|
+
* additionally restores the stored previous formatting wholesale (the change
|
|
179
|
+
* element stores the complete old property set — see propertyChangeScope.ts).
|
|
180
|
+
* Returns the next attrs, or `null` when no record matches.
|
|
181
|
+
*/
|
|
182
|
+
function resolveTablePropertyChangeAttrs(node, attrName, mode, revisionSet) {
|
|
183
|
+
const changes = node.attrs[attrName];
|
|
184
|
+
if (!Array.isArray(changes) || changes.length === 0) return null;
|
|
185
|
+
const matches = changes.filter((c) => revisionSet === null || c.info && revisionSet.has(c.info.id));
|
|
186
|
+
if (matches.length === 0) return null;
|
|
187
|
+
const remaining = changes.filter((c) => revisionSet !== null && (!c.info || !revisionSet.has(c.info.id)));
|
|
188
|
+
const nextAttrs = {
|
|
189
|
+
...node.attrs,
|
|
190
|
+
[attrName]: remaining.length > 0 ? remaining : null
|
|
191
|
+
};
|
|
192
|
+
if (mode === "reject") for (const change of matches.toReversed()) if (attrName === "tblPrChange") Object.assign(nextAttrs, tableRejectAttrPatch(change.previousFormatting));
|
|
193
|
+
else if (attrName === "trPrChange") Object.assign(nextAttrs, tableRowRejectAttrPatch(change.previousFormatting));
|
|
194
|
+
else Object.assign(nextAttrs, tableCellRejectAttrPatch(change.previousFormatting, node.attrs["_originalFormatting"]));
|
|
195
|
+
return nextAttrs;
|
|
196
|
+
}
|
|
127
197
|
function isPPrMarkAttr(value) {
|
|
128
198
|
if (typeof value !== "object" || value === null) return false;
|
|
129
199
|
const kind = value.kind;
|
|
@@ -39,7 +39,8 @@ const editHyperlinkAtCursor = (view, { displayText, href }) => {
|
|
|
39
39
|
const { $from } = view.state.selection;
|
|
40
40
|
const linkMark = $from.marks().find((m) => m.type === hlType);
|
|
41
41
|
if (!linkMark) return true;
|
|
42
|
-
const
|
|
42
|
+
const ranges = collectHyperlinkRanges($from.parent, $from.start(), hlType, hyperlinkHref(linkMark));
|
|
43
|
+
const targetRange = rangeAtCursor(ranges, $from.pos);
|
|
43
44
|
if (!targetRange) return false;
|
|
44
45
|
const { tooltip } = expectHyperlinkMarkAttrs(linkMark);
|
|
45
46
|
const newMark = hlType.create({
|
|
@@ -69,7 +70,8 @@ const removeHyperlinkAtCursor = (view, { popupHref }) => {
|
|
|
69
70
|
}
|
|
70
71
|
});
|
|
71
72
|
if (!linkMark) return false;
|
|
72
|
-
const
|
|
73
|
+
const ranges = collectHyperlinkRanges($from.parent, $from.start(), hlType, hyperlinkHref(linkMark));
|
|
74
|
+
const targetRange = rangeAtCursor(ranges, $from.pos);
|
|
73
75
|
if (!targetRange) return false;
|
|
74
76
|
const tr = view.state.tr.removeMark(targetRange.start, targetRange.end, hlType);
|
|
75
77
|
view.dispatch(tr.scrollIntoView());
|
|
@@ -124,7 +124,8 @@ const applyImageWrapType = (view, pos, wrapType) => {
|
|
|
124
124
|
const applyImageTransform = (view, pos, action) => {
|
|
125
125
|
const node = imageNodeAt(view, pos);
|
|
126
126
|
if (!node) return false;
|
|
127
|
-
const
|
|
127
|
+
const currentTransform = expectImageAttrs(node).transform ?? "";
|
|
128
|
+
const transform = computeImageTransform(currentTransform, action);
|
|
128
129
|
const tr = view.state.tr.setNodeMarkup(pos, void 0, mergeImageAttrs(node, { transform }));
|
|
129
130
|
view.dispatch(tr.scrollIntoView());
|
|
130
131
|
return true;
|