@docx4j/core-ts 0.5.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 (155) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +32 -4
  3. package/dist/html/convert.mjs +2 -1
  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 +145 -23
  24. package/dist/model/content/Body.mjs +509 -141
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +195 -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 +11 -0
  32. package/dist/model/content/InlinePicture.mjs +21 -1
  33. package/dist/model/content/List.mjs +3 -0
  34. package/dist/model/content/Paragraph.d.mts +189 -13
  35. package/dist/model/content/Paragraph.mjs +870 -74
  36. package/dist/model/content/Range.d.mts +227 -12
  37. package/dist/model/content/Range.mjs +785 -59
  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 +172 -13
  43. package/dist/model/content/TrackedChange.mjs +728 -167
  44. package/dist/model/content/comments.d.mts +20 -6
  45. package/dist/model/content/comments.mjs +67 -57
  46. package/dist/model/content/generalException.d.mts +18 -0
  47. package/dist/model/content/generalException.mjs +24 -0
  48. package/dist/model/content/index.d.mts +8 -3
  49. package/dist/model/content/index.mjs +7 -2
  50. package/dist/model/content/mathText.d.mts +21 -0
  51. package/dist/model/content/mathText.mjs +267 -0
  52. package/dist/model/content/officeText.d.mts +27 -0
  53. package/dist/model/content/officeText.mjs +74 -0
  54. package/dist/model/content/outline.d.mts +5 -0
  55. package/dist/model/content/outline.mjs +13 -0
  56. package/dist/model/content/placeholders.d.mts +110 -0
  57. package/dist/model/content/placeholders.mjs +253 -0
  58. package/dist/model/content/search.d.mts +72 -0
  59. package/dist/model/content/search.mjs +204 -2
  60. package/dist/model/content/spanEdits.d.mts +159 -0
  61. package/dist/model/content/spanEdits.mjs +728 -0
  62. package/dist/model/content/story.d.mts +44 -0
  63. package/dist/model/content/story.mjs +92 -0
  64. package/dist/model/content/tableColumns.d.mts +1 -1
  65. package/dist/model/content/tableColumns.mjs +108 -80
  66. package/dist/model/content/tableRevisions.d.mts +11 -3
  67. package/dist/model/content/tableRevisions.mjs +72 -42
  68. package/dist/model/content/tableRules.d.mts +30 -0
  69. package/dist/model/content/tableRules.mjs +100 -0
  70. package/dist/model/content/textBoxes.d.mts +18 -0
  71. package/dist/model/content/textBoxes.mjs +105 -0
  72. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  73. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  74. package/dist/model/content/tracking.d.mts +28 -0
  75. package/dist/model/content/tracking.mjs +149 -10
  76. package/dist/model/content/tree.d.mts +171 -22
  77. package/dist/model/content/tree.mjs +329 -53
  78. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  79. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  80. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  81. package/dist/model/customxml/XmlMapping.mjs +11 -0
  82. package/dist/model/customxml/bindings.d.mts +21 -2
  83. package/dist/model/customxml/bindings.mjs +76 -32
  84. package/dist/model/customxml/kinds.d.mts +8 -3
  85. package/dist/model/customxml/kinds.mjs +26 -5
  86. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  87. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  88. package/dist/model/customxml/pictures.d.mts +4 -3
  89. package/dist/model/customxml/pictures.mjs +18 -6
  90. package/dist/model/customxml/xhtml.mjs +8 -2
  91. package/dist/model/index.d.mts +2 -0
  92. package/dist/model/index.mjs +4 -0
  93. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  94. package/dist/model/listnumbering/Emulator.mjs +63 -9
  95. package/dist/model/properties/PropertyResolver.d.mts +60 -1
  96. package/dist/model/properties/PropertyResolver.mjs +115 -2
  97. package/dist/model/properties/catalogue.d.mts +19 -5
  98. package/dist/model/properties/catalogue.mjs +41 -15
  99. package/dist/model/properties/log.d.mts +1 -1
  100. package/dist/model/properties/log.mjs +5 -5
  101. package/dist/model/properties/table.mjs +4 -2
  102. package/dist/model/text/Extractor.d.mts +289 -0
  103. package/dist/model/text/Extractor.mjs +2162 -0
  104. package/dist/model/text/LinkTargets.d.mts +45 -0
  105. package/dist/model/text/LinkTargets.mjs +170 -0
  106. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  107. package/dist/model/text/MarkdownExporter.mjs +255 -0
  108. package/dist/model/text/PlainText.d.mts +29 -0
  109. package/dist/model/text/PlainText.mjs +539 -0
  110. package/dist/model/text/TextContext.d.mts +161 -0
  111. package/dist/model/text/TextContext.mjs +425 -0
  112. package/dist/model/text/TextOptions.d.mts +150 -0
  113. package/dist/model/text/TextOptions.mjs +278 -0
  114. package/dist/model/text/TextResult.d.mts +504 -0
  115. package/dist/model/text/TextResult.mjs +526 -0
  116. package/dist/model/text/Texts.d.mts +99 -0
  117. package/dist/model/text/Texts.mjs +187 -0
  118. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  119. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  120. package/dist/model/text/commonmark.d.mts +121 -0
  121. package/dist/model/text/commonmark.mjs +655 -0
  122. package/dist/model/text/index.d.mts +7 -0
  123. package/dist/model/text/index.mjs +8 -0
  124. package/dist/model/text/normalize.d.mts +8 -0
  125. package/dist/model/text/normalize.mjs +13 -0
  126. package/dist/model/text/outline.d.mts +4 -0
  127. package/dist/model/text/outline.mjs +71 -0
  128. package/dist/office-js/fragment.d.mts +7 -5
  129. package/dist/office-js/fragment.mjs +63 -14
  130. package/dist/office-js/proxy.d.mts +8 -0
  131. package/dist/office-js/proxy.mjs +29 -0
  132. package/dist/office-js/run.mjs +2 -0
  133. package/dist/office-js/supported.generated.mjs +16 -0
  134. package/dist/office-js/toApiScript.mjs +21 -5
  135. package/dist/opc/Save.d.mts +17 -0
  136. package/dist/opc/Save.mjs +12 -2
  137. package/dist/opc/index.d.mts +3 -3
  138. package/dist/opc/index.mjs +1 -1
  139. package/dist/opc/mce/preprocessor.d.mts +28 -3
  140. package/dist/opc/mce/preprocessor.mjs +4 -4
  141. package/dist/opc/mce/understood.d.mts +30 -6
  142. package/dist/opc/mce/understood.mjs +37 -7
  143. package/dist/packages/OpcPackage.d.mts +13 -0
  144. package/dist/packages/OpcPackage.mjs +1 -0
  145. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  146. package/dist/packages/WordprocessingMLPackage.mjs +155 -20
  147. package/dist/parts/DefaultXmlPart.mjs +2 -2
  148. package/dist/parts/Part.d.mts +10 -2
  149. package/dist/parts/XmlPart.d.mts +12 -30
  150. package/dist/parts/XmlPart.mjs +44 -93
  151. package/dist/parts/wml/comments.mjs +30 -0
  152. package/dist/parts/wml/index.mjs +12 -0
  153. package/dist/xml/dom.d.mts +7 -0
  154. package/dist/xml/dom.mjs +18 -0
  155. 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
+ }
@@ -33,9 +33,10 @@ export declare function pictureWidthOf(params: Map<string, string>): PictureWidt
33
33
  * fallback), a tag-bound control holding none is left and noted. Otherwise the control's content
34
34
  * becomes one run holding a new inline picture, sized as `PictureWidth` says, in the first
35
35
  * paragraph of a block-level control (its properties kept) or as a run-level control's content.
36
- * An empty value, a value that is not an image this package reads (PNG, JPEG, GIF, BMP; SVG where
37
- * the drawing is kept, since it has no pixel size to place a new picture at), and a body with no
38
- * 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
39
40
  * stays in the package, as docx4j leaves it.
40
41
  */
41
42
  export declare function bindPicture(control: ContentControl, value: string, width: PictureWidth, label: string): PictureOutcome;
@@ -5,6 +5,7 @@ import { ImagePart } from '../../parts/BinaryPart.mjs';
5
5
  import { base64Decode, base64Encode } from '../../xml/dom.mjs';
6
6
  import { enclosingCellOf } from '../properties/table.mjs';
7
7
  import { tagParamsOf, selectEntryNode, PICTURE_HANDLER } from './opendope.mjs';
8
+ import { showPicturePlaceholder } from './picturePlaceholder.mjs';
8
9
  const EMU_PER_TWIP = 635;
9
10
  const BLIP = 'org_docx4j_dml.CTBlip';
10
11
  /** Whether a control is bound to a picture through its tag (`od:Handler=picture` with an `od:xpath`). */
@@ -81,16 +82,26 @@ function maxWidthEmu(control, width) {
81
82
  * fallback), a tag-bound control holding none is left and noted. Otherwise the control's content
82
83
  * becomes one run holding a new inline picture, sized as `PictureWidth` says, in the first
83
84
  * paragraph of a block-level control (its properties kept) or as a run-level control's content.
84
- * An empty value, a value that is not an image this package reads (PNG, JPEG, GIF, BMP; SVG where
85
- * the drawing is kept, since it has no pixel size to place a new picture at), and a body with no
86
- * 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
87
89
  * stays in the package, as docx4j leaves it.
88
90
  */
89
91
  export function bindPicture(control, value, width, label) {
90
92
  const base64 = base64Of(value);
91
- if (base64 === '')
92
- return { done: false, note: `${label}: the node is empty; the control is left as it is` };
93
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
+ }
94
105
  if (!source)
95
106
  return { done: false, note: `${label}: the control's body has no part to hold an image` };
96
107
  if (width.kind === 'keep') {
@@ -158,7 +169,8 @@ export function applyPictureHandlersTo(controls, parts, result) {
158
169
  export async function updateFromPictureControl(control) {
159
170
  var _a, _b;
160
171
  const mapping = control.xmlMapping;
161
- 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)
162
174
  return false;
163
175
  const node = mapping.customXmlNode;
164
176
  if (!node)
@@ -1,7 +1,8 @@
1
1
  import { deepCopy } from '@docx4j/generated-objects-ts';
2
2
  import { Namespaces } from '../../parts/Namespaces.mjs';
3
- import { typeNameOf, linkParents, childrenOf } from '../content/tree.mjs';
3
+ import { typeNameOf, linkParents, childrenOf, writingEveryBranch } from '../content/tree.mjs';
4
4
  import { contentOf } from '../content/ooxml.mjs';
5
+ import { wrappingCellOf, endWithParagraph } from '../content/tableRules.mjs';
5
6
  import { convertHtml, styleLookupOf } from '../../html/convert.mjs';
6
7
  import { blocksToXml, needsOf, HYPERLINK_STYLE } from '../../html/elements.mjs';
7
8
  import { tagParamsOf, xpathsEntriesOf, XHTML_CONTENT_TYPE } from './opendope.mjs';
@@ -137,8 +138,13 @@ export async function applyXhtmlBindingTo(control, parts, host, options = {}) {
137
138
  content.length = 0;
138
139
  content.push(...elements);
139
140
  linkParents(content, sdt.sdtContent);
141
+ // markup ending in a table, in a cell's control: the cell ends with a paragraph after it, docx4j's repair (CR-002 section 55)
142
+ const cell = wrappingCellOf(sdt.PARENT);
143
+ if (cell)
144
+ endWithParagraph(cell);
140
145
  }
141
- control.isShowingPlaceholder = false;
146
+ // a control in a branch of a kept mc:AlternateContent too: the pass reaches every branch (CR-013 phase 3)
147
+ writingEveryBranch(() => { control.isShowingPlaceholder = false; });
142
148
  return Object.keys(dropped).length > 0 ? { done: true, dropped } : { done: true };
143
149
  }
144
150
  /** The XHTML pass over the controls, after the text bindings: counted into the result, what was left said in its notes. */
@@ -2,5 +2,7 @@ export * from './content/index.mjs';
2
2
  export * from './properties/index.mjs';
3
3
  export * from './listnumbering/index.mjs';
4
4
  export * from './fonts/index.mjs';
5
+ export * from './color/index.mjs';
5
6
  export * from './customxml/index.mjs';
7
+ export * from './text/index.mjs';
6
8
  export { toApiScript, type ApiScriptOptions, type ApiScriptTarget } from '../office-js/toApiScript.mjs';
@@ -7,8 +7,12 @@ export * from './properties/index.mjs';
7
7
  export * from './listnumbering/index.mjs';
8
8
  // CR-001 Phase B step 4: RunFontSelector, the Mapper and the default theme.
9
9
  export * from './fonts/index.mjs';
10
+ // CR-008 phase 2: colours as Office draws them (ThemeColors, WordColors, DrawingMLColors, SpreadsheetColors).
11
+ export * from './color/index.mjs';
10
12
  // CR-002 phase E: custom XML parts, XML mapping, the XPath engine, the typed content controls.
11
13
  export * from './customxml/index.mjs';
14
+ // CR-012: the text model (Texts, the block model, plain text).
15
+ export * from './text/index.mjs';
12
16
  // The source generator of CR-002 phase I: the content-API calls that reproduce a paragraph, a
13
17
  // range or a table. The `Word` shim it lives next to stays on the ./office-js subpath.
14
18
  export { toApiScript } from '../office-js/toApiScript.mjs';