@portone/docx-editor 0.3.0 → 0.5.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/CHANGELOG.md +205 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +45 -25
- package/dist/core.d.ts +1 -1
- package/dist/docx/cloning.js +4 -9
- package/dist/docx/commentOnlyChange.d.ts +5 -3
- package/dist/docx/comments/grammar.d.ts +27 -10
- package/dist/docx/comments/grammar.js +18 -67
- package/dist/docx/comments/model.d.ts +5 -6
- package/dist/docx/comments/model.js +0 -9
- package/dist/docx/comments/parts.d.ts +16 -10
- package/dist/docx/comments/parts.js +65 -13
- package/dist/docx/comments/people.d.ts +11 -3
- package/dist/docx/comments/people.js +20 -6
- package/dist/docx/comments/policy.js +7 -1
- package/dist/docx/comments/reading.d.ts +4 -2
- package/dist/docx/comments/reading.js +1 -14
- package/dist/docx/comments/writing.js +78 -35
- package/dist/docx/exportDocx.js +30 -18
- package/dist/docx/exportRefs.d.ts +8 -3
- package/dist/docx/exportRefs.js +2 -1
- package/dist/docx/fidelity.js +24 -10
- package/dist/docx/fields.d.ts +42 -0
- package/dist/docx/fields.js +76 -0
- package/dist/docx/formatting/attrs.d.ts +20 -2
- package/dist/docx/formatting/attrs.js +57 -2
- package/dist/docx/formatting/context.d.ts +10 -2
- package/dist/docx/formatting/context.js +23 -5
- package/dist/docx/formatting/resolve.d.ts +10 -4
- package/dist/docx/formatting/resolve.js +31 -11
- package/dist/docx/formatting/styles.d.ts +25 -2
- package/dist/docx/formatting/styles.js +84 -3
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/hyperlink.d.ts +14 -4
- package/dist/docx/hyperlink.js +4 -1
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +135 -74
- package/dist/docx/importParagraph.d.ts +16 -4
- package/dist/docx/importParagraph.js +87 -93
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +190 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.d.ts +2 -2
- package/dist/docx/importTable.js +107 -66
- package/dist/docx/invariants.js +42 -31
- package/dist/docx/newLists.d.ts +30 -0
- package/dist/docx/newLists.js +24 -1
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- package/dist/docx/numberingPlanner.d.ts +6 -3
- package/dist/docx/numberingPlanner.js +38 -7
- package/dist/docx/pageGeometry.d.ts +5 -14
- package/dist/docx/pageGeometry.js +0 -12
- package/dist/docx/protectionPolicy.d.ts +13 -5
- package/dist/docx/protectionPolicy.js +32 -22
- package/dist/docx/scan.d.ts +18 -8
- package/dist/docx/scan.js +17 -11
- package/dist/docx/sections.d.ts +126 -0
- package/dist/docx/sections.js +207 -0
- package/dist/docx/serializeBlock.d.ts +13 -4
- package/dist/docx/serializeBlock.js +16 -18
- package/dist/docx/serializeParagraph.d.ts +4 -0
- package/dist/docx/serializeParagraph.js +1 -0
- package/dist/docx/serializePreserved.d.ts +14 -0
- package/dist/docx/serializePreserved.js +24 -0
- package/dist/docx/serializeStory.d.ts +17 -0
- package/dist/docx/serializeStory.js +17 -0
- package/dist/docx/serializeTable.js +10 -13
- package/dist/docx/session.d.ts +24 -7
- package/dist/docx/session.js +37 -6
- package/dist/docx/story.d.ts +140 -0
- package/dist/docx/story.js +237 -0
- package/dist/docx/storyProjection.js +6 -1
- package/dist/docx/tableFormatting/conditions.d.ts +76 -0
- package/dist/docx/tableFormatting/conditions.js +196 -0
- package/dist/docx/tableFormatting/editing.d.ts +7 -3
- package/dist/docx/tableFormatting/editing.js +4 -5
- package/dist/docx/tableFormatting/reading.d.ts +49 -19
- package/dist/docx/tableFormatting/reading.js +109 -35
- package/dist/docx/tableFormatting.d.ts +1 -0
- package/dist/docx/tableFormatting.js +1 -0
- package/dist/docx/tableTemplate.js +12 -12
- package/dist/editor/clipboard/htmlReader.d.ts +30 -0
- package/dist/editor/clipboard/htmlReader.js +302 -0
- package/dist/editor/clipboard/internalChannel.d.ts +57 -0
- package/dist/editor/clipboard/internalChannel.js +58 -0
- package/dist/editor/clipboard/normalizers.d.ts +99 -0
- package/dist/editor/clipboard/normalizers.js +199 -0
- package/dist/editor/clipboard/parser.d.ts +34 -0
- package/dist/editor/clipboard/parser.js +58 -0
- package/dist/editor/clipboard/plugin.d.ts +21 -0
- package/dist/editor/clipboard/plugin.js +320 -0
- package/dist/editor/clipboard/readContext.d.ts +24 -0
- package/dist/editor/clipboard/readContext.js +19 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +175 -105
- package/dist/editor/commands/comments/model.d.ts +34 -17
- package/dist/editor/commands/comments/model.js +11 -0
- package/dist/editor/commands/comments/reading.d.ts +5 -3
- package/dist/editor/commands/comments/reading.js +8 -58
- package/dist/editor/commands/formatting/propertyCommands.js +6 -1
- package/dist/editor/commands/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/listCommands.d.ts +12 -3
- package/dist/editor/commands/listCommands.js +76 -30
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +6 -1
- package/dist/editor/createEditor.js +14 -4
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- package/dist/editor/editorDocument.d.ts +7 -6
- package/dist/editor/editorDocument.js +18 -6
- package/dist/editor/imageFiles.d.ts +2 -2
- package/dist/editor/imageFiles.js +2 -0
- package/dist/editor/insertTable.d.ts +1 -1
- package/dist/editor/insertTable.js +2 -2
- package/dist/editor/paragraphEdits.d.ts +16 -1
- package/dist/editor/paragraphEdits.js +13 -5
- package/dist/editor/paragraphPlacement.d.ts +11 -0
- package/dist/editor/paragraphPlacement.js +18 -0
- package/dist/editor/plainText.d.ts +12 -0
- package/dist/editor/plainText.js +2 -1
- package/dist/editor/plugins/columnResize.js +6 -3
- package/dist/editor/plugins/commentComposer.d.ts +28 -0
- package/dist/editor/plugins/commentComposer.js +46 -0
- package/dist/editor/plugins/commentDecorations.d.ts +29 -2
- package/dist/editor/plugins/commentDecorations.js +89 -15
- package/dist/editor/plugins/documentProjection.d.ts +31 -0
- package/dist/editor/plugins/documentProjection.js +22 -0
- package/dist/editor/plugins/imagePaste.js +20 -10
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
- package/dist/editor/plugins/numberingDecorations.js +43 -2
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/paragraphDisplay.js +9 -3
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/tableDisplay.js +19 -5
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- package/dist/model/format.d.ts +50 -0
- package/dist/model/format.js +65 -0
- package/dist/numbering/listRegistry.d.ts +37 -0
- package/dist/numbering/listRegistry.js +112 -0
- package/dist/numbering/listTemplate.d.ts +26 -17
- package/dist/numbering/listTemplate.js +66 -20
- package/dist/numbering/markers.d.ts +15 -6
- package/dist/numbering/markers.js +26 -59
- package/dist/numbering/parseNumbering.d.ts +70 -5
- package/dist/numbering/parseNumbering.js +100 -26
- package/dist/numbering/spellers.d.ts +15 -0
- package/dist/numbering/spellers.js +96 -0
- package/dist/numbering/writeNumbering.d.ts +5 -3
- package/dist/numbering/writeNumbering.js +15 -15
- package/dist/ooxml/conformance.d.ts +28 -0
- package/dist/ooxml/conformance.js +20 -0
- package/dist/ooxml/errors.d.ts +5 -5
- package/dist/ooxml/fragment.d.ts +11 -0
- package/dist/ooxml/fragment.js +14 -4
- package/dist/ooxml/image.d.ts +3 -4
- package/dist/ooxml/image.js +50 -5
- package/dist/ooxml/names.d.ts +15 -0
- package/dist/ooxml/names.js +10 -1
- package/dist/ooxml/partSplice.d.ts +1 -1
- package/dist/ooxml/partSplice.js +10 -3
- package/dist/ooxml/props.d.ts +8 -0
- package/dist/ooxml/props.js +14 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/ooxml/xml.d.ts +8 -9
- package/dist/ooxml/xml.js +6 -6
- package/dist/page/PageGuides.d.ts +8 -4
- package/dist/page/PageGuides.js +13 -37
- package/dist/page/pageLayout.d.ts +62 -13
- package/dist/page/pageLayout.js +91 -37
- package/dist/page/usePageLayout.d.ts +14 -11
- package/dist/page/usePageLayout.js +21 -29
- package/dist/schema/attrRoles.js +44 -19
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +220 -139
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +2 -2
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/preservedFragments.d.ts +12 -0
- package/dist/schema/preservedFragments.js +38 -0
- package/dist/schema/preservedGuards.d.ts +9 -7
- package/dist/schema/preservedGuards.js +112 -11
- package/dist/schema/protection.d.ts +34 -5
- package/dist/schema/protection.js +42 -14
- package/dist/schema/rendering.js +3 -1
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/styles/classNames.d.ts +16 -7
- package/dist/styles/classNames.js +16 -7
- package/dist/styles.css +90 -30
- package/dist/table/cellFormatting.js +1 -2
- package/dist/table/gridBorders.d.ts +11 -15
- package/dist/table/gridBorders.js +32 -18
- package/dist/table/resize.d.ts +5 -3
- package/dist/ui/CommentsPanel.d.ts +1 -3
- package/dist/ui/CommentsPanel.js +24 -15
- package/dist/ui/NotesPanel.js +2 -2
- package/dist/ui/TextMenu.d.ts +1 -3
- package/dist/ui/TextMenu.js +10 -6
- package/dist/ui/comments/CommentComposer.d.ts +6 -1
- package/dist/ui/comments/CommentComposer.js +26 -3
- package/package.json +1 -1
- package/dist/editor/externalClipboard.d.ts +0 -11
- package/dist/editor/externalClipboard.js +0 -456
package/dist/docx/scan.js
CHANGED
|
@@ -13,15 +13,15 @@ function tagAt(source, lt) {
|
|
|
13
13
|
return tag;
|
|
14
14
|
}
|
|
15
15
|
function isBodyTag(tag, depth) {
|
|
16
|
-
return localPart(tag.name) === "body" && depth === 1
|
|
16
|
+
return localPart(tag.name) === "body" && depth === 1;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function scanBlocksIn(source, isContainer) {
|
|
19
19
|
const stack = [];
|
|
20
20
|
const blocks = [];
|
|
21
|
-
let
|
|
21
|
+
let containerDepth = null;
|
|
22
22
|
let contentStart = -1;
|
|
23
23
|
let sliceStart = -1;
|
|
24
|
-
const
|
|
24
|
+
const atBlockLevel = () => containerDepth !== null && stack.length === containerDepth;
|
|
25
25
|
const takeBlock = (name, end) => {
|
|
26
26
|
blocks.push({ name, xml: source.slice(sliceStart, end) });
|
|
27
27
|
sliceStart = end;
|
|
@@ -43,24 +43,22 @@ function scanBody(source) {
|
|
|
43
43
|
`mismatched tags: ${opened} vs ${tag.name}`
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
|
-
if (
|
|
46
|
+
if (atBlockLevel()) takeBlock(tag.name, tag.end);
|
|
47
47
|
i = tag.end;
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
-
if (
|
|
50
|
+
if (containerDepth === null && tag.kind === "open" && isContainer(tag, stack.length)) {
|
|
51
51
|
stack.push(tag.name);
|
|
52
|
-
|
|
52
|
+
containerDepth = stack.length;
|
|
53
53
|
contentStart = tag.end;
|
|
54
54
|
sliceStart = tag.end;
|
|
55
55
|
} else {
|
|
56
|
-
if (
|
|
56
|
+
if (atBlockLevel() && tag.kind === "empty") takeBlock(tag.name, tag.end);
|
|
57
57
|
if (tag.kind === "open") stack.push(tag.name);
|
|
58
58
|
}
|
|
59
59
|
i = tag.end;
|
|
60
60
|
}
|
|
61
|
-
if (
|
|
62
|
-
throw new DocxImportError("missing-body", "document has no w:body");
|
|
63
|
-
}
|
|
61
|
+
if (containerDepth === null) return null;
|
|
64
62
|
if (stack.length > 0) {
|
|
65
63
|
throw new DocxImportError(
|
|
66
64
|
"malformed-xml",
|
|
@@ -73,6 +71,14 @@ function scanBody(source) {
|
|
|
73
71
|
suffix: source.slice(sliceStart)
|
|
74
72
|
};
|
|
75
73
|
}
|
|
74
|
+
function scanBody(source) {
|
|
75
|
+
const scan = scanBlocksIn(source, isBodyTag);
|
|
76
|
+
if (scan === null) {
|
|
77
|
+
throw new DocxImportError("missing-body", "document has no w:body");
|
|
78
|
+
}
|
|
79
|
+
return scan;
|
|
80
|
+
}
|
|
76
81
|
export {
|
|
82
|
+
scanBlocksIn,
|
|
77
83
|
scanBody
|
|
78
84
|
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each section of a document lays down, read once out of the one `w:sectPr` that says it.
|
|
3
|
+
*
|
|
4
|
+
* A document is a sequence of sections. Every section but the last is closed by the paragraph that
|
|
5
|
+
* ends it, which carries that section's `w:sectPr` inside its own `w:pPr` (§17.6.17); the last one
|
|
6
|
+
* is closed by the body, whose `w:sectPr` stands after the final block (§17.6.18). The body's is
|
|
7
|
+
* the one the document node carries as `sectPr`, so an edit to it goes through a transaction and
|
|
8
|
+
* select-all + delete cannot take it away along with the blocks.
|
|
9
|
+
*
|
|
10
|
+
* A fragment is only ever sliced, never rebuilt: an untouched section goes back out as the bytes it
|
|
11
|
+
* arrived as, and a rewritten child is swapped into the spot `CT_SectPr` lays down.
|
|
12
|
+
*/
|
|
13
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
14
|
+
import { type PageGeometry } from "./pageGeometry";
|
|
15
|
+
export type HeaderFooterVariant = "default" | "first" | "even";
|
|
16
|
+
/** The variants a `w:headerReference` or a `w:footerReference` may name. §17.6.12, §17.6.5 */
|
|
17
|
+
export declare const HEADER_FOOTER_VARIANTS: readonly HeaderFooterVariant[];
|
|
18
|
+
/** The part each variant of one story kind is drawn from, as the relationship id naming it */
|
|
19
|
+
export type HeaderFooterRefs = Readonly<Record<HeaderFooterVariant, string | null>>;
|
|
20
|
+
/** What one `w:sectPr` says, read once and shared by pagination, headers and editing */
|
|
21
|
+
export interface SectionProperties {
|
|
22
|
+
/**
|
|
23
|
+
* The `<w:sectPr>...</w:sectPr>` fragment exactly as written.
|
|
24
|
+
*
|
|
25
|
+
* "" for a section read out of a parsed document rather than out of text: the only fragments this
|
|
26
|
+
* package writes back are the slices it took (a paragraph's `w:pPr` child, the document node's
|
|
27
|
+
* own attr), so a section read off a DOM has no text of its own to hand back.
|
|
28
|
+
*/
|
|
29
|
+
xml: string;
|
|
30
|
+
geometry: PageGeometry;
|
|
31
|
+
headerRefs: HeaderFooterRefs;
|
|
32
|
+
footerRefs: HeaderFooterRefs;
|
|
33
|
+
titlePg: boolean;
|
|
34
|
+
/** §17.6.19 `w:pgNumType/@w:start`; null where the section carries the numbering on */
|
|
35
|
+
pageNumberStart: number | null;
|
|
36
|
+
/** §17.6.22 `w:type/@w:val`; null when omitted (nextPage) */
|
|
37
|
+
type: "continuous" | "evenPage" | "nextPage" | "oddPage" | "nextColumn" | null;
|
|
38
|
+
}
|
|
39
|
+
/** What a section the document does not spell out lays down: the paper every document used to be drawn on */
|
|
40
|
+
export declare const DEFAULT_SECTION: SectionProperties;
|
|
41
|
+
/** Which `w:sectPr` closes a section: the paragraph carrying it, or the body itself */
|
|
42
|
+
export type SectionAnchor = {
|
|
43
|
+
kind: "paragraph";
|
|
44
|
+
pos: number;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "body";
|
|
47
|
+
};
|
|
48
|
+
export interface DocumentSection {
|
|
49
|
+
index: number;
|
|
50
|
+
/** Top-level block indexes this section covers, inclusive */
|
|
51
|
+
firstBlock: number;
|
|
52
|
+
lastBlock: number;
|
|
53
|
+
anchor: SectionAnchor;
|
|
54
|
+
props: SectionProperties;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Every relationship the sections of this body name as a header or a footer story.
|
|
58
|
+
*
|
|
59
|
+
* A `w:headerReference` and a `w:footerReference` stand only inside a `w:sectPr` (§17.6.12,
|
|
60
|
+
* §17.6.5), so the body holds all of them however many sections it is written in. A part no
|
|
61
|
+
* section names is one a producer left behind rather than content this document shows.
|
|
62
|
+
*/
|
|
63
|
+
export declare function storyReferenceIds(body: Element): ReadonlySet<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Everything one `w:sectPr` lays down.
|
|
66
|
+
*
|
|
67
|
+
* The reading is deliberately forgiving: it runs against a document already opened, and a value
|
|
68
|
+
* that cannot be read is a reason to fall back on what a section saying nothing lays down, never a
|
|
69
|
+
* reason to refuse the file.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readSectionProperties(sectPr: Element): SectionProperties;
|
|
72
|
+
/**
|
|
73
|
+
* The same, off a fragment held as text, which keeps the text as `xml`.
|
|
74
|
+
*
|
|
75
|
+
* null for anything that is not a readable `w:sectPr`, which leaves the caller holding the
|
|
76
|
+
* fragment it already has rather than a section read out of a guess.
|
|
77
|
+
*/
|
|
78
|
+
export declare function parseSectionProperties(xml: string): SectionProperties | null;
|
|
79
|
+
/**
|
|
80
|
+
* The first `w:sectPr` of an already parsed body: the one closing the first section, wherever it
|
|
81
|
+
* stands, and the body's own where the document holds a single section.
|
|
82
|
+
*/
|
|
83
|
+
export declare function firstSectPrElement(body: Element): Element | null;
|
|
84
|
+
/** The `w:sectPr` this paragraph's properties carry, and null for a paragraph that ends no section */
|
|
85
|
+
export declare function sectionBreakOf(pPr: string | null): string | null;
|
|
86
|
+
/**
|
|
87
|
+
* The paragraph properties without the section break, and null for properties left holding nothing
|
|
88
|
+
* at all. Properties carrying no break, and properties whose shape cannot be made out, are handed
|
|
89
|
+
* back as they stand.
|
|
90
|
+
*
|
|
91
|
+
* `parseProps` lists direct children alone, so the `w:sectPr` inside a `w:pPrChange` - the
|
|
92
|
+
* properties this paragraph wore before a tracked change - is not this paragraph's own break and
|
|
93
|
+
* stays where it stands.
|
|
94
|
+
*/
|
|
95
|
+
export declare function withoutSectionBreak(pPr: string | null): string | null;
|
|
96
|
+
/** The paragraph properties with this section break in the spot `CT_PPr` gives it */
|
|
97
|
+
export declare function withSectionBreak(pPr: string | null, sectPr: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* One child of a section swapped for new XML, or taken away with a null.
|
|
100
|
+
*
|
|
101
|
+
* A child that was not there goes into the spot `CT_SectPr` lays down, and every other child of
|
|
102
|
+
* the section keeps the text it was written as, whitespace between them included. A header or
|
|
103
|
+
* footer reference names one variant of one story, so writing one leaves the section's references
|
|
104
|
+
* to the other variants where they stand.
|
|
105
|
+
*
|
|
106
|
+
* A fragment whose shape cannot be made out is handed back untouched, which leaves the caller with
|
|
107
|
+
* the section the document wrote rather than one written over a guess.
|
|
108
|
+
*/
|
|
109
|
+
export declare function setSectionChild(sectPrXml: string, name: string, childXml: string | null): string;
|
|
110
|
+
/**
|
|
111
|
+
* Every section of the document in order, the last of them the one the document node carries.
|
|
112
|
+
*
|
|
113
|
+
* A document naming no section at all still has one, drawn on the paper `DEFAULT_SECTION` lays
|
|
114
|
+
* down. The last section covers no block where the final paragraph itself ends a section, which
|
|
115
|
+
* leaves `firstBlock` past `lastBlock`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function sectionsOf(doc: PMNode): readonly DocumentSection[];
|
|
118
|
+
/**
|
|
119
|
+
* The same, against a table already read.
|
|
120
|
+
*
|
|
121
|
+
* Reading the table walks every block of the document, so anything asking about more than one
|
|
122
|
+
* position - the pages of a preview, one per section - reads it once and asks here.
|
|
123
|
+
*/
|
|
124
|
+
export declare function sectionIn(sections: readonly DocumentSection[], doc: PMNode, pos: number): DocumentSection;
|
|
125
|
+
/** The section the block at this position belongs to */
|
|
126
|
+
export declare function sectionAt(doc: PMNode, pos: number): DocumentSection;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// src/docx/sections.ts
|
|
2
|
+
import {
|
|
3
|
+
parseProps,
|
|
4
|
+
parsePropsXml,
|
|
5
|
+
renderElement,
|
|
6
|
+
renderProps,
|
|
7
|
+
setChild
|
|
8
|
+
} from "../ooxml/props.js";
|
|
9
|
+
import { rootTagAt } from "../ooxml/tagScan.js";
|
|
10
|
+
import { isOnElement } from "../ooxml/units.js";
|
|
11
|
+
import {
|
|
12
|
+
attributeByLocalName,
|
|
13
|
+
childByLocalName,
|
|
14
|
+
elementChildren,
|
|
15
|
+
R_NS,
|
|
16
|
+
W_NS
|
|
17
|
+
} from "../ooxml/xml.js";
|
|
18
|
+
import {
|
|
19
|
+
A4_PORTRAIT,
|
|
20
|
+
readPageGeometry
|
|
21
|
+
} from "./pageGeometry.js";
|
|
22
|
+
var HEADER_FOOTER_VARIANTS = [
|
|
23
|
+
"default",
|
|
24
|
+
"first",
|
|
25
|
+
"even"
|
|
26
|
+
];
|
|
27
|
+
var NO_REFS = { default: null, first: null, even: null };
|
|
28
|
+
var DEFAULT_SECTION = {
|
|
29
|
+
xml: "",
|
|
30
|
+
geometry: A4_PORTRAIT,
|
|
31
|
+
headerRefs: NO_REFS,
|
|
32
|
+
footerRefs: NO_REFS,
|
|
33
|
+
titlePg: false,
|
|
34
|
+
pageNumberStart: null,
|
|
35
|
+
type: null
|
|
36
|
+
};
|
|
37
|
+
var SECTION_TYPES = [
|
|
38
|
+
"continuous",
|
|
39
|
+
"evenPage",
|
|
40
|
+
"nextPage",
|
|
41
|
+
"oddPage",
|
|
42
|
+
"nextColumn"
|
|
43
|
+
];
|
|
44
|
+
function sectionType(sectPr) {
|
|
45
|
+
const declared = childByLocalName(sectPr, "type");
|
|
46
|
+
const value = declared ? attributeByLocalName(declared, "val") : null;
|
|
47
|
+
return SECTION_TYPES.find((known) => known === value) ?? null;
|
|
48
|
+
}
|
|
49
|
+
function pageNumberStart(sectPr) {
|
|
50
|
+
const pgNumType = childByLocalName(sectPr, "pgNumType");
|
|
51
|
+
const declared = pgNumType ? attributeByLocalName(pgNumType, "start") : null;
|
|
52
|
+
if (declared === null || declared.trim() === "") return null;
|
|
53
|
+
const start = Number(declared);
|
|
54
|
+
return Number.isSafeInteger(start) && start >= 0 ? start : null;
|
|
55
|
+
}
|
|
56
|
+
function storyRefs(sectPr, kind) {
|
|
57
|
+
const refs = { ...NO_REFS };
|
|
58
|
+
for (const reference of elementChildren(sectPr)) {
|
|
59
|
+
if (reference.localName !== `${kind}Reference`) continue;
|
|
60
|
+
const declared = attributeByLocalName(reference, "type") ?? "default";
|
|
61
|
+
const variant = HEADER_FOOTER_VARIANTS.find((known) => known === declared);
|
|
62
|
+
if (variant === void 0 || refs[variant] !== null) continue;
|
|
63
|
+
const id = referenceId(reference);
|
|
64
|
+
if (id) refs[variant] = id;
|
|
65
|
+
}
|
|
66
|
+
return refs;
|
|
67
|
+
}
|
|
68
|
+
function referenceId(reference) {
|
|
69
|
+
return reference.getAttributeNS(R_NS, "id") ?? attributeByLocalName(reference, "id");
|
|
70
|
+
}
|
|
71
|
+
function storyReferenceIds(body) {
|
|
72
|
+
const ids = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const name of ["headerReference", "footerReference"]) {
|
|
74
|
+
for (const reference of body.getElementsByTagNameNS(W_NS, name)) {
|
|
75
|
+
const id = referenceId(reference);
|
|
76
|
+
if (id) ids.add(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return ids;
|
|
80
|
+
}
|
|
81
|
+
function readSectionProperties(sectPr) {
|
|
82
|
+
return {
|
|
83
|
+
xml: "",
|
|
84
|
+
geometry: readPageGeometry(sectPr),
|
|
85
|
+
headerRefs: storyRefs(sectPr, "header"),
|
|
86
|
+
footerRefs: storyRefs(sectPr, "footer"),
|
|
87
|
+
titlePg: isOnElement(childByLocalName(sectPr, "titlePg")),
|
|
88
|
+
pageNumberStart: pageNumberStart(sectPr),
|
|
89
|
+
type: sectionType(sectPr)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function parseSectionProperties(xml) {
|
|
93
|
+
const el = parsePropsXml(xml);
|
|
94
|
+
if (el?.localName !== "sectPr") return null;
|
|
95
|
+
return { ...readSectionProperties(el), xml };
|
|
96
|
+
}
|
|
97
|
+
function firstSectPrElement(body) {
|
|
98
|
+
const wordSection = body.getElementsByTagNameNS(W_NS, "sectPr")[0];
|
|
99
|
+
if (wordSection) return wordSection;
|
|
100
|
+
for (const el of body.getElementsByTagName("*")) {
|
|
101
|
+
if (el.localName === "sectPr") return el;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function pPrText(node) {
|
|
106
|
+
const pPr = node.attrs.pPr;
|
|
107
|
+
return typeof pPr === "string" ? pPr : null;
|
|
108
|
+
}
|
|
109
|
+
function sectionBreakOf(pPr) {
|
|
110
|
+
if (pPr === null) return null;
|
|
111
|
+
const props = parseProps(pPr);
|
|
112
|
+
return props?.children.find((child) => child.name === "sectPr")?.xml ?? null;
|
|
113
|
+
}
|
|
114
|
+
function withoutSectionBreak(pPr) {
|
|
115
|
+
if (pPr === null) return null;
|
|
116
|
+
const props = parseProps(pPr);
|
|
117
|
+
if (props === null) return pPr;
|
|
118
|
+
const without = setChild(props, "sectPr", null);
|
|
119
|
+
if (without.children.length === props.children.length) return pPr;
|
|
120
|
+
return renderProps(without) || null;
|
|
121
|
+
}
|
|
122
|
+
function withSectionBreak(pPr, sectPr) {
|
|
123
|
+
const props = (pPr === null ? null : parseProps(pPr)) ?? EMPTY_P_PR;
|
|
124
|
+
return renderElement(setChild(props, "sectPr", sectPr));
|
|
125
|
+
}
|
|
126
|
+
var EMPTY_P_PR = { tag: "w:pPr", attrs: null, children: [] };
|
|
127
|
+
var REFERENCE_CHILDREN = [
|
|
128
|
+
"headerReference",
|
|
129
|
+
"footerReference"
|
|
130
|
+
];
|
|
131
|
+
function referenceVariant(xml) {
|
|
132
|
+
const el = parsePropsXml(xml);
|
|
133
|
+
return (el && attributeByLocalName(el, "type")) ?? "default";
|
|
134
|
+
}
|
|
135
|
+
function withReference(props, name, childXml) {
|
|
136
|
+
const variant = referenceVariant(childXml);
|
|
137
|
+
let last = -1;
|
|
138
|
+
let replaced = false;
|
|
139
|
+
const children = props.children.map((child, index) => {
|
|
140
|
+
if (child.name !== name) return child;
|
|
141
|
+
last = index;
|
|
142
|
+
if (referenceVariant(child.xml) !== variant) return child;
|
|
143
|
+
replaced = true;
|
|
144
|
+
return { ...child, xml: childXml };
|
|
145
|
+
});
|
|
146
|
+
if (replaced) return { ...props, children };
|
|
147
|
+
if (last === -1) return setChild(props, name, childXml);
|
|
148
|
+
children.splice(last + 1, 0, { name, xml: childXml });
|
|
149
|
+
return { ...props, children };
|
|
150
|
+
}
|
|
151
|
+
function setSectionChild(sectPrXml, name, childXml) {
|
|
152
|
+
const start = rootTagAt(sectPrXml);
|
|
153
|
+
if (start === -1) return sectPrXml;
|
|
154
|
+
const gap = sectPrXml.slice(0, start);
|
|
155
|
+
const props = parseProps(sectPrXml.slice(start));
|
|
156
|
+
if (props === null) return sectPrXml;
|
|
157
|
+
const written = childXml !== null && REFERENCE_CHILDREN.includes(name) ? withReference(props, name, childXml) : setChild(props, name, childXml);
|
|
158
|
+
return gap + renderElement(written);
|
|
159
|
+
}
|
|
160
|
+
function sectionsOf(doc) {
|
|
161
|
+
const sections = [];
|
|
162
|
+
let firstBlock = 0;
|
|
163
|
+
doc.forEach((child, offset, index) => {
|
|
164
|
+
const brk = sectionBreakOf(pPrText(child));
|
|
165
|
+
if (brk === null) return;
|
|
166
|
+
sections.push({
|
|
167
|
+
index: sections.length,
|
|
168
|
+
firstBlock,
|
|
169
|
+
lastBlock: index,
|
|
170
|
+
anchor: { kind: "paragraph", pos: offset },
|
|
171
|
+
props: parseSectionProperties(brk) ?? { ...DEFAULT_SECTION, xml: brk }
|
|
172
|
+
});
|
|
173
|
+
firstBlock = index + 1;
|
|
174
|
+
});
|
|
175
|
+
const body = doc.attrs.sectPr;
|
|
176
|
+
const xml = typeof body === "string" ? body : null;
|
|
177
|
+
sections.push({
|
|
178
|
+
index: sections.length,
|
|
179
|
+
firstBlock,
|
|
180
|
+
lastBlock: doc.childCount - 1,
|
|
181
|
+
anchor: { kind: "body" },
|
|
182
|
+
props: xml === null ? DEFAULT_SECTION : parseSectionProperties(xml) ?? { ...DEFAULT_SECTION, xml }
|
|
183
|
+
});
|
|
184
|
+
return sections;
|
|
185
|
+
}
|
|
186
|
+
function sectionIn(sections, doc, pos) {
|
|
187
|
+
const index = doc.resolve(Math.min(Math.max(pos, 0), doc.content.size)).index(0);
|
|
188
|
+
return sections.find((section) => index <= section.lastBlock) ?? sections[sections.length - 1];
|
|
189
|
+
}
|
|
190
|
+
function sectionAt(doc, pos) {
|
|
191
|
+
return sectionIn(sectionsOf(doc), doc, pos);
|
|
192
|
+
}
|
|
193
|
+
export {
|
|
194
|
+
DEFAULT_SECTION,
|
|
195
|
+
HEADER_FOOTER_VARIANTS,
|
|
196
|
+
firstSectPrElement,
|
|
197
|
+
parseSectionProperties,
|
|
198
|
+
readSectionProperties,
|
|
199
|
+
sectionAt,
|
|
200
|
+
sectionBreakOf,
|
|
201
|
+
sectionIn,
|
|
202
|
+
sectionsOf,
|
|
203
|
+
setSectionChild,
|
|
204
|
+
storyReferenceIds,
|
|
205
|
+
withSectionBreak,
|
|
206
|
+
withoutSectionBreak
|
|
207
|
+
};
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { Node as PMNode } from "prosemirror-model";
|
|
2
2
|
import { type ExportRefs } from "./exportRefs";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export declare function serializeBlock(node: PMNode, refs?: ExportRefs): string;
|
|
4
|
+
/**
|
|
5
|
+
* An unchanged block is written back as its original XML; only a changed block is rebuilt.
|
|
6
|
+
*
|
|
7
|
+
* Unchanged is judged by `sameSource` rather than by `Node.eq`, because opening a file works the
|
|
8
|
+
* display attrs out again (`schema/attrRoles`) and a block rebuilt over that would lose the markup
|
|
9
|
+
* the writer does not model, the properties of a cell continuing a vertical merge among it.
|
|
10
|
+
*
|
|
11
|
+
* `onlyBlock` says the block stands alone in its story. An original of empty XML belongs to the
|
|
12
|
+
* editable placeholder a story with no block of its own opens with; once another block joins it,
|
|
13
|
+
* that paragraph stands for a real blank line and has to be written.
|
|
14
|
+
*/
|
|
15
|
+
export declare function blockXml(node: PMNode, refs: ExportRefs, onlyBlock: boolean): string;
|
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
// src/docx/serializeBlock.ts
|
|
2
2
|
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import { sameSource } from "../schema/sourceEquality.js";
|
|
3
4
|
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
4
|
-
import {
|
|
5
|
+
import { serializeParagraph } from "./serializeParagraph.js";
|
|
6
|
+
import { serializePreservedBlock } from "./serializePreserved.js";
|
|
5
7
|
import { serializeTable } from "./serializeTable.js";
|
|
6
|
-
import { originalBlock
|
|
7
|
-
function
|
|
8
|
-
const srcId = node.attrs.srcId;
|
|
9
|
-
const key = typeof srcId === "string" ? splitBlockKey(srcId) : null;
|
|
10
|
-
return key === null || key.sessionId === session.sessionId ? "a preserved block has lost its original XML" : `a preserved block comes from another document (${key.sessionId})`;
|
|
11
|
-
}
|
|
12
|
-
function serializeRaw(node, session) {
|
|
13
|
-
const imported = originalBlock(node, session);
|
|
14
|
-
if (!imported) {
|
|
15
|
-
throw new DocxExportError("lost-original", lostOriginal(node, session));
|
|
16
|
-
}
|
|
17
|
-
return imported.xml;
|
|
18
|
-
}
|
|
19
|
-
function serializeBlock(node, session, refs = NO_EXPORT_REFS) {
|
|
8
|
+
import { originalBlock } from "./session.js";
|
|
9
|
+
function serializeBlock(node, refs = NO_EXPORT_REFS) {
|
|
20
10
|
if (node.type.name === "paragraph") return serializeParagraph(node, refs);
|
|
21
11
|
if (node.type.name === "table") return serializeTable(node, refs);
|
|
22
|
-
if (node.type.
|
|
23
|
-
|
|
12
|
+
if (node.type.isInGroup("preserved")) {
|
|
13
|
+
return serializePreservedBlock(node, refs);
|
|
14
|
+
}
|
|
24
15
|
throw new DocxExportError(
|
|
25
16
|
"unsupported-content",
|
|
26
17
|
`block we cannot serialize: ${node.type.name}`
|
|
27
18
|
);
|
|
28
19
|
}
|
|
20
|
+
function blockXml(node, refs, onlyBlock) {
|
|
21
|
+
const imported = refs.session === null ? void 0 : originalBlock(node, refs.session);
|
|
22
|
+
if (imported && (imported.xml !== "" || onlyBlock) && sameSource(node, imported.node)) {
|
|
23
|
+
return imported.xml;
|
|
24
|
+
}
|
|
25
|
+
return serializeBlock(node, refs);
|
|
26
|
+
}
|
|
29
27
|
export {
|
|
30
|
-
|
|
28
|
+
blockXml,
|
|
31
29
|
serializeBlock
|
|
32
30
|
};
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Body paragraphs and paragraphs inside table cells take the same path.
|
|
5
5
|
*
|
|
6
|
+
* A fragment the reader kept whole goes back where it stood: one kept inside a run is written as
|
|
7
|
+
* a piece of that run, and one kept beside the runs is written between them, which is what the
|
|
8
|
+
* two content models admit.
|
|
9
|
+
*
|
|
6
10
|
* The inlines are grouped three times over: neighbours that share their formatting become one run,
|
|
7
11
|
* the runs that share a hyperlink (`w:hyperlink`) go back inside it, and the links and runs that
|
|
8
12
|
* share a content control (`w:sdt`) go back inside the wrapper that mark carries. The control is
|
|
@@ -65,6 +65,7 @@ function renderInline(node, images) {
|
|
|
65
65
|
}
|
|
66
66
|
if (node.type.name === "hardBreak")
|
|
67
67
|
return emptyTagXml(wName("br"), rawAttrsOf(node.attrs.brAttrs));
|
|
68
|
+
if (node.type.name === "rawRunContent") return preservedXml(node);
|
|
68
69
|
if (node.type.name === "image") return renderImage(node, images);
|
|
69
70
|
if (node.type.name === "commentReference") {
|
|
70
71
|
const original = node.attrs.referenceXml;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a block the editor never modelled goes back out, wherever in the document it stands.
|
|
3
|
+
*
|
|
4
|
+
* A block opened under the body names the fragment it stands for and the session holds the bytes;
|
|
5
|
+
* one opened inside a table cell was never a fragment of its own and carries its XML along. An
|
|
6
|
+
* edit may move either into the other's place, so the body writer and the cell writer read both
|
|
7
|
+
* the same way, from here.
|
|
8
|
+
*/
|
|
9
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
10
|
+
import type { ExportRefs } from "./exportRefs";
|
|
11
|
+
import { type SessionStore } from "./session";
|
|
12
|
+
/** Why there is no original to write, which a block that came in from another document answers differently */
|
|
13
|
+
export declare function lostOriginal(node: PMNode, session: SessionStore | null): string;
|
|
14
|
+
export declare function serializePreservedBlock(node: PMNode, refs: ExportRefs): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/docx/serializePreserved.ts
|
|
2
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import { originalBlock, splitBlockKey } from "./session.js";
|
|
4
|
+
function lostOriginal(node, session) {
|
|
5
|
+
const srcId = node.attrs.srcId;
|
|
6
|
+
const key = typeof srcId === "string" ? splitBlockKey(srcId) : null;
|
|
7
|
+
return key === null || key.sessionId === session?.sessionId ? "a preserved block has lost its original XML" : `a preserved block comes from another document (${key.sessionId})`;
|
|
8
|
+
}
|
|
9
|
+
function serializePreservedBlock(node, refs) {
|
|
10
|
+
const xml = node.attrs.xml;
|
|
11
|
+
if (typeof xml === "string") return xml;
|
|
12
|
+
const imported = refs.session ? originalBlock(node, refs.session) : void 0;
|
|
13
|
+
if (!imported) {
|
|
14
|
+
throw new DocxExportError(
|
|
15
|
+
"lost-original",
|
|
16
|
+
lostOriginal(node, refs.session)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return imported.xml;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
lostOriginal,
|
|
23
|
+
serializePreservedBlock
|
|
24
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes a side story back into the element it stands in.
|
|
3
|
+
*
|
|
4
|
+
* The rule is the body's rule (`./serializeBlock`): a story nobody edited goes back out as the
|
|
5
|
+
* bytes it arrived as, and one that was edited is written block by block, each untouched block
|
|
6
|
+
* still verbatim. That is what keeps a comment's second paragraph and its run formatting through
|
|
7
|
+
* an edit of the first, and what keeps an untouched Comments part byte-identical.
|
|
8
|
+
*/
|
|
9
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
10
|
+
import type { ExportRefs } from "./exportRefs";
|
|
11
|
+
import type { ImportedStory } from "./story";
|
|
12
|
+
/** The two ends of the element a story stands in, for a story the package did not arrive holding */
|
|
13
|
+
export interface StoryContainer {
|
|
14
|
+
open: string;
|
|
15
|
+
close: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function serializeStory(current: PMNode, imported: ImportedStory | null, container: StoryContainer, refs: ExportRefs): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/docx/serializeStory.ts
|
|
2
|
+
import { sameSource } from "../schema/sourceEquality.js";
|
|
3
|
+
import { blockXml } from "./serializeBlock.js";
|
|
4
|
+
function serializeStory(current, imported, container, refs) {
|
|
5
|
+
const ends = imported ?? container;
|
|
6
|
+
if (imported && sameSource(current, imported.doc)) {
|
|
7
|
+
return ends.open + imported.blocks.map((block) => block.xml).join("") + ends.close;
|
|
8
|
+
}
|
|
9
|
+
const pieces = [];
|
|
10
|
+
current.forEach((block) => {
|
|
11
|
+
pieces.push(blockXml(block, refs, current.childCount === 1));
|
|
12
|
+
});
|
|
13
|
+
return ends.open + pieces.join("") + ends.close;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
serializeStory
|
|
17
|
+
};
|
|
@@ -17,11 +17,8 @@ import {
|
|
|
17
17
|
} from "../ooxml/props.js";
|
|
18
18
|
import { ST_MeasurementOrPercent, ST_TwipsMeasure } from "../ooxml/simpleTypes.js";
|
|
19
19
|
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
rawAttrsOf,
|
|
23
|
-
serializeParagraph
|
|
24
|
-
} from "./serializeParagraph.js";
|
|
20
|
+
import { rawAttrsOf, serializeParagraph } from "./serializeParagraph.js";
|
|
21
|
+
import { serializePreservedBlock } from "./serializePreserved.js";
|
|
25
22
|
function propsOf(xml, tag) {
|
|
26
23
|
if (typeof xml !== "string") return { tag, attrs: null, children: [] };
|
|
27
24
|
const parsed = parseProps(xml);
|
|
@@ -99,12 +96,12 @@ function cellPropsXml(cell, role) {
|
|
|
99
96
|
}
|
|
100
97
|
function cellBlockXml(block, refs) {
|
|
101
98
|
if (block.type.name === "paragraph") return serializeParagraph(block, refs);
|
|
102
|
-
if (block.type.name === "rawBlock") return preservedXml(block);
|
|
103
99
|
if (block.type.name === "table") return serializeTable(block, refs);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
return serializePreservedBlock(block, refs);
|
|
101
|
+
}
|
|
102
|
+
function markerXml(node, attr) {
|
|
103
|
+
const xml = node.attrs[attr];
|
|
104
|
+
return typeof xml === "string" ? xml : "";
|
|
108
105
|
}
|
|
109
106
|
function wrapInSdt(xml, cell, role) {
|
|
110
107
|
const prefix = cell.attrs.sdtPrefix;
|
|
@@ -114,7 +111,7 @@ function wrapInSdt(xml, cell, role) {
|
|
|
114
111
|
function cellXml(cell, role, refs) {
|
|
115
112
|
const body = role === "continue" ? elementXml(wName("p"), []) : cell.children.map((block) => cellBlockXml(block, refs)).join("");
|
|
116
113
|
const xml = openTagXml(wName("tc"), rawAttrsOf(cell.attrs.tcAttrs)) + cellPropsXml(cell, role) + body + "</w:tc>";
|
|
117
|
-
return wrapInSdt(xml, cell, role);
|
|
114
|
+
return wrapInSdt(xml, cell, role) + (role === "start" ? markerXml(cell, "trailingXml") : "");
|
|
118
115
|
}
|
|
119
116
|
function placeRow(row, covering) {
|
|
120
117
|
const placed = [];
|
|
@@ -147,7 +144,7 @@ function rowXml(row, covering, refs) {
|
|
|
147
144
|
const cells = placeRow(row, covering).map((placed) => cellXml(placed.cell, placed.role, refs)).join("");
|
|
148
145
|
const tblPrEx = row.attrs.tblPrEx;
|
|
149
146
|
const trPr = row.attrs.trPr;
|
|
150
|
-
return openTagXml(wName("tr"), rawAttrsOf(row.attrs.trAttrs)) + (typeof tblPrEx === "string" ? tblPrEx : "") + (typeof trPr === "string" ? trPr : "") + cells + "</w:tr>";
|
|
147
|
+
return openTagXml(wName("tr"), rawAttrsOf(row.attrs.trAttrs)) + (typeof tblPrEx === "string" ? tblPrEx : "") + (typeof trPr === "string" ? trPr : "") + markerXml(row, "leadingXml") + cells + "</w:tr>" + markerXml(row, "trailingXml");
|
|
151
148
|
}
|
|
152
149
|
function serializeTable(table, refs = NO_EXPORT_REFS) {
|
|
153
150
|
const covering = [];
|
|
@@ -158,7 +155,7 @@ function serializeTable(table, refs = NO_EXPORT_REFS) {
|
|
|
158
155
|
"a vertical merge in the table reaches past the last row"
|
|
159
156
|
);
|
|
160
157
|
}
|
|
161
|
-
return openTagXml(wName("tbl"), rawAttrsOf(table.attrs.tblAttrs)) + tablePropsXml(table) + tableGridXml(table) + rows + "</w:tbl>";
|
|
158
|
+
return openTagXml(wName("tbl"), rawAttrsOf(table.attrs.tblAttrs)) + tablePropsXml(table) + tableGridXml(table) + markerXml(table, "leadingXml") + rows + "</w:tbl>";
|
|
162
159
|
}
|
|
163
160
|
export {
|
|
164
161
|
serializeTable
|