@portone/docx-editor 0.2.0 → 0.3.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 +174 -0
- package/CONTRIBUTING.md +9 -1
- package/dist/DocxEditor.d.ts +6 -0
- package/dist/DocxEditor.js +12 -29
- package/dist/core.d.ts +23 -8
- package/dist/core.js +9 -4
- package/dist/docx/cloning.d.ts +38 -0
- package/dist/docx/cloning.js +74 -0
- package/dist/docx/commentOnlyChange.d.ts +14 -7
- package/dist/docx/commentOnlyChange.js +9 -139
- package/dist/docx/comments/constants.d.ts +3 -4
- package/dist/docx/comments/constants.js +4 -5
- package/dist/docx/comments/grammar.d.ts +86 -0
- package/dist/docx/comments/grammar.js +223 -0
- package/dist/docx/comments/parts.d.ts +52 -0
- package/dist/docx/comments/parts.js +279 -0
- package/dist/docx/comments/people.d.ts +11 -2
- package/dist/docx/comments/people.js +28 -106
- package/dist/docx/comments/policy.d.ts +7 -0
- package/dist/docx/comments/policy.js +43 -0
- package/dist/docx/comments/reading.d.ts +5 -3
- package/dist/docx/comments/reading.js +30 -29
- package/dist/docx/comments/writing.d.ts +19 -7
- package/dist/docx/comments/writing.js +105 -137
- package/dist/docx/documentSettings.d.ts +7 -0
- package/dist/docx/documentSettings.js +10 -2
- package/dist/docx/exportDocx.d.ts +40 -5
- package/dist/docx/exportDocx.js +71 -72
- package/dist/docx/exportRefs.d.ts +5 -2
- package/dist/docx/exportRefs.js +3 -1
- package/dist/docx/fidelity.d.ts +43 -0
- package/dist/docx/fidelity.js +64 -0
- package/dist/docx/formatting/attrs.d.ts +27 -0
- package/dist/docx/formatting/attrs.js +31 -0
- package/dist/docx/formatting/context.d.ts +31 -0
- package/dist/docx/formatting/context.js +42 -0
- package/dist/docx/formatting/direct.d.ts +6 -5
- package/dist/docx/formatting/direct.js +52 -81
- package/dist/docx/formatting/resolve.d.ts +45 -0
- package/dist/docx/formatting/resolve.js +140 -0
- package/dist/docx/formatting/runProperties.d.ts +93 -0
- package/dist/docx/formatting/runProperties.js +316 -0
- package/dist/docx/formatting/styles.js +3 -3
- package/dist/docx/formatting/tabStops.js +7 -22
- package/dist/docx/formatting.d.ts +4 -1
- package/dist/docx/formatting.js +4 -1
- package/dist/docx/headersFooters.js +13 -22
- package/dist/docx/identities.d.ts +67 -0
- package/dist/docx/identities.js +174 -0
- package/dist/docx/importDocx.d.ts +12 -1
- package/dist/docx/importDocx.js +72 -79
- package/dist/docx/importParagraph.d.ts +1 -1
- package/dist/docx/importParagraph.js +4 -3
- package/dist/docx/importTable.d.ts +1 -1
- package/dist/docx/importTable.js +17 -1
- package/dist/docx/invariants.d.ts +33 -0
- package/dist/docx/invariants.js +256 -0
- package/dist/docx/media.d.ts +6 -4
- package/dist/docx/media.js +6 -37
- package/dist/docx/newLists.d.ts +20 -0
- package/dist/docx/newLists.js +36 -0
- package/dist/docx/notes.js +6 -12
- package/dist/docx/numberingPlanner.d.ts +8 -0
- package/dist/docx/numberingPlanner.js +19 -0
- package/dist/docx/packageParts.d.ts +42 -0
- package/dist/docx/packageParts.js +135 -0
- package/dist/docx/pageGeometry.d.ts +2 -0
- package/dist/docx/pageGeometry.js +18 -9
- package/dist/docx/paraProps.d.ts +10 -22
- package/dist/docx/paraProps.js +75 -99
- package/dist/docx/partPlan.d.ts +36 -0
- package/dist/docx/partPlan.js +59 -0
- package/dist/docx/protectionPolicy.d.ts +127 -0
- package/dist/docx/protectionPolicy.js +169 -0
- package/dist/docx/relationships.d.ts +1 -1
- package/dist/docx/relationships.js +15 -15
- package/dist/docx/runProps.d.ts +9 -22
- package/dist/docx/runProps.js +15 -168
- package/dist/docx/scan.js +20 -52
- package/dist/docx/sdt.js +14 -37
- package/dist/docx/sdtProps.d.ts +8 -1
- package/dist/docx/sdtProps.js +10 -0
- package/dist/docx/serializeBlock.d.ts +2 -0
- package/dist/docx/serializeBlock.js +9 -7
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +63 -32
- package/dist/docx/session.d.ts +42 -14
- package/dist/docx/session.js +43 -17
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +130 -185
- package/dist/docx/tableFormatting/reading.d.ts +16 -6
- package/dist/docx/tableFormatting/reading.js +40 -20
- package/dist/docx/tableTemplate.js +37 -5
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/download.d.ts +8 -5
- package/dist/download.js +2 -0
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +20 -30
- 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.d.ts +6 -2
- package/dist/editor/commands/comments/editing.js +21 -36
- package/dist/editor/commands/exportQueries.d.ts +15 -0
- package/dist/editor/commands/exportQueries.js +14 -0
- package/dist/editor/commands/fidelityQueries.d.ts +12 -0
- package/dist/editor/commands/fidelityQueries.js +8 -0
- package/dist/editor/commands/formatting/editing.d.ts +2 -2
- package/dist/editor/commands/formatting/editing.js +26 -108
- package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
- package/dist/editor/commands/formatting/propertyCommands.js +103 -0
- package/dist/editor/commands/formatting/shared.d.ts +3 -3
- package/dist/editor/commands/formatting/shared.js +5 -2
- package/dist/editor/commands/historyCommands.js +2 -1
- package/dist/editor/commands/indentCommands.js +5 -4
- package/dist/editor/commands/index.d.ts +11 -0
- package/dist/editor/commands/index.js +5 -0
- package/dist/editor/commands/linkCommands.js +5 -6
- package/dist/editor/commands/listCommands.js +8 -10
- package/dist/editor/commands/lockCommands.d.ts +7 -1
- package/dist/editor/commands/lockCommands.js +35 -37
- package/dist/editor/commands/paragraphCommands.js +28 -31
- package/dist/editor/commands/spacingCommands.js +1 -1
- package/dist/editor/commands/tabCommands.js +4 -3
- package/dist/editor/createEditor.d.ts +17 -29
- package/dist/editor/createEditor.js +61 -67
- package/dist/editor/documentStyles.d.ts +11 -35
- package/dist/editor/documentStyles.js +9 -52
- package/dist/editor/editorDocument.d.ts +56 -0
- package/dist/editor/editorDocument.js +74 -0
- package/dist/editor/externalClipboard.js +166 -33
- package/dist/editor/insertImage.js +4 -3
- package/dist/editor/insertTable.js +4 -3
- package/dist/editor/paragraphEdits.d.ts +11 -18
- package/dist/editor/paragraphEdits.js +7 -16
- package/dist/editor/plugins/displayDerivation.d.ts +47 -0
- package/dist/editor/plugins/displayDerivation.js +78 -0
- package/dist/editor/plugins/documentProtection.d.ts +3 -3
- package/dist/editor/plugins/imagePaste.js +4 -3
- package/dist/editor/plugins/keymap.js +18 -3
- package/dist/editor/plugins/lockedContent.d.ts +4 -4
- package/dist/editor/plugins/lockedContent.js +1 -1
- package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
- package/dist/editor/plugins/numberingDecorations.js +7 -17
- package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
- package/dist/editor/plugins/paragraphDisplay.js +51 -0
- package/dist/editor/plugins/tabLayout.js +1 -1
- package/dist/editor/plugins/tableDisplay.d.ts +6 -0
- package/dist/editor/plugins/tableDisplay.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/model/format.d.ts +30 -8
- package/dist/model/format.js +35 -22
- package/dist/model/tabStops.d.ts +9 -0
- package/dist/model/tabStops.js +18 -0
- package/dist/numbering/listTemplate.js +49 -10
- package/dist/numbering/parseNumbering.d.ts +14 -1
- package/dist/numbering/parseNumbering.js +35 -22
- package/dist/numbering/writeNumbering.d.ts +3 -4
- package/dist/numbering/writeNumbering.js +14 -25
- package/dist/ooxml/childOrder.d.ts +34 -0
- package/dist/ooxml/childOrder.js +496 -0
- package/dist/ooxml/element.d.ts +65 -0
- package/dist/ooxml/element.js +68 -0
- package/dist/ooxml/errors.d.ts +3 -2
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.d.ts +4 -0
- package/dist/ooxml/image.js +3 -5
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/partSplice.d.ts +67 -0
- package/dist/ooxml/partSplice.js +169 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/props.d.ts +112 -0
- package/dist/ooxml/props.js +214 -0
- package/dist/ooxml/simpleTypes.d.ts +103 -0
- package/dist/ooxml/simpleTypes.js +182 -0
- package/dist/ooxml/tabStops.js +8 -27
- package/dist/ooxml/tagScan.d.ts +34 -0
- package/dist/ooxml/tagScan.js +108 -0
- package/dist/ooxml/units.d.ts +25 -13
- package/dist/ooxml/units.js +65 -31
- package/dist/ooxml/xml.d.ts +52 -0
- package/dist/ooxml/xml.js +64 -5
- package/dist/page/blockKinds.d.ts +103 -0
- package/dist/page/blockKinds.js +11 -0
- package/dist/page/kinds/index.d.ts +6 -0
- package/dist/page/kinds/index.js +10 -0
- package/dist/page/kinds/paragraphKind.d.ts +10 -0
- package/dist/page/kinds/paragraphKind.js +71 -0
- package/dist/page/kinds/tableKind.d.ts +10 -0
- package/dist/page/kinds/tableKind.js +178 -0
- package/dist/page/measureBlocks.d.ts +3 -1
- package/dist/page/measureBlocks.js +24 -25
- package/dist/page/pageDecorations.d.ts +27 -28
- package/dist/page/pageDecorations.js +70 -151
- package/dist/page/pageLayout.d.ts +10 -62
- package/dist/page/pageLayout.js +60 -53
- package/dist/page/usePageLayout.d.ts +2 -2
- package/dist/page/usePageLayout.js +3 -11
- package/dist/schema/attrRoles.d.ts +42 -0
- package/dist/schema/attrRoles.js +141 -0
- package/dist/schema/displayDerivation.d.ts +82 -0
- package/dist/schema/displayDerivation.js +130 -0
- package/dist/schema/docxSchema.d.ts +9 -0
- package/dist/schema/docxSchema.js +237 -114
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +91 -0
- package/dist/schema/guards.js +108 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/locks.d.ts +17 -77
- package/dist/schema/locks.js +26 -70
- package/dist/schema/preservedGuards.d.ts +50 -0
- package/dist/schema/preservedGuards.js +80 -0
- package/dist/schema/protection.d.ts +17 -4
- package/dist/schema/protection.js +4 -2
- package/dist/schema/sourceEquality.d.ts +19 -0
- package/dist/schema/sourceEquality.js +39 -0
- package/dist/styles/inlineStyle.js +15 -6
- package/dist/table/cellFormatting.d.ts +8 -1
- package/dist/table/cellFormatting.js +10 -17
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +20 -18
- package/dist/table/format.d.ts +3 -0
- package/dist/table/format.js +3 -9
- package/dist/table/gridBorders.d.ts +14 -16
- package/dist/table/gridBorders.js +3 -41
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +4 -8
- package/dist/table/merge.js +5 -5
- package/package.json +10 -3
- package/dist/docx/comments/contentTypes.d.ts +0 -7
- package/dist/docx/comments/contentTypes.js +0 -38
- package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
- package/dist/docx/formatting/effectiveParagraph.js +0 -81
- package/dist/docx/propsXml.d.ts +0 -49
- package/dist/docx/propsXml.js +0 -302
- package/dist/docx/uniqueControls.d.ts +0 -14
- package/dist/docx/uniqueControls.js +0 -62
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/commentReservations.d.ts +0 -5
- package/dist/editor/plugins/commentReservations.js +0 -26
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
- package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
- package/dist/editor/plugins/styledParagraphs.js +0 -65
- package/dist/page/tableMeasurements.d.ts +0 -10
- package/dist/page/tableMeasurements.js +0 -115
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a protection level lets a change rewrite, as one value every side of the package reads.
|
|
3
|
+
*
|
|
4
|
+
* A level says three things: which parts of the package a change may write, what an entry in one
|
|
5
|
+
* of those parts may look like, and how the story reads once the markup the level is about is
|
|
6
|
+
* taken out of it. The part planners write those parts and the verifier judges them, and each
|
|
7
|
+
* carried its own copy of the first two with nothing but a test holding them together. Declaring
|
|
8
|
+
* them once means a part the writer starts adding is a part the verifier already knows.
|
|
9
|
+
*
|
|
10
|
+
* A level with no policy registered is one no file is judged under: `protectionPolicyFor` answers
|
|
11
|
+
* undefined, and a caller holding the level alone has nothing to run.
|
|
12
|
+
*/
|
|
13
|
+
import { type XmlParser } from "../ooxml/xml";
|
|
14
|
+
import type { EditableComments, EditingProtection } from "../schema/protection";
|
|
15
|
+
import { type DocxBytes } from "./importDocx";
|
|
16
|
+
import type { SessionStore } from "./session";
|
|
17
|
+
import { type Story } from "./storyProjection";
|
|
18
|
+
/** One entry of a story part: the element read out of it, and its text for the byte-for-byte case */
|
|
19
|
+
export interface StoryEntry {
|
|
20
|
+
el: Element;
|
|
21
|
+
xml: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* How a part's entries are read.
|
|
25
|
+
*
|
|
26
|
+
* `arrived` is the reference a submission is held against rather than something to judge, so it
|
|
27
|
+
* takes what it can read and passes over the rest, leaving an entry standing on something
|
|
28
|
+
* unreadable to be judged as one that appeared. `submitted` is what is judged.
|
|
29
|
+
*/
|
|
30
|
+
export type EntryReading = "arrived" | "submitted";
|
|
31
|
+
/** What a judgement may be told about the reader beyond the two documents */
|
|
32
|
+
export interface PolicyOptions {
|
|
33
|
+
editableComments: EditableComments;
|
|
34
|
+
}
|
|
35
|
+
/** What a judgement over two files may be told beyond that */
|
|
36
|
+
export interface VerifyOptions extends PolicyOptions {
|
|
37
|
+
xmlParser?: XmlParser;
|
|
38
|
+
}
|
|
39
|
+
/** One package part a protection lets an editor rewrite, and how a rewritten entry is judged */
|
|
40
|
+
export interface StoryPartKind {
|
|
41
|
+
relType: string;
|
|
42
|
+
contentType: string;
|
|
43
|
+
/** Where the reader found this part, and null for a package that holds none */
|
|
44
|
+
pathIn(session: SessionStore): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Where the part goes when it is written: where it already sits, or the first name in the
|
|
47
|
+
* story's own folder that no part of the package has taken.
|
|
48
|
+
*/
|
|
49
|
+
writePathIn(session: SessionStore): string;
|
|
50
|
+
/**
|
|
51
|
+
* The part's entries keyed by the id the story or a sibling part refers to them by, and null
|
|
52
|
+
* for a submitted part this editor's writer could not have put out at all.
|
|
53
|
+
*/
|
|
54
|
+
entriesIn(session: SessionStore, reading: EntryReading): ReadonlyMap<string, StoryEntry> | null;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the element the entries stand in came back as it left, save for the compatibility
|
|
57
|
+
* markup the writer declares on a part it writes a thread key into.
|
|
58
|
+
*/
|
|
59
|
+
rootKept(before: SessionStore, after: SessionStore): boolean;
|
|
60
|
+
/** The keys this file still stands behind, which is what an entry has to be keyed by */
|
|
61
|
+
referents(story: Story): ReadonlySet<string>;
|
|
62
|
+
/** Grammar alone: whether the entry is one this editor's writer could have put out, whoever it belongs to */
|
|
63
|
+
wellFormed(entry: Element): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the entry came back differing from the one that arrived in nothing but a change this
|
|
66
|
+
* protection leaves to everyone. Such an entry is nobody's rewrite, so it is held neither to
|
|
67
|
+
* the grammar this editor writes in nor to who owns it.
|
|
68
|
+
*/
|
|
69
|
+
anyonesChange(entry: Element, original: Element): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Permission alone: whether `authorId` may have written (`original === null`) or rewritten this
|
|
72
|
+
* entry under `options`. `session` is the submission's, so a kind can look across at a sibling
|
|
73
|
+
* part, the way a comment's author resolves through the people part.
|
|
74
|
+
*/
|
|
75
|
+
allowed(entry: Element, original: Element | null, authorId: string, options: PolicyOptions, session: SessionStore): boolean;
|
|
76
|
+
}
|
|
77
|
+
/** The two reasons the package comparison answers with, whatever the policy */
|
|
78
|
+
export type PackageReason = "part-changed" | "relationship-changed";
|
|
79
|
+
/**
|
|
80
|
+
* The shape every verdict takes: a story reason carries no part, a part reason names the part it
|
|
81
|
+
* was reached over. A public verdict keeps its own declaration and is one instance of this.
|
|
82
|
+
*/
|
|
83
|
+
export type ChangeVerdict<StoryReason extends string, PartReason extends string> = {
|
|
84
|
+
ok: true;
|
|
85
|
+
} | {
|
|
86
|
+
ok: false;
|
|
87
|
+
reason: StoryReason;
|
|
88
|
+
} | {
|
|
89
|
+
ok: false;
|
|
90
|
+
reason: PartReason;
|
|
91
|
+
part: string;
|
|
92
|
+
};
|
|
93
|
+
export interface ProtectionPolicy<StoryReason extends string, PartReason extends string> {
|
|
94
|
+
level: EditingProtection;
|
|
95
|
+
parts: readonly StoryPartKind[];
|
|
96
|
+
/** The reason a rewritten entry is refused under when no part takes it as well formed and allowed */
|
|
97
|
+
rejectedMarkup: PartReason;
|
|
98
|
+
/** Whether the story reads the same once this protection's own markup is taken out */
|
|
99
|
+
storyKept(before: Story, after: Story, authorId: string, options: PolicyOptions): ChangeVerdict<StoryReason, PartReason>;
|
|
100
|
+
}
|
|
101
|
+
/** Records the policy under its level and hands it back, so a module declares and registers at once */
|
|
102
|
+
export declare function registerProtectionPolicy<S extends string, P extends string>(policy: ProtectionPolicy<S, P>): ProtectionPolicy<S, P>;
|
|
103
|
+
/**
|
|
104
|
+
* The policy for a level, and undefined for a level no loaded module declares one for, `none` and
|
|
105
|
+
* `readOnly` among them.
|
|
106
|
+
*/
|
|
107
|
+
export declare function protectionPolicyFor(level: EditingProtection): ProtectionPolicy<string, string> | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Whether the parts this protection lets an edit rewrite came back holding only entries this
|
|
110
|
+
* editor writes, each of them one this author was in a position to write.
|
|
111
|
+
*
|
|
112
|
+
* This runs after the story has been judged, so a comment rewritten, re-anchored or deleted by the
|
|
113
|
+
* wrong hand is already named for what it is. What is left to this is everything the story cannot
|
|
114
|
+
* show: markup forged into a body, an entry nothing refers to, an identity recorded for somebody
|
|
115
|
+
* else.
|
|
116
|
+
*/
|
|
117
|
+
export declare function partsKept<P extends string>(policy: ProtectionPolicy<string, P>, before: Story, after: Story, authorId: string, options: PolicyOptions): ChangeVerdict<never, P>;
|
|
118
|
+
/**
|
|
119
|
+
* Whether the submitted file differs from the original in nothing this protection forbids.
|
|
120
|
+
*
|
|
121
|
+
* Both files are opened inside the one parser scope, so the parser named here is the parser both
|
|
122
|
+
* reads go through even though neither `importDocx` call is given it. The package is judged first,
|
|
123
|
+
* then the story, then the parts the policy excused from the package comparison: the parts are
|
|
124
|
+
* last so that an edit the wrong hand made is named for the edit rather than for the part it was
|
|
125
|
+
* written across.
|
|
126
|
+
*/
|
|
127
|
+
export declare function verifyChange<S extends string, P extends string>(policy: ProtectionPolicy<S, P>, original: DocxBytes, submitted: DocxBytes, authorId: string, { xmlParser, ...options }: VerifyOptions): ChangeVerdict<S, P | PackageReason>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// src/docx/protectionPolicy.ts
|
|
2
|
+
import {
|
|
3
|
+
decodeUtf8,
|
|
4
|
+
elementChildren,
|
|
5
|
+
parseXml,
|
|
6
|
+
withXmlParser
|
|
7
|
+
} from "../ooxml/xml.js";
|
|
8
|
+
import { importDocx } from "./importDocx.js";
|
|
9
|
+
import { CONTENT_TYPES_PATH } from "./packageParts.js";
|
|
10
|
+
import {
|
|
11
|
+
readRelationships,
|
|
12
|
+
relsPathOf
|
|
13
|
+
} from "./relationships.js";
|
|
14
|
+
import { isModelledBlock } from "./storyProjection.js";
|
|
15
|
+
var policies = /* @__PURE__ */ new Map();
|
|
16
|
+
function registerProtectionPolicy(policy) {
|
|
17
|
+
policies.set(policy.level, policy);
|
|
18
|
+
return policy;
|
|
19
|
+
}
|
|
20
|
+
function protectionPolicyFor(level) {
|
|
21
|
+
return policies.get(level);
|
|
22
|
+
}
|
|
23
|
+
function sameBytes(before, after) {
|
|
24
|
+
return before.length === after.length && before.every((byte, index) => byte === after[index]);
|
|
25
|
+
}
|
|
26
|
+
function partPathsOf(policy, session) {
|
|
27
|
+
return policy.parts.map((kind) => kind.pathIn(session));
|
|
28
|
+
}
|
|
29
|
+
function excusedPaths(policy, session) {
|
|
30
|
+
return partPathsOf(policy, session).filter(
|
|
31
|
+
(path) => path !== null
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function aroundTheStory(session) {
|
|
35
|
+
const preserved = session.blocks.filter((block) => !isModelledBlock(block.node)).map((block) => block.xml).join("");
|
|
36
|
+
return session.documentPrefix + preserved + session.documentSuffix;
|
|
37
|
+
}
|
|
38
|
+
function relationshipsKept(before, after, relTypes) {
|
|
39
|
+
if (new Set(before.map((entry) => entry.id)).size !== before.length || new Set(after.map((entry) => entry.id)).size !== after.length) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const now = new Map(after.map((entry) => [entry.id, entry]));
|
|
43
|
+
const kept = before.every((entry) => {
|
|
44
|
+
const current = now.get(entry.id);
|
|
45
|
+
return current !== void 0 && current.type === entry.type && current.target === entry.target && current.external === entry.external;
|
|
46
|
+
});
|
|
47
|
+
const ids = new Set(before.map((entry) => entry.id));
|
|
48
|
+
const parts = after.filter((entry) => !entry.external);
|
|
49
|
+
return kept && after.filter((entry) => !ids.has(entry.id)).every(
|
|
50
|
+
(entry) => relTypes.includes(entry.type) && !entry.external && parts.filter((other) => other.type === entry.type).length === 1
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function contentTypes(bytes) {
|
|
54
|
+
if (bytes === void 0) return /* @__PURE__ */ new Map();
|
|
55
|
+
const declared = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const el of elementChildren(
|
|
57
|
+
parseXml(decodeUtf8(bytes).text).documentElement
|
|
58
|
+
)) {
|
|
59
|
+
const key = el.localName === "Default" ? el.getAttribute("Extension") : el.localName === "Override" ? el.getAttribute("PartName") : null;
|
|
60
|
+
if (key !== null) declared.set(key, el.getAttribute("ContentType") ?? "");
|
|
61
|
+
}
|
|
62
|
+
return declared;
|
|
63
|
+
}
|
|
64
|
+
function contentTypesKept(before, after, declarable) {
|
|
65
|
+
for (const [key, type] of before) {
|
|
66
|
+
if (after.get(key) !== type) return false;
|
|
67
|
+
}
|
|
68
|
+
for (const [key, type] of after) {
|
|
69
|
+
if (!before.has(key) && !declarable.includes(type)) return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
function gainedPartsAreNew(policy, before, after) {
|
|
74
|
+
const had = partPathsOf(policy, before);
|
|
75
|
+
return partPathsOf(policy, after).every(
|
|
76
|
+
(path, kind) => path === null || path === had[kind] || !before.parts.has(path)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function packageKept(policy, before, after) {
|
|
80
|
+
if (before.mainPartPath !== after.mainPartPath) {
|
|
81
|
+
return { ok: false, reason: "part-changed", part: before.mainPartPath };
|
|
82
|
+
}
|
|
83
|
+
const relsPath = relsPathOf(before.mainPartPath);
|
|
84
|
+
const untouched = /* @__PURE__ */ new Set([
|
|
85
|
+
before.mainPartPath,
|
|
86
|
+
relsPath,
|
|
87
|
+
CONTENT_TYPES_PATH,
|
|
88
|
+
...excusedPaths(policy, before),
|
|
89
|
+
...excusedPaths(policy, after)
|
|
90
|
+
]);
|
|
91
|
+
for (const path of /* @__PURE__ */ new Set([...before.parts.keys(), ...after.parts.keys()])) {
|
|
92
|
+
if (untouched.has(path)) continue;
|
|
93
|
+
const was = before.parts.get(path);
|
|
94
|
+
const now = after.parts.get(path);
|
|
95
|
+
if (was === void 0 || now === void 0 || !sameBytes(was, now)) {
|
|
96
|
+
return { ok: false, reason: "part-changed", part: path };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!relationshipsKept(
|
|
100
|
+
readRelationships(before.parts, relsPath),
|
|
101
|
+
readRelationships(after.parts, relsPath),
|
|
102
|
+
policy.parts.map((kind) => kind.relType)
|
|
103
|
+
) || !gainedPartsAreNew(policy, before, after)) {
|
|
104
|
+
return { ok: false, reason: "relationship-changed", part: relsPath };
|
|
105
|
+
}
|
|
106
|
+
if (!contentTypesKept(
|
|
107
|
+
contentTypes(before.parts.get(CONTENT_TYPES_PATH)),
|
|
108
|
+
contentTypes(after.parts.get(CONTENT_TYPES_PATH)),
|
|
109
|
+
policy.parts.map((kind) => kind.contentType)
|
|
110
|
+
)) {
|
|
111
|
+
return { ok: false, reason: "part-changed", part: CONTENT_TYPES_PATH };
|
|
112
|
+
}
|
|
113
|
+
if (aroundTheStory(before) !== aroundTheStory(after)) {
|
|
114
|
+
return { ok: false, reason: "part-changed", part: before.mainPartPath };
|
|
115
|
+
}
|
|
116
|
+
return { ok: true };
|
|
117
|
+
}
|
|
118
|
+
function partKept(kind, before, after, authorId, options) {
|
|
119
|
+
const arrived = kind.entriesIn(before.session, "arrived") ?? /* @__PURE__ */ new Map();
|
|
120
|
+
const submitted = kind.entriesIn(after.session, "submitted");
|
|
121
|
+
if (submitted === null) return false;
|
|
122
|
+
if (!kind.rootKept(before.session, after.session)) return false;
|
|
123
|
+
const stoodBehindNow = kind.referents(after);
|
|
124
|
+
for (const [id, entry] of submitted) {
|
|
125
|
+
const original = arrived.get(id);
|
|
126
|
+
if (original && original.xml === entry.xml) continue;
|
|
127
|
+
if (!stoodBehindNow.has(id)) return false;
|
|
128
|
+
if (original && kind.anyonesChange(entry.el, original.el)) continue;
|
|
129
|
+
if (!kind.wellFormed(entry.el) || !kind.allowed(
|
|
130
|
+
entry.el,
|
|
131
|
+
original?.el ?? null,
|
|
132
|
+
authorId,
|
|
133
|
+
options,
|
|
134
|
+
after.session
|
|
135
|
+
)) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const stoodBehindBefore = kind.referents(before);
|
|
140
|
+
return Array.from(arrived.keys()).every(
|
|
141
|
+
(id) => stoodBehindBefore.has(id) || submitted.has(id)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
function partsKept(policy, before, after, authorId, options) {
|
|
145
|
+
for (const kind of policy.parts) {
|
|
146
|
+
const path = kind.pathIn(after.session) ?? kind.pathIn(before.session);
|
|
147
|
+
if (path === null) continue;
|
|
148
|
+
if (!partKept(kind, before, after, authorId, options)) {
|
|
149
|
+
return { ok: false, reason: policy.rejectedMarkup, part: path };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { ok: true };
|
|
153
|
+
}
|
|
154
|
+
function verifyChange(policy, original, submitted, authorId, { xmlParser, ...options }) {
|
|
155
|
+
return withXmlParser(xmlParser, () => {
|
|
156
|
+
const before = importDocx(original);
|
|
157
|
+
const after = importDocx(submitted);
|
|
158
|
+
const packaged = packageKept(policy, before.session, after.session);
|
|
159
|
+
if (!packaged.ok) return packaged;
|
|
160
|
+
const story = policy.storyKept(before, after, authorId, options);
|
|
161
|
+
return story.ok ? partsKept(policy, before, after, authorId, options) : story;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
export {
|
|
165
|
+
partsKept,
|
|
166
|
+
protectionPolicyFor,
|
|
167
|
+
registerProtectionPolicy,
|
|
168
|
+
verifyChange
|
|
169
|
+
};
|
|
@@ -20,7 +20,7 @@ export interface Relationship {
|
|
|
20
20
|
/** Set when the target is a file outside the package */
|
|
21
21
|
external: boolean;
|
|
22
22
|
}
|
|
23
|
-
export declare function readRelationships(parts:
|
|
23
|
+
export declare function readRelationships(parts: ReadonlyMap<string, Uint8Array>, relsPath: string): Relationship[];
|
|
24
24
|
/** A relationship an export puts in. The target is escaped here, so hand it in as it reads */
|
|
25
25
|
export interface NewRelationship {
|
|
26
26
|
type: string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/docx/relationships.ts
|
|
2
|
-
import {
|
|
2
|
+
import { elementXml } from "../ooxml/element.js";
|
|
3
|
+
import { rootPrefixOf, splicePart } from "../ooxml/partSplice.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";
|
|
@@ -47,28 +47,28 @@ function takeRelId(taken) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
function relationshipXml(added) {
|
|
51
|
-
const mode = added.external ?
|
|
52
|
-
return
|
|
50
|
+
function relationshipXml(added, prefix) {
|
|
51
|
+
const mode = added.external ? [["TargetMode", "External"]] : [];
|
|
52
|
+
return elementXml(`${prefix}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
|
-
const entries = added.map(relationshipXml).join("");
|
|
56
60
|
if (!original) {
|
|
61
|
+
const entries2 = added.map((entry) => relationshipXml(entry, "")).join("");
|
|
57
62
|
return encodeUtf8(
|
|
58
|
-
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="${RELATIONSHIPS_NS}">${
|
|
63
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="${RELATIONSHIPS_NS}">${entries2}</Relationships>`,
|
|
59
64
|
false
|
|
60
65
|
);
|
|
61
66
|
}
|
|
62
67
|
const { text, hadBom } = decodeUtf8(original);
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
throw new DocxExportError(
|
|
66
|
-
"malformed-xml",
|
|
67
|
-
"the relationships part has no closing tag"
|
|
68
|
-
);
|
|
69
|
-
}
|
|
68
|
+
const prefix = rootPrefixOf(text);
|
|
69
|
+
const entries = added.map((entry) => relationshipXml(entry, prefix)).join("");
|
|
70
70
|
return encodeUtf8(
|
|
71
|
-
text
|
|
71
|
+
splicePart(text, { root: "Relationships", append: entries }),
|
|
72
72
|
hadBom
|
|
73
73
|
);
|
|
74
74
|
}
|
package/dist/docx/runProps.d.ts
CHANGED
|
@@ -5,30 +5,18 @@
|
|
|
5
5
|
* even things we do not know about). So instead of building a new fragment we swap out just the one
|
|
6
6
|
* child. It is the same principle `tableFormatting` applies to `w:tcPr`.
|
|
7
7
|
*
|
|
8
|
+
* Which children a job swaps, and what it swaps them for, is the run property table's to say
|
|
9
|
+
* (`formatting/runProperties`); this module holds the surgery around it.
|
|
10
|
+
*
|
|
8
11
|
* The display values produced here are used on screen only. What goes back into the document is
|
|
9
12
|
* always the rPr string.
|
|
10
13
|
*/
|
|
11
14
|
import { type RunFormat } from "../model/format";
|
|
15
|
+
import { type EditableRunKey, type RunEdit } from "./formatting";
|
|
16
|
+
export type { RunEdit } from "./formatting";
|
|
17
|
+
export { matchesRunEdit } from "./formatting";
|
|
12
18
|
/** The character formatting that is toggled on and off */
|
|
13
19
|
export type RunToggle = "bold" | "italic" | "underline" | "strike";
|
|
14
|
-
/** A job that changes one piece of character formatting. A null value means withdrawing the setting */
|
|
15
|
-
export type RunEdit = {
|
|
16
|
-
kind: "toggle";
|
|
17
|
-
toggle: RunToggle;
|
|
18
|
-
on: boolean;
|
|
19
|
-
} | {
|
|
20
|
-
kind: "fontSize";
|
|
21
|
-
pt: number | null;
|
|
22
|
-
} | {
|
|
23
|
-
kind: "fontFamily";
|
|
24
|
-
name: string | null;
|
|
25
|
-
} | {
|
|
26
|
-
kind: "color";
|
|
27
|
-
hex: string | null;
|
|
28
|
-
} | {
|
|
29
|
-
kind: "background";
|
|
30
|
-
hex: string | null;
|
|
31
|
-
};
|
|
32
20
|
/** The formatting a run holds. The original XML and the display values derived from that XML form a pair */
|
|
33
21
|
export interface RunProps {
|
|
34
22
|
/** The whole `<w:rPr>...</w:rPr>` XML. null for a run with no formatting */
|
|
@@ -40,12 +28,11 @@ export declare function readRunProps(rPr: string | null): RunFormat | null;
|
|
|
40
28
|
/**
|
|
41
29
|
* The rPr and display values after changing one piece of character formatting.
|
|
42
30
|
*
|
|
43
|
-
* `
|
|
31
|
+
* `inherited` is everything the layers below the run lay down (`inheritedRunFormat` in
|
|
32
|
+
* `formatting`), which decides whether an off is pinned down or the element simply removed.
|
|
44
33
|
* null for an rPr whose shape could not be made out, or for a value that cannot be written into
|
|
45
34
|
* the document, in which case the original is left untouched.
|
|
46
35
|
*/
|
|
47
|
-
export declare function editRunProps(current: RunProps,
|
|
36
|
+
export declare function editRunProps<K extends EditableRunKey>(current: RunProps, inherited: RunFormat, edit: RunEdit<K>): RunProps | null;
|
|
48
37
|
/** Whether one of the toggled formats is on in these display values */
|
|
49
38
|
export declare function isRunToggleOn(format: RunFormat | null, toggle: RunToggle): boolean;
|
|
50
|
-
/** Whether this text is already in the state the job wants. If it already is, we leave it untouched */
|
|
51
|
-
export declare function matchesRunEdit(format: RunFormat | null, edit: RunEdit): boolean;
|
package/dist/docx/runProps.js
CHANGED
|
@@ -1,140 +1,20 @@
|
|
|
1
1
|
// src/docx/runProps.ts
|
|
2
2
|
import { toRunFormat } from "../model/format.js";
|
|
3
|
-
import { normalizeHex } from "../ooxml/units.js";
|
|
4
|
-
import { escapeXml, localPart } from "../ooxml/xml.js";
|
|
5
|
-
import { isEastAsianFontName } from "../styles/fontStack.js";
|
|
6
|
-
import { fontNamesOf, readRunFormat } from "./formatting.js";
|
|
7
3
|
import {
|
|
8
4
|
parseProps,
|
|
9
5
|
parsePropsXml,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
strike: ["strike"]
|
|
21
|
-
};
|
|
22
|
-
var TOGGLE_ON_VALUE = {
|
|
23
|
-
bold: null,
|
|
24
|
-
italic: null,
|
|
25
|
-
underline: "single",
|
|
26
|
-
strike: null
|
|
27
|
-
};
|
|
28
|
-
var TOGGLE_OFF_VALUE = {
|
|
29
|
-
bold: "0",
|
|
30
|
-
italic: "0",
|
|
31
|
-
underline: "none",
|
|
32
|
-
strike: "0"
|
|
33
|
-
};
|
|
34
|
-
var MAX_FONT_SIZE_PT = 819;
|
|
35
|
-
function elementXml(name, value) {
|
|
36
|
-
return value === null ? `<w:${name}/>` : `<w:${name} w:val="${value}"/>`;
|
|
37
|
-
}
|
|
38
|
-
function shadingXml(fill) {
|
|
39
|
-
return `<w:shd w:val="clear" w:color="auto" w:fill="${fill}"/>`;
|
|
40
|
-
}
|
|
41
|
-
function halfPoints(pt) {
|
|
42
|
-
if (!Number.isFinite(pt) || pt <= 0 || pt > MAX_FONT_SIZE_PT) return null;
|
|
43
|
-
const half = pt * 2;
|
|
44
|
-
return Number.isInteger(half) ? half : null;
|
|
45
|
-
}
|
|
46
|
-
var LATIN_SLOTS = ["ascii", "hAnsi"];
|
|
47
|
-
var EAST_ASIAN_SLOTS = [...LATIN_SLOTS, "eastAsia"];
|
|
48
|
-
function fontSlots(name, hasComplexScript) {
|
|
49
|
-
const slots = isEastAsianFontName(name) ? EAST_ASIAN_SLOTS : LATIN_SLOTS;
|
|
50
|
-
return hasComplexScript ? [...slots, "cs"] : slots;
|
|
51
|
-
}
|
|
52
|
-
function fontName(value) {
|
|
53
|
-
const name = value.trim();
|
|
54
|
-
return name.length > 0 && !/["';<>&]/.test(name) ? name : null;
|
|
55
|
-
}
|
|
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
|
-
function rFontsXml(current, name) {
|
|
66
|
-
const attrs = rFontsAttrs(current);
|
|
67
|
-
if (!attrs) return null;
|
|
68
|
-
const slots = fontSlots(
|
|
69
|
-
name,
|
|
70
|
-
attrs.some(([attr]) => localPart(attr) === "cs")
|
|
71
|
-
);
|
|
72
|
-
const dropped = slots.flatMap((slot) => [slot, ...THEME_ATTRS[slot] ?? []]);
|
|
73
|
-
const kept = attrs.filter(([attr]) => !dropped.includes(localPart(attr)));
|
|
74
|
-
const text = [
|
|
75
|
-
...slots.map((slot) => [`w:${slot}`, name]),
|
|
76
|
-
...kept
|
|
77
|
-
].map(([attr, value]) => `${attr}="${escapeXml(value)}"`).join(" ");
|
|
78
|
-
return `<w:rFonts ${text}/>`;
|
|
79
|
-
}
|
|
80
|
-
function hasChild(xml, name) {
|
|
81
|
-
if (xml === null) return false;
|
|
82
|
-
const props = parseProps(xml);
|
|
83
|
-
return props !== null && propsChild(props.children, name) !== void 0;
|
|
84
|
-
}
|
|
6
|
+
renderProps
|
|
7
|
+
} from "../ooxml/props.js";
|
|
8
|
+
import {
|
|
9
|
+
EMPTY_RUN_PROPS,
|
|
10
|
+
RUN_PROPERTIES,
|
|
11
|
+
readRunFormat,
|
|
12
|
+
runChildEdits,
|
|
13
|
+
withChildEdits
|
|
14
|
+
} from "./formatting.js";
|
|
15
|
+
import { matchesRunEdit } from "./formatting.js";
|
|
85
16
|
function propsOf(rPr) {
|
|
86
|
-
|
|
87
|
-
return parseProps(rPr);
|
|
88
|
-
}
|
|
89
|
-
function offEdit(name, value, inherited) {
|
|
90
|
-
return [name, inherited ? elementXml(name, value) : null];
|
|
91
|
-
}
|
|
92
|
-
function childEdits(edit, inherited, rFonts) {
|
|
93
|
-
switch (edit.kind) {
|
|
94
|
-
case "toggle": {
|
|
95
|
-
const names = TOGGLE_CHILDREN[edit.toggle];
|
|
96
|
-
if (edit.on) {
|
|
97
|
-
const value = TOGGLE_ON_VALUE[edit.toggle];
|
|
98
|
-
return names.map((name) => [name, elementXml(name, value)]);
|
|
99
|
-
}
|
|
100
|
-
const off = TOGGLE_OFF_VALUE[edit.toggle];
|
|
101
|
-
return names.map((name) => offEdit(name, off, inherited));
|
|
102
|
-
}
|
|
103
|
-
case "fontSize": {
|
|
104
|
-
if (edit.pt === null) {
|
|
105
|
-
return [
|
|
106
|
-
["sz", null],
|
|
107
|
-
["szCs", null]
|
|
108
|
-
];
|
|
109
|
-
}
|
|
110
|
-
const half = halfPoints(edit.pt);
|
|
111
|
-
if (half === null) return null;
|
|
112
|
-
return [
|
|
113
|
-
["sz", elementXml("sz", `${half}`)],
|
|
114
|
-
["szCs", elementXml("szCs", `${half}`)]
|
|
115
|
-
];
|
|
116
|
-
}
|
|
117
|
-
case "fontFamily": {
|
|
118
|
-
if (edit.name === null) return [["rFonts", null]];
|
|
119
|
-
const name = fontName(edit.name);
|
|
120
|
-
if (name === null) return null;
|
|
121
|
-
const xml = rFontsXml(rFonts, name);
|
|
122
|
-
return xml === null ? null : [["rFonts", xml]];
|
|
123
|
-
}
|
|
124
|
-
case "color": {
|
|
125
|
-
if (edit.hex === null) return [offEdit("color", "auto", inherited)];
|
|
126
|
-
const hex = normalizeHex(edit.hex);
|
|
127
|
-
return hex === null ? null : [["color", elementXml("color", hex)]];
|
|
128
|
-
}
|
|
129
|
-
case "background": {
|
|
130
|
-
const highlight = offEdit("highlight", "none", inherited);
|
|
131
|
-
if (edit.hex === null) {
|
|
132
|
-
return [["shd", inherited ? shadingXml("auto") : null], highlight];
|
|
133
|
-
}
|
|
134
|
-
const hex = normalizeHex(edit.hex);
|
|
135
|
-
return hex === null ? null : [["shd", shadingXml(hex)], highlight];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
17
|
+
return rPr === null ? EMPTY_RUN_PROPS : parseProps(rPr);
|
|
138
18
|
}
|
|
139
19
|
function readRunProps(rPr) {
|
|
140
20
|
return rPr === null ? null : readRunFormat(parsePropsXml(rPr));
|
|
@@ -155,49 +35,16 @@ function nextProps(rPr, previous) {
|
|
|
155
35
|
}
|
|
156
36
|
return { rPr, format };
|
|
157
37
|
}
|
|
158
|
-
function editRunProps(current,
|
|
38
|
+
function editRunProps(current, inherited, edit) {
|
|
159
39
|
const props = propsOf(current.rPr);
|
|
160
40
|
if (!props) return null;
|
|
161
|
-
const
|
|
162
|
-
const rFonts = propsChild(props.children, "rFonts")?.xml ?? null;
|
|
163
|
-
const edits = childEdits(edit, inherited, rFonts);
|
|
41
|
+
const edits = runChildEdits(edit, { rPr: props, inherited });
|
|
164
42
|
if (!edits) return null;
|
|
165
|
-
const
|
|
166
|
-
(kept, [name, xml]) => setPropsChild(kept, name, xml, RUN_PR_ORDER),
|
|
167
|
-
props.children
|
|
168
|
-
);
|
|
169
|
-
const rPr = renderProps({ ...props, children });
|
|
43
|
+
const rPr = renderProps(withChildEdits(props, edits));
|
|
170
44
|
return nextProps(rPr === "" ? null : rPr, current);
|
|
171
45
|
}
|
|
172
46
|
function isRunToggleOn(format, toggle) {
|
|
173
|
-
|
|
174
|
-
if (toggle === "bold") return format.bold === true;
|
|
175
|
-
if (toggle === "italic") return format.italic === true;
|
|
176
|
-
if (toggle === "strike") return format.strike === true;
|
|
177
|
-
return format.underline !== void 0;
|
|
178
|
-
}
|
|
179
|
-
function matchesRunEdit(format, edit) {
|
|
180
|
-
switch (edit.kind) {
|
|
181
|
-
case "toggle":
|
|
182
|
-
return isRunToggleOn(format, edit.toggle) === edit.on;
|
|
183
|
-
case "fontSize":
|
|
184
|
-
return (format?.fontSizePt ?? null) === edit.pt;
|
|
185
|
-
case "fontFamily": {
|
|
186
|
-
const names = fontNamesOf(format?.fontFamily);
|
|
187
|
-
if (edit.name === null) return names.length === 0;
|
|
188
|
-
return names.length === 1 && names[0] === fontName(edit.name);
|
|
189
|
-
}
|
|
190
|
-
case "color": {
|
|
191
|
-
const current = format?.color ?? null;
|
|
192
|
-
if (current === null || edit.hex === null) return current === edit.hex;
|
|
193
|
-
return normalizeHex(current) === normalizeHex(edit.hex);
|
|
194
|
-
}
|
|
195
|
-
case "background": {
|
|
196
|
-
if (format?.highlight !== void 0) return false;
|
|
197
|
-
const fill = format?.background ? normalizeHex(format.background) : null;
|
|
198
|
-
return fill === (edit.hex === null ? null : normalizeHex(edit.hex));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
47
|
+
return RUN_PROPERTIES[toggle].isOn(format ?? {});
|
|
201
48
|
}
|
|
202
49
|
export {
|
|
203
50
|
editRunProps,
|