@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
@@ -12,7 +12,9 @@ export { layoutModeOf, textWidthTwips, gridWidthsOf, widthTypeOf, insertGridColu
12
12
  export { InlinePicture, imageInfoOf, naturalSizeEmu, addImage, addImagePart, writableWidthEmu, type ImageFormat, type ImageInfo, type InlinePictureOptions, type NewPicture, type NewImagePart } from './InlinePicture.mjs';
13
13
  export { ContentControl, collectControls, collectRunControls, W14_NS, W15_NS, type ContentControlType, type ContentControlForm, type ContentControlAppearance } from './ContentControl.mjs';
14
14
  export { contentOf, isFlatOpc, rewriteRelationshipIds, type OoxmlOptions } from './ooxml.mjs';
15
- export { type SearchOptions, searchPattern, findAll, matchesOf, expandReplacement, officeMatches, type OfficeMatch, type SearchSegment } from './search.mjs';
15
+ export { DEFAULT_ENTRIES, DEFAULT_PLACEHOLDER_ENTRY, DEFAULT_PLACEHOLDER_TEXT, defaultEntryFor, placeholderEntryOf, namePlaceholderEntry, type DefaultEntry, } from './placeholders.mjs';
16
+ export { type SearchOptions, searchPattern, findAll, matchesOf, expandReplacement, officeMatches, type OfficeMatch, type SearchSegment, wildcardMatchesAcross, paragraphMarkPieces, type MarkedSegments, type MarkedMatch, } from './search.mjs';
17
+ export { dropExcluded, officeItems } from './officeText.mjs';
16
18
  export { segmentsOf, runsOf, runItemsOf, selectedBranchOf, isAlternateContent, childrenOf, rowsOf, cellsOf, BLOCK_LEVEL_TYPES, SDT_TYPES, runOf, paragraphOf, textOfView, revisionKindOf, officeItemText, hiddenRunsOf, type TextSegment, type Located, type TextView, type TextViewOptions, type SegmentOptions, type HiddenRuns, type AlternateLocation, type RevisionKind, type RevisionHolder } from './tree.mjs';
17
19
  export { mathTextOf, mathLetter } from './mathText.mjs';
18
20
  export { textBoxesOf, type AnchoredBox } from './textBoxes.mjs';
@@ -15,7 +15,10 @@ export { layoutModeOf, textWidthTwips, gridWidthsOf, widthTypeOf, insertGridColu
15
15
  export { InlinePicture, imageInfoOf, naturalSizeEmu, addImage, addImagePart, writableWidthEmu } from './InlinePicture.mjs';
16
16
  export { ContentControl, collectControls, collectRunControls, W14_NS, W15_NS } from './ContentControl.mjs';
17
17
  export { contentOf, isFlatOpc, rewriteRelationshipIds } from './ooxml.mjs';
18
- export { searchPattern, findAll, matchesOf, expandReplacement, officeMatches } from './search.mjs';
18
+ export { DEFAULT_ENTRIES, DEFAULT_PLACEHOLDER_ENTRY, DEFAULT_PLACEHOLDER_TEXT, defaultEntryFor, placeholderEntryOf, namePlaceholderEntry, } from './placeholders.mjs';
19
+ export { searchPattern, findAll, matchesOf, expandReplacement, officeMatches, wildcardMatchesAcross, paragraphMarkPieces, } from './search.mjs';
20
+ // the writers' rule for Office JS's characters, for a caller's own text (CR-002 section 60.9; the editor's UPSTREAM-25)
21
+ export { dropExcluded, officeItems } from './officeText.mjs';
19
22
  export { segmentsOf, runsOf, runItemsOf, selectedBranchOf, isAlternateContent, childrenOf, rowsOf, cellsOf, BLOCK_LEVEL_TYPES, SDT_TYPES, runOf, paragraphOf, textOfView, revisionKindOf, officeItemText, hiddenRunsOf } from './tree.mjs';
20
23
  export { mathTextOf, mathLetter } from './mathText.mjs';
21
24
  export { textBoxesOf } from './textBoxes.mjs';
@@ -7,6 +7,8 @@
7
7
  // `insertXml` takes; the two share this module.
8
8
  import { wml as parseFragment, walkAll } from '@docx4j/generated-objects-ts/builders/wml';
9
9
  import { Docx4JException } from '../../opc/exceptions.mjs';
10
+ import { repairBreaksOutsideRuns } from '../../opc/repair/breaks.mjs';
11
+ import { unknownElementsIn } from '../../opc/repair/unknownElements.mjs';
10
12
  import { PartName } from '../../opc/PartName.mjs';
11
13
  import { ContentTypes } from '../../opc/ContentTypes.mjs';
12
14
  import { BinaryPart, ImagePart } from '../../parts/BinaryPart.mjs';
@@ -25,6 +27,12 @@ export function isFlatOpc(ooxml) {
25
27
  const REL_ATTRIBUTES = ['embed', 'link', 'id', 'href', 'pict', 'dm', 'lo', 'qs', 'cs', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'];
26
28
  /** The relationships namespace, for the same attributes on DOM nodes kept as `any` content. */
27
29
  const R_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
30
+ /** CR-026 D12: a caller's own fragment holding an element Word doesn't know is refused, where a file arriving is read as Word reads it. */
31
+ function refuseUnknown(names) {
32
+ if (names.length === 0)
33
+ return;
34
+ throw new Docx4JException(`insertOoxml refused: the content holds ${names.length === 1 ? 'an element' : 'elements'} Word doesn't know (${[...new Set(names)].join(', ')})`);
35
+ }
28
36
  /**
29
37
  * The block-level content of `ooxml`, ready to insert: a flat OPC package's body content with
30
38
  * its parts copied and its references rewritten, or a fragment's elements.
@@ -32,7 +40,9 @@ const R_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationship
32
40
  export async function contentOf(ooxml, options = {}) {
33
41
  var _a, _b;
34
42
  if (!isFlatOpc(ooxml)) {
35
- return parseFragment(ooxml, { wrapper: 'body', preprocess: options.preprocess });
43
+ // a w:br outside a w:r read as Word reads it first, as on load (CR-021 D1): a created package has no hook
44
+ const preprocess = options.preprocess;
45
+ return parseFragment(ooxml, { wrapper: 'body', preprocess: (doc) => { refuseUnknown(unknownElementsIn(doc).map((e) => e.nodeName)); repairBreaksOutsideRuns(doc); preprocess === null || preprocess === void 0 ? void 0 : preprocess(doc); } });
36
46
  }
37
47
  const target = options.target;
38
48
  if (!target)
@@ -43,6 +53,7 @@ export async function contentOf(ooxml, options = {}) {
43
53
  const source = await WordprocessingMLPackage.load(ooxml, options.preprocess ? { preprocessor: options.preprocess } : {});
44
54
  const main = source.getMainDocumentPart();
45
55
  const document = await main.getContents();
56
+ refuseUnknown(main.warnings.flatMap((w) => (w.code === 'part.skipped' && w.kind === 'element' ? [w.name] : [])));
46
57
  const content = ((_b = (_a = document.body) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : []);
47
58
  if (content.length === 0)
48
59
  return [];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Gives every paragraph and row in `added`, at any depth, a w14:paraId, where the story `within` stands in uses them (CR-002
3
+ * section 66). A document that uses none stays so. `added` has to be in the tree already, or `within` be what it goes in.
4
+ */
5
+ export declare function assignParaIds(within: object, added: Iterable<unknown>): void;
@@ -0,0 +1,54 @@
1
+ // w14:paraId on what the content API adds. In some files, one paragraph or row without an id makes Word 365 renumber the whole
2
+ // part at the next open (check 79: 79d-table, 79f-one-missing, 79f-row-ids; check 80), which breaks a `w14:` address held across
3
+ // that open. In others it fills the gap, and what separates them isn't known (CR-002 section 66). With an id on everything nothing
4
+ // is left to chance, so a new paragraph or row takes one wherever the story already uses them.
5
+ import { walk } from '@docx4j/generated-objects-ts/builders/wml';
6
+ const P = 'org_docx4j_wml.P';
7
+ const TR = 'org_docx4j_wml.Tr';
8
+ const holdsAnId = (value) => {
9
+ const type = value.TYPE_NAME;
10
+ return type === P || type === TR;
11
+ };
12
+ /** The top of the tree `within` stands in, up `PARENT`: the whole story, so a cell's own body sees the document's ids. */
13
+ function storyOf(within) {
14
+ let top = within;
15
+ for (let guard = 0; top.PARENT !== undefined && guard < 4096; guard++)
16
+ top = top.PARENT;
17
+ return top;
18
+ }
19
+ /** The ids the story uses. A paragraph's own content isn't entered: what it holds matters only to a clash, one in two billion. */
20
+ function idsIn(story) {
21
+ const used = new Set();
22
+ walk(story, (value) => {
23
+ if (!holdsAnId(value))
24
+ return undefined;
25
+ if (value.paraId)
26
+ used.add(value.paraId.toUpperCase());
27
+ return value.TYPE_NAME === P ? false : undefined;
28
+ });
29
+ return used;
30
+ }
31
+ function freeId(used) {
32
+ let id;
33
+ do {
34
+ id = Math.floor(Math.random() * 0x7fffffff).toString(16).toUpperCase().padStart(8, '0');
35
+ } while (used.has(id));
36
+ used.add(id);
37
+ return id;
38
+ }
39
+ /**
40
+ * Gives every paragraph and row in `added`, at any depth, a w14:paraId, where the story `within` stands in uses them (CR-002
41
+ * section 66). A document that uses none stays so. `added` has to be in the tree already, or `within` be what it goes in.
42
+ */
43
+ export function assignParaIds(within, added) {
44
+ const used = idsIn(storyOf(within));
45
+ if (used.size === 0)
46
+ return;
47
+ for (const block of added) {
48
+ walk(block, (value) => {
49
+ if (holdsAnId(value) && !value.paraId)
50
+ value.paraId = freeId(used);
51
+ return undefined;
52
+ });
53
+ }
54
+ }
@@ -34,6 +34,8 @@ export declare function findAll(text: string, pattern: RegExp): [number, number]
34
34
  * section 32 item 1, found by the editor's agreement suite). The clusters are `Intl.Segmenter`'s,
35
35
  * computed only for a text with a code unit at or above U+0300, below which every code unit is a
36
36
  * cluster of its own, so Latin text pays nothing; a runtime without `Intl.Segmenter` keeps every match.
37
+ * Only the stretches between control characters that a match's ends fall in are segmented, each once
38
+ * (`graphemeBoundaries`), so a search across a long body's paragraphs costs what each paragraph's does.
37
39
  */
38
40
  export declare function matchesOf(text: string, pattern: RegExp): RegExpExecArray[];
39
41
  /**
@@ -110,6 +110,8 @@ export function findAll(text, pattern) {
110
110
  * section 32 item 1, found by the editor's agreement suite). The clusters are `Intl.Segmenter`'s,
111
111
  * computed only for a text with a code unit at or above U+0300, below which every code unit is a
112
112
  * cluster of its own, so Latin text pays nothing; a runtime without `Intl.Segmenter` keeps every match.
113
+ * Only the stretches between control characters that a match's ends fall in are segmented, each once
114
+ * (`graphemeBoundaries`), so a search across a long body's paragraphs costs what each paragraph's does.
113
115
  */
114
116
  export function matchesOf(text, pattern) {
115
117
  const out = [];
@@ -121,20 +123,62 @@ export function matchesOf(text, pattern) {
121
123
  }
122
124
  out.push(m);
123
125
  }
124
- const boundaries = out.length > 0 ? graphemeBoundaries(text) : undefined;
125
- return boundaries === undefined ? out : out.filter((m) => boundaries.has(m.index) && boundaries.has(m.index + m[0].length));
126
+ const isBoundary = out.length > 0 ? graphemeBoundaries(text) : undefined;
127
+ return isBoundary === undefined ? out : out.filter((m) => isBoundary(m.index) && isBoundary(m.index + m[0].length));
126
128
  }
127
- /** The code-unit offsets where a grapheme cluster starts, and the text's end; undefined where every unit is its own cluster. */
129
+ /** A C0 or C1 control character: a grapheme cluster of its own, always a break before and after it but between CR and LF (UAX #29, GB3 to GB5). */
130
+ const isControl = (code) => code < 0x20 || (code >= 0x7F && code < 0xA0);
131
+ /**
132
+ * Whether a grapheme cluster starts at a code-unit offset (the text's end counts); undefined where every unit is its own
133
+ * cluster. A control character always has a break before and after it, and no rule looks back past one, so each stretch
134
+ * between controls (a paragraph's, in a search across marks) is segmented on its own, as it would be within the whole
135
+ * text, and only the stretches asked about, each once. V8's segmenter is quadratic in its input's length: the whole of a
136
+ * 510,000-character body didn't come back in 100 s (the editor's finding, 2026-10-26; CR-002 section 64).
137
+ */
128
138
  function graphemeBoundaries(text) {
129
139
  if (!/[\u0300-\uffff]/.test(text))
130
140
  return undefined;
131
141
  const Segmenter = Intl.Segmenter;
132
142
  if (Segmenter === undefined)
133
143
  return undefined;
134
- const boundaries = new Set([text.length]);
135
- for (const segment of new Segmenter(undefined, { granularity: 'grapheme' }).segment(text))
136
- boundaries.add(segment.index);
137
- return boundaries;
144
+ const segmenter = new Segmenter(undefined, { granularity: 'grapheme' });
145
+ const controls = [];
146
+ for (let i = 0; i < text.length; i++)
147
+ if (isControl(text.charCodeAt(i)))
148
+ controls.push(i);
149
+ // each stretch's cluster starts, by the stretch's start; undefined where every unit of it is its own cluster
150
+ const stretches = new Map();
151
+ return (at) => {
152
+ if (at <= 0 || at >= text.length)
153
+ return true;
154
+ const before = text.charCodeAt(at - 1);
155
+ const after = text.charCodeAt(at);
156
+ if (isControl(before) || isControl(after))
157
+ return before !== 0x0D || after !== 0x0A;
158
+ // the stretch holding `at`: from just after the last control before it to the first control after it
159
+ let lo = 0;
160
+ let hi = controls.length;
161
+ while (lo < hi) {
162
+ const mid = (lo + hi) >> 1;
163
+ if (controls[mid] < at)
164
+ lo = mid + 1;
165
+ else
166
+ hi = mid;
167
+ }
168
+ const start = lo > 0 ? controls[lo - 1] + 1 : 0;
169
+ if (!stretches.has(start)) {
170
+ const stretch = text.slice(start, lo < controls.length ? controls[lo] : text.length);
171
+ let starts;
172
+ if (/[\u0300-\uffff]/.test(stretch)) {
173
+ starts = new Set();
174
+ for (const segment of segmenter.segment(stretch))
175
+ starts.add(start + segment.index);
176
+ }
177
+ stretches.set(start, starts);
178
+ }
179
+ const starts = stretches.get(start);
180
+ return starts === undefined || starts.has(at);
181
+ };
138
182
  }
139
183
  /**
140
184
  * A replacement with its group references expanded against one match: `$1` to `$9`, `$&` and `$$`
@@ -23,10 +23,13 @@ export interface StoryLayout {
23
23
  }
24
24
  /**
25
25
  * Whether a paragraph's mark is written in Word's default view: not where it is tracked as deleted, which reads as
26
- * nothing there, the paragraphs staying apart (check 58 cases 6 to 8, CR-012 section 19). A cell's last paragraph is
27
- * ended by the cell's `\t` whatever its mark (cases 9 and 10).
26
+ * nothing there, the paragraphs staying apart (check 58 cases 6 to 8, CR-012 section 19), nor where it is tracked as
27
+ * moved from, as the paragraph's runs are (CR-002 section 64). A cell's last paragraph is ended by the cell's `\t`
28
+ * whatever its mark (cases 9 and 10).
28
29
  */
29
30
  export declare const shownInDefaultView: (p: wml.P) => boolean;
31
+ /** Whether a paragraph's mark is written in the original view, the marks Reject All keeps: not one tracked as inserted, nor as moved to. */
32
+ export declare const shownInOriginalView: (p: wml.P) => boolean;
30
33
  /**
31
34
  * Lays out the blocks `items`, each paragraph's text read by `textOf` (its `Paragraph.text`, or another view's), its
32
35
  * mark written where `markShown` says (Word's default view's rule unless another view's is given).
@@ -2,10 +2,13 @@ import { Docx4JException } from '../../opc/exceptions.mjs';
2
2
  import { typeNameOf, childrenOf, rowsOf, cellsOf, selectedBranchOf, BRANCH_TYPES } from './tree.mjs';
3
3
  /**
4
4
  * Whether a paragraph's mark is written in Word's default view: not where it is tracked as deleted, which reads as
5
- * nothing there, the paragraphs staying apart (check 58 cases 6 to 8, CR-012 section 19). A cell's last paragraph is
6
- * ended by the cell's `\t` whatever its mark (cases 9 and 10).
5
+ * nothing there, the paragraphs staying apart (check 58 cases 6 to 8, CR-012 section 19), nor where it is tracked as
6
+ * moved from, as the paragraph's runs are (CR-002 section 64). A cell's last paragraph is ended by the cell's `\t`
7
+ * whatever its mark (cases 9 and 10).
7
8
  */
8
- export const shownInDefaultView = (p) => { var _a, _b; return ((_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.del) === undefined; };
9
+ export const shownInDefaultView = (p) => { var _a, _b, _c, _d; return ((_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.del) === undefined && ((_d = (_c = p.pPr) === null || _c === void 0 ? void 0 : _c.rPr) === null || _d === void 0 ? void 0 : _d.moveFrom) === undefined; };
10
+ /** Whether a paragraph's mark is written in the original view, the marks Reject All keeps: not one tracked as inserted, nor as moved to. */
11
+ export const shownInOriginalView = (p) => { var _a, _b, _c, _d; return ((_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.ins) === undefined && ((_d = (_c = p.pPr) === null || _c === void 0 ? void 0 : _c.rPr) === null || _d === void 0 ? void 0 : _d.moveTo) === undefined; };
9
12
  /**
10
13
  * Lays out the blocks `items`, each paragraph's text read by `textOf` (its `Paragraph.text`, or another view's), its
11
14
  * mark written where `markShown` says (Word's default view's rule unless another view's is given).
@@ -1,4 +1,5 @@
1
1
  import { typeNameOf, childrenOf, linkParents, selectedBranchOf, paragraphOf, rowsOf, cellsOf, ALTERNATE_CONTENT, BRANCH_TYPES } from './tree.mjs';
2
+ import { assignParaIds } from './paraIds.mjs';
2
3
  const P = 'org_docx4j_wml.P';
3
4
  const TBL = 'org_docx4j_wml.Tbl';
4
5
  const TC = 'org_docx4j_wml.Tc';
@@ -51,6 +52,7 @@ export function endWithParagraph(tc) {
51
52
  const p = paragraphOf([]);
52
53
  content.push(p);
53
54
  linkParents(p, tc);
55
+ assignParaIds(tc, [p]);
54
56
  return p.value;
55
57
  }
56
58
  /** The cell whose content holds this container (a `w:tc`, a control's content, a branch, ...): up `PARENT` past the wrappers. */
@@ -26,6 +26,11 @@ export interface TrackingHost {
26
26
  readonly author: Author;
27
27
  /** A fixed date for new revisions; `undefined` means now, read once per revision. */
28
28
  readonly trackedChangeDate?: Date | undefined;
29
+ /**
30
+ * The zone a stamp's `w:date` wall-clock fields are written in, and a `w:date` without `dateUtc` read in: the
31
+ * package's `dateTimeZone`, an IANA id; undefined is the runtime's zone (CR-018, the portfolio's date rule).
32
+ */
33
+ readonly dateTimeZone?: string | undefined;
29
34
  /** The trees to scan for the highest annotation id in use: the parts already unmarshalled. */
30
35
  markupRoots(): object[];
31
36
  /**
@@ -73,19 +78,43 @@ export declare function dateOf(cal: wml.XmlCalendar | undefined): Date | undefin
73
78
  /**
74
79
  * A revision's `w:date` as Word writes it: the local wall-clock time, with a `Z` that does not mean
75
80
  * UTC (CR-002 section 29, check 23: Office JS read `01:00:00Z` as 15:00 UTC the day before, at
76
- * UTC+10; Word's own files carry the true UTC beside it in `w16du:dateUtc`). "Local" is this
77
- * process's time zone, as it is the add-in's for Office JS.
81
+ * UTC+10; Word's own files carry the true UTC beside it in `w16du:dateUtc`). "Local" is `zone`, the
82
+ * package's `dateTimeZone` (CR-018), else this process's time zone, which is the add-in's for Office JS.
78
83
  */
79
- export declare function wordCalendarOf(date: Date): wml.XmlCalendar;
84
+ export declare function wordCalendarOf(date: Date, zone?: string): wml.XmlCalendar;
80
85
  /**
81
86
  * A revision's date as Word means it: `w16du:dateUtc` when there is one, which is UTC; else
82
- * `w:date` read as local wall-clock time, its `Z` notwithstanding, as Office JS reads it (check 23).
87
+ * `w:date` read as local wall-clock time, its `Z` notwithstanding, as Office JS reads it (check 23),
88
+ * in `zone` (the package's `dateTimeZone`, CR-018), else the runtime's zone.
83
89
  * A `w:date` with a non-zero offset, which Word does not write, is read as it says.
84
90
  */
85
91
  export declare function revisionDateOf(value: {
86
92
  date?: wml.XmlCalendar;
87
93
  dateUtc?: wml.XmlCalendar;
88
- }): Date | undefined;
94
+ }, zone?: string): Date | undefined;
95
+ /** A stamp's fields as written: an `XmlCalendar`, or its lexical form as a reader may hold it. */
96
+ type Stamp = wml.XmlCalendar | string | null | undefined;
97
+ /**
98
+ * The author's local time of a revision's or comment's `w:date`: its date and time as stored, in ISO 8601's extended form
99
+ * with no designator (`2026-10-31T11:00:00`), any designator not read, a missing time 00:00:00, fractions of a second
100
+ * dropped; undefined for no date. Word writes the author's wall clock labelled `Z` (CR-002 section 29), and shows it as it
101
+ * is. docx4j `RevisionDates.localTime`.
102
+ */
103
+ export declare function authorLocalTime(date: Stamp): string | undefined;
104
+ /**
105
+ * The author's UTC offset in minutes: `w:date` less `dateUtc` (`w16du:dateUtc`, or a comment's `w16cex:dateUtc`), where both
106
+ * are present and differ by a whole number of quarter hours from -12:00 to +14:00, as a clock's offset does; else
107
+ * undefined, so a pair a producer wrote inconsistently gives no offset rather than a wrong one. `dateUtc` without a zone is
108
+ * UTC, and one with an offset is moved by it. docx4j `RevisionDates.offset`.
109
+ */
110
+ export declare function authorOffsetMinutes(date: Stamp, dateUtc: Stamp): number | undefined;
111
+ /**
112
+ * A revision's or comment's stamp as WD REQ-702 writes it: the author's local time (`authorLocalTime`), with the author's
113
+ * offset after it where it is known (`2026-10-31T11:00:00+11:00`; `+00:00` for UTC, never `Z`); `dateUtc` with `Z` where
114
+ * there is no `w:date`; undefined where there is neither. The `Z` Word stores on `w:date` is never written, and nothing is
115
+ * converted to another zone. docx4j `RevisionDates.text`.
116
+ */
117
+ export declare function revisionDateText(date: Stamp, dateUtc: Stamp): string | undefined;
89
118
  /** Properties compared as values: keys sorted, `PARENT`, `TYPE_NAME` and absent values left out. */
90
119
  export declare function canonical(value: unknown): string;
91
120
  /** The part a tracker writes into, as far as the tracker needs it: its tree, once unmarshalled. */
@@ -143,7 +172,7 @@ export declare class ChangeTracker {
143
172
  nextId(): number;
144
173
  /**
145
174
  * The `w:id`, `w:author` and `w:date` a new revision carries, `w:date` in Word's local wall-clock
146
- * convention (`wordCalendarOf`), and `w16du:dateUtc`, the true UTC, as Word writes them both. The
175
+ * convention (`wordCalendarOf`, in the host's `dateTimeZone`), and `w16du:dateUtc`, the true UTC, as Word writes them both. The
147
176
  * part's root is made to list `w16du` in `mc:Ignorable` first (`declareDateUtc`): a consumer that
148
177
  * does not know `w16du` must reject it unless it is declared ignorable. A tracker bound to no part -
149
178
  * the package's own - writes `w:date` alone (CR-002 section 29).
@@ -4,6 +4,7 @@ import { createCTTrackChange, createCTRPrChange, createCTRPrChangeRPr, createCTP
4
4
  import { rPrToElements, rPrFromElements } from '@docx4j/generated-objects-ts/builders/wml';
5
5
  import { Docx4JException } from '../../opc/exceptions.mjs';
6
6
  import { W_NS, linkParents, walk, typeNameOf, revisionKindOf, childrenOf, rowsOf, cellsOf, isAlternateContent, selectedBranchOf, branchesOf, ALTERNATE_CONTENT } from './tree.mjs';
7
+ import { wallClockIn, instantOf } from '../dateZone.mjs';
7
8
  /**
8
9
  * `w:id` on these is the one annotation id space (ECMA-376 17.13.5.4): revisions, bookmarks,
9
10
  * comment range marks and permissions share it. `w:comment/@w:id` (docx4j `Comments.Comment`,
@@ -69,21 +70,20 @@ export function dateOf(cal) {
69
70
  /**
70
71
  * A revision's `w:date` as Word writes it: the local wall-clock time, with a `Z` that does not mean
71
72
  * UTC (CR-002 section 29, check 23: Office JS read `01:00:00Z` as 15:00 UTC the day before, at
72
- * UTC+10; Word's own files carry the true UTC beside it in `w16du:dateUtc`). "Local" is this
73
- * process's time zone, as it is the add-in's for Office JS.
73
+ * UTC+10; Word's own files carry the true UTC beside it in `w16du:dateUtc`). "Local" is `zone`, the
74
+ * package's `dateTimeZone` (CR-018), else this process's time zone, which is the add-in's for Office JS.
74
75
  */
75
- export function wordCalendarOf(date) {
76
- return {
77
- year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate(),
78
- hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), timezone: 0,
79
- };
76
+ export function wordCalendarOf(date, zone) {
77
+ const clock = wallClockIn(date, zone);
78
+ return { year: clock.year, month: clock.month, day: clock.day, hour: clock.hour, minute: clock.minute, second: clock.second, timezone: 0 };
80
79
  }
81
80
  /**
82
81
  * A revision's date as Word means it: `w16du:dateUtc` when there is one, which is UTC; else
83
- * `w:date` read as local wall-clock time, its `Z` notwithstanding, as Office JS reads it (check 23).
82
+ * `w:date` read as local wall-clock time, its `Z` notwithstanding, as Office JS reads it (check 23),
83
+ * in `zone` (the package's `dateTimeZone`, CR-018), else the runtime's zone.
84
84
  * A `w:date` with a non-zero offset, which Word does not write, is read as it says.
85
85
  */
86
- export function revisionDateOf(value) {
86
+ export function revisionDateOf(value, zone) {
87
87
  var _a, _b, _c, _d, _e, _f;
88
88
  if (((_a = value.dateUtc) === null || _a === void 0 ? void 0 : _a.year) !== undefined)
89
89
  return dateOf(value.dateUtc);
@@ -92,7 +92,88 @@ export function revisionDateOf(value) {
92
92
  return undefined;
93
93
  if (cal.timezone !== undefined && cal.timezone !== 0)
94
94
  return dateOf(cal);
95
- return new Date(cal.year, ((_b = cal.month) !== null && _b !== void 0 ? _b : 1) - 1, (_c = cal.day) !== null && _c !== void 0 ? _c : 1, (_d = cal.hour) !== null && _d !== void 0 ? _d : 0, (_e = cal.minute) !== null && _e !== void 0 ? _e : 0, (_f = cal.second) !== null && _f !== void 0 ? _f : 0);
95
+ return instantOf({ year: cal.year, month: (_b = cal.month) !== null && _b !== void 0 ? _b : 1, day: (_c = cal.day) !== null && _c !== void 0 ? _c : 1, hour: (_d = cal.hour) !== null && _d !== void 0 ? _d : 0, minute: (_e = cal.minute) !== null && _e !== void 0 ? _e : 0, second: (_f = cal.second) !== null && _f !== void 0 ? _f : 0 }, zone);
96
+ }
97
+ const STAMP = /^(-?\d{4,})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?)?(Z|[+-]\d{2}:\d{2})?$/;
98
+ /** A stamp's date and time as written, its fields read as a UTC clock's (milliseconds), and its zone in minutes; undefined for no year. */
99
+ function clockOf(stamp) {
100
+ var _a, _b, _c, _d, _e, _f, _g, _h;
101
+ if (stamp === undefined || stamp === null)
102
+ return undefined;
103
+ if (typeof stamp === 'string') {
104
+ const m = STAMP.exec(stamp.trim());
105
+ if (!m)
106
+ return undefined;
107
+ const zone = m[7];
108
+ const timezone = zone === undefined ? undefined : zone === 'Z' ? 0
109
+ : (zone.startsWith('-') ? -1 : 1) * (Number(zone.slice(1, 3)) * 60 + Number(zone.slice(4, 6)));
110
+ return { ms: utcClock(Number(m[1]), Number(m[2]), Number(m[3]), Number((_a = m[4]) !== null && _a !== void 0 ? _a : 0), Number((_b = m[5]) !== null && _b !== void 0 ? _b : 0), Number((_c = m[6]) !== null && _c !== void 0 ? _c : 0)), timezone };
111
+ }
112
+ if (stamp.year === undefined)
113
+ return undefined;
114
+ return { ms: utcClock(stamp.year, (_d = stamp.month) !== null && _d !== void 0 ? _d : 1, (_e = stamp.day) !== null && _e !== void 0 ? _e : 1, (_f = stamp.hour) !== null && _f !== void 0 ? _f : 0, (_g = stamp.minute) !== null && _g !== void 0 ? _g : 0, (_h = stamp.second) !== null && _h !== void 0 ? _h : 0), timezone: stamp.timezone };
115
+ }
116
+ function utcClock(year, month, day, hour, minute, second) {
117
+ const d = new Date(Date.UTC(2000, month - 1, day, hour, minute, second));
118
+ d.setUTCFullYear(year);
119
+ return d.getTime();
120
+ }
121
+ /** The offsets clocks keep, in seconds: UTC-12:00 to UTC+14:00. */
122
+ const MIN_OFFSET = -12 * 3600;
123
+ const MAX_OFFSET = 14 * 3600;
124
+ /**
125
+ * The author's local time of a revision's or comment's `w:date`: its date and time as stored, in ISO 8601's extended form
126
+ * with no designator (`2026-10-31T11:00:00`), any designator not read, a missing time 00:00:00, fractions of a second
127
+ * dropped; undefined for no date. Word writes the author's wall clock labelled `Z` (CR-002 section 29), and shows it as it
128
+ * is. docx4j `RevisionDates.localTime`.
129
+ */
130
+ export function authorLocalTime(date) {
131
+ const clock = clockOf(date);
132
+ return clock === undefined ? undefined : clockText(clock.ms);
133
+ }
134
+ /**
135
+ * The author's UTC offset in minutes: `w:date` less `dateUtc` (`w16du:dateUtc`, or a comment's `w16cex:dateUtc`), where both
136
+ * are present and differ by a whole number of quarter hours from -12:00 to +14:00, as a clock's offset does; else
137
+ * undefined, so a pair a producer wrote inconsistently gives no offset rather than a wrong one. `dateUtc` without a zone is
138
+ * UTC, and one with an offset is moved by it. docx4j `RevisionDates.offset`.
139
+ */
140
+ export function authorOffsetMinutes(date, dateUtc) {
141
+ var _a;
142
+ const local = clockOf(date);
143
+ const utc = clockOf(dateUtc);
144
+ if (local === undefined || utc === undefined)
145
+ return undefined;
146
+ const seconds = Math.round((local.ms - (utc.ms - ((_a = utc.timezone) !== null && _a !== void 0 ? _a : 0) * 60000)) / 1000);
147
+ if (seconds % 900 !== 0 || seconds < MIN_OFFSET || seconds > MAX_OFFSET)
148
+ return undefined;
149
+ return seconds / 60;
150
+ }
151
+ /**
152
+ * A revision's or comment's stamp as WD REQ-702 writes it: the author's local time (`authorLocalTime`), with the author's
153
+ * offset after it where it is known (`2026-10-31T11:00:00+11:00`; `+00:00` for UTC, never `Z`); `dateUtc` with `Z` where
154
+ * there is no `w:date`; undefined where there is neither. The `Z` Word stores on `w:date` is never written, and nothing is
155
+ * converted to another zone. docx4j `RevisionDates.text`.
156
+ */
157
+ export function revisionDateText(date, dateUtc) {
158
+ var _a;
159
+ const local = clockOf(date);
160
+ if (local === undefined) {
161
+ const utc = clockOf(dateUtc);
162
+ return utc === undefined ? undefined : `${clockText(utc.ms - ((_a = utc.timezone) !== null && _a !== void 0 ? _a : 0) * 60000)}Z`;
163
+ }
164
+ const offset = authorOffsetMinutes(date, dateUtc);
165
+ if (offset === undefined)
166
+ return clockText(local.ms);
167
+ const abs = Math.abs(offset);
168
+ return `${clockText(local.ms)}${offset < 0 ? '-' : '+'}${String(Math.floor(abs / 60)).padStart(2, '0')}:${String(abs % 60).padStart(2, '0')}`;
169
+ }
170
+ /** A UTC clock's milliseconds as `uuuu-MM-ddTHH:mm:ss`. */
171
+ function clockText(ms) {
172
+ const d = new Date(ms);
173
+ const p2 = (n) => String(n).padStart(2, '0');
174
+ const year = d.getUTCFullYear();
175
+ const y = year < 0 ? `-${String(-year).padStart(4, '0')}` : String(year).padStart(4, '0');
176
+ return `${y}-${p2(d.getUTCMonth() + 1)}-${p2(d.getUTCDate())}T${p2(d.getUTCHours())}:${p2(d.getUTCMinutes())}:${p2(d.getUTCSeconds())}`;
96
177
  }
97
178
  /** Properties compared as values: keys sorted, `PARENT`, `TYPE_NAME` and absent values left out. */
98
179
  export function canonical(value) {
@@ -194,7 +275,7 @@ export class ChangeTracker {
194
275
  }
195
276
  /**
196
277
  * The `w:id`, `w:author` and `w:date` a new revision carries, `w:date` in Word's local wall-clock
197
- * convention (`wordCalendarOf`), and `w16du:dateUtc`, the true UTC, as Word writes them both. The
278
+ * convention (`wordCalendarOf`, in the host's `dateTimeZone`), and `w16du:dateUtc`, the true UTC, as Word writes them both. The
198
279
  * part's root is made to list `w16du` in `mc:Ignorable` first (`declareDateUtc`): a consumer that
199
280
  * does not know `w16du` must reject it unless it is declared ignorable. A tracker bound to no part -
200
281
  * the package's own - writes `w:date` alone (CR-002 section 29).
@@ -202,7 +283,7 @@ export class ChangeTracker {
202
283
  markup() {
203
284
  var _a;
204
285
  const when = (_a = this.host.trackedChangeDate) !== null && _a !== void 0 ? _a : nowToTheMinute();
205
- const markup = { id: this.nextId(), author: this.author, date: wordCalendarOf(when) };
286
+ const markup = { id: this.nextId(), author: this.author, date: wordCalendarOf(when, this.host.dateTimeZone) };
206
287
  if (declareDateUtc(this.part))
207
288
  markup.dateUtc = calendarOf(when);
208
289
  return markup;
@@ -1,6 +1,6 @@
1
1
  import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
2
  import type * as mce from '@docx4j/generated-objects-ts/modules/org_docx4j_mce';
3
- import { isElement, runItemsOf, type Element } from '@docx4j/generated-objects-ts/builders/wml';
3
+ import { isElement, runItemsOf, type McOptions, type Element } from '@docx4j/generated-objects-ts/builders/wml';
4
4
  export { isElement, typeNameOf, walk, find, linkParents, textOf, W_NS, type Element } from '@docx4j/generated-objects-ts/builders/wml';
5
5
  /** The two views of a tracked document: as it will read once accepted, or as it read before the changes. */
6
6
  export type TextView = 'accepted' | 'original';
@@ -56,14 +56,13 @@ export declare const OBJECT_ITEMS: ReadonlySet<string>;
56
56
  export declare const ALTERNATE_CONTENT = "org_docx4j_mce.AlternateContent";
57
57
  /**
58
58
  * The branch of a kept `mc:AlternateContent` the content API reads (docx4j's `McMode.READ`, CR-010
59
- * phase 3): the objects package's `mcBranchOf` over `READ_UNDERSTOOD_PREFIXES`, the prefixes load
60
- * understands and the four Word 365 understands that load leaves alone (CR-010 section 15). So a reader
61
- * takes the branch the preprocessor would have resolved the element to, unless a Choice requires one of
62
- * those four: then it takes that Choice, as Word does, where a resolved load takes the next one or the
63
- * Fallback. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined for an element with
64
- * no branches.
59
+ * phase 3): the objects package's `mcBranchOf`, the first `mc:Choice` whose `Requires` names only namespaces in the
60
+ * understood set, by URI, else the `mc:Fallback`, else nothing (WD REQ-302 to REQ-305, CR-029). So a reader takes the
61
+ * branch the preprocessor resolves the element to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined
62
+ * where nothing is selected, as for an element with no branches. `options` are `mcBranchOf`'s: the text model passes
63
+ * `onNothingSelected` for `mce.nothing_selected`.
65
64
  */
66
- export declare function selectedBranchOf(value: mce.AlternateContent | undefined): Element[] | undefined;
65
+ export declare function selectedBranchOf(value: mce.AlternateContent | undefined, options?: McOptions): Element[] | undefined;
67
66
  /** True for a kept `mc:AlternateContent`: its element pair, or its value. */
68
67
  export declare function isAlternateContent(value: unknown): boolean;
69
68
  /**
@@ -1,5 +1,4 @@
1
1
  import { isElement, typeNameOf, runItemsOf, mcBranchOf, sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
2
- import { READ_UNDERSTOOD_PREFIXES } from '../../opc/mce/understood.mjs';
3
2
  import { isMath, mathTextOf } from './mathText.mjs';
4
3
  import { NotSupportedError } from '../../office-js/errors.mjs';
5
4
  export { isElement, typeNameOf, walk, find, linkParents, textOf, W_NS } from '@docx4j/generated-objects-ts/builders/wml';
@@ -14,15 +13,14 @@ export const OBJECT_ITEMS = new Set(['drawing', 'pict', 'object']);
14
13
  export const ALTERNATE_CONTENT = 'org_docx4j_mce.AlternateContent';
15
14
  /**
16
15
  * The branch of a kept `mc:AlternateContent` the content API reads (docx4j's `McMode.READ`, CR-010
17
- * phase 3): the objects package's `mcBranchOf` over `READ_UNDERSTOOD_PREFIXES`, the prefixes load
18
- * understands and the four Word 365 understands that load leaves alone (CR-010 section 15). So a reader
19
- * takes the branch the preprocessor would have resolved the element to, unless a Choice requires one of
20
- * those four: then it takes that Choice, as Word does, where a resolved load takes the next one or the
21
- * Fallback. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined for an element with
22
- * no branches.
16
+ * phase 3): the objects package's `mcBranchOf`, the first `mc:Choice` whose `Requires` names only namespaces in the
17
+ * understood set, by URI, else the `mc:Fallback`, else nothing (WD REQ-302 to REQ-305, CR-029). So a reader takes the
18
+ * branch the preprocessor resolves the element to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined
19
+ * where nothing is selected, as for an element with no branches. `options` are `mcBranchOf`'s: the text model passes
20
+ * `onNothingSelected` for `mce.nothing_selected`.
23
21
  */
24
- export function selectedBranchOf(value) {
25
- return mcBranchOf(value, { understood: READ_UNDERSTOOD_PREFIXES });
22
+ export function selectedBranchOf(value, options) {
23
+ return mcBranchOf(value, options);
26
24
  }
27
25
  /** True for a kept `mc:AlternateContent`: its element pair, or its value. */
28
26
  export function isAlternateContent(value) {
@@ -0,0 +1,48 @@
1
+ /** How Word 365 stores an attribute a setter writes, and the range the setter writes within, in the attribute's unit. */
2
+ export interface WordStorage {
3
+ /** The attribute, as the part writes it. */
4
+ attribute: string;
5
+ /** The attribute's unit, and how many make a point. */
6
+ unit: 'twips' | 'half-points' | 'fiftieths of a percent';
7
+ min: number;
8
+ max: number;
9
+ /** What Word reads a value as, once it is stored: the wrap, as CR-066 measured it (the objects package's `wordIntegerReading`). */
10
+ reads(value: number): string;
11
+ /** Measured by CR-066's rounds 5 and 6: every row is, since round 6 (section 65.6). */
12
+ measured: boolean;
13
+ /** The range Word keeps as written, the objects table's (`keptRange`), before the setter's policy narrows it. */
14
+ stored: readonly [number, number];
15
+ /** Why a value Word would keep as written is refused all the same: the range narrowed below Word's storage. */
16
+ why?: string;
17
+ /** The objects table's entry the row reads: `TYPE_NAME`, property, and the rule beside the attribute where one applies. */
18
+ objects: {
19
+ typeName: string;
20
+ property: string;
21
+ rule?: 'auto' | 'exact' | 'atLeast';
22
+ };
23
+ }
24
+ /**
25
+ * The attributes the content API's setters write, by Word 365's storage (the objects table; CR-066 sections 3.1 and 3.4),
26
+ * narrowed where Word's own setter stops short of its storage (check 71b, CR-002 section 65.9, D2): there this package
27
+ * refuses where Word does, and no earlier.
28
+ */
29
+ export declare const WORD_STORAGE: {
30
+ readonly indLeft: WordStorage;
31
+ readonly indRight: WordStorage;
32
+ readonly indFirstLine: WordStorage;
33
+ readonly indHanging: WordStorage;
34
+ readonly lvlIndLeft: WordStorage;
35
+ readonly spacingBefore: WordStorage;
36
+ readonly spacingAfter: WordStorage;
37
+ readonly spacingLineAuto: WordStorage;
38
+ readonly tcWDxa: WordStorage;
39
+ readonly tcWPct: WordStorage;
40
+ readonly sz: WordStorage;
41
+ };
42
+ /**
43
+ * `points` in the attribute's unit, rounded, where Word stores it as written and the setter's policy allows; else
44
+ * `InvalidArgument`, naming the range and what Word would read, or why the policy stops short of Word's storage (CR-002
45
+ * section 65; the policy section 65.9's). `where` is the setter (`Paragraph.leftIndent`). `units` is the value already
46
+ * in the attribute's unit, where the setter computes it otherwise (a percentage width's fiftieths).
47
+ */
48
+ export declare function withinWord(where: string, points: number, storage: WordStorage, units?: number): number;