@silurus/ooxml 0.70.2 → 0.71.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.
@@ -68,6 +68,21 @@ export declare interface BlipBullet {
68
68
  sizePct: number | null;
69
69
  }
70
70
 
71
+ /**
72
+ * Populate a highlight overlay layer with a box per matched run-slice, grouped
73
+ * by shape frame (with the shape's rotation) so each box lands on the drawn
74
+ * glyphs.
75
+ *
76
+ * @param layer the overlay div (cleared + re-sized here).
77
+ * @param runs the slide's runs (same array the slide was rendered from).
78
+ * @param matches the slide's matches (run-slices + active flag).
79
+ * @param cssWidth rendered canvas CSS width (px, number).
80
+ * @param cssHeight rendered canvas CSS height (px, number).
81
+ * @param measureForFont returns a width-measurer primed with a run's font.
82
+ * @param colors optional colour overrides.
83
+ */
84
+ export declare function buildPptxHighlightLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], matches: PptxHighlightMatch[], cssWidth: number, cssHeight: number, measureForFont: (font: string) => (s: string) => number, colors?: PptxHighlightColors): void;
85
+
71
86
  /**
72
87
  * Build the transparent text-selection overlay for a rendered pptx slide. Unlike
73
88
  * docx (flat spans), pptx groups runs into one positioned `<div>` per shape frame
@@ -77,15 +92,25 @@ export declare interface BlipBullet {
77
92
  * shape div (`inShapeX`/`inShapeY`). Extracted verbatim from
78
93
  * `PptxViewer._buildTextLayer` so the pager (PptxViewer) and the continuous-scroll
79
94
  * viewer (PptxScrollViewer, WS4) share one implementation; public API for
80
- * integrators (design §10). MAIN render mode only `onTextRun` cannot cross the
81
- * worker boundary.
95
+ * integrators (design §10). IX6 usable in BOTH render modes: worker mode
96
+ * collects the same `PptxTextRunInfo[]` off-thread and ships it back beside the
97
+ * bitmap, so the overlay is built from identical geometry regardless of thread.
98
+ *
99
+ * IX1 — when a run carries a resolved `hyperlink` (from `<a:hlinkClick>`) and an
100
+ * `onHyperlinkClick` callback is supplied, its span becomes a click target
101
+ * (`cursor:pointer`, a `title` tooltip, and a `click` handler). A plain span
102
+ * (no hyperlink) is byte-identical to before. A JS click handler is used rather
103
+ * than an `<a href>` so the URL never bypasses the viewer's sanitisation.
82
104
  *
83
105
  * @param layer the overlay div.
84
106
  * @param runs per-run + per-shape geometry from `renderSlide({ onTextRun })`.
85
107
  * @param cssWidth the rendered canvas's CSS width (px, number).
86
108
  * @param cssHeight the rendered canvas's CSS height (px, number).
109
+ * @param onHyperlinkClick called with the run's resolved {@link HyperlinkTarget}
110
+ * when a hyperlink span is clicked. Omit to leave links
111
+ * non-interactive (spans stay plain, selectable text).
87
112
  */
88
- export declare function buildPptxTextLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], cssWidth: number, cssHeight: number): void;
113
+ export declare function buildPptxTextLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], cssWidth: number, cssHeight: number, onHyperlinkClick?: (target: HyperlinkTarget) => void): void;
89
114
 
90
115
  /**
91
116
  * PPTX bullet marker. The shared core {@link CoreBullet} union
@@ -136,6 +161,30 @@ declare interface ChartDataLabelOverride {
136
161
  fontSizeHpt?: number;
137
162
  /** `<a:defRPr b="1">` inside the per-idx rich text. */
138
163
  fontBold?: boolean;
164
+ /** Per-point callout box (`<c:dLbl><c:spPr>`, ECMA-376 §21.2.2.47/§21.2.2.197):
165
+ * overrides the series-default box for this one slice. */
166
+ labelBox?: ChartLabelBox;
167
+ /**
168
+ * Per-point label-content flags (`<c:dLbl>` §21.2.2.47 carries the same
169
+ * show-flag group as the series `<c:dLbls>` §21.2.2.49: §21.2.2.189
170
+ * `<c:showVal>`, §21.2.2.177 `<c:showCatName>`, §21.2.2.180 `<c:showSerName>`,
171
+ * §21.2.2.187 `<c:showPercent>`). When present they OVERRIDE the series-level
172
+ * defaults for that one point (e.g. sample-14 slide-7's pie sets
173
+ * `showCatName=0 showPercent=1` per slice while the series default is
174
+ * `showCatName=1`, so each label is percent only). undefined = inherit the
175
+ * series default for that flag.
176
+ */
177
+ showVal?: boolean;
178
+ showCatName?: boolean;
179
+ showSerName?: boolean;
180
+ showPercent?: boolean;
181
+ /**
182
+ * `<c:dLbl><c:delete val="1"/>` (ECMA-376 §21.2.2.43) — the point's label is
183
+ * removed. Distinguishes a genuine delete from a `<c:dLbl>` that only carries
184
+ * style / flag overrides with no `<c:tx>` (both otherwise present as
185
+ * `text === ''`). true = skip the label; undefined/absent = not deleted.
186
+ */
187
+ deleted?: boolean;
139
188
  }
140
189
 
141
190
  declare interface ChartDataPointOverride {
@@ -146,6 +195,18 @@ declare interface ChartDataPointOverride {
146
195
  markerSize?: number;
147
196
  markerFill?: string;
148
197
  markerLine?: string;
198
+ /**
199
+ * `<c:dPt><c:explosion val>` (ECMA-376 §21.2.2.61) — the amount this
200
+ * pie/doughnut slice is moved out from the center. The schema type is
201
+ * `CT_UnsignedInt` (unbounded `xsd:unsignedInt`); the spec text only says
202
+ * "the amount the data point shall be moved from the center of the pie"
203
+ * and does not itself define units or a 0–100 range. We treat it as a
204
+ * de-facto percentage of the outer radius (0–100 typical), matching
205
+ * Office's UI (the Point Explosion slider caps at 100%) rather than a
206
+ * spec-mandated bound. undefined/absent = 0 (no explosion, flush with the
207
+ * ring). Only consulted by the pie/doughnut renderer.
208
+ */
209
+ explosion?: number;
149
210
  }
150
211
 
151
212
  /**
@@ -185,6 +246,74 @@ declare interface ChartErrBars {
185
246
  dash?: string;
186
247
  }
187
248
 
249
+ /**
250
+ * One box-and-whisker series (chartEx `boxWhisker`, MS 2014 chartex ext). Each
251
+ * `<cx:series>` references its own raw sample points via `<cx:dataId>`; the
252
+ * parser groups them by category and threads the `<cx:layoutPr>` flags. The
253
+ * renderer derives the statistics.
254
+ */
255
+ declare interface ChartexBoxSeries {
256
+ /** Series display name (`<cx:tx><cx:v>`). */
257
+ name: string;
258
+ /** Fill (hex, no '#') — theme accent cycled by series index. null = fall
259
+ * back to the renderer palette. */
260
+ color?: string | null;
261
+ /** Raw sample values grouped by category (outer = category index parallel to
262
+ * {@link ChartexBoxWhisker.categories}, inner = the points in that group). */
263
+ valuesByCategory: number[][];
264
+ /** `<cx:visibility meanMarker>` — draw the mean `×`. */
265
+ meanMarker: boolean;
266
+ /** `<cx:visibility meanLine>` — draw a mean connector line across categories. */
267
+ meanLine: boolean;
268
+ /** `<cx:visibility outliers>` — draw outlier points. */
269
+ showOutliers: boolean;
270
+ /** `<cx:visibility nonoutliers>` — draw the interior (non-outlier) sample
271
+ * points as jittered dots on top of the box. Flag parsed; interior-dot
272
+ * rendering is pending a fixture that enables it (every sample-24 series
273
+ * ships `nonoutliers="0"`, so there is nothing to verify against yet). */
274
+ showNonoutliers: boolean;
275
+ /** `<cx:statistics quartileMethod>` — "exclusive" (Excel default) | "inclusive". */
276
+ quartileMethod: string;
277
+ }
278
+
279
+ /** A chartEx box-and-whisker chart: unique categories + one series per column. */
280
+ declare interface ChartexBoxWhisker {
281
+ /** Unique category labels in first-seen order. */
282
+ categories: string[];
283
+ /** One entry per `<cx:series>`. */
284
+ series: ChartexBoxSeries[];
285
+ }
286
+
287
+ /** A chartEx sunburst: the flat rows the renderer folds into a ring tree. */
288
+ declare interface ChartexSunburst {
289
+ rows: ChartexSunburstRow[];
290
+ }
291
+
292
+ /**
293
+ * One row of a chartEx `sunburst`: the branch→…→leaf label chain (empty
294
+ * trailing segments trimmed) and its size value.
295
+ */
296
+ declare interface ChartexSunburstRow {
297
+ /** Label chain root→leaf. */
298
+ path: string[];
299
+ /** `<cx:numDim type="size">` value attaching to the deepest node in `path`. */
300
+ size: number;
301
+ }
302
+
303
+ /** Callout-box style for a pie/doughnut data label — the white (or themed)
304
+ * rounded rectangle with a thin border Word draws around a `bestFit` label
305
+ * placed outside its slice. From the label's `<c:spPr>` (§21.2.2.197). All
306
+ * fields optional: absent → transparent / unbordered. Mirror of Rust
307
+ * `ChartLabelBox`. */
308
+ declare interface ChartLabelBox {
309
+ /** `<a:solidFill>` resolved hex (no `#`). Box background. */
310
+ fill?: string;
311
+ /** `<a:ln><a:solidFill>` resolved hex (no `#`). Border stroke. */
312
+ borderColor?: string;
313
+ /** `<a:ln w>` border width in EMU (12700 EMU = 1 pt). */
314
+ borderWidthEmu?: number;
315
+ }
316
+
188
317
  /**
189
318
  * `<c:manualLayout>` block. Fractions are of the chart-space rect.
190
319
  * `xMode`/`yMode`: "edge" = absolute fraction from top-left, "factor" =
@@ -315,6 +444,35 @@ export declare interface ChartModel {
315
444
  valAxisTitleFontBold?: boolean | null;
316
445
  /** `<c:valAx><c:title>` run-prop color (hex without '#'). null = default. */
317
446
  valAxisTitleFontColor?: string | null;
447
+ /** `<c:catAx><c:txPr>…<a:latin typeface>` tick-label font. */
448
+ catAxisFontFace?: string | null;
449
+ /** `<c:valAx><c:txPr>…<a:latin typeface>` tick-label font. */
450
+ valAxisFontFace?: string | null;
451
+ /** `<c:catAx><c:title>…<a:latin typeface>` axis-title font. */
452
+ catAxisTitleFontFace?: string | null;
453
+ /** `<c:valAx><c:title>…<a:latin typeface>` axis-title font. */
454
+ valAxisTitleFontFace?: string | null;
455
+ /** `<c:dLbls><c:txPr>…<a:latin typeface>` data-label font. */
456
+ dataLabelFontFace?: string | null;
457
+ /** `<c:legend><c:txPr>…<a:latin typeface>` legend font. */
458
+ legendFontFace?: string | null;
459
+ /** `<c:legend><c:txPr>…<a:solidFill>` legend text color (hex without '#'). */
460
+ legendFontColor?: string | null;
461
+ /** `<c:legend><c:txPr>` legend font size (OOXML hundredths of a point). */
462
+ legendFontSizeHpt?: number | null;
463
+ /** `<c:legend><c:txPr>…defRPr@b` legend bold flag. */
464
+ legendFontBold?: boolean | null;
465
+ /**
466
+ * Theme font-scheme faces (`<a:fontScheme>`, ECMA-376 §20.1.4.2). Latin
467
+ * heading (majorFont) and body (minorFont) typefaces, used as the fallback
468
+ * for any chart text element whose own `<c:txPr>` supplies no `<a:latin>`.
469
+ * null when the theme is not threaded to the chart (then the renderer's
470
+ * built-in sans-serif remains, byte-stable). Axis titles / chart title use
471
+ * the major (heading) face; tick labels / data labels / legend use the
472
+ * minor (body) face — matching Office's default chart text styling.
473
+ */
474
+ themeMajorFontLatin?: string | null;
475
+ themeMinorFontLatin?: string | null;
318
476
  /** Explicit chart border color (hex without '#') from
319
477
  * `<c:chartSpace><c:spPr><a:ln><a:solidFill><a:srgbClr>`. Only set when the
320
478
  * XML explicitly declares a paintable line; null otherwise (no default
@@ -389,6 +547,163 @@ export declare interface ChartModel {
389
547
  * value axis (the common case). See {@link SecondaryValueAxis}.
390
548
  */
391
549
  secondaryValAxis?: SecondaryValueAxis | null;
550
+ /**
551
+ * `<c:date1904>` (ECMA-376 §21.2.2.38). When true the chart's serial
552
+ * date-times resolve against the 1904 date system (base 1904-01-01) instead
553
+ * of the default 1900 system. Threaded to the date formatters for date-axis
554
+ * category labels and value-axis tick labels. Omitted/false ⇒ 1900 system.
555
+ * Note: per §21.2.2.38 the element's `val` defaults to true when present but
556
+ * the attribute is omitted, so `<c:date1904/>` alone means date1904=true.
557
+ */
558
+ date1904?: boolean;
559
+ /**
560
+ * `<c:doughnutChart><c:holeSize val>` (ECMA-376 §21.2.2.60,
561
+ * `ST_HoleSizePercent` §21.2.3.55) — the doughnut hole diameter as a
562
+ * percentage 1–90 of the outer diameter. Ignored for pie (which has no
563
+ * hole). null/undefined = use the renderer's doughnut default when the
564
+ * element is absent. Note the ECMA `CT_HoleSize` schema default is 10%, but
565
+ * a real doughnut file always writes an explicit `<c:holeSize>` (Excel /
566
+ * PowerPoint emit 50–75%); the renderer falls back to 50% only for the
567
+ * pathological absent case.
568
+ */
569
+ holeSize?: number | null;
570
+ /**
571
+ * `<c:pieChart | doughnutChart><c:firstSliceAng val>` (ECMA-376 §21.2.2.52,
572
+ * `ST_FirstSliceAng` §21.2.3.15) — the angle in degrees (0–360, clockwise
573
+ * from the 12 o'clock position) at which the first slice begins.
574
+ * null/undefined = 0 (start at 12 o'clock), which matches the renderer's
575
+ * historical fixed −90° (canvas up) start.
576
+ */
577
+ firstSliceAngle?: number | null;
578
+ /**
579
+ * `<c:chartSpace><c:chart><c:dispBlanksAs val>` (ECMA-376 §21.2.2.42,
580
+ * `ST_DispBlanksAs` §21.2.3.10) — how blank (null) cells are plotted on
581
+ * line/area charts:
582
+ * - "gap" → leave a gap (break the line). The renderer's historical
583
+ * behavior and the model default when the element is absent.
584
+ * - "zero" → plot the blank as the value 0 (the point drops to the axis).
585
+ * - "span" → skip the blank but connect its neighbours with a straight
586
+ * line (bridge the gap).
587
+ * Note the XSD `@val` default is "zero" (applies when `<c:dispBlanksAs/>` is
588
+ * present but the attribute is omitted); when the ELEMENT is absent entirely
589
+ * Office falls back to "gap", which is what we model as the default. Only
590
+ * consulted for the line and area families. null/undefined = "gap".
591
+ */
592
+ dispBlanksAs?: string | null;
593
+ /**
594
+ * `<c:valAx><c:majorGridlines>` presence (ECMA-376 §21.2.2.100). `false` when
595
+ * the value axis exists but omits the element (Office suppresses value
596
+ * gridlines). null/undefined ⇒ the renderer's historical always-on value
597
+ * gridlines (byte-stable). `true` is redundant with the default but honored.
598
+ */
599
+ valAxisMajorGridlines?: boolean | null;
600
+ /**
601
+ * `<c:catAx><c:majorGridlines>` presence (§21.2.2.100). `true` turns on
602
+ * category-axis gridlines (Office omits them by default). null/undefined/false
603
+ * ⇒ no category gridlines (the historical default, byte-stable).
604
+ */
605
+ catAxisMajorGridlines?: boolean | null;
606
+ /**
607
+ * `<c:valAx><c:majorGridlines><c:spPr><a:ln><a:solidFill>` resolved gridline
608
+ * color (hex without `#`) — ECMA-376 §21.2.2.100. When set, the value-axis
609
+ * major gridlines are stroked in this color instead of the renderer's faint
610
+ * `#e0e0e0` default (e.g. sample-1 slide 5's `accent3` gridlines). null/absent
611
+ * ⇒ the historical default (byte-stable).
612
+ */
613
+ valAxisGridlineColor?: string | null;
614
+ /**
615
+ * `<c:valAx><c:majorGridlines><c:spPr><a:ln w>` gridline width in EMU. When
616
+ * set, the value-axis gridline stroke width is derived from this (floored so a
617
+ * hairline stays visible). null/absent ⇒ the renderer's 0.5 px default.
618
+ */
619
+ valAxisGridlineWidthEmu?: number | null;
620
+ /**
621
+ * `<c:catAx><c:majorGridlines><c:spPr><a:ln><a:solidFill>` resolved gridline
622
+ * color (hex without `#`). Only meaningful when {@link catAxisMajorGridlines}
623
+ * is on. null/absent ⇒ the faint default.
624
+ */
625
+ catAxisGridlineColor?: string | null;
626
+ /** `<c:catAx><c:majorGridlines><c:spPr><a:ln w>` gridline width in EMU. */
627
+ catAxisGridlineWidthEmu?: number | null;
628
+ /** `<c:valAx><c:minorGridlines>` presence (§21.2.2.109). Only drawn when a
629
+ * minor step is resolvable (see {@link valAxisMinorUnit}). */
630
+ valAxisMinorGridlines?: boolean | null;
631
+ /**
632
+ * `<c:valAx><c:majorUnit val>` (§21.2.2.103) — explicit distance between major
633
+ * gridlines/ticks, overriding the Excel-style auto "nice" step. null/undefined
634
+ * ⇒ auto step (byte-stable).
635
+ */
636
+ valAxisMajorUnit?: number | null;
637
+ /** `<c:valAx><c:minorUnit val>` (§21.2.2.112) — explicit minor step. Drives
638
+ * minor gridlines/ticks when present. null ⇒ no minor divisions. */
639
+ valAxisMinorUnit?: number | null;
640
+ /**
641
+ * `<c:valAx><c:scaling><c:logBase val>` (§21.2.2.98, `ST_LogBase` §21.2.3.25)
642
+ * — logarithmic value-axis base (>= 2). When set, values map to pixels in log
643
+ * space and gridlines fall on powers of the base. null/undefined ⇒ linear
644
+ * (byte-stable).
645
+ */
646
+ valAxisLogBase?: number | null;
647
+ /**
648
+ * `<c:valAx><c:scaling><c:orientation val>` (§21.2.2.130, `ST_Orientation`
649
+ * §21.2.3.30) — "minMax" (normal) | "maxMin" (reversed, so the value axis runs
650
+ * top→bottom max→min). null/undefined/"minMax" ⇒ normal (byte-stable).
651
+ */
652
+ valAxisOrientation?: 'minMax' | 'maxMin' | string | null;
653
+ /** `<c:catAx><c:scaling><c:orientation val>` — "maxMin" reverses the category
654
+ * axis left↔right. null/"minMax" ⇒ normal. */
655
+ catAxisOrientation?: 'minMax' | 'maxMin' | string | null;
656
+ /**
657
+ * `<c:catAx><c:tickLblPos val>` (§21.2.2.207, `ST_TickLblPos` §21.2.3.47) —
658
+ * "nextTo" (default) | "low" | "high" | "none". "none" hides the category tick
659
+ * labels. null/undefined ⇒ nextTo (byte-stable).
660
+ */
661
+ catAxisTickLabelPos?: string | null;
662
+ /** `<c:valAx><c:tickLblPos val>` (§21.2.2.207). "none" hides value tick labels. */
663
+ valAxisTickLabelPos?: string | null;
664
+ /**
665
+ * `<c:catAx><c:txPr><a:bodyPr rot>` (DrawingML `ST_Angle`, 60000ths of a
666
+ * degree) — category tick-label rotation. e.g. -2700000 = -45°. null/undefined
667
+ * /0 ⇒ horizontal labels (byte-stable).
668
+ */
669
+ catAxisLabelRotation?: number | null;
670
+ /**
671
+ * `<c:stockChart><c:hiLowLines>` presence (ECMA-376 §21.2.2.60). When true
672
+ * the stock renderer draws a vertical line spanning each category's low↔high
673
+ * value. Only set for `chartType === "stock"`; null/undefined on every other
674
+ * chart type (byte-stable).
675
+ */
676
+ stockHiLowLines?: boolean | null;
677
+ /**
678
+ * `<c:hiLowLines><c:spPr><a:ln><a:solidFill>` resolved color (hex, no `#`).
679
+ * null = the renderer's default gray hi-lo line.
680
+ */
681
+ stockHiLowLineColor?: string | null;
682
+ /**
683
+ * `<c:stockChart><c:upDownBars>` presence (ECMA-376 §21.2.2.227). Parsed so a
684
+ * stock file carrying open-close up/down bars is recognized; the renderer does
685
+ * NOT yet draw them (tracked follow-up). null/undefined when absent.
686
+ */
687
+ stockUpDownBars?: boolean | null;
688
+ /**
689
+ * Structured box-and-whisker data (`chartType === 'boxWhisker'`). Present
690
+ * ONLY for boxWhisker charts; null/absent otherwise so the flat
691
+ * `categories`/`series` model the other chartEx renderers consume is
692
+ * untouched. The renderer computes quartiles / mean / whiskers / outliers.
693
+ */
694
+ chartexBox?: ChartexBoxWhisker | null;
695
+ /**
696
+ * Structured sunburst hierarchy (`chartType === 'sunburst'`). Present ONLY
697
+ * for sunburst charts; null/absent otherwise.
698
+ */
699
+ chartexSunburst?: ChartexSunburst | null;
700
+ /**
701
+ * Theme accent palette (`accent1..6`, hex without '#') for chartEx charts
702
+ * that color by branch/series index (boxWhisker series, sunburst branches).
703
+ * null/absent when the resolver supplies no default palette (pptx); the
704
+ * renderer then falls back to its own `CHART_PALETTE`.
705
+ */
706
+ chartexAccents?: string[] | null;
392
707
  }
393
708
 
394
709
  export declare interface ChartSeries {
@@ -498,6 +813,31 @@ export declare interface ChartSeries {
498
813
  * series.
499
814
  */
500
815
  bubbleSizes?: (number | null)[] | null;
816
+ /**
817
+ * `<c:ser><c:smooth val>` (ECMA-376 §21.2.2.194) — line/area series flag
818
+ * requesting a smoothed (spline) curve through the points instead of straight
819
+ * segments. Only consulted for the line and area families (scatter carries its
820
+ * smoothing in `ChartModel.scatterStyle`). null/undefined/false = straight
821
+ * polyline (the default; byte-stable for series that never set it).
822
+ */
823
+ smooth?: boolean | null;
824
+ /**
825
+ * `<c:ser><c:trendline>` per-series trendlines (ECMA-376 §21.2.2.211,
826
+ * `CT_Trendline`). A series can carry several (e.g. a linear fit + a moving
827
+ * average). null/undefined/empty = no trendline (the default; byte-stable for
828
+ * series that never declare one).
829
+ */
830
+ trendLines?: ChartTrendline[] | null;
831
+ /**
832
+ * `<c:ser><c:spPr><a:ln><a:noFill/>` (ECMA-376 §21.2.2.198 CT_ShapeProperties
833
+ * → DrawingML §20.1.2.2.24 CT_LineProperties). true when the series connecting
834
+ * line is explicitly turned OFF. For a scatter/line series this OVERRIDES the
835
+ * chart-group `<c:scatterStyle>` (§21.2.2.42) / line default — Excel and
836
+ * PowerPoint draw markers only (no connecting line) even when the group style
837
+ * is `lineMarker`. null/undefined = no explicit line-off, so the group default
838
+ * governs (byte-stable for series that carry a paintable line).
839
+ */
840
+ lineHidden?: boolean | null;
501
841
  }
502
842
 
503
843
  declare interface ChartSeriesDataLabels {
@@ -512,6 +852,51 @@ declare interface ChartSeriesDataLabels {
512
852
  fontBold?: boolean;
513
853
  /** Series-level font size for data labels (OOXML hundredths of a point). */
514
854
  fontSizeHpt?: number;
855
+ /** Series-default callout box (`<c:dLbls><c:spPr>`, ECMA-376 §21.2.2.49/
856
+ * §21.2.2.197). When present the pie/doughnut renderer draws Word's boxed
857
+ * callout layout (box + optional leader line) instead of plain text. */
858
+ labelBox?: ChartLabelBox;
859
+ /** `<c:dLbls><c:showLeaderLines val>` (§21.2.2.183) — draw leader lines from
860
+ * a pulled-away label back to its slice. Default false. */
861
+ showLeaderLines?: boolean;
862
+ /** `<c:leaderLines><c:spPr><a:ln><a:solidFill>` (§21.2.2.92) resolved hex
863
+ * (no `#`). undefined → renderer uses a neutral grey. */
864
+ leaderLineColor?: string;
865
+ /** `<c:leaderLines><c:spPr><a:ln w>` leader-line width in EMU. */
866
+ leaderLineWidthEmu?: number;
867
+ }
868
+
869
+ /**
870
+ * `<c:ser><c:trendline>` (ECMA-376 §21.2.2.211). A regression/smoothing curve
871
+ * fitted to the series' data points.
872
+ */
873
+ declare interface ChartTrendline {
874
+ /**
875
+ * `<c:trendlineType val>` (§21.2.2.213, `ST_TrendlineType` §21.2.3.50):
876
+ * "linear" | "exp" | "log" | "power" | "poly" | "movingAvg". The renderer
877
+ * currently draws "linear" (least squares) and "movingAvg"; other types parse
878
+ * but are not yet plotted (tracked as a follow-up).
879
+ */
880
+ trendlineType: string;
881
+ /** `<c:order val>` — polynomial order (`poly`, default 2). */
882
+ order?: number | null;
883
+ /** `<c:period val>` — moving-average window (`movingAvg`, default 2). */
884
+ period?: number | null;
885
+ /** `<c:forward val>` — units to extend the line past the last point. */
886
+ forward?: number | null;
887
+ /** `<c:backward val>` — units to extend the line before the first point. */
888
+ backward?: number | null;
889
+ /** `<c:intercept val>` — forced y-intercept (linear/exp). null = free fit. */
890
+ intercept?: number | null;
891
+ /** `<c:dispRSqr val="1">` — show the R² value (label; not yet rendered). */
892
+ dispRSqr?: boolean | null;
893
+ /** `<c:dispEq val="1">` — show the fit equation (label; not yet rendered). */
894
+ dispEq?: boolean | null;
895
+ /** `<c:spPr><a:ln><a:solidFill>` trendline color (hex without '#'). null =
896
+ * inherit the series color. */
897
+ lineColor?: string | null;
898
+ /** `<c:spPr><a:ln w>` trendline width in EMU. */
899
+ lineWidthEmu?: number | null;
515
900
  }
516
901
 
517
902
  /**
@@ -519,7 +904,7 @@ declare interface ChartSeriesDataLabels {
519
904
  * grouping (`Pct` = percent-stacked) so renderers do not need to inspect
520
905
  * separate `barDir`/`grouping` fields.
521
906
  */
522
- declare type ChartType = 'line' | 'stackedLine' | 'stackedLinePct' | 'clusteredBar' | 'clusteredBarH' | 'stackedBar' | 'stackedBarH' | 'stackedBarPct' | 'stackedBarHPct' | 'area' | 'stackedArea' | 'stackedAreaPct' | 'pie' | 'doughnut' | 'scatter' | 'bubble' | 'radar' | 'waterfall' | string;
907
+ declare type ChartType = 'line' | 'stackedLine' | 'stackedLinePct' | 'clusteredBar' | 'clusteredBarH' | 'stackedBar' | 'stackedBarH' | 'stackedBarPct' | 'stackedBarHPct' | 'area' | 'stackedArea' | 'stackedAreaPct' | 'pie' | 'doughnut' | 'scatter' | 'bubble' | 'radar' | 'waterfall' | 'stock' | 'boxWhisker' | 'sunburst' | string;
523
908
 
524
909
  /**
525
910
  * Translucent overlay drawn over a finished slide so it reads faintly
@@ -535,6 +920,19 @@ export declare interface DimOptions {
535
920
  opacity: number;
536
921
  }
537
922
 
923
+ /** A duotone effect resolved to its two endpoint colours. Both are 6-char
924
+ * uppercase hex WITHOUT a leading `#` (the form the Rust parsers emit). `clr1`
925
+ * is the dark endpoint (luminance 0), `clr2` the light endpoint (luminance 1),
926
+ * matching the child order of `<a:duotone>` in §20.1.8.23. Any per-colour
927
+ * transforms (lumMod/lumOff/tint/satMod/…) are already baked into these hexes
928
+ * by the parser's colour-resolution machinery. */
929
+ declare interface Duotone {
930
+ /** First `EG_ColorChoice` child — the dark endpoint. 6-char hex, no `#`. */
931
+ clr1: string;
932
+ /** Second `EG_ColorChoice` child — the light endpoint. 6-char hex, no `#`. */
933
+ clr2: string;
934
+ }
935
+
538
936
  /**
539
937
  * An OMML equation embedded in a paragraph (ECMA-376 §22.1). Parsed into the
540
938
  * shared math AST and rendered by `@silurus/ooxml-core`'s math engine.
@@ -567,6 +965,47 @@ export declare interface FillRect {
567
965
  b?: number;
568
966
  }
569
967
 
968
+ /**
969
+ * IX2 public find-result shape, shared by all three viewers.
970
+ *
971
+ * `findText` returns an ordered list of {@link FindMatch}. Every match carries
972
+ * its ordinal position (`matchIndex`, 0-based, document order — the same index
973
+ * `findNext` / `findPrev` cycle through), the matched `text`, and a
974
+ * format-specific `location`. The location is where the three formats
975
+ * legitimately differ — a docx match lives on a page, a pptx match on a slide,
976
+ * an xlsx match in a sheet cell — so `FindMatch` is generic over it rather than
977
+ * forcing an artificial common shape. Each viewer instantiates it with its own
978
+ * location type:
979
+ *
980
+ * - `DocxViewer.findText` → `FindMatch<DocxMatchLocation>` ({ page })
981
+ * - `PptxViewer.findText` → `FindMatch<PptxMatchLocation>` ({ slide })
982
+ * - `XlsxViewer.findText` → `FindMatch<XlsxMatchLocation>` ({ sheet, ref, … })
983
+ *
984
+ * The generic default is `unknown` so `FindMatch` can be referenced without a
985
+ * type argument (e.g. in generic UI code) while each viewer's return type stays
986
+ * precise.
987
+ */
988
+ export declare interface FindMatch<Loc = unknown> {
989
+ /** 0-based ordinal among all matches, in document order. This is the index
990
+ * `findNext`/`findPrev` make active, so a caller can correlate the array it
991
+ * got from `findText` with the active-match reported by navigation. */
992
+ matchIndex: number;
993
+ /** The text that matched (the query as it appears in the document — its
994
+ * original case, not the folded form used for case-insensitive matching). */
995
+ text: string;
996
+ /** Where the match is, in the format's own coordinates. */
997
+ location: Loc;
998
+ }
999
+
1000
+ /** Options for {@link findMatches}. */
1001
+ export declare interface FindMatchesOptions {
1002
+ /**
1003
+ * Match case exactly. Default `false` (case-insensitive, like a browser's
1004
+ * find-in-page). IX2 default — an integrator can pass `true`.
1005
+ */
1006
+ caseSensitive?: boolean;
1007
+ }
1008
+
570
1009
  /** ECMA-376 §20.1.8.17 (CT_GlowEffect) — coloured halo with blur radius. */
571
1010
  export declare interface Glow {
572
1011
  color: string;
@@ -592,6 +1031,49 @@ export declare interface GradientStop {
592
1031
  /** How {@link PptxViewer} presents hidden slides (`<p:sld show="0">`). */
593
1032
  export declare type HiddenSlideMode = 'show' | 'skip' | 'dim';
594
1033
 
1034
+ /**
1035
+ * Shared hyperlink model + URL sanitisation for docx / pptx / xlsx (IX1).
1036
+ *
1037
+ * All three formats carry the same two ECMA-376 concepts:
1038
+ * - an **external** hyperlink — an absolute URL resolved from a relationship
1039
+ * part target (`document.xml.rels` for docx §17.16.22, the slide rels for
1040
+ * pptx §21.1.2.3.5, the worksheet rels for xlsx §18.3.1.47), with
1041
+ * `TargetMode="External"`.
1042
+ * - an **internal** hyperlink — a jump within the document itself:
1043
+ * docx `w:anchor` -> a `<w:bookmarkStart w:name>` (§17.16.23), pptx
1044
+ * `action="ppaction://hlinksldjump"` -> a slide, xlsx `location` -> a defined
1045
+ * name or a `Sheet!A1` cell reference.
1046
+ *
1047
+ * The parsers (Rust, one per format) do the format-specific rels lookup and hand
1048
+ * each run / shape / cell a {@link HyperlinkTarget}. Everything downstream — the
1049
+ * text-layer overlay, the viewer default click behaviour, and any integrator
1050
+ * callback — is format-agnostic and consumes this one shape. Keeping the type +
1051
+ * the pure `sanitizeHyperlinkUrl` predicate here (not duplicated per package)
1052
+ * follows the cross-package unification principle: a scheme-allowlist bug fixed
1053
+ * once is fixed everywhere.
1054
+ */
1055
+ /**
1056
+ * A resolved hyperlink attached to a run, shape, or cell.
1057
+ *
1058
+ * - `external` — `url` is the raw target as authored in the file. It is NOT
1059
+ * guaranteed safe; run it through {@link sanitizeHyperlinkUrl} before
1060
+ * navigating. It is kept verbatim here so an integrator can apply its own
1061
+ * policy (e.g. allow `file:` on a trusted intranet viewer).
1062
+ * - `internal` — `ref` is the in-document destination, verbatim from the file:
1063
+ * docx: the bookmark name (`w:anchor`).
1064
+ * pptx: the internal action (e.g. `ppaction://hlinksldjump`), with the
1065
+ * resolved 0-based `slideIndex` when the rels target names a slide.
1066
+ * xlsx: the `location` string (a defined name or `Sheet1!A1`).
1067
+ */
1068
+ export declare type HyperlinkTarget = {
1069
+ kind: 'external';
1070
+ url: string;
1071
+ } | {
1072
+ kind: 'internal';
1073
+ ref: string;
1074
+ slideIndex?: number;
1075
+ };
1076
+
595
1077
  /**
596
1078
  * Image fill — ECMA-376 §20.1.8.14 (CT_BlipFillProperties). The embedded blip
597
1079
  * is carried as a zip path + MIME; the renderer fetches the bytes on demand via
@@ -684,6 +1166,35 @@ declare interface LoadOptions_2 {
684
1166
  * via `@font-face` in your application CSS.
685
1167
  */
686
1168
  useGoogleFonts?: boolean;
1169
+ /**
1170
+ * Password for an encrypted OOXML file ([MS-OFFCRYPTO] Agile Encryption).
1171
+ *
1172
+ * Password-protected Office documents are CFB (OLE2) containers, not ZIPs.
1173
+ * When this is set and the input is Agile-encrypted, `load()` decrypts it on
1174
+ * the main thread (via WebCrypto) and parses the recovered plaintext ZIP.
1175
+ *
1176
+ * Errors (thrown as {@link import('../errors/ooxml-error').OoxmlError}):
1177
+ * - no `password` on an encrypted file → code `'encrypted'`
1178
+ * - wrong `password` → code `'invalid-password'`
1179
+ * - a non-Agile scheme (Standard / Extensible / legacy) → code
1180
+ * `'unsupported-encryption'`
1181
+ *
1182
+ * Note: Agile Encryption uses a high password-hash spin count (commonly
1183
+ * 100,000), so decryption of a protected file adds roughly a second of
1184
+ * WebCrypto work before parsing begins.
1185
+ *
1186
+ * Security notes:
1187
+ * - This value is held as an ordinary JS `string` in memory for the
1188
+ * duration of key derivation. The library does not zero it, and does
1189
+ * not wrap it in a `SecureString`-equivalent — it becomes eligible for
1190
+ * garbage collection like any other string once nothing references it,
1191
+ * but no explicit wipe is performed. It is never logged or included in
1192
+ * thrown errors.
1193
+ * - Decryption recovers the plaintext but does not verify the file's HMAC
1194
+ * data-integrity tag ([MS-OFFCRYPTO] §2.3.4.14), so ciphertext tampering
1195
+ * is not detected — see "Security & Privacy" in the README.
1196
+ */
1197
+ password?: string;
687
1198
  /**
688
1199
  * Override the URL the parser worker fetches the WebAssembly module from.
689
1200
  *
@@ -728,6 +1239,23 @@ declare interface LoadOptions_2 {
728
1239
  math?: MathRenderer;
729
1240
  }
730
1241
 
1242
+ /**
1243
+ * The slice of one run a match covers: the run's index in the original `runs[]`
1244
+ * and the `[start, end)` character range within that run's own `text`. A match
1245
+ * that straddles N runs yields N of these (the first sliced from its start
1246
+ * offset to the run end, the last from 0 to its end offset, any middle run
1247
+ * whole). The viewer measures each slice against that run's font to get a pixel
1248
+ * rectangle.
1249
+ */
1250
+ declare interface MatchRunSlice {
1251
+ /** Index into the original `runs[]` handed to {@link buildTextIndex}. */
1252
+ runIndex: number;
1253
+ /** Start offset within `runs[runIndex].text` (inclusive). */
1254
+ start: number;
1255
+ /** End offset within `runs[runIndex].text` (exclusive). */
1256
+ end: number;
1257
+ }
1258
+
731
1259
  /** Accent (`m:acc`), e.g. hat, bar, vector arrow over the base. */
732
1260
  declare interface MathAccent {
733
1261
  kind: 'accent';
@@ -750,6 +1278,31 @@ declare interface MathBar {
750
1278
  base: MathNode[];
751
1279
  }
752
1280
 
1281
+ /** Border-box object (`m:borderBox`, §22.1.2.11): a border/strikes around the
1282
+ * base. Absent flags ⇒ a full rectangular box. */
1283
+ declare interface MathBorderBox {
1284
+ kind: 'borderBox';
1285
+ /** §22.1.2 hide* — when true the corresponding edge is NOT drawn. */
1286
+ hideTop?: boolean;
1287
+ hideBot?: boolean;
1288
+ hideLeft?: boolean;
1289
+ hideRight?: boolean;
1290
+ /** §22.1.2 strike* — strikeBLTR = bottom-left→top-right, strikeTLBR =
1291
+ * top-left→bottom-right diagonal. */
1292
+ strikeH?: boolean;
1293
+ strikeV?: boolean;
1294
+ strikeBltr?: boolean;
1295
+ strikeTlbr?: boolean;
1296
+ base: MathNode[];
1297
+ }
1298
+
1299
+ /** Box object (`m:box`, §22.1.2.13): a logical grouping (operator emulator /
1300
+ * line-break control). Draws NO border — a transparent group around `base`. */
1301
+ declare interface MathBox {
1302
+ kind: 'box';
1303
+ base: MathNode[];
1304
+ }
1305
+
753
1306
  declare interface MathDelimiter {
754
1307
  kind: 'delimiter';
755
1308
  /** opening char (default '('). */
@@ -807,7 +1360,23 @@ declare interface MathNary {
807
1360
  body: MathNode[];
808
1361
  }
809
1362
 
810
- declare type MathNode = MathRun | MathFraction | MathScript | MathNary | MathDelimiter | MathRadical | MathLimit | MathArray | MathGroupChr | MathBar | MathAccent | MathFunc | MathGroup;
1363
+ declare type MathNode = MathRun | MathFraction | MathScript | MathNary | MathDelimiter | MathRadical | MathLimit | MathArray | MathGroupChr | MathBar | MathAccent | MathFunc | MathGroup | MathPhant | MathSPre | MathBox | MathBorderBox;
1364
+
1365
+ /** Phantom object (`m:phant`, §22.1.2.81): contributes the spacing of `base`
1366
+ * while optionally hiding it and/or zeroing individual dimensions. */
1367
+ declare interface MathPhant {
1368
+ kind: 'phant';
1369
+ /** §22.1.2.96 `m:show` — `false` hides the base (invisible but occupies space,
1370
+ * i.e. `<mphantom>`); `true` (default) shows it and the phant only tweaks
1371
+ * spacing. */
1372
+ show: boolean;
1373
+ /** §22.1.2 zeroWid / zeroAsc / zeroDesc — suppress width / ascent / descent so
1374
+ * the base takes no space along that axis. Omitted ⇒ false. */
1375
+ zeroWid?: boolean;
1376
+ zeroAsc?: boolean;
1377
+ zeroDesc?: boolean;
1378
+ base: MathNode[];
1379
+ }
811
1380
 
812
1381
  declare interface MathRadical {
813
1382
  kind: 'radical';
@@ -850,6 +1419,15 @@ declare interface MathScript {
850
1419
  sub?: MathNode[];
851
1420
  }
852
1421
 
1422
+ /** Pre-sub-superscript object (`m:sPre`, §22.1.2.99): sub + sup to the LEFT of
1423
+ * the base (e.g. ²₁A). */
1424
+ declare interface MathSPre {
1425
+ kind: 'sPre';
1426
+ sub: MathNode[];
1427
+ sup: MathNode[];
1428
+ base: MathNode[];
1429
+ }
1430
+
853
1431
  declare type MathStyle = 'roman' | 'italic' | 'bold' | 'boldItalic';
854
1432
 
855
1433
  declare interface MathSvg {
@@ -883,6 +1461,63 @@ export declare interface NoFill {
883
1461
  fillType: 'none';
884
1462
  }
885
1463
 
1464
+ /**
1465
+ * Typed error thrown by the docx / pptx / xlsx `load()` factories for failures
1466
+ * that carry a stable, programmatic {@link OoxmlErrorCode} (e.g. a
1467
+ * password-protected or legacy-binary file detected from its container magic).
1468
+ *
1469
+ * Note on workers: `instanceof OoxmlError` does not survive a structured-clone
1470
+ * across the worker boundary. Detection that needs a typed error is therefore
1471
+ * done on the main thread (before the worker is involved) so a genuine
1472
+ * `OoxmlError` instance is thrown to the caller. Errors that must cross the
1473
+ * worker boundary should carry the `code` string and be reconstructed on the
1474
+ * main side.
1475
+ */
1476
+ export declare class OoxmlError extends Error {
1477
+ readonly code: OoxmlErrorCode;
1478
+ constructor(code: OoxmlErrorCode, message: string);
1479
+ }
1480
+
1481
+ /**
1482
+ * Machine-readable code for a typed load-time failure.
1483
+ *
1484
+ * The container-level failures the `load()` factories detect on the main thread
1485
+ * before handing bytes to the parser worker (see `sniffCfb` / `decryptOoxml`).
1486
+ * This is the seed of the broader typed-error surface tracked as PD4 (OoxmlError
1487
+ * typed errors). Add codes here rather than throwing bare `Error(string)`, so
1488
+ * callers can `switch` on `err.code` instead of matching message text.
1489
+ *
1490
+ * - `'encrypted'` — password-protected, but no `password` was
1491
+ * supplied (pass `LoadOptions.password` to decrypt).
1492
+ * - `'invalid-password'` — a `password` was supplied but did not match.
1493
+ * - `'unsupported-encryption'`— encrypted with a scheme other than Agile
1494
+ * (Standard / Extensible / a legacy binary encryptor), which this library
1495
+ * cannot decrypt (PD8 implements Agile only).
1496
+ * - `'legacy-binary-format'` — a raw .doc / .xls / .ppt (not OOXML).
1497
+ * - `'not-ooxml'` — a CFB of an unrecognised kind, or otherwise
1498
+ * not an OOXML ZIP.
1499
+ */
1500
+ export declare type OoxmlErrorCode = 'encrypted' | 'invalid-password' | 'unsupported-encryption' | 'legacy-binary-format' | 'not-ooxml';
1501
+
1502
+ /**
1503
+ * The default action a viewer takes for an **external** hyperlink click when
1504
+ * the integrator supplies no `onHyperlinkClick` handler: sanitise the URL and,
1505
+ * if allowed, open it in a new tab with `noopener,noreferrer` so the opened page
1506
+ * gets no `window.opener` handle back into this document. A blocked scheme is a
1507
+ * silent no-op (returns `false`) — the click does nothing rather than navigate
1508
+ * somewhere dangerous.
1509
+ *
1510
+ * Internal targets are intentionally NOT handled here: the in-document jump
1511
+ * (page / slide / cell) is format-specific and lives in each viewer.
1512
+ *
1513
+ * Split out (not inlined in three viewers) so the "open in new tab, drop opener,
1514
+ * refuse unsafe schemes" policy is defined once. `win` is injected for tests;
1515
+ * defaults to the ambient `window`.
1516
+ *
1517
+ * @returns `true` if navigation was initiated, `false` if the URL was blocked.
1518
+ */
1519
+ export declare function openExternalHyperlink(url: string, allowed?: readonly string[], win?: Pick<Window, 'open'> | undefined): boolean;
1520
+
886
1521
  /**
887
1522
  * PPTX paragraph. Extends the shared core `Paragraph` with the PPTX-only
888
1523
  * `eaLnBrk` flag that the pptx parser emits but the shared core model does not
@@ -1053,6 +1688,13 @@ export declare interface PictureElement {
1053
1688
  };
1054
1689
  /** a:blip > a:alphaModFix@amt as 0..1. Undefined = fully opaque. */
1055
1690
  alpha?: number;
1691
+ /**
1692
+ * ECMA-376 §20.1.8.23 `<a:duotone>` recolour, resolved to its two endpoint
1693
+ * colours (through the slide theme). Undefined ⇒ no duotone. When present the
1694
+ * renderer decodes the raster once, remaps it along the `clr1`→`clr2`
1695
+ * luminance ramp, and caches the recoloured bitmap under a colour-suffixed key.
1696
+ */
1697
+ duotone?: Duotone;
1056
1698
  /**
1057
1699
  * `<p:spPr><a:custGeom>` clipping path. Same `PathCmd` model as
1058
1700
  * `ShapeElement.custGeom` (one entry per `<a:path>`; coords normalized
@@ -1095,6 +1737,21 @@ export declare interface PptxComment {
1095
1737
  text: string;
1096
1738
  }
1097
1739
 
1740
+ export declare interface PptxHighlightColors {
1741
+ match?: string;
1742
+ active?: string;
1743
+ }
1744
+
1745
+ export declare interface PptxHighlightMatch {
1746
+ slices: MatchRunSlice[];
1747
+ active: boolean;
1748
+ }
1749
+
1750
+ /** Where a pptx match lives: its 0-based slide index. */
1751
+ export declare interface PptxMatchLocation {
1752
+ slide: number;
1753
+ }
1754
+
1098
1755
  /**
1099
1756
  * Headless PPTX rendering engine.
1100
1757
  *
@@ -1116,15 +1773,24 @@ export declare class PptxPresentation {
1116
1773
  private _mode;
1117
1774
  private _presentation;
1118
1775
  private _meta;
1776
+ /** Lazily-built `partName → slide index` map for internal hyperlink slide
1777
+ * jumps (IX-nav). Cleared on {@link destroy}; built on first
1778
+ * {@link getSlideIndexByPartName}/{@link resolveInternalTarget} from either
1779
+ * the parsed slides (main) or the worker meta's `partNames` (worker). */
1780
+ private _slidePartIndex;
1119
1781
  private _mediaCache;
1120
1782
  private _imageCache;
1783
+ /** Google-Fonts `FontFace` objects this deck preloaded into `document.fonts`
1784
+ * (main mode only — in worker mode the worker owns them and terminates with
1785
+ * its own FontFaceSet). Released in {@link destroy} so they do not leak into
1786
+ * the shared FontFaceSet for the lifetime of the SPA (deduped + refcounted in
1787
+ * core, so a web font shared with another open deck survives until both go). */
1788
+ private _googleFontFaces;
1121
1789
  /** One stable closure per instance: the decoded-bitmap and SVG caches key on
1122
1790
  * this identity to scope decodes per deck (so two open decks never swap
1123
1791
  * images for a shared zip path like ppt/media/image1.png). Reusing the same
1124
1792
  * reference across every render also lets those caches hit across slides. */
1125
1793
  private readonly _fetchImage;
1126
- private _workerReady;
1127
- private _workerReadyCallbacks;
1128
1794
  /** Opt-in OMML equation engine, injected once at {@link load}. Every
1129
1795
  * `renderSlide` / `presentSlide` reuses it — equations render when present,
1130
1796
  * and are skipped (engine tree-shaken) when omitted. */
@@ -1132,7 +1798,6 @@ export declare class PptxPresentation {
1132
1798
  private constructor();
1133
1799
  /** Parse a PPTX from URL or ArrayBuffer. */
1134
1800
  static load(source: string | ArrayBuffer, opts?: LoadOptions): Promise<PptxPresentation>;
1135
- private _waitForWorker;
1136
1801
  private _parse;
1137
1802
  /** Total number of slides in the loaded presentation. */
1138
1803
  get slideCount(): number;
@@ -1173,6 +1838,44 @@ export declare class PptxPresentation {
1173
1838
  * caller's policy (see {@link PptxViewer}'s `hiddenSlideMode` modes).
1174
1839
  */
1175
1840
  isHidden(slideIndex: number): boolean;
1841
+ /** The per-slide `partName` array (`sldIdLst` order) from either the parsed
1842
+ * model (main) or the worker meta (worker). Backs the lazy part-index map. */
1843
+ private _partNames;
1844
+ /** Lazily build (and cache) the `partName → index` map. Nulled by
1845
+ * {@link destroy} so a reused reference never serves a stale deck's indices. */
1846
+ private _partIndex;
1847
+ /**
1848
+ * Resolve a slide's OPC part name (e.g. `ppt/slides/slide3.xml`) to its
1849
+ * 0-based index in `sldIdLst` order, or `undefined` when no slide has that
1850
+ * part name. This is the map an internal hyperlink slide jump
1851
+ * (`<a:hlinkClick action="ppaction://hlinksldjump" r:id>`, ECMA-376
1852
+ * §21.1.2.3.5) resolves against: the click's rel Target names a slide part, and
1853
+ * this turns it into the index a viewer can navigate to. Works in both `main`
1854
+ * and `worker` mode (the part names ride along in the worker meta).
1855
+ */
1856
+ getSlideIndexByPartName(partName: string): number | undefined;
1857
+ /**
1858
+ * Resolve an internal hyperlink target string to a 0-based slide index, or
1859
+ * `undefined` when it names no reachable slide. Handles both
1860
+ * `<a:hlinkClick @action>` classes (§21.1.2.3.5):
1861
+ *
1862
+ * - a **relative** show jump — `ppaction://hlinkshowjump?jump=firstslide |
1863
+ * lastslide | nextslide | previousslide` — resolved arithmetically from
1864
+ * `currentIndex` (clamped at the deck ends);
1865
+ * - a **specific** slide-part jump — `ppaction://hlinksldjump`, whose
1866
+ * resolved target is a slide-rel part name like `../slides/slide3.xml` —
1867
+ * resolved through {@link getSlideIndexByPartName}.
1868
+ *
1869
+ * `ref` is the internal reference a `HyperlinkTarget` of kind `'internal'`
1870
+ * carries: the raw `ppaction://…` action string for a relative jump, or the
1871
+ * resolved slide-part target string for a specific jump. A viewer's
1872
+ * `onHyperlinkClick` default calls this with `ref` and the current slide, then
1873
+ * navigates to the returned index.
1874
+ *
1875
+ * @param ref the internal action/target string.
1876
+ * @param currentIndex the 0-based slide the jump is relative to (default 0).
1877
+ */
1878
+ resolveInternalTarget(ref: string, currentIndex?: number): number | undefined;
1176
1879
  /** Render a slide onto the given canvas. */
1177
1880
  renderSlide(canvas: HTMLCanvasElement | OffscreenCanvas, slideIndex: number, opts?: RenderSlideOptions): Promise<void>;
1178
1881
  /**
@@ -1185,6 +1888,16 @@ export declare class PptxPresentation {
1185
1888
  * when done, or its backing memory is held until GC.
1186
1889
  */
1187
1890
  renderSlideToBitmap(slideIndex: number, opts?: RenderSlideToBitmapOptions): Promise<ImageBitmap>;
1891
+ /**
1892
+ * IX6 — collect a slide's text-run geometry (`PptxTextRunInfo[]`) without
1893
+ * painting a visible canvas. Works in BOTH modes: worker mode renders the
1894
+ * slide off-thread and ships only the runs (no bitmap transfer); main mode
1895
+ * renders to a throwaway offscreen canvas. Used by the find controller to scan
1896
+ * every slide for matches. Run geometry is in CSS px (independent of dpr) and
1897
+ * dimming does not move glyphs, so only `width` is threaded — matching the
1898
+ * historical main-mode `_collectSlideRuns`.
1899
+ */
1900
+ collectSlideRuns(slideIndex: number, width?: number): Promise<PptxTextRunInfo[]>;
1188
1901
  /**
1189
1902
  * Extract raw media bytes for a zip path referenced by {@link MediaElement}.
1190
1903
  * Results are cached by path for the lifetime of this instance.
@@ -1199,6 +1912,23 @@ export declare class PptxPresentation {
1199
1912
  * decoded lazily rather than inlined as base64 at parse time.
1200
1913
  */
1201
1914
  getImage(imagePath: string, mimeType: string): Promise<Blob>;
1915
+ /**
1916
+ * Project the presentation to GitHub-flavoured markdown: title slides become
1917
+ * `#` headings, body shapes become nested bullets at each paragraph's `lvl`,
1918
+ * tables become pipe tables, charts become summarised bullets, and speaker
1919
+ * notes and comments are collated. Positioning, animations, images, and
1920
+ * drawing detail are discarded — the projection is meant for AI ingestion and
1921
+ * full-text search, not layout.
1922
+ *
1923
+ * Runs entirely in the worker off the archive opened at {@link load} (no
1924
+ * re-copy of the file, no re-parse of the model on the main thread), so it
1925
+ * works in BOTH `mode: 'main'` and `mode: 'worker'`.
1926
+ *
1927
+ * @example
1928
+ * const pres = await PptxPresentation.load(buffer);
1929
+ * const md = await pres.toMarkdown();
1930
+ */
1931
+ toMarkdown(): Promise<string>;
1202
1932
  /**
1203
1933
  * Render a slide and attach canvas-native playback controls for any
1204
1934
  * embedded audio/video. Returns a {@link PresentationHandle} that owns the
@@ -1210,7 +1940,7 @@ export declare class PptxPresentation {
1210
1940
  destroy(): void;
1211
1941
  }
1212
1942
 
1213
- export declare class PptxScrollViewer {
1943
+ export declare class PptxScrollViewer implements ZoomableViewer {
1214
1944
  private _pres;
1215
1945
  private readonly _injected;
1216
1946
  private readonly _opts;
@@ -1234,6 +1964,18 @@ export declare class PptxScrollViewer {
1234
1964
  * than a `_scale === 1` sentinel because a fit scale of exactly 1 is a valid
1235
1965
  * established state (a 1× fit would otherwise be re-fit forever). */
1236
1966
  private _scaleEstablished;
1967
+ /**
1968
+ * IX9 F1 — a `setScale` factor requested BEFORE the base fit is established
1969
+ * (pre-load, or a zero-width container), already clamped to
1970
+ * `[zoomMin, zoomMax]`, or `null` when none is pending. The single-canvas
1971
+ * viewers latch a pre-load `setScale` and honour it on the first render; the
1972
+ * scroll viewers used to silently DROP it — the family-unified semantics are
1973
+ * "latch and apply once the layout establishes". `relayout()` applies (and
1974
+ * clears) this right after establishing the base, firing `onScaleChange` at
1975
+ * application time; `getScale()` reports it while pending so the caller sees
1976
+ * the same value a single-canvas viewer would show.
1977
+ */
1978
+ private _pendingScale;
1237
1979
  /** Live slots keyed by slide index. */
1238
1980
  private readonly _slots;
1239
1981
  /** Recyclable detached slots (canvas + textLayer reused across slides). */
@@ -1248,6 +1990,23 @@ export declare class PptxScrollViewer {
1248
1990
  * reporting an error so a rejection that lands after teardown is swallowed
1249
1991
  * rather than surfaced to a `onError` on a dead viewer. */
1250
1992
  private _destroyed;
1993
+ /**
1994
+ * Concurrent-load latch (generation token). Every self-loading `load()`
1995
+ * increments this and captures the value; after its engine finishes loading it
1996
+ * re-checks the live value and BAILS (destroying its own just-loaded engine) if
1997
+ * a newer `load()` has since started. Without it, two overlapping
1998
+ * `load(A)`/`load(B)` calls race the WASM parse / worker init, and whichever
1999
+ * RESOLVES last wins the swap — even the stale `load(A)` resolving after
2000
+ * `load(B)`; the loser's freshly created engine (never installed, or installed
2001
+ * then overwritten) then leaks its worker + pinned WASM allocation. The latch
2002
+ * composes with SC20: the check runs AFTER the new engine loads but BEFORE the
2003
+ * field assignment, `previous?.destroy()`, and the recycle/relayout post-load
2004
+ * work, so a superseded load never touches `this._pres` nor frees the current
2005
+ * (newer) engine. Only the self-loading path uses it — the injected path throws
2006
+ * up-front and never reaches here. `destroy()` also bumps it so a load in flight
2007
+ * at teardown is treated as superseded and its engine cleaned up.
2008
+ */
2009
+ private _loadGen;
1251
2010
  /** Worker mode: slide indices whose bitmap render is currently dispatched to the
1252
2011
  * engine. Coalesces a scroll storm — we never dispatch a second render for a
1253
2012
  * slide whose first is still in flight — and lets us drop slides that scrolled
@@ -1278,11 +2037,14 @@ export declare class PptxScrollViewer {
1278
2037
  * is host-agnostic. */
1279
2038
  private _settleTimer;
1280
2039
  private _wheelListener;
1281
- /** One-shot latch for the worker-mode text-selection warning. The overlay is a
1282
- * main-mode-only feature: in worker mode the per-run `onTextRun` geometry
1283
- * cannot cross the worker boundary, so an `enableTextSelection` overlay stays
1284
- * empty. We warn once (parity with `PptxViewer`) rather than per slot. */
1285
- private _warnedNoTextSelection;
2040
+ /** Gesture-only pointer anchor for the NEXT `setScale`, in scrollHost-viewport
2041
+ * px (`{ x, y }` from the wheel event, relative to the scroll host's top-left).
2042
+ * Set by the Ctrl/⌘+wheel handler right before it calls `setScale` so the zoom
2043
+ * pivots on the cursor ("zoom toward the pointer") in BOTH axes; consumed and
2044
+ * cleared by `setScale`. `null` for every non-gesture source (the public
2045
+ * `setScale`, the +/- steppers, `fitWidth`/`fitPage`, the resize re-fit), which
2046
+ * keep the historical viewport-TOP re-anchor so their behaviour is unchanged. */
2047
+ private _pendingZoomAnchor;
1286
2048
  /** Observes the container so a width change re-fits the base scale. Disconnected
1287
2049
  * in `destroy()`. */
1288
2050
  private _resizeObserver;
@@ -1358,6 +2120,12 @@ export declare class PptxScrollViewer {
1358
2120
  * `_positionSlot` (the flush-left floor), and by `_syncSpacerWidth` (the spacer
1359
2121
  * width). Resolved here (not stored) to mirror `_gap()`/`_pad()`. */
1360
2122
  private _padH;
2123
+ /** Index of the slide whose slot spans content-offset `y` (largest `i` with
2124
+ * `offsets[i] <= y`), for the pointer-anchored zoom re-anchor. Mirrors the
2125
+ * `topIndex` search `computeVisibleRange` runs for the scrollTop, but for an
2126
+ * ARBITRARY content-y (the pointer, not the viewport top). Clamped into
2127
+ * `[0, n-1]`; a `y` below the first slide (inside the leading pad) yields 0. */
2128
+ private _slideIndexAtOffset;
1361
2129
  private _range;
1362
2130
  private _syncSpacer;
1363
2131
  /** Horizontal scroll extent: the (uniform deck-wide) slide width plus both
@@ -1406,10 +2174,6 @@ export declare class PptxScrollViewer {
1406
2174
  * can pass for an old-epoch resolution). We gate them on the captured epoch.
1407
2175
  */
1408
2176
  private _renderSlot;
1409
- /** Warn once when an `enableTextSelection` overlay was requested but the render
1410
- * mode is `worker` (so the overlay stays empty). Same wording as
1411
- * `PptxViewer` — one warning per viewer, not per slot. */
1412
- private _maybeWarnNoTextSelection;
1413
2177
  /** Route an async render failure to `onError`, or `console.error` when none is
1414
2178
  * set (so failures are never fully silent), and never after teardown. */
1415
2179
  private _reportRenderError;
@@ -1445,7 +2209,10 @@ export declare class PptxScrollViewer {
1445
2209
  * `[zoomMin ?? 0.1, zoomMax ?? 4]` (absolute bounds, XlsxViewer convention — NOT
1446
2210
  * multiples of the base fit; design §3 keeps the clamp in the viewer, not core),
1447
2211
  * then re-anchor VERTICALLY so the slide currently under the viewport top stays
1448
- * fixed. A no-op when nothing is loaded or when the clamped scale is unchanged.
2212
+ * fixed. A no-op when the clamped scale is unchanged. Called BEFORE the deck is
2213
+ * loaded / the base fit is established, the clamped factor is LATCHED (IX9 F1,
2214
+ * family-unified with the single-canvas viewers) and applied by `relayout()`
2215
+ * once the layout establishes — `onScaleChange` fires then.
1449
2216
  *
1450
2217
  * FLICKER-FREE (design §7): this does NOT re-render the visible slides inline.
1451
2218
  * It shows an immediate CSS preview (stretch the existing bitmaps, scale the
@@ -1466,6 +2233,39 @@ export declare class PptxScrollViewer {
1466
2233
  * can no longer return below the floor to the original base fit through this API.
1467
2234
  */
1468
2235
  setScale(scale: number): void;
2236
+ /** IX9 {@link ZoomableViewer} — the current zoom factor, where `1` = 100% (a
2237
+ * slide at its natural EMU→px size). This is the viewer's absolute `_scale`
2238
+ * (`slideWidth/EMU_PER_PX × _scale` is the drawn width), so it reads `1` at
2239
+ * true 100% and, after the initial fit-to-width, the base fit factor. Before
2240
+ * the fit is established it reports a latched pre-load `setScale` (IX9 F1) if
2241
+ * one is pending — matching what a single-canvas viewer would show — else `1`. */
2242
+ getScale(): number;
2243
+ /** IX9 {@link ZoomableViewer} — step up to the next rung of the shared zoom
2244
+ * ladder above the current factor (clamped to `zoomMax` by {@link setScale}). */
2245
+ zoomIn(): void;
2246
+ /** IX9 {@link ZoomableViewer} — step down to the next lower ladder rung. */
2247
+ zoomOut(): void;
2248
+ /**
2249
+ * IX9 {@link ZoomableViewer} — fit a slide's WIDTH to the container (the classic
2250
+ * continuous-scroll "fit width"). Sets the scale to the width-fit base for the
2251
+ * current container, then re-anchors + re-renders via {@link setScale}. Defers
2252
+ * (no-op) while the container is unlaid-out. The `zoomMin`/`zoomMax` clamp still
2253
+ * applies, so a fit below `zoomMin` pins to `zoomMin`.
2254
+ */
2255
+ fitWidth(): void;
2256
+ /**
2257
+ * IX9 {@link ZoomableViewer} — fit a WHOLE slide (width and height) inside the
2258
+ * container so one slide is visible without scrolling; takes the tighter of the
2259
+ * width/height fit. Uses the deck-wide (uniform) slide size. Defers while
2260
+ * unlaid-out.
2261
+ */
2262
+ fitPage(): void;
2263
+ /** Shared fit for {@link fitWidth}/{@link fitPage}: the width-fit factor is the
2264
+ * established base (`_baseScale`); the page-fit additionally bounds by the
2265
+ * container height against the (uniform) slide height. Applies via
2266
+ * {@link setScale} so the flicker-free re-anchor / settle path and
2267
+ * `onScaleChange` all run. */
2268
+ private _fit;
1469
2269
  /**
1470
2270
  * CSS preview of the visible window at the current `_scale` (design §7
1471
2271
  * mechanism 1), WITHOUT re-rendering. Slots leaving the window recycle normally;
@@ -1492,12 +2292,11 @@ export declare class PptxScrollViewer {
1492
2292
  private _scheduleSettle;
1493
2293
  /** Full-resolution settle re-render of the visible window (design §7 mechanisms
1494
2294
  * 2+3). Re-renders each mounted slot at the current scale via the double-buffer
1495
- * swap (main) / same-canvas transfer (worker). Main mode also rebuilds the text
1496
- * overlay and clears its preview transform; in worker mode the overlay is
1497
- * permanently empty (text selection is main-mode-only), so the transform is
1498
- * inert there and is reset on recycle. Dispatched at the CURRENT epoch; the
1499
- * existing epoch gate discards it if a later `setScale` supersedes it
1500
- * mid-render. */
2295
+ * swap (main) / same-canvas transfer (worker). Both modes rebuild the text
2296
+ * overlay from the fresh render's run geometry (IX6 — worker mode collects the
2297
+ * runs off-thread via `_renderSlotBitmap`) and clear the preview transform.
2298
+ * Dispatched at the CURRENT epoch; the existing epoch gate discards it if a
2299
+ * later `setScale` supersedes it mid-render. */
1501
2300
  private _settleRender;
1502
2301
  /**
1503
2302
  * Settle-render one slot at the current scale (design §7 mechanism 3).
@@ -1539,6 +2338,22 @@ export declare class PptxScrollViewer {
1539
2338
  scrollToSlide(index: number, opts?: {
1540
2339
  behavior?: 'auto' | 'smooth';
1541
2340
  }): void;
2341
+ /**
2342
+ * IX1 hyperlink click dispatch (mirrors {@link PptxViewer._onHyperlinkClick}).
2343
+ * When the integrator supplies `opts.onHyperlinkClick` it OWNS the click (no
2344
+ * default). Otherwise: an external link opens in a new tab via the shared,
2345
+ * scheme-sanitised {@link openExternalHyperlink}; an internal slide jump scrolls
2346
+ * to the target slide via {@link scrollToSlide} once the action resolves to a
2347
+ * slide index (a jump resolving to no reachable slide is a safe no-op).
2348
+ */
2349
+ private _onHyperlinkClick;
2350
+ /** Populate an internal {@link HyperlinkTarget}'s `slideIndex` from its `ref`
2351
+ * via the engine's stamped part names. Relative `hlinkshowjump` verbs are
2352
+ * resolved against the slide currently at the viewport top
2353
+ * (`_range().topIndex`); a `../slides/slideN.xml` part target resolves through
2354
+ * the part-name map. An already-set index, an external target, and an
2355
+ * unresolvable ref all pass through unchanged (safe no-op). */
2356
+ private _resolveInternalSlideIndex;
1542
2357
  /**
1543
2358
  * Re-fit the base scale on a container resize while PRESERVING the current zoom
1544
2359
  * multiplier (design §11), then re-anchor + re-render. A `ResizeObserver` fires
@@ -1572,6 +2387,8 @@ export declare class PptxScrollViewer {
1572
2387
  /* Excluded from this release type: baseScaleForTest */
1573
2388
  /* Excluded from this release type: renderEpochForTest */
1574
2389
  /* Excluded from this release type: resizeForTest */
2390
+ /* Excluded from this release type: contentAtViewportYForTest */
2391
+ /* Excluded from this release type: viewportYOfForTest */
1575
2392
  /**
1576
2393
  * Tear down the viewer: remove the DOM subtree and (only for a self-loaded
1577
2394
  * engine) destroy the engine. An injected engine is left intact — the caller
@@ -1624,9 +2441,10 @@ export declare interface PptxScrollViewerOptions extends Omit<RenderSlideOptions
1624
2441
  paddingRight?: number;
1625
2442
  /** Slides kept mounted beyond the viewport on each side. Default 1. */
1626
2443
  overscan?: number;
1627
- /** Per-slide transparent text-selection overlay. MAIN render mode only:
1628
- * in worker mode `onTextRun` cannot cross the worker boundary, so the overlay
1629
- * stays empty and the viewer logs one warning (design §11). */
2444
+ /** Per-slide transparent text-selection overlay. IX6 works in BOTH render
2445
+ * modes: in worker mode the per-run geometry is collected off-thread and
2446
+ * shipped back beside the slide bitmap, so the overlay is populated identically
2447
+ * to main mode (no more empty overlay / one-time warning). */
1630
2448
  enableTextSelection?: boolean;
1631
2449
  /** Minimum zoom scale — a DIMENSIONLESS multiplier over the 96-dpi natural
1632
2450
  * slide size (10% = 0.1), matching `DocxScrollViewer`. Default 0.1. */
@@ -1670,6 +2488,12 @@ export declare interface PptxScrollViewerOptions extends Omit<RenderSlideOptions
1670
2488
  * `computeVisibleRange` (the first slide intersecting the viewport top,
1671
2489
  * EXCLUDING overscan). */
1672
2490
  onVisibleSlideChange?: (topIndex: number, total: number) => void;
2491
+ /** IX9 — fires whenever the zoom factor actually changes (`1` = 100% = a slide
2492
+ * at its natural EMU→px size): from {@link PptxScrollViewer.setScale},
2493
+ * `zoomIn`/`zoomOut`, `fitWidth`/`fitPage`, a Ctrl/⌘+wheel gesture, or a
2494
+ * container-resize re-fit. Named `onScaleChange` to match the single-canvas
2495
+ * viewers so all five share one notification shape. */
2496
+ onScaleChange?: (scale: number) => void;
1673
2497
  /** Error callback. When set, `load()` invokes it and resolves (otherwise the
1674
2498
  * error is rethrown — shared viewer error contract). It ALSO fires for async
1675
2499
  * per-slot render failures (both main `renderSlide` and worker
@@ -1677,6 +2501,17 @@ export declare interface PptxScrollViewerOptions extends Omit<RenderSlideOptions
1677
2501
  * crashing the loop. Without an `onError`, render failures are logged via
1678
2502
  * `console.error` so they are never fully silent. */
1679
2503
  onError?: (err: Error) => void;
2504
+ /**
2505
+ * IX1 (design decision — NOT user-confirmed, integrator may veto). Fires on a
2506
+ * hyperlink click in any mounted slide's text overlay (requires
2507
+ * {@link enableTextSelection}). Default when omitted: external →
2508
+ * {@link openExternalHyperlink} (new tab, sanitised, noopener); internal
2509
+ * slide-jump → {@link scrollToSlide} once the action resolves to a slide index
2510
+ * via {@link PptxPresentation.resolveInternalTarget} (a jump that resolves to
2511
+ * no reachable slide is a safe no-op). When provided, the viewer calls this
2512
+ * instead and takes NO default action.
2513
+ */
2514
+ onHyperlinkClick?: (target: HyperlinkTarget) => void;
1680
2515
  }
1681
2516
 
1682
2517
  /** Information about a rendered text segment for building a transparent selection overlay. */
@@ -1709,6 +2544,14 @@ export declare interface PptxTextRunInfo {
1709
2544
  * `vert="vert270"` → -90). The CSS overlay must add this to `rotation`.
1710
2545
  */
1711
2546
  textBodyRotation?: number;
2547
+ /**
2548
+ * Resolved hyperlink target for this run (IX1), classified into the shared
2549
+ * {@link HyperlinkTarget} shape. Present only for runs whose `<a:rPr>` carried
2550
+ * an `<a:hlinkClick>`; the overlay makes such spans clickable. The glyph
2551
+ * drawing (colour + underline) is unaffected — this is metadata for the
2552
+ * transparent overlay only.
2553
+ */
2554
+ hyperlink?: HyperlinkTarget;
1712
2555
  }
1713
2556
 
1714
2557
  /**
@@ -1721,9 +2564,18 @@ export declare interface PptxTextRunInfo {
1721
2564
  *
1722
2565
  * For custom layouts (multi-canvas, thumbnails, scroll view) use PptxPresentation directly.
1723
2566
  */
1724
- export declare class PptxViewer {
2567
+ export declare class PptxViewer implements ZoomableViewer {
1725
2568
  private readonly canvas;
1726
2569
  private readonly wrapper;
2570
+ /**
2571
+ * IX9 explicit zoom factor (`1` = 100% = the slide at its natural EMU→px
2572
+ * width), or `null` when the caller has never invoked a zoom method. `null`
2573
+ * preserves the pre-IX9 render path EXACTLY: the slide renders at `opts.width`
2574
+ * (or `canvas.offsetWidth || 960` when unset), so default rendering is
2575
+ * byte-identical. The first zoom call latches a number here, after which
2576
+ * {@link _targetWidth} derives the render width from it.
2577
+ */
2578
+ private _scale;
1727
2579
  /** The canvas's DOM position BEFORE the constructor reparented it into
1728
2580
  * {@link wrapper}, captured so {@link destroy} can return the caller-owned
1729
2581
  * canvas to exactly where it was. `null` parent = canvas was passed
@@ -1734,6 +2586,13 @@ export declare class PptxViewer {
1734
2586
  * (empty string if it was unset), restored on {@link destroy}. */
1735
2587
  private readonly _originalDisplay;
1736
2588
  private textLayer;
2589
+ /** IX2 — the find-highlight overlay layer (always created, above the text
2590
+ * layer, `pointer-events:none`). */
2591
+ private highlightLayer;
2592
+ /** IX2 — find state (per-slide runs, matches, active cursor). */
2593
+ private _find;
2594
+ /** Private 2d context for measuring highlight text (own 1×1 canvas). */
2595
+ private _measureCtx;
1737
2596
  private engine;
1738
2597
  private readonly opts;
1739
2598
  private currentSlide;
@@ -1744,14 +2603,39 @@ export declare class PptxViewer {
1744
2603
  * render path. The media-playback path keeps a 2d context (via presentSlide),
1745
2604
  * so this is obtained only when worker mode renders without media playback. */
1746
2605
  private _bitmapCtx;
1747
- private _warnedNoTextSelection;
2606
+ /** Set by {@link destroy} (first line). Guards {@link _reportRenderError} so a
2607
+ * render rejection that lands AFTER teardown is swallowed rather than surfaced
2608
+ * to an `onError` / `console.error` on a dead viewer — parity with the scroll
2609
+ * viewers' `_destroyed` flag. */
2610
+ private _destroyed;
2611
+ /**
2612
+ * Concurrent-load latch (generation token). Every {@link load} increments this
2613
+ * and captures the value; after its engine finishes loading it re-checks the
2614
+ * live value and BAILS (destroying its own just-loaded engine) if a newer
2615
+ * `load()` has since started. Without it, two overlapping `load(A)`/`load(B)`
2616
+ * calls race the WASM parse / worker init, and whichever RESOLVES last wins the
2617
+ * swap — even the stale `load(A)` resolving after `load(B)`; the loser's freshly
2618
+ * created engine (never installed, or installed then overwritten) then leaks its
2619
+ * worker + pinned WASM allocation. The latch composes with SC20: the check runs
2620
+ * AFTER the new engine loads but BEFORE the field assignment and
2621
+ * `previous?.destroy()`, so a superseded load never touches `this.engine` nor
2622
+ * frees the current (newer) engine. {@link destroy} also bumps it so a load in
2623
+ * flight at teardown is treated as superseded and its engine cleaned up.
2624
+ */
2625
+ private _loadGen;
1748
2626
  constructor(canvas: HTMLCanvasElement, opts?: PptxViewerOptions);
1749
2627
  /**
1750
2628
  * Load a PPTX from URL or ArrayBuffer and render the first slide.
1751
2629
  *
1752
- * Error contract (shared by all three viewers): on failure, if an `onError`
1753
- * callback was provided it is invoked and `load` resolves normally; if not,
1754
- * the error is rethrown so it is never silently swallowed.
2630
+ * Error contract (shared by all three viewers):
2631
+ * - Parse/load failure (the underlying `PptxPresentation.load()` call itself
2632
+ * rejects): if an `onError` callback was provided it is invoked and `load`
2633
+ * resolves normally; if not, the error is rethrown so it is never silently
2634
+ * swallowed.
2635
+ * - Render failure (the first slide fails to draw AFTER a successful
2636
+ * parse/load): routed to the shared `_reportRenderError` contract (`onError`
2637
+ * if provided, else `console.error` — never silent) and `load` still
2638
+ * RESOLVES, matching every subsequent navigation call.
1755
2639
  */
1756
2640
  load(source: string | ArrayBuffer): Promise<void>;
1757
2641
  /** Navigate to a specific slide (0-indexed). */
@@ -1784,8 +2668,115 @@ export declare class PptxViewer {
1784
2668
  getNotes(slideIndex: number): string | null;
1785
2669
  /** The underlying <canvas> element. */
1786
2670
  get canvasElement(): HTMLCanvasElement;
2671
+ /** Natural (100%) CSS-px width of a slide — `slideWidth(EMU) / EMU_PER_PX`.
2672
+ * 0 when nothing is loaded. The scale-1 reference every zoom factor
2673
+ * multiplies. */
2674
+ private _naturalWidthPx;
2675
+ /**
2676
+ * The width (CSS px) the render paths draw the slide at, honouring the zoom
2677
+ * state. `_scale === null` (no zoom method ever called) ⇒ the pre-IX9 value
2678
+ * `opts.width ?? (canvas.offsetWidth || 960)` verbatim (byte-identical
2679
+ * default). Once a factor latched ⇒ `naturalWidth × scale` (rounded), so the
2680
+ * slide is exactly `scale ×` its natural size regardless of `opts.width`.
2681
+ */
2682
+ private _targetWidth;
2683
+ /** IX9 {@link ZoomableViewer} — the current zoom factor (`1` = 100%). Before
2684
+ * any zoom method is called this is the EFFECTIVE scale implied by the render
2685
+ * width: `targetWidth / naturalWidth`, or `1` when nothing is loaded. */
2686
+ getScale(): number;
2687
+ private _zoomMin;
2688
+ private _zoomMax;
2689
+ /**
2690
+ * IX9 {@link ZoomableViewer} — set the absolute zoom factor (`1` = 100% = the
2691
+ * slide at its natural EMU→px width), clamped to `[zoomMin, zoomMax]`, and
2692
+ * re-render the current slide at the new size. Fires `onScaleChange` when the
2693
+ * clamped factor actually changes. Resolves once the re-render settles.
2694
+ */
2695
+ setScale(scale: number): Promise<void>;
2696
+ /** IX9 {@link ZoomableViewer} — step up to the next rung of the shared zoom
2697
+ * ladder (clamped to `zoomMax`). */
2698
+ zoomIn(): Promise<void>;
2699
+ /** IX9 {@link ZoomableViewer} — step down to the next lower ladder rung. */
2700
+ zoomOut(): Promise<void>;
2701
+ /**
2702
+ * IX9 {@link ZoomableViewer} — fit the current slide's WIDTH to the host
2703
+ * container (the element the canvas lives in), then re-render. Defers (no-op)
2704
+ * when nothing is loaded or the container is unlaid-out. Routes through
2705
+ * {@link setScale}.
2706
+ */
2707
+ fitWidth(): Promise<void>;
2708
+ /**
2709
+ * IX9 {@link ZoomableViewer} — fit the WHOLE current slide (width and height)
2710
+ * inside the container so it is fully visible; takes the tighter of the
2711
+ * width/height fit. Defers when unloaded / unlaid-out.
2712
+ */
2713
+ fitPage(): Promise<void>;
2714
+ /** Shared fit for {@link fitWidth}/{@link fitPage}: measure the natural slide
2715
+ * size + the container box, ask core's pure `fitScale`, apply via setScale. */
2716
+ private _fit;
1787
2717
  private renderCurrentSlide;
2718
+ /** Draw the find-highlight boxes for the current slide from its runs. */
2719
+ private _buildHighlightLayer;
2720
+ /** A width-measurer primed with `font`, backed by a private 1×1 canvas. */
2721
+ private _measureForFont;
2722
+ /** IX6 — collect a slide's runs for search without touching the visible
2723
+ * canvas. Delegates to `collectSlideRuns`, which works in BOTH modes (worker:
2724
+ * off-thread, ships only the runs; main: throwaway offscreen canvas). Used for
2725
+ * slides other than the one on screen. */
2726
+ private _collectSlideRuns;
2727
+ /**
2728
+ * IX2 — find every occurrence of `query` across all slides and highlight them
2729
+ * (a soft box per match on the highlight overlay). Returns every match in
2730
+ * document order, each tagged with its `{ slide }` (0-based). Case-insensitive
2731
+ * by default; pass `{ caseSensitive: true }` for an exact match.
2732
+ *
2733
+ * Scans all slides (each rendered once offscreen to read its text; the visible
2734
+ * slide reuses its on-screen render). IX6 — works in BOTH `mode: 'main'` and
2735
+ * `mode: 'worker'`: in worker mode each slide's run geometry is collected
2736
+ * off-thread and shipped back, so find returns the same matches on the same
2737
+ * code path. An empty query clears the find.
2738
+ */
2739
+ findText(query: string, opts?: FindMatchesOptions): Promise<FindMatch<PptxMatchLocation>[]>;
2740
+ /**
2741
+ * IX2 — move to the next match (wrap-around), navigating to its slide if
2742
+ * needed, and draw it in the active-match colour. Returns the now-active
2743
+ * match, or `null` when there are none. Call {@link findText} first.
2744
+ */
2745
+ findNext(): Promise<FindMatch<PptxMatchLocation> | null>;
2746
+ /** IX2 — move to the previous match (wrap-around). */
2747
+ findPrev(): Promise<FindMatch<PptxMatchLocation> | null>;
2748
+ /** IX2 — clear all highlights and reset the find state. */
2749
+ clearFind(): void;
2750
+ private _activateMatch;
2751
+ /** Rebuild the highlight overlay for the current slide from cached runs. */
2752
+ private _redrawHighlights;
1788
2753
  private _buildTextLayer;
2754
+ /**
2755
+ * IX1/IX-nav hyperlink click dispatch. An internal target is first *enriched*
2756
+ * with its resolved 0-based `slideIndex` (via
2757
+ * {@link PptxPresentation.resolveInternalTarget}, relative to the current
2758
+ * slide) so a jump verb / slide-part ref arrives already mapped — this is the
2759
+ * field that was previously always `undefined`. When the integrator supplies
2760
+ * `opts.onHyperlinkClick` it OWNS the (enriched) click and takes NO default
2761
+ * action. Otherwise the viewer's default policy applies: an external link
2762
+ * opens in a new tab via the shared, scheme-sanitised
2763
+ * {@link openExternalHyperlink}; an internal slide jump navigates via
2764
+ * {@link goToSlide} to the resolved index (a target that resolves to no
2765
+ * reachable slide is a safe no-op).
2766
+ */
2767
+ private _onHyperlinkClick;
2768
+ /** Populate an internal {@link HyperlinkTarget}'s `slideIndex` from its `ref`
2769
+ * (a `ppaction://hlinkshowjump?jump=…` verb resolved relative to the current
2770
+ * slide, or a `../slides/slideN.xml` part target resolved through the stamped
2771
+ * part-name map — no filename-suffix heuristic). Any already-set `slideIndex`
2772
+ * is kept; an external target and an unresolvable ref pass through unchanged so
2773
+ * the caller no-ops safely. */
2774
+ private _resolveInternalSlideIndex;
2775
+ /** PD14 render-error contract: route a render failure to `onError`, or
2776
+ * `console.error` when none is given (never fully silent), and never after
2777
+ * teardown. Mirrors the scroll viewers' `_reportRenderError` so all three
2778
+ * single-canvas viewers agree. */
2779
+ private _reportRenderError;
1789
2780
  /**
1790
2781
  * Clean up the viewer and terminate the background worker.
1791
2782
  *
@@ -1803,6 +2794,17 @@ export declare interface PptxViewerOptions extends RenderOptions, LoadOptions {
1803
2794
  onSlideChange?: (index: number, total: number) => void;
1804
2795
  /** Called on parse or render errors */
1805
2796
  onError?: (err: Error) => void;
2797
+ /** IX9 zoom contract ({@link ZoomableViewer}) — the clamp range for
2798
+ * {@link PptxViewer.setScale} / `zoomIn` / `zoomOut` / `fitWidth` / `fitPage`,
2799
+ * as user-facing zoom factors (`1` = 100% = the slide at its natural
2800
+ * EMU→px size). Defaults 0.1–4 (10%–400%), matching the other viewers. */
2801
+ zoomMin?: number;
2802
+ zoomMax?: number;
2803
+ /** IX9 — fires whenever the zoom factor actually changes (`1` = 100%): from
2804
+ * {@link PptxViewer.setScale}, `zoomIn`/`zoomOut`, or `fitWidth`/`fitPage`.
2805
+ * Named `onScaleChange` to match the docx/xlsx viewers so all five share one
2806
+ * notification shape. */
2807
+ onScaleChange?: (scale: number) => void;
1806
2808
  /**
1807
2809
  * Enable interactive audio/video playback. When true, slides are rendered
1808
2810
  * via {@link PptxPresentation.presentSlide} so media elements become
@@ -1834,6 +2836,17 @@ export declare interface PptxViewerOptions extends RenderOptions, LoadOptions {
1834
2836
  * in sync if {@link DimOptions} gains a field.
1835
2837
  */
1836
2838
  hiddenSlideDim?: Partial<DimOptions>;
2839
+ /**
2840
+ * IX1 (design decision — NOT user-confirmed, integrator may veto). Fires on a
2841
+ * hyperlink click (a text run whose `<a:rPr>` carried an `<a:hlinkClick>`;
2842
+ * requires {@link enableTextSelection} so the overlay spans exist). Default
2843
+ * when omitted: external → {@link openExternalHyperlink} (new tab, sanitised,
2844
+ * noopener); internal slide-jump → {@link goToSlide} once the action resolves
2845
+ * to a slide index via {@link PptxPresentation.resolveInternalTarget} (a jump
2846
+ * that resolves to no reachable slide is a safe no-op). When provided, the
2847
+ * viewer calls this instead and takes NO default action.
2848
+ */
2849
+ onHyperlinkClick?: (target: HyperlinkTarget) => void;
1837
2850
  }
1838
2851
 
1839
2852
  export declare interface Presentation {
@@ -1949,6 +2962,14 @@ export declare interface RenderSlideToBitmapOptions {
1949
2962
  /* Excluded from this release type: skipMediaControls */
1950
2963
  /** Translucent overlay drawn over the finished slide (hidden-slide dimming). */
1951
2964
  dim?: DimOptions;
2965
+ /**
2966
+ * IX6 — receives the slide's text-run geometry (the same stream `renderSlide`
2967
+ * emits in main mode). Stays main-thread (never crosses the wire); in worker
2968
+ * mode the proxy invokes it with the runs the worker shipped back beside the
2969
+ * bitmap, so a caller builds the selection / find overlay on the SAME code
2970
+ * path in both modes.
2971
+ */
2972
+ onTextRun?: TextRunCallback;
1952
2973
  }
1953
2974
 
1954
2975
  /**
@@ -2004,6 +3025,13 @@ declare interface SecondaryValueAxis {
2004
3025
  lineHidden: boolean;
2005
3026
  /** `<c:majorTickMark>` — "cross" (default) | "out" | "in" | "none". */
2006
3027
  majorTickMark: string;
3028
+ /**
3029
+ * `<c:valAx><c:majorUnit val>` (§21.2.2.103) — explicit distance between
3030
+ * major ticks/gridlines on THIS secondary axis, overriding the Excel-style
3031
+ * auto "nice" step. null/undefined ⇒ auto step (byte-stable). Symmetric with
3032
+ * {@link ChartModel.valAxisMajorUnit} on the primary axis.
3033
+ */
3034
+ majorUnit?: number | null;
2007
3035
  /** `<c:title>` run-prop font size (hpt). */
2008
3036
  titleFontSizeHpt?: number | null;
2009
3037
  /** `<c:title>` run-prop bold flag. */
@@ -2077,12 +3105,32 @@ export declare interface ShapeElement {
2077
3105
  /** `<a:sp3d>` 3D shape properties (ECMA-376 §20.1.5.12). Parsed but not
2078
3106
  * rendered in Phase A. */
2079
3107
  sp3d?: Sp3d;
3108
+ /** Shape-level hyperlink target resolved from `<p:cNvPr><a:hlinkClick @r:id>`
3109
+ * via slide _rels (ECMA-376 §21.1.2.3.5). For an external link this is the
3110
+ * URL; for an internal slide jump it is the resolved internal part name.
3111
+ * Undefined when the shape carries no hlinkClick. */
3112
+ hyperlink?: string;
3113
+ /** Raw `<a:hlinkClick @action>` (e.g. `"ppaction://hlinksldjump"`) when the
3114
+ * shape link is an internal PowerPoint action rather than an external URL.
3115
+ * Undefined when absent. */
3116
+ hyperlinkAction?: string;
2080
3117
  }
2081
3118
 
2082
3119
  export declare interface Slide {
2083
3120
  index: number;
2084
3121
  /** 1-based slide number (index + 1); used to render slidenum fields */
2085
3122
  slideNumber: number;
3123
+ /**
3124
+ * The slide's normalized OPC part name (e.g. `ppt/slides/slide3.xml`),
3125
+ * resolved through `presentation.xml.rels` in `sldIdLst` order (ECMA-376
3126
+ * §19.3.1.42). An internal hyperlink slide jump
3127
+ * (`<a:hlinkClick action="ppaction://hlinksldjump" r:id>`, §21.1.2.3.5)
3128
+ * carries a rel Target that resolves to this same part name — so
3129
+ * {@link PptxPresentation.getSlideIndexByPartName} can turn a click into a
3130
+ * slide index. Absent (`undefined`) only for a slide whose part path was not
3131
+ * recorded; healthy and broken slides both carry it.
3132
+ */
3133
+ partName?: string;
2086
3134
  background: Fill | null;
2087
3135
  elements: SlideElement[];
2088
3136
  /**
@@ -2106,6 +3154,14 @@ export declare interface Slide {
2106
3154
  * slide modes (read it via `PptxPresentation.isHidden`).
2107
3155
  */
2108
3156
  hidden?: boolean;
3157
+ /**
3158
+ * RB7 partial degradation: set when this slide's part could not be parsed. The
3159
+ * deck still opens with the OTHER slides intact; this one is a placeholder
3160
+ * (`elements` empty) whose `parseError` names the offending part (e.g.
3161
+ * `"ppt/slides/slide3.xml: <detail>"`). Absent (`undefined`) for every healthy
3162
+ * slide. The renderer paints a visible error box instead of slide content.
3163
+ */
3164
+ parseError?: string;
2109
3165
  }
2110
3166
 
2111
3167
  export declare type SlideElement = ShapeElement | PictureElement | TableElement | ChartElement | MediaElement;
@@ -2243,6 +3299,19 @@ export declare interface TextBody extends TextBody_2 {
2243
3299
  * omitted from JSON when false. Only meaningful when `numCol > 1`.
2244
3300
  */
2245
3301
  rtlCol?: boolean;
3302
+ /**
3303
+ * `<a:bodyPr><a:prstTxWarp>` (ECMA-376 §20.1.9.19) — WordArt text warp. When
3304
+ * present the renderer maps each glyph through the named envelope
3305
+ * (presetTextWarpDefinitions) instead of laying text out flat. Omitted from
3306
+ * JSON when the body has no warp, so unwarped bodies are byte-identical.
3307
+ */
3308
+ textWarp?: {
3309
+ /** The `prst` name, e.g. `"textArchUp"`, `"textWave1"`. */
3310
+ preset: string;
3311
+ /** `<a:avLst>` adjust values (adj1, adj2, …) in thousandths of a percent.
3312
+ * Omitted when the author supplied none (preset defaults apply). */
3313
+ adj?: number[];
3314
+ };
2246
3315
  /**
2247
3316
  * Narrow the inherited `paragraphs` to the PPTX `Paragraph` so consumers see
2248
3317
  * the PPTX-only `eaLnBrk` flag. PPTX `Paragraph extends CoreParagraph`, so
@@ -2375,10 +3444,19 @@ export declare interface TextRunData {
2375
3444
  /** Set for OOXML field runs (e.g. "slidenum"). When set, renderer replaces text with field value. */
2376
3445
  fieldType?: string;
2377
3446
  /**
2378
- * Hyperlink target URL resolved from rPr > a:hlinkClick @r:id via the slide's _rels.
2379
- * Undefined for runs without a hyperlink. ECMA-376 §21.1.2.3.5 (CT_Hyperlink).
3447
+ * Hyperlink target resolved from rPr > a:hlinkClick @r:id via the slide's _rels.
3448
+ * For an external link this is the URL; for an internal slide jump it is the
3449
+ * resolved internal part name (e.g. "../slides/slide3.xml"). Undefined for runs
3450
+ * without a hyperlink. ECMA-376 §21.1.2.3.5 (CT_Hyperlink).
2380
3451
  */
2381
3452
  hyperlink?: string;
3453
+ /**
3454
+ * Raw `<a:hlinkClick @action>` string (e.g. "ppaction://hlinksldjump") when
3455
+ * present — its presence marks {@link hyperlink} as an INTERNAL PowerPoint
3456
+ * action (slide jump / first / last …) rather than an external URL. Undefined
3457
+ * when the hlinkClick has no @action. ECMA-376 §21.1.2.3.5. (IX1)
3458
+ */
3459
+ hyperlinkAction?: string;
2382
3460
  /**
2383
3461
  * Run-level drop shadow on glyphs (`<a:rPr><a:effectLst><a:outerShdw>`),
2384
3462
  * ECMA-376 §20.1.8.45. Independent of the shape-level shadow on `spPr`.
@@ -2428,4 +3506,90 @@ export declare interface TileInfo {
2428
3506
  algn: string;
2429
3507
  }
2430
3508
 
3509
+ /**
3510
+ * IX9 — the shared zoom API contract for every viewer (DocxViewer, PptxViewer,
3511
+ * DocxScrollViewer, PptxScrollViewer, XlsxViewer).
3512
+ *
3513
+ * This module owns ONLY the pure, DOM-free pieces of the contract: the type
3514
+ * ({@link ZoomableViewer}), the discrete zoom-step ladder ({@link nextZoomStep} /
3515
+ * {@link prevZoomStep}), the fit-to-content scale math ({@link fitScale}), and the
3516
+ * range clamp ({@link clampScale}). Each viewer implements the interface with its
3517
+ * own scale field and re-render path; this keeps ONE definition of "what a zoom
3518
+ * factor means" and "what the +/- steps are" across all five, so a host can drive
3519
+ * any viewer through the same six calls without special-casing the format.
3520
+ *
3521
+ * SCALE SEMANTICS (the contract): a scale of `1` means 100% — the content at its
3522
+ * natural size (a docx page at `widthPt × PT_TO_PX`, a pptx slide at
3523
+ * `slideWidth / EMU_PER_PX`, an xlsx grid at `cellScale` 1). `getScale()` and
3524
+ * `setScale(n)` speak this user-facing factor for EVERY viewer.
3525
+ *
3526
+ * KNOWN FAMILY DIFFERENCE — the INITIAL scale right after load (deliberate,
3527
+ * documented rather than papered over): the single-canvas viewers (DocxViewer /
3528
+ * PptxViewer) and XlsxViewer start at `1` (or the effective factor implied by an
3529
+ * explicit `width` option); the continuous-scroll viewers (DocxScrollViewer /
3530
+ * PptxScrollViewer) AUTO-FIT to the container on first layout, so their
3531
+ * `getScale()` right after load reports the fit-to-width BASE factor (≠ 1 unless
3532
+ * the container happens to match the natural width). The unit is identical — only
3533
+ * the starting point differs, because fit-to-width is the natural resting state
3534
+ * of a continuous document viewer.
3535
+ *
3536
+ * PRE-LOAD `setScale` (family-unified, IX9 F1): a `setScale` called before the
3537
+ * content is loaded / before the layout is established is LATCHED — never
3538
+ * silently dropped — and applied once the viewer establishes its scale (the
3539
+ * single-canvas viewers honour it on the first render; the scroll viewers apply
3540
+ * it right after the base fit establishes, firing `onScaleChange` at application
3541
+ * time). `getScale()` reports the latched factor while it is pending.
3542
+ *
3543
+ * API SHAPE (idiomatic default — the integrator MAY veto; see the IX9 PR): a
3544
+ * six-method surface plus one change notification (`onScaleChange`). Deliberately
3545
+ * NO new UI here — the contract is API only (design decision IX9 §4). Touch-pinch
3546
+ * (IX8) is out of scope.
3547
+ */
3548
+ /**
3549
+ * The zoom contract every viewer satisfies. All scales are the user-facing factor
3550
+ * where `1` = 100% (see the module note). `fitWidth`/`fitPage` are async because a
3551
+ * fit re-renders at the new scale; the getters/steppers resolve synchronously.
3552
+ */
3553
+ declare interface ZoomableViewer {
3554
+ /** The current zoom factor (`1` = 100%). Never throws — returns the default
3555
+ * (`1`) before anything is loaded, or the latched pending factor when a
3556
+ * pre-load `setScale` is waiting to be applied (see the module note). */
3557
+ getScale(): number;
3558
+ /** Set the absolute zoom factor (`1` = 100%), clamped to the viewer's
3559
+ * `[zoomMin, zoomMax]`. Re-renders at the new scale and fires `onScaleChange`
3560
+ * when the clamped value actually changes. Called BEFORE the content is
3561
+ * loaded / the layout is established, the (clamped) factor is LATCHED and
3562
+ * applied once the viewer establishes its scale — family-unified semantics
3563
+ * (IX9 F1): never silently dropped by any viewer. */
3564
+ setScale(scale: number): void | Promise<void>;
3565
+ /** Step up to the next larger rung of the shared zoom ladder (25 %→400 %),
3566
+ * clamped to `zoomMax`. Equivalent to `setScale(nextZoomStep(getScale()))`. */
3567
+ zoomIn(): void | Promise<void>;
3568
+ /** Step down to the next smaller ladder rung, clamped to `zoomMin`. */
3569
+ zoomOut(): void | Promise<void>;
3570
+ /** Fit the content's WIDTH to the container (the common "fit width" / "fit
3571
+ * page width" verb). Sets the scale so one page/slide/sheet-column-run spans
3572
+ * the available width, then re-renders. Resolves once the fit render settles.
3573
+ *
3574
+ * PERSISTENCE is viewer-implementation-dependent (deliberate, by family): the
3575
+ * single-canvas viewers (DocxViewer / PptxViewer) and XlsxViewer apply the fit
3576
+ * ONE-SHOT — they observe no container resizes, so a later resize does NOT
3577
+ * re-fit (call `fitWidth()` again after a layout change). The continuous-
3578
+ * scroll viewers (DocxScrollViewer / PptxScrollViewer) re-fit their width-fit
3579
+ * base on every container resize, so a `fitWidth()` there effectively
3580
+ * PERSISTS across resizes (the resize re-fit preserves the width-fit state). */
3581
+ fitWidth(): void | Promise<void>;
3582
+ /** Fit the WHOLE content (width AND height) inside the container, so an entire
3583
+ * page/slide is visible without scrolling. Sets the scale to the smaller of the
3584
+ * width- and height-fit factors, then re-renders.
3585
+ *
3586
+ * PERSISTENCE is viewer-implementation-dependent, and — unlike `fitWidth` —
3587
+ * a page fit does NOT persist across container resizes on ANY viewer: the
3588
+ * single-canvas viewers and XlsxViewer observe no resizes at all (one-shot),
3589
+ * and the continuous-scroll viewers' resize handler re-applies the WIDTH fit
3590
+ * (preserving the zoom multiplier), not the page fit. Re-invoke `fitPage()`
3591
+ * after a layout change to re-fit. */
3592
+ fitPage(): void | Promise<void>;
3593
+ }
3594
+
2431
3595
  export { }