@docx4j/core-ts 0.6.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 (119) hide show
  1. package/README.md +60 -0
  2. package/dist/model/content/Body.mjs +6 -17
  3. package/dist/model/content/Comment.d.mts +2 -2
  4. package/dist/model/content/Comment.mjs +12 -7
  5. package/dist/model/content/ContentControl.d.mts +1 -1
  6. package/dist/model/content/ContentControl.mjs +8 -4
  7. package/dist/model/content/Font.d.mts +4 -1
  8. package/dist/model/content/Font.mjs +6 -2
  9. package/dist/model/content/List.d.mts +6 -3
  10. package/dist/model/content/List.mjs +15 -8
  11. package/dist/model/content/Paragraph.d.mts +12 -2
  12. package/dist/model/content/Paragraph.mjs +31 -11
  13. package/dist/model/content/Table.mjs +26 -16
  14. package/dist/model/content/TrackedChange.d.mts +2 -2
  15. package/dist/model/content/TrackedChange.mjs +6 -3
  16. package/dist/model/content/index.d.mts +3 -1
  17. package/dist/model/content/index.mjs +4 -1
  18. package/dist/model/content/ooxml.mjs +12 -1
  19. package/dist/model/content/paraIds.d.mts +5 -0
  20. package/dist/model/content/paraIds.mjs +54 -0
  21. package/dist/model/content/search.d.mts +2 -0
  22. package/dist/model/content/search.mjs +51 -7
  23. package/dist/model/content/story.d.mts +5 -2
  24. package/dist/model/content/story.mjs +6 -3
  25. package/dist/model/content/tableRules.mjs +2 -0
  26. package/dist/model/content/tracking.d.mts +35 -6
  27. package/dist/model/content/tracking.mjs +93 -12
  28. package/dist/model/content/tree.d.mts +7 -8
  29. package/dist/model/content/tree.mjs +7 -9
  30. package/dist/model/content/wordRange.d.mts +48 -0
  31. package/dist/model/content/wordRange.mjs +97 -0
  32. package/dist/model/customxml/CustomXmlPartCollection.d.mts +22 -1
  33. package/dist/model/customxml/CustomXmlPartCollection.mjs +48 -2
  34. package/dist/model/customxml/XmlMapping.d.mts +31 -7
  35. package/dist/model/customxml/XmlMapping.mjs +53 -10
  36. package/dist/model/customxml/bindings.d.mts +12 -2
  37. package/dist/model/customxml/bindings.mjs +98 -16
  38. package/dist/model/customxml/index.d.mts +1 -0
  39. package/dist/model/customxml/index.mjs +1 -0
  40. package/dist/model/customxml/opendope.d.mts +4 -1
  41. package/dist/model/customxml/opendope.mjs +9 -6
  42. package/dist/model/customxml/pictures.mjs +5 -3
  43. package/dist/model/customxml/storeItems.d.mts +57 -0
  44. package/dist/model/customxml/storeItems.mjs +122 -0
  45. package/dist/model/customxml/xhtml.mjs +15 -20
  46. package/dist/model/dateZone.d.mts +25 -0
  47. package/dist/model/dateZone.mjs +57 -0
  48. package/dist/model/index.d.mts +1 -0
  49. package/dist/model/index.mjs +1 -0
  50. package/dist/model/properties/PropertyResolver.d.mts +14 -1
  51. package/dist/model/properties/PropertyResolver.mjs +18 -3
  52. package/dist/model/properties/index.d.mts +1 -1
  53. package/dist/model/properties/log.mjs +2 -2
  54. package/dist/model/properties/styleUtil.d.mts +3 -1
  55. package/dist/model/properties/styleUtil.mjs +20 -24
  56. package/dist/model/text/CaseMapping.d.mts +38 -0
  57. package/dist/model/text/CaseMapping.mjs +86 -0
  58. package/dist/model/text/Extractor.d.mts +66 -6
  59. package/dist/model/text/Extractor.mjs +300 -56
  60. package/dist/model/text/PlainText.mjs +7 -4
  61. package/dist/model/text/TextContext.d.mts +103 -8
  62. package/dist/model/text/TextContext.mjs +228 -53
  63. package/dist/model/text/TextOptions.d.mts +88 -5
  64. package/dist/model/text/TextOptions.mjs +158 -17
  65. package/dist/model/text/TextResult.d.mts +126 -9
  66. package/dist/model/text/TextResult.mjs +115 -12
  67. package/dist/model/text/WmlToMarkdown.mjs +32 -7
  68. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  69. package/dist/model/text/caseMapping.generated.mjs +214 -0
  70. package/dist/model/text/index.d.mts +3 -2
  71. package/dist/model/text/index.mjs +1 -0
  72. package/dist/model/wordIntegers.d.mts +29 -0
  73. package/dist/model/wordIntegers.mjs +78 -0
  74. package/dist/office-js/document.mjs +15 -1
  75. package/dist/office-js/toApiScript.mjs +8 -7
  76. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  77. package/dist/opc/DirectoryPartStore.mjs +11 -4
  78. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  79. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  80. package/dist/opc/Load.d.mts +32 -0
  81. package/dist/opc/Load.mjs +115 -5
  82. package/dist/opc/PartStore.d.mts +34 -1
  83. package/dist/opc/PartStore.mjs +71 -8
  84. package/dist/opc/Save.d.mts +17 -2
  85. package/dist/opc/Save.mjs +100 -4
  86. package/dist/opc/ZipPartStore.d.mts +4 -2
  87. package/dist/opc/ZipPartStore.mjs +12 -7
  88. package/dist/opc/exceptions.d.mts +140 -0
  89. package/dist/opc/exceptions.mjs +108 -0
  90. package/dist/opc/index.d.mts +7 -4
  91. package/dist/opc/index.mjs +5 -3
  92. package/dist/opc/mce/preprocessor.d.mts +121 -10
  93. package/dist/opc/mce/preprocessor.mjs +122 -21
  94. package/dist/opc/mce/understood.d.mts +3 -35
  95. package/dist/opc/mce/understood.mjs +27 -53
  96. package/dist/opc/repair/breaks.d.mts +23 -0
  97. package/dist/opc/repair/breaks.mjs +288 -0
  98. package/dist/opc/repair/hosts.d.mts +12 -0
  99. package/dist/opc/repair/hosts.mjs +33 -0
  100. package/dist/opc/repair/mainRoot.d.mts +19 -0
  101. package/dist/opc/repair/mainRoot.mjs +168 -0
  102. package/dist/opc/repair/unknownElements.d.mts +8 -0
  103. package/dist/opc/repair/unknownElements.mjs +180 -0
  104. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  105. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  106. package/dist/opc/skipped.d.mts +50 -0
  107. package/dist/opc/skipped.mjs +140 -0
  108. package/dist/packages/OpcPackage.d.mts +98 -5
  109. package/dist/packages/OpcPackage.mjs +122 -7
  110. package/dist/packages/WordprocessingMLPackage.mjs +3 -1
  111. package/dist/packages/index.d.mts +1 -1
  112. package/dist/parts/Part.d.mts +5 -1
  113. package/dist/parts/RelationshipsPart.d.mts +15 -0
  114. package/dist/parts/RelationshipsPart.mjs +66 -1
  115. package/dist/parts/XmlPart.d.mts +54 -5
  116. package/dist/parts/XmlPart.mjs +203 -10
  117. package/dist/parts/wml/index.d.mts +12 -1
  118. package/dist/parts/wml/index.mjs +25 -1
  119. package/package.json +2 -2
@@ -3,6 +3,10 @@ import { Part } from './Part.mjs';
3
3
  import { Docx4JException, PartUnmarshalException } from '../opc/exceptions.mjs';
4
4
  import { parseXml, serializeXml, encodeText, decodeXmlText, XML_DECLARATION, assertXmlCharacters } from '../xml/dom.mjs';
5
5
  import { bindSnapshot, boundsOf } from '../model/content/binder.mjs';
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';
6
10
  /**
7
11
  * The element or attribute a located runtime error names, out of its message: jsonix writes
8
12
  * `w:sz/@w:val (line 7): ...` when it could place the fault and `w:bogus (line 3): ...` for an
@@ -31,14 +35,88 @@ function valueKept(reading, partName) {
31
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;
32
36
  const value = reading.value;
33
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`;
34
40
  return {
35
41
  code: reading.kind, partName,
36
42
  ...(typeof node.lineNumber === 'number' ? { line: node.lineNumber } : {}),
37
43
  ...(typeof node.columnNumber === 'number' ? { column: node.columnNumber } : {}),
38
44
  element: node.nodeName, attribute, text: reading.text, value,
39
- message: `${partName}: ${node.nodeName}/@${attribute}=${JSON.stringify(reading.text)}${positionOf(node.lineNumber, node.columnNumber)} is not a value its type allows; read as ${read}, as Word 365 reads it`,
45
+ message: `${partName}: ${node.nodeName}/@${attribute}=${JSON.stringify(reading.text)}${positionOf(node.lineNumber, node.columnNumber)} is not a value its type allows; ${outcome}`,
40
46
  };
41
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 = {};
@@ -76,9 +154,13 @@ export class XmlPart extends Part {
76
154
  }
77
155
  /**
78
156
  * What the last parse of this part reported: an mc:AlternateContent whose branches' text differs
79
- * (`mce.branches_differ`, CR-010 phase 1), and each attribute value its type refuses that was read as
80
- * Word 365 reads it (`value.kept.exact`, `value.kept.invalid`: CR-015 D8). Empty until the part is
81
- * parsed, by `getContents()` or a private read, and again once its contents are set.
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.
82
164
  */
83
165
  get warnings() {
84
166
  return this.parseWarnings;
@@ -98,6 +180,7 @@ export class XmlPart extends Part {
98
180
  var _a, _b;
99
181
  if (this.element === undefined) {
100
182
  const bytes = (_a = this.bytes) !== null && _a !== void 0 ? _a : (await this.loadSourceBytes());
183
+ this.parsedBytes = this.bytes;
101
184
  if (bytes === undefined)
102
185
  throw new Docx4JException(`Part ${this.partName} has no content: set contents or load it from a container`);
103
186
  const doc = this.parse(bytes);
@@ -118,20 +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
215
  this.preprocess(doc);
129
- 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;
130
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
+ */
131
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) {
132
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);
133
257
  const warnings = (_a = this.preprocessor) === null || _a === void 0 ? void 0 : _a.call(this, doc, this);
134
- this.parseWarnings = warnings ? [...warnings] : [];
258
+ return [...rooted, ...read, ...repaired, ...(warnings !== null && warnings !== void 0 ? warnings : [])];
135
259
  }
136
260
  /** Replaces the contents (docx4j setJaxbElement / setContents). The root name defaults to the part's. */
137
261
  setContents(value, rootName) {
@@ -141,12 +265,58 @@ export class XmlPart extends Part {
141
265
  throw new Docx4JException(`Part ${this.partName}: no root element name; pass one`);
142
266
  this.element = { name: { namespaceURI: name.namespaceURI, localPart: name.localPart }, value };
143
267
  this.bytes = undefined;
268
+ this.parsedBytes = undefined;
144
269
  this.parseWarnings = [];
145
270
  }
146
271
  /** Replaces the contents with raw XML bytes; they are unmarshalled on the next getContents(). */
147
272
  setBytes(bytes) {
148
273
  this.bytes = bytes;
149
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
+ }
150
320
  }
151
321
  /** Replaces the contents with XML text (docx4j setContents from a string). */
152
322
  setXml(xml) {
@@ -212,6 +382,10 @@ export class XmlPart extends Part {
212
382
  marshal.normaliseOnSave = normalise;
213
383
  if ((options === null || options === void 0 ? void 0 : options.onNormalised) !== undefined)
214
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;
215
389
  return marshalNode(this.element, marshal);
216
390
  }
217
391
  /**
@@ -245,12 +419,31 @@ export class XmlPart extends Part {
245
419
  async unmarshal(doc) {
246
420
  var _a, _b, _c, _d, _e, _f;
247
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();
248
426
  try {
249
427
  const element = await unmarshalNode(doc, {
250
- onWordReading: (reading) => kept.push(valueKept(reading, this.partName.name)),
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
+ },
251
443
  });
252
- if (kept.length > 0)
253
- this.parseWarnings = [...this.parseWarnings, ...kept];
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];
254
447
  return element;
255
448
  }
256
449
  catch (cause) {
@@ -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';
@@ -123,10 +124,33 @@ export class MainDocumentPart extends DocumentPart {
123
124
  }
124
125
  return super.setPartShortcut(part, relationshipType);
125
126
  }
126
- /** 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
+ */
127
143
  get body() {
128
144
  var _a;
129
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
+ }
130
154
  (_a = doc.body) !== null && _a !== void 0 ? _a : (doc.body = { TYPE_NAME: 'org_docx4j_wml.Body' });
131
155
  return bodyOf(this, doc.body, 'body');
132
156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docx4j/core-ts",
3
- "version": "0.6.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.5.0",
86
+ "@docx4j/generated-objects-ts": "^0.8.0",
87
87
  "fflate": "^0.8.2"
88
88
  },
89
89
  "devDependencies": {