@arbidocs/blocks 0.4.101 → 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/chunk-6MG62I5B.cjs +272 -0
- package/dist/chunk-6MG62I5B.cjs.map +1 -0
- package/dist/chunk-EQB3WELX.js +269 -0
- package/dist/chunk-EQB3WELX.js.map +1 -0
- 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/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
|
@@ -0,0 +1,3151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@docx-editor.dev/core/layout` — DOM-free pagination, shaping, and hit testing.
|
|
3
|
+
*
|
|
4
|
+
* Text is measured through an injected `TextMeasurer` and shaped through an injected
|
|
5
|
+
* `TextShaper`, so the same code paginates in a browser and on a server. Points everywhere;
|
|
6
|
+
* twips convert at property-read boundaries.
|
|
7
|
+
*
|
|
8
|
+
* Incremental by construction: per-block cache keys and flow checkpoints mean a pass that
|
|
9
|
+
* changes nothing returns the previous pages by identity.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
import { s as NumberingLevel, t as NumberingIndex, S as SemanticLayout, b as LayoutBox, D as DrawingPoint, u as DrawingInsets, e as StyleSpanRecord, I as InlineDrawingRecord, M as ModelRange, v as TableBorderSide, w as TableFloatXSpec, x as TableFloatYSpec, P as ParagraphFragmentRecord, B as BlockFragmentRecord, A as AnchoredDrawingRecord, a as PageRecord, y as ResolvedListItem, z as RefFieldContext, T as TextMeasurer, E as InlineDrawingLayoutContext, G as CellBorderBox, J as TableBorderBox, K as TableCellStyleFormatting, O as TableFloatPosition, Q as StyleCascadeTable, C as ContentControlBoundaryRecord$1, m as FixedPointRoundingMode, U as FixedPoint, V as VersionedShapingLibrary, j as TextShaper, W as FontByteValidator, r as ShapedRun, L as LineRecord, X as SourceRange, Y as HyperlinkProjector, Z as FieldLinkProjector, _ as StoryPageFieldNeeds, $ as HyperlinkFieldSpec, a0 as SpanLinkRecord, f as SectionPageBorders, a1 as PageBorderFrameRecord, a2 as ParagraphBorderStrokeRecord, a3 as StrikeoutStrokePt } from './semantic-records-4B3WwFAi.js';
|
|
15
|
+
export { a4 as AUTO_PARAGRAPH_SPACING_PT, a5 as AbstractNumDefinition, a6 as AnchoredDrawingLayoutFallback, a7 as BidiEmbeddingLevels, a8 as BorderGridGeometry, a9 as CascadedParagraphFormatting, aa as CjkTypographySettings, ab as ContentControlGeometryFragment, ac as ContentControlLevel, d as ContentControlLock, ad as ContentControlMappedType, ae as DEFAULT_RUN_STYLE, af as DEFAULT_TAB_INTERVAL_PT, ag as DEFAULT_TAB_INTERVAL_TWIPS, ah as DeclaredFontSubstitution, ai as DrawingClipFallback, aj as DrawingGeometry, ak as DrawingGroupPictureRecord, al as EMPTY_NUMBERING_INDEX, am as EMPTY_TAB_STOPS, an as EquationFractionGeometry, ao as EquationGeometry, ap as EquationNaryGeometry, aq as EquationRadicalGeometry, ar as EquationRowGeometry, as as EquationScriptGeometry, at as EquationSpanRecord, au as EquationTextGeometry, av as FieldAtomMarker, aw as FontFingerprintInputs, n as FontRequest, ax as FontResolutionError, ay as FontResolutionErrorCode, az as FontResourceDefinition, aA as FontResourceInstrumentation, F as FontResourceSnapshot, aB as FontResourceSnapshotOptions, aC as FontSlot, q as FontSubstitution, aD as FontValidationResult, aE as GlyphOutline, aF as GroupTextboxStoryRecord, aG as HARD_MAX_AGGREGATE_FONT_BYTES, aH as HARD_MAX_FONT_BYTES, aI as HARD_MAX_FONT_SOURCES, H as HeaderFooterStoryRecord, aJ as LevelOverride, aK as LineSpacingRule, aL as ListMarkerAlign, p as ListMarkerPictureRecord, aM as ListMarkerRecord, aN as ListSuffix, aO as MAX_BORDER_SPACE_PT, aP as MAX_BORDER_WIDTH_PT, aQ as MAX_LVL_OVERRIDES, aR as MAX_NUMBERING_DEFINITIONS, aS as MAX_PARAGRAPH_SPACING_PT, aT as MAX_SHAPING_CONTEXT, aU as MAX_STYLE_DEFINITIONS, aV as MAX_TABLE_BORDER_STROKES, aW as MAX_TAB_POSITION_TWIPS, aX as MAX_TAB_STOPS, l as NormalizationPolicy, N as NoteAreaRecord, c as NoteStoryRecord, aY as NumDefinition, aZ as NumberingLevelIndent, a_ as NumberingPictureBullet, a$ as PAGE_BORDER_SIDES, b0 as PARAGRAPH_BORDER_SIDES, b1 as PageBorderDisplay, b2 as PageBorderOffsetFrom, b3 as PageBorderSide, b4 as PageBorderStrokeRecord, b5 as PageBorderZOrder, b6 as PageRefCalibrationCell, b7 as PageRefFieldProjection, b8 as ParagraphAutoSpacingContext, b9 as ParagraphBorderEdge, ba as ParagraphBorderSide, bb as ParagraphBorders, bc as ParagraphBottomBorderRecord, bd as ParagraphIndent, be as ParagraphLayoutInputs, bf as ParagraphLineSpacing, bg as ParagraphSpacing, bh as RefFieldSpec, bi as RefNoteParts, bj as ResolvedCellBorders, k as ResolvedFont, bk as ResolvedPictureBullet, R as ResolvedRunStyle, bl as ResolvedTabStops, bm as ResolvedTableBorderEdge, bn as ResolvedTableBorderEdgeSegment, bo as ResolvedUnderline, bp as SINGLE_LINE_SPACING, bq as ScriptItem, br as SemanticArtifactRootStoryKind, bs as SemanticArtifactStoryKind, bt as SemanticCommentArtifactRecord, bu as SemanticReviewArtifactOccurrence, bv as SemanticReviewArtifactOccurrenceGeometry, bw as SemanticReviewArtifactPosition, o as SemanticReviewArtifactRecord, bx as SemanticReviewArtifactSource, by as SemanticTrackedChangeArtifactRecord, bz as ShapeInput, bA as ShapedCluster, bB as ShapedFontSpan, bC as ShapedGlyph, bD as ShapedRunComparatorInputs, bE as ShapedVerticalMetrics, bF as ShapingContext, bG as ShapingEnvironment, bH as ShapingEnvironmentFingerprintInputs, i as ShapingEnvironmentInput, bI as StyleDefinition, bJ as TAB_LEADER_GLYPH, bK as TabAlignment, bL as TabDestination, bM as TabLeader, bN as TabStop, bO as TableBorderSideName, bP as TableBorderStrokeRecord, h as TableCellFragmentRecord, bQ as TableFragmentRecord, g as TableRowFragmentRecord, bR as TextDirection, bS as TextboxStoryFallbackReason, bT as TextboxStoryLayout, bU as UnsupportedScriptError, bV as VerticalAlign, bW as appliedSpaceBefore, bX as applyLineSpacing, bY as baselineShiftPtOf, bZ as borderExtentPt, b_ as borderWeight, b$ as bottomBorderExtentPt, c0 as boundedStructuralFontValidator, c1 as buildNumberingIndex, c2 as buildStyleCascadeTable, c3 as cascadeParagraphFormatting, c4 as cascadeRunProperties, c5 as cascadedBottomBorder, c6 as cascadedParagraphBorders, c7 as cascadedTabStops, c8 as collapsedSpaceBefore, c9 as createFontResourceSnapshot, ca as createShapedRun, cb as createShapingEnvironment, cc as defaultTabIntervalFromSettings, cd as displayText, ce as fixedPoint, cf as fontRequestKey, cg as glyphSizeFactorOf, ch as isValidStyleId, ci as itemizeScriptFontSlots, cj as layoutEquation, ck as measureDisplayText, cl as mergeListIndent, cm as nextTabDestination, cn as pageBordersFingerprint, co as paragraphBorderExtentPt, cp as paragraphBorderStrokeWidthPt, cq as paragraphBorders, cr as paragraphBordersFingerprint, cs as paragraphBreaksBefore, ct as paragraphContextualSpacing, cu as paragraphLineSpacing, cv as paragraphSpacing, cw as paragraphTabStops, cx as parsePageBorders, cy as parseRefInstruction, cz as readBorderSide, cA as readCellBorders, cB as readNumPr, cC as readTableBorders, cD as resolveBorderConflict, cE as resolveNumberingLevel, cF as resolveParagraphLayoutInputs, cG as resolveRunStyle, cH as resolveStoryListItems, cI as resolveStoryRefFields, cJ as resolveTableCellBorderGrid, cK as runStylesEqual, cL as shapedRunComparatorInputs, cM as shapingEnvironmentFingerprint, cN as shapingEnvironmentFingerprintInputs, cO as styleForFontSlot, cP as tabAdvanceWidth, cQ as tabStopsFingerprint, cR as withDefaultTabInterval, cS as withNumberingStyleLinks, cT as withResolvedListItems } from './semantic-records-4B3WwFAi.js';
|
|
16
|
+
export { C as CacheLookup, b as CacheMiss, c as CacheProvenance, e as LayoutEnvironmentShapedMeasurerOptions, f as LayoutFontConfiguration, g as LayoutFontSource, h as LayoutFontSubstitution, i as LayoutShapingConfigurationError, j as LayoutShapingEnvironment, L as LayoutShapingInstrumentation, a as LayoutShapingOptions, O as OperationSnapshot, k as OperationSnapshotField, l as OperationSnapshotGuard, P as PreparedLayoutFontConfiguration, R as ResolvedCache, m as ResourceDependencyProvenance, S as ShapedMeasurerOptions, n as captureOperationSnapshot, o as createLayoutShapedMeasurer, p as createLayoutShaping, q as createShapedMeasurer, d as disposeLayoutShaping, r as guardOperationSnapshot, s as layoutFontConfigurationFingerprint, t as prepareLayoutFontConfiguration } from './layout-shaping-BEyuiQOk.js';
|
|
17
|
+
import { a as SemanticStoryKind, S as SemanticRootStoryKind, b as SemanticStoryVisit, T as TextboxOwnerRecord } from './semantic-record-queries-CBlZqZNa.js';
|
|
18
|
+
export { c as SemanticDrawingLayer, d as SemanticDrawingVisit, e as StoryDrawingContext, f as StoryDrawingHost, g as StoryParagraphFragmentContext, h as contentControlsOfLayout, i as effectiveContentControlLock, j as forEachSemanticDrawing, k as forEachSemanticStory, l as forEachStoryParagraphFragment, m as fragmentsOfParagraph, n as lineAtPosition, o as linesOf, p as paragraphFragmentsOf, q as paragraphFragmentsOfBlocks, u as unionLayoutBoxes } from './semantic-record-queries-CBlZqZNa.js';
|
|
19
|
+
import { ak as ImpactClass, N as OoxmlNode, X as OoxmlProperty, Z as OoxmlElement, O as OoxmlPart, W as TreeDocOp, Q as OoxmlPackage, b9 as ContentControlKind, ba as ContentControlLevel, bc as ContentControlLock, b6 as ContentControlDataBinding, a as ReviewItem } from './types-barrel-D3xCgQX6.js';
|
|
20
|
+
export { b1 as CommentAnchor, b2 as CommentPosition, C as CommentRecord, aY as CommentThreadState, aK as DrawingAccessibility, aw as DrawingHorizontalReferenceFrame, aL as DrawingImageEffects, aM as DrawingTransform, ax as DrawingVerticalReferenceFrame, an as ImageWrapTarget, c as ReviewCommentItem, d as ReviewCustomItem, R as ReviewModelInput, e as ReviewPosition, f as ReviewRange, b as ReviewRevisionItem, g as ReviewRevisionKind, al as SourceCrop, aJ as TableBorderStyle, dB as VectorShapeComponent, aN as VectorShapeProjection, cs as W15_NAMESPACE_URI, dC as activeReviewItem, cW as firstReviewRange, ds as reviewItemKey, dD as reviewItemPositionRank, dt as reviewItemRanges, dE as reviewItemsAt, dF as reviewThreadRootOf } from './types-barrel-D3xCgQX6.js';
|
|
21
|
+
import { D as DocumentProperties, H as HeadlessDocumentView, r as relationshipTargetIn } from './headless-document-view-qH331moE.js';
|
|
22
|
+
import { o as RevisionAttribution, b as RevisionDisplayMode, c as RevisionAuthorFilter } from './revision-projection-DZ4kKVev.js';
|
|
23
|
+
export { p as DEFAULT_REVISION_DISPLAY_MODE, q as RevisionFilter, s as RevisionKind, t as formatRevisionOf, u as markRevisionRemovesMark, v as paragraphMarkFormatRevisionOf, w as paragraphMarkRevisionOf, x as paragraphMarkRevisionsOf, y as revisionAuthorFilter, z as revisionsAreDeletion, A as revisionsVisible, B as shownMarkRevision } from './revision-projection-DZ4kKVev.js';
|
|
24
|
+
import { b as ResolvedFootnoteProperties, R as ResolvedEndnoteProperties } from './note-properties-4Y9o6Gyk.js';
|
|
25
|
+
import { e as TreeModelChange, N as NoteKind, aA as noteTypeOf, k as FieldResultsMode } from './tree-package-fragment-BeeKxDw8.js';
|
|
26
|
+
import { P as PageGeometry, a as SectionColumns } from './semantic-cell-selection-D9D1KSXq.js';
|
|
27
|
+
export { C as CellSelection, D as DEFAULT_PAGE_GEOMETRY, b as DEFAULT_SECTION_PROPERTIES, c as DEFAULT_VERTICAL_WEIGHT, d as DocumentSection, e as DocumentSectionsEnumeration, f as DrawingOverlayFrame, H as HitPoint, g as HitTestOptions, M as MAX_DOCUMENT_SECTIONS, h as PlacedCell, i as SectionBreakType, j as SectionColumnDefinition, k as SectionMargins, l as SectionPageNumbering, S as SectionProperties, m as SemanticHit, n as SemanticHitDrawing, T as TableCellAddress, o as TableCellContext, p as caretBoxOnLine, q as cellSelectionBetween, r as cellSelectionRects, s as cellSelectionText, t as contentControlAtPoint, u as enumerateDocumentSections, v as enumerateDocumentSectionsBounded, w as findDrawingOverlayFrameInLayout, x as geometryOfSection, y as hitTestPage, z as hitTestSheet, A as isFurniturePoint, B as lineEndOffset, E as pageAtY, F as paragraphSectionNode, G as paragraphsInCells, I as parsePageNumbering, J as parseSectionProperties, K as projectedSectionSourceIndexes, L as readSectionProperties, N as spanOffsetX, O as spansInCells, Q as tableContextAt } from './semantic-cell-selection-D9D1KSXq.js';
|
|
28
|
+
export { M as MAX_SDT_NESTING, c as collectFlowBlocks, a as commentBodyText, b as commentInitials, j as contentControlContentChildren, i as isContentControl, f as isContentControlContent, p as paragraphOrderOfPart } from './review-paragraph-order-DT-n6ouC.js';
|
|
29
|
+
import { S as SemanticPosition, b as SelectionRect, a as SemanticSelection, C as CaretGeometry } from './semantic-interaction-CIC-PooQ.js';
|
|
30
|
+
export { c as CaretAtOptions, M as MoveCaretOptions, N as NavigationCommand, d as caretAt, e as caretStops, f as caretStopsForBlocks, g as compositionAnchor, h as contentControlsInLayout, i as deletedTextBoundaries, m as moveCaret, p as paragraphTextFromLayout, s as spansInSelection } from './semantic-interaction-CIC-PooQ.js';
|
|
31
|
+
import './image-resources-BN-hUgCn.js';
|
|
32
|
+
import './omml-equation-CuzBL5DD.js';
|
|
33
|
+
import './editor-scope-OHPwVQ5l.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The result of counting ONE list paragraph: the level that applied, the counter vector after it,
|
|
37
|
+
* and the marker text a reader sees.
|
|
38
|
+
*
|
|
39
|
+
* Counters are a vector across all nine levels, not a single number, because a deeper level
|
|
40
|
+
* restarting resets the ones below it while leaving those above intact.
|
|
41
|
+
*/
|
|
42
|
+
interface ListCounterAdvance {
|
|
43
|
+
/** Effective abstract numbering template for this num instance. */
|
|
44
|
+
readonly abstractNumId: string;
|
|
45
|
+
readonly numId: string;
|
|
46
|
+
readonly ilvl: number;
|
|
47
|
+
readonly level: NumberingLevel;
|
|
48
|
+
/** Counter vector after this item was counted (indices 0..8). */
|
|
49
|
+
readonly counters: readonly number[];
|
|
50
|
+
/** Expanded marker text (empty when vanished / empty lvlText). */
|
|
51
|
+
readonly markerText: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The running counters for one layout pass over one story.
|
|
55
|
+
*
|
|
56
|
+
* Stateful and order-dependent by nature: a list number is a function of every numbered paragraph
|
|
57
|
+
* before it, which is why markers are computed during layout and cannot be read off a paragraph
|
|
58
|
+
* in isolation.
|
|
59
|
+
*/
|
|
60
|
+
interface ListCounterState {
|
|
61
|
+
/**
|
|
62
|
+
* Advance counters for one list paragraph.
|
|
63
|
+
*
|
|
64
|
+
* Returns null when the numbering definition cannot be resolved — callers treat the
|
|
65
|
+
* paragraph as non-list (inert fallback).
|
|
66
|
+
*/
|
|
67
|
+
advance(numId: string, ilvl: number): ListCounterAdvance | null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a fresh counter bag for one story (body, or one header/footer part).
|
|
71
|
+
*/
|
|
72
|
+
declare function createListCounterState(index: NumberingIndex): ListCounterState;
|
|
73
|
+
|
|
74
|
+
/** Synchronous platform-neutral SHA-256 used before font bytes cross into a shaping implementation. */
|
|
75
|
+
declare const sha256FontBytes: (bytes: Uint8Array) => string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* What a batch of commits can affect.
|
|
79
|
+
*
|
|
80
|
+
* `paragraphIds` is the set the commits touched directly. What that set MEANS depends on
|
|
81
|
+
* `impact`: for a local impact it bounds the work, for a structural one it only says where
|
|
82
|
+
* the reflow starts.
|
|
83
|
+
*/
|
|
84
|
+
interface LayoutScope {
|
|
85
|
+
readonly impact: ImpactClass;
|
|
86
|
+
/** Node ids touched directly by the coalesced commits. */
|
|
87
|
+
readonly paragraphIds: ReadonlySet<string>;
|
|
88
|
+
/** Ids the commits created — no previous layout exists for these. */
|
|
89
|
+
readonly created: ReadonlySet<string>;
|
|
90
|
+
/** Ids the commits removed — any retained layout for these must be released. */
|
|
91
|
+
readonly deleted: ReadonlySet<string>;
|
|
92
|
+
/** Cache keys the commits invalidated (styles, numbering, fonts). */
|
|
93
|
+
readonly dependencyKeys: ReadonlySet<string>;
|
|
94
|
+
/** True when the block SEQUENCE changed, so ids alone cannot bound the reflow. */
|
|
95
|
+
readonly structural: boolean;
|
|
96
|
+
/** The revision this scope describes — the layout result must carry the same one. */
|
|
97
|
+
readonly revision: number;
|
|
98
|
+
}
|
|
99
|
+
/** How the scheduler produces layouts and when it publishes them. */
|
|
100
|
+
interface LayoutSchedulerOptions {
|
|
101
|
+
/**
|
|
102
|
+
* Produce a complete layout for the CURRENT model state.
|
|
103
|
+
*
|
|
104
|
+
* Given the accumulated scope so an incremental implementation can use it. It must tag
|
|
105
|
+
* the result with the revision it actually read, which is what makes staleness detectable
|
|
106
|
+
* rather than assumed.
|
|
107
|
+
*/
|
|
108
|
+
readonly run: (scope: LayoutScope) => SemanticLayout;
|
|
109
|
+
/** The model's revision right now. Read at publish time, never cached. */
|
|
110
|
+
readonly currentRevision: () => number;
|
|
111
|
+
/** Called with a layout that is known current. Never called with a stale one. */
|
|
112
|
+
readonly publish: (layout: SemanticLayout, scope: LayoutScope) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Defer work to a later turn, returning a canceller.
|
|
115
|
+
*
|
|
116
|
+
* Injected rather than assumed: the browser wants an animation frame, tests want to step
|
|
117
|
+
* the queue by hand, and a server has neither.
|
|
118
|
+
*/
|
|
119
|
+
readonly schedule?: (run: () => void) => () => void;
|
|
120
|
+
/**
|
|
121
|
+
* Run a layout in slices instead of in one call (task 9.5).
|
|
122
|
+
*
|
|
123
|
+
* Optional: without it `run` is used and completes in one turn, which is right for a
|
|
124
|
+
* document small enough that slicing costs more than it saves.
|
|
125
|
+
*/
|
|
126
|
+
readonly runCooperatively?: (scope: LayoutScope) => CooperativeRun;
|
|
127
|
+
}
|
|
128
|
+
interface CooperativeRun {
|
|
129
|
+
/**
|
|
130
|
+
* Advance the work. Returns the finished layout, or null to be called again.
|
|
131
|
+
*
|
|
132
|
+
* Split so a global relayout does not hold the main thread for the whole document: the
|
|
133
|
+
* scheduler yields between slices and a newer revision can cancel the run mid-flight
|
|
134
|
+
* rather than the user waiting for work whose result is already stale.
|
|
135
|
+
*/
|
|
136
|
+
step(): SemanticLayout | null;
|
|
137
|
+
/** Abandon the run. Nothing partial is ever published. */
|
|
138
|
+
cancel(): void;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Coalesces commits into layout passes.
|
|
142
|
+
*
|
|
143
|
+
* A keystroke is one commit but must not be one full layout pass, so changes accumulate into a
|
|
144
|
+
* scope and are laid out together. Every published layout is tagged with the revision it actually
|
|
145
|
+
* read, which is what makes a stale result detectable rather than merely late.
|
|
146
|
+
*/
|
|
147
|
+
interface LayoutScheduler {
|
|
148
|
+
/** Record a commit. Coalesces with anything already pending. */
|
|
149
|
+
notify(change: TreeModelChange): void;
|
|
150
|
+
/** Request a relayout for a reason the store did not report (page size, zoom, fonts). */
|
|
151
|
+
invalidateAll(revision: number, reason?: string): void;
|
|
152
|
+
/** Run any pending work now, synchronously. Returns whether a layout was published. */
|
|
153
|
+
flush(): boolean;
|
|
154
|
+
/** The scope that would be used if `flush` ran now, or null when nothing is pending. */
|
|
155
|
+
pending(): LayoutScope | null;
|
|
156
|
+
/** Drop pending work without publishing — for teardown. */
|
|
157
|
+
cancel(): void;
|
|
158
|
+
/** How many layouts were discarded for being stale. Diagnostics, and a test hook. */
|
|
159
|
+
readonly staleDiscards: number;
|
|
160
|
+
/** How many cooperative runs were abandoned because a newer revision arrived. */
|
|
161
|
+
readonly cancelledRuns: number;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Build the scheduler that turns a stream of commits into coalesced layout passes.
|
|
165
|
+
*
|
|
166
|
+
* Keystrokes arrive faster than a document can be laid out, so changes accumulate into one scope
|
|
167
|
+
* and are laid out together rather than once per commit.
|
|
168
|
+
*/
|
|
169
|
+
declare function createLayoutScheduler(options: LayoutSchedulerOptions): LayoutScheduler;
|
|
170
|
+
|
|
171
|
+
interface PositionedTableAnchorSignal {
|
|
172
|
+
readonly anchorId: string;
|
|
173
|
+
readonly column: number;
|
|
174
|
+
readonly fragmentIndex: number;
|
|
175
|
+
readonly anchorY: number;
|
|
176
|
+
}
|
|
177
|
+
interface PendingPositionedTableTokens {
|
|
178
|
+
readonly token: string;
|
|
179
|
+
readonly previous: PendingPositionedTableTokens | undefined;
|
|
180
|
+
readonly length: number;
|
|
181
|
+
readonly signature: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* What the BODY flow knows about page numbering while it MEASURES a page-field placeholder.
|
|
186
|
+
*
|
|
187
|
+
* The section's `w:pgNumType/@w:fmt` binds a PAGE field with no `\#` picture and no `\*`
|
|
188
|
+
* number format. A field that states either renders through it on every section.
|
|
189
|
+
*/
|
|
190
|
+
interface BodyPageFieldContext {
|
|
191
|
+
/** The section's `w:pgNumType/@w:fmt`; absent when the section authors none. */
|
|
192
|
+
readonly format?: string;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Page-field evaluation context for furniture projection.
|
|
196
|
+
*
|
|
197
|
+
* `pageNumber` is the displayed PAGE value after section `w:pgNumType/@w:start` (1-based).
|
|
198
|
+
* `pageCount` is document NUMPAGES. `sectionPageCount` is SECTIONPAGES for the attached
|
|
199
|
+
* section. `format` is the authored `w:pgNumType/@w:fmt` applied only to PAGE. `sheetNumber` is
|
|
200
|
+
* the 1-based physical sheet, which a restart does not renumber: it decides which margin an
|
|
201
|
+
* `inside` or `outside` header frame takes.
|
|
202
|
+
*
|
|
203
|
+
* A new field here must also join `sameFieldPageContext` below, or finalize compares two
|
|
204
|
+
* contexts that differ in it as equal and keeps a reused story's stale text.
|
|
205
|
+
*/
|
|
206
|
+
interface FieldPageContext {
|
|
207
|
+
/** Local character-style results for one physical page. @internal */
|
|
208
|
+
readonly characterStyleValues?: ReadonlyMap<string, string>;
|
|
209
|
+
readonly pageNumber: number;
|
|
210
|
+
readonly pageCount: number;
|
|
211
|
+
/** SECTIONPAGES; defaults to `pageCount` when omitted (single-section callers). */
|
|
212
|
+
readonly sectionPageCount?: number;
|
|
213
|
+
/** Authored ST_NumberFormat for PAGE; absent → decimal. */
|
|
214
|
+
readonly format?: string;
|
|
215
|
+
/** 1-based physical sheet; absent → `pageNumber` (single-section callers). */
|
|
216
|
+
readonly sheetNumber?: number;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Format a displayed PAGE value through the shared ST_NumberFormat resolver.
|
|
220
|
+
*
|
|
221
|
+
* Unknown / script-specific formats fall back to decimal (same convention as list markers).
|
|
222
|
+
* `none` / `bullet` are meaningless for page numbers and also fall back to decimal so a
|
|
223
|
+
* hostile fmt cannot blank the furniture.
|
|
224
|
+
*/
|
|
225
|
+
declare function formatPageNumber(value: number, format: string | undefined): string;
|
|
226
|
+
|
|
227
|
+
type WrapTextSide = 'bothSides' | 'left' | 'right' | 'largest';
|
|
228
|
+
interface WrapExclusionInput {
|
|
229
|
+
readonly mode: 'square' | 'tight' | 'through' | 'topAndBottom';
|
|
230
|
+
readonly contentBounds: LayoutBox;
|
|
231
|
+
readonly polygon: readonly DrawingPoint[] | null;
|
|
232
|
+
readonly clipPolygon: readonly DrawingPoint[] | null;
|
|
233
|
+
readonly wrapDistances: DrawingInsets;
|
|
234
|
+
readonly effectInsets: DrawingInsets;
|
|
235
|
+
readonly textSide: WrapTextSide;
|
|
236
|
+
readonly contentLeft: number;
|
|
237
|
+
readonly contentRight: number;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Paint layer relative to body text — not the OOXML wrap element. */
|
|
241
|
+
type DrawingPaintLayer = 'behind' | 'inFront';
|
|
242
|
+
/** The header or footer object a furniture exclusion zone comes from. */
|
|
243
|
+
interface FurnitureZoneSource {
|
|
244
|
+
readonly partName: string;
|
|
245
|
+
readonly kind: 'drawing' | 'table';
|
|
246
|
+
readonly nodeId: string;
|
|
247
|
+
}
|
|
248
|
+
interface ExclusionZone {
|
|
249
|
+
/** Objects outside body drawing flow publish their exclusion directly instead of synthesizing it. */
|
|
250
|
+
readonly sourceKind?: 'table' | 'frame' | 'furniture';
|
|
251
|
+
/**
|
|
252
|
+
* Placed by an earlier section on the sheet a `continuous` section continues on. Its anchor
|
|
253
|
+
* is not in the continued section, so the zone reaches every line and column there.
|
|
254
|
+
*/
|
|
255
|
+
readonly earlierSection?: boolean;
|
|
256
|
+
/**
|
|
257
|
+
* The header or footer object a `furniture` zone comes from. Node ids are unique only within
|
|
258
|
+
* one part, so a furniture zone is identified by its part, its kind, and its node together
|
|
259
|
+
* (see {@link exclusionZoneIdentity}), never by a composed id string.
|
|
260
|
+
*/
|
|
261
|
+
readonly furnitureSource?: FurnitureZoneSource;
|
|
262
|
+
readonly drawingNodeId: string;
|
|
263
|
+
readonly anchorParagraphId: string;
|
|
264
|
+
/** UTF-16 model offset of the anchor atom — exclusions apply at/after this point in the paragraph. */
|
|
265
|
+
readonly anchorModelStart: number;
|
|
266
|
+
/**
|
|
267
|
+
* The band's vertical position is resolved against the page or a margin, not against the
|
|
268
|
+
* anchor's own flow position, so it cannot move when the text beside it reflows.
|
|
269
|
+
*
|
|
270
|
+
* Such a band excludes every line it crosses on its page, including lines that PRECEDE the
|
|
271
|
+
* anchor — a picture pinned to the top of the margin wraps the paragraphs above its anchor
|
|
272
|
+
* exactly as it wraps the ones below. Flow-relative bands (`paragraph`, `line`) stay
|
|
273
|
+
* forward-only: reaching back would move the anchor that positions them.
|
|
274
|
+
*/
|
|
275
|
+
readonly pageFramedBand?: boolean;
|
|
276
|
+
readonly sourceOrder: number;
|
|
277
|
+
readonly paintLayer: DrawingPaintLayer;
|
|
278
|
+
readonly relativeHeight: number;
|
|
279
|
+
readonly allowOverlap: boolean;
|
|
280
|
+
/** Owning column index in a multi-column section — 0 for single-column and HF stories. */
|
|
281
|
+
readonly columnIndex: number;
|
|
282
|
+
/** Resolved top edge in page-content coordinates after overlap displacement. */
|
|
283
|
+
readonly y: number;
|
|
284
|
+
readonly verticalBand: LayoutBox;
|
|
285
|
+
readonly input: WrapExclusionInput;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
type AutonumFieldKind = 'AUTONUM' | 'AUTONUMLGL' | 'AUTONUMOUT';
|
|
289
|
+
/** One recognized AUTONUM-family instruction: the kind and its supported switches. */
|
|
290
|
+
interface AutonumFieldSpec {
|
|
291
|
+
readonly kind: AutonumFieldKind;
|
|
292
|
+
/** ST_NumberFormat resolved from the `\*` switch; null paints decimal. */
|
|
293
|
+
readonly numFmt: string | null;
|
|
294
|
+
/** `\e`: display the number without its trailing period. */
|
|
295
|
+
readonly suppressPeriod: boolean;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Recognize `AUTONUM | AUTONUMLGL | AUTONUMOUT [\* <format>] [\e] [\* MERGEFORMAT]`, or null
|
|
299
|
+
* for anything else. Any unrecognized switch fails the parse so the field stays inert (paints
|
|
300
|
+
* nothing — its historical rendering), never the raw instruction, never a guessed number.
|
|
301
|
+
*/
|
|
302
|
+
declare function parseAutonumInstruction(raw: string): AutonumFieldSpec | null;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Lookup of derived display marks keyed by {@link formatNoteScopeId}.
|
|
306
|
+
*
|
|
307
|
+
* `null` mark = customMarkFollows (no automatic digits). Absent key = dangling / unknown
|
|
308
|
+
* — fail-open with an empty display (model atom preserved).
|
|
309
|
+
*/
|
|
310
|
+
interface NoteMarkContext {
|
|
311
|
+
/**
|
|
312
|
+
* scopeId → formatted mark (or null when suppressed).
|
|
313
|
+
*
|
|
314
|
+
* IMMUTABLE once the context is built. `noteMarksCacheToken` memoizes on the context object
|
|
315
|
+
* and that token reaches every paragraph's cache key, so a later `marks.set` would pin the
|
|
316
|
+
* whole document to a token for marks it no longer has.
|
|
317
|
+
*/
|
|
318
|
+
readonly marks: ReadonlyMap<string, string | null>;
|
|
319
|
+
/**
|
|
320
|
+
* When set, every automatic mark measures at least this string's width (eachPage
|
|
321
|
+
* reservation). The string is the widest-measuring candidate under the effective mark
|
|
322
|
+
* style (actual marks plus a bounded per-section value window) — not merely the longest
|
|
323
|
+
* by codepoint count. Display text may still be the real mark; measurement uses the wider
|
|
324
|
+
* of the two so digit-width / proportional-glyph feedback cannot oscillate.
|
|
325
|
+
*/
|
|
326
|
+
readonly reservedMarkText?: string;
|
|
327
|
+
/**
|
|
328
|
+
* Scope id of the note story currently being laid out (`footnote:N` / `endnote:N`).
|
|
329
|
+
*
|
|
330
|
+
* Body `noteReference` atoms carry `@w:id`; note-body `noteRef` atoms do not — they
|
|
331
|
+
* inherit identity from the enclosing note via this key.
|
|
332
|
+
*/
|
|
333
|
+
readonly activeNoteKey?: string;
|
|
334
|
+
/** Project separator markers as inline rules inside their authored stories. */
|
|
335
|
+
readonly measureSeparatorMarkers?: true;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The key one note's mark is stored under — `footnote:N` / `endnote:N`.
|
|
339
|
+
*
|
|
340
|
+
* The same encoding `EditorScope` uses for note ids, so a mark context and a scope address name
|
|
341
|
+
* the same note without a translation step.
|
|
342
|
+
*/
|
|
343
|
+
declare function noteMarkKey(noteKind: NoteKind, noteId: number): string;
|
|
344
|
+
/**
|
|
345
|
+
* Navigation role for a projected note atom:
|
|
346
|
+
* - `to-note` — body `noteReference` jumps into the note story
|
|
347
|
+
* - `to-body` — note-body `noteRef` jumps back to the body citation
|
|
348
|
+
*/
|
|
349
|
+
type NoteNavDirection = 'to-note' | 'to-body';
|
|
350
|
+
interface ProjectedNoteMark {
|
|
351
|
+
readonly noteSeparator?: 'separator' | 'continuationSeparator';
|
|
352
|
+
readonly text: string;
|
|
353
|
+
readonly measureText?: string;
|
|
354
|
+
readonly projected: boolean;
|
|
355
|
+
readonly kind: NoteKind | null;
|
|
356
|
+
readonly noteId: number | null;
|
|
357
|
+
readonly nav?: NoteNavDirection;
|
|
358
|
+
readonly scopeId?: string;
|
|
359
|
+
}
|
|
360
|
+
/** Resolve display text for a noteReference / noteRef node under a mark context. */
|
|
361
|
+
declare function projectedNoteMarkText(node: OoxmlNode, context: NoteMarkContext | undefined): ProjectedNoteMark | null;
|
|
362
|
+
|
|
363
|
+
/** A fingerprint over one paragraph's layout inputs. */
|
|
364
|
+
type ParagraphLayoutKey = string;
|
|
365
|
+
/** Cache counters, for asserting that incremental layout is actually reusing work. */
|
|
366
|
+
interface LayoutCacheStats {
|
|
367
|
+
readonly hits: number;
|
|
368
|
+
readonly misses: number;
|
|
369
|
+
readonly evictions: number;
|
|
370
|
+
readonly size: number;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The per-paragraph measurement cache.
|
|
374
|
+
*
|
|
375
|
+
* Caches the BREAK only — where a paragraph's lines fall at a given width — never its placement.
|
|
376
|
+
* An edit high in a document still repaginates everything below it, while paragraphs nobody
|
|
377
|
+
* touched are never measured again.
|
|
378
|
+
*/
|
|
379
|
+
interface ParagraphLayoutCache<T> {
|
|
380
|
+
/**
|
|
381
|
+
* Whether values released after placement remain available to later layout passes.
|
|
382
|
+
*
|
|
383
|
+
* One-shot exporters set this false so producers may avoid defensive snapshots whose
|
|
384
|
+
* only purpose is protecting a value reused by a future pass.
|
|
385
|
+
*/
|
|
386
|
+
readonly retainAcrossPasses?: boolean;
|
|
387
|
+
/**
|
|
388
|
+
* Derive a break key inside this cache's ownership scope.
|
|
389
|
+
*
|
|
390
|
+
* Export caches use this hook to release construction-only subtree fingerprints together
|
|
391
|
+
* with measured breaks. Optional so structural/custom cache implementations remain valid.
|
|
392
|
+
*/
|
|
393
|
+
keyFor?(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
|
|
394
|
+
get(key: ParagraphLayoutKey): T | undefined;
|
|
395
|
+
set(key: ParagraphLayoutKey, value: T): void;
|
|
396
|
+
/** Release a break after final placement when this cache was created for a one-shot pass. */
|
|
397
|
+
release?(key: ParagraphLayoutKey): void;
|
|
398
|
+
/** Drop entries for paragraphs a commit removed, so the cache cannot grow without bound. */
|
|
399
|
+
retain(keys: ReadonlySet<ParagraphLayoutKey>): void;
|
|
400
|
+
/**
|
|
401
|
+
* Whether THIS published pass should pay for a document-wide {@link retain} sweep.
|
|
402
|
+
*
|
|
403
|
+
* Retention only trims memory — the aging window tolerates deferral — while building the
|
|
404
|
+
* union of live keys costs real time on a large document, so callers ask per pass and
|
|
405
|
+
* sweep on a stride. Per cache instance, so one editor's cadence cannot starve another's.
|
|
406
|
+
* Optional for compatibility: a cache without it is retained on every pass.
|
|
407
|
+
*/
|
|
408
|
+
retentionPassDue?(): boolean;
|
|
409
|
+
clear(): void;
|
|
410
|
+
readonly stats: LayoutCacheStats;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Everything that decides whether a cached paragraph break is still valid.
|
|
414
|
+
*
|
|
415
|
+
* `producer` is in the key because a font arriving after first paint changes every advance in the
|
|
416
|
+
* document while no content changes — without it the cache would serve the pre-font layout
|
|
417
|
+
* forever.
|
|
418
|
+
*/
|
|
419
|
+
interface ParagraphKeyInputs {
|
|
420
|
+
readonly paragraph: OoxmlNode;
|
|
421
|
+
readonly properties: readonly OoxmlProperty[];
|
|
422
|
+
/** Available width, which decides where the lines break. */
|
|
423
|
+
readonly width: number;
|
|
424
|
+
/**
|
|
425
|
+
* Who produced the measurements.
|
|
426
|
+
*
|
|
427
|
+
* Fonts loading after first paint change every advance while no content changes, so a
|
|
428
|
+
* cache keyed on content alone would serve the pre-font layout forever.
|
|
429
|
+
*/
|
|
430
|
+
readonly producer: string;
|
|
431
|
+
/**
|
|
432
|
+
* Inline drawing projection/resource epoch for this paragraph.
|
|
433
|
+
*
|
|
434
|
+
* Pending→ready/refused transitions and extent/hidden changes must invalidate breaks even
|
|
435
|
+
* when paragraph text is unchanged.
|
|
436
|
+
*/
|
|
437
|
+
readonly drawingToken?: string;
|
|
438
|
+
/** Paragraph-local semantic projection identity (links and live metadata fields). */
|
|
439
|
+
readonly projectionToken?: string;
|
|
440
|
+
/** Active page exclusion zones affecting this paragraph's break. */
|
|
441
|
+
readonly exclusionToken?: string;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* The cache key for one paragraph's measured break.
|
|
445
|
+
*
|
|
446
|
+
* Folds in the content, the available width, and the measurement producer. Anything that changes
|
|
447
|
+
* where lines fall must be in here, or the cache serves a break taken under different conditions.
|
|
448
|
+
*/
|
|
449
|
+
declare function paragraphLayoutKey(inputs: ParagraphKeyInputs): ParagraphLayoutKey;
|
|
450
|
+
/** How large the paragraph cache grows before least-recently-used eviction. */
|
|
451
|
+
interface ParagraphLayoutCacheOptions {
|
|
452
|
+
/**
|
|
453
|
+
* Entries retained before the least recently used are dropped.
|
|
454
|
+
*
|
|
455
|
+
* The default has to exceed a realistic document, or a full pass evicts exactly what the
|
|
456
|
+
* next one needs and the cache costs more than it saves. Unset, an estimated byte budget
|
|
457
|
+
* bounds the cache instead, because one cached break can be a hundred times another.
|
|
458
|
+
*/
|
|
459
|
+
readonly maxEntries?: number;
|
|
460
|
+
/** Keep placed breaks for later revisions. Default true; false bounds one-shot exporters. */
|
|
461
|
+
readonly retainAcrossPasses?: boolean;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* A bounded least-recently-used cache with generation-scoped retention.
|
|
466
|
+
*
|
|
467
|
+
* Bounded because a long editing session touches far more paragraph states than a document
|
|
468
|
+
* contains — every keystroke mints a new key for the paragraph being typed in — and an
|
|
469
|
+
* unbounded cache would hold every intermediate state of the session.
|
|
470
|
+
*
|
|
471
|
+
* The bound never evicts the CURRENT working set: entries named by this generation's
|
|
472
|
+
* retain or touched since it began are skipped, and the cache grows past its soft budget
|
|
473
|
+
* when a document is larger than it — evicting live entries made every full pass on a
|
|
474
|
+
* 500-page document re-measure the whole document.
|
|
475
|
+
*/
|
|
476
|
+
declare function createParagraphLayoutCache<T>(options?: ParagraphLayoutCacheOptions): ParagraphLayoutCache<T>;
|
|
477
|
+
|
|
478
|
+
interface TocLinkRange {
|
|
479
|
+
readonly start: number;
|
|
480
|
+
readonly end: number;
|
|
481
|
+
}
|
|
482
|
+
type TocLinkRanges = ReadonlyMap<string, readonly TocLinkRange[]>;
|
|
483
|
+
|
|
484
|
+
interface FieldCodeRange {
|
|
485
|
+
readonly start: number;
|
|
486
|
+
readonly end: number;
|
|
487
|
+
readonly code?: string;
|
|
488
|
+
readonly sourceNodeId?: string;
|
|
489
|
+
readonly suppressParagraph: boolean;
|
|
490
|
+
}
|
|
491
|
+
type FieldCodeRanges = ReadonlyMap<string, readonly FieldCodeRange[]>;
|
|
492
|
+
|
|
493
|
+
interface PendingLine {
|
|
494
|
+
readonly spans: StyleSpanRecord[];
|
|
495
|
+
readonly drawings: InlineDrawingRecord[];
|
|
496
|
+
readonly start: number;
|
|
497
|
+
end: number;
|
|
498
|
+
width: number;
|
|
499
|
+
height: number;
|
|
500
|
+
baseline: number;
|
|
501
|
+
/**
|
|
502
|
+
* Space ABOVE the glyph band inside {@link height}.
|
|
503
|
+
*
|
|
504
|
+
* Exact spacing can center the glyphs and move the baseline. Auto/atLeast spacing leaves
|
|
505
|
+
* this at zero and puts its extra depth below instead.
|
|
506
|
+
*/
|
|
507
|
+
leading: number;
|
|
508
|
+
/**
|
|
509
|
+
* Auto/atLeast line-spacing depth below the painted glyph band.
|
|
510
|
+
*
|
|
511
|
+
* Word lets this external depth cross the bottom text margin when the glyphs themselves
|
|
512
|
+
* still fit. Pagination therefore budgets {@link height} minus this amount at a page
|
|
513
|
+
* bottom, while paint keeps the full box and padding.
|
|
514
|
+
*/
|
|
515
|
+
trailingSpacing: number;
|
|
516
|
+
/** When true, layout must start a new page after this line is placed. */
|
|
517
|
+
pageBreakAfter?: boolean;
|
|
518
|
+
/** When true, layout must advance to the next authored section column. */
|
|
519
|
+
columnBreakAfter?: boolean;
|
|
520
|
+
/** An authored line break (`w:br` textWrapping or `w:cr`), never an automatic wrap. */
|
|
521
|
+
manualBreakAfter?: true;
|
|
522
|
+
/**
|
|
523
|
+
* The flow kept the paragraph's last word on this line by borrowing inter-word space.
|
|
524
|
+
* Alignment compresses a paragraph's last line only when this is set, never for another
|
|
525
|
+
* overflow such as hanging punctuation.
|
|
526
|
+
*/
|
|
527
|
+
spaceShrink?: true;
|
|
528
|
+
/** Model ranges on this line covering deleted content; see {@link LineRecord.deletedRanges}. */
|
|
529
|
+
deletedRanges?: readonly ModelRange[];
|
|
530
|
+
/** Vertical gap inserted before this line to clear a drawing exclusion band. */
|
|
531
|
+
exclusionSkipBefore?: number;
|
|
532
|
+
/**
|
|
533
|
+
* The line follows a text wrapping break that clears floating objects (`w:br w:clear`).
|
|
534
|
+
* Placement keeps its skip even on a line before the paragraph's first anchor.
|
|
535
|
+
*/
|
|
536
|
+
breakClearance?: true;
|
|
537
|
+
/** Clearance inherited by an empty anchor paragraph from other drawing bands. */
|
|
538
|
+
anchorClearanceBefore?: number;
|
|
539
|
+
/**
|
|
540
|
+
* The first-line offset this line was broken with: the paragraph's first line, and the
|
|
541
|
+
* first line after page breaks that open it ({@link holdsOnlyPageBreak}). Absent when zero.
|
|
542
|
+
*/
|
|
543
|
+
firstLineOffset?: number;
|
|
544
|
+
/**
|
|
545
|
+
* The horizontal passage a float left this line, when that passage is narrower than the
|
|
546
|
+
* paragraph's measure and holds the whole line.
|
|
547
|
+
*
|
|
548
|
+
* `start` is where the line's content begins (after any first-line indent) and `end` is the
|
|
549
|
+
* passage's right edge, both in paragraph-local coordinates. Centring, right alignment and
|
|
550
|
+
* justification align INSIDE this passage: Word centres a heading between the two pictures
|
|
551
|
+
* beside it, not between the page margins, and stretches a justified line to the float's
|
|
552
|
+
* near edge rather than through it. Absent when no float shortens the line, when the line
|
|
553
|
+
* steps over a float into a later passage, or when the passage is the whole measure.
|
|
554
|
+
*/
|
|
555
|
+
wrapSegment?: {
|
|
556
|
+
readonly start: number;
|
|
557
|
+
readonly end: number;
|
|
558
|
+
};
|
|
559
|
+
/** Tracked anchored-drawing attributions on this line; see {@link LineRecord.anchorRevisions}. */
|
|
560
|
+
anchorRevisions?: readonly RevisionAttribution[];
|
|
561
|
+
/** Revisions a resolved view answered on this line; see {@link LineRecord.changeSites}. */
|
|
562
|
+
changeSites?: readonly RevisionAttribution[];
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* What one cell side is before any ADJACENT cell has its say.
|
|
567
|
+
*
|
|
568
|
+
* - omitted → the table's own rule for that position (`tblBorders`, `insideH`/`insideV`)
|
|
569
|
+
* - edge → the cell wins outright; no weight fight with the table
|
|
570
|
+
* - none → an explicit `w:val="nil"`. Suppresses a matching table border on interior
|
|
571
|
+
* and perimeter sides alike, so a table whose cells all declare none paints borderless
|
|
572
|
+
* like Word even when `tblBorders` still carry `single` rules.
|
|
573
|
+
*/
|
|
574
|
+
declare function effectiveBorderSide(authored: TableBorderSide, tableSide: TableBorderSide, _options?: {
|
|
575
|
+
readonly interior?: boolean;
|
|
576
|
+
}): TableBorderSide;
|
|
577
|
+
|
|
578
|
+
/** Minimum stroke width for a compound (double/triple) band, in points. */
|
|
579
|
+
declare const COMPOUND_BORDER_MIN_STROKE_PT = 0.25;
|
|
580
|
+
/** Minimum gap between compound strokes, in points. */
|
|
581
|
+
declare const COMPOUND_BORDER_MIN_GAP_PT = 0.25;
|
|
582
|
+
/**
|
|
583
|
+
* How a multi-line border style (double, triple) is drawn: stroke width, gap, and total extent.
|
|
584
|
+
*
|
|
585
|
+
* Table stroke coordinates centre the painted band on the authored edge box.
|
|
586
|
+
* Content insets must also account for the portion that extends inward.
|
|
587
|
+
*/
|
|
588
|
+
interface CompoundBorderMetrics {
|
|
589
|
+
readonly strokePt: number;
|
|
590
|
+
readonly gapPt: number;
|
|
591
|
+
readonly extentPt: number;
|
|
592
|
+
/** Centers the compound band on the authored width; negative extends outward. */
|
|
593
|
+
readonly insetPt: number;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Double strokes use the authored width, with a quarter-point minimum.
|
|
597
|
+
* The complete band is centred on the authored edge box. Device pixel rounding
|
|
598
|
+
* belongs to rendering; these layout metrics remain in continuous points.
|
|
599
|
+
*/
|
|
600
|
+
declare function computeDoubleBorderMetricsPt(widthPt: number): CompoundBorderMetrics;
|
|
601
|
+
|
|
602
|
+
/** Soft ceiling on fragments emitted for one authored row (hostile / runaway splits). */
|
|
603
|
+
declare const MAX_TABLE_ROW_FRAGMENTS = 4096;
|
|
604
|
+
/**
|
|
605
|
+
* Why a table could not be paginated as authored.
|
|
606
|
+
*
|
|
607
|
+
* Each is a bound: a row taller than a page, a row that cannot be split, or a row producing more
|
|
608
|
+
* fragments than the limit allows.
|
|
609
|
+
*/
|
|
610
|
+
type TablePaginationErrorCode = 'table-row-overheight' | 'table-row-split-unsupported' | 'table-row-fragment-limit';
|
|
611
|
+
/**
|
|
612
|
+
* Bounded table pagination failure. Prefer this over emitting a fragment that overflows
|
|
613
|
+
* the page content box.
|
|
614
|
+
*/
|
|
615
|
+
declare class TablePaginationError extends Error {
|
|
616
|
+
readonly code: TablePaginationErrorCode;
|
|
617
|
+
constructor(code: TablePaginationErrorCode, message: string);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
type ParagraphFrameHeightRule = 'auto' | 'atLeast' | 'exact';
|
|
621
|
+
type ParagraphFrameWrap = 'auto' | 'around' | 'tight' | 'through' | 'none' | 'notBeside';
|
|
622
|
+
/** Bounded text-frame properties resolved for semantic layout. */
|
|
623
|
+
interface ParagraphFrame {
|
|
624
|
+
/** Explicit-size drop cap aligned against this many lines of its anchor paragraph. */
|
|
625
|
+
readonly dropCapLines?: number;
|
|
626
|
+
readonly dropCap?: 'drop' | 'margin';
|
|
627
|
+
readonly dropCapRtl?: boolean;
|
|
628
|
+
readonly x: number;
|
|
629
|
+
readonly y: number;
|
|
630
|
+
/** Zero until the containing text width resolves an omitted `w:w`. */
|
|
631
|
+
readonly width: number;
|
|
632
|
+
readonly autoWidth: boolean;
|
|
633
|
+
/** Authored exact or minimum height. Absent for `hRule="auto"`. */
|
|
634
|
+
readonly height?: number;
|
|
635
|
+
readonly heightRule: ParagraphFrameHeightRule;
|
|
636
|
+
readonly horizontalAnchor: 'page' | 'margin' | 'text';
|
|
637
|
+
readonly verticalAnchor: 'page' | 'margin' | 'text';
|
|
638
|
+
readonly xAlign?: TableFloatXSpec;
|
|
639
|
+
readonly yAlign?: TableFloatYSpec;
|
|
640
|
+
readonly anchorLocked: boolean;
|
|
641
|
+
/** Equal authored frame properties group adjacent paragraphs into one frame. */
|
|
642
|
+
readonly token: string;
|
|
643
|
+
readonly wrap: ParagraphFrameWrap;
|
|
644
|
+
readonly hSpace: number;
|
|
645
|
+
readonly vSpace: number;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
interface PendingParagraphFrame {
|
|
649
|
+
readonly frame: ParagraphFrame;
|
|
650
|
+
readonly fragment: ParagraphFragmentRecord;
|
|
651
|
+
readonly groupId: string;
|
|
652
|
+
readonly sourceOrder: number;
|
|
653
|
+
}
|
|
654
|
+
/** Immutable prefix shared by every checkpoint; one node per pending paragraph. */
|
|
655
|
+
interface PendingParagraphFrames {
|
|
656
|
+
readonly item: PendingParagraphFrame;
|
|
657
|
+
readonly previous: PendingParagraphFrames | undefined;
|
|
658
|
+
readonly length: number;
|
|
659
|
+
readonly signature: string;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** The flow state as it stood immediately before one block was placed. */
|
|
663
|
+
interface FlowCheckpoint {
|
|
664
|
+
/** Completed pages at this point. The prefix of the previous layout that still stands. */
|
|
665
|
+
readonly pageCount: number;
|
|
666
|
+
/** Fragments already on the page being built. */
|
|
667
|
+
readonly pageFragments: readonly BlockFragmentRecord[];
|
|
668
|
+
/** Anchored drawings already collected for the open page. */
|
|
669
|
+
readonly pendingAnchoredDrawings: readonly AnchoredDrawingRecord[];
|
|
670
|
+
/** Local frame paragraphs waiting for their next regular paragraph anchor. */
|
|
671
|
+
readonly pendingParagraphFrames?: PendingParagraphFrames;
|
|
672
|
+
/**
|
|
673
|
+
* Anchored drawings overlap resolution pushed onto the NEXT page, and how many times each
|
|
674
|
+
* drawing has been pushed.
|
|
675
|
+
*
|
|
676
|
+
* Flow state like the rest of this record, and carried for the same reason: a resume that
|
|
677
|
+
* started with an empty list dropped a drawing that the previous pass had deferred but not
|
|
678
|
+
* yet placed, and a convergence that did not compare them accepted a flow that still owed
|
|
679
|
+
* the next page a drawing as equal to one that owed it nothing.
|
|
680
|
+
*/
|
|
681
|
+
readonly deferredAnchoredDrawings: readonly AnchoredDrawingRecord[];
|
|
682
|
+
readonly anchorPageDeferCounts: ReadonlyMap<string, number>;
|
|
683
|
+
/** Sheet-positioned tables and zero-line anchors still owed by the open page. */
|
|
684
|
+
readonly pendingPositionedTableTokens?: PendingPositionedTableTokens;
|
|
685
|
+
readonly positionedTableAnchorSignals?: readonly PositionedTableAnchorSignal[];
|
|
686
|
+
readonly cursorY: number;
|
|
687
|
+
readonly lineCounter: number;
|
|
688
|
+
/** Trailing paragraph spacing participating in adjacent-spacing collapse. */
|
|
689
|
+
readonly previousSpaceAfter: number;
|
|
690
|
+
/** Active column index when the section uses multiple columns. */
|
|
691
|
+
readonly flowColumnIndex: number;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* What the last layout pass actually did — the observable evidence that incremental layout is
|
|
695
|
+
* working.
|
|
696
|
+
*
|
|
697
|
+
* `reusedPages` and `fullPasses` are the ones that matter: a typing keystroke that rebuilds every
|
|
698
|
+
* page produces identical output and unusable performance, so the tests assert on these rather
|
|
699
|
+
* than on the rendered result.
|
|
700
|
+
*/
|
|
701
|
+
interface LayoutSessionStats {
|
|
702
|
+
/** Paragraphs placed by the last pass, against the number in the document. */
|
|
703
|
+
readonly placed: number;
|
|
704
|
+
readonly total: number;
|
|
705
|
+
/** Pages carried over from the previous layout without being rebuilt. */
|
|
706
|
+
readonly reusedPages: number;
|
|
707
|
+
/** Passes that could not resume and laid the document out from the top. */
|
|
708
|
+
readonly fullPasses: number;
|
|
709
|
+
}
|
|
710
|
+
/** One section's place on the previous document sheet stack. */
|
|
711
|
+
interface SectionStackSpan {
|
|
712
|
+
readonly startIndex: number;
|
|
713
|
+
readonly pageCount: number;
|
|
714
|
+
readonly sheetY: number;
|
|
715
|
+
/** Remapped pages (projectors intact) from the last pass for this section. */
|
|
716
|
+
readonly remappedPages: readonly PageRecord[];
|
|
717
|
+
/**
|
|
718
|
+
* The section-local pages this span's pass returned — identity evidence for reuse. (On a
|
|
719
|
+
* continuous section the host tail merges instead of remapping, so `remappedPages` covers
|
|
720
|
+
* only the overflow sheets while this list still holds every returned page.)
|
|
721
|
+
*
|
|
722
|
+
* A section can lay out more than once inside one document pass (a reserve re-run, a
|
|
723
|
+
* balancing probe), and the last run then reports "nothing placed" against its OWN session
|
|
724
|
+
* even though an earlier run this pass rebuilt the pages. Counts and stats cannot tell
|
|
725
|
+
* those apart; only these object identities can say the span still describes the pages the
|
|
726
|
+
* section just returned.
|
|
727
|
+
*/
|
|
728
|
+
readonly sourcePages: readonly PageRecord[];
|
|
729
|
+
/** The blank parity sheet published in front of the section, kept for identity reuse. */
|
|
730
|
+
readonly paritySheet?: PageRecord;
|
|
731
|
+
}
|
|
732
|
+
/** Orchestrator state for multi-section incremental layout. */
|
|
733
|
+
interface MultiSectionLayoutState {
|
|
734
|
+
structureKey: string;
|
|
735
|
+
/** Each section's part of {@link structureKey}, so an added section keeps the others. */
|
|
736
|
+
sectionKeys: readonly string[];
|
|
737
|
+
sections: LayoutSession[];
|
|
738
|
+
spans: SectionStackSpan[];
|
|
739
|
+
previousRemapped: readonly PageRecord[];
|
|
740
|
+
previousFinalized: SemanticLayout | null;
|
|
741
|
+
previousPageCount: number;
|
|
742
|
+
/**
|
|
743
|
+
* The PAGEREF assignment token of the pages behind `previousFinalized`. A target can move
|
|
744
|
+
* sheets while the TOC page's own remapped record stays identity-unchanged, so the finalized
|
|
745
|
+
* restore must compare this before handing back a previously substituted sheet.
|
|
746
|
+
*/
|
|
747
|
+
previousPageRefToken: string;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Carried-over state that makes layout incremental.
|
|
751
|
+
*
|
|
752
|
+
* A caller creates one and hands the SAME object back each pass. It holds the previous pages, the
|
|
753
|
+
* per-block cache keys and the flow checkpoints a pass resumes from, so an edit low in a document
|
|
754
|
+
* re-lays only what follows it. A no-change pass returns the previous pages by identity.
|
|
755
|
+
*/
|
|
756
|
+
interface LayoutSession {
|
|
757
|
+
/** @internal Mutable across passes; a caller only creates one and passes it back. */
|
|
758
|
+
previous: SemanticLayout | null;
|
|
759
|
+
checkpoints: FlowCheckpoint[];
|
|
760
|
+
keys: string[];
|
|
761
|
+
/** Geometry and flow context of the previous pass; a change forces a full pass. */
|
|
762
|
+
context: string;
|
|
763
|
+
/**
|
|
764
|
+
* Producer of the previous pass, compared beside {@link context} rather than embedded in
|
|
765
|
+
* it: the producer carries the content-control token, which runs to kilobytes on a
|
|
766
|
+
* control-heavy document, and embedding it copied that token into every section's context
|
|
767
|
+
* string on every pass. Identity-stable when unchanged, so the comparison is a pointer
|
|
768
|
+
* check.
|
|
769
|
+
*
|
|
770
|
+
* Optional so a session literal built before this field existed still type-checks; an
|
|
771
|
+
* absent producer never matches and forces a conservative full pass.
|
|
772
|
+
*/
|
|
773
|
+
producer?: string;
|
|
774
|
+
/**
|
|
775
|
+
* Whether the previous pass read page PARITY: even/odd header variants, or an anchored
|
|
776
|
+
* drawing positioned against an inside/outside frame or alignment.
|
|
777
|
+
*
|
|
778
|
+
* The document page index is not part of {@link context} — page numbers re-project at
|
|
779
|
+
* finalize and page shells renumber at remap — so parity is the only page-position input
|
|
780
|
+
* that can invalidate a section's layout. Tracked separately so the common document pays
|
|
781
|
+
* nothing for a start-index shift.
|
|
782
|
+
*/
|
|
783
|
+
parityDependent: boolean;
|
|
784
|
+
/** Parity (0/1) of the document page index this session's layout started on. */
|
|
785
|
+
startPageParity: number;
|
|
786
|
+
/**
|
|
787
|
+
* The section's memoized prepass (prepared blocks, cache keys, flow keys, document
|
|
788
|
+
* order), reused verbatim while its inputs are unchanged. Opaque here: the shape is
|
|
789
|
+
* owned by `semantic-layout.ts`, which is the only reader and writer.
|
|
790
|
+
*/
|
|
791
|
+
prepass: unknown;
|
|
792
|
+
/**
|
|
793
|
+
* The notes pass's memoized state (reference hits, mark contexts, per-page attach
|
|
794
|
+
* results), reused while its inputs are unchanged. Opaque here: the shape is owned by
|
|
795
|
+
* `note-pagination.ts`, which is the only reader and writer.
|
|
796
|
+
*/
|
|
797
|
+
notes: unknown;
|
|
798
|
+
/** Line counter at the start of the previous pass, for translating reused section counts. */
|
|
799
|
+
startLineCounter: number;
|
|
800
|
+
/**
|
|
801
|
+
* Line counter after the last block of the previous pass.
|
|
802
|
+
*
|
|
803
|
+
* Multi-section orchestration threads a global line counter across sections; early-exit
|
|
804
|
+
* paths (unchanged / converged) must report this rather than the resume cursor.
|
|
805
|
+
*/
|
|
806
|
+
endLineCounter: number;
|
|
807
|
+
/**
|
|
808
|
+
* Flow state after the last block of the previous pass, for a section that CONTINUES
|
|
809
|
+
* onto this one's last sheet (`w:type="continuous"`).
|
|
810
|
+
*
|
|
811
|
+
* `endCursorY` is the used height of that sheet's content column; `endSpaceAfter` is the
|
|
812
|
+
* trailing paragraph spacing still eligible for adjacent-spacing collapse. Reported by
|
|
813
|
+
* the early-exit paths (unchanged / converged) for the same reason as
|
|
814
|
+
* {@link endLineCounter}: the resume cursor is not the end of the flow.
|
|
815
|
+
*/
|
|
816
|
+
endCursorY: number;
|
|
817
|
+
endSpaceAfter: number;
|
|
818
|
+
/** Whether the last page of that pass was still open (no trailing page break). */
|
|
819
|
+
endsOpenPage: boolean;
|
|
820
|
+
stats: LayoutSessionStats;
|
|
821
|
+
/**
|
|
822
|
+
* Column-height limit chosen by the last balanced multi-column pass, or null when the
|
|
823
|
+
* last pass did not balance.
|
|
824
|
+
*
|
|
825
|
+
* Lets the next pass try the remembered limit FIRST: an unchanged balanced section
|
|
826
|
+
* early-exits on that single attempt instead of re-running the natural pass and the
|
|
827
|
+
* whole balance search every time.
|
|
828
|
+
*/
|
|
829
|
+
balanceLimit: number | null;
|
|
830
|
+
/** Present when the last pass was multi-section; child sessions live here. */
|
|
831
|
+
multi: MultiSectionLayoutState | null;
|
|
832
|
+
/**
|
|
833
|
+
* Page-bottom footnote reserves from the last published notes layout.
|
|
834
|
+
*
|
|
835
|
+
* Seeded into the next {@link layoutSemanticDocumentWithNotes} call so the first body
|
|
836
|
+
* pass already matches the reserved context key — without this, every notes document
|
|
837
|
+
* starts empty, mismatches, and throws away the session on reflow.
|
|
838
|
+
*/
|
|
839
|
+
notePageBottomReserves: ReadonlyMap<number, number> | null;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* A layout session, retained across revisions by the caller.
|
|
843
|
+
*/
|
|
844
|
+
declare function createLayoutSession(): LayoutSession;
|
|
845
|
+
|
|
846
|
+
/** Collect story paragraphs, preserving table depth limits across immutable row reuse. */
|
|
847
|
+
declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Horizontal marker box inside the first-line indent slot.
|
|
851
|
+
*
|
|
852
|
+
* Coordinates are relative to the same origin as paragraph content (`indent.left` is the
|
|
853
|
+
* text start). Returns null when there is nothing to paint.
|
|
854
|
+
*
|
|
855
|
+
* `w:hanging` and a positive `w:firstLine` are one mutually exclusive slot
|
|
856
|
+
* (§17.3.1.10, §17.3.1.12), so the marker has two placements, not an interaction:
|
|
857
|
+
* a hanging level puts the marker BEFORE the text start (`left - hanging`); a
|
|
858
|
+
* positive-firstLine level puts it AFTER (`left + firstLine`) — the standard legal
|
|
859
|
+
* shape `w:ind w:left="0" w:firstLine="720"` numbers at 0.5" while continuation
|
|
860
|
+
* lines return to the margin. Reading only the hanging model painted every such
|
|
861
|
+
* marker at the left margin.
|
|
862
|
+
*/
|
|
863
|
+
declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
|
|
864
|
+
x: number;
|
|
865
|
+
y: number;
|
|
866
|
+
width: number;
|
|
867
|
+
height: number;
|
|
868
|
+
} | null;
|
|
869
|
+
|
|
870
|
+
/** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
|
|
871
|
+
declare const MAX_STYLE_BASED_ON_DEPTH = 32;
|
|
872
|
+
|
|
873
|
+
/** Far above anything Word authors (its UI caps at 63) while keeping allocation bounded. */
|
|
874
|
+
declare const MAX_TABLE_COLUMNS = 1024;
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* `w:tblW` / `w:tcW` / `w:wBefore` (CT_TblWidth, 17.4.63 / 17.4.71 / 17.4.86): a PREFERRED
|
|
878
|
+
* width plus the unit it is stated in. Preferred is the operative word — it is what the
|
|
879
|
+
* producer asked for, not what the table resolved to.
|
|
880
|
+
*
|
|
881
|
+
* `pct` is stated in fiftieths of a percent (5000 = 100%) by Word, and in the `"50%"`
|
|
882
|
+
* string form of `ST_Percentage` by others; both are read. `auto` and `nil` carry no width.
|
|
883
|
+
*/
|
|
884
|
+
type PreferredWidthType = 'dxa' | 'pct' | 'auto' | 'nil';
|
|
885
|
+
/**
|
|
886
|
+
* `w:tblW` / `w:tcW` — a requested width, whose UNIT depends on its type.
|
|
887
|
+
*
|
|
888
|
+
* Points for `dxa`, percent for `pct`, and zero for `auto`/`nil`. Reading `value` without `type`
|
|
889
|
+
* is always wrong.
|
|
890
|
+
*/
|
|
891
|
+
interface PreferredWidth {
|
|
892
|
+
readonly type: PreferredWidthType;
|
|
893
|
+
/** POINTS for `dxa`, PERCENT for `pct` (0–100, or up to 655.34 for a table), 0 otherwise. */
|
|
894
|
+
readonly value: number;
|
|
895
|
+
}
|
|
896
|
+
/** The frozen "no preferred width" value — what a table or cell that declares none resolves to. */
|
|
897
|
+
declare const AUTO_PREFERRED_WIDTH: PreferredWidth;
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* The uniform 3 pt (60 twip) inset this lane used to apply on every side.
|
|
901
|
+
*
|
|
902
|
+
* Word's defaults are not uniform — see {@link DEFAULT_CELL_MARGINS} — so nothing resolves
|
|
903
|
+
* against this any more. Kept because it is published.
|
|
904
|
+
*/
|
|
905
|
+
declare const CELL_PAD = 3;
|
|
906
|
+
/** Resolved cell padding in points, after the table default and any per-cell override. */
|
|
907
|
+
interface CellMarginsPt {
|
|
908
|
+
readonly top: number;
|
|
909
|
+
readonly right: number;
|
|
910
|
+
readonly bottom: number;
|
|
911
|
+
readonly left: number;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Traditional default table-style padding: 0 vertical, 108 twips horizontal.
|
|
915
|
+
*
|
|
916
|
+
* Kept as a published constant. Authored styles can supply these values; omitted
|
|
917
|
+
* margins instead resolve to a 10-twip horizontal application fallback.
|
|
918
|
+
*/
|
|
919
|
+
declare const DEFAULT_CELL_MARGINS: CellMarginsPt;
|
|
920
|
+
|
|
921
|
+
/** `w:vAlign` — where a cell's content sits when the row is taller than the content. */
|
|
922
|
+
type CellVerticalAlign = 'top' | 'center' | 'bottom';
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* What autofit needs from layout to measure each cell the way layout paints it.
|
|
926
|
+
* @public
|
|
927
|
+
*/
|
|
928
|
+
interface TableAutofitContext {
|
|
929
|
+
/** Measures text exactly as line breaking does. */
|
|
930
|
+
readonly measurer: TextMeasurer;
|
|
931
|
+
/** List items by paragraph id: a list level indents its paragraph. */
|
|
932
|
+
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
933
|
+
/** Resolves inline pictures, so a picture column keeps the width the picture paints at. */
|
|
934
|
+
readonly inlineDrawingLayout?: InlineDrawingLayoutContext;
|
|
935
|
+
/** Field and note context, so the minimum measures the text layout paints. */
|
|
936
|
+
readonly fields?: AutofitFieldContext;
|
|
937
|
+
/** The document's default tab interval, where an unwrapped line's tabs advance to. */
|
|
938
|
+
readonly defaultTabStopPt?: number;
|
|
939
|
+
/**
|
|
940
|
+
* A token that changes whenever a paragraph's projected text can change: the same tokens
|
|
941
|
+
* the paragraph break cache keys on. Minimums are cached only when this and
|
|
942
|
+
* {@link TableAutofitContext.passToken} are both given.
|
|
943
|
+
*/
|
|
944
|
+
readonly paragraphToken?: (paragraph: OoxmlElement) => string;
|
|
945
|
+
/**
|
|
946
|
+
* A token over every other input the minimum reads: the drawing context and every member of
|
|
947
|
+
* {@link TableAutofitContext.fields}. It must change when any of them changes. List state is
|
|
948
|
+
* not part of it: {@link TableAutofitContext.paragraphToken} must carry each paragraph's
|
|
949
|
+
* list tokens.
|
|
950
|
+
*/
|
|
951
|
+
readonly passToken?: string;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* The field and note context cell layout projects paragraphs with.
|
|
955
|
+
* @public
|
|
956
|
+
*/
|
|
957
|
+
interface AutofitFieldContext {
|
|
958
|
+
/** The page a header or footer paints on, for its PAGE and NUMPAGES results. */
|
|
959
|
+
readonly pageContext?: FieldPageContext;
|
|
960
|
+
/** Footnote and endnote reference marks, as numbered for this pass. Treated as immutable. */
|
|
961
|
+
readonly noteMarks?: NoteMarkContext;
|
|
962
|
+
/** Package properties that AUTHOR, TITLE, and similar fields paint. */
|
|
963
|
+
readonly documentProperties?: DocumentProperties;
|
|
964
|
+
/** Body page-field placeholders; `false` paints cached results instead. */
|
|
965
|
+
readonly bodyPageFields?: BodyPageFieldContext | false;
|
|
966
|
+
/** Refreshed cross-reference results. */
|
|
967
|
+
readonly refFields?: RefFieldContext;
|
|
968
|
+
/** Paint field instructions instead of results. */
|
|
969
|
+
readonly showFieldCodes?: boolean;
|
|
970
|
+
/** Field-code ranges by paragraph id, when instructions are shown. */
|
|
971
|
+
readonly fieldCodeRanges?: FieldCodeRanges;
|
|
972
|
+
/** Table-of-contents link styling by paragraph id. */
|
|
973
|
+
readonly tocLinkStyleRanges?: TocLinkRanges;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Where a table's left edge sits inside the box that contains it.
|
|
978
|
+
*
|
|
979
|
+
* Ordinary left-aligned tables start at their indent, which may be negative and is not
|
|
980
|
+
* limited to the remaining width. Centered and right-aligned left-to-right tables use the
|
|
981
|
+
* remaining width and ignore the indent. `outerRuleOffsetPt` then moves the grid inward by
|
|
982
|
+
* half the outer side rule where the table puts that rule's outer edge on the aligned edge.
|
|
983
|
+
* For a supported mode-14 fixed table, that offset instead aligns the first cell's content.
|
|
984
|
+
* A legacy content-aligned table instead puts the content edge of its leading or trailing
|
|
985
|
+
* cell on the aligned edge of the text column; its indent still applies from the leading edge.
|
|
986
|
+
*/
|
|
987
|
+
declare function tableOriginX(structure: SemanticTableStructure, containerWidthPt: number): number;
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Layout-time nesting ceiling. Parse-time depth (MAX_DEPTH = 256 XML levels) alone still
|
|
991
|
+
* admits ~80 levels of `w:tbl` recursion into the layout walk; deeper tables render as an
|
|
992
|
+
* empty cell box rather than recursing.
|
|
993
|
+
*/
|
|
994
|
+
declare const MAX_TABLE_NESTING = 16;
|
|
995
|
+
/**
|
|
996
|
+
* Soft ceiling on an authored `w:trHeight` (~22"). Hostile `w:val` otherwise becomes a
|
|
997
|
+
* multi-page row that every pagination preflight and cell box inherits.
|
|
998
|
+
*/
|
|
999
|
+
declare const MAX_TABLE_ROW_HEIGHT_PT: number;
|
|
1000
|
+
/**
|
|
1001
|
+
* `w:trPr/w:trHeight` (17.4.81) resolved for layout. Points leave the reader already —
|
|
1002
|
+
* twips convert once here, matching every other table geometry boundary.
|
|
1003
|
+
*
|
|
1004
|
+
* Word quirk (matches Form025U and Word's UI export): a present `@w:val` with an omitted
|
|
1005
|
+
* `@w:hRule` is treated as `atLeast`, not ECMA's `auto`. Explicit `auto` still ignores val.
|
|
1006
|
+
*/
|
|
1007
|
+
type TableRowHeightRule = 'auto' | 'atLeast' | 'exact';
|
|
1008
|
+
/**
|
|
1009
|
+
* `w:trHeight` — a row's height rule and its value.
|
|
1010
|
+
*
|
|
1011
|
+
* `auto` carries no value at all, which is why this is a union rather than a rule plus an
|
|
1012
|
+
* optional number.
|
|
1013
|
+
*/
|
|
1014
|
+
type TableRowHeight = {
|
|
1015
|
+
readonly rule: 'auto';
|
|
1016
|
+
} | {
|
|
1017
|
+
readonly rule: 'atLeast' | 'exact';
|
|
1018
|
+
readonly valuePt: number;
|
|
1019
|
+
};
|
|
1020
|
+
/** `w:tblPr/w:jc` (17.4.29, ST_JcTable): where the table sits within the text column. */
|
|
1021
|
+
type TableAlignment = 'left' | 'center' | 'right';
|
|
1022
|
+
/**
|
|
1023
|
+
* One cell in the resolved table structure.
|
|
1024
|
+
*
|
|
1025
|
+
* `gridSpan` is clamped at READ time and layout never re-derives it — the value comes from a file
|
|
1026
|
+
* and would otherwise be a loop bound an attacker controls.
|
|
1027
|
+
*/
|
|
1028
|
+
interface SemanticTableCell {
|
|
1029
|
+
readonly revisionShading?: 'inserted' | 'deleted' | 'merged' | 'split';
|
|
1030
|
+
readonly revisionShadingAuthor?: string;
|
|
1031
|
+
readonly id: string;
|
|
1032
|
+
/** Derived content-edge geometry for a verified legacy percentage-width parent table. */
|
|
1033
|
+
readonly legacyContentAlignment?: true;
|
|
1034
|
+
/** Resolved simple side rules centered on a legacy absolute-width table grid. */
|
|
1035
|
+
readonly centeredSideRules?: true;
|
|
1036
|
+
/** Simple side rules painted centered on the grid line, without moving the content edge. */
|
|
1037
|
+
readonly centeredSidePaint?: true;
|
|
1038
|
+
/** Clamped to [1, MAX_TABLE_COLUMNS] at read time; layout never re-derives it. */
|
|
1039
|
+
readonly gridSpan: number;
|
|
1040
|
+
/** Physical grid column after width/style resolution and the bidiVisual projection. */
|
|
1041
|
+
readonly gridColumn: number;
|
|
1042
|
+
/** Stored grid index when bidiVisual maps this cell into a physical RTL grid. */
|
|
1043
|
+
readonly logicalGridColumn?: number;
|
|
1044
|
+
/** Canonical `w:gridCol` node id for this cell's start column, when the grid is authored. */
|
|
1045
|
+
readonly gridColumnId?: string;
|
|
1046
|
+
/** A vMerge cell that is not the restart continues the cell above: box, no content. */
|
|
1047
|
+
readonly vMergeContinue: boolean;
|
|
1048
|
+
/** `w:hideMark` excludes the end-of-cell glyph from row sizing. */
|
|
1049
|
+
readonly hideEndMark?: boolean;
|
|
1050
|
+
/** `w:vAlign` — defaults to top when omitted/unrecognised. */
|
|
1051
|
+
readonly vAlign: CellVerticalAlign;
|
|
1052
|
+
/** `w:textDirection`; unsupported values keep horizontal layout. */
|
|
1053
|
+
readonly textDirection: 'horizontal' | 'btLr';
|
|
1054
|
+
/** Resolved per-side margins (tcMar over tblCellMar over the table style over Word's default). */
|
|
1055
|
+
readonly margins: CellMarginsPt;
|
|
1056
|
+
/** Three-state authored `tcBorders` (omitted / none / edge). */
|
|
1057
|
+
readonly borders: CellBorderBox;
|
|
1058
|
+
/** The row's `w:tblPrEx` table borders, which replace the table's own for this cell. */
|
|
1059
|
+
readonly rowTableBorders?: TableBorderBox;
|
|
1060
|
+
/** Resolved incident edges used for content clearance, preserving authored border provenance. */
|
|
1061
|
+
readonly contentBorders?: CellBorderBox;
|
|
1062
|
+
/** The resolved cell (including a vertical merge) ends at the authored table bottom. */
|
|
1063
|
+
readonly contentBottomIsOuter?: boolean;
|
|
1064
|
+
/** Clearance this AUTHORED ROW reserves above its content for its own top rule, in points. */
|
|
1065
|
+
readonly topBandClearancePt?: number;
|
|
1066
|
+
/** The CELL declares `w:tcBorders/w:top` as `nil`, rather than a table style doing so. */
|
|
1067
|
+
readonly suppressesTopBand?: true;
|
|
1068
|
+
/** Row-local clearance in points for authored minima, before vMerge combines content boxes. */
|
|
1069
|
+
readonly minimumContentInsets?: {
|
|
1070
|
+
readonly top: number;
|
|
1071
|
+
readonly bottom: number;
|
|
1072
|
+
};
|
|
1073
|
+
/** Validated 6-hex shading fill, absent for none/auto. */
|
|
1074
|
+
readonly shading?: string;
|
|
1075
|
+
/**
|
|
1076
|
+
* `w:tcW` — the width this cell asked for, as authored.
|
|
1077
|
+
*
|
|
1078
|
+
* Published for consumers that need the cell's own statement (a column-resize handle has
|
|
1079
|
+
* to write back to it). Column geometry is NOT derived from this field: the resolver works
|
|
1080
|
+
* from a flat claim list built in the same pass, because resolving a column means looking
|
|
1081
|
+
* at every cell that covers it across every row, not at one cell at a time. Read
|
|
1082
|
+
* `columnWidthsPt` for what the table actually laid out.
|
|
1083
|
+
*/
|
|
1084
|
+
readonly preferredWidth: PreferredWidth;
|
|
1085
|
+
/**
|
|
1086
|
+
* What the table style says about this cell's paragraphs and runs (17.7.6.6) — a header
|
|
1087
|
+
* row's bold and centring live here, not in the cell's own properties.
|
|
1088
|
+
*/
|
|
1089
|
+
readonly styleFormatting: TableCellStyleFormatting;
|
|
1090
|
+
/** Block children in reading order, with content-control wrappers flattened. */
|
|
1091
|
+
readonly blocks: readonly OoxmlElement[];
|
|
1092
|
+
}
|
|
1093
|
+
/** One row in the resolved structure: its cells, its height rule, and any row-level revision. */
|
|
1094
|
+
interface SemanticTableRow {
|
|
1095
|
+
readonly id: string;
|
|
1096
|
+
/** Pending Word row insertion/deletion authored in `w:trPr`. */
|
|
1097
|
+
readonly revisionKind?: 'insert' | 'delete';
|
|
1098
|
+
/**
|
|
1099
|
+
* The `w:trPr/w:ins|w:del` attribution, carried with the kind so a painted row can say
|
|
1100
|
+
* WHOSE pending decision it is — the review model addresses the decision by exactly this
|
|
1101
|
+
* `(id, author, date)` triple, and a surface with only the kind could highlight the row
|
|
1102
|
+
* but never open its card.
|
|
1103
|
+
*/
|
|
1104
|
+
readonly revisionId?: string;
|
|
1105
|
+
readonly revisionAuthor?: string;
|
|
1106
|
+
readonly revisionDate?: string;
|
|
1107
|
+
/** The row insertion a resolved view kept the row through; see the fragment record. */
|
|
1108
|
+
readonly changeSites?: readonly RevisionAttribution[];
|
|
1109
|
+
/** `w:trPr/w:tblHeader` — the row repeats atop each page the table continues onto. */
|
|
1110
|
+
readonly isHeader: boolean;
|
|
1111
|
+
/**
|
|
1112
|
+
* `w:trPr/w:cantSplit` — the row must stay on one page. When no page can hold it, the
|
|
1113
|
+
* row starts on a fresh page and splits there, so its content is kept.
|
|
1114
|
+
*/
|
|
1115
|
+
readonly cantSplit: boolean;
|
|
1116
|
+
/** `w:trPr/w:trHeight` — auto / atLeast floor / exact (clipped) row height. */
|
|
1117
|
+
readonly height: TableRowHeight;
|
|
1118
|
+
readonly cells: readonly SemanticTableCell[];
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* A table resolved into a rectangular grid: column widths, rows, and the widths it asked for.
|
|
1122
|
+
*
|
|
1123
|
+
* The grid is normalized here so layout never has to reconcile `w:gridCol` against actual cell
|
|
1124
|
+
* spans — vertical merges and column spans are already accounted for.
|
|
1125
|
+
*/
|
|
1126
|
+
interface SemanticTableStructure {
|
|
1127
|
+
/** Whether stored columns and horizontal table properties display right to left. */
|
|
1128
|
+
readonly bidiVisual?: true;
|
|
1129
|
+
readonly columnWidthsPt: readonly number[];
|
|
1130
|
+
readonly rows: readonly SemanticTableRow[];
|
|
1131
|
+
/** Verified pre-2013 percentage-width inline table; derived, never serialized. */
|
|
1132
|
+
readonly legacyContentAlignment?: true;
|
|
1133
|
+
/** A legacy content-aligned table whose outer trailing edge, not its content, meets the
|
|
1134
|
+
* trailing text edge: a top-level table in a text box. Derived, never serialized. */
|
|
1135
|
+
readonly legacyTrailingOuterEdge?: true;
|
|
1136
|
+
/**
|
|
1137
|
+
* Derived, never serialized: how far the grid moves from the aligned table edge, in points.
|
|
1138
|
+
* A supported mode-14 fixed table shifts by its leading cell margin in the other direction.
|
|
1139
|
+
* A compatibility-mode-15 left- or right-aligned table puts the outer edge of its side rule
|
|
1140
|
+
* on that edge, so its first grid line sits half a rule inward. Positive moves right.
|
|
1141
|
+
*/
|
|
1142
|
+
readonly outerRuleOffsetPt?: number;
|
|
1143
|
+
/** `w:tblPr/w:tblW` — the width the table asked for. */
|
|
1144
|
+
readonly tableWidth: PreferredWidth;
|
|
1145
|
+
/**
|
|
1146
|
+
* `w:tblInd` (17.4.50) in points — "this indentation should shift the table into the text
|
|
1147
|
+
* margin by the specified amount". Applies to a left-aligned table; `w:jc` decides the
|
|
1148
|
+
* placement outright for the other two. Negative on a top-level table, which the indent
|
|
1149
|
+
* pulls into the leading margin; never negative on a nested table.
|
|
1150
|
+
*/
|
|
1151
|
+
readonly indentPt: number;
|
|
1152
|
+
/** `w:tblPr/w:jc` (17.4.29) — where the table sits in the text column. */
|
|
1153
|
+
readonly alignment: TableAlignment;
|
|
1154
|
+
/**
|
|
1155
|
+
* `w:tblPr/w:tblpPr` (17.4.57) — present when the table is positioned against an anchor
|
|
1156
|
+
* box. Placement then comes from {@link tableFloatOriginX} rather than `w:jc`/`w:tblInd`.
|
|
1157
|
+
*/
|
|
1158
|
+
readonly float?: TableFloatPosition;
|
|
1159
|
+
/**
|
|
1160
|
+
* `w:tblCellSpacing` (17.4.45) in points: the space on each side of every cell. Cells sit
|
|
1161
|
+
* twice this apart and from the table edge; the gaps come out of the table width, and each
|
|
1162
|
+
* column gives up its share in proportion (`table-cell-spacing.ts`). Rows do not yet get
|
|
1163
|
+
* the matching gaps above and below them, so a spaced table's rows stay as tall as they
|
|
1164
|
+
* would be without it.
|
|
1165
|
+
*/
|
|
1166
|
+
readonly cellSpacingPt: number;
|
|
1167
|
+
/**
|
|
1168
|
+
* `w:tblPr/w:tblLayout/@w:type="fixed"` (17.4.52 — 17.4.53 is the `w:tblPrEx` exception
|
|
1169
|
+
* variant, not this element). Fixed layout takes the grid as final;
|
|
1170
|
+
* anything else is autofit, which in Word never renders wider than the text column.
|
|
1171
|
+
*/
|
|
1172
|
+
readonly layoutFixed: boolean;
|
|
1173
|
+
/** Table-level `tblBorders` (three-state, including insideH/insideV). */
|
|
1174
|
+
readonly tableBorders: TableBorderBox;
|
|
1175
|
+
/** Table-level `tblCellMar` defaults (per-side, Word's own default when a side is omitted). */
|
|
1176
|
+
readonly defaultMargins: CellMarginsPt;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Read one typed table node into a bounded structure, or null when the node is not a
|
|
1180
|
+
* typed table or sits beyond the nesting ceiling.
|
|
1181
|
+
*/
|
|
1182
|
+
declare function readTableStructure(table: OoxmlNode, contentWidthPt: number, depth: number, styleCascade?: StyleCascadeTable,
|
|
1183
|
+
/** Which revisions the view resolves away; only the proposed result performs the join. */
|
|
1184
|
+
displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter, compatibilityMode?: number,
|
|
1185
|
+
/** Widens autofit columns to their content minimums; layout passes its measurer. */
|
|
1186
|
+
autofit?: TableAutofitContext,
|
|
1187
|
+
/** The table is a top-level table of a text box story. */
|
|
1188
|
+
textBox?: boolean): SemanticTableStructure | null;
|
|
1189
|
+
|
|
1190
|
+
/** Paragraph ids in document order, deduplicated across fragments. */
|
|
1191
|
+
declare function documentOrder(layout: SemanticLayout): string[];
|
|
1192
|
+
/**
|
|
1193
|
+
* Paragraph ids of EVERY story the layout paints, in the order they sit on the page.
|
|
1194
|
+
*
|
|
1195
|
+
* Body, then each page's header and footer, then its note areas — per page, so a story's own
|
|
1196
|
+
* paragraphs stay adjacent and in reading order. That is all any caller needs: a selection
|
|
1197
|
+
* cannot span two stories (the engine refuses one), so only the order WITHIN a story is ever
|
|
1198
|
+
* compared, and this gets that right for all of them at once.
|
|
1199
|
+
*
|
|
1200
|
+
* What a caller passes when it has no story in hand — `selectionRects` and
|
|
1201
|
+
* `spansInSelection` both REQUIRE an order rather than defaulting to one, deliberately.
|
|
1202
|
+
* {@link documentOrder} is the body alone, and
|
|
1203
|
+
* using it as a default is what let selection reads silently answer about the wrong story —
|
|
1204
|
+
* two paragraphs selected in a header both ranked -1, the walk gave up, and the run properties
|
|
1205
|
+
* came back short. A caller that DOES know its story should still pass that story's order:
|
|
1206
|
+
* it is smaller, and it cannot match a paragraph the caret is not among.
|
|
1207
|
+
*/
|
|
1208
|
+
declare function everyStoryOrder(layout: SemanticLayout): string[];
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* The physical rectangles covering a selection, including disjoint bands within bidi lines.
|
|
1212
|
+
*
|
|
1213
|
+
* BODY fragments only, and a selection outside the body paints nothing. That is a real gap —
|
|
1214
|
+
* a retained pin in a header shows no highlight, and a comment anchored in one draws no band
|
|
1215
|
+
* even though the review queue lists it — but it is the honest shape of what this can answer
|
|
1216
|
+
* today. Widening the walk to the other stories is not enough on its own: header and footer
|
|
1217
|
+
* fragments carry positions relative to their own story box, note fragments relative to their
|
|
1218
|
+
* note area, and one header story object is attached to EVERY page it applies to. Fed straight
|
|
1219
|
+
* into a page-content-relative rect, those produce a band per page, at coordinates belonging
|
|
1220
|
+
* to a different box. Measured, that put a header comment's band on all six pages of a
|
|
1221
|
+
* document at the top-left of the body text. Painting nothing is wrong; painting over the
|
|
1222
|
+
* wrong words is worse, so the walk stays here until the geometry is carried with it.
|
|
1223
|
+
*/
|
|
1224
|
+
declare function selectionRects(layout: SemanticLayout, selection: SemanticSelection,
|
|
1225
|
+
/**
|
|
1226
|
+
* Reading order of the ACTIVE story.
|
|
1227
|
+
*
|
|
1228
|
+
* REQUIRED, for the reason `spansInSelection` gives: any default is one story's order, and
|
|
1229
|
+
* it is wrong for every caret outside that story. A caller with no story in hand passes
|
|
1230
|
+
* {@link everyStoryOrder}.
|
|
1231
|
+
*/
|
|
1232
|
+
order: readonly string[],
|
|
1233
|
+
/**
|
|
1234
|
+
* For EXACT edge x positions inside a span. Layout no longer publishes eager per-character
|
|
1235
|
+
* caret edges, so without a measurer an intra-span boundary interpolates across the span's
|
|
1236
|
+
* advance — visibly wrong in a proportional face. Pass the layout's measurer.
|
|
1237
|
+
*/
|
|
1238
|
+
measurer?: TextMeasurer): SelectionRect[];
|
|
1239
|
+
/** A model range to highlight, and the key the caller knows it by. */
|
|
1240
|
+
interface KeyedRange {
|
|
1241
|
+
readonly key: string;
|
|
1242
|
+
readonly from: SemanticPosition;
|
|
1243
|
+
readonly to: SemanticPosition;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Rectangles for MANY ranges in ONE pass over the lines.
|
|
1247
|
+
*
|
|
1248
|
+
* Not `selectionRects` in a loop. That walks every page, fragment and line per range, and a
|
|
1249
|
+
* contract with two hundred comments would re-walk the whole document two hundred times on
|
|
1250
|
+
* every layout — the highlight would cost more than the layout it decorates. One pass tests
|
|
1251
|
+
* each line against every range instead, which is the same work a single selection does.
|
|
1252
|
+
*/
|
|
1253
|
+
declare function keyedRangeRects(layout: SemanticLayout, ranges: readonly KeyedRange[],
|
|
1254
|
+
/**
|
|
1255
|
+
* Pages to measure, or every page when absent.
|
|
1256
|
+
*
|
|
1257
|
+
* A band that is not on screen is not painted, so measuring it is pure cost — and it is
|
|
1258
|
+
* cost paid per keystroke, because an edit republishes the layout. Bounding this to the
|
|
1259
|
+
* materialized pages is what keeps typing in a heavily reviewed document as fast as
|
|
1260
|
+
* typing in a clean one.
|
|
1261
|
+
*/
|
|
1262
|
+
pages?: ReadonlySet<number>,
|
|
1263
|
+
/** For exact intra-span edge positions (see {@link selectionRects}). */
|
|
1264
|
+
measurer?: TextMeasurer): Map<string, SelectionRect[]>;
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* The next word boundary from `offset`, in `direction`.
|
|
1268
|
+
*
|
|
1269
|
+
* Word-LEFT skips any whitespace immediately behind the caret and then the word behind that,
|
|
1270
|
+
* which makes repeated presses walk words rather than alternate with the preceding space.
|
|
1271
|
+
*
|
|
1272
|
+
* `stops` are offsets a word may not run THROUGH even when the characters either side are
|
|
1273
|
+
* word characters. They exist because the text this walks is what the VIEW is showing, and a
|
|
1274
|
+
* view can show two versions of the document side by side: struck text and the text proposed
|
|
1275
|
+
* to replace it abut with no separator, so `ALL CAPS` deleted and `fsdfsd` inserted read as
|
|
1276
|
+
* the single word `CAPSfsdfsd`. Those characters are never adjacent in any one version, and a
|
|
1277
|
+
* double-click that took both selected across a decision the reader had not made. See
|
|
1278
|
+
* `deletedTextBoundaries`.
|
|
1279
|
+
*/
|
|
1280
|
+
declare function wordBoundary(text: string, offset: number, direction: -1 | 1, stops?: ReadonlySet<number>): number;
|
|
1281
|
+
|
|
1282
|
+
/**
|
|
1283
|
+
* Where an INSERT aimed at this position actually lands: past any deletion it sits inside.
|
|
1284
|
+
*
|
|
1285
|
+
* The caret may rest anywhere in struck text — Word's rule, and the tracked lane's
|
|
1286
|
+
* (`tree-op-tracked.ts`): all-markup shows the words, so the reader can put the caret
|
|
1287
|
+
* between two of them. What may NOT happen is new content landing inside the `w:del`,
|
|
1288
|
+
* where it would serialize as `w:t` under a wrapper that requires `w:delText` and be taken
|
|
1289
|
+
* down by an accept of someone else's deletion. A deletion stays contiguous, so the words
|
|
1290
|
+
* go after it — the order a replacement reads in.
|
|
1291
|
+
*
|
|
1292
|
+
* RANGE endpoints are not this function's business: a drag may legitimately cover deleted
|
|
1293
|
+
* text, so callers adjust only collapsed insertion points.
|
|
1294
|
+
*/
|
|
1295
|
+
declare function positionPastDeletion(layout: SemanticLayout, position: SemanticPosition): SemanticPosition;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* The caret position nearest a point, in PAGE-CONTENT coordinates.
|
|
1299
|
+
*
|
|
1300
|
+
* Never returns null for a point inside the document: a click in the margin, past the end of
|
|
1301
|
+
* a line, or below the last line still has an obvious intended caret, and refusing to answer
|
|
1302
|
+
* would make those clicks do nothing.
|
|
1303
|
+
*
|
|
1304
|
+
* The rules live in `semantic-hit-test.ts`, which answers with the cell address and the
|
|
1305
|
+
* on-glyphs flag a pointer controller needs too; this keeps the geometry-only shape for
|
|
1306
|
+
* callers that want nothing else.
|
|
1307
|
+
*/
|
|
1308
|
+
declare function hitTestSemantic(layout: SemanticLayout, point: {
|
|
1309
|
+
readonly x: number;
|
|
1310
|
+
readonly y: number;
|
|
1311
|
+
readonly pageIndex?: number;
|
|
1312
|
+
}): CaretGeometry | null;
|
|
1313
|
+
/**
|
|
1314
|
+
* Innermost content-control boundary at a page-content point, or null outside every control.
|
|
1315
|
+
*
|
|
1316
|
+
* Prefers the deepest nesting depth when nested boundaries share geometry.
|
|
1317
|
+
*/
|
|
1318
|
+
declare function contentControlAtSemantic(layout: SemanticLayout, point: {
|
|
1319
|
+
readonly x: number;
|
|
1320
|
+
readonly y: number;
|
|
1321
|
+
readonly pageIndex?: number;
|
|
1322
|
+
}): ContentControlBoundaryRecord$1 | null;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Rewrite private-use symbol-font codepoints to their Unicode equivalents.
|
|
1326
|
+
*
|
|
1327
|
+
* Applies only to a known symbol-encoded family, only to the U+F020–U+F0FF range, and only
|
|
1328
|
+
* to codepoints with an exact Unicode counterpart — anything else is returned untouched, so
|
|
1329
|
+
* an unrecognised glyph is never replaced by a wrong one. Returns the input string by
|
|
1330
|
+
* identity when nothing changes.
|
|
1331
|
+
*
|
|
1332
|
+
* `isFamilyAvailable` is the escape hatch: a host that KNOWS the authored font is present
|
|
1333
|
+
* should return true, and the file's own codepoint is kept so the real typeface draws it.
|
|
1334
|
+
* Absent, the fallback applies, because an unmapped PUA codepoint renders as a tofu box in
|
|
1335
|
+
* every font that is not the symbol-encoded original.
|
|
1336
|
+
*
|
|
1337
|
+
* Called without the oracle it is also the EXTRACTION answer: the text a symbol glyph should
|
|
1338
|
+
* read as when the drawn glyph stays the authored one. A PDF exporter maps the drawn private
|
|
1339
|
+
* codepoint back through this for its `ToUnicode` table, so copying a Word bullet out of the
|
|
1340
|
+
* page yields a bullet rather than a private-use character.
|
|
1341
|
+
*
|
|
1342
|
+
* @public
|
|
1343
|
+
*/
|
|
1344
|
+
declare function mapSymbolPuaText(text: string, fontFamily: string | null | undefined, isFamilyAvailable?: (family: string) => boolean): string;
|
|
1345
|
+
|
|
1346
|
+
/** Convert signed font units by an exact rational multiplier using the declared tie rule. */
|
|
1347
|
+
declare const roundFontUnitToFixedPoint: (fontUnits: number, denominator: number, numerator: number, mode: FixedPointRoundingMode) => FixedPoint;
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* The exact HarfBuzz build this engine shapes against.
|
|
1351
|
+
*
|
|
1352
|
+
* Pinned and verified at load: a runtime reporting a different version is REFUSED rather than
|
|
1353
|
+
* used, because glyph positioning can change between releases and a cached measurement taken
|
|
1354
|
+
* under one build must not be trusted under another.
|
|
1355
|
+
*/
|
|
1356
|
+
declare const HARFBUZZ_SHAPING_LIBRARY: VersionedShapingLibrary;
|
|
1357
|
+
/** Load and verify the HarfBuzz WASM runtime without adding top-level await to import graphs. */
|
|
1358
|
+
declare function initializeHarfBuzz(): Promise<void>;
|
|
1359
|
+
/** Whether the WASM runtime is loaded and shaping can proceed synchronously. */
|
|
1360
|
+
declare function isHarfBuzzInitialized(): boolean;
|
|
1361
|
+
/**
|
|
1362
|
+
* Why shaping refused.
|
|
1363
|
+
*
|
|
1364
|
+
* Mostly RESOURCE limits, because every input here derives from a file: text length, codepoint
|
|
1365
|
+
* count, glyph count and outline size are all attacker-influenced, and an unbounded shape call is
|
|
1366
|
+
* a denial-of-service vector rather than a rendering bug.
|
|
1367
|
+
*/
|
|
1368
|
+
type HarfBuzzShapingErrorCode = 'notInitialized' | 'wasmUnavailable' | 'unsupportedRuntime' | 'fontOverLimit' | 'malformedFont' | 'textOverLimit' | 'codepointsOverLimit' | 'glyphOverLimit' | 'outlineOverLimit' | 'shapedRunOverLimit' | 'unsupportedVariationAxes' | 'unsupportedFallback' | 'unsupportedColorFont' | 'unsupportedNormalization' | 'invalidBidiLevel' | 'shapingLibraryMismatch' | 'disposed';
|
|
1369
|
+
/**
|
|
1370
|
+
* A shaping call that was refused, carrying the limit it exceeded where there was one.
|
|
1371
|
+
*
|
|
1372
|
+
* Thrown rather than returned: unlike a missing font, a run that cannot be shaped has no sensible
|
|
1373
|
+
* fallback measurement, and continuing would lay text out at made-up widths.
|
|
1374
|
+
*/
|
|
1375
|
+
declare class HarfBuzzShapingError extends Error {
|
|
1376
|
+
readonly name = "HarfBuzzShapingError";
|
|
1377
|
+
readonly code: HarfBuzzShapingErrorCode;
|
|
1378
|
+
readonly limit?: number;
|
|
1379
|
+
readonly actual?: number;
|
|
1380
|
+
readonly diagnostic?: string;
|
|
1381
|
+
constructor(code: HarfBuzzShapingErrorCode, details?: {
|
|
1382
|
+
readonly limit?: number;
|
|
1383
|
+
readonly actual?: number;
|
|
1384
|
+
readonly diagnostic?: string;
|
|
1385
|
+
readonly cause?: unknown;
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Resource budgets and cache sizes for one shaper. Every field optional.
|
|
1390
|
+
*
|
|
1391
|
+
* The caches exist because shaping is the expensive step: a face is opened once and reused, and
|
|
1392
|
+
* identical runs return their previous result. The caps exist because file-derived input decides
|
|
1393
|
+
* how much work is asked for.
|
|
1394
|
+
*/
|
|
1395
|
+
interface HarfBuzzTextShaperOptions {
|
|
1396
|
+
readonly maxFontBytes?: number;
|
|
1397
|
+
readonly maxInputUtf16?: number;
|
|
1398
|
+
readonly maxCodepoints?: number;
|
|
1399
|
+
readonly maxGlyphs?: number;
|
|
1400
|
+
readonly maxCachedFaces?: number;
|
|
1401
|
+
/** Aggregate retained native font bytes; oversized faces are shaped without retention. */
|
|
1402
|
+
readonly maxCachedFontBytes?: number;
|
|
1403
|
+
readonly maxCachedShapes?: number;
|
|
1404
|
+
readonly maxOutlineBytes?: number;
|
|
1405
|
+
readonly maxCachedOutlineBytes?: number;
|
|
1406
|
+
readonly maxShapedRunBytes?: number;
|
|
1407
|
+
readonly maxCachedShapeBytes?: number;
|
|
1408
|
+
readonly instrumentation?: HarfBuzzTextShaperInstrumentation;
|
|
1409
|
+
}
|
|
1410
|
+
/** One face-cache transition, for instrumentation. */
|
|
1411
|
+
interface HarfBuzzFaceCacheEvent {
|
|
1412
|
+
readonly kind: 'created' | 'hit' | 'evicted';
|
|
1413
|
+
readonly identity: string;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Optional counters for the work a shaper is expected to do rarely.
|
|
1417
|
+
*
|
|
1418
|
+
* Exists so tests can assert the ABSENCE of work — re-opening a face or re-copying its bytes per
|
|
1419
|
+
* shape call would not change any output, only make typing slow, which no rendering assertion
|
|
1420
|
+
* would catch.
|
|
1421
|
+
*/
|
|
1422
|
+
interface HarfBuzzTextShaperInstrumentation {
|
|
1423
|
+
readonly onFaceCacheEvent?: (event: HarfBuzzFaceCacheEvent) => void;
|
|
1424
|
+
readonly onShapeCacheEvent?: (event: HarfBuzzShapeCacheEvent) => void;
|
|
1425
|
+
readonly onByteCopy?: () => void;
|
|
1426
|
+
readonly onTableScan?: () => void;
|
|
1427
|
+
readonly onShapeCall?: () => void;
|
|
1428
|
+
readonly onOutlinePathCall?: () => void;
|
|
1429
|
+
readonly onOutlineCacheEvent?: (event: HarfBuzzOutlineCacheEvent) => void;
|
|
1430
|
+
}
|
|
1431
|
+
/** One shape-cache transition, for instrumentation. */
|
|
1432
|
+
interface HarfBuzzShapeCacheEvent {
|
|
1433
|
+
readonly kind: 'hit' | 'miss' | 'stored' | 'evicted' | 'skipped' | 'cleared';
|
|
1434
|
+
readonly retainedBytes: number;
|
|
1435
|
+
}
|
|
1436
|
+
/** One outline-cache transition, for instrumentation. */
|
|
1437
|
+
interface HarfBuzzOutlineCacheEvent {
|
|
1438
|
+
readonly kind: 'created' | 'hit' | 'evicted' | 'skipped' | 'cleared';
|
|
1439
|
+
readonly retainedBytes: number;
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* A {@link TextShaper} backed by HarfBuzz, holding WASM resources.
|
|
1443
|
+
*
|
|
1444
|
+
* {@link HarfBuzzTextShaper.dispose} is not optional housekeeping: the cached faces are WASM
|
|
1445
|
+
* allocations that garbage collection cannot reclaim, so a shaper outliving its editor leaks
|
|
1446
|
+
* until the page goes away.
|
|
1447
|
+
*/
|
|
1448
|
+
interface HarfBuzzTextShaper extends TextShaper {
|
|
1449
|
+
dispose(): void;
|
|
1450
|
+
}
|
|
1451
|
+
/** Structural sfnt validation that does not construct native HarfBuzz objects. */
|
|
1452
|
+
declare const harfBuzzFontValidator: FontByteValidator;
|
|
1453
|
+
/**
|
|
1454
|
+
* Build a HarfBuzz-backed shaper.
|
|
1455
|
+
*
|
|
1456
|
+
* Requires `initializeHarfBuzz()` to have resolved — shaping is synchronous, so the WASM runtime
|
|
1457
|
+
* must already be loaded. Dispose the result when the editor goes away, or its cached faces leak.
|
|
1458
|
+
*/
|
|
1459
|
+
declare const createHarfBuzzTextShaper: (options?: HarfBuzzTextShaperOptions) => HarfBuzzTextShaper;
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* Point the text shaper at an externally hosted copy of `harfbuzz.wasm`.
|
|
1463
|
+
*
|
|
1464
|
+
* Needed only under bundlers that do not emit `new URL(..., import.meta.url)`
|
|
1465
|
+
* assets. esbuild and Bun are the common ones, and so is any build that inlines
|
|
1466
|
+
* dynamic imports, such as a library bundle. There, the build succeeds and the
|
|
1467
|
+
* shaper fails at runtime with an `EditorFontError` whose code is
|
|
1468
|
+
* `wasmUnavailable`; this function is the fix. Webpack, Turbopack and Vite emit
|
|
1469
|
+
* the binary on their own, and passing a URL there simply overrides theirs.
|
|
1470
|
+
*
|
|
1471
|
+
* ```ts
|
|
1472
|
+
* import { setHarfBuzzWasmUrl } from '@docx-editor.dev/core/layout';
|
|
1473
|
+
* setHarfBuzzWasmUrl('/static/harfbuzz.wasm');
|
|
1474
|
+
* ```
|
|
1475
|
+
*
|
|
1476
|
+
* Pass a URL your application controls. It is fetched and instantiated as
|
|
1477
|
+
* WebAssembly, so never derive it from user input, a query parameter, or remote
|
|
1478
|
+
* configuration. Serving it cross-origin needs that origin in your `connect-src`
|
|
1479
|
+
* CSP directive, and WebAssembly needs `wasm-unsafe-eval` in `script-src` either
|
|
1480
|
+
* way.
|
|
1481
|
+
*
|
|
1482
|
+
* Call it before the first editor is created. The runtime reads the location once
|
|
1483
|
+
* and caches the result, so a call afterwards warns and does nothing: fix the
|
|
1484
|
+
* call site and reload. The file to serve is exported as
|
|
1485
|
+
* `@docx-editor.dev/core/harfbuzz.wasm`, and it must be the copy from the
|
|
1486
|
+
* installed package version, because the runtime refuses a version mismatch at
|
|
1487
|
+
* load rather than shaping with unverified metrics.
|
|
1488
|
+
*
|
|
1489
|
+
* @public
|
|
1490
|
+
*/
|
|
1491
|
+
declare function setHarfBuzzWasmUrl(url: string | URL): void;
|
|
1492
|
+
|
|
1493
|
+
/** UTF-16 boundaries that are both whole-grapheme and HarfBuzz cluster edges. */
|
|
1494
|
+
declare function shapedHorizontalBoundaries(run: ShapedRun): readonly number[];
|
|
1495
|
+
/**
|
|
1496
|
+
* Whether an offset is a real caret position: both a grapheme boundary and a shaped cluster edge.
|
|
1497
|
+
*
|
|
1498
|
+
* Both conditions, because they disagree. A ligature is one cluster spanning two graphemes, and
|
|
1499
|
+
* an offset inside it has no geometry the shaper can answer for — placing a caret there would
|
|
1500
|
+
* mean inventing an x coordinate.
|
|
1501
|
+
*/
|
|
1502
|
+
declare function isWholeGraphemeHorizontalBoundary(run: ShapedRun, utf16Offset: number): boolean;
|
|
1503
|
+
/** Every published shaped boundary has exact geometry from the shaping result. */
|
|
1504
|
+
declare function isGeometryTrustedCaretOffset(run: ShapedRun, utf16Offset: number): boolean;
|
|
1505
|
+
/**
|
|
1506
|
+
* Whether the distance from a line's start to an offset can be trusted as exact.
|
|
1507
|
+
*
|
|
1508
|
+
* Requires BOTH ends to be shaped boundaries: accumulating advances across an offset the shaper
|
|
1509
|
+
* cannot place would produce a caret x that drifts further along the line.
|
|
1510
|
+
*/
|
|
1511
|
+
declare function isCumulativeGeometryTrustedFromLineOrigin(run: ShapedRun, lineStartUtf16Offset: number, utf16Offset: number): boolean;
|
|
1512
|
+
/** Grapheme offsets corresponding to exact shaped boundaries. */
|
|
1513
|
+
declare function semanticHorizontalBoundaries(run: ShapedRun): readonly number[];
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* One user-perceived character, and the UTF-16 range that encodes it.
|
|
1517
|
+
*
|
|
1518
|
+
* The unit a caret moves by. An emoji with a skin-tone modifier is ONE segment spanning several
|
|
1519
|
+
* UTF-16 code units, so stepping by code unit would put the caret inside it.
|
|
1520
|
+
*/
|
|
1521
|
+
interface GraphemeSegment {
|
|
1522
|
+
readonly index: number;
|
|
1523
|
+
readonly text: string;
|
|
1524
|
+
readonly utf16From: number;
|
|
1525
|
+
readonly utf16To: number;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* The replaceable segmentation strategy.
|
|
1529
|
+
*
|
|
1530
|
+
* An explicit seam rather than a direct `Intl.Segmenter` call, so tests can install a
|
|
1531
|
+
* deterministic boundary and a runtime without `Intl.Segmenter` can be given one.
|
|
1532
|
+
*/
|
|
1533
|
+
interface GraphemeBoundary {
|
|
1534
|
+
segment(text: string): readonly GraphemeSegment[];
|
|
1535
|
+
}
|
|
1536
|
+
/** Invariant locale for deterministic cross-runtime grapheme boundaries. */
|
|
1537
|
+
declare const GRAPHEME_SEGMENTER_LOCALE: 'und';
|
|
1538
|
+
/** Whether this runtime provides `Intl.Segmenter`, which the default boundary requires. */
|
|
1539
|
+
declare function isIntlSegmenterAvailable(): boolean;
|
|
1540
|
+
/**
|
|
1541
|
+
* The default boundary, over `Intl.Segmenter` at the invariant `und` locale.
|
|
1542
|
+
*
|
|
1543
|
+
* Locale-invariant on purpose: grapheme boundaries must not vary with the user's locale, or the
|
|
1544
|
+
* same document would paginate differently for different readers.
|
|
1545
|
+
*/
|
|
1546
|
+
declare const intlGraphemeBoundary: GraphemeBoundary;
|
|
1547
|
+
/** Current boundary generation, for callers that cache segmentation-derived answers. */
|
|
1548
|
+
declare function graphemeBoundaryEpoch(): number;
|
|
1549
|
+
/**
|
|
1550
|
+
* Split text into graphemes through the active boundary.
|
|
1551
|
+
*
|
|
1552
|
+
* Memoized on the last texts seen, because paragraph layout asks about the same string
|
|
1553
|
+
* repeatedly and a full segmentation pass per call made layout quadratic in paragraph length.
|
|
1554
|
+
*/
|
|
1555
|
+
declare function segmentGraphemes(text: string): readonly GraphemeSegment[];
|
|
1556
|
+
/**
|
|
1557
|
+
* Install a different segmentation strategy, invalidating the memo.
|
|
1558
|
+
*
|
|
1559
|
+
* For tests and for runtimes lacking `Intl.Segmenter`. Call {@link resetGraphemeBoundary} to
|
|
1560
|
+
* restore the default.
|
|
1561
|
+
*/
|
|
1562
|
+
declare function setGraphemeBoundary(boundary: GraphemeBoundary): void;
|
|
1563
|
+
/** Restore the default `Intl.Segmenter` boundary and clear the memo. */
|
|
1564
|
+
declare function resetGraphemeBoundary(): void;
|
|
1565
|
+
/** How many user-perceived characters the text holds. */
|
|
1566
|
+
declare function graphemeCount(text: string): number;
|
|
1567
|
+
/**
|
|
1568
|
+
* The grapheme index containing a UTF-16 offset. Clamps rather than throwing.
|
|
1569
|
+
*
|
|
1570
|
+
* Backed by a single-entry index cache: this runs once per character during paragraph layout, and
|
|
1571
|
+
* re-segmenting per call is what made a 20,000-character paragraph take minutes to open. One
|
|
1572
|
+
* entry rather than a map, because the keys are file-derived strings of unbounded size.
|
|
1573
|
+
*/
|
|
1574
|
+
declare function utf16OffsetToGrapheme(text: string, utf16Offset: number): number;
|
|
1575
|
+
/** The UTF-16 offset a grapheme index starts at. Clamps rather than throwing. */
|
|
1576
|
+
declare function graphemeOffsetToUtf16(text: string, graphemeOffset: number): number;
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* One word-segmentation span. `wordLike` separates words from the whitespace and punctuation
|
|
1580
|
+
* between them, which is what double-click selection needs to skip.
|
|
1581
|
+
*/
|
|
1582
|
+
interface WordSegment {
|
|
1583
|
+
readonly utf16From: number;
|
|
1584
|
+
readonly utf16To: number;
|
|
1585
|
+
readonly wordLike: boolean;
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* The replaceable word-segmentation strategy.
|
|
1589
|
+
*
|
|
1590
|
+
* Falls back to a BOUNDED grapheme-safe splitter where `Intl.Segmenter` is absent — bounded
|
|
1591
|
+
* because the input is file-derived, and grapheme-safe so a fallback never splits inside an
|
|
1592
|
+
* emoji.
|
|
1593
|
+
*/
|
|
1594
|
+
interface WordBoundary {
|
|
1595
|
+
segment(text: string): readonly WordSegment[];
|
|
1596
|
+
}
|
|
1597
|
+
/** Invariant locale for deterministic cross-runtime word boundaries. */
|
|
1598
|
+
declare const WORD_SEGMENTER_LOCALE: 'und';
|
|
1599
|
+
/** Whether this runtime provides word-granularity `Intl.Segmenter`. */
|
|
1600
|
+
declare function isIntlWordSegmenterAvailable(): boolean;
|
|
1601
|
+
/** Intl.Segmenter word boundary for the invariant locale. */
|
|
1602
|
+
declare function createIntlWordBoundary(): WordBoundary;
|
|
1603
|
+
/** Bounded deterministic fallback — not full UAX #29 word conformance. */
|
|
1604
|
+
declare function boundedFallbackWordSegments(text: string): readonly WordSegment[];
|
|
1605
|
+
/** Explicit bounded fallback boundary (grapheme-safe, narrower than Intl). */
|
|
1606
|
+
declare function createBoundedFallbackWordBoundary(): WordBoundary;
|
|
1607
|
+
/** Injection points for {@link createDefaultWordBoundary}, so tests can force either path. */
|
|
1608
|
+
interface WordBoundaryResolverDeps {
|
|
1609
|
+
readonly isIntlAvailable?: () => boolean;
|
|
1610
|
+
readonly createIntlBoundary?: () => WordBoundary;
|
|
1611
|
+
readonly createFallbackBoundary?: () => WordBoundary;
|
|
1612
|
+
}
|
|
1613
|
+
/** Resolve production word boundary: Intl when available/construction succeeds, else bounded fallback. */
|
|
1614
|
+
declare function createDefaultWordBoundary(deps?: WordBoundaryResolverDeps): WordBoundary;
|
|
1615
|
+
/** Cached immutable production boundary (first resolved instance only). */
|
|
1616
|
+
declare function resolveDefaultWordBoundary(): WordBoundary;
|
|
1617
|
+
/** Split text into word segments through the given boundary, or the resolved default. */
|
|
1618
|
+
declare function segmentWords(text: string, boundary?: WordBoundary): readonly WordSegment[];
|
|
1619
|
+
/**
|
|
1620
|
+
* A word span expressed in GRAPHEME offsets rather than UTF-16 ones.
|
|
1621
|
+
*
|
|
1622
|
+
* What selection actually uses: a range whose ends are UTF-16 offsets could land inside a
|
|
1623
|
+
* grapheme, and selecting half an emoji is not a word.
|
|
1624
|
+
*/
|
|
1625
|
+
interface GraphemeWordSegmentRecord {
|
|
1626
|
+
readonly graphemeFrom: number;
|
|
1627
|
+
readonly graphemeTo: number;
|
|
1628
|
+
readonly wordLike: boolean;
|
|
1629
|
+
}
|
|
1630
|
+
/** Map UTF-16 word segments to grapheme-safe half-open ranges within one paragraph. */
|
|
1631
|
+
declare function wordSegmentsToGraphemeRecords(text: string, segments: readonly WordSegment[]): readonly GraphemeWordSegmentRecord[];
|
|
1632
|
+
|
|
1633
|
+
/** One span in the engine's published story order. @public */
|
|
1634
|
+
interface SemanticSpanVisit {
|
|
1635
|
+
readonly page: PageRecord;
|
|
1636
|
+
readonly story: SemanticStoryKind;
|
|
1637
|
+
/** Root story from which textbox descent began; equal to `story` outside textboxes. */
|
|
1638
|
+
readonly rootStory: SemanticRootStoryKind;
|
|
1639
|
+
/** Precise root host and absolute origin for story-relative geometry. */
|
|
1640
|
+
readonly root: SemanticStoryVisit;
|
|
1641
|
+
/** Absolute origin of the immediate root or textbox story containing this span. */
|
|
1642
|
+
readonly storyOrigin: Readonly<{
|
|
1643
|
+
x: number;
|
|
1644
|
+
y: number;
|
|
1645
|
+
}>;
|
|
1646
|
+
/** Absolute laid-out span bounds in page-stack coordinates. */
|
|
1647
|
+
readonly absoluteBox: LayoutBox;
|
|
1648
|
+
/** Owning note scope/area where applicable; null for body and page furniture. */
|
|
1649
|
+
readonly noteScopeId: string | null;
|
|
1650
|
+
readonly noteAreaKind: SemanticStoryVisit['noteAreaKind'];
|
|
1651
|
+
/** Zero outside a textbox, otherwise its bounded nesting depth. */
|
|
1652
|
+
readonly textboxDepth: number;
|
|
1653
|
+
/** Immediate textbox-owning drawing, anchored or inline, or null in the root story. */
|
|
1654
|
+
readonly textboxOwner: TextboxOwnerRecord | null;
|
|
1655
|
+
/** Root-to-leaf textbox owners, preserving drawing identity for future exporters. */
|
|
1656
|
+
readonly textboxPath: readonly TextboxOwnerRecord[];
|
|
1657
|
+
/** Enclosing published fragment; use paragraphId for the authored span owner. */
|
|
1658
|
+
readonly paragraph: ParagraphFragmentRecord;
|
|
1659
|
+
/** Authored paragraph owning this span, including spans merged into another fragment. */
|
|
1660
|
+
readonly paragraphId: string;
|
|
1661
|
+
readonly line: LineRecord;
|
|
1662
|
+
readonly span: StyleSpanRecord;
|
|
1663
|
+
/**
|
|
1664
|
+
* Model address for authored text. Projected atoms intentionally return null even though
|
|
1665
|
+
* their geometry record carries a range used internally by layout.
|
|
1666
|
+
*/
|
|
1667
|
+
readonly sourceRange: SourceRange | null;
|
|
1668
|
+
}
|
|
1669
|
+
/** Return the model address exporters may use, excluding layout-projected atoms. @public */
|
|
1670
|
+
declare function exportSourceRangeOf(span: StyleSpanRecord): SourceRange | null;
|
|
1671
|
+
/**
|
|
1672
|
+
* Visit every published span in page/story order without consulting the source package.
|
|
1673
|
+
* @public
|
|
1674
|
+
*/
|
|
1675
|
+
declare function forEachSemanticSpan(layout: SemanticLayout, visitor: (visit: SemanticSpanVisit) => void): void;
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* The part of a line that belongs to ONE paragraph.
|
|
1679
|
+
*
|
|
1680
|
+
* A line normally belongs to one paragraph outright, and then this is the whole of it — the
|
|
1681
|
+
* same object every caller read before, so nothing about an ordinary document takes a new
|
|
1682
|
+
* path. A resolved display mode merges paragraphs that a tracked decision merges, and the line
|
|
1683
|
+
* carrying the join holds spans from two of them. Offsets there are ambiguous by themselves:
|
|
1684
|
+
* both paragraphs start at zero, so an offset means nothing without the paragraph it counts in.
|
|
1685
|
+
*/
|
|
1686
|
+
interface LineSegment {
|
|
1687
|
+
readonly paragraphId: string;
|
|
1688
|
+
readonly start: number;
|
|
1689
|
+
readonly end: number;
|
|
1690
|
+
readonly spans: readonly StyleSpanRecord[];
|
|
1691
|
+
readonly drawings: readonly InlineDrawingRecord[];
|
|
1692
|
+
}
|
|
1693
|
+
/** Every paragraph a line carries, in visual order. One entry for an ordinary line. */
|
|
1694
|
+
declare function lineSegments(line: LineRecord): readonly LineSegment[];
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* The box a painter clips a vector shape to: `bounds` grown on each side where a stroked
|
|
1698
|
+
* outline reaches past the drawing extent.
|
|
1699
|
+
*
|
|
1700
|
+
* An outline is centred on its geometry and is not cut at the extent, a text column, or a cell
|
|
1701
|
+
* edge that meets the extent: a rule at x=0 of a group with `wp:effectExtent l="0"` keeps
|
|
1702
|
+
* its full width. The reach comes from butt-capped segments and miter joins, measured
|
|
1703
|
+
* only on the part of each segment inside the extent, so geometry outside the extent stays
|
|
1704
|
+
* clipped and a side grows by at most the miter reach. A line-end triangle on a line that
|
|
1705
|
+
* ends inside the extent grows it too. A side stays as it is when a layout region or a
|
|
1706
|
+
* negative effect extent cut into the shape itself, and a drawing clipped to nothing stays
|
|
1707
|
+
* empty.
|
|
1708
|
+
*
|
|
1709
|
+
* Only the paint clip grows. Collisions, hit testing, and pointer targets keep `paintBounds`,
|
|
1710
|
+
* so a painter clips with this box without making it clickable.
|
|
1711
|
+
*
|
|
1712
|
+
* `bounds` is the drawing's `paintBounds` in any translation: the record's own, or the
|
|
1713
|
+
* absolute paint bounds of an export visit.
|
|
1714
|
+
*
|
|
1715
|
+
* @public
|
|
1716
|
+
*/
|
|
1717
|
+
declare function vectorShapeInkClip(drawing: InlineDrawingRecord | AnchoredDrawingRecord, bounds: LayoutBox): LayoutBox;
|
|
1718
|
+
|
|
1719
|
+
/** Memoized style inputs shared by every story in one document view. @public */
|
|
1720
|
+
interface DocumentStyleDependencies {
|
|
1721
|
+
readonly styleCascade: () => StyleCascadeTable | undefined;
|
|
1722
|
+
readonly defaultTabStopPt: () => number;
|
|
1723
|
+
readonly compatibilityMode?: () => number | undefined;
|
|
1724
|
+
readonly numberingIndex: () => NumberingIndex;
|
|
1725
|
+
}
|
|
1726
|
+
/** Build the cascade and numbering projections layout consumes. @public */
|
|
1727
|
+
declare function createDocumentStyleDependencies(view: HeadlessDocumentView): DocumentStyleDependencies;
|
|
1728
|
+
|
|
1729
|
+
/** Paired projector/cache identities shared by browser, exporters, and future story hosts. */
|
|
1730
|
+
interface StoryProjectionDependencies {
|
|
1731
|
+
readonly tokenForParagraphForPart: (partName: string, paragraph: OoxmlNode) => string;
|
|
1732
|
+
/** Memoized aggregate for every paragraph in an immutable table subtree. */
|
|
1733
|
+
readonly tokenForTableForPart: (partName: string, table: OoxmlNode) => string;
|
|
1734
|
+
readonly epochForPart: (partName: string) => string;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
/**
|
|
1738
|
+
* Sanitized projectors paired with the cache identities required to use them safely.
|
|
1739
|
+
*
|
|
1740
|
+
* Keep this object intact when passing it to a document composition helper. Projected text and
|
|
1741
|
+
* its dependency tokens are one contract: accepting either half independently permits stale
|
|
1742
|
+
* paragraph caches after relationship- or property-only revisions.
|
|
1743
|
+
* @public
|
|
1744
|
+
*/
|
|
1745
|
+
interface DocumentLinkProjectors extends StoryProjectionDependencies {
|
|
1746
|
+
readonly projectLink: HyperlinkProjector;
|
|
1747
|
+
readonly projectLinkForPart: (partName: string) => HyperlinkProjector;
|
|
1748
|
+
}
|
|
1749
|
+
/** Sanitized body and per-part link projectors over one neutral document view. @public */
|
|
1750
|
+
declare function createDocumentLinkProjectors(view: HeadlessDocumentView): DocumentLinkProjectors;
|
|
1751
|
+
|
|
1752
|
+
/** Page geometry for header/footer anchored frame resolution (story-relative layout space). */
|
|
1753
|
+
interface HeaderFooterPageContext {
|
|
1754
|
+
readonly pageNumber: number;
|
|
1755
|
+
readonly pageWidth: number;
|
|
1756
|
+
readonly pageHeight: number;
|
|
1757
|
+
readonly marginLeft: number;
|
|
1758
|
+
readonly marginRight: number;
|
|
1759
|
+
readonly marginTop: number;
|
|
1760
|
+
readonly marginBottom: number;
|
|
1761
|
+
/** `w:pgMar/@w:header` for a header, `@w:footer` for a footer: the story edge's distance. */
|
|
1762
|
+
readonly storyDistance?: number;
|
|
1763
|
+
}
|
|
1764
|
+
/** Per-sheet geometry needed to resolve header/footer anchors before wrap and clipping. */
|
|
1765
|
+
interface HeaderFooterLayoutPageContext extends FieldPageContext {
|
|
1766
|
+
/** Effective content-box inset after this page's header/footer reserves are applied. */
|
|
1767
|
+
readonly contentInsetTop?: number;
|
|
1768
|
+
/** Effective bottom content-box inset after footer reserve. */
|
|
1769
|
+
readonly contentInsetBottom?: number;
|
|
1770
|
+
/** Story-box top measured from the physical sheet top. */
|
|
1771
|
+
readonly storyTop?: number;
|
|
1772
|
+
}
|
|
1773
|
+
interface HeaderFooterStoryLayout {
|
|
1774
|
+
readonly partName: string;
|
|
1775
|
+
/**
|
|
1776
|
+
* The part this story was laid out from.
|
|
1777
|
+
*
|
|
1778
|
+
* Carried because a derivation that runs over the finished layout cannot reach it otherwise:
|
|
1779
|
+
* the layout receives furniture as fragments plus a NAME, and a name is not a tree. The
|
|
1780
|
+
* content-control boundary pass needs the tree to know which controls exist at all.
|
|
1781
|
+
*/
|
|
1782
|
+
readonly part?: OoxmlPart;
|
|
1783
|
+
/** Main-document relationship id when the furniture source knows it. */
|
|
1784
|
+
readonly rId?: string;
|
|
1785
|
+
/**
|
|
1786
|
+
* Bounded identity of the story's canonical OOXML content.
|
|
1787
|
+
*
|
|
1788
|
+
* Furniture cache keys must not rely on {@link flowHeight} alone: equal-height A→B edits
|
|
1789
|
+
* would otherwise reuse stale page furniture. Derived as a 16-hex FNV-1a over the part's
|
|
1790
|
+
* canonical fingerprint — never DOM identity or unbounded raw serialization in the key.
|
|
1791
|
+
* PAGE/NUMPAGES/SECTIONPAGES projection shares this key; page context is cached separately.
|
|
1792
|
+
*/
|
|
1793
|
+
readonly contentKey: string;
|
|
1794
|
+
/** Snapshot identity for projected links and live document-property fields in this story. */
|
|
1795
|
+
readonly projectionEpoch?: string;
|
|
1796
|
+
/** Story-relative fragments; origin at the story box's top-left. */
|
|
1797
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
1798
|
+
/** The height the blocks actually flow to — what sizes the box on every page. */
|
|
1799
|
+
readonly flowHeight: number;
|
|
1800
|
+
/**
|
|
1801
|
+
* Allowlisted complex PAGE / NUMPAGES / SECTIONPAGES presence detected for this story.
|
|
1802
|
+
*
|
|
1803
|
+
* Callers use this to skip attaching a page-field projector when the baseline is enough.
|
|
1804
|
+
*/
|
|
1805
|
+
readonly pageFieldNeeds: StoryPageFieldNeeds;
|
|
1806
|
+
/** Anchored drawings owned by this story, in story-relative coordinates. */
|
|
1807
|
+
readonly anchoredDrawings?: readonly AnchoredDrawingRecord[];
|
|
1808
|
+
/**
|
|
1809
|
+
* Re-layout this story under page-field and optional attached-page geometry context.
|
|
1810
|
+
*
|
|
1811
|
+
* Field-free stories return `this`. Count-only stories cache by the counts they read.
|
|
1812
|
+
* PAGE stories cache by the distinct evaluated values (including format) with a bounded LRU.
|
|
1813
|
+
*/
|
|
1814
|
+
readonly withPageContext: (ctx: HeaderFooterLayoutPageContext) => HeaderFooterStoryLayout;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Story-level inputs the body path has always had and furniture never did.
|
|
1818
|
+
*
|
|
1819
|
+
* A BAG, not more positional parameters. The signature below already carries fifteen, and its
|
|
1820
|
+
* own doc comment used to tell new callers to "keep passing `undefined` for what they do not
|
|
1821
|
+
* set" — which is why `numberingIndex` was never added and a numbered paragraph in a header
|
|
1822
|
+
* painted no marker for as long as headers have been editable. Everything new goes here.
|
|
1823
|
+
*/
|
|
1824
|
+
interface HeaderFooterStoryInputs {
|
|
1825
|
+
readonly compatibilityMode?: number;
|
|
1826
|
+
/**
|
|
1827
|
+
* `numbering.xml`, so a `w:numPr` paragraph in this story resolves a marker — directly or
|
|
1828
|
+
* inside an anchored text box, which lays out with its own per-box counters.
|
|
1829
|
+
*
|
|
1830
|
+
* Absent, the story lays out exactly as before: no marker record, and no numbering indent
|
|
1831
|
+
* merged into the paragraph's own.
|
|
1832
|
+
*/
|
|
1833
|
+
readonly numberingIndex?: NumberingIndex;
|
|
1834
|
+
/** Sanitized hyperlink seams scoped to this header/footer part. */
|
|
1835
|
+
readonly projectLink?: HyperlinkProjector;
|
|
1836
|
+
readonly projectFieldLink?: FieldLinkProjector;
|
|
1837
|
+
/** @internal */
|
|
1838
|
+
readonly showFieldCodes?: boolean;
|
|
1839
|
+
/** Per-paragraph identity for links and live document-property projection. */
|
|
1840
|
+
readonly projectionTokenForParagraph?: (paragraph: OoxmlNode) => string;
|
|
1841
|
+
/** Memoized aggregate projection identity for table subtrees. */
|
|
1842
|
+
readonly projectionTokenForTable?: (table: OoxmlNode) => string;
|
|
1843
|
+
/** Aggregate identity of link and live-field projections for furniture reuse keys. */
|
|
1844
|
+
readonly projectionEpoch?: string;
|
|
1845
|
+
/** Reviewers whose changes project as accepted in this story. */
|
|
1846
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Lay one header/footer part out at `contentWidth`.
|
|
1850
|
+
*
|
|
1851
|
+
* Line ids are namespaced by part so the body's `line-N` counter — which incremental
|
|
1852
|
+
* convergence compares — never moves because a header changed.
|
|
1853
|
+
*
|
|
1854
|
+
* When `pageContext` is set, allowlisted PAGE/NUMPAGES/SECTIONPAGES instructions project
|
|
1855
|
+
* live values; otherwise those fields contribute only cached result text (often empty).
|
|
1856
|
+
* Field-free stories ignore `pageContext` and share one baseline layout.
|
|
1857
|
+
*
|
|
1858
|
+
* `defaultTabStopPt` is the document's `w:settings/w:defaultTabStop` (ECMA-376 §17.15.1.25)
|
|
1859
|
+
* in points; absent keeps the 0.5" schema default. Furniture tabs on the SAME grid as the
|
|
1860
|
+
* body — a page-number tab in a metric-locale footer belongs on the document's interval, not
|
|
1861
|
+
* on a constant. It sits at the tail because the parameters ahead of it are already
|
|
1862
|
+
* positional; new callers should keep passing `undefined` for what they do not set.
|
|
1863
|
+
*
|
|
1864
|
+
* NEW inputs belong in {@link HeaderFooterStoryInputs}, the trailing bag, rather than as a
|
|
1865
|
+
* sixteenth position. Fifteen is what stopped `numberingIndex` being threaded here at all.
|
|
1866
|
+
*/
|
|
1867
|
+
declare function layoutHeaderFooterStory(part: OoxmlPart, contentWidth: number, measurer: TextMeasurer, producer: string, cache?: ParagraphLayoutCache<readonly PendingLine[]>, styleCascade?: StyleCascadeTable, pageContext?: FieldPageContext, maxPageContextEntries?: number, defaultTabStopPt?: number, displayMode?: RevisionDisplayMode, inlineDrawingLayout?: InlineDrawingLayoutContext, drawingTokenForParagraph?: (paragraph: OoxmlNode) => string, drawingLayoutToken?: string, hfPageContext?: HeaderFooterPageContext, documentProperties?: DocumentProperties, inputs?: HeaderFooterStoryInputs): HeaderFooterStoryLayout;
|
|
1868
|
+
|
|
1869
|
+
/** Which header/footer variant a page shows (ECMA-376 §17.10.5). */
|
|
1870
|
+
type HeaderFooterVariantName = 'default' | 'first' | 'even';
|
|
1871
|
+
/**
|
|
1872
|
+
* Pre-laid page furniture, supplied by the host (phase 2).
|
|
1873
|
+
*
|
|
1874
|
+
* Baseline stories are laid out once per variant (`layoutHeaderFooterStory`) for furniture
|
|
1875
|
+
* height. Stories that actually contain allowlisted PAGE/NUMPAGES fields attach a projector
|
|
1876
|
+
* so document-level finalize can re-layout under the known page count; field-free furniture
|
|
1877
|
+
* reuses the baseline on every sheet.
|
|
1878
|
+
*/
|
|
1879
|
+
interface PageFurniture {
|
|
1880
|
+
readonly titlePage: boolean;
|
|
1881
|
+
readonly evenAndOddHeaders: boolean;
|
|
1882
|
+
readonly headers: ReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>;
|
|
1883
|
+
readonly footers: ReadonlyMap<HeaderFooterVariantName, HeaderFooterStoryLayout>;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/** Page furniture supplied to semantic layout. @public */
|
|
1887
|
+
interface DocumentFurnitureSource {
|
|
1888
|
+
furniture(): PageFurniture | undefined;
|
|
1889
|
+
sectionFurniture(): readonly (PageFurniture | undefined)[];
|
|
1890
|
+
}
|
|
1891
|
+
/** Inputs that remain valid for the lifetime of one furniture source. @public */
|
|
1892
|
+
interface CreateDocumentFurnitureSourceOptions {
|
|
1893
|
+
readonly view: HeadlessDocumentView;
|
|
1894
|
+
readonly measurer: TextMeasurer;
|
|
1895
|
+
readonly producer: string;
|
|
1896
|
+
readonly cache: ParagraphLayoutCache<readonly PendingLine[]>;
|
|
1897
|
+
readonly styleCascade?: () => StyleCascadeTable | undefined;
|
|
1898
|
+
readonly numberingIndex?: () => NumberingIndex;
|
|
1899
|
+
readonly defaultTabStopPt?: () => number;
|
|
1900
|
+
readonly compatibilityMode?: () => number | undefined;
|
|
1901
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
1902
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
1903
|
+
readonly inlineDrawingLayoutForPart?: (partName: string) => InlineDrawingLayoutContext | undefined;
|
|
1904
|
+
readonly drawingLayoutTokenForPart?: (partName: string) => string;
|
|
1905
|
+
readonly drawingTokenForParagraphForPart?: (partName: string, paragraph: OoxmlNode) => string;
|
|
1906
|
+
/** Link/property projection and its inseparable cache identities. */
|
|
1907
|
+
readonly linkProjectors: DocumentLinkProjectors;
|
|
1908
|
+
readonly projectFieldLink?: FieldLinkProjector;
|
|
1909
|
+
/** Field-code inspection projection. @internal */
|
|
1910
|
+
readonly showFieldCodes?: boolean;
|
|
1911
|
+
}
|
|
1912
|
+
/** Build section-aware header/footer layout from a neutral document view. @public */
|
|
1913
|
+
declare function createDocumentFurnitureSource(options: CreateDocumentFurnitureSourceOptions): DocumentFurnitureSource;
|
|
1914
|
+
|
|
1915
|
+
/**
|
|
1916
|
+
* Where one note is referenced from — what per-page and per-section restart rules are computed
|
|
1917
|
+
* against.
|
|
1918
|
+
*/
|
|
1919
|
+
interface NoteReferenceSite {
|
|
1920
|
+
/** Stable note id (`w:id`). */
|
|
1921
|
+
readonly noteId: number;
|
|
1922
|
+
/** Section index of the reference (0-based). */
|
|
1923
|
+
readonly sectionIndex: number;
|
|
1924
|
+
/**
|
|
1925
|
+
* Page index of the reference when known (0-based). Required for `eachPage` restart;
|
|
1926
|
+
* when omitted, `eachPage` behaves like `continuous` for that site.
|
|
1927
|
+
*/
|
|
1928
|
+
readonly pageIndex?: number;
|
|
1929
|
+
/** When true, consumes no automatic number (`customMarkFollows`). */
|
|
1930
|
+
readonly customMarkFollows?: boolean;
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* The mark a note reference paints.
|
|
1934
|
+
*
|
|
1935
|
+
* `null` where `w:customMarkFollows` suppresses it: the document supplies its own glyph, and
|
|
1936
|
+
* painting an automatic number too would show the note twice.
|
|
1937
|
+
*/
|
|
1938
|
+
interface NoteDisplayMark {
|
|
1939
|
+
readonly noteId: number;
|
|
1940
|
+
/** Formatted mark, or `null` when suppressed by customMarkFollows. */
|
|
1941
|
+
readonly mark: string | null;
|
|
1942
|
+
/** 1-based automatic sequence number when assigned; absent when suppressed. */
|
|
1943
|
+
readonly displayNumber?: number;
|
|
1944
|
+
}
|
|
1945
|
+
type ResolvedNoteProperties = ResolvedFootnoteProperties | ResolvedEndnoteProperties;
|
|
1946
|
+
/**
|
|
1947
|
+
* Derive display marks for references of one note kind in document order.
|
|
1948
|
+
*
|
|
1949
|
+
* Restart rules:
|
|
1950
|
+
* - `continuous` — single sequence across the document from `numStart`
|
|
1951
|
+
* - `eachSect` — restart at `numStart` when `sectionIndex` changes
|
|
1952
|
+
* - `eachPage` — restart when `pageIndex` changes (falls back to continuous if unknown)
|
|
1953
|
+
*
|
|
1954
|
+
* IDs are stable; only display numbers change. Non-mutating.
|
|
1955
|
+
*/
|
|
1956
|
+
declare function deriveNoteDisplayMarks(noteKind: NoteKind, references: readonly NoteReferenceSite[], properties: ResolvedNoteProperties): readonly NoteDisplayMark[];
|
|
1957
|
+
/**
|
|
1958
|
+
* Derive marks using per-reference-section resolved properties (`numFmt` / `numStart` /
|
|
1959
|
+
* `numRestart`). Restart rules consult each site's own section props.
|
|
1960
|
+
*/
|
|
1961
|
+
declare function deriveNoteDisplayMarksResolved(_noteKind: NoteKind, references: readonly NoteReferenceSite[], resolveProps: (sectionIndex: number) => ResolvedNoteProperties): readonly NoteDisplayMark[];
|
|
1962
|
+
/** Map noteId → formatted mark for quick lookup (last site wins if duplicated). */
|
|
1963
|
+
declare function noteDisplayMarkMap(marks: readonly NoteDisplayMark[]): ReadonlyMap<number, string | null>;
|
|
1964
|
+
|
|
1965
|
+
/** Hard ceiling on notes laid out in one pass (fail closed beyond). */
|
|
1966
|
+
declare const MAX_NOTES_LAID_OUT = 10000;
|
|
1967
|
+
/** Hard ceiling on fragments emitted for one note (split / continuation). */
|
|
1968
|
+
declare const MAX_NOTE_FRAGMENTS = 512;
|
|
1969
|
+
/**
|
|
1970
|
+
* Why note layout stopped short and fell back.
|
|
1971
|
+
*
|
|
1972
|
+
* Every one is a BOUND rather than a bug: note counts, fragment counts and heights all come from
|
|
1973
|
+
* a file, and a document can ask for more note area than a page has. Falling back with a reason
|
|
1974
|
+
* keeps the document open instead of failing to lay out.
|
|
1975
|
+
*/
|
|
1976
|
+
type NoteLayoutFallbackReason = 'note-count-limit' | 'note-fragment-limit' | 'note-reflow-exhausted' | 'note-height-cap'
|
|
1977
|
+
/** Authored separator/continuationSeparator taller than the content column. */
|
|
1978
|
+
| 'note-separator-height-cap' | 'note-continuation-notice-height-cap' | 'missing-note-body' | 'dangling-note-reference';
|
|
1979
|
+
/**
|
|
1980
|
+
* One note's body laid out as its own story, in story-relative coordinates.
|
|
1981
|
+
*
|
|
1982
|
+
* Relative rather than page-absolute because a note moves between pages during pagination — the
|
|
1983
|
+
* page it lands on is decided after its content is measured.
|
|
1984
|
+
*/
|
|
1985
|
+
interface NoteStoryLayout {
|
|
1986
|
+
readonly noteKind: NoteKind;
|
|
1987
|
+
readonly noteId: number;
|
|
1988
|
+
/** `footnote:N` / `endnote:N` — matches EditorScope note id encoding. */
|
|
1989
|
+
readonly scopeId: string;
|
|
1990
|
+
readonly noteType: ReturnType<typeof noteTypeOf>;
|
|
1991
|
+
/** Story-relative fragments; origin at the story box's top-left. */
|
|
1992
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
1993
|
+
/** Height the blocks flow to (points). */
|
|
1994
|
+
readonly flowHeight: number;
|
|
1995
|
+
/** True when layout hit a named bound and returned a truncated / empty story. */
|
|
1996
|
+
readonly fallbackReason?: NoteLayoutFallbackReason;
|
|
1997
|
+
}
|
|
1998
|
+
/** Paint style for Word-default / marker-only separator rules (not CSS inventing content). */
|
|
1999
|
+
type NoteSeparatorRuleStyle = 'single' | 'double';
|
|
2000
|
+
/**
|
|
2001
|
+
* The rule between body text and the note area.
|
|
2002
|
+
*
|
|
2003
|
+
* Synthesized when the document declares none, because Word draws one regardless — a document
|
|
2004
|
+
* without an authored separator still shows the line a reader expects.
|
|
2005
|
+
*/
|
|
2006
|
+
interface NoteSeparatorLayout {
|
|
2007
|
+
readonly kind: 'separator' | 'continuationSeparator';
|
|
2008
|
+
readonly fragments: readonly BlockFragmentRecord[];
|
|
2009
|
+
readonly flowHeight: number;
|
|
2010
|
+
/** True when the engine synthesized a default rule (document had none). */
|
|
2011
|
+
readonly synthetic: boolean;
|
|
2012
|
+
/**
|
|
2013
|
+
* Layout-owned rule when the separator is marker-only (`w:separator` /
|
|
2014
|
+
* `w:continuationSeparator`) or fully synthetic. Absent when an authored separator
|
|
2015
|
+
* story has real paragraph/run/border content that paint should render as fragments.
|
|
2016
|
+
*/
|
|
2017
|
+
readonly ruleStyle?: NoteSeparatorRuleStyle;
|
|
2018
|
+
/** Marker rule geometry inside the measured separator story. */
|
|
2019
|
+
readonly ruleBox?: LayoutBox;
|
|
2020
|
+
/** Resolved marker-run color; null/absent uses automatic black. */
|
|
2021
|
+
readonly ruleColor?: string | null;
|
|
2022
|
+
/** Set when an oversize authored separator was replaced with a synthetic rule. */
|
|
2023
|
+
readonly fallbackReason?: NoteLayoutFallbackReason;
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Inline drawing support for ONE notes part.
|
|
2027
|
+
*
|
|
2028
|
+
* A note lives in `/word/footnotes.xml` or `/word/endnotes.xml`, not in the body part, so its
|
|
2029
|
+
* pictures resolve against that part's relationships — the same per-part shape header/footer
|
|
2030
|
+
* furniture uses. Without it a note paragraph flows with no drawing context at all and a
|
|
2031
|
+
* picture inside it contributes no record: no image, and no placeholder either.
|
|
2032
|
+
*/
|
|
2033
|
+
interface NoteStoryDrawings {
|
|
2034
|
+
readonly inlineDrawingLayout: InlineDrawingLayoutContext;
|
|
2035
|
+
/**
|
|
2036
|
+
* Per-paragraph projection + RESOURCE token for the break cache key.
|
|
2037
|
+
*
|
|
2038
|
+
* Image resources settle asynchronously, and the authored extent does not move when one
|
|
2039
|
+
* does — so without the resource in the key the cached `pending` lines are served forever
|
|
2040
|
+
* and a decoded picture never reaches the page.
|
|
2041
|
+
*/
|
|
2042
|
+
readonly drawingTokenForParagraph?: (paragraph: OoxmlNode) => string;
|
|
2043
|
+
}
|
|
2044
|
+
interface LayoutNoteStoryOptions {
|
|
2045
|
+
readonly measurer: TextMeasurer;
|
|
2046
|
+
readonly producer: string;
|
|
2047
|
+
readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
|
|
2048
|
+
readonly styleCascade?: StyleCascadeTable;
|
|
2049
|
+
/**
|
|
2050
|
+
* `numbering.xml`, so a `w:numPr` paragraph inside a note resolves a marker.
|
|
2051
|
+
*
|
|
2052
|
+
* Per story, like every other: `createListCounterState` is fresh per walk, so a numbered
|
|
2053
|
+
* list in a footnote starts at `w:start` rather than continuing the body's sequence.
|
|
2054
|
+
*/
|
|
2055
|
+
readonly numberingIndex?: NumberingIndex;
|
|
2056
|
+
readonly defaultTabStopPt?: number;
|
|
2057
|
+
readonly compatibilityMode?: number;
|
|
2058
|
+
/**
|
|
2059
|
+
* The active line-grid pitch of the section that owns the note, in points. Note lines snap
|
|
2060
|
+
* to it like body lines do; absent means no grid.
|
|
2061
|
+
*/
|
|
2062
|
+
readonly lineGridPitchPt?: number;
|
|
2063
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
2064
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
2065
|
+
/**
|
|
2066
|
+
* Same projector seams the BODY walk uses. Without them a `w:hyperlink` or a HYPERLINK
|
|
2067
|
+
* field inside a note painted as plain text — measured, but carrying no link record for
|
|
2068
|
+
* paint to anchor and navigation to activate.
|
|
2069
|
+
*/
|
|
2070
|
+
readonly projectLink?: HyperlinkProjector;
|
|
2071
|
+
/**
|
|
2072
|
+
* Per-part projector, preferred over `projectLink` when `ownerPartName` names the part.
|
|
2073
|
+
*
|
|
2074
|
+
* A `w:hyperlink` in `/word/footnotes.xml` declares its `r:id` in `footnotes.xml.rels`,
|
|
2075
|
+
* so it must resolve there — the body projector answers from the body part's
|
|
2076
|
+
* relationships, and when the two parts assign one id to different targets the footnote
|
|
2077
|
+
* link navigated to the body's target. Same per-part rule pictures follow through
|
|
2078
|
+
* {@link drawingsForPart}.
|
|
2079
|
+
*/
|
|
2080
|
+
readonly projectLinkForPart?: (ownerPartName: string) => HyperlinkProjector | undefined;
|
|
2081
|
+
readonly projectFieldLink?: FieldLinkProjector;
|
|
2082
|
+
/** Field-code inspection projection. @internal */
|
|
2083
|
+
readonly showFieldCodes?: boolean;
|
|
2084
|
+
readonly projectionTokenForParagraphForPart?: (ownerPartName: string, paragraph: OoxmlNode) => string;
|
|
2085
|
+
readonly projectionTokenForTableForPart?: (ownerPartName: string, table: OoxmlNode) => string;
|
|
2086
|
+
/** Document properties for a document-property field inside a note story. */
|
|
2087
|
+
readonly documentProperties?: DocumentProperties;
|
|
2088
|
+
/**
|
|
2089
|
+
* The document's resolved REF inputs, so a footnote's cross-reference paints the live
|
|
2090
|
+
* value the body paints. The flow folds each paragraph's resolved values into its break
|
|
2091
|
+
* key, which is what repaints a note after a renumbering edit it cannot otherwise see.
|
|
2092
|
+
*/
|
|
2093
|
+
readonly refFields?: RefFieldContext;
|
|
2094
|
+
/** Derived display marks for noteRef projection inside the note body. */
|
|
2095
|
+
readonly noteMarks?: NoteMarkContext;
|
|
2096
|
+
/**
|
|
2097
|
+
* Cap on flow height for a single note story (points). Hostile notes must not allocate
|
|
2098
|
+
* unbounded page fragments; overflow is split by the pagination layer, not here.
|
|
2099
|
+
*/
|
|
2100
|
+
readonly maxFlowHeightPt?: number;
|
|
2101
|
+
/** Resolves inline drawing support for the notes part a story lives in. */
|
|
2102
|
+
readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
|
|
2103
|
+
/**
|
|
2104
|
+
* Notes part the story being laid out came from. Set by {@link layoutNoteById} /
|
|
2105
|
+
* {@link layoutNoteSeparator}, which are the callers that hold the part.
|
|
2106
|
+
*/
|
|
2107
|
+
readonly ownerPartName?: string;
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Stable line-id namespace for one note. Body line counters compare these as opaque strings
|
|
2111
|
+
* and must not collide with `line-N` / `hf-…` ids.
|
|
2112
|
+
*/
|
|
2113
|
+
declare function noteLineIdPrefix(noteKind: NoteKind, noteId: number): string;
|
|
2114
|
+
/** Collect normal (body) notes from a notes part, bounded. */
|
|
2115
|
+
declare function normalNotesOf(part: OoxmlPart | null | undefined): readonly OoxmlElement[];
|
|
2116
|
+
/** Find separator / continuationSeparator note body in a notes part. */
|
|
2117
|
+
declare function findSeparatorNote(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator'): OoxmlElement | undefined;
|
|
2118
|
+
/**
|
|
2119
|
+
* Lay one note node out at `contentWidth`.
|
|
2120
|
+
*
|
|
2121
|
+
* Does not paginate. Callers that need splits ask for fragments and cut at paragraph/line
|
|
2122
|
+
* boundaries in the note-pagination layer.
|
|
2123
|
+
*/
|
|
2124
|
+
declare function layoutNoteStory(note: OoxmlNode, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;
|
|
2125
|
+
/** Layout a note by id from a notes part; null when missing. */
|
|
2126
|
+
declare function layoutNoteById(part: OoxmlPart | null | undefined, noteId: number, contentWidth: number, options: LayoutNoteStoryOptions): NoteStoryLayout | null;
|
|
2127
|
+
/**
|
|
2128
|
+
* Word-default paint style for a separator marker.
|
|
2129
|
+
*
|
|
2130
|
+
* Footnote and endnote separators both use a short single rule. A full-width double
|
|
2131
|
+
* border on a body heading (e.g. the comprehensive fixture’s end banner) is ordinary
|
|
2132
|
+
* paragraph `w:pBdr` ownership and must not transfer onto the note separator record.
|
|
2133
|
+
* Authored separator stories with real paragraph/run/border content bypass this via
|
|
2134
|
+
* fragment paint.
|
|
2135
|
+
*/
|
|
2136
|
+
declare function defaultNoteSeparatorRuleStyle(_noteKind: NoteKind, _kind: 'separator' | 'continuationSeparator'): NoteSeparatorRuleStyle;
|
|
2137
|
+
/**
|
|
2138
|
+
* True when a separator note contains only OOXML separator markers (and empty noteRef
|
|
2139
|
+
* atoms Word often authors beside them) — no measurable text or paragraph borders.
|
|
2140
|
+
*/
|
|
2141
|
+
declare function isMarkerOnlySeparatorNote(note: OoxmlNode, displayMode?: RevisionDisplayMode, revisionAuthorFilter?: RevisionAuthorFilter): boolean;
|
|
2142
|
+
/**
|
|
2143
|
+
* Layout the document's separator note, or synthesize a short horizontal rule.
|
|
2144
|
+
*
|
|
2145
|
+
* Single-marker / missing separators emit no paragraph fragments — paint draws the rule
|
|
2146
|
+
* from {@link NoteSeparatorLayout.ruleStyle} + box geometry. Authored separators with
|
|
2147
|
+
* real paragraph/run/border content keep their fragment story (including `w:pBdr`).
|
|
2148
|
+
* Explicitly empty stories and hidden markers do not acquire a default rule.
|
|
2149
|
+
*
|
|
2150
|
+
* When `maxFlowHeightPt` is set and an authored separator exceeds it, the engine fails
|
|
2151
|
+
* closed to a short synthetic rule ({@link note-separator-height-cap}) so note pagination
|
|
2152
|
+
* cannot burn the overflow budget on zero-progress separator-only pages.
|
|
2153
|
+
*/
|
|
2154
|
+
declare function layoutNoteSeparator(part: OoxmlPart | null | undefined, kind: 'separator' | 'continuationSeparator', contentWidth: number, options: LayoutNoteStoryOptions, noteKind: NoteKind, maxFlowHeightPt?: number): NoteSeparatorLayout;
|
|
2155
|
+
/** Default rule geometry for a synthetic / marker-only separator, story-relative. */
|
|
2156
|
+
declare function syntheticSeparatorBox(contentWidth: number, flowHeight: number, kind?: 'separator' | 'continuationSeparator'): LayoutBox;
|
|
2157
|
+
/** Absolute separator box: short rule for marker/synthetic, full width for authored stories. */
|
|
2158
|
+
declare function noteSeparatorAreaBox(separator: NoteSeparatorLayout, contentX: number, contentWidth: number, areaTop: number): LayoutBox;
|
|
2159
|
+
|
|
2160
|
+
interface PageRefHit {
|
|
2161
|
+
readonly noteKind: NoteKind;
|
|
2162
|
+
readonly noteId: number;
|
|
2163
|
+
readonly paragraphId: string;
|
|
2164
|
+
/** Canonical UTF-16 atom offset within the paragraph. */
|
|
2165
|
+
readonly atomOffset: number;
|
|
2166
|
+
readonly customMarkFollows: boolean;
|
|
2167
|
+
readonly sectionIndex: number;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* Bound on reflow attempts for an UNSEEDED document layout pass (cold open).
|
|
2172
|
+
*
|
|
2173
|
+
* Sized for a cold open of a reference-dense document: adoption is a forward fixed-point
|
|
2174
|
+
* iteration whose settled prefix extends a few pages per round, so a legal document with a
|
|
2175
|
+
* hundred footnotes converges in tens of rounds (a 53-page/108-note fixture took 24).
|
|
2176
|
+
* Orbits exit earlier through the fingerprint/envelope checks below, so the cap is a
|
|
2177
|
+
* safety bound, not the expected cost.
|
|
2178
|
+
*/
|
|
2179
|
+
declare const MAX_NOTE_REFLOW_ATTEMPTS = 64;
|
|
2180
|
+
/** Cap on empty pages created solely to drain footnote/endnote overflow. */
|
|
2181
|
+
declare const MAX_NOTE_OVERFLOW_PAGES = 256;
|
|
2182
|
+
/**
|
|
2183
|
+
* Cap on synthetic eachPage mark candidates measured per section (plus actual marks).
|
|
2184
|
+
*
|
|
2185
|
+
* eachPage sequences restart every page, so a page almost never carries more than a
|
|
2186
|
+
* handful of auto-numbered notes. Measuring `numStart .. numStart + N - 1` covers
|
|
2187
|
+
* single→double digit decimal growth and typical roman width peaks (e.g. `viii` vs `ix`)
|
|
2188
|
+
* without scanning hostile `numStart` ranges unboundedly. Derived marks already assigned
|
|
2189
|
+
* for the pass are always included in addition to this window.
|
|
2190
|
+
*/
|
|
2191
|
+
declare const MAX_EACH_PAGE_MARK_CANDIDATES = 12;
|
|
2192
|
+
/**
|
|
2193
|
+
* Why note PAGINATION fell back, widening {@link NoteLayoutFallbackReason} with the reasons that
|
|
2194
|
+
* only arise while distributing notes across pages.
|
|
2195
|
+
*/
|
|
2196
|
+
type NotePaginationFallbackReason = NoteLayoutFallbackReason | 'note-reflow-exhausted' | 'note-area-fragment-limit' | 'note-overflow-page-limit'
|
|
2197
|
+
/**
|
|
2198
|
+
* Overflow/drain iteration placed zero note stories while carry/pending remained —
|
|
2199
|
+
* abort rather than minting blank separator-only sheets up to the page budget.
|
|
2200
|
+
*/
|
|
2201
|
+
| 'note-overflow-stalled'
|
|
2202
|
+
/** A single note line exceeds the full content column; content is not placed overflowing. */
|
|
2203
|
+
| 'note-line-exceeds-page';
|
|
2204
|
+
/**
|
|
2205
|
+
* Everything note pagination needs: the note parts, and the per-section properties governing
|
|
2206
|
+
* them.
|
|
2207
|
+
*
|
|
2208
|
+
* Per-SECTION because numbering, restart rules and placement are all section properties — one
|
|
2209
|
+
* document can restart footnote numbering at every section and end notes at the document end.
|
|
2210
|
+
*/
|
|
2211
|
+
interface NotesLayoutInput {
|
|
2212
|
+
readonly footnotesPart: OoxmlPart | null;
|
|
2213
|
+
readonly endnotesPart: OoxmlPart | null;
|
|
2214
|
+
/** Per-section resolved footnote properties (index-aligned with document sections). */
|
|
2215
|
+
readonly footnotePropsBySection: readonly ResolvedFootnoteProperties[];
|
|
2216
|
+
/** Per-section resolved endnote properties. */
|
|
2217
|
+
readonly endnotePropsBySection: readonly ResolvedEndnoteProperties[];
|
|
2218
|
+
/** Document-level defaults (section 0 fallback). */
|
|
2219
|
+
readonly documentFootnoteProps: ResolvedFootnoteProperties;
|
|
2220
|
+
readonly documentEndnoteProps: ResolvedEndnoteProperties;
|
|
2221
|
+
readonly measurer: TextMeasurer;
|
|
2222
|
+
readonly producer: string;
|
|
2223
|
+
readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
|
|
2224
|
+
readonly styleCascade?: StyleCascadeTable;
|
|
2225
|
+
/** `numbering.xml`, so a `w:numPr` paragraph inside a note resolves a marker. */
|
|
2226
|
+
readonly numberingIndex?: NumberingIndex;
|
|
2227
|
+
/** Per-section active line-grid pitch in points; note lines snap to their section's grid. */
|
|
2228
|
+
readonly lineGridPitchBySection?: readonly (number | undefined)[];
|
|
2229
|
+
readonly defaultTabStopPt?: number;
|
|
2230
|
+
readonly compatibilityMode?: number;
|
|
2231
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
2232
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
2233
|
+
/**
|
|
2234
|
+
* Link projector seams, same as the body walk's. Normally injected by `semantic-layout`
|
|
2235
|
+
* from its own options, so a note's `w:hyperlink` / HYPERLINK field carries the same
|
|
2236
|
+
* sanitized record a body one does instead of painting dead text.
|
|
2237
|
+
*/
|
|
2238
|
+
readonly projectLink?: HyperlinkProjector;
|
|
2239
|
+
/**
|
|
2240
|
+
* Per notes-part link projector, preferred over `projectLink`: a `w:hyperlink` inside
|
|
2241
|
+
* `/word/footnotes.xml` or `/word/endnotes.xml` declares its `r:id` in that part's own
|
|
2242
|
+
* `.rels`, not the body part's. The surface supplies this; the inherited body projector
|
|
2243
|
+
* remains only a fallback for callers without per-part resolution.
|
|
2244
|
+
*/
|
|
2245
|
+
readonly projectLinkForPart?: (ownerPartName: string) => HyperlinkProjector | undefined;
|
|
2246
|
+
/**
|
|
2247
|
+
* Content token over the notes parts' relationship records, standing in for
|
|
2248
|
+
* {@link projectLinkForPart}'s closure in the notes-pass memo. The projector reads
|
|
2249
|
+
* relationship state the pinned part identity cannot see move: a replicated rels-only
|
|
2250
|
+
* change lands without splicing the notes part, so only a content token catches it.
|
|
2251
|
+
* Required whenever `projectLinkForPart` is supplied — without it the memo is disabled,
|
|
2252
|
+
* the same fail-closed rule `drawingsForPart` follows through `drawingLayoutEpoch`.
|
|
2253
|
+
*/
|
|
2254
|
+
readonly linkRelsEpoch?: string;
|
|
2255
|
+
/** Per-notes-part paragraph identity for projected links and metadata fields. */
|
|
2256
|
+
readonly projectionTokenForParagraphForPart?: (ownerPartName: string, paragraph: OoxmlNode) => string;
|
|
2257
|
+
/** Memoized table aggregate counterpart to `projectionTokenForParagraphForPart`. */
|
|
2258
|
+
readonly projectionTokenForTableForPart?: (ownerPartName: string, table: OoxmlNode) => string;
|
|
2259
|
+
/** Combined notes-part freshness signal; outer memo only, never a paragraph producer. */
|
|
2260
|
+
readonly projectionEpoch?: string;
|
|
2261
|
+
readonly projectFieldLink?: FieldLinkProjector;
|
|
2262
|
+
/** Field-code inspection projection. @internal */
|
|
2263
|
+
readonly showFieldCodes?: boolean;
|
|
2264
|
+
/** Document properties for a document-property field inside a note story. */
|
|
2265
|
+
readonly documentProperties?: DocumentProperties;
|
|
2266
|
+
/**
|
|
2267
|
+
* The document's resolved REF inputs, so a footnote's cross-reference paints the live
|
|
2268
|
+
* value the body paints. Normally injected by `semantic-layout` from the context it built
|
|
2269
|
+
* over the body and note stories; its values token joins the notes-pass fingerprint so a
|
|
2270
|
+
* renumbering edit repaints the notes that cite the renumbered target.
|
|
2271
|
+
*/
|
|
2272
|
+
readonly refFields?: RefFieldContext;
|
|
2273
|
+
/**
|
|
2274
|
+
* Inline drawing support per notes part. Absent means note paragraphs flow without
|
|
2275
|
+
* drawing records, which is what a headless caller with no image port wants.
|
|
2276
|
+
*/
|
|
2277
|
+
readonly drawingsForPart?: (ownerPartName: string) => NoteStoryDrawings | undefined;
|
|
2278
|
+
/**
|
|
2279
|
+
* Part-level drawing epoch covering the notes parts, standing in for
|
|
2280
|
+
* {@link drawingsForPart}'s closure in the notes-pass memo (the closure is rebuilt per
|
|
2281
|
+
* pass, so only an epoch can say "the drawing state did not move"). A caller that
|
|
2282
|
+
* supplies `drawingsForPart` without this keeps the rebuild path.
|
|
2283
|
+
*/
|
|
2284
|
+
readonly drawingLayoutEpoch?: string;
|
|
2285
|
+
}
|
|
2286
|
+
/**
|
|
2287
|
+
* The layout with notes attached, plus any fallbacks taken and the mark context used.
|
|
2288
|
+
*
|
|
2289
|
+
* The marks come back because they feed the body's incremental cache tokens: a note number that
|
|
2290
|
+
* changed must invalidate the paragraph that references it.
|
|
2291
|
+
*/
|
|
2292
|
+
interface NotesAttachResult {
|
|
2293
|
+
readonly layout: SemanticLayout;
|
|
2294
|
+
readonly fallbackReasons: readonly NotePaginationFallbackReason[];
|
|
2295
|
+
/** Mark context used for the final body projection (for incremental cache tokens). */
|
|
2296
|
+
readonly noteMarks: NoteMarkContext;
|
|
2297
|
+
}
|
|
2298
|
+
/**
|
|
2299
|
+
* Whether a paragraph fragment owns a note atom at `atomOffset`.
|
|
2300
|
+
*
|
|
2301
|
+
* Fragment ranges are half-open for content ownership: `[start, end)`. The shared
|
|
2302
|
+
* boundary offset belongs to the later fragment (downstream affinity), matching line
|
|
2303
|
+
* splits where `fragmentStart = previous.range.end`.
|
|
2304
|
+
*/
|
|
2305
|
+
declare function fragmentOwnsAtomOffset(fragment: ParagraphFragmentRecord, atomOffset: number): boolean;
|
|
2306
|
+
/** Paragraph-id → refs index for linear {@link filterRefsOnPage} over a layout pass. */
|
|
2307
|
+
type PageRefIndex = ReadonlyMap<string, readonly PageRefHit[]>;
|
|
2308
|
+
/** Build a reusable paragraph-id index (document order preserved per paragraph). */
|
|
2309
|
+
declare function buildPageRefIndex(allRefs: readonly PageRefHit[]): PageRefIndex;
|
|
2310
|
+
/**
|
|
2311
|
+
* Collect note references that appear in laid-out body fragments on a page.
|
|
2312
|
+
* Matches {@link ParagraphFragmentRecord.range} ownership (half-open + boundary affinity).
|
|
2313
|
+
*
|
|
2314
|
+
* Pass {@link buildPageRefIndex} result as `refIndex` for O(fragments + matching refs)
|
|
2315
|
+
* instead of scanning every document ref against every page fragment.
|
|
2316
|
+
*/
|
|
2317
|
+
declare function filterRefsOnPage(page: PageRecord, allRefs: readonly PageRefHit[], refIndex?: PageRefIndex): readonly PageRefHit[];
|
|
2318
|
+
/**
|
|
2319
|
+
* Compute per-page bottom reserves (points) needed for footnotes given a provisional layout.
|
|
2320
|
+
* Used by the bounded reflow loop before final attach.
|
|
2321
|
+
*
|
|
2322
|
+
* Height is measured against a column-derived note budget (not leftover body slack). Measuring
|
|
2323
|
+
* from slack makes `stable` true on the first pass and never shrinks the body — references and
|
|
2324
|
+
* notes then compete for the same band. Oversized notes still split/continue within the budget;
|
|
2325
|
+
* {@link MIN_FOOTNOTE_BODY_BAND_PT} keeps a body band so reflow cannot chase blank sheets.
|
|
2326
|
+
*
|
|
2327
|
+
* Answers under the STRICT co-location policy: every footnote starts on the page that
|
|
2328
|
+
* references it. The bounded orphan-pair refinement is a second phase of the internal
|
|
2329
|
+
* reflow loop, which owns the strict fixed point the refinement needs as its starting
|
|
2330
|
+
* point and as its fallback, so a single call from outside that loop cannot reach it.
|
|
2331
|
+
*/
|
|
2332
|
+
declare function computeFootnoteReserves(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, noteMarks: NoteMarkContext,
|
|
2333
|
+
/** Session-carried notes memo (opaque; owned by this module). */
|
|
2334
|
+
passMemo?: unknown,
|
|
2335
|
+
/**
|
|
2336
|
+
* The reserve map `layout` was laid under. Feeds the hold-out's observed-refusal test
|
|
2337
|
+
* (a block the body pass declined to split under a known budget only moves whole);
|
|
2338
|
+
* absent, the hold-out releases the ambiguous cases — a caller outside the reflow loop
|
|
2339
|
+
* must not manufacture holds the loop never observed.
|
|
2340
|
+
*/
|
|
2341
|
+
previousReserves?: ReadonlyMap<number, number>): {
|
|
2342
|
+
readonly reserves: ReadonlyMap<number, number>;
|
|
2343
|
+
readonly stable: boolean;
|
|
2344
|
+
readonly reasons: readonly NotePaginationFallbackReason[];
|
|
2345
|
+
};
|
|
2346
|
+
/**
|
|
2347
|
+
* Attach footnote/endnote areas onto a body layout. Does not re-paginate — callers that
|
|
2348
|
+
* need reservation must re-run body layout with {@link pageBottomReserves} first.
|
|
2349
|
+
*/
|
|
2350
|
+
declare function attachNotesToLayout(layout: SemanticLayout, allRefs: readonly PageRefHit[], input: NotesLayoutInput, options?: {
|
|
2351
|
+
readonly fallbackReasons?: readonly NotePaginationFallbackReason[];
|
|
2352
|
+
readonly paragraphSectionIndex?: ReadonlyMap<string, number>;
|
|
2353
|
+
/**
|
|
2354
|
+
* Session-carried notes memo, already validated against `allRefs` and `input`.
|
|
2355
|
+
* Opaque at this boundary; the shape is owned by this module.
|
|
2356
|
+
*/
|
|
2357
|
+
readonly memo?: unknown;
|
|
2358
|
+
}): NotesAttachResult;
|
|
2359
|
+
/**
|
|
2360
|
+
* Build a continuous (pre-page) mark context for the first body layout pass.
|
|
2361
|
+
* eachPage reserves digit width; {@link reprojectBodyNoteMarks} publishes final marks
|
|
2362
|
+
* onto body citations after page assignment in {@link attachNotesToLayout}.
|
|
2363
|
+
*/
|
|
2364
|
+
declare function provisionalNoteMarks(refs: readonly PageRefHit[], input: NotesLayoutInput): NoteMarkContext;
|
|
2365
|
+
|
|
2366
|
+
/** Inputs for projecting notes through the same semantic layout pass. @public */
|
|
2367
|
+
interface CreateDocumentNotesInputOptions {
|
|
2368
|
+
readonly view: HeadlessDocumentView;
|
|
2369
|
+
readonly measurer: TextMeasurer;
|
|
2370
|
+
readonly producer: string;
|
|
2371
|
+
readonly cache: Parameters<typeof layoutHeaderFooterStory>[4];
|
|
2372
|
+
readonly styleCascade?: () => StyleCascadeTable | undefined;
|
|
2373
|
+
readonly numberingIndex?: () => NumberingIndex;
|
|
2374
|
+
readonly defaultTabStopPt?: number;
|
|
2375
|
+
readonly compatibilityMode?: number;
|
|
2376
|
+
readonly inlineDrawingLayoutForPart?: (partName: string) => InlineDrawingLayoutContext | undefined;
|
|
2377
|
+
readonly drawingTokenForParagraphForPart?: (partName: string, paragraph: OoxmlNode) => string;
|
|
2378
|
+
readonly drawingLayoutEpochForPart?: (partName: string) => string;
|
|
2379
|
+
/** Link/property projection and its inseparable cache identities. */
|
|
2380
|
+
readonly linkProjectors: DocumentLinkProjectors;
|
|
2381
|
+
readonly projectFieldLink?: NotesLayoutInput['projectFieldLink'];
|
|
2382
|
+
/** Field-code inspection projection. @internal */
|
|
2383
|
+
readonly showFieldCodes?: boolean;
|
|
2384
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
2385
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
2386
|
+
}
|
|
2387
|
+
/** Build note layout input from the neutral package view, or undefined when unused. @public */
|
|
2388
|
+
declare function createDocumentNotesInput(options: CreateDocumentNotesInputOptions): NotesLayoutInput | undefined;
|
|
2389
|
+
/** Resolve a relationship in a non-body story for shared link projection. @public */
|
|
2390
|
+
declare function documentRelationshipTargetIn(view: HeadlessDocumentView, partName: string, relationshipId: string): ReturnType<typeof relationshipTargetIn>;
|
|
2391
|
+
|
|
2392
|
+
/** Sanitized field link retained for interactive consumers. @public */
|
|
2393
|
+
interface RegisteredFieldLink {
|
|
2394
|
+
readonly id: string;
|
|
2395
|
+
readonly paragraphId: '';
|
|
2396
|
+
readonly start: 0;
|
|
2397
|
+
readonly end: 0;
|
|
2398
|
+
readonly text: '';
|
|
2399
|
+
readonly kind: 'external' | 'internal' | 'unresolved';
|
|
2400
|
+
readonly href: string | null;
|
|
2401
|
+
readonly authored: string;
|
|
2402
|
+
readonly anchor?: string;
|
|
2403
|
+
readonly tooltip?: string;
|
|
2404
|
+
}
|
|
2405
|
+
/** Per-layout registry for projected HYPERLINK fields. @public */
|
|
2406
|
+
interface FieldLinkRegistry {
|
|
2407
|
+
project(spec: HyperlinkFieldSpec): SpanLinkRecord | null;
|
|
2408
|
+
linkById(linkId: string): RegisteredFieldLink | null;
|
|
2409
|
+
clear(): void;
|
|
2410
|
+
}
|
|
2411
|
+
/** Create a bounded, content-keyed field-link registry. @public */
|
|
2412
|
+
declare function createFieldLinkRegistry(): FieldLinkRegistry;
|
|
2413
|
+
|
|
2414
|
+
/**
|
|
2415
|
+
* Strict hex fill: exactly six hex digits. Rejects `auto`, `nil`, and any non-hex payload
|
|
2416
|
+
* (CSS functions, URLs, short hex, theme tokens).
|
|
2417
|
+
*/
|
|
2418
|
+
declare function resolveStrictHexFill(raw: string | undefined): string | undefined;
|
|
2419
|
+
/**
|
|
2420
|
+
* Resolve a `w:shd` attribute bag to a validated RRGGBB fill, or undefined.
|
|
2421
|
+
*
|
|
2422
|
+
* `w:themeFill` is a REFERENCE, and Word always writes the value it resolved to alongside
|
|
2423
|
+
* it: `<w:shd w:val="clear" w:fill="D9E2F3" w:themeFill="accent1" w:themeFillTint="33"/>`.
|
|
2424
|
+
* Reading `w:fill` in that case is not inventing a colour from the theme — it is reading
|
|
2425
|
+
* the colour the producer computed. Dropping the fill because a theme reference sat next
|
|
2426
|
+
* to it left every accent-shaded cell and paragraph unpainted.
|
|
2427
|
+
*
|
|
2428
|
+
* `val="nil"` clears shading. Pattern vals are not rendered; a valid solid `fill` still
|
|
2429
|
+
* paints as a clear fill until pattern support lands. A theme reference with no usable
|
|
2430
|
+
* `w:fill` still resolves to nothing rather than a guess.
|
|
2431
|
+
*/
|
|
2432
|
+
declare function resolveOoxmlShadingFill(attributes: Readonly<Record<string, string>> | undefined): string | undefined;
|
|
2433
|
+
/** Read `w:shd` from a typed/generic element (table `tcPr`, nested `pPr`, …). */
|
|
2434
|
+
declare function shadingFillFromElement(shd: OoxmlElement | undefined): string | undefined;
|
|
2435
|
+
/**
|
|
2436
|
+
* Resolve paragraph shading from cascaded flat `w:pPr` properties.
|
|
2437
|
+
*
|
|
2438
|
+
* Later `w:shd` entries win (defaults → style → direct), matching spacing/border cascade.
|
|
2439
|
+
*/
|
|
2440
|
+
declare function paragraphShading(props: readonly OoxmlProperty[]): string | undefined;
|
|
2441
|
+
/**
|
|
2442
|
+
* Page-content box for paragraph shading: union of this fragment's line boxes.
|
|
2443
|
+
*
|
|
2444
|
+
* Excludes collapsed before/after spacing and bottom-border extent so the painted band
|
|
2445
|
+
* matches Word's content-area fill (character shading height on a single line).
|
|
2446
|
+
*/
|
|
2447
|
+
declare function paragraphShadingBox(lines: readonly {
|
|
2448
|
+
readonly box: LayoutBox;
|
|
2449
|
+
}[], x: number, width: number): LayoutBox | undefined;
|
|
2450
|
+
|
|
2451
|
+
/**
|
|
2452
|
+
* Everything a layout pass needs beyond the document itself.
|
|
2453
|
+
*
|
|
2454
|
+
* `measurer` is the only required field — layout is DOM-free and measures through whatever is
|
|
2455
|
+
* injected here, which is what lets the same code paginate on a server and in a browser.
|
|
2456
|
+
*/
|
|
2457
|
+
interface SemanticLayoutOptions {
|
|
2458
|
+
readonly geometry?: PageGeometry;
|
|
2459
|
+
readonly measurer: TextMeasurer;
|
|
2460
|
+
/**
|
|
2461
|
+
* Reuse of measured-and-broken paragraphs across revisions (task 9.2).
|
|
2462
|
+
*
|
|
2463
|
+
* Only the BREAK is cached. Placement — y, fragments, page cuts — is always redone, so
|
|
2464
|
+
* an edit high in the document still repaginates everything below it while paragraphs
|
|
2465
|
+
* nobody touched are never measured again.
|
|
2466
|
+
*/
|
|
2467
|
+
readonly cache?: ParagraphLayoutCache<readonly PendingLine[]>;
|
|
2468
|
+
/**
|
|
2469
|
+
* Collector for the cache keys a pass wants retained, instead of retaining directly.
|
|
2470
|
+
* Supplied by the multi-section orchestrator, which retains once over the union —
|
|
2471
|
+
* retaining per section evicted every other section's entries. `false` skips retention
|
|
2472
|
+
* for this pass entirely (the orchestrator strides sweeps). See
|
|
2473
|
+
* `retainLiveBreakKeys`.
|
|
2474
|
+
*/
|
|
2475
|
+
readonly retainKeys?: Set<string> | false;
|
|
2476
|
+
/**
|
|
2477
|
+
* Part-level drawing projection/resource epoch for the section prepass memo.
|
|
2478
|
+
*
|
|
2479
|
+
* Moves whenever any drawing projection or resource state in the part does, standing in
|
|
2480
|
+
* for the per-paragraph drawing tokens the memo would otherwise have to recompute to
|
|
2481
|
+
* validate. A caller that supplies `drawingTokenForParagraph` without this epoch keeps
|
|
2482
|
+
* the recompute path — the memo must never miss a token move it cannot see.
|
|
2483
|
+
*/
|
|
2484
|
+
readonly drawingLayoutEpoch?: string;
|
|
2485
|
+
/** Part-level freshness signal for paragraph-local semantic projection tokens. */
|
|
2486
|
+
readonly projectionEpoch?: string;
|
|
2487
|
+
/**
|
|
2488
|
+
* Who produced the measurements, folded into every cache key.
|
|
2489
|
+
*
|
|
2490
|
+
* A font arriving after first paint changes every advance in the document while no
|
|
2491
|
+
* content changes; without this the cache would serve the pre-font layout forever.
|
|
2492
|
+
*/
|
|
2493
|
+
readonly producer?: string;
|
|
2494
|
+
/**
|
|
2495
|
+
* Incremental placement across revisions (task 9.3).
|
|
2496
|
+
*
|
|
2497
|
+
* Holds the previous complete layout and a flow checkpoint per paragraph, so a pass can
|
|
2498
|
+
* resume just before the first affected paragraph instead of re-placing the document from
|
|
2499
|
+
* the top, and can stop early when the flow reconverges with the previous run.
|
|
2500
|
+
*
|
|
2501
|
+
* Multi-section documents keep per-section child sessions on {@link LayoutSession.multi}.
|
|
2502
|
+
*/
|
|
2503
|
+
readonly session?: LayoutSession;
|
|
2504
|
+
/** Header/footer stories to attach per page; absent means no furniture. */
|
|
2505
|
+
readonly furniture?: PageFurniture;
|
|
2506
|
+
/**
|
|
2507
|
+
* Which tracked revisions this pass resolves away (ECMA-376 §17.13).
|
|
2508
|
+
*
|
|
2509
|
+
* `all-markup` (the default) lays out both halves of every change. `proposed` lays out what
|
|
2510
|
+
* the document becomes if every change is accepted; `original` what it was before any of
|
|
2511
|
+
* them. Both are LAYOUT INPUTS: neither applies a `TreeDocOp` nor publishes a `ModelChange`,
|
|
2512
|
+
* so a user who switches to the proposed result, saves, and sends the file has not silently
|
|
2513
|
+
* accepted every proposal in it.
|
|
2514
|
+
*/
|
|
2515
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
2516
|
+
/** Reviewers whose revisions render as their accepted projection. */
|
|
2517
|
+
readonly revisionAuthorFilter?: RevisionAuthorFilter;
|
|
2518
|
+
/**
|
|
2519
|
+
* Per-section furniture, index-aligned with `enumerateDocumentSections`.
|
|
2520
|
+
*
|
|
2521
|
+
* When present, multi-section layout attaches each section's own headers/footers (after
|
|
2522
|
+
* OOXML inheritance). `furniture` remains the single-section / last-section fallback.
|
|
2523
|
+
*/
|
|
2524
|
+
readonly sectionFurniture?: readonly (PageFurniture | undefined)[];
|
|
2525
|
+
/**
|
|
2526
|
+
* `w:evenAndOddHeaders` from settings.xml. A section that restarts its page numbering then
|
|
2527
|
+
* starts on a sheet whose position has the parity of the restarted number. When absent,
|
|
2528
|
+
* layout reads the flag from the section furniture.
|
|
2529
|
+
*/
|
|
2530
|
+
readonly evenAndOddHeaders?: boolean;
|
|
2531
|
+
/** Authored column count/gap for anchored `relativeFrom="column"` frame resolution. */
|
|
2532
|
+
readonly sectionColumns?: SectionColumns;
|
|
2533
|
+
/** Active section line-grid pitch: line-unit margins and line snapping. Absent: no grid. */
|
|
2534
|
+
readonly paragraphLineUnitPt?: number;
|
|
2535
|
+
/**
|
|
2536
|
+
* This section's resolved `w:pgBorders`, for the frame each of its sheets publishes.
|
|
2537
|
+
*
|
|
2538
|
+
* Section-scoped, not document-scoped: `w:display="firstPage"` means the first page of THIS
|
|
2539
|
+
* section, and multi-section layout hands each pass its own.
|
|
2540
|
+
*/
|
|
2541
|
+
readonly sectionPageBorders?: SectionPageBorders;
|
|
2542
|
+
/**
|
|
2543
|
+
* Styles-part cascade table (docDefaults + `w:style` last-wins). Absent keeps direct
|
|
2544
|
+
* formatting only — the pre-cascade behaviour, used by unit tests that never open a
|
|
2545
|
+
* package.
|
|
2546
|
+
*/
|
|
2547
|
+
readonly styleCascade?: StyleCascadeTable;
|
|
2548
|
+
/**
|
|
2549
|
+
* Projection of `/word/numbering.xml`. Absent keeps pre-list behaviour (no markers /
|
|
2550
|
+
* level indents). The index is immutable for a session; list counter state is derived
|
|
2551
|
+
* per layout pass from document order.
|
|
2552
|
+
*/
|
|
2553
|
+
readonly numberingIndex?: NumberingIndex;
|
|
2554
|
+
/**
|
|
2555
|
+
* Optional precomputed list items for the body story. When absent and
|
|
2556
|
+
* {@link numberingIndex} is set, layout walks the full body (including table cells)
|
|
2557
|
+
* once so counters continue across section boundaries and table document order.
|
|
2558
|
+
*/
|
|
2559
|
+
readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
|
|
2560
|
+
/**
|
|
2561
|
+
* `w:settings/w:defaultTabStop` in points (ECMA-376 §17.15.1.25); absent keeps the 0.5"
|
|
2562
|
+
* schema default.
|
|
2563
|
+
*
|
|
2564
|
+
* It arrives as an option because the paragraph cascade cannot see `settings.xml`. A
|
|
2565
|
+
* metric-locale template declares 1134 twips (2cm) and every default-interval tab in the
|
|
2566
|
+
* document belongs on that grid. Constant for a session — the settings part is immutable
|
|
2567
|
+
* here — which is why the prepared-block memo does not key on it.
|
|
2568
|
+
*/
|
|
2569
|
+
readonly defaultTabStopPt?: number;
|
|
2570
|
+
/** Explicit Word compatibility mode; absent retains existing geometry. */
|
|
2571
|
+
readonly compatibilityMode?: number;
|
|
2572
|
+
/**
|
|
2573
|
+
* Turns a typed `w:hyperlink` into the SANITIZED record its spans carry.
|
|
2574
|
+
*
|
|
2575
|
+
* An option because resolving `r:id` needs the package's relationships, which layout — a
|
|
2576
|
+
* per-part walk — cannot see. Absent means link runs still measure, break and paint;
|
|
2577
|
+
* they simply carry no link, so nothing is clickable and no text is lost. That is the
|
|
2578
|
+
* degradation a headless test or a furniture-only pass gets, and it is the safe one.
|
|
2579
|
+
*/
|
|
2580
|
+
readonly projectLink?: HyperlinkProjector;
|
|
2581
|
+
/**
|
|
2582
|
+
* Turns a parsed HYPERLINK field instruction into the SANITIZED record its result carries.
|
|
2583
|
+
*
|
|
2584
|
+
* An option for the same reason as {@link projectLink}: the raw target must cross the
|
|
2585
|
+
* surface's href trust boundary, which layout cannot see. Absent means the field's cached
|
|
2586
|
+
* result still measures, breaks and paints — it simply is not clickable.
|
|
2587
|
+
*/
|
|
2588
|
+
readonly projectFieldLink?: FieldLinkProjector;
|
|
2589
|
+
/** Field-code inspection projection. @internal */
|
|
2590
|
+
readonly showFieldCodes?: boolean;
|
|
2591
|
+
/**
|
|
2592
|
+
* How the pass addresses saved field results (DATE, MERGEFIELD, HYPERLINK display text, see
|
|
2593
|
+
* `FieldResultsMode`). `atomic` (the default) lays every field out as one model unit.
|
|
2594
|
+
* `editable` lays a saved result out as ordinary text at the offsets the store's editable
|
|
2595
|
+
* mode addresses, so a caret, a hit, and a selection inside it land on its characters. The
|
|
2596
|
+
* mode is folded into every cache key.
|
|
2597
|
+
*/
|
|
2598
|
+
readonly fieldResults?: FieldResultsMode;
|
|
2599
|
+
/** @internal */
|
|
2600
|
+
readonly fieldCodeRanges?: FieldCodeRanges;
|
|
2601
|
+
/** @internal Word TOC character-style suppression. */
|
|
2602
|
+
readonly tocLinkStyleRanges?: TocLinkRanges;
|
|
2603
|
+
/**
|
|
2604
|
+
* The document's parsed metadata, for document-property fields (TITLE, AUTHOR, …). Read once
|
|
2605
|
+
* by the surface and shared across body, table, note and header/footer flows.
|
|
2606
|
+
*/
|
|
2607
|
+
readonly documentProperties?: DocumentProperties;
|
|
2608
|
+
/**
|
|
2609
|
+
* Footnote/endnote layout input. When present, body layout projects note marks and a
|
|
2610
|
+
* post-pass attaches note areas (with bounded reflow for pageBottom reservation).
|
|
2611
|
+
*/
|
|
2612
|
+
readonly notes?: NotesLayoutInput;
|
|
2613
|
+
/**
|
|
2614
|
+
* Per-page bottom reserves (points) subtracted from content height before line placement,
|
|
2615
|
+
* keyed by DOCUMENT page index. A section's pass reads its own slice through
|
|
2616
|
+
* `pageIndexStart`. Produced by the note reflow loop; absent means full content column.
|
|
2617
|
+
*/
|
|
2618
|
+
readonly pageBottomReserves?: ReadonlyMap<number, number>;
|
|
2619
|
+
/** Derived note marks for body/note projection (provisional or final). */
|
|
2620
|
+
readonly noteMarks?: NoteMarkContext;
|
|
2621
|
+
/** Inline drawing projection for typed `w:drawing` / `wp:inline` nodes. */
|
|
2622
|
+
readonly inlineDrawingLayout?: InlineDrawingLayoutContext;
|
|
2623
|
+
/** Per-paragraph drawing projection/resource token for break cache keys. */
|
|
2624
|
+
readonly drawingTokenForParagraph?: (paragraph: OoxmlNode) => string;
|
|
2625
|
+
/** Per-paragraph identity for projected links and live document-property text. */
|
|
2626
|
+
readonly projectionTokenForParagraph?: (paragraph: OoxmlNode) => string;
|
|
2627
|
+
/** Memoized aggregate projection identity for an immutable table subtree. */
|
|
2628
|
+
readonly projectionTokenForTable?: (table: OoxmlNode) => string;
|
|
2629
|
+
/** @deprecated Prefer {@link drawingTokenForParagraph}. */
|
|
2630
|
+
readonly drawingLayoutToken?: string;
|
|
2631
|
+
/** Internal: reflow pass index; negative values establish anchor pages before backward wrap. */
|
|
2632
|
+
readonly drawingExclusionPass?: number;
|
|
2633
|
+
/** Internal: converged exclusion zones — skips the reflow loop when set with zones. */
|
|
2634
|
+
readonly drawingExclusionConverged?: boolean;
|
|
2635
|
+
/** Internal: exclusion zones from the prior reflow pass, keyed by page index. */
|
|
2636
|
+
readonly drawingExclusionZonesByPage?: ReadonlyMap<number, readonly ExclusionZone[]>;
|
|
2637
|
+
/** Canonical drawing traversal order within the owner story part. */
|
|
2638
|
+
readonly drawingSourceOrder?: ReadonlyMap<string, number>;
|
|
2639
|
+
/**
|
|
2640
|
+
* TOC instruction-only opening and ending chrome paragraph ids. Empty chrome suppresses
|
|
2641
|
+
* its placeholder line; opening paragraph marks after a separator remain visible result lines.
|
|
2642
|
+
* Tree nodes stay intact for refresh/save.
|
|
2643
|
+
*/
|
|
2644
|
+
readonly tocFieldChromeParagraphIds?: ReadonlySet<string>;
|
|
2645
|
+
/**
|
|
2646
|
+
* Begin-paragraph ids of empty TOCs. These keep one layout line so paint can host an
|
|
2647
|
+
* identifiable empty-TOC furniture placeholder (overrides chrome suppression).
|
|
2648
|
+
*/
|
|
2649
|
+
readonly emptyTocPlaceholderParagraphIds?: ReadonlySet<string>;
|
|
2650
|
+
/**
|
|
2651
|
+
* Empty result-paragraph ids inside empty TOCs. Suppressed like field chrome so blank
|
|
2652
|
+
* cached rows do not stack under the empty placeholder.
|
|
2653
|
+
*/
|
|
2654
|
+
readonly emptyTocSuppressedResultParagraphIds?: ReadonlySet<string>;
|
|
2655
|
+
/**
|
|
2656
|
+
* Lay out only the first `bodyBlockLimit` body blocks. A large open lays out a growing
|
|
2657
|
+
* prefix in separate tasks, each pass resuming from the last, so the page stays
|
|
2658
|
+
* responsive; the final pass has no limit. @internal
|
|
2659
|
+
*/
|
|
2660
|
+
readonly bodyBlockLimit?: number;
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
/**
|
|
2664
|
+
* A deterministic measurer for tests and headless use.
|
|
2665
|
+
*
|
|
2666
|
+
* Monospace by construction: every character is the same width and every line the same
|
|
2667
|
+
* height, scaled by `w:sz` when present. Real shaping is the HarfBuzz path; this exists so
|
|
2668
|
+
* layout behaviour can be asserted without a font stack deciding the answer.
|
|
2669
|
+
*/
|
|
2670
|
+
declare function createFixedMeasurer(charWidth?: number, lineHeight?: number): TextMeasurer;
|
|
2671
|
+
|
|
2672
|
+
/**
|
|
2673
|
+
* Every content control a part declares, in document order, WITHOUT geometry.
|
|
2674
|
+
*
|
|
2675
|
+
* For callers that want the ROSTER rather than the rectangles — the Tab walk through form
|
|
2676
|
+
* fields is the one, and it needs story order, not boxes. `fragments` comes back empty here
|
|
2677
|
+
* by design; a caller that needs geometry reads `layout.contentControls`, which covers every
|
|
2678
|
+
* story the layout draws.
|
|
2679
|
+
*/
|
|
2680
|
+
declare function contentControlRecordsInPart(part: OoxmlPart,
|
|
2681
|
+
/**
|
|
2682
|
+
* Keep only controls holding one of these paragraphs.
|
|
2683
|
+
*
|
|
2684
|
+
* For a notes PART, which holds every note in the document rather than one story. Rostered
|
|
2685
|
+
* whole, Tab walked out of the open footnote and into the next one, and the keystrokes after
|
|
2686
|
+
* it landed in a note the reader was not in.
|
|
2687
|
+
*/
|
|
2688
|
+
withinParagraphs?: ReadonlySet<string>): readonly ContentControlBoundaryRecord$1[];
|
|
2689
|
+
/**
|
|
2690
|
+
* The innermost content control holding `paragraphId`, in `part`.
|
|
2691
|
+
*
|
|
2692
|
+
* Deepest wins, matching the geometry path's innermost-by-nesting rule: a control inside a
|
|
2693
|
+
* control is the one the caret is actually in.
|
|
2694
|
+
*/
|
|
2695
|
+
declare function contentControlHoldingParagraph(part: OoxmlPart, paragraphId: string): ContentControlBoundaryRecord$1 | null;
|
|
2696
|
+
|
|
2697
|
+
/**
|
|
2698
|
+
* Lay one story part out into pages.
|
|
2699
|
+
*
|
|
2700
|
+
* The engine's layout entry point. Walks body, header, footer and note roots, flattens block
|
|
2701
|
+
* SDTs, paginates tables with header-row repeats and vertical merges, and resolves every
|
|
2702
|
+
* paragraph through the style cascade.
|
|
2703
|
+
*
|
|
2704
|
+
* Incremental when given a {@link LayoutSession}: per-block cache keys plus flow checkpoints mean
|
|
2705
|
+
* a pass that changes nothing returns the previous pages by identity.
|
|
2706
|
+
*/
|
|
2707
|
+
declare function layoutSemanticDocument(part: OoxmlPart, revision: number, options: SemanticLayoutOptions): SemanticLayout;
|
|
2708
|
+
|
|
2709
|
+
/** Soft ceiling on an expanded marker string (codepoints). */
|
|
2710
|
+
declare const MAX_MARKER_TEXT_LENGTH = 64;
|
|
2711
|
+
/** Soft ceiling on authored `w:lvlText` before expansion. */
|
|
2712
|
+
declare const MAX_LVL_TEXT_LENGTH = 64;
|
|
2713
|
+
/** Clamp a list counter into a safe non-negative integer Word can format. */
|
|
2714
|
+
declare function clampListValue(value: number): number;
|
|
2715
|
+
/** `decimal` (§17.18.59). Clamped, because the counter derives from file-declared restarts. */
|
|
2716
|
+
declare function formatDecimal(value: number): string;
|
|
2717
|
+
/** `decimalZero` (§17.18.59): single digits zero-padded to two. */
|
|
2718
|
+
declare function formatDecimalZero(value: number): string;
|
|
2719
|
+
/**
|
|
2720
|
+
* `upperRoman` (§17.18.59). Saturates at 3999, the largest value classical Roman numerals
|
|
2721
|
+
* express — beyond it there is nothing correct to emit, so it stops rather than inventing
|
|
2722
|
+
* notation.
|
|
2723
|
+
*/
|
|
2724
|
+
declare function formatUpperRoman(value: number): string;
|
|
2725
|
+
/** `lowerRoman` (§17.18.59). */
|
|
2726
|
+
declare function formatLowerRoman(value: number): string;
|
|
2727
|
+
/**
|
|
2728
|
+
* Excel-style letter sequence: 1→A … 26→Z, 27→AA.
|
|
2729
|
+
* Caps length so a hostile counter cannot grow without bound.
|
|
2730
|
+
*/
|
|
2731
|
+
declare function formatUpperLetter(value: number): string;
|
|
2732
|
+
/** `lowerLetter` (§17.18.59): the {@link formatUpperLetter} sequence, lower-cased. */
|
|
2733
|
+
declare function formatLowerLetter(value: number): string;
|
|
2734
|
+
/**
|
|
2735
|
+
* Format one counter for a `w:numFmt` value (ST_NumberFormat, §17.18.59).
|
|
2736
|
+
*
|
|
2737
|
+
* `none` prints NOTHING — it is the format Word uses for a level that contributes only
|
|
2738
|
+
* literal text, and formatting it as decimal invents a number the document never had.
|
|
2739
|
+
* `bullet` is not formatted here — callers use the literal `lvlText`.
|
|
2740
|
+
*
|
|
2741
|
+
* The remaining enumerants (`japaneseCounting`, `thaiNumbers`, `ganada`, …) are
|
|
2742
|
+
* per-script numeral sequences we do not carry glyph tables for. They fall back to decimal
|
|
2743
|
+
* deliberately: the ORDINAL is still the authored one, only the script differs, which reads
|
|
2744
|
+
* as a number in the wrong alphabet rather than as a missing or wrong marker.
|
|
2745
|
+
*/
|
|
2746
|
+
declare function formatNumFmt(numFmt: string, value: number): string;
|
|
2747
|
+
/**
|
|
2748
|
+
* Expand `w:lvlText` placeholders `%1`…`%9` using per-level counters and formats.
|
|
2749
|
+
*
|
|
2750
|
+
* `formats[i]` / `counters[i]` correspond to ilvl `i`. Missing slots use decimal / 1.
|
|
2751
|
+
* Literal percent signs that are not `%1`…`%9` are kept. Output is hard-capped.
|
|
2752
|
+
*/
|
|
2753
|
+
declare function expandLvlText(lvlText: string, counters: readonly number[], formats: readonly string[]): string;
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* The stack used when a run names no font (or names one the sink refuses).
|
|
2757
|
+
*
|
|
2758
|
+
* Paint only sets `font-family` when `w:rFonts` supplies a validated name, so an unstyled
|
|
2759
|
+
* run inherits the surrounding face. Measuring one stack and painting another drifts every
|
|
2760
|
+
* advance; this is the Word-like Latin fallback the canvas path measures against.
|
|
2761
|
+
*/
|
|
2762
|
+
declare const DEFAULT_CANVAS_FONT_STACK = "Calibri, Carlito, Helvetica, Arial, sans-serif";
|
|
2763
|
+
/**
|
|
2764
|
+
* The canvas text-metrics surface the editor injects.
|
|
2765
|
+
*
|
|
2766
|
+
* Structural subset of `CanvasRenderingContext2D` — declared here so the layout lane stays
|
|
2767
|
+
* off the DOM lib. Hosts pass a real 2d context; tests pass a controllable mock.
|
|
2768
|
+
*/
|
|
2769
|
+
interface CanvasTextMetrics {
|
|
2770
|
+
readonly width: number;
|
|
2771
|
+
readonly actualBoundingBoxLeft?: number;
|
|
2772
|
+
readonly actualBoundingBoxRight?: number;
|
|
2773
|
+
readonly fontBoundingBoxAscent?: number;
|
|
2774
|
+
readonly fontBoundingBoxDescent?: number;
|
|
2775
|
+
}
|
|
2776
|
+
/**
|
|
2777
|
+
* The slice of a 2D canvas context measurement needs.
|
|
2778
|
+
*
|
|
2779
|
+
* A structural subset rather than `CanvasRenderingContext2D`, so layout stays DOM-free and a test
|
|
2780
|
+
* can supply a deterministic stub.
|
|
2781
|
+
*/
|
|
2782
|
+
interface CanvasTextContext {
|
|
2783
|
+
font: string;
|
|
2784
|
+
/** Optional browser kerning control; older contexts retain their native behavior. */
|
|
2785
|
+
fontKerning?: 'auto' | 'normal' | 'none';
|
|
2786
|
+
textAlign?: 'left' | 'right' | 'center' | 'start' | 'end';
|
|
2787
|
+
direction?: 'ltr' | 'rtl' | 'inherit';
|
|
2788
|
+
measureText(text: string): CanvasTextMetrics;
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* How the canvas measurer resolves fonts, scales, and bounds its caches. Every field optional.
|
|
2792
|
+
*
|
|
2793
|
+
* Layout never creates a canvas itself: without a `context` this measurer does not exist and the
|
|
2794
|
+
* surface falls back to fixed metrics.
|
|
2795
|
+
*/
|
|
2796
|
+
interface CanvasMeasurerOptions {
|
|
2797
|
+
/**
|
|
2798
|
+
* Layout units to CSS pixels — the same value the painter uses.
|
|
2799
|
+
*
|
|
2800
|
+
* Measuring at the layout size and multiplying afterwards is not the same as measuring at
|
|
2801
|
+
* the painted size: font metrics are hinted per pixel size. Everything here measures at
|
|
2802
|
+
* the painted size and converts back.
|
|
2803
|
+
*/
|
|
2804
|
+
readonly scale?: number;
|
|
2805
|
+
readonly fallbackFamily?: string;
|
|
2806
|
+
/**
|
|
2807
|
+
* The engine-minted family a document-embedded face was registered under, if any.
|
|
2808
|
+
*
|
|
2809
|
+
* Paint prefers this alias over the declared family, so measurement has to as well: a
|
|
2810
|
+
* document whose face is embedded rather than installed would otherwise be measured
|
|
2811
|
+
* against the fallback and painted with the embedded glyphs, and every advance, wrap
|
|
2812
|
+
* point and page break would be taken from a font the reader never sees.
|
|
2813
|
+
*/
|
|
2814
|
+
readonly fontAlias?: (family: string) => string | undefined;
|
|
2815
|
+
/**
|
|
2816
|
+
* Injected 2d text context from the editor/browser seam.
|
|
2817
|
+
*
|
|
2818
|
+
* `undefined` or `null` makes {@link tryCreateCanvasMeasurer} return null so the surface
|
|
2819
|
+
* falls back to the fixed measurer. Layout never creates a canvas element itself.
|
|
2820
|
+
*/
|
|
2821
|
+
readonly context?: CanvasTextContext | null;
|
|
2822
|
+
/**
|
|
2823
|
+
* Unique `(font, text)` width entries retained before LRU eviction.
|
|
2824
|
+
*
|
|
2825
|
+
* Long editing sessions measure many transient paragraph states; bounding this cache keeps
|
|
2826
|
+
* memory predictable without changing layout output.
|
|
2827
|
+
*/
|
|
2828
|
+
readonly maxWidthEntries?: number;
|
|
2829
|
+
/** Distinct font-shorthand line metrics retained before LRU eviction. */
|
|
2830
|
+
readonly maxMetricsEntries?: number;
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* The measurer a surface ended up with, plus the identity its cache keys must include.
|
|
2834
|
+
*
|
|
2835
|
+
* The `producer` string is load-bearing: the same canvas measuring against document-embedded
|
|
2836
|
+
* faces produces DIFFERENT advances, so the two must not share a cache key space or a document
|
|
2837
|
+
* would keep its pre-font pagination after the font arrived.
|
|
2838
|
+
*/
|
|
2839
|
+
interface ResolvedSurfaceMeasurer {
|
|
2840
|
+
readonly measurer: TextMeasurer;
|
|
2841
|
+
/**
|
|
2842
|
+
* Cache-invalidation identity when the caller did not supply `producer`.
|
|
2843
|
+
*
|
|
2844
|
+
* `canvas-measurer+embedded` is the canvas measurer resolving document-embedded faces:
|
|
2845
|
+
* the same canvas, different advances, so it must not share a cache key space.
|
|
2846
|
+
*/
|
|
2847
|
+
readonly producer: 'canvas-measurer' | 'canvas-measurer+embedded' | 'fixed-measurer';
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Whether an injected canvas text context is usable for measurement.
|
|
2851
|
+
*
|
|
2852
|
+
* Availability is decided by the editor seam (which alone may create a canvas). Layout
|
|
2853
|
+
* never probes `document` — a missing context is simply "unavailable".
|
|
2854
|
+
*/
|
|
2855
|
+
declare function isCanvasMeasurementAvailable(context?: CanvasTextContext | null | undefined): boolean;
|
|
2856
|
+
/**
|
|
2857
|
+
* Build a canvas-backed measurer, or `null` when no 2d context was injected.
|
|
2858
|
+
*
|
|
2859
|
+
* Prefer {@link resolveDefaultSurfaceMeasurer} at the editor surface: that keeps the
|
|
2860
|
+
* fixed fallback for SSR/tests in one place.
|
|
2861
|
+
*/
|
|
2862
|
+
declare function tryCreateCanvasMeasurer(options?: CanvasMeasurerOptions): TextMeasurer | null;
|
|
2863
|
+
/**
|
|
2864
|
+
* The surface's default measurer: canvas when a 2d context was injected, otherwise fixed.
|
|
2865
|
+
*
|
|
2866
|
+
* Host-supplied and shaping measurers override this entirely — call only when the options
|
|
2867
|
+
* did not already name one. The editor seam is responsible for creating the canvas context.
|
|
2868
|
+
*/
|
|
2869
|
+
declare function resolveDefaultSurfaceMeasurer(scale?: number, options?: CanvasMeasurerOptions): ResolvedSurfaceMeasurer;
|
|
2870
|
+
|
|
2871
|
+
type RefreshFieldResultsOp = Extract<TreeDocOp, {
|
|
2872
|
+
op: 'refreshFieldResults';
|
|
2873
|
+
}>;
|
|
2874
|
+
|
|
2875
|
+
interface RefFieldRefreshOptions {
|
|
2876
|
+
/**
|
|
2877
|
+
* The package the part belongs to, for the note-part half of the resolution context —
|
|
2878
|
+
* the same context paint uses, so a body REF that targets a footnote bookmark computes
|
|
2879
|
+
* the same value both places. Absent narrows resolution to body-declared bookmarks.
|
|
2880
|
+
*/
|
|
2881
|
+
readonly package?: OoxmlPackage;
|
|
2882
|
+
readonly styleCascade?: StyleCascadeTable;
|
|
2883
|
+
readonly numberingIndex?: NumberingIndex;
|
|
2884
|
+
/** The mode the surface painted under, so the saved values match the painted ones. */
|
|
2885
|
+
readonly displayMode?: RevisionDisplayMode;
|
|
2886
|
+
/**
|
|
2887
|
+
* The DISPLAYED page number of one paragraph in the current finalized layout, or null when
|
|
2888
|
+
* it is not placed. PAGEREF results ride the plan through this: the value is pagination's,
|
|
2889
|
+
* so only a caller holding the laid-out pages can answer, and a plan without it keeps every
|
|
2890
|
+
* PAGEREF result as loaded. The same calibration verdict paint took gates each rewrite.
|
|
2891
|
+
*/
|
|
2892
|
+
readonly pageRefPageNumberOf?: (targetParagraphId: string) => string | null;
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* A {@link RefFieldRefreshOptions.pageRefPageNumberOf} source over one finalized layout.
|
|
2896
|
+
*
|
|
2897
|
+
* Builds the target → host-page index once, on first demand, and answers every field from
|
|
2898
|
+
* it — the same walk finalize substitution takes, so the saved number is the painted one.
|
|
2899
|
+
*/
|
|
2900
|
+
declare function pageRefPageNumbersFromLayout(layout: SemanticLayout): (targetParagraphId: string) => string | null;
|
|
2901
|
+
/**
|
|
2902
|
+
* Plan the refresh for one body part, or null when every supported REF result is already
|
|
2903
|
+
* fresh (the no-op save path — no transaction, no revision bump, no undo entry).
|
|
2904
|
+
*/
|
|
2905
|
+
declare function planRefFieldResultRefresh(part: OoxmlPart, options: RefFieldRefreshOptions): RefreshFieldResultsOp | null;
|
|
2906
|
+
|
|
2907
|
+
/**
|
|
2908
|
+
* Return story blocks in document order, flattening block-level content controls.
|
|
2909
|
+
* Compatible hidden paragraph marks join in body stories using the supplied styles and numbering.
|
|
2910
|
+
* Repeated calls with the same part and projection inputs share one array. Treat it as read-only.
|
|
2911
|
+
*
|
|
2912
|
+
* @public
|
|
2913
|
+
*/
|
|
2914
|
+
declare function storyBlocks(part: OoxmlPart, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter, styles?: StyleCascadeTable, numberingIndex?: NumberingIndex): OoxmlElement[];
|
|
2915
|
+
/**
|
|
2916
|
+
* Blocks of one typed footnote/endnote node — a separate semantic story root.
|
|
2917
|
+
*
|
|
2918
|
+
* The footnotes/endnotes part root is never a story; each note is laid out independently
|
|
2919
|
+
* so line ids and incremental convergence stay namespaced by note identity.
|
|
2920
|
+
*/
|
|
2921
|
+
declare function noteStoryBlocks(note: OoxmlNode, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter): OoxmlElement[];
|
|
2922
|
+
|
|
2923
|
+
/** TOC boundary paragraphs whose empty instruction/chrome lines are suppressed. */
|
|
2924
|
+
declare function tocFieldChromeParagraphIds(part: OoxmlPart): ReadonlySet<string>;
|
|
2925
|
+
/**
|
|
2926
|
+
* Begin-paragraph ids of TOCs that have no visible cached result rows.
|
|
2927
|
+
*
|
|
2928
|
+
* Layout keeps a single caret-height line on these ids so paint can host an identifiable
|
|
2929
|
+
* empty-TOC furniture placeholder; ordinary field chrome on the same ids stays suppressed.
|
|
2930
|
+
*/
|
|
2931
|
+
declare function emptyTocPlaceholderParagraphIds(part: OoxmlPart): ReadonlySet<string>;
|
|
2932
|
+
/**
|
|
2933
|
+
* Empty result-paragraph ids inside an empty TOC.
|
|
2934
|
+
*
|
|
2935
|
+
* Suppressed like field chrome so blank cached rows do not stack under the empty placeholder.
|
|
2936
|
+
*/
|
|
2937
|
+
declare function emptyTocSuppressedResultParagraphIds(part: OoxmlPart): ReadonlySet<string>;
|
|
2938
|
+
|
|
2939
|
+
/** One page-local rectangle covering the part of a control that sits on that page. */
|
|
2940
|
+
interface ContentControlFragmentRecord extends LayoutBox {
|
|
2941
|
+
readonly pageIndex: number;
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Everything a consumer needs to point at one control without walking the tree.
|
|
2945
|
+
*
|
|
2946
|
+
* `controlId` is the canonical NODE id — the identity `w:id` cannot provide, because the file
|
|
2947
|
+
* may omit it and may repeat it. `id` is the file's own `w:id` where it wrote one, carried as
|
|
2948
|
+
* metadata for a caller that has to speak to something outside this engine.
|
|
2949
|
+
*/
|
|
2950
|
+
interface ContentControlBoundaryRecord {
|
|
2951
|
+
readonly controlId: string;
|
|
2952
|
+
readonly type: ContentControlKind;
|
|
2953
|
+
readonly level: ContentControlLevel;
|
|
2954
|
+
/** 0 for a top-level control; the nesting depth otherwise. */
|
|
2955
|
+
readonly depth: number;
|
|
2956
|
+
readonly lock: ContentControlLock;
|
|
2957
|
+
/** The lock in force here, including every lock an enclosing control imposes. */
|
|
2958
|
+
readonly effectiveLock: ContentControlLock;
|
|
2959
|
+
readonly showingPlaceholder: boolean;
|
|
2960
|
+
readonly temporary: boolean;
|
|
2961
|
+
readonly tag?: string;
|
|
2962
|
+
readonly alias?: string;
|
|
2963
|
+
readonly id?: number;
|
|
2964
|
+
/** Preserved `w:dataBinding` metadata. Present means value writes are refused as bound. */
|
|
2965
|
+
readonly dataBinding?: ContentControlDataBinding;
|
|
2966
|
+
/** Paragraphs the control's content holds, in reading order. */
|
|
2967
|
+
readonly paragraphIds: readonly string[];
|
|
2968
|
+
/** One rectangle per page the content reaches, in the page's own coordinate space. */
|
|
2969
|
+
readonly fragments: readonly ContentControlFragmentRecord[];
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Derive one boundary record per control the part declares, in document order.
|
|
2973
|
+
*
|
|
2974
|
+
* A control whose content the layout never placed — one inside a story this layout is not of,
|
|
2975
|
+
* or one holding nothing — answers no fragments rather than a zero-sized box at the origin: a
|
|
2976
|
+
* rectangle nothing painted is a rectangle a hit test would match.
|
|
2977
|
+
*/
|
|
2978
|
+
declare function contentControlBoundaries(part: OoxmlPart, layout: SemanticLayout): readonly ContentControlBoundaryRecord[];
|
|
2979
|
+
|
|
2980
|
+
/** Where one paragraph sits, resolved once so a card is an O(1) lookup. */
|
|
2981
|
+
interface ReviewParagraphAnchor {
|
|
2982
|
+
readonly pageIndex: number;
|
|
2983
|
+
/** Sheet-absolute y of the page's content box. */
|
|
2984
|
+
readonly contentY: number;
|
|
2985
|
+
/** The fragment's own y, measured from that content box. */
|
|
2986
|
+
readonly fragmentY: number;
|
|
2987
|
+
readonly lines?: readonly {
|
|
2988
|
+
readonly range: {
|
|
2989
|
+
readonly end: number;
|
|
2990
|
+
};
|
|
2991
|
+
readonly box: {
|
|
2992
|
+
readonly y: number;
|
|
2993
|
+
};
|
|
2994
|
+
/**
|
|
2995
|
+
* Present on a real line. A merged fragment's join line carries spans from two
|
|
2996
|
+
* paragraphs, and its `range` can only name one of them, so the OTHER paragraph's extent
|
|
2997
|
+
* on that line is readable here and nowhere else.
|
|
2998
|
+
*/
|
|
2999
|
+
readonly spans?: readonly {
|
|
3000
|
+
readonly range: {
|
|
3001
|
+
readonly paragraphId: string;
|
|
3002
|
+
readonly end: number;
|
|
3003
|
+
};
|
|
3004
|
+
}[];
|
|
3005
|
+
}[];
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* The y of the line a position sits on, measured from the anchor's own origin.
|
|
3009
|
+
*
|
|
3010
|
+
* An ordinary line answers from its own range, exactly as it always did. On a merged
|
|
3011
|
+
* fragment's join line the range names one of the two paragraphs, so an offset compared
|
|
3012
|
+
* against it either overshot — putting a card in the absorbed half beside the wrong line —
|
|
3013
|
+
* or matched the first line every time.
|
|
3014
|
+
*/
|
|
3015
|
+
declare function anchorLineY(anchor: ReviewParagraphAnchor, paragraphId: string, offset: number): number;
|
|
3016
|
+
/**
|
|
3017
|
+
* Paragraph id to its place on the page, in ONE pass over the layout.
|
|
3018
|
+
*
|
|
3019
|
+
* Built once per layout and reused by every card. The straightforward version — scan the
|
|
3020
|
+
* pages until the paragraph turns up, per card — is a full-document walk per card, and a
|
|
3021
|
+
* contract with two hundred comments walked the document two hundred times every time the
|
|
3022
|
+
* caret moved. Toggling the pane was visibly slow for exactly that reason.
|
|
3023
|
+
*/
|
|
3024
|
+
declare function reviewAnchorIndex<TPage extends {
|
|
3025
|
+
readonly index: number;
|
|
3026
|
+
readonly contentBox: {
|
|
3027
|
+
readonly y: number;
|
|
3028
|
+
};
|
|
3029
|
+
}>(layout: {
|
|
3030
|
+
readonly pages: readonly TPage[];
|
|
3031
|
+
}, paragraphFragments: (page: TPage) => readonly {
|
|
3032
|
+
readonly paragraphId: string;
|
|
3033
|
+
readonly box: {
|
|
3034
|
+
readonly y: number;
|
|
3035
|
+
};
|
|
3036
|
+
readonly lines?: readonly {
|
|
3037
|
+
readonly range: {
|
|
3038
|
+
readonly end: number;
|
|
3039
|
+
};
|
|
3040
|
+
readonly box: {
|
|
3041
|
+
readonly y: number;
|
|
3042
|
+
};
|
|
3043
|
+
/** Present on a real line; a merged one carries spans from more than one paragraph. */
|
|
3044
|
+
readonly spans?: readonly {
|
|
3045
|
+
readonly range: {
|
|
3046
|
+
readonly paragraphId: string;
|
|
3047
|
+
readonly end: number;
|
|
3048
|
+
};
|
|
3049
|
+
}[];
|
|
3050
|
+
}[];
|
|
3051
|
+
}[]): Map<string, ReviewParagraphAnchor>;
|
|
3052
|
+
/**
|
|
3053
|
+
* Where a card belongs beside the page, from LAYOUT RECORDS.
|
|
3054
|
+
*
|
|
3055
|
+
* The one question the tree cannot answer, and the one a surface must not answer for itself:
|
|
3056
|
+
* measuring painted DOM puts the sidebar a repaint behind the document and breaks outright
|
|
3057
|
+
* while pagination is in flight.
|
|
3058
|
+
*
|
|
3059
|
+
* Returns null when the item has no resolvable range, or when its paragraph is not in this
|
|
3060
|
+
* layout — a comment anchored in a header belongs to a story the body layout never saw.
|
|
3061
|
+
*/
|
|
3062
|
+
declare function reviewItemGeometry(item: ReviewItem, index: ReadonlyMap<string, ReviewParagraphAnchor>): {
|
|
3063
|
+
readonly pageIndex: number;
|
|
3064
|
+
readonly y: number;
|
|
3065
|
+
} | null;
|
|
3066
|
+
|
|
3067
|
+
/**
|
|
3068
|
+
* The frame for one sheet, or undefined when this page does not carry one.
|
|
3069
|
+
*
|
|
3070
|
+
* `isFirstPageOfSection` is the SECTION's first page, not the document's: `w:display` is a
|
|
3071
|
+
* section property, so in a three-section document a `firstPage` frame draws three times.
|
|
3072
|
+
*
|
|
3073
|
+
* The four rules close a rectangle rather than each spanning its own side. Word draws one box,
|
|
3074
|
+
* and horizontal rules that stopped at the text column while the verticals sat outside it read
|
|
3075
|
+
* as two rules with two detached bars beside them — the same thing `w:pBdr` gets right by
|
|
3076
|
+
* spanning from the left rule's outer edge to the right rule's.
|
|
3077
|
+
*/
|
|
3078
|
+
declare function pageBorderFrame(borders: SectionPageBorders | undefined, geometry: PageGeometry, isFirstPageOfSection: boolean): PageBorderFrameRecord | undefined;
|
|
3079
|
+
|
|
3080
|
+
/** The visible band of the document, in layout units. */
|
|
3081
|
+
interface ViewportWindow {
|
|
3082
|
+
/** Distance from the top of the document to the top of the visible area, in layout units. */
|
|
3083
|
+
readonly top: number;
|
|
3084
|
+
readonly height: number;
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Which pages to build in detail.
|
|
3088
|
+
*
|
|
3089
|
+
* A page left out keeps its size and position but no content, so the document's height and page
|
|
3090
|
+
* count are unchanged and scrolling to it reveals it rather than reflowing everything below.
|
|
3091
|
+
*/
|
|
3092
|
+
interface MaterializationInput {
|
|
3093
|
+
readonly layout: SemanticLayout;
|
|
3094
|
+
/** Omitted means "no viewport": everything is materialized, which is the honest default. */
|
|
3095
|
+
readonly viewport?: ViewportWindow;
|
|
3096
|
+
/** Extra pages kept ready either side of the visible band. */
|
|
3097
|
+
readonly overscanPages?: number;
|
|
3098
|
+
/** Pages that must be built wherever they are — caret, selection, a search hit. */
|
|
3099
|
+
readonly pinnedPages?: Iterable<number>;
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* The page indices to build in detail.
|
|
3103
|
+
*
|
|
3104
|
+
* Returns indices rather than records so a caller can compare cheaply against what it
|
|
3105
|
+
* already has mounted, and so the decision can be made without touching the layout.
|
|
3106
|
+
*/
|
|
3107
|
+
declare function pagesToMaterialize(input: MaterializationInput): Set<number>;
|
|
3108
|
+
|
|
3109
|
+
/** Character border geometry from placed spans, shared by screen and export painters. */
|
|
3110
|
+
declare function runBorderStrokesForLine(line: LineRecord): readonly ParagraphBorderStrokeRecord[];
|
|
3111
|
+
|
|
3112
|
+
/**
|
|
3113
|
+
* `w:pPr/w:rPr/w:del` or `w:moveFrom` — a tracked revision REMOVES the paragraph mark.
|
|
3114
|
+
*
|
|
3115
|
+
* Both say the break goes away once the decision is taken: a deletion outright, a `moveFrom`
|
|
3116
|
+
* because the paragraph left this place for another. Read from the paragraph-mark run
|
|
3117
|
+
* properties only. A `w:del` anywhere else in the paragraph deletes run content, which is a
|
|
3118
|
+
* different statement entirely.
|
|
3119
|
+
*/
|
|
3120
|
+
declare function paragraphMarkDeleted(paragraph: OoxmlNode): boolean;
|
|
3121
|
+
/**
|
|
3122
|
+
* True when a tracked revision has removed this paragraph from the rendered document, so
|
|
3123
|
+
* layout should emit no box for it at all.
|
|
3124
|
+
*/
|
|
3125
|
+
declare function revisionRemovesParagraph(paragraph: OoxmlNode, displayMode?: RevisionDisplayMode, authorFilter?: RevisionAuthorFilter): boolean;
|
|
3126
|
+
|
|
3127
|
+
/**
|
|
3128
|
+
* Fit complete leader advances to one shared horizontal grid.
|
|
3129
|
+
*
|
|
3130
|
+
* `startPt` is measured from the paper's left edge, or the local textbox frame.
|
|
3131
|
+
* Offsets remain relative to the supplied gap. No glyph is clipped at either end,
|
|
3132
|
+
* and lines in the same face share their dot columns regardless of preceding text.
|
|
3133
|
+
* @public
|
|
3134
|
+
*/
|
|
3135
|
+
declare function tabLeaderPattern(startPt: number, widthPt: number, advancePt: number): {
|
|
3136
|
+
readonly offsetPt: number;
|
|
3137
|
+
readonly count: number;
|
|
3138
|
+
};
|
|
3139
|
+
|
|
3140
|
+
/**
|
|
3141
|
+
* Story-relative rule box for a measured separator span.
|
|
3142
|
+
*
|
|
3143
|
+
* Word paints `w:separator` as a strikeout stroke, so `stroke` carries the face's `OS/2`
|
|
3144
|
+
* `yStrikeoutPosition` and `yStrikeoutSize` at the run's size. Without a resolved face the
|
|
3145
|
+
* box keeps the legacy quarter-em offset and hairline thickness.
|
|
3146
|
+
* @public
|
|
3147
|
+
*/
|
|
3148
|
+
declare function noteSeparatorRuleBox(span: StyleSpanRecord, line: LineRecord, stroke?: StrikeoutStrokePt): LayoutBox;
|
|
3149
|
+
|
|
3150
|
+
export { AUTO_PREFERRED_WIDTH, AnchoredDrawingRecord, BlockFragmentRecord, CELL_PAD, COMPOUND_BORDER_MIN_GAP_PT, COMPOUND_BORDER_MIN_STROKE_PT, CaretGeometry, CellBorderBox, ContentControlBoundaryRecord$1 as ContentControlBoundaryRecord, DEFAULT_CANVAS_FONT_STACK, DEFAULT_CELL_MARGINS, DrawingInsets, DrawingPoint, FieldLinkProjector, FixedPoint, FixedPointRoundingMode, FontByteValidator, GRAPHEME_SEGMENTER_LOCALE, HARFBUZZ_SHAPING_LIBRARY, HarfBuzzShapingError, HyperlinkFieldSpec, HyperlinkProjector, InlineDrawingRecord, LayoutBox, LineRecord, MAX_EACH_PAGE_MARK_CANDIDATES, MAX_LVL_TEXT_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_NOTES_LAID_OUT, MAX_NOTE_FRAGMENTS, MAX_NOTE_OVERFLOW_PAGES, MAX_NOTE_REFLOW_ATTEMPTS, MAX_STYLE_BASED_ON_DEPTH, MAX_TABLE_COLUMNS, MAX_TABLE_NESTING, MAX_TABLE_ROW_FRAGMENTS, MAX_TABLE_ROW_HEIGHT_PT, NumberingIndex, NumberingLevel, PageBorderFrameRecord, PageGeometry, PageRecord, ParagraphBorderStrokeRecord, ParagraphFragmentRecord, RefFieldContext, ResolvedListItem, ReviewItem, RevisionAttribution, RevisionAuthorFilter, RevisionDisplayMode, SectionColumns, SectionPageBorders, SelectionRect, SemanticLayout, SemanticPosition, SemanticRootStoryKind, SemanticSelection, SemanticStoryKind, SemanticStoryVisit, ShapedRun, SourceRange, SpanLinkRecord, StrikeoutStrokePt, StyleCascadeTable, StyleSpanRecord, TableBorderBox, TableBorderSide, TableCellStyleFormatting, TablePaginationError, TextMeasurer, TextShaper, TextboxOwnerRecord, VersionedShapingLibrary, WORD_SEGMENTER_LOCALE, anchorLineY, attachNotesToLayout, boundedFallbackWordSegments, buildPageRefIndex, clampListValue, computeDoubleBorderMetricsPt, computeFootnoteReserves, contentControlAtSemantic, contentControlBoundaries, contentControlHoldingParagraph, contentControlRecordsInPart, createBoundedFallbackWordBoundary, createDefaultWordBoundary, createDocumentFurnitureSource, createDocumentLinkProjectors, createDocumentNotesInput, createDocumentStyleDependencies, createFieldLinkRegistry, createFixedMeasurer, createHarfBuzzTextShaper, createIntlWordBoundary, createLayoutScheduler, createLayoutSession, createListCounterState, createParagraphLayoutCache, defaultNoteSeparatorRuleStyle, deriveNoteDisplayMarks, deriveNoteDisplayMarksResolved, documentOrder, documentRelationshipTargetIn, effectiveBorderSide, emptyTocPlaceholderParagraphIds, emptyTocSuppressedResultParagraphIds, everyStoryOrder, expandLvlText, exportSourceRangeOf, filterRefsOnPage, findSeparatorNote, forEachSemanticSpan, formatDecimal, formatDecimalZero, formatLowerLetter, formatLowerRoman, formatNumFmt, formatPageNumber, formatUpperLetter, formatUpperRoman, fragmentOwnsAtomOffset, graphemeBoundaryEpoch, graphemeCount, graphemeOffsetToUtf16, harfBuzzFontValidator, hitTestSemantic, initializeHarfBuzz, intlGraphemeBoundary, isCanvasMeasurementAvailable, isCumulativeGeometryTrustedFromLineOrigin, isGeometryTrustedCaretOffset, isHarfBuzzInitialized, isIntlSegmenterAvailable, isIntlWordSegmenterAvailable, isMarkerOnlySeparatorNote, isWholeGraphemeHorizontalBoundary, keyedRangeRects, layoutHeaderFooterStory, layoutNoteById, layoutNoteSeparator, layoutNoteStory, layoutSemanticDocument, lineSegments, listMarkerBox, mapSymbolPuaText, normalNotesOf, noteDisplayMarkMap, noteLineIdPrefix, noteMarkKey, noteSeparatorAreaBox, noteSeparatorRuleBox, noteStoryBlocks, pageBorderFrame, pageRefPageNumbersFromLayout, pagesToMaterialize, paragraphLayoutKey, paragraphMarkDeleted, paragraphShading, paragraphShadingBox, parseAutonumInstruction, planRefFieldResultRefresh, positionPastDeletion, projectedNoteMarkText, provisionalNoteMarks, readTableStructure, resetGraphemeBoundary, resolveDefaultSurfaceMeasurer, resolveDefaultWordBoundary, resolveOoxmlShadingFill, resolveStrictHexFill, reviewAnchorIndex, reviewItemGeometry, revisionRemovesParagraph, roundFontUnitToFixedPoint, runBorderStrokesForLine, segmentGraphemes, segmentWords, selectionRects, semanticHorizontalBoundaries, setGraphemeBoundary, setHarfBuzzWasmUrl, sha256FontBytes, shadingFillFromElement, shapedHorizontalBoundaries, storyBlocks, syntheticSeparatorBox, tabLeaderPattern, tableOriginX, tocFieldChromeParagraphIds, tryCreateCanvasMeasurer, utf16OffsetToGrapheme, vectorShapeInkClip, walkStoryParagraphs, wordBoundary, wordSegmentsToGraphemeRecords };
|
|
3151
|
+
export type { AutofitFieldContext, AutonumFieldKind, AutonumFieldSpec, CanvasMeasurerOptions, CanvasTextContext, CanvasTextMetrics, CellMarginsPt, CellVerticalAlign, CompoundBorderMetrics, ContentControlFragmentRecord, CreateDocumentFurnitureSourceOptions, CreateDocumentNotesInputOptions, DocumentFurnitureSource, DocumentLinkProjectors, DocumentStyleDependencies, FieldLinkRegistry, GraphemeBoundary, GraphemeSegment, GraphemeWordSegmentRecord, HarfBuzzFaceCacheEvent, HarfBuzzOutlineCacheEvent, HarfBuzzShapeCacheEvent, HarfBuzzShapingErrorCode, HarfBuzzTextShaper, HarfBuzzTextShaperInstrumentation, HarfBuzzTextShaperOptions, HeaderFooterVariantName, KeyedRange, LayoutCacheStats, LayoutScheduler, LayoutSchedulerOptions, LayoutScope, LayoutSession, LayoutSessionStats, LineSegment, ListCounterAdvance, ListCounterState, MaterializationInput, NoteDisplayMark, NoteLayoutFallbackReason, NoteMarkContext, NotePaginationFallbackReason, NoteReferenceSite, NoteSeparatorLayout, NoteSeparatorRuleStyle, NoteStoryDrawings, NoteStoryLayout, NotesAttachResult, NotesLayoutInput, PageFurniture, PageRefIndex, ParagraphKeyInputs, ParagraphLayoutCache, ParagraphLayoutCacheOptions, PreferredWidth, PreferredWidthType, RefFieldRefreshOptions, RegisteredFieldLink, ResolvedSurfaceMeasurer, ReviewParagraphAnchor, SemanticLayoutOptions, SemanticSpanVisit, SemanticTableCell, SemanticTableRow, SemanticTableStructure, StoryProjectionDependencies, TableAlignment, TableAutofitContext, TablePaginationErrorCode, TableRowHeight, TableRowHeightRule, ViewportWindow, WordBoundary, WordBoundaryResolverDeps, WordSegment };
|