@portone/docx-editor 0.1.1 → 0.2.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 +82 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +17 -5
- package/dist/DocxEditor.d.ts +34 -10
- package/dist/DocxEditor.js +69 -24
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/docx/commentOnlyChange.d.ts +47 -0
- package/dist/docx/commentOnlyChange.js +162 -0
- package/dist/docx/comments/constants.d.ts +11 -3
- package/dist/docx/comments/constants.js +10 -3
- package/dist/docx/comments/contentTypes.d.ts +7 -0
- package/dist/docx/comments/contentTypes.js +38 -0
- package/dist/docx/comments/grammar.d.ts +80 -0
- package/dist/docx/comments/grammar.js +213 -0
- package/dist/docx/comments/model.d.ts +2 -0
- package/dist/docx/comments/model.js +3 -0
- package/dist/docx/comments/people.d.ts +39 -0
- package/dist/docx/comments/people.js +196 -0
- package/dist/docx/comments/reading.d.ts +18 -1
- package/dist/docx/comments/reading.js +33 -20
- package/dist/docx/comments/verifying.d.ts +44 -0
- package/dist/docx/comments/verifying.js +206 -0
- package/dist/docx/comments/writing.d.ts +4 -1
- package/dist/docx/comments/writing.js +84 -77
- package/dist/docx/exportDocx.js +2 -1
- package/dist/docx/formatting/direct.js +3 -6
- package/dist/docx/headersFooters.js +8 -10
- package/dist/docx/importParagraph.js +1 -0
- package/dist/docx/notes.js +3 -5
- package/dist/docx/paraProps.js +51 -46
- package/dist/docx/propsXml.d.ts +20 -2
- package/dist/docx/propsXml.js +72 -32
- package/dist/docx/relationships.js +8 -3
- package/dist/docx/runProps.js +34 -34
- package/dist/docx/sdt.js +16 -14
- package/dist/docx/serializeBlock.js +1 -2
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +38 -24
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +49 -73
- package/dist/docx/tableTemplate.js +24 -3
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +1 -0
- package/dist/editor/commands/breakCommands.js +6 -2
- package/dist/editor/commands/canRunCommand.d.ts +3 -2
- package/dist/editor/commands/canRunCommand.js +2 -2
- package/dist/editor/commands/comments/editing.js +9 -10
- package/dist/editor/commands/comments/model.d.ts +9 -0
- package/dist/editor/commands/comments/reading.d.ts +7 -0
- package/dist/editor/commands/comments/reading.js +14 -0
- package/dist/editor/commands/formatting/editing.js +6 -1
- package/dist/editor/commands/historyCommands.js +14 -5
- package/dist/editor/commands/index.d.ts +7 -1
- package/dist/editor/commands/index.js +4 -0
- package/dist/editor/commands/linkCommands.js +2 -0
- package/dist/editor/commands/lockCommands.js +12 -1
- package/dist/editor/commands/tabCommands.js +4 -2
- package/dist/editor/createEditor.d.ts +13 -3
- package/dist/editor/createEditor.js +23 -15
- package/dist/editor/externalClipboard.js +166 -4
- package/dist/editor/insertImage.js +6 -2
- package/dist/editor/insertTable.js +2 -1
- package/dist/editor/paragraphEdits.d.ts +2 -0
- package/dist/editor/paragraphEdits.js +2 -0
- package/dist/editor/plugins/documentProtection.d.ts +21 -0
- package/dist/editor/plugins/documentProtection.js +44 -0
- package/dist/editor/plugins/imagePaste.js +4 -1
- package/dist/editor/plugins/keymap.d.ts +11 -1
- package/dist/editor/plugins/keymap.js +24 -4
- package/dist/editor/plugins/lockedContent.d.ts +4 -2
- package/dist/editor/plugins/lockedContent.js +2 -2
- package/dist/editor/plugins/tableContextMenu.js +2 -1
- package/dist/editor/plugins/textContextMenu.js +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/numbering/listTemplate.js +32 -10
- package/dist/ooxml/element.d.ts +52 -0
- package/dist/ooxml/element.js +49 -0
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.js +4 -3
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/xml.d.ts +31 -0
- package/dist/ooxml/xml.js +27 -0
- package/dist/page/blockKinds.d.ts +42 -0
- package/dist/page/blockKinds.js +0 -0
- package/dist/page/measureBlocks.d.ts +14 -1
- package/dist/page/measureBlocks.js +31 -11
- package/dist/page/pageDecorations.d.ts +16 -0
- package/dist/page/pageDecorations.js +1 -0
- package/dist/page/pageLayout.d.ts +8 -43
- package/dist/page/pageLayout.js +28 -51
- package/dist/page/tableMeasurements.d.ts +10 -2
- package/dist/page/tableMeasurements.js +20 -16
- package/dist/page/usePageLayout.d.ts +15 -0
- package/dist/page/usePageLayout.js +30 -2
- package/dist/schema/attrRoles.d.ts +35 -0
- package/dist/schema/attrRoles.js +106 -0
- package/dist/schema/docxSchema.d.ts +6 -0
- package/dist/schema/docxSchema.js +216 -100
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +68 -0
- package/dist/schema/guards.js +82 -0
- package/dist/schema/locks.d.ts +9 -8
- package/dist/schema/locks.js +32 -16
- package/dist/schema/preservedGuards.d.ts +30 -0
- package/dist/schema/preservedGuards.js +50 -0
- package/dist/schema/protection.d.ts +84 -0
- package/dist/schema/protection.js +171 -0
- package/dist/schema/protectionState.d.ts +20 -0
- package/dist/schema/protectionState.js +37 -0
- package/dist/schema/sourceEquality.d.ts +27 -0
- package/dist/schema/sourceEquality.js +65 -0
- package/dist/table/cellFormatting.js +5 -2
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +2 -2
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +2 -2
- package/dist/table/merge.js +3 -3
- package/dist/ui/CommentsPanel.d.ts +3 -3
- package/dist/ui/CommentsPanel.js +26 -18
- package/dist/ui/LinkCard.d.ts +1 -2
- package/dist/ui/LinkCard.js +3 -4
- package/dist/ui/TextMenu.js +15 -10
- package/package.json +5 -2
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
// src/docx/comments/writing.ts
|
|
2
|
+
import { elementXml, xmlnsAttr } from "../../ooxml/element.js";
|
|
2
3
|
import { DocxExportError } from "../../ooxml/errors.js";
|
|
3
|
-
import {
|
|
4
|
+
import { wName, xmlnsDecl } from "../../ooxml/names.js";
|
|
5
|
+
import { encodeUtf8 } from "../../ooxml/xml.js";
|
|
4
6
|
import { directoryOf } from "../relationships.js";
|
|
5
7
|
import {
|
|
6
8
|
COMMENTS_CONTENT_TYPE,
|
|
7
9
|
COMMENTS_EXTENDED_CONTENT_TYPE,
|
|
8
10
|
COMMENTS_EXTENDED_REL_TYPE,
|
|
9
11
|
COMMENTS_REL_TYPE,
|
|
10
|
-
CONTENT_TYPES_PATH
|
|
11
|
-
MC_NS,
|
|
12
|
-
W14_NS,
|
|
13
|
-
W15_NS
|
|
12
|
+
CONTENT_TYPES_PATH
|
|
14
13
|
} from "./constants.js";
|
|
14
|
+
import { withContentType } from "./contentTypes.js";
|
|
15
|
+
import {
|
|
16
|
+
arrivedEntries,
|
|
17
|
+
renderCommentBody,
|
|
18
|
+
renderCommentExtension,
|
|
19
|
+
withThreadKey
|
|
20
|
+
} from "./grammar.js";
|
|
15
21
|
import {
|
|
16
22
|
commentReferencesIn
|
|
17
23
|
} from "./model.js";
|
|
24
|
+
import { planPeoplePart } from "./people.js";
|
|
25
|
+
function needsThreadKey(id, comment, session) {
|
|
26
|
+
return comment.imported && carriesThreadMetadata(comment) && (session.comments.byId.get(id)?.paraId ?? null) === null;
|
|
27
|
+
}
|
|
18
28
|
function commentsChanged(doc, session) {
|
|
19
29
|
const current = commentReferencesIn(doc);
|
|
20
30
|
if (current.size !== session.commentReferenceIds.size) return true;
|
|
@@ -34,14 +44,11 @@ function commentsChanged(doc, session) {
|
|
|
34
44
|
for (const comment of current.values()) {
|
|
35
45
|
if (comment.replies.some((reply) => !reply.imported)) return true;
|
|
36
46
|
}
|
|
47
|
+
for (const [id, comment] of current) {
|
|
48
|
+
if (needsThreadKey(id, comment, session)) return true;
|
|
49
|
+
}
|
|
37
50
|
return false;
|
|
38
51
|
}
|
|
39
|
-
function renderedExtension(comment) {
|
|
40
|
-
if (comment.extensionXml !== null) return comment.extensionXml;
|
|
41
|
-
const parent = "parentParaId" in comment ? ` w15:paraIdParent="${escapeXml(comment.parentParaId)}"` : "";
|
|
42
|
-
const done = "resolved" in comment ? ` w15:done="${comment.resolved ? "1" : "0"}"` : "";
|
|
43
|
-
return `<w15:commentEx w15:paraId="${escapeXml(comment.paraId)}"${parent}${done}/>`;
|
|
44
|
-
}
|
|
45
52
|
function extensionsXml(references, comments, originallyReferenced) {
|
|
46
53
|
const current = currentCommentBodies(references);
|
|
47
54
|
const originalThreads = originalThreadIds(comments, originallyReferenced);
|
|
@@ -56,19 +63,23 @@ function extensionsXml(references, comments, originallyReferenced) {
|
|
|
56
63
|
const id = idByParaId.get(original.paraId);
|
|
57
64
|
const item = id === void 0 ? void 0 : current.get(id);
|
|
58
65
|
if (id !== void 0 && item && !written.has(id)) {
|
|
59
|
-
pieces.push(
|
|
66
|
+
pieces.push(renderCommentExtension(item));
|
|
60
67
|
written.add(id);
|
|
61
68
|
} else if (id === void 0 || !originalThreads.has(id)) {
|
|
62
69
|
pieces.push(original.xml);
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
72
|
for (const [id, comment] of current) {
|
|
66
|
-
if (!written.has(id)
|
|
73
|
+
if (!written.has(id) && carriesThreadMetadata(comment)) {
|
|
74
|
+
pieces.push(renderCommentExtension(comment));
|
|
75
|
+
}
|
|
67
76
|
}
|
|
68
77
|
if (comments.extendedXml === null) {
|
|
69
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w15:commentsEx
|
|
78
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w15:commentsEx ${xmlnsDecl("w15")}>${pieces.join("")}</w15:commentsEx>`;
|
|
70
79
|
}
|
|
71
|
-
const open = /<(?:[
|
|
80
|
+
const open = /<(?:[^\s<>/:="']+:)?commentsEx\b[^>]*>/.exec(
|
|
81
|
+
comments.extendedXml
|
|
82
|
+
);
|
|
72
83
|
if (!open) {
|
|
73
84
|
throw new DocxExportError(
|
|
74
85
|
"malformed-xml",
|
|
@@ -76,14 +87,23 @@ function extensionsXml(references, comments, originallyReferenced) {
|
|
|
76
87
|
);
|
|
77
88
|
}
|
|
78
89
|
const name = /^<([^\s>]+)/.exec(open[0])?.[1];
|
|
79
|
-
|
|
90
|
+
if (name === void 0) {
|
|
91
|
+
throw new DocxExportError(
|
|
92
|
+
"malformed-xml",
|
|
93
|
+
"the Comments Extended part has no commentsEx root element"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const bodyAt = open.index + open[0].length;
|
|
97
|
+
if (open[0].endsWith("/>")) {
|
|
98
|
+
return comments.extendedXml.slice(0, open.index) + `${open[0].slice(0, -2)}>${pieces.join("")}</${name}>` + comments.extendedXml.slice(bodyAt);
|
|
99
|
+
}
|
|
100
|
+
const close = comments.extendedXml.lastIndexOf(`</${name}>`);
|
|
80
101
|
if (close === -1) {
|
|
81
102
|
throw new DocxExportError(
|
|
82
103
|
"malformed-xml",
|
|
83
104
|
"the Comments Extended part has no closing commentsEx tag"
|
|
84
105
|
);
|
|
85
106
|
}
|
|
86
|
-
const bodyAt = open.index + open[0].length;
|
|
87
107
|
return comments.extendedXml.slice(0, bodyAt) + pieces.join("") + comments.extendedXml.slice(close);
|
|
88
108
|
}
|
|
89
109
|
function extensionsChanged(doc, session) {
|
|
@@ -101,41 +121,42 @@ function extensionsChanged(doc, session) {
|
|
|
101
121
|
}
|
|
102
122
|
return false;
|
|
103
123
|
}
|
|
104
|
-
function commentTextXml(text) {
|
|
105
|
-
const lines = text.split("\n");
|
|
106
|
-
const pieces = [];
|
|
107
|
-
lines.forEach((line, index) => {
|
|
108
|
-
if (index > 0) pieces.push("<w:br/>");
|
|
109
|
-
if (line.length > 0 || lines.length === 1) {
|
|
110
|
-
pieces.push(`<w:t xml:space="preserve">${escapeXml(line)}</w:t>`);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return `<w:r>${pieces.join("")}</w:r>`;
|
|
114
|
-
}
|
|
115
124
|
function carriesThreadMetadata(comment) {
|
|
116
125
|
return comment.extensionXml !== null || "threadImported" in comment && !comment.threadImported || "parentParaId" in comment;
|
|
117
126
|
}
|
|
118
|
-
function
|
|
127
|
+
function keyedEntry(comment, arrivedKeyed) {
|
|
128
|
+
return carriesThreadMetadata(comment) || arrivedKeyed.has(comment.id);
|
|
129
|
+
}
|
|
130
|
+
function renderedComment(comment, arrivedKeyed, arrived) {
|
|
119
131
|
if (comment.imported && comment.commentXml !== null) {
|
|
120
|
-
return comment
|
|
132
|
+
return carriesThreadMetadata(comment) ? withThreadKey(
|
|
133
|
+
comment.commentXml,
|
|
134
|
+
comment.paraId,
|
|
135
|
+
arrived.get(comment.id) ?? null
|
|
136
|
+
) : comment.commentXml;
|
|
121
137
|
}
|
|
122
138
|
const attrs = [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
comment.
|
|
126
|
-
comment.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
139
|
+
xmlnsAttr("w"),
|
|
140
|
+
[wName("id"), comment.id],
|
|
141
|
+
comment.author === null ? null : [wName("author"), comment.author],
|
|
142
|
+
comment.date === null ? null : [wName("date"), comment.date],
|
|
143
|
+
comment.initials === null ? null : [wName("initials"), comment.initials]
|
|
144
|
+
];
|
|
145
|
+
const paraId = keyedEntry(comment, arrivedKeyed) ? comment.paraId : null;
|
|
146
|
+
const body = renderCommentBody(comment.text, paraId);
|
|
147
|
+
return elementXml(
|
|
148
|
+
wName("comment"),
|
|
149
|
+
attrs.filter((attr) => attr !== null),
|
|
150
|
+
[body]
|
|
151
|
+
);
|
|
131
152
|
}
|
|
132
153
|
function withThreadMarkupCompatibility(openTag) {
|
|
133
154
|
let updated = openTag;
|
|
134
155
|
if (!/\sxmlns:w14\s*=/.test(updated)) {
|
|
135
|
-
updated = updated.replace(/>$/, `
|
|
156
|
+
updated = updated.replace(/>$/, ` ${xmlnsDecl("w14")}>`);
|
|
136
157
|
}
|
|
137
158
|
if (!/\sxmlns:mc\s*=/.test(updated)) {
|
|
138
|
-
updated = updated.replace(/>$/, `
|
|
159
|
+
updated = updated.replace(/>$/, ` ${xmlnsDecl("mc")}>`);
|
|
139
160
|
}
|
|
140
161
|
const ignorable = /\smc:Ignorable\s*=\s*(["'])([^"']*)\1/.exec(updated);
|
|
141
162
|
if (!ignorable) {
|
|
@@ -170,16 +191,22 @@ function originalThreadIds(comments, rootIds) {
|
|
|
170
191
|
}
|
|
171
192
|
function commentsXml(references, comments, originallyReferenced) {
|
|
172
193
|
const currentBodies = currentCommentBodies(references);
|
|
194
|
+
const arrivedKeyed = new Set(
|
|
195
|
+
Array.from(comments.byId.values()).flatMap(
|
|
196
|
+
(entry) => entry.paraId === null ? [] : [entry.id]
|
|
197
|
+
)
|
|
198
|
+
);
|
|
173
199
|
const hasThreadMetadata = Array.from(currentBodies.values()).some(
|
|
174
|
-
|
|
200
|
+
(comment) => keyedEntry(comment, arrivedKeyed)
|
|
175
201
|
);
|
|
202
|
+
const arrived = arrivedEntries(comments.xml);
|
|
176
203
|
const originalThreads = originalThreadIds(comments, originallyReferenced);
|
|
177
204
|
const pieces = [];
|
|
178
205
|
const written = /* @__PURE__ */ new Set();
|
|
179
206
|
for (const original of comments.ordered) {
|
|
180
207
|
const current = currentBodies.get(original.id);
|
|
181
208
|
if (current) {
|
|
182
|
-
pieces.push(renderedComment(current));
|
|
209
|
+
pieces.push(renderedComment(current, arrivedKeyed, arrived));
|
|
183
210
|
written.add(original.id);
|
|
184
211
|
continue;
|
|
185
212
|
}
|
|
@@ -189,13 +216,15 @@ function commentsXml(references, comments, originallyReferenced) {
|
|
|
189
216
|
}
|
|
190
217
|
}
|
|
191
218
|
for (const [id, comment] of currentBodies) {
|
|
192
|
-
if (!written.has(id))
|
|
219
|
+
if (!written.has(id)) {
|
|
220
|
+
pieces.push(renderedComment(comment, arrivedKeyed, arrived));
|
|
221
|
+
}
|
|
193
222
|
}
|
|
194
223
|
if (comments.xml === null) {
|
|
195
|
-
const compatibility = hasThreadMetadata ? `
|
|
196
|
-
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:comments
|
|
224
|
+
const compatibility = hasThreadMetadata ? ` ${xmlnsDecl("w14")} ${xmlnsDecl("mc")} mc:Ignorable="w14"` : "";
|
|
225
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:comments ${xmlnsDecl("w")}${compatibility}>${pieces.join("")}</w:comments>`;
|
|
197
226
|
}
|
|
198
|
-
const open = /<(?:[
|
|
227
|
+
const open = /<(?:[^\s<>/:="']+:)?comments\b[^>]*>/.exec(comments.xml);
|
|
199
228
|
if (!open) {
|
|
200
229
|
throw new DocxExportError(
|
|
201
230
|
"malformed-xml",
|
|
@@ -213,37 +242,6 @@ function commentsXml(references, comments, originallyReferenced) {
|
|
|
213
242
|
const openTag = hasThreadMetadata ? withThreadMarkupCompatibility(open[0]) : open[0];
|
|
214
243
|
return comments.xml.slice(0, open.index) + openTag + pieces.join("") + comments.xml.slice(close);
|
|
215
244
|
}
|
|
216
|
-
var TYPES_OPEN_TAG = /<(?:[\w.-]+:)?Types\b[^>]*>/;
|
|
217
|
-
function withContentType(parts, partPath, contentType, current) {
|
|
218
|
-
const original = current ?? parts.get(CONTENT_TYPES_PATH);
|
|
219
|
-
if (!original) {
|
|
220
|
-
throw new DocxExportError(
|
|
221
|
-
"missing-content-types",
|
|
222
|
-
`cannot add a Comments part to a package that has no ${CONTENT_TYPES_PATH}`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
const { text, hadBom } = decodeUtf8(original);
|
|
226
|
-
const partName = `/${partPath}`;
|
|
227
|
-
if (new RegExp(
|
|
228
|
-
`<(?:[\\w.-]+:)?Override[^>]+PartName=["']${partName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`,
|
|
229
|
-
"i"
|
|
230
|
-
).test(text)) {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
const open = TYPES_OPEN_TAG.exec(text);
|
|
234
|
-
if (!open) {
|
|
235
|
-
throw new DocxExportError(
|
|
236
|
-
"malformed-xml",
|
|
237
|
-
`${CONTENT_TYPES_PATH} has no Types element`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
const rootName = /^<([^\s>]+)/.exec(open[0])?.[1] ?? "Types";
|
|
241
|
-
const separator = rootName.indexOf(":");
|
|
242
|
-
const prefix = separator < 0 ? "" : `${rootName.slice(0, separator)}:`;
|
|
243
|
-
const declaration = `<${prefix}Override PartName="${partName}" ContentType="${contentType}"/>`;
|
|
244
|
-
const at = open.index + open[0].length;
|
|
245
|
-
return encodeUtf8(text.slice(0, at) + declaration + text.slice(at), hadBom);
|
|
246
|
-
}
|
|
247
245
|
function availableCommentsPath(session) {
|
|
248
246
|
const directory = directoryOf(session.mainPartPath);
|
|
249
247
|
for (let suffix = 0; ; suffix += 1) {
|
|
@@ -320,6 +318,15 @@ function planCommentParts(doc, session, relationships, currentContentTypes) {
|
|
|
320
318
|
if (contentTypes) parts.set(CONTENT_TYPES_PATH, contentTypes);
|
|
321
319
|
}
|
|
322
320
|
}
|
|
321
|
+
if (bodyChanged) {
|
|
322
|
+
const people = planPeoplePart(
|
|
323
|
+
currentCommentBodies(references).values(),
|
|
324
|
+
session,
|
|
325
|
+
relationships,
|
|
326
|
+
parts.get(CONTENT_TYPES_PATH) ?? currentContentTypes
|
|
327
|
+
);
|
|
328
|
+
for (const [path, bytes] of people ?? []) parts.set(path, bytes);
|
|
329
|
+
}
|
|
323
330
|
return { parts };
|
|
324
331
|
}
|
|
325
332
|
export {
|
package/dist/docx/exportDocx.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parseNumbering } from "../numbering/parseNumbering.js";
|
|
|
4
4
|
import { addListDefinitions } from "../numbering/writeNumbering.js";
|
|
5
5
|
import { DocxExportError } from "../ooxml/errors.js";
|
|
6
6
|
import { decodeUtf8, encodeUtf8, parseXml, W_NS } from "../ooxml/xml.js";
|
|
7
|
+
import { sameSource } from "../schema/sourceEquality.js";
|
|
7
8
|
import { planCommentParts } from "./comments.js";
|
|
8
9
|
import { repackParts } from "./container.js";
|
|
9
10
|
import { hyperlinkRefs } from "./hyperlink.js";
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
import { withUniqueControls } from "./uniqueControls.js";
|
|
22
23
|
function blockXml(node, session, refs) {
|
|
23
24
|
const imported = originalBlock(node, session);
|
|
24
|
-
if (imported && node
|
|
25
|
+
if (imported && sameSource(node, imported.node)) return imported.xml;
|
|
25
26
|
return serializeBlock(node, session, refs);
|
|
26
27
|
}
|
|
27
28
|
function buildDocumentXml(doc, session, refs) {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isHighlightName,
|
|
4
4
|
isUnderlineKind
|
|
5
5
|
} from "../../model/format.js";
|
|
6
|
+
import { overridingAttrs } from "../../ooxml/precedence.js";
|
|
6
7
|
import { readTabStopDirectives } from "../../ooxml/tabStops.js";
|
|
7
8
|
import {
|
|
8
9
|
ALIGN_BY_JC,
|
|
@@ -18,11 +19,7 @@ import {
|
|
|
18
19
|
wAttr
|
|
19
20
|
} from "../../ooxml/units.js";
|
|
20
21
|
import { childByLocalName } from "../../ooxml/xml.js";
|
|
21
|
-
import {
|
|
22
|
-
NO_THEME_FONTS,
|
|
23
|
-
THEME_ATTRS,
|
|
24
|
-
themeFontName
|
|
25
|
-
} from "../theme.js";
|
|
22
|
+
import { NO_THEME_FONTS, themeFontName } from "../theme.js";
|
|
26
23
|
var VERTICAL_ALIGN_BY_VAL = {
|
|
27
24
|
superscript: "superscript",
|
|
28
25
|
subscript: "subscript"
|
|
@@ -35,7 +32,7 @@ var FONT_SLOTS = ["ascii", "eastAsia", "hAnsi", "cs"];
|
|
|
35
32
|
function slotFontName(rFonts, slot, themeFonts) {
|
|
36
33
|
const written = wAttr(rFonts, slot);
|
|
37
34
|
if (written !== null) return written;
|
|
38
|
-
for (const attr of
|
|
35
|
+
for (const attr of overridingAttrs("rFonts", slot)) {
|
|
39
36
|
const resolved = themeFontName(themeFonts, wAttr(rFonts, attr));
|
|
40
37
|
if (resolved !== null) return resolved;
|
|
41
38
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/docx/headersFooters.ts
|
|
2
2
|
import { ALIGN_BY_JC } from "../ooxml/units.js";
|
|
3
3
|
import {
|
|
4
|
+
attributeByLocalName,
|
|
4
5
|
decodeUtf8,
|
|
5
6
|
elementChildren,
|
|
6
7
|
parseXml,
|
|
@@ -20,12 +21,9 @@ var NO_HEADERS_FOOTERS = {
|
|
|
20
21
|
evenAndOdd: false,
|
|
21
22
|
pageNumberStart: 1
|
|
22
23
|
};
|
|
23
|
-
function attribute(el, localName) {
|
|
24
|
-
return Array.from(el.attributes).find((entry) => entry.localName === localName)?.value ?? null;
|
|
25
|
-
}
|
|
26
24
|
function isOn(el) {
|
|
27
25
|
if (!el) return false;
|
|
28
|
-
const value =
|
|
26
|
+
const value = attributeByLocalName(el, "val")?.toLowerCase();
|
|
29
27
|
return value !== "0" && value !== "false" && value !== "off";
|
|
30
28
|
}
|
|
31
29
|
function firstSectPrIn(root) {
|
|
@@ -58,13 +56,13 @@ function paragraphSegments(paragraph) {
|
|
|
58
56
|
return;
|
|
59
57
|
}
|
|
60
58
|
if (el.namespaceURI === W_NS && el.localName === "fldSimple") {
|
|
61
|
-
const field = pageField(
|
|
59
|
+
const field = pageField(attributeByLocalName(el, "instr") ?? "");
|
|
62
60
|
if (field) dynamic(field);
|
|
63
61
|
else for (const child of elementChildren(el)) visit(child);
|
|
64
62
|
return;
|
|
65
63
|
}
|
|
66
64
|
if (el.namespaceURI === W_NS && el.localName === "fldChar") {
|
|
67
|
-
const kind =
|
|
65
|
+
const kind = attributeByLocalName(el, "fldCharType");
|
|
68
66
|
if (kind === "begin") {
|
|
69
67
|
fields.push({ instruction: "", separated: false, field: null });
|
|
70
68
|
} else if (kind === "separate") {
|
|
@@ -129,7 +127,7 @@ function readContent(bytes) {
|
|
|
129
127
|
) : void 0;
|
|
130
128
|
return {
|
|
131
129
|
segments,
|
|
132
|
-
align: jc ? ALIGN_BY_JC[
|
|
130
|
+
align: jc ? ALIGN_BY_JC[attributeByLocalName(jc, "val") ?? ""] ?? null : null
|
|
133
131
|
};
|
|
134
132
|
}
|
|
135
133
|
function settingsEvenAndOdd(parts, mainPartPath) {
|
|
@@ -153,9 +151,9 @@ function readVariants(parts, mainPartPath, sectPr, kind) {
|
|
|
153
151
|
const variants = { ...EMPTY_VARIANTS };
|
|
154
152
|
for (const reference of elementChildren(sectPr)) {
|
|
155
153
|
if (reference.localName !== `${kind}Reference`) continue;
|
|
156
|
-
const type =
|
|
154
|
+
const type = attributeByLocalName(reference, "type") ?? "default";
|
|
157
155
|
if (type !== "default" && type !== "first" && type !== "even") continue;
|
|
158
|
-
const id = reference.getAttributeNS(R_NS, "id") ??
|
|
156
|
+
const id = reference.getAttributeNS(R_NS, "id") ?? attributeByLocalName(reference, "id");
|
|
159
157
|
if (!id) continue;
|
|
160
158
|
const relationship = relationships.get(id);
|
|
161
159
|
if (!relationship || relationship.external || relationship.type !== `${R_NS}/${kind}`) {
|
|
@@ -170,7 +168,7 @@ function pageNumberStart(sectPr) {
|
|
|
170
168
|
const pgNumType = elementChildren(sectPr).find(
|
|
171
169
|
(child) => child.localName === "pgNumType"
|
|
172
170
|
);
|
|
173
|
-
const declared = pgNumType ?
|
|
171
|
+
const declared = pgNumType ? attributeByLocalName(pgNumType, "start") : null;
|
|
174
172
|
if (declared === null) return 1;
|
|
175
173
|
const start = Number(declared);
|
|
176
174
|
return Number.isSafeInteger(start) && start >= 0 ? start : 1;
|
|
@@ -81,6 +81,7 @@ function buildRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
81
81
|
id,
|
|
82
82
|
referenceXml: serializeXml(el),
|
|
83
83
|
author: comment?.author ?? null,
|
|
84
|
+
authorId: comment?.authorId ?? null,
|
|
84
85
|
initials: comment?.initials ?? null,
|
|
85
86
|
date: comment?.date ?? null,
|
|
86
87
|
text: comment?.text ?? "",
|
package/dist/docx/notes.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/docx/notes.ts
|
|
2
2
|
import {
|
|
3
|
+
attributeByLocalName,
|
|
3
4
|
decodeUtf8,
|
|
4
5
|
elementChildren,
|
|
5
6
|
parseXml,
|
|
@@ -16,9 +17,6 @@ var NO_NOTES = {
|
|
|
16
17
|
footnotes: EMPTY_PART,
|
|
17
18
|
endnotes: EMPTY_PART
|
|
18
19
|
};
|
|
19
|
-
function attribute(el, localName) {
|
|
20
|
-
return Array.from(el.attributes).find((entry) => entry.localName === localName)?.value ?? null;
|
|
21
|
-
}
|
|
22
20
|
function inlineText(node) {
|
|
23
21
|
if (node.localName === "t") return node.textContent ?? "";
|
|
24
22
|
if (node.localName === "tab") return " ";
|
|
@@ -42,9 +40,9 @@ function notePart(parts, mainPartPath, kind) {
|
|
|
42
40
|
const root = parseXml(decodeUtf8(bytes).text).documentElement;
|
|
43
41
|
const elements = elementChildren(root).filter((el) => el.localName === kind);
|
|
44
42
|
const ordered = elements.flatMap((el) => {
|
|
45
|
-
const id =
|
|
43
|
+
const id = attributeByLocalName(el, "id");
|
|
46
44
|
if (id === null) return [];
|
|
47
|
-
const type =
|
|
45
|
+
const type = attributeByLocalName(el, "type");
|
|
48
46
|
const regular = type === null || type === "normal";
|
|
49
47
|
const label = regular ? "?" : "";
|
|
50
48
|
return [{ kind, id, label, text: noteText(el), type }];
|
package/dist/docx/paraProps.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// src/docx/paraProps.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
attrPairs,
|
|
4
|
+
elementXml,
|
|
5
|
+
withoutAttrs
|
|
6
|
+
} from "../ooxml/element.js";
|
|
7
|
+
import { wName } from "../ooxml/names.js";
|
|
8
|
+
import { setAttr } from "../ooxml/precedence.js";
|
|
9
|
+
import { childByLocalName, localPart } from "../ooxml/xml.js";
|
|
3
10
|
import {
|
|
4
11
|
layerParagraphFormat,
|
|
5
12
|
layerRunFormat,
|
|
@@ -33,65 +40,66 @@ function readParagraphStyleRun(pPr, styles = NO_STYLES, defaultStyleId = null) {
|
|
|
33
40
|
}
|
|
34
41
|
function numPrXml(ref) {
|
|
35
42
|
if (!ref) return null;
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
return elementXml(
|
|
44
|
+
wName("numPr"),
|
|
45
|
+
[],
|
|
46
|
+
[
|
|
47
|
+
elementXml(wName("ilvl"), [[wName("val"), `${ref.ilvl}`]]),
|
|
48
|
+
elementXml(wName("numId"), [[wName("val"), `${ref.numId}`]])
|
|
49
|
+
]
|
|
50
|
+
);
|
|
41
51
|
}
|
|
42
52
|
function levelIndAttrs(indent) {
|
|
43
53
|
const attrs = [];
|
|
44
54
|
if (indent.startTwips !== null)
|
|
45
|
-
attrs.push(["
|
|
55
|
+
attrs.push([wName("left"), `${indent.startTwips}`]);
|
|
46
56
|
if (indent.hangingTwips !== null) {
|
|
47
|
-
attrs.push(["
|
|
57
|
+
attrs.push([wName("hanging"), `${indent.hangingTwips}`]);
|
|
48
58
|
} else if (indent.firstLineTwips !== null) {
|
|
49
|
-
attrs.push(["
|
|
59
|
+
attrs.push([wName("firstLine"), `${indent.firstLineTwips}`]);
|
|
50
60
|
}
|
|
51
61
|
return attrs;
|
|
52
62
|
}
|
|
53
|
-
function attrsXml(tag, attrs) {
|
|
54
|
-
if (attrs.length === 0) return null;
|
|
55
|
-
const text = attrs.map(([name, value]) => `${name}="${escapeXml(value)}"`).join(" ");
|
|
56
|
-
return `<${tag} ${text}/>`;
|
|
57
|
-
}
|
|
58
63
|
function indXml(attrs) {
|
|
59
|
-
return
|
|
64
|
+
return attrs.length === 0 ? null : elementXml(wName("ind"), attrs);
|
|
60
65
|
}
|
|
61
66
|
function nextIndXml(ind, change) {
|
|
62
67
|
if (change.kind === "keep") return void 0;
|
|
63
68
|
if (change.kind === "clearHanging") {
|
|
64
|
-
return indXml(
|
|
69
|
+
return indXml(withoutAttrs(attrPairs(ind), HANGING_IND_ATTRS));
|
|
65
70
|
}
|
|
66
71
|
const dropped = [...LEFT_IND_ATTRS, ...FIRST_LINE_IND_ATTRS];
|
|
67
72
|
return indXml([
|
|
68
73
|
...levelIndAttrs(change.indent),
|
|
69
|
-
...
|
|
74
|
+
...withoutAttrs(attrPairs(ind), dropped)
|
|
70
75
|
]);
|
|
71
76
|
}
|
|
72
77
|
function leftIndAttrs(ind, leftTwips) {
|
|
73
|
-
const original =
|
|
78
|
+
const original = attrPairs(ind);
|
|
74
79
|
const slot = original.find(
|
|
75
|
-
(
|
|
76
|
-
)?.
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
([name]) => LEFT_TWIPS_IND_ATTRS.includes(localPart(name))
|
|
81
|
+
)?.[0];
|
|
82
|
+
const written = leftTwips > 0 ? `${leftTwips}` : null;
|
|
83
|
+
const kept = withoutAttrs(
|
|
84
|
+
original,
|
|
85
|
+
LEFT_IND_ATTRS.filter(
|
|
86
|
+
(name) => slot === void 0 || name !== localPart(slot)
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
if (slot !== void 0) return setAttr(kept, "ind", localPart(slot), written);
|
|
90
|
+
return written === null ? kept : [[wName("left"), written], ...kept];
|
|
84
91
|
}
|
|
85
92
|
var EMPTY_P_PR = { tag: "w:pPr", attrs: null, children: [] };
|
|
86
93
|
function editParagraphProps(pPr, styles, defaultStyleId, plan) {
|
|
87
94
|
const props = pPr === null ? EMPTY_P_PR : parseProps(pPr);
|
|
88
95
|
const element = pPr === null ? null : parsePropsXml(pPr);
|
|
89
96
|
if (!props || pPr !== null && !element) return null;
|
|
90
|
-
const
|
|
91
|
-
(
|
|
92
|
-
|
|
97
|
+
const rendered = renderProps(
|
|
98
|
+
plan(element).reduce(
|
|
99
|
+
(kept, [name, xml]) => setPropsChild(kept, name, xml, P_PR_ORDER),
|
|
100
|
+
props
|
|
101
|
+
)
|
|
93
102
|
);
|
|
94
|
-
const rendered = renderProps({ ...props, children });
|
|
95
103
|
const next = rendered === "" ? null : rendered;
|
|
96
104
|
return {
|
|
97
105
|
pPr: next,
|
|
@@ -116,19 +124,16 @@ function withLeftIndent(pPr, leftTwips, styles = NO_STYLES, defaultStyleId = nul
|
|
|
116
124
|
});
|
|
117
125
|
}
|
|
118
126
|
function spacingAttrs(spacing, edits) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
([name]) =>
|
|
127
|
+
const changed = attrPairs(spacing).map(
|
|
128
|
+
([name, value]) => [
|
|
129
|
+
name,
|
|
130
|
+
edits.find(([edited]) => edited === localPart(name))?.[1] ?? value
|
|
131
|
+
]
|
|
132
|
+
);
|
|
133
|
+
return edits.reduce(
|
|
134
|
+
(attrs, [name, value]) => setAttr(attrs, "spacing", name, value),
|
|
135
|
+
changed
|
|
127
136
|
);
|
|
128
|
-
return [
|
|
129
|
-
...changed,
|
|
130
|
-
...added.map(([name, value]) => [`w:${name}`, value])
|
|
131
|
-
];
|
|
132
137
|
}
|
|
133
138
|
function withLineSpacing(pPr, spacing, styles = NO_STYLES, defaultStyleId = null) {
|
|
134
139
|
const line = spacing.rule === "auto" ? Math.round(spacing.lines * 240) : Math.round(spacing.pt * 20);
|
|
@@ -138,11 +143,11 @@ function withLineSpacing(pPr, spacing, styles = NO_STYLES, defaultStyleId = null
|
|
|
138
143
|
["line", `${line}`],
|
|
139
144
|
["lineRule", spacing.rule]
|
|
140
145
|
]);
|
|
141
|
-
return [["spacing",
|
|
146
|
+
return [["spacing", elementXml(wName("spacing"), attrs)]];
|
|
142
147
|
});
|
|
143
148
|
}
|
|
144
149
|
function withParagraphStyle(pPr, styleId, styles = NO_STYLES, defaultStyleId = null) {
|
|
145
|
-
const pStyle = styleId === null ? null :
|
|
150
|
+
const pStyle = styleId === null ? null : elementXml(wName("pStyle"), [[wName("val"), styleId]]);
|
|
146
151
|
return editParagraphProps(pPr, styles, defaultStyleId, () => [
|
|
147
152
|
["pStyle", pStyle]
|
|
148
153
|
]);
|
|
@@ -154,7 +159,7 @@ var JC_BY_ALIGN = {
|
|
|
154
159
|
justify: "both"
|
|
155
160
|
};
|
|
156
161
|
function withParagraphAlign(pPr, align, styles = NO_STYLES, defaultStyleId = null) {
|
|
157
|
-
const jc =
|
|
162
|
+
const jc = elementXml(wName("jc"), [[wName("val"), JC_BY_ALIGN[align]]]);
|
|
158
163
|
return editParagraphProps(pPr, styles, defaultStyleId, () => [["jc", jc]]);
|
|
159
164
|
}
|
|
160
165
|
export {
|
package/dist/docx/propsXml.d.ts
CHANGED
|
@@ -10,12 +10,20 @@ export interface PropsChild {
|
|
|
10
10
|
name: string;
|
|
11
11
|
/** This child's original XML fragment exactly as it was */
|
|
12
12
|
xml: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whatever stood between the child before this one and this one: line breaks a producer laid
|
|
15
|
+
* out, comments, anything that is not an element. Carried so that rewriting one child does not
|
|
16
|
+
* quietly drop the rest of what the fragment said. Absent rather than empty when nothing did.
|
|
17
|
+
*/
|
|
18
|
+
before?: string;
|
|
13
19
|
}
|
|
14
20
|
export interface Props {
|
|
15
21
|
/** The opening tag name exactly as written (e.g. `w:tcPr`) */
|
|
16
22
|
tag: string;
|
|
17
23
|
attrs: string | null;
|
|
18
24
|
children: PropsChild[];
|
|
25
|
+
/** The same, for what stood between the last child and the closing tag */
|
|
26
|
+
tail?: string;
|
|
19
27
|
}
|
|
20
28
|
/** The order the children are laid out in under `w:rPr` (CT_RPr) */
|
|
21
29
|
export declare const RUN_PR_ORDER: readonly string[];
|
|
@@ -27,18 +35,28 @@ export declare const TC_PR_ORDER: readonly string[];
|
|
|
27
35
|
export declare const TR_PR_ORDER: readonly string[];
|
|
28
36
|
/** The order the children are laid out in under `w:tblPr` (CT_TblPr) */
|
|
29
37
|
export declare const TBL_PR_ORDER: readonly string[];
|
|
38
|
+
/**
|
|
39
|
+
* What stood inside a single element, and "" for one that stood empty or cannot be made out.
|
|
40
|
+
*
|
|
41
|
+
* The opening tag is read rather than scanned for, so a `>` inside an attribute value does not
|
|
42
|
+
* pass for the end of it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function innerXml(xml: string): string;
|
|
30
45
|
/**
|
|
31
46
|
* Splits a formatting fragment into its opening tag and its list of children.
|
|
32
47
|
* null if its shape cannot be made out (in which case leaving the original untouched is the safe move).
|
|
33
48
|
*/
|
|
34
49
|
export declare function parseProps(xml: string): Props | null;
|
|
35
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* With nothing to say, the formatting fragment itself is not written. A fragment holding only
|
|
52
|
+
* whitespace says nothing; one holding a comment does, so it is written back.
|
|
53
|
+
*/
|
|
36
54
|
export declare function renderProps(props: Props): string;
|
|
37
55
|
/**
|
|
38
56
|
* Replaces a single child with new XML.
|
|
39
57
|
* A null `xml` removes that child. A child that was not there goes into the spot the order calls for.
|
|
40
58
|
*/
|
|
41
|
-
export declare function setPropsChild(
|
|
59
|
+
export declare function setPropsChild(props: Props, name: string, xml: string | null, order: readonly string[]): Props;
|
|
42
60
|
export declare function propsChild(children: PropsChild[], name: string): PropsChild | undefined;
|
|
43
61
|
/**
|
|
44
62
|
* Reads a single formatting fragment into an element.
|