@docx4j/core-ts 0.4.0 → 0.6.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.
Files changed (166) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +34 -3
  3. package/dist/html/convert.mjs +40 -15
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +153 -19
  24. package/dist/model/content/Body.mjs +570 -131
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +202 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  31. package/dist/model/content/InlinePicture.d.mts +82 -7
  32. package/dist/model/content/InlinePicture.mjs +221 -28
  33. package/dist/model/content/List.mjs +13 -1
  34. package/dist/model/content/Paragraph.d.mts +213 -14
  35. package/dist/model/content/Paragraph.mjs +936 -100
  36. package/dist/model/content/Range.d.mts +242 -11
  37. package/dist/model/content/Range.mjs +975 -119
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +301 -16
  43. package/dist/model/content/TrackedChange.mjs +1284 -158
  44. package/dist/model/content/binder.d.mts +2 -1
  45. package/dist/model/content/binder.mjs +48 -1
  46. package/dist/model/content/comments.d.mts +20 -6
  47. package/dist/model/content/comments.mjs +67 -57
  48. package/dist/model/content/generalException.d.mts +18 -0
  49. package/dist/model/content/generalException.mjs +24 -0
  50. package/dist/model/content/index.d.mts +8 -3
  51. package/dist/model/content/index.mjs +7 -2
  52. package/dist/model/content/mathText.d.mts +21 -0
  53. package/dist/model/content/mathText.mjs +267 -0
  54. package/dist/model/content/officeText.d.mts +27 -0
  55. package/dist/model/content/officeText.mjs +74 -0
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/placeholders.d.mts +110 -0
  59. package/dist/model/content/placeholders.mjs +253 -0
  60. package/dist/model/content/relationships.d.mts +30 -0
  61. package/dist/model/content/relationships.mjs +133 -0
  62. package/dist/model/content/search.d.mts +72 -0
  63. package/dist/model/content/search.mjs +204 -2
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +44 -0
  67. package/dist/model/content/story.mjs +92 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +100 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +28 -0
  79. package/dist/model/content/tracking.mjs +149 -10
  80. package/dist/model/content/tree.d.mts +212 -13
  81. package/dist/model/content/tree.mjs +356 -35
  82. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  83. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  84. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  85. package/dist/model/customxml/XmlMapping.mjs +11 -0
  86. package/dist/model/customxml/bindings.d.mts +21 -2
  87. package/dist/model/customxml/bindings.mjs +76 -32
  88. package/dist/model/customxml/kinds.d.mts +8 -3
  89. package/dist/model/customxml/kinds.mjs +26 -5
  90. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  91. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  92. package/dist/model/customxml/pictures.d.mts +9 -5
  93. package/dist/model/customxml/pictures.mjs +31 -13
  94. package/dist/model/customxml/xhtml.mjs +8 -2
  95. package/dist/model/fonts/families.generated.mjs +2 -2
  96. package/dist/model/fonts/registry.mjs +7 -1
  97. package/dist/model/fonts/substitutions.generated.mjs +2 -2
  98. package/dist/model/fonts/themes.generated.mjs +1 -1
  99. package/dist/model/index.d.mts +2 -0
  100. package/dist/model/index.mjs +4 -0
  101. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  102. package/dist/model/listnumbering/Emulator.mjs +63 -9
  103. package/dist/model/listnumbering/definitions.mjs +18 -0
  104. package/dist/model/properties/PropertyResolver.d.mts +87 -7
  105. package/dist/model/properties/PropertyResolver.mjs +197 -17
  106. package/dist/model/properties/catalogue.d.mts +32 -4
  107. package/dist/model/properties/catalogue.mjs +115 -36
  108. package/dist/model/properties/log.d.mts +1 -1
  109. package/dist/model/properties/log.mjs +5 -5
  110. package/dist/model/properties/table.d.mts +16 -2
  111. package/dist/model/properties/table.mjs +17 -5
  112. package/dist/model/text/Extractor.d.mts +289 -0
  113. package/dist/model/text/Extractor.mjs +2162 -0
  114. package/dist/model/text/LinkTargets.d.mts +45 -0
  115. package/dist/model/text/LinkTargets.mjs +170 -0
  116. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  117. package/dist/model/text/MarkdownExporter.mjs +255 -0
  118. package/dist/model/text/PlainText.d.mts +29 -0
  119. package/dist/model/text/PlainText.mjs +539 -0
  120. package/dist/model/text/TextContext.d.mts +161 -0
  121. package/dist/model/text/TextContext.mjs +425 -0
  122. package/dist/model/text/TextOptions.d.mts +150 -0
  123. package/dist/model/text/TextOptions.mjs +278 -0
  124. package/dist/model/text/TextResult.d.mts +504 -0
  125. package/dist/model/text/TextResult.mjs +526 -0
  126. package/dist/model/text/Texts.d.mts +99 -0
  127. package/dist/model/text/Texts.mjs +187 -0
  128. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  129. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  130. package/dist/model/text/commonmark.d.mts +121 -0
  131. package/dist/model/text/commonmark.mjs +655 -0
  132. package/dist/model/text/index.d.mts +7 -0
  133. package/dist/model/text/index.mjs +8 -0
  134. package/dist/model/text/normalize.d.mts +8 -0
  135. package/dist/model/text/normalize.mjs +13 -0
  136. package/dist/model/text/outline.d.mts +4 -0
  137. package/dist/model/text/outline.mjs +71 -0
  138. package/dist/office-js/fragment.d.mts +7 -5
  139. package/dist/office-js/fragment.mjs +63 -14
  140. package/dist/office-js/proxy.d.mts +8 -0
  141. package/dist/office-js/proxy.mjs +29 -0
  142. package/dist/office-js/run.mjs +2 -0
  143. package/dist/office-js/supported.generated.mjs +16 -0
  144. package/dist/office-js/toApiScript.mjs +21 -5
  145. package/dist/opc/Load.d.mts +9 -2
  146. package/dist/opc/Save.d.mts +17 -0
  147. package/dist/opc/Save.mjs +12 -2
  148. package/dist/opc/index.d.mts +3 -3
  149. package/dist/opc/index.mjs +2 -2
  150. package/dist/opc/mce/preprocessor.d.mts +88 -7
  151. package/dist/opc/mce/preprocessor.mjs +144 -37
  152. package/dist/opc/mce/understood.d.mts +34 -0
  153. package/dist/opc/mce/understood.mjs +55 -3
  154. package/dist/packages/OpcPackage.d.mts +20 -0
  155. package/dist/packages/OpcPackage.mjs +16 -3
  156. package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
  157. package/dist/packages/WordprocessingMLPackage.mjs +242 -11
  158. package/dist/parts/DefaultXmlPart.mjs +2 -2
  159. package/dist/parts/Part.d.mts +10 -2
  160. package/dist/parts/XmlPart.d.mts +35 -32
  161. package/dist/parts/XmlPart.mjs +107 -107
  162. package/dist/parts/wml/comments.mjs +30 -0
  163. package/dist/parts/wml/index.mjs +12 -0
  164. package/dist/xml/dom.d.mts +7 -0
  165. package/dist/xml/dom.mjs +18 -0
  166. package/package.json +2 -2
@@ -1,9 +1,12 @@
1
1
  import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
2
  import type { Element } from '../content/tree.mjs';
3
3
  import type { ContentControl } from '../content/ContentControl.mjs';
4
- /** The style Word gives placeholder text, and the text docx4j's placeholder.xml carries. */
4
+ /**
5
+ * The style Word gives placeholder text, and the text docx4j's placeholder.xml carries: Word 365's since docx4j's CR-063
6
+ * D5 (CR-002 section 63.22), where it was Word 2010's "Click here to enter text.".
7
+ */
5
8
  export declare const PLACEHOLDER_STYLE = "PlaceholderText";
6
- export declare const PLACEHOLDER_TEXT = "Click here to enter text.";
9
+ export declare const PLACEHOLDER_TEXT = "Click or tap here to enter text.";
7
10
  /** What `applyBindings` and `updateFromContentControls` report. */
8
11
  export interface BindingResult {
9
12
  /** Controls carrying a `w:dataBinding`. */
@@ -22,13 +25,29 @@ export interface BindingResult {
22
25
  * control bound to the same node - so neither direction moves it as text (CR-002 section 40).
23
26
  */
24
27
  export declare function holdsFlatOpc(value: string): boolean;
28
+ /**
29
+ * A control's text as its node takes it (CR-002 section 60 D4, F1, F2): `textOf`'s characters, as before CR-012 phase 4
30
+ * moved `ContentControl.text` to Office JS's, its paragraphs and breaks a line feed each, a non-breaking hyphen `-` as
31
+ * Word writes it (check 55b), an optional hyphen U+00AD, a note reference nothing. Office JS's U+000B and U+001E are no
32
+ * XML characters.
33
+ */
34
+ export declare function boundValueOf(control: ContentControl): string;
25
35
  /** docx4j ValueInserterPlainTextImpl: the runs for a value, with the w:sdtPr run properties. */
26
36
  export declare function runsForValue(value: string, rPr: wml.RPr | undefined, multiLine: boolean): Element[];
37
+ /**
38
+ * docx4j's placeholder.xml: a PlaceholderText-styled run over the control's own run properties, which goes with
39
+ * w:showingPlcHdr; its text docx4j's, or a glossary entry's (CR-002 section 63, D8).
40
+ */
41
+ export declare function placeholderRun(rPr: wml.RPr | undefined, text?: string): Element;
27
42
  /**
28
43
  * A date in Word's `w:dateFormat` pattern (the .NET forms Word writes: `d MMMM yyyy`,
29
44
  * `dddd, d MMMM yyyy`, `d/MM/yyyy`), in the control's `w:lid` locale. Month and day names come
30
45
  * from `Intl`; text inside single or double quotes is literal. docx4j uses Java's
31
46
  * `SimpleDateFormat` with `dddd` mapped to `EEEE`; this is the same vocabulary.
47
+ *
48
+ * The date's **UTC fields** are formatted: a bound date is its nominal date, the fields as written,
49
+ * which `w:fullDate` keeps with a `Z` and `DatePickerContentControl.fullDate` returns as a `Date` in
50
+ * UTC, so the day shown is the same whatever the machine's time zone (CR-002 section 56).
32
51
  */
33
52
  export declare function formatDate(pattern: string, date: Date, locale?: string): string;
34
53
  /**
@@ -1,10 +1,13 @@
1
1
  import { deepCopy } from '@docx4j/generated-objects-ts';
2
2
  import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
3
- import { sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
3
+ import { sdtProperty, textOf } from '@docx4j/generated-objects-ts/builders/wml';
4
4
  import { bindPicture } from './pictures.mjs';
5
- /** The style Word gives placeholder text, and the text docx4j's placeholder.xml carries. */
5
+ /**
6
+ * The style Word gives placeholder text, and the text docx4j's placeholder.xml carries: Word 365's since docx4j's CR-063
7
+ * D5 (CR-002 section 63.22), where it was Word 2010's "Click here to enter text.".
8
+ */
6
9
  export const PLACEHOLDER_STYLE = 'PlaceholderText';
7
- export const PLACEHOLDER_TEXT = 'Click here to enter text.';
10
+ export const PLACEHOLDER_TEXT = 'Click or tap here to enter text.';
8
11
  /**
9
12
  * Whether a node's value is a Flat OPC package: what Word writes into the node of a mapped rich
10
13
  * text control when its content is edited, a whole `pkg:package` as escaped text (`test/README.md`
@@ -33,6 +36,16 @@ function isContainer(control) {
33
36
  return true;
34
37
  return control.form === 'Block' && control.tables.length > 0;
35
38
  }
39
+ /**
40
+ * A control's text as its node takes it (CR-002 section 60 D4, F1, F2): `textOf`'s characters, as before CR-012 phase 4
41
+ * moved `ContentControl.text` to Office JS's, its paragraphs and breaks a line feed each, a non-breaking hyphen `-` as
42
+ * Word writes it (check 55b), an optional hyphen U+00AD, a note reference nothing. Office JS's U+000B and U+001E are no
43
+ * XML characters.
44
+ */
45
+ export function boundValueOf(control) {
46
+ var _a;
47
+ return textOf(((_a = control.sdt.sdtContent) !== null && _a !== void 0 ? _a : {})).replace(/\u2011/g, '-');
48
+ }
36
49
  /** docx4j ValueInserterPlainTextImpl: the runs for a value, with the w:sdtPr run properties. */
37
50
  export function runsForValue(value, rPr, multiLine) {
38
51
  if (value === '')
@@ -49,68 +62,90 @@ export function runsForValue(value, rPr, multiLine) {
49
62
  });
50
63
  return out;
51
64
  }
52
- /** docx4j's placeholder.xml: a PlaceholderText-styled run, which goes with w:showingPlcHdr. */
53
- function placeholderRun(rPr) {
65
+ /**
66
+ * docx4j's placeholder.xml: a PlaceholderText-styled run over the control's own run properties, which goes with
67
+ * w:showingPlcHdr; its text docx4j's, or a glossary entry's (CR-002 section 63, D8).
68
+ */
69
+ export function placeholderRun(rPr, text = PLACEHOLDER_TEXT) {
54
70
  const properties = rPr ? deepCopy(rPr) : {};
55
71
  properties.rStyle = { val: PLACEHOLDER_STYLE };
56
- return el.r({ rPr: properties, content: [el.t({ value: PLACEHOLDER_TEXT })] });
72
+ const t = { TYPE_NAME: 'org_docx4j_wml.Text', value: text };
73
+ if (text.startsWith(' ') || text.endsWith(' '))
74
+ t.space = 'preserve';
75
+ return el.r({ rPr: properties, content: [el.t(t)] });
57
76
  }
58
77
  /**
59
78
  * A date in Word's `w:dateFormat` pattern (the .NET forms Word writes: `d MMMM yyyy`,
60
79
  * `dddd, d MMMM yyyy`, `d/MM/yyyy`), in the control's `w:lid` locale. Month and day names come
61
80
  * from `Intl`; text inside single or double quotes is literal. docx4j uses Java's
62
81
  * `SimpleDateFormat` with `dddd` mapped to `EEEE`; this is the same vocabulary.
82
+ *
83
+ * The date's **UTC fields** are formatted: a bound date is its nominal date, the fields as written,
84
+ * which `w:fullDate` keeps with a `Z` and `DatePickerContentControl.fullDate` returns as a `Date` in
85
+ * UTC, so the day shown is the same whatever the machine's time zone (CR-002 section 56).
63
86
  */
64
87
  export function formatDate(pattern, date, locale = 'en-US') {
65
88
  const pad = (n, width) => String(n).padStart(width, '0');
66
89
  const name = (options) => {
67
90
  try {
68
- return new Intl.DateTimeFormat(locale, options).format(date);
91
+ return new Intl.DateTimeFormat(locale, { ...options, timeZone: 'UTC' }).format(date);
69
92
  }
70
93
  catch {
71
- return new Intl.DateTimeFormat('en-US', options).format(date);
94
+ return new Intl.DateTimeFormat('en-US', { ...options, timeZone: 'UTC' }).format(date);
72
95
  }
73
96
  };
74
- const hours12 = date.getHours() % 12 === 0 ? 12 : date.getHours() % 12;
97
+ const hours = date.getUTCHours();
98
+ const hours12 = hours % 12 === 0 ? 12 : hours % 12;
75
99
  return pattern.replace(/d{1,4}|M{1,4}|y{2,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|tt|'[^']*'|"[^"]*"/g, (token) => {
76
100
  if (token.startsWith("'") || token.startsWith('"'))
77
101
  return token.slice(1, -1);
78
102
  switch (token) {
79
- case 'd': return String(date.getDate());
80
- case 'dd': return pad(date.getDate(), 2);
103
+ case 'd': return String(date.getUTCDate());
104
+ case 'dd': return pad(date.getUTCDate(), 2);
81
105
  case 'ddd': return name({ weekday: 'short' });
82
106
  case 'dddd': return name({ weekday: 'long' });
83
- case 'M': return String(date.getMonth() + 1);
84
- case 'MM': return pad(date.getMonth() + 1, 2);
107
+ case 'M': return String(date.getUTCMonth() + 1);
108
+ case 'MM': return pad(date.getUTCMonth() + 1, 2);
85
109
  case 'MMM': return name({ month: 'short' });
86
110
  case 'MMMM': return name({ month: 'long' });
87
- case 'yy': return pad(date.getFullYear() % 100, 2);
111
+ case 'yy': return pad(date.getUTCFullYear() % 100, 2);
88
112
  case 'yyy':
89
- case 'yyyy': return String(date.getFullYear());
90
- case 'H': return String(date.getHours());
91
- case 'HH': return pad(date.getHours(), 2);
113
+ case 'yyyy': return String(date.getUTCFullYear());
114
+ case 'H': return String(hours);
115
+ case 'HH': return pad(hours, 2);
92
116
  case 'h': return String(hours12);
93
117
  case 'hh': return pad(hours12, 2);
94
- case 'm': return String(date.getMinutes());
95
- case 'mm': return pad(date.getMinutes(), 2);
96
- case 's': return String(date.getSeconds());
97
- case 'ss': return pad(date.getSeconds(), 2);
98
- case 'tt': return date.getHours() < 12 ? 'AM' : 'PM';
118
+ case 'm': return String(date.getUTCMinutes());
119
+ case 'mm': return pad(date.getUTCMinutes(), 2);
120
+ case 's': return String(date.getUTCSeconds());
121
+ case 'ss': return pad(date.getUTCSeconds(), 2);
122
+ case 'tt': return hours < 12 ? 'AM' : 'PM';
99
123
  default: return token;
100
124
  }
101
125
  });
102
126
  }
103
- /** Word's stored date form, `2015-01-29T00:00:00` (local, as `w:fullDate` without a zone). */
127
+ /** Word's stored date form, `2015-01-29T00:00:00`: a nominal date's fields, with no zone (CR-002 section 56). */
104
128
  function isoOf(date) {
105
129
  const pad = (n) => String(n).padStart(2, '0');
106
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
130
+ return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`
131
+ + `T${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`;
107
132
  }
108
- /** The date a bound value holds, when it is one (`2015-01-29T00:00:00`, `2015-01-29`, or with a Z). */
133
+ /**
134
+ * The date a bound value holds, when it is one (`2015-01-29`, `2015-01-29T00:00:00`, with seconds' fractions,
135
+ * a `Z` or an offset), as its nominal date: the fields as written, in UTC, the zone marker ignored, so that
136
+ * `2026-10-22T00:00:00Z` and `2026-10-22T00:00:00+05:00` are 22 October in any time zone (docx4j f62314d3b's
137
+ * rule, as Word shows them; CR-002 section 56). A date that doesn't exist (`2026-02-30`) is not one.
138
+ */
109
139
  function dateOf(value) {
110
- if (!/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?)?/.test(value))
140
+ const m = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?)?(?:Z|[+-]\d{2}:?\d{2})?$/.exec(value);
141
+ if (!m)
111
142
  return undefined;
112
- const parsed = new Date(value.replace(/Z$/, '').replace(' ', 'T'));
113
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
143
+ const [year, month, day, hour, minute, second] = m.slice(1).map((f) => Number(f !== null && f !== void 0 ? f : 0));
144
+ const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
145
+ // Date.UTC rolls an impossible field over (30 February is 2 March): such a value is not a date
146
+ const exists = date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day
147
+ && date.getUTCHours() === hour && date.getUTCMinutes() === minute && date.getUTCSeconds() === second;
148
+ return exists ? date : undefined;
114
149
  }
115
150
  /**
116
151
  * Pushes the custom XML value into one control (docx4j BindingHandler's per-sdt work). Returns
@@ -135,8 +170,9 @@ export function applyBindingTo(control, notes) {
135
170
  notes === null || notes === void 0 ? void 0 : notes.push(outcome.note);
136
171
  return outcome.done;
137
172
  }
138
- // Explicit rich text is bound from flat OPC or XHTML in docx4j; deferred, as it is there for this route.
139
- if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined)
173
+ // Explicit rich text is bound from flat OPC or XHTML in docx4j; deferred, as it is there for this route. A block
174
+ // control bound by w:dataBinding is plain text to Word, w:richText or not (CR-002 section 61 H4), and is bound as text.
175
+ if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined && control.type === 'RichText')
140
176
  return false;
141
177
  if (isContainer(control))
142
178
  return false;
@@ -170,6 +206,13 @@ export function applyBindingTo(control, notes) {
170
206
  display = formatDate(date.dateFormat, parsed, date.dateDisplayLocale || undefined);
171
207
  }
172
208
  }
209
+ // an empty value shows the control's placeholder in Word's form: the entry its w:placeholder names, else Word's default
210
+ // entry for a text control, written into the glossary on save (CR-002 section 63, D8; docx4j CR-063's D2 and D3).
211
+ // Another kind naming no entry, or one the glossary lacks, keeps docx4j's run.
212
+ if (display === '' && control.showsWordsFormPlaceholder) {
213
+ control.showPlaceholder();
214
+ return true;
215
+ }
173
216
  control.setBoundContent(runsForValue(display, control.runProperties, control.isMultiLine), display === '');
174
217
  return true;
175
218
  }
@@ -208,7 +251,8 @@ export function updateFromControl(control) {
208
251
  // image's bytes go back to the node as base64, as Word 15 writes them (check 38).
209
252
  if (control.type === 'Picture')
210
253
  return false;
211
- if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined)
254
+ // a block control bound by w:dataBinding is plain text to Word, w:richText or not (H4); its text goes back as text
255
+ if (sdtProperty(control.sdt.sdtPr, 'richText') !== undefined && control.type === 'RichText')
212
256
  return false;
213
257
  if (isContainer(control))
214
258
  return false;
@@ -218,7 +262,7 @@ export function updateFromControl(control) {
218
262
  // The node holds a rich text control's content as Flat OPC: text is not written over it.
219
263
  if (holdsFlatOpc(node.text))
220
264
  return false;
221
- let value = control.text;
265
+ let value = boundValueOf(control);
222
266
  if (control.type === 'CheckBox') {
223
267
  const checkbox = control.checkboxContentControl;
224
268
  if (!checkbox)
@@ -27,7 +27,9 @@ export declare class CheckboxContentControl {
27
27
  /** Office JS `Word.DatePickerContentControl` over `w:date`. */
28
28
  export declare class DatePickerContentControl {
29
29
  private readonly date;
30
- constructor(date: wml.CTSdtDate);
30
+ private readonly guard;
31
+ /** `guard` refuses a write to a control in a branch of a kept `mc:AlternateContent` (CR-013 D4). */
32
+ constructor(date: wml.CTSdtDate, guard?: () => void);
31
33
  /** `w:dateFormat`, Word's .NET date pattern ('d MMMM yyyy'). Office JS calls it dateDisplayFormat. */
32
34
  get dateFormat(): string;
33
35
  set dateFormat(format: string);
@@ -51,7 +53,8 @@ export declare class DatePickerContentControl {
51
53
  export declare class ContentControlListItem {
52
54
  private readonly items;
53
55
  private readonly item;
54
- constructor(items: wml.CTSdtListItem[], item: wml.CTSdtListItem);
56
+ private readonly guard;
57
+ constructor(items: wml.CTSdtListItem[], item: wml.CTSdtListItem, guard?: () => void);
55
58
  get displayText(): string;
56
59
  set displayText(text: string);
57
60
  get value(): string;
@@ -62,7 +65,9 @@ export declare class ContentControlListItem {
62
65
  /** Office JS `Word.DropDownListContentControl` / `Word.ComboBoxContentControl`: the same shape. */
63
66
  export declare class ListContentControl {
64
67
  private readonly list;
65
- constructor(list: wml.CTSdtDropDownList | wml.CTSdtComboBox);
68
+ private readonly guard;
69
+ /** `guard` refuses a write to a control in a branch of a kept `mc:AlternateContent` (CR-013 D4). */
70
+ constructor(list: wml.CTSdtDropDownList | wml.CTSdtComboBox, guard?: () => void);
66
71
  get listItems(): ContentControlListItem[];
67
72
  /** Adds an entry; `value` defaults to the display text, `index` appends when omitted. */
68
73
  addListItem(displayText: string, value?: string, index?: number): ContentControlListItem;
@@ -34,6 +34,7 @@ export class CheckboxContentControl {
34
34
  set isChecked(value) {
35
35
  var _a;
36
36
  var _b;
37
+ this.control.assertWritable();
37
38
  ((_a = (_b = this.checkbox).checked) !== null && _a !== void 0 ? _a : (_b.checked = {})).val = value;
38
39
  this.control.setCheckboxGlyph(value ? this.checkedSymbol : this.uncheckedSymbol);
39
40
  }
@@ -44,6 +45,7 @@ export class CheckboxContentControl {
44
45
  set checkedSymbol(symbol) {
45
46
  var _a, _b;
46
47
  var _c, _d;
48
+ this.control.assertWritable();
47
49
  ((_a = (_c = this.checkbox).checkedState) !== null && _a !== void 0 ? _a : (_c.checkedState = {})).val = codeOf(symbol);
48
50
  (_b = (_d = this.checkbox.checkedState).font) !== null && _b !== void 0 ? _b : (_d.font = CHECKBOX_FONT);
49
51
  }
@@ -53,6 +55,7 @@ export class CheckboxContentControl {
53
55
  set uncheckedSymbol(symbol) {
54
56
  var _a, _b;
55
57
  var _c, _d;
58
+ this.control.assertWritable();
56
59
  ((_a = (_c = this.checkbox).uncheckedState) !== null && _a !== void 0 ? _a : (_c.uncheckedState = {})).val = codeOf(symbol);
57
60
  (_b = (_d = this.checkbox.uncheckedState).font) !== null && _b !== void 0 ? _b : (_d.font = CHECKBOX_FONT);
58
61
  }
@@ -64,8 +67,10 @@ export class CheckboxContentControl {
64
67
  }
65
68
  /** Office JS `Word.DatePickerContentControl` over `w:date`. */
66
69
  export class DatePickerContentControl {
67
- constructor(date) {
70
+ /** `guard` refuses a write to a control in a branch of a kept `mc:AlternateContent` (CR-013 D4). */
71
+ constructor(date, guard = () => { }) {
68
72
  this.date = date;
73
+ this.guard = guard;
69
74
  }
70
75
  /** `w:dateFormat`, Word's .NET date pattern ('d MMMM yyyy'). Office JS calls it dateDisplayFormat. */
71
76
  get dateFormat() {
@@ -73,6 +78,7 @@ export class DatePickerContentControl {
73
78
  return (_b = (_a = this.date.dateFormat) === null || _a === void 0 ? void 0 : _a.val) !== null && _b !== void 0 ? _b : '';
74
79
  }
75
80
  set dateFormat(format) {
81
+ this.guard();
76
82
  if (format === '') {
77
83
  delete this.date.dateFormat;
78
84
  return;
@@ -92,6 +98,7 @@ export class DatePickerContentControl {
92
98
  return (_b = (_a = this.date.lid) === null || _a === void 0 ? void 0 : _a.val) !== null && _b !== void 0 ? _b : '';
93
99
  }
94
100
  set dateDisplayLocale(locale) {
101
+ this.guard();
95
102
  if (locale === '') {
96
103
  delete this.date.lid;
97
104
  return;
@@ -104,6 +111,7 @@ export class DatePickerContentControl {
104
111
  return (_b = (_a = this.date.calendar) === null || _a === void 0 ? void 0 : _a.val) !== null && _b !== void 0 ? _b : 'gregorian';
105
112
  }
106
113
  set dateCalendarType(calendar) {
114
+ this.guard();
107
115
  this.date.calendar = { val: calendar };
108
116
  }
109
117
  /** `w:storeMappedDataAs`: how a bound date is written into the custom XML ('dateTime', 'date', 'text'). */
@@ -112,6 +120,7 @@ export class DatePickerContentControl {
112
120
  return (_b = (_a = this.date.storeMappedDataAs) === null || _a === void 0 ? void 0 : _a.val) !== null && _b !== void 0 ? _b : 'text';
113
121
  }
114
122
  set dateStorageFormat(format) {
123
+ this.guard();
115
124
  this.date.storeMappedDataAs = { val: format };
116
125
  }
117
126
  /** `w:fullDate`, the date itself when Word has one (extension). */
@@ -123,6 +132,7 @@ export class DatePickerContentControl {
123
132
  return new Date(Date.UTC(c.year, ((_a = c.month) !== null && _a !== void 0 ? _a : 1) - 1, (_b = c.day) !== null && _b !== void 0 ? _b : 1, (_c = c.hour) !== null && _c !== void 0 ? _c : 0, (_d = c.minute) !== null && _d !== void 0 ? _d : 0, Math.floor((_e = c.second) !== null && _e !== void 0 ? _e : 0)));
124
133
  }
125
134
  set fullDate(value) {
135
+ this.guard();
126
136
  if (!value) {
127
137
  delete this.date.fullDate;
128
138
  return;
@@ -135,15 +145,17 @@ export class DatePickerContentControl {
135
145
  }
136
146
  /** One entry of a drop-down list or combo box (Office JS `Word.ContentControlListItem`). */
137
147
  export class ContentControlListItem {
138
- constructor(items, item) {
148
+ constructor(items, item, guard = () => { }) {
139
149
  this.items = items;
140
150
  this.item = item;
151
+ this.guard = guard;
141
152
  }
142
153
  get displayText() {
143
154
  var _a;
144
155
  return (_a = this.item.displayText) !== null && _a !== void 0 ? _a : '';
145
156
  }
146
157
  set displayText(text) {
158
+ this.guard();
147
159
  this.item.displayText = text;
148
160
  }
149
161
  get value() {
@@ -151,12 +163,14 @@ export class ContentControlListItem {
151
163
  return (_a = this.item.value) !== null && _a !== void 0 ? _a : '';
152
164
  }
153
165
  set value(value) {
166
+ this.guard();
154
167
  this.item.value = value;
155
168
  }
156
169
  get index() {
157
170
  return this.items.indexOf(this.item);
158
171
  }
159
172
  delete() {
173
+ this.guard();
160
174
  const at = this.index;
161
175
  if (at >= 0)
162
176
  this.items.splice(at, 1);
@@ -164,28 +178,32 @@ export class ContentControlListItem {
164
178
  }
165
179
  /** Office JS `Word.DropDownListContentControl` / `Word.ComboBoxContentControl`: the same shape. */
166
180
  export class ListContentControl {
167
- constructor(list) {
181
+ /** `guard` refuses a write to a control in a branch of a kept `mc:AlternateContent` (CR-013 D4). */
182
+ constructor(list, guard = () => { }) {
168
183
  this.list = list;
184
+ this.guard = guard;
169
185
  }
170
186
  get listItems() {
171
187
  var _a;
172
188
  var _b;
173
189
  const items = ((_a = (_b = this.list).listItem) !== null && _a !== void 0 ? _a : (_b.listItem = []));
174
- return items.map((item) => new ContentControlListItem(items, item));
190
+ return items.map((item) => new ContentControlListItem(items, item, this.guard));
175
191
  }
176
192
  /** Adds an entry; `value` defaults to the display text, `index` appends when omitted. */
177
193
  addListItem(displayText, value, index) {
178
194
  var _a;
179
195
  var _b;
196
+ this.guard();
180
197
  const items = ((_a = (_b = this.list).listItem) !== null && _a !== void 0 ? _a : (_b.listItem = []));
181
198
  const item = { TYPE_NAME: 'org_docx4j_wml.CTSdtListItem', displayText, value: value !== null && value !== void 0 ? value : displayText };
182
199
  if (index === undefined || index >= items.length)
183
200
  items.push(item);
184
201
  else
185
202
  items.splice(Math.max(0, index), 0, item);
186
- return new ContentControlListItem(items, item);
203
+ return new ContentControlListItem(items, item, this.guard);
187
204
  }
188
205
  deleteAllListItems() {
206
+ this.guard();
189
207
  this.list.listItem = [];
190
208
  }
191
209
  /** `w:lastValue`, what Word last showed (extension). */
@@ -194,6 +212,7 @@ export class ListContentControl {
194
212
  return (_a = this.list.lastValue) !== null && _a !== void 0 ? _a : '';
195
213
  }
196
214
  set lastValue(value) {
215
+ this.guard();
197
216
  this.list.lastValue = value;
198
217
  }
199
218
  }
@@ -227,6 +246,7 @@ export class RepeatingSectionContentControl {
227
246
  return (_b = (_a = this.section.sectionTitle) === null || _a === void 0 ? void 0 : _a.val) !== null && _b !== void 0 ? _b : '';
228
247
  }
229
248
  set sectionTitle(title) {
249
+ this.control.assertWritable();
230
250
  if (title === '') {
231
251
  delete this.section.sectionTitle;
232
252
  return;
@@ -239,6 +259,7 @@ export class RepeatingSectionContentControl {
239
259
  return !(flag !== undefined && flag.val !== false);
240
260
  }
241
261
  set allowInsertDeleteSection(allow) {
262
+ this.control.assertWritable();
242
263
  if (allow)
243
264
  delete this.section.doNotAllowInsertDeleteSection;
244
265
  else
@@ -0,0 +1,17 @@
1
+ import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
+ import type { Part } from '../../parts/Part.mjs';
3
+ import type { Element } from '../content/tree.mjs';
4
+ import type { ContentControl } from '../content/ContentControl.mjs';
5
+ /** docx4j's placeholder picture, a PNG, as base64. */
6
+ export declare const PICTURE_PLACEHOLDER_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAACXBIWXMAABYlAAAWJQFJUiTwAAACVklEQVR42u3cwU3jYBCGYRtx50gDNOUOckwDOaWBHNOBm6IAuEEBSByQIkv5B1njgGLP8xx3vasVeuVh82m3f/v46uDWHnwJEBbCQlggLISFsEBYCAthgbAQFsICYSEshAXCQlgU87jkF59Px+indvuDx9b+2PTH/y+sX/6gXdd9vr/O+U08ds+PnU/HdFtOIX+iT/8rncsbaxgGX8eNGcdx4TX0xsLfChEWwgJhISwq65f8pyAzP39jpZ6eX7yxcAoRFuTcYIQ26WyPSQenEGGBsBAWwgJhISxqMEITMkLjFCIsSDNC02CExilEWCAshIWwQFgIixqM0ISM0DiFCAvSjNA0GKFxChEWCAthISwQFsKiBiM0ISM0TiHCgjQjNA1GaJxChAXCQlgIC4SFsKjBCE3ICI1TiLAgzQhNgxEapxBhgbAQFsICYSEsajBCEzJC4xQiLEgzQtNghMYpRFggLISFsEBYCIsajNCEjNA4hQgL0ozQNBihcQoRFggLYSEsEBbCogYjNCEjNE4hwoI0IzQNRmicQoQFwkJYCAuEhbCowQhNyAiNU4iwIM0ITYMRGqcQYYGwEBbCAmEhLGowQhMyQuMUIixIM0LTYITGKURYICyEhbBAWAiLGozQhIzQOIUIC9KM0DQYoXEKERYIC2EhLBAWwqIGIzQhIzROIcKCNCM0DUZoNvfGuq57avoaaz7gsdU95o3Fmt9Yu/3h8m3WtZkfgXjsnh9Lf4PVLfyAFJxChIWwQFgIC2GBsBAWwgJhISyEBcJCWAgLhIWwEBb8+AbsaRgh6xNNrAAAAABJRU5ErkJggg==";
7
+ /** The drawing's extent, Word's: 1270000 EMU (100 points) square. */
8
+ export declare const PICTURE_PLACEHOLDER_EMU = 1270000;
9
+ /** The run holding the placeholder drawing in `source` (`scope` its drawing ids'), the image part added once per part. */
10
+ export declare function picturePlaceholderRun(source: Part, scope: object): Element<wml.R>;
11
+ /**
12
+ * Word's form for a picture control (`w:picture`): the flag, and its content one run holding the placeholder drawing, in
13
+ * a paragraph where the control is block-level. What a mapped control whose node is empty shows (CR-005 section 10, D1
14
+ * (a2)) and what `insertContentControl('Picture')` makes (D2), as Word's do. False, and nothing changed, where the
15
+ * control's body has no part to hold the image.
16
+ */
17
+ export declare function showPicturePlaceholder(control: ContentControl): boolean;
@@ -0,0 +1,32 @@
1
+ import { ImagePart } from '../../parts/BinaryPart.mjs';
2
+ import { addImagePart, imageRunOf } from '../content/InlinePicture.mjs';
3
+ /** docx4j's placeholder picture, a PNG, as base64. */
4
+ export const PICTURE_PLACEHOLDER_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAACXBIWXMAABYlAAAWJQFJUiTwAAACVklEQVR42u3cwU3jYBCGYRtx50gDNOUOckwDOaWBHNOBm6IAuEEBSByQIkv5B1njgGLP8xx3vasVeuVh82m3f/v46uDWHnwJEBbCQlggLISFsEBYCAthgbAQFsICYSEshAXCQlgU87jkF59Px+indvuDx9b+2PTH/y+sX/6gXdd9vr/O+U08ds+PnU/HdFtOIX+iT/8rncsbaxgGX8eNGcdx4TX0xsLfChEWwgJhISwq65f8pyAzP39jpZ6eX7yxcAoRFuTcYIQ26WyPSQenEGGBsBAWwgJhISxqMEITMkLjFCIsSDNC02CExilEWCAshIWwQFgIixqM0ISM0DiFCAvSjNA0GKFxChEWCAthISwQFsKiBiM0ISM0TiHCgjQjNA1GaJxChAXCQlgIC4SFsKjBCE3ICI1TiLAgzQhNgxEapxBhgbAQFsICYSEsajBCEzJC4xQiLEgzQtNghMYpRFggLISFsEBYCIsajNCEjNA4hQgL0ozQNBihcQoRFggLYSEsEBbCogYjNCEjNE4hwoI0IzQNRmicQoQFwkJYCAuEhbCowQhNyAiNU4iwIM0ITYMRGqcQYYGwEBbCAmEhLGowQhMyQuMUIixIM0LTYITGKURYICyEhbBAWAiLGozQhIzQOIUIC9KM0DQYoXEKERYIC2EhLBAWwqIGIzQhIzROIcKCNCM0DUZoNvfGuq57avoaaz7gsdU95o3Fmt9Yu/3h8m3WtZkfgXjsnh9Lf4PVLfyAFJxChIWwQFgIC2GBsBAWwgJhISyEBcJCWAgLhIWwEBb8+AbsaRgh6xNNrAAAAABJRU5ErkJggg==';
5
+ /** The drawing's extent, Word's: 1270000 EMU (100 points) square. */
6
+ export const PICTURE_PLACEHOLDER_EMU = 1270000;
7
+ /** The relationship each source part's placeholder image was added under, so that every control shares one part. */
8
+ const added = new WeakMap();
9
+ /** The run holding the placeholder drawing in `source` (`scope` its drawing ids'), the image part added once per part. */
10
+ export function picturePlaceholderRun(source, scope) {
11
+ var _a;
12
+ let relId = added.get(source);
13
+ if (relId === undefined || !(((_a = source.relationshipsPart) === null || _a === void 0 ? void 0 : _a.getPart(relId)) instanceof ImagePart)) {
14
+ relId = addImagePart(source, PICTURE_PLACEHOLDER_BASE64).relId;
15
+ added.set(source, relId);
16
+ }
17
+ return imageRunOf(relId, scope, PICTURE_PLACEHOLDER_EMU, PICTURE_PLACEHOLDER_EMU);
18
+ }
19
+ /**
20
+ * Word's form for a picture control (`w:picture`): the flag, and its content one run holding the placeholder drawing, in
21
+ * a paragraph where the control is block-level. What a mapped control whose node is empty shows (CR-005 section 10, D1
22
+ * (a2)) and what `insertContentControl('Picture')` makes (D2), as Word's do. False, and nothing changed, where the
23
+ * control's body has no part to hold the image.
24
+ */
25
+ export function showPicturePlaceholder(control) {
26
+ const source = control.parentBody.part;
27
+ if (!source)
28
+ return false;
29
+ control.setBoundContent([picturePlaceholderRun(source, control.parentBody.container)]);
30
+ control.isShowingPlaceholder = true;
31
+ return true;
32
+ }
@@ -26,14 +26,17 @@ export declare function isPictureHandlerBound(control: ContentControl): boolean;
26
26
  export declare function pictureWidthOf(params: Map<string, string>): PictureWidth;
27
27
  /**
28
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
29
+ * `a:blip` is pointed at a new image part and the drawing is left as it is, as Word binds a picture
30
+ * control (`setBlipImage`: a raster image in `r:embed`, the `asvg:svgBlip` taken away; an SVG under
31
+ * the `asvg:svgBlip`, `r:embed` taken away; the effects kept; CR-002 section 52); a picture
30
32
  * control holding no picture gets a new inline one at its natural size instead (docx4j's pre-v3
31
33
  * fallback), a tag-bound control holding none is left and noted. Otherwise the control's content
32
34
  * becomes one run holding a new inline picture, sized as `PictureWidth` says, in the first
33
35
  * 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
36
+ * An empty value gives a picture control Word's form, the flag and a placeholder picture (`picturePlaceholderRun`;
37
+ * CR-005 section 10, D1 (a2)), unless it shows one already; a tag-bound control it leaves, with a note. A value that is
38
+ * not an image this package reads (PNG, JPEG, GIF, BMP; SVG where the drawing is kept, since it has no pixel size to
39
+ * place a new picture at), and a body with no part each leave the control as it was, with a note. The image part the control showed before
37
40
  * stays in the package, as docx4j leaves it.
38
41
  */
39
42
  export declare function bindPicture(control: ContentControl, value: string, width: PictureWidth, label: string): PictureOutcome;
@@ -44,7 +47,8 @@ export declare function applyPictureHandlersTo(controls: ContentControl[], parts
44
47
  /**
45
48
  * The reverse direction for a mapped picture control (docx4j's `UpdateXmlFromDocumentSurface`
46
49
  * 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
50
+ * first `a:blip` shows, as base64, into the node: its `asvg:svgBlip`'s SVG where it has one, as
51
+ * `InlinePicture.getBase64` gives (CR-002 section 52), else its `r:embed`'s image. Nothing is written when the node already holds
48
52
  * those bytes (compared decoded, so Word's line-wrapped base64 counts as the same), when the
49
53
  * control shows no picture, or when the blip's part is not an image part. Asynchronous, since a
50
54
  * part's bytes may still be in the container.
@@ -1,9 +1,11 @@
1
1
  import { find } from '../content/tree.mjs';
2
- import { addImage, addImagePart, writableWidthEmu } from '../content/InlinePicture.mjs';
2
+ import { addImage, addImagePart, writableWidthEmu, setBlipImage, svgBlipOf } from '../content/InlinePicture.mjs';
3
+ import { ContentTypes } from '../../opc/ContentTypes.mjs';
3
4
  import { ImagePart } from '../../parts/BinaryPart.mjs';
4
5
  import { base64Decode, base64Encode } from '../../xml/dom.mjs';
5
6
  import { enclosingCellOf } from '../properties/table.mjs';
6
7
  import { tagParamsOf, selectEntryNode, PICTURE_HANDLER } from './opendope.mjs';
8
+ import { showPicturePlaceholder } from './picturePlaceholder.mjs';
7
9
  const EMU_PER_TWIP = 635;
8
10
  const BLIP = 'org_docx4j_dml.CTBlip';
9
11
  /** Whether a control is bound to a picture through its tag (`od:Handler=picture` with an `od:xpath`). */
@@ -73,28 +75,41 @@ function maxWidthEmu(control, width) {
73
75
  }
74
76
  /**
75
77
  * Binds one control to a value: base64 image data (a data URL accepted). With `keep`, the first
76
- * `a:blip`'s `r:embed` is pointed at a new image part and the drawing is left as it is; a picture
78
+ * `a:blip` is pointed at a new image part and the drawing is left as it is, as Word binds a picture
79
+ * control (`setBlipImage`: a raster image in `r:embed`, the `asvg:svgBlip` taken away; an SVG under
80
+ * the `asvg:svgBlip`, `r:embed` taken away; the effects kept; CR-002 section 52); a picture
77
81
  * control holding no picture gets a new inline one at its natural size instead (docx4j's pre-v3
78
82
  * fallback), a tag-bound control holding none is left and noted. Otherwise the control's content
79
83
  * becomes one run holding a new inline picture, sized as `PictureWidth` says, in the first
80
84
  * paragraph of a block-level control (its properties kept) or as a run-level control's content.
81
- * An empty value, a value that is not an image this package reads (PNG, JPEG, GIF, BMP; SVG where
82
- * the drawing is kept, since it has no pixel size to place a new picture at), and a body with no
83
- * part each leave the control as it was, with a note. The image part the control showed before
85
+ * An empty value gives a picture control Word's form, the flag and a placeholder picture (`picturePlaceholderRun`;
86
+ * CR-005 section 10, D1 (a2)), unless it shows one already; a tag-bound control it leaves, with a note. A value that is
87
+ * not an image this package reads (PNG, JPEG, GIF, BMP; SVG where the drawing is kept, since it has no pixel size to
88
+ * place a new picture at), and a body with no part each leave the control as it was, with a note. The image part the control showed before
84
89
  * stays in the package, as docx4j leaves it.
85
90
  */
86
91
  export function bindPicture(control, value, width, label) {
87
92
  const base64 = base64Of(value);
88
- if (base64 === '')
89
- return { done: false, note: `${label}: the node is empty; the control is left as it is` };
90
93
  const source = control.parentBody.part;
94
+ if (base64 === '') {
95
+ // a picture control's empty node: Word's form, the flag and a placeholder picture (CR-005 section 10, D1 (a2));
96
+ // one already showing a placeholder picture is left as it is, a tag-bound control left as before
97
+ if (control.type !== 'Picture')
98
+ return { done: false, note: `${label}: the node is empty; the control is left as it is` };
99
+ if (control.isShowingPlaceholder && firstBlipOf(control))
100
+ return { done: false };
101
+ if (!showPicturePlaceholder(control))
102
+ return { done: false, note: `${label}: the control's body has no part to hold an image` };
103
+ return { done: true };
104
+ }
91
105
  if (!source)
92
106
  return { done: false, note: `${label}: the control's body has no part to hold an image` };
93
107
  if (width.kind === 'keep') {
94
108
  const blip = firstBlipOf(control);
95
109
  if (blip) {
96
110
  try {
97
- blip.embed = addImagePart(source, base64).relId;
111
+ const added = addImagePart(source, base64);
112
+ setBlipImage(blip, added.relId, added.info.contentType === ContentTypes.IMAGE_SVG);
98
113
  }
99
114
  catch (e) {
100
115
  return { done: false, note: `${label}: not an image this package reads (${messageOf(e)})` };
@@ -145,24 +160,27 @@ export function applyPictureHandlersTo(controls, parts, result) {
145
160
  /**
146
161
  * The reverse direction for a mapped picture control (docx4j's `UpdateXmlFromDocumentSurface`
147
162
  * skips pictures; Word 15 writes the picture back, check 38): the bytes of the image part the
148
- * first `a:blip` embeds, as base64, into the node. Nothing is written when the node already holds
163
+ * first `a:blip` shows, as base64, into the node: its `asvg:svgBlip`'s SVG where it has one, as
164
+ * `InlinePicture.getBase64` gives (CR-002 section 52), else its `r:embed`'s image. Nothing is written when the node already holds
149
165
  * those bytes (compared decoded, so Word's line-wrapped base64 counts as the same), when the
150
166
  * control shows no picture, or when the blip's part is not an image part. Asynchronous, since a
151
167
  * part's bytes may still be in the container.
152
168
  */
153
169
  export async function updateFromPictureControl(control) {
154
- var _a;
170
+ var _a, _b;
155
171
  const mapping = control.xmlMapping;
156
- if (!mapping.isMapped || control.type !== 'Picture')
172
+ // a control showing its placeholder picture holds no picture of its node's (CR-005 section 10)
173
+ if (!mapping.isMapped || control.type !== 'Picture' || control.isShowingPlaceholder)
157
174
  return false;
158
175
  const node = mapping.customXmlNode;
159
176
  if (!node)
160
177
  return false;
161
178
  const blip = firstBlipOf(control);
162
179
  const source = control.parentBody.part;
163
- if (!(blip === null || blip === void 0 ? void 0 : blip.embed) || !source)
180
+ const relId = ((_a = svgBlipOf(blip)) === null || _a === void 0 ? void 0 : _a.embed) || (blip === null || blip === void 0 ? void 0 : blip.embed);
181
+ if (!relId || !source)
164
182
  return false;
165
- const part = (_a = source.relationshipsPart) === null || _a === void 0 ? void 0 : _a.getPart(blip.embed);
183
+ const part = (_b = source.relationshipsPart) === null || _b === void 0 ? void 0 : _b.getPart(relId);
166
184
  if (!(part instanceof ImagePart))
167
185
  return false;
168
186
  const bytes = await part.getBytes();