@portone/docx-editor 0.2.0 → 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 +65 -0
- package/CONTRIBUTING.md +4 -0
- package/dist/docx/commentOnlyChange.d.ts +3 -1
- package/dist/docx/commentOnlyChange.js +38 -21
- package/dist/docx/comments/constants.d.ts +3 -3
- package/dist/docx/comments/constants.js +4 -3
- package/dist/docx/comments/grammar.d.ts +80 -0
- package/dist/docx/comments/grammar.js +213 -0
- package/dist/docx/comments/people.js +12 -14
- package/dist/docx/comments/reading.d.ts +5 -3
- package/dist/docx/comments/reading.js +23 -20
- package/dist/docx/comments/verifying.d.ts +44 -0
- package/dist/docx/comments/verifying.js +206 -0
- package/dist/docx/comments/writing.js +73 -46
- 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/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 +4 -3
- package/dist/editor/commands/canRunCommand.d.ts +2 -2
- package/dist/editor/commands/canRunCommand.js +1 -1
- package/dist/editor/commands/comments/editing.js +3 -6
- package/dist/editor/commands/historyCommands.js +2 -1
- package/dist/editor/commands/lockCommands.js +10 -1
- package/dist/editor/commands/tabCommands.js +4 -3
- package/dist/editor/createEditor.js +3 -9
- package/dist/editor/externalClipboard.js +166 -4
- package/dist/editor/insertImage.js +4 -3
- package/dist/editor/plugins/documentProtection.d.ts +3 -3
- package/dist/editor/plugins/lockedContent.d.ts +4 -4
- package/dist/editor/plugins/lockedContent.js +1 -1
- 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 +212 -101
- 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 +8 -35
- package/dist/schema/locks.js +29 -64
- package/dist/schema/preservedGuards.d.ts +30 -0
- package/dist/schema/preservedGuards.js +50 -0
- package/dist/schema/protection.d.ts +10 -3
- package/dist/schema/protection.js +2 -1
- package/dist/schema/sourceEquality.d.ts +27 -0
- package/dist/schema/sourceEquality.js +65 -0
- package/dist/table/cellFormatting.js +2 -1
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +2 -2
- package/dist/table/merge.js +1 -1
- package/package.json +4 -1
- 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,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;
|
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.
|
package/dist/docx/propsXml.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/docx/propsXml.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
elementChildren,
|
|
4
|
+
localPart,
|
|
5
|
+
namespaceDecls,
|
|
6
|
+
parseXml
|
|
7
|
+
} from "../ooxml/xml.js";
|
|
3
8
|
var RUN_PR_ORDER = [
|
|
4
9
|
"rStyle",
|
|
5
10
|
"rFonts",
|
|
@@ -179,6 +184,12 @@ function attrsOf(source, tag) {
|
|
|
179
184
|
const attrs = source.slice(tag.nameEnd, tag.end - closeLength).trim();
|
|
180
185
|
return attrs.length > 0 ? attrs : null;
|
|
181
186
|
}
|
|
187
|
+
function innerXml(xml) {
|
|
188
|
+
const open = readTag(xml, 0);
|
|
189
|
+
if (!open || xml[0] !== "<" || open.kind !== "open") return "";
|
|
190
|
+
const close = xml.lastIndexOf("</");
|
|
191
|
+
return close > open.end ? xml.slice(open.end, close) : "";
|
|
192
|
+
}
|
|
182
193
|
function parseProps(xml) {
|
|
183
194
|
const open = readTag(xml, 0);
|
|
184
195
|
if (!open || xml[0] !== "<") return null;
|
|
@@ -192,6 +203,11 @@ function parseProps(xml) {
|
|
|
192
203
|
let childStart = -1;
|
|
193
204
|
let childName = "";
|
|
194
205
|
let i = open.end;
|
|
206
|
+
let gapStart = open.end;
|
|
207
|
+
const gapBefore = (start) => {
|
|
208
|
+
const gap = xml.slice(gapStart, start);
|
|
209
|
+
return gap.length > 0 ? { before: gap } : {};
|
|
210
|
+
};
|
|
195
211
|
while (i < xml.length) {
|
|
196
212
|
const lt = xml.indexOf("<", i);
|
|
197
213
|
if (lt === -1) return null;
|
|
@@ -204,15 +220,18 @@ function parseProps(xml) {
|
|
|
204
220
|
if (depth === 0) {
|
|
205
221
|
if (tag.kind === "close") {
|
|
206
222
|
if (tag.name !== open.name || tag.end !== xml.length) return null;
|
|
207
|
-
|
|
223
|
+
const tail = xml.slice(gapStart, lt);
|
|
224
|
+
return tail.length > 0 ? { tag: open.name, attrs, children, tail } : { tag: open.name, attrs, children };
|
|
208
225
|
}
|
|
209
226
|
childStart = lt;
|
|
210
227
|
childName = tag.name;
|
|
211
228
|
if (tag.kind === "empty") {
|
|
212
229
|
children.push({
|
|
213
230
|
name: localPart(childName),
|
|
214
|
-
xml: xml.slice(childStart, tag.end)
|
|
231
|
+
xml: xml.slice(childStart, tag.end),
|
|
232
|
+
...gapBefore(childStart)
|
|
215
233
|
});
|
|
234
|
+
gapStart = tag.end;
|
|
216
235
|
} else {
|
|
217
236
|
depth = 1;
|
|
218
237
|
}
|
|
@@ -223,8 +242,10 @@ function parseProps(xml) {
|
|
|
223
242
|
if (depth === 0) {
|
|
224
243
|
children.push({
|
|
225
244
|
name: localPart(childName),
|
|
226
|
-
xml: xml.slice(childStart, tag.end)
|
|
245
|
+
xml: xml.slice(childStart, tag.end),
|
|
246
|
+
...gapBefore(childStart)
|
|
227
247
|
});
|
|
248
|
+
gapStart = tag.end;
|
|
228
249
|
}
|
|
229
250
|
}
|
|
230
251
|
i = tag.end;
|
|
@@ -232,9 +253,11 @@ function parseProps(xml) {
|
|
|
232
253
|
return null;
|
|
233
254
|
}
|
|
234
255
|
function renderProps(props) {
|
|
235
|
-
|
|
256
|
+
const tail = props.tail ?? "";
|
|
257
|
+
if (props.children.length === 0 && tail.trim().length === 0) return "";
|
|
236
258
|
const open = props.attrs ? `<${props.tag} ${props.attrs}>` : `<${props.tag}>`;
|
|
237
|
-
|
|
259
|
+
const inner = props.children.map((child) => (child.before ?? "") + child.xml).join("");
|
|
260
|
+
return open + inner + tail + `</${props.tag}>`;
|
|
238
261
|
}
|
|
239
262
|
function insertIndex(children, name, order) {
|
|
240
263
|
const target = order.indexOf(name);
|
|
@@ -248,37 +271,53 @@ function insertIndex(children, name, order) {
|
|
|
248
271
|
}
|
|
249
272
|
return children.length;
|
|
250
273
|
}
|
|
251
|
-
function
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
const child
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
274
|
+
function dropChildren(children, name) {
|
|
275
|
+
const kept = [];
|
|
276
|
+
let carried = "";
|
|
277
|
+
for (const child of children) {
|
|
278
|
+
const before = carried + (child.before ?? "");
|
|
279
|
+
if (child.name === name) {
|
|
280
|
+
carried = before;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
carried = "";
|
|
284
|
+
kept.push(
|
|
285
|
+
before === "" ? { name: child.name, xml: child.xml } : { ...child, before }
|
|
286
|
+
);
|
|
259
287
|
}
|
|
260
|
-
return
|
|
261
|
-
...children.slice(0, at),
|
|
262
|
-
child,
|
|
263
|
-
...children.slice(at + 1).filter((entry) => entry.name !== name)
|
|
264
|
-
];
|
|
288
|
+
return { kept, carried };
|
|
265
289
|
}
|
|
266
|
-
function
|
|
267
|
-
|
|
290
|
+
function tailWith(props, carried) {
|
|
291
|
+
const tail = carried + (props.tail ?? "");
|
|
292
|
+
return tail === "" ? {} : { tail };
|
|
268
293
|
}
|
|
269
|
-
function
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
294
|
+
function setPropsChild(props, name, xml, order) {
|
|
295
|
+
const at = props.children.findIndex((entry) => entry.name === name);
|
|
296
|
+
if (at !== -1 && xml !== null) {
|
|
297
|
+
const rest = dropChildren(props.children.slice(at + 1), name);
|
|
298
|
+
return {
|
|
299
|
+
...props,
|
|
300
|
+
children: [
|
|
301
|
+
...props.children.slice(0, at),
|
|
302
|
+
{ ...props.children[at], xml },
|
|
303
|
+
...rest.kept
|
|
304
|
+
],
|
|
305
|
+
...tailWith(props, rest.carried)
|
|
306
|
+
};
|
|
273
307
|
}
|
|
274
|
-
|
|
275
|
-
|
|
308
|
+
const { kept, carried } = dropChildren(props.children, name);
|
|
309
|
+
if (xml === null) {
|
|
310
|
+
return { ...props, children: kept, ...tailWith(props, carried) };
|
|
276
311
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
(
|
|
281
|
-
|
|
312
|
+
const index = insertIndex(kept, name, order);
|
|
313
|
+
return {
|
|
314
|
+
...props,
|
|
315
|
+
children: [...kept.slice(0, index), { name, xml }, ...kept.slice(index)],
|
|
316
|
+
...tailWith(props, carried)
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function propsChild(children, name) {
|
|
320
|
+
return children.find((child) => child.name === name);
|
|
282
321
|
}
|
|
283
322
|
function parsePropsXml(xml) {
|
|
284
323
|
try {
|
|
@@ -294,6 +333,7 @@ export {
|
|
|
294
333
|
TBL_PR_ORDER,
|
|
295
334
|
TC_PR_ORDER,
|
|
296
335
|
TR_PR_ORDER,
|
|
336
|
+
innerXml,
|
|
297
337
|
parseProps,
|
|
298
338
|
parsePropsXml,
|
|
299
339
|
propsChild,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/docx/relationships.ts
|
|
2
|
+
import { elementXml } from "../ooxml/element.js";
|
|
2
3
|
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
4
|
import {
|
|
4
5
|
decodeUtf8,
|
|
5
6
|
elementChildren,
|
|
6
7
|
encodeUtf8,
|
|
7
|
-
escapeXml,
|
|
8
8
|
parseXml
|
|
9
9
|
} from "../ooxml/xml.js";
|
|
10
10
|
var RELATIONSHIPS_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
@@ -48,8 +48,13 @@ function takeRelId(taken) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
function relationshipXml(added) {
|
|
51
|
-
const mode = added.external ?
|
|
52
|
-
return
|
|
51
|
+
const mode = added.external ? [["TargetMode", "External"]] : [];
|
|
52
|
+
return elementXml("Relationship", [
|
|
53
|
+
["Id", added.id],
|
|
54
|
+
["Type", added.type],
|
|
55
|
+
["Target", added.target],
|
|
56
|
+
...mode
|
|
57
|
+
]);
|
|
53
58
|
}
|
|
54
59
|
function relationshipsPart(original, added) {
|
|
55
60
|
const entries = added.map(relationshipXml).join("");
|
package/dist/docx/runProps.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// src/docx/runProps.ts
|
|
2
2
|
import { toRunFormat } from "../model/format.js";
|
|
3
|
+
import {
|
|
4
|
+
attrPairs,
|
|
5
|
+
attrValue,
|
|
6
|
+
elementXml
|
|
7
|
+
} from "../ooxml/element.js";
|
|
8
|
+
import { wName } from "../ooxml/names.js";
|
|
9
|
+
import { setAttr } from "../ooxml/precedence.js";
|
|
3
10
|
import { normalizeHex } from "../ooxml/units.js";
|
|
4
|
-
import { escapeXml, localPart } from "../ooxml/xml.js";
|
|
5
11
|
import { isEastAsianFontName } from "../styles/fontStack.js";
|
|
6
12
|
import { fontNamesOf, readRunFormat } from "./formatting.js";
|
|
7
13
|
import {
|
|
@@ -12,7 +18,6 @@ import {
|
|
|
12
18
|
renderProps,
|
|
13
19
|
setPropsChild
|
|
14
20
|
} from "./propsXml.js";
|
|
15
|
-
import { THEME_ATTRS } from "./theme.js";
|
|
16
21
|
var TOGGLE_CHILDREN = {
|
|
17
22
|
bold: ["b", "bCs"],
|
|
18
23
|
italic: ["i", "iCs"],
|
|
@@ -32,11 +37,15 @@ var TOGGLE_OFF_VALUE = {
|
|
|
32
37
|
strike: "0"
|
|
33
38
|
};
|
|
34
39
|
var MAX_FONT_SIZE_PT = 819;
|
|
35
|
-
function
|
|
36
|
-
return value === null ?
|
|
40
|
+
function valXml(name, value) {
|
|
41
|
+
return elementXml(wName(name), value === null ? [] : [[wName("val"), value]]);
|
|
37
42
|
}
|
|
38
43
|
function shadingXml(fill) {
|
|
39
|
-
return
|
|
44
|
+
return elementXml(wName("shd"), [
|
|
45
|
+
[wName("val"), "clear"],
|
|
46
|
+
[wName("color"), "auto"],
|
|
47
|
+
[wName("fill"), fill]
|
|
48
|
+
]);
|
|
40
49
|
}
|
|
41
50
|
function halfPoints(pt) {
|
|
42
51
|
if (!Number.isFinite(pt) || pt <= 0 || pt > MAX_FONT_SIZE_PT) return null;
|
|
@@ -53,29 +62,19 @@ function fontName(value) {
|
|
|
53
62
|
const name = value.trim();
|
|
54
63
|
return name.length > 0 && !/["';<>&]/.test(name) ? name : null;
|
|
55
64
|
}
|
|
56
|
-
function rFontsAttrs(xml) {
|
|
57
|
-
if (xml === null) return [];
|
|
58
|
-
const el = parsePropsXml(xml);
|
|
59
|
-
if (!el) return null;
|
|
60
|
-
return Array.from(el.attributes, (attr) => [
|
|
61
|
-
attr.name,
|
|
62
|
-
attr.value
|
|
63
|
-
]);
|
|
64
|
-
}
|
|
65
65
|
function rFontsXml(current, name) {
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
const el = current === null ? null : parsePropsXml(current);
|
|
67
|
+
if (current !== null && !el) return null;
|
|
68
|
+
const attrs = attrPairs(el);
|
|
69
|
+
const slots = fontSlots(name, attrValue(attrs, "cs") !== null);
|
|
70
|
+
const kept = slots.reduce(
|
|
71
|
+
(rest, slot) => setAttr(rest, "rFonts", slot, null),
|
|
72
|
+
attrs
|
|
71
73
|
);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const text = [
|
|
75
|
-
...slots.map((slot) => [`w:${slot}`, name]),
|
|
74
|
+
return elementXml(wName("rFonts"), [
|
|
75
|
+
...slots.map((slot) => [wName(slot), name]),
|
|
76
76
|
...kept
|
|
77
|
-
]
|
|
78
|
-
return `<w:rFonts ${text}/>`;
|
|
77
|
+
]);
|
|
79
78
|
}
|
|
80
79
|
function hasChild(xml, name) {
|
|
81
80
|
if (xml === null) return false;
|
|
@@ -87,7 +86,7 @@ function propsOf(rPr) {
|
|
|
87
86
|
return parseProps(rPr);
|
|
88
87
|
}
|
|
89
88
|
function offEdit(name, value, inherited) {
|
|
90
|
-
return [name, inherited ?
|
|
89
|
+
return [name, inherited ? valXml(name, value) : null];
|
|
91
90
|
}
|
|
92
91
|
function childEdits(edit, inherited, rFonts) {
|
|
93
92
|
switch (edit.kind) {
|
|
@@ -95,7 +94,7 @@ function childEdits(edit, inherited, rFonts) {
|
|
|
95
94
|
const names = TOGGLE_CHILDREN[edit.toggle];
|
|
96
95
|
if (edit.on) {
|
|
97
96
|
const value = TOGGLE_ON_VALUE[edit.toggle];
|
|
98
|
-
return names.map((name) => [name,
|
|
97
|
+
return names.map((name) => [name, valXml(name, value)]);
|
|
99
98
|
}
|
|
100
99
|
const off = TOGGLE_OFF_VALUE[edit.toggle];
|
|
101
100
|
return names.map((name) => offEdit(name, off, inherited));
|
|
@@ -110,8 +109,8 @@ function childEdits(edit, inherited, rFonts) {
|
|
|
110
109
|
const half = halfPoints(edit.pt);
|
|
111
110
|
if (half === null) return null;
|
|
112
111
|
return [
|
|
113
|
-
["sz",
|
|
114
|
-
["szCs",
|
|
112
|
+
["sz", valXml("sz", `${half}`)],
|
|
113
|
+
["szCs", valXml("szCs", `${half}`)]
|
|
115
114
|
];
|
|
116
115
|
}
|
|
117
116
|
case "fontFamily": {
|
|
@@ -124,7 +123,7 @@ function childEdits(edit, inherited, rFonts) {
|
|
|
124
123
|
case "color": {
|
|
125
124
|
if (edit.hex === null) return [offEdit("color", "auto", inherited)];
|
|
126
125
|
const hex = normalizeHex(edit.hex);
|
|
127
|
-
return hex === null ? null : [["color",
|
|
126
|
+
return hex === null ? null : [["color", valXml("color", hex)]];
|
|
128
127
|
}
|
|
129
128
|
case "background": {
|
|
130
129
|
const highlight = offEdit("highlight", "none", inherited);
|
|
@@ -162,11 +161,12 @@ function editRunProps(current, pPr, edit) {
|
|
|
162
161
|
const rFonts = propsChild(props.children, "rFonts")?.xml ?? null;
|
|
163
162
|
const edits = childEdits(edit, inherited, rFonts);
|
|
164
163
|
if (!edits) return null;
|
|
165
|
-
const
|
|
166
|
-
(
|
|
167
|
-
|
|
164
|
+
const rPr = renderProps(
|
|
165
|
+
edits.reduce(
|
|
166
|
+
(kept, [name, xml]) => setPropsChild(kept, name, xml, RUN_PR_ORDER),
|
|
167
|
+
props
|
|
168
|
+
)
|
|
168
169
|
);
|
|
169
|
-
const rPr = renderProps({ ...props, children });
|
|
170
170
|
return nextProps(rPr === "" ? null : rPr, current);
|
|
171
171
|
}
|
|
172
172
|
function isRunToggleOn(format, toggle) {
|