@docx4j/core-ts 0.3.1 → 0.4.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/README.md +3 -1
- package/dist/html/convert.d.mts +116 -0
- package/dist/html/convert.mjs +541 -0
- package/dist/html/elements.d.mts +19 -0
- package/dist/html/elements.mjs +182 -0
- package/dist/html/index.d.mts +2 -0
- package/dist/html/index.mjs +4 -0
- package/dist/model/content/Body.mjs +9 -1
- package/dist/model/content/ContentControl.d.mts +5 -1
- package/dist/model/content/ContentControl.mjs +18 -5
- package/dist/model/content/InlinePicture.d.mts +18 -3
- package/dist/model/content/InlinePicture.mjs +44 -7
- package/dist/model/content/Paragraph.d.mts +11 -11
- package/dist/model/content/Paragraph.mjs +30 -43
- package/dist/model/content/Range.mjs +3 -2
- package/dist/model/content/Table.d.mts +38 -0
- package/dist/model/content/Table.mjs +80 -2
- package/dist/model/content/index.d.mts +2 -1
- package/dist/model/content/index.mjs +2 -1
- package/dist/model/content/stylesFacade.d.mts +4 -2
- package/dist/model/content/stylesFacade.mjs +23 -6
- package/dist/model/content/tableColumns.d.mts +48 -0
- package/dist/model/content/tableColumns.mjs +484 -0
- package/dist/model/content/tracking.d.mts +14 -0
- package/dist/model/content/tracking.mjs +19 -10
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +6 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +19 -3
- package/dist/model/customxml/XmlMapping.d.mts +20 -4
- package/dist/model/customxml/XmlMapping.mjs +44 -6
- package/dist/model/customxml/bindings.d.mts +13 -3
- package/dist/model/customxml/bindings.mjs +38 -7
- package/dist/model/customxml/index.d.mts +4 -1
- package/dist/model/customxml/index.mjs +4 -1
- package/dist/model/customxml/insert.d.mts +10 -0
- package/dist/model/customxml/insert.mjs +13 -0
- package/dist/model/customxml/opendope.d.mts +32 -0
- package/dist/model/customxml/opendope.mjs +56 -0
- package/dist/model/customxml/pictures.d.mts +54 -0
- package/dist/model/customxml/pictures.mjs +186 -0
- package/dist/model/customxml/xhtml.d.mts +42 -0
- package/dist/model/customxml/xhtml.mjs +162 -0
- package/dist/model/fonts/RunFontSelector.d.mts +8 -1
- package/dist/model/fonts/RunFontSelector.mjs +6 -5
- package/dist/model/properties/PropertyResolver.d.mts +110 -10
- package/dist/model/properties/PropertyResolver.mjs +300 -31
- package/dist/model/properties/index.d.mts +3 -0
- package/dist/model/properties/index.mjs +2 -0
- package/dist/model/properties/table.d.mts +155 -0
- package/dist/model/properties/table.mjs +315 -0
- package/dist/model/properties/tableStyleConditions.d.mts +101 -0
- package/dist/model/properties/tableStyleConditions.mjs +296 -0
- package/dist/packages/WordprocessingMLPackage.mjs +1 -0
- package/dist/parts/wml/knownStyles.d.mts +1 -0
- package/dist/parts/wml/knownStyles.mjs +2233 -0
- package/dist/parts/wml/placeholderStyle.d.mts +3 -0
- package/dist/parts/wml/placeholderStyle.mjs +13 -0
- package/package.json +7 -2
|
@@ -8,7 +8,9 @@ export interface CustomXmlPartLookup {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* A subset of Office JS `Word.XmlMapping`: the content control's data binding. `isMapped` is
|
|
11
|
-
* whether the control has a `w:dataBinding
|
|
11
|
+
* whether the control has a `w:dataBinding` (or, on a rich text control or a repeating section,
|
|
12
|
+
* the `w15:dataBinding` Word writes there, which is what `setMapping` writes on one: CR-002
|
|
13
|
+
* section 40); `customXmlNode` evaluates the XPath now, so it needs
|
|
12
14
|
* the XPath engine to be warm (`await pkg.customXmlParts.load()` once, CR-002 section 3.6).
|
|
13
15
|
*/
|
|
14
16
|
export declare class XmlMapping {
|
|
@@ -33,13 +35,27 @@ export declare class XmlMapping {
|
|
|
33
35
|
* Binds the control to what the XPath selects, as Word's own mapping does: the part is the one
|
|
34
36
|
* given, else the one the control is already bound to, else the first custom XML part (the
|
|
35
37
|
* built-in property stores last) in which the XPath selects a node. Returns false and changes
|
|
36
|
-
* nothing when nothing is selected, which is what Word reports.
|
|
38
|
+
* nothing when nothing is selected, which is what Word reports. **The node's value then goes into
|
|
39
|
+
* the control**, whatever text it held: Office JS's `setMapping` on a plain text control holding
|
|
40
|
+
* "quick brown", on an empty one and on one already holding the value, and Word's XML Mapping
|
|
41
|
+
* Pane mapping an existing control, each showed the node's "Ann" and left the part unchanged
|
|
42
|
+
* (`test/README.md` check 34, 2026-10-03; CR-002 section 39). Before, only the binding was
|
|
43
|
+
* written and the values stayed apart until `applyBindings`.
|
|
37
44
|
*/
|
|
38
45
|
setMapping(xpath: string, prefixMappings?: string, part?: CustomXmlPart): boolean;
|
|
39
|
-
/** Binds to a node: its canonical XPath and the prefixes that path needs, as Word writes them. */
|
|
46
|
+
/** Binds to a node: its canonical XPath and the prefixes that path needs, as Word writes them; the node's value goes into the control (check 34). */
|
|
40
47
|
setMappingByNode(node: CustomXmlNode): boolean;
|
|
41
|
-
/** Removes the binding (`w:dataBinding`); the control keeps the content it shows. */
|
|
48
|
+
/** Removes the binding (`w:dataBinding` and `w15:dataBinding`); the control keeps the content it shows. */
|
|
42
49
|
delete(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Whether the control's binding is `w15:dataBinding`, as Word 2013 and later write it: on a rich
|
|
52
|
+
* text control (one with `w:richText`, or with no kind element at all) and on a repeating
|
|
53
|
+
* section. `w:dataBinding` on a rich text control makes it a plain text control to Word 15 and
|
|
54
|
+
* Word 2010 alike - shown from its node on open, given `<w:text/>` on saving, an edit written
|
|
55
|
+
* back as text (`test/README.md` check 35, 1f; CR-002 section 40).
|
|
56
|
+
*/
|
|
57
|
+
get usesW15(): boolean;
|
|
58
|
+
/** Writes the binding in the element the control's kind takes, replacing one in the other namespace where it stood. */
|
|
43
59
|
private write;
|
|
44
60
|
/** The parts to try, the customer's data before Word's property stores. */
|
|
45
61
|
private candidates;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
|
|
2
|
+
import * as w15el from '@docx4j/generated-objects-ts/el/org_docx4j_w15';
|
|
2
3
|
import { sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
|
|
3
4
|
import { canonicalXPathOf } from './xpath.mjs';
|
|
5
|
+
import { applyBindingTo } from './bindings.mjs';
|
|
4
6
|
/** The Word 2012 (w15) namespace; not imported from `ContentControl`, which imports this module. */
|
|
5
7
|
const W15_NS = 'http://schemas.microsoft.com/office/word/2012/wordml';
|
|
8
|
+
const W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
6
9
|
/**
|
|
7
10
|
* A subset of Office JS `Word.XmlMapping`: the content control's data binding. `isMapped` is
|
|
8
|
-
* whether the control has a `w:dataBinding
|
|
11
|
+
* whether the control has a `w:dataBinding` (or, on a rich text control or a repeating section,
|
|
12
|
+
* the `w15:dataBinding` Word writes there, which is what `setMapping` writes on one: CR-002
|
|
13
|
+
* section 40); `customXmlNode` evaluates the XPath now, so it needs
|
|
9
14
|
* the XPath engine to be warm (`await pkg.customXmlParts.load()` once, CR-002 section 3.6).
|
|
10
15
|
*/
|
|
11
16
|
export class XmlMapping {
|
|
@@ -58,7 +63,12 @@ export class XmlMapping {
|
|
|
58
63
|
* Binds the control to what the XPath selects, as Word's own mapping does: the part is the one
|
|
59
64
|
* given, else the one the control is already bound to, else the first custom XML part (the
|
|
60
65
|
* built-in property stores last) in which the XPath selects a node. Returns false and changes
|
|
61
|
-
* nothing when nothing is selected, which is what Word reports.
|
|
66
|
+
* nothing when nothing is selected, which is what Word reports. **The node's value then goes into
|
|
67
|
+
* the control**, whatever text it held: Office JS's `setMapping` on a plain text control holding
|
|
68
|
+
* "quick brown", on an empty one and on one already holding the value, and Word's XML Mapping
|
|
69
|
+
* Pane mapping an existing control, each showed the node's "Ann" and left the part unchanged
|
|
70
|
+
* (`test/README.md` check 34, 2026-10-03; CR-002 section 39). Before, only the binding was
|
|
71
|
+
* written and the values stayed apart until `applyBindings`.
|
|
62
72
|
*/
|
|
63
73
|
setMapping(xpath, prefixMappings, part) {
|
|
64
74
|
const candidates = part ? [part] : this.candidates();
|
|
@@ -67,25 +77,53 @@ export class XmlMapping {
|
|
|
67
77
|
if (!node)
|
|
68
78
|
continue;
|
|
69
79
|
this.write(xpath, prefixMappings !== null && prefixMappings !== void 0 ? prefixMappings : '', candidate.id);
|
|
80
|
+
applyBindingTo(this.contentControl);
|
|
70
81
|
return true;
|
|
71
82
|
}
|
|
72
83
|
return false;
|
|
73
84
|
}
|
|
74
|
-
/** Binds to a node: its canonical XPath and the prefixes that path needs, as Word writes them. */
|
|
85
|
+
/** Binds to a node: its canonical XPath and the prefixes that path needs, as Word writes them; the node's value goes into the control (check 34). */
|
|
75
86
|
setMappingByNode(node) {
|
|
76
87
|
const { xpath, prefixMappings } = canonicalXPathOf(node.node);
|
|
77
88
|
this.write(xpath, prefixMappings, node.ownerPart.id);
|
|
89
|
+
applyBindingTo(this.contentControl);
|
|
78
90
|
return true;
|
|
79
91
|
}
|
|
80
|
-
/** Removes the binding (`w:dataBinding`); the control keeps the content it shows. */
|
|
92
|
+
/** Removes the binding (`w:dataBinding` and `w15:dataBinding`); the control keeps the content it shows. */
|
|
81
93
|
delete() {
|
|
82
|
-
this.contentControl.removeProperty('dataBinding');
|
|
94
|
+
this.contentControl.removeProperty('dataBinding', W_NS);
|
|
95
|
+
this.contentControl.removeProperty('dataBinding', W15_NS);
|
|
83
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Whether the control's binding is `w15:dataBinding`, as Word 2013 and later write it: on a rich
|
|
99
|
+
* text control (one with `w:richText`, or with no kind element at all) and on a repeating
|
|
100
|
+
* section. `w:dataBinding` on a rich text control makes it a plain text control to Word 15 and
|
|
101
|
+
* Word 2010 alike - shown from its node on open, given `<w:text/>` on saving, an edit written
|
|
102
|
+
* back as text (`test/README.md` check 35, 1f; CR-002 section 40).
|
|
103
|
+
*/
|
|
104
|
+
get usesW15() {
|
|
105
|
+
const type = this.contentControl.type;
|
|
106
|
+
return type === 'RichText' || type === 'RepeatingSection';
|
|
107
|
+
}
|
|
108
|
+
/** Writes the binding in the element the control's kind takes, replacing one in the other namespace where it stood. */
|
|
84
109
|
write(xpath, prefixMappings, storeItemID) {
|
|
110
|
+
var _a, _b;
|
|
85
111
|
const binding = { TYPE_NAME: 'org_docx4j_wml.CTDataBinding', xpath, storeItemID };
|
|
86
112
|
if (prefixMappings !== '')
|
|
87
113
|
binding.prefixMappings = prefixMappings;
|
|
88
|
-
this.
|
|
114
|
+
const w15 = this.usesW15;
|
|
115
|
+
const element = (w15 ? w15el.dataBinding(binding) : el.dataBinding(binding));
|
|
116
|
+
const items = (_a = this.contentControl.sdt.sdtPr) === null || _a === void 0 ? void 0 : _a.rPrOrAliasOrLock;
|
|
117
|
+
const other = w15 ? W_NS : W15_NS;
|
|
118
|
+
const at = (_b = items === null || items === void 0 ? void 0 : items.findIndex((i) => i.name.localPart === 'dataBinding' && i.name.namespaceURI === other)) !== null && _b !== void 0 ? _b : -1;
|
|
119
|
+
if (items && at >= 0) {
|
|
120
|
+
if (items.some((i) => i.name.localPart === 'dataBinding' && i.name.namespaceURI === element.name.namespaceURI))
|
|
121
|
+
items.splice(at, 1);
|
|
122
|
+
else
|
|
123
|
+
items[at] = element;
|
|
124
|
+
}
|
|
125
|
+
// putProperty also lists w15 in the part's mc:Ignorable when the element is a w15 one
|
|
126
|
+
this.contentControl.putProperty(element);
|
|
89
127
|
}
|
|
90
128
|
/** The parts to try, the customer's data before Word's property stores. */
|
|
91
129
|
candidates() {
|
|
@@ -12,7 +12,16 @@ export interface BindingResult {
|
|
|
12
12
|
updated: number;
|
|
13
13
|
/** Controls left alone: the part is missing, the XPath selects nothing, or the kind is not handled. */
|
|
14
14
|
skipped: number;
|
|
15
|
+
/** What the XHTML and picture passes left or dropped, one line each (CR-005 sections 8.2 and 9; REQ-076). */
|
|
16
|
+
notes?: string[];
|
|
15
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether a node's value is a Flat OPC package: what Word writes into the node of a mapped rich
|
|
20
|
+
* text control when its content is edited, a whole `pkg:package` as escaped text (`test/README.md`
|
|
21
|
+
* check 35). It is content, never a control's text - Word 2010 shows it as raw XML in a plain text
|
|
22
|
+
* control bound to the same node - so neither direction moves it as text (CR-002 section 40).
|
|
23
|
+
*/
|
|
24
|
+
export declare function holdsFlatOpc(value: string): boolean;
|
|
16
25
|
/** docx4j ValueInserterPlainTextImpl: the runs for a value, with the w:sdtPr run properties. */
|
|
17
26
|
export declare function runsForValue(value: string, rPr: wml.RPr | undefined, multiLine: boolean): Element[];
|
|
18
27
|
/**
|
|
@@ -24,10 +33,11 @@ export declare function runsForValue(value: string, rPr: wml.RPr | undefined, mu
|
|
|
24
33
|
export declare function formatDate(pattern: string, date: Date, locale?: string): string;
|
|
25
34
|
/**
|
|
26
35
|
* Pushes the custom XML value into one control (docx4j BindingHandler's per-sdt work). Returns
|
|
27
|
-
* false when nothing was written: no binding, no part, an XPath that selects nothing,
|
|
28
|
-
* this phase does not handle (
|
|
36
|
+
* false when nothing was written: no binding, no part, an XPath that selects nothing, a kind
|
|
37
|
+
* this phase does not handle (explicit rich text), a node holding a Flat OPC package, or a
|
|
38
|
+
* picture control whose node holds no image (`notes` says which; CR-005 section 9).
|
|
29
39
|
*/
|
|
30
|
-
export declare function applyBindingTo(control: ContentControl): boolean;
|
|
40
|
+
export declare function applyBindingTo(control: ContentControl, notes?: string[]): boolean;
|
|
31
41
|
/** `applyBindings` over a list of controls. */
|
|
32
42
|
export declare function applyBindingsTo(controls: ContentControl[]): BindingResult;
|
|
33
43
|
/**
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
2
2
|
import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
|
|
3
3
|
import { sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
|
+
import { bindPicture } from './pictures.mjs';
|
|
4
5
|
/** The style Word gives placeholder text, and the text docx4j's placeholder.xml carries. */
|
|
5
6
|
export const PLACEHOLDER_STYLE = 'PlaceholderText';
|
|
6
7
|
export const PLACEHOLDER_TEXT = 'Click here to enter text.';
|
|
8
|
+
/**
|
|
9
|
+
* Whether a node's value is a Flat OPC package: what Word writes into the node of a mapped rich
|
|
10
|
+
* text control when its content is edited, a whole `pkg:package` as escaped text (`test/README.md`
|
|
11
|
+
* check 35). It is content, never a control's text - Word 2010 shows it as raw XML in a plain text
|
|
12
|
+
* control bound to the same node - so neither direction moves it as text (CR-002 section 40).
|
|
13
|
+
*/
|
|
14
|
+
export function holdsFlatOpc(value) {
|
|
15
|
+
return /^\s*(<\?[^>]*\?>\s*)*<([\w.-]+:)?package[\s>]/.test(value)
|
|
16
|
+
&& value.includes('http://schemas.microsoft.com/office/2006/xmlPackage');
|
|
17
|
+
}
|
|
7
18
|
function isTrue(value) {
|
|
8
19
|
const v = value.trim().toLowerCase();
|
|
9
20
|
return v === 'true' || v === '1' || v === 'on' || v === 'yes';
|
|
@@ -103,10 +114,11 @@ function dateOf(value) {
|
|
|
103
114
|
}
|
|
104
115
|
/**
|
|
105
116
|
* Pushes the custom XML value into one control (docx4j BindingHandler's per-sdt work). Returns
|
|
106
|
-
* false when nothing was written: no binding, no part, an XPath that selects nothing,
|
|
107
|
-
* this phase does not handle (
|
|
117
|
+
* false when nothing was written: no binding, no part, an XPath that selects nothing, a kind
|
|
118
|
+
* this phase does not handle (explicit rich text), a node holding a Flat OPC package, or a
|
|
119
|
+
* picture control whose node holds no image (`notes` says which; CR-005 section 9).
|
|
108
120
|
*/
|
|
109
|
-
export function applyBindingTo(control) {
|
|
121
|
+
export function applyBindingTo(control, notes) {
|
|
110
122
|
var _a;
|
|
111
123
|
const mapping = control.xmlMapping;
|
|
112
124
|
if (!mapping.isMapped)
|
|
@@ -115,14 +127,22 @@ export function applyBindingTo(control) {
|
|
|
115
127
|
if (!node)
|
|
116
128
|
return false;
|
|
117
129
|
const value = node.text.trim();
|
|
118
|
-
// A picture
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
// A picture control's node holds base64 image data: the picture's a:blip is pointed at a new
|
|
131
|
+
// image part and the drawing kept, as docx4j's bind.xslt mode picture3 does (CR-005 section 9).
|
|
132
|
+
if (control.type === 'Picture') {
|
|
133
|
+
const outcome = bindPicture(control, value, { kind: 'keep' }, control.title || control.tag || `picture control ${control.id}`);
|
|
134
|
+
if (outcome.note)
|
|
135
|
+
notes === null || notes === void 0 ? void 0 : notes.push(outcome.note);
|
|
136
|
+
return outcome.done;
|
|
137
|
+
}
|
|
121
138
|
// Explicit rich text is bound from flat OPC or XHTML in docx4j; deferred, as it is there for this route.
|
|
122
139
|
if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined)
|
|
123
140
|
return false;
|
|
124
141
|
if (isContainer(control))
|
|
125
142
|
return false;
|
|
143
|
+
// A Flat OPC package is a rich text control's content, not text: no control is filled with it.
|
|
144
|
+
if (holdsFlatOpc(value))
|
|
145
|
+
return false;
|
|
126
146
|
if (control.type === 'CheckBox') {
|
|
127
147
|
const checkbox = control.checkboxContentControl;
|
|
128
148
|
if (!checkbox)
|
|
@@ -156,15 +176,18 @@ export function applyBindingTo(control) {
|
|
|
156
176
|
/** `applyBindings` over a list of controls. */
|
|
157
177
|
export function applyBindingsTo(controls) {
|
|
158
178
|
const result = { bound: 0, updated: 0, skipped: 0 };
|
|
179
|
+
const notes = [];
|
|
159
180
|
for (const control of controls) {
|
|
160
181
|
if (!control.xmlMapping.isMapped)
|
|
161
182
|
continue;
|
|
162
183
|
result.bound++;
|
|
163
|
-
if (applyBindingTo(control))
|
|
184
|
+
if (applyBindingTo(control, notes))
|
|
164
185
|
result.updated++;
|
|
165
186
|
else
|
|
166
187
|
result.skipped++;
|
|
167
188
|
}
|
|
189
|
+
if (notes.length > 0)
|
|
190
|
+
result.notes = notes;
|
|
168
191
|
return result;
|
|
169
192
|
}
|
|
170
193
|
/**
|
|
@@ -181,6 +204,8 @@ export function updateFromControl(control) {
|
|
|
181
204
|
const node = mapping.customXmlNode;
|
|
182
205
|
if (!node)
|
|
183
206
|
return false;
|
|
207
|
+
// A picture control is the asynchronous pass's (updateFromPictureControl, CR-005 section 9): its
|
|
208
|
+
// image's bytes go back to the node as base64, as Word 15 writes them (check 38).
|
|
184
209
|
if (control.type === 'Picture')
|
|
185
210
|
return false;
|
|
186
211
|
if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined)
|
|
@@ -190,6 +215,9 @@ export function updateFromControl(control) {
|
|
|
190
215
|
// A control showing its placeholder holds no value.
|
|
191
216
|
if (control.isShowingPlaceholder)
|
|
192
217
|
return false;
|
|
218
|
+
// The node holds a rich text control's content as Flat OPC: text is not written over it.
|
|
219
|
+
if (holdsFlatOpc(node.text))
|
|
220
|
+
return false;
|
|
193
221
|
let value = control.text;
|
|
194
222
|
if (control.type === 'CheckBox') {
|
|
195
223
|
const checkbox = control.checkboxContentControl;
|
|
@@ -220,6 +248,9 @@ export function updateFromControls(controls) {
|
|
|
220
248
|
for (const control of controls) {
|
|
221
249
|
if (!control.xmlMapping.isMapped)
|
|
222
250
|
continue;
|
|
251
|
+
// counted by the picture pass, which reads the image part (CR-005 section 9)
|
|
252
|
+
if (control.type === 'Picture')
|
|
253
|
+
continue;
|
|
223
254
|
result.bound++;
|
|
224
255
|
if (updateFromControl(control))
|
|
225
256
|
result.updated++;
|
|
@@ -3,5 +3,8 @@ export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection, type Cu
|
|
|
3
3
|
export { CustomXmlPartCollection, type CustomXmlHost } from './CustomXmlPartCollection.mjs';
|
|
4
4
|
export { XmlMapping, type CustomXmlPartLookup } from './XmlMapping.mjs';
|
|
5
5
|
export { CheckboxContentControl, DatePickerContentControl, ListContentControl, ContentControlListItem, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKED_SYMBOL, UNCHECKED_SYMBOL, CHECKBOX_FONT, } from './kinds.mjs';
|
|
6
|
-
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT, type BindingResult } from './bindings.mjs';
|
|
6
|
+
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, holdsFlatOpc, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT, type BindingResult } from './bindings.mjs';
|
|
7
7
|
export { controlIdScope, sdtKindFor, isRunLevel } from './insert.mjs';
|
|
8
|
+
export { tagParamsOf, xpathsEntriesOf, selectEntryNode, OPENDOPE_XPATHS_NS, XHTML_CONTENT_TYPE, PICTURE_HANDLER, type XPathsEntry } from './opendope.mjs';
|
|
9
|
+
export { applyXhtmlBindingTo, applyXhtmlBindingsTo, isXhtmlBound, type XhtmlHost, type XhtmlBindingOptions } from './xhtml.mjs';
|
|
10
|
+
export { applyPictureHandlerTo, applyPictureHandlersTo, isPictureHandlerBound, bindPicture, pictureWidthOf, updateFromPictureControl, updateFromPictureControls, type PictureWidth, type PictureOutcome } from './pictures.mjs';
|
|
@@ -5,5 +5,8 @@ export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection } from '
|
|
|
5
5
|
export { CustomXmlPartCollection } from './CustomXmlPartCollection.mjs';
|
|
6
6
|
export { XmlMapping } from './XmlMapping.mjs';
|
|
7
7
|
export { CheckboxContentControl, DatePickerContentControl, ListContentControl, ContentControlListItem, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKED_SYMBOL, UNCHECKED_SYMBOL, CHECKBOX_FONT, } from './kinds.mjs';
|
|
8
|
-
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT } from './bindings.mjs';
|
|
8
|
+
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, holdsFlatOpc, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT } from './bindings.mjs';
|
|
9
9
|
export { controlIdScope, sdtKindFor, isRunLevel } from './insert.mjs';
|
|
10
|
+
export { tagParamsOf, xpathsEntriesOf, selectEntryNode, OPENDOPE_XPATHS_NS, XHTML_CONTENT_TYPE, PICTURE_HANDLER } from './opendope.mjs';
|
|
11
|
+
export { applyXhtmlBindingTo, applyXhtmlBindingsTo, isXhtmlBound } from './xhtml.mjs';
|
|
12
|
+
export { applyPictureHandlerTo, applyPictureHandlersTo, isPictureHandlerBound, bindPicture, pictureWidthOf, updateFromPictureControl, updateFromPictureControls } from './pictures.mjs';
|
|
@@ -16,5 +16,15 @@ export declare function controlIdScope(body: {
|
|
|
16
16
|
part?: XmlPart<unknown> | undefined;
|
|
17
17
|
container: object;
|
|
18
18
|
}): object;
|
|
19
|
+
/**
|
|
20
|
+
* Lists `w15` in the `mc:Ignorable` of the body's part when the new control's `w:sdtPr` holds a
|
|
21
|
+
* `w15` element (a repeating section or its item): Word 2010 knows no `w15` and opens a part with
|
|
22
|
+
* one only when the prefix is ignorable (CR-002 section 40, `test/README.md` check 35).
|
|
23
|
+
*/
|
|
24
|
+
export declare function declareSdtExtensions(body: {
|
|
25
|
+
part?: XmlPart<unknown> | undefined;
|
|
26
|
+
}, sdt: {
|
|
27
|
+
value?: unknown;
|
|
28
|
+
}): void;
|
|
19
29
|
/** True for a run-level item (what a Range wraps). */
|
|
20
30
|
export declare function isRunLevel(element: Element): boolean;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { typeNameOf } from '../content/tree.mjs';
|
|
2
|
+
import { declareIgnorable } from '../content/tracking.mjs';
|
|
3
|
+
const W15_NS = 'http://schemas.microsoft.com/office/word/2012/wordml';
|
|
2
4
|
/**
|
|
3
5
|
* The kind the builders' `sdt` takes: Office JS's `Unknown` has no kind element, as `RichText`
|
|
4
6
|
* has none (Word reports an untyped control as rich text).
|
|
@@ -15,6 +17,17 @@ export function controlIdScope(body) {
|
|
|
15
17
|
const part = body.part;
|
|
16
18
|
return (part === null || part === void 0 ? void 0 : part.isUnmarshalled) ? part.contents : body.container;
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Lists `w15` in the `mc:Ignorable` of the body's part when the new control's `w:sdtPr` holds a
|
|
22
|
+
* `w15` element (a repeating section or its item): Word 2010 knows no `w15` and opens a part with
|
|
23
|
+
* one only when the prefix is ignorable (CR-002 section 40, `test/README.md` check 35).
|
|
24
|
+
*/
|
|
25
|
+
export function declareSdtExtensions(body, sdt) {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
const items = (_b = (_a = sdt.value) === null || _a === void 0 ? void 0 : _a.sdtPr) === null || _b === void 0 ? void 0 : _b.rPrOrAliasOrLock;
|
|
28
|
+
if (items === null || items === void 0 ? void 0 : items.some((item) => { var _a; return ((_a = item.name) === null || _a === void 0 ? void 0 : _a.namespaceURI) === W15_NS; }))
|
|
29
|
+
declareIgnorable(body.part, 'w15');
|
|
30
|
+
}
|
|
18
31
|
/** True for a run-level item (what a Range wraps). */
|
|
19
32
|
export function isRunLevel(element) {
|
|
20
33
|
const tn = typeNameOf(element);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CustomXmlPartLookup } from './XmlMapping.mjs';
|
|
2
|
+
import type { CustomXmlNode } from './CustomXmlPart.mjs';
|
|
3
|
+
export declare const OPENDOPE_XPATHS_NS = "http://opendope.org/xpaths";
|
|
4
|
+
export declare const XHTML_CONTENT_TYPE = "application/xhtml+xml";
|
|
5
|
+
/** The tag's `od:Handler` value for a picture (the specification's section 9.3; CR-005 section 9). */
|
|
6
|
+
export declare const PICTURE_HANDLER = "picture";
|
|
7
|
+
/** The tag's parameters, `key=value` pairs separated by `&`, in order; a value is as written (the content type keeps its `/` and `+`). */
|
|
8
|
+
export declare function tagParamsOf(tag: string): Map<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* An XPaths part entry: `<xpath id="x1"><dataBinding storeItemID xpath prefixMappings/></xpath>`.
|
|
11
|
+
* The attributes are unqualified (`xpaths.xsd`: `attributeFormDefault="unqualified"`; docx4j's
|
|
12
|
+
* `org.opendope.xpaths.Xpaths` and its invoice.docx write them so); a `w:`-qualified form, which
|
|
13
|
+
* this package's own check 37 and 38 files were made with, is read as well.
|
|
14
|
+
*/
|
|
15
|
+
export interface XPathsEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
storeItemID: string;
|
|
18
|
+
xpath: string;
|
|
19
|
+
prefixMappings: string;
|
|
20
|
+
}
|
|
21
|
+
/** The XPaths part's entries by id, from the first custom XML part in the OpenDoPE XPaths namespace; empty without one. */
|
|
22
|
+
export declare function xpathsEntriesOf(parts: CustomXmlPartLookup): Map<string, XPathsEntry>;
|
|
23
|
+
/**
|
|
24
|
+
* The node an `od:xpath` entry selects: the entry's XPath over the part its `storeItemID` names
|
|
25
|
+
* (the id compared without braces or case, as `getItem` does). What a tag-bound control's pass
|
|
26
|
+
* starts from; the note says why there is no node.
|
|
27
|
+
*/
|
|
28
|
+
export declare function selectEntryNode(entryId: string, parts: CustomXmlPartLookup): {
|
|
29
|
+
node: CustomXmlNode;
|
|
30
|
+
} | {
|
|
31
|
+
note: string;
|
|
32
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const OPENDOPE_XPATHS_NS = 'http://opendope.org/xpaths';
|
|
2
|
+
export const XHTML_CONTENT_TYPE = 'application/xhtml+xml';
|
|
3
|
+
/** The tag's `od:Handler` value for a picture (the specification's section 9.3; CR-005 section 9). */
|
|
4
|
+
export const PICTURE_HANDLER = 'picture';
|
|
5
|
+
/** The tag's parameters, `key=value` pairs separated by `&`, in order; a value is as written (the content type keeps its `/` and `+`). */
|
|
6
|
+
export function tagParamsOf(tag) {
|
|
7
|
+
const out = new Map();
|
|
8
|
+
for (const pair of tag.split('&')) {
|
|
9
|
+
const eq = pair.indexOf('=');
|
|
10
|
+
if (eq <= 0)
|
|
11
|
+
continue;
|
|
12
|
+
const key = pair.slice(0, eq).trim();
|
|
13
|
+
if (!out.has(key))
|
|
14
|
+
out.set(key, pair.slice(eq + 1).trim());
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
/** The XPaths part's entries by id, from the first custom XML part in the OpenDoPE XPaths namespace; empty without one. */
|
|
19
|
+
export function xpathsEntriesOf(parts) {
|
|
20
|
+
const out = new Map();
|
|
21
|
+
const part = parts.items.find((p) => p.namespaceUri === OPENDOPE_XPATHS_NS);
|
|
22
|
+
if (!part)
|
|
23
|
+
return out;
|
|
24
|
+
const root = part.document.documentElement;
|
|
25
|
+
if (!root)
|
|
26
|
+
return out;
|
|
27
|
+
const entries = root.getElementsByTagNameNS(OPENDOPE_XPATHS_NS, 'xpath');
|
|
28
|
+
for (let i = 0; i < entries.length; i++) {
|
|
29
|
+
const entry = entries[i];
|
|
30
|
+
const id = entry.getAttribute('id');
|
|
31
|
+
const binding = entry.getElementsByTagNameNS(OPENDOPE_XPATHS_NS, 'dataBinding')[0];
|
|
32
|
+
if (!id || !binding)
|
|
33
|
+
continue;
|
|
34
|
+
const read = (name) => binding.getAttribute(name) || binding.getAttributeNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', name) || binding.getAttribute(`w:${name}`) || '';
|
|
35
|
+
out.set(id, { id, storeItemID: read('storeItemID'), xpath: read('xpath'), prefixMappings: read('prefixMappings') });
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The node an `od:xpath` entry selects: the entry's XPath over the part its `storeItemID` names
|
|
41
|
+
* (the id compared without braces or case, as `getItem` does). What a tag-bound control's pass
|
|
42
|
+
* starts from; the note says why there is no node.
|
|
43
|
+
*/
|
|
44
|
+
export function selectEntryNode(entryId, parts) {
|
|
45
|
+
var _a;
|
|
46
|
+
const entry = xpathsEntriesOf(parts).get(entryId);
|
|
47
|
+
if (!entry)
|
|
48
|
+
return { note: `od:xpath=${entryId}: no such entry in the XPaths part` };
|
|
49
|
+
const part = (_a = parts.getItem(entry.storeItemID)) !== null && _a !== void 0 ? _a : parts.items.find((p) => p.id.replace(/[{}]/g, '').toLowerCase() === entry.storeItemID.replace(/[{}]/g, '').toLowerCase());
|
|
50
|
+
if (!part)
|
|
51
|
+
return { note: `od:xpath=${entryId}: no custom XML part ${entry.storeItemID}` };
|
|
52
|
+
const node = part.selectSingleNode(entry.xpath, entry.prefixMappings);
|
|
53
|
+
if (!node)
|
|
54
|
+
return { note: `od:xpath=${entryId}: ${entry.xpath} selects nothing` };
|
|
55
|
+
return { node };
|
|
56
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ContentControl } from '../content/ContentControl.mjs';
|
|
2
|
+
import type { CustomXmlPartLookup } from './XmlMapping.mjs';
|
|
3
|
+
import type { BindingResult } from './bindings.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* What the tag's `width` says: absent keeps the control's drawing and replaces its image; `auto`
|
|
6
|
+
* a new inline picture at its natural size, scaled down to the text width; `N` the same, scaled
|
|
7
|
+
* down to N twips when that is narrower than the text width (a value that is neither is `auto`,
|
|
8
|
+
* which is what docx4j does with one it cannot parse).
|
|
9
|
+
*/
|
|
10
|
+
export type PictureWidth = {
|
|
11
|
+
kind: 'keep';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'auto';
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'twips';
|
|
16
|
+
twips: number;
|
|
17
|
+
};
|
|
18
|
+
export interface PictureOutcome {
|
|
19
|
+
done: boolean;
|
|
20
|
+
/** Why the control was left as it was. */
|
|
21
|
+
note?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Whether a control is bound to a picture through its tag (`od:Handler=picture` with an `od:xpath`). */
|
|
24
|
+
export declare function isPictureHandlerBound(control: ContentControl): boolean;
|
|
25
|
+
/** The tag's `width` parameter, read as `PictureWidth` says. */
|
|
26
|
+
export declare function pictureWidthOf(params: Map<string, string>): PictureWidth;
|
|
27
|
+
/**
|
|
28
|
+
* Binds one control to a value: base64 image data (a data URL accepted). With `keep`, the first
|
|
29
|
+
* `a:blip`'s `r:embed` is pointed at a new image part and the drawing is left as it is; a picture
|
|
30
|
+
* control holding no picture gets a new inline one at its natural size instead (docx4j's pre-v3
|
|
31
|
+
* fallback), a tag-bound control holding none is left and noted. Otherwise the control's content
|
|
32
|
+
* becomes one run holding a new inline picture, sized as `PictureWidth` says, in the first
|
|
33
|
+
* paragraph of a block-level control (its properties kept) or as a run-level control's content.
|
|
34
|
+
* An empty value, a value that is not an image this package reads (PNG, JPEG, GIF, BMP; SVG where
|
|
35
|
+
* the drawing is kept, since it has no pixel size to place a new picture at), and a body with no
|
|
36
|
+
* part each leave the control as it was, with a note. The image part the control showed before
|
|
37
|
+
* stays in the package, as docx4j leaves it.
|
|
38
|
+
*/
|
|
39
|
+
export declare function bindPicture(control: ContentControl, value: string, width: PictureWidth, label: string): PictureOutcome;
|
|
40
|
+
/** Binds one tag-bound control (shape b): the entry's node, the tag's width. */
|
|
41
|
+
export declare function applyPictureHandlerTo(control: ContentControl, parts: CustomXmlPartLookup): PictureOutcome;
|
|
42
|
+
/** The picture pass over the tag-bound controls, after the text bindings: counted into the result, what was left said in its notes. */
|
|
43
|
+
export declare function applyPictureHandlersTo(controls: ContentControl[], parts: CustomXmlPartLookup, result: BindingResult): void;
|
|
44
|
+
/**
|
|
45
|
+
* The reverse direction for a mapped picture control (docx4j's `UpdateXmlFromDocumentSurface`
|
|
46
|
+
* skips pictures; Word 15 writes the picture back, check 38): the bytes of the image part the
|
|
47
|
+
* first `a:blip` embeds, as base64, into the node. Nothing is written when the node already holds
|
|
48
|
+
* those bytes (compared decoded, so Word's line-wrapped base64 counts as the same), when the
|
|
49
|
+
* control shows no picture, or when the blip's part is not an image part. Asynchronous, since a
|
|
50
|
+
* part's bytes may still be in the container.
|
|
51
|
+
*/
|
|
52
|
+
export declare function updateFromPictureControl(control: ContentControl): Promise<boolean>;
|
|
53
|
+
/** `updateFromContentControls`' picture pass: the mapped picture controls, counted into the result. */
|
|
54
|
+
export declare function updateFromPictureControls(controls: ContentControl[], result: BindingResult): Promise<void>;
|