@portone/docx-editor 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +201 -0
- package/CONTRIBUTING.md +2 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +44 -25
- 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 +65 -28
- package/dist/docx/exportDocx.js +10 -14
- 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/styles.d.ts +0 -2
- package/dist/docx/formatting/styles.js +2 -15
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/identities.js +17 -26
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +100 -48
- package/dist/docx/importParagraph.d.ts +18 -5
- package/dist/docx/importParagraph.js +75 -128
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +195 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.js +56 -12
- package/dist/docx/invariants.js +16 -18
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- 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 +9 -4
- package/dist/docx/serializeParagraph.js +23 -58
- 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 +23 -6
- package/dist/docx/session.js +26 -5
- 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/tableTemplate.d.ts +25 -0
- package/dist/docx/tableTemplate.js +30 -16
- package/dist/docx/wrappers.d.ts +67 -0
- package/dist/docx/wrappers.js +117 -0
- package/dist/editor/clipboard/blockReaders.d.ts +74 -0
- package/dist/editor/clipboard/blockReaders.js +193 -0
- package/dist/editor/clipboard/htmlReader.d.ts +26 -0
- package/dist/editor/clipboard/htmlReader.js +376 -0
- package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
- package/dist/editor/clipboard/inlineFormatting.js +1 -7
- 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 +188 -0
- package/dist/editor/clipboard/readContext.d.ts +34 -0
- package/dist/editor/clipboard/readContext.js +25 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/clipboard/source.d.ts +12 -0
- package/dist/editor/clipboard/source.js +21 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +177 -106
- 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/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/linkCommands.js +20 -5
- package/dist/editor/commands/listCommands.d.ts +6 -0
- package/dist/editor/commands/listCommands.js +3 -2
- package/dist/editor/commands/lockCommands.js +59 -16
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +2 -2
- package/dist/editor/createEditor.js +13 -3
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- 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/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 +15 -8
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- 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 +6 -0
- package/dist/ooxml/names.js +2 -0
- package/dist/ooxml/props.d.ts +10 -0
- package/dist/ooxml/props.js +28 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/page/PageGuides.d.ts +7 -3
- package/dist/page/PageGuides.js +13 -24
- package/dist/page/pageLayout.d.ts +64 -5
- package/dist/page/pageLayout.js +90 -29
- package/dist/page/usePageLayout.d.ts +14 -4
- package/dist/page/usePageLayout.js +19 -20
- package/dist/schema/attrRoles.js +46 -23
- package/dist/schema/clipboard.d.ts +72 -0
- package/dist/schema/clipboard.js +288 -0
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +217 -165
- 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/locks.d.ts +3 -0
- package/dist/schema/locks.js +29 -12
- 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.d.ts +14 -0
- package/dist/schema/rendering.js +27 -12
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/schema/wrappers.d.ts +58 -0
- package/dist/schema/wrappers.js +77 -0
- package/dist/styles/classNames.d.ts +3 -5
- package/dist/styles/classNames.js +3 -5
- package/dist/styles.css +74 -12
- 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 +5 -4
- package/dist/editor/externalClipboard.d.ts +0 -22
- package/dist/editor/externalClipboard.js +0 -486
package/dist/docx/importDocx.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/docx/importDocx.ts
|
|
2
|
-
import { Fragment } from "prosemirror-model";
|
|
3
2
|
import {
|
|
4
3
|
bindsWritingPrefix,
|
|
5
4
|
conformanceOf,
|
|
@@ -16,7 +15,10 @@ import {
|
|
|
16
15
|
withXmlParser
|
|
17
16
|
} from "../ooxml/xml.js";
|
|
18
17
|
import { docxSchema } from "../schema/index.js";
|
|
19
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
commentReferencesIn,
|
|
20
|
+
readComments
|
|
21
|
+
} from "./comments.js";
|
|
20
22
|
import { openParts } from "./container.js";
|
|
21
23
|
import {
|
|
22
24
|
DEFAULT_TAB_STOP_PT,
|
|
@@ -28,26 +30,35 @@ import {
|
|
|
28
30
|
formattingContextOf,
|
|
29
31
|
NO_DOCUMENT_DEFAULTS,
|
|
30
32
|
readDocumentDefaults,
|
|
31
|
-
readParagraphStyles
|
|
32
|
-
styledParagraph
|
|
33
|
+
readParagraphStyles
|
|
33
34
|
} from "./formatting.js";
|
|
34
|
-
import {
|
|
35
|
+
import { readHeaderFooterStories } from "./headersFooters.js";
|
|
35
36
|
import { readLinkTargets } from "./hyperlink.js";
|
|
36
|
-
import {
|
|
37
|
-
import { buildTable } from "./importTable.js";
|
|
37
|
+
import { NO_IMPORT_SOURCES } from "./importParagraph.js";
|
|
38
38
|
import { readImageSources } from "./media.js";
|
|
39
39
|
import { NUMBERING_REL_TYPE } from "./newLists.js";
|
|
40
40
|
import { readNotes } from "./notes.js";
|
|
41
41
|
import { readPart, relatedPartPath } from "./packageParts.js";
|
|
42
|
-
import {
|
|
42
|
+
import { A4_PORTRAIT } from "./pageGeometry.js";
|
|
43
43
|
import { readRelationships } from "./relationships.js";
|
|
44
44
|
import { scanBody } from "./scan.js";
|
|
45
|
+
import {
|
|
46
|
+
firstSectPrElement,
|
|
47
|
+
readSectionProperties,
|
|
48
|
+
storyReferenceIds
|
|
49
|
+
} from "./sections.js";
|
|
45
50
|
import {
|
|
46
51
|
BODY_STORY_KEY,
|
|
47
52
|
blockKey,
|
|
48
53
|
newSessionId,
|
|
49
54
|
SessionStore
|
|
50
55
|
} from "./session.js";
|
|
56
|
+
import {
|
|
57
|
+
buildBlock,
|
|
58
|
+
readStories,
|
|
59
|
+
storiesByKey,
|
|
60
|
+
withStyleFormats
|
|
61
|
+
} from "./story.js";
|
|
51
62
|
import { NO_THEME_FONTS, readThemeFonts } from "./theme.js";
|
|
52
63
|
var OFFICE_DOCUMENT_REL = `${R_NS}/officeDocument`;
|
|
53
64
|
var STYLES_REL = `${R_NS}/styles`;
|
|
@@ -92,36 +103,6 @@ function assertWritableMainPart(root) {
|
|
|
92
103
|
);
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
|
-
function buildBlock(el, srcId, sources, context) {
|
|
96
|
-
if (el.localName === "bookmarkStart" || el.localName === "bookmarkEnd") {
|
|
97
|
-
return docxSchema.nodes.bookmarkBlock.create({
|
|
98
|
-
srcId,
|
|
99
|
-
name: el.nodeName
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
if (el.localName === "p") {
|
|
103
|
-
const paragraph = buildParagraph(el, srcId, sources);
|
|
104
|
-
if (paragraph) return paragraph;
|
|
105
|
-
}
|
|
106
|
-
if (el.localName === "tbl") {
|
|
107
|
-
const table = buildTable(el, srcId, sources, context);
|
|
108
|
-
if (table) return table;
|
|
109
|
-
}
|
|
110
|
-
return docxSchema.nodes.docxRaw.create({ srcId, name: el.nodeName });
|
|
111
|
-
}
|
|
112
|
-
function withStyleFormats(node, context) {
|
|
113
|
-
if (node.type === docxSchema.nodes.paragraph) {
|
|
114
|
-
return styledParagraph(node, context);
|
|
115
|
-
}
|
|
116
|
-
if (node.childCount === 0 || node.type === docxSchema.nodes.table) {
|
|
117
|
-
return node;
|
|
118
|
-
}
|
|
119
|
-
const children = node.children.map(
|
|
120
|
-
(child) => withStyleFormats(child, context)
|
|
121
|
-
);
|
|
122
|
-
if (children.every((child, i) => child === node.child(i))) return node;
|
|
123
|
-
return node.copy(Fragment.fromArray(children));
|
|
124
|
-
}
|
|
125
106
|
function assertScanMatchesDom(children, scan) {
|
|
126
107
|
if (children.length !== scan.blocks.length) {
|
|
127
108
|
throw new DocxImportError(
|
|
@@ -138,16 +119,46 @@ function assertScanMatchesDom(children, scan) {
|
|
|
138
119
|
}
|
|
139
120
|
});
|
|
140
121
|
}
|
|
141
|
-
function
|
|
122
|
+
function splitTrailingSectPr(scan) {
|
|
142
123
|
const last = scan.blocks.at(-1);
|
|
143
|
-
if (
|
|
144
|
-
|
|
124
|
+
if (!last || localPart(last.name) !== "sectPr") {
|
|
125
|
+
return { ...scan, sectPr: null };
|
|
126
|
+
}
|
|
145
127
|
return {
|
|
146
128
|
prefix: scan.prefix,
|
|
147
129
|
blocks: scan.blocks.slice(0, -1),
|
|
148
|
-
suffix:
|
|
130
|
+
suffix: scan.suffix,
|
|
131
|
+
sectPr: last.xml
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function storyDeps(parts, partPath, sessionId, formatting) {
|
|
135
|
+
return {
|
|
136
|
+
session: { sessionId },
|
|
137
|
+
sources: {
|
|
138
|
+
...NO_IMPORT_SOURCES,
|
|
139
|
+
images: readImageSources(parts, partPath),
|
|
140
|
+
links: readLinkTargets(parts, partPath),
|
|
141
|
+
themeFonts: formatting.themeFonts
|
|
142
|
+
},
|
|
143
|
+
formatting
|
|
149
144
|
};
|
|
150
145
|
}
|
|
146
|
+
function readCommentStories(comments, parts, sessionId, formatting) {
|
|
147
|
+
const { partPath, xml } = comments;
|
|
148
|
+
if (partPath === null || xml === null) return [];
|
|
149
|
+
return readStories(
|
|
150
|
+
{ kind: "comment", partPath, xml, entryName: "comment", idAttr: "id" },
|
|
151
|
+
storyDeps(parts, partPath, sessionId, formatting)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
function readNoteStories(part, kind, parts, sessionId, formatting) {
|
|
155
|
+
const { partPath, xml } = part;
|
|
156
|
+
if (partPath === null || xml === null) return [];
|
|
157
|
+
return readStories(
|
|
158
|
+
{ kind, partPath, xml, entryName: kind, idAttr: "id" },
|
|
159
|
+
storyDeps(parts, partPath, sessionId, formatting)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
151
162
|
function importDocx(input, options) {
|
|
152
163
|
return withXmlParser(options?.xmlParser, () => readDocx(input));
|
|
153
164
|
}
|
|
@@ -169,8 +180,10 @@ function readDocx(input) {
|
|
|
169
180
|
}
|
|
170
181
|
const children = elementChildren(body);
|
|
171
182
|
assertScanMatchesDom(children, scanned);
|
|
172
|
-
const
|
|
173
|
-
const
|
|
183
|
+
const firstSectPr = firstSectPrElement(body);
|
|
184
|
+
const firstSection = firstSectPr ? readSectionProperties(firstSectPr) : null;
|
|
185
|
+
const geometry = firstSection?.geometry ?? A4_PORTRAIT;
|
|
186
|
+
const scan = splitTrailingSectPr(scanned);
|
|
174
187
|
const blockElements = children.slice(0, scan.blocks.length);
|
|
175
188
|
const stylesXml = readPart(
|
|
176
189
|
parts,
|
|
@@ -214,7 +227,6 @@ function readDocx(input) {
|
|
|
214
227
|
labels.set(id, label);
|
|
215
228
|
return label;
|
|
216
229
|
};
|
|
217
|
-
const headersFooters = readHeadersFooters(parts, mainPartPath, body);
|
|
218
230
|
const sources = {
|
|
219
231
|
images: readImageSources(parts, mainPartPath),
|
|
220
232
|
themeFonts,
|
|
@@ -224,6 +236,24 @@ function readDocx(input) {
|
|
|
224
236
|
noteLabel
|
|
225
237
|
};
|
|
226
238
|
const sessionId = newSessionId();
|
|
239
|
+
const headerFooters = readHeaderFooterStories(
|
|
240
|
+
parts,
|
|
241
|
+
mainPartPath,
|
|
242
|
+
storyReferenceIds(body),
|
|
243
|
+
(partPath) => storyDeps(parts, partPath, sessionId, formatting)
|
|
244
|
+
);
|
|
245
|
+
const stories = storiesByKey([
|
|
246
|
+
...readCommentStories(comments, parts, sessionId, formatting),
|
|
247
|
+
...readNoteStories(
|
|
248
|
+
notes.footnotes,
|
|
249
|
+
"footnote",
|
|
250
|
+
parts,
|
|
251
|
+
sessionId,
|
|
252
|
+
formatting
|
|
253
|
+
),
|
|
254
|
+
...readNoteStories(notes.endnotes, "endnote", parts, sessionId, formatting),
|
|
255
|
+
...headerFooters.stories
|
|
256
|
+
]);
|
|
227
257
|
const blockNodes = blockElements.map(
|
|
228
258
|
(el, i) => withStyleFormats(
|
|
229
259
|
buildBlock(
|
|
@@ -235,7 +265,25 @@ function readDocx(input) {
|
|
|
235
265
|
formatting
|
|
236
266
|
)
|
|
237
267
|
);
|
|
238
|
-
|
|
268
|
+
if (blockNodes.length === 0 && scan.sectPr !== null) {
|
|
269
|
+
blockNodes.push(
|
|
270
|
+
withStyleFormats(
|
|
271
|
+
docxSchema.nodes.paragraph.create({
|
|
272
|
+
srcId: blockKey({ sessionId }, BODY_STORY_KEY, 0)
|
|
273
|
+
}),
|
|
274
|
+
formatting
|
|
275
|
+
)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const doc = docxSchema.nodes.doc.create(
|
|
279
|
+
{
|
|
280
|
+
sectPr: scan.sectPr,
|
|
281
|
+
stories: Object.fromEntries(
|
|
282
|
+
Array.from(stories, ([key, story]) => [key, story.doc.toJSON()])
|
|
283
|
+
)
|
|
284
|
+
},
|
|
285
|
+
blockNodes
|
|
286
|
+
);
|
|
239
287
|
return {
|
|
240
288
|
doc,
|
|
241
289
|
notes: fidelityNotesOf(doc, mainPartPath),
|
|
@@ -246,7 +294,10 @@ function readDocx(input) {
|
|
|
246
294
|
documentPrefix: scan.prefix,
|
|
247
295
|
documentSuffix: scan.suffix,
|
|
248
296
|
documentHadBom: hadBom,
|
|
249
|
-
blocks: blockNodes.map((node, i) => ({
|
|
297
|
+
blocks: blockNodes.map((node, i) => ({
|
|
298
|
+
xml: scan.blocks[i]?.xml ?? "",
|
|
299
|
+
node
|
|
300
|
+
})),
|
|
250
301
|
defaults: stylesDom ? readDocumentDefaults(stylesDom, themeFonts) : NO_DOCUMENT_DEFAULTS,
|
|
251
302
|
defaultTabStopPt,
|
|
252
303
|
geometry,
|
|
@@ -256,7 +307,8 @@ function readDocx(input) {
|
|
|
256
307
|
numberingPartPath,
|
|
257
308
|
comments,
|
|
258
309
|
commentReferenceIds: new Set(commentReferencesIn(doc).keys()),
|
|
259
|
-
|
|
310
|
+
headerFooterStories: headerFooters.refs,
|
|
311
|
+
stories
|
|
260
312
|
})
|
|
261
313
|
};
|
|
262
314
|
}
|
|
@@ -5,11 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
* A `w:sdt` content control and a `w:hyperlink` standing inside the paragraph are both unwrapped, so
|
|
7
7
|
* the text they hold stays editable, and the wrapper each came in rides along on a mark to go back
|
|
8
|
-
* out around the same text.
|
|
9
|
-
*
|
|
8
|
+
* out around the same text. Either may hold the other and a control may hold a control: the nesting
|
|
9
|
+
* is read as the file wrote it and recorded on the marks (`docx/wrappers`). A link inside a link is
|
|
10
|
+
* the one arrangement no mark can record, and the inner one stays whole as it always has.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here demotes a paragraph. What the editor has no model for - a field character, a
|
|
13
|
+
* tracked insertion, a symbol, a drawing nobody could read - is kept where it stood, inside its
|
|
14
|
+
* run or beside it, by the rule `docx/importPolicy` gives it for the level it stands at. The
|
|
15
|
+
* paragraph around it stays editable, which is what a document full of `w:lastRenderedPageBreak`
|
|
16
|
+
* needs.
|
|
10
17
|
*/
|
|
11
18
|
import type { Node as PMNode } from "prosemirror-model";
|
|
12
|
-
import { type ImportedComments } from "./comments";
|
|
19
|
+
import { type ImportedComments } from "./comments/reading";
|
|
13
20
|
import { type LinkTargets } from "./hyperlink";
|
|
14
21
|
import type { ImageSources } from "./media";
|
|
15
22
|
import { type ImportedNotes, type NoteKind } from "./notes";
|
|
@@ -28,5 +35,11 @@ export interface ImportSources {
|
|
|
28
35
|
noteLabel: (kind: NoteKind, id: string) => string;
|
|
29
36
|
}
|
|
30
37
|
export declare const NO_IMPORT_SOURCES: ImportSources;
|
|
31
|
-
/**
|
|
32
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Moves a paragraph into an editable node.
|
|
40
|
+
*
|
|
41
|
+
* Every paragraph opens editable. A child the reader has no model for, and a wrapper whose shape
|
|
42
|
+
* it could not put back together, is kept whole where it stood instead of standing the whole
|
|
43
|
+
* paragraph down.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildParagraph(el: Element, srcId: string | null, sources?: ImportSources): PMNode;
|
|
@@ -9,20 +9,20 @@ import {
|
|
|
9
9
|
serializeXml
|
|
10
10
|
} from "../ooxml/xml.js";
|
|
11
11
|
import { docxSchema } from "../schema/index.js";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
importedCommentReplies,
|
|
15
|
-
NO_COMMENTS
|
|
16
|
-
} from "./comments.js";
|
|
12
|
+
import { commentParaId, importedCommentReplies } from "./comments/model.js";
|
|
13
|
+
import { NO_COMMENTS } from "./comments/reading.js";
|
|
17
14
|
import { readParagraphFormat, readRunFormat } from "./formatting.js";
|
|
15
|
+
import { NO_LINK_TARGETS } from "./hyperlink.js";
|
|
16
|
+
import { policyFor } from "./importPolicy.js";
|
|
18
17
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
buildPreservedInline,
|
|
19
|
+
buildPreservedRunContent,
|
|
20
|
+
preservationOf
|
|
21
|
+
} from "./importPreserved.js";
|
|
22
22
|
import { NO_IMAGES } from "./media.js";
|
|
23
23
|
import { NO_NOTES, noteById } from "./notes.js";
|
|
24
|
-
import { readSdtWrapper } from "./sdt.js";
|
|
25
24
|
import { NO_THEME_FONTS } from "./theme.js";
|
|
25
|
+
import { wrapperFits, wrapperKindFor } from "./wrappers.js";
|
|
26
26
|
function runMark(run, themeFonts) {
|
|
27
27
|
const rPr = childByLocalName(run, "rPr");
|
|
28
28
|
return docxSchema.marks.run.create({
|
|
@@ -47,7 +47,7 @@ function buildImage(el, marks, images) {
|
|
|
47
47
|
marks
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
|
-
function
|
|
50
|
+
function buildModelledRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
51
51
|
switch (el.localName) {
|
|
52
52
|
case "t": {
|
|
53
53
|
const text = el.textContent ?? "";
|
|
@@ -86,9 +86,6 @@ function buildRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
86
86
|
authorId: comment?.authorId ?? null,
|
|
87
87
|
initials: comment?.initials ?? null,
|
|
88
88
|
date: comment?.date ?? null,
|
|
89
|
-
text: comment?.text ?? "",
|
|
90
|
-
commentXml: comment?.xml ?? null,
|
|
91
|
-
imported: true,
|
|
92
89
|
paraId,
|
|
93
90
|
resolved: comment?.resolved ?? false,
|
|
94
91
|
extensionXml: comment?.extensionXml ?? null,
|
|
@@ -114,7 +111,6 @@ function buildRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
114
111
|
kind,
|
|
115
112
|
id,
|
|
116
113
|
label: note ? noteLabel(kind, id) : "?",
|
|
117
|
-
text: note?.text ?? "",
|
|
118
114
|
customMarkFollows,
|
|
119
115
|
referenceXml: serializeXml(el)
|
|
120
116
|
},
|
|
@@ -135,22 +131,30 @@ function buildRunNodes(run, images, themeFonts, comments, notes, noteLabel, wrap
|
|
|
135
131
|
);
|
|
136
132
|
const nodes = [];
|
|
137
133
|
for (const child of elementChildren(run)) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
child
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
134
|
+
const policy = policyFor(child, "r");
|
|
135
|
+
if (policy.tier === "model") {
|
|
136
|
+
if (child.localName === "rPr") continue;
|
|
137
|
+
const built = buildModelledRunChild(
|
|
138
|
+
child,
|
|
139
|
+
marks,
|
|
140
|
+
images,
|
|
141
|
+
comments,
|
|
142
|
+
notes,
|
|
143
|
+
noteLabel
|
|
144
|
+
);
|
|
145
|
+
if (built !== null) {
|
|
146
|
+
nodes.push(...built);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
nodes.push(
|
|
151
|
+
buildPreservedRunContent(child, marks, preservationOf(policy, "r"))
|
|
146
152
|
);
|
|
147
|
-
if (built === null) return null;
|
|
148
|
-
nodes.push(...built);
|
|
149
153
|
}
|
|
150
154
|
if (nodes.length === 0) {
|
|
151
155
|
return [
|
|
152
156
|
docxSchema.nodes.rawInline.create(
|
|
153
|
-
{ xml: serializeXml(run) },
|
|
157
|
+
{ xml: serializeXml(run), element: run.localName },
|
|
154
158
|
null,
|
|
155
159
|
// The wrappers have to close again around this XML on export, so their marks ride along here too
|
|
156
160
|
wrappers
|
|
@@ -159,20 +163,6 @@ function buildRunNodes(run, images, themeFonts, comments, notes, noteLabel, wrap
|
|
|
159
163
|
}
|
|
160
164
|
return nodes;
|
|
161
165
|
}
|
|
162
|
-
var controlCounts = /* @__PURE__ */ new WeakMap();
|
|
163
|
-
var linkCounts = /* @__PURE__ */ new WeakMap();
|
|
164
|
-
function nextKey(counts, el) {
|
|
165
|
-
const key = counts.get(el.ownerDocument) ?? 0;
|
|
166
|
-
counts.set(el.ownerDocument, key + 1);
|
|
167
|
-
return key;
|
|
168
|
-
}
|
|
169
|
-
var NOTHING_ON_SCREEN = [
|
|
170
|
-
"bookmarkStart",
|
|
171
|
-
"bookmarkEnd",
|
|
172
|
-
"proofErr",
|
|
173
|
-
"permStart",
|
|
174
|
-
"permEnd"
|
|
175
|
-
];
|
|
176
166
|
function annotationId(el) {
|
|
177
167
|
return Array.from(el.attributes).find((attribute) => attribute.localName === "id")?.value ?? null;
|
|
178
168
|
}
|
|
@@ -190,109 +180,66 @@ var NO_IMPORT_SOURCES = {
|
|
|
190
180
|
notes: NO_NOTES,
|
|
191
181
|
noteLabel: () => "?"
|
|
192
182
|
};
|
|
193
|
-
function
|
|
183
|
+
function buildModelledInline(child, sources, depth, wrappers) {
|
|
184
|
+
if (child.localName === "r") {
|
|
185
|
+
return buildRunNodes(
|
|
186
|
+
child,
|
|
187
|
+
sources.images,
|
|
188
|
+
sources.themeFonts,
|
|
189
|
+
sources.comments,
|
|
190
|
+
sources.notes,
|
|
191
|
+
sources.noteLabel,
|
|
192
|
+
wrappers
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const kind = wrapperKindFor(child);
|
|
196
|
+
if (kind) {
|
|
197
|
+
if (!wrapperFits(kind, wrappers)) return null;
|
|
198
|
+
const reading = kind.read(child, depth, sources);
|
|
199
|
+
if (!reading) return null;
|
|
200
|
+
return buildContent(reading.content, sources, depth + 1, [
|
|
201
|
+
...wrappers,
|
|
202
|
+
reading.mark
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
const commentMarker = commentRangeNode(child, wrappers);
|
|
206
|
+
return commentMarker === null ? null : [commentMarker];
|
|
207
|
+
}
|
|
208
|
+
function buildContent(content, sources, depth, wrappers) {
|
|
194
209
|
const nodes = [];
|
|
195
210
|
for (const child of elementChildren(content)) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
sources.noteLabel,
|
|
204
|
-
wrappers
|
|
205
|
-
);
|
|
206
|
-
if (built === null) return null;
|
|
207
|
-
nodes.push(...built);
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (child.localName === "hyperlink" && linkAllowed) {
|
|
211
|
-
const built = buildHyperlinkNodes(child, sources, wrappers);
|
|
212
|
-
if (built === null) return null;
|
|
213
|
-
nodes.push(...built);
|
|
214
|
-
continue;
|
|
211
|
+
const policy = policyFor(child, "wrapper");
|
|
212
|
+
if (policy.tier === "model") {
|
|
213
|
+
const built = buildModelledInline(child, sources, depth, wrappers);
|
|
214
|
+
if (built !== null) {
|
|
215
|
+
nodes.push(...built);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
215
218
|
}
|
|
216
|
-
const commentMarker = commentRangeNode(child, wrappers);
|
|
217
|
-
if (commentMarker) {
|
|
218
|
-
nodes.push(commentMarker);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
if (!NOTHING_ON_SCREEN.includes(child.localName)) return null;
|
|
222
219
|
nodes.push(
|
|
223
|
-
|
|
224
|
-
{ xml: serializeXml(child) },
|
|
225
|
-
null,
|
|
226
|
-
wrappers
|
|
227
|
-
)
|
|
220
|
+
buildPreservedInline(child, wrappers, preservationOf(policy, "wrapper"))
|
|
228
221
|
);
|
|
229
222
|
}
|
|
230
223
|
if (nodes.length === 0) return null;
|
|
231
224
|
return nodes;
|
|
232
225
|
}
|
|
233
|
-
function buildHyperlinkNodes(el, sources, wrappers) {
|
|
234
|
-
const wrapper = readHyperlinkWrapper(el);
|
|
235
|
-
if (!wrapper) return null;
|
|
236
|
-
const mark = docxSchema.marks.link.create({
|
|
237
|
-
linkPrefix: wrapper.prefix,
|
|
238
|
-
href: wrapper.relId === null ? null : sources.links.get(wrapper.relId) ?? null,
|
|
239
|
-
linkKey: nextKey(linkCounts, el)
|
|
240
|
-
});
|
|
241
|
-
return buildWrappedNodes(el, sources, [...wrappers, mark], false);
|
|
242
|
-
}
|
|
243
|
-
function buildSdtNodes(el, sources) {
|
|
244
|
-
const wrapper = readSdtWrapper(el);
|
|
245
|
-
if (!wrapper) return null;
|
|
246
|
-
const mark = docxSchema.marks.sdt.create({
|
|
247
|
-
sdtPrefix: wrapper.prefix,
|
|
248
|
-
sdtKey: nextKey(controlCounts, el),
|
|
249
|
-
contentsLocked: wrapper.contentsLocked,
|
|
250
|
-
deletionLocked: wrapper.deletionLocked
|
|
251
|
-
});
|
|
252
|
-
return buildWrappedNodes(wrapper.content, sources, [mark], true);
|
|
253
|
-
}
|
|
254
226
|
function buildParagraph(el, srcId, sources = NO_IMPORT_SOURCES) {
|
|
255
|
-
const { images, themeFonts } = sources;
|
|
256
227
|
const inline = [];
|
|
257
228
|
let pPrElement = null;
|
|
258
229
|
for (const child of elementChildren(el)) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
230
|
+
const policy = policyFor(child, "p");
|
|
231
|
+
if (policy.tier === "model") {
|
|
232
|
+
if (child.localName === "pPr") {
|
|
233
|
+
pPrElement = child;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const built = buildModelledInline(child, sources, 0, []);
|
|
237
|
+
if (built !== null) {
|
|
238
|
+
inline.push(...built);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
262
241
|
}
|
|
263
|
-
|
|
264
|
-
const runNodes = buildRunNodes(
|
|
265
|
-
child,
|
|
266
|
-
images,
|
|
267
|
-
themeFonts,
|
|
268
|
-
sources.comments,
|
|
269
|
-
sources.notes,
|
|
270
|
-
sources.noteLabel
|
|
271
|
-
);
|
|
272
|
-
if (runNodes === null) return null;
|
|
273
|
-
inline.push(...runNodes);
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
if (child.localName === "sdt") {
|
|
277
|
-
const sdtNodes = buildSdtNodes(child, sources);
|
|
278
|
-
if (sdtNodes === null) return null;
|
|
279
|
-
inline.push(...sdtNodes);
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (child.localName === "hyperlink") {
|
|
283
|
-
const linkNodes = buildHyperlinkNodes(child, sources, []);
|
|
284
|
-
if (linkNodes === null) return null;
|
|
285
|
-
inline.push(...linkNodes);
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
const commentMarker = commentRangeNode(child);
|
|
289
|
-
if (commentMarker) {
|
|
290
|
-
inline.push(commentMarker);
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
inline.push(
|
|
294
|
-
docxSchema.nodes.rawInline.create({ xml: serializeXml(child) })
|
|
295
|
-
);
|
|
242
|
+
inline.push(buildPreservedInline(child, [], preservationOf(policy, "p")));
|
|
296
243
|
}
|
|
297
244
|
return docxSchema.nodes.paragraph.create(
|
|
298
245
|
{
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every WordprocessingML element the readers may meet is worth keeping as, one sub-table per
|
|
3
|
+
* level of the document.
|
|
4
|
+
*
|
|
5
|
+
* A reader used to know a handful of elements and answer `null` for the rest, and that `null`
|
|
6
|
+
* travelled up through the run and the wrapper until a whole paragraph stood as a placeholder. The
|
|
7
|
+
* table below turns that around: every level has a narrowest preservation of its own - a run child
|
|
8
|
+
* stays inside its run, a paragraph child inside its paragraph, a block stays a block - so an
|
|
9
|
+
* element nobody modelled costs the element and nothing around it.
|
|
10
|
+
*
|
|
11
|
+
* Only `tbl` and `tr` have no node to hold a stranger (`table` holds rows, `tableRow` holds
|
|
12
|
+
* cells), so those two levels demote the table instead, and that is the whole of what demotion is
|
|
13
|
+
* now: this default and a structure a reader could not take apart.
|
|
14
|
+
*
|
|
15
|
+
* A level is a content model of `wml.xsd` rather than an element name, because the same name means
|
|
16
|
+
* different things in different places: `sdt` is a control the paragraph reader unwraps, a cell
|
|
17
|
+
* wrapper under `tr`, a block placeholder under the body, and a row wrapper nothing reads. Keys are
|
|
18
|
+
* Clark names (`{namespace}localName`) so that `m:oMath` and `w:sdt` sit in one map.
|
|
19
|
+
*/
|
|
20
|
+
import type { PreservedDisplay } from "../schema";
|
|
21
|
+
export type { PreservedDisplay };
|
|
22
|
+
export type PreservationTier =
|
|
23
|
+
/** A reader turns it into an editable node */
|
|
24
|
+
"model"
|
|
25
|
+
/** A range marker: invisible, and its pair and order must survive every edit */
|
|
26
|
+
| "marker"
|
|
27
|
+
/** A trace the producer regenerates: invisible, and an edit may drop it */
|
|
28
|
+
| "ignorable"
|
|
29
|
+
/** A `CT_R` child: kept verbatim inside its run, wearing the run mark */
|
|
30
|
+
| "runContent"
|
|
31
|
+
/** A paragraph child: kept verbatim inside the paragraph */
|
|
32
|
+
| "inline"
|
|
33
|
+
/** A body or cell child: kept verbatim as a block placeholder */
|
|
34
|
+
| "block";
|
|
35
|
+
export interface PreservationRule {
|
|
36
|
+
readonly tier: Exclude<PreservationTier, "model">;
|
|
37
|
+
readonly display: PreservedDisplay;
|
|
38
|
+
/** The character a `text` display puts on screen */
|
|
39
|
+
readonly text?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the deletion guard answers for this element: a range marker whose pair must survive,
|
|
42
|
+
* and a field piece whose begin/separate/end order must survive.
|
|
43
|
+
*/
|
|
44
|
+
readonly guarded: boolean;
|
|
45
|
+
}
|
|
46
|
+
export type ElementPolicy = {
|
|
47
|
+
readonly tier: "model";
|
|
48
|
+
} | PreservationRule;
|
|
49
|
+
/** What a level with no node to hold a stranger answers: the container around it is demoted */
|
|
50
|
+
export type DemotionPolicy = {
|
|
51
|
+
readonly tier: "demote";
|
|
52
|
+
};
|
|
53
|
+
/** The levels a reader walks. Each is a content model of its own in `wml.xsd` */
|
|
54
|
+
export type ContentLevel = "body" | "tbl" | "tr" | "tc" | "p" | "wrapper" | "r";
|
|
55
|
+
export declare function clarkName(el: Element): string;
|
|
56
|
+
export declare const WML_POLICY: Readonly<Record<ContentLevel, ReadonlyMap<string, ElementPolicy>>>;
|
|
57
|
+
/** The levels that have a node of their own to keep a stranger in */
|
|
58
|
+
export type PreservingLevel = Exclude<ContentLevel, "tbl" | "tr">;
|
|
59
|
+
export declare function defaultPreservation(level: PreservingLevel): PreservationRule;
|
|
60
|
+
export declare function policyFor(el: Element, level: ContentLevel): ElementPolicy | DemotionPolicy;
|
|
61
|
+
/**
|
|
62
|
+
* The text one run child puts on screen, or null for a child that puts nothing there.
|
|
63
|
+
*
|
|
64
|
+
* This is where the table's answer becomes text, and a preserved fragment carries it along as the
|
|
65
|
+
* `text` a reader wrote onto it. Everything reading a story as plain text afterwards - a footnote
|
|
66
|
+
* body, a comment body, a header - asks `docx/story`'s `storyLeafText` for that same answer rather
|
|
67
|
+
* than keeping a vocabulary of its own, so the three of them cannot disagree about what a `w:cr`
|
|
68
|
+
* or a `w:noBreakHyphen` reads as.
|
|
69
|
+
*/
|
|
70
|
+
export declare function runContentText(el: Element): string | null;
|