@docx-editor.dev/core 2.21.1 → 2.23.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.
- package/README.md +11 -5
- package/dist/automation.cjs +1 -1
- package/dist/automation.d.ts +13 -12
- package/dist/automation.js +1 -1
- package/dist/binding.cjs +1 -1
- package/dist/binding.d.ts +11 -10
- package/dist/binding.js +1 -1
- package/dist/chunk-4OAXZNBU.js +5 -0
- package/dist/chunk-6745I4XF.cjs +20 -0
- package/dist/chunk-6KA3L3FR.cjs +11 -0
- package/dist/chunk-6QS5WL63.js +1 -0
- package/dist/{chunk-E5OUYRNM.cjs → chunk-BUDXZ7T2.cjs} +5 -5
- package/dist/chunk-DYRXA5UJ.cjs +2 -0
- package/dist/chunk-DZOGUV7O.js +1 -0
- package/dist/chunk-EHT4LKKZ.cjs +1 -0
- package/dist/chunk-F5LAOPAD.js +20 -0
- package/dist/chunk-FWX535CS.cjs +1 -0
- package/dist/chunk-GRVRQBKU.js +1 -0
- package/dist/chunk-JN62FLHE.cjs +4 -0
- package/dist/{chunk-BF2TOG2V.js → chunk-KRBBWATQ.js} +1 -1
- package/dist/chunk-KV5HFE23.cjs +2 -0
- package/dist/{chunk-T5B4NMKF.cjs → chunk-PCPBDYEL.cjs} +1 -1
- package/dist/chunk-Q4RDWB3U.cjs +18 -0
- package/dist/{chunk-4TYA4RLP.cjs → chunk-QK23BSRK.cjs} +1 -1
- package/dist/chunk-QTFAXWVI.cjs +1 -0
- package/dist/chunk-RLO64PHP.cjs +1 -0
- package/dist/chunk-T7RGKBCE.cjs +5 -0
- package/dist/{chunk-FPDIJ4RF.js → chunk-UMBHUW6R.js} +1 -1
- package/dist/chunk-VE6H5EJA.js +5 -0
- package/dist/chunk-WHG23WNI.js +1 -0
- package/dist/chunk-XBYZTDF7.js +4 -0
- package/dist/chunk-XRBPZCNK.js +11 -0
- package/dist/chunk-Y5JCIWTF.js +2 -0
- package/dist/chunk-ZA6HEWUM.js +2 -0
- package/dist/chunk-ZCRSPN4Q.js +18 -0
- package/dist/collaboration/replication.cjs +1 -1
- package/dist/collaboration/replication.d.ts +6 -5
- package/dist/collaboration/replication.js +1 -1
- package/dist/collaboration.d.ts +7 -6
- package/dist/contracts/document.d.ts +4 -3
- package/dist/contracts/editor.d.ts +9 -9
- package/dist/contracts/interaction.d.ts +2 -1
- package/dist/contracts/modules.d.ts +9 -8
- package/dist/contracts/types.d.ts +1 -1
- package/dist/{custom-node-writes-CncR6KfC.d.ts → custom-node-writes-77rvPW2-.d.ts} +4 -3
- package/dist/{document-protection-DM6mA8Rf.d.ts → document-protection-CYIjO-li.d.ts} +3 -2
- package/dist/{editor-BRh7GuZF.d.ts → editor-DhUQsWWH.d.ts} +179 -52
- package/dist/editor.cjs +1 -1
- package/dist/editor.css +1 -1
- package/dist/editor.d.ts +181 -28
- package/dist/editor.js +1 -1
- package/dist/export.cjs +2 -2
- package/dist/export.d.ts +36 -18
- package/dist/export.js +2 -2
- package/dist/{failure-DNNmwsgD.d.ts → failure-BIPVQbRm.d.ts} +2 -1
- package/dist/{font-resolver-DwuQYj3P.d.ts → font-resolver-BwoHFBlr.d.ts} +3 -2
- package/dist/{headless-document-view-C3fc_jV0.d.ts → headless-document-view-Cc3fFWhv.d.ts} +4 -3
- package/dist/{image-resources-DxF-SN_8.d.ts → image-resources-DkuzBnTM.d.ts} +3 -2
- package/dist/{index-3pNnG4z4.d.ts → index-BYaCkGKO.d.ts} +179 -26
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +18 -18
- package/dist/index.js +1 -1
- package/dist/{layout-shaping-_GGjhhVc.d.ts → layout-shaping-DpWQG26a.d.ts} +7 -3
- package/dist/layout.cjs +1 -1
- package/dist/layout.d.ts +473 -64
- package/dist/layout.js +1 -1
- package/dist/{note-properties-CjT31AuP.d.ts → note-properties-DMfKK3QU.d.ts} +3 -2
- package/dist/{omml-equation-Bk3OP1fv.d.ts → omml-equation-BY7OdfGY.d.ts} +3 -2
- package/dist/output.cjs +1 -1
- package/dist/output.d.ts +9 -8
- package/dist/output.js +1 -1
- package/dist/{protocol-CrbTIW2Z.d.ts → protocol-CPAakntQ.d.ts} +4 -3
- package/dist/{review-paragraph-order-D3KZohIL.d.ts → review-paragraph-order-B0BP_wJP.d.ts} +1 -1
- package/dist/{revision-projection-kVd7p6Wd.d.ts → revision-projection-CVJeA2jp.d.ts} +3 -2
- package/dist/{semantic-cell-selection-CkSsZHDL.d.ts → semantic-cell-selection-B8GF_H2e.d.ts} +10 -6
- package/dist/{semantic-interaction-Z2Xa68U5.d.ts → semantic-interaction-DQjtIruQ.d.ts} +3 -2
- package/dist/{semantic-paint-BHKprI9P.d.ts → semantic-paint-DoPl85nR.d.ts} +4 -3
- package/dist/{semantic-records-StH-AnHK.d.ts → semantic-records-DB0VgWsr.d.ts} +1125 -1039
- package/dist/store.cjs +1 -1
- package/dist/store.d.ts +17 -16
- package/dist/store.js +1 -1
- package/dist/{tree-package-fragment-CpEV1gl3.d.ts → tree-package-fragment-BIyGh6L8.d.ts} +4 -3
- package/dist/{tree-session-contract-CTT9nM7Z.d.ts → tree-session-contract-CHcCnz3W.d.ts} +7 -6
- package/dist/{types-barrel-PwcfEkaX.d.ts → types-barrel-DN_-gaRX.d.ts} +72 -5
- package/package.json +3 -3
- package/dist/chunk-25OQBAKA.js +0 -2
- package/dist/chunk-3B3DQMSL.js +0 -11
- package/dist/chunk-3P3DNJYM.js +0 -5
- package/dist/chunk-3PA64KPK.js +0 -5
- package/dist/chunk-7N77EN2Z.cjs +0 -2
- package/dist/chunk-AGWWNVIJ.cjs +0 -1
- package/dist/chunk-DFTVN25X.cjs +0 -5
- package/dist/chunk-DGONGMAV.js +0 -2
- package/dist/chunk-DZYKU2HG.js +0 -1
- package/dist/chunk-F4H3LC6M.cjs +0 -2
- package/dist/chunk-FGBADUSK.cjs +0 -1
- package/dist/chunk-IUTTOBQW.cjs +0 -20
- package/dist/chunk-IYCXQAFT.cjs +0 -4
- package/dist/chunk-JL3SI5SZ.js +0 -18
- package/dist/chunk-OFWUHH52.js +0 -20
- package/dist/chunk-OKY6JRJL.js +0 -4
- package/dist/chunk-P24FHMQW.cjs +0 -1
- package/dist/chunk-QBKAOSVF.cjs +0 -18
- package/dist/chunk-RA2TDTEQ.cjs +0 -1
- package/dist/chunk-U5GUJKTS.js +0 -1
- package/dist/chunk-VP7SDFCM.js +0 -1
- package/dist/chunk-XRYSPKKC.js +0 -1
- package/dist/chunk-ZZRCVDQU.cjs +0 -11
|
@@ -1,7 +1,85 @@
|
|
|
1
|
-
import { c as RevisionAttribution, a as RevisionDisplayMode
|
|
2
|
-
import { a0 as OoxmlProperty, a1 as OoxmlElement,
|
|
3
|
-
import {
|
|
4
|
-
import { O as OmmlEquationProjection } from './omml-equation-
|
|
1
|
+
import { c as RevisionAttribution, a as RevisionDisplayMode } from './revision-projection-CVJeA2jp.js';
|
|
2
|
+
import { a0 as OoxmlProperty, a1 as OoxmlElement, N as OoxmlNode, O as OoxmlPart, aG as TableBorderStyle, aH as DrawingAccessibility, aI as DrawingImageEffects, a8 as SourceCrop, aJ as DrawingTransform, aK as VectorShapeProjection, aj as DrawingHorizontalReferenceFrame, ak as DrawingVerticalReferenceFrame, aa as ImageWrapTarget, a4 as DrawingProjection, aL as OoxmlDrawingNode } from './types-barrel-DN_-gaRX.js';
|
|
3
|
+
import { b as ImageResourceState } from './image-resources-DkuzBnTM.js';
|
|
4
|
+
import { O as OmmlEquationProjection } from './omml-equation-BY7OdfGY.js';
|
|
5
|
+
|
|
6
|
+
/** A strikeout stroke at the drawn size, in points. `offsetPt` is the stroke TOP. @public */
|
|
7
|
+
interface StrikeoutStrokePt {
|
|
8
|
+
/** Distance from the baseline UP to the top of the stroke. */
|
|
9
|
+
readonly offsetPt: number;
|
|
10
|
+
/** Stroke height. Always positive. */
|
|
11
|
+
readonly thicknessPt: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `w:tblpPr/@w:horzAnchor` (17.4.58) and `@w:vertAnchor` (17.4.66): the box a floated
|
|
16
|
+
* table's offsets are measured from. Absent means `text` for both.
|
|
17
|
+
*/
|
|
18
|
+
type TableFloatAnchor = 'text' | 'margin' | 'page';
|
|
19
|
+
/** `w:tblpPr/@w:tblpXSpec` (17.4.63, ST_XAlign). */
|
|
20
|
+
type TableFloatXSpec = 'left' | 'center' | 'right' | 'inside' | 'outside';
|
|
21
|
+
/** `w:tblpPr/@w:tblpYSpec` (17.4.65, ST_YAlign). */
|
|
22
|
+
type TableFloatYSpec = 'inline' | 'top' | 'center' | 'bottom' | 'inside' | 'outside';
|
|
23
|
+
/**
|
|
24
|
+
* `w:tblPr/w:tblpPr` (17.4.57) — a table positioned against an anchor box rather than at
|
|
25
|
+
* the point in the text where it was authored.
|
|
26
|
+
*
|
|
27
|
+
* A spec (`tblpXSpec`/`tblpYSpec`) supersedes the matching offset when both are present:
|
|
28
|
+
* 17.4.57 states the alignment outright, and the offset only answers "how far from the
|
|
29
|
+
* anchor" for the case where no alignment was stated.
|
|
30
|
+
*/
|
|
31
|
+
interface TableFloatPosition {
|
|
32
|
+
readonly horzAnchor: TableFloatAnchor;
|
|
33
|
+
readonly vertAnchor: TableFloatAnchor;
|
|
34
|
+
readonly xSpec?: TableFloatXSpec;
|
|
35
|
+
/** `w:tblpX` in points; signed, so a table can be pulled into the margin. */
|
|
36
|
+
readonly xPt: number;
|
|
37
|
+
readonly ySpec?: TableFloatYSpec;
|
|
38
|
+
/** `w:tblpY` in points; signed. */
|
|
39
|
+
readonly yPt: number;
|
|
40
|
+
readonly distances?: {
|
|
41
|
+
readonly top: number;
|
|
42
|
+
readonly right: number;
|
|
43
|
+
readonly bottom: number;
|
|
44
|
+
readonly left: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One `w:numPicBullet`: the image a level draws instead of its `w:lvlText`.
|
|
50
|
+
*
|
|
51
|
+
* `relationshipId` is unresolved on purpose — it names a relationship of the numbering part,
|
|
52
|
+
* and resolving it is the image-resource lookup's job, under its own validation and caps.
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
interface NumberingPictureBullet {
|
|
56
|
+
/** `w:numPicBulletId` — what a level's `w:lvlPicBulletId` names. */
|
|
57
|
+
readonly picBulletId: string;
|
|
58
|
+
/** `v:imagedata/@r:id`, relative to the NUMBERING part's relationships. */
|
|
59
|
+
readonly relationshipId: string;
|
|
60
|
+
/** Authored `v:shape` width in points. */
|
|
61
|
+
readonly width: number;
|
|
62
|
+
/** Authored `v:shape` height in points. */
|
|
63
|
+
readonly height: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A level's picture bullet with the size it is actually DRAWN at.
|
|
67
|
+
*
|
|
68
|
+
* Separate from {@link NumberingPictureBullet} because the authored `v:shape` extent is not
|
|
69
|
+
* the painted extent: Word scales it by the marker run's font size. Layout, hit testing and
|
|
70
|
+
* both paint sinks read `width` / `height`; `authored` is kept so the scale stays inspectable.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
interface ResolvedPictureBullet {
|
|
74
|
+
/** `v:imagedata/@r:id`, relative to the NUMBERING part's relationships. */
|
|
75
|
+
readonly relationshipId: string;
|
|
76
|
+
/** Painted width in points, after the marker font scale. */
|
|
77
|
+
readonly width: number;
|
|
78
|
+
/** Painted height in points, after the marker font scale. */
|
|
79
|
+
readonly height: number;
|
|
80
|
+
/** The `v:shape` extent exactly as the file authored it. */
|
|
81
|
+
readonly authored: NumberingPictureBullet;
|
|
82
|
+
}
|
|
5
83
|
|
|
6
84
|
/** Soft ceiling on abstractNum / num entries read from one part. */
|
|
7
85
|
declare const MAX_NUMBERING_DEFINITIONS = 512;
|
|
@@ -18,7 +96,10 @@ type ListMarkerAlign = 'left' | 'center' | 'right';
|
|
|
18
96
|
* must not overwrite an inherited `hanging` with a synthesized zero.
|
|
19
97
|
*/
|
|
20
98
|
interface NumberingLevelIndent {
|
|
21
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* The sides as the level spells them, in points. `left` and `start` both name the leading
|
|
101
|
+
* indent, and `right` and `end` the trailing one; `left`/`right` below hold those two.
|
|
102
|
+
*/
|
|
22
103
|
readonly authored?: {
|
|
23
104
|
readonly left?: number;
|
|
24
105
|
readonly right?: number;
|
|
@@ -32,9 +113,9 @@ interface NumberingLevelIndent {
|
|
|
32
113
|
/**
|
|
33
114
|
* Which of these the LEVEL actually authored.
|
|
34
115
|
*
|
|
35
|
-
* A level's `w:pPr/w:ind` sits between the
|
|
36
|
-
* merge has to tell "the level says left = 0" from
|
|
37
|
-
* the style's value stands". Absent (the default) reads as "says nothing", which is what a
|
|
116
|
+
* A level's `w:pPr/w:ind` sits between the formatting it outranks and the formatting that
|
|
117
|
+
* outranks it (`mergeListIndent`), so the merge has to tell "the level says left = 0" from
|
|
118
|
+
* "the level says nothing about left and the style's value stands". Absent (the default) reads as "says nothing", which is what a
|
|
38
119
|
* hand-built level in a test means.
|
|
39
120
|
*/
|
|
40
121
|
readonly stated?: {
|
|
@@ -63,6 +144,11 @@ interface NumberingLevel {
|
|
|
63
144
|
* decimal, whatever number format those levels declare for themselves.
|
|
64
145
|
*/
|
|
65
146
|
readonly isLgl: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* `w:lvlPicBulletId` (§17.9.12): this level's marker is the `w:numPicBullet` of that id,
|
|
149
|
+
* drawn INSTEAD of `w:lvlText`. Absent on every ordinary level.
|
|
150
|
+
*/
|
|
151
|
+
readonly picBulletId?: string;
|
|
66
152
|
/** Level `w:rPr` as flat properties (for marker face / vanish). */
|
|
67
153
|
readonly runProperties: readonly OoxmlProperty[];
|
|
68
154
|
/** True when level run props request vanish — marker must not paint. */
|
|
@@ -119,6 +205,13 @@ interface NumDefinition {
|
|
|
119
205
|
interface NumberingIndex {
|
|
120
206
|
readonly abstractNums: ReadonlyMap<string, AbstractNumDefinition>;
|
|
121
207
|
readonly nums: ReadonlyMap<string, NumDefinition>;
|
|
208
|
+
/**
|
|
209
|
+
* `w:numPicBullet` declarations keyed by `w:numPicBulletId` (§17.9.20).
|
|
210
|
+
*
|
|
211
|
+
* Optional so a hand-built index stays valid; absent reads as "this part declares none",
|
|
212
|
+
* which is what a level's `w:lvlPicBulletId` then falls back from.
|
|
213
|
+
*/
|
|
214
|
+
readonly pictureBullets?: ReadonlyMap<string, NumberingPictureBullet>;
|
|
122
215
|
}
|
|
123
216
|
/**
|
|
124
217
|
* Build a numbering index from the root of a numbering part (`w:numbering`).
|
|
@@ -148,535 +241,291 @@ declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilv
|
|
|
148
241
|
/** Empty index for tests / documents without numbering. */
|
|
149
242
|
declare const EMPTY_NUMBERING_INDEX: NumberingIndex;
|
|
150
243
|
|
|
151
|
-
/** Soft ceiling matching Word's practical custom-tab UI limit. */
|
|
152
|
-
declare const MAX_TAB_STOPS = 64;
|
|
153
|
-
/**
|
|
154
|
-
* Soft ceiling on a tab position (31_680 twips ≈ 22"), matching paragraph-spacing bounds so
|
|
155
|
-
* a hostile stop cannot shove layout into pathological widths.
|
|
156
|
-
*/
|
|
157
|
-
declare const MAX_TAB_POSITION_TWIPS = 31680;
|
|
158
|
-
/** OOXML / Word default when `w:settings/w:defaultTabStop` is absent: 720 twips = 0.5". */
|
|
159
|
-
declare const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
160
|
-
/** The default tab interval in points — {@link DEFAULT_TAB_INTERVAL_TWIPS} converted. */
|
|
161
|
-
declare const DEFAULT_TAB_INTERVAL_PT: number;
|
|
162
244
|
/**
|
|
163
|
-
*
|
|
245
|
+
* A face, as something asks for it: family plus the two axes this engine admits.
|
|
164
246
|
*
|
|
165
|
-
* Only
|
|
166
|
-
*
|
|
247
|
+
* Only static weights and slants. Variable-font axes are deliberately outside this vocabulary —
|
|
248
|
+
* the shaper refuses variation axes, and a variable file admitted here would render bold at
|
|
249
|
+
* regular weight.
|
|
167
250
|
*/
|
|
168
|
-
|
|
251
|
+
interface FontRequest {
|
|
252
|
+
readonly family: string;
|
|
253
|
+
readonly weight: number;
|
|
254
|
+
readonly style: 'normal' | 'italic';
|
|
255
|
+
}
|
|
169
256
|
/**
|
|
170
|
-
*
|
|
171
|
-
* space a tab reserves. `none` is the default and is represented by an absent leader.
|
|
257
|
+
* A request that was answered by a DIFFERENT face than the one asked for.
|
|
172
258
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
259
|
+
* Recorded rather than silently applied, because a substitution changes measurement: it is part
|
|
260
|
+
* of the shaping fingerprint, so a cached run shaped against a substitute is never reused for the
|
|
261
|
+
* real face.
|
|
176
262
|
*/
|
|
177
|
-
|
|
263
|
+
interface FontSubstitution {
|
|
264
|
+
readonly requested: FontRequest;
|
|
265
|
+
readonly resolved: FontRequest;
|
|
266
|
+
readonly lineMetrics?: {
|
|
267
|
+
readonly heightEm: number;
|
|
268
|
+
readonly baselineEm: number;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
declare const RESOLVED_FONT_BRAND: unique symbol;
|
|
178
272
|
/**
|
|
179
|
-
*
|
|
273
|
+
* A face that passed admission: the bytes are present, within limits, hash-verified, and parse as
|
|
274
|
+
* a font.
|
|
180
275
|
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* typing it would is to ask the measurer how wide it actually is.
|
|
276
|
+
* Branded, so a `ResolvedFont` cannot be constructed by an object literal. Holding one is proof
|
|
277
|
+
* the checks ran — which is what lets shaping skip re-validating on every call.
|
|
184
278
|
*/
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
readonly
|
|
190
|
-
readonly
|
|
191
|
-
/**
|
|
192
|
-
readonly
|
|
279
|
+
interface ResolvedFont {
|
|
280
|
+
readonly [RESOLVED_FONT_BRAND]: true;
|
|
281
|
+
readonly id: string;
|
|
282
|
+
readonly identity: string;
|
|
283
|
+
readonly request: FontRequest;
|
|
284
|
+
readonly family: string;
|
|
285
|
+
/** Owned byte length, available without creating a defensive byte copy. */
|
|
286
|
+
readonly byteLength: number;
|
|
287
|
+
readonly bytes: Uint8Array;
|
|
288
|
+
readonly hash: string;
|
|
289
|
+
readonly faceIndex: number;
|
|
290
|
+
readonly substitution: FontSubstitution | null;
|
|
193
291
|
}
|
|
194
292
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
293
|
+
* Largest single face this engine will ever admit, whatever a caller configures.
|
|
294
|
+
*
|
|
295
|
+
* A CEILING, not a default: a host may set a smaller `maxFontBytes`, but nothing can raise it
|
|
296
|
+
* past this. Font bytes come from files, and an unbounded face is a memory-exhaustion vector.
|
|
197
297
|
*/
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
/** The frozen "no custom stops" value, so a paragraph without tabs mints no object. */
|
|
205
|
-
declare const EMPTY_TAB_STOPS: ResolvedTabStops;
|
|
298
|
+
declare const HARD_MAX_FONT_BYTES: number;
|
|
299
|
+
/** Most faces one snapshot may hold, whatever a caller configures. */
|
|
300
|
+
declare const HARD_MAX_FONT_SOURCES = 256;
|
|
301
|
+
/** Most bytes all admitted faces may total, whatever a caller configures. */
|
|
302
|
+
declare const HARD_MAX_AGGREGATE_FONT_BYTES: number;
|
|
206
303
|
/**
|
|
207
|
-
*
|
|
304
|
+
* Why a face was not admitted.
|
|
208
305
|
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
306
|
+
* `forbidden` and `hashMismatch` are adversarial signals rather than ordinary failures: the first
|
|
307
|
+
* is a face the host declared off-limits, the second is bytes that are not what their source
|
|
308
|
+
* claimed.
|
|
211
309
|
*/
|
|
212
|
-
|
|
213
|
-
/** Direct `w:pPr` only — used when no style cascade table is present. */
|
|
214
|
-
declare function paragraphTabStops(pPr: OoxmlNode | undefined): ResolvedTabStops;
|
|
310
|
+
type FontResolutionErrorCode = 'missing' | 'forbidden' | 'overLimit' | 'malformed' | 'hashMismatch';
|
|
215
311
|
/**
|
|
216
|
-
*
|
|
312
|
+
* A face that could not be admitted, carrying whatever evidence the refusal produced.
|
|
217
313
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
314
|
+
* RETURNED rather than thrown by `FontResourceSnapshot.resolve`, because a missing face is an
|
|
315
|
+
* ordinary condition — layout falls back and carries on. It extends `Error` so a caller that
|
|
316
|
+
* would rather throw can.
|
|
221
317
|
*/
|
|
222
|
-
declare
|
|
318
|
+
declare class FontResolutionError extends Error {
|
|
319
|
+
readonly name = "FontResolutionError";
|
|
320
|
+
readonly request: FontRequest;
|
|
321
|
+
readonly code: FontResolutionErrorCode;
|
|
322
|
+
readonly limit?: number;
|
|
323
|
+
readonly actual?: number;
|
|
324
|
+
readonly diagnostic?: string;
|
|
325
|
+
readonly expectedHash?: string;
|
|
326
|
+
readonly actualHash?: string;
|
|
327
|
+
constructor(code: FontResolutionErrorCode, request: FontRequest, details?: {
|
|
328
|
+
readonly limit?: number;
|
|
329
|
+
readonly actual?: number;
|
|
330
|
+
readonly diagnostic?: string;
|
|
331
|
+
readonly expectedHash?: string;
|
|
332
|
+
readonly actualHash?: string;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
223
335
|
/**
|
|
224
|
-
*
|
|
336
|
+
* One face offered to a snapshot, before admission.
|
|
225
337
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
|
|
338
|
+
* `hash` is checked against the bytes, so a source cannot claim a face it did not supply. Setting
|
|
339
|
+
* `availability` to `forbidden` declares a face that exists but must not be used, which resolves
|
|
340
|
+
* as a typed refusal rather than as "missing".
|
|
341
|
+
*/
|
|
342
|
+
interface FontResourceDefinition {
|
|
343
|
+
readonly request: FontRequest;
|
|
344
|
+
readonly id: string;
|
|
345
|
+
readonly bytes: Uint8Array;
|
|
346
|
+
readonly hash: string;
|
|
347
|
+
readonly faceIndex: number;
|
|
348
|
+
readonly availability?: 'available' | 'forbidden';
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* A host-declared redirect: requests for `from` resolve to `to`.
|
|
230
352
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
353
|
+
* How a metric-compatible substitute is wired in — a document naming Calibri resolves to Carlito
|
|
354
|
+
* without the document being rewritten.
|
|
233
355
|
*/
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
readonly
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
356
|
+
interface DeclaredFontSubstitution {
|
|
357
|
+
readonly from: FontRequest;
|
|
358
|
+
readonly to: FontRequest;
|
|
359
|
+
readonly lineMetrics?: {
|
|
360
|
+
readonly heightEm: number;
|
|
361
|
+
readonly baselineEm: number;
|
|
362
|
+
};
|
|
241
363
|
}
|
|
364
|
+
/** Whether bytes parse as a usable font, with a diagnostic when they do not. */
|
|
365
|
+
type FontValidationResult = {
|
|
366
|
+
readonly valid: true;
|
|
367
|
+
} | {
|
|
368
|
+
readonly valid: false;
|
|
369
|
+
readonly diagnostic: string;
|
|
370
|
+
};
|
|
242
371
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
372
|
+
* The injected check that bytes really are a font.
|
|
373
|
+
*
|
|
374
|
+
* Injected because it is the shaper that knows — HarfBuzz can open a face and read its tables,
|
|
375
|
+
* and this layer must not duplicate that judgement. Every byte reaching it is file input.
|
|
245
376
|
*/
|
|
246
|
-
|
|
377
|
+
type FontByteValidator = (bytes: Uint8Array, faceIndex: number) => FontValidationResult;
|
|
247
378
|
/**
|
|
248
|
-
*
|
|
379
|
+
* An immutable set of admitted faces, and the only way to reach one.
|
|
249
380
|
*
|
|
250
|
-
* `
|
|
251
|
-
*
|
|
381
|
+
* `epoch` identifies the snapshot: fonts change by REPLACING it, never by mutation, so a layout
|
|
382
|
+
* pass holds one snapshot for its whole run and cannot observe a face appearing or vanishing
|
|
383
|
+
* midway.
|
|
252
384
|
*/
|
|
253
|
-
|
|
385
|
+
interface FontResourceSnapshot {
|
|
386
|
+
readonly epoch: number;
|
|
387
|
+
/** The admitted face, or a typed refusal. Never throws. */
|
|
388
|
+
resolve(request: FontRequest): ResolvedFont | FontResolutionError;
|
|
389
|
+
}
|
|
254
390
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
391
|
+
* How a snapshot is built: which faces, under what limits, validated how.
|
|
392
|
+
*
|
|
393
|
+
* `maxFontBytes` is clamped to {@link HARD_MAX_FONT_BYTES} — a caller may tighten the budget but
|
|
394
|
+
* never widen it past the engine's own ceiling.
|
|
257
395
|
*/
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
readonly variant: string;
|
|
266
|
-
/** RRGGBB, or null when the underline follows the text colour. */
|
|
267
|
-
readonly color: string | null;
|
|
396
|
+
interface FontResourceSnapshotOptions {
|
|
397
|
+
readonly epoch: number;
|
|
398
|
+
readonly maxFontBytes: number;
|
|
399
|
+
readonly resources: readonly FontResourceDefinition[];
|
|
400
|
+
readonly substitutions?: readonly DeclaredFontSubstitution[];
|
|
401
|
+
readonly validateFont: FontByteValidator;
|
|
402
|
+
readonly instrumentation?: FontResourceInstrumentation;
|
|
268
403
|
}
|
|
269
404
|
/**
|
|
270
|
-
*
|
|
405
|
+
* Optional counters for the operations font admission is expected to do RARELY.
|
|
271
406
|
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
407
|
+
* Exists so tests can assert the absence of work: hashing and byte-copying a 64 MB face on every
|
|
408
|
+
* resolve would not be visible in output, only in a stalled document, so the checks assert these
|
|
409
|
+
* fire once rather than per call.
|
|
274
410
|
*/
|
|
275
|
-
interface
|
|
276
|
-
readonly
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
*
|
|
281
|
-
* OOXML picks a run's face per SCRIPT: `fontFamily` covers the ascii/hAnsi slots, and
|
|
282
|
-
* ideographic/kana/hangul text resolves through this one instead. Kept beside — not
|
|
283
|
-
* folded into — `fontFamily`, because a mixed run needs both at once and the split into
|
|
284
|
-
* slot-homogeneous pieces happens downstream (`piecesOfParagraph`).
|
|
285
|
-
*/
|
|
286
|
-
readonly fontFamilyEastAsia: string | null;
|
|
287
|
-
/** Script and paragraph-resolved direction for shaping and visual placement. */
|
|
288
|
-
readonly shaping?: {
|
|
289
|
-
readonly script: string;
|
|
290
|
-
readonly direction: 'ltr' | 'rtl';
|
|
291
|
-
readonly level: number;
|
|
292
|
-
readonly baseLevel: number;
|
|
293
|
-
/** Resolved w:rtl context; absent when authored Unicode controls govern the paragraph. */
|
|
294
|
-
readonly runDirection?: 'ltr' | 'rtl';
|
|
295
|
-
readonly wordSpacingPt?: number;
|
|
296
|
-
};
|
|
297
|
-
/** Points. `w:sz` is half-points, so 22 becomes 11. */
|
|
298
|
-
readonly fontSizePt: number;
|
|
299
|
-
/** RRGGBB, or null for the inherited/automatic colour. */
|
|
300
|
-
readonly color: string | null;
|
|
301
|
-
readonly bold: boolean;
|
|
302
|
-
/** Opaque solid `w14:textOutline`, in points; paint only, never a font-weight change. */
|
|
303
|
-
readonly textOutline?: {
|
|
304
|
-
readonly widthPt: number;
|
|
305
|
-
readonly color: string;
|
|
306
|
-
};
|
|
307
|
-
readonly italic: boolean;
|
|
308
|
-
readonly underline: ResolvedUnderline | null;
|
|
309
|
-
readonly strike: boolean;
|
|
310
|
-
readonly doubleStrike: boolean;
|
|
311
|
-
/** An `ST_HighlightColor` name, or null. */
|
|
312
|
-
readonly highlight: string | null;
|
|
313
|
-
/**
|
|
314
|
-
* Character shading fill (`w:rPr/w:shd`), validated RRGGBB, or null.
|
|
315
|
-
*
|
|
316
|
-
* Paint applies this as the glyph-box background; a recognised highlight overrides it.
|
|
317
|
-
* Measurement ignores shading.
|
|
318
|
-
*/
|
|
319
|
-
readonly shading: string | null;
|
|
320
|
-
readonly verticalAlign: VerticalAlign;
|
|
321
|
-
/** `w:position`, in points. Positive raises the baseline. */
|
|
322
|
-
readonly baselineShiftPt: number;
|
|
323
|
-
readonly caps: boolean;
|
|
324
|
-
readonly smallCaps: boolean;
|
|
325
|
-
/** `w:spacing`, in points. Added to every advance. */
|
|
326
|
-
readonly characterSpacingPt: number;
|
|
327
|
-
/** `w:w`, as a percentage. 100 is unscaled. */
|
|
328
|
-
readonly horizontalScalePercent: number;
|
|
329
|
-
/** `w:kern`, in points: the size at or above which kerning applies. 0 disables it. */
|
|
330
|
-
readonly kerningMinPt: number;
|
|
331
|
-
/**
|
|
332
|
-
* `w:vanish` (ECMA-376 §17.3.2.45): the run is hidden text.
|
|
333
|
-
*
|
|
334
|
-
* Word does not draw it AND does not paginate it — hidden index or comment text takes no
|
|
335
|
-
* space at all. So this cannot be a paint-time opacity: a hidden run that is still measured
|
|
336
|
-
* pushes every following line, and every following page break, to the wrong place. Layout
|
|
337
|
-
* drops the content instead (see `piecesOfParagraph`).
|
|
338
|
-
*
|
|
339
|
-
* `w:specVanish` (§17.3.2.36) is a different property — an always-hidden paragraph mark on
|
|
340
|
-
* a heading — and never sets this.
|
|
341
|
-
*/
|
|
342
|
-
readonly hidden: boolean;
|
|
411
|
+
interface FontResourceInstrumentation {
|
|
412
|
+
readonly onOwnedByteCopy?: () => void;
|
|
413
|
+
readonly onHash?: () => void;
|
|
414
|
+
readonly onTableScan?: () => void;
|
|
415
|
+
readonly onAdmission?: () => void;
|
|
343
416
|
}
|
|
344
|
-
/** The style a run inherits when it authors nothing. */
|
|
345
|
-
declare const DEFAULT_RUN_STYLE: ResolvedRunStyle;
|
|
346
417
|
/**
|
|
347
|
-
* The
|
|
418
|
+
* The canonical string identifying one face request.
|
|
348
419
|
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
420
|
+
* Case- and whitespace-normalized, so `"Times New Roman"` and `"times new roman"` are one key —
|
|
421
|
+
* a document may name a family either way and both must reach the same face.
|
|
351
422
|
*/
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
/** `a:minorFont` latin typeface — body text. */
|
|
356
|
-
readonly minor: string | null;
|
|
357
|
-
/** `a:majorFont` east asian typeface (`a:ea`) — headings. Optional for back-compat. */
|
|
358
|
-
readonly majorEastAsia?: string | null;
|
|
359
|
-
/** `a:minorFont` east asian typeface (`a:ea`) — body text. Optional for back-compat. */
|
|
360
|
-
readonly minorEastAsia?: string | null;
|
|
361
|
-
/** Complex-script heading and body theme faces. */
|
|
362
|
-
readonly majorBidi?: string | null;
|
|
363
|
-
readonly minorBidi?: string | null;
|
|
364
|
-
/** Language-specific theme faces, keyed by ISO 15924 script. */
|
|
365
|
-
readonly majorSupplemental?: Readonly<Record<string, string>>;
|
|
366
|
-
readonly minorSupplemental?: Readonly<Record<string, string>>;
|
|
367
|
-
}
|
|
423
|
+
declare const fontRequestKey: (request: FontRequest) => string;
|
|
424
|
+
/** Bounded minimum sfnt/TTC check; Task 4 supplies full parser-backed validation. */
|
|
425
|
+
declare const boundedStructuralFontValidator: FontByteValidator;
|
|
368
426
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
* Unrecognised values are DROPPED rather than guessed: a `w:sz` of `"large"` leaves the
|
|
372
|
-
* default size rather than inventing one, because a wrong measurement moves every glyph
|
|
373
|
-
* after it and a missing one is visible immediately.
|
|
427
|
+
* Admit a set of faces and return the immutable snapshot layout resolves against.
|
|
374
428
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
429
|
+
* Admission is the trust boundary for font bytes: counts and sizes are checked against the hard
|
|
430
|
+
* ceilings, each face's hash is re-derived and compared, and the bytes are handed to the injected
|
|
431
|
+
* validator. A face failing any of these is recorded as a typed refusal rather than dropped, so
|
|
432
|
+
* `resolve` can explain itself instead of answering "missing".
|
|
378
433
|
*/
|
|
379
|
-
declare
|
|
380
|
-
|
|
381
|
-
declare
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
434
|
+
declare const createFontResourceSnapshot: (options: FontResourceSnapshotOptions) => FontResourceSnapshot;
|
|
435
|
+
|
|
436
|
+
declare const FIXED_POINT: unique symbol;
|
|
437
|
+
/** Fixed-point coordinates are safe integers in units declared by ShapingEnvironment.fixedPointScale. */
|
|
438
|
+
type FixedPoint = number & {
|
|
439
|
+
readonly [FIXED_POINT]: true;
|
|
440
|
+
};
|
|
386
441
|
/**
|
|
387
|
-
*
|
|
442
|
+
* Brand a safe integer as a {@link FixedPoint} coordinate.
|
|
388
443
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
444
|
+
* Fixed point rather than float throughout shaping so that two runs shaped identically compare
|
|
445
|
+
* EQUAL — float accumulation would make the same text measure differently depending on how it was
|
|
446
|
+
* split, and pagination is decided on those measurements.
|
|
447
|
+
*
|
|
448
|
+
* @throws RangeError when the value is not a safe integer.
|
|
392
449
|
*/
|
|
393
|
-
declare
|
|
394
|
-
|
|
450
|
+
declare const fixedPoint: (value: number) => FixedPoint;
|
|
451
|
+
/** Which way a run reads. The parity projection of its bidi embedding level. */
|
|
452
|
+
type TextDirection = 'ltr' | 'rtl';
|
|
453
|
+
/** How fixed-point conversion breaks ties. Part of the shaping fingerprint. */
|
|
454
|
+
type FixedPointRoundingMode = 'halfAwayFromZero' | 'halfToEven' | 'towardZero';
|
|
455
|
+
/** Which Unicode normalization is applied before shaping, if any. */
|
|
456
|
+
type NormalizationPolicy = 'none' | 'NFC' | 'NFD' | 'NFKC' | 'NFKD';
|
|
395
457
|
/**
|
|
396
|
-
*
|
|
397
|
-
* within.
|
|
458
|
+
* The shaping library and its exact version.
|
|
398
459
|
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
460
|
+
* Versioned because a library upgrade can change glyph positioning, and a cached measurement
|
|
461
|
+
* taken under the old one must not be reused under the new one.
|
|
401
462
|
*/
|
|
402
|
-
interface
|
|
403
|
-
readonly
|
|
404
|
-
readonly
|
|
405
|
-
readonly start: number;
|
|
406
|
-
readonly end: number;
|
|
407
|
-
readonly level: number;
|
|
408
|
-
}[];
|
|
463
|
+
interface VersionedShapingLibrary {
|
|
464
|
+
readonly name: string;
|
|
465
|
+
readonly version: string;
|
|
409
466
|
}
|
|
410
|
-
|
|
411
467
|
/**
|
|
412
|
-
*
|
|
468
|
+
* Everything that determines how text shapes — the complete input to a
|
|
469
|
+
* {@link ShapingEnvironment}.
|
|
413
470
|
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
* regular weight.
|
|
471
|
+
* Exhaustive on purpose. Any field that could change a glyph's position belongs here, because the
|
|
472
|
+
* environment's fingerprint is what decides whether a cached shaped run may be reused.
|
|
417
473
|
*/
|
|
418
|
-
interface
|
|
419
|
-
readonly
|
|
420
|
-
readonly
|
|
421
|
-
readonly
|
|
474
|
+
interface ShapingEnvironmentInput {
|
|
475
|
+
readonly font: ResolvedFont;
|
|
476
|
+
readonly variationAxes: Readonly<Record<string, number>>;
|
|
477
|
+
readonly shapingLibrary: VersionedShapingLibrary;
|
|
478
|
+
readonly unicodeDataVersion: string;
|
|
479
|
+
readonly normalization: NormalizationPolicy;
|
|
480
|
+
readonly script: string;
|
|
481
|
+
readonly language: string;
|
|
482
|
+
readonly direction: TextDirection;
|
|
483
|
+
readonly features: Readonly<Record<string, number>>;
|
|
484
|
+
readonly fallbackOrder: readonly ResolvedFont[];
|
|
485
|
+
readonly fixedPointScale: number;
|
|
486
|
+
readonly roundingMode: FixedPointRoundingMode;
|
|
422
487
|
}
|
|
423
488
|
/**
|
|
424
|
-
* A
|
|
489
|
+
* A validated {@link ShapingEnvironmentInput} — build one with `createShapingEnvironment`.
|
|
425
490
|
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* real face.
|
|
491
|
+
* Structurally identical to its input, but the nominal distinction is the point: holding one
|
|
492
|
+
* means the tags, axes and fonts inside it have already been checked.
|
|
429
493
|
*/
|
|
430
|
-
interface
|
|
431
|
-
readonly requested: FontRequest;
|
|
432
|
-
readonly resolved: FontRequest;
|
|
433
|
-
readonly lineMetrics?: {
|
|
434
|
-
readonly heightEm: number;
|
|
435
|
-
readonly baselineEm: number;
|
|
436
|
-
};
|
|
494
|
+
interface ShapingEnvironment extends ShapingEnvironmentInput {
|
|
437
495
|
}
|
|
438
|
-
|
|
496
|
+
/** One shaping call: the text, its size, its bidi level, and the environment to shape it in. */
|
|
497
|
+
interface ShapeInput {
|
|
498
|
+
readonly text: string;
|
|
499
|
+
readonly fontSizeHalfPoints: number;
|
|
500
|
+
/** Exact UAX #9 embedding/isolate level; direction is its parity projection. */
|
|
501
|
+
readonly bidiLevel: number;
|
|
502
|
+
readonly environment: ShapingEnvironment;
|
|
503
|
+
/**
|
|
504
|
+
* Logical text around `text` that the shaper reads for contextual forms but never
|
|
505
|
+
* shapes: HarfBuzz pre- and post-context. A word split across two formatting runs joins
|
|
506
|
+
* across the split only when each half sees the other's letters. At most
|
|
507
|
+
* {@link MAX_SHAPING_CONTEXT} UTF-16 units each; glyphs and clusters cover `text` alone.
|
|
508
|
+
*/
|
|
509
|
+
readonly context?: ShapingContext;
|
|
510
|
+
}
|
|
511
|
+
/** Pre- and post-context for one shaping call. See {@link ShapeInput.context}. @public */
|
|
512
|
+
interface ShapingContext {
|
|
513
|
+
readonly before: string;
|
|
514
|
+
readonly after: string;
|
|
515
|
+
}
|
|
516
|
+
/** Longest context on either side, in UTF-16 units. Joining needs only the nearest letters. @public */
|
|
517
|
+
declare const MAX_SHAPING_CONTEXT = 16;
|
|
439
518
|
/**
|
|
440
|
-
*
|
|
441
|
-
* a font.
|
|
519
|
+
* One positioned glyph.
|
|
442
520
|
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
|
|
446
|
-
interface ResolvedFont {
|
|
447
|
-
readonly [RESOLVED_FONT_BRAND]: true;
|
|
448
|
-
readonly id: string;
|
|
449
|
-
readonly identity: string;
|
|
450
|
-
readonly request: FontRequest;
|
|
451
|
-
readonly family: string;
|
|
452
|
-
/** Owned byte length, available without creating a defensive byte copy. */
|
|
453
|
-
readonly byteLength: number;
|
|
454
|
-
readonly bytes: Uint8Array;
|
|
455
|
-
readonly hash: string;
|
|
456
|
-
readonly faceIndex: number;
|
|
457
|
-
readonly substitution: FontSubstitution | null;
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Largest single face this engine will ever admit, whatever a caller configures.
|
|
461
|
-
*
|
|
462
|
-
* A CEILING, not a default: a host may set a smaller `maxFontBytes`, but nothing can raise it
|
|
463
|
-
* past this. Font bytes come from files, and an unbounded face is a memory-exhaustion vector.
|
|
464
|
-
*/
|
|
465
|
-
declare const HARD_MAX_FONT_BYTES: number;
|
|
466
|
-
/** Most faces one snapshot may hold, whatever a caller configures. */
|
|
467
|
-
declare const HARD_MAX_FONT_SOURCES = 256;
|
|
468
|
-
/** Most bytes all admitted faces may total, whatever a caller configures. */
|
|
469
|
-
declare const HARD_MAX_AGGREGATE_FONT_BYTES: number;
|
|
470
|
-
/**
|
|
471
|
-
* Why a face was not admitted.
|
|
472
|
-
*
|
|
473
|
-
* `forbidden` and `hashMismatch` are adversarial signals rather than ordinary failures: the first
|
|
474
|
-
* is a face the host declared off-limits, the second is bytes that are not what their source
|
|
475
|
-
* claimed.
|
|
476
|
-
*/
|
|
477
|
-
type FontResolutionErrorCode = 'missing' | 'forbidden' | 'overLimit' | 'malformed' | 'hashMismatch';
|
|
478
|
-
/**
|
|
479
|
-
* A face that could not be admitted, carrying whatever evidence the refusal produced.
|
|
480
|
-
*
|
|
481
|
-
* RETURNED rather than thrown by `FontResourceSnapshot.resolve`, because a missing face is an
|
|
482
|
-
* ordinary condition — layout falls back and carries on. It extends `Error` so a caller that
|
|
483
|
-
* would rather throw can.
|
|
484
|
-
*/
|
|
485
|
-
declare class FontResolutionError extends Error {
|
|
486
|
-
readonly name = "FontResolutionError";
|
|
487
|
-
readonly request: FontRequest;
|
|
488
|
-
readonly code: FontResolutionErrorCode;
|
|
489
|
-
readonly limit?: number;
|
|
490
|
-
readonly actual?: number;
|
|
491
|
-
readonly diagnostic?: string;
|
|
492
|
-
readonly expectedHash?: string;
|
|
493
|
-
readonly actualHash?: string;
|
|
494
|
-
constructor(code: FontResolutionErrorCode, request: FontRequest, details?: {
|
|
495
|
-
readonly limit?: number;
|
|
496
|
-
readonly actual?: number;
|
|
497
|
-
readonly diagnostic?: string;
|
|
498
|
-
readonly expectedHash?: string;
|
|
499
|
-
readonly actualHash?: string;
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* One face offered to a snapshot, before admission.
|
|
504
|
-
*
|
|
505
|
-
* `hash` is checked against the bytes, so a source cannot claim a face it did not supply. Setting
|
|
506
|
-
* `availability` to `forbidden` declares a face that exists but must not be used, which resolves
|
|
507
|
-
* as a typed refusal rather than as "missing".
|
|
508
|
-
*/
|
|
509
|
-
interface FontResourceDefinition {
|
|
510
|
-
readonly request: FontRequest;
|
|
511
|
-
readonly id: string;
|
|
512
|
-
readonly bytes: Uint8Array;
|
|
513
|
-
readonly hash: string;
|
|
514
|
-
readonly faceIndex: number;
|
|
515
|
-
readonly availability?: 'available' | 'forbidden';
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* A host-declared redirect: requests for `from` resolve to `to`.
|
|
519
|
-
*
|
|
520
|
-
* How a metric-compatible substitute is wired in — a document naming Calibri resolves to Carlito
|
|
521
|
-
* without the document being rewritten.
|
|
522
|
-
*/
|
|
523
|
-
interface DeclaredFontSubstitution {
|
|
524
|
-
readonly from: FontRequest;
|
|
525
|
-
readonly to: FontRequest;
|
|
526
|
-
readonly lineMetrics?: {
|
|
527
|
-
readonly heightEm: number;
|
|
528
|
-
readonly baselineEm: number;
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
/** Whether bytes parse as a usable font, with a diagnostic when they do not. */
|
|
532
|
-
type FontValidationResult = {
|
|
533
|
-
readonly valid: true;
|
|
534
|
-
} | {
|
|
535
|
-
readonly valid: false;
|
|
536
|
-
readonly diagnostic: string;
|
|
537
|
-
};
|
|
538
|
-
/**
|
|
539
|
-
* The injected check that bytes really are a font.
|
|
540
|
-
*
|
|
541
|
-
* Injected because it is the shaper that knows — HarfBuzz can open a face and read its tables,
|
|
542
|
-
* and this layer must not duplicate that judgement. Every byte reaching it is file input.
|
|
543
|
-
*/
|
|
544
|
-
type FontByteValidator = (bytes: Uint8Array, faceIndex: number) => FontValidationResult;
|
|
545
|
-
/**
|
|
546
|
-
* An immutable set of admitted faces, and the only way to reach one.
|
|
547
|
-
*
|
|
548
|
-
* `epoch` identifies the snapshot: fonts change by REPLACING it, never by mutation, so a layout
|
|
549
|
-
* pass holds one snapshot for its whole run and cannot observe a face appearing or vanishing
|
|
550
|
-
* midway.
|
|
551
|
-
*/
|
|
552
|
-
interface FontResourceSnapshot {
|
|
553
|
-
readonly epoch: number;
|
|
554
|
-
/** The admitted face, or a typed refusal. Never throws. */
|
|
555
|
-
resolve(request: FontRequest): ResolvedFont | FontResolutionError;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* How a snapshot is built: which faces, under what limits, validated how.
|
|
559
|
-
*
|
|
560
|
-
* `maxFontBytes` is clamped to {@link HARD_MAX_FONT_BYTES} — a caller may tighten the budget but
|
|
561
|
-
* never widen it past the engine's own ceiling.
|
|
562
|
-
*/
|
|
563
|
-
interface FontResourceSnapshotOptions {
|
|
564
|
-
readonly epoch: number;
|
|
565
|
-
readonly maxFontBytes: number;
|
|
566
|
-
readonly resources: readonly FontResourceDefinition[];
|
|
567
|
-
readonly substitutions?: readonly DeclaredFontSubstitution[];
|
|
568
|
-
readonly validateFont: FontByteValidator;
|
|
569
|
-
readonly instrumentation?: FontResourceInstrumentation;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Optional counters for the operations font admission is expected to do RARELY.
|
|
573
|
-
*
|
|
574
|
-
* Exists so tests can assert the absence of work: hashing and byte-copying a 64 MB face on every
|
|
575
|
-
* resolve would not be visible in output, only in a stalled document, so the checks assert these
|
|
576
|
-
* fire once rather than per call.
|
|
577
|
-
*/
|
|
578
|
-
interface FontResourceInstrumentation {
|
|
579
|
-
readonly onOwnedByteCopy?: () => void;
|
|
580
|
-
readonly onHash?: () => void;
|
|
581
|
-
readonly onTableScan?: () => void;
|
|
582
|
-
readonly onAdmission?: () => void;
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* The canonical string identifying one face request.
|
|
586
|
-
*
|
|
587
|
-
* Case- and whitespace-normalized, so `"Times New Roman"` and `"times new roman"` are one key —
|
|
588
|
-
* a document may name a family either way and both must reach the same face.
|
|
589
|
-
*/
|
|
590
|
-
declare const fontRequestKey: (request: FontRequest) => string;
|
|
591
|
-
/** Bounded minimum sfnt/TTC check; Task 4 supplies full parser-backed validation. */
|
|
592
|
-
declare const boundedStructuralFontValidator: FontByteValidator;
|
|
593
|
-
/**
|
|
594
|
-
* Admit a set of faces and return the immutable snapshot layout resolves against.
|
|
595
|
-
*
|
|
596
|
-
* Admission is the trust boundary for font bytes: counts and sizes are checked against the hard
|
|
597
|
-
* ceilings, each face's hash is re-derived and compared, and the bytes are handed to the injected
|
|
598
|
-
* validator. A face failing any of these is recorded as a typed refusal rather than dropped, so
|
|
599
|
-
* `resolve` can explain itself instead of answering "missing".
|
|
600
|
-
*/
|
|
601
|
-
declare const createFontResourceSnapshot: (options: FontResourceSnapshotOptions) => FontResourceSnapshot;
|
|
602
|
-
|
|
603
|
-
declare const FIXED_POINT: unique symbol;
|
|
604
|
-
/** Fixed-point coordinates are safe integers in units declared by ShapingEnvironment.fixedPointScale. */
|
|
605
|
-
type FixedPoint = number & {
|
|
606
|
-
readonly [FIXED_POINT]: true;
|
|
607
|
-
};
|
|
608
|
-
/**
|
|
609
|
-
* Brand a safe integer as a {@link FixedPoint} coordinate.
|
|
610
|
-
*
|
|
611
|
-
* Fixed point rather than float throughout shaping so that two runs shaped identically compare
|
|
612
|
-
* EQUAL — float accumulation would make the same text measure differently depending on how it was
|
|
613
|
-
* split, and pagination is decided on those measurements.
|
|
614
|
-
*
|
|
615
|
-
* @throws RangeError when the value is not a safe integer.
|
|
616
|
-
*/
|
|
617
|
-
declare const fixedPoint: (value: number) => FixedPoint;
|
|
618
|
-
/** Which way a run reads. The parity projection of its bidi embedding level. */
|
|
619
|
-
type TextDirection = 'ltr' | 'rtl';
|
|
620
|
-
/** How fixed-point conversion breaks ties. Part of the shaping fingerprint. */
|
|
621
|
-
type FixedPointRoundingMode = 'halfAwayFromZero' | 'halfToEven' | 'towardZero';
|
|
622
|
-
/** Which Unicode normalization is applied before shaping, if any. */
|
|
623
|
-
type NormalizationPolicy = 'none' | 'NFC' | 'NFD' | 'NFKC' | 'NFKD';
|
|
624
|
-
/**
|
|
625
|
-
* The shaping library and its exact version.
|
|
626
|
-
*
|
|
627
|
-
* Versioned because a library upgrade can change glyph positioning, and a cached measurement
|
|
628
|
-
* taken under the old one must not be reused under the new one.
|
|
629
|
-
*/
|
|
630
|
-
interface VersionedShapingLibrary {
|
|
631
|
-
readonly name: string;
|
|
632
|
-
readonly version: string;
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Everything that determines how text shapes — the complete input to a
|
|
636
|
-
* {@link ShapingEnvironment}.
|
|
637
|
-
*
|
|
638
|
-
* Exhaustive on purpose. Any field that could change a glyph's position belongs here, because the
|
|
639
|
-
* environment's fingerprint is what decides whether a cached shaped run may be reused.
|
|
640
|
-
*/
|
|
641
|
-
interface ShapingEnvironmentInput {
|
|
642
|
-
readonly font: ResolvedFont;
|
|
643
|
-
readonly variationAxes: Readonly<Record<string, number>>;
|
|
644
|
-
readonly shapingLibrary: VersionedShapingLibrary;
|
|
645
|
-
readonly unicodeDataVersion: string;
|
|
646
|
-
readonly normalization: NormalizationPolicy;
|
|
647
|
-
readonly script: string;
|
|
648
|
-
readonly language: string;
|
|
649
|
-
readonly direction: TextDirection;
|
|
650
|
-
readonly features: Readonly<Record<string, number>>;
|
|
651
|
-
readonly fallbackOrder: readonly ResolvedFont[];
|
|
652
|
-
readonly fixedPointScale: number;
|
|
653
|
-
readonly roundingMode: FixedPointRoundingMode;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* A validated {@link ShapingEnvironmentInput} — build one with `createShapingEnvironment`.
|
|
657
|
-
*
|
|
658
|
-
* Structurally identical to its input, but the nominal distinction is the point: holding one
|
|
659
|
-
* means the tags, axes and fonts inside it have already been checked.
|
|
660
|
-
*/
|
|
661
|
-
interface ShapingEnvironment extends ShapingEnvironmentInput {
|
|
662
|
-
}
|
|
663
|
-
/** One shaping call: the text, its size, its bidi level, and the environment to shape it in. */
|
|
664
|
-
interface ShapeInput {
|
|
665
|
-
readonly text: string;
|
|
666
|
-
readonly fontSizeHalfPoints: number;
|
|
667
|
-
/** Exact UAX #9 embedding/isolate level; direction is its parity projection. */
|
|
668
|
-
readonly bidiLevel: number;
|
|
669
|
-
readonly environment: ShapingEnvironment;
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* One positioned glyph.
|
|
673
|
-
*
|
|
674
|
-
* `id` is a glyph index in its FACE, not a character — a ligature is one glyph spanning several
|
|
675
|
-
* characters, and a single character may produce several glyphs. Use `cluster` to get back to
|
|
676
|
-
* text.
|
|
521
|
+
* `id` is a glyph index in its FACE, not a character — a ligature is one glyph spanning several
|
|
522
|
+
* characters, and a single character may produce several glyphs. Use `cluster` to get back to
|
|
523
|
+
* text.
|
|
677
524
|
*/
|
|
678
525
|
interface ShapedGlyph {
|
|
679
526
|
readonly id: number;
|
|
527
|
+
/** Optional synthetic glyph-size factor; positions and advances already include it. */
|
|
528
|
+
readonly drawScale?: number;
|
|
680
529
|
/** UTF-16 text offset identifying the cluster that produced this glyph. */
|
|
681
530
|
readonly cluster: number;
|
|
682
531
|
/** Pen origin before this glyph's shaping offsets, in fixed-point run coordinates. */
|
|
@@ -749,6 +598,19 @@ interface ShapedRun {
|
|
|
749
598
|
readonly clusters: readonly ShapedCluster[];
|
|
750
599
|
readonly fontSpans: readonly ShapedFontSpan[];
|
|
751
600
|
readonly metrics: ShapedVerticalMetrics;
|
|
601
|
+
/**
|
|
602
|
+
* The same horizontal advances as `glyphs[i].advanceX`, in the same fixed-point units, but
|
|
603
|
+
* BEFORE the per-glyph rounding to the fixed-point grid. Parallel to `glyphs`.
|
|
604
|
+
*
|
|
605
|
+
* Measurement and line breaking read `advanceX` and must keep doing so: pagination is decided
|
|
606
|
+
* on those numbers, and two runs shaped identically have to compare equal. A painter that
|
|
607
|
+
* gives every glyph its own absolute position sums the advances instead, and a sum of ROUNDED
|
|
608
|
+
* advances drifts by up to half a fixed-point unit per glyph — an error that only grows along
|
|
609
|
+
* a line. Summing these and rounding once, at the position actually written, does not.
|
|
610
|
+
*
|
|
611
|
+
* Absent on runs an assembly path cannot state exactly; a reader falls back to `originX`.
|
|
612
|
+
*/
|
|
613
|
+
readonly exactAdvancesX?: readonly number[];
|
|
752
614
|
}
|
|
753
615
|
/**
|
|
754
616
|
* A {@link ShapedRun} reduced to the fields two shaping results must agree on to be considered
|
|
@@ -830,34 +692,497 @@ interface ShapingEnvironmentFingerprintInputs {
|
|
|
830
692
|
* validated. Throws rather than coercing — a bad tag that shapes anyway produces a document that
|
|
831
693
|
* measures wrong everywhere and looks fine.
|
|
832
694
|
*
|
|
833
|
-
* @throws TypeError on a malformed tag, name, or axis value.
|
|
695
|
+
* @throws TypeError on a malformed tag, name, or axis value.
|
|
696
|
+
*/
|
|
697
|
+
declare const createShapingEnvironment: (input: ShapingEnvironmentInput) => ShapingEnvironment;
|
|
698
|
+
/**
|
|
699
|
+
* Reduce an environment to its comparable form, with records sorted so key order cannot affect
|
|
700
|
+
* the result.
|
|
701
|
+
*/
|
|
702
|
+
declare const shapingEnvironmentFingerprintInputs: (input: ShapingEnvironmentInput) => ShapingEnvironmentFingerprintInputs;
|
|
703
|
+
/** Canonical serialization of every shaping variable, including byte hash and provenance. */
|
|
704
|
+
declare const shapingEnvironmentFingerprint: (environment: ShapingEnvironmentInput) => string;
|
|
705
|
+
/**
|
|
706
|
+
* Validate and freeze a shaped run against the environment that produced it.
|
|
707
|
+
*
|
|
708
|
+
* Checks the internal consistency a shaper must satisfy: direction matches the environment,
|
|
709
|
+
* cluster ranges tile the text without gaps or overlap, glyph ranges stay in bounds, font spans
|
|
710
|
+
* cover every glyph, and caret edges are monotonic. A shaper that violates any of these produces
|
|
711
|
+
* a caret that lands in the wrong place, which is far harder to diagnose downstream than here.
|
|
712
|
+
*
|
|
713
|
+
* @throws TypeError when the run and environment disagree, or the run is internally inconsistent.
|
|
714
|
+
*/
|
|
715
|
+
declare const createShapedRun: (input: ShapedRun, environmentInput: ShapingEnvironmentInput) => ShapedRun;
|
|
716
|
+
/**
|
|
717
|
+
* Reduce a shaped run to its comparable form, validating it on the way through.
|
|
718
|
+
*
|
|
719
|
+
* What the D9 determinism oracles compare: two shaping runs of the same text in the same
|
|
720
|
+
* environment must produce byte-identical structures here.
|
|
721
|
+
*/
|
|
722
|
+
declare const shapedRunComparatorInputs: (input: ShapedRun, environment: ShapingEnvironmentInput) => ShapedRunComparatorInputs;
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Whether a paragraph must start a new page (`w:pageBreakBefore`).
|
|
726
|
+
*
|
|
727
|
+
* LAST WINS, like every other toggle read off the cascade (`paragraphKeeps` does the same
|
|
728
|
+
* for `w:keepNext`). An any-wins `.some()` cannot be switched off: a Chapter or Heading style
|
|
729
|
+
* carries `w:pageBreakBefore`, and Word writes `w:val="0"` on the one instance whose author
|
|
730
|
+
* unchecked the box — that paragraph still broke, so the document grew a blank page and every
|
|
731
|
+
* page number after it was wrong.
|
|
732
|
+
*
|
|
733
|
+
* An absent `w:val` is on (§17.17.4), and the off vocabulary is the whole of it — `off` is a
|
|
734
|
+
* spelling too, which {@link isOn} beside this already accepts.
|
|
735
|
+
*/
|
|
736
|
+
declare function paragraphBreaksBefore(props: readonly OoxmlProperty[]): boolean;
|
|
737
|
+
/**
|
|
738
|
+
* Soft ceiling matching the spike's resolved-style limit (31_680 twips ≈ 22"). Beyond
|
|
739
|
+
* that an attacker-authored spacing would push pagination into pathological page counts.
|
|
740
|
+
*/
|
|
741
|
+
declare const MAX_PARAGRAPH_SPACING_PT: number;
|
|
742
|
+
/** Soft ceiling on border width (96 eighths = 12pt). Word's UI tops out well below this. */
|
|
743
|
+
declare const MAX_BORDER_WIDTH_PT = 12;
|
|
744
|
+
/** Soft ceiling on border-to-text gap (`w:space`, already in points). */
|
|
745
|
+
declare const MAX_BORDER_SPACE_PT = 3168;
|
|
746
|
+
/**
|
|
747
|
+
* A paragraph's resolved space before and after, in points.
|
|
748
|
+
*
|
|
749
|
+
* Already collapsed against `w:contextualSpacing`, so adjacent same-style paragraphs that suppress
|
|
750
|
+
* their gap arrive here with it removed rather than leaving that to whoever stacks them.
|
|
751
|
+
*/
|
|
752
|
+
interface ParagraphSpacing {
|
|
753
|
+
/** `w:spacing/@before`, in points. */
|
|
754
|
+
readonly before: number;
|
|
755
|
+
/** `w:spacing/@after`, in points. */
|
|
756
|
+
readonly after: number;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* The gap Word substitutes when `w:beforeAutospacing` / `w:afterAutospacing` is on
|
|
760
|
+
* (ECMA-376 §17.3.1.33, the `w:spacing` clause both attributes belong to).
|
|
761
|
+
*
|
|
762
|
+
* The attribute means "the consumer decides", and the authored `@before` / `@after` beside it
|
|
763
|
+
* is IGNORED rather than used as the value. Word's answer is HTML's default `<p>` margin,
|
|
764
|
+
* 14pt, which is what a document round-tripped through Word's HTML filter carries — and this
|
|
765
|
+
* one is everywhere, because Word writes `w:before="100" w:beforeAutospacing="1"` for it. Reading
|
|
766
|
+
* only the literal 100 twips lays every such paragraph out 9pt tight, which moves page breaks.
|
|
767
|
+
*/
|
|
768
|
+
declare const AUTO_PARAGRAPH_SPACING_PT = 14;
|
|
769
|
+
/**
|
|
770
|
+
* Where a paragraph sits, for the contexts in which Word's auto spacing resolves to 0
|
|
771
|
+
* instead of {@link AUTO_PARAGRAPH_SPACING_PT}.
|
|
772
|
+
*
|
|
773
|
+
* This resolves the interior list-item value. Body layout restores each outer list margin
|
|
774
|
+
* in `resolveListAutoSpacing`, where neighboring blocks are available. A caller that says
|
|
775
|
+
* nothing gets the body answer.
|
|
776
|
+
*
|
|
777
|
+
* A TABLE CELL is not such a context. Measured on a captured control at 10pt, where the
|
|
778
|
+
* line pitch is 11.52pt: the reference puts 26.16pt between two paragraphs in the body with
|
|
779
|
+
* auto spacing on, and the SAME 26.16pt between two paragraphs inside a cell. With an empty
|
|
780
|
+
* paragraph between them it is 52.32, exactly twice. Reading it as 0 in a cell lays every
|
|
781
|
+
* such paragraph 13.92pt tight, which is enough to pull a signature block back onto the
|
|
782
|
+
* previous page. See `.cache/pdf/claude-autospacing/`.
|
|
783
|
+
*/
|
|
784
|
+
interface ParagraphAutoSpacingContext {
|
|
785
|
+
/** The paragraph participates in numbering (`w:numPr`), i.e. it is a list item. */
|
|
786
|
+
readonly inList?: boolean;
|
|
787
|
+
/**
|
|
788
|
+
* The paragraph lives in a table cell. Kept because callers describe position with it and
|
|
789
|
+
* list resolution reads it; it does NOT zero auto spacing.
|
|
790
|
+
*/
|
|
791
|
+
readonly inTableCell?: boolean;
|
|
792
|
+
/** Section grid pitch in points; no grid uses Word's fixed 12pt line unit. */
|
|
793
|
+
readonly lineUnitPt?: number;
|
|
794
|
+
/**
|
|
795
|
+
* `w:doNotUseHTMLParagraphAutoSpacing` is on: automatic spacing outside a list resolves to
|
|
796
|
+
* the fixed 5pt before and 10pt after instead of {@link AUTO_PARAGRAPH_SPACING_PT}.
|
|
797
|
+
*/
|
|
798
|
+
readonly fixedAutoSpacing?: boolean;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Resolved line spacing (`w:spacing/@line` + `@lineRule`, ECMA-376 17.3.1.33).
|
|
802
|
+
*
|
|
803
|
+
* `auto` is the interesting one: `@line` is 240ths of a line, so 240 is single, 360 is
|
|
804
|
+
* one-and-a-half, 480 is double — and Word's own Normal style since 2013 is 259, i.e.
|
|
805
|
+
* 1.08. A document laid out at a flat single spacing is ~8% tight on EVERY line, which
|
|
806
|
+
* moves every page break, so this is not a cosmetic detail.
|
|
807
|
+
*
|
|
808
|
+
* `exact` fixes the line box at `@line` twips and lets tall glyphs clip, the way Word
|
|
809
|
+
* does. `atLeast` uses it as a floor.
|
|
810
|
+
*/
|
|
811
|
+
type LineSpacingRule = 'auto' | 'exact' | 'atLeast';
|
|
812
|
+
/**
|
|
813
|
+
* Resolved line spacing: the rule, and the value it applies.
|
|
814
|
+
*
|
|
815
|
+
* `value` means different things per rule — 240ths of a line under `auto`, points under `exact`
|
|
816
|
+
* and `atLeast` — which is why the two travel together and neither is useful alone.
|
|
817
|
+
*/
|
|
818
|
+
interface ParagraphLineSpacing {
|
|
819
|
+
readonly rule: LineSpacingRule;
|
|
820
|
+
/** `auto`: the 240ths-of-a-line multiplier numerator. Otherwise points. */
|
|
821
|
+
readonly value: number;
|
|
822
|
+
/** Legacy noExtraLineSpacing: retain the natural baseline within an exact-height box. */
|
|
823
|
+
readonly preserveExactBaseline?: true;
|
|
824
|
+
/**
|
|
825
|
+
* Active section line-grid pitch in points (`w:docGrid/@w:linePitch`). Present only when
|
|
826
|
+
* the paragraph snaps to the grid; only with `auto`. See `line-grid.ts`.
|
|
827
|
+
*/
|
|
828
|
+
readonly gridPitch?: number;
|
|
829
|
+
}
|
|
830
|
+
/** Single spacing: what a paragraph that says nothing gets. */
|
|
831
|
+
declare const SINGLE_LINE_SPACING: ParagraphLineSpacing;
|
|
832
|
+
/**
|
|
833
|
+
* One resolved `w:pBdr` edge: its style, colour, thickness and gap.
|
|
834
|
+
*
|
|
835
|
+
* `widthPt` and `spacePt` are already converted and CLAMPED — both come from a file, and an
|
|
836
|
+
* unbounded border width becomes a layout dimension.
|
|
837
|
+
*/
|
|
838
|
+
interface ParagraphBorderEdge {
|
|
839
|
+
/** Authored `ST_Border` value (`single`, `dashed`, …). */
|
|
840
|
+
readonly val: string;
|
|
841
|
+
/** RRGGBB, or null when auto/missing (paint defaults to black). */
|
|
842
|
+
readonly color: string | null;
|
|
843
|
+
/** Border thickness in points (`w:sz` is eighths of a point). */
|
|
844
|
+
readonly widthPt: number;
|
|
845
|
+
/** Gap from text to the rule, in points (`w:space`). */
|
|
846
|
+
readonly spacePt: number;
|
|
847
|
+
/**
|
|
848
|
+
* `w:shadow` — Word offsets a drop shadow behind the rule.
|
|
849
|
+
*
|
|
850
|
+
* Present only when authored true, so an edge that says nothing keeps the shape earlier
|
|
851
|
+
* fixtures assert. Resolved and carried; drawing it is deferred.
|
|
852
|
+
*/
|
|
853
|
+
readonly shadow?: true;
|
|
854
|
+
}
|
|
855
|
+
/** The six `CT_PBdr` children, in schema order (ECMA-376 §17.3.1.24). */
|
|
856
|
+
declare const PARAGRAPH_BORDER_SIDES: readonly ["top", "left", "bottom", "right", "between", "bar"];
|
|
857
|
+
/**
|
|
858
|
+
* Which of the six `CT_PBdr` edges.
|
|
859
|
+
*
|
|
860
|
+
* Four are physical box edges; `between` and `bar` are group-relative, drawn only where
|
|
861
|
+
* consecutive paragraphs share a border definition.
|
|
862
|
+
*/
|
|
863
|
+
type ParagraphBorderSide = (typeof PARAGRAPH_BORDER_SIDES)[number];
|
|
864
|
+
/**
|
|
865
|
+
* A paragraph's resolved `w:pBdr` (ECMA-376 §17.3.1.24).
|
|
866
|
+
*
|
|
867
|
+
* `top`/`left`/`bottom`/`right` are the four physical edges of the box. The other two are
|
|
868
|
+
* group-relative: `between` draws at a boundary INSIDE a run of consecutive paragraphs whose
|
|
869
|
+
* border settings are identical, and `bar` is the vertical change-bar rule beside the
|
|
870
|
+
* paragraph, drawn whether or not the paragraph groups with its neighbours.
|
|
871
|
+
*/
|
|
872
|
+
interface ParagraphBorders {
|
|
873
|
+
readonly top?: ParagraphBorderEdge;
|
|
874
|
+
readonly left?: ParagraphBorderEdge;
|
|
875
|
+
readonly bottom?: ParagraphBorderEdge;
|
|
876
|
+
readonly right?: ParagraphBorderEdge;
|
|
877
|
+
readonly between?: ParagraphBorderEdge;
|
|
878
|
+
readonly bar?: ParagraphBorderEdge;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Resolve `w:spacing` before/after from flat paragraph properties.
|
|
882
|
+
*
|
|
883
|
+
* Line spacing (`w:line` / `w:lineRule`) is a separate concern — it changes measured line
|
|
884
|
+
* height, not the gap between paragraphs — and is not resolved here.
|
|
885
|
+
*
|
|
886
|
+
* `w:beforeAutospacing` / `w:afterAutospacing` REPLACE the authored measurement on their own
|
|
887
|
+
* side rather than adding to it; see {@link AUTO_PARAGRAPH_SPACING_PT}.
|
|
888
|
+
*/
|
|
889
|
+
declare function paragraphSpacing(props: readonly OoxmlProperty[], context?: ParagraphAutoSpacingContext): ParagraphSpacing;
|
|
890
|
+
/**
|
|
891
|
+
* Resolve `w:line` / `w:lineRule` from flat paragraph properties.
|
|
892
|
+
*
|
|
893
|
+
* Merged per attribute for the same reason as before/after: `w:spacing` is one element
|
|
894
|
+
* carrying independent attributes, and a style that states only `@line` must not reset the
|
|
895
|
+
* rule an earlier entry in the cascade set.
|
|
896
|
+
*/
|
|
897
|
+
declare function paragraphLineSpacing(props: readonly OoxmlProperty[]): ParagraphLineSpacing;
|
|
898
|
+
/**
|
|
899
|
+
* Apply resolved line spacing to a line's natural (glyph-derived) box.
|
|
900
|
+
*
|
|
901
|
+
* `auto` places its extra BELOW the line — the last line's multiple spacing still separates
|
|
902
|
+
* it from the next paragraph. Putting that delta above inverted cover-page rhythm:
|
|
903
|
+
* `w:line="460"` on "between" opened a large gap above the word and almost none before
|
|
904
|
+
* "MERIDIAN".
|
|
905
|
+
*
|
|
906
|
+
* `atLeast` does the OPPOSITE: the box grows upward and the glyphs sit on its floor. A
|
|
907
|
+
* captured control settles it — Times New Roman 12 pt at `w:line="360" w:lineRule="atLeast"`
|
|
908
|
+
* puts the reference's baseline at 15.36 pt, which is `18 - descent` snapped to the device
|
|
909
|
+
* grid, where treating it like `auto` leaves the baseline at 11.28 and the text 4.08 pt too
|
|
910
|
+
* high. `w:line="240" atLeast` is below the natural line, falls through to the natural
|
|
911
|
+
* height, and is unaffected. See `.cache/pdf/claude-linerule/`.
|
|
912
|
+
*
|
|
913
|
+
* Exact-height boxes place their baseline at 80% of the height; the legacy
|
|
914
|
+
* noExtraLineSpacing switch instead preserves the face baseline within the box.
|
|
915
|
+
*
|
|
916
|
+
* An `auto` line of a snapping paragraph under an active line grid takes whole pitches
|
|
917
|
+
* instead ({@link gridLineBox}); `trailing` then names the extra below the grid line.
|
|
918
|
+
*/
|
|
919
|
+
declare function applyLineSpacing(spacing: ParagraphLineSpacing, naturalHeight: number, naturalBaseline: number): {
|
|
920
|
+
height: number;
|
|
921
|
+
baseline: number;
|
|
922
|
+
trailing?: number;
|
|
923
|
+
};
|
|
924
|
+
/**
|
|
925
|
+
* `w:contextualSpacing` (17.3.1.9): drop before/after between paragraphs of the SAME
|
|
926
|
+
* style. Word's built-in `ListParagraph` sets it, so every list authored in Word gets a
|
|
927
|
+
* paragraph gap between items without this.
|
|
928
|
+
*/
|
|
929
|
+
declare function paragraphContextualSpacing(props: readonly OoxmlProperty[]): boolean;
|
|
930
|
+
/**
|
|
931
|
+
* Resolve `w:pBdr` from the paragraph-properties node.
|
|
932
|
+
*
|
|
933
|
+
* Nested — every edge is a child of `pBdr`, not an attribute — so this reads the typed tree
|
|
934
|
+
* rather than the flattened `OoxmlProperty[]` bag `propertiesOf` builds for leaf props.
|
|
935
|
+
*/
|
|
936
|
+
declare function paragraphBorders(pPr: OoxmlNode | undefined): ParagraphBorders;
|
|
937
|
+
/**
|
|
938
|
+
* `w:pBdr` after the style cascade: a later `w:pBdr` replaces an earlier one WHOLESALE.
|
|
939
|
+
*
|
|
940
|
+
* Word does not merge edges across the cascade. A style that states only `w:bottom` discards
|
|
941
|
+
* the box its `basedOn` ancestor declared, so folding edge by edge would leave a lone
|
|
942
|
+
* underline surrounded by a box no one authored. Absence inherits; `nil`/`none` clear.
|
|
943
|
+
*/
|
|
944
|
+
declare function cascadedParagraphBorders(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorders;
|
|
945
|
+
/**
|
|
946
|
+
* Visual stroke thickness layout publishes for one edge (points).
|
|
947
|
+
*
|
|
948
|
+
* Compound `ST_Border` values (`double`, …) use the shared inflated band so a thin
|
|
949
|
+
* `w:sz="3"` double still occupies a visible double-line box — matching table borders.
|
|
950
|
+
*/
|
|
951
|
+
declare function paragraphBorderStrokeWidthPt(edge: ParagraphBorderEdge): number;
|
|
952
|
+
/**
|
|
953
|
+
* Extent one border edge occupies away from the text it decorates: gap plus rule, in points.
|
|
954
|
+
*
|
|
955
|
+
* Vertically that is flow height — a top rule pushes the first line down, a bottom rule holds
|
|
956
|
+
* the page open below the last one — so pagination has to see it. Horizontally it is
|
|
957
|
+
* publish-only: Word draws left/right paragraph rules OUTSIDE the text column and never
|
|
958
|
+
* re-breaks the lines, which is why adding a box to a paragraph in Word does not reflow it.
|
|
959
|
+
*/
|
|
960
|
+
declare function paragraphBorderExtentPt(edge: ParagraphBorderEdge | undefined): number;
|
|
961
|
+
/** Vertical extent a bottom border adds below the last line (gap + rule). */
|
|
962
|
+
declare function bottomBorderExtentPt(edge: ParagraphBorderEdge | undefined): number;
|
|
963
|
+
/**
|
|
964
|
+
* Identity of a paragraph's border set, for the `w:between` group rule.
|
|
965
|
+
*
|
|
966
|
+
* Word treats consecutive paragraphs whose border settings are IDENTICAL as ONE bordered
|
|
967
|
+
* block: the top rule draws above the first, the bottom rule below the last, and each
|
|
968
|
+
* interior boundary gets `w:between` or nothing (§17.3.1.24). That is why applying a box to
|
|
969
|
+
* three selected paragraphs in Word draws one box and not three.
|
|
970
|
+
*
|
|
971
|
+
* Empty string means "no borders", which never groups with anything.
|
|
972
|
+
*/
|
|
973
|
+
declare function paragraphBordersFingerprint(borders: ParagraphBorders): string;
|
|
974
|
+
/**
|
|
975
|
+
* Gap to insert before a paragraph once the previous paragraph's `after` is already in the
|
|
976
|
+
* flow cursor — Word takes the larger of the two rather than summing them.
|
|
977
|
+
*/
|
|
978
|
+
declare function collapsedSpaceBefore(before: number, previousAfter: number): number;
|
|
979
|
+
/**
|
|
980
|
+
* Applied before-spacing for placement (Word 2013+ / compat mode 15).
|
|
981
|
+
*
|
|
982
|
+
* Adjacent before/after still collapse to the larger gap, but before is dropped entirely when
|
|
983
|
+
* the paragraph naturally moves to the top of a page mid-section. An explicit page-break-before
|
|
984
|
+
* paragraph, or the first paragraph of a document/section, retains before. Callers publish this
|
|
985
|
+
* applied value on the fragment so shading, borders,
|
|
986
|
+
* selection, and paint share one geometry.
|
|
987
|
+
*/
|
|
988
|
+
declare function appliedSpaceBefore(before: number, previousAfter: number, atTopOfPage: boolean, preserveAtPageStart: boolean): number;
|
|
989
|
+
|
|
990
|
+
/** `w:vertAlign` — script position, which also scales the run's effective size. */
|
|
991
|
+
type VerticalAlign = 'baseline' | 'superscript' | 'subscript';
|
|
992
|
+
/** A resolved underline: its variant, and its colour when it does not follow the text. */
|
|
993
|
+
interface ResolvedUnderline {
|
|
994
|
+
/** The authored `ST_Underline` variant. */
|
|
995
|
+
readonly variant: string;
|
|
996
|
+
/** RRGGBB, or null when the underline follows the text colour. */
|
|
997
|
+
readonly color: string | null;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* A run's character properties after the full cascade, in the units layout works in.
|
|
1001
|
+
*
|
|
1002
|
+
* Points rather than half-points, RRGGBB rather than theme references — everything already
|
|
1003
|
+
* resolved, so measurement and paint never re-run the cascade per glyph.
|
|
1004
|
+
*/
|
|
1005
|
+
interface ResolvedRunStyle {
|
|
1006
|
+
readonly fontFamily: string | null;
|
|
1007
|
+
/**
|
|
1008
|
+
* The `eastAsia` slot's typeface (`w:rFonts w:eastAsia`/`w:eastAsiaTheme`), or null when
|
|
1009
|
+
* no level authors one.
|
|
1010
|
+
*
|
|
1011
|
+
* OOXML picks a run's face per SCRIPT: `fontFamily` covers the ascii/hAnsi slots, and
|
|
1012
|
+
* ideographic/kana/hangul text resolves through this one instead. Kept beside — not
|
|
1013
|
+
* folded into — `fontFamily`, because a mixed run needs both at once and the split into
|
|
1014
|
+
* slot-homogeneous pieces happens downstream (`piecesOfParagraph`).
|
|
1015
|
+
*/
|
|
1016
|
+
readonly fontFamilyEastAsia: string | null;
|
|
1017
|
+
/**
|
|
1018
|
+
* Present only on a `w:rtl` or `w:cs` run: the LATIN lane that the complex-script values
|
|
1019
|
+
* replaced in `fontFamily`, `fontSizePt`, `bold` and `italic`. Nothing draws with it; it
|
|
1020
|
+
* lets a copy of the formatting (the format painter) write each lane back separately.
|
|
1021
|
+
*/
|
|
1022
|
+
readonly latinLane?: {
|
|
1023
|
+
readonly fontFamily: string | null;
|
|
1024
|
+
readonly fontFamilyEastAsia: string | null;
|
|
1025
|
+
readonly fontSizePt: number;
|
|
1026
|
+
readonly bold: boolean;
|
|
1027
|
+
readonly italic: boolean;
|
|
1028
|
+
};
|
|
1029
|
+
/**
|
|
1030
|
+
* The complex-script lane as the cascade states it, on every resolved run: the values a
|
|
1031
|
+
* `w:rtl` or `w:cs` run draws with, and what a copy of the formatting writes to `w:bCs`,
|
|
1032
|
+
* `w:iCs`, `w:szCs` and `w:rFonts/@w:cs`. `fontFamily` is null when no level names a face.
|
|
1033
|
+
*/
|
|
1034
|
+
readonly complexLane?: {
|
|
1035
|
+
readonly fontFamily: string | null;
|
|
1036
|
+
readonly fontSizePt: number;
|
|
1037
|
+
readonly bold: boolean;
|
|
1038
|
+
readonly italic: boolean;
|
|
1039
|
+
};
|
|
1040
|
+
/** Script and paragraph-resolved direction for shaping and visual placement. */
|
|
1041
|
+
readonly shaping?: {
|
|
1042
|
+
readonly script: string;
|
|
1043
|
+
readonly direction: 'ltr' | 'rtl';
|
|
1044
|
+
readonly level: number;
|
|
1045
|
+
readonly baseLevel: number;
|
|
1046
|
+
/** Resolved w:rtl context; absent when authored Unicode controls govern the paragraph. */
|
|
1047
|
+
readonly runDirection?: 'ltr' | 'rtl';
|
|
1048
|
+
readonly wordSpacingPt?: number;
|
|
1049
|
+
/** Neighbouring text a joining script reads across a formatting-run boundary. */
|
|
1050
|
+
readonly context?: ShapingContext;
|
|
1051
|
+
};
|
|
1052
|
+
/** Points. `w:sz` is half-points, so 22 becomes 11. */
|
|
1053
|
+
readonly fontSizePt: number;
|
|
1054
|
+
/** RRGGBB, or null for the inherited/automatic colour. */
|
|
1055
|
+
readonly color: string | null;
|
|
1056
|
+
readonly bold: boolean;
|
|
1057
|
+
/** Opaque solid `w14:textOutline`, in points; paint only, never a font-weight change. */
|
|
1058
|
+
readonly textOutline?: {
|
|
1059
|
+
readonly widthPt: number;
|
|
1060
|
+
readonly color: string;
|
|
1061
|
+
};
|
|
1062
|
+
/** Character border; adjacent runs with matching edges share one outline. */
|
|
1063
|
+
readonly border?: ParagraphBorderEdge;
|
|
1064
|
+
readonly italic: boolean;
|
|
1065
|
+
readonly underline: ResolvedUnderline | null;
|
|
1066
|
+
readonly strike: boolean;
|
|
1067
|
+
readonly doubleStrike: boolean;
|
|
1068
|
+
/** An `ST_HighlightColor` name, or null. */
|
|
1069
|
+
readonly highlight: string | null;
|
|
1070
|
+
/**
|
|
1071
|
+
* Character shading fill (`w:rPr/w:shd`), validated RRGGBB, or null.
|
|
1072
|
+
*
|
|
1073
|
+
* Paint applies this as the glyph-box background; a recognised highlight overrides it.
|
|
1074
|
+
* Measurement ignores shading.
|
|
1075
|
+
*/
|
|
1076
|
+
readonly shading: string | null;
|
|
1077
|
+
readonly verticalAlign: VerticalAlign;
|
|
1078
|
+
/** `w:position`, in points. Positive raises the baseline. */
|
|
1079
|
+
readonly baselineShiftPt: number;
|
|
1080
|
+
readonly caps: boolean;
|
|
1081
|
+
readonly smallCaps: boolean;
|
|
1082
|
+
/** `w:spacing`, in points. Added to every advance. */
|
|
1083
|
+
readonly characterSpacingPt: number;
|
|
1084
|
+
/** `w:w`, as a percentage. 100 is unscaled. */
|
|
1085
|
+
readonly horizontalScalePercent: number;
|
|
1086
|
+
/** `w:kern`, in points: the size at or above which kerning applies. */
|
|
1087
|
+
readonly kerningMinPt: number;
|
|
1088
|
+
/** Explicit kerning selection; a zero threshold disables it. Absent uses a positive threshold. */
|
|
1089
|
+
readonly kerningEnabled?: boolean;
|
|
1090
|
+
/** Resolved optional OpenType substitutions; required script ligatures are independent. */
|
|
1091
|
+
readonly ligatures?: {
|
|
1092
|
+
readonly standard: boolean;
|
|
1093
|
+
readonly contextual: boolean;
|
|
1094
|
+
readonly historical: boolean;
|
|
1095
|
+
readonly discretionary: boolean;
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* `w:vanish` (ECMA-376 §17.3.2.45): the run is hidden text.
|
|
1099
|
+
*
|
|
1100
|
+
* Word does not draw it AND does not paginate it — hidden index or comment text takes no
|
|
1101
|
+
* space at all. So this cannot be a paint-time opacity: a hidden run that is still measured
|
|
1102
|
+
* pushes every following line, and every following page break, to the wrong place. Layout
|
|
1103
|
+
* drops the content instead (see `piecesOfParagraph`).
|
|
1104
|
+
*
|
|
1105
|
+
* `w:specVanish` (§17.3.2.36) is a different property — an always-hidden paragraph mark on
|
|
1106
|
+
* a heading — and never sets this.
|
|
1107
|
+
*/
|
|
1108
|
+
readonly hidden: boolean;
|
|
1109
|
+
}
|
|
1110
|
+
/** The style a run inherits when it authors nothing. */
|
|
1111
|
+
declare const DEFAULT_RUN_STYLE: ResolvedRunStyle;
|
|
1112
|
+
/**
|
|
1113
|
+
* The theme part's typefaces, for resolving `w:rFonts` theme attributes.
|
|
1114
|
+
*
|
|
1115
|
+
* Structurally identical to the binding lane's `DocumentThemeFonts` and assignable from it.
|
|
1116
|
+
* Declared here so this lane reads validated strings rather than the theme tree.
|
|
1117
|
+
*/
|
|
1118
|
+
interface ThemeFonts {
|
|
1119
|
+
/** `a:majorFont` latin typeface — headings. */
|
|
1120
|
+
readonly major: string | null;
|
|
1121
|
+
/** `a:minorFont` latin typeface — body text. */
|
|
1122
|
+
readonly minor: string | null;
|
|
1123
|
+
/** `a:majorFont` east asian typeface (`a:ea`) — headings. Optional for back-compat. */
|
|
1124
|
+
readonly majorEastAsia?: string | null;
|
|
1125
|
+
/** `a:minorFont` east asian typeface (`a:ea`) — body text. Optional for back-compat. */
|
|
1126
|
+
readonly minorEastAsia?: string | null;
|
|
1127
|
+
/** Complex-script heading and body theme faces. */
|
|
1128
|
+
readonly majorBidi?: string | null;
|
|
1129
|
+
readonly minorBidi?: string | null;
|
|
1130
|
+
/** Language-specific theme faces, keyed by ISO 15924 script. */
|
|
1131
|
+
readonly majorSupplemental?: Readonly<Record<string, string>>;
|
|
1132
|
+
readonly minorSupplemental?: Readonly<Record<string, string>>;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Resolve one run's direct formatting.
|
|
1136
|
+
*
|
|
1137
|
+
* Unrecognised values are DROPPED rather than guessed: a `w:sz` of `"large"` leaves the
|
|
1138
|
+
* default size rather than inventing one, because a wrong measurement moves every glyph
|
|
1139
|
+
* after it and a missing one is visible immediately.
|
|
1140
|
+
*
|
|
1141
|
+
* `themeFonts` resolves `w:rFonts` theme references. Absent, a theme-only `rFonts` leaves
|
|
1142
|
+
* the family inherited — which is what every run of a theme-fonted document does, so the
|
|
1143
|
+
* whole document falls back to the surface default face.
|
|
834
1144
|
*/
|
|
835
|
-
declare
|
|
1145
|
+
declare function resolveRunStyle(props: readonly OoxmlProperty[], themeFonts?: ThemeFonts): ResolvedRunStyle;
|
|
1146
|
+
/** The text as it is DRAWN, after case transforms. Measurement must use this, not the source. */
|
|
1147
|
+
declare function displayText(text: string, style: ResolvedRunStyle): string;
|
|
1148
|
+
/** Measure run text the way layout breaks lines and paints glyphs (caps/small-caps aware). */
|
|
1149
|
+
declare function measureDisplayText(text: string, style: ResolvedRunStyle, measurer: TextMeasurer): number;
|
|
1150
|
+
/** Whether two resolved styles are identical, for span merging and cache keys. */
|
|
1151
|
+
declare function runStylesEqual(a: ResolvedRunStyle, b: ResolvedRunStyle): boolean;
|
|
836
1152
|
/**
|
|
837
|
-
*
|
|
838
|
-
*
|
|
1153
|
+
* How far a run's glyphs are lifted off the line's baseline, in points. Positive is up.
|
|
1154
|
+
*
|
|
1155
|
+
* Super and subscript move the GLYPHS without moving the run's box, so the box keeps tiling
|
|
1156
|
+
* the line and the selection band stays continuous. Anything drawing at the glyphs — the
|
|
1157
|
+
* painter, and the caret — has to apply this itself, and from one place, or the two drift.
|
|
839
1158
|
*/
|
|
840
|
-
declare
|
|
841
|
-
/** Canonical serialization of every shaping variable, including byte hash and provenance. */
|
|
842
|
-
declare const shapingEnvironmentFingerprint: (environment: ShapingEnvironmentInput) => string;
|
|
1159
|
+
declare function baselineShiftPtOf(style: ResolvedRunStyle): number;
|
|
843
1160
|
/**
|
|
844
|
-
*
|
|
1161
|
+
* Shared glyph-size factor for baseline, superscript, and subscript text.
|
|
845
1162
|
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
* @throws TypeError when the run and environment disagree, or the run is internally inconsistent.
|
|
1163
|
+
* The 65% script scale follows the common Word font metrics (Arial, Calibri, Times New
|
|
1164
|
+
* Roman) and improves the saved Word reference at 10pt and 11pt. It is a uniform policy;
|
|
1165
|
+
* per-face script metrics and Word's output-device rounding are not modeled here.
|
|
1166
|
+
* Layout, surface paint, and exporters must apply the same factor to avoid caret drift.
|
|
1167
|
+
* @public
|
|
852
1168
|
*/
|
|
853
|
-
declare
|
|
1169
|
+
declare function glyphSizeFactorOf(style: ResolvedRunStyle): number;
|
|
1170
|
+
|
|
854
1171
|
/**
|
|
855
|
-
*
|
|
1172
|
+
* UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved
|
|
1173
|
+
* within.
|
|
856
1174
|
*
|
|
857
|
-
*
|
|
858
|
-
*
|
|
1175
|
+
* The level is EXACT, not a direction: its parity gives direction, but the numeric value is what
|
|
1176
|
+
* reordering needs, and collapsing it early loses nested isolates.
|
|
859
1177
|
*/
|
|
860
|
-
|
|
1178
|
+
interface BidiEmbeddingLevels {
|
|
1179
|
+
readonly levels: Uint8Array;
|
|
1180
|
+
readonly paragraphs: readonly {
|
|
1181
|
+
readonly start: number;
|
|
1182
|
+
readonly end: number;
|
|
1183
|
+
readonly level: number;
|
|
1184
|
+
}[];
|
|
1185
|
+
}
|
|
861
1186
|
|
|
862
1187
|
/**
|
|
863
1188
|
* Which `w:rFonts` slot a character resolves its face through.
|
|
@@ -911,6 +1236,27 @@ declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, e
|
|
|
911
1236
|
*/
|
|
912
1237
|
declare function styleForFontSlot(style: ResolvedRunStyle, slot: FontSlot | undefined): ResolvedRunStyle;
|
|
913
1238
|
|
|
1239
|
+
type AllowlistedPageField = 'PAGE' | 'NUMPAGES' | 'SECTIONPAGES';
|
|
1240
|
+
/**
|
|
1241
|
+
* Which allowlisted page fields a header/footer story actually contains.
|
|
1242
|
+
*
|
|
1243
|
+
* Drives layout reuse: no fields → one baseline; NUMPAGES only → one layout per page count;
|
|
1244
|
+
* SECTIONPAGES only → one layout per section page count; PAGE (alone or combined) → per
|
|
1245
|
+
* distinct evaluated values with a bounded cache. Counts both complex markers and
|
|
1246
|
+
* `w:fldSimple` (including an allowlisted page field nested inside a non-page simple field).
|
|
1247
|
+
*/
|
|
1248
|
+
interface StoryPageFieldNeeds {
|
|
1249
|
+
readonly hasPage: boolean;
|
|
1250
|
+
readonly hasNumPages: boolean;
|
|
1251
|
+
readonly hasSectionPages: boolean;
|
|
1252
|
+
/**
|
|
1253
|
+
* The story places something by the physical sheet's parity: a header frame aligned
|
|
1254
|
+
* `inside` or `outside`. Set by the header/footer layout, never by field detection. The
|
|
1255
|
+
* story then lays out once per sheet parity, with or without a page field.
|
|
1256
|
+
*/
|
|
1257
|
+
readonly hasPageParity?: boolean;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
914
1260
|
/**
|
|
915
1261
|
* One parsed `HYPERLINK` instruction: the raw, unsanitized pieces.
|
|
916
1262
|
*
|
|
@@ -1035,21 +1381,6 @@ interface RefNoteParts {
|
|
|
1035
1381
|
*/
|
|
1036
1382
|
declare function resolveStoryRefFields(blocks: readonly OoxmlElement[], listItems: ReadonlyMap<string, ResolvedListItem> | undefined, notes?: RefNoteParts): RefFieldContext | null;
|
|
1037
1383
|
|
|
1038
|
-
type AllowlistedPageField = 'PAGE' | 'NUMPAGES' | 'SECTIONPAGES';
|
|
1039
|
-
/**
|
|
1040
|
-
* Which allowlisted page fields a header/footer story actually contains.
|
|
1041
|
-
*
|
|
1042
|
-
* Drives layout reuse: no fields → one baseline; NUMPAGES only → one layout per page count;
|
|
1043
|
-
* SECTIONPAGES only → one layout per section page count; PAGE (alone or combined) → per
|
|
1044
|
-
* distinct evaluated values with a bounded cache. Counts both complex markers and
|
|
1045
|
-
* `w:fldSimple` (including an allowlisted page field nested inside a non-page simple field).
|
|
1046
|
-
*/
|
|
1047
|
-
interface StoryPageFieldNeeds {
|
|
1048
|
-
readonly hasPage: boolean;
|
|
1049
|
-
readonly hasNumPages: boolean;
|
|
1050
|
-
readonly hasSectionPages: boolean;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
1384
|
/**
|
|
1054
1385
|
* What a piece says about the FIELD result it came from, for Word's shading.
|
|
1055
1386
|
*
|
|
@@ -1143,236 +1474,126 @@ interface CjkTypographySettings {
|
|
|
1143
1474
|
readonly after: Readonly<Record<string, string>>;
|
|
1144
1475
|
}
|
|
1145
1476
|
|
|
1477
|
+
/** Soft ceiling matching Word's practical custom-tab UI limit. */
|
|
1478
|
+
declare const MAX_TAB_STOPS = 64;
|
|
1146
1479
|
/**
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
* LAST WINS, like every other toggle read off the cascade (`paragraphKeeps` does the same
|
|
1150
|
-
* for `w:keepNext`). An any-wins `.some()` cannot be switched off: a Chapter or Heading style
|
|
1151
|
-
* carries `w:pageBreakBefore`, and Word writes `w:val="0"` on the one instance whose author
|
|
1152
|
-
* unchecked the box — that paragraph still broke, so the document grew a blank page and every
|
|
1153
|
-
* page number after it was wrong.
|
|
1154
|
-
*
|
|
1155
|
-
* An absent `w:val` is on (§17.17.4), and the off vocabulary is the whole of it — `off` is a
|
|
1156
|
-
* spelling too, which {@link isOn} beside this already accepts.
|
|
1157
|
-
*/
|
|
1158
|
-
declare function paragraphBreaksBefore(props: readonly OoxmlProperty[]): boolean;
|
|
1159
|
-
/**
|
|
1160
|
-
* Soft ceiling matching the spike's resolved-style limit (31_680 twips ≈ 22"). Beyond
|
|
1161
|
-
* that an attacker-authored spacing would push pagination into pathological page counts.
|
|
1162
|
-
*/
|
|
1163
|
-
declare const MAX_PARAGRAPH_SPACING_PT: number;
|
|
1164
|
-
/** Soft ceiling on border width (96 eighths = 12pt). Word's UI tops out well below this. */
|
|
1165
|
-
declare const MAX_BORDER_WIDTH_PT = 12;
|
|
1166
|
-
/** Soft ceiling on border-to-text gap (`w:space`, already in points). */
|
|
1167
|
-
declare const MAX_BORDER_SPACE_PT = 3168;
|
|
1168
|
-
/**
|
|
1169
|
-
* A paragraph's resolved space before and after, in points.
|
|
1170
|
-
*
|
|
1171
|
-
* Already collapsed against `w:contextualSpacing`, so adjacent same-style paragraphs that suppress
|
|
1172
|
-
* their gap arrive here with it removed rather than leaving that to whoever stacks them.
|
|
1173
|
-
*/
|
|
1174
|
-
interface ParagraphSpacing {
|
|
1175
|
-
/** `w:spacing/@before`, in points. */
|
|
1176
|
-
readonly before: number;
|
|
1177
|
-
/** `w:spacing/@after`, in points. */
|
|
1178
|
-
readonly after: number;
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* The gap Word substitutes when `w:beforeAutospacing` / `w:afterAutospacing` is on
|
|
1182
|
-
* (ECMA-376 §17.3.1.33, the `w:spacing` clause both attributes belong to).
|
|
1183
|
-
*
|
|
1184
|
-
* The attribute means "the consumer decides", and the authored `@before` / `@after` beside it
|
|
1185
|
-
* is IGNORED rather than used as the value. Word's answer is HTML's default `<p>` margin,
|
|
1186
|
-
* 14pt, which is what a document round-tripped through Word's HTML filter carries — and this
|
|
1187
|
-
* one is everywhere, because Word writes `w:before="100" w:beforeAutospacing="1"` for it. Reading
|
|
1188
|
-
* only the literal 100 twips lays every such paragraph out 9pt tight, which moves page breaks.
|
|
1189
|
-
*/
|
|
1190
|
-
declare const AUTO_PARAGRAPH_SPACING_PT = 14;
|
|
1191
|
-
/**
|
|
1192
|
-
* Where a paragraph sits, for the contexts in which Word's auto spacing resolves to 0
|
|
1193
|
-
* instead of {@link AUTO_PARAGRAPH_SPACING_PT}.
|
|
1194
|
-
*
|
|
1195
|
-
* This resolves the interior list-item or table-cell value. Body layout restores each
|
|
1196
|
-
* outer list margin in `resolveListAutoSpacing`, where neighboring blocks are available.
|
|
1197
|
-
* A caller that says nothing gets the body answer.
|
|
1198
|
-
*/
|
|
1199
|
-
interface ParagraphAutoSpacingContext {
|
|
1200
|
-
/** The paragraph participates in numbering (`w:numPr`), i.e. it is a list item. */
|
|
1201
|
-
readonly inList?: boolean;
|
|
1202
|
-
/** The paragraph lives in a table cell. */
|
|
1203
|
-
readonly inTableCell?: boolean;
|
|
1204
|
-
/** Section grid pitch in points; no grid uses Word's fixed 12pt line unit. */
|
|
1205
|
-
readonly lineUnitPt?: number;
|
|
1206
|
-
}
|
|
1207
|
-
/**
|
|
1208
|
-
* Resolved line spacing (`w:spacing/@line` + `@lineRule`, ECMA-376 17.3.1.33).
|
|
1209
|
-
*
|
|
1210
|
-
* `auto` is the interesting one: `@line` is 240ths of a line, so 240 is single, 360 is
|
|
1211
|
-
* one-and-a-half, 480 is double — and Word's own Normal style since 2013 is 259, i.e.
|
|
1212
|
-
* 1.08. A document laid out at a flat single spacing is ~8% tight on EVERY line, which
|
|
1213
|
-
* moves every page break, so this is not a cosmetic detail.
|
|
1214
|
-
*
|
|
1215
|
-
* `exact` fixes the line box at `@line` twips and lets tall glyphs clip, the way Word
|
|
1216
|
-
* does. `atLeast` uses it as a floor.
|
|
1217
|
-
*/
|
|
1218
|
-
type LineSpacingRule = 'auto' | 'exact' | 'atLeast';
|
|
1219
|
-
/**
|
|
1220
|
-
* Resolved line spacing: the rule, and the value it applies.
|
|
1221
|
-
*
|
|
1222
|
-
* `value` means different things per rule — 240ths of a line under `auto`, points under `exact`
|
|
1223
|
-
* and `atLeast` — which is why the two travel together and neither is useful alone.
|
|
1224
|
-
*/
|
|
1225
|
-
interface ParagraphLineSpacing {
|
|
1226
|
-
readonly rule: LineSpacingRule;
|
|
1227
|
-
/** `auto`: the 240ths-of-a-line multiplier numerator. Otherwise points. */
|
|
1228
|
-
readonly value: number;
|
|
1229
|
-
}
|
|
1230
|
-
/** Single spacing: what a paragraph that says nothing gets. */
|
|
1231
|
-
declare const SINGLE_LINE_SPACING: ParagraphLineSpacing;
|
|
1232
|
-
/**
|
|
1233
|
-
* One resolved `w:pBdr` edge: its style, colour, thickness and gap.
|
|
1234
|
-
*
|
|
1235
|
-
* `widthPt` and `spacePt` are already converted and CLAMPED — both come from a file, and an
|
|
1236
|
-
* unbounded border width becomes a layout dimension.
|
|
1237
|
-
*/
|
|
1238
|
-
interface ParagraphBorderEdge {
|
|
1239
|
-
/** Authored `ST_Border` value (`single`, `dashed`, …). */
|
|
1240
|
-
readonly val: string;
|
|
1241
|
-
/** RRGGBB, or null when auto/missing (paint defaults to black). */
|
|
1242
|
-
readonly color: string | null;
|
|
1243
|
-
/** Border thickness in points (`w:sz` is eighths of a point). */
|
|
1244
|
-
readonly widthPt: number;
|
|
1245
|
-
/** Gap from text to the rule, in points (`w:space`). */
|
|
1246
|
-
readonly spacePt: number;
|
|
1247
|
-
/**
|
|
1248
|
-
* `w:shadow` — Word offsets a drop shadow behind the rule.
|
|
1249
|
-
*
|
|
1250
|
-
* Present only when authored true, so an edge that says nothing keeps the shape earlier
|
|
1251
|
-
* fixtures assert. Resolved and carried; drawing it is deferred.
|
|
1252
|
-
*/
|
|
1253
|
-
readonly shadow?: true;
|
|
1254
|
-
}
|
|
1255
|
-
/** The six `CT_PBdr` children, in schema order (ECMA-376 §17.3.1.24). */
|
|
1256
|
-
declare const PARAGRAPH_BORDER_SIDES: readonly ["top", "left", "bottom", "right", "between", "bar"];
|
|
1257
|
-
/**
|
|
1258
|
-
* Which of the six `CT_PBdr` edges.
|
|
1259
|
-
*
|
|
1260
|
-
* Four are physical box edges; `between` and `bar` are group-relative, drawn only where
|
|
1261
|
-
* consecutive paragraphs share a border definition.
|
|
1262
|
-
*/
|
|
1263
|
-
type ParagraphBorderSide = (typeof PARAGRAPH_BORDER_SIDES)[number];
|
|
1264
|
-
/**
|
|
1265
|
-
* A paragraph's resolved `w:pBdr` (ECMA-376 §17.3.1.24).
|
|
1266
|
-
*
|
|
1267
|
-
* `top`/`left`/`bottom`/`right` are the four physical edges of the box. The other two are
|
|
1268
|
-
* group-relative: `between` draws at a boundary INSIDE a run of consecutive paragraphs whose
|
|
1269
|
-
* border settings are identical, and `bar` is the vertical change-bar rule beside the
|
|
1270
|
-
* paragraph, drawn whether or not the paragraph groups with its neighbours.
|
|
1271
|
-
*/
|
|
1272
|
-
interface ParagraphBorders {
|
|
1273
|
-
readonly top?: ParagraphBorderEdge;
|
|
1274
|
-
readonly left?: ParagraphBorderEdge;
|
|
1275
|
-
readonly bottom?: ParagraphBorderEdge;
|
|
1276
|
-
readonly right?: ParagraphBorderEdge;
|
|
1277
|
-
readonly between?: ParagraphBorderEdge;
|
|
1278
|
-
readonly bar?: ParagraphBorderEdge;
|
|
1279
|
-
}
|
|
1280
|
-
/**
|
|
1281
|
-
* Resolve `w:spacing` before/after from flat paragraph properties.
|
|
1282
|
-
*
|
|
1283
|
-
* Line spacing (`w:line` / `w:lineRule`) is a separate concern — it changes measured line
|
|
1284
|
-
* height, not the gap between paragraphs — and is not resolved here.
|
|
1285
|
-
*
|
|
1286
|
-
* `w:beforeAutospacing` / `w:afterAutospacing` REPLACE the authored measurement on their own
|
|
1287
|
-
* side rather than adding to it; see {@link AUTO_PARAGRAPH_SPACING_PT}.
|
|
1480
|
+
* Soft ceiling on a tab position (31_680 twips ≈ 22"), matching paragraph-spacing bounds so
|
|
1481
|
+
* a hostile stop cannot shove layout into pathological widths.
|
|
1288
1482
|
*/
|
|
1289
|
-
declare
|
|
1483
|
+
declare const MAX_TAB_POSITION_TWIPS = 31680;
|
|
1484
|
+
/** OOXML / Word default when `w:settings/w:defaultTabStop` is absent: 720 twips = 0.5". */
|
|
1485
|
+
declare const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
1486
|
+
/** The default tab interval in points — {@link DEFAULT_TAB_INTERVAL_TWIPS} converted. */
|
|
1487
|
+
declare const DEFAULT_TAB_INTERVAL_PT: number;
|
|
1290
1488
|
/**
|
|
1291
|
-
*
|
|
1489
|
+
* How a tab stop positions the text that follows it.
|
|
1292
1490
|
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* rule an earlier entry in the cascade set.
|
|
1491
|
+
* Only `left` is a plain cursor jump. The other three size the tab glyph from the MEASURED
|
|
1492
|
+
* following segment, so its end, centre or decimal point lands on the stop.
|
|
1296
1493
|
*/
|
|
1297
|
-
|
|
1494
|
+
type TabAlignment = 'left' | 'center' | 'right' | 'decimal';
|
|
1298
1495
|
/**
|
|
1299
|
-
*
|
|
1496
|
+
* `w:tab/@w:leader` (ECMA-376 §17.3.1.38, ST_TabTlc): the character repeated across the
|
|
1497
|
+
* space a tab reserves. `none` is the default and is represented by an absent leader.
|
|
1300
1498
|
*
|
|
1301
|
-
*
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1304
|
-
* before "MERIDIAN". `exact` taller than the glyphs centers the text (ECMA-376 17.3.1.33).
|
|
1305
|
-
* An `exact` box smaller than the glyphs keeps the baseline inside so clipped text still
|
|
1306
|
-
* sits on it.
|
|
1307
|
-
*/
|
|
1308
|
-
declare function applyLineSpacing(spacing: ParagraphLineSpacing, naturalHeight: number, naturalBaseline: number): {
|
|
1309
|
-
height: number;
|
|
1310
|
-
baseline: number;
|
|
1311
|
-
};
|
|
1312
|
-
/**
|
|
1313
|
-
* `w:contextualSpacing` (17.3.1.9): drop before/after between paragraphs of the SAME
|
|
1314
|
-
* style. Word's built-in `ListParagraph` sets it, so every list authored in Word gets a
|
|
1315
|
-
* paragraph gap between items without this.
|
|
1499
|
+
* This is the difference between a Word table of contents and a column of headings floating
|
|
1500
|
+
* next to a column of page numbers, so it is carried through layout to paint rather than
|
|
1501
|
+
* dropped as a geometry-irrelevant attribute.
|
|
1316
1502
|
*/
|
|
1317
|
-
|
|
1503
|
+
type TabLeader = 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot';
|
|
1318
1504
|
/**
|
|
1319
|
-
*
|
|
1505
|
+
* The character each leader repeats (§17.3.1.38, ST_TabTlc).
|
|
1320
1506
|
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1507
|
+
* Lives with the type rather than with the painter because LAYOUT has to measure it: a
|
|
1508
|
+
* leader is the same character typed over and over, and the only way to space it the way
|
|
1509
|
+
* typing it would is to ask the measurer how wide it actually is.
|
|
1323
1510
|
*/
|
|
1324
|
-
declare
|
|
1511
|
+
declare const TAB_LEADER_GLYPH: ReadonlyMap<TabLeader, string>;
|
|
1512
|
+
/** One authored `w:tab`: where it sits, how it aligns, and what fills the gap. */
|
|
1513
|
+
interface TabStop {
|
|
1514
|
+
/** Position from the paragraph content origin, in points. */
|
|
1515
|
+
readonly positionPt: number;
|
|
1516
|
+
readonly alignment: TabAlignment;
|
|
1517
|
+
/** Legacy `num` stop: positions a numbering suffix, not a tab in ordinary text. */
|
|
1518
|
+
readonly numberingOnly?: true;
|
|
1519
|
+
/**
|
|
1520
|
+
* Supplied at a hanging paragraph's text indent, not authored. It positions ordinary text
|
|
1521
|
+
* tabs only: the numbering suffix treats the indent by its own rule.
|
|
1522
|
+
*/
|
|
1523
|
+
readonly implied?: true;
|
|
1524
|
+
/** Absent for `none` — the schema default and the overwhelming majority of stops. */
|
|
1525
|
+
readonly leader?: TabLeader;
|
|
1526
|
+
}
|
|
1325
1527
|
/**
|
|
1326
|
-
*
|
|
1327
|
-
*
|
|
1328
|
-
* Word does not merge edges across the cascade. A style that states only `w:bottom` discards
|
|
1329
|
-
* the box its `basedOn` ancestor declared, so folding edge by edge would leave a lone
|
|
1330
|
-
* underline surrounded by a box no one authored. Absence inherits; `nil`/`none` clear.
|
|
1528
|
+
* A paragraph's tab stops after the style cascade, with the default interval that applies past
|
|
1529
|
+
* the last explicit one.
|
|
1331
1530
|
*/
|
|
1332
|
-
|
|
1531
|
+
interface ResolvedTabStops {
|
|
1532
|
+
/** Custom stops sorted by ascending position. */
|
|
1533
|
+
readonly stops: readonly TabStop[];
|
|
1534
|
+
/** Default-tab interval in points (always positive and bounded). */
|
|
1535
|
+
readonly defaultIntervalPt: number;
|
|
1536
|
+
/**
|
|
1537
|
+
* `w:doNotUseIndentAsNumberingTabStop` (§17.15.3): a numbering suffix tab goes to the
|
|
1538
|
+
* first authored stop past the marker, wherever it is, before it falls back to the indent.
|
|
1539
|
+
*/
|
|
1540
|
+
readonly ignoreIndentAsNumberingTabStop?: true;
|
|
1541
|
+
}
|
|
1542
|
+
/** The frozen "no custom stops" value, so a paragraph without tabs mints no object. */
|
|
1543
|
+
declare const EMPTY_TAB_STOPS: ResolvedTabStops;
|
|
1333
1544
|
/**
|
|
1334
|
-
*
|
|
1545
|
+
* Resolve tab stops from cascaded `w:pPr` nodes (docDefaults → style chain → direct).
|
|
1335
1546
|
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1547
|
+
* Each `w:tabs` merges with `clear` support; absence inherits. The leader travels with the
|
|
1548
|
+
* stop that declared it — a `clear` at the same position discards both together.
|
|
1338
1549
|
*/
|
|
1339
|
-
declare function
|
|
1550
|
+
declare function cascadedTabStops(paragraphPropertyNodes: readonly OoxmlNode[]): ResolvedTabStops;
|
|
1551
|
+
/** Direct `w:pPr` only — used when no style cascade table is present. */
|
|
1552
|
+
declare function paragraphTabStops(pPr: OoxmlNode | undefined): ResolvedTabStops;
|
|
1340
1553
|
/**
|
|
1341
|
-
*
|
|
1554
|
+
* Republish stops under a document-wide default-tab interval (`w:defaultTabStop`).
|
|
1342
1555
|
*
|
|
1343
|
-
*
|
|
1344
|
-
* the
|
|
1345
|
-
*
|
|
1346
|
-
* re-breaks the lines, which is why adding a box to a paragraph in Word does not reflow it.
|
|
1556
|
+
* The cascade resolves stops from the paragraph's own property chain, which cannot see
|
|
1557
|
+
* `settings.xml`; the interval is a document constant that arrives from the session. Returns
|
|
1558
|
+
* the input unchanged when nothing moves, so a cache-key fingerprint stays stable.
|
|
1347
1559
|
*/
|
|
1348
|
-
declare function
|
|
1349
|
-
/** Vertical extent a bottom border adds below the last line (gap + rule). */
|
|
1350
|
-
declare function bottomBorderExtentPt(edge: ParagraphBorderEdge | undefined): number;
|
|
1560
|
+
declare function withDefaultTabInterval(tabs: ResolvedTabStops, defaultIntervalPt: number | undefined): ResolvedTabStops;
|
|
1351
1561
|
/**
|
|
1352
|
-
*
|
|
1562
|
+
* Read `w:settings/w:defaultTabStop` (ECMA-376 §17.15.1.25), in points.
|
|
1353
1563
|
*
|
|
1354
|
-
* Word
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1357
|
-
*
|
|
1564
|
+
* Word's own interval, not a constant: a metric-locale template writes `w:val="1134"` (2cm)
|
|
1565
|
+
* and every default-interval tab in the document lands on that grid instead of the 0.5"
|
|
1566
|
+
* one. The value is FILE-DERIVED, so a non-integer, non-positive or out-of-range `val` falls
|
|
1567
|
+
* back to the schema default rather than being trusted into layout arithmetic.
|
|
1358
1568
|
*
|
|
1359
|
-
*
|
|
1569
|
+
* `ST_TwipsMeasure` also admits a universal measure (`"2cm"`); Word writes plain twips, and
|
|
1570
|
+
* the spelled form falls back to the default rather than being parsed here.
|
|
1360
1571
|
*/
|
|
1361
|
-
declare function
|
|
1572
|
+
declare function defaultTabIntervalFromSettings(settings: OoxmlNode | null | undefined): number;
|
|
1573
|
+
/** Where one tab character lands: the resolved x, and the stop that decided it. */
|
|
1574
|
+
interface TabDestination {
|
|
1575
|
+
readonly positionPt: number;
|
|
1576
|
+
readonly alignment: TabAlignment;
|
|
1577
|
+
/** Leader of the stop that was reached; absent for `none` and for default-interval tabs. */
|
|
1578
|
+
readonly leader?: TabLeader;
|
|
1579
|
+
}
|
|
1362
1580
|
/**
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1581
|
+
* Next tab destination strictly past `currentX`, preferring custom stops then the default
|
|
1582
|
+
* interval. Destination is clamped to `rightEdge` so stops cannot escape the content box.
|
|
1365
1583
|
*/
|
|
1366
|
-
declare function
|
|
1584
|
+
declare function nextTabDestination(tabs: ResolvedTabStops, currentX: number, rightEdge: number, forNumbering?: boolean): TabDestination;
|
|
1367
1585
|
/**
|
|
1368
|
-
*
|
|
1586
|
+
* Width of a tab glyph so the following segment lands on the destination.
|
|
1369
1587
|
*
|
|
1370
|
-
*
|
|
1371
|
-
* the
|
|
1372
|
-
|
|
1373
|
-
|
|
1588
|
+
* `segmentWidth` / `decimalOffset` are already measured in points. Decimal offset is the
|
|
1589
|
+
* advance from the segment start to the decimal point (0 when none — treated like right).
|
|
1590
|
+
*/
|
|
1591
|
+
declare function tabAdvanceWidth(alignment: TabAlignment, currentX: number, destinationX: number, segmentWidth: number, decimalOffset: number): number;
|
|
1592
|
+
/**
|
|
1593
|
+
* Stable fingerprint for layout cache keys — nested `w:tabs` are not in flat `OoxmlProperty`
|
|
1594
|
+
* bags, so style-inherited stops must be named explicitly or breaks would collide.
|
|
1374
1595
|
*/
|
|
1375
|
-
declare function
|
|
1596
|
+
declare function tabStopsFingerprint(tabs: ResolvedTabStops): string;
|
|
1376
1597
|
|
|
1377
1598
|
interface DrawingPoint {
|
|
1378
1599
|
readonly x: number;
|
|
@@ -1390,6 +1611,8 @@ interface DrawingGeometry {
|
|
|
1390
1611
|
readonly paintBounds: LayoutBox;
|
|
1391
1612
|
readonly hitBounds: LayoutBox;
|
|
1392
1613
|
readonly transformedCorners: readonly DrawingPoint[];
|
|
1614
|
+
/** Unclipped image corners for affine encoding; clipping must not stretch the image. */
|
|
1615
|
+
readonly imageTransformCorners?: readonly DrawingPoint[];
|
|
1393
1616
|
readonly clipPolygon: readonly DrawingPoint[] | null;
|
|
1394
1617
|
readonly clipFallback: DrawingClipFallback;
|
|
1395
1618
|
readonly effectInsets: DrawingInsets;
|
|
@@ -1411,9 +1634,9 @@ type TableBorderSideName = 'top' | 'right' | 'bottom' | 'left';
|
|
|
1411
1634
|
/**
|
|
1412
1635
|
* One border edge in one of three states.
|
|
1413
1636
|
*
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1637
|
+
* Omitted edges inherit. Explicit no-border edges suppress inherited rules; a directly
|
|
1638
|
+
* authored opposing edge can still win. This matches the saved Word sample's mixed and
|
|
1639
|
+
* borderless tables, including cells authored with `w:val="none"`.
|
|
1417
1640
|
*/
|
|
1418
1641
|
type TableBorderSide = {
|
|
1419
1642
|
readonly state: 'omitted';
|
|
@@ -1508,21 +1731,15 @@ declare function readBorderSide(node: OoxmlElement | undefined): TableBorderSide
|
|
|
1508
1731
|
declare function readTableBorders(tblPr: OoxmlElement | undefined): TableBorderBox;
|
|
1509
1732
|
/** Read one cell's `w:tcBorders`, under the same bounds {@link readTableBorders} applies. */
|
|
1510
1733
|
declare function readCellBorders(tcPr: OoxmlElement | undefined): CellBorderBox;
|
|
1511
|
-
/**
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
* Word-matching, not conformance — §17.4.39 (`w:tblBorders`) and §17.4.66 (`w:tcBorders`)
|
|
1515
|
-
* describe the elements and specify no conflict algorithm. Word picks the heavier RULE, so
|
|
1516
|
-
* a 6pt dashed rule beats a hairline single. Folding the style rank into the weight (an
|
|
1517
|
-
* `sz × border-number` product) made a 0.5pt double outrank a 1pt single and made every
|
|
1518
|
-
* dashed or dotted rule weigh 1 regardless of `w:sz`, which erased its width entirely.
|
|
1734
|
+
/** Word's weighted border selection (MS-OI29500 §2.1.169, tcBorders).
|
|
1735
|
+
* https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oi29500/7c791c5d-44c3-4fe8-abdd-8f136761bb93
|
|
1519
1736
|
*/
|
|
1520
1737
|
declare function borderWeight(side: TableBorderSide): number;
|
|
1521
1738
|
/**
|
|
1522
1739
|
* Pick the winner between two candidates on a shared grid line (zero cell spacing).
|
|
1523
1740
|
*
|
|
1524
|
-
* `none` loses to any edge; two `none`/omitted
|
|
1525
|
-
* equal
|
|
1741
|
+
* `none` loses to any edge; two `none`/omitted do not paint. Weighted widths decide;
|
|
1742
|
+
* equal weights rank by style, then prefer the darker color, then `preferFirst`
|
|
1526
1743
|
* (reading-order / first candidate).
|
|
1527
1744
|
*/
|
|
1528
1745
|
declare function resolveBorderConflict(first: TableBorderSide, second: TableBorderSide, preferFirst?: boolean): TableBorderSide;
|
|
@@ -1541,6 +1758,8 @@ interface BorderGridCell {
|
|
|
1541
1758
|
* own and leaving a hairline gap between them.
|
|
1542
1759
|
*/
|
|
1543
1760
|
interface BorderGridGeometry {
|
|
1761
|
+
/** Center simple horizontal strokes on collapsed row boundaries. */
|
|
1762
|
+
readonly collapsedHorizontal?: boolean;
|
|
1544
1763
|
/** Absolute column widths for the whole table (points). */
|
|
1545
1764
|
readonly columnWidthsPt: readonly number[];
|
|
1546
1765
|
/**
|
|
@@ -1555,6 +1774,12 @@ interface BorderGridGeometry {
|
|
|
1555
1774
|
readonly cellBoxes: readonly (readonly {
|
|
1556
1775
|
readonly width: number;
|
|
1557
1776
|
readonly height: number;
|
|
1777
|
+
/** False when a split occurrence still reserves a shared bottom stroke. */
|
|
1778
|
+
readonly outerBottomInsetReserved?: boolean;
|
|
1779
|
+
/** Simple vertical strokes share the cell grid line with legacy padding. */
|
|
1780
|
+
readonly centeredSideRules?: boolean;
|
|
1781
|
+
/** Simple vertical strokes paint centered on the grid line. */
|
|
1782
|
+
readonly centeredSidePaint?: boolean;
|
|
1558
1783
|
}[])[];
|
|
1559
1784
|
}
|
|
1560
1785
|
/**
|
|
@@ -1570,7 +1795,7 @@ interface BorderGridGeometry {
|
|
|
1570
1795
|
* corner-adjusted endpoints in cell-local points.
|
|
1571
1796
|
*/
|
|
1572
1797
|
declare function resolveTableCellBorderGrid(rows: readonly (readonly BorderGridCell[])[], table: TableBorderBox, columnCount: number, geometry?: BorderGridGeometry, work?: TableBorderGridResolveWork, ownershipBudget?: TableBorderOwnershipBudget): ResolvedCellBorders[][];
|
|
1573
|
-
/**
|
|
1798
|
+
/** Painted extent inward from the cell edge; existing padding may already clear it. */
|
|
1574
1799
|
declare function borderExtentPt(edge: ResolvedTableBorderEdge | TableBorderSide | undefined): number;
|
|
1575
1800
|
|
|
1576
1801
|
/**
|
|
@@ -1625,6 +1850,15 @@ interface TextboxStoryLayout {
|
|
|
1625
1850
|
readonly clippedResourceToken?: string;
|
|
1626
1851
|
}
|
|
1627
1852
|
|
|
1853
|
+
/** How a drawing record holds the picture member of a shape group. */
|
|
1854
|
+
interface DrawingGroupPictureRecord {
|
|
1855
|
+
/**
|
|
1856
|
+
* Whether the group is the chosen branch of a run-level `mc:AlternateContent`. An output
|
|
1857
|
+
* that cannot draw the picture leaves such a group out, as it leaves out other MC content
|
|
1858
|
+
* it cannot draw. A group without it shows the output's unsupported-drawing result.
|
|
1859
|
+
*/
|
|
1860
|
+
readonly alternateContent: boolean;
|
|
1861
|
+
}
|
|
1628
1862
|
interface InlineDrawingRecord {
|
|
1629
1863
|
readonly kind: 'inlineDrawing';
|
|
1630
1864
|
readonly drawingNodeId: string;
|
|
@@ -1659,6 +1893,15 @@ interface InlineDrawingRecord {
|
|
|
1659
1893
|
readonly placeholderGraphicKind: string | null;
|
|
1660
1894
|
/** Typed solid-geometry payload for a renderable `wps:wsp` shape; null otherwise. */
|
|
1661
1895
|
readonly vectorShape: VectorShapeProjection | null;
|
|
1896
|
+
/**
|
|
1897
|
+
* Present when the image is the picture member of a drawing group. The image then fills
|
|
1898
|
+
* the bounds of `geometry.imageTransformCorners`, not `geometry.contentBounds`, and paints
|
|
1899
|
+
* below `vectorShape`, which holds the group's other members. `placeholderGraphicKind`
|
|
1900
|
+
* stays non-null, as for a vector shape, so image commands do not treat the group as a
|
|
1901
|
+
* picture. The group paints whole or not at all: when the picture cannot paint, an output
|
|
1902
|
+
* shows no part of the group.
|
|
1903
|
+
*/
|
|
1904
|
+
readonly groupPicture?: DrawingGroupPictureRecord;
|
|
1662
1905
|
/**
|
|
1663
1906
|
* The revision wrappers enclosing the owning run, outermost first — the same stack spans
|
|
1664
1907
|
* carry, so paint and review chrome give a tracked picture the same cues as tracked text.
|
|
@@ -1672,6 +1915,19 @@ interface InlineDrawingLayoutContext {
|
|
|
1672
1915
|
readonly projectionForAtom?: (atomNodeId: string) => DrawingProjection | null;
|
|
1673
1916
|
readonly project: (drawing: OoxmlDrawingNode) => DrawingProjection | null;
|
|
1674
1917
|
readonly resourceOf: (projection: DrawingProjection) => ImageResourceState;
|
|
1918
|
+
/**
|
|
1919
|
+
* Resolve a numbering picture bullet's image by relationship id.
|
|
1920
|
+
*
|
|
1921
|
+
* The owner is the NUMBERING part, not this context's story part: `w:numPicBullet` lives in
|
|
1922
|
+
* `numbering.xml` and its `r:id` names a relationship of that part. The resolver answers the
|
|
1923
|
+
* owner it used so a sink can key the resource without assuming the part name. Absent on a
|
|
1924
|
+
* context whose host resolves no image resources, which degrades a picture bullet to its
|
|
1925
|
+
* level's `w:lvlText`.
|
|
1926
|
+
*/
|
|
1927
|
+
readonly pictureBulletResource?: (relationshipId: string) => {
|
|
1928
|
+
readonly ownerPartName: string;
|
|
1929
|
+
readonly resource: ImageResourceState;
|
|
1930
|
+
} | null;
|
|
1675
1931
|
}
|
|
1676
1932
|
/** Named fallback when a positioning frame cannot be resolved (OpenSpec 4.6). */
|
|
1677
1933
|
type AnchoredDrawingLayoutFallback = 'unresolvable-frame' | 'page-defer-exhausted';
|
|
@@ -1684,6 +1940,11 @@ interface AnchoredDrawingRecord extends Omit<InlineDrawingRecord, 'kind' | 'base
|
|
|
1684
1940
|
readonly verticalFrameOrigin: number;
|
|
1685
1941
|
readonly behindDocument: boolean;
|
|
1686
1942
|
readonly allowOverlap: boolean;
|
|
1943
|
+
/**
|
|
1944
|
+
* Whether the object is laid out in the box it flows in. For an anchor in a table cell this
|
|
1945
|
+
* is the layout's reading: `true` from compatibility mode 15, which ignores
|
|
1946
|
+
* `layoutInCell="0"`. Everywhere else it is the authored `wp:anchor/@layoutInCell`.
|
|
1947
|
+
*/
|
|
1687
1948
|
readonly layoutInCell: boolean;
|
|
1688
1949
|
readonly relativeHeight: number;
|
|
1689
1950
|
readonly wrap: Exclude<ImageWrapTarget, 'inline'>;
|
|
@@ -1718,6 +1979,17 @@ interface ResolvedListItem {
|
|
|
1718
1979
|
/** Effective indent after merging level + paragraph indents, in points. */
|
|
1719
1980
|
readonly indent: NumberingLevelIndent;
|
|
1720
1981
|
readonly markerStyle: ResolvedRunStyle;
|
|
1982
|
+
/**
|
|
1983
|
+
* The image marker a `w:lvlPicBulletId` level resolved to, absent on every other item.
|
|
1984
|
+
*
|
|
1985
|
+
* Present means the marker is an IMAGE: it occupies `width` x `height` points in the
|
|
1986
|
+
* hanging slot and `markerText` is only what a sink that cannot draw the image falls back
|
|
1987
|
+
* to. The extent is the AUTHORED `v:shape` size scaled by the marker font size — see
|
|
1988
|
+
* {@link resolvePictureBullet}, which also decides when the answer is no picture at all.
|
|
1989
|
+
* Absent means the level declared none, named one the part does not declare, declared one
|
|
1990
|
+
* this projection could not read, or resolved to a size nothing can paint.
|
|
1991
|
+
*/
|
|
1992
|
+
readonly picBullet?: ResolvedPictureBullet;
|
|
1721
1993
|
/** Fingerprint for layout cache keys (indent, marker text, marker face). */
|
|
1722
1994
|
readonly cacheToken: string;
|
|
1723
1995
|
}
|
|
@@ -1748,18 +2020,22 @@ declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
|
|
|
1748
2020
|
*/
|
|
1749
2021
|
declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;
|
|
1750
2022
|
/**
|
|
1751
|
-
* The effective indent of a list paragraph:
|
|
2023
|
+
* The effective indent of a list paragraph: `inherited`, then the numbering LEVEL, then
|
|
2024
|
+
* `direct`, per attribute.
|
|
1752
2025
|
*
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1756
|
-
* `
|
|
1757
|
-
* the
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
2026
|
+
* A level's `w:pPr/w:ind` applies per attribute between the two tiers, and the ordering
|
|
2027
|
+
* matters on real documents. A converted agreement numbers its `(a)` items with a level
|
|
2028
|
+
* stating `left=1512 hanging=738` under a `ListParagraph` style stating
|
|
2029
|
+
* `left=775 hanging=624`, and states only `hanging="737"` on the paragraph itself. Reading
|
|
2030
|
+
* the flattened cascade as "the paragraph's indent" gave the STYLE's 775 to a level that had
|
|
2031
|
+
* overridden it, so every lettered sub-item hung a full indent step to the left of where
|
|
2032
|
+
* Word puts it.
|
|
1760
2033
|
*
|
|
1761
|
-
* `inherited` is the
|
|
1762
|
-
*
|
|
2034
|
+
* `inherited` is what the level outranks and `direct` is what outranks it. When the
|
|
2035
|
+
* paragraph's own `w:pPr` states a `w:numPr`, these are the style chain (after the document
|
|
2036
|
+
* defaults) and that `w:pPr`. Otherwise the level sits directly below the nearest style that
|
|
2037
|
+
* states a `w:numPr`: that style, the styles based on it and the paragraph's own `w:pPr` go
|
|
2038
|
+
* in `direct`, and the defaults and that style's bases go in `inherited`.
|
|
1763
2039
|
*/
|
|
1764
2040
|
declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;
|
|
1765
2041
|
/**
|
|
@@ -1786,33 +2062,15 @@ declare function withResolvedListItems<T extends {
|
|
|
1786
2062
|
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
1787
2063
|
readonly styleCascade?: StyleCascadeTable;
|
|
1788
2064
|
readonly isFontAvailable?: (family: string) => boolean;
|
|
2065
|
+
readonly measurer?: TextMeasurer;
|
|
1789
2066
|
}>(options: T, blocks: readonly OoxmlElement[]): T & {
|
|
1790
2067
|
readonly numberingIndex: NumberingIndex;
|
|
1791
2068
|
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
1792
2069
|
};
|
|
1793
|
-
/**
|
|
1794
|
-
* Horizontal marker box inside the first-line indent slot.
|
|
1795
|
-
*
|
|
1796
|
-
* Coordinates are relative to the same origin as paragraph content (`indent.left` is the
|
|
1797
|
-
* text start). Returns null when there is nothing to paint.
|
|
1798
|
-
*
|
|
1799
|
-
* `w:hanging` and a positive `w:firstLine` are one mutually exclusive slot
|
|
1800
|
-
* (§17.3.1.10, §17.3.1.12), so the marker has two placements, not an interaction:
|
|
1801
|
-
* a hanging level puts the marker BEFORE the text start (`left - hanging`); a
|
|
1802
|
-
* positive-firstLine level puts it AFTER (`left + firstLine`) — the standard legal
|
|
1803
|
-
* shape `w:ind w:left="0" w:firstLine="720"` numbers at 0.5" while continuation
|
|
1804
|
-
* lines return to the margin. Reading only the hanging model painted every such
|
|
1805
|
-
* marker at the left margin.
|
|
1806
|
-
*/
|
|
1807
|
-
declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
|
|
1808
|
-
x: number;
|
|
1809
|
-
y: number;
|
|
1810
|
-
width: number;
|
|
1811
|
-
height: number;
|
|
1812
|
-
} | null;
|
|
1813
2070
|
|
|
1814
2071
|
interface StyleDefinition {
|
|
1815
2072
|
readonly styleId: string;
|
|
2073
|
+
readonly name?: string;
|
|
1816
2074
|
readonly type: string;
|
|
1817
2075
|
readonly basedOn: string | null;
|
|
1818
2076
|
/** `w:next` — the authoring style for a following paragraph. */
|
|
@@ -1823,12 +2081,11 @@ interface StyleDefinition {
|
|
|
1823
2081
|
readonly paragraphPropertiesNode: OoxmlElement | undefined;
|
|
1824
2082
|
readonly tablePropertiesNode: OoxmlElement | undefined;
|
|
1825
2083
|
readonly tableRowPropertiesNode: OoxmlElement | undefined;
|
|
2084
|
+
readonly tableCellPropertiesNode?: OoxmlElement;
|
|
1826
2085
|
readonly conditionalTableFormats: ReadonlyMap<string, OoxmlElement>;
|
|
1827
2086
|
}
|
|
1828
2087
|
declare function isValidStyleId(raw: string | undefined): raw is string;
|
|
1829
2088
|
|
|
1830
|
-
/** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
|
|
1831
|
-
declare const MAX_STYLE_BASED_ON_DEPTH = 32;
|
|
1832
2089
|
/** Soft ceiling on style definitions read from one styles part. */
|
|
1833
2090
|
declare const MAX_STYLE_DEFINITIONS = 4096;
|
|
1834
2091
|
/**
|
|
@@ -1838,6 +2095,23 @@ declare const MAX_STYLE_DEFINITIONS = 4096;
|
|
|
1838
2095
|
* styles part are never reused under another.
|
|
1839
2096
|
*/
|
|
1840
2097
|
interface StyleCascadeTable {
|
|
2098
|
+
/** Keep floating tables that fit a full page together under the compatibility flag. */
|
|
2099
|
+
readonly doNotBreakWrappedTables?: true;
|
|
2100
|
+
/** Legacy or explicitly disabled optional OpenType substitutions. */
|
|
2101
|
+
readonly disableOptionalLigatures?: true;
|
|
2102
|
+
/** Legacy noExtraLineSpacing behavior, included in the producer fingerprint. */
|
|
2103
|
+
readonly preserveExactLineBaseline?: true;
|
|
2104
|
+
/** `w:adjustLineHeightInTable`: cell paragraphs snap to the section line grid too. */
|
|
2105
|
+
readonly adjustLineHeightInTable?: true;
|
|
2106
|
+
/** `w:doNotUseIndentAsNumberingTabStop`, carried onto each paragraph's tab stops. */
|
|
2107
|
+
readonly ignoreIndentAsNumberingTabStop?: true;
|
|
2108
|
+
/**
|
|
2109
|
+
* `w:doNotUseHTMLParagraphAutoSpacing`: adjacent paragraph spacing adds up, and automatic
|
|
2110
|
+
* spacing is a fixed 5pt before and 10pt after.
|
|
2111
|
+
*/
|
|
2112
|
+
readonly fixedParagraphSpacing?: true;
|
|
2113
|
+
/** Explicit compatibility opt-in to the unmodified ISO table style hierarchy. */
|
|
2114
|
+
readonly strictTableStyleHierarchy?: boolean;
|
|
1841
2115
|
readonly typography?: CjkTypographySettings;
|
|
1842
2116
|
/**
|
|
1843
2117
|
* Bounded fingerprint folded into layout cache producers so a different styles part cannot
|
|
@@ -1878,12 +2152,7 @@ interface StyleCascadeTable {
|
|
|
1878
2152
|
interface CascadedParagraphFormatting {
|
|
1879
2153
|
/** Flat paragraph properties in cascade order (defaults → bases → style → direct). */
|
|
1880
2154
|
readonly paragraphProperties: readonly OoxmlProperty[];
|
|
1881
|
-
/**
|
|
1882
|
-
* The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits.
|
|
1883
|
-
*
|
|
1884
|
-
* Numbering needs the two tiers apart: a level's `w:pPr/w:ind` outranks the style's and is
|
|
1885
|
-
* outranked by the paragraph's own, and a flattened list cannot say which is which.
|
|
1886
|
-
*/
|
|
2155
|
+
/** The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits. */
|
|
1887
2156
|
readonly inheritedParagraphProperties: readonly OoxmlProperty[];
|
|
1888
2157
|
/** Matching `w:pPr` nodes for nested border resolution. */
|
|
1889
2158
|
readonly paragraphPropertyNodes: readonly OoxmlNode[];
|
|
@@ -1896,7 +2165,9 @@ interface CascadedParagraphFormatting {
|
|
|
1896
2165
|
*/
|
|
1897
2166
|
readonly runProperties: readonly OoxmlProperty[];
|
|
1898
2167
|
/**
|
|
1899
|
-
* Content cascade plus
|
|
2168
|
+
* Content cascade plus the mark's `w:pPr/w:rPr`, including its `w:rStyle` character style —
|
|
2169
|
+
* empty-line metrics and the numbering marker. A line with content grows from the mark
|
|
2170
|
+
* without that character style.
|
|
1900
2171
|
*/
|
|
1901
2172
|
readonly markRunProperties: readonly OoxmlProperty[];
|
|
1902
2173
|
/**
|
|
@@ -1995,8 +2266,8 @@ interface ParagraphLayoutInputs {
|
|
|
1995
2266
|
readonly shading: string | undefined;
|
|
1996
2267
|
readonly inheritedRunProperties: readonly OoxmlProperty[];
|
|
1997
2268
|
/**
|
|
1998
|
-
* Paragraph-mark cascade (`inheritedRunProperties` +
|
|
1999
|
-
* Empty-line sizing and last-line mark height — never content
|
|
2269
|
+
* Paragraph-mark cascade (`inheritedRunProperties` + `w:pPr/w:rPr` and its `w:rStyle`).
|
|
2270
|
+
* Empty-line sizing and last-line mark height (without the `w:rStyle`) — never content face.
|
|
2000
2271
|
*/
|
|
2001
2272
|
readonly markRunProperties: readonly OoxmlProperty[];
|
|
2002
2273
|
/** Cascaded custom tab stops + default interval for paragraph-flow breaking. */
|
|
@@ -2022,255 +2293,11 @@ interface ParagraphLayoutInputs {
|
|
|
2022
2293
|
*
|
|
2023
2294
|
* `inTableCell` is asked for separately because a cell paragraph may have no table style to
|
|
2024
2295
|
* inherit at all, and `w:beforeAutospacing` still needs to know it is in a cell.
|
|
2025
|
-
*/
|
|
2026
|
-
declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean, lineUnitPt?: number): ParagraphLayoutInputs;
|
|
2027
|
-
|
|
2028
|
-
/**
|
|
2029
|
-
* `w:tblW` / `w:tcW` / `w:wBefore` (CT_TblWidth, 17.4.63 / 17.4.71 / 17.4.86): a PREFERRED
|
|
2030
|
-
* width plus the unit it is stated in. Preferred is the operative word — it is what the
|
|
2031
|
-
* producer asked for, not what the table resolved to.
|
|
2032
|
-
*
|
|
2033
|
-
* `pct` is stated in fiftieths of a percent (5000 = 100%) by Word, and in the `"50%"`
|
|
2034
|
-
* string form of `ST_Percentage` by others; both are read. `auto` and `nil` carry no width.
|
|
2035
|
-
*/
|
|
2036
|
-
type PreferredWidthType = 'dxa' | 'pct' | 'auto' | 'nil';
|
|
2037
|
-
/**
|
|
2038
|
-
* `w:tblW` / `w:tcW` — a requested width, whose UNIT depends on its type.
|
|
2039
2296
|
*
|
|
2040
|
-
*
|
|
2041
|
-
*
|
|
2297
|
+
* `lineUnitPt` is the section's ACTIVE line-grid pitch. It sizes line-unit paragraph margins
|
|
2298
|
+
* and snaps the paragraph's lines to the grid; absent means no grid and the fixed 12pt unit.
|
|
2042
2299
|
*/
|
|
2043
|
-
|
|
2044
|
-
readonly type: PreferredWidthType;
|
|
2045
|
-
/** POINTS for `dxa`, PERCENT (0–100) for `pct`, 0 for `auto`/`nil`. */
|
|
2046
|
-
readonly value: number;
|
|
2047
|
-
}
|
|
2048
|
-
/** The frozen "no preferred width" value — what a table or cell that declares none resolves to. */
|
|
2049
|
-
declare const AUTO_PREFERRED_WIDTH: PreferredWidth;
|
|
2050
|
-
|
|
2051
|
-
/**
|
|
2052
|
-
* The uniform 3 pt (60 twip) inset this lane used to apply on every side.
|
|
2053
|
-
*
|
|
2054
|
-
* Word's defaults are not uniform — see {@link DEFAULT_CELL_MARGINS} — so nothing resolves
|
|
2055
|
-
* against this any more. Kept because it is published.
|
|
2056
|
-
*/
|
|
2057
|
-
declare const CELL_PAD = 3;
|
|
2058
|
-
/** Resolved cell padding in points, after the table default and any per-cell override. */
|
|
2059
|
-
interface CellMarginsPt {
|
|
2060
|
-
readonly top: number;
|
|
2061
|
-
readonly right: number;
|
|
2062
|
-
readonly bottom: number;
|
|
2063
|
-
readonly left: number;
|
|
2064
|
-
}
|
|
2065
|
-
/**
|
|
2066
|
-
* Word's own default cell padding, for a document whose styles part states none.
|
|
2067
|
-
*
|
|
2068
|
-
* 0 top, 0 bottom, 108 twips left and right — the values `TableNormal` carries. A uniform
|
|
2069
|
-
* 3 pt on all four sides made every row of every table that authored no `w:tblCellMar` 6 pt
|
|
2070
|
-
* taller than Word's, and that error compounds down a table until it paginates a page early.
|
|
2071
|
-
* A document that DOES ship a default table style resolves against that instead; this is the
|
|
2072
|
-
* floor beneath it.
|
|
2073
|
-
*/
|
|
2074
|
-
declare const DEFAULT_CELL_MARGINS: CellMarginsPt;
|
|
2075
|
-
|
|
2076
|
-
/** `w:vAlign` — where a cell's content sits when the row is taller than the content. */
|
|
2077
|
-
type CellVerticalAlign = 'top' | 'center' | 'bottom';
|
|
2078
|
-
|
|
2079
|
-
/**
|
|
2080
|
-
* Layout-time nesting ceiling. Parse-time depth (MAX_DEPTH = 256 XML levels) alone still
|
|
2081
|
-
* admits ~80 levels of `w:tbl` recursion into the layout walk; deeper tables render as an
|
|
2082
|
-
* empty cell box rather than recursing.
|
|
2083
|
-
*/
|
|
2084
|
-
declare const MAX_TABLE_NESTING = 16;
|
|
2085
|
-
/**
|
|
2086
|
-
* Soft ceiling on an authored `w:trHeight` (~22"). Hostile `w:val` otherwise becomes a
|
|
2087
|
-
* multi-page row that every pagination preflight and cell box inherits.
|
|
2088
|
-
*/
|
|
2089
|
-
declare const MAX_TABLE_ROW_HEIGHT_PT: number;
|
|
2090
|
-
/**
|
|
2091
|
-
* `w:trPr/w:trHeight` (17.4.81) resolved for layout. Points leave the reader already —
|
|
2092
|
-
* twips convert once here, matching every other table geometry boundary.
|
|
2093
|
-
*
|
|
2094
|
-
* Word quirk (matches Form025U and Word's UI export): a present `@w:val` with an omitted
|
|
2095
|
-
* `@w:hRule` is treated as `atLeast`, not ECMA's `auto`. Explicit `auto` still ignores val.
|
|
2096
|
-
*/
|
|
2097
|
-
type TableRowHeightRule = 'auto' | 'atLeast' | 'exact';
|
|
2098
|
-
/**
|
|
2099
|
-
* `w:trHeight` — a row's height rule and its value.
|
|
2100
|
-
*
|
|
2101
|
-
* `auto` carries no value at all, which is why this is a union rather than a rule plus an
|
|
2102
|
-
* optional number.
|
|
2103
|
-
*/
|
|
2104
|
-
type TableRowHeight = {
|
|
2105
|
-
readonly rule: 'auto';
|
|
2106
|
-
} | {
|
|
2107
|
-
readonly rule: 'atLeast' | 'exact';
|
|
2108
|
-
readonly valuePt: number;
|
|
2109
|
-
};
|
|
2110
|
-
/** `w:tblPr/w:jc` (17.4.29, ST_JcTable): where the table sits within the text column. */
|
|
2111
|
-
type TableAlignment = 'left' | 'center' | 'right';
|
|
2112
|
-
/**
|
|
2113
|
-
* `w:tblpPr/@w:horzAnchor` (17.4.58) and `@w:vertAnchor` (17.4.66): the box a floated
|
|
2114
|
-
* table's offsets are measured from. Absent means `text` for both.
|
|
2115
|
-
*/
|
|
2116
|
-
type TableFloatAnchor = 'text' | 'margin' | 'page';
|
|
2117
|
-
/** `w:tblpPr/@w:tblpXSpec` (17.4.63, ST_XAlign). */
|
|
2118
|
-
type TableFloatXSpec = 'left' | 'center' | 'right' | 'inside' | 'outside';
|
|
2119
|
-
/** `w:tblpPr/@w:tblpYSpec` (17.4.65, ST_YAlign). */
|
|
2120
|
-
type TableFloatYSpec = 'inline' | 'top' | 'center' | 'bottom' | 'inside' | 'outside';
|
|
2121
|
-
/**
|
|
2122
|
-
* `w:tblPr/w:tblpPr` (17.4.57) — a table positioned against an anchor box rather than at
|
|
2123
|
-
* the point in the text where it was authored.
|
|
2124
|
-
*
|
|
2125
|
-
* A spec (`tblpXSpec`/`tblpYSpec`) supersedes the matching offset when both are present:
|
|
2126
|
-
* 17.4.57 states the alignment outright, and the offset only answers "how far from the
|
|
2127
|
-
* anchor" for the case where no alignment was stated.
|
|
2128
|
-
*/
|
|
2129
|
-
interface TableFloatPosition {
|
|
2130
|
-
readonly horzAnchor: TableFloatAnchor;
|
|
2131
|
-
readonly vertAnchor: TableFloatAnchor;
|
|
2132
|
-
readonly xSpec?: TableFloatXSpec;
|
|
2133
|
-
/** `w:tblpX` in points; signed, so a table can be pulled into the margin. */
|
|
2134
|
-
readonly xPt: number;
|
|
2135
|
-
readonly ySpec?: TableFloatYSpec;
|
|
2136
|
-
/** `w:tblpY` in points; signed. */
|
|
2137
|
-
readonly yPt: number;
|
|
2138
|
-
readonly distances?: {
|
|
2139
|
-
readonly top: number;
|
|
2140
|
-
readonly right: number;
|
|
2141
|
-
readonly bottom: number;
|
|
2142
|
-
readonly left: number;
|
|
2143
|
-
};
|
|
2144
|
-
}
|
|
2145
|
-
/**
|
|
2146
|
-
* One cell in the resolved table structure.
|
|
2147
|
-
*
|
|
2148
|
-
* `gridSpan` is clamped at READ time and layout never re-derives it — the value comes from a file
|
|
2149
|
-
* and would otherwise be a loop bound an attacker controls.
|
|
2150
|
-
*/
|
|
2151
|
-
interface SemanticTableCell {
|
|
2152
|
-
readonly id: string;
|
|
2153
|
-
/** Derived content-edge geometry for a verified legacy full-width parent table. */
|
|
2154
|
-
readonly legacyContentAlignment?: true;
|
|
2155
|
-
/** Clamped to [1, MAX_TABLE_COLUMNS] at read time; layout never re-derives it. */
|
|
2156
|
-
readonly gridSpan: number;
|
|
2157
|
-
/** Physical grid column after width/style resolution and the bidiVisual projection. */
|
|
2158
|
-
readonly gridColumn: number;
|
|
2159
|
-
/** Stored grid index when bidiVisual maps this cell into a physical RTL grid. */
|
|
2160
|
-
readonly logicalGridColumn?: number;
|
|
2161
|
-
/** Canonical `w:gridCol` node id for this cell's start column, when the grid is authored. */
|
|
2162
|
-
readonly gridColumnId?: string;
|
|
2163
|
-
/** A vMerge cell that is not the restart continues the cell above: box, no content. */
|
|
2164
|
-
readonly vMergeContinue: boolean;
|
|
2165
|
-
/** `w:vAlign` — defaults to top when omitted/unrecognised. */
|
|
2166
|
-
readonly vAlign: CellVerticalAlign;
|
|
2167
|
-
/** `w:textDirection`; unsupported values keep horizontal layout. */
|
|
2168
|
-
readonly textDirection: 'horizontal' | 'btLr';
|
|
2169
|
-
/** Resolved per-side margins (tcMar over tblCellMar over the table style over Word's default). */
|
|
2170
|
-
readonly margins: CellMarginsPt;
|
|
2171
|
-
/** Three-state authored `tcBorders` (omitted / none / edge). */
|
|
2172
|
-
readonly borders: CellBorderBox;
|
|
2173
|
-
/** Validated 6-hex shading fill, absent for none/auto. */
|
|
2174
|
-
readonly shading?: string;
|
|
2175
|
-
/**
|
|
2176
|
-
* `w:tcW` — the width this cell asked for, as authored.
|
|
2177
|
-
*
|
|
2178
|
-
* Published for consumers that need the cell's own statement (a column-resize handle has
|
|
2179
|
-
* to write back to it). Column geometry is NOT derived from this field: the resolver works
|
|
2180
|
-
* from a flat claim list built in the same pass, because resolving a column means looking
|
|
2181
|
-
* at every cell that covers it across every row, not at one cell at a time. Read
|
|
2182
|
-
* `columnWidthsPt` for what the table actually laid out.
|
|
2183
|
-
*/
|
|
2184
|
-
readonly preferredWidth: PreferredWidth;
|
|
2185
|
-
/**
|
|
2186
|
-
* What the table style says about this cell's paragraphs and runs (17.7.6.6) — a header
|
|
2187
|
-
* row's bold and centring live here, not in the cell's own properties.
|
|
2188
|
-
*/
|
|
2189
|
-
readonly styleFormatting: TableCellStyleFormatting;
|
|
2190
|
-
/** Block children in reading order, with content-control wrappers flattened. */
|
|
2191
|
-
readonly blocks: readonly OoxmlElement[];
|
|
2192
|
-
}
|
|
2193
|
-
/** One row in the resolved structure: its cells, its height rule, and any row-level revision. */
|
|
2194
|
-
interface SemanticTableRow {
|
|
2195
|
-
readonly id: string;
|
|
2196
|
-
/** Pending Word row insertion/deletion authored in `w:trPr`. */
|
|
2197
|
-
readonly revisionKind?: 'insert' | 'delete';
|
|
2198
|
-
/**
|
|
2199
|
-
* The `w:trPr/w:ins|w:del` attribution, carried with the kind so a painted row can say
|
|
2200
|
-
* WHOSE pending decision it is — the review model addresses the decision by exactly this
|
|
2201
|
-
* `(id, author, date)` triple, and a surface with only the kind could highlight the row
|
|
2202
|
-
* but never open its card.
|
|
2203
|
-
*/
|
|
2204
|
-
readonly revisionId?: string;
|
|
2205
|
-
readonly revisionAuthor?: string;
|
|
2206
|
-
readonly revisionDate?: string;
|
|
2207
|
-
/** The row insertion a resolved view kept the row through; see the fragment record. */
|
|
2208
|
-
readonly changeSites?: readonly RevisionAttribution[];
|
|
2209
|
-
/** `w:trPr/w:tblHeader` — the row repeats atop each page the table continues onto. */
|
|
2210
|
-
readonly isHeader: boolean;
|
|
2211
|
-
/**
|
|
2212
|
-
* `w:trPr/w:cantSplit` — the row must stay on one page. When it cannot fit a fresh page,
|
|
2213
|
-
* layout fails closed rather than fragmenting or overflowing the content box.
|
|
2214
|
-
*/
|
|
2215
|
-
readonly cantSplit: boolean;
|
|
2216
|
-
/** `w:trPr/w:trHeight` — auto / atLeast floor / exact (clipped) row height. */
|
|
2217
|
-
readonly height: TableRowHeight;
|
|
2218
|
-
readonly cells: readonly SemanticTableCell[];
|
|
2219
|
-
}
|
|
2220
|
-
/**
|
|
2221
|
-
* A table resolved into a rectangular grid: column widths, rows, and the widths it asked for.
|
|
2222
|
-
*
|
|
2223
|
-
* The grid is normalized here so layout never has to reconcile `w:gridCol` against actual cell
|
|
2224
|
-
* spans — vertical merges and column spans are already accounted for.
|
|
2225
|
-
*/
|
|
2226
|
-
interface SemanticTableStructure {
|
|
2227
|
-
/** Whether stored columns and horizontal table properties display right to left. */
|
|
2228
|
-
readonly bidiVisual?: true;
|
|
2229
|
-
readonly columnWidthsPt: readonly number[];
|
|
2230
|
-
readonly rows: readonly SemanticTableRow[];
|
|
2231
|
-
/** Verified pre-2013 content-aligned full-width inline table; derived, never serialized. */
|
|
2232
|
-
readonly legacyContentAlignment?: true;
|
|
2233
|
-
/** `w:tblPr/w:tblW` — the width the table asked for. */
|
|
2234
|
-
readonly tableWidth: PreferredWidth;
|
|
2235
|
-
/**
|
|
2236
|
-
* `w:tblInd` (17.4.50) in points — "this indentation should shift the table into the text
|
|
2237
|
-
* margin by the specified amount". Applies to a left-aligned table; `w:jc` decides the
|
|
2238
|
-
* placement outright for the other two.
|
|
2239
|
-
*/
|
|
2240
|
-
readonly indentPt: number;
|
|
2241
|
-
/** `w:tblPr/w:jc` (17.4.29) — where the table sits in the text column. */
|
|
2242
|
-
readonly alignment: TableAlignment;
|
|
2243
|
-
/**
|
|
2244
|
-
* `w:tblPr/w:tblpPr` (17.4.57) — present when the table is positioned against an anchor
|
|
2245
|
-
* box. Placement then comes from {@link tableFloatOriginX} rather than `w:jc`/`w:tblInd`.
|
|
2246
|
-
*/
|
|
2247
|
-
readonly float?: TableFloatPosition;
|
|
2248
|
-
/**
|
|
2249
|
-
* `w:tblCellSpacing` (17.4.45) in points: the gap between adjacent cell edges. Applied as
|
|
2250
|
-
* a half-gap inset on each side of every cell, so cells separate visually without the grid
|
|
2251
|
-
* itself moving. Word ALSO grows the table's overall width by the spacing it adds around
|
|
2252
|
-
* the outside; that part is not modelled, so a spaced table is laid out on the same grid
|
|
2253
|
-
* its file states rather than a wider one.
|
|
2254
|
-
*/
|
|
2255
|
-
readonly cellSpacingPt: number;
|
|
2256
|
-
/**
|
|
2257
|
-
* `w:tblPr/w:tblLayout/@w:type="fixed"` (17.4.52 — 17.4.53 is the `w:tblPrEx` exception
|
|
2258
|
-
* variant, not this element). Fixed layout takes the grid as final;
|
|
2259
|
-
* anything else is autofit, which in Word never renders wider than the text column.
|
|
2260
|
-
*/
|
|
2261
|
-
readonly layoutFixed: boolean;
|
|
2262
|
-
/** Table-level `tblBorders` (three-state, including insideH/insideV). */
|
|
2263
|
-
readonly tableBorders: TableBorderBox;
|
|
2264
|
-
/** Table-level `tblCellMar` defaults (per-side, Word's own default when a side is omitted). */
|
|
2265
|
-
readonly defaultMargins: CellMarginsPt;
|
|
2266
|
-
}
|
|
2267
|
-
/**
|
|
2268
|
-
* Read one typed table node into a bounded structure, or null when the node is not a
|
|
2269
|
-
* typed table or sits beyond the nesting ceiling.
|
|
2270
|
-
*/
|
|
2271
|
-
declare function readTableStructure(table: OoxmlNode, contentWidthPt: number, depth: number, styleCascade?: StyleCascadeTable,
|
|
2272
|
-
/** Which revisions the view resolves away; only the proposed result performs the join. */
|
|
2273
|
-
displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter, compatibilityMode?: number): SemanticTableStructure | null;
|
|
2300
|
+
declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean, lineUnitPt?: number): ParagraphLayoutInputs;
|
|
2274
2301
|
|
|
2275
2302
|
interface EquationGeometryBase {
|
|
2276
2303
|
readonly box: LayoutBox;
|
|
@@ -2570,6 +2597,8 @@ interface StyleSpanRecord {
|
|
|
2570
2597
|
* ink displacement. Exporters must apply this offset to the ink origin.
|
|
2571
2598
|
*/
|
|
2572
2599
|
readonly glyphOffsetPt?: number;
|
|
2600
|
+
/** Measured face baseline for character-border geometry; span boxes remain selection bands. */
|
|
2601
|
+
readonly borderBaselinePt?: number;
|
|
2573
2602
|
readonly range: SourceRange;
|
|
2574
2603
|
readonly text: string;
|
|
2575
2604
|
/** The run's authored properties, retained as evidence. */
|
|
@@ -2674,6 +2703,8 @@ interface StyleSpanRecord {
|
|
|
2674
2703
|
* and selection mapping refuses them the way it refuses markers.
|
|
2675
2704
|
*/
|
|
2676
2705
|
readonly projected?: boolean;
|
|
2706
|
+
/** Authored inline separator rule; its advance belongs to one note atom. */
|
|
2707
|
+
readonly noteSeparator?: 'separator' | 'continuationSeparator';
|
|
2677
2708
|
/** Paint-ready geometry for one atomic Office Math equation. */
|
|
2678
2709
|
readonly equation?: EquationSpanRecord;
|
|
2679
2710
|
/**
|
|
@@ -2717,9 +2748,9 @@ interface LineRecord {
|
|
|
2717
2748
|
/** Distance from the line box top to the text baseline. */
|
|
2718
2749
|
readonly baseline: number;
|
|
2719
2750
|
/**
|
|
2720
|
-
* Space ABOVE the glyph band inside {@link box} (exact centering,
|
|
2751
|
+
* Space ABOVE the glyph band inside {@link box} (exact centering, atLeast growth).
|
|
2721
2752
|
*
|
|
2722
|
-
* `auto`
|
|
2753
|
+
* `auto` extras grow the box BELOW the glyphs — paint puts that depth in
|
|
2723
2754
|
* padding-bottom. {@link baseline} is measured from the line top and already includes
|
|
2724
2755
|
* this above-band when present.
|
|
2725
2756
|
*
|
|
@@ -2728,18 +2759,18 @@ interface LineRecord {
|
|
|
2728
2759
|
*/
|
|
2729
2760
|
readonly leading: number;
|
|
2730
2761
|
/**
|
|
2731
|
-
* Auto
|
|
2762
|
+
* Auto line-spacing depth BELOW the glyph band, inside {@link box}.
|
|
2732
2763
|
*
|
|
2733
|
-
* The complement of {@link leading}: exact
|
|
2734
|
-
*
|
|
2764
|
+
* The complement of {@link leading}: exact centres the glyphs and atLeast grows the box
|
|
2765
|
+
* above them, while auto leaves the band at the top and grows the box beneath it.
|
|
2735
2766
|
* So the band a consumer needs is `box.height - trailingSpacing - leading`, and
|
|
2736
|
-
* subtracting `leading` alone is right only under
|
|
2767
|
+
* subtracting `leading` alone is right only under exact and atLeast.
|
|
2737
2768
|
*
|
|
2738
2769
|
* A line WITH spans carries its band in the span heights too. An empty paragraph carries
|
|
2739
2770
|
* nothing, which is why the caret, paint's `padding-bottom` and the content-control
|
|
2740
2771
|
* boundary all need this published rather than recovered from the box.
|
|
2741
2772
|
*
|
|
2742
|
-
* Zero under
|
|
2773
|
+
* Zero under exact and atLeast, and on lines holding drawings (the box is authored).
|
|
2743
2774
|
* Absent on lines published before this was measured; treat as zero.
|
|
2744
2775
|
*/
|
|
2745
2776
|
readonly trailingSpacing?: number;
|
|
@@ -2813,10 +2844,16 @@ interface ParagraphFragmentRecord {
|
|
|
2813
2844
|
readonly emptyParagraphStyle?: ResolvedRunStyle;
|
|
2814
2845
|
/** A fixed text frame clips its painted ink to this fragment's box; source ranges remain intact. */
|
|
2815
2846
|
readonly clipToBox?: true;
|
|
2816
|
-
/**
|
|
2847
|
+
/**
|
|
2848
|
+
* The fragment retains its source identity without consuming body flow height: a positioned
|
|
2849
|
+
* text frame (with {@link ParagraphFragmentRecord.positionedFrame}), an empty section-mark
|
|
2850
|
+
* paragraph before a continuous section, or the empty line in front of a paragraph's leading
|
|
2851
|
+
* page break at the bottom of a full page. The last two paint nothing.
|
|
2852
|
+
*/
|
|
2817
2853
|
readonly outOfFlow?: true;
|
|
2818
2854
|
/** Placement and wrapping of one authored text-frame group, in page-content coordinates. */
|
|
2819
2855
|
readonly positionedFrame?: {
|
|
2856
|
+
readonly dropCapLines?: number;
|
|
2820
2857
|
readonly anchorId: string;
|
|
2821
2858
|
readonly columnIndex: number;
|
|
2822
2859
|
readonly groupId: string;
|
|
@@ -2971,17 +3008,53 @@ interface ListMarkerRecord {
|
|
|
2971
3008
|
*/
|
|
2972
3009
|
readonly level: number;
|
|
2973
3010
|
/**
|
|
2974
|
-
* The `w:numId` this marker resolved through.
|
|
2975
|
-
*
|
|
2976
|
-
* Published with the level because the two together are what identifies a list: whether
|
|
2977
|
-
* a demote is even possible depends on which levels THIS definition declares, and a
|
|
2978
|
-
* document may hold several lists whose level 0 looks identical.
|
|
3011
|
+
* The `w:numId` this marker resolved through. With the level it identifies a list: whether
|
|
3012
|
+
* a demote is possible depends on which levels THIS definition declares.
|
|
2979
3013
|
*/
|
|
2980
3014
|
readonly numId: string;
|
|
2981
3015
|
/** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
|
|
2982
3016
|
readonly numFmt: string;
|
|
2983
3017
|
/** Resolved counter at this marker's own level; absent for bullets. */
|
|
2984
3018
|
readonly ordinal?: number;
|
|
3019
|
+
/**
|
|
3020
|
+
* The IMAGE this marker draws instead of {@link ListMarkerRecord.text}, when the resolved
|
|
3021
|
+
* level carries `w:lvlPicBulletId` (§17.9.12) and the referenced `w:numPicBullet` resolved.
|
|
3022
|
+
*
|
|
3023
|
+
* Absent on every ordinary marker, and absent when the picture bullet is missing, external,
|
|
3024
|
+
* malformed or oversized — in which case `text` stands and the marker paints as the level's
|
|
3025
|
+
* `w:lvlText` did before. A sink that cannot draw the resource paints `text` too, which is
|
|
3026
|
+
* why both are published rather than one replacing the other.
|
|
3027
|
+
*/
|
|
3028
|
+
readonly picture?: ListMarkerPictureRecord;
|
|
3029
|
+
/**
|
|
3030
|
+
* In a right-to-left paragraph: `text` in VISUAL order, one piece per direction run, each
|
|
3031
|
+
* styled at its bidi level. Paint draws these instead of `text`, which stays logical.
|
|
3032
|
+
*/
|
|
3033
|
+
readonly pieces?: readonly Pick<ListMarkerRecord, 'text' | 'style' | 'box'>[];
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* The picture a `w:lvlPicBulletId` marker paints, with its geometry already decided.
|
|
3037
|
+
*
|
|
3038
|
+
* Geometry is in the same coordinate space as {@link ListMarkerRecord.box}: paint positions
|
|
3039
|
+
* from `box` and MUST NOT rescale it, neither from the resource's intrinsic pixels nor from
|
|
3040
|
+
* the authored `v:shape` extent, which the marker font size has already scaled. The image
|
|
3041
|
+
* sits with its BOTTOM on the first line's baseline, which is what makes the line tall
|
|
3042
|
+
* enough for it.
|
|
3043
|
+
*
|
|
3044
|
+
* `resource` is the same validated-image projection an inline drawing carries, resolved
|
|
3045
|
+
* through the numbering part's OWN relationships. A `ready` resource is the only one that
|
|
3046
|
+
* can be drawn; every other state is a clean fall back to the marker text.
|
|
3047
|
+
* @public
|
|
3048
|
+
*/
|
|
3049
|
+
interface ListMarkerPictureRecord {
|
|
3050
|
+
/** The part whose relationships `relationshipId` belongs to — the numbering part. */
|
|
3051
|
+
readonly ownerPartName: string;
|
|
3052
|
+
/** `v:imagedata/@r:id` of the `w:numPicBullet` shape. */
|
|
3053
|
+
readonly relationshipId: string;
|
|
3054
|
+
/** Where the image paints, authored extent, in the marker's coordinate space. */
|
|
3055
|
+
readonly box: LayoutBox;
|
|
3056
|
+
/** Validated image state, or why there is none. */
|
|
3057
|
+
readonly resource: ImageResourceState;
|
|
2985
3058
|
}
|
|
2986
3059
|
/**
|
|
2987
3060
|
* The part of one table that sits on one page.
|
|
@@ -3041,16 +3114,14 @@ interface TableRowFragmentRecord {
|
|
|
3041
3114
|
readonly rowIndex: number;
|
|
3042
3115
|
/** True when the authored row resolves `w:tblHeader`, including its first occurrence. */
|
|
3043
3116
|
readonly isHeaderRow: boolean;
|
|
3044
|
-
/**
|
|
3045
|
-
* True for a `w:tblHeader` row RE-EMITTED at the top of a continuation page. Painted,
|
|
3046
|
-
* but excluded from interaction walks so each caret stop exists exactly once.
|
|
3047
|
-
*/
|
|
3117
|
+
/** True for a `w:tblHeader` row repeated atop a continuation page; not an interaction stop. */
|
|
3048
3118
|
readonly isHeaderRepeat: boolean;
|
|
3049
|
-
/**
|
|
3050
|
-
* True when this record continues a row that already emitted content on a prior page
|
|
3051
|
-
* (cell content fragmented at a paragraph/line boundary). Same `id` as the lead fragment.
|
|
3052
|
-
*/
|
|
3119
|
+
/** True when this record continues a row split at a line boundary; same `id` as its head. */
|
|
3053
3120
|
readonly isContinuation?: boolean;
|
|
3121
|
+
/** True when the rest of this row continues in the next fragment: the head of a split row. */
|
|
3122
|
+
readonly hasContinuation?: boolean;
|
|
3123
|
+
/** True for a `w:cantSplit` or exact-height row: it stays whole where a page holds it. */
|
|
3124
|
+
readonly placesWhole?: boolean;
|
|
3054
3125
|
readonly cells: readonly TableCellFragmentRecord[];
|
|
3055
3126
|
readonly box: LayoutBox;
|
|
3056
3127
|
}
|
|
@@ -3181,13 +3252,17 @@ interface NoteAreaRecord {
|
|
|
3181
3252
|
readonly box: LayoutBox;
|
|
3182
3253
|
/** Separator rule / authored separator story; absent when no notes on this page. */
|
|
3183
3254
|
readonly separator?: {
|
|
3184
|
-
readonly kind: 'separator' | 'continuationSeparator';
|
|
3255
|
+
readonly kind: 'separator' | 'continuationSeparator' | 'continuationNotice';
|
|
3185
3256
|
readonly box: LayoutBox;
|
|
3186
3257
|
readonly fragments: readonly BlockFragmentRecord[];
|
|
3187
3258
|
readonly synthetic: boolean;
|
|
3188
3259
|
/** Layout-owned single/double rule when marker-only or synthetic; absent for authored stories. */
|
|
3189
3260
|
readonly ruleStyle?: 'single' | 'double';
|
|
3261
|
+
/** Resolved marker-run color; null/absent uses automatic black. */
|
|
3262
|
+
readonly ruleColor?: string | null;
|
|
3190
3263
|
};
|
|
3264
|
+
/** Authored furniture below a note that continues on the next page. */
|
|
3265
|
+
readonly continuationNotice?: NonNullable<NoteAreaRecord['separator']>;
|
|
3191
3266
|
readonly notes: readonly NoteStoryRecord[];
|
|
3192
3267
|
readonly fallbackReason?: string;
|
|
3193
3268
|
}
|
|
@@ -3287,6 +3362,8 @@ interface PageRecord {
|
|
|
3287
3362
|
* pages as free body space.
|
|
3288
3363
|
*/
|
|
3289
3364
|
readonly noteStream?: PageNoteStream;
|
|
3365
|
+
/** An empty sheet that gives the next section its page parity; SECTIONPAGES skips it. */
|
|
3366
|
+
readonly parityBlank?: true;
|
|
3290
3367
|
/**
|
|
3291
3368
|
* Section-local PAGE/SECTIONPAGES inputs for finalize. Absent → physical page index and
|
|
3292
3369
|
* document-wide section count (empty `w:pgNumType` behaviour).
|
|
@@ -3387,11 +3464,20 @@ interface TextMeasurer {
|
|
|
3387
3464
|
left: number;
|
|
3388
3465
|
right: number;
|
|
3389
3466
|
} | undefined;
|
|
3390
|
-
/**
|
|
3391
|
-
|
|
3467
|
+
/** Whether line metrics use an admitted face instead of approximate fallback metrics. */
|
|
3468
|
+
hasResolvedFont?(style: ResolvedRunStyle): boolean;
|
|
3469
|
+
/**
|
|
3470
|
+
* Optional `OS/2` strikeout stroke for the resolved face, at the drawn size. Word paints
|
|
3471
|
+
* `w:separator` as a strikeout, so the note rule reads its thickness and offset here rather
|
|
3472
|
+
* than assuming one face's numbers. Undefined keeps the caller's own default.
|
|
3473
|
+
*/
|
|
3474
|
+
strikeoutMetrics?(style: ResolvedRunStyle): StrikeoutStrokePt | undefined;
|
|
3475
|
+
/** Line metrics for the actual text faces; omitted text measures the primary face. */
|
|
3476
|
+
lineMetrics(style: ResolvedRunStyle, text?: string): {
|
|
3392
3477
|
height: number;
|
|
3393
3478
|
baseline: number;
|
|
3394
3479
|
};
|
|
3395
3480
|
}
|
|
3396
3481
|
|
|
3397
|
-
export {
|
|
3482
|
+
export { PARAGRAPH_BORDER_SIDES as a$, AUTO_PARAGRAPH_SPACING_PT as a3, HARD_MAX_AGGREGATE_FONT_BYTES as aF, HARD_MAX_FONT_BYTES as aG, HARD_MAX_FONT_SOURCES as aH, MAX_BORDER_SPACE_PT as aN, MAX_BORDER_WIDTH_PT as aO, MAX_LVL_OVERRIDES as aP, MAX_NUMBERING_DEFINITIONS as aQ, MAX_PARAGRAPH_SPACING_PT as aR, MAX_SHAPING_CONTEXT as aS, MAX_STYLE_DEFINITIONS as aT, MAX_TABLE_BORDER_STROKES as aU, MAX_TAB_POSITION_TWIPS as aV, MAX_TAB_STOPS as aW, PAGE_BORDER_SIDES as a_, DEFAULT_PAGE_GEOMETRY as ad, DEFAULT_RUN_STYLE as ae, DEFAULT_TAB_INTERVAL_PT as af, DEFAULT_TAB_INTERVAL_TWIPS as ag, EMPTY_NUMBERING_INDEX as al, EMPTY_TAB_STOPS as am, FontResolutionError as ax, boundedStructuralFontValidator as b$, TAB_LEADER_GLYPH as bI, UnsupportedScriptError as bT, appliedSpaceBefore as bV, applyLineSpacing as bW, baselineShiftPtOf as bX, borderExtentPt as bY, borderWeight as bZ, bottomBorderExtentPt as b_, SINGLE_LINE_SPACING as bo, buildNumberingIndex as c0, buildStyleCascadeTable as c1, cascadeParagraphFormatting as c2, cascadeRunProperties as c3, cascadedBottomBorder as c4, cascadedParagraphBorders as c5, cascadedTabStops as c6, collapsedSpaceBefore as c7, createFontResourceSnapshot as c8, createShapedRun as c9, readNumPr as cA, readTableBorders as cB, resolveBorderConflict as cC, resolveNumberingLevel as cD, resolveParagraphLayoutInputs as cE, resolveRunStyle as cF, resolveStoryListItems as cG, resolveStoryRefFields as cH, resolveTableCellBorderGrid as cI, runStylesEqual as cJ, shapedRunComparatorInputs as cK, shapingEnvironmentFingerprint as cL, shapingEnvironmentFingerprintInputs as cM, styleForFontSlot as cN, tabAdvanceWidth as cO, tabStopsFingerprint as cP, walkStoryParagraphs as cQ, withDefaultTabInterval as cR, withNumberingStyleLinks as cS, withResolvedListItems as cT, createShapingEnvironment as ca, defaultTabIntervalFromSettings as cb, displayText as cc, fixedPoint as cd, fontRequestKey as ce, glyphSizeFactorOf as cf, isValidStyleId as cg, itemizeScriptFontSlots as ch, layoutEquation as ci, measureDisplayText as cj, mergeListIndent as ck, nextTabDestination as cl, pageBordersFingerprint as cm, paragraphBorderExtentPt as cn, paragraphBorderStrokeWidthPt as co, paragraphBorders as cp, paragraphBordersFingerprint as cq, paragraphBreaksBefore as cr, paragraphContextualSpacing as cs, paragraphLineSpacing as ct, paragraphSpacing as cu, paragraphTabStops as cv, parsePageBorders as cw, parseRefInstruction as cx, readBorderSide as cy, readCellBorders as cz };
|
|
3483
|
+
export type { SpanLinkRecord as $, AnchoredDrawingRecord as A, BlockFragmentRecord as B, ContentControlBoundaryRecord as C, DrawingPoint as D, NoteAreaRecord as E, FontResourceSnapshot as F, NoteStoryRecord as G, HeaderFooterStoryRecord as H, InlineDrawingRecord as I, ContentControlLock as J, FontByteValidator as K, LineRecord as L, ModelRange as M, NormalizationPolicy as N, FixedPoint as O, PageGeometry as P, SourceRange as Q, ResolvedRunStyle as R, SemanticLayout as S, TextMeasurer as T, HyperlinkProjector as U, VersionedShapingLibrary as V, InlineDrawingLayoutContext as W, FieldLinkProjector as X, StoryPageFieldNeeds as Y, RefFieldContext as Z, HyperlinkFieldSpec as _, StyleSpanRecord as a, PageBorderFrameRecord as a0, ParagraphBorderStrokeRecord as a1, StrikeoutStrokePt as a2, AbstractNumDefinition as a4, AnchoredDrawingLayoutFallback as a5, BidiEmbeddingLevels as a6, BorderGridGeometry as a7, CascadedParagraphFormatting as a8, CjkTypographySettings as a9, FontResourceInstrumentation as aA, FontResourceSnapshotOptions as aB, FontSlot as aC, FontValidationResult as aD, GlyphOutline as aE, LevelOverride as aI, LineSpacingRule as aJ, ListMarkerAlign as aK, ListMarkerRecord as aL, ListSuffix as aM, NumDefinition as aX, NumberingLevelIndent as aY, NumberingPictureBullet as aZ, ContentControlGeometryFragment as aa, ContentControlLevel as ab, ContentControlMappedType as ac, DeclaredFontSubstitution as ah, DrawingClipFallback as ai, DrawingGeometry as aj, DrawingGroupPictureRecord as ak, EquationFractionGeometry as an, EquationGeometry as ao, EquationNaryGeometry as ap, EquationRadicalGeometry as aq, EquationRowGeometry as ar, EquationScriptGeometry as as, EquationSpanRecord as at, EquationTextGeometry as au, FieldAtomMarker as av, FontFingerprintInputs as aw, FontResolutionErrorCode as ay, FontResourceDefinition as az, SectionPageBorders as b, PageBorderDisplay as b0, PageBorderOffsetFrom as b1, PageBorderSide as b2, PageBorderStrokeRecord as b3, PageBorderZOrder as b4, PageRefCalibrationCell as b5, PageRefFieldProjection as b6, ParagraphAutoSpacingContext as b7, ParagraphBorderEdge as b8, ParagraphBorderSide as b9, ShapedFontSpan as bA, ShapedGlyph as bB, ShapedRunComparatorInputs as bC, ShapedVerticalMetrics as bD, ShapingContext as bE, ShapingEnvironment as bF, ShapingEnvironmentFingerprintInputs as bG, StyleDefinition as bH, TabAlignment as bJ, TabDestination as bK, TabLeader as bL, TabStop as bM, TableBorderSideName as bN, TableBorderStrokeRecord as bO, TableFragmentRecord as bP, TextDirection as bQ, TextboxStoryFallbackReason as bR, TextboxStoryLayout as bS, VerticalAlign as bU, ParagraphBorders as ba, ParagraphBottomBorderRecord as bb, ParagraphIndent as bc, ParagraphLayoutInputs as bd, ParagraphLineSpacing as be, ParagraphSpacing as bf, RefFieldSpec as bg, RefNoteParts as bh, ResolvedCellBorders as bi, ResolvedPictureBullet as bj, ResolvedTabStops as bk, ResolvedTableBorderEdge as bl, ResolvedTableBorderEdgeSegment as bm, ResolvedUnderline as bn, ScriptItem as bp, SemanticArtifactRootStoryKind as bq, SemanticArtifactStoryKind as br, SemanticCommentArtifactRecord as bs, SemanticReviewArtifactOccurrence as bt, SemanticReviewArtifactOccurrenceGeometry as bu, SemanticReviewArtifactPosition as bv, SemanticReviewArtifactSource as bw, SemanticTrackedChangeArtifactRecord as bx, ShapeInput as by, ShapedCluster as bz, TableRowFragmentRecord as c, TableCellFragmentRecord as d, ShapingEnvironmentInput as e, TextShaper as f, ResolvedFont as g, FixedPointRoundingMode as h, FontRequest as i, SemanticReviewArtifactRecord as j, ListMarkerPictureRecord as k, FontSubstitution as l, ShapedRun as m, NumberingLevel as n, NumberingIndex as o, ParagraphFragmentRecord as p, LayoutBox as q, DrawingInsets as r, TableBorderSide as s, PageRecord as t, ResolvedListItem as u, CellBorderBox as v, TableCellStyleFormatting as w, TableFloatPosition as x, TableBorderBox as y, StyleCascadeTable as z };
|