@portone/docx-editor 0.4.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 +159 -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/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +100 -48
- 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.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 +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 +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/editor/clipboard/htmlReader.d.ts +30 -0
- package/dist/editor/{externalClipboard.js → clipboard/htmlReader.js} +39 -223
- 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/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/listCommands.d.ts +6 -0
- package/dist/editor/commands/listCommands.js +3 -2
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- 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 +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/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 +38 -20
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +191 -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 +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 +1 -1
- package/dist/editor/externalClipboard.d.ts +0 -22
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module knows nothing about the screen. All it knows is the file and the document model.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
7
7
|
import { type XmlParser } from "../ooxml/xml";
|
|
8
8
|
import { type FidelityNote } from "./fidelity";
|
|
9
9
|
import { SessionStore } from "./session";
|
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
|
}
|
|
@@ -6,10 +6,16 @@
|
|
|
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
8
|
* out around the same text. A control may hold a link; a link holding a control is a nesting the
|
|
9
|
-
* marks cannot record in that order,
|
|
9
|
+
* marks cannot record in that order, and that control stays whole as one preserved fragment.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here demotes a paragraph. What the editor has no model for - a field character, a
|
|
12
|
+
* tracked insertion, a symbol, a drawing nobody could read - is kept where it stood, inside its
|
|
13
|
+
* run or beside it, by the rule `docx/importPolicy` gives it for the level it stands at. The
|
|
14
|
+
* paragraph around it stays editable, which is what a document full of `w:lastRenderedPageBreak`
|
|
15
|
+
* needs.
|
|
10
16
|
*/
|
|
11
17
|
import type { Node as PMNode } from "prosemirror-model";
|
|
12
|
-
import { type ImportedComments } from "./comments";
|
|
18
|
+
import { type ImportedComments } from "./comments/reading";
|
|
13
19
|
import { type LinkTargets } from "./hyperlink";
|
|
14
20
|
import type { ImageSources } from "./media";
|
|
15
21
|
import { type ImportedNotes, type NoteKind } from "./notes";
|
|
@@ -28,5 +34,11 @@ export interface ImportSources {
|
|
|
28
34
|
noteLabel: (kind: NoteKind, id: string) => string;
|
|
29
35
|
}
|
|
30
36
|
export declare const NO_IMPORT_SOURCES: ImportSources;
|
|
31
|
-
/**
|
|
32
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Moves a paragraph into an editable node.
|
|
39
|
+
*
|
|
40
|
+
* Every paragraph opens editable. A child the reader has no model for, and a control or a link
|
|
41
|
+
* whose shape it could not put back together, is kept whole where it stood instead of standing
|
|
42
|
+
* the whole paragraph down.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildParagraph(el: Element, srcId: string | null, sources?: ImportSources): PMNode;
|
|
@@ -9,16 +9,19 @@ 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";
|
|
18
15
|
import {
|
|
19
16
|
NO_LINK_TARGETS,
|
|
20
17
|
readHyperlinkWrapper
|
|
21
18
|
} from "./hyperlink.js";
|
|
19
|
+
import { policyFor } from "./importPolicy.js";
|
|
20
|
+
import {
|
|
21
|
+
buildPreservedInline,
|
|
22
|
+
buildPreservedRunContent,
|
|
23
|
+
preservationOf
|
|
24
|
+
} from "./importPreserved.js";
|
|
22
25
|
import { NO_IMAGES } from "./media.js";
|
|
23
26
|
import { NO_NOTES, noteById } from "./notes.js";
|
|
24
27
|
import { readSdtWrapper } from "./sdt.js";
|
|
@@ -47,7 +50,7 @@ function buildImage(el, marks, images) {
|
|
|
47
50
|
marks
|
|
48
51
|
);
|
|
49
52
|
}
|
|
50
|
-
function
|
|
53
|
+
function buildModelledRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
51
54
|
switch (el.localName) {
|
|
52
55
|
case "t": {
|
|
53
56
|
const text = el.textContent ?? "";
|
|
@@ -86,9 +89,6 @@ function buildRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
86
89
|
authorId: comment?.authorId ?? null,
|
|
87
90
|
initials: comment?.initials ?? null,
|
|
88
91
|
date: comment?.date ?? null,
|
|
89
|
-
text: comment?.text ?? "",
|
|
90
|
-
commentXml: comment?.xml ?? null,
|
|
91
|
-
imported: true,
|
|
92
92
|
paraId,
|
|
93
93
|
resolved: comment?.resolved ?? false,
|
|
94
94
|
extensionXml: comment?.extensionXml ?? null,
|
|
@@ -114,7 +114,6 @@ function buildRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
114
114
|
kind,
|
|
115
115
|
id,
|
|
116
116
|
label: note ? noteLabel(kind, id) : "?",
|
|
117
|
-
text: note?.text ?? "",
|
|
118
117
|
customMarkFollows,
|
|
119
118
|
referenceXml: serializeXml(el)
|
|
120
119
|
},
|
|
@@ -135,22 +134,30 @@ function buildRunNodes(run, images, themeFonts, comments, notes, noteLabel, wrap
|
|
|
135
134
|
);
|
|
136
135
|
const nodes = [];
|
|
137
136
|
for (const child of elementChildren(run)) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
child
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
137
|
+
const policy = policyFor(child, "r");
|
|
138
|
+
if (policy.tier === "model") {
|
|
139
|
+
if (child.localName === "rPr") continue;
|
|
140
|
+
const built = buildModelledRunChild(
|
|
141
|
+
child,
|
|
142
|
+
marks,
|
|
143
|
+
images,
|
|
144
|
+
comments,
|
|
145
|
+
notes,
|
|
146
|
+
noteLabel
|
|
147
|
+
);
|
|
148
|
+
if (built !== null) {
|
|
149
|
+
nodes.push(...built);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
nodes.push(
|
|
154
|
+
buildPreservedRunContent(child, marks, preservationOf(policy, "r"))
|
|
146
155
|
);
|
|
147
|
-
if (built === null) return null;
|
|
148
|
-
nodes.push(...built);
|
|
149
156
|
}
|
|
150
157
|
if (nodes.length === 0) {
|
|
151
158
|
return [
|
|
152
159
|
docxSchema.nodes.rawInline.create(
|
|
153
|
-
{ xml: serializeXml(run) },
|
|
160
|
+
{ xml: serializeXml(run), element: run.localName },
|
|
154
161
|
null,
|
|
155
162
|
// The wrappers have to close again around this XML on export, so their marks ride along here too
|
|
156
163
|
wrappers
|
|
@@ -166,13 +173,6 @@ function nextKey(counts, el) {
|
|
|
166
173
|
counts.set(el.ownerDocument, key + 1);
|
|
167
174
|
return key;
|
|
168
175
|
}
|
|
169
|
-
var NOTHING_ON_SCREEN = [
|
|
170
|
-
"bookmarkStart",
|
|
171
|
-
"bookmarkEnd",
|
|
172
|
-
"proofErr",
|
|
173
|
-
"permStart",
|
|
174
|
-
"permEnd"
|
|
175
|
-
];
|
|
176
176
|
function annotationId(el) {
|
|
177
177
|
return Array.from(el.attributes).find((attribute) => attribute.localName === "id")?.value ?? null;
|
|
178
178
|
}
|
|
@@ -190,41 +190,42 @@ var NO_IMPORT_SOURCES = {
|
|
|
190
190
|
notes: NO_NOTES,
|
|
191
191
|
noteLabel: () => "?"
|
|
192
192
|
};
|
|
193
|
+
function buildModelledWrapperChild(child, sources, wrappers, linkAllowed) {
|
|
194
|
+
if (child.localName === "r") {
|
|
195
|
+
return buildRunNodes(
|
|
196
|
+
child,
|
|
197
|
+
sources.images,
|
|
198
|
+
sources.themeFonts,
|
|
199
|
+
sources.comments,
|
|
200
|
+
sources.notes,
|
|
201
|
+
sources.noteLabel,
|
|
202
|
+
wrappers
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (child.localName === "hyperlink") {
|
|
206
|
+
return linkAllowed ? buildHyperlinkNodes(child, sources, wrappers) : null;
|
|
207
|
+
}
|
|
208
|
+
const commentMarker = commentRangeNode(child, wrappers);
|
|
209
|
+
return commentMarker === null ? null : [commentMarker];
|
|
210
|
+
}
|
|
193
211
|
function buildWrappedNodes(content, sources, wrappers, linkAllowed) {
|
|
194
212
|
const nodes = [];
|
|
195
213
|
for (const child of elementChildren(content)) {
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
const policy = policyFor(child, "wrapper");
|
|
215
|
+
if (policy.tier === "model") {
|
|
216
|
+
const built = buildModelledWrapperChild(
|
|
198
217
|
child,
|
|
199
|
-
sources
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
sources.notes,
|
|
203
|
-
sources.noteLabel,
|
|
204
|
-
wrappers
|
|
218
|
+
sources,
|
|
219
|
+
wrappers,
|
|
220
|
+
linkAllowed
|
|
205
221
|
);
|
|
206
|
-
if (built
|
|
207
|
-
|
|
208
|
-
|
|
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;
|
|
222
|
+
if (built !== null) {
|
|
223
|
+
nodes.push(...built);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
215
226
|
}
|
|
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
227
|
nodes.push(
|
|
223
|
-
|
|
224
|
-
{ xml: serializeXml(child) },
|
|
225
|
-
null,
|
|
226
|
-
wrappers
|
|
227
|
-
)
|
|
228
|
+
buildPreservedInline(child, wrappers, preservationOf(policy, "wrapper"))
|
|
228
229
|
);
|
|
229
230
|
}
|
|
230
231
|
if (nodes.length === 0) return null;
|
|
@@ -251,48 +252,41 @@ function buildSdtNodes(el, sources) {
|
|
|
251
252
|
});
|
|
252
253
|
return buildWrappedNodes(wrapper.content, sources, [mark], true);
|
|
253
254
|
}
|
|
255
|
+
function buildModelledParagraphChild(child, sources) {
|
|
256
|
+
if (child.localName === "r") {
|
|
257
|
+
return buildRunNodes(
|
|
258
|
+
child,
|
|
259
|
+
sources.images,
|
|
260
|
+
sources.themeFonts,
|
|
261
|
+
sources.comments,
|
|
262
|
+
sources.notes,
|
|
263
|
+
sources.noteLabel
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (child.localName === "sdt") return buildSdtNodes(child, sources);
|
|
267
|
+
if (child.localName === "hyperlink") {
|
|
268
|
+
return buildHyperlinkNodes(child, sources, []);
|
|
269
|
+
}
|
|
270
|
+
const commentMarker = commentRangeNode(child);
|
|
271
|
+
return commentMarker === null ? null : [commentMarker];
|
|
272
|
+
}
|
|
254
273
|
function buildParagraph(el, srcId, sources = NO_IMPORT_SOURCES) {
|
|
255
|
-
const { images, themeFonts } = sources;
|
|
256
274
|
const inline = [];
|
|
257
275
|
let pPrElement = null;
|
|
258
276
|
for (const child of elementChildren(el)) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
277
|
+
const policy = policyFor(child, "p");
|
|
278
|
+
if (policy.tier === "model") {
|
|
279
|
+
if (child.localName === "pPr") {
|
|
280
|
+
pPrElement = child;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const built = buildModelledParagraphChild(child, sources);
|
|
284
|
+
if (built !== null) {
|
|
285
|
+
inline.push(...built);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
262
288
|
}
|
|
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
|
-
);
|
|
289
|
+
inline.push(buildPreservedInline(child, [], preservationOf(policy, "p")));
|
|
296
290
|
}
|
|
297
291
|
return docxSchema.nodes.paragraph.create(
|
|
298
292
|
{
|
|
@@ -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;
|