@docx4j/core-ts 0.5.0 → 0.7.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 (207) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +92 -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 +507 -150
  25. package/dist/model/content/Comment.d.mts +23 -5
  26. package/dist/model/content/Comment.mjs +207 -26
  27. package/dist/model/content/ContentControl.d.mts +198 -19
  28. package/dist/model/content/ContentControl.mjs +672 -99
  29. package/dist/model/content/Font.d.mts +70 -17
  30. package/dist/model/content/Font.mjs +119 -36
  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.d.mts +6 -3
  34. package/dist/model/content/List.mjs +18 -8
  35. package/dist/model/content/Paragraph.d.mts +200 -14
  36. package/dist/model/content/Paragraph.mjs +898 -82
  37. package/dist/model/content/Range.d.mts +227 -12
  38. package/dist/model/content/Range.mjs +785 -59
  39. package/dist/model/content/ResolvedView.d.mts +104 -0
  40. package/dist/model/content/ResolvedView.mjs +596 -0
  41. package/dist/model/content/Table.d.mts +29 -3
  42. package/dist/model/content/Table.mjs +135 -49
  43. package/dist/model/content/TrackedChange.d.mts +174 -15
  44. package/dist/model/content/TrackedChange.mjs +734 -170
  45. package/dist/model/content/comments.d.mts +20 -6
  46. package/dist/model/content/comments.mjs +67 -57
  47. package/dist/model/content/generalException.d.mts +18 -0
  48. package/dist/model/content/generalException.mjs +24 -0
  49. package/dist/model/content/index.d.mts +10 -3
  50. package/dist/model/content/index.mjs +10 -2
  51. package/dist/model/content/mathText.d.mts +21 -0
  52. package/dist/model/content/mathText.mjs +267 -0
  53. package/dist/model/content/officeText.d.mts +27 -0
  54. package/dist/model/content/officeText.mjs +74 -0
  55. package/dist/model/content/ooxml.mjs +12 -1
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/paraIds.d.mts +5 -0
  59. package/dist/model/content/paraIds.mjs +54 -0
  60. package/dist/model/content/placeholders.d.mts +110 -0
  61. package/dist/model/content/placeholders.mjs +253 -0
  62. package/dist/model/content/search.d.mts +74 -0
  63. package/dist/model/content/search.mjs +255 -9
  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 +47 -0
  67. package/dist/model/content/story.mjs +95 -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 +102 -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 +63 -6
  79. package/dist/model/content/tracking.mjs +242 -22
  80. package/dist/model/content/tree.d.mts +172 -24
  81. package/dist/model/content/tree.mjs +328 -54
  82. package/dist/model/content/wordRange.d.mts +48 -0
  83. package/dist/model/content/wordRange.mjs +97 -0
  84. package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
  85. package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
  86. package/dist/model/customxml/XmlMapping.d.mts +32 -8
  87. package/dist/model/customxml/XmlMapping.mjs +64 -10
  88. package/dist/model/customxml/bindings.d.mts +33 -4
  89. package/dist/model/customxml/bindings.mjs +171 -45
  90. package/dist/model/customxml/index.d.mts +1 -0
  91. package/dist/model/customxml/index.mjs +1 -0
  92. package/dist/model/customxml/kinds.d.mts +8 -3
  93. package/dist/model/customxml/kinds.mjs +26 -5
  94. package/dist/model/customxml/opendope.d.mts +4 -1
  95. package/dist/model/customxml/opendope.mjs +9 -6
  96. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  97. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  98. package/dist/model/customxml/pictures.d.mts +4 -3
  99. package/dist/model/customxml/pictures.mjs +22 -8
  100. package/dist/model/customxml/storeItems.d.mts +57 -0
  101. package/dist/model/customxml/storeItems.mjs +122 -0
  102. package/dist/model/customxml/xhtml.mjs +23 -22
  103. package/dist/model/dateZone.d.mts +25 -0
  104. package/dist/model/dateZone.mjs +57 -0
  105. package/dist/model/index.d.mts +3 -0
  106. package/dist/model/index.mjs +5 -0
  107. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  108. package/dist/model/listnumbering/Emulator.mjs +63 -9
  109. package/dist/model/properties/PropertyResolver.d.mts +74 -2
  110. package/dist/model/properties/PropertyResolver.mjs +133 -5
  111. package/dist/model/properties/catalogue.d.mts +19 -5
  112. package/dist/model/properties/catalogue.mjs +41 -15
  113. package/dist/model/properties/index.d.mts +1 -1
  114. package/dist/model/properties/log.d.mts +1 -1
  115. package/dist/model/properties/log.mjs +5 -5
  116. package/dist/model/properties/styleUtil.d.mts +3 -1
  117. package/dist/model/properties/styleUtil.mjs +20 -24
  118. package/dist/model/properties/table.mjs +4 -2
  119. package/dist/model/text/CaseMapping.d.mts +38 -0
  120. package/dist/model/text/CaseMapping.mjs +86 -0
  121. package/dist/model/text/Extractor.d.mts +349 -0
  122. package/dist/model/text/Extractor.mjs +2406 -0
  123. package/dist/model/text/LinkTargets.d.mts +45 -0
  124. package/dist/model/text/LinkTargets.mjs +170 -0
  125. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  126. package/dist/model/text/MarkdownExporter.mjs +255 -0
  127. package/dist/model/text/PlainText.d.mts +29 -0
  128. package/dist/model/text/PlainText.mjs +542 -0
  129. package/dist/model/text/TextContext.d.mts +256 -0
  130. package/dist/model/text/TextContext.mjs +600 -0
  131. package/dist/model/text/TextOptions.d.mts +233 -0
  132. package/dist/model/text/TextOptions.mjs +419 -0
  133. package/dist/model/text/TextResult.d.mts +621 -0
  134. package/dist/model/text/TextResult.mjs +629 -0
  135. package/dist/model/text/Texts.d.mts +99 -0
  136. package/dist/model/text/Texts.mjs +187 -0
  137. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  138. package/dist/model/text/WmlToMarkdown.mjs +936 -0
  139. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  140. package/dist/model/text/caseMapping.generated.mjs +214 -0
  141. package/dist/model/text/commonmark.d.mts +121 -0
  142. package/dist/model/text/commonmark.mjs +655 -0
  143. package/dist/model/text/index.d.mts +8 -0
  144. package/dist/model/text/index.mjs +9 -0
  145. package/dist/model/text/normalize.d.mts +8 -0
  146. package/dist/model/text/normalize.mjs +13 -0
  147. package/dist/model/text/outline.d.mts +4 -0
  148. package/dist/model/text/outline.mjs +71 -0
  149. package/dist/model/wordIntegers.d.mts +29 -0
  150. package/dist/model/wordIntegers.mjs +78 -0
  151. package/dist/office-js/document.mjs +15 -1
  152. package/dist/office-js/fragment.d.mts +7 -5
  153. package/dist/office-js/fragment.mjs +63 -14
  154. package/dist/office-js/proxy.d.mts +8 -0
  155. package/dist/office-js/proxy.mjs +29 -0
  156. package/dist/office-js/run.mjs +2 -0
  157. package/dist/office-js/supported.generated.mjs +16 -0
  158. package/dist/office-js/toApiScript.mjs +29 -12
  159. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  160. package/dist/opc/DirectoryPartStore.mjs +11 -4
  161. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  162. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  163. package/dist/opc/Load.d.mts +32 -0
  164. package/dist/opc/Load.mjs +115 -5
  165. package/dist/opc/PartStore.d.mts +34 -1
  166. package/dist/opc/PartStore.mjs +71 -8
  167. package/dist/opc/Save.d.mts +32 -0
  168. package/dist/opc/Save.mjs +110 -4
  169. package/dist/opc/ZipPartStore.d.mts +4 -2
  170. package/dist/opc/ZipPartStore.mjs +12 -7
  171. package/dist/opc/exceptions.d.mts +140 -0
  172. package/dist/opc/exceptions.mjs +108 -0
  173. package/dist/opc/index.d.mts +8 -5
  174. package/dist/opc/index.mjs +5 -3
  175. package/dist/opc/mce/preprocessor.d.mts +141 -5
  176. package/dist/opc/mce/preprocessor.mjs +119 -18
  177. package/dist/opc/mce/understood.d.mts +3 -11
  178. package/dist/opc/mce/understood.mjs +27 -23
  179. package/dist/opc/repair/breaks.d.mts +23 -0
  180. package/dist/opc/repair/breaks.mjs +288 -0
  181. package/dist/opc/repair/hosts.d.mts +12 -0
  182. package/dist/opc/repair/hosts.mjs +33 -0
  183. package/dist/opc/repair/mainRoot.d.mts +19 -0
  184. package/dist/opc/repair/mainRoot.mjs +168 -0
  185. package/dist/opc/repair/unknownElements.d.mts +8 -0
  186. package/dist/opc/repair/unknownElements.mjs +180 -0
  187. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  188. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  189. package/dist/opc/skipped.d.mts +50 -0
  190. package/dist/opc/skipped.mjs +140 -0
  191. package/dist/packages/OpcPackage.d.mts +110 -4
  192. package/dist/packages/OpcPackage.mjs +123 -7
  193. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  194. package/dist/packages/WordprocessingMLPackage.mjs +158 -21
  195. package/dist/packages/index.d.mts +1 -1
  196. package/dist/parts/DefaultXmlPart.mjs +2 -2
  197. package/dist/parts/Part.d.mts +14 -2
  198. package/dist/parts/RelationshipsPart.d.mts +15 -0
  199. package/dist/parts/RelationshipsPart.mjs +66 -1
  200. package/dist/parts/XmlPart.d.mts +63 -32
  201. package/dist/parts/XmlPart.mjs +240 -96
  202. package/dist/parts/wml/comments.mjs +30 -0
  203. package/dist/parts/wml/index.d.mts +12 -1
  204. package/dist/parts/wml/index.mjs +37 -1
  205. package/dist/xml/dom.d.mts +7 -0
  206. package/dist/xml/dom.mjs +18 -0
  207. package/package.json +2 -2
@@ -1,24 +1,12 @@
1
1
  import { unmarshalNode, marshalNode, NAMESPACE_PREFIXES } from '@docx4j/generated-objects-ts';
2
2
  import { Part } from './Part.mjs';
3
- import { Namespaces } from './Namespaces.mjs';
4
3
  import { Docx4JException, PartUnmarshalException } from '../opc/exceptions.mjs';
5
- import { parseXml, serializeXml, encodeText, decodeXmlText, XML_DECLARATION } from '../xml/dom.mjs';
6
- import { log } from '../model/properties/log.mjs';
4
+ import { parseXml, serializeXml, encodeText, decodeXmlText, XML_DECLARATION, assertXmlCharacters } from '../xml/dom.mjs';
7
5
  import { bindSnapshot, boundsOf } from '../model/content/binder.mjs';
8
- const XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
9
- const MC_NS = Namespaces.MARKUP_COMPATIBILITY;
10
- /** The objects package's prefix table the other way round, prefix to URI, built once. */
11
- let prefixToUri;
12
- function knownUriOf(prefix) {
13
- if (prefixToUri === undefined) {
14
- prefixToUri = new Map();
15
- for (const [uri, p] of Object.entries(NAMESPACE_PREFIXES))
16
- if (p !== '' && !prefixToUri.has(p))
17
- prefixToUri.set(p, uri);
18
- }
19
- return prefixToUri.get(prefix);
20
- }
21
- const warnedPrefixes = new Set();
6
+ import { repairBreaksOutsideRuns } from '../opc/repair/breaks.mjs';
7
+ import { readThroughUnknownElements } from '../opc/repair/unknownElements.mjs';
8
+ import { skippedAttribute, skippedElement, skippedText } from '../opc/skipped.mjs';
9
+ import { rangeOf, describeReading, UNMEASURED_RANGE } from '../model/wordIntegers.mjs';
22
10
  /**
23
11
  * The element or attribute a located runtime error names, out of its message: jsonix writes
24
12
  * `w:sz/@w:val (line 7): ...` when it could place the fault and `w:bogus (line 3): ...` for an
@@ -39,6 +27,96 @@ function positionOf(line, column) {
39
27
  return '';
40
28
  return column === undefined ? ` (line ${line})` : ` (line ${line}, column ${column})`;
41
29
  }
30
+ /** A value the objects facade read by Word's rule, as a warning on the part (CR-015 D8). */
31
+ function valueKept(reading, partName) {
32
+ var _a, _b, _c;
33
+ const node = reading.element;
34
+ const name = reading.attribute;
35
+ const attribute = (_c = (_b = (_a = node.getAttributeNodeNS) === null || _a === void 0 ? void 0 : _a.call(node, name.namespaceURI || null, name.localPart)) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : name.localPart;
36
+ const value = reading.value;
37
+ const read = value === true ? 'on' : value === undefined ? 'absent' : JSON.stringify(value);
38
+ // a form Word refuses a document for is dropped (the objects package's CR-015; CR-017 P1)
39
+ const outcome = reading.kind === 'value.refused' ? 'dropped: Word 365 refuses a document with this value' : `read as ${read}, as Word 365 reads it`;
40
+ return {
41
+ code: reading.kind, partName,
42
+ ...(typeof node.lineNumber === 'number' ? { line: node.lineNumber } : {}),
43
+ ...(typeof node.columnNumber === 'number' ? { column: node.columnNumber } : {}),
44
+ element: node.nodeName, attribute, text: reading.text, value,
45
+ message: `${partName}: ${node.nodeName}/@${attribute}=${JSON.stringify(reading.text)}${positionOf(node.lineNumber, node.columnNumber)} is not a value its type allows; ${outcome}`,
46
+ };
47
+ }
48
+ /**
49
+ * An integer attribute value Word 365 reads differently, as the objects facade reports it on read or on save, as a
50
+ * warning (CR-017 P2 and P3). The value stays as written; the message says what Word would read, the storage and the
51
+ * range Word keeps as written, from the table entry the report names (`storage`, `property` and `rule`, objects 0.7.0).
52
+ */
53
+ export function valueOutOfRange(report, partName) {
54
+ var _a, _b, _c;
55
+ const node = report.element;
56
+ const name = report.attribute;
57
+ const attribute = (_c = (_b = (_a = node.getAttributeNodeNS) === null || _a === void 0 ? void 0 : _a.call(node, name.namespaceURI || null, name.localPart)) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : `w:${name.localPart}`;
58
+ const reading = report.wordReads;
59
+ const unmeasured = !report.storage.measured;
60
+ // the rule beside a line or a height (`report.rule`, from the tree) decides what Word keeps there
61
+ const range = rangeOf(report.storage, report.property, report.rule);
62
+ const at = report.path !== undefined ? `${report.path}=${report.value}` : `${node.nodeName}/@${attribute}=${report.value}${positionOf(node.lineNumber, node.columnNumber)}`;
63
+ const where = unmeasured
64
+ ? `is outside ${UNMEASURED_RANGE[0]} to ${UNMEASURED_RANGE[1]}, where Word 365 changes every measured value of its kind (${node.nodeName}/@${attribute} is unmeasured); what Word reads is not known`
65
+ : `is past what Word 365 stores there (${reading.storage}${range ? `, ${range[0]} to ${range[1]}` : ''}); Word reads it as ${describeReading(reading, name.localPart)}`;
66
+ const prefix = partName === undefined ? '' : `${partName}: `;
67
+ return {
68
+ code: 'value.out_of_range', partName,
69
+ ...(report.path === undefined && typeof node.lineNumber === 'number' ? { line: node.lineNumber } : {}),
70
+ ...(report.path === undefined && typeof node.columnNumber === 'number' ? { column: node.columnNumber } : {}),
71
+ ...(report.path !== undefined ? { path: report.path } : {}),
72
+ element: node.nodeName, attribute, value: report.value, wordReads: reading.value,
73
+ ...(reading.attribute !== undefined && reading.attribute !== name.localPart ? { readAs: `w:${reading.attribute}` } : {}),
74
+ ...(reading.rule !== undefined ? { rule: reading.rule } : {}),
75
+ storage: reading.storage, range,
76
+ message: `${prefix}${at} ${where}`,
77
+ };
78
+ }
79
+ /** The last segment of an objects `TYPE_NAME`, docx4j's class name: `org_docx4j_wml.R` is `R`. */
80
+ const className = (typeName) => typeName.slice(typeName.lastIndexOf('.') + 1);
81
+ /**
82
+ * A child its container never admits, as the objects facade reports it on read or on save (its CR-017's
83
+ * `onMisplaced`), as a warning (CR-016 D7, D2): docx4j-python's code and fields (`target.misplaced`; `holder` and `item`
84
+ * docx4j's class names), docx4j's message after the part and the place (the path on save; the container, the child's
85
+ * place in its list and the line on read).
86
+ */
87
+ export function misplacedContent(report, partName) {
88
+ var _a;
89
+ const container = report.element;
90
+ const located = (_a = report.childElement) !== null && _a !== void 0 ? _a : container;
91
+ const isText = report.child === '#text';
92
+ const element = isText ? undefined : qualifiedName(report.child);
93
+ const onRead = report.path === undefined;
94
+ const line = onRead && typeof located.lineNumber === 'number' ? located.lineNumber : undefined;
95
+ const column = onRead && typeof located.columnNumber === 'number' ? located.columnNumber : undefined;
96
+ const shown = report.text === undefined ? '' : ` ${JSON.stringify(report.text.length > 80 ? `${report.text.slice(0, 80)}...` : report.text)}`;
97
+ const at = onRead
98
+ ? `${container.nodeName}${report.via === undefined ? '' : `/${report.via}`}, child ${report.index} (${element !== null && element !== void 0 ? element : 'a string'}${shown})${positionOf(line, column)}`
99
+ : report.path;
100
+ const prefix = partName === undefined ? '' : `${partName}: `;
101
+ return {
102
+ code: 'target.misplaced', partName,
103
+ ...(line !== undefined ? { line } : {}),
104
+ ...(column !== undefined ? { column } : {}),
105
+ ...(onRead ? {} : { path: report.path }),
106
+ holder: className(report.container), index: report.index,
107
+ ...(report.childType !== undefined ? { item: className(report.childType) } : {}),
108
+ ...(element !== undefined ? { element } : {}),
109
+ rule: report.rule,
110
+ ...(report.text !== undefined ? { text: report.text } : {}),
111
+ message: `${prefix}${at}: ${report.message}`,
112
+ };
113
+ }
114
+ /** `w:tbl` for a child's name: its prefix as the report has it, else the objects package's for the namespace. */
115
+ function qualifiedName(name) {
116
+ var _a;
117
+ const prefix = (_a = name.prefix) !== null && _a !== void 0 ? _a : NAMESPACE_PREFIXES[name.namespaceURI];
118
+ return prefix ? `${prefix}:${name.localPart}` : name.localPart;
119
+ }
42
120
  /** The objects package's prefix table as XPath wants it, prefix to URI; built once. */
43
121
  const NAMESPACE_PREFIXES_BY_PREFIX = (() => {
44
122
  const out = {};
@@ -75,9 +153,14 @@ export class XmlPart extends Part {
75
153
  return this.element === undefined && this.bytes === undefined;
76
154
  }
77
155
  /**
78
- * What the preprocessor reported the last time this part was parsed (CR-010 phase 1): an
79
- * mc:AlternateContent whose branches' text differs (`mce.branches_differ`). Empty until the part
80
- * is parsed, by `getContents()` or a private read, and again once its contents are set.
156
+ * What the last parse of this part reported: an mc:AlternateContent whose branches' text differs
157
+ * (`mce.branches_differ`, CR-010 phase 1), each attribute value its type refuses that was read as
158
+ * Word 365 reads it (`value.kept.exact`, `value.kept.invalid`, `value.truncated`, `value.refused`: CR-015 D8,
159
+ * CR-017 P1), each integer attribute value past what Word stores, kept as written (`value.out_of_range`,
160
+ * CR-017 P2), and each child its container never admits, kept as read but a string the runtime drops
161
+ * (`target.misplaced`, CR-016 D7), and a w:br outside a w:r read as Word reads it (`br.outside_run`, CR-021).
162
+ * Empty until the part is parsed, by `getContents()` or a private read, and again
163
+ * once its contents are set.
81
164
  */
82
165
  get warnings() {
83
166
  return this.parseWarnings;
@@ -97,10 +180,10 @@ export class XmlPart extends Part {
97
180
  var _a, _b;
98
181
  if (this.element === undefined) {
99
182
  const bytes = (_a = this.bytes) !== null && _a !== void 0 ? _a : (await this.loadSourceBytes());
183
+ this.parsedBytes = this.bytes;
100
184
  if (bytes === undefined)
101
185
  throw new Docx4JException(`Part ${this.partName} has no content: set contents or load it from a container`);
102
186
  const doc = this.parse(bytes);
103
- this.recordDeclarations(doc);
104
187
  this.preprocess(doc);
105
188
  this.element = await this.unmarshal(doc);
106
189
  (_b = this.rootName) !== null && _b !== void 0 ? _b : (this.rootName = { namespaceURI: this.element.name.namespaceURI, localPart: this.element.name.localPart });
@@ -118,21 +201,61 @@ export class XmlPart extends Part {
118
201
  * reads the styles and numbering parts of a document nobody has otherwise touched.
119
202
  */
120
203
  async readContents() {
204
+ return (await this.readElement()).value;
205
+ }
206
+ /** `readContents`'s typed element: the live one, else a private tree from the part's bytes. */
207
+ async readElement() {
121
208
  var _a;
122
209
  if (this.element !== undefined)
123
- return this.element.value;
210
+ return this.element;
124
211
  const bytes = (_a = this.bytes) !== null && _a !== void 0 ? _a : (await this.loadSourceBytes());
125
212
  if (bytes === undefined)
126
213
  throw new Docx4JException(`Part ${this.partName} has no content: set contents or load it from a container`);
127
214
  const doc = this.parse(bytes);
128
- this.recordDeclarations(doc);
129
215
  this.preprocess(doc);
130
- return (await this.unmarshal(doc)).value;
216
+ return this.unmarshal(doc);
217
+ }
218
+ /**
219
+ * Every child the part holds that its container never admits (CR-016 D6, the check on request): what a save would
220
+ * refuse the part for, each with its path, the rule and docx4j's message naming the fix, found by the objects
221
+ * facade's walk over the tree (a marshal to nothing); the part is not changed, and one never unmarshalled stays
222
+ * untouched, read privately. `[]` for a part whose content is all in its place.
223
+ */
224
+ async checkContent() {
225
+ const element = await this.readElement();
226
+ const out = [];
227
+ try {
228
+ await marshalNode(element, { onMisplaced: (report) => out.push(misplacedContent(report, this.partName.name)) });
229
+ }
230
+ catch (cause) {
231
+ // the runtime refuses to write what the walk reported (a string in a run); with nothing reported, the fault is another
232
+ if (out.length === 0)
233
+ throw new Docx4JException(`Part ${this.partName}: its content cannot be written`, { cause });
234
+ }
235
+ return out;
131
236
  }
237
+ /**
238
+ * The DOM before unmarshalling: a w:br outside a w:r put in a run, as Word reads it (CR-021: a reading of the file,
239
+ * not an option, so ahead of the package's hook, which sees the repaired DOM), then the hook.
240
+ */
132
241
  preprocess(doc) {
242
+ this.parseWarnings = this.preprocessed(doc);
243
+ }
244
+ /**
245
+ * Reads the part's root before anything else meets the DOM; nothing by default. A main document part reads a root other than
246
+ * `w:document` here (WD REQ-318 to REQ-320, CR-028), throwing for one Word refuses.
247
+ */
248
+ readRoot(_doc) {
249
+ return [];
250
+ }
251
+ preprocessed(doc) {
133
252
  var _a;
253
+ const rooted = this.readRoot(doc);
254
+ // ahead of CR-021's, which then gives a w:br the element held a run, as Word does (`76t42`)
255
+ const read = readThroughUnknownElements(doc, this.partName.name);
256
+ const repaired = repairBreaksOutsideRuns(doc, this.partName.name);
134
257
  const warnings = (_a = this.preprocessor) === null || _a === void 0 ? void 0 : _a.call(this, doc, this);
135
- this.parseWarnings = warnings ? [...warnings] : [];
258
+ return [...rooted, ...read, ...repaired, ...(warnings !== null && warnings !== void 0 ? warnings : [])];
136
259
  }
137
260
  /** Replaces the contents (docx4j setJaxbElement / setContents). The root name defaults to the part's. */
138
261
  setContents(value, rootName) {
@@ -142,12 +265,58 @@ export class XmlPart extends Part {
142
265
  throw new Docx4JException(`Part ${this.partName}: no root element name; pass one`);
143
266
  this.element = { name: { namespaceURI: name.namespaceURI, localPart: name.localPart }, value };
144
267
  this.bytes = undefined;
268
+ this.parsedBytes = undefined;
145
269
  this.parseWarnings = [];
146
270
  }
147
271
  /** Replaces the contents with raw XML bytes; they are unmarshalled on the next getContents(). */
148
272
  setBytes(bytes) {
149
273
  this.bytes = bytes;
150
274
  this.element = undefined;
275
+ this.parsedBytes = undefined;
276
+ }
277
+ /** What the read of the tree held now dropped (CR-025): nothing for a part not unmarshalled, or given contents. */
278
+ get skippedByRead() {
279
+ if (this.element === undefined)
280
+ return [];
281
+ return this.parseWarnings.filter((w) => w.code === 'part.skipped');
282
+ }
283
+ /** The bytes the tree held now was read from. */
284
+ async bytesRead() {
285
+ var _a;
286
+ return (_a = this.parsedBytes) !== null && _a !== void 0 ? _a : this.loadSourceBytes();
287
+ }
288
+ /**
289
+ * Whether the tree is still what its source reads as (CR-025 section 2.2): the source read again and the two marshalled
290
+ * alike. The save has no change log to ask, so this is for a part that holds skipped content, and only that: such a part,
291
+ * read and not changed, is copied, where a marshal would lose what the read dropped. What can't be compared counts as changed.
292
+ */
293
+ async isUnchangedSinceRead() {
294
+ var _a;
295
+ if (this.element === undefined)
296
+ return true;
297
+ const bytes = await this.bytesRead();
298
+ if (bytes === undefined)
299
+ return false;
300
+ const marshal = {};
301
+ const normalise = (_a = this.package) === null || _a === void 0 ? void 0 : _a.normaliseOnSave;
302
+ if (normalise !== undefined)
303
+ marshal.normaliseOnSave = normalise;
304
+ try {
305
+ const doc = this.parse(bytes);
306
+ this.preprocessed(doc);
307
+ // read with the read's callbacks, their reports discarded: without onUnexpectedElement the runtime refuses an unknown
308
+ // element in the hosts route B skips it in (CR-026), and the part would count as changed
309
+ const ignore = () => { };
310
+ const source = await unmarshalNode(doc, {
311
+ onWordReading: ignore, onOutOfRange: ignore, onMisplaced: ignore,
312
+ onUnexpectedElement: ignore, onUnexpectedAttribute: ignore, onUnexpectedText: ignore,
313
+ });
314
+ return serializeXml(await marshalNode(source, marshal))
315
+ === serializeXml(await marshalNode(this.element, marshal));
316
+ }
317
+ catch {
318
+ return false;
319
+ }
151
320
  }
152
321
  /** Replaces the contents with XML text (docx4j setContents from a string). */
153
322
  setXml(xml) {
@@ -158,9 +327,9 @@ export class XmlPart extends Part {
158
327
  return decodeXmlText(await this.getBytes());
159
328
  }
160
329
  /** Marshalled bytes when unmarshalled or set, else the source bytes untouched. */
161
- async getBytes() {
330
+ async getBytes(options) {
162
331
  if (this.element !== undefined)
163
- return encodeText(XML_DECLARATION + serializeXml(await this.marshalToNode()));
332
+ return encodeText(assertXmlCharacters(XML_DECLARATION + serializeXml(await this.marshalToNode(options)), `Part ${this.partName}`));
164
333
  if (this.bytes !== undefined)
165
334
  return this.bytes;
166
335
  const loaded = await this.loadSourceBytes();
@@ -198,13 +367,26 @@ export class XmlPart extends Part {
198
367
  const namespaces = { ...NAMESPACE_PREFIXES_BY_PREFIX, ...options.namespaces };
199
368
  return boundsOf(engine.select(xpath, snapshot.root, namespaces), snapshot);
200
369
  }
201
- /** The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). */
202
- async marshalToNode() {
370
+ /**
371
+ * The root element as a DOM element (docx4j XmlUtils.marshaltoW3CDomDocument). Run properties are written in
372
+ * their normal form unless the package's `normaliseOnSave` (else the objects package's `setNormaliseOnSave`) is
373
+ * false (CR-015 D2, the objects facade's CR-013 phase C): the tree keeps what it read.
374
+ */
375
+ async marshalToNode(options) {
376
+ var _a;
203
377
  if (this.element === undefined)
204
378
  await this.getContents();
205
- const root = await marshalNode(this.element);
206
- this.declareIgnorablePrefixes(root);
207
- return root;
379
+ const marshal = {};
380
+ const normalise = (_a = this.package) === null || _a === void 0 ? void 0 : _a.normaliseOnSave;
381
+ if (normalise !== undefined)
382
+ marshal.normaliseOnSave = normalise;
383
+ if ((options === null || options === void 0 ? void 0 : options.onNormalised) !== undefined)
384
+ marshal.onNormalised = options.onNormalised;
385
+ if ((options === null || options === void 0 ? void 0 : options.onOutOfRange) !== undefined)
386
+ marshal.onOutOfRange = options.onOutOfRange; // the report carries its rule (objects 0.7.0)
387
+ if ((options === null || options === void 0 ? void 0 : options.onMisplaced) !== undefined)
388
+ marshal.onMisplaced = options.onMisplaced;
389
+ return marshalNode(this.element, marshal);
208
390
  }
209
391
  /**
210
392
  * Parses the part's bytes, and names the part when the XML is not well-formed. xmldom's error
@@ -236,8 +418,33 @@ export class XmlPart extends Part {
236
418
  */
237
419
  async unmarshal(doc) {
238
420
  var _a, _b, _c, _d, _e, _f;
421
+ const kept = [];
422
+ const partName = this.partName.name;
423
+ // A string CR-016's walk reports as misplaced isn't reported twice: that walk runs after the text's own callback.
424
+ const textHolders = new Map();
425
+ const misplacedStrings = new Set();
239
426
  try {
240
- return await unmarshalNode(doc);
427
+ const element = await unmarshalNode(doc, {
428
+ onWordReading: (reading) => kept.push(valueKept(reading, partName)),
429
+ onOutOfRange: (report) => kept.push(valueOutOfRange(report, partName)),
430
+ onMisplaced: (report) => {
431
+ kept.push(misplacedContent(report, partName));
432
+ if (report.child === '#text')
433
+ misplacedStrings.add(report.element);
434
+ },
435
+ // What the read drops (CR-025). These stay at this one call: objects CR-007 keeps them per call.
436
+ onUnexpectedElement: (_name, node, classInfo) => kept.push(skippedElement(node, partName, classInfo.name)),
437
+ onUnexpectedAttribute: (name, node, classInfo) => kept.push(skippedAttribute(name, node, partName, classInfo.name)),
438
+ onUnexpectedText: (text, node, classInfo) => {
439
+ const skipped = skippedText(text, node, partName, classInfo.name);
440
+ textHolders.set(skipped, node.parentNode);
441
+ kept.push(skipped);
442
+ },
443
+ });
444
+ const reported = kept.filter((w) => !(w.code === 'part.skipped' && misplacedStrings.has(textHolders.get(w))));
445
+ if (reported.length > 0)
446
+ this.parseWarnings = [...this.parseWarnings, ...reported];
447
+ return element;
241
448
  }
242
449
  catch (cause) {
243
450
  const located = cause;
@@ -256,67 +463,4 @@ export class XmlPart extends Part {
256
463
  throw new PartUnmarshalException(`${this.partName}: ${detail}`, this.partName.name, { cause, location, line, column });
257
464
  }
258
465
  }
259
- /** The source root element's namespace declarations, prefix to URI (`xmlns=` is the empty prefix). */
260
- recordDeclarations(doc) {
261
- var _a;
262
- const root = doc.documentElement;
263
- if (!root)
264
- return;
265
- const map = new Map();
266
- for (const attr of Array.from(root.attributes)) {
267
- if (attr.name === 'xmlns')
268
- map.set('', attr.value);
269
- else if (attr.namespaceURI === XMLNS_NS || attr.name.startsWith('xmlns:'))
270
- map.set((_a = attr.localName) !== null && _a !== void 0 ? _a : attr.name.slice(6), attr.value);
271
- }
272
- this.sourceDeclarations = map;
273
- }
274
- /**
275
- * Every prefix the marshalled root's `mc:Ignorable` names must be declared on that root
276
- * (ECMA-376 Part 3 10.1.2; Word and Excel treat an undeclared one as a corrupt part and offer
277
- * to repair the file). The object model binds only the namespaces it has modules for, so an
278
- * `mc:Ignorable` prefix whose content was dropped on unmarshal - `xr6`, `xr10` and `xr2` on
279
- * Excel's `xl/workbook.xml`, for instance - loses its declaration: the runtime declares one
280
- * prefix per entry of the objects package's table and the facade can only *remove* unused
281
- * declarations, never add one it never made (CR-001 section 17).
282
- *
283
- * So the part re-declares them itself: from what the source root declared, else from the
284
- * objects package's `NAMESPACE_PREFIXES`. A prefix neither knows is dropped from
285
- * `mc:Ignorable` - an undeclared prefix is the one thing that must not be written - and warned
286
- * about once.
287
- */
288
- declareIgnorablePrefixes(root) {
289
- var _a, _b;
290
- const attr = Array.from(root.attributes).find((a) => { var _a; return a.namespaceURI === MC_NS && ((_a = a.localName) !== null && _a !== void 0 ? _a : a.name) === 'Ignorable'; });
291
- if (!attr)
292
- return;
293
- const prefixes = attr.value.split(/\s+/).filter((p) => p !== '');
294
- if (prefixes.length === 0)
295
- return;
296
- const kept = [];
297
- let dropped = false;
298
- for (const prefix of prefixes) {
299
- if (root.getAttributeNS(XMLNS_NS, prefix) || root.getAttribute(`xmlns:${prefix}`)) {
300
- kept.push(prefix);
301
- continue;
302
- }
303
- const uri = (_b = (_a = this.sourceDeclarations) === null || _a === void 0 ? void 0 : _a.get(prefix)) !== null && _b !== void 0 ? _b : knownUriOf(prefix);
304
- if (uri === undefined) {
305
- dropped = true;
306
- if (!warnedPrefixes.has(prefix)) {
307
- warnedPrefixes.add(prefix);
308
- log.warn(`${this.partName}: mc:Ignorable names the prefix "${prefix}", which nothing declares; dropping it (an undeclared prefix makes Word and Excel offer to repair the file)`);
309
- }
310
- continue;
311
- }
312
- root.setAttributeNS(XMLNS_NS, `xmlns:${prefix}`, uri);
313
- kept.push(prefix);
314
- }
315
- if (!dropped)
316
- return;
317
- if (kept.length === 0)
318
- root.removeAttributeNode(attr);
319
- else
320
- root.setAttributeNS(MC_NS, attr.name, kept.join(' '));
321
- }
322
466
  }
@@ -47,6 +47,36 @@ class DocumentCommentParts {
47
47
  this.loadedAll = true;
48
48
  return this;
49
49
  }
50
+ async loadExisting() {
51
+ var _a, _b, _c;
52
+ const main = this.main;
53
+ if (main.commentsExtendedPart)
54
+ (_a = this.commentsEx) !== null && _a !== void 0 ? _a : (this.commentsEx = await main.commentsExtendedPart.getContents());
55
+ if (main.commentsIdsPart)
56
+ (_b = this.commentsIds) !== null && _b !== void 0 ? _b : (this.commentsIds = await main.commentsIdsPart.getContents());
57
+ (_c = this.extensible) !== null && _c !== void 0 ? _c : (this.extensible = await loadExtensible(main));
58
+ return this;
59
+ }
60
+ drop(people) {
61
+ var _a, _b;
62
+ const main = this.main;
63
+ const parts = [main.commentsPart, main.commentsExtendedPart, main.commentsIdsPart, main.commentsExtensiblePart, people ? main.peoplePart : undefined];
64
+ for (const part of parts) {
65
+ if (!part)
66
+ continue;
67
+ const rels = main.relationshipsPart;
68
+ const rel = rels === null || rels === void 0 ? void 0 : rels.getRel(part.partName);
69
+ if (rel)
70
+ rels.removeRelationship(rel);
71
+ if (part.sourceRelationships.length > 0)
72
+ continue;
73
+ (_a = part.relationshipsPart) === null || _a === void 0 ? void 0 : _a.removeParts();
74
+ (_b = part.package) === null || _b === void 0 ? void 0 : _b.parts.remove(part.partName);
75
+ }
76
+ main.commentsPart = main.commentsExtendedPart = main.commentsIdsPart = main.commentsExtensiblePart = undefined;
77
+ if (people)
78
+ main.peoplePart = undefined;
79
+ }
50
80
  }
51
81
  /** The document part a body's comments belong to: the body's own part, else the package's main part. */
52
82
  function mainPartOf(body) {
@@ -9,6 +9,7 @@ import { PartName } from '../../opc/PartName.mjs';
9
9
  import { DefaultXmlPart } from '../DefaultXmlPart.mjs';
10
10
  import { ThemePart } from '../dml/index.mjs';
11
11
  import { Body } from '../../model/content/Body.mjs';
12
+ import type { PartWarning } from '../../opc/mce/preprocessor.mjs';
12
13
  import type { AbstractListDefinition, ListDefinition } from '../../model/listnumbering/definitions.mjs';
13
14
  import { NumberingDefinitions } from '../../model/listnumbering/definitions.mjs';
14
15
  import { Emulator } from '../../model/listnumbering/Emulator.mjs';
@@ -44,7 +45,17 @@ export declare class MainDocumentPart extends DocumentPart<wml.Document> {
44
45
  glossaryDocumentPart: GlossaryDocumentPart | undefined;
45
46
  constructor(partName?: PartName | string, contentType?: string);
46
47
  setPartShortcut(part: Part, relationshipType: string): boolean;
47
- /** The Office JS-shaped view of `w:body`; the part must be unmarshalled (`getBody()` does that). */
48
+ /**
49
+ * The root's qualified name where it is one Word opens showing nothing of (WD REQ-320, CR-028 D11), read as an empty
50
+ * document; undefined for a `w:document`, a root read as one, or contents a caller set.
51
+ */
52
+ get unexpectedRoot(): string | undefined;
53
+ protected readRoot(doc: Document): readonly PartWarning[];
54
+ /**
55
+ * The Office JS-shaped view of `w:body`; the part must be unmarshalled (`getBody()` does that). Refused where the root is one
56
+ * Word shows nothing of, or `mc:AlternateContent` (CR-028 D10): either reads as no document here, and a write there would go
57
+ * nowhere a save keeps.
58
+ */
48
59
  get body(): Body;
49
60
  getBody(): Promise<Body>;
50
61
  private fontSelector;
@@ -5,6 +5,7 @@ import { DefaultXmlPart } from '../DefaultXmlPart.mjs';
5
5
  import { ThemePart } from '../dml/index.mjs';
6
6
  import { Body } from '../../model/content/Body.mjs';
7
7
  import { Docx4JException } from '../../opc/exceptions.mjs';
8
+ import { readMainPartRoot } from '../../opc/repair/mainRoot.mjs';
8
9
  import { NumberingDefinitions } from '../../model/listnumbering/definitions.mjs';
9
10
  import { Emulator } from '../../model/listnumbering/Emulator.mjs';
10
11
  import { DEFAULT_NUMBERING_XML } from './defaultNumbering.mjs';
@@ -23,6 +24,15 @@ async function ensurePropertyResolver(part) {
23
24
  const pkg = part.package;
24
25
  await ((_a = pkg === null || pkg === void 0 ? void 0 : pkg.getPropertyResolver) === null || _a === void 0 ? void 0 : _a.call(pkg));
25
26
  }
27
+ /**
28
+ * The glossary's placeholder entries, read privately, which `ContentControl.placeholderText` reads synchronously
29
+ * (CR-002 section 63, D8).
30
+ */
31
+ async function ensureGlossaryEntries(part) {
32
+ var _a, _b;
33
+ const pkg = part.package;
34
+ await ((_a = pkg === null || pkg === void 0 ? void 0 : pkg.glossaryEntries) === null || _a === void 0 ? void 0 : _a.load((_b = pkg.mainDocumentPart) === null || _b === void 0 ? void 0 : _b.glossaryDocumentPart));
35
+ }
26
36
  /** The Body view of a part's block-level container, one per container object. */
27
37
  function bodyOf(part, container, prefix) {
28
38
  if (!container)
@@ -114,16 +124,40 @@ export class MainDocumentPart extends DocumentPart {
114
124
  }
115
125
  return super.setPartShortcut(part, relationshipType);
116
126
  }
117
- /** The Office JS-shaped view of `w:body`; the part must be unmarshalled (`getBody()` does that). */
127
+ /**
128
+ * The root's qualified name where it is one Word opens showing nothing of (WD REQ-320, CR-028 D11), read as an empty
129
+ * document; undefined for a `w:document`, a root read as one, or contents a caller set.
130
+ */
131
+ get unexpectedRoot() {
132
+ var _a;
133
+ return (_a = this.warnings.find((w) => w.code === 'part.unexpected_root')) === null || _a === void 0 ? void 0 : _a.root;
134
+ }
135
+ readRoot(doc) {
136
+ return readMainPartRoot(doc, this.partName.name);
137
+ }
138
+ /**
139
+ * The Office JS-shaped view of `w:body`; the part must be unmarshalled (`getBody()` does that). Refused where the root is one
140
+ * Word shows nothing of, or `mc:AlternateContent` (CR-028 D10): either reads as no document here, and a write there would go
141
+ * nowhere a save keeps.
142
+ */
118
143
  get body() {
119
144
  var _a;
120
145
  const doc = this.contents;
146
+ const unexpected = this.unexpectedRoot;
147
+ if (unexpected !== undefined) {
148
+ throw new Docx4JException(`${this.partName}: its root is ${unexpected}, which Word opens showing nothing of (WD REQ-320), so it has no body to read or write`);
149
+ }
150
+ if (doc.TYPE_NAME === 'org_docx4j_mce.AlternateContent') {
151
+ // the branch isn't read as the document yet (WD REQ-317), so a body added here would go nowhere a save writes (CR-028 D10)
152
+ throw new Docx4JException(`${this.partName}: its root is mc:AlternateContent, whose branch this package doesn't yet read as the document (WD REQ-317), so it has no body to read or write`);
153
+ }
121
154
  (_a = doc.body) !== null && _a !== void 0 ? _a : (doc.body = { TYPE_NAME: 'org_docx4j_wml.Body' });
122
155
  return bodyOf(this, doc.body, 'body');
123
156
  }
124
157
  async getBody() {
125
158
  await this.getContents();
126
159
  await ensurePropertyResolver(this);
160
+ await ensureGlossaryEntries(this);
127
161
  return this.body;
128
162
  }
129
163
  /**
@@ -439,6 +473,7 @@ export class HeaderPart extends XmlPart {
439
473
  async getBody() {
440
474
  await this.getContents();
441
475
  await ensurePropertyResolver(this);
476
+ await ensureGlossaryEntries(this);
442
477
  return this.body;
443
478
  }
444
479
  }
@@ -454,6 +489,7 @@ export class FooterPart extends XmlPart {
454
489
  async getBody() {
455
490
  await this.getContents();
456
491
  await ensurePropertyResolver(this);
492
+ await ensureGlossaryEntries(this);
457
493
  return this.body;
458
494
  }
459
495
  }
@@ -14,6 +14,13 @@ export declare function parseXml(text: string): Document;
14
14
  /** Serialises a node (document or element) without an XML declaration. */
15
15
  export declare function serializeXml(node: Node): string;
16
16
  export declare function createDocument(): Document;
17
+ /**
18
+ * The XML, refused where it holds a character XML 1.0 excludes, which no escape can write (CR-002 section 60 D3): the
19
+ * part would not be well-formed, and a conforming parser must refuse it. The content API's writers drop such characters
20
+ * or write them as their elements; this catches what reached the tree some other way (an attribute, a custom XML
21
+ * node's value, a caller's own edit of the tree).
22
+ */
23
+ export declare function assertXmlCharacters(xml: string, where: string): string;
17
24
  /** XML declaration plus the serialised document element, as one string. */
18
25
  export declare function serializeXmlPart(node: Node): string;
19
26
  /** UTF-8 bytes of a string. */
package/dist/xml/dom.mjs CHANGED
@@ -59,6 +59,24 @@ export function serializeXml(node) {
59
59
  export function createDocument() {
60
60
  return DOM.createDocument();
61
61
  }
62
+ /** A character XML 1.0 excludes: a C0 control but tab, line feed and carriage return, U+FFFE, U+FFFF, an unpaired surrogate. */
63
+ const NOT_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
64
+ /**
65
+ * The XML, refused where it holds a character XML 1.0 excludes, which no escape can write (CR-002 section 60 D3): the
66
+ * part would not be well-formed, and a conforming parser must refuse it. The content API's writers drop such characters
67
+ * or write them as their elements; this catches what reached the tree some other way (an attribute, a custom XML
68
+ * node's value, a caller's own edit of the tree).
69
+ */
70
+ export function assertXmlCharacters(xml, where) {
71
+ const m = NOT_XML.exec(xml);
72
+ if (m) {
73
+ const code = m[0].charCodeAt(0).toString(16).toUpperCase().padStart(4, '0');
74
+ const before = xml.slice(Math.max(0, m.index - 40), m.index).replace(/[^\x20-\x7e]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
75
+ throw new Docx4JException(`${where} holds U+${code}, which XML 1.0 excludes, so it can't be written as well-formed XML `
76
+ + `(character ${m.index}, after "${before}"); remove it, or write it as the element it stands for (CR-002 section 60)`);
77
+ }
78
+ return xml;
79
+ }
62
80
  /** XML declaration plus the serialised document element, as one string. */
63
81
  export function serializeXmlPart(node) {
64
82
  const el = node.nodeType === 9 ? node.documentElement : node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docx4j/core-ts",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "docx4j-core for TypeScript: Open Packaging (docx/pptx/xlsx zip and flat OPC), typed parts and relationships, style, numbering and font resolution over @docx4j/generated-objects-ts. Node, browsers, Word add-ins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "license": "Apache-2.0",
85
85
  "dependencies": {
86
- "@docx4j/generated-objects-ts": "^0.4.2",
86
+ "@docx4j/generated-objects-ts": "^0.8.0",
87
87
  "fflate": "^0.8.2"
88
88
  },
89
89
  "devDependencies": {