@arbidocs/blocks 0.4.100 → 0.4.102
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/dist/agents/index.cjs +207 -207
- package/dist/agents/index.js +2 -2
- package/dist/{chunk-NJX774HB.js → chunk-5NQIBJPP.js} +3 -3
- package/dist/{chunk-NJX774HB.js.map → chunk-5NQIBJPP.js.map} +1 -1
- package/dist/chunk-6MG62I5B.cjs +272 -0
- package/dist/chunk-6MG62I5B.cjs.map +1 -0
- package/dist/{chunk-NW3LFDZL.cjs → chunk-DDYXFALG.cjs} +3 -3
- package/dist/{chunk-NW3LFDZL.cjs.map → chunk-DDYXFALG.cjs.map} +1 -1
- package/dist/chunk-EQB3WELX.js +269 -0
- package/dist/chunk-EQB3WELX.js.map +1 -0
- package/dist/{chunk-B7P73VHX.cjs → chunk-FAGQTB3D.cjs} +30 -214
- package/dist/chunk-FAGQTB3D.cjs.map +1 -0
- package/dist/{chunk-6HZNFOHW.js → chunk-G5JH2YMQ.js} +3 -3
- package/dist/{chunk-6HZNFOHW.js.map → chunk-G5JH2YMQ.js.map} +1 -1
- package/dist/{chunk-72PQ42YI.js → chunk-MYALCKDR.js} +34 -217
- package/dist/chunk-MYALCKDR.js.map +1 -0
- package/dist/{chunk-O24WPHLD.cjs → chunk-Y5XW57U4.cjs} +5 -5
- package/dist/{chunk-O24WPHLD.cjs.map → chunk-Y5XW57U4.cjs.map} +1 -1
- package/dist/{chunk-E2QWWLK5.js → chunk-YGREW5TZ.js} +3 -3
- package/dist/{chunk-E2QWWLK5.js.map → chunk-YGREW5TZ.js.map} +1 -1
- package/dist/{chunk-IE55TWIG.cjs → chunk-ZT2EBBSC.cjs} +5 -5
- package/dist/{chunk-IE55TWIG.cjs.map → chunk-ZT2EBBSC.cjs.map} +1 -1
- package/dist/connect/docx.cjs +6 -6
- package/dist/connect/docx.js +4 -4
- package/dist/connect/index.cjs +3 -3
- package/dist/connect/index.js +2 -2
- package/dist/document-tools/AdoptSignatureDialog.d.ts +25 -0
- package/dist/document-tools/DocumentToolButtons.d.ts +2 -0
- package/dist/document-tools/SignatureRequestBanner.d.ts +18 -0
- package/dist/document-tools/index.cjs +175 -0
- package/dist/document-tools/index.cjs.map +1 -1
- package/dist/document-tools/index.d.ts +3 -1
- package/dist/document-tools/index.js +168 -3
- package/dist/document-tools/index.js.map +1 -1
- package/dist/document-tools/viewer/GuidedSigningViewer.d.ts +26 -0
- package/dist/document-tools/viewer/SignatureFieldsOverlay.d.ts +41 -0
- package/dist/document-tools/viewer/SignatureRequestEditor.d.ts +26 -0
- package/dist/document-tools/viewer/index.cjs +983 -0
- package/dist/document-tools/viewer/index.cjs.map +1 -1
- package/dist/document-tools/viewer/index.d.ts +3 -0
- package/dist/document-tools/viewer/index.js +983 -5
- package/dist/document-tools/viewer/index.js.map +1 -1
- package/dist/docx/NativeDocxEditor.d.ts +1 -1
- package/dist/docx/definitions/DefinedTerms.d.ts +1 -12
- package/dist/docx/definitions/commands.d.ts +3 -27
- package/dist/docx/index.cjs +35 -39
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/index.js +3 -3
- package/dist/facts/index.cjs +1 -1
- package/dist/facts/index.js +1 -1
- package/dist/files/index.cjs +1 -1
- package/dist/files/index.js +1 -1
- package/dist/home/index.cjs +1 -1
- package/dist/home/index.js +1 -1
- package/dist/index.cjs +253 -253
- package/dist/index.js +3 -3
- package/dist/shell/index.cjs +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/studio/index.cjs +1 -1
- package/dist/studio/index.js +1 -1
- package/dist/workflow-canvas/index.cjs +9 -9
- package/dist/workflow-canvas/index.js +2 -2
- package/node_modules/@arbidocs/coedit/LICENSE +21 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.cjs +751 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.cjs.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.d.cts +192 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.d.ts +192 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.js +716 -0
- package/node_modules/@arbidocs/coedit/dist/encoding.js.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.cjs +1759 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.cjs.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.d.cts +39 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.d.ts +39 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.js +1736 -0
- package/node_modules/@arbidocs/coedit/dist/hocuspocus.js.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/index.cjs +1692 -0
- package/node_modules/@arbidocs/coedit/dist/index.cjs.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/index.d.cts +16 -0
- package/node_modules/@arbidocs/coedit/dist/index.d.ts +16 -0
- package/node_modules/@arbidocs/coedit/dist/index.js +1664 -0
- package/node_modules/@arbidocs/coedit/dist/index.js.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/server.cjs +1852 -0
- package/node_modules/@arbidocs/coedit/dist/server.cjs.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/server.d.cts +43 -0
- package/node_modules/@arbidocs/coedit/dist/server.d.ts +43 -0
- package/node_modules/@arbidocs/coedit/dist/server.js +1822 -0
- package/node_modules/@arbidocs/coedit/dist/server.js.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/session-Cf9iu1Vt.d.cts +82 -0
- package/node_modules/@arbidocs/coedit/dist/session-Cf9iu1Vt.d.ts +82 -0
- package/node_modules/@arbidocs/coedit/dist/template.cjs +1627 -0
- package/node_modules/@arbidocs/coedit/dist/template.cjs.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/template.d.cts +125 -0
- package/node_modules/@arbidocs/coedit/dist/template.d.ts +125 -0
- package/node_modules/@arbidocs/coedit/dist/template.js +1612 -0
- package/node_modules/@arbidocs/coedit/dist/template.js.map +1 -0
- package/node_modules/@arbidocs/coedit/dist/versions-DXiFQMA6.d.cts +8 -0
- package/node_modules/@arbidocs/coedit/dist/versions-DXiFQMA6.d.ts +8 -0
- package/node_modules/@arbidocs/coedit/package.json +68 -0
- package/node_modules/@docx-editor.dev/core/LICENSE +201 -0
- package/node_modules/@docx-editor.dev/core/README.md +125 -0
- package/node_modules/@docx-editor.dev/core/THIRD_PARTY_NOTICES.md +56 -0
- package/node_modules/@docx-editor.dev/core/dist/automation.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/automation.d.ts +162 -0
- package/node_modules/@docx-editor.dev/core/dist/automation.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/binding.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/binding.d.ts +164 -0
- package/node_modules/@docx-editor.dev/core/dist/binding.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-2GO5T2JU.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-2RC2EPND.js +3 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-4GBTELJV.cjs +28 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-4R3TEXMN.js +18 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-5CSUYANV.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-6APGXLBB.cjs +3 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-6AWEKBLB.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-6EYYL7FL.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-6JIJOZDW.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-BTQKU64Q.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-E7BQA6UP.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-EO4C5QJW.cjs +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-ETTO6GK3.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-FIJZIQXA.cjs +18 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-FRXAOVTB.js +28 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-J6BVWIDB.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-JGNVNT2A.cjs +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-K5U2PLDP.cjs +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-KKIJG3C6.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-LWDSDCMO.js +27 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-NBCVVZOM.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-O2AL2HSJ.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-O5INAWZQ.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-QMLCVPCD.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-RCHS4ZEH.cjs +27 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-RIYSCEFQ.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-S6J4O5XP.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-SRPILEWV.cjs +5 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-TSLBZ26X.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-UKVVUHZL.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-VDNNLG6X.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-VHAVHCQY.js +7 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-VX43VBAX.cjs +7 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-Z6WBFXJU.js +5 -0
- package/node_modules/@docx-editor.dev/core/dist/chunk-ZUHOMK52.cjs +2 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.d.ts +270 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration/replication.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration.d.ts +269 -0
- package/node_modules/@docx-editor.dev/core/dist/collaboration.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/document.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/document.d.ts +290 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/document.js +0 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/editor.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/editor.d.ts +22 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/editor.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.d.ts +103 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/interaction.js +0 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/modules.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/modules.d.ts +143 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/modules.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/types.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/types.d.ts +11 -0
- package/node_modules/@docx-editor.dev/core/dist/contracts/types.js +0 -0
- package/node_modules/@docx-editor.dev/core/dist/custom-node-writes-cvL5rhcD.d.ts +323 -0
- package/node_modules/@docx-editor.dev/core/dist/dist-PKZR6RSH.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/document-protection-BAGVGqaP.d.ts +20 -0
- package/node_modules/@docx-editor.dev/core/dist/editor-Ci7IZGmG.d.ts +2448 -0
- package/node_modules/@docx-editor.dev/core/dist/editor-scope-OHPwVQ5l.d.ts +84 -0
- package/node_modules/@docx-editor.dev/core/dist/editor.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/editor.css +15 -0
- package/node_modules/@docx-editor.dev/core/dist/editor.d.ts +1394 -0
- package/node_modules/@docx-editor.dev/core/dist/editor.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/export.cjs +2 -0
- package/node_modules/@docx-editor.dev/core/dist/export.d.ts +391 -0
- package/node_modules/@docx-editor.dev/core/dist/export.js +2 -0
- package/node_modules/@docx-editor.dev/core/dist/failure-NS2lsya4.d.ts +115 -0
- package/node_modules/@docx-editor.dev/core/dist/font-resolver-BVQB14qT.d.ts +256 -0
- package/node_modules/@docx-editor.dev/core/dist/harfbuzz.wasm +0 -0
- package/node_modules/@docx-editor.dev/core/dist/headless-document-view-qH331moE.d.ts +143 -0
- package/node_modules/@docx-editor.dev/core/dist/image-resources-BN-hUgCn.d.ts +211 -0
- package/node_modules/@docx-editor.dev/core/dist/index-Bsg7oIKP.d.ts +3869 -0
- package/node_modules/@docx-editor.dev/core/dist/index.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/index.d.ts +53 -0
- package/node_modules/@docx-editor.dev/core/dist/index.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/layout-shaping-BEyuiQOk.d.ts +246 -0
- package/node_modules/@docx-editor.dev/core/dist/layout.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/layout.d.ts +3151 -0
- package/node_modules/@docx-editor.dev/core/dist/layout.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/node-module-shim-TTKTT66I.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/note-properties-4Y9o6Gyk.d.ts +68 -0
- package/node_modules/@docx-editor.dev/core/dist/omml-equation-CuzBL5DD.d.ts +85 -0
- package/node_modules/@docx-editor.dev/core/dist/output.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/output.d.ts +97 -0
- package/node_modules/@docx-editor.dev/core/dist/output.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/protocol-Bqk4Izjc.d.ts +1651 -0
- package/node_modules/@docx-editor.dev/core/dist/review-pane-D9jm8DcY.d.ts +128 -0
- package/node_modules/@docx-editor.dev/core/dist/review-paragraph-order-DT-n6ouC.d.ts +85 -0
- package/node_modules/@docx-editor.dev/core/dist/revision-projection-DZ4kKVev.d.ts +242 -0
- package/node_modules/@docx-editor.dev/core/dist/semantic-cell-selection-D9D1KSXq.d.ts +464 -0
- package/node_modules/@docx-editor.dev/core/dist/semantic-interaction-CIC-PooQ.d.ts +135 -0
- package/node_modules/@docx-editor.dev/core/dist/semantic-paint-BME37OMv.d.ts +326 -0
- package/node_modules/@docx-editor.dev/core/dist/semantic-record-queries-CBlZqZNa.d.ts +177 -0
- package/node_modules/@docx-editor.dev/core/dist/semantic-records-4B3WwFAi.d.ts +3532 -0
- package/node_modules/@docx-editor.dev/core/dist/store.cjs +1 -0
- package/node_modules/@docx-editor.dev/core/dist/store.d.ts +2706 -0
- package/node_modules/@docx-editor.dev/core/dist/store.js +1 -0
- package/node_modules/@docx-editor.dev/core/dist/tree-package-fragment-BeeKxDw8.d.ts +1201 -0
- package/node_modules/@docx-editor.dev/core/dist/tree-session-contract-CVbmlMlq.d.ts +664 -0
- package/node_modules/@docx-editor.dev/core/dist/types-barrel-D3xCgQX6.d.ts +4269 -0
- package/node_modules/@docx-editor.dev/core/licenses/HarfBuzz-COPYING.txt +42 -0
- package/node_modules/@docx-editor.dev/core/package.json +140 -0
- package/node_modules/@docx-editor.dev/react/LICENSE +201 -0
- package/node_modules/@docx-editor.dev/react/README.md +141 -0
- package/node_modules/@docx-editor.dev/react/THIRD_PARTY_NOTICES.md +18 -0
- package/node_modules/@docx-editor.dev/react/dist/index.d.mts +4928 -0
- package/node_modules/@docx-editor.dev/react/dist/index.d.ts +4928 -0
- package/node_modules/@docx-editor.dev/react/dist/index.js +4 -0
- package/node_modules/@docx-editor.dev/react/dist/index.mjs +4 -0
- package/node_modules/@docx-editor.dev/react/package.json +74 -0
- package/node_modules/@docx-editor.dev/react/src/styles/editor.css +6 -0
- package/node_modules/@docx-editor.dev/react/src/styles/zIndex.ts +28 -0
- package/package.json +26 -15
- package/src/docx/README.md +2 -3
- package/dist/chunk-72PQ42YI.js.map +0 -1
- package/dist/chunk-B7P73VHX.cjs.map +0 -1
|
@@ -0,0 +1,3532 @@
|
|
|
1
|
+
import { o as RevisionAttribution, b as RevisionDisplayMode } from './revision-projection-DZ4kKVev.js';
|
|
2
|
+
import { X as OoxmlProperty, Z as OoxmlElement, N as OoxmlNode, O as OoxmlPart, aJ as TableBorderStyle, aK as DrawingAccessibility, aL as DrawingImageEffects, al as SourceCrop, aM as DrawingTransform, aN as VectorShapeProjection, aw as DrawingHorizontalReferenceFrame, ax as DrawingVerticalReferenceFrame, an as ImageWrapTarget, a0 as DrawingProjection, aO as OoxmlDrawingNode } from './types-barrel-D3xCgQX6.js';
|
|
3
|
+
import { b as ImageResourceState } from './image-resources-BN-hUgCn.js';
|
|
4
|
+
import { O as OmmlEquationProjection } from './omml-equation-CuzBL5DD.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
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Soft ceiling on abstractNum / num entries read from one part. */
|
|
85
|
+
declare const MAX_NUMBERING_DEFINITIONS = 512;
|
|
86
|
+
/** Soft ceiling on override entries per `w:num`. */
|
|
87
|
+
declare const MAX_LVL_OVERRIDES = 9;
|
|
88
|
+
/** `w:suff` — what separates a list marker from the text after it. */
|
|
89
|
+
type ListSuffix = 'tab' | 'space' | 'nothing';
|
|
90
|
+
/** `w:lvlJc` — how a list marker aligns within its own indent. */
|
|
91
|
+
type ListMarkerAlign = 'left' | 'center' | 'right';
|
|
92
|
+
/**
|
|
93
|
+
* One level's indent, plus which parts the level actually AUTHORED.
|
|
94
|
+
*
|
|
95
|
+
* The provenance matters: `w:ind` cascades per-attribute, so a level that authored only `left`
|
|
96
|
+
* must not overwrite an inherited `hanging` with a synthesized zero.
|
|
97
|
+
*/
|
|
98
|
+
interface NumberingLevelIndent {
|
|
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
|
+
*/
|
|
103
|
+
readonly authored?: {
|
|
104
|
+
readonly left?: number;
|
|
105
|
+
readonly right?: number;
|
|
106
|
+
readonly start?: number;
|
|
107
|
+
readonly end?: number;
|
|
108
|
+
};
|
|
109
|
+
readonly left: number;
|
|
110
|
+
readonly right: number;
|
|
111
|
+
readonly hanging: number;
|
|
112
|
+
readonly firstLine: number;
|
|
113
|
+
/**
|
|
114
|
+
* Which of these the LEVEL actually authored.
|
|
115
|
+
*
|
|
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
|
|
119
|
+
* hand-built level in a test means.
|
|
120
|
+
*/
|
|
121
|
+
readonly stated?: {
|
|
122
|
+
readonly left: boolean;
|
|
123
|
+
readonly right: boolean;
|
|
124
|
+
/** `w:hanging`/`w:firstLine` are one mutually exclusive slot (§17.3.1.10, §17.3.1.12). */
|
|
125
|
+
readonly firstLineOffset: boolean;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** One `w:lvl`: how this depth numbers, what its marker looks like, and how it indents. */
|
|
129
|
+
interface NumberingLevel {
|
|
130
|
+
readonly ilvl: number;
|
|
131
|
+
readonly start: number;
|
|
132
|
+
readonly numFmt: string;
|
|
133
|
+
readonly lvlText: string;
|
|
134
|
+
readonly lvlJc: ListMarkerAlign;
|
|
135
|
+
readonly suff: ListSuffix;
|
|
136
|
+
readonly indent: NumberingLevelIndent;
|
|
137
|
+
/**
|
|
138
|
+
* `w:lvlRestart` one-based trigger level, or `0` when the level never restarts.
|
|
139
|
+
* Omitted in XML → restart when the previous level (or any earlier level) is used.
|
|
140
|
+
*/
|
|
141
|
+
readonly lvlRestart?: number;
|
|
142
|
+
/**
|
|
143
|
+
* `w:isLgl` (§17.9.9): render EVERY level referenced by this level's `w:lvlText` in
|
|
144
|
+
* decimal, whatever number format those levels declare for themselves.
|
|
145
|
+
*/
|
|
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;
|
|
152
|
+
/** Level `w:rPr` as flat properties (for marker face / vanish). */
|
|
153
|
+
readonly runProperties: readonly OoxmlProperty[];
|
|
154
|
+
/** True when level run props request vanish — marker must not paint. */
|
|
155
|
+
readonly vanish: boolean;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* One `w:lvlOverride` on a `w:num`: a restart value, a replacement level, or both.
|
|
159
|
+
*
|
|
160
|
+
* How two lists share an abstract definition while numbering independently.
|
|
161
|
+
*/
|
|
162
|
+
interface LevelOverride {
|
|
163
|
+
readonly startOverride?: number;
|
|
164
|
+
/** Full level replacement when `w:lvl` is present under the override. */
|
|
165
|
+
readonly level?: NumberingLevel;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* One `w:abstractNum` — the shape of a list, without being a list.
|
|
169
|
+
*
|
|
170
|
+
* Never referenced by a paragraph directly. Paragraphs name a {@link NumDefinition}, which names
|
|
171
|
+
* one of these, so several lists can share a definition and still count separately.
|
|
172
|
+
*/
|
|
173
|
+
interface AbstractNumDefinition {
|
|
174
|
+
readonly abstractNumId: string;
|
|
175
|
+
readonly levels: ReadonlyMap<number, NumberingLevel>;
|
|
176
|
+
/**
|
|
177
|
+
* `w:numStyleLink` (§17.9.21): this definition carries no levels of its own — the numbering
|
|
178
|
+
* lives on the named style. Word follows the link; resolve it with
|
|
179
|
+
* {@link resolveNumberingStyleLinks} before reading levels, or every paragraph on this
|
|
180
|
+
* abstract renders with NO marker at all.
|
|
181
|
+
*/
|
|
182
|
+
readonly numStyleLink?: string;
|
|
183
|
+
/**
|
|
184
|
+
* `w:styleLink` (§17.9.23): the definition side of the same pair — this abstract IS the
|
|
185
|
+
* numbering of the named style. Kept so a link can be verified rather than assumed.
|
|
186
|
+
*/
|
|
187
|
+
readonly styleLink?: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* One `w:num` — the thing a paragraph's `w:numId` actually names.
|
|
191
|
+
*
|
|
192
|
+
* Points at an {@link AbstractNumDefinition} and may override any of its levels.
|
|
193
|
+
*/
|
|
194
|
+
interface NumDefinition {
|
|
195
|
+
readonly numId: string;
|
|
196
|
+
readonly abstractNumId: string;
|
|
197
|
+
readonly overrides: ReadonlyMap<number, LevelOverride>;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The bounded projection of `numbering.xml` that list layout resolves against.
|
|
201
|
+
*
|
|
202
|
+
* Projection ONLY — never a mutation or serialization authority. Hostile values are dropped or
|
|
203
|
+
* clamped, and a missing definition resolves to "no list" rather than a guess.
|
|
204
|
+
*/
|
|
205
|
+
interface NumberingIndex {
|
|
206
|
+
readonly abstractNums: ReadonlyMap<string, AbstractNumDefinition>;
|
|
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>;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Build a numbering index from the root of a numbering part (`w:numbering`).
|
|
218
|
+
*
|
|
219
|
+
* Empty / missing roots yield an empty index. Duplicate ids keep the first definition.
|
|
220
|
+
*/
|
|
221
|
+
/**
|
|
222
|
+
* Project `numbering.xml` into the bounded index.
|
|
223
|
+
*
|
|
224
|
+
* Every ceiling here exists because the input is file-derived: definition counts, override
|
|
225
|
+
* counts, indent magnitudes and style-link hop depth are all capped, and nothing from the file
|
|
226
|
+
* becomes a loop bound or an allocation size.
|
|
227
|
+
*/
|
|
228
|
+
declare function buildNumberingIndex(root: OoxmlElement | null | undefined): NumberingIndex;
|
|
229
|
+
/** Resolve the effective level for a `numId` + `ilvl`, applying overrides. */
|
|
230
|
+
/**
|
|
231
|
+
* The effective level for one paragraph's numbering reference, after overrides and style links.
|
|
232
|
+
*
|
|
233
|
+
* Answers null for a reference the index cannot resolve — a paragraph naming a definition the
|
|
234
|
+
* file never declared is an unnumbered paragraph, not an error.
|
|
235
|
+
*/
|
|
236
|
+
declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilvl: number): {
|
|
237
|
+
readonly abstractNumId: string;
|
|
238
|
+
readonly level: NumberingLevel;
|
|
239
|
+
readonly startOverride?: number;
|
|
240
|
+
} | null;
|
|
241
|
+
/** Empty index for tests / documents without numbering. */
|
|
242
|
+
declare const EMPTY_NUMBERING_INDEX: NumberingIndex;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A face, as something asks for it: family plus the two axes this engine admits.
|
|
246
|
+
*
|
|
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.
|
|
250
|
+
*/
|
|
251
|
+
interface FontRequest {
|
|
252
|
+
readonly family: string;
|
|
253
|
+
readonly weight: number;
|
|
254
|
+
readonly style: 'normal' | 'italic';
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A request that was answered by a DIFFERENT face than the one asked for.
|
|
258
|
+
*
|
|
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.
|
|
262
|
+
*/
|
|
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;
|
|
272
|
+
/**
|
|
273
|
+
* A face that passed admission: the bytes are present, within limits, hash-verified, and parse as
|
|
274
|
+
* a font.
|
|
275
|
+
*
|
|
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.
|
|
278
|
+
*/
|
|
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;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
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.
|
|
297
|
+
*/
|
|
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;
|
|
303
|
+
/**
|
|
304
|
+
* Why a face was not admitted.
|
|
305
|
+
*
|
|
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.
|
|
309
|
+
*/
|
|
310
|
+
type FontResolutionErrorCode = 'missing' | 'forbidden' | 'overLimit' | 'malformed' | 'hashMismatch';
|
|
311
|
+
/**
|
|
312
|
+
* A face that could not be admitted, carrying whatever evidence the refusal produced.
|
|
313
|
+
*
|
|
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.
|
|
317
|
+
*/
|
|
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
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* One face offered to a snapshot, before admission.
|
|
337
|
+
*
|
|
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`.
|
|
352
|
+
*
|
|
353
|
+
* How a metric-compatible substitute is wired in — a document naming Calibri resolves to Carlito
|
|
354
|
+
* without the document being rewritten.
|
|
355
|
+
*/
|
|
356
|
+
interface DeclaredFontSubstitution {
|
|
357
|
+
readonly from: FontRequest;
|
|
358
|
+
readonly to: FontRequest;
|
|
359
|
+
readonly lineMetrics?: {
|
|
360
|
+
readonly heightEm: number;
|
|
361
|
+
readonly baselineEm: number;
|
|
362
|
+
};
|
|
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
|
+
};
|
|
371
|
+
/**
|
|
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.
|
|
376
|
+
*/
|
|
377
|
+
type FontByteValidator = (bytes: Uint8Array, faceIndex: number) => FontValidationResult;
|
|
378
|
+
/**
|
|
379
|
+
* An immutable set of admitted faces, and the only way to reach one.
|
|
380
|
+
*
|
|
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.
|
|
384
|
+
*/
|
|
385
|
+
interface FontResourceSnapshot {
|
|
386
|
+
readonly epoch: number;
|
|
387
|
+
/** The admitted face, or a typed refusal. Never throws. */
|
|
388
|
+
resolve(request: FontRequest): ResolvedFont | FontResolutionError;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
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.
|
|
395
|
+
*/
|
|
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;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Optional counters for the operations font admission is expected to do RARELY.
|
|
406
|
+
*
|
|
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.
|
|
410
|
+
*/
|
|
411
|
+
interface FontResourceInstrumentation {
|
|
412
|
+
readonly onOwnedByteCopy?: () => void;
|
|
413
|
+
readonly onHash?: () => void;
|
|
414
|
+
readonly onTableScan?: () => void;
|
|
415
|
+
readonly onAdmission?: () => void;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* The canonical string identifying one face request.
|
|
419
|
+
*
|
|
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.
|
|
422
|
+
*/
|
|
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;
|
|
426
|
+
/**
|
|
427
|
+
* Admit a set of faces and return the immutable snapshot layout resolves against.
|
|
428
|
+
*
|
|
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".
|
|
433
|
+
*/
|
|
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
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Brand a safe integer as a {@link FixedPoint} coordinate.
|
|
443
|
+
*
|
|
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.
|
|
449
|
+
*/
|
|
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';
|
|
457
|
+
/**
|
|
458
|
+
* The shaping library and its exact version.
|
|
459
|
+
*
|
|
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.
|
|
462
|
+
*/
|
|
463
|
+
interface VersionedShapingLibrary {
|
|
464
|
+
readonly name: string;
|
|
465
|
+
readonly version: string;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Everything that determines how text shapes — the complete input to a
|
|
469
|
+
* {@link ShapingEnvironment}.
|
|
470
|
+
*
|
|
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.
|
|
473
|
+
*/
|
|
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;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* A validated {@link ShapingEnvironmentInput} — build one with `createShapingEnvironment`.
|
|
490
|
+
*
|
|
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.
|
|
493
|
+
*/
|
|
494
|
+
interface ShapingEnvironment extends ShapingEnvironmentInput {
|
|
495
|
+
}
|
|
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;
|
|
518
|
+
/**
|
|
519
|
+
* One positioned glyph.
|
|
520
|
+
*
|
|
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.
|
|
524
|
+
*/
|
|
525
|
+
interface ShapedGlyph {
|
|
526
|
+
readonly id: number;
|
|
527
|
+
/** Optional synthetic glyph-size factor; positions and advances already include it. */
|
|
528
|
+
readonly drawScale?: number;
|
|
529
|
+
/** UTF-16 text offset identifying the cluster that produced this glyph. */
|
|
530
|
+
readonly cluster: number;
|
|
531
|
+
/** Pen origin before this glyph's shaping offsets, in fixed-point run coordinates. */
|
|
532
|
+
readonly originX: FixedPoint;
|
|
533
|
+
readonly originY: FixedPoint;
|
|
534
|
+
readonly advanceX: FixedPoint;
|
|
535
|
+
readonly advanceY: FixedPoint;
|
|
536
|
+
readonly offsetX: FixedPoint;
|
|
537
|
+
readonly offsetY: FixedPoint;
|
|
538
|
+
/** Exact monochrome outline returned by the admitted HarfBuzz face. */
|
|
539
|
+
readonly outline: GlyphOutline;
|
|
540
|
+
}
|
|
541
|
+
/** A glyph's outline as SVG path data, in font design units. */
|
|
542
|
+
interface GlyphOutline {
|
|
543
|
+
readonly path: string;
|
|
544
|
+
/** Design units per em — divide by this to scale the path to a point size. */
|
|
545
|
+
readonly unitsPerEm: number;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* One cluster: the smallest indivisible text-to-glyph correspondence.
|
|
549
|
+
*
|
|
550
|
+
* The unit the CARET moves by. A cluster may be several characters (a ligature) or several glyphs
|
|
551
|
+
* (a decomposed mark), so neither a character index nor a glyph index is a valid caret position —
|
|
552
|
+
* `caretEdges` is, and it includes both endpoints.
|
|
553
|
+
*/
|
|
554
|
+
interface ShapedCluster {
|
|
555
|
+
/** Half-open logical UTF-16 range in ShapedRun.text. */
|
|
556
|
+
readonly textStart: number;
|
|
557
|
+
readonly textEnd: number;
|
|
558
|
+
/** Half-open visual glyph range in ShapedRun.glyphs. */
|
|
559
|
+
readonly glyphStart: number;
|
|
560
|
+
readonly glyphEnd: number;
|
|
561
|
+
readonly advance: FixedPoint;
|
|
562
|
+
/** Fixed-point edges from this cluster's visual origin, including both endpoints. */
|
|
563
|
+
readonly caretEdges: readonly FixedPoint[];
|
|
564
|
+
/** Index into ShapedRun.fontSpans, preserving the exact fallback choice. */
|
|
565
|
+
readonly fontSpan: number;
|
|
566
|
+
}
|
|
567
|
+
/** A run's vertical metrics, from the face that shaped it. Drives line height and baseline. */
|
|
568
|
+
interface ShapedVerticalMetrics {
|
|
569
|
+
readonly ascent: FixedPoint;
|
|
570
|
+
readonly descent: FixedPoint;
|
|
571
|
+
readonly lineGap: FixedPoint;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* A stretch of glyphs that came from ONE face.
|
|
575
|
+
*
|
|
576
|
+
* A run whose text needed fallback has several of these. Recording the exact face per span is
|
|
577
|
+
* what lets a re-shape reproduce the same result rather than re-running fallback selection and
|
|
578
|
+
* possibly choosing differently.
|
|
579
|
+
*/
|
|
580
|
+
interface ShapedFontSpan {
|
|
581
|
+
readonly glyphStart: number;
|
|
582
|
+
readonly glyphEnd: number;
|
|
583
|
+
readonly font: ResolvedFont;
|
|
584
|
+
/** null denotes the primary font; otherwise this is the environment fallback-order index. */
|
|
585
|
+
readonly fallbackIndex: number | null;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* One shaped run: text turned into positioned glyphs, with everything needed to measure it, paint
|
|
589
|
+
* it, and put a caret in it.
|
|
590
|
+
*
|
|
591
|
+
* The engine's measurement unit. Layout never measures characters — it measures these.
|
|
592
|
+
*/
|
|
593
|
+
interface ShapedRun {
|
|
594
|
+
readonly text: string;
|
|
595
|
+
readonly direction: TextDirection;
|
|
596
|
+
readonly bidiLevel: number;
|
|
597
|
+
readonly glyphs: readonly ShapedGlyph[];
|
|
598
|
+
readonly clusters: readonly ShapedCluster[];
|
|
599
|
+
readonly fontSpans: readonly ShapedFontSpan[];
|
|
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[];
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* A {@link ShapedRun} reduced to the fields two shaping results must agree on to be considered
|
|
617
|
+
* identical.
|
|
618
|
+
*
|
|
619
|
+
* Fonts appear as {@link FontFingerprintInputs} rather than whole `ResolvedFont` objects, so the
|
|
620
|
+
* comparison is over VALUES and does not depend on object identity — which is what makes it work
|
|
621
|
+
* across a reload or a worker boundary.
|
|
622
|
+
*/
|
|
623
|
+
interface ShapedRunComparatorInputs {
|
|
624
|
+
readonly text: string;
|
|
625
|
+
readonly direction: TextDirection;
|
|
626
|
+
readonly script: string;
|
|
627
|
+
readonly language: string;
|
|
628
|
+
readonly bidiLevel: number;
|
|
629
|
+
readonly glyphs: readonly ShapedGlyph[];
|
|
630
|
+
readonly clusters: readonly ShapedCluster[];
|
|
631
|
+
readonly fontSpans: readonly {
|
|
632
|
+
readonly glyphStart: number;
|
|
633
|
+
readonly glyphEnd: number;
|
|
634
|
+
readonly fallbackIndex: number | null;
|
|
635
|
+
readonly font: FontFingerprintInputs;
|
|
636
|
+
}[];
|
|
637
|
+
readonly metrics: ShapedVerticalMetrics;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* The one thing layout needs from a shaping backend.
|
|
641
|
+
*
|
|
642
|
+
* Injected rather than imported, which is what keeps layout DOM-free and testable: a fixed-metric
|
|
643
|
+
* shaper measures deterministically in a test, HarfBuzz measures for real in a browser, and
|
|
644
|
+
* layout cannot tell the difference.
|
|
645
|
+
*/
|
|
646
|
+
interface TextShaper {
|
|
647
|
+
shape(input: ShapeInput): ShapedRun;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* A font reduced to the values that identify it for fingerprinting.
|
|
651
|
+
*
|
|
652
|
+
* Includes the content `hash` and `faceIndex`, so two faces with the same family name but
|
|
653
|
+
* different bytes fingerprint differently — which is what stops a cached measurement being reused
|
|
654
|
+
* against a substituted face.
|
|
655
|
+
*/
|
|
656
|
+
interface FontFingerprintInputs {
|
|
657
|
+
readonly identity: string;
|
|
658
|
+
readonly id: string;
|
|
659
|
+
readonly family: string;
|
|
660
|
+
readonly request: FontRequest;
|
|
661
|
+
readonly hash: string;
|
|
662
|
+
readonly faceIndex: number;
|
|
663
|
+
readonly byteLength: number;
|
|
664
|
+
readonly substitution: FontSubstitution | null;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* A {@link ShapingEnvironment} reduced to comparable values.
|
|
668
|
+
*
|
|
669
|
+
* Records and axis maps become SORTED entry arrays, because two environments differing only in
|
|
670
|
+
* key insertion order must fingerprint the same — otherwise a cache would miss on a difference
|
|
671
|
+
* that changes no glyph.
|
|
672
|
+
*/
|
|
673
|
+
interface ShapingEnvironmentFingerprintInputs {
|
|
674
|
+
readonly font: FontFingerprintInputs;
|
|
675
|
+
readonly variationAxes: readonly (readonly [string, number])[];
|
|
676
|
+
readonly shapingLibrary: VersionedShapingLibrary;
|
|
677
|
+
readonly unicodeDataVersion: string;
|
|
678
|
+
readonly normalization: NormalizationPolicy;
|
|
679
|
+
readonly script: string;
|
|
680
|
+
readonly language: string;
|
|
681
|
+
readonly direction: TextDirection;
|
|
682
|
+
readonly features: readonly (readonly [string, number])[];
|
|
683
|
+
readonly fallbackOrder: readonly FontFingerprintInputs[];
|
|
684
|
+
readonly fixedPointScale: number;
|
|
685
|
+
readonly roundingMode: FixedPointRoundingMode;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Validate and freeze a shaping environment.
|
|
689
|
+
*
|
|
690
|
+
* Checks every field that could silently corrupt a measurement: OpenType tags must be four ASCII
|
|
691
|
+
* bytes, script and language must be non-blank and control-character free, fonts must already be
|
|
692
|
+
* validated. Throws rather than coercing — a bad tag that shapes anyway produces a document that
|
|
693
|
+
* measures wrong everywhere and looks fine.
|
|
694
|
+
*
|
|
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` or `atLeast` line of a snapping paragraph under an active line grid takes whole
|
|
917
|
+
* pitches instead ({@link gridLineBox}); `trailing` then names the box below the glyphs.
|
|
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
|
+
/** Lower-cased font table names that declare a Chinese character set. */
|
|
1134
|
+
readonly chineseFontTableFaces?: readonly string[];
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Resolve one run's direct formatting.
|
|
1138
|
+
*
|
|
1139
|
+
* Unrecognised values are DROPPED rather than guessed: a `w:sz` of `"large"` leaves the
|
|
1140
|
+
* default size rather than inventing one, because a wrong measurement moves every glyph
|
|
1141
|
+
* after it and a missing one is visible immediately.
|
|
1142
|
+
*
|
|
1143
|
+
* `themeFonts` resolves `w:rFonts` theme references. Absent, a theme-only `rFonts` leaves
|
|
1144
|
+
* the family inherited — which is what every run of a theme-fonted document does, so the
|
|
1145
|
+
* whole document falls back to the surface default face.
|
|
1146
|
+
*/
|
|
1147
|
+
declare function resolveRunStyle(props: readonly OoxmlProperty[], themeFonts?: ThemeFonts): ResolvedRunStyle;
|
|
1148
|
+
/** The text as it is DRAWN, after case transforms. Measurement must use this, not the source. */
|
|
1149
|
+
declare function displayText(text: string, style: ResolvedRunStyle): string;
|
|
1150
|
+
/** Measure run text the way layout breaks lines and paints glyphs (caps/small-caps aware). */
|
|
1151
|
+
declare function measureDisplayText(text: string, style: ResolvedRunStyle, measurer: TextMeasurer): number;
|
|
1152
|
+
/** Whether two resolved styles are identical, for span merging and cache keys. */
|
|
1153
|
+
declare function runStylesEqual(a: ResolvedRunStyle, b: ResolvedRunStyle): boolean;
|
|
1154
|
+
/**
|
|
1155
|
+
* How far a run's glyphs are lifted off the line's baseline, in points. Positive is up.
|
|
1156
|
+
*
|
|
1157
|
+
* Super and subscript move the GLYPHS without moving the run's box, so the box keeps tiling
|
|
1158
|
+
* the line and the selection band stays continuous. Anything drawing at the glyphs — the
|
|
1159
|
+
* painter, and the caret — has to apply this itself, and from one place, or the two drift.
|
|
1160
|
+
*/
|
|
1161
|
+
declare function baselineShiftPtOf(style: ResolvedRunStyle): number;
|
|
1162
|
+
/**
|
|
1163
|
+
* Shared glyph-size factor for baseline, superscript, and subscript text.
|
|
1164
|
+
*
|
|
1165
|
+
* The 65% script scale follows the common Word font metrics (Arial, Calibri, Times New
|
|
1166
|
+
* Roman) and improves the saved Word reference at 10pt and 11pt. It is a uniform policy;
|
|
1167
|
+
* per-face script metrics and Word's output-device rounding are not modeled here.
|
|
1168
|
+
* Layout, surface paint, and exporters must apply the same factor to avoid caret drift.
|
|
1169
|
+
* @public
|
|
1170
|
+
*/
|
|
1171
|
+
declare function glyphSizeFactorOf(style: ResolvedRunStyle): number;
|
|
1172
|
+
|
|
1173
|
+
/** Horizontal alignment of a paragraph (`w:jc`, ECMA-376 §17.3.1.13). */
|
|
1174
|
+
type Alignment = 'left' | 'center' | 'right' | 'both';
|
|
1175
|
+
|
|
1176
|
+
/** The number formats a page field's `\*` switch can select (ST_NumberFormat names). */
|
|
1177
|
+
type PageFieldNumberFormat = 'decimal' | 'lowerRoman' | 'upperRoman' | 'lowerLetter' | 'upperLetter' | 'numberInDash';
|
|
1178
|
+
|
|
1179
|
+
type AllowlistedPageField = 'PAGE' | 'NUMPAGES' | 'SECTIONPAGES';
|
|
1180
|
+
/**
|
|
1181
|
+
* Which allowlisted page fields a header/footer story actually contains.
|
|
1182
|
+
*
|
|
1183
|
+
* Drives layout reuse: no fields → one baseline; NUMPAGES only → one layout per page count;
|
|
1184
|
+
* SECTIONPAGES only → one layout per section page count; PAGE (alone or combined) → per
|
|
1185
|
+
* distinct evaluated values with a bounded cache. Counts both complex markers and
|
|
1186
|
+
* `w:fldSimple` (including an allowlisted page field nested inside a non-page simple field).
|
|
1187
|
+
*/
|
|
1188
|
+
interface StoryPageFieldNeeds {
|
|
1189
|
+
readonly hasPage: boolean;
|
|
1190
|
+
readonly hasNumPages: boolean;
|
|
1191
|
+
readonly hasSectionPages: boolean;
|
|
1192
|
+
/**
|
|
1193
|
+
* The story places something by the physical sheet's parity: a header frame aligned
|
|
1194
|
+
* `inside` or `outside`. Set by the header/footer layout, never by field detection. The
|
|
1195
|
+
* story then lays out once per sheet parity, with or without a page field.
|
|
1196
|
+
*/
|
|
1197
|
+
readonly hasPageParity?: boolean;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
interface DrawingPoint {
|
|
1201
|
+
readonly x: number;
|
|
1202
|
+
readonly y: number;
|
|
1203
|
+
}
|
|
1204
|
+
interface DrawingInsets {
|
|
1205
|
+
readonly top: number;
|
|
1206
|
+
readonly right: number;
|
|
1207
|
+
readonly bottom: number;
|
|
1208
|
+
readonly left: number;
|
|
1209
|
+
}
|
|
1210
|
+
type DrawingClipFallback = 'none' | 'unsupported-preset';
|
|
1211
|
+
interface DrawingGeometry {
|
|
1212
|
+
readonly contentBounds: LayoutBox;
|
|
1213
|
+
readonly paintBounds: LayoutBox;
|
|
1214
|
+
readonly hitBounds: LayoutBox;
|
|
1215
|
+
readonly transformedCorners: readonly DrawingPoint[];
|
|
1216
|
+
/** Unclipped image corners for affine encoding; clipping must not stretch the image. */
|
|
1217
|
+
readonly imageTransformCorners?: readonly DrawingPoint[];
|
|
1218
|
+
readonly clipPolygon: readonly DrawingPoint[] | null;
|
|
1219
|
+
readonly clipFallback: DrawingClipFallback;
|
|
1220
|
+
readonly effectInsets: DrawingInsets;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved
|
|
1225
|
+
* within.
|
|
1226
|
+
*
|
|
1227
|
+
* The level is EXACT, not a direction: its parity gives direction, but the numeric value is what
|
|
1228
|
+
* reordering needs, and collapsing it early loses nested isolates.
|
|
1229
|
+
*/
|
|
1230
|
+
interface BidiEmbeddingLevels {
|
|
1231
|
+
readonly levels: Uint8Array;
|
|
1232
|
+
readonly paragraphs: readonly {
|
|
1233
|
+
readonly start: number;
|
|
1234
|
+
readonly end: number;
|
|
1235
|
+
readonly level: number;
|
|
1236
|
+
}[];
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Which `w:rFonts` slot a character resolves its face through.
|
|
1241
|
+
*
|
|
1242
|
+
* OOXML gives a run up to four faces and picks between them by SCRIPT, so one run of mixed Latin
|
|
1243
|
+
* and CJK text uses two different fonts without saying so anywhere in its properties.
|
|
1244
|
+
*/
|
|
1245
|
+
type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';
|
|
1246
|
+
/**
|
|
1247
|
+
* A run of text sharing one script, direction and font slot — the unit handed to the shaper.
|
|
1248
|
+
*
|
|
1249
|
+
* Shaping cannot span a script change: Arabic and Latin in one call would produce wrong joining
|
|
1250
|
+
* behaviour, so a run is itemized into these first.
|
|
1251
|
+
*/
|
|
1252
|
+
interface ScriptItem {
|
|
1253
|
+
readonly from: number;
|
|
1254
|
+
readonly to: number;
|
|
1255
|
+
readonly direction: TextDirection;
|
|
1256
|
+
readonly bidiLevel: number;
|
|
1257
|
+
readonly script: 'Zyyy' | 'Latn' | 'Grek' | 'Cyrl' | 'Hani' | 'Hebr' | 'Arab' | 'Deva' | 'Beng' | 'Thai' | 'Khmr';
|
|
1258
|
+
readonly slot: FontSlot;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* A code point whose script this engine does not itemize.
|
|
1262
|
+
*
|
|
1263
|
+
* Carries the offending `codePoint` so the caller can report which character stopped it, rather
|
|
1264
|
+
* than failing anonymously somewhere in the middle of a paragraph.
|
|
1265
|
+
*/
|
|
1266
|
+
declare class UnsupportedScriptError extends Error {
|
|
1267
|
+
readonly name = "UnsupportedScriptError";
|
|
1268
|
+
readonly code = "unsupportedScript";
|
|
1269
|
+
readonly codePoint: number;
|
|
1270
|
+
constructor(codePoint: number);
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Split text into shapeable runs by script, bidi level and font slot.
|
|
1274
|
+
*
|
|
1275
|
+
* Consumes the bidi levels rather than re-deriving direction, so itemization and reordering agree
|
|
1276
|
+
* by construction instead of by two implementations happening to match.
|
|
1277
|
+
*/
|
|
1278
|
+
declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, embedding: BidiEmbeddingLevels): readonly ScriptItem[];
|
|
1279
|
+
/**
|
|
1280
|
+
* The style a piece's text is MEASURED AND PAINTED in, given the font slot it carries.
|
|
1281
|
+
*
|
|
1282
|
+
* This is the measurer-boundary resolution: pieces and spans keep the run's real resolved
|
|
1283
|
+
* style — with `fontFamily` and `fontFamilyEastAsia` both intact, so formatting readback
|
|
1284
|
+
* and the format painter see the run as authored — and every consumer that turns text into
|
|
1285
|
+
* geometry or ink resolves the face through here. The derived object is memoized per
|
|
1286
|
+
* (style, family) by {@link withFontFamily}, so measurers amortizing over style identity
|
|
1287
|
+
* keep their caches.
|
|
1288
|
+
*/
|
|
1289
|
+
declare function styleForFontSlot(style: ResolvedRunStyle, slot: FontSlot | undefined): ResolvedRunStyle;
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* One parsed `HYPERLINK` instruction: the raw, unsanitized pieces.
|
|
1293
|
+
*
|
|
1294
|
+
* `target` and `anchor` are verbatim from the instruction — the projector at the trust
|
|
1295
|
+
* boundary decides what, if anything, they become in a DOM sink.
|
|
1296
|
+
*/
|
|
1297
|
+
interface HyperlinkFieldSpec {
|
|
1298
|
+
/** The target as authored (first quoted or bare non-switch token), or null. */
|
|
1299
|
+
readonly target: string | null;
|
|
1300
|
+
/** `\l` — a bookmark name in this document, or null. */
|
|
1301
|
+
readonly anchor: string | null;
|
|
1302
|
+
/** `\o` — the hover tooltip, or null. */
|
|
1303
|
+
readonly tooltip: string | null;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/** One recognized REF instruction: the target name and the supported switches, nothing else. */
|
|
1307
|
+
interface RefFieldSpec {
|
|
1308
|
+
readonly bookmark: string;
|
|
1309
|
+
/** `r` / `w` paint the target's number; `n` the same without context; null the range text. */
|
|
1310
|
+
readonly numberSwitch: 'r' | 'w' | 'n' | null;
|
|
1311
|
+
/** `\h` marks a reference whose displayed result links to its bookmark. */
|
|
1312
|
+
readonly hyperlink: boolean;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Recognize `REF <bookmark> [\r|\w|\n] [\t] [\h] [\* MERGEFORMAT]` or
|
|
1316
|
+
* `NOTEREF <bookmark> [\h] [\* MERGEFORMAT]`, or null for anything else.
|
|
1317
|
+
*
|
|
1318
|
+
* The keyword matches case-insensitively; the bookmark name keeps its authored case (it is a
|
|
1319
|
+
* lookup key into a Map, never an object property, so hostile names like `__proto__` are just
|
|
1320
|
+
* names that resolve to nothing). Any unrecognized switch fails the parse so the field falls
|
|
1321
|
+
* back to its cached result — never the raw instruction, never a guess. NOTEREF's `\p`
|
|
1322
|
+
* (above/below position text) and `\f` (note-style formatting) are unrecognized on purpose.
|
|
1323
|
+
*/
|
|
1324
|
+
declare function parseRefInstruction(raw: string): RefFieldSpec | null;
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* The story's resolved REF inputs for one layout pass.
|
|
1328
|
+
*
|
|
1329
|
+
* Threaded as a runtime rider on the layout options (see `layoutSemanticDocument`) rather
|
|
1330
|
+
* than a `SemanticLayoutOptions` member, so the public options surface stays put. Absent
|
|
1331
|
+
* means every REF field paints its cached result — the pre-existing degradation, and the one
|
|
1332
|
+
* header/footer and text-box stories still take. Note stories receive the body's context
|
|
1333
|
+
* through `NotesLayoutInput.refFields`.
|
|
1334
|
+
*/
|
|
1335
|
+
interface RefFieldContext {
|
|
1336
|
+
/**
|
|
1337
|
+
* Content token over each paragraph ID and its PAINTED REF outputs in the story, for the
|
|
1338
|
+
* section prepass memo. IDs keep structural edits from moving an identical sequence of
|
|
1339
|
+
* outputs between paragraphs while the aggregate falsely reports unchanged dependencies.
|
|
1340
|
+
* A renumbering edit can move a REF value in a section whose own blocks and list map are
|
|
1341
|
+
* identity-unchanged, and this token is the only validator that sees it. A field that
|
|
1342
|
+
* failed calibration contributes its (session-constant) cached text, so the token still
|
|
1343
|
+
* moves when painted output or its owning paragraph changes.
|
|
1344
|
+
*/
|
|
1345
|
+
readonly valuesToken: string;
|
|
1346
|
+
/** The paragraph's REF outputs folded for its block cache key; `''` when it holds none. */
|
|
1347
|
+
tokenForParagraph(paragraphId: string): string;
|
|
1348
|
+
/**
|
|
1349
|
+
* The live value ONE field paints, keyed by its begin / `w:fldSimple` node id, or null to
|
|
1350
|
+
* keep the cached result (failed calibration, unresolvable, or an anchor this scan never
|
|
1351
|
+
* saw). Anchor-keyed so the projection's paint and this context's token fold read the SAME
|
|
1352
|
+
* calibration verdict, however each walk collected the field's cached text.
|
|
1353
|
+
*/
|
|
1354
|
+
liveValueOf(anchorId: string, spec: RefFieldSpec): string | null;
|
|
1355
|
+
/**
|
|
1356
|
+
* The synthesized display of ONE AUTONUM-family field, keyed by its begin / `w:fldSimple`
|
|
1357
|
+
* node id, or null to paint nothing (unsupported switches, or an anchor this scan never
|
|
1358
|
+
* saw — both the field's historical rendering). These fields carry no cached result at
|
|
1359
|
+
* all, so there is no calibration: the sequential value is the only display they have.
|
|
1360
|
+
* Optional so hand-built contexts predating it stay valid.
|
|
1361
|
+
*/
|
|
1362
|
+
autonumValueOf?(anchorId: string): string | null;
|
|
1363
|
+
/**
|
|
1364
|
+
* The deferred projection of ONE `PAGEREF` field, or null to keep the cached result
|
|
1365
|
+
* (missing bookmark, unsupported switches, or an anchor this scan never saw).
|
|
1366
|
+
*
|
|
1367
|
+
* A `PAGEREF` value is a property of pagination, so the scan cannot compute it — it hands
|
|
1368
|
+
* the projection the resolved TARGET and the calibration inputs instead, and document
|
|
1369
|
+
* finalize (`finalizePageFieldProjection`) substitutes the page number the target's first
|
|
1370
|
+
* fragment lands on. Optional so hand-built contexts predating it stay valid.
|
|
1371
|
+
*/
|
|
1372
|
+
pageRefProjectionOf?(anchorId: string, spec: RefFieldSpec): PageRefFieldProjection | null;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Sticky calibration identity for one `PAGEREF` field.
|
|
1376
|
+
*
|
|
1377
|
+
* An opaque frozen object rather than a mutable verdict holder because span markers are
|
|
1378
|
+
* serialized into fragment signatures — a verdict written into the marker would move a
|
|
1379
|
+
* signature that no painted output moved. The verdict itself lives beside the finalize pass
|
|
1380
|
+
* (`field-page-furniture.ts`), keyed weakly on this object; the registry below carries the
|
|
1381
|
+
* object across passes the same way REF verdicts are carried.
|
|
1382
|
+
*/
|
|
1383
|
+
type PageRefCalibrationCell = Readonly<Record<never, never>>;
|
|
1384
|
+
/**
|
|
1385
|
+
* What a `PAGEREF` span carries to document finalize: the resolved target, the normalized
|
|
1386
|
+
* authored cache (the calibration oracle), and the sticky calibration identity.
|
|
1387
|
+
*/
|
|
1388
|
+
interface PageRefFieldProjection {
|
|
1389
|
+
/** Canonical id of the paragraph the bookmark names — first declaration wins. */
|
|
1390
|
+
readonly targetParagraphId: string;
|
|
1391
|
+
/** The authored cached result, whitespace-collapsed — what the computed number must reproduce. */
|
|
1392
|
+
readonly cached: string;
|
|
1393
|
+
readonly calibration: PageRefCalibrationCell;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Note parts whose stories join the context: their REF fields resolve against the body's
|
|
1397
|
+
* bookmarks and numbering (a footnote citing "Section 1.2(c)" targets a body paragraph),
|
|
1398
|
+
* and their bookmark declarations become plain-REF targets. Number switches aimed AT a
|
|
1399
|
+
* note paragraph still fall back to the cached result — the list map is the body's.
|
|
1400
|
+
*/
|
|
1401
|
+
interface RefNoteParts {
|
|
1402
|
+
readonly footnotesPart: OoxmlPart | null;
|
|
1403
|
+
readonly endnotesPart: OoxmlPart | null;
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Resolve the document's REF fields for one layout pass, or null when it has none.
|
|
1407
|
+
*
|
|
1408
|
+
* Bookmarks and REF fields resolve across the body story and (when `notes` is given) the
|
|
1409
|
+
* footnote/endnote stories, against ONE shared target index — a footnote REF finds the
|
|
1410
|
+
* body bookmark it cites. Header/footer and text-box stories are not given a context and
|
|
1411
|
+
* keep painting cached results.
|
|
1412
|
+
*/
|
|
1413
|
+
declare function resolveStoryRefFields(blocks: readonly OoxmlElement[], listItems: ReadonlyMap<string, ResolvedListItem> | undefined, notes?: RefNoteParts): RefFieldContext | null;
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* What a piece says about the FIELD result it came from, for Word's shading.
|
|
1417
|
+
*
|
|
1418
|
+
* Carried from layout rather than decided at paint time because only the walk knows an atom was
|
|
1419
|
+
* a field at all — by paint the result is just text. Whether the shading is actually drawn is a
|
|
1420
|
+
* view decision made downstream, so this states the fact and nothing about the appearance.
|
|
1421
|
+
*/
|
|
1422
|
+
interface FieldAtomMarker {
|
|
1423
|
+
/**
|
|
1424
|
+
* A legacy form field: `w:fldChar/w:ffData` (FORMTEXT, FORMCHECKBOX, FORMDROPDOWN).
|
|
1425
|
+
*
|
|
1426
|
+
* Word shades these on a different rule from ordinary fields — always, unless the document
|
|
1427
|
+
* turns it off — because they mark the blanks somebody is meant to fill in.
|
|
1428
|
+
*/
|
|
1429
|
+
readonly formField: boolean;
|
|
1430
|
+
/**
|
|
1431
|
+
* A legacy form CONTROL the reader can operate, with the state it paints.
|
|
1432
|
+
*
|
|
1433
|
+
* Checkboxes carry their checked state; dropdowns carry their declared entries and index.
|
|
1434
|
+
* Paint supplies a matching hit target without parsing the displayed text back.
|
|
1435
|
+
*/
|
|
1436
|
+
readonly formControl?: {
|
|
1437
|
+
readonly kind: 'checkbox';
|
|
1438
|
+
readonly checked: boolean;
|
|
1439
|
+
/** Plain accessible name; omitted for fields containing macro references. */
|
|
1440
|
+
readonly accessibleName?: string;
|
|
1441
|
+
} | {
|
|
1442
|
+
readonly kind: 'dropdown';
|
|
1443
|
+
readonly entries: readonly string[];
|
|
1444
|
+
readonly selectedIndex: number;
|
|
1445
|
+
readonly accessibleName?: string;
|
|
1446
|
+
};
|
|
1447
|
+
/**
|
|
1448
|
+
* A BODY PAGE / NUMPAGES / SECTIONPAGES atom whose value depends on pagination.
|
|
1449
|
+
*
|
|
1450
|
+
* The paragraph walk cannot know which page the field lands on — layout runs before the page
|
|
1451
|
+
* count — so it paints a placeholder and records the field's kind here. Document finalize
|
|
1452
|
+
* (`substituteBodyPageFields`) reads this marker and substitutes the real value per page.
|
|
1453
|
+
* Absent in headers/footers, which evaluate live through their own per-page projector.
|
|
1454
|
+
*/
|
|
1455
|
+
readonly pageField?: {
|
|
1456
|
+
readonly kind: AllowlistedPageField;
|
|
1457
|
+
/** The field's `\#` numeric picture, applied when finalize substitutes the value. */
|
|
1458
|
+
readonly picture?: string;
|
|
1459
|
+
/** The field's `\*` number format, applied when finalize substitutes the value. */
|
|
1460
|
+
readonly numberFormat?: PageFieldNumberFormat;
|
|
1461
|
+
};
|
|
1462
|
+
/**
|
|
1463
|
+
* A BODY `PAGEREF` atom whose value is the page number its bookmark target lands on.
|
|
1464
|
+
*
|
|
1465
|
+
* Deferred exactly like {@link pageField}: the paragraph walk paints the field's cached
|
|
1466
|
+
* result (or the placeholder digit when the file cached none) and records the resolved
|
|
1467
|
+
* target here; document finalize substitutes the number of the page hosting the target's
|
|
1468
|
+
* first fragment, gated per field on the calibration verdict.
|
|
1469
|
+
*/
|
|
1470
|
+
readonly pageRef?: PageRefFieldProjection;
|
|
1471
|
+
/**
|
|
1472
|
+
* The first model offset of the editable saved result this piece belongs to.
|
|
1473
|
+
*
|
|
1474
|
+
* Present only when saved results are laid out as text (`fieldResults: 'editable'`). The
|
|
1475
|
+
* result is ordinary text then, so its pieces have their own offsets; every piece of one
|
|
1476
|
+
* result shares this value, which keeps a multi-run link result one anchor and lets the
|
|
1477
|
+
* caret find the whole result it is in.
|
|
1478
|
+
*/
|
|
1479
|
+
readonly resultStart?: number;
|
|
1480
|
+
/** The model offset just past the same result, beside {@link resultStart}. */
|
|
1481
|
+
readonly resultEnd?: number;
|
|
1482
|
+
}
|
|
1483
|
+
/** A half-open model-offset range, in the paragraph's own UTF-16 offset space. */
|
|
1484
|
+
interface ModelRange {
|
|
1485
|
+
readonly start: number;
|
|
1486
|
+
readonly end: number;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* How layout turns a typed `w:hyperlink` node into the sanitized record spans carry.
|
|
1490
|
+
*
|
|
1491
|
+
* Injected rather than computed here because resolving `r:id` needs the PACKAGE's
|
|
1492
|
+
* relationships and this module only ever sees one part's tree. `null` means the caller
|
|
1493
|
+
* declined to project — the runs still measure and paint, they simply carry no link, which is
|
|
1494
|
+
* the right degradation: text is never lost for want of a target.
|
|
1495
|
+
*/
|
|
1496
|
+
type HyperlinkProjector = (link: OoxmlNode) => SpanLinkRecord | null;
|
|
1497
|
+
/**
|
|
1498
|
+
* How layout turns a parsed HYPERLINK field instruction into the sanitized record spans carry.
|
|
1499
|
+
*
|
|
1500
|
+
* Injected for the same reason as {@link HyperlinkProjector}: the spec's raw target must cross
|
|
1501
|
+
* the surface's ONE href trust boundary, and layout owns no sanitization policy. `null` means
|
|
1502
|
+
* no link — the cached result still paints as plain text, which is the right degradation.
|
|
1503
|
+
*/
|
|
1504
|
+
type FieldLinkProjector = (spec: HyperlinkFieldSpec) => SpanLinkRecord | null;
|
|
1505
|
+
|
|
1506
|
+
/** Soft ceiling matching Word's practical custom-tab UI limit. */
|
|
1507
|
+
declare const MAX_TAB_STOPS = 64;
|
|
1508
|
+
/**
|
|
1509
|
+
* Soft ceiling on a tab position (31_680 twips ≈ 22"), matching paragraph-spacing bounds so
|
|
1510
|
+
* a hostile stop cannot shove layout into pathological widths.
|
|
1511
|
+
*/
|
|
1512
|
+
declare const MAX_TAB_POSITION_TWIPS = 31680;
|
|
1513
|
+
/** OOXML / Word default when `w:settings/w:defaultTabStop` is absent: 720 twips = 0.5". */
|
|
1514
|
+
declare const DEFAULT_TAB_INTERVAL_TWIPS = 720;
|
|
1515
|
+
/** The default tab interval in points — {@link DEFAULT_TAB_INTERVAL_TWIPS} converted. */
|
|
1516
|
+
declare const DEFAULT_TAB_INTERVAL_PT: number;
|
|
1517
|
+
/**
|
|
1518
|
+
* How a tab stop positions the text that follows it.
|
|
1519
|
+
*
|
|
1520
|
+
* Only `left` is a plain cursor jump. The other three size the tab glyph from the MEASURED
|
|
1521
|
+
* following segment, so its end, centre or decimal point lands on the stop.
|
|
1522
|
+
*/
|
|
1523
|
+
type TabAlignment = 'left' | 'center' | 'right' | 'decimal';
|
|
1524
|
+
/**
|
|
1525
|
+
* `w:tab/@w:leader` (ECMA-376 §17.3.1.38, ST_TabTlc): the character repeated across the
|
|
1526
|
+
* space a tab reserves. `none` is the default and is represented by an absent leader.
|
|
1527
|
+
*
|
|
1528
|
+
* This is the difference between a Word table of contents and a column of headings floating
|
|
1529
|
+
* next to a column of page numbers, so it is carried through layout to paint rather than
|
|
1530
|
+
* dropped as a geometry-irrelevant attribute.
|
|
1531
|
+
*/
|
|
1532
|
+
type TabLeader = 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot';
|
|
1533
|
+
/**
|
|
1534
|
+
* The character each leader repeats (§17.3.1.38, ST_TabTlc).
|
|
1535
|
+
*
|
|
1536
|
+
* Lives with the type rather than with the painter because LAYOUT has to measure it: a
|
|
1537
|
+
* leader is the same character typed over and over, and the only way to space it the way
|
|
1538
|
+
* typing it would is to ask the measurer how wide it actually is.
|
|
1539
|
+
*/
|
|
1540
|
+
declare const TAB_LEADER_GLYPH: ReadonlyMap<TabLeader, string>;
|
|
1541
|
+
/** One authored `w:tab`: where it sits, how it aligns, and what fills the gap. */
|
|
1542
|
+
interface TabStop {
|
|
1543
|
+
/** Position from the paragraph content origin, in points. */
|
|
1544
|
+
readonly positionPt: number;
|
|
1545
|
+
readonly alignment: TabAlignment;
|
|
1546
|
+
/** Legacy `num` stop: positions a numbering suffix, not a tab in ordinary text. */
|
|
1547
|
+
readonly numberingOnly?: true;
|
|
1548
|
+
/**
|
|
1549
|
+
* Supplied at a hanging paragraph's text indent, not authored. It positions ordinary text
|
|
1550
|
+
* tabs only: the numbering suffix treats the indent by its own rule.
|
|
1551
|
+
*/
|
|
1552
|
+
readonly implied?: true;
|
|
1553
|
+
/** Absent for `none` — the schema default and the overwhelming majority of stops. */
|
|
1554
|
+
readonly leader?: TabLeader;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* A paragraph's tab stops after the style cascade, with the default interval that applies past
|
|
1558
|
+
* the last explicit one.
|
|
1559
|
+
*/
|
|
1560
|
+
interface ResolvedTabStops {
|
|
1561
|
+
/** Custom stops sorted by ascending position. */
|
|
1562
|
+
readonly stops: readonly TabStop[];
|
|
1563
|
+
/** Default-tab interval in points (always positive and bounded). */
|
|
1564
|
+
readonly defaultIntervalPt: number;
|
|
1565
|
+
/**
|
|
1566
|
+
* `w:doNotUseIndentAsNumberingTabStop` (§17.15.3): a numbering suffix tab goes to the
|
|
1567
|
+
* first authored stop past the marker, wherever it is, before it falls back to the indent.
|
|
1568
|
+
*/
|
|
1569
|
+
readonly ignoreIndentAsNumberingTabStop?: true;
|
|
1570
|
+
}
|
|
1571
|
+
/** The frozen "no custom stops" value, so a paragraph without tabs mints no object. */
|
|
1572
|
+
declare const EMPTY_TAB_STOPS: ResolvedTabStops;
|
|
1573
|
+
/**
|
|
1574
|
+
* Resolve tab stops from cascaded `w:pPr` nodes (docDefaults → style chain → direct).
|
|
1575
|
+
*
|
|
1576
|
+
* Each `w:tabs` merges with `clear` support; absence inherits. The leader travels with the
|
|
1577
|
+
* stop that declared it — a `clear` at the same position discards both together.
|
|
1578
|
+
*/
|
|
1579
|
+
declare function cascadedTabStops(paragraphPropertyNodes: readonly OoxmlNode[]): ResolvedTabStops;
|
|
1580
|
+
/** Direct `w:pPr` only — used when no style cascade table is present. */
|
|
1581
|
+
declare function paragraphTabStops(pPr: OoxmlNode | undefined): ResolvedTabStops;
|
|
1582
|
+
/**
|
|
1583
|
+
* Republish stops under a document-wide default-tab interval (`w:defaultTabStop`).
|
|
1584
|
+
*
|
|
1585
|
+
* The cascade resolves stops from the paragraph's own property chain, which cannot see
|
|
1586
|
+
* `settings.xml`; the interval is a document constant that arrives from the session. Returns
|
|
1587
|
+
* the input unchanged when nothing moves, so a cache-key fingerprint stays stable.
|
|
1588
|
+
*/
|
|
1589
|
+
declare function withDefaultTabInterval(tabs: ResolvedTabStops, defaultIntervalPt: number | undefined): ResolvedTabStops;
|
|
1590
|
+
/**
|
|
1591
|
+
* Read `w:settings/w:defaultTabStop` (ECMA-376 §17.15.1.25), in points.
|
|
1592
|
+
*
|
|
1593
|
+
* Word's own interval, not a constant: a metric-locale template writes `w:val="1134"` (2cm)
|
|
1594
|
+
* and every default-interval tab in the document lands on that grid instead of the 0.5"
|
|
1595
|
+
* one. The value is FILE-DERIVED, so a non-integer, non-positive or out-of-range `val` falls
|
|
1596
|
+
* back to the schema default rather than being trusted into layout arithmetic.
|
|
1597
|
+
*
|
|
1598
|
+
* `ST_TwipsMeasure` also admits a universal measure (`"2cm"`); Word writes plain twips, and
|
|
1599
|
+
* the spelled form falls back to the default rather than being parsed here.
|
|
1600
|
+
*/
|
|
1601
|
+
declare function defaultTabIntervalFromSettings(settings: OoxmlNode | null | undefined): number;
|
|
1602
|
+
/** Where one tab character lands: the resolved x, and the stop that decided it. */
|
|
1603
|
+
interface TabDestination {
|
|
1604
|
+
readonly positionPt: number;
|
|
1605
|
+
readonly alignment: TabAlignment;
|
|
1606
|
+
/** Leader of the stop that was reached; absent for `none` and for default-interval tabs. */
|
|
1607
|
+
readonly leader?: TabLeader;
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Next tab destination strictly past `currentX`, preferring custom stops then the default
|
|
1611
|
+
* interval. Destination is clamped to `rightEdge` so stops cannot escape the content box.
|
|
1612
|
+
*/
|
|
1613
|
+
declare function nextTabDestination(tabs: ResolvedTabStops, currentX: number, rightEdge: number, forNumbering?: boolean): TabDestination;
|
|
1614
|
+
/**
|
|
1615
|
+
* Width of a tab glyph so the following segment lands on the destination.
|
|
1616
|
+
*
|
|
1617
|
+
* `segmentWidth` / `decimalOffset` are already measured in points. Decimal offset is the
|
|
1618
|
+
* advance from the segment start to the decimal point (0 when none — treated like right).
|
|
1619
|
+
*/
|
|
1620
|
+
declare function tabAdvanceWidth(alignment: TabAlignment, currentX: number, destinationX: number, segmentWidth: number, decimalOffset: number): number;
|
|
1621
|
+
/**
|
|
1622
|
+
* Stable fingerprint for layout cache keys — nested `w:tabs` are not in flat `OoxmlProperty`
|
|
1623
|
+
* bags, so style-inherited stops must be named explicitly or breaks would collide.
|
|
1624
|
+
*/
|
|
1625
|
+
declare function tabStopsFingerprint(tabs: ResolvedTabStops): string;
|
|
1626
|
+
|
|
1627
|
+
/** Document-wide East Asian line-break and whitespace policy. @public */
|
|
1628
|
+
interface CjkTypographySettings {
|
|
1629
|
+
/** Apply the strict Japanese small-kana and prolonged-sound-mark restrictions. */
|
|
1630
|
+
readonly strict: boolean;
|
|
1631
|
+
/** Whitespace compression selected by `w:characterSpacingControl`. */
|
|
1632
|
+
readonly compression: 'doNotCompress' | 'compressPunctuation' | 'compressPunctuationAndJapaneseKana';
|
|
1633
|
+
/** Custom no-line-start characters, keyed by normalized language tag. */
|
|
1634
|
+
readonly before: Readonly<Record<string, string>>;
|
|
1635
|
+
/** Custom no-line-end characters, keyed by normalized language tag. */
|
|
1636
|
+
readonly after: Readonly<Record<string, string>>;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
/** Test-only mutable counters; omitted by production call sites. Not package-public. */
|
|
1640
|
+
interface TableBorderGridResolveWork {
|
|
1641
|
+
/** Sparse intervals emitted (not dense column slots). */
|
|
1642
|
+
ownershipSlotsWritten: number;
|
|
1643
|
+
columnLookups: number;
|
|
1644
|
+
}
|
|
1645
|
+
/** Shared remaining interval budget for nested table finalization in one layout. */
|
|
1646
|
+
interface TableBorderOwnershipBudget {
|
|
1647
|
+
intervalsRemaining: number;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/** Which physical edge of a box a border sits on. */
|
|
1651
|
+
type TableBorderSideName = 'top' | 'right' | 'bottom' | 'left';
|
|
1652
|
+
/**
|
|
1653
|
+
* One border edge in one of three states.
|
|
1654
|
+
*
|
|
1655
|
+
* Omitted edges inherit. Explicit no-border edges suppress their own inherited rule.
|
|
1656
|
+
* An opposing edge can still win, including a rule inherited by the neighboring cell.
|
|
1657
|
+
*/
|
|
1658
|
+
type TableBorderSide = {
|
|
1659
|
+
readonly state: 'omitted';
|
|
1660
|
+
} | {
|
|
1661
|
+
readonly state: 'none';
|
|
1662
|
+
} | {
|
|
1663
|
+
readonly state: 'edge';
|
|
1664
|
+
readonly style: TableBorderStyle;
|
|
1665
|
+
/** Validated RRGGBB, or null for auto/missing (paint defaults to black). */
|
|
1666
|
+
readonly color: string | null;
|
|
1667
|
+
/** Thickness in points (`w:sz` is eighths of a point). */
|
|
1668
|
+
readonly widthPt: number;
|
|
1669
|
+
};
|
|
1670
|
+
/** A table's six authored border edges: four outer, plus the two interior intervals. */
|
|
1671
|
+
interface TableBorderBox {
|
|
1672
|
+
readonly top: TableBorderSide;
|
|
1673
|
+
readonly left: TableBorderSide;
|
|
1674
|
+
readonly bottom: TableBorderSide;
|
|
1675
|
+
readonly right: TableBorderSide;
|
|
1676
|
+
readonly insideH: TableBorderSide;
|
|
1677
|
+
readonly insideV: TableBorderSide;
|
|
1678
|
+
}
|
|
1679
|
+
/** The four resolved edges of one cell, after conflict resolution against its neighbours. */
|
|
1680
|
+
interface CellBorderBox {
|
|
1681
|
+
readonly top: TableBorderSide;
|
|
1682
|
+
readonly left: TableBorderSide;
|
|
1683
|
+
readonly bottom: TableBorderSide;
|
|
1684
|
+
readonly right: TableBorderSide;
|
|
1685
|
+
}
|
|
1686
|
+
/** Final edge winner; absent means the side/interval is not drawn. */
|
|
1687
|
+
interface ResolvedTableBorderEdge {
|
|
1688
|
+
readonly style: TableBorderStyle;
|
|
1689
|
+
readonly color: string | null;
|
|
1690
|
+
readonly widthPt: number;
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Conflict winner over one grid interval of a cell side.
|
|
1694
|
+
*
|
|
1695
|
+
* `gridStart`/`gridEnd` are absolute grid column indices for horizontal sides and absolute
|
|
1696
|
+
* row indices for vertical sides (half-open). `startPt`/`endPt` are cell-local along-axis
|
|
1697
|
+
* positions in layout points.
|
|
1698
|
+
*/
|
|
1699
|
+
interface ResolvedTableBorderEdgeSegment {
|
|
1700
|
+
readonly side: TableBorderSideName;
|
|
1701
|
+
readonly gridStart: number;
|
|
1702
|
+
readonly gridEnd: number;
|
|
1703
|
+
readonly startPt: number;
|
|
1704
|
+
readonly endPt: number;
|
|
1705
|
+
readonly edge: ResolvedTableBorderEdge;
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* One published stroke rectangle in cell-local layout points.
|
|
1709
|
+
*
|
|
1710
|
+
* Paint multiplies x/y/width/height by scale and draws — no metrics, gaps, or corner math.
|
|
1711
|
+
*/
|
|
1712
|
+
interface TableBorderStrokeRecord {
|
|
1713
|
+
readonly side: TableBorderSideName;
|
|
1714
|
+
readonly role: 'outer' | 'inner' | 'middle' | 'edge';
|
|
1715
|
+
readonly color: string | null;
|
|
1716
|
+
/** CSS keyword for this stroke (compound strokes are always solid). */
|
|
1717
|
+
readonly cssStyle: 'solid' | 'dashed' | 'dotted';
|
|
1718
|
+
readonly x: number;
|
|
1719
|
+
readonly y: number;
|
|
1720
|
+
readonly width: number;
|
|
1721
|
+
readonly height: number;
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Layout-owned cell borders after conflict resolution and compound expansion.
|
|
1725
|
+
*
|
|
1726
|
+
* Convenience `top`/`left`/`bottom`/`right` are set when that side has a single uniform
|
|
1727
|
+
* full-span winner (existing consumers / CSS simple edges). Multi-interval winners live
|
|
1728
|
+
* only on `edgeSegments`. Compound geometry is always on `strokes`.
|
|
1729
|
+
*/
|
|
1730
|
+
interface ResolvedCellBorders {
|
|
1731
|
+
readonly top?: ResolvedTableBorderEdge;
|
|
1732
|
+
readonly left?: ResolvedTableBorderEdge;
|
|
1733
|
+
readonly bottom?: ResolvedTableBorderEdge;
|
|
1734
|
+
readonly right?: ResolvedTableBorderEdge;
|
|
1735
|
+
readonly edgeSegments?: readonly ResolvedTableBorderEdgeSegment[];
|
|
1736
|
+
readonly strokes?: readonly TableBorderStrokeRecord[];
|
|
1737
|
+
}
|
|
1738
|
+
/** Soft cap on published stroke rectangles per cell (security / pathological spans). */
|
|
1739
|
+
declare const MAX_TABLE_BORDER_STROKES = 256;
|
|
1740
|
+
/** Read one OOXML border child into the three-state model. */
|
|
1741
|
+
declare function readBorderSide(node: OoxmlElement | undefined): TableBorderSide;
|
|
1742
|
+
/**
|
|
1743
|
+
* Read a table's `w:tblBorders`, dropping or clamping hostile values.
|
|
1744
|
+
*
|
|
1745
|
+
* Widths and colours come from a file: an out-of-range `w:sz` becomes a layout dimension, so it is
|
|
1746
|
+
* bounded here rather than downstream.
|
|
1747
|
+
*/
|
|
1748
|
+
declare function readTableBorders(tblPr: OoxmlElement | undefined): TableBorderBox;
|
|
1749
|
+
/** Read one cell's `w:tcBorders`, under the same bounds {@link readTableBorders} applies. */
|
|
1750
|
+
declare function readCellBorders(tcPr: OoxmlElement | undefined): CellBorderBox;
|
|
1751
|
+
/** Word's weighted border selection (MS-OI29500 §2.1.169, tcBorders).
|
|
1752
|
+
* https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oi29500/7c791c5d-44c3-4fe8-abdd-8f136761bb93
|
|
1753
|
+
*/
|
|
1754
|
+
declare function borderWeight(side: TableBorderSide): number;
|
|
1755
|
+
/**
|
|
1756
|
+
* Pick the winner between two candidates on a shared grid line (zero cell spacing).
|
|
1757
|
+
*
|
|
1758
|
+
* `none` loses to any edge; two `none`/omitted do not paint. Weighted widths decide;
|
|
1759
|
+
* equal weights rank by style, then prefer the darker color, then `preferFirst`
|
|
1760
|
+
* (reading-order / first candidate).
|
|
1761
|
+
*/
|
|
1762
|
+
declare function resolveBorderConflict(first: TableBorderSide, second: TableBorderSide, preferFirst?: boolean): TableBorderSide;
|
|
1763
|
+
interface BorderGridCell {
|
|
1764
|
+
readonly gridColumn: number;
|
|
1765
|
+
readonly gridSpan: number;
|
|
1766
|
+
readonly vMergeContinue: boolean;
|
|
1767
|
+
readonly borders: CellBorderBox;
|
|
1768
|
+
/** Set on restart cells that visually span into later rows. */
|
|
1769
|
+
readonly mergeRowSpan?: number;
|
|
1770
|
+
/** The row's `w:tblPrEx` table borders, which replace the table's own for this cell. */
|
|
1771
|
+
readonly rowTableBorders?: TableBorderBox;
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* The absolute grid a table's borders are drawn on: column widths, and per-row tops and heights.
|
|
1775
|
+
*
|
|
1776
|
+
* Shared by every edge so adjacent cells resolve to the SAME line, rather than each computing its
|
|
1777
|
+
* own and leaving a hairline gap between them.
|
|
1778
|
+
*/
|
|
1779
|
+
interface BorderGridGeometry {
|
|
1780
|
+
/** Center simple horizontal strokes on collapsed row boundaries. */
|
|
1781
|
+
readonly collapsedHorizontal?: boolean;
|
|
1782
|
+
/** Absolute column widths for the whole table (points). */
|
|
1783
|
+
readonly columnWidthsPt: readonly number[];
|
|
1784
|
+
/**
|
|
1785
|
+
* Per laid-out row: absolute top and height in the same coordinate space as cell boxes
|
|
1786
|
+
* (only relative differences matter for vertical edge segmentation).
|
|
1787
|
+
*/
|
|
1788
|
+
readonly rowBands: readonly {
|
|
1789
|
+
readonly y: number;
|
|
1790
|
+
readonly height: number;
|
|
1791
|
+
}[];
|
|
1792
|
+
/** Per laid-out cell: width/height after vMerge expansion (cell-local stroke space). */
|
|
1793
|
+
readonly cellBoxes: readonly (readonly {
|
|
1794
|
+
readonly width: number;
|
|
1795
|
+
readonly height: number;
|
|
1796
|
+
/** False when a split occurrence still reserves a shared bottom stroke. */
|
|
1797
|
+
readonly outerBottomInsetReserved?: boolean;
|
|
1798
|
+
/** Simple vertical strokes share the cell grid line with legacy padding. */
|
|
1799
|
+
readonly centeredSideRules?: boolean;
|
|
1800
|
+
/** Simple vertical strokes paint centered on the grid line. */
|
|
1801
|
+
readonly centeredSidePaint?: boolean;
|
|
1802
|
+
}[])[];
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Resolve borders for every cell in a laid-out table fragment.
|
|
1806
|
+
*
|
|
1807
|
+
* Shared vertical edges: conflict(left.right, right.left) → assigned to the left cell,
|
|
1808
|
+
* segmented per row when a vMerge restart faces differing neighbors.
|
|
1809
|
+
* Shared horizontal edges: conflict(above.bottom, below.top) → assigned to the above cell,
|
|
1810
|
+
* segmented per grid column when a gridSpan cell faces differing below neighbors; vMerge
|
|
1811
|
+
* interior seams are suppressed per interval.
|
|
1812
|
+
*
|
|
1813
|
+
* When `geometry` is provided, compound edges expand into explicit stroke records with
|
|
1814
|
+
* corner-adjusted endpoints in cell-local points.
|
|
1815
|
+
*/
|
|
1816
|
+
declare function resolveTableCellBorderGrid(rows: readonly (readonly BorderGridCell[])[], table: TableBorderBox, columnCount: number, geometry?: BorderGridGeometry, work?: TableBorderGridResolveWork, ownershipBudget?: TableBorderOwnershipBudget): ResolvedCellBorders[][];
|
|
1817
|
+
/** Painted extent inward from the cell edge; existing padding may already clear it. */
|
|
1818
|
+
declare function borderExtentPt(edge: ResolvedTableBorderEdge | TableBorderSide | undefined): number;
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Why textbox story layout stopped short.
|
|
1822
|
+
*
|
|
1823
|
+
* Every one is a BOUND rather than a bug: nesting depth, fragment counts and the extent all
|
|
1824
|
+
* come from a file. Falling back with a reason keeps the drawing rendered (clipped) instead
|
|
1825
|
+
* of failing the layout pass.
|
|
1826
|
+
*/
|
|
1827
|
+
type TextboxStoryFallbackReason = 'textbox-nesting-limit' | 'textbox-fragment-limit'
|
|
1828
|
+
/** Flowed content is taller than the extent; trailing fragments were dropped. */
|
|
1829
|
+
| 'textbox-height-clip';
|
|
1830
|
+
/**
|
|
1831
|
+
* One text box's story laid out inside its extent, in content-box-relative coordinates.
|
|
1832
|
+
*
|
|
1833
|
+
* Fragments origin at the content box's top-left; paint places the content box at
|
|
1834
|
+
* `drawing origin + contentOffset` and clips to the extent.
|
|
1835
|
+
*/
|
|
1836
|
+
interface TextboxStoryLayout {
|
|
1837
|
+
/** Content-box-relative fragments (origin at the content box's top-left). */
|
|
1838
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
1839
|
+
/** Height the blocks flow to (points), before vertical anchoring. */
|
|
1840
|
+
readonly flowHeight: number;
|
|
1841
|
+
/** Offset of the content box inside the drawing extent: insets plus vertical anchoring. */
|
|
1842
|
+
readonly contentOffset: Readonly<{
|
|
1843
|
+
x: number;
|
|
1844
|
+
y: number;
|
|
1845
|
+
}>;
|
|
1846
|
+
/** Content box width (extent minus horizontal insets; the widest line when unwrapped). */
|
|
1847
|
+
readonly contentWidth: number;
|
|
1848
|
+
/**
|
|
1849
|
+
* Box width (points) that unwrapped text sizes its shape to: the widest line plus the
|
|
1850
|
+
* insets. Placement resolves the shape at this width. Absent when the extent width holds.
|
|
1851
|
+
*/
|
|
1852
|
+
readonly extentWidth?: number;
|
|
1853
|
+
/** Content box height (extent minus vertical insets). */
|
|
1854
|
+
readonly contentHeight: number;
|
|
1855
|
+
/** Solid fill of the hosting shape, painted behind the story; null for no fill. */
|
|
1856
|
+
readonly fillHex: string | null;
|
|
1857
|
+
/** Solid outline of the hosting shape; null for no outline. */
|
|
1858
|
+
readonly strokeHex: string | null;
|
|
1859
|
+
/** Outline width in points; 0 when absent. */
|
|
1860
|
+
readonly strokeWidthPt: number;
|
|
1861
|
+
/** True when layout hit a named bound and returned a truncated / empty story. */
|
|
1862
|
+
readonly fallbackReason?: TextboxStoryFallbackReason;
|
|
1863
|
+
/**
|
|
1864
|
+
* Resource identities of the drawings the clip DROPPED, in flow order.
|
|
1865
|
+
*
|
|
1866
|
+
* The furniture invalidation token walks laid-out records, and a dropped fragment leaves
|
|
1867
|
+
* none — while a `withPageContext` projection of the same story can wrap differently
|
|
1868
|
+
* (PAGE digits) and keep the drawing. Without this a picture clipped out of the baseline
|
|
1869
|
+
* but painted by a projection never reaches the session context, so its decode settling
|
|
1870
|
+
* moves nothing and the unchanged-pass early exit reuses the placeholder pages forever
|
|
1871
|
+
* (#467). Absent when the clip dropped no drawings, so the common story keys byte-for-byte
|
|
1872
|
+
* as before.
|
|
1873
|
+
*/
|
|
1874
|
+
readonly clippedResourceToken?: string;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/** One laid-out group member story. */
|
|
1878
|
+
interface GroupTextboxStoryRecord {
|
|
1879
|
+
/** Canonical node id of the member `wps:wsp`. */
|
|
1880
|
+
readonly memberNodeId: string;
|
|
1881
|
+
/** The member box in points, relative to the drawing extent's top-left corner. */
|
|
1882
|
+
readonly box: LayoutBox;
|
|
1883
|
+
/** The member's story, in content-box coordinates inside {@link box}. */
|
|
1884
|
+
readonly story: TextboxStoryLayout;
|
|
1885
|
+
/**
|
|
1886
|
+
* Clockwise turn of the painted member about the center of {@link box}, in degrees. The
|
|
1887
|
+
* story lays out upright; paint and export turn it. Hit testing never maps into it.
|
|
1888
|
+
*/
|
|
1889
|
+
readonly rotationDegrees: number;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
/** Clip `box` to a horizontal content band; preserves authored size, zeroes clipped axes. */
|
|
1893
|
+
/** How a drawing record holds the picture member of a shape group. */
|
|
1894
|
+
interface DrawingGroupPictureRecord {
|
|
1895
|
+
/**
|
|
1896
|
+
* Whether the group is the chosen branch of a run-level `mc:AlternateContent`. An output
|
|
1897
|
+
* that cannot draw the picture leaves such a group out, as it leaves out other MC content
|
|
1898
|
+
* it cannot draw. A group without it shows the output's unsupported-drawing result.
|
|
1899
|
+
*/
|
|
1900
|
+
readonly alternateContent: boolean;
|
|
1901
|
+
}
|
|
1902
|
+
interface InlineDrawingRecord {
|
|
1903
|
+
readonly kind: 'inlineDrawing';
|
|
1904
|
+
readonly drawingNodeId: string;
|
|
1905
|
+
readonly paragraphId: string;
|
|
1906
|
+
readonly ownerPartName: string;
|
|
1907
|
+
readonly start: number;
|
|
1908
|
+
/** Left edge of the extent box (slot + distL). */
|
|
1909
|
+
readonly x: number;
|
|
1910
|
+
readonly y: number;
|
|
1911
|
+
readonly width: number;
|
|
1912
|
+
readonly height: number;
|
|
1913
|
+
readonly distL: number;
|
|
1914
|
+
readonly distR: number;
|
|
1915
|
+
readonly distT: number;
|
|
1916
|
+
readonly distB: number;
|
|
1917
|
+
/** Caret/hit advance start (slot left, before distL). */
|
|
1918
|
+
readonly advanceStart: number;
|
|
1919
|
+
/** Caret/hit advance end (slot + totalWidth). */
|
|
1920
|
+
readonly advanceEnd: number;
|
|
1921
|
+
readonly baselineOffset: number;
|
|
1922
|
+
readonly paintBounds: LayoutBox;
|
|
1923
|
+
readonly hitBounds: LayoutBox;
|
|
1924
|
+
readonly geometry: DrawingGeometry;
|
|
1925
|
+
readonly resource: ImageResourceState;
|
|
1926
|
+
readonly accessibility: DrawingAccessibility;
|
|
1927
|
+
/** Sanitized external hyperlink projection; inert until an explicit gesture activates it. */
|
|
1928
|
+
readonly hyperlinkHref: string | null;
|
|
1929
|
+
readonly effects: DrawingImageEffects;
|
|
1930
|
+
readonly crop: SourceCrop;
|
|
1931
|
+
readonly transform: DrawingTransform;
|
|
1932
|
+
/** Fixed non-picture graphic kind for refusal labels (`chart`, `group`, …); null for pictures. */
|
|
1933
|
+
readonly placeholderGraphicKind: string | null;
|
|
1934
|
+
/** Typed solid-geometry payload for a renderable `wps:wsp` shape; null otherwise. */
|
|
1935
|
+
readonly vectorShape: VectorShapeProjection | null;
|
|
1936
|
+
/**
|
|
1937
|
+
* Present when the image is the picture member of a drawing group. The image then fills
|
|
1938
|
+
* the bounds of `geometry.imageTransformCorners`, not `geometry.contentBounds`, and paints
|
|
1939
|
+
* below `vectorShape`, which holds the group's other members. `placeholderGraphicKind`
|
|
1940
|
+
* stays non-null, as for a vector shape, so image commands do not treat the group as a
|
|
1941
|
+
* picture. The group paints whole or not at all: when the picture cannot paint, an output
|
|
1942
|
+
* shows no part of the group.
|
|
1943
|
+
*/
|
|
1944
|
+
readonly groupPicture?: DrawingGroupPictureRecord;
|
|
1945
|
+
/**
|
|
1946
|
+
* The revision wrappers enclosing the owning run, outermost first — the same stack spans
|
|
1947
|
+
* carry, so paint and review chrome give a tracked picture the same cues as tracked text.
|
|
1948
|
+
* Absent when the drawing is untracked.
|
|
1949
|
+
*/
|
|
1950
|
+
readonly revisions?: readonly RevisionAttribution[];
|
|
1951
|
+
/**
|
|
1952
|
+
* The picture's resolved Unicode bidi level in a right-to-left or mixed-direction paragraph.
|
|
1953
|
+
* An odd level reads right to left: its logical start is its right edge. Absent when the
|
|
1954
|
+
* paragraph needed no bidi resolution.
|
|
1955
|
+
*/
|
|
1956
|
+
readonly bidiLevel?: number;
|
|
1957
|
+
/**
|
|
1958
|
+
* Laid-out textbox story for a `wps:txbx` drawing; paint renders it clipped inside the
|
|
1959
|
+
* extent instead of a placeholder. Absent when the drawing carries no story or the host
|
|
1960
|
+
* did not thread story layout (the record then degrades to the placeholder path). An
|
|
1961
|
+
* inline text box carries one too: its story sits inside the extent the line reserves.
|
|
1962
|
+
*/
|
|
1963
|
+
readonly textboxStory?: TextboxStoryLayout;
|
|
1964
|
+
/** Read-only stories of a group's text box members, each placed in its member box. */
|
|
1965
|
+
readonly groupTextboxStories?: readonly GroupTextboxStoryRecord[];
|
|
1966
|
+
}
|
|
1967
|
+
interface InlineDrawingLayoutContext {
|
|
1968
|
+
readonly ownerPartName: string;
|
|
1969
|
+
/** Precomputed run-level atom id (`w:drawing` or MC wrapper) → projection. */
|
|
1970
|
+
readonly projectionForAtom?: (atomNodeId: string) => DrawingProjection | null;
|
|
1971
|
+
readonly project: (drawing: OoxmlDrawingNode) => DrawingProjection | null;
|
|
1972
|
+
readonly resourceOf: (projection: DrawingProjection) => ImageResourceState;
|
|
1973
|
+
/**
|
|
1974
|
+
* Resolve a numbering picture bullet's image by relationship id.
|
|
1975
|
+
*
|
|
1976
|
+
* The owner is the NUMBERING part, not this context's story part: `w:numPicBullet` lives in
|
|
1977
|
+
* `numbering.xml` and its `r:id` names a relationship of that part. The resolver answers the
|
|
1978
|
+
* owner it used so a sink can key the resource without assuming the part name. Absent on a
|
|
1979
|
+
* context whose host resolves no image resources, which degrades a picture bullet to its
|
|
1980
|
+
* level's `w:lvlText`.
|
|
1981
|
+
*/
|
|
1982
|
+
readonly pictureBulletResource?: (relationshipId: string) => {
|
|
1983
|
+
readonly ownerPartName: string;
|
|
1984
|
+
readonly resource: ImageResourceState;
|
|
1985
|
+
} | null;
|
|
1986
|
+
}
|
|
1987
|
+
/** Named fallback when a positioning frame cannot be resolved (OpenSpec 4.6). */
|
|
1988
|
+
type AnchoredDrawingLayoutFallback = 'unresolvable-frame' | 'page-defer-exhausted';
|
|
1989
|
+
interface AnchoredDrawingRecord extends Omit<InlineDrawingRecord, 'kind' | 'baselineOffset' | 'advanceStart' | 'advanceEnd' | 'distL' | 'distR' | 'distT' | 'distB'> {
|
|
1990
|
+
readonly kind: 'anchoredDrawing';
|
|
1991
|
+
readonly anchorParagraphId: string;
|
|
1992
|
+
readonly horizontalFrame: DrawingHorizontalReferenceFrame;
|
|
1993
|
+
readonly verticalFrame: DrawingVerticalReferenceFrame;
|
|
1994
|
+
readonly horizontalFrameOrigin: number;
|
|
1995
|
+
readonly verticalFrameOrigin: number;
|
|
1996
|
+
readonly behindDocument: boolean;
|
|
1997
|
+
readonly allowOverlap: boolean;
|
|
1998
|
+
/**
|
|
1999
|
+
* Whether the object is laid out in the box it flows in. For an anchor in a table cell this
|
|
2000
|
+
* is the layout's reading: `true` from compatibility mode 15, which ignores
|
|
2001
|
+
* `layoutInCell="0"`. Everywhere else it is the authored `wp:anchor/@layoutInCell`.
|
|
2002
|
+
*/
|
|
2003
|
+
readonly layoutInCell: boolean;
|
|
2004
|
+
readonly relativeHeight: number;
|
|
2005
|
+
readonly wrap: Exclude<ImageWrapTarget, 'inline'>;
|
|
2006
|
+
readonly layoutFallback?: AnchoredDrawingLayoutFallback;
|
|
2007
|
+
/** Canonical document traversal index within the owner story part. */
|
|
2008
|
+
readonly sourceOrder?: number;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
/**
|
|
2012
|
+
* A paragraph's list membership fully resolved: definition, level, marker text and geometry.
|
|
2013
|
+
*
|
|
2014
|
+
* `markerText` is already expanded through the counter state, so it is the string a reader sees
|
|
2015
|
+
* rather than the `w:lvlText` template.
|
|
2016
|
+
*/
|
|
2017
|
+
interface ResolvedListItem {
|
|
2018
|
+
readonly numId: string;
|
|
2019
|
+
readonly ilvl: number;
|
|
2020
|
+
readonly abstractNumId: string;
|
|
2021
|
+
/** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
|
|
2022
|
+
readonly numFmt: string;
|
|
2023
|
+
readonly markerText: string;
|
|
2024
|
+
/** Counter value at this item's own level; absent for bullets. */
|
|
2025
|
+
readonly ordinal?: number;
|
|
2026
|
+
readonly markerAlign: ListMarkerAlign;
|
|
2027
|
+
readonly suffix: ListSuffix;
|
|
2028
|
+
/** Effective indent after merging level + paragraph indents, in points. */
|
|
2029
|
+
readonly indent: NumberingLevelIndent;
|
|
2030
|
+
readonly markerStyle: ResolvedRunStyle;
|
|
2031
|
+
/**
|
|
2032
|
+
* The image marker a `w:lvlPicBulletId` level resolved to, absent on every other item.
|
|
2033
|
+
*
|
|
2034
|
+
* Present means the marker is an IMAGE: it occupies `width` x `height` points in the
|
|
2035
|
+
* hanging slot and `markerText` is only what a sink that cannot draw the image falls back
|
|
2036
|
+
* to. The extent is the AUTHORED `v:shape` size scaled by the marker font size — see
|
|
2037
|
+
* {@link resolvePictureBullet}, which also decides when the answer is no picture at all.
|
|
2038
|
+
* Absent means the level declared none, named one the part does not declare, declared one
|
|
2039
|
+
* this projection could not read, or resolved to a size nothing can paint.
|
|
2040
|
+
*/
|
|
2041
|
+
readonly picBullet?: ResolvedPictureBullet;
|
|
2042
|
+
/** Fingerprint for layout cache keys (indent, marker text, marker face). */
|
|
2043
|
+
readonly cacheToken: string;
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Read `w:numPr` from cascaded paragraph-property nodes (lowest precedence first).
|
|
2047
|
+
*
|
|
2048
|
+
* Flat `OoxmlProperty[]` bags drop nested `ilvl`/`numId`, so this walks the tree nodes
|
|
2049
|
+
* the same way borders and tabs do.
|
|
2050
|
+
*
|
|
2051
|
+
* `w:ilvl` and `w:numId` inherit INDEPENDENTLY through the style chain and the direct
|
|
2052
|
+
* `w:pPr` (§17.3.1.19): a tier stating only the level keeps the id it inherits — Word's
|
|
2053
|
+
* standard Heading2–Heading9 shape, where only Heading1 names the `w:num` — and a tier
|
|
2054
|
+
* stating only the id keeps the inherited level. Treating each `w:numPr` node as a full
|
|
2055
|
+
* replacement dropped the id at every level-only tier and unnumbered the paragraph. A
|
|
2056
|
+
* stated `w:numId w:val="0"` (or an invalid value) still switches numbering off at that
|
|
2057
|
+
* tier even when a lower tier set one; a higher tier stating a valid id re-enables. A
|
|
2058
|
+
* level-only tier with no id inherited from below resolves to no numbering.
|
|
2059
|
+
*/
|
|
2060
|
+
declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
|
|
2061
|
+
numId: string;
|
|
2062
|
+
ilvl: number;
|
|
2063
|
+
} | null;
|
|
2064
|
+
/**
|
|
2065
|
+
* Resolve `w:numStyleLink` delegation using the document's styles (§17.9.21).
|
|
2066
|
+
*
|
|
2067
|
+
* Without a style table there is nothing to follow, so the index is returned unchanged —
|
|
2068
|
+
* and so it is when nothing delegates, which keeps layout cache identity.
|
|
2069
|
+
*/
|
|
2070
|
+
declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;
|
|
2071
|
+
/**
|
|
2072
|
+
* The effective indent of a list paragraph: `inherited`, then the numbering LEVEL, then
|
|
2073
|
+
* `direct`, per attribute.
|
|
2074
|
+
*
|
|
2075
|
+
* A level's `w:pPr/w:ind` applies per attribute between the two tiers, and the ordering
|
|
2076
|
+
* matters on real documents. A converted agreement numbers its `(a)` items with a level
|
|
2077
|
+
* stating `left=1512 hanging=738` under a `ListParagraph` style stating
|
|
2078
|
+
* `left=775 hanging=624`, and states only `hanging="737"` on the paragraph itself. Reading
|
|
2079
|
+
* the flattened cascade as "the paragraph's indent" gave the STYLE's 775 to a level that had
|
|
2080
|
+
* overridden it, so every lettered sub-item hung a full indent step to the left of where
|
|
2081
|
+
* Word puts it.
|
|
2082
|
+
*
|
|
2083
|
+
* `inherited` is what the level outranks and `direct` is what outranks it. When the
|
|
2084
|
+
* paragraph's own `w:pPr` states a `w:numPr`, these are the style chain (after the document
|
|
2085
|
+
* defaults) and that `w:pPr`. Otherwise the level sits directly below the nearest style that
|
|
2086
|
+
* states a `w:numPr`: that style, the styles based on it and the paragraph's own `w:pPr` go
|
|
2087
|
+
* in `direct`, and the defaults and that style's bases go in `inherited`.
|
|
2088
|
+
*/
|
|
2089
|
+
declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;
|
|
2090
|
+
|
|
2091
|
+
/**
|
|
2092
|
+
* Resolve every list paragraph in a story to a {@link ResolvedListItem}, keyed by node id.
|
|
2093
|
+
*
|
|
2094
|
+
* Non-list paragraphs are absent from the map. Hostile / missing numbering resolves inertly
|
|
2095
|
+
* (paragraph omitted — laid out as ordinary text).
|
|
2096
|
+
*/
|
|
2097
|
+
declare function resolveStoryListItems(blocks: readonly OoxmlElement[], index: NumberingIndex, styleCascade: StyleCascadeTable | undefined, isFontAvailable?: (family: string) => boolean): ReadonlyMap<string, ResolvedListItem>;
|
|
2098
|
+
/**
|
|
2099
|
+
* Attach a full-story list-item map to layout options.
|
|
2100
|
+
*
|
|
2101
|
+
* Resolves once over `blocks` (body story including table cells) so counters continue across
|
|
2102
|
+
* section boundaries. No-ops when numbering is absent.
|
|
2103
|
+
*/
|
|
2104
|
+
declare function withResolvedListItems<T extends {
|
|
2105
|
+
readonly numberingIndex?: NumberingIndex;
|
|
2106
|
+
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
2107
|
+
readonly styleCascade?: StyleCascadeTable;
|
|
2108
|
+
readonly isFontAvailable?: (family: string) => boolean;
|
|
2109
|
+
readonly measurer?: TextMeasurer;
|
|
2110
|
+
}>(options: T, blocks: readonly OoxmlElement[]): T & {
|
|
2111
|
+
readonly numberingIndex: NumberingIndex;
|
|
2112
|
+
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2115
|
+
interface StyleDefinition {
|
|
2116
|
+
readonly styleId: string;
|
|
2117
|
+
readonly name?: string;
|
|
2118
|
+
readonly type: string;
|
|
2119
|
+
readonly basedOn: string | null;
|
|
2120
|
+
/** `w:next` — the authoring style for a following paragraph. */
|
|
2121
|
+
readonly next: string | null;
|
|
2122
|
+
readonly outlineLevel: number | null;
|
|
2123
|
+
readonly paragraphProperties: readonly OoxmlProperty[];
|
|
2124
|
+
readonly runProperties: readonly OoxmlProperty[];
|
|
2125
|
+
readonly paragraphPropertiesNode: OoxmlElement | undefined;
|
|
2126
|
+
readonly tablePropertiesNode: OoxmlElement | undefined;
|
|
2127
|
+
readonly tableRowPropertiesNode: OoxmlElement | undefined;
|
|
2128
|
+
readonly tableCellPropertiesNode?: OoxmlElement;
|
|
2129
|
+
readonly conditionalTableFormats: ReadonlyMap<string, OoxmlElement>;
|
|
2130
|
+
}
|
|
2131
|
+
declare function isValidStyleId(raw: string | undefined): raw is string;
|
|
2132
|
+
|
|
2133
|
+
/** Soft ceiling on style definitions read from one styles part. */
|
|
2134
|
+
declare const MAX_STYLE_DEFINITIONS = 4096;
|
|
2135
|
+
/**
|
|
2136
|
+
* The whole styles part, indexed and ready to resolve against.
|
|
2137
|
+
*
|
|
2138
|
+
* `cacheToken` is load-bearing: it folds into layout cache producers so breaks measured under one
|
|
2139
|
+
* styles part are never reused under another.
|
|
2140
|
+
*/
|
|
2141
|
+
interface StyleCascadeTable {
|
|
2142
|
+
/** Keep floating tables that fit a full page together under the compatibility flag. */
|
|
2143
|
+
readonly doNotBreakWrappedTables?: true;
|
|
2144
|
+
/** Legacy or explicitly disabled optional OpenType substitutions. */
|
|
2145
|
+
readonly disableOptionalLigatures?: true;
|
|
2146
|
+
/** Legacy noExtraLineSpacing behavior, included in the producer fingerprint. */
|
|
2147
|
+
readonly preserveExactLineBaseline?: true;
|
|
2148
|
+
/** `w:adjustLineHeightInTable`: cell paragraphs snap to the section line grid too. */
|
|
2149
|
+
readonly adjustLineHeightInTable?: true;
|
|
2150
|
+
/** `w:doNotUseIndentAsNumberingTabStop`, carried onto each paragraph's tab stops. */
|
|
2151
|
+
readonly ignoreIndentAsNumberingTabStop?: true;
|
|
2152
|
+
/**
|
|
2153
|
+
* `w:doNotUseHTMLParagraphAutoSpacing`: adjacent paragraph spacing adds up, and automatic
|
|
2154
|
+
* spacing is a fixed 5pt before and 10pt after.
|
|
2155
|
+
*/
|
|
2156
|
+
readonly fixedParagraphSpacing?: true;
|
|
2157
|
+
/**
|
|
2158
|
+
* `w:doNotExpandShiftReturn`: a justified line ending in a manual line break keeps its
|
|
2159
|
+
* natural spacing, as a last line does.
|
|
2160
|
+
*/
|
|
2161
|
+
readonly unstretchedManualBreakLines?: true;
|
|
2162
|
+
/**
|
|
2163
|
+
* Legacy modes: a justified line ending in a page or column break stretches to the measure
|
|
2164
|
+
* unless it is the last line of its paragraph.
|
|
2165
|
+
*/
|
|
2166
|
+
readonly pageBreakLinesStretch?: true;
|
|
2167
|
+
/** Explicit compatibility opt-in to the unmodified ISO table style hierarchy. */
|
|
2168
|
+
readonly strictTableStyleHierarchy?: boolean;
|
|
2169
|
+
readonly typography?: CjkTypographySettings;
|
|
2170
|
+
/**
|
|
2171
|
+
* Bounded fingerprint folded into layout cache producers so a different styles part cannot
|
|
2172
|
+
* reuse breaks measured under another cascade. Computed once per table (FNV-1a hex).
|
|
2173
|
+
*/
|
|
2174
|
+
readonly cacheToken: string;
|
|
2175
|
+
readonly docDefaultsRun: readonly OoxmlProperty[];
|
|
2176
|
+
readonly docDefaultsParagraph: readonly OoxmlProperty[];
|
|
2177
|
+
readonly docDefaultsParagraphNode: OoxmlElement | undefined;
|
|
2178
|
+
/** `w:style[@w:default='1'][@w:type='paragraph']` — last wins among defaults of that type. */
|
|
2179
|
+
readonly defaultParagraphStyleId: string | null;
|
|
2180
|
+
/** `w:style[@w:default='1'][@w:type='character']` — last wins among defaults of that type. */
|
|
2181
|
+
readonly defaultCharacterStyleId: string | null;
|
|
2182
|
+
/**
|
|
2183
|
+
* `w:style[@w:default='1'][@w:type='table']` — last wins among defaults of that type.
|
|
2184
|
+
*
|
|
2185
|
+
* A `w:tbl` with no `w:tblStyle` still resolves against this one. Word's own
|
|
2186
|
+
* `TableNormal` is where the 0/108/0/108 twip cell margins live, so skipping it gives
|
|
2187
|
+
* every unstyled table padding the document says it does not have.
|
|
2188
|
+
*/
|
|
2189
|
+
readonly defaultTableStyleId: string | null;
|
|
2190
|
+
/**
|
|
2191
|
+
* The theme part's typefaces, for `w:rFonts` theme references.
|
|
2192
|
+
*
|
|
2193
|
+
* Lives on the cascade because it is document-level style material with the same
|
|
2194
|
+
* lifetime, and because every site that resolves run properties already holds this table.
|
|
2195
|
+
*/
|
|
2196
|
+
readonly themeFonts: ThemeFonts;
|
|
2197
|
+
readonly styles: ReadonlyMap<string, StyleDefinition>;
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* A paragraph's properties after the cascade, plus the same list WITHOUT its own `w:pPr`.
|
|
2201
|
+
*
|
|
2202
|
+
* Both, because a writer needs to know what a paragraph INHERITS to decide whether setting a
|
|
2203
|
+
* value is a change or a no-op — and writing back an inherited value freezes it into the
|
|
2204
|
+
* paragraph as though the author had chosen it.
|
|
2205
|
+
*/
|
|
2206
|
+
interface CascadedParagraphFormatting {
|
|
2207
|
+
/** Flat paragraph properties in cascade order (defaults → bases → style → direct). */
|
|
2208
|
+
readonly paragraphProperties: readonly OoxmlProperty[];
|
|
2209
|
+
/** The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits. */
|
|
2210
|
+
readonly inheritedParagraphProperties: readonly OoxmlProperty[];
|
|
2211
|
+
/** Matching `w:pPr` nodes for nested border resolution. */
|
|
2212
|
+
readonly paragraphPropertyNodes: readonly OoxmlNode[];
|
|
2213
|
+
/**
|
|
2214
|
+
* Inherited run properties for CONTENT runs (before direct run `rPr`).
|
|
2215
|
+
*
|
|
2216
|
+
* Does NOT include direct `w:pPr/w:rPr` — that formats the paragraph MARK only
|
|
2217
|
+
* (ECMA-376 §17.3.1.36). Folding mark `w:sz` into content made BodyText runs with no
|
|
2218
|
+
* direct size paint at the mark's size (Selection Notice "or" alternative → 6.5pt).
|
|
2219
|
+
*/
|
|
2220
|
+
readonly runProperties: readonly OoxmlProperty[];
|
|
2221
|
+
/**
|
|
2222
|
+
* Content cascade plus the mark's `w:pPr/w:rPr`, including its `w:rStyle` character style —
|
|
2223
|
+
* empty-line metrics and the numbering marker. A line with content grows from the mark
|
|
2224
|
+
* without that character style.
|
|
2225
|
+
*/
|
|
2226
|
+
readonly markRunProperties: readonly OoxmlProperty[];
|
|
2227
|
+
/**
|
|
2228
|
+
* The style this paragraph resolved to, or null when it names none and there is no
|
|
2229
|
+
* document default. `w:contextualSpacing` compares neighbours by exactly this.
|
|
2230
|
+
*/
|
|
2231
|
+
readonly styleId: string | null;
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* What a table style contributes to the paragraphs of ONE cell: the style's whole-table
|
|
2235
|
+
* `w:pPr`/`w:rPr` followed by every `w:tblStylePr` the cell is under (17.7.6.6), weakest
|
|
2236
|
+
* first in the caller's condition order (banding, column, row, corner).
|
|
2237
|
+
*
|
|
2238
|
+
* This is how Word makes a header row bold and centred while the document states nothing
|
|
2239
|
+
* but `<w:tblStyle w:val="…"/>` on the table and plain runs in the cells.
|
|
2240
|
+
*/
|
|
2241
|
+
interface TableCellStyleFormatting {
|
|
2242
|
+
readonly paragraphProperties: readonly OoxmlProperty[];
|
|
2243
|
+
/** Matching `w:pPr` nodes, for nested `w:pBdr` / `w:tabs` resolution. */
|
|
2244
|
+
readonly paragraphPropertyNodes: readonly OoxmlElement[];
|
|
2245
|
+
/** Inherited run properties for every run in the cell, before the paragraph style. */
|
|
2246
|
+
readonly runProperties: readonly OoxmlProperty[];
|
|
2247
|
+
}
|
|
2248
|
+
/**
|
|
2249
|
+
* Build a cascade table from a styles part root.
|
|
2250
|
+
*
|
|
2251
|
+
* Only direct `w:style` children of the root participate (bounded count). Duplicate
|
|
2252
|
+
* `styleId` values keep the last definition, matching Word's reader for this fixture class.
|
|
2253
|
+
* Default paragraph/character style ids track `w:default="1"` with the same last-wins rule.
|
|
2254
|
+
*/
|
|
2255
|
+
declare function buildStyleCascadeTable(stylesRoot: OoxmlElement | null, themeFonts?: ThemeFonts, settingsRoot?: OoxmlElement | null): StyleCascadeTable;
|
|
2256
|
+
/**
|
|
2257
|
+
* Cascade paragraph + inherited run properties for one paragraph's direct `w:pPr`.
|
|
2258
|
+
*
|
|
2259
|
+
* Order: `docDefaults` → table style → `basedOn` ancestors → paragraph style → direct
|
|
2260
|
+
* formatting, which is the style hierarchy of 17.7.2: a table style sits above the document
|
|
2261
|
+
* defaults and below the paragraph style a cell paragraph names for itself.
|
|
2262
|
+
* When `w:pStyle` is absent, the document's default paragraph style (`w:default="1"`) is used.
|
|
2263
|
+
* Direct formatting is last so it overrides inherited values inside the existing resolvers.
|
|
2264
|
+
*/
|
|
2265
|
+
declare function cascadeParagraphFormatting(table: StyleCascadeTable, directPPr: OoxmlNode | undefined, tableCellStyle?: TableCellStyleFormatting): CascadedParagraphFormatting;
|
|
2266
|
+
/**
|
|
2267
|
+
* Bottom border after cascade: a later `w:pBdr` replaces an earlier one; absence inherits.
|
|
2268
|
+
* `nil`/`none` clear the edge via `paragraphBorders`.
|
|
2269
|
+
*/
|
|
2270
|
+
declare function cascadedBottomBorder(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorderEdge | undefined;
|
|
2271
|
+
/**
|
|
2272
|
+
* Merge inherited paragraph-style run props with a run's direct `rPr` (direct last).
|
|
2273
|
+
*
|
|
2274
|
+
* When a cascade table is supplied, also resolves `w:rStyle` character styles (basedOn chain,
|
|
2275
|
+
* cycle/depth capped). Runs without an explicit `rStyle` pick up the default character style
|
|
2276
|
+
* (`w:default="1"`). Precedence: inherited → character style chain → direct formatting.
|
|
2277
|
+
*
|
|
2278
|
+
* PASS `inheritedRunProperties` BY IDENTITY. It must be an array
|
|
2279
|
+
* {@link cascadeParagraphFormatting} returned — `runProperties` or `markRunProperties` — and
|
|
2280
|
+
* not a copy of one. A toggle property (ECMA-376 §17.7.3) resolves per level of the style
|
|
2281
|
+
* hierarchy, and the levels below the character style resolve to more than a true or a false:
|
|
2282
|
+
* whether the document defaults' short circuit is still standing decides what the character
|
|
2283
|
+
* style's own toggle does next, and no single `w:b` element can spell that. The paragraph
|
|
2284
|
+
* cascade attaches that state to the array it returns, so spreading, filtering or sorting the
|
|
2285
|
+
* list drops it. (`Object.freeze` returns the same object, so that one is safe.) A list
|
|
2286
|
+
* without it is read as one ordinary level, which is the most a bare property list can say
|
|
2287
|
+
* and is what a caller assembling its own list gets.
|
|
2288
|
+
*/
|
|
2289
|
+
declare function cascadeRunProperties(inheritedRunProperties: readonly OoxmlProperty[], directRunProperties: readonly OoxmlProperty[], table?: StyleCascadeTable): readonly OoxmlProperty[];
|
|
2290
|
+
/** Everything the line breaker needs about one paragraph, already cascaded and converted. */
|
|
2291
|
+
interface ParagraphLayoutInputs {
|
|
2292
|
+
readonly props: OoxmlProperty[];
|
|
2293
|
+
readonly indent: {
|
|
2294
|
+
left: number;
|
|
2295
|
+
right: number;
|
|
2296
|
+
hanging: number;
|
|
2297
|
+
firstLine: number;
|
|
2298
|
+
};
|
|
2299
|
+
readonly available: number;
|
|
2300
|
+
readonly alignment: Alignment;
|
|
2301
|
+
readonly spacing: ParagraphSpacing;
|
|
2302
|
+
/** Resolved `w:line` / `w:lineRule`; single spacing where the cascade says nothing. */
|
|
2303
|
+
readonly lineSpacing: ParagraphLineSpacing;
|
|
2304
|
+
/** `w:contextualSpacing`: drop before/after between same-style neighbours. */
|
|
2305
|
+
readonly contextualSpacing: boolean;
|
|
2306
|
+
/** Resolved paragraph style id, for the `w:contextualSpacing` neighbour comparison. */
|
|
2307
|
+
readonly styleId: string | null;
|
|
2308
|
+
/** Resolved outline level, with 9/invalid values treated as body text. */
|
|
2309
|
+
readonly outlineLevel: number | null;
|
|
2310
|
+
readonly bottomBorder: ParagraphBorderEdge | undefined;
|
|
2311
|
+
/**
|
|
2312
|
+
* Every `CT_PBdr` edge after cascade, not just the bottom one.
|
|
2313
|
+
*
|
|
2314
|
+
* `bottomBorder` stays alongside it because the fragment signature and the table flow
|
|
2315
|
+
* read that field by name; this is the whole box, so a cell paragraph gets the same
|
|
2316
|
+
* frame a body paragraph does.
|
|
2317
|
+
*/
|
|
2318
|
+
readonly borders: ParagraphBorders;
|
|
2319
|
+
/** Validated 6-hex paragraph shading fill from cascaded `w:pPr/w:shd`, absent for none. */
|
|
2320
|
+
readonly shading: string | undefined;
|
|
2321
|
+
readonly inheritedRunProperties: readonly OoxmlProperty[];
|
|
2322
|
+
/**
|
|
2323
|
+
* Paragraph-mark cascade (`inheritedRunProperties` + `w:pPr/w:rPr` and its `w:rStyle`).
|
|
2324
|
+
* Empty-line sizing and last-line mark height (without the `w:rStyle`) — never content face.
|
|
2325
|
+
*/
|
|
2326
|
+
readonly markRunProperties: readonly OoxmlProperty[];
|
|
2327
|
+
/** Cascaded custom tab stops + default interval for paragraph-flow breaking. */
|
|
2328
|
+
readonly tabStops: ResolvedTabStops;
|
|
2329
|
+
/**
|
|
2330
|
+
* Fingerprint folded into the paragraph layout cache key — nested `w:tabs` are absent
|
|
2331
|
+
* from flat property bags, so style-inherited stops must be named explicitly.
|
|
2332
|
+
*/
|
|
2333
|
+
readonly tabStopsCacheToken: string;
|
|
2334
|
+
/** Resolved list marker inputs when the paragraph participates in numbering. */
|
|
2335
|
+
readonly listItem?: ResolvedListItem;
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Resolve every paragraph input semantic layout / table cells share: cascaded props when a
|
|
2339
|
+
* style table is present, otherwise direct formatting only.
|
|
2340
|
+
*
|
|
2341
|
+
* When `listItem` is provided, its merged level indent becomes the paragraph indent (list
|
|
2342
|
+
* hanging / left from `numbering.xml`), which is what Word uses for fixture list paragraphs
|
|
2343
|
+
* that author no direct `w:ind`.
|
|
2344
|
+
*
|
|
2345
|
+
* `tableCellStyle` carries what the enclosing table's style says about this cell's
|
|
2346
|
+
* paragraphs; body paragraphs pass nothing.
|
|
2347
|
+
*
|
|
2348
|
+
* `inTableCell` is asked for separately because a cell paragraph may have no table style to
|
|
2349
|
+
* inherit at all, and `w:beforeAutospacing` still needs to know it is in a cell.
|
|
2350
|
+
*
|
|
2351
|
+
* `lineUnitPt` is the section's ACTIVE line-grid pitch. It sizes line-unit paragraph margins
|
|
2352
|
+
* and snaps the paragraph's lines to the grid; absent means no grid and the fixed 12pt unit.
|
|
2353
|
+
*/
|
|
2354
|
+
declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean, lineUnitPt?: number): ParagraphLayoutInputs;
|
|
2355
|
+
|
|
2356
|
+
interface EquationGeometryBase {
|
|
2357
|
+
readonly box: LayoutBox;
|
|
2358
|
+
/** Baseline measured from this node's top edge. */
|
|
2359
|
+
readonly baseline: number;
|
|
2360
|
+
}
|
|
2361
|
+
interface EquationTextGeometry extends EquationGeometryBase {
|
|
2362
|
+
readonly kind: 'text' | 'fallback';
|
|
2363
|
+
readonly text: string;
|
|
2364
|
+
readonly fontSizePt: number;
|
|
2365
|
+
}
|
|
2366
|
+
interface EquationRowGeometry extends EquationGeometryBase {
|
|
2367
|
+
readonly kind: 'row';
|
|
2368
|
+
readonly items: readonly EquationGeometry[];
|
|
2369
|
+
}
|
|
2370
|
+
interface EquationFractionGeometry extends EquationGeometryBase {
|
|
2371
|
+
readonly kind: 'fraction';
|
|
2372
|
+
readonly numerator: EquationGeometry;
|
|
2373
|
+
readonly denominator: EquationGeometry;
|
|
2374
|
+
readonly bar: LayoutBox;
|
|
2375
|
+
}
|
|
2376
|
+
interface EquationRadicalGeometry extends EquationGeometryBase {
|
|
2377
|
+
readonly kind: 'radical';
|
|
2378
|
+
readonly sign: EquationTextGeometry;
|
|
2379
|
+
readonly radicand: EquationGeometry;
|
|
2380
|
+
readonly degree?: EquationGeometry;
|
|
2381
|
+
readonly bar: LayoutBox;
|
|
2382
|
+
}
|
|
2383
|
+
interface EquationScriptGeometry extends EquationGeometryBase {
|
|
2384
|
+
readonly kind: 'script';
|
|
2385
|
+
readonly base: EquationGeometry;
|
|
2386
|
+
readonly subscript?: EquationGeometry;
|
|
2387
|
+
readonly superscript?: EquationGeometry;
|
|
2388
|
+
}
|
|
2389
|
+
interface EquationNaryGeometry extends EquationGeometryBase {
|
|
2390
|
+
readonly kind: 'nary';
|
|
2391
|
+
readonly operator: EquationTextGeometry;
|
|
2392
|
+
readonly body: EquationGeometry;
|
|
2393
|
+
readonly lowerLimit?: EquationGeometry;
|
|
2394
|
+
readonly upperLimit?: EquationGeometry;
|
|
2395
|
+
}
|
|
2396
|
+
type EquationGeometry = EquationTextGeometry | EquationRowGeometry | EquationFractionGeometry | EquationRadicalGeometry | EquationScriptGeometry | EquationNaryGeometry;
|
|
2397
|
+
/** Equation metadata carried by one atomic semantic span. */
|
|
2398
|
+
interface EquationSpanRecord {
|
|
2399
|
+
readonly sourceNodeId: string;
|
|
2400
|
+
readonly geometry: EquationGeometry;
|
|
2401
|
+
readonly fallbackText: string;
|
|
2402
|
+
readonly truncated: boolean;
|
|
2403
|
+
}
|
|
2404
|
+
/** Compose one bounded OMML projection into paint-ready point geometry. */
|
|
2405
|
+
declare function layoutEquation(projection: OmmlEquationProjection, measurer: TextMeasurer, style: ResolvedRunStyle): EquationSpanRecord;
|
|
2406
|
+
|
|
2407
|
+
/**
|
|
2408
|
+
* The four physical sides of `CT_PageBorders`.
|
|
2409
|
+
*
|
|
2410
|
+
* Four, not the six of `w:pBdr`: `w:between` and `w:bar` are paragraph-group rules and the
|
|
2411
|
+
* page has no analogue for either.
|
|
2412
|
+
*/
|
|
2413
|
+
declare const PAGE_BORDER_SIDES: readonly ['top', 'left', 'bottom', 'right'];
|
|
2414
|
+
/** Which of the four page-frame edges. */
|
|
2415
|
+
type PageBorderSide = (typeof PAGE_BORDER_SIDES)[number];
|
|
2416
|
+
/**
|
|
2417
|
+
* `w:offsetFrom` (`ST_PgBorderOffset`) — what `w:space` on each edge is measured from.
|
|
2418
|
+
*
|
|
2419
|
+
* The schema default is `text`, NOT `page`. Word's own UI opens on "Edge of page", which is
|
|
2420
|
+
* why the wrong default is easy to assume and expensive to hold: at `text` the rule sits
|
|
2421
|
+
* `w:space` points outside the TEXT, so its distance from the sheet edge is the margin minus
|
|
2422
|
+
* the space — a number that moves whenever the margins do.
|
|
2423
|
+
*/
|
|
2424
|
+
type PageBorderOffsetFrom = 'page' | 'text';
|
|
2425
|
+
/** `w:display` (`ST_PgBorderDisplay`) — which pages of the section carry the frame. */
|
|
2426
|
+
type PageBorderDisplay = 'allPages' | 'firstPage' | 'notFirstPage';
|
|
2427
|
+
/** `w:zOrder` (`ST_PgBorderZOrder`) — whether the frame paints over the text or under it. */
|
|
2428
|
+
type PageBorderZOrder = 'front' | 'back';
|
|
2429
|
+
/**
|
|
2430
|
+
* One section's resolved `w:pgBorders`.
|
|
2431
|
+
*
|
|
2432
|
+
* Resolved, not raw: every attribute the file omitted is filled in with the SCHEMA default,
|
|
2433
|
+
* so a consumer never has to know which of the three were authored. An edge is present only
|
|
2434
|
+
* when it paints — `nil` / `none` and art borders are already gone (see {@link parsePageBorders}).
|
|
2435
|
+
*/
|
|
2436
|
+
interface SectionPageBorders {
|
|
2437
|
+
/** `w:offsetFrom`; absent attribute reads as `text` (§17.6.10). */
|
|
2438
|
+
readonly offsetFrom: PageBorderOffsetFrom;
|
|
2439
|
+
/** `w:display`; absent attribute reads as `allPages`. */
|
|
2440
|
+
readonly display: PageBorderDisplay;
|
|
2441
|
+
/** `w:zOrder`; absent attribute reads as `front`. */
|
|
2442
|
+
readonly zOrder: PageBorderZOrder;
|
|
2443
|
+
readonly top?: ParagraphBorderEdge;
|
|
2444
|
+
readonly left?: ParagraphBorderEdge;
|
|
2445
|
+
readonly bottom?: ParagraphBorderEdge;
|
|
2446
|
+
readonly right?: ParagraphBorderEdge;
|
|
2447
|
+
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Parse `w:pgBorders` off one `w:sectPr`.
|
|
2450
|
+
*
|
|
2451
|
+
* Undefined when the element is absent AND when it paints nothing — every edge off, or every
|
|
2452
|
+
* edge an art border. Unlike `w:pgNumType`, an empty-but-present element is not distinguished:
|
|
2453
|
+
* these properties are a LAYOUT input, serialization re-emits the canonical tree, and a frame
|
|
2454
|
+
* with no edges has exactly one meaning downstream.
|
|
2455
|
+
*/
|
|
2456
|
+
declare function parsePageBorders(sectPr: OoxmlNode): SectionPageBorders | undefined;
|
|
2457
|
+
/**
|
|
2458
|
+
* Identity of one section's page frame, for incremental-layout context keys.
|
|
2459
|
+
*
|
|
2460
|
+
* A `w:pgBorders` edit changes nothing the flow measures — the frame is drawn beside the text,
|
|
2461
|
+
* never through it — so no per-paragraph key moves and every reuse path would hand back the
|
|
2462
|
+
* previous sheets with the previous frame. This token goes in the pass context so it does not.
|
|
2463
|
+
*/
|
|
2464
|
+
declare function pageBordersFingerprint(borders: SectionPageBorders | undefined): string;
|
|
2465
|
+
|
|
2466
|
+
/** Published root story containing an artifact occurrence. @public */
|
|
2467
|
+
type SemanticArtifactRootStoryKind = 'body' | 'header' | 'footer' | 'footnote' | 'endnote' | 'note-separator';
|
|
2468
|
+
/** Exact story in which a review artifact's source anchor was laid out. @public */
|
|
2469
|
+
type SemanticArtifactStoryKind = SemanticArtifactRootStoryKind | 'textbox';
|
|
2470
|
+
/** One model position retained as exporter provenance. @public */
|
|
2471
|
+
interface SemanticReviewArtifactPosition {
|
|
2472
|
+
readonly paragraphId: string;
|
|
2473
|
+
readonly offset: number;
|
|
2474
|
+
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Package-relative snapshot provenance, not durable public identity.
|
|
2477
|
+
*
|
|
2478
|
+
* Engine-produced occurrences keep `start.paragraphId === end.paragraphId`. Cross-paragraph
|
|
2479
|
+
* source ranges are sliced into per-paragraph occurrences before geometry attaches.
|
|
2480
|
+
* @public
|
|
2481
|
+
*/
|
|
2482
|
+
interface SemanticReviewArtifactSource {
|
|
2483
|
+
readonly partName: string;
|
|
2484
|
+
readonly start: SemanticReviewArtifactPosition;
|
|
2485
|
+
readonly end: SemanticReviewArtifactPosition;
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* Laid-out bounds for one review occurrence.
|
|
2489
|
+
*
|
|
2490
|
+
* `pageContent` uses the same space as line boxes. `pageStack` uses stacked page coordinates.
|
|
2491
|
+
* A point occurrence publishes `width: 0`.
|
|
2492
|
+
* @public
|
|
2493
|
+
*/
|
|
2494
|
+
interface SemanticReviewArtifactOccurrenceGeometry {
|
|
2495
|
+
readonly pageContent: readonly LayoutBox[];
|
|
2496
|
+
readonly pageStack: readonly LayoutBox[];
|
|
2497
|
+
}
|
|
2498
|
+
/** One physical occurrence of a comment or tracked-change source range. @public */
|
|
2499
|
+
interface SemanticReviewArtifactOccurrence {
|
|
2500
|
+
readonly pageIndex: number;
|
|
2501
|
+
readonly physicalPageNumber: number;
|
|
2502
|
+
readonly story: SemanticArtifactStoryKind;
|
|
2503
|
+
readonly rootStory: SemanticArtifactRootStoryKind;
|
|
2504
|
+
/** Root-to-leaf drawing ids for a textbox occurrence; empty outside textboxes. */
|
|
2505
|
+
readonly textboxPath: readonly string[];
|
|
2506
|
+
readonly noteScopeId: string | null;
|
|
2507
|
+
readonly noteAreaKind: 'footnotes' | 'endnotes' | null;
|
|
2508
|
+
/** Replacement-half meaning for tracked-change occurrences; absent for comments. */
|
|
2509
|
+
readonly revisionRole?: 'replaced' | 'replacement' | 'neutral';
|
|
2510
|
+
readonly source: SemanticReviewArtifactSource;
|
|
2511
|
+
/** Laid-out bounds when this occurrence can be measured; omitted otherwise. */
|
|
2512
|
+
readonly geometry?: SemanticReviewArtifactOccurrenceGeometry;
|
|
2513
|
+
}
|
|
2514
|
+
/** Normalized change; ids are opaque and stable only within the source snapshot. @public */
|
|
2515
|
+
interface SemanticTrackedChangeArtifactRecord {
|
|
2516
|
+
readonly kind: 'tracked-change';
|
|
2517
|
+
readonly id: string;
|
|
2518
|
+
readonly change: 'insert' | 'delete' | 'replace' | 'moveFrom' | 'moveTo' | 'format' | 'paragraphMark' | 'structural';
|
|
2519
|
+
/** Structural changes represented by this review artifact. */
|
|
2520
|
+
readonly structuralChanges?: readonly ('rowInsert' | 'rowDelete' | 'cellInsert' | 'cellDelete' | 'cellMerge' | 'numberingInsert')[];
|
|
2521
|
+
readonly markDirection?: 'insert' | 'delete' | 'moveFrom' | 'moveTo';
|
|
2522
|
+
readonly author: string;
|
|
2523
|
+
readonly date?: string;
|
|
2524
|
+
readonly text: string;
|
|
2525
|
+
readonly replacedText: string;
|
|
2526
|
+
/** Nested tracked-change depth, where the innermost decision is operative. */
|
|
2527
|
+
readonly nesting: number;
|
|
2528
|
+
/** Number of leading source ranges belonging to struck text in a replacement. */
|
|
2529
|
+
readonly replacedRangeCount?: number;
|
|
2530
|
+
readonly readOnly: boolean;
|
|
2531
|
+
readonly pairedWith?: string;
|
|
2532
|
+
readonly replyIds: readonly string[];
|
|
2533
|
+
readonly occurrences: readonly SemanticReviewArtifactOccurrence[];
|
|
2534
|
+
}
|
|
2535
|
+
/** Normalized comment; all relation ids share the opaque snapshot-local id space. @public */
|
|
2536
|
+
interface SemanticCommentArtifactRecord {
|
|
2537
|
+
readonly kind: 'comment';
|
|
2538
|
+
readonly id: string;
|
|
2539
|
+
readonly author: string;
|
|
2540
|
+
readonly initials: string;
|
|
2541
|
+
readonly date?: string;
|
|
2542
|
+
readonly text: string;
|
|
2543
|
+
readonly resolved: boolean;
|
|
2544
|
+
readonly parentId?: string;
|
|
2545
|
+
readonly parentRevisionId?: string;
|
|
2546
|
+
readonly replyIds: readonly string[];
|
|
2547
|
+
readonly orphaned: boolean;
|
|
2548
|
+
readonly occurrences: readonly SemanticReviewArtifactOccurrence[];
|
|
2549
|
+
}
|
|
2550
|
+
/** Exporter-neutral review artifact normalized by core. @public */
|
|
2551
|
+
type SemanticReviewArtifactRecord = SemanticTrackedChangeArtifactRecord | SemanticCommentArtifactRecord;
|
|
2552
|
+
|
|
2553
|
+
/** A half-open UTF-16 range inside one paragraph, addressed by its canonical node id. */
|
|
2554
|
+
interface SourceRange {
|
|
2555
|
+
readonly paragraphId: string;
|
|
2556
|
+
readonly start: number;
|
|
2557
|
+
readonly end: number;
|
|
2558
|
+
}
|
|
2559
|
+
/**
|
|
2560
|
+
* A rectangle in layout POINTS.
|
|
2561
|
+
*
|
|
2562
|
+
* Points everywhere in this layer — twips convert at property-read boundaries and CSS pixels at
|
|
2563
|
+
* paint. A box carrying either of those would eventually be added to one carrying the other.
|
|
2564
|
+
*/
|
|
2565
|
+
interface LayoutBox {
|
|
2566
|
+
readonly x: number;
|
|
2567
|
+
readonly y: number;
|
|
2568
|
+
readonly width: number;
|
|
2569
|
+
readonly height: number;
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* A bottom paragraph border as layout published it.
|
|
2573
|
+
*
|
|
2574
|
+
* `box` is the rule's geometry in the same coordinate space as the fragment (page-content
|
|
2575
|
+
* relative). Paint positions from this box and MUST NOT remeasure the border.
|
|
2576
|
+
*/
|
|
2577
|
+
interface ParagraphBottomBorderRecord {
|
|
2578
|
+
readonly edge: ParagraphBorderEdge;
|
|
2579
|
+
readonly box: LayoutBox;
|
|
2580
|
+
}
|
|
2581
|
+
/**
|
|
2582
|
+
* One `w:pBdr` rule as layout published it.
|
|
2583
|
+
*
|
|
2584
|
+
* `box` is the STROKE rectangle in the same coordinate space as the fragment, so paint sets a
|
|
2585
|
+
* position and a colour and nothing else. It matters that paint cannot derive these itself:
|
|
2586
|
+
* Word draws the side rules OUTSIDE the text column, so `box.x` on a `left`/`bar` stroke is
|
|
2587
|
+
* left of the fragment box and a painter reasoning from the fragment alone would put it
|
|
2588
|
+
* inside the text.
|
|
2589
|
+
*/
|
|
2590
|
+
interface ParagraphBorderStrokeRecord {
|
|
2591
|
+
readonly side: ParagraphBorderSide;
|
|
2592
|
+
readonly edge: ParagraphBorderEdge;
|
|
2593
|
+
readonly box: LayoutBox;
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* One `w:pgBorders` rule as layout published it.
|
|
2597
|
+
*
|
|
2598
|
+
* `box` is the stroke rectangle in PAGE-BOX-relative points — measured from the sheet's own
|
|
2599
|
+
* top-left, not from the document-stacked absolute origin and not from the content box. That
|
|
2600
|
+
* is what lets a reused sheet move down the stack (`remapPage`) without the frame being
|
|
2601
|
+
* recomputed or shifted: the frame's place on the paper never depended on where the paper is.
|
|
2602
|
+
*
|
|
2603
|
+
* Paint MUST NOT re-derive the inset. `w:offsetFrom` decides whether `w:space` counts from the
|
|
2604
|
+
* sheet edge or from the text, and resolving that needs the section margins, which is layout's
|
|
2605
|
+
* to know — the same division `columnSeparators` already draws.
|
|
2606
|
+
*/
|
|
2607
|
+
interface PageBorderStrokeRecord {
|
|
2608
|
+
readonly side: PageBorderSide;
|
|
2609
|
+
readonly edge: ParagraphBorderEdge;
|
|
2610
|
+
readonly box: LayoutBox;
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* The page frame on one sheet, already filtered by `w:display`.
|
|
2614
|
+
*
|
|
2615
|
+
* Present only on the pages that actually carry it, so a consumer holding a single page record
|
|
2616
|
+
* needs no section context to know what to draw. `display` rides along because a sheet MINTED
|
|
2617
|
+
* after layout (note overflow) has to decide whether it inherits its template's frame, and a
|
|
2618
|
+
* `firstPage` frame is the one that must not be inherited.
|
|
2619
|
+
*/
|
|
2620
|
+
interface PageBorderFrameRecord {
|
|
2621
|
+
readonly zOrder: PageBorderZOrder;
|
|
2622
|
+
readonly display: PageBorderDisplay;
|
|
2623
|
+
readonly strokes: readonly PageBorderStrokeRecord[];
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* The hyperlink a span sits inside, as layout resolved it.
|
|
2627
|
+
*
|
|
2628
|
+
* Already SANITIZED: `href` is the runtime projection produced once at the trust boundary,
|
|
2629
|
+
* and `null` means the link is inert — a refused scheme, or a relationship the package does
|
|
2630
|
+
* not declare. Paint, hit-testing and the popover consume this and never the authored target,
|
|
2631
|
+
* so there is exactly one place a file-derived URL becomes something a browser can follow.
|
|
2632
|
+
*
|
|
2633
|
+
* `id` is the `w:hyperlink` node's canonical id, which is what makes the spans of one link
|
|
2634
|
+
* recognisable as one link across the several lines it wraps onto — and what an unlink or a
|
|
2635
|
+
* retarget addresses.
|
|
2636
|
+
*/
|
|
2637
|
+
interface SpanLinkRecord {
|
|
2638
|
+
readonly id: string;
|
|
2639
|
+
readonly kind: 'external' | 'internal' | 'unresolved';
|
|
2640
|
+
/** Sanitized runtime projection: an absolute URL, `#anchor`, or null when inert. */
|
|
2641
|
+
readonly href: string | null;
|
|
2642
|
+
/** Bookmark name for an internal link, so navigation need not re-parse the fragment. */
|
|
2643
|
+
readonly anchor?: string;
|
|
2644
|
+
/** `w:tooltip` — paint puts it on the anchor's `title`. */
|
|
2645
|
+
readonly tooltip?: string;
|
|
2646
|
+
}
|
|
2647
|
+
/** A run of text on one line sharing identical resolved formatting. */
|
|
2648
|
+
interface StyleSpanRecord {
|
|
2649
|
+
/**
|
|
2650
|
+
* Horizontal ink displacement in points from box.x, before drawing the glyphs.
|
|
2651
|
+
* CJK opening punctuation removes its left side bearing; its advance and caret
|
|
2652
|
+
* boundaries remain box-based. Zero marks trailing-bearing compression with no
|
|
2653
|
+
* ink displacement. Exporters must apply this offset to the ink origin.
|
|
2654
|
+
*/
|
|
2655
|
+
readonly glyphOffsetPt?: number;
|
|
2656
|
+
/** Measured face baseline for character-border geometry; span boxes remain selection bands. */
|
|
2657
|
+
readonly borderBaselinePt?: number;
|
|
2658
|
+
readonly range: SourceRange;
|
|
2659
|
+
readonly text: string;
|
|
2660
|
+
/** The run's authored properties, retained as evidence. */
|
|
2661
|
+
readonly props: readonly OoxmlProperty[];
|
|
2662
|
+
/**
|
|
2663
|
+
* The same properties RESOLVED — one unit system, defaults applied.
|
|
2664
|
+
*
|
|
2665
|
+
* Carried on the span so the measurer, the span and the painter all read one resolution
|
|
2666
|
+
* rather than each deriving its own. Two derivations that disagree by a fraction of a
|
|
2667
|
+
* point put the caret where no glyph is.
|
|
2668
|
+
*/
|
|
2669
|
+
readonly style: ResolvedRunStyle;
|
|
2670
|
+
/**
|
|
2671
|
+
* The `w:rFonts` slot this span's text resolves its face through; absent means the base
|
|
2672
|
+
* (ascii/hAnsi) slots. {@link style} stays the run's full resolution — with both
|
|
2673
|
+
* `fontFamily` and `fontFamilyEastAsia` — so formatting readback and the format painter
|
|
2674
|
+
* see the run as authored; measurement, paint and hit-testing resolve the effective face
|
|
2675
|
+
* with `styleForFontSlot(span.style, span.fontSlot)`.
|
|
2676
|
+
*/
|
|
2677
|
+
readonly fontSlot?: FontSlot;
|
|
2678
|
+
readonly box: LayoutBox;
|
|
2679
|
+
/** Authored trailing spaces that layout may clip at the line's right edge. */
|
|
2680
|
+
readonly lineEndWhitespace?: true;
|
|
2681
|
+
/**
|
|
2682
|
+
* An optional hyphen (`w:softHyphen`) where the line breaks. {@link text} stays U+00AD, one
|
|
2683
|
+
* model character; {@link box} holds the advance of the visible hyphen that paint draws in
|
|
2684
|
+
* its place. Absent for an optional hyphen inside a line, which draws nothing.
|
|
2685
|
+
*/
|
|
2686
|
+
readonly optionalHyphenBreak?: true;
|
|
2687
|
+
/**
|
|
2688
|
+
* Cumulative advances from {@link box}.x to each UTF-16 caret boundary in {@link text}.
|
|
2689
|
+
*
|
|
2690
|
+
* Length is `text.length + 1` (both endpoints). When PRESENT, these are the interaction
|
|
2691
|
+
* authority: hit-testing and the caret read them instead of re-measuring. Layout does not
|
|
2692
|
+
* publish them eagerly — measuring every character of every laid span dominated page
|
|
2693
|
+
* repagination — so interactions measure prefixes on demand through the same measurer the
|
|
2694
|
+
* span was laid with (memoized per span; see `prefixWidth` in semantic-hit-test.ts), and
|
|
2695
|
+
* only interpolate across {@link box}.width when no measurer is supplied.
|
|
2696
|
+
*/
|
|
2697
|
+
readonly caretEdges?: readonly number[];
|
|
2698
|
+
/**
|
|
2699
|
+
* `w:tab/@w:leader` of the stop a `\t` span advanced to (ECMA-376 §17.3.1.38).
|
|
2700
|
+
*
|
|
2701
|
+
* Only ever set on a tab span, and only for a non-`none` leader. Paint repeats the glyph
|
|
2702
|
+
* across the advance THIS box already reserved — the leader adds no width of its own, so
|
|
2703
|
+
* it can never move the text that follows it.
|
|
2704
|
+
*/
|
|
2705
|
+
readonly tabLeader?: TabLeader;
|
|
2706
|
+
/**
|
|
2707
|
+
* Advance of ONE leader glyph in this run's face, in points, measured by layout.
|
|
2708
|
+
*
|
|
2709
|
+
* Paint cannot ask a font how wide a character is, so without this it had to guess and
|
|
2710
|
+
* deliberately overfill, leaving the dots at whatever spacing an over-long string
|
|
2711
|
+
* happened to produce. Measured, the leader is spaced exactly as the same character
|
|
2712
|
+
* typed there would be — which is what Word draws.
|
|
2713
|
+
*/
|
|
2714
|
+
readonly tabLeaderAdvancePt?: number;
|
|
2715
|
+
/**
|
|
2716
|
+
* The hyperlink this span belongs to, or absent for ordinary text.
|
|
2717
|
+
*
|
|
2718
|
+
* Carried on the SPAN rather than looked up at paint time because a link that wraps
|
|
2719
|
+
* produces one set of spans per line, and paint has no paragraph to walk — only the line
|
|
2720
|
+
* it was handed.
|
|
2721
|
+
*/
|
|
2722
|
+
readonly link?: SpanLinkRecord;
|
|
2723
|
+
/**
|
|
2724
|
+
* Horizontal jump, in points, that a floating object's wrap zone forced before this span.
|
|
2725
|
+
*
|
|
2726
|
+
* A line that resumes on the far side of a float is still ONE line: its spans keep running
|
|
2727
|
+
* model offsets and share a selection band. Only layout knows the jump is an obstacle
|
|
2728
|
+
* rather than justification slack, so it publishes it here. Paint reserves it with an inert
|
|
2729
|
+
* advance and excludes it from the word spacing it reconstructs — inferring the difference
|
|
2730
|
+
* from the gap alone spread the float's whole width across every space on the line.
|
|
2731
|
+
*/
|
|
2732
|
+
readonly wrapAdvanceBefore?: number;
|
|
2733
|
+
/**
|
|
2734
|
+
* The revision wrappers this text sits inside, outermost first, absent when untracked.
|
|
2735
|
+
*
|
|
2736
|
+
* Carried on the span for the same reason `style` is: paint and the review surface must read
|
|
2737
|
+
* ONE attribution. A sidebar that re-derived which revision covers a span by walking the tree
|
|
2738
|
+
* could disagree with what was painted, and the card would point at the wrong text.
|
|
2739
|
+
*/
|
|
2740
|
+
readonly revisions?: readonly RevisionAttribution[];
|
|
2741
|
+
/**
|
|
2742
|
+
* The revisions a resolved view accepted into this text, which paints as ordinary text.
|
|
2743
|
+
*
|
|
2744
|
+
* Absent in All Markup, where {@link revisions} carries the markup instead, and absent for
|
|
2745
|
+
* text no included revision touched. The Simple Markup change bar is its only reader; nothing
|
|
2746
|
+
* inline draws from it.
|
|
2747
|
+
*/
|
|
2748
|
+
readonly changeSites?: readonly RevisionAttribution[];
|
|
2749
|
+
/**
|
|
2750
|
+
* Present when this span is a field's displayed RESULT, for the shading Word draws under one.
|
|
2751
|
+
*
|
|
2752
|
+
* `projected` cannot answer this: it is also set for note marks and inline drawings, so it
|
|
2753
|
+
* says "layout owns these glyphs" rather than "this is a field". Word shades legacy form
|
|
2754
|
+
* fields on a different rule from ordinary ones, which is why the marker distinguishes them.
|
|
2755
|
+
*
|
|
2756
|
+
* States the FACT, never the appearance — whether shading is drawn is a view decision, made
|
|
2757
|
+
* downstream from a host option and the document's own `w:doNotShadeFormData`. Deciding it
|
|
2758
|
+
* here would put the caret into layout's cache key and repaginate on every arrow press.
|
|
2759
|
+
*/
|
|
2760
|
+
readonly fieldAtom?: FieldAtomMarker;
|
|
2761
|
+
/**
|
|
2762
|
+
* Live PAGE/NUMPAGES/SECTIONPAGES projection (layout-time evaluated text).
|
|
2763
|
+
*
|
|
2764
|
+
* Computed substitutions are not model-editable: paint treats these as atomic furniture
|
|
2765
|
+
* and selection mapping refuses them the way it refuses markers.
|
|
2766
|
+
*/
|
|
2767
|
+
readonly projected?: boolean;
|
|
2768
|
+
/** Authored inline separator rule; its advance belongs to one note atom. */
|
|
2769
|
+
readonly noteSeparator?: 'separator' | 'continuationSeparator';
|
|
2770
|
+
/** Paint-ready geometry for one atomic Office Math equation. */
|
|
2771
|
+
readonly equation?: EquationSpanRecord;
|
|
2772
|
+
/**
|
|
2773
|
+
* Footnote/endnote navigation metadata for projected note atoms.
|
|
2774
|
+
*
|
|
2775
|
+
* Paint tags body citations (`to-note`) and note-body marks (`to-body`) so React chrome
|
|
2776
|
+
* can jump without owning layout logic.
|
|
2777
|
+
*/
|
|
2778
|
+
readonly noteNav?: {
|
|
2779
|
+
readonly scopeId: string;
|
|
2780
|
+
readonly direction: 'to-note' | 'to-body';
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
/**
|
|
2784
|
+
* One laid-out line: its geometry, its baseline, and the styled spans it renders.
|
|
2785
|
+
*
|
|
2786
|
+
* The unit hit-testing and caret placement resolve against. Geometry comes from HERE, never from
|
|
2787
|
+
* the DOM, which is what lets an empty paragraph still get a caret.
|
|
2788
|
+
*/
|
|
2789
|
+
interface LineRecord {
|
|
2790
|
+
/** An authored manual line break ends this line; optional display furniture can show ↵. */
|
|
2791
|
+
readonly manualBreakAfter?: true;
|
|
2792
|
+
readonly id: string;
|
|
2793
|
+
readonly range: SourceRange;
|
|
2794
|
+
readonly spans: readonly StyleSpanRecord[];
|
|
2795
|
+
readonly box: LayoutBox;
|
|
2796
|
+
/**
|
|
2797
|
+
* Where the line's content actually starts, after alignment and the first-line indent.
|
|
2798
|
+
*
|
|
2799
|
+
* {@link box} is the content BAND the line was broken against: its `x` is the indented column
|
|
2800
|
+
* edge and its `width` the available measure, and neither moves with `w:jc`. A line with no
|
|
2801
|
+
* spans needs this aligned origin, or paint, hit testing and the caret fall back to `box.x`.
|
|
2802
|
+
*
|
|
2803
|
+
* The leftmost x of any span box or inline drawing `advanceStart`: one origin for every
|
|
2804
|
+
* consumer. On a line that opens with a picture, the text starts at the leftmost span.
|
|
2805
|
+
*/
|
|
2806
|
+
readonly contentX: number;
|
|
2807
|
+
/** Distance from the line box top to the text baseline. */
|
|
2808
|
+
readonly baseline: number;
|
|
2809
|
+
/**
|
|
2810
|
+
* Space ABOVE the glyph band inside {@link box} (exact centering, atLeast growth).
|
|
2811
|
+
*
|
|
2812
|
+
* `auto` extras grow the box BELOW the glyphs — paint puts that depth in
|
|
2813
|
+
* padding-bottom. {@link baseline} is measured from the line top and already includes
|
|
2814
|
+
* this above-band when present.
|
|
2815
|
+
*
|
|
2816
|
+
* Published by layout rather than recovered by paint: three paint sites each deriving it
|
|
2817
|
+
* separately is how they came to disagree about where a line's text sits.
|
|
2818
|
+
*/
|
|
2819
|
+
readonly leading: number;
|
|
2820
|
+
/**
|
|
2821
|
+
* Auto line-spacing depth BELOW the glyph band, inside {@link box}.
|
|
2822
|
+
*
|
|
2823
|
+
* The complement of {@link leading}: exact centres the glyphs and atLeast grows the box
|
|
2824
|
+
* above them, while auto leaves the band at the top and grows the box beneath it.
|
|
2825
|
+
* So the band a consumer needs is `box.height - trailingSpacing - leading`, and
|
|
2826
|
+
* subtracting `leading` alone is right only under exact and atLeast.
|
|
2827
|
+
*
|
|
2828
|
+
* A line WITH spans carries its band in the span heights too. An empty paragraph carries
|
|
2829
|
+
* nothing, which is why the caret, paint's `padding-bottom` and the content-control
|
|
2830
|
+
* boundary all need this published rather than recovered from the box.
|
|
2831
|
+
*
|
|
2832
|
+
* Zero under exact and atLeast. On a line holding drawings, only an auto multiple above
|
|
2833
|
+
* single spacing sets it: the multiple's extra text band below the drawings.
|
|
2834
|
+
* Absent on lines published before this was measured; treat as zero.
|
|
2835
|
+
*/
|
|
2836
|
+
readonly trailingSpacing?: number;
|
|
2837
|
+
/**
|
|
2838
|
+
* Model ranges on this line covering DELETED content, absent when there is none.
|
|
2839
|
+
*
|
|
2840
|
+
* The caret steps over these rather than entering them: text typed inside a deletion exists
|
|
2841
|
+
* in neither the original nor the proposal, and there is no valid tree for the result.
|
|
2842
|
+
*
|
|
2843
|
+
* Recorded even in display modes that lay the deletion out invisibly, because the offsets
|
|
2844
|
+
* exist in the model in every mode and an offset-by-offset walk would otherwise stop at
|
|
2845
|
+
* positions with no glyph.
|
|
2846
|
+
*/
|
|
2847
|
+
readonly deletedRanges?: readonly ModelRange[];
|
|
2848
|
+
/**
|
|
2849
|
+
* Inline drawings on this line, absent when there are none.
|
|
2850
|
+
*
|
|
2851
|
+
* Each occupies one UTF-16 model unit at {@link InlineDrawingRecord.start}. Hidden drawings
|
|
2852
|
+
* are omitted — they remain in the tree and projection but publish no geometry.
|
|
2853
|
+
*/
|
|
2854
|
+
readonly drawings?: readonly InlineDrawingRecord[];
|
|
2855
|
+
/**
|
|
2856
|
+
* Revision attributions of tracked ANCHORED drawings whose anchor sits on this line,
|
|
2857
|
+
* absent when there are none.
|
|
2858
|
+
*
|
|
2859
|
+
* An anchored drawing paints from the page layer and leaves no span or line drawing at its
|
|
2860
|
+
* anchor offset, so this is the only carrier the margin change bar can read for it. Only
|
|
2861
|
+
* attributions the display mode lays out are recorded, matching the published records.
|
|
2862
|
+
*/
|
|
2863
|
+
readonly anchorRevisions?: readonly RevisionAttribution[];
|
|
2864
|
+
/**
|
|
2865
|
+
* Revisions a resolved view answered on this line — content it kept as ordinary text and
|
|
2866
|
+
* content it removed between the line's offsets — absent when there are none, and always
|
|
2867
|
+
* absent in All Markup, where the spans carry the markup themselves.
|
|
2868
|
+
*
|
|
2869
|
+
* What Simple Markup's change bar draws beside the line. Nothing inline reads it: the view
|
|
2870
|
+
* shows the document as accepting every change would leave it, and the bar in the margin is
|
|
2871
|
+
* the one place that still says the line changed.
|
|
2872
|
+
*/
|
|
2873
|
+
readonly changeSites?: readonly RevisionAttribution[];
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* A paragraph's resolved indent in points, in the vocabulary `w:ind` uses.
|
|
2877
|
+
*
|
|
2878
|
+
* `left`/`right` are signed; `hanging` is not (`ST_TwipsMeasure`). `firstLine` is signed
|
|
2879
|
+
* even though the schema declares it unsigned, because Word's model keeps one signed
|
|
2880
|
+
* first-line indent and this engine follows it.
|
|
2881
|
+
*/
|
|
2882
|
+
interface ParagraphIndent {
|
|
2883
|
+
readonly left: number;
|
|
2884
|
+
readonly right: number;
|
|
2885
|
+
readonly firstLine: number;
|
|
2886
|
+
readonly hanging: number;
|
|
2887
|
+
}
|
|
2888
|
+
/**
|
|
2889
|
+
* The part of one paragraph that sits on one page.
|
|
2890
|
+
*
|
|
2891
|
+
* A paragraph that crosses a page boundary produces several fragments that all name the SAME
|
|
2892
|
+
* `paragraphId`, which is what lets selection and hit-testing treat it as one paragraph while
|
|
2893
|
+
* pagination treats it as two boxes.
|
|
2894
|
+
*/
|
|
2895
|
+
interface ParagraphFragmentRecord {
|
|
2896
|
+
readonly kind: 'paragraph';
|
|
2897
|
+
readonly id: string;
|
|
2898
|
+
readonly paragraphId: string;
|
|
2899
|
+
/** 0 for the first fragment of the paragraph, 1 for its continuation, and so on. */
|
|
2900
|
+
readonly fragmentIndex: number;
|
|
2901
|
+
/** This fragment contains the visible paragraph end. */
|
|
2902
|
+
readonly paragraphEnd?: true;
|
|
2903
|
+
/** Resolved paragraph-mark style when this fragment has no text spans, including picture-only fragments. */
|
|
2904
|
+
readonly emptyParagraphStyle?: ResolvedRunStyle;
|
|
2905
|
+
/** Font size of the paragraph mark, on the fragment that ends the paragraph; sizes ¶. */
|
|
2906
|
+
readonly paragraphMarkSizePt?: number;
|
|
2907
|
+
/** A fixed text frame clips its painted ink to this fragment's box; source ranges remain intact. */
|
|
2908
|
+
readonly clipToBox?: true;
|
|
2909
|
+
/**
|
|
2910
|
+
* The fragment retains its source identity without consuming body flow height: a positioned
|
|
2911
|
+
* text frame (with {@link ParagraphFragmentRecord.positionedFrame}), an empty section-mark
|
|
2912
|
+
* paragraph before a continuous section, or the empty line in front of a paragraph's leading
|
|
2913
|
+
* page break at the bottom of a full page. The last two paint nothing.
|
|
2914
|
+
*/
|
|
2915
|
+
readonly outOfFlow?: true;
|
|
2916
|
+
/** Placement and wrapping of one authored text-frame group, in page-content coordinates. */
|
|
2917
|
+
readonly positionedFrame?: {
|
|
2918
|
+
readonly dropCapLines?: number;
|
|
2919
|
+
readonly anchorId: string;
|
|
2920
|
+
readonly columnIndex: number;
|
|
2921
|
+
readonly groupId: string;
|
|
2922
|
+
readonly sourceOrder: number;
|
|
2923
|
+
readonly wrap: 'auto' | 'around' | 'tight' | 'through' | 'none' | 'notBeside';
|
|
2924
|
+
readonly hSpace: number;
|
|
2925
|
+
readonly vSpace: number;
|
|
2926
|
+
readonly box: LayoutBox;
|
|
2927
|
+
};
|
|
2928
|
+
readonly range: SourceRange;
|
|
2929
|
+
readonly props: readonly OoxmlProperty[];
|
|
2930
|
+
/** Resolved paragraph style after the style/default cascade, or null when none applies. */
|
|
2931
|
+
readonly styleId: string | null;
|
|
2932
|
+
/** Resolved Word outline level (0 = Heading 1), or null for body text. */
|
|
2933
|
+
readonly outlineLevel: number | null;
|
|
2934
|
+
/** Resolved `w:jc` alignment used by layout. */
|
|
2935
|
+
readonly alignment: 'left' | 'center' | 'right' | 'both';
|
|
2936
|
+
/**
|
|
2937
|
+
* Before/after spacing applied to THIS fragment, in points.
|
|
2938
|
+
*
|
|
2939
|
+
* Continuations carry `before: 0`; only the final fragment carries `after`. The numbers
|
|
2940
|
+
* already reflect Word's adjacent-collapse against the previous paragraph's after.
|
|
2941
|
+
*/
|
|
2942
|
+
readonly spacing: ParagraphSpacing;
|
|
2943
|
+
/**
|
|
2944
|
+
* The paragraph's EFFECTIVE indent in points, cascade and numbering merge included.
|
|
2945
|
+
*
|
|
2946
|
+
* Published because it is not recoverable from anything else here. `props` carries the
|
|
2947
|
+
* cascaded `w:ind`, but a list paragraph's indent comes from `numbering.xml` and is merged
|
|
2948
|
+
* in after the cascade, so a numbered item that authors no `w:ind` reads zero there while
|
|
2949
|
+
* its text sits indented. The geometry is no better an answer: `box.x` is cell-relative
|
|
2950
|
+
* inside a table and is displaced by float zones.
|
|
2951
|
+
*
|
|
2952
|
+
* `firstLine` and `hanging` are kept as OOXML spells them. A consumer wanting the one
|
|
2953
|
+
* signed first-line offset Word models takes `hanging > 0 ? -hanging : firstLine` —
|
|
2954
|
+
* hanging WINS, it is not summed (ECMA-376 §17.3.1.12).
|
|
2955
|
+
*/
|
|
2956
|
+
readonly indent: ParagraphIndent;
|
|
2957
|
+
/** Bottom rule on the final fragment when `w:pBdr/w:bottom` resolves; absent otherwise. */
|
|
2958
|
+
readonly bottomBorder?: ParagraphBottomBorderRecord;
|
|
2959
|
+
/**
|
|
2960
|
+
* Every `w:pBdr` rule this fragment draws, in paint order.
|
|
2961
|
+
*
|
|
2962
|
+
* A paragraph split across pages opens and closes exactly once: the `top` stroke rides the
|
|
2963
|
+
* first fragment, the closing stroke the last. `bottomBorder` remains the bottom rule alone
|
|
2964
|
+
* — a `between` rule closing a grouped paragraph is not one.
|
|
2965
|
+
*/
|
|
2966
|
+
readonly borders?: readonly ParagraphBorderStrokeRecord[];
|
|
2967
|
+
/**
|
|
2968
|
+
* Validated 6-hex paragraph shading fill (`w:pPr/w:shd`), absent for none/auto.
|
|
2969
|
+
*
|
|
2970
|
+
* Paint fills {@link shadingBox}. Measurement ignores it.
|
|
2971
|
+
*/
|
|
2972
|
+
readonly shading?: string;
|
|
2973
|
+
/**
|
|
2974
|
+
* Geometry of the paragraph shading band when {@link shading} is set. Absent when there
|
|
2975
|
+
* is no fill.
|
|
2976
|
+
*
|
|
2977
|
+
* TWO SHAPES, because Word fills two different things. An UNBORDERED paragraph is filled
|
|
2978
|
+
* across its line boxes (indent-aware width), and before/after spacing stays unfilled. A
|
|
2979
|
+
* paragraph that publishes any {@link borders} stroke is filled across the rectangle those
|
|
2980
|
+
* strokes draw instead — `w:space` padding and rule extent included — so a callout's fill
|
|
2981
|
+
* reaches its frame rather than leaving a pale stripe floating inside an empty box. Paint
|
|
2982
|
+
* draws the strokes after the fill, so the frame is never covered.
|
|
2983
|
+
*/
|
|
2984
|
+
readonly shadingBox?: LayoutBox;
|
|
2985
|
+
/**
|
|
2986
|
+
* The revisions on this paragraph's own MARK (`w:pPr/w:rPr/w:ins|w:del`), absent when there
|
|
2987
|
+
* are none.
|
|
2988
|
+
*
|
|
2989
|
+
* Carried on the fragment rather than on a span because they decorate no characters: the
|
|
2990
|
+
* pilcrow was inserted or deleted, which is how a split or a merge is recorded. Only the
|
|
2991
|
+
* FINAL fragment carries them — the mark lives at the end of the paragraph, so a paragraph
|
|
2992
|
+
* split across pages must not draw two of them.
|
|
2993
|
+
*
|
|
2994
|
+
* A LIST because one mark can hold two decisions at once — an insertion by one author and
|
|
2995
|
+
* a deletion of that insertion by the next. Published only in `all-markup`: the other two
|
|
2996
|
+
* display modes answer what the document WOULD be, and a resolved view draws no
|
|
2997
|
+
* attribution.
|
|
2998
|
+
*
|
|
2999
|
+
* Those two modes are ATTRIBUTION-resolved, not STRUCTURE-resolved. Taking a deleted mark
|
|
3000
|
+
* in `proposed` merges the paragraph into the next one, and taking an inserted mark in
|
|
3001
|
+
* `original` un-splits it, but `revision-visibility.ts` does that only for a paragraph that
|
|
3002
|
+
* renders no text. So a resolved view still shows the break — it just no longer draws a
|
|
3003
|
+
* coloured pilcrow beside it. The merge is the fix for that; suppressing the glyph is not,
|
|
3004
|
+
* and must not be read as it.
|
|
3005
|
+
*/
|
|
3006
|
+
readonly markRevisions?: readonly RevisionAttribution[];
|
|
3007
|
+
/**
|
|
3008
|
+
* The one decision a single-field reader sees, absent when there are none.
|
|
3009
|
+
*
|
|
3010
|
+
* Derived from {@link markRevisions} at publish time, never authored beside it, so the two
|
|
3011
|
+
* cannot disagree: a deletion when the mark carries one, because that is where the pair
|
|
3012
|
+
* lands once every decision is taken, and it is the face paint draws for the same reason.
|
|
3013
|
+
*
|
|
3014
|
+
* @deprecated Shows one of the decisions a mark can carry. Read {@link markRevisions}.
|
|
3015
|
+
*/
|
|
3016
|
+
readonly markRevision?: RevisionAttribution;
|
|
3017
|
+
/**
|
|
3018
|
+
* The tracked FORMAT change on this paragraph's mark (`w:pPr/w:rPr/w:rPrChange`), absent
|
|
3019
|
+
* when there is none. Final fragment only, and `all-markup` only, like the decisions above.
|
|
3020
|
+
*
|
|
3021
|
+
* Published without a glyph of its own on purpose. Word draws no pilcrow for a mark whose
|
|
3022
|
+
* only change is its own formatting — the decision belongs to the review pane, which lists
|
|
3023
|
+
* it either way. What was missing was GEOMETRY: with nothing on the fragment, a card had
|
|
3024
|
+
* no place on the page to point at.
|
|
3025
|
+
*/
|
|
3026
|
+
readonly markFormatRevision?: RevisionAttribution;
|
|
3027
|
+
/**
|
|
3028
|
+
* The decisions a resolved view answered on this paragraph's MARK: an inserted break it
|
|
3029
|
+
* kept, a paragraph-property change or a mark-property change it accepted. Final fragment
|
|
3030
|
+
* only, resolved views only; absent when there are none. A removed break merges its
|
|
3031
|
+
* paragraph away and reports on the join line instead (see {@link LineRecord.changeSites}).
|
|
3032
|
+
*/
|
|
3033
|
+
readonly markChangeSites?: readonly RevisionAttribution[];
|
|
3034
|
+
/**
|
|
3035
|
+
* List marker painted in the hanging-indent slot of the FIRST fragment only.
|
|
3036
|
+
*
|
|
3037
|
+
* Not part of model text: no UTF-16 range, never contributes to caret/selection offsets,
|
|
3038
|
+
* and must not be serialised back into the paragraph.
|
|
3039
|
+
*/
|
|
3040
|
+
readonly marker?: ListMarkerRecord;
|
|
3041
|
+
/**
|
|
3042
|
+
* The paragraph's resolved tab stops — cascade included — and the default interval past
|
|
3043
|
+
* the last of them.
|
|
3044
|
+
*
|
|
3045
|
+
* Published because it is not recoverable from `props`: `w:tabs` carries its meaning in
|
|
3046
|
+
* `w:tab` CHILDREN and the flat property projection has none, so a control reading the
|
|
3047
|
+
* cascade sees the element and not a single stop. A tab-stop editor that showed only what
|
|
3048
|
+
* the paragraph authors would hide a style's stops and then leave them in force.
|
|
3049
|
+
*/
|
|
3050
|
+
readonly tabStops: ResolvedTabStops;
|
|
3051
|
+
readonly lines: readonly LineRecord[];
|
|
3052
|
+
readonly box: LayoutBox;
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* A numbering marker as layout published it.
|
|
3056
|
+
*
|
|
3057
|
+
* Geometry is in the same coordinate space as the fragment (page-content or cell-content
|
|
3058
|
+
* relative). Paint positions from this box and MUST NOT remeasure the marker.
|
|
3059
|
+
*/
|
|
3060
|
+
interface ListMarkerRecord {
|
|
3061
|
+
readonly text: string;
|
|
3062
|
+
readonly style: ResolvedRunStyle;
|
|
3063
|
+
readonly box: LayoutBox;
|
|
3064
|
+
/**
|
|
3065
|
+
* The `w:ilvl` this marker was resolved at, 0..8.
|
|
3066
|
+
*
|
|
3067
|
+
* Published because the level, not the geometry, is what Increase/Decrease Indent moves
|
|
3068
|
+
* on a list paragraph — demoting an item re-resolves its format from `numbering.xml`,
|
|
3069
|
+
* which is why a bullet becomes a hollow circle and a `1.` becomes an `a.`.
|
|
3070
|
+
*/
|
|
3071
|
+
readonly level: number;
|
|
3072
|
+
/**
|
|
3073
|
+
* The `w:numId` this marker resolved through. With the level it identifies a list: whether
|
|
3074
|
+
* a demote is possible depends on which levels THIS definition declares.
|
|
3075
|
+
*/
|
|
3076
|
+
readonly numId: string;
|
|
3077
|
+
/** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
|
|
3078
|
+
readonly numFmt: string;
|
|
3079
|
+
/** Resolved counter at this marker's own level; absent for bullets. */
|
|
3080
|
+
readonly ordinal?: number;
|
|
3081
|
+
/**
|
|
3082
|
+
* The IMAGE this marker draws instead of {@link ListMarkerRecord.text}, when the resolved
|
|
3083
|
+
* level carries `w:lvlPicBulletId` (§17.9.12) and the referenced `w:numPicBullet` resolved.
|
|
3084
|
+
*
|
|
3085
|
+
* Absent on every ordinary marker, and absent when the picture bullet is missing, external,
|
|
3086
|
+
* malformed or oversized — in which case `text` stands and the marker paints as the level's
|
|
3087
|
+
* `w:lvlText` did before. A sink that cannot draw the resource paints `text` too, which is
|
|
3088
|
+
* why both are published rather than one replacing the other.
|
|
3089
|
+
*/
|
|
3090
|
+
readonly picture?: ListMarkerPictureRecord;
|
|
3091
|
+
/**
|
|
3092
|
+
* In a right-to-left paragraph: `text` in VISUAL order, one piece per direction run, each
|
|
3093
|
+
* styled at its bidi level. Paint draws these instead of `text`, which stays logical.
|
|
3094
|
+
*/
|
|
3095
|
+
readonly pieces?: readonly Pick<ListMarkerRecord, 'text' | 'style' | 'box'>[];
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* The picture a `w:lvlPicBulletId` marker paints, with its geometry already decided.
|
|
3099
|
+
*
|
|
3100
|
+
* Geometry is in the same coordinate space as {@link ListMarkerRecord.box}: paint positions
|
|
3101
|
+
* from `box` and MUST NOT rescale it, neither from the resource's intrinsic pixels nor from
|
|
3102
|
+
* the authored `v:shape` extent, which the marker font size has already scaled. The image
|
|
3103
|
+
* sits with its BOTTOM on the first line's baseline, which is what makes the line tall
|
|
3104
|
+
* enough for it.
|
|
3105
|
+
*
|
|
3106
|
+
* `resource` is the same validated-image projection an inline drawing carries, resolved
|
|
3107
|
+
* through the numbering part's OWN relationships. A `ready` resource is the only one that
|
|
3108
|
+
* can be drawn; every other state is a clean fall back to the marker text.
|
|
3109
|
+
* @public
|
|
3110
|
+
*/
|
|
3111
|
+
interface ListMarkerPictureRecord {
|
|
3112
|
+
/** The part whose relationships `relationshipId` belongs to — the numbering part. */
|
|
3113
|
+
readonly ownerPartName: string;
|
|
3114
|
+
/** `v:imagedata/@r:id` of the `w:numPicBullet` shape. */
|
|
3115
|
+
readonly relationshipId: string;
|
|
3116
|
+
/** Where the image paints, authored extent, in the marker's coordinate space. */
|
|
3117
|
+
readonly box: LayoutBox;
|
|
3118
|
+
/** Validated image state, or why there is none. */
|
|
3119
|
+
readonly resource: ImageResourceState;
|
|
3120
|
+
}
|
|
3121
|
+
/**
|
|
3122
|
+
* The part of one table that sits on one page.
|
|
3123
|
+
*
|
|
3124
|
+
* A table that crosses a page boundary produces one fragment per page it spans, which is
|
|
3125
|
+
* what lets it checkpoint like a paragraph: the flow loop places whole fragments, and
|
|
3126
|
+
* resuming after a table needs no knowledge of its interior.
|
|
3127
|
+
*/
|
|
3128
|
+
interface TableFragmentRecord {
|
|
3129
|
+
readonly kind: 'table';
|
|
3130
|
+
/** Source and positioning inputs needed to reconstruct wrapping on an incremental pass. */
|
|
3131
|
+
readonly floatingWrap?: {
|
|
3132
|
+
readonly anchorId: string;
|
|
3133
|
+
readonly columnIndex: number;
|
|
3134
|
+
readonly float: TableFloatPosition;
|
|
3135
|
+
readonly sourceOrder: number;
|
|
3136
|
+
};
|
|
3137
|
+
readonly id: string;
|
|
3138
|
+
/** Canonical node id of the `w:tbl`. */
|
|
3139
|
+
readonly tableId: string;
|
|
3140
|
+
/** 0 for the first page the table touches, 1 for its continuation, and so on. */
|
|
3141
|
+
readonly fragmentIndex: number;
|
|
3142
|
+
/** Nesting depth: 0 for body-level tables, increasing for nested tables. */
|
|
3143
|
+
readonly nestingDepth: number;
|
|
3144
|
+
/**
|
|
3145
|
+
* Resolved column boundary x positions in table-local points, left edge through right edge.
|
|
3146
|
+
* Length is column count + 1.
|
|
3147
|
+
*/
|
|
3148
|
+
readonly columnEdges: readonly number[];
|
|
3149
|
+
readonly rows: readonly TableRowFragmentRecord[];
|
|
3150
|
+
readonly box: LayoutBox;
|
|
3151
|
+
}
|
|
3152
|
+
/**
|
|
3153
|
+
* One table row on one page.
|
|
3154
|
+
*
|
|
3155
|
+
* A FRAGMENT, not the row: a row split across a page break appears once per page it touches, and
|
|
3156
|
+
* a repeated header row appears on every page of its table.
|
|
3157
|
+
*/
|
|
3158
|
+
interface TableRowFragmentRecord {
|
|
3159
|
+
/** Canonical node id of the `w:tr`. */
|
|
3160
|
+
readonly id: string;
|
|
3161
|
+
/** Pending tracked row insertion/deletion, when authored in `w:trPr`. */
|
|
3162
|
+
readonly revisionKind?: 'insert' | 'delete';
|
|
3163
|
+
/**
|
|
3164
|
+
* The `w:trPr/w:ins|w:del` attribution — the `(id, author, date)` triple the review model
|
|
3165
|
+
* addresses this decision by, so painted rows can carry it the way revision spans do.
|
|
3166
|
+
*/
|
|
3167
|
+
readonly revisionId?: string;
|
|
3168
|
+
readonly revisionAuthor?: string;
|
|
3169
|
+
readonly revisionDate?: string;
|
|
3170
|
+
/**
|
|
3171
|
+
* The row insertion a resolved view kept this row through, absent in All Markup (where
|
|
3172
|
+
* {@link revisionKind} carries it) and for an untracked row. A removed row has no record.
|
|
3173
|
+
*/
|
|
3174
|
+
readonly changeSites?: readonly RevisionAttribution[];
|
|
3175
|
+
/** Authored row ordinal within the table; repeats share the original row's index. */
|
|
3176
|
+
readonly rowIndex: number;
|
|
3177
|
+
/** True when the authored row resolves `w:tblHeader`, including its first occurrence. */
|
|
3178
|
+
readonly isHeaderRow: boolean;
|
|
3179
|
+
/** True for a `w:tblHeader` row repeated atop a continuation page; not an interaction stop. */
|
|
3180
|
+
readonly isHeaderRepeat: boolean;
|
|
3181
|
+
/** True when this record continues a row split at a line boundary; same `id` as its head. */
|
|
3182
|
+
readonly isContinuation?: boolean;
|
|
3183
|
+
/** True when the rest of this row continues in the next fragment: the head of a split row. */
|
|
3184
|
+
readonly hasContinuation?: boolean;
|
|
3185
|
+
/** True for a `w:cantSplit` or exact-height row: it stays whole where a page holds it. */
|
|
3186
|
+
readonly placesWhole?: boolean;
|
|
3187
|
+
readonly cells: readonly TableCellFragmentRecord[];
|
|
3188
|
+
readonly box: LayoutBox;
|
|
3189
|
+
}
|
|
3190
|
+
/**
|
|
3191
|
+
* One table cell on one page, already resolved against the grid.
|
|
3192
|
+
*
|
|
3193
|
+
* `gridSpan` is clamped at read time, because it comes from a file and an unclamped span is a
|
|
3194
|
+
* loop bound an attacker controls. A vertical-merge continuation paints its box but holds no
|
|
3195
|
+
* blocks — its content belongs to the cell that started the merge.
|
|
3196
|
+
*/
|
|
3197
|
+
interface TableCellFragmentRecord {
|
|
3198
|
+
readonly revisionShading?: 'inserted' | 'deleted' | 'merged' | 'split';
|
|
3199
|
+
/** Author of the visible cell revision, or its enclosing row revision. */
|
|
3200
|
+
readonly revisionShadingAuthor?: string;
|
|
3201
|
+
/** Canonical node id of the `w:tc`. */
|
|
3202
|
+
readonly id: string;
|
|
3203
|
+
/** First grid column this cell occupies. */
|
|
3204
|
+
readonly gridColumn: number;
|
|
3205
|
+
/** Stored grid index for an RTL cell; gridColumn indexes physical columnEdges. */
|
|
3206
|
+
readonly logicalGridColumn?: number;
|
|
3207
|
+
/** Canonical `w:gridCol` node id for this cell's start column, when authored. */
|
|
3208
|
+
readonly gridColumnId?: string;
|
|
3209
|
+
/** Grid columns spanned, already clamped at read time. */
|
|
3210
|
+
readonly gridSpan: number;
|
|
3211
|
+
/** A vertical-merge continuation paints its box but holds no blocks. */
|
|
3212
|
+
readonly vMergeContinue: boolean;
|
|
3213
|
+
/**
|
|
3214
|
+
* When true, paint skips borders/fill/content for this cell (vMerge continue). Grid
|
|
3215
|
+
* bookkeeping and the box remain so selection/geometry walks stay consistent.
|
|
3216
|
+
*/
|
|
3217
|
+
readonly paintInert?: boolean;
|
|
3218
|
+
/** Number of rows this restart cell visually spans (1 when not a vertical merge). */
|
|
3219
|
+
readonly rowSpan?: number;
|
|
3220
|
+
/** Validated 6-hex cell shading fill, absent for none/auto. */
|
|
3221
|
+
readonly shading?: string;
|
|
3222
|
+
/** Bottom-to-top cell content uses a rotated local inline axis. */
|
|
3223
|
+
readonly textDirection?: 'btLr';
|
|
3224
|
+
/**
|
|
3225
|
+
* Layout-owned resolved borders after collapsed conflict resolution.
|
|
3226
|
+
*
|
|
3227
|
+
* Includes convenience per-side edges, per-grid-interval winners, and explicit compound
|
|
3228
|
+
* stroke rectangles in cell-local points. Paint only scales and draws — it must not
|
|
3229
|
+
* invent stroke/gap/corner geometry.
|
|
3230
|
+
*/
|
|
3231
|
+
readonly borders?: ResolvedCellBorders;
|
|
3232
|
+
/** Nested blocks in reading order; recursion carries nested tables. */
|
|
3233
|
+
readonly blocks: readonly BlockFragmentRecord[];
|
|
3234
|
+
readonly box: LayoutBox;
|
|
3235
|
+
}
|
|
3236
|
+
/** A top-level (or cell-level) block fragment, discriminated by `kind`. */
|
|
3237
|
+
type BlockFragmentRecord = ParagraphFragmentRecord | TableFragmentRecord;
|
|
3238
|
+
/**
|
|
3239
|
+
* One header or footer story as it sits on one page.
|
|
3240
|
+
*
|
|
3241
|
+
* `box` is absolute (sheet coordinates) and sized to the story's FLOW height — never to
|
|
3242
|
+
* any anchored-object extent, which is the rule that keeps a decorated header's hit area
|
|
3243
|
+
* from covering the body. `fragments` are story-relative (origin at the box's top-left).
|
|
3244
|
+
* Baseline furniture may be shared across pages of the same variant when the story has no
|
|
3245
|
+
* allowlisted PAGE/NUMPAGES/SECTIONPAGES fields; after page-field finalize, projections are
|
|
3246
|
+
* per page (or per distinct field values for count-only stories).
|
|
3247
|
+
*/
|
|
3248
|
+
interface HeaderFooterStoryRecord {
|
|
3249
|
+
readonly kind: 'header' | 'footer';
|
|
3250
|
+
readonly variant: 'default' | 'first' | 'even';
|
|
3251
|
+
readonly partName: string;
|
|
3252
|
+
/**
|
|
3253
|
+
* The part this story was laid out from. See {@link HeaderFooterStoryLayout.part}.
|
|
3254
|
+
*
|
|
3255
|
+
* Present when the furniture source supplied it. A pass that needs the story's TREE — the
|
|
3256
|
+
* content-control boundary derivation is the one — reads it here rather than resolving a
|
|
3257
|
+
* name against a package it does not have.
|
|
3258
|
+
*/
|
|
3259
|
+
readonly part?: OoxmlPart;
|
|
3260
|
+
/**
|
|
3261
|
+
* Main-document relationship id that resolves to this part (`EditorScope.rId`).
|
|
3262
|
+
*
|
|
3263
|
+
* Present when the furniture source could name the relationship; scoped editing binds
|
|
3264
|
+
* on this id so shared parts stay one story across pages/sections.
|
|
3265
|
+
*/
|
|
3266
|
+
readonly rId?: string;
|
|
3267
|
+
readonly box: LayoutBox;
|
|
3268
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
3269
|
+
/** Anchored drawings owned by this story, in story-relative coordinates. */
|
|
3270
|
+
readonly anchoredDrawings?: readonly AnchoredDrawingRecord[];
|
|
3271
|
+
/**
|
|
3272
|
+
* Transient projector used between furniture attach and document-level page-field
|
|
3273
|
+
* finalize. Absent on published layout records after finalize — but not dead: finalize
|
|
3274
|
+
* retains it (with the context it last ran under) in a side table keyed on the published
|
|
3275
|
+
* record (`strippedStoryProjections` in `field-page-furniture.ts`), so a reused sheet can
|
|
3276
|
+
* re-project when the page count moves (#441). The retained closure keeps its minting
|
|
3277
|
+
* pass's scope alive for as long as the sheet is reused, and anything that clones a
|
|
3278
|
+
* published field-bearing story must carry the entry onto the clone
|
|
3279
|
+
* (`carryStrippedPageFieldProjection`).
|
|
3280
|
+
*/
|
|
3281
|
+
readonly pageFieldProjector?: (context: {
|
|
3282
|
+
readonly pageNumber: number;
|
|
3283
|
+
readonly pageCount: number;
|
|
3284
|
+
readonly sectionPageCount?: number;
|
|
3285
|
+
readonly format?: string;
|
|
3286
|
+
}) => HeaderFooterStoryRecord;
|
|
3287
|
+
}
|
|
3288
|
+
/**
|
|
3289
|
+
* One footnote or endnote story as it sits on one page (or continuation page).
|
|
3290
|
+
*
|
|
3291
|
+
* Notes are ordinary editable stories — NOT `[data-docx-hf]` furniture. `box` is absolute
|
|
3292
|
+
* (sheet coordinates). `fragments` are story-relative. Separators are nonselectable paint
|
|
3293
|
+
* geometry owned by the parent {@link NoteAreaRecord}.
|
|
3294
|
+
*/
|
|
3295
|
+
interface NoteStoryRecord {
|
|
3296
|
+
readonly noteKind: 'footnote' | 'endnote';
|
|
3297
|
+
readonly noteId: number;
|
|
3298
|
+
/** `footnote:N` / `endnote:N` — EditorScope note id. */
|
|
3299
|
+
readonly scopeId: string;
|
|
3300
|
+
/** Derived display mark for this occurrence; null when customMarkFollows / continuation. */
|
|
3301
|
+
readonly mark: string | null;
|
|
3302
|
+
/** True when this is a continuation fragment (no leading mark). */
|
|
3303
|
+
readonly continuation?: boolean;
|
|
3304
|
+
readonly box: LayoutBox;
|
|
3305
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* Footnote / endnote area on one page: separator + stacked note stories.
|
|
3309
|
+
*
|
|
3310
|
+
* `placement` records how the area was positioned. `fallbackReason` is set when the
|
|
3311
|
+
* bounded reflow loop exhausted and layout kept the reference with its note on a later
|
|
3312
|
+
* page (D12 named fallback).
|
|
3313
|
+
*/
|
|
3314
|
+
interface NoteAreaRecord {
|
|
3315
|
+
readonly kind: 'footnotes' | 'endnotes';
|
|
3316
|
+
readonly placement: 'pageBottom' | 'beneathText' | 'sectEnd' | 'docEnd';
|
|
3317
|
+
readonly box: LayoutBox;
|
|
3318
|
+
/** Separator rule / authored separator story; absent when no notes on this page. */
|
|
3319
|
+
readonly separator?: {
|
|
3320
|
+
readonly kind: 'separator' | 'continuationSeparator' | 'continuationNotice';
|
|
3321
|
+
readonly box: LayoutBox;
|
|
3322
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
3323
|
+
readonly synthetic: boolean;
|
|
3324
|
+
/** Layout-owned single/double rule when marker-only or synthetic; absent for authored stories. */
|
|
3325
|
+
readonly ruleStyle?: 'single' | 'double';
|
|
3326
|
+
/** Resolved marker-run color; null/absent uses automatic black. */
|
|
3327
|
+
readonly ruleColor?: string | null;
|
|
3328
|
+
};
|
|
3329
|
+
/** Authored furniture below a note that continues on the next page. */
|
|
3330
|
+
readonly continuationNotice?: NonNullable<NoteAreaRecord['separator']>;
|
|
3331
|
+
readonly notes: readonly NoteStoryRecord[];
|
|
3332
|
+
readonly fallbackReason?: string;
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Note-stream ownership for overflow sheets created by note pagination.
|
|
3336
|
+
*
|
|
3337
|
+
* - `footnote-drain`: continuation pages that exist only to finish pageBottom footnotes —
|
|
3338
|
+
* not free body hosts for sectEnd/docEnd endnotes.
|
|
3339
|
+
* - `endnote-overflow`: sheets inserted to finish sectEnd/docEnd collections.
|
|
3340
|
+
*/
|
|
3341
|
+
type PageNoteStream = 'footnote-drain' | 'endnote-overflow';
|
|
3342
|
+
/**
|
|
3343
|
+
* Raw `w:lock/@w:val` on one control, or `unlocked` when absent / unrecognised.
|
|
3344
|
+
*
|
|
3345
|
+
* Effective permissions across a nesting chain are the union of every ancestor's lock on two
|
|
3346
|
+
* axes (content edit / removal); see {@link ContentControlBoundaryRecord.effectiveLock}.
|
|
3347
|
+
*/
|
|
3348
|
+
type ContentControlLock = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked';
|
|
3349
|
+
/**
|
|
3350
|
+
* Mapped control type for layout / chrome — same members as the shipped public
|
|
3351
|
+
* `ContentControlType`. Untyped and preserved-only kinds report as `richText`.
|
|
3352
|
+
*/
|
|
3353
|
+
type ContentControlMappedType = 'richText' | 'plainText' | 'checkbox' | 'dropdown' | 'comboBox' | 'date' | 'picture' | 'buildingBlockGallery' | 'repeatingSection';
|
|
3354
|
+
/** Where the control sits in the tree relative to its content. */
|
|
3355
|
+
type ContentControlLevel = 'block' | 'inline' | 'row' | 'cell';
|
|
3356
|
+
/**
|
|
3357
|
+
* One piece of a control's content geometry.
|
|
3358
|
+
*
|
|
3359
|
+
* A block control that crosses a page break publishes one fragment per page rather than a
|
|
3360
|
+
* single rectangle covering the inter-page gap. An inline control publishes one fragment per
|
|
3361
|
+
* LINE it touches, covering the text's vertical extent (line-spacing leading excluded), so a
|
|
3362
|
+
* wrapped control never claims the words beside it. Coordinates match fragment boxes
|
|
3363
|
+
* (page-content space).
|
|
3364
|
+
*/
|
|
3365
|
+
interface ContentControlGeometryFragment {
|
|
3366
|
+
readonly pageIndex: number;
|
|
3367
|
+
readonly box: LayoutBox;
|
|
3368
|
+
}
|
|
3369
|
+
/**
|
|
3370
|
+
* Layout-published boundary for one content control (`w:sdt` / typed `contentControl`).
|
|
3371
|
+
*
|
|
3372
|
+
* Chrome, lock feedback, and hit resolution read this record — never painted DOM. The wrapper
|
|
3373
|
+
* itself is not a layout box; `fragments` cover the content that already flowed in place.
|
|
3374
|
+
*/
|
|
3375
|
+
interface ContentControlBoundaryRecord {
|
|
3376
|
+
/** Canonical node id of the control wrapper — not `w:id`. */
|
|
3377
|
+
readonly id: string;
|
|
3378
|
+
readonly alias?: string;
|
|
3379
|
+
readonly tag?: string;
|
|
3380
|
+
readonly controlType: ContentControlMappedType;
|
|
3381
|
+
/** This control's own `w:lock`, before ancestor union. */
|
|
3382
|
+
readonly lock: ContentControlLock;
|
|
3383
|
+
/**
|
|
3384
|
+
* Nested lock union with every ancestor control, collapsed back to a single `ST_Lock`
|
|
3385
|
+
* vocabulary value (both axes locked → `sdtContentLocked`).
|
|
3386
|
+
*/
|
|
3387
|
+
readonly effectiveLock: ContentControlLock;
|
|
3388
|
+
/** `w:showingPlcHdr` is present on the control's properties. */
|
|
3389
|
+
readonly placeholder: boolean;
|
|
3390
|
+
/** `w:dataBinding` is present — content edits are refused as bound. */
|
|
3391
|
+
readonly bound: boolean;
|
|
3392
|
+
/** 0 for a top-level control; increments through nested wrappers under the shared nesting bound. */
|
|
3393
|
+
readonly nestingDepth: number;
|
|
3394
|
+
readonly level: ContentControlLevel;
|
|
3395
|
+
readonly fragments: readonly ContentControlGeometryFragment[];
|
|
3396
|
+
}
|
|
3397
|
+
/**
|
|
3398
|
+
* One laid-out page: the sheet, the content area, and everything that landed on it.
|
|
3399
|
+
*
|
|
3400
|
+
* Page identity is REUSED across incremental passes — a pass that changes nothing returns the
|
|
3401
|
+
* previous records by reference, which is what lets paint skip untouched pages entirely.
|
|
3402
|
+
*/
|
|
3403
|
+
interface PageRecord {
|
|
3404
|
+
readonly id: string;
|
|
3405
|
+
readonly index: number;
|
|
3406
|
+
/** The whole sheet. */
|
|
3407
|
+
readonly box: LayoutBox;
|
|
3408
|
+
/** The area inside the margins that content flows into. */
|
|
3409
|
+
readonly contentBox: LayoutBox;
|
|
3410
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
3411
|
+
/** Layout-owned vertical rules requested by `w:cols/@w:sep`, content-box relative. */
|
|
3412
|
+
readonly columnSeparators?: readonly LayoutBox[];
|
|
3413
|
+
/** Layout-owned `w:pgBorders` frame for this sheet, page-box relative. Absent when none. */
|
|
3414
|
+
readonly pageBorders?: PageBorderFrameRecord;
|
|
3415
|
+
/** Page-content anchored drawings on this sheet, absent when there are none. */
|
|
3416
|
+
readonly anchoredDrawings?: readonly AnchoredDrawingRecord[];
|
|
3417
|
+
/** Page furniture for this page's variant, absent when the document declares none. */
|
|
3418
|
+
readonly header?: HeaderFooterStoryRecord;
|
|
3419
|
+
readonly footer?: HeaderFooterStoryRecord;
|
|
3420
|
+
/** Footnotes reserved on this page (pageBottom / beneathText / continuations). */
|
|
3421
|
+
readonly footnotes?: NoteAreaRecord;
|
|
3422
|
+
/** Endnotes collected on this page (sectEnd / docEnd). */
|
|
3423
|
+
readonly endnotes?: NoteAreaRecord;
|
|
3424
|
+
/**
|
|
3425
|
+
* Ownership of note-only overflow sheets. Absent on ordinary body pages.
|
|
3426
|
+
* Layout pagination sets this so endnote hosting does not treat footnote drain
|
|
3427
|
+
* pages as free body space.
|
|
3428
|
+
*/
|
|
3429
|
+
readonly noteStream?: PageNoteStream;
|
|
3430
|
+
/** An empty sheet that gives the next section its page parity; SECTIONPAGES skips it. */
|
|
3431
|
+
readonly parityBlank?: true;
|
|
3432
|
+
/**
|
|
3433
|
+
* Section-local PAGE/SECTIONPAGES inputs for finalize. Absent → physical page index and
|
|
3434
|
+
* document-wide section count (empty `w:pgNumType` behaviour).
|
|
3435
|
+
*/
|
|
3436
|
+
readonly pageFieldSource?: {
|
|
3437
|
+
readonly pageNumber: number;
|
|
3438
|
+
readonly sectionPageCount: number;
|
|
3439
|
+
readonly format?: string;
|
|
3440
|
+
};
|
|
3441
|
+
/**
|
|
3442
|
+
* `true` when this page's body flow (or a body table) carries a PAGE/NUMPAGES/SECTIONPAGES
|
|
3443
|
+
* placeholder that document finalize must substitute. Set when the page is assembled, so it
|
|
3444
|
+
* rides the record through incremental reuse. `false` lets `finalizePageFieldProjection` skip
|
|
3445
|
+
* the substitution walk; `undefined` (a page built by a path that does not stamp it) still
|
|
3446
|
+
* walks, which is safe.
|
|
3447
|
+
*/
|
|
3448
|
+
readonly hasBodyPageFields?: boolean;
|
|
3449
|
+
/**
|
|
3450
|
+
* Content-control boundaries whose geometry intersects this page.
|
|
3451
|
+
*
|
|
3452
|
+
* Carried on the page so a consumer that only holds a page record still sees current
|
|
3453
|
+
* metadata; identity reuse of an unchanged page keeps the same array only when the
|
|
3454
|
+
* control-context token matches.
|
|
3455
|
+
*/
|
|
3456
|
+
readonly contentControls?: readonly ContentControlBoundaryRecord[];
|
|
3457
|
+
}
|
|
3458
|
+
/**
|
|
3459
|
+
* A complete layout pass: every page, plus the document-wide indexes derived alongside them.
|
|
3460
|
+
*
|
|
3461
|
+
* Stamped with the store `revision` it was laid out from, so anything holding geometry can tell
|
|
3462
|
+
* whether the document has moved underneath it — which is how stale pointer gestures and
|
|
3463
|
+
* overlays are refused rather than applied to coordinates that no longer describe anything.
|
|
3464
|
+
*/
|
|
3465
|
+
interface SemanticLayout {
|
|
3466
|
+
/** The store revision these records were laid out from. */
|
|
3467
|
+
readonly revision: number;
|
|
3468
|
+
/** Revision projection already applied to every published record. */
|
|
3469
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
3470
|
+
readonly pages: readonly PageRecord[];
|
|
3471
|
+
/**
|
|
3472
|
+
* Normalized comments and tracked changes from the same package revision as these pages.
|
|
3473
|
+
* Exporters consume this plain-data stream instead of re-reading OOXML or review state.
|
|
3474
|
+
*/
|
|
3475
|
+
readonly reviewArtifacts?: readonly SemanticReviewArtifactRecord[];
|
|
3476
|
+
/**
|
|
3477
|
+
* Every content-control boundary in document order, including multi-page fragment lists.
|
|
3478
|
+
*
|
|
3479
|
+
* Always recomputed when a document layout pass finishes, so incremental page-identity
|
|
3480
|
+
* reuse cannot publish stale alias/tag/lock metadata.
|
|
3481
|
+
*/
|
|
3482
|
+
readonly contentControls?: readonly ContentControlBoundaryRecord[];
|
|
3483
|
+
/**
|
|
3484
|
+
* Fingerprint of wrapper-only control metadata (alias, tag, lock, type, placeholder, binding).
|
|
3485
|
+
*
|
|
3486
|
+
* Folded into the layout producer / session context so a metadata-only edit invalidates
|
|
3487
|
+
* reuse paths that would otherwise return previous pages by identity with stale boundaries.
|
|
3488
|
+
*/
|
|
3489
|
+
readonly controlContextToken?: string;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
/**
|
|
3493
|
+
* Text measurement, injected.
|
|
3494
|
+
*
|
|
3495
|
+
* A real implementation shapes with the resolved font; the tests supply a deterministic one.
|
|
3496
|
+
* Layout never reads the DOM, so this is the only way width and height enter it.
|
|
3497
|
+
*/
|
|
3498
|
+
interface TextMeasurer {
|
|
3499
|
+
/** Advance width of `text` in the resolved style. */
|
|
3500
|
+
measure(text: string, style: ResolvedRunStyle): number;
|
|
3501
|
+
/**
|
|
3502
|
+
* Optional whole-span logical caret advances, indexed by UTF-16 offset. Values
|
|
3503
|
+
* are nondecreasing, start at zero, and end at the measured advance. Ligature
|
|
3504
|
+
* interiors may share an edge. Undefined selects a bounded approximate fallback.
|
|
3505
|
+
*/
|
|
3506
|
+
caretAdvances?(text: string, style: ResolvedRunStyle): readonly number[] | undefined;
|
|
3507
|
+
/**
|
|
3508
|
+
* Optional conservative ink bounds for one grapheme, in points from its origin.
|
|
3509
|
+
* Includes size and horizontal scale, but not trailing character spacing.
|
|
3510
|
+
* Undefined keeps layout on the advance-only path.
|
|
3511
|
+
*/
|
|
3512
|
+
inkBounds?(text: string, style: ResolvedRunStyle): {
|
|
3513
|
+
left: number;
|
|
3514
|
+
right: number;
|
|
3515
|
+
} | undefined;
|
|
3516
|
+
/** Whether line metrics use an admitted face instead of approximate fallback metrics. */
|
|
3517
|
+
hasResolvedFont?(style: ResolvedRunStyle): boolean;
|
|
3518
|
+
/**
|
|
3519
|
+
* Optional `OS/2` strikeout stroke for the resolved face, at the drawn size. Word paints
|
|
3520
|
+
* `w:separator` as a strikeout, so the note rule reads its thickness and offset here rather
|
|
3521
|
+
* than assuming one face's numbers. Undefined keeps the caller's own default.
|
|
3522
|
+
*/
|
|
3523
|
+
strikeoutMetrics?(style: ResolvedRunStyle): StrikeoutStrokePt | undefined;
|
|
3524
|
+
/** Line metrics for the actual text faces; omitted text measures the primary face. */
|
|
3525
|
+
lineMetrics(style: ResolvedRunStyle, text?: string): {
|
|
3526
|
+
height: number;
|
|
3527
|
+
baseline: number;
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
export { PAGE_BORDER_SIDES as a$, AUTO_PARAGRAPH_SPACING_PT as a4, HARD_MAX_AGGREGATE_FONT_BYTES as aG, HARD_MAX_FONT_BYTES as aH, HARD_MAX_FONT_SOURCES as aI, MAX_BORDER_SPACE_PT as aO, MAX_BORDER_WIDTH_PT as aP, MAX_LVL_OVERRIDES as aQ, MAX_NUMBERING_DEFINITIONS as aR, MAX_PARAGRAPH_SPACING_PT as aS, MAX_SHAPING_CONTEXT as aT, MAX_STYLE_DEFINITIONS as aU, MAX_TABLE_BORDER_STROKES as aV, MAX_TAB_POSITION_TWIPS as aW, MAX_TAB_STOPS as aX, 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, bottomBorderExtentPt as b$, PARAGRAPH_BORDER_SIDES as b0, TAB_LEADER_GLYPH as bJ, UnsupportedScriptError as bU, appliedSpaceBefore as bW, applyLineSpacing as bX, baselineShiftPtOf as bY, borderExtentPt as bZ, borderWeight as b_, SINGLE_LINE_SPACING as bp, boundedStructuralFontValidator as c0, buildNumberingIndex as c1, buildStyleCascadeTable as c2, cascadeParagraphFormatting as c3, cascadeRunProperties as c4, cascadedBottomBorder as c5, cascadedParagraphBorders as c6, cascadedTabStops as c7, collapsedSpaceBefore as c8, createFontResourceSnapshot as c9, readCellBorders as cA, readNumPr as cB, readTableBorders as cC, resolveBorderConflict as cD, resolveNumberingLevel as cE, resolveParagraphLayoutInputs as cF, resolveRunStyle as cG, resolveStoryListItems as cH, resolveStoryRefFields as cI, resolveTableCellBorderGrid as cJ, runStylesEqual as cK, shapedRunComparatorInputs as cL, shapingEnvironmentFingerprint as cM, shapingEnvironmentFingerprintInputs as cN, styleForFontSlot as cO, tabAdvanceWidth as cP, tabStopsFingerprint as cQ, withDefaultTabInterval as cR, withNumberingStyleLinks as cS, withResolvedListItems as cT, createShapedRun as ca, createShapingEnvironment as cb, defaultTabIntervalFromSettings as cc, displayText as cd, fixedPoint as ce, fontRequestKey as cf, glyphSizeFactorOf as cg, isValidStyleId as ch, itemizeScriptFontSlots as ci, layoutEquation as cj, measureDisplayText as ck, mergeListIndent as cl, nextTabDestination as cm, pageBordersFingerprint as cn, paragraphBorderExtentPt as co, paragraphBorderStrokeWidthPt as cp, paragraphBorders as cq, paragraphBordersFingerprint as cr, paragraphBreaksBefore as cs, paragraphContextualSpacing as ct, paragraphLineSpacing as cu, paragraphSpacing as cv, paragraphTabStops as cw, parsePageBorders as cx, parseRefInstruction as cy, readBorderSide as cz };
|
|
3532
|
+
export type { HyperlinkFieldSpec as $, AnchoredDrawingRecord as A, BlockFragmentRecord as B, ContentControlBoundaryRecord as C, DrawingPoint as D, InlineDrawingLayoutContext as E, FontResourceSnapshot as F, CellBorderBox as G, HeaderFooterStoryRecord as H, InlineDrawingRecord as I, TableBorderBox as J, TableCellStyleFormatting as K, LineRecord as L, ModelRange as M, NoteAreaRecord as N, TableFloatPosition as O, ParagraphFragmentRecord as P, StyleCascadeTable as Q, ResolvedRunStyle as R, SemanticLayout as S, TextMeasurer as T, FixedPoint as U, VersionedShapingLibrary as V, FontByteValidator as W, SourceRange as X, HyperlinkProjector as Y, FieldLinkProjector as Z, StoryPageFieldNeeds as _, PageRecord as a, SpanLinkRecord as a0, PageBorderFrameRecord as a1, ParagraphBorderStrokeRecord as a2, StrikeoutStrokePt as a3, AbstractNumDefinition as a5, AnchoredDrawingLayoutFallback as a6, BidiEmbeddingLevels as a7, BorderGridGeometry as a8, CascadedParagraphFormatting as a9, FontResourceInstrumentation as aA, FontResourceSnapshotOptions as aB, FontSlot as aC, FontValidationResult as aD, GlyphOutline as aE, GroupTextboxStoryRecord as aF, LevelOverride as aJ, LineSpacingRule as aK, ListMarkerAlign as aL, ListMarkerRecord as aM, ListSuffix as aN, NumDefinition as aY, NumberingLevelIndent as aZ, NumberingPictureBullet as a_, CjkTypographySettings as aa, ContentControlGeometryFragment as ab, ContentControlLevel as ac, ContentControlMappedType as ad, 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, LayoutBox as b, PageBorderDisplay as b1, PageBorderOffsetFrom as b2, PageBorderSide as b3, PageBorderStrokeRecord as b4, PageBorderZOrder as b5, PageRefCalibrationCell as b6, PageRefFieldProjection as b7, ParagraphAutoSpacingContext as b8, ParagraphBorderEdge as b9, ShapedCluster as bA, ShapedFontSpan as bB, ShapedGlyph as bC, ShapedRunComparatorInputs as bD, ShapedVerticalMetrics as bE, ShapingContext as bF, ShapingEnvironment as bG, ShapingEnvironmentFingerprintInputs as bH, StyleDefinition as bI, TabAlignment as bK, TabDestination as bL, TabLeader as bM, TabStop as bN, TableBorderSideName as bO, TableBorderStrokeRecord as bP, TableFragmentRecord as bQ, TextDirection as bR, TextboxStoryFallbackReason as bS, TextboxStoryLayout as bT, VerticalAlign as bV, ParagraphBorderSide as ba, ParagraphBorders as bb, ParagraphBottomBorderRecord as bc, ParagraphIndent as bd, ParagraphLayoutInputs as be, ParagraphLineSpacing as bf, ParagraphSpacing as bg, RefFieldSpec as bh, RefNoteParts as bi, ResolvedCellBorders as bj, ResolvedPictureBullet as bk, ResolvedTabStops as bl, ResolvedTableBorderEdge as bm, ResolvedTableBorderEdgeSegment as bn, ResolvedUnderline as bo, ScriptItem as bq, SemanticArtifactRootStoryKind as br, SemanticArtifactStoryKind as bs, SemanticCommentArtifactRecord as bt, SemanticReviewArtifactOccurrence as bu, SemanticReviewArtifactOccurrenceGeometry as bv, SemanticReviewArtifactPosition as bw, SemanticReviewArtifactSource as bx, SemanticTrackedChangeArtifactRecord as by, ShapeInput as bz, NoteStoryRecord as c, ContentControlLock as d, StyleSpanRecord as e, SectionPageBorders as f, TableRowFragmentRecord as g, TableCellFragmentRecord as h, ShapingEnvironmentInput as i, TextShaper as j, ResolvedFont as k, NormalizationPolicy as l, FixedPointRoundingMode as m, FontRequest as n, SemanticReviewArtifactRecord as o, ListMarkerPictureRecord as p, FontSubstitution as q, ShapedRun as r, NumberingLevel as s, NumberingIndex as t, DrawingInsets as u, TableBorderSide as v, TableFloatXSpec as w, TableFloatYSpec as x, ResolvedListItem as y, RefFieldContext as z };
|