@docx-editor.dev/core 2.12.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +18 -14
  2. package/dist/automation.cjs +1 -1
  3. package/dist/automation.d.cts +5 -5
  4. package/dist/automation.d.ts +5 -5
  5. package/dist/automation.js +1 -1
  6. package/dist/binding.cjs +1 -1
  7. package/dist/binding.d.cts +8 -8
  8. package/dist/binding.d.ts +8 -8
  9. package/dist/binding.js +1 -1
  10. package/dist/chunk-2PIHW2QM.cjs +1 -0
  11. package/dist/chunk-2QTY5S6T.js +1 -0
  12. package/dist/chunk-4Z5MODAE.cjs +2 -0
  13. package/dist/chunk-5TKAPFZT.cjs +2 -0
  14. package/dist/chunk-DRQHIQ4R.cjs +1 -0
  15. package/dist/chunk-F2K6SDHA.cjs +20 -0
  16. package/dist/chunk-FBA57EUJ.js +2 -0
  17. package/dist/{chunk-XCFM3G5L.cjs → chunk-KIAP7PFQ.cjs} +1 -1
  18. package/dist/chunk-KTAQ7BRQ.js +20 -0
  19. package/dist/chunk-LBAHXZCU.cjs +5 -0
  20. package/dist/chunk-MCDUQYWP.js +5 -0
  21. package/dist/chunk-MQP26KCF.js +1 -0
  22. package/dist/chunk-NDJGSR6M.cjs +19 -0
  23. package/dist/chunk-NJNFJPGB.cjs +11 -0
  24. package/dist/chunk-O5M4CR4P.cjs +2 -0
  25. package/dist/{chunk-W77HJGYX.js → chunk-OKEDGR65.js} +1 -1
  26. package/dist/{chunk-WU7PUTVA.js → chunk-RIYSCEFQ.js} +1 -1
  27. package/dist/chunk-UKUOZXUD.js +19 -0
  28. package/dist/chunk-VQ4R5LTU.js +2 -0
  29. package/dist/chunk-ZBEJHJN2.js +11 -0
  30. package/dist/chunk-ZVTW7RGD.js +2 -0
  31. package/dist/collaboration/replication.cjs +1 -1
  32. package/dist/collaboration/replication.d.cts +3 -3
  33. package/dist/collaboration/replication.d.ts +3 -3
  34. package/dist/collaboration/replication.js +1 -1
  35. package/dist/collaboration.d.cts +3 -3
  36. package/dist/collaboration.d.ts +3 -3
  37. package/dist/contracts/editor.d.cts +7 -7
  38. package/dist/contracts/editor.d.ts +7 -7
  39. package/dist/contracts/interaction.d.cts +7 -7
  40. package/dist/contracts/interaction.d.ts +7 -7
  41. package/dist/contracts/modules.d.cts +7 -7
  42. package/dist/contracts/modules.d.ts +7 -7
  43. package/dist/{custom-node-writes-D4rMJIES.d.cts → custom-node-writes-C46r3_5O.d.cts} +2 -2
  44. package/dist/{custom-node-writes-CcNeHH5C.d.ts → custom-node-writes-DntmaAVW.d.ts} +2 -2
  45. package/dist/{dist-YFULGVS3.js → dist-OSUJGKTK.js} +1 -1
  46. package/dist/{editor-B76SEMZw.d.cts → editor-AJ06hyyg.d.cts} +45 -9
  47. package/dist/{editor-BcdsPdbY.d.ts → editor-a6VvsfYU.d.ts} +45 -9
  48. package/dist/editor.cjs +1 -1
  49. package/dist/editor.css +1 -1
  50. package/dist/editor.d.cts +22 -22
  51. package/dist/editor.d.ts +22 -22
  52. package/dist/editor.js +1 -1
  53. package/dist/{index-BYTvklN5.d.ts → index-C5ZnSRkF.d.ts} +209 -20
  54. package/dist/{index-CDGhPib1.d.cts → index-Cbw4lkOt.d.cts} +209 -20
  55. package/dist/index.cjs +1 -1
  56. package/dist/index.d.cts +15 -15
  57. package/dist/index.d.ts +15 -15
  58. package/dist/index.js +1 -1
  59. package/dist/layout.cjs +1 -1
  60. package/dist/layout.d.cts +203 -593
  61. package/dist/layout.d.ts +203 -593
  62. package/dist/layout.js +1 -1
  63. package/dist/{note-properties-Bx7rXruA.d.ts → note-properties-CDFiNXcD.d.ts} +1 -1
  64. package/dist/{note-properties-zQ8w04en.d.cts → note-properties-Y6qYh2Hv.d.cts} +1 -1
  65. package/dist/{omml-equation-69O1Pd8G.d.ts → omml-equation-BY49UOIF.d.ts} +1 -1
  66. package/dist/{omml-equation-BmV51rim.d.cts → omml-equation-DB8d-jCq.d.cts} +1 -1
  67. package/dist/output.cjs +1 -1
  68. package/dist/output.d.cts +6 -6
  69. package/dist/output.d.ts +6 -6
  70. package/dist/output.js +1 -1
  71. package/dist/{protocol-DC7ueVuo.d.ts → protocol-BsmQgblT.d.ts} +2 -2
  72. package/dist/{protocol-FTXtrAIT.d.cts → protocol-gOY5GA8K.d.cts} +2 -2
  73. package/dist/{review-items-B78OsJU2.d.ts → review-items-CCS1h93f.d.ts} +1 -1
  74. package/dist/{review-items-C-2WQdEb.d.cts → review-items-CaAkgi5s.d.cts} +1 -1
  75. package/dist/{review-reads-UexYBp-m.d.ts → review-reads-BjZYeMd4.d.ts} +8 -21
  76. package/dist/{review-reads-CbBQlb-n.d.cts → review-reads-DEsV0OOW.d.cts} +8 -21
  77. package/dist/{revision-projection-6u8lae9J.d.cts → revision-projection-BeXVrSSV.d.ts} +24 -3
  78. package/dist/{revision-projection-B2v9U3qx.d.ts → revision-projection-DXbrfNGD.d.cts} +24 -3
  79. package/dist/{semantic-cell-selection-DsYz2Vg3.d.ts → semantic-cell-selection-BK91NCCG.d.ts} +16 -7
  80. package/dist/{semantic-cell-selection-nEE7E3Z8.d.cts → semantic-cell-selection-BMhku3B1.d.cts} +16 -7
  81. package/dist/{semantic-interaction-C00BpIyC.d.ts → semantic-interaction-BmQ7h2cq.d.ts} +4 -2
  82. package/dist/{semantic-interaction-DiOX0FIH.d.cts → semantic-interaction-DUAnxbgf.d.cts} +4 -2
  83. package/dist/{semantic-paint-Cc11nrDL.d.cts → semantic-paint-DufNL1Zr.d.cts} +2 -2
  84. package/dist/{semantic-paint-ofuoBsYo.d.ts → semantic-paint-WJD2KeG1.d.ts} +2 -2
  85. package/dist/{semantic-records-CRG1r6-a.d.cts → semantic-records-92UnKeU_.d.ts} +1146 -10
  86. package/dist/{semantic-records-CzI0ZEuR.d.ts → semantic-records-BpozY7-j.d.cts} +1146 -10
  87. package/dist/shaped-measurer-BWbindlI.d.ts +168 -0
  88. package/dist/shaped-measurer-CFPnXFs9.d.cts +168 -0
  89. package/dist/store.cjs +1 -1
  90. package/dist/store.d.cts +26 -16
  91. package/dist/store.d.ts +26 -16
  92. package/dist/store.js +1 -1
  93. package/dist/{tree-op-types-CA7qgAUF.d.cts → tree-op-types-BUulYD5y.d.cts} +15 -2
  94. package/dist/{tree-op-types-HKfUzt_-.d.ts → tree-op-types-D6HzlPR4.d.ts} +15 -2
  95. package/dist/{tree-package-fragment-oJZX_5iN.d.ts → tree-package-fragment-CWFjgH6a.d.ts} +3 -11
  96. package/dist/{tree-package-fragment-Cz-v-0B3.d.cts → tree-package-fragment-Dhyw7oPm.d.cts} +3 -11
  97. package/dist/{tree-session-contract-BPDLrEPX.d.ts → tree-session-contract-B2HfGt79.d.ts} +33 -6
  98. package/dist/{tree-session-contract-Cbl87xa8.d.cts → tree-session-contract-BY9xd7s_.d.cts} +33 -6
  99. package/dist/{tree-store-B7hd6cEa.d.ts → tree-store-APgWx6qn.d.ts} +1 -1
  100. package/dist/{tree-store-BenW0Mg_.d.cts → tree-store-DEEQ9wz_.d.cts} +1 -1
  101. package/package.json +5 -2
  102. package/dist/chunk-3JON7JMD.cjs +0 -1
  103. package/dist/chunk-5V6FNKBE.cjs +0 -2
  104. package/dist/chunk-BXRVGHTY.cjs +0 -18
  105. package/dist/chunk-BXTTPJSH.js +0 -5
  106. package/dist/chunk-ILTZTY3K.cjs +0 -10
  107. package/dist/chunk-IUM7FZPA.js +0 -2
  108. package/dist/chunk-JWZG4QJ5.cjs +0 -2
  109. package/dist/chunk-JXZYDBKU.cjs +0 -19
  110. package/dist/chunk-KBOOIC2Q.js +0 -19
  111. package/dist/chunk-L6XZTLPS.js +0 -10
  112. package/dist/chunk-L7OR5TJK.cjs +0 -5
  113. package/dist/chunk-MHQNR2OU.js +0 -2
  114. package/dist/chunk-MLGPISGH.js +0 -1
  115. package/dist/chunk-NJXQABQU.js +0 -18
  116. package/dist/chunk-TKEGK4GQ.cjs +0 -2
  117. package/dist/chunk-VGFMPWND.js +0 -2
  118. package/dist/shaped-measurer-CkOGZQn9.d.ts +0 -611
  119. package/dist/shaped-measurer-CneACGcP.d.cts +0 -611
@@ -1,6 +1,6 @@
1
- import { a as OoxmlNode, s as OoxmlProperty, x as ImageResourceState, G as DrawingAccessibility, S as SourceCrop, H as DrawingTransform, J as VectorShapeProjection, z as DrawingHorizontalReferenceFrame, A as DrawingVerticalReferenceFrame, u as ImageWrapTarget, K as DrawingProjection, L as OoxmlDrawingNode, M as TableBorderStyle, g as OoxmlElement, O as OoxmlPart } from './tree-op-types-CA7qgAUF.cjs';
2
- import { a as RevisionAttribution } from './revision-projection-6u8lae9J.cjs';
3
- import { O as OmmlEquationProjection } from './omml-equation-BmV51rim.cjs';
1
+ import { a as OoxmlNode, s as OoxmlProperty, g as OoxmlElement, O as OoxmlPart, x as ImageResourceState, U as DrawingAccessibility, S as SourceCrop, W as DrawingTransform, X as VectorShapeProjection, z as DrawingHorizontalReferenceFrame, A as DrawingVerticalReferenceFrame, u as ImageWrapTarget, Y as DrawingProjection, Z as OoxmlDrawingNode, _ as TableBorderStyle } from './tree-op-types-D6HzlPR4.js';
2
+ import { b as RevisionAttribution } from './revision-projection-BeXVrSSV.js';
3
+ import { O as OmmlEquationProjection } from './omml-equation-BY49UOIF.js';
4
4
 
5
5
  /**
6
6
  * Whether a paragraph must start a new page (`w:pageBreakBefore`).
@@ -339,6 +339,473 @@ declare function tabAdvanceWidth(alignment: TabAlignment, currentX: number, dest
339
339
  */
340
340
  declare function tabStopsFingerprint(tabs: ResolvedTabStops): string;
341
341
 
342
+ /**
343
+ * UAX #9 embedding levels, one per UTF-16 code unit, plus the paragraph ranges they were resolved
344
+ * within.
345
+ *
346
+ * The level is EXACT, not a direction: its parity gives direction, but the numeric value is what
347
+ * reordering needs, and collapsing it early loses nested isolates.
348
+ */
349
+ interface BidiEmbeddingLevels {
350
+ readonly levels: Uint8Array;
351
+ readonly paragraphs: readonly {
352
+ readonly start: number;
353
+ readonly end: number;
354
+ readonly level: number;
355
+ }[];
356
+ }
357
+
358
+ /**
359
+ * A face, as something asks for it: family plus the two axes this engine admits.
360
+ *
361
+ * Only static weights and slants. Variable-font axes are deliberately outside this vocabulary —
362
+ * the shaper refuses variation axes, and a variable file admitted here would render bold at
363
+ * regular weight.
364
+ */
365
+ interface FontRequest {
366
+ readonly family: string;
367
+ readonly weight: number;
368
+ readonly style: 'normal' | 'italic';
369
+ }
370
+ /**
371
+ * A request that was answered by a DIFFERENT face than the one asked for.
372
+ *
373
+ * Recorded rather than silently applied, because a substitution changes measurement: it is part
374
+ * of the shaping fingerprint, so a cached run shaped against a substitute is never reused for the
375
+ * real face.
376
+ */
377
+ interface FontSubstitution {
378
+ readonly requested: FontRequest;
379
+ readonly resolved: FontRequest;
380
+ readonly lineMetrics?: {
381
+ readonly heightEm: number;
382
+ readonly baselineEm: number;
383
+ };
384
+ }
385
+ declare const RESOLVED_FONT_BRAND: unique symbol;
386
+ /**
387
+ * A face that passed admission: the bytes are present, within limits, hash-verified, and parse as
388
+ * a font.
389
+ *
390
+ * Branded, so a `ResolvedFont` cannot be constructed by an object literal. Holding one is proof
391
+ * the checks ran — which is what lets shaping skip re-validating on every call.
392
+ */
393
+ interface ResolvedFont {
394
+ readonly [RESOLVED_FONT_BRAND]: true;
395
+ readonly id: string;
396
+ readonly identity: string;
397
+ readonly request: FontRequest;
398
+ readonly family: string;
399
+ /** Owned byte length, available without creating a defensive byte copy. */
400
+ readonly byteLength: number;
401
+ readonly bytes: Uint8Array;
402
+ readonly hash: string;
403
+ readonly faceIndex: number;
404
+ readonly substitution: FontSubstitution | null;
405
+ }
406
+ /**
407
+ * Largest single face this engine will ever admit, whatever a caller configures.
408
+ *
409
+ * A CEILING, not a default: a host may set a smaller `maxFontBytes`, but nothing can raise it
410
+ * past this. Font bytes come from files, and an unbounded face is a memory-exhaustion vector.
411
+ */
412
+ declare const HARD_MAX_FONT_BYTES: number;
413
+ /** Most faces one snapshot may hold, whatever a caller configures. */
414
+ declare const HARD_MAX_FONT_SOURCES = 256;
415
+ /** Most bytes all admitted faces may total, whatever a caller configures. */
416
+ declare const HARD_MAX_AGGREGATE_FONT_BYTES: number;
417
+ /**
418
+ * Why a face was not admitted.
419
+ *
420
+ * `forbidden` and `hashMismatch` are adversarial signals rather than ordinary failures: the first
421
+ * is a face the host declared off-limits, the second is bytes that are not what their source
422
+ * claimed.
423
+ */
424
+ type FontResolutionErrorCode = 'missing' | 'forbidden' | 'overLimit' | 'malformed' | 'hashMismatch';
425
+ /**
426
+ * A face that could not be admitted, carrying whatever evidence the refusal produced.
427
+ *
428
+ * RETURNED rather than thrown by `FontResourceSnapshot.resolve`, because a missing face is an
429
+ * ordinary condition — layout falls back and carries on. It extends `Error` so a caller that
430
+ * would rather throw can.
431
+ */
432
+ declare class FontResolutionError extends Error {
433
+ readonly name = "FontResolutionError";
434
+ readonly request: FontRequest;
435
+ readonly code: FontResolutionErrorCode;
436
+ readonly limit?: number;
437
+ readonly actual?: number;
438
+ readonly diagnostic?: string;
439
+ readonly expectedHash?: string;
440
+ readonly actualHash?: string;
441
+ constructor(code: FontResolutionErrorCode, request: FontRequest, details?: {
442
+ readonly limit?: number;
443
+ readonly actual?: number;
444
+ readonly diagnostic?: string;
445
+ readonly expectedHash?: string;
446
+ readonly actualHash?: string;
447
+ });
448
+ }
449
+ /**
450
+ * One face offered to a snapshot, before admission.
451
+ *
452
+ * `hash` is checked against the bytes, so a source cannot claim a face it did not supply. Setting
453
+ * `availability` to `forbidden` declares a face that exists but must not be used, which resolves
454
+ * as a typed refusal rather than as "missing".
455
+ */
456
+ interface FontResourceDefinition {
457
+ readonly request: FontRequest;
458
+ readonly id: string;
459
+ readonly bytes: Uint8Array;
460
+ readonly hash: string;
461
+ readonly faceIndex: number;
462
+ readonly availability?: 'available' | 'forbidden';
463
+ }
464
+ /**
465
+ * A host-declared redirect: requests for `from` resolve to `to`.
466
+ *
467
+ * How a metric-compatible substitute is wired in — a document naming Calibri resolves to Carlito
468
+ * without the document being rewritten.
469
+ */
470
+ interface DeclaredFontSubstitution {
471
+ readonly from: FontRequest;
472
+ readonly to: FontRequest;
473
+ readonly lineMetrics?: {
474
+ readonly heightEm: number;
475
+ readonly baselineEm: number;
476
+ };
477
+ }
478
+ /** Whether bytes parse as a usable font, with a diagnostic when they do not. */
479
+ type FontValidationResult = {
480
+ readonly valid: true;
481
+ } | {
482
+ readonly valid: false;
483
+ readonly diagnostic: string;
484
+ };
485
+ /**
486
+ * The injected check that bytes really are a font.
487
+ *
488
+ * Injected because it is the shaper that knows — HarfBuzz can open a face and read its tables,
489
+ * and this layer must not duplicate that judgement. Every byte reaching it is file input.
490
+ */
491
+ type FontByteValidator = (bytes: Uint8Array, faceIndex: number) => FontValidationResult;
492
+ /**
493
+ * An immutable set of admitted faces, and the only way to reach one.
494
+ *
495
+ * `epoch` identifies the snapshot: fonts change by REPLACING it, never by mutation, so a layout
496
+ * pass holds one snapshot for its whole run and cannot observe a face appearing or vanishing
497
+ * midway.
498
+ */
499
+ interface FontResourceSnapshot {
500
+ readonly epoch: number;
501
+ /** The admitted face, or a typed refusal. Never throws. */
502
+ resolve(request: FontRequest): ResolvedFont | FontResolutionError;
503
+ }
504
+ /**
505
+ * How a snapshot is built: which faces, under what limits, validated how.
506
+ *
507
+ * `maxFontBytes` is clamped to {@link HARD_MAX_FONT_BYTES} — a caller may tighten the budget but
508
+ * never widen it past the engine's own ceiling.
509
+ */
510
+ interface FontResourceSnapshotOptions {
511
+ readonly epoch: number;
512
+ readonly maxFontBytes: number;
513
+ readonly resources: readonly FontResourceDefinition[];
514
+ readonly substitutions?: readonly DeclaredFontSubstitution[];
515
+ readonly validateFont: FontByteValidator;
516
+ readonly instrumentation?: FontResourceInstrumentation;
517
+ }
518
+ /**
519
+ * Optional counters for the operations font admission is expected to do RARELY.
520
+ *
521
+ * Exists so tests can assert the absence of work: hashing and byte-copying a 64 MB face on every
522
+ * resolve would not be visible in output, only in a stalled document, so the checks assert these
523
+ * fire once rather than per call.
524
+ */
525
+ interface FontResourceInstrumentation {
526
+ readonly onOwnedByteCopy?: () => void;
527
+ readonly onHash?: () => void;
528
+ readonly onTableScan?: () => void;
529
+ readonly onAdmission?: () => void;
530
+ }
531
+ /**
532
+ * The canonical string identifying one face request.
533
+ *
534
+ * Case- and whitespace-normalized, so `"Times New Roman"` and `"times new roman"` are one key —
535
+ * a document may name a family either way and both must reach the same face.
536
+ */
537
+ declare const fontRequestKey: (request: FontRequest) => string;
538
+ /** Bounded minimum sfnt/TTC check; Task 4 supplies full parser-backed validation. */
539
+ declare const boundedStructuralFontValidator: FontByteValidator;
540
+ /**
541
+ * Admit a set of faces and return the immutable snapshot layout resolves against.
542
+ *
543
+ * Admission is the trust boundary for font bytes: counts and sizes are checked against the hard
544
+ * ceilings, each face's hash is re-derived and compared, and the bytes are handed to the injected
545
+ * validator. A face failing any of these is recorded as a typed refusal rather than dropped, so
546
+ * `resolve` can explain itself instead of answering "missing".
547
+ */
548
+ declare const createFontResourceSnapshot: (options: FontResourceSnapshotOptions) => FontResourceSnapshot;
549
+
550
+ declare const FIXED_POINT: unique symbol;
551
+ /** Fixed-point coordinates are safe integers in units declared by ShapingEnvironment.fixedPointScale. */
552
+ type FixedPoint = number & {
553
+ readonly [FIXED_POINT]: true;
554
+ };
555
+ /**
556
+ * Brand a safe integer as a {@link FixedPoint} coordinate.
557
+ *
558
+ * Fixed point rather than float throughout shaping so that two runs shaped identically compare
559
+ * EQUAL — float accumulation would make the same text measure differently depending on how it was
560
+ * split, and pagination is decided on those measurements.
561
+ *
562
+ * @throws RangeError when the value is not a safe integer.
563
+ */
564
+ declare const fixedPoint: (value: number) => FixedPoint;
565
+ /** Which way a run reads. The parity projection of its bidi embedding level. */
566
+ type TextDirection = 'ltr' | 'rtl';
567
+ /** How fixed-point conversion breaks ties. Part of the shaping fingerprint. */
568
+ type FixedPointRoundingMode = 'halfAwayFromZero' | 'halfToEven' | 'towardZero';
569
+ /** Which Unicode normalization is applied before shaping, if any. */
570
+ type NormalizationPolicy = 'none' | 'NFC' | 'NFD' | 'NFKC' | 'NFKD';
571
+ /**
572
+ * The shaping library and its exact version.
573
+ *
574
+ * Versioned because a library upgrade can change glyph positioning, and a cached measurement
575
+ * taken under the old one must not be reused under the new one.
576
+ */
577
+ interface VersionedShapingLibrary {
578
+ readonly name: string;
579
+ readonly version: string;
580
+ }
581
+ /**
582
+ * Everything that determines how text shapes — the complete input to a
583
+ * {@link ShapingEnvironment}.
584
+ *
585
+ * Exhaustive on purpose. Any field that could change a glyph's position belongs here, because the
586
+ * environment's fingerprint is what decides whether a cached shaped run may be reused.
587
+ */
588
+ interface ShapingEnvironmentInput {
589
+ readonly font: ResolvedFont;
590
+ readonly variationAxes: Readonly<Record<string, number>>;
591
+ readonly shapingLibrary: VersionedShapingLibrary;
592
+ readonly unicodeDataVersion: string;
593
+ readonly normalization: NormalizationPolicy;
594
+ readonly script: string;
595
+ readonly language: string;
596
+ readonly direction: TextDirection;
597
+ readonly features: Readonly<Record<string, number>>;
598
+ readonly fallbackOrder: readonly ResolvedFont[];
599
+ readonly fixedPointScale: number;
600
+ readonly roundingMode: FixedPointRoundingMode;
601
+ }
602
+ /**
603
+ * A validated {@link ShapingEnvironmentInput} — build one with `createShapingEnvironment`.
604
+ *
605
+ * Structurally identical to its input, but the nominal distinction is the point: holding one
606
+ * means the tags, axes and fonts inside it have already been checked.
607
+ */
608
+ interface ShapingEnvironment extends ShapingEnvironmentInput {
609
+ }
610
+ /** One shaping call: the text, its size, its bidi level, and the environment to shape it in. */
611
+ interface ShapeInput {
612
+ readonly text: string;
613
+ readonly fontSizeHalfPoints: number;
614
+ /** Exact UAX #9 embedding/isolate level; direction is its parity projection. */
615
+ readonly bidiLevel: number;
616
+ readonly environment: ShapingEnvironment;
617
+ }
618
+ /**
619
+ * One positioned glyph.
620
+ *
621
+ * `id` is a glyph index in its FACE, not a character — a ligature is one glyph spanning several
622
+ * characters, and a single character may produce several glyphs. Use `cluster` to get back to
623
+ * text.
624
+ */
625
+ interface ShapedGlyph {
626
+ readonly id: number;
627
+ /** UTF-16 text offset identifying the cluster that produced this glyph. */
628
+ readonly cluster: number;
629
+ /** Pen origin before this glyph's shaping offsets, in fixed-point run coordinates. */
630
+ readonly originX: FixedPoint;
631
+ readonly originY: FixedPoint;
632
+ readonly advanceX: FixedPoint;
633
+ readonly advanceY: FixedPoint;
634
+ readonly offsetX: FixedPoint;
635
+ readonly offsetY: FixedPoint;
636
+ /** Exact monochrome outline returned by the admitted HarfBuzz face. */
637
+ readonly outline: GlyphOutline;
638
+ }
639
+ /** A glyph's outline as SVG path data, in font design units. */
640
+ interface GlyphOutline {
641
+ readonly path: string;
642
+ /** Design units per em — divide by this to scale the path to a point size. */
643
+ readonly unitsPerEm: number;
644
+ }
645
+ /**
646
+ * One cluster: the smallest indivisible text-to-glyph correspondence.
647
+ *
648
+ * The unit the CARET moves by. A cluster may be several characters (a ligature) or several glyphs
649
+ * (a decomposed mark), so neither a character index nor a glyph index is a valid caret position —
650
+ * `caretEdges` is, and it includes both endpoints.
651
+ */
652
+ interface ShapedCluster {
653
+ /** Half-open logical UTF-16 range in ShapedRun.text. */
654
+ readonly textStart: number;
655
+ readonly textEnd: number;
656
+ /** Half-open visual glyph range in ShapedRun.glyphs. */
657
+ readonly glyphStart: number;
658
+ readonly glyphEnd: number;
659
+ readonly advance: FixedPoint;
660
+ /** Fixed-point edges from this cluster's visual origin, including both endpoints. */
661
+ readonly caretEdges: readonly FixedPoint[];
662
+ /** Index into ShapedRun.fontSpans, preserving the exact fallback choice. */
663
+ readonly fontSpan: number;
664
+ }
665
+ /** A run's vertical metrics, from the face that shaped it. Drives line height and baseline. */
666
+ interface ShapedVerticalMetrics {
667
+ readonly ascent: FixedPoint;
668
+ readonly descent: FixedPoint;
669
+ readonly lineGap: FixedPoint;
670
+ }
671
+ /**
672
+ * A stretch of glyphs that came from ONE face.
673
+ *
674
+ * A run whose text needed fallback has several of these. Recording the exact face per span is
675
+ * what lets a re-shape reproduce the same result rather than re-running fallback selection and
676
+ * possibly choosing differently.
677
+ */
678
+ interface ShapedFontSpan {
679
+ readonly glyphStart: number;
680
+ readonly glyphEnd: number;
681
+ readonly font: ResolvedFont;
682
+ /** null denotes the primary font; otherwise this is the environment fallback-order index. */
683
+ readonly fallbackIndex: number | null;
684
+ }
685
+ /**
686
+ * One shaped run: text turned into positioned glyphs, with everything needed to measure it, paint
687
+ * it, and put a caret in it.
688
+ *
689
+ * The engine's measurement unit. Layout never measures characters — it measures these.
690
+ */
691
+ interface ShapedRun {
692
+ readonly text: string;
693
+ readonly direction: TextDirection;
694
+ readonly bidiLevel: number;
695
+ readonly glyphs: readonly ShapedGlyph[];
696
+ readonly clusters: readonly ShapedCluster[];
697
+ readonly fontSpans: readonly ShapedFontSpan[];
698
+ readonly metrics: ShapedVerticalMetrics;
699
+ }
700
+ /**
701
+ * A {@link ShapedRun} reduced to the fields two shaping results must agree on to be considered
702
+ * identical.
703
+ *
704
+ * Fonts appear as {@link FontFingerprintInputs} rather than whole `ResolvedFont` objects, so the
705
+ * comparison is over VALUES and does not depend on object identity — which is what makes it work
706
+ * across a reload or a worker boundary.
707
+ */
708
+ interface ShapedRunComparatorInputs {
709
+ readonly text: string;
710
+ readonly direction: TextDirection;
711
+ readonly script: string;
712
+ readonly language: string;
713
+ readonly bidiLevel: number;
714
+ readonly glyphs: readonly ShapedGlyph[];
715
+ readonly clusters: readonly ShapedCluster[];
716
+ readonly fontSpans: readonly {
717
+ readonly glyphStart: number;
718
+ readonly glyphEnd: number;
719
+ readonly fallbackIndex: number | null;
720
+ readonly font: FontFingerprintInputs;
721
+ }[];
722
+ readonly metrics: ShapedVerticalMetrics;
723
+ }
724
+ /**
725
+ * The one thing layout needs from a shaping backend.
726
+ *
727
+ * Injected rather than imported, which is what keeps layout DOM-free and testable: a fixed-metric
728
+ * shaper measures deterministically in a test, HarfBuzz measures for real in a browser, and
729
+ * layout cannot tell the difference.
730
+ */
731
+ interface TextShaper {
732
+ shape(input: ShapeInput): ShapedRun;
733
+ }
734
+ /**
735
+ * A font reduced to the values that identify it for fingerprinting.
736
+ *
737
+ * Includes the content `hash` and `faceIndex`, so two faces with the same family name but
738
+ * different bytes fingerprint differently — which is what stops a cached measurement being reused
739
+ * against a substituted face.
740
+ */
741
+ interface FontFingerprintInputs {
742
+ readonly identity: string;
743
+ readonly id: string;
744
+ readonly family: string;
745
+ readonly request: FontRequest;
746
+ readonly hash: string;
747
+ readonly faceIndex: number;
748
+ readonly byteLength: number;
749
+ readonly substitution: FontSubstitution | null;
750
+ }
751
+ /**
752
+ * A {@link ShapingEnvironment} reduced to comparable values.
753
+ *
754
+ * Records and axis maps become SORTED entry arrays, because two environments differing only in
755
+ * key insertion order must fingerprint the same — otherwise a cache would miss on a difference
756
+ * that changes no glyph.
757
+ */
758
+ interface ShapingEnvironmentFingerprintInputs {
759
+ readonly font: FontFingerprintInputs;
760
+ readonly variationAxes: readonly (readonly [string, number])[];
761
+ readonly shapingLibrary: VersionedShapingLibrary;
762
+ readonly unicodeDataVersion: string;
763
+ readonly normalization: NormalizationPolicy;
764
+ readonly script: string;
765
+ readonly language: string;
766
+ readonly direction: TextDirection;
767
+ readonly features: readonly (readonly [string, number])[];
768
+ readonly fallbackOrder: readonly FontFingerprintInputs[];
769
+ readonly fixedPointScale: number;
770
+ readonly roundingMode: FixedPointRoundingMode;
771
+ }
772
+ /**
773
+ * Validate and freeze a shaping environment.
774
+ *
775
+ * Checks every field that could silently corrupt a measurement: OpenType tags must be four ASCII
776
+ * bytes, script and language must be non-blank and control-character free, fonts must already be
777
+ * validated. Throws rather than coercing — a bad tag that shapes anyway produces a document that
778
+ * measures wrong everywhere and looks fine.
779
+ *
780
+ * @throws TypeError on a malformed tag, name, or axis value.
781
+ */
782
+ declare const createShapingEnvironment: (input: ShapingEnvironmentInput) => ShapingEnvironment;
783
+ /**
784
+ * Reduce an environment to its comparable form, with records sorted so key order cannot affect
785
+ * the result.
786
+ */
787
+ declare const shapingEnvironmentFingerprintInputs: (input: ShapingEnvironmentInput) => ShapingEnvironmentFingerprintInputs;
788
+ /** Canonical serialization of every shaping variable, including byte hash and provenance. */
789
+ declare const shapingEnvironmentFingerprint: (environment: ShapingEnvironmentInput) => string;
790
+ /**
791
+ * Validate and freeze a shaped run against the environment that produced it.
792
+ *
793
+ * Checks the internal consistency a shaper must satisfy: direction matches the environment,
794
+ * cluster ranges tile the text without gaps or overlap, glyph ranges stay in bounds, font spans
795
+ * cover every glyph, and caret edges are monotonic. A shaper that violates any of these produces
796
+ * a caret that lands in the wrong place, which is far harder to diagnose downstream than here.
797
+ *
798
+ * @throws TypeError when the run and environment disagree, or the run is internally inconsistent.
799
+ */
800
+ declare const createShapedRun: (input: ShapedRun, environmentInput: ShapingEnvironmentInput) => ShapedRun;
801
+ /**
802
+ * Reduce a shaped run to its comparable form, validating it on the way through.
803
+ *
804
+ * What the D9 determinism oracles compare: two shaping runs of the same text in the same
805
+ * environment must produce byte-identical structures here.
806
+ */
807
+ declare const shapedRunComparatorInputs: (input: ShapedRun, environment: ShapingEnvironmentInput) => ShapedRunComparatorInputs;
808
+
342
809
  /** `w:vertAlign` — script position, which also scales the run's effective size. */
343
810
  type VerticalAlign = 'baseline' | 'superscript' | 'subscript';
344
811
  /** A resolved underline: its variant, and its colour when it does not follow the text. */
@@ -356,6 +823,16 @@ interface ResolvedUnderline {
356
823
  */
357
824
  interface ResolvedRunStyle {
358
825
  readonly fontFamily: string | null;
826
+ /**
827
+ * The `eastAsia` slot's typeface (`w:rFonts w:eastAsia`/`w:eastAsiaTheme`), or null when
828
+ * no level authors one.
829
+ *
830
+ * OOXML picks a run's face per SCRIPT: `fontFamily` covers the ascii/hAnsi slots, and
831
+ * ideographic/kana/hangul text resolves through this one instead. Kept beside — not
832
+ * folded into — `fontFamily`, because a mixed run needs both at once and the split into
833
+ * slot-homogeneous pieces happens downstream (`piecesOfParagraph`).
834
+ */
835
+ readonly fontFamilyEastAsia: string | null;
359
836
  /** Points. `w:sz` is half-points, so 22 becomes 11. */
360
837
  readonly fontSizePt: number;
361
838
  /** RRGGBB, or null for the inherited/automatic colour. */
@@ -401,16 +878,20 @@ interface ResolvedRunStyle {
401
878
  /** The style a run inherits when it authors nothing. */
402
879
  declare const DEFAULT_RUN_STYLE: ResolvedRunStyle;
403
880
  /**
404
- * The theme part's two Latin typefaces, for resolving `w:rFonts` theme attributes.
881
+ * The theme part's typefaces, for resolving `w:rFonts` theme attributes.
405
882
  *
406
883
  * Structurally identical to the binding lane's `DocumentThemeFonts` and assignable from it.
407
- * Declared here so this lane reads two validated strings rather than the theme tree.
884
+ * Declared here so this lane reads validated strings rather than the theme tree.
408
885
  */
409
886
  interface ThemeFonts {
410
887
  /** `a:majorFont` latin typeface — headings. */
411
888
  readonly major: string | null;
412
889
  /** `a:minorFont` latin typeface — body text. */
413
890
  readonly minor: string | null;
891
+ /** `a:majorFont` east asian typeface (`a:ea`) — headings. Optional for back-compat. */
892
+ readonly majorEastAsia?: string | null;
893
+ /** `a:minorFont` east asian typeface (`a:ea`) — body text. Optional for back-compat. */
894
+ readonly minorEastAsia?: string | null;
414
895
  }
415
896
  /**
416
897
  * Resolve one run's direct formatting.
@@ -439,6 +920,58 @@ declare function runStylesEqual(a: ResolvedRunStyle, b: ResolvedRunStyle): boole
439
920
  */
440
921
  declare function baselineShiftPtOf(style: ResolvedRunStyle): number;
441
922
 
923
+ /**
924
+ * Which `w:rFonts` slot a character resolves its face through.
925
+ *
926
+ * OOXML gives a run up to four faces and picks between them by SCRIPT, so one run of mixed Latin
927
+ * and CJK text uses two different fonts without saying so anywhere in its properties.
928
+ */
929
+ type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';
930
+ /**
931
+ * A run of text sharing one script, direction and font slot — the unit handed to the shaper.
932
+ *
933
+ * Shaping cannot span a script change: Arabic and Latin in one call would produce wrong joining
934
+ * behaviour, so a run is itemized into these first.
935
+ */
936
+ interface ScriptItem {
937
+ readonly from: number;
938
+ readonly to: number;
939
+ readonly direction: TextDirection;
940
+ readonly bidiLevel: number;
941
+ readonly script: 'Zyyy' | 'Latn' | 'Grek' | 'Cyrl' | 'Hani' | 'Hebr' | 'Arab' | 'Deva' | 'Beng' | 'Thai' | 'Khmr';
942
+ readonly slot: FontSlot;
943
+ }
944
+ /**
945
+ * A code point whose script this engine does not itemize.
946
+ *
947
+ * Carries the offending `codePoint` so the caller can report which character stopped it, rather
948
+ * than failing anonymously somewhere in the middle of a paragraph.
949
+ */
950
+ declare class UnsupportedScriptError extends Error {
951
+ readonly name = "UnsupportedScriptError";
952
+ readonly code = "unsupportedScript";
953
+ readonly codePoint: number;
954
+ constructor(codePoint: number);
955
+ }
956
+ /**
957
+ * Split text into shapeable runs by script, bidi level and font slot.
958
+ *
959
+ * Consumes the bidi levels rather than re-deriving direction, so itemization and reordering agree
960
+ * by construction instead of by two implementations happening to match.
961
+ */
962
+ declare function itemizeScriptFontSlots(text: string, paragraphOffset: number, embedding: BidiEmbeddingLevels): readonly ScriptItem[];
963
+ /**
964
+ * The style a piece's text is MEASURED AND PAINTED in, given the font slot it carries.
965
+ *
966
+ * This is the measurer-boundary resolution: pieces and spans keep the run's real resolved
967
+ * style — with `fontFamily` and `fontFamilyEastAsia` both intact, so formatting readback
968
+ * and the format painter see the run as authored — and every consumer that turns text into
969
+ * geometry or ink resolves the face through here. The derived object is memoized per
970
+ * (style, family) by {@link withFontFamily}, so measurers amortizing over style identity
971
+ * keep their caches.
972
+ */
973
+ declare function styleForFontSlot(style: ResolvedRunStyle, slot: FontSlot | undefined): ResolvedRunStyle;
974
+
442
975
  type AllowlistedPageField = 'PAGE' | 'NUMPAGES' | 'SECTIONPAGES';
443
976
  /**
444
977
  * Which allowlisted page fields a header/footer story actually contains.
@@ -454,6 +987,164 @@ interface StoryPageFieldNeeds {
454
987
  readonly hasSectionPages: boolean;
455
988
  }
456
989
 
990
+ /** One recognized REF instruction: the target name and the supported switches, nothing else. */
991
+ interface RefFieldSpec {
992
+ readonly bookmark: string;
993
+ /** `r` / `w` paint the target's number; `n` the same without context; null the range text. */
994
+ readonly numberSwitch: 'r' | 'w' | 'n' | null;
995
+ /** `\h` parsed and inert — the reference paints; navigation is a follow-up. */
996
+ readonly hyperlink: boolean;
997
+ }
998
+ /**
999
+ * Recognize `REF <bookmark> [\r|\w|\n] [\t] [\h] [\* MERGEFORMAT]` or
1000
+ * `NOTEREF <bookmark> [\h] [\* MERGEFORMAT]`, or null for anything else.
1001
+ *
1002
+ * The keyword matches case-insensitively; the bookmark name keeps its authored case (it is a
1003
+ * lookup key into a Map, never an object property, so hostile names like `__proto__` are just
1004
+ * names that resolve to nothing). Any unrecognized switch fails the parse so the field falls
1005
+ * back to its cached result — never the raw instruction, never a guess. NOTEREF's `\p`
1006
+ * (above/below position text) and `\f` (note-style formatting) are unrecognized on purpose.
1007
+ */
1008
+ declare function parseRefInstruction(raw: string): RefFieldSpec | null;
1009
+
1010
+ /** Soft ceiling on abstractNum / num entries read from one part. */
1011
+ declare const MAX_NUMBERING_DEFINITIONS = 512;
1012
+ /** Soft ceiling on override entries per `w:num`. */
1013
+ declare const MAX_LVL_OVERRIDES = 9;
1014
+ /** `w:suff` — what separates a list marker from the text after it. */
1015
+ type ListSuffix = 'tab' | 'space' | 'nothing';
1016
+ /** `w:lvlJc` — how a list marker aligns within its own indent. */
1017
+ type ListMarkerAlign = 'left' | 'center' | 'right';
1018
+ /**
1019
+ * One level's indent, plus which parts the level actually AUTHORED.
1020
+ *
1021
+ * The provenance matters: `w:ind` cascades per-attribute, so a level that authored only `left`
1022
+ * must not overwrite an inherited `hanging` with a synthesized zero.
1023
+ */
1024
+ interface NumberingLevelIndent {
1025
+ readonly left: number;
1026
+ readonly right: number;
1027
+ readonly hanging: number;
1028
+ readonly firstLine: number;
1029
+ /**
1030
+ * Which of these the LEVEL actually authored.
1031
+ *
1032
+ * A level's `w:pPr/w:ind` sits between the paragraph style and direct formatting, so the
1033
+ * merge has to tell "the level says left = 0" from "the level says nothing about left and
1034
+ * the style's value stands". Absent (the default) reads as "says nothing", which is what a
1035
+ * hand-built level in a test means.
1036
+ */
1037
+ readonly stated?: {
1038
+ readonly left: boolean;
1039
+ readonly right: boolean;
1040
+ /** `w:hanging`/`w:firstLine` are one mutually exclusive slot (§17.3.1.10, §17.3.1.12). */
1041
+ readonly firstLineOffset: boolean;
1042
+ };
1043
+ }
1044
+ /** One `w:lvl`: how this depth numbers, what its marker looks like, and how it indents. */
1045
+ interface NumberingLevel {
1046
+ readonly ilvl: number;
1047
+ readonly start: number;
1048
+ readonly numFmt: string;
1049
+ readonly lvlText: string;
1050
+ readonly lvlJc: ListMarkerAlign;
1051
+ readonly suff: ListSuffix;
1052
+ readonly indent: NumberingLevelIndent;
1053
+ /**
1054
+ * `w:lvlRestart` one-based trigger level, or `0` when the level never restarts.
1055
+ * Omitted in XML → restart when the previous level (or any earlier level) is used.
1056
+ */
1057
+ readonly lvlRestart?: number;
1058
+ /**
1059
+ * `w:isLgl` (§17.9.9): render EVERY level referenced by this level's `w:lvlText` in
1060
+ * decimal, whatever number format those levels declare for themselves.
1061
+ */
1062
+ readonly isLgl: boolean;
1063
+ /** Level `w:rPr` as flat properties (for marker face / vanish). */
1064
+ readonly runProperties: readonly OoxmlProperty[];
1065
+ /** True when level run props request vanish — marker must not paint. */
1066
+ readonly vanish: boolean;
1067
+ }
1068
+ /**
1069
+ * One `w:lvlOverride` on a `w:num`: a restart value, a replacement level, or both.
1070
+ *
1071
+ * How two lists share an abstract definition while numbering independently.
1072
+ */
1073
+ interface LevelOverride {
1074
+ readonly startOverride?: number;
1075
+ /** Full level replacement when `w:lvl` is present under the override. */
1076
+ readonly level?: NumberingLevel;
1077
+ }
1078
+ /**
1079
+ * One `w:abstractNum` — the shape of a list, without being a list.
1080
+ *
1081
+ * Never referenced by a paragraph directly. Paragraphs name a {@link NumDefinition}, which names
1082
+ * one of these, so several lists can share a definition and still count separately.
1083
+ */
1084
+ interface AbstractNumDefinition {
1085
+ readonly abstractNumId: string;
1086
+ readonly levels: ReadonlyMap<number, NumberingLevel>;
1087
+ /**
1088
+ * `w:numStyleLink` (§17.9.21): this definition carries no levels of its own — the numbering
1089
+ * lives on the named style. Word follows the link; resolve it with
1090
+ * {@link resolveNumberingStyleLinks} before reading levels, or every paragraph on this
1091
+ * abstract renders with NO marker at all.
1092
+ */
1093
+ readonly numStyleLink?: string;
1094
+ /**
1095
+ * `w:styleLink` (§17.9.23): the definition side of the same pair — this abstract IS the
1096
+ * numbering of the named style. Kept so a link can be verified rather than assumed.
1097
+ */
1098
+ readonly styleLink?: string;
1099
+ }
1100
+ /**
1101
+ * One `w:num` — the thing a paragraph's `w:numId` actually names.
1102
+ *
1103
+ * Points at an {@link AbstractNumDefinition} and may override any of its levels.
1104
+ */
1105
+ interface NumDefinition {
1106
+ readonly numId: string;
1107
+ readonly abstractNumId: string;
1108
+ readonly overrides: ReadonlyMap<number, LevelOverride>;
1109
+ }
1110
+ /**
1111
+ * The bounded projection of `numbering.xml` that list layout resolves against.
1112
+ *
1113
+ * Projection ONLY — never a mutation or serialization authority. Hostile values are dropped or
1114
+ * clamped, and a missing definition resolves to "no list" rather than a guess.
1115
+ */
1116
+ interface NumberingIndex {
1117
+ readonly abstractNums: ReadonlyMap<string, AbstractNumDefinition>;
1118
+ readonly nums: ReadonlyMap<string, NumDefinition>;
1119
+ }
1120
+ /**
1121
+ * Build a numbering index from the root of a numbering part (`w:numbering`).
1122
+ *
1123
+ * Empty / missing roots yield an empty index. Duplicate ids keep the first definition.
1124
+ */
1125
+ /**
1126
+ * Project `numbering.xml` into the bounded index.
1127
+ *
1128
+ * Every ceiling here exists because the input is file-derived: definition counts, override
1129
+ * counts, indent magnitudes and style-link hop depth are all capped, and nothing from the file
1130
+ * becomes a loop bound or an allocation size.
1131
+ */
1132
+ declare function buildNumberingIndex(root: OoxmlElement | null | undefined): NumberingIndex;
1133
+ /** Resolve the effective level for a `numId` + `ilvl`, applying overrides. */
1134
+ /**
1135
+ * The effective level for one paragraph's numbering reference, after overrides and style links.
1136
+ *
1137
+ * Answers null for a reference the index cannot resolve — a paragraph naming a definition the
1138
+ * file never declared is an unnumbered paragraph, not an error.
1139
+ */
1140
+ declare function resolveNumberingLevel(index: NumberingIndex, numId: string, ilvl: number): {
1141
+ readonly abstractNumId: string;
1142
+ readonly level: NumberingLevel;
1143
+ readonly startOverride?: number;
1144
+ } | null;
1145
+ /** Empty index for tests / documents without numbering. */
1146
+ declare const EMPTY_NUMBERING_INDEX: NumberingIndex;
1147
+
457
1148
  interface DrawingPoint {
458
1149
  readonly x: number;
459
1150
  readonly y: number;
@@ -475,6 +1166,432 @@ interface DrawingGeometry {
475
1166
  readonly effectInsets: DrawingInsets;
476
1167
  }
477
1168
 
1169
+ /** Horizontal alignment of a paragraph (`w:jc`, ECMA-376 §17.3.1.13). */
1170
+ type Alignment = 'left' | 'center' | 'right' | 'both';
1171
+
1172
+ /** Soft ceiling on `basedOn` chain length — enough for real templates, refuses hostile graphs. */
1173
+ declare const MAX_STYLE_BASED_ON_DEPTH = 32;
1174
+ /** Soft ceiling on style definitions read from one styles part. */
1175
+ declare const MAX_STYLE_DEFINITIONS = 4096;
1176
+ /** One `w:style` as the cascade reads it: its properties, and the style it is based on. */
1177
+ interface StyleDefinition {
1178
+ readonly styleId: string;
1179
+ readonly type: string;
1180
+ readonly basedOn: string | null;
1181
+ /**
1182
+ * `w:next` — the style Word gives the paragraph that FOLLOWS one in this style.
1183
+ *
1184
+ * Nothing in layout reads it: it is a rule about authoring, not about painting. It is
1185
+ * read here because this is the one place `styles.xml` is parsed, and a second reader
1186
+ * would be a second answer to what a style id means.
1187
+ */
1188
+ readonly next: string | null;
1189
+ readonly paragraphProperties: readonly OoxmlProperty[];
1190
+ readonly runProperties: readonly OoxmlProperty[];
1191
+ /** The style's `w:pPr` node, when present — needed for nested `w:pBdr`. */
1192
+ readonly paragraphPropertiesNode: OoxmlElement | undefined;
1193
+ /** The style's `w:tblPr`, for a `w:type="table"` style. */
1194
+ readonly tablePropertiesNode: OoxmlElement | undefined;
1195
+ /**
1196
+ * `w:tblStylePr` conditional formats by `w:type` (`firstRow`, `band1Horz`, …).
1197
+ *
1198
+ * Word puts a table's real appearance here: `Table Grid` carries its grid in the style's
1199
+ * `w:tblBorders`, and the banded/​header looks live in these. A document states only
1200
+ * `<w:tblStyle w:val="TableGrid"/>`.
1201
+ */
1202
+ readonly conditionalTableFormats: ReadonlyMap<string, OoxmlElement>;
1203
+ }
1204
+ /**
1205
+ * The whole styles part, indexed and ready to resolve against.
1206
+ *
1207
+ * `cacheToken` is load-bearing: it folds into layout cache producers so breaks measured under one
1208
+ * styles part are never reused under another.
1209
+ */
1210
+ interface StyleCascadeTable {
1211
+ /**
1212
+ * Bounded fingerprint folded into layout cache producers so a different styles part cannot
1213
+ * reuse breaks measured under another cascade. Computed once per table (FNV-1a hex).
1214
+ */
1215
+ readonly cacheToken: string;
1216
+ readonly docDefaultsRun: readonly OoxmlProperty[];
1217
+ readonly docDefaultsParagraph: readonly OoxmlProperty[];
1218
+ readonly docDefaultsParagraphNode: OoxmlElement | undefined;
1219
+ /** `w:style[@w:default='1'][@w:type='paragraph']` — last wins among defaults of that type. */
1220
+ readonly defaultParagraphStyleId: string | null;
1221
+ /** `w:style[@w:default='1'][@w:type='character']` — last wins among defaults of that type. */
1222
+ readonly defaultCharacterStyleId: string | null;
1223
+ /**
1224
+ * `w:style[@w:default='1'][@w:type='table']` — last wins among defaults of that type.
1225
+ *
1226
+ * A `w:tbl` with no `w:tblStyle` still resolves against this one. Word's own
1227
+ * `TableNormal` is where the 0/108/0/108 twip cell margins live, so skipping it gives
1228
+ * every unstyled table padding the document says it does not have.
1229
+ */
1230
+ readonly defaultTableStyleId: string | null;
1231
+ /**
1232
+ * The theme part's typefaces, for `w:rFonts` theme references.
1233
+ *
1234
+ * Lives on the cascade because it is document-level style material with the same
1235
+ * lifetime, and because every site that resolves run properties already holds this table.
1236
+ */
1237
+ readonly themeFonts: ThemeFonts;
1238
+ readonly styles: ReadonlyMap<string, StyleDefinition>;
1239
+ }
1240
+ /**
1241
+ * A paragraph's properties after the cascade, plus the same list WITHOUT its own `w:pPr`.
1242
+ *
1243
+ * Both, because a writer needs to know what a paragraph INHERITS to decide whether setting a
1244
+ * value is a change or a no-op — and writing back an inherited value freezes it into the
1245
+ * paragraph as though the author had chosen it.
1246
+ */
1247
+ interface CascadedParagraphFormatting {
1248
+ /** Flat paragraph properties in cascade order (defaults → bases → style → direct). */
1249
+ readonly paragraphProperties: readonly OoxmlProperty[];
1250
+ /**
1251
+ * The same list WITHOUT the paragraph's own `w:pPr` — everything it inherits.
1252
+ *
1253
+ * Numbering needs the two tiers apart: a level's `w:pPr/w:ind` outranks the style's and is
1254
+ * outranked by the paragraph's own, and a flattened list cannot say which is which.
1255
+ */
1256
+ readonly inheritedParagraphProperties: readonly OoxmlProperty[];
1257
+ /** Matching `w:pPr` nodes for nested border resolution. */
1258
+ readonly paragraphPropertyNodes: readonly OoxmlNode[];
1259
+ /**
1260
+ * Inherited run properties for CONTENT runs (before direct run `rPr`).
1261
+ *
1262
+ * Does NOT include direct `w:pPr/w:rPr` — that formats the paragraph MARK only
1263
+ * (ECMA-376 §17.3.1.36). Folding mark `w:sz` into content made BodyText runs with no
1264
+ * direct size paint at the mark's size (Selection Notice "or" alternative → 6.5pt).
1265
+ */
1266
+ readonly runProperties: readonly OoxmlProperty[];
1267
+ /**
1268
+ * Content cascade plus direct `w:pPr/w:rPr` — empty-line metrics and last-line mark height.
1269
+ */
1270
+ readonly markRunProperties: readonly OoxmlProperty[];
1271
+ /**
1272
+ * The style this paragraph resolved to, or null when it names none and there is no
1273
+ * document default. `w:contextualSpacing` compares neighbours by exactly this.
1274
+ */
1275
+ readonly styleId: string | null;
1276
+ }
1277
+ /** Accepted style ids only — over-long, control-bearing, or dangerous keys are dropped. */
1278
+ declare function isValidStyleId(raw: string | undefined): raw is string;
1279
+ /**
1280
+ * What a table style contributes to the paragraphs of ONE cell: the style's whole-table
1281
+ * `w:pPr`/`w:rPr` followed by every `w:tblStylePr` the cell is under (17.7.6.6), weakest
1282
+ * first in the caller's condition order (banding, column, row, corner).
1283
+ *
1284
+ * This is how Word makes a header row bold and centred while the document states nothing
1285
+ * but `<w:tblStyle w:val="…"/>` on the table and plain runs in the cells.
1286
+ */
1287
+ interface TableCellStyleFormatting {
1288
+ readonly paragraphProperties: readonly OoxmlProperty[];
1289
+ /** Matching `w:pPr` nodes, for nested `w:pBdr` / `w:tabs` resolution. */
1290
+ readonly paragraphPropertyNodes: readonly OoxmlElement[];
1291
+ /** Inherited run properties for every run in the cell, before the paragraph style. */
1292
+ readonly runProperties: readonly OoxmlProperty[];
1293
+ }
1294
+ /**
1295
+ * Build a cascade table from a styles part root.
1296
+ *
1297
+ * Only direct `w:style` children of the root participate (bounded count). Duplicate
1298
+ * `styleId` values keep the last definition, matching Word's reader for this fixture class.
1299
+ * Default paragraph/character style ids track `w:default="1"` with the same last-wins rule.
1300
+ */
1301
+ declare function buildStyleCascadeTable(stylesRoot: OoxmlElement | null, themeFonts?: ThemeFonts): StyleCascadeTable;
1302
+ /**
1303
+ * Cascade paragraph + inherited run properties for one paragraph's direct `w:pPr`.
1304
+ *
1305
+ * Order: `docDefaults` → table style → `basedOn` ancestors → paragraph style → direct
1306
+ * formatting, which is the style hierarchy of 17.7.2: a table style sits above the document
1307
+ * defaults and below the paragraph style a cell paragraph names for itself.
1308
+ * When `w:pStyle` is absent, the document's default paragraph style (`w:default="1"`) is used.
1309
+ * Direct formatting is last so it overrides inherited values inside the existing resolvers.
1310
+ */
1311
+ declare function cascadeParagraphFormatting(table: StyleCascadeTable, directPPr: OoxmlNode | undefined, tableCellStyle?: TableCellStyleFormatting): CascadedParagraphFormatting;
1312
+ /**
1313
+ * Bottom border after cascade: a later `w:pBdr` replaces an earlier one; absence inherits.
1314
+ * `nil`/`none` clear the edge via `paragraphBorders`.
1315
+ */
1316
+ declare function cascadedBottomBorder(paragraphPropertyNodes: readonly OoxmlNode[]): ParagraphBorderEdge | undefined;
1317
+ /**
1318
+ * Merge inherited paragraph-style run props with a run's direct `rPr` (direct last).
1319
+ *
1320
+ * When a cascade table is supplied, also resolves `w:rStyle` character styles (basedOn chain,
1321
+ * cycle/depth capped). Runs without an explicit `rStyle` pick up the default character style
1322
+ * (`w:default="1"`). Precedence: inherited → character style chain → direct formatting.
1323
+ *
1324
+ * PASS `inheritedRunProperties` BY IDENTITY. It must be an array
1325
+ * {@link cascadeParagraphFormatting} returned — `runProperties` or `markRunProperties` — and
1326
+ * not a copy of one. A toggle property (ECMA-376 §17.7.3) resolves per level of the style
1327
+ * hierarchy, and the levels below the character style resolve to more than a true or a false:
1328
+ * whether the document defaults' short circuit is still standing decides what the character
1329
+ * style's own toggle does next, and no single `w:b` element can spell that. The paragraph
1330
+ * cascade attaches that state to the array it returns, so spreading, filtering or sorting the
1331
+ * list drops it. (`Object.freeze` returns the same object, so that one is safe.) A list
1332
+ * without it is read as one ordinary level, which is the most a bare property list can say
1333
+ * and is what a caller assembling its own list gets.
1334
+ */
1335
+ declare function cascadeRunProperties(inheritedRunProperties: readonly OoxmlProperty[], directRunProperties: readonly OoxmlProperty[], table?: StyleCascadeTable): readonly OoxmlProperty[];
1336
+ /** Everything the line breaker needs about one paragraph, already cascaded and converted. */
1337
+ interface ParagraphLayoutInputs {
1338
+ readonly props: OoxmlProperty[];
1339
+ readonly indent: {
1340
+ left: number;
1341
+ right: number;
1342
+ hanging: number;
1343
+ firstLine: number;
1344
+ };
1345
+ readonly available: number;
1346
+ readonly alignment: Alignment;
1347
+ readonly spacing: ParagraphSpacing;
1348
+ /** Resolved `w:line` / `w:lineRule`; single spacing where the cascade says nothing. */
1349
+ readonly lineSpacing: ParagraphLineSpacing;
1350
+ /** `w:contextualSpacing`: drop before/after between same-style neighbours. */
1351
+ readonly contextualSpacing: boolean;
1352
+ /** Resolved paragraph style id, for the `w:contextualSpacing` neighbour comparison. */
1353
+ readonly styleId: string | null;
1354
+ readonly bottomBorder: ParagraphBorderEdge | undefined;
1355
+ /**
1356
+ * Every `CT_PBdr` edge after cascade, not just the bottom one.
1357
+ *
1358
+ * `bottomBorder` stays alongside it because the fragment signature and the table flow
1359
+ * read that field by name; this is the whole box, so a cell paragraph gets the same
1360
+ * frame a body paragraph does.
1361
+ */
1362
+ readonly borders: ParagraphBorders;
1363
+ /** Validated 6-hex paragraph shading fill from cascaded `w:pPr/w:shd`, absent for none. */
1364
+ readonly shading: string | undefined;
1365
+ readonly inheritedRunProperties: readonly OoxmlProperty[];
1366
+ /**
1367
+ * Paragraph-mark cascade (`inheritedRunProperties` + direct `w:pPr/w:rPr`).
1368
+ * Empty-line sizing and last-line mark height — never content-run face.
1369
+ */
1370
+ readonly markRunProperties: readonly OoxmlProperty[];
1371
+ /** Cascaded custom tab stops + default interval for paragraph-flow breaking. */
1372
+ readonly tabStops: ResolvedTabStops;
1373
+ /**
1374
+ * Fingerprint folded into the paragraph layout cache key — nested `w:tabs` are absent
1375
+ * from flat property bags, so style-inherited stops must be named explicitly.
1376
+ */
1377
+ readonly tabStopsCacheToken: string;
1378
+ /** Resolved list marker inputs when the paragraph participates in numbering. */
1379
+ readonly listItem?: ResolvedListItem;
1380
+ }
1381
+ /**
1382
+ * Resolve every paragraph input semantic layout / table cells share: cascaded props when a
1383
+ * style table is present, otherwise direct formatting only.
1384
+ *
1385
+ * When `listItem` is provided, its merged level indent becomes the paragraph indent (list
1386
+ * hanging / left from `numbering.xml`), which is what Word uses for fixture list paragraphs
1387
+ * that author no direct `w:ind`.
1388
+ *
1389
+ * `tableCellStyle` carries what the enclosing table's style says about this cell's
1390
+ * paragraphs; body paragraphs pass nothing.
1391
+ *
1392
+ * `inTableCell` is asked for separately because a cell paragraph may have no table style to
1393
+ * inherit at all, and `w:beforeAutospacing` still needs to know it is in a cell.
1394
+ */
1395
+ declare function resolveParagraphLayoutInputs(paragraph: OoxmlElement, contentWidth: number, styleCascade: StyleCascadeTable | undefined, listItem?: ResolvedListItem, tableCellStyle?: TableCellStyleFormatting, inTableCell?: boolean): ParagraphLayoutInputs;
1396
+
1397
+ /**
1398
+ * A paragraph's list membership fully resolved: definition, level, marker text and geometry.
1399
+ *
1400
+ * `markerText` is already expanded through the counter state, so it is the string a reader sees
1401
+ * rather than the `w:lvlText` template.
1402
+ */
1403
+ interface ResolvedListItem {
1404
+ readonly numId: string;
1405
+ readonly ilvl: number;
1406
+ readonly abstractNumId: string;
1407
+ /** `w:numFmt` of the resolved level — `bullet` or a numbering format. */
1408
+ readonly numFmt: string;
1409
+ readonly markerText: string;
1410
+ readonly markerAlign: ListMarkerAlign;
1411
+ readonly suffix: ListSuffix;
1412
+ /** Effective indent after merging level + paragraph indents, in points. */
1413
+ readonly indent: NumberingLevelIndent;
1414
+ readonly markerStyle: ResolvedRunStyle;
1415
+ /** Fingerprint for layout cache keys (indent, marker text, marker face). */
1416
+ readonly cacheToken: string;
1417
+ }
1418
+ /**
1419
+ * Read `w:numPr` from cascaded paragraph-property nodes (lowest precedence first).
1420
+ *
1421
+ * Flat `OoxmlProperty[]` bags drop nested `ilvl`/`numId`, so this walks the tree nodes
1422
+ * the same way borders and tabs do.
1423
+ *
1424
+ * `w:ilvl` and `w:numId` inherit INDEPENDENTLY through the style chain and the direct
1425
+ * `w:pPr` (§17.3.1.19): a tier stating only the level keeps the id it inherits — Word's
1426
+ * standard Heading2–Heading9 shape, where only Heading1 names the `w:num` — and a tier
1427
+ * stating only the id keeps the inherited level. Treating each `w:numPr` node as a full
1428
+ * replacement dropped the id at every level-only tier and unnumbered the paragraph. A
1429
+ * stated `w:numId w:val="0"` (or an invalid value) still switches numbering off at that
1430
+ * tier even when a lower tier set one; a higher tier stating a valid id re-enables. A
1431
+ * level-only tier with no id inherited from below resolves to no numbering.
1432
+ */
1433
+ declare function readNumPr(paragraphPropertyNodes: readonly OoxmlNode[]): {
1434
+ numId: string;
1435
+ ilvl: number;
1436
+ } | null;
1437
+ /**
1438
+ * Resolve `w:numStyleLink` delegation using the document's styles (§17.9.21).
1439
+ *
1440
+ * Without a style table there is nothing to follow, so the index is returned unchanged —
1441
+ * and so it is when nothing delegates, which keeps layout cache identity.
1442
+ */
1443
+ declare function withNumberingStyleLinks(index: NumberingIndex, styleCascade: StyleCascadeTable | undefined): NumberingIndex;
1444
+ /**
1445
+ * The effective indent of a list paragraph: STYLE, then the numbering LEVEL, then DIRECT.
1446
+ *
1447
+ * Word applies a level's `w:pPr/w:ind` between the paragraph style and the paragraph's own
1448
+ * formatting, per attribute — and the ordering matters on real documents. A converted
1449
+ * agreement numbers its `(a)` items with a level stating `left=1512 hanging=738` under a
1450
+ * `ListParagraph` style stating `left=775 hanging=624`, and states only `hanging="737"` on
1451
+ * the paragraph itself. Reading the flattened cascade as "the paragraph's indent" gave the
1452
+ * STYLE's 775 to a level that had overridden it, so every lettered sub-item hung a full
1453
+ * indent step to the left of where Word puts it.
1454
+ *
1455
+ * `inherited` is the cascade WITHOUT the paragraph's own `w:pPr` (defaults, table cell style,
1456
+ * style chain); `direct` is that `w:pPr` alone.
1457
+ */
1458
+ declare function mergeListIndent(levelIndent: NumberingLevelIndent, inherited: readonly OoxmlProperty[], direct?: readonly OoxmlProperty[]): NumberingLevelIndent;
1459
+ /**
1460
+ * Collect paragraphs of a block list in document order, descending into tables.
1461
+ *
1462
+ * Caps nesting so a hostile nested-table document cannot recurse without bound.
1463
+ */
1464
+ declare function walkStoryParagraphs(blocks: readonly OoxmlElement[], maxTableDepth?: number): OoxmlElement[];
1465
+ /**
1466
+ * Resolve every list paragraph in a story to a {@link ResolvedListItem}, keyed by node id.
1467
+ *
1468
+ * Non-list paragraphs are absent from the map. Hostile / missing numbering resolves inertly
1469
+ * (paragraph omitted — laid out as ordinary text).
1470
+ */
1471
+ declare function resolveStoryListItems(blocks: readonly OoxmlElement[], index: NumberingIndex, styleCascade: StyleCascadeTable | undefined, isFontAvailable?: (family: string) => boolean): ReadonlyMap<string, ResolvedListItem>;
1472
+ /**
1473
+ * Attach a full-story list-item map to layout options.
1474
+ *
1475
+ * Resolves once over `blocks` (body story including table cells) so counters continue across
1476
+ * section boundaries. No-ops when numbering is absent.
1477
+ */
1478
+ declare function withResolvedListItems<T extends {
1479
+ readonly numberingIndex?: NumberingIndex;
1480
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
1481
+ readonly styleCascade?: StyleCascadeTable;
1482
+ readonly isFontAvailable?: (family: string) => boolean;
1483
+ }>(options: T, blocks: readonly OoxmlElement[]): T & {
1484
+ readonly numberingIndex: NumberingIndex;
1485
+ readonly listItems?: ReadonlyMap<string, ResolvedListItem>;
1486
+ };
1487
+ /**
1488
+ * Horizontal marker box inside the first-line indent slot.
1489
+ *
1490
+ * Coordinates are relative to the same origin as paragraph content (`indent.left` is the
1491
+ * text start). Returns null when there is nothing to paint.
1492
+ *
1493
+ * `w:hanging` and a positive `w:firstLine` are one mutually exclusive slot
1494
+ * (§17.3.1.10, §17.3.1.12), so the marker has two placements, not an interaction:
1495
+ * a hanging level puts the marker BEFORE the text start (`left - hanging`); a
1496
+ * positive-firstLine level puts it AFTER (`left + firstLine`) — the standard legal
1497
+ * shape `w:ind w:left="0" w:firstLine="720"` numbers at 0.5" while continuation
1498
+ * lines return to the margin. Reading only the hanging model painted every such
1499
+ * marker at the left margin.
1500
+ */
1501
+ declare function listMarkerBox(item: ResolvedListItem, markerWidth: number, lineY: number, lineHeight: number): {
1502
+ x: number;
1503
+ y: number;
1504
+ width: number;
1505
+ height: number;
1506
+ } | null;
1507
+
1508
+ /**
1509
+ * The story's resolved REF inputs for one layout pass.
1510
+ *
1511
+ * Threaded as a runtime rider on the layout options (see `layoutSemanticDocument`) rather
1512
+ * than a `SemanticLayoutOptions` member, so the public options surface stays put. Absent
1513
+ * means every REF field paints its cached result — the pre-existing degradation, and the one
1514
+ * header/footer and text-box stories still take. Note stories receive the body's context
1515
+ * through `NotesLayoutInput.refFields`.
1516
+ */
1517
+ interface RefFieldContext {
1518
+ /**
1519
+ * Content token over every PAINTED REF output in the story, for the section prepass memo.
1520
+ * A renumbering edit can move a REF value in a section whose own blocks and list map are
1521
+ * identity-unchanged, and this token is the only validator that sees it. A field that
1522
+ * failed calibration contributes its (session-constant) cached text, so the token still
1523
+ * moves exactly when painted output moves.
1524
+ */
1525
+ readonly valuesToken: string;
1526
+ /** The paragraph's REF outputs folded for its block cache key; `''` when it holds none. */
1527
+ tokenForParagraph(paragraphId: string): string;
1528
+ /**
1529
+ * The live value ONE field paints, keyed by its begin / `w:fldSimple` node id, or null to
1530
+ * keep the cached result (failed calibration, unresolvable, or an anchor this scan never
1531
+ * saw). Anchor-keyed so the projection's paint and this context's token fold read the SAME
1532
+ * calibration verdict, however each walk collected the field's cached text.
1533
+ */
1534
+ liveValueOf(anchorId: string, spec: RefFieldSpec): string | null;
1535
+ /**
1536
+ * The synthesized display of ONE AUTONUM-family field, keyed by its begin / `w:fldSimple`
1537
+ * node id, or null to paint nothing (unsupported switches, or an anchor this scan never
1538
+ * saw — both the field's historical rendering). These fields carry no cached result at
1539
+ * all, so there is no calibration: the sequential value is the only display they have.
1540
+ * Optional so hand-built contexts predating it stay valid.
1541
+ */
1542
+ autonumValueOf?(anchorId: string): string | null;
1543
+ /**
1544
+ * The deferred projection of ONE `PAGEREF` field, or null to keep the cached result
1545
+ * (missing bookmark, unsupported switches, or an anchor this scan never saw).
1546
+ *
1547
+ * A `PAGEREF` value is a property of pagination, so the scan cannot compute it — it hands
1548
+ * the projection the resolved TARGET and the calibration inputs instead, and document
1549
+ * finalize (`finalizePageFieldProjection`) substitutes the page number the target's first
1550
+ * fragment lands on. Optional so hand-built contexts predating it stay valid.
1551
+ */
1552
+ pageRefProjectionOf?(anchorId: string, spec: RefFieldSpec): PageRefFieldProjection | null;
1553
+ }
1554
+ /**
1555
+ * Sticky calibration identity for one `PAGEREF` field.
1556
+ *
1557
+ * An opaque frozen object rather than a mutable verdict holder because span markers are
1558
+ * serialized into fragment signatures — a verdict written into the marker would move a
1559
+ * signature that no painted output moved. The verdict itself lives beside the finalize pass
1560
+ * (`field-page-furniture.ts`), keyed weakly on this object; the registry below carries the
1561
+ * object across passes the same way REF verdicts are carried.
1562
+ */
1563
+ type PageRefCalibrationCell = Readonly<Record<never, never>>;
1564
+ /**
1565
+ * What a `PAGEREF` span carries to document finalize: the resolved target, the normalized
1566
+ * authored cache (the calibration oracle), and the sticky calibration identity.
1567
+ */
1568
+ interface PageRefFieldProjection {
1569
+ /** Canonical id of the paragraph the bookmark names — first declaration wins. */
1570
+ readonly targetParagraphId: string;
1571
+ /** The authored cached result, whitespace-collapsed — what the computed number must reproduce. */
1572
+ readonly cached: string;
1573
+ readonly calibration: PageRefCalibrationCell;
1574
+ }
1575
+ /**
1576
+ * Note parts whose stories join the context: their REF fields resolve against the body's
1577
+ * bookmarks and numbering (a footnote citing "Section 1.2(c)" targets a body paragraph),
1578
+ * and their bookmark declarations become plain-REF targets. Number switches aimed AT a
1579
+ * note paragraph still fall back to the cached result — the list map is the body's.
1580
+ */
1581
+ interface RefNoteParts {
1582
+ readonly footnotesPart: OoxmlPart | null;
1583
+ readonly endnotesPart: OoxmlPart | null;
1584
+ }
1585
+ /**
1586
+ * Resolve the document's REF fields for one layout pass, or null when it has none.
1587
+ *
1588
+ * Bookmarks and REF fields resolve across the body story and (when `notes` is given) the
1589
+ * footnote/endnote stories, against ONE shared target index — a footnote REF finds the
1590
+ * body bookmark it cites. Header/footer and text-box stories are not given a context and
1591
+ * keep painting cached results.
1592
+ */
1593
+ declare function resolveStoryRefFields(blocks: readonly OoxmlElement[], listItems: ReadonlyMap<string, ResolvedListItem> | undefined, notes?: RefNoteParts): RefFieldContext | null;
1594
+
478
1595
  /**
479
1596
  * Why textbox story layout stopped short.
480
1597
  *
@@ -647,6 +1764,15 @@ interface FieldAtomMarker {
647
1764
  /** The field's `\#` numeric picture, applied when finalize substitutes the value. */
648
1765
  readonly picture?: string;
649
1766
  };
1767
+ /**
1768
+ * A BODY `PAGEREF` atom whose value is the page number its bookmark target lands on.
1769
+ *
1770
+ * Deferred exactly like {@link pageField}: the paragraph walk paints the field's cached
1771
+ * result (or the placeholder digit when the file cached none) and records the resolved
1772
+ * target here; document finalize substitutes the number of the page hosting the target's
1773
+ * first fragment, gated per field on the calibration verdict.
1774
+ */
1775
+ readonly pageRef?: PageRefFieldProjection;
650
1776
  }
651
1777
  /** A half-open model-offset range, in the paragraph's own UTF-16 offset space. */
652
1778
  interface ModelRange {
@@ -978,14 +2104,24 @@ interface StyleSpanRecord {
978
2104
  * point put the caret where no glyph is.
979
2105
  */
980
2106
  readonly style: ResolvedRunStyle;
2107
+ /**
2108
+ * The `w:rFonts` slot this span's text resolves its face through; absent means the base
2109
+ * (ascii/hAnsi) slots. {@link style} stays the run's full resolution — with both
2110
+ * `fontFamily` and `fontFamilyEastAsia` — so formatting readback and the format painter
2111
+ * see the run as authored; measurement, paint and hit-testing resolve the effective face
2112
+ * with `styleForFontSlot(span.style, span.fontSlot)`.
2113
+ */
2114
+ readonly fontSlot?: FontSlot;
981
2115
  readonly box: LayoutBox;
982
2116
  /**
983
2117
  * Cumulative advances from {@link box}.x to each UTF-16 caret boundary in {@link text}.
984
2118
  *
985
- * Length is `text.length + 1` (both endpoints). Layout publishes these so hit-testing and
986
- * the caret read the same per-cluster edges the span was measured with, rather than
987
- * re-measuring a prefix at interaction time or interpolating across {@link box}.width —
988
- * OpenSpec task 13.5. Absent on older records; consumers fall back to the measurer.
2119
+ * Length is `text.length + 1` (both endpoints). When PRESENT, these are the interaction
2120
+ * authority: hit-testing and the caret read them instead of re-measuring. Layout does not
2121
+ * publish them eagerly — measuring every character of every laid span dominated page
2122
+ * repagination — so interactions measure prefixes on demand through the same measurer the
2123
+ * span was laid with (memoized per span; see `prefixWidth` in semantic-hit-test.ts), and
2124
+ * only interpolate across {@link box}.width when no measurer is supplied.
989
2125
  */
990
2126
  readonly caretEdges?: readonly number[];
991
2127
  /**
@@ -1682,4 +2818,4 @@ interface TextMeasurer {
1682
2818
  };
1683
2819
  }
1684
2820
 
1685
- export { type LineSpacingRule as $, type AnchoredDrawingRecord as A, type BlockFragmentRecord as B, type ContentControlBoundaryRecord as C, type DrawingPoint as D, DEFAULT_TAB_INTERVAL_PT as E, type FieldLinkProjector as F, DEFAULT_TAB_INTERVAL_TWIPS as G, type HyperlinkProjector as H, type InlineDrawingRecord as I, EMPTY_TAB_STOPS as J, type EquationFractionGeometry as K, type LineRecord as L, type ModelRange as M, type EquationGeometry as N, type EquationNaryGeometry as O, type PageGeometry as P, type EquationRadicalGeometry as Q, type ResolvedRunStyle as R, type SemanticLayout as S, type TextMeasurer as T, type EquationRowGeometry as U, type EquationScriptGeometry as V, type EquationSpanRecord as W, type EquationTextGeometry as X, type FieldAtomMarker as Y, type HeaderFooterStoryRecord as Z, type HyperlinkFieldSpec as _, type StyleSpanRecord as a, type ListMarkerRecord as a0, MAX_BORDER_SPACE_PT as a1, MAX_BORDER_WIDTH_PT as a2, MAX_PARAGRAPH_SPACING_PT as a3, MAX_TABLE_BORDER_STROKES as a4, MAX_TAB_POSITION_TWIPS as a5, MAX_TAB_STOPS as a6, PARAGRAPH_BORDER_SIDES as a7, type ParagraphAutoSpacingContext as a8, type ParagraphBorderSide as a9, cascadedTabStops as aA, collapsedSpaceBefore as aB, defaultTabIntervalFromSettings as aC, displayText as aD, layoutEquation as aE, measureDisplayText as aF, nextTabDestination as aG, paragraphBorderExtentPt as aH, paragraphBorderStrokeWidthPt as aI, paragraphBorders as aJ, paragraphBordersFingerprint as aK, paragraphBreaksBefore as aL, paragraphContextualSpacing as aM, paragraphLineSpacing as aN, paragraphSpacing as aO, paragraphTabStops as aP, readBorderSide as aQ, readCellBorders as aR, readTableBorders as aS, resolveBorderConflict as aT, resolveRunStyle as aU, resolveTableCellBorderGrid as aV, runStylesEqual as aW, tabAdvanceWidth as aX, tabStopsFingerprint as aY, withDefaultTabInterval as aZ, type ParagraphBorderStrokeRecord as aa, type ParagraphBottomBorderRecord as ab, type ParagraphIndent as ac, type ResolvedCellBorders as ad, type ResolvedTableBorderEdge as ae, type ResolvedTableBorderEdgeSegment as af, type ResolvedUnderline as ag, SINGLE_LINE_SPACING as ah, type SourceRange as ai, type SpanLinkRecord as aj, TAB_LEADER_GLYPH as ak, type TabAlignment as al, type TabDestination as am, type TabLeader as an, type TabStop as ao, type TableBorderSideName as ap, type TableBorderStrokeRecord as aq, type TableFragmentRecord as ar, type VerticalAlign as as, appliedSpaceBefore as at, applyLineSpacing as au, baselineShiftPtOf as av, borderExtentPt as aw, borderWeight as ax, bottomBorderExtentPt as ay, cascadedParagraphBorders as az, type TableRowFragmentRecord as b, type TableCellFragmentRecord as c, type LayoutBox as d, type DrawingInsets as e, type ThemeFonts as f, type ParagraphSpacing as g, type ParagraphLineSpacing as h, type ParagraphBorderEdge as i, type ParagraphBorders as j, type ResolvedTabStops as k, type PageRecord as l, type TableBorderSide as m, type ContentControlLock as n, type ParagraphFragmentRecord as o, type InlineDrawingLayoutContext as p, type StoryPageFieldNeeds as q, type CellBorderBox as r, type TableBorderBox as s, AUTO_PARAGRAPH_SPACING_PT as t, type BorderGridGeometry as u, type ContentControlGeometryFragment as v, type ContentControlLevel as w, type ContentControlMappedType as x, DEFAULT_PAGE_GEOMETRY as y, DEFAULT_RUN_STYLE as z };
2821
+ export { type DeclaredFontSubstitution as $, type AnchoredDrawingRecord as A, type BlockFragmentRecord as B, type ContentControlBoundaryRecord as C, type DrawingPoint as D, AUTO_PARAGRAPH_SPACING_PT as E, type FontResourceSnapshot as F, type AbstractNumDefinition as G, type HyperlinkProjector as H, type InlineDrawingRecord as I, type BidiEmbeddingLevels as J, type BorderGridGeometry as K, type LineRecord as L, type ModelRange as M, type NormalizationPolicy as N, type CascadedParagraphFormatting as O, type PageGeometry as P, type ContentControlGeometryFragment as Q, type ResolvedRunStyle as R, type SemanticLayout as S, type TextMeasurer as T, type ContentControlLevel as U, type VersionedShapingLibrary as V, type ContentControlMappedType as W, DEFAULT_PAGE_GEOMETRY as X, DEFAULT_RUN_STYLE as Y, DEFAULT_TAB_INTERVAL_PT as Z, DEFAULT_TAB_INTERVAL_TWIPS as _, type StyleSpanRecord as a, type ResolvedUnderline as a$, EMPTY_NUMBERING_INDEX as a0, EMPTY_TAB_STOPS as a1, type EquationFractionGeometry as a2, type EquationGeometry as a3, type EquationNaryGeometry as a4, type EquationRadicalGeometry as a5, type EquationRowGeometry as a6, type EquationScriptGeometry as a7, type EquationSpanRecord as a8, type EquationTextGeometry as a9, MAX_PARAGRAPH_SPACING_PT as aA, MAX_STYLE_BASED_ON_DEPTH as aB, MAX_STYLE_DEFINITIONS as aC, MAX_TABLE_BORDER_STROKES as aD, MAX_TAB_POSITION_TWIPS as aE, MAX_TAB_STOPS as aF, type NumDefinition as aG, type NumberingLevelIndent as aH, PARAGRAPH_BORDER_SIDES as aI, type PageRefCalibrationCell as aJ, type PageRefFieldProjection as aK, type ParagraphAutoSpacingContext as aL, type ParagraphBorderEdge as aM, type ParagraphBorderSide as aN, type ParagraphBorderStrokeRecord as aO, type ParagraphBorders as aP, type ParagraphBottomBorderRecord as aQ, type ParagraphIndent as aR, type ParagraphLayoutInputs as aS, type ParagraphLineSpacing as aT, type ParagraphSpacing as aU, type RefFieldSpec as aV, type RefNoteParts as aW, type ResolvedCellBorders as aX, type ResolvedTabStops as aY, type ResolvedTableBorderEdge as aZ, type ResolvedTableBorderEdgeSegment as a_, type FieldAtomMarker as aa, type FontFingerprintInputs as ab, type FontRequest as ac, FontResolutionError as ad, type FontResolutionErrorCode as ae, type FontResourceDefinition as af, type FontResourceInstrumentation as ag, type FontResourceSnapshotOptions as ah, type FontSlot as ai, type FontSubstitution as aj, type FontValidationResult as ak, type GlyphOutline as al, HARD_MAX_AGGREGATE_FONT_BYTES as am, HARD_MAX_FONT_BYTES as an, HARD_MAX_FONT_SOURCES as ao, type HeaderFooterStoryRecord as ap, type HyperlinkFieldSpec as aq, type LevelOverride as ar, type LineSpacingRule as as, type ListMarkerAlign as at, type ListMarkerRecord as au, type ListSuffix as av, MAX_BORDER_SPACE_PT as aw, MAX_BORDER_WIDTH_PT as ax, MAX_LVL_OVERRIDES as ay, MAX_NUMBERING_DEFINITIONS as az, type TextShaper as b, parseRefInstruction as b$, SINGLE_LINE_SPACING as b0, type ScriptItem as b1, type ShapeInput as b2, type ShapedCluster as b3, type ShapedFontSpan as b4, type ShapedGlyph as b5, type ShapedRunComparatorInputs as b6, type ShapedVerticalMetrics as b7, type ShapingEnvironment as b8, type ShapingEnvironmentFingerprintInputs as b9, cascadedBottomBorder as bA, cascadedParagraphBorders as bB, cascadedTabStops as bC, collapsedSpaceBefore as bD, createFontResourceSnapshot as bE, createShapedRun as bF, createShapingEnvironment as bG, defaultTabIntervalFromSettings as bH, displayText as bI, fixedPoint as bJ, fontRequestKey as bK, isValidStyleId as bL, itemizeScriptFontSlots as bM, layoutEquation as bN, listMarkerBox as bO, measureDisplayText as bP, mergeListIndent as bQ, nextTabDestination as bR, paragraphBorderExtentPt as bS, paragraphBorderStrokeWidthPt as bT, paragraphBorders as bU, paragraphBordersFingerprint as bV, paragraphBreaksBefore as bW, paragraphContextualSpacing as bX, paragraphLineSpacing as bY, paragraphSpacing as bZ, paragraphTabStops as b_, type ShapingEnvironmentInput as ba, type SourceRange as bb, type SpanLinkRecord as bc, type StyleDefinition as bd, TAB_LEADER_GLYPH as be, type TabAlignment as bf, type TabDestination as bg, type TabLeader as bh, type TabStop as bi, type TableBorderSideName as bj, type TableBorderStrokeRecord as bk, type TableFragmentRecord as bl, type TextDirection as bm, UnsupportedScriptError as bn, type VerticalAlign as bo, appliedSpaceBefore as bp, applyLineSpacing as bq, baselineShiftPtOf as br, borderExtentPt as bs, borderWeight as bt, bottomBorderExtentPt as bu, boundedStructuralFontValidator as bv, buildNumberingIndex as bw, buildStyleCascadeTable as bx, cascadeParagraphFormatting as by, cascadeRunProperties as bz, type FixedPointRoundingMode as c, readBorderSide as c0, readCellBorders as c1, readNumPr as c2, readTableBorders as c3, resolveBorderConflict as c4, resolveNumberingLevel as c5, resolveParagraphLayoutInputs as c6, resolveRunStyle as c7, resolveStoryListItems as c8, resolveStoryRefFields as c9, resolveTableCellBorderGrid as ca, runStylesEqual as cb, shapedRunComparatorInputs as cc, shapingEnvironmentFingerprint as cd, shapingEnvironmentFingerprintInputs as ce, styleForFontSlot as cf, tabAdvanceWidth as cg, tabStopsFingerprint as ch, walkStoryParagraphs as ci, withDefaultTabInterval as cj, withNumberingStyleLinks as ck, withResolvedListItems as cl, type ResolvedFont as d, type TableRowFragmentRecord as e, type TableCellFragmentRecord as f, type NumberingLevel as g, type NumberingIndex as h, type LayoutBox as i, type DrawingInsets as j, type PageRecord as k, type TableBorderSide as l, type ContentControlLock as m, type ParagraphFragmentRecord as n, type FontByteValidator as o, type FixedPoint as p, type ShapedRun as q, type InlineDrawingLayoutContext as r, type StyleCascadeTable as s, type FieldLinkProjector as t, type RefFieldContext as u, type StoryPageFieldNeeds as v, type ResolvedListItem as w, type CellBorderBox as x, type TableCellStyleFormatting as y, type TableBorderBox as z };