@silurus/ooxml 0.75.3 → 0.75.5

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.
@@ -1,4 +1,4 @@
1
- //#region dist/.types-work/mathjax-BO1uUSCW.d.ts
1
+ //#region dist/.types-work/mathjax-Cp9KrvN5.d.ts
2
2
  //#region packages/core/src/math/mathjax.d.ts
3
3
  interface MathSvg {
4
4
  /** standalone `<svg>…</svg>` markup. */
@@ -29,7 +29,7 @@ interface MathRenderer {
29
29
  mathMLToSvg(mathml: string): Promise<MathSvg>;
30
30
  }
31
31
  //#endregion
32
- //#region dist/.types-work/hyperlink-CHHEOeCL.d.ts
32
+ //#region dist/.types-work/hyperlink-DTGUpBI3.d.ts
33
33
  //#region packages/core/src/types/math.d.ts
34
34
  type MathStyle = 'roman' | 'italic' | 'bold' | 'boldItalic';
35
35
  interface MathRun {
@@ -180,182 +180,654 @@ interface Duotone$1 {
180
180
  clr2: string;
181
181
  }
182
182
  //#endregion
183
- //#region packages/core/src/types/chart.d.ts
184
- interface ChartSeries {
185
- name: string;
186
- /** Hex without '#'. null = fall back to palette. */
187
- color: string | null;
188
- /** Numeric values; null = missing data point. */
189
- values: (number | null)[];
183
+ //#region packages/core/src/types/common.d.ts
184
+ type PathCmd$2 = {
185
+ cmd: 'moveTo';
186
+ x: number;
187
+ y: number;
188
+ } | {
189
+ cmd: 'lineTo';
190
+ x: number;
191
+ y: number;
192
+ } | {
193
+ cmd: 'cubicBezTo';
194
+ x1: number;
195
+ y1: number;
196
+ x2: number;
197
+ y2: number;
198
+ x: number;
199
+ y: number;
200
+ } | {
201
+ cmd: 'arcTo';
202
+ wr: number;
203
+ hr: number;
204
+ stAng: number;
205
+ swAng: number;
206
+ } | {
207
+ cmd: 'close';
208
+ };
209
+ type Fill = SolidFill | NoFill | GradientFill | PatternFill | ImageFill;
210
+ interface SolidFill {
211
+ fillType: 'solid';
212
+ color: string;
213
+ }
214
+ interface NoFill {
215
+ fillType: 'none';
216
+ }
217
+ interface GradientStop$1 {
218
+ position: number;
219
+ color: string;
220
+ }
221
+ interface GradientFill {
222
+ fillType: 'gradient';
223
+ stops: GradientStop$1[];
224
+ /** degrees: 0 = left→right, 90 = top→bottom */
225
+ angle: number;
226
+ /** 'linear' | 'radial' */
227
+ gradType: string;
228
+ }
229
+ /**
230
+ * Preset pattern fill — ECMA-376 §20.1.8.40 (CT_PatternFillProperties)
231
+ * with `preset` drawn from §20.1.10.59 (ST_PresetPatternVal).
232
+ */
233
+ interface PatternFill {
234
+ fillType: 'pattern';
235
+ /** Foreground hex colour — used for the "1" pixels of the preset bitmap. */
236
+ fg: string;
237
+ /** Background hex colour — used for the "0" pixels. */
238
+ bg: string;
239
+ /** Preset name, e.g. "pct25", "horz", "diagCross", "lgGrid". */
240
+ preset: string;
241
+ }
242
+ /**
243
+ * ECMA-376 §20.1.8.30 (CT_RelativeRect) — the destination rectangle a stretched
244
+ * blip is mapped into, as edge insets relative to the fill region. Values are
245
+ * fractions (ST_Percentage / 100000); **negative values let the image bleed
246
+ * past the box (overscan)**. Absent edges default to 0.
247
+ */
248
+ interface FillRect {
249
+ l?: number;
250
+ t?: number;
251
+ r?: number;
252
+ b?: number;
253
+ }
254
+ /**
255
+ * ECMA-376 §20.1.8.58 (CT_TileInfoProperties) — tiled blip-fill placement.
256
+ * The blip repeats at its native size (scaled by sx/sy) across the fill box.
257
+ * Mutually exclusive with {@link ImageFill.fillRect} (the `stretch` mode).
258
+ */
259
+ interface TileInfo {
260
+ /** Horizontal offset of the first tile, in EMU (`tx`). Default 0. */
261
+ tx: number;
262
+ /** Vertical offset of the first tile, in EMU (`ty`). Default 0. */
263
+ ty: number;
264
+ /** Horizontal tile scale as a fraction (`sx` / 100000). Default 1.0. */
265
+ sx: number;
266
+ /** Vertical tile scale as a fraction (`sy` / 100000). Default 1.0. */
267
+ sy: number;
268
+ /** Mirror mode: `'none' | 'x' | 'y' | 'xy'` (`flip`). Default `'none'`. */
269
+ flip: string;
190
270
  /**
191
- * Per-data-point colors (pie / doughnut). Hex without '#'. null inside the
192
- * array = use palette for that slice. Omit entirely for non-pie series.
271
+ * Anchor corner the tile grid registers against:
272
+ * `tl|t|tr|l|ctr|r|bl|b|br` (`algn`). The schema has no default; a host may
273
+ * apply a compatibility fallback when omitted.
193
274
  */
194
- dataPointColors?: (string | null)[] | null;
275
+ algn?: string;
276
+ }
277
+ /**
278
+ * Image fill — ECMA-376 §20.1.8.14 (CT_BlipFillProperties). The embedded blip
279
+ * is carried as a zip path + MIME; the renderer fetches the bytes on demand via
280
+ * {@link RenderOptions.fetchImage} (no base64 inlined at parse time). Both
281
+ * fill-modes are modelled and mutually exclusive: `stretch` (§20.1.8.56) carries
282
+ * {@link ImageFill.fillRect}; `tile` (§20.1.8.58) carries {@link ImageFill.tile}.
283
+ */
284
+ interface ImageFill {
285
+ fillType: 'image';
195
286
  /**
196
- * Per-data-point data-label text colors. Used by chartEx (`<cx:dataLabel idx>`)
197
- * to override label colour per bar sample-2's waterfall paints negative
198
- * values in red while positive values stay black. Null inside the array =
199
- * fall back to the chart-level `dataLabelFontColor`.
287
+ * Embedded zip path of the blip (e.g. "word/media/image1.png"), for the lazy
288
+ * byte-on-demand pipeline. The renderer fetches the bytes via a path-keyed
289
+ * loader ({@link RenderOptions.fetchImage}) instead of inlining base64.
200
290
  */
201
- dataLabelColors?: (string | null)[] | null;
291
+ imagePath: string;
292
+ /** MIME type of the blip at {@link ImageFill.imagePath} (e.g. `image/png`). */
293
+ mimeType: string;
202
294
  /**
203
- * Series-level data-label text colour (`<c:ser><c:dLbls><c:txPr>…solidFill`,
204
- * ECMA-376 §21.2.2.216). Hex without '#'. Stacked-bar charts colour each
205
- * segment's label independently (e.g. white on the dark segment, black on
206
- * the light one), which a single chart-level `dataLabelFontColor` can't
207
- * express. Takes precedence over `dataLabelFontColor`; null = no override.
295
+ * `<a:stretch><a:fillRect>` insets. Absent fills the whole box (or the
296
+ * fill is tiled see {@link ImageFill.tile}).
208
297
  */
209
- labelColor?: string | null;
298
+ fillRect?: FillRect;
210
299
  /**
211
- * Mixed chart: per-series chart type override. Currently only "line" (XLSX
212
- * and PPTX combo charts) is honoured; other values are treated as the
213
- * chart's primary type.
300
+ * `<a:tile>` descriptor. Present only when the blipFill is tiled; mutually
301
+ * exclusive with {@link ImageFill.fillRect}.
214
302
  */
215
- seriesType?: string | null;
303
+ tile?: TileInfo;
304
+ /** `a:blip > a:alphaModFix@amt` as a fraction (0.0–1.0). Absent = opaque. */
305
+ alpha?: number;
216
306
  /**
217
- * Combo chart: this series is plotted against the SECONDARY value axis
218
- * (`ChartModel.secondaryValAxis`) the `<c:valAx>` with `axPos="r"` /
219
- * `<c:crosses val="max">`. When false/absent the series uses the primary
220
- * (left) value-axis scale. PowerPoint's "Revenue vs. gross margin" combo
221
- * (sample-14 slide-8) puts the margin line on a 0–100% secondary axis.
307
+ * ECMA-376 §20.1.8.23 `<a:duotone>` recolour, resolved to its two endpoint
308
+ * colours (through the slide theme). Absent no duotone. When present the
309
+ * renderer maps the blip's luminance ramp between the two colours (core
310
+ * `applyDuotone`) the same recolour a `<p:pic>` duotone applies, wired onto
311
+ * the picture-FILL path (§20.1.8.14) by issue #889.
222
312
  */
223
- useSecondaryAxis?: boolean | null;
313
+ duotone?: Duotone$1;
314
+ }
315
+ interface Shadow {
316
+ color: string;
317
+ alpha: number;
318
+ blur: number;
319
+ dist: number;
320
+ /** degrees clockwise from East */
321
+ dir: number;
322
+ }
323
+ /** ECMA-376 §20.1.8.17 (CT_GlowEffect) — coloured halo with blur radius. */
324
+ interface Glow {
325
+ color: string;
326
+ alpha: number;
327
+ /** Blur radius in EMU. */
328
+ radius: number;
329
+ }
330
+ /** ECMA-376 §20.1.8.31 (CT_SoftEdgesEffect) — feather radius in EMU. */
331
+ interface SoftEdge {
332
+ radius: number;
333
+ }
334
+ /** ECMA-376 §20.1.8.27 (CT_ReflectionEffect) — mirrored copy below the
335
+ * shape with a linear alpha gradient. Carries the spec attributes whose
336
+ * defaults the renderer needs to interpret correctly. */
337
+ interface Reflection {
338
+ blur: number;
339
+ dist: number;
340
+ /** Direction in degrees, clockwise from East. */
341
+ dir: number;
342
+ /** Start alpha (0–1). Default 1.0. */
343
+ stA: number;
344
+ /** Start position along the gradient (0–1). Default 0. */
345
+ stPos: number;
346
+ /** End alpha. Default 0. */
347
+ endA: number;
348
+ /** End position. Default 1.0. */
349
+ endPos: number;
350
+ /** Horizontal scale (1.0 = same width). */
351
+ sx: number;
352
+ /** Vertical scale (-1.0 = full mirror). */
353
+ sy: number;
354
+ }
355
+ interface ArrowEnd {
356
+ /** OOXML type: "none" | "triangle" | "stealth" | "diamond" | "oval" | "arrow" */
357
+ type: string;
358
+ /** Width multiplier: "sm" | "med" | "lg" */
359
+ w: string;
360
+ /** Length multiplier: "sm" | "med" | "lg" */
361
+ len: string;
362
+ }
363
+ interface Stroke {
364
+ color: string;
365
+ /** Width in EMU */
366
+ width: number;
367
+ /** Authored non-solid DrawingML line paint. Solid lines use `color`. */
368
+ fill?: Exclude<Fill, {
369
+ fillType: 'image';
370
+ } | {
371
+ fillType: 'none';
372
+ }>;
373
+ /** OOXML prstDash value: "dash", "dot", "dashDot", "lgDash", "lgDashDot", etc. */
374
+ dashStyle?: string;
375
+ /** Canvas line cap normalized from DrawingML/VML (`flat` → `butt`). */
376
+ lineCap?: CanvasLineCap;
377
+ /** Arrow head at the start of the line */
378
+ headEnd?: ArrowEnd;
379
+ /** Arrow head at the end of the line */
380
+ tailEnd?: ArrowEnd;
224
381
  /**
225
- * Scatter-only X values (as strings). When null the series uses
226
- * `ChartModel.categories` as X.
382
+ * ECMA-376 §20.1.8.42 ST_CompoundLine. "sng" (default) | "dbl" |
383
+ * "thinThick" | "thickThin" | "tri". Absent means single line.
227
384
  */
228
- categories?: string[] | null;
385
+ cmpd?: string;
386
+ }
387
+ interface TextBody$1 {
388
+ /** Vertical anchor: "t" | "ctr" | "b" */
389
+ verticalAnchor: string;
390
+ paragraphs: Paragraph$1[];
391
+ /** Default pt size from lstStyle (overrides renderer default when present) */
392
+ defaultFontSize: number | null;
393
+ /** Inherited bold from layout/master defRPr (null = not set, use false as final default) */
394
+ defaultBold: boolean | null;
395
+ /** Inherited italic from layout/master defRPr (null = not set, use false as final default) */
396
+ defaultItalic: boolean | null;
397
+ /** Text insets in EMU (defaults: lIns=rIns=91440, tIns=bIns=45720) */
398
+ lIns: number;
399
+ rIns: number;
400
+ tIns: number;
401
+ bIns: number;
402
+ /** "square" = wrap, "none" = no wrap */
403
+ wrap: string;
404
+ /** Text direction: "horz" | "vert" | "vert270" | "eaVert" etc. */
405
+ vert: string;
406
+ /** Auto-fit: "sp" = shape grows to fit text, "norm" = font shrinks, "none" = no fit */
407
+ autoFit: string;
229
408
  /**
230
- * Resolved marker visibility for line/scatter series. ECMA-376 §21.2.2.32
231
- * `<c:marker><c:symbol>` defaults to "none" for line charts unless the
232
- * chart-level `<c:marker val="1"/>` or a per-series symbol opts in. When
233
- * undefined/null the renderer uses its own default (visible) so callers
234
- * that don't parse markers (e.g. pptx today) keep their existing behavior.
409
+ * `<a:normAutofit fontScale>` (ECMA-376 §21.1.2.1.3) — PowerPoint's stored,
410
+ * pre-computed font-shrink ratio for `autoFit === "norm"`, as a fraction
411
+ * (e.g. 0.625 for `fontScale="62500"`). Null/absent when PowerPoint stored no
412
+ * scale; the renderer then re-derives one. Applying the stored value matches
413
+ * PowerPoint exactly instead of guessing from our own text metrics.
235
414
  */
236
- showMarker?: boolean | null;
237
- /**
238
- * Excel number-format code for this series' values (ECMA-376 §21.2.2.37,
239
- * `<c:val>/<c:numRef>/<c:formatCode>`). Used to format data labels when the
240
- * chart-level `<c:dLbls><c:numFmt>` is not set. null = no series-level code.
415
+ fontScale?: number | null;
416
+ /** `<a:normAutofit lnSpcReduction>` — stored line-spacing reduction fraction
417
+ * (e.g. 0.20 for `lnSpcReduction="20000"`). Null/absent when not stored. */
418
+ lnSpcReduction?: number | null;
419
+ /**
420
+ * `<a:bodyPr numCol>` (ECMA-376 §20.1.10.34) — number of text columns inside
421
+ * the shape. Defaults to 1; values > 1 cause the renderer to flow paragraphs
422
+ * across N columns left-to-right, top-to-bottom.
241
423
  */
242
- valFormatCode?: string | null;
424
+ numCol?: number;
425
+ /** `<a:bodyPr spcCol>` — gap between columns in EMU. Default 0. */
426
+ spcCol?: number;
427
+ }
428
+ type SpaceLine = {
429
+ type: 'pct';
430
+ val: number;
431
+ } | {
432
+ type: 'pts';
433
+ val: number;
434
+ };
435
+ /**
436
+ * A paragraph's bullet marker. For `char`, the marker size is EITHER `sizePct`
437
+ * (a percentage of the run size — ECMA-376 §21.1.2.4.9 `<a:buSzPct>`) OR `sizePts`
438
+ * (an absolute size in points — §21.1.2.4.10 `<a:buSzPts>`), never both: they are
439
+ * the one `EG_TextBulletSize` xsd:choice. `sizePts` is optional (absent when no
440
+ * `<a:buSzPts>` was declared); when present it takes precedence over `sizePct`.
441
+ */
442
+ type Bullet$1 = {
443
+ type: 'none';
444
+ } | {
445
+ type: 'inherit';
446
+ } | {
447
+ type: 'char';
448
+ char: string;
449
+ color: string | null;
450
+ sizePct: number | null;
451
+ sizePts?: number;
452
+ fontFamily: string | null;
453
+ } | {
454
+ type: 'autoNum';
455
+ numType: string;
456
+ startAt: number | null;
457
+ color: string | null;
458
+ };
459
+ interface TabStop$1 {
460
+ /** Position in EMU from the LEADING text-inset edge of the text area —
461
+ * logical, not physical (ECMA-376 §21.1.2.1): the left edge (after lIns)
462
+ * in an LTR paragraph, the right edge (before rIns) in an RTL
463
+ * (`<a:pPr rtl="1">`) paragraph. */
464
+ pos: number;
465
+ /** Alignment: "l" | "r" | "ctr" | "dec" */
466
+ algn: string;
467
+ }
468
+ interface Paragraph$1 {
469
+ /** Alignment: "l" | "ctr" | "r" | "just" */
470
+ alignment: string;
471
+ /** Left margin in EMU */
472
+ marL: number;
473
+ /** Right margin in EMU */
474
+ marR: number;
475
+ /** First-line indent in EMU (negative = hanging indent) */
476
+ indent: number;
477
+ spaceBefore: number | null;
478
+ spaceAfter: number | null;
479
+ spaceLine: SpaceLine | null;
480
+ /** List nesting level (0–8) */
481
+ lvl: number;
482
+ bullet: Bullet$1;
483
+ defFontSize: number | null;
484
+ defColor: string | null;
485
+ defBold: boolean | null;
486
+ defItalic: boolean | null;
487
+ defFontFamily: string | null;
488
+ /** Tab stops from pPr > tabLst */
489
+ tabStops: TabStop$1[];
243
490
  /**
244
- * `<c:marker><c:symbol val>` (ECMA-376 §21.2.2.32) — point marker shape.
245
- * One of "circle"|"square"|"diamond"|"triangle"|"x"|"plus"|"star"|
246
- * "dot"|"dash"|"picture"|"none". null = renderer default (circle when
247
- * showMarker is true).
491
+ * `<a:pPr rtl="1">` — right-to-left paragraph (ECMA-376 §21.1.2.2.7).
492
+ * When true and no explicit `algn`, the parser-side default flips from
493
+ * "l" to "r"; renderers can also use this flag to flow runs RTL.
248
494
  */
249
- markerSymbol?: string | null;
495
+ rtl?: boolean;
496
+ runs: TextRun[];
497
+ }
498
+ type TextRun = TextRunData | LineBreak | EquationRun;
499
+ /**
500
+ * An OMML equation embedded in a paragraph (ECMA-376 §22.1). Parsed into the
501
+ * shared math AST and rendered by `@silurus/ooxml-core`'s math engine.
502
+ * PowerPoint stores these as `a14:m` inside `mc:AlternateContent`.
503
+ */
504
+ interface EquationRun {
505
+ type: 'math';
506
+ /** Parsed OMML node list. */
507
+ nodes: MathNode[];
508
+ /** True for block (`m:oMathPara`) math, false for inline (`m:oMath`). */
509
+ display: boolean;
510
+ /** Paragraph default run size in pt, if declared; absent → renderer inherits. */
511
+ fontSize?: number | null;
512
+ /** Equation colour (hex, no '#') from the math run's rPr; absent → inherit. */
513
+ color?: string | null;
514
+ }
515
+ interface TextRunData {
516
+ type: 'text';
517
+ text: string;
518
+ /** null = not set, inherit from paragraph/body defaults */
519
+ bold: boolean | null;
520
+ /** null = not set, inherit from paragraph/body defaults */
521
+ italic: boolean | null;
522
+ underline: boolean;
250
523
  /**
251
- * `<c:marker><c:size val>` (ECMA-376 §21.2.2.34) marker side length in
252
- * points. null = renderer default (~5 pt).
524
+ * Specific underline style when not the default single line. Values come
525
+ * from ECMA-376 §21.1.2.3.16 (ST_TextUnderlineType): "dbl", "heavy",
526
+ * "dotted", "dottedHeavy", "dash", "dashHeavy", "dashLong",
527
+ * "dashLongHeavy", "dotDash", "dotDashHeavy", "dotDotDash",
528
+ * "dotDotDashHeavy", "wavy", "wavyHeavy", "wavyDbl". Absent means either
529
+ * no underline (when `underline` is false) or the default single line.
253
530
  */
254
- markerSize?: number | null;
255
- /** `<c:marker><c:spPr><a:solidFill>` resolved hex (no `#`). */
256
- markerFill?: string | null;
257
- /** `<c:marker><c:spPr><a:ln><a:solidFill>` resolved hex (no `#`). */
258
- markerLine?: string | null;
531
+ underlineStyle?: string;
259
532
  /**
260
- * Per-data-point overrides (ECMA-376 §21.2.2.39 `<c:dPt>`). Keyed by point
261
- * index. Any unset field falls back to the series-level value.
533
+ * Underline-only colour from rPr > uFill (ECMA-376 §21.1.2.3.20). Absent
534
+ * means the underline follows the text colour (uFillTx default).
262
535
  */
263
- dataPointOverrides?: ChartDataPointOverride[] | null;
536
+ underlineColor?: string;
537
+ /** True when rPr strike is sngStrike or dblStrike. */
538
+ strikethrough: boolean;
264
539
  /**
265
- * Per-data-point custom labels (ECMA-376 §21.2.2.45 `<c:dLbl idx>`).
266
- * `text` is the resolved plain string — `<a:fld type="CELLRANGE">`
267
- * placeholders are already substituted at parse time. An empty string
268
- * means the point's label was deleted with `<c:delete val="1"/>` and
269
- * the renderer should skip it.
540
+ * True only when rPr strike = "dblStrike". Lets the renderer draw two parallel
541
+ * lines instead of one. ECMA-376 §21.1.2.3.10 (ST_TextStrikeType).
270
542
  */
271
- dataLabelOverrides?: ChartDataLabelOverride[] | null;
543
+ strikeDouble?: boolean;
544
+ /** Font size in points */
545
+ fontSize: number | null;
546
+ color: string | null;
547
+ fontFamily: string | null;
272
548
  /**
273
- * Series-level `<c:dLbls>` block (showVal / showSerName / position).
274
- * Applied to every point lacking its own `<c:dLbl>` override.
549
+ * East Asian font family from rPr > a:ea (ECMA-376 §21.1.2.3.7),
550
+ * resolved through the theme. Renderer uses this for CJK glyphs when
551
+ * present; absent means CJK falls back to fontFamily.
275
552
  */
276
- seriesDataLabels?: ChartSeriesDataLabels | null;
553
+ fontFamilyEa?: string;
277
554
  /**
278
- * `<c:errBars>` per-series error bars (ECMA-376 §21.2.2.20). Up to two
279
- * (one per direction). Plus / minus deltas are absolute per-point values
280
- * regardless of `errValType`.
555
+ * Symbol font family from rPr > a:sym (ECMA-376 §21.1.2.3.10), resolved
556
+ * through the theme. PowerPoint stores symbol-font glyphs as Private-Use
557
+ * codepoints U+F020–U+F0FF; the renderer uses this font to resolve them.
558
+ * Absent means no symbol font was declared.
281
559
  */
282
- errBars?: ChartErrBars[] | null;
560
+ fontFamilySym?: string;
561
+ /** Baseline shift in thousandths of a point. Positive = superscript, negative = subscript. */
562
+ baseline?: number;
283
563
  /**
284
- * `<c:bubbleSize>` per-point sizes for bubble charts (ECMA-376 §21.2.2.4).
285
- * Drives marker radius renderer treats the values as areas (radius
286
- * scales by sqrt) so visual area is proportional to value, matching
287
- * Excel. null / empty array = uniform marker size. Ignored for non-bubble
288
- * series.
564
+ * Capitalisation transform ECMA-376 §21.1.2.3.13 (ST_TextCapsType).
565
+ * 'all' renders text in upper case; 'small' uses small caps (rendered as
566
+ * upper case at ~80% size when no smcp font feature is available).
567
+ * 'none' or omitted leaves the text unchanged.
289
568
  */
290
- bubbleSizes?: (number | null)[] | null;
569
+ caps?: 'none' | 'small' | 'all';
291
570
  /**
292
- * `<c:ser><c:smooth val>` (ECMA-376 §21.2.2.194) — line/area series flag
293
- * requesting a smoothed (spline) curve through the points instead of straight
294
- * segments. Only consulted for the line and area families (scatter carries its
295
- * smoothing in `ChartModel.scatterStyle`). null/undefined/false = straight
296
- * polyline (the default; byte-stable for series that never set it).
571
+ * Inter-character spacing in 100ths of a point — ECMA-376 §21.1.2.3.5
572
+ * (rPr @spc). Positive values add space, negative values tighten.
297
573
  */
298
- smooth?: boolean | null;
574
+ letterSpacing?: number;
575
+ /** Set for OOXML field runs (e.g. "slidenum"). When set, renderer replaces text with field value. */
576
+ fieldType?: string;
299
577
  /**
300
- * `<c:ser><c:trendline>` per-series trendlines (ECMA-376 §21.2.2.211,
301
- * `CT_Trendline`). A series can carry several (e.g. a linear fit + a moving
302
- * average). null/undefined/empty = no trendline (the default; byte-stable for
303
- * series that never declare one).
578
+ * Hyperlink target resolved from rPr > a:hlinkClick @r:id via the slide's _rels.
579
+ * For an external link this is the URL; for an internal slide jump it is the
580
+ * resolved internal part name (e.g. "../slides/slide3.xml"). Undefined for runs
581
+ * without a hyperlink. ECMA-376 §21.1.2.3.5 (CT_Hyperlink).
304
582
  */
305
- trendLines?: ChartTrendline[] | null;
583
+ hyperlink?: string;
306
584
  /**
307
- * `<c:ser><c:spPr><a:ln><a:noFill/>` (ECMA-376 §21.2.2.198 CT_ShapeProperties
308
- * DrawingML §20.1.2.2.24 CT_LineProperties). true when the series connecting
309
- * line is explicitly turned OFF. For a scatter/line series this OVERRIDES the
310
- * chart-group `<c:scatterStyle>` (§21.2.2.42) / line default — Excel and
311
- * PowerPoint draw markers only (no connecting line) even when the group style
312
- * is `lineMarker`. null/undefined = no explicit line-off, so the group default
313
- * governs (byte-stable for series that carry a paintable line).
585
+ * Raw `<a:hlinkClick @action>` string (e.g. "ppaction://hlinksldjump") when
586
+ * present its presence marks {@link hyperlink} as an INTERNAL PowerPoint
587
+ * action (slide jump / first / last …) rather than an external URL. Undefined
588
+ * when the hlinkClick has no @action. ECMA-376 §21.1.2.3.5. (IX1)
314
589
  */
315
- lineHidden?: boolean | null;
316
- }
317
- /**
318
- * `<c:ser><c:trendline>` (ECMA-376 §21.2.2.211). A regression/smoothing curve
319
- * fitted to the series' data points.
320
- */
321
- interface ChartTrendline {
590
+ hyperlinkAction?: string;
322
591
  /**
323
- * `<c:trendlineType val>` (§21.2.2.213, `ST_TrendlineType` §21.2.3.50):
324
- * "linear" | "exp" | "log" | "power" | "poly" | "movingAvg". The renderer
325
- * currently draws "linear" (least squares) and "movingAvg"; other types parse
326
- * but are not yet plotted (tracked as a follow-up).
592
+ * Run-level drop shadow on glyphs (`<a:rPr><a:effectLst><a:outerShdw>`),
593
+ * ECMA-376 §20.1.8.45. Independent of the shape-level shadow on `spPr`.
594
+ * Absent means no run-level shadow.
327
595
  */
328
- trendlineType: string;
329
- /** `<c:order val>` — polynomial order (`poly`, default 2). */
330
- order?: number | null;
331
- /** `<c:period val>` — moving-average window (`movingAvg`, default 2). */
332
- period?: number | null;
333
- /** `<c:forward val>` — units to extend the line past the last point. */
334
- forward?: number | null;
335
- /** `<c:backward val>` — units to extend the line before the first point. */
336
- backward?: number | null;
337
- /** `<c:intercept val>` — forced y-intercept (linear/exp). null = free fit. */
338
- intercept?: number | null;
339
- /** `<c:dispRSqr val="1">` — show the R² value (label; not yet rendered). */
340
- dispRSqr?: boolean | null;
341
- /** `<c:dispEq val="1">` — show the fit equation (label; not yet rendered). */
342
- dispEq?: boolean | null;
343
- /** `<c:spPr><a:ln><a:solidFill>` trendline color (hex without '#'). null =
344
- * inherit the series color. */
345
- lineColor?: string | null;
346
- /** `<c:spPr><a:ln w>` trendline width in EMU. */
347
- lineWidthEmu?: number | null;
348
- }
349
- interface ChartDataPointOverride {
350
- idx: number;
351
- /** Resolved fill hex (no `#`). */
352
- color?: string;
353
- markerSymbol?: string;
354
- markerSize?: number;
355
- markerFill?: string;
356
- markerLine?: string;
596
+ shadow?: Shadow;
357
597
  /**
358
- * `<c:dPt><c:explosion val>` (ECMA-376 §21.2.2.61) — the amount this
598
+ * Run-level glyph outline (`<a:rPr><a:ln w="..">`), ECMA-376 §20.1.2.2.24
599
+ * (CT_TextOutlineEffect). Renderer strokes each glyph with the given
600
+ * width / colour in addition to the normal fill. Absent means glyphs are
601
+ * fill-only.
602
+ */
603
+ outline?: TextOutline;
604
+ /**
605
+ * Run-level text highlight / marker colour (`<a:rPr><a:highlight>`),
606
+ * ECMA-376 §21.1.2.3.4. In DrawingML this is a full CT_Color (any
607
+ * srgbClr / schemeClr / sysClr / prstClr + transforms), unlike
608
+ * WordprocessingML's fixed 16-name highlight enum — so the parser already
609
+ * resolves it through the theme/clrMap. The value is a hex string without
610
+ * `#` (6-char opaque, or 8-char RRGGBBAA when an alpha transform applies);
611
+ * the renderer paints a background rectangle behind the run's glyphs.
612
+ * Absent means no highlight.
613
+ */
614
+ highlight?: string;
615
+ }
616
+ /** Run-level glyph outline. Width is in OOXML EMU (12700 EMU = 1 pt). */
617
+ interface TextOutline {
618
+ width: number;
619
+ /** Hex without '#'. Absent = inherit from text fill colour. */
620
+ color?: string;
621
+ }
622
+ interface LineBreak {
623
+ type: 'break';
624
+ }
625
+ interface RenderOptions {
626
+ width?: number;
627
+ defaultTextColor?: string | null;
628
+ dpr?: number;
629
+ majorFont?: string | null;
630
+ minorFont?: string | null;
631
+ /** Theme hyperlink colour (hex 6 chars). Used to colour hyperlink runs without an explicit colour. */
632
+ hlinkColor?: string | null;
633
+ /**
634
+ * Lazily resolve an archive-internal asset (by zip path) to a Blob. The
635
+ * renderer uses this to fetch posters and other large embedded assets on
636
+ * demand, keeping the parse output free of inlined base64.
637
+ */
638
+ fetchMedia?: (path: string) => Promise<Blob>;
639
+ /**
640
+ * Lazily resolve an embedded image (by zip path + MIME) to a Blob. Twin of
641
+ * {@link RenderOptions.fetchMedia} for pictures and blip fills: the renderer
642
+ * fetches raster/SVG bytes on demand and decodes them (`createImageBitmap` /
643
+ * path-keyed `<img>`), so the parse output carries only paths, never base64.
644
+ */
645
+ fetchImage?: (path: string, mimeType: string) => Promise<Blob>;
646
+ /**
647
+ * When true, renderMedia draws only the poster frame — play/pause badges
648
+ * and progress bars are left to the caller. Set by the pptx presentSlide
649
+ * API so its interactive handle can own all control chrome without
650
+ * the static renderer drawing a duplicate play badge.
651
+ */
652
+ skipMediaControls?: boolean;
653
+ }
654
+ //#endregion
655
+ //#region packages/core/src/types/chart.d.ts
656
+ interface ChartSeries {
657
+ name: string;
658
+ /** Hex without '#'. null = fall back to palette. */
659
+ color: string | null;
660
+ /** Numeric values; null = missing data point. */
661
+ values: (number | null)[];
662
+ /**
663
+ * Per-data-point colors (pie / doughnut). Hex without '#'. null inside the
664
+ * array = use palette for that slice. Omit entirely for non-pie series.
665
+ */
666
+ dataPointColors?: (string | null)[] | null;
667
+ /**
668
+ * Per-data-point data-label text colors. Used by chartEx (`<cx:dataLabel idx>`)
669
+ * to override label colour per bar — sample-2's waterfall paints negative
670
+ * △ values in red while positive values stay black. Null inside the array =
671
+ * fall back to the chart-level `dataLabelFontColor`.
672
+ */
673
+ dataLabelColors?: (string | null)[] | null;
674
+ /**
675
+ * Series-level data-label text colour (`<c:ser><c:dLbls><c:txPr>…solidFill`,
676
+ * ECMA-376 §21.2.2.216). Hex without '#'. Stacked-bar charts colour each
677
+ * segment's label independently (e.g. white on the dark segment, black on
678
+ * the light one), which a single chart-level `dataLabelFontColor` can't
679
+ * express. Takes precedence over `dataLabelFontColor`; null = no override.
680
+ */
681
+ labelColor?: string | null;
682
+ /**
683
+ * Mixed chart: per-series chart type override. Currently only "line" (XLSX
684
+ * and PPTX combo charts) is honoured; other values are treated as the
685
+ * chart's primary type.
686
+ */
687
+ seriesType?: string | null;
688
+ /**
689
+ * Combo chart: this series is plotted against the SECONDARY value axis
690
+ * (`ChartModel.secondaryValAxis`) — the `<c:valAx>` with `axPos="r"` /
691
+ * `<c:crosses val="max">`. When false/absent the series uses the primary
692
+ * (left) value-axis scale. PowerPoint's "Revenue vs. gross margin" combo
693
+ * (sample-14 slide-8) puts the margin line on a 0–100% secondary axis.
694
+ */
695
+ useSecondaryAxis?: boolean | null;
696
+ /**
697
+ * Scatter-only X values (as strings). When null the series uses
698
+ * `ChartModel.categories` as X.
699
+ */
700
+ categories?: string[] | null;
701
+ /**
702
+ * Resolved marker visibility for line/scatter series. ECMA-376 §21.2.2.32
703
+ * `<c:marker><c:symbol>` defaults to "none" for line charts unless the
704
+ * chart-level `<c:marker val="1"/>` or a per-series symbol opts in. When
705
+ * undefined/null the renderer uses its own default (visible) so callers
706
+ * that don't parse markers (e.g. pptx today) keep their existing behavior.
707
+ */
708
+ showMarker?: boolean | null;
709
+ /**
710
+ * Excel number-format code for this series' values (ECMA-376 §21.2.2.37,
711
+ * `<c:val>/<c:numRef>/<c:formatCode>`). Used to format data labels when the
712
+ * chart-level `<c:dLbls><c:numFmt>` is not set. null = no series-level code.
713
+ */
714
+ valFormatCode?: string | null;
715
+ /**
716
+ * `<c:marker><c:symbol val>` (ECMA-376 §21.2.2.32) — point marker shape.
717
+ * One of "circle"|"square"|"diamond"|"triangle"|"x"|"plus"|"star"|
718
+ * "dot"|"dash"|"picture"|"none". null = renderer default (circle when
719
+ * showMarker is true).
720
+ */
721
+ markerSymbol?: string | null;
722
+ /**
723
+ * `<c:marker><c:size val>` (ECMA-376 §21.2.2.34) — marker side length in
724
+ * points. null = renderer default (~5 pt).
725
+ */
726
+ markerSize?: number | null;
727
+ /** `<c:marker><c:spPr><a:solidFill>` resolved hex (no `#`). */
728
+ markerFill?: string | null;
729
+ /** `<c:marker><c:spPr><a:ln><a:solidFill>` resolved hex (no `#`). */
730
+ markerLine?: string | null;
731
+ /**
732
+ * Per-data-point overrides (ECMA-376 §21.2.2.39 `<c:dPt>`). Keyed by point
733
+ * index. Any unset field falls back to the series-level value.
734
+ */
735
+ dataPointOverrides?: ChartDataPointOverride[] | null;
736
+ /**
737
+ * Per-data-point custom labels (ECMA-376 §21.2.2.45 `<c:dLbl idx>`).
738
+ * `text` is the resolved plain string — `<a:fld type="CELLRANGE">`
739
+ * placeholders are already substituted at parse time. An empty string
740
+ * means the point's label was deleted with `<c:delete val="1"/>` and
741
+ * the renderer should skip it.
742
+ */
743
+ dataLabelOverrides?: ChartDataLabelOverride[] | null;
744
+ /**
745
+ * Series-level `<c:dLbls>` block (showVal / showSerName / position).
746
+ * Applied to every point lacking its own `<c:dLbl>` override.
747
+ */
748
+ seriesDataLabels?: ChartSeriesDataLabels | null;
749
+ /**
750
+ * `<c:errBars>` per-series error bars (ECMA-376 §21.2.2.20). Up to two
751
+ * (one per direction). Plus / minus deltas are absolute per-point values
752
+ * regardless of `errValType`.
753
+ */
754
+ errBars?: ChartErrBars[] | null;
755
+ /**
756
+ * `<c:bubbleSize>` per-point sizes for bubble charts (ECMA-376 §21.2.2.4).
757
+ * Drives marker radius — renderer treats the values as areas (radius
758
+ * scales by sqrt) so visual area is proportional to value, matching
759
+ * Excel. null / empty array = uniform marker size. Ignored for non-bubble
760
+ * series.
761
+ */
762
+ bubbleSizes?: (number | null)[] | null;
763
+ /**
764
+ * `<c:ser><c:smooth val>` (ECMA-376 §21.2.2.194) — line/area series flag
765
+ * requesting a smoothed (spline) curve through the points instead of straight
766
+ * segments. Only consulted for the line and area families (scatter carries its
767
+ * smoothing in `ChartModel.scatterStyle`). null/undefined/false = straight
768
+ * polyline (the default; byte-stable for series that never set it).
769
+ */
770
+ smooth?: boolean | null;
771
+ /**
772
+ * `<c:ser><c:trendline>` per-series trendlines (ECMA-376 §21.2.2.211,
773
+ * `CT_Trendline`). A series can carry several (e.g. a linear fit + a moving
774
+ * average). null/undefined/empty = no trendline (the default; byte-stable for
775
+ * series that never declare one).
776
+ */
777
+ trendLines?: ChartTrendline[] | null;
778
+ /**
779
+ * `<c:ser><c:spPr><a:ln><a:noFill/>` (ECMA-376 §21.2.2.198 CT_ShapeProperties
780
+ * → DrawingML §20.1.2.2.24 CT_LineProperties). true when the series connecting
781
+ * line is explicitly turned OFF. For a scatter/line series this OVERRIDES the
782
+ * chart-group `<c:scatterStyle>` (§21.2.2.42) / line default — Excel and
783
+ * PowerPoint draw markers only (no connecting line) even when the group style
784
+ * is `lineMarker`. null/undefined = no explicit line-off, so the group default
785
+ * governs (byte-stable for series that carry a paintable line).
786
+ */
787
+ lineHidden?: boolean | null;
788
+ }
789
+ /**
790
+ * `<c:ser><c:trendline>` (ECMA-376 §21.2.2.211). A regression/smoothing curve
791
+ * fitted to the series' data points.
792
+ */
793
+ interface ChartTrendline {
794
+ /**
795
+ * `<c:trendlineType val>` (§21.2.2.213, `ST_TrendlineType` §21.2.3.50):
796
+ * "linear" | "exp" | "log" | "power" | "poly" | "movingAvg". The renderer
797
+ * currently draws "linear" (least squares) and "movingAvg"; other types parse
798
+ * but are not yet plotted (tracked as a follow-up).
799
+ */
800
+ trendlineType: string;
801
+ /** `<c:order val>` — polynomial order (`poly`, default 2). */
802
+ order?: number | null;
803
+ /** `<c:period val>` — moving-average window (`movingAvg`, default 2). */
804
+ period?: number | null;
805
+ /** `<c:forward val>` — units to extend the line past the last point. */
806
+ forward?: number | null;
807
+ /** `<c:backward val>` — units to extend the line before the first point. */
808
+ backward?: number | null;
809
+ /** `<c:intercept val>` — forced y-intercept (linear/exp). null = free fit. */
810
+ intercept?: number | null;
811
+ /** `<c:dispRSqr val="1">` — show the R² value (label; not yet rendered). */
812
+ dispRSqr?: boolean | null;
813
+ /** `<c:dispEq val="1">` — show the fit equation (label; not yet rendered). */
814
+ dispEq?: boolean | null;
815
+ /** `<c:spPr><a:ln><a:solidFill>` trendline color (hex without '#'). null =
816
+ * inherit the series color. */
817
+ lineColor?: string | null;
818
+ /** `<c:spPr><a:ln w>` trendline width in EMU. */
819
+ lineWidthEmu?: number | null;
820
+ }
821
+ interface ChartDataPointOverride {
822
+ idx: number;
823
+ /** Resolved fill hex (no `#`). */
824
+ color?: string;
825
+ markerSymbol?: string;
826
+ markerSize?: number;
827
+ markerFill?: string;
828
+ markerLine?: string;
829
+ /**
830
+ * `<c:dPt><c:explosion val>` (ECMA-376 §21.2.2.61) — the amount this
359
831
  * pie/doughnut slice is moved out from the center. The schema type is
360
832
  * `CT_UnsignedInt` (unbounded `xsd:unsignedInt`); the spec text only says
361
833
  * "the amount the data point shall be moved from the center of the pie"
@@ -1314,7 +1786,7 @@ type HyperlinkTarget = {
1314
1786
  */
1315
1787
  declare function openExternalHyperlink(url: string, allowed?: readonly string[], win?: Pick<Window, 'open'> | undefined): boolean;
1316
1788
  //#endregion
1317
- //#region dist/.types-work/find-highlight-gMP3FAnW.d.ts
1789
+ //#region dist/.types-work/find-highlight-CnpZn3yb.d.ts
1318
1790
  //#region packages/core/src/autoResize.d.ts
1319
1791
  interface AutoResizeOptions {
1320
1792
  /**
@@ -1501,7 +1973,7 @@ interface FindHighlightColors {
1501
1973
  active?: string;
1502
1974
  }
1503
1975
  //#endregion
1504
- //#region dist/.types-work/renderer-CU7Ays06.d.ts
1976
+ //#region dist/.types-work/renderer-BwmBZ5GX.d.ts
1505
1977
  //#region packages/docx/src/types.d.ts
1506
1978
  interface DocxDocumentModel {
1507
1979
  section: SectionProps;
@@ -1889,7 +2361,7 @@ interface DocParagraph {
1889
2361
  spaceAfter: number;
1890
2362
  lineSpacing: LineSpacing | null;
1891
2363
  numbering: NumberingInfo | null;
1892
- tabStops: TabStop$1[];
2364
+ tabStops: TabStop[];
1893
2365
  runs: DocRun[];
1894
2366
  /**
1895
2367
  * ECMA-376 §17.13.6.2 `<w:bookmarkStart w:name>` — names of the bookmarks that
@@ -1937,10 +2409,9 @@ interface DocParagraph {
1937
2409
  defaultFontFamilyEastAsia?: string | null;
1938
2410
  /** ECMA-376 §17.3.1.29 — the paragraph mark run's resolved `w:color` (direct
1939
2411
  * pPr/rPr → pStyle chain → docDefaults; hex 6 without `#`, lowercased; an
1940
- * explicit `auto` surfaces as absent, §17.3.2.6). The numbering level rPr
1941
- * (§17.9.24) layers over the mark's run properties, so the renderer uses
1942
- * this as the marker-color fallback when {@link NumberingInfo.color} is
1943
- * absent. */
2412
+ * explicit `auto` surfaces as absent, §17.3.2.6). Compatibility rule
2413
+ * `word-numbering-marker-paragraph-mark-fallback` uses this when §17.9.24
2414
+ * leaves {@link NumberingInfo.color} absent. */
1944
2415
  paragraphMarkColor?: string | null;
1945
2416
  /**
1946
2417
  * ECMA-376 §17.3.1.6 `<w:bidi>` — right-to-left paragraph. `true` = RTL,
@@ -2032,7 +2503,7 @@ interface DocxRunBorder {
2032
2503
  /** pt spacing between the border and the run text (w:space) */
2033
2504
  space: number;
2034
2505
  }
2035
- interface TabStop$1 {
2506
+ interface TabStop {
2036
2507
  /** tab stop position in pt (from the left of paragraph content area) */
2037
2508
  pos: number;
2038
2509
  /** ECMA-376 ST_TabJc. `num` is the list tab between a numbering marker and
@@ -2066,7 +2537,8 @@ interface NumberingInfo {
2066
2537
  * it (the renderer treats absent as "left"). */
2067
2538
  jc?: string;
2068
2539
  /** ECMA-376 §17.3.2.26 ascii axis for the marker glyph, resolved through the
2069
- * level's `rPr` (§17.9.6) merged over the paragraph's run formatting. The
2540
+ * level's `rPr` (§17.9.24) plus compatibility rule
2541
+ * `word-numbering-marker-paragraph-mark-fallback`. The
2070
2542
  * renderer draws Latin marker chars (e.g. a decimal "1") with this family, so
2071
2543
  * a heading whose ascii=Times renders its auto-number in Times (serif) even
2072
2544
  * when eastAsia=Gothic. Absent ⇒ the renderer falls back to its default. */
@@ -2079,9 +2551,11 @@ interface NumberingInfo {
2079
2551
  /** ECMA-376 §17.9.24 — the numbering level rPr's `w:color` (hex 6 without
2080
2552
  * `#`, lowercased). Colors the marker glyph only, never the paragraph's
2081
2553
  * runs. Absent ⇒ the renderer falls back to
2082
- * {@link DocParagraph.paragraphMarkColor} (§17.3.1.29 the level rPr layers
2083
- * over the paragraph mark's run properties) and finally to its
2084
- * default ink. An explicit `w:val="auto"` is absent here + {@link colorAuto}. */
2554
+ * {@link DocParagraph.paragraphMarkColor} under compatibility rule
2555
+ * `word-numbering-marker-paragraph-mark-fallback`, then to its default ink.
2556
+ * §17.3.1.29 defines the
2557
+ * mark properties but not that fallback. An explicit `w:val="auto"` is
2558
+ * absent here + {@link colorAuto}. */
2085
2559
  color?: string | null;
2086
2560
  /** ECMA-376 §17.3.2.6 / ST_HexColorAuto (§17.18.39) — true when the level
2087
2561
  * rPr carries an EXPLICIT `w:color w:val="auto"`. Auto names no concrete
@@ -2193,7 +2667,7 @@ interface PTabRun {
2193
2667
  /** Resolved run font size (pt) — matches the surrounding text's leader/gap. */
2194
2668
  fontSize: number;
2195
2669
  }
2196
- type PathCmd$2 = {
2670
+ type PathCmd$1 = {
2197
2671
  cmd: 'moveTo';
2198
2672
  x: number;
2199
2673
  y: number;
@@ -2232,6 +2706,9 @@ interface AnchorHostMetrics {
2232
2706
  italic?: boolean;
2233
2707
  }
2234
2708
  interface ShapeRun {
2709
+ /** ECMA-376 §20.4.2.8 — true when the shape is hosted by `<wp:inline>` and
2710
+ * therefore advances the paragraph pen like an inline drawing object. */
2711
+ inline?: boolean;
2235
2712
  widthPt: number;
2236
2713
  heightPt: number;
2237
2714
  /** X offset in pt */
@@ -2277,7 +2754,7 @@ interface ShapeRun {
2277
2754
  zOrder: number;
2278
2755
  /** Normalized [0,1] custom-geometry sub-paths. Empty when `presetGeometry`
2279
2756
  * is set; the renderer chooses between buildCustomPath and buildShapePath. */
2280
- subpaths: PathCmd$2[][];
2757
+ subpaths: PathCmd$1[][];
2281
2758
  /** OOXML <a:prstGeom prst> name (e.g. "rect", "ellipse", "rtTriangle").
2282
2759
  * When set the renderer calls core's buildShapePath with `adjValues`. */
2283
2760
  presetGeometry?: string | null;
@@ -2402,6 +2879,13 @@ interface ShapeText$1 {
2402
2879
  text: string;
2403
2880
  fontSizePt: number;
2404
2881
  color?: string | null;
2882
+ /** Resolved paragraph-mark run color used by compatibility rule
2883
+ * `word-numbering-marker-paragraph-mark-fallback` when the numbering level
2884
+ * has no explicit color; kept separate from the first content run's
2885
+ * compatibility-level {@link ShapeText.color}. `null` means the parser
2886
+ * resolved automatic/default ink; `undefined` preserves the legacy contract
2887
+ * for hand-built values that did not provide paragraph-mark facts. */
2888
+ paragraphMarkColor?: string | null;
2405
2889
  fontFamily?: string | null;
2406
2890
  bold?: boolean;
2407
2891
  italic?: boolean;
@@ -2443,7 +2927,7 @@ interface ShapeText$1 {
2443
2927
  * Absent/empty ⇒ only the automatic default-tab grid applies. The renderer
2444
2928
  * feeds these to the SAME line engine the body uses so a `\t` inside a text box
2445
2929
  * advances to its stop (the old shape wrapper dropped tabs entirely). */
2446
- tabStops?: TabStop$1[];
2930
+ tabStops?: TabStop[];
2447
2931
  /** ECMA-376 §17.3.1.6 `<w:bidi>` — right-to-left text-box paragraph, resolved
2448
2932
  * through the style chain like {@link DocParagraph.bidi}. `true` = RTL,
2449
2933
  * `false` = explicitly LTR, absent = unspecified. Consumed as the paragraph
@@ -2481,23 +2965,41 @@ type ShapeFill = {
2481
2965
  color: string;
2482
2966
  } | {
2483
2967
  fillType: 'gradient';
2484
- stops: GradientStop$1[];
2968
+ stops: GradientStop[];
2485
2969
  angle: number;
2486
2970
  gradType: string;
2487
- };
2488
- interface GradientStop$1 {
2489
- /** 0.0–1.0 */
2490
- position: number;
2491
- /** hex 6-char */
2492
- color: string;
2493
- }
2494
- interface FieldRun {
2495
- /** "page" | "numPages" | "other" */
2496
- fieldType: string;
2497
- instruction: string;
2498
- fallbackText: string;
2499
- bold: boolean;
2500
- italic: boolean;
2971
+ } | {
2972
+ /** ECMA-376 §20.1.8.14 picture fill on a DrawingML shape. */
2973
+ fillType: 'image';
2974
+ imagePath: string;
2975
+ mimeType: string;
2976
+ /** Microsoft 2016 SVG original retained beside the raster fallback. */
2977
+ svgImagePath?: string;
2978
+ /** ECMA-376 §20.1.8.55 source-image crop. */
2979
+ srcRect?: {
2980
+ l: number;
2981
+ t: number;
2982
+ r: number;
2983
+ b: number;
2984
+ };
2985
+ fillRect?: FillRect;
2986
+ tile?: TileInfo;
2987
+ alpha?: number;
2988
+ duotone?: Duotone$1;
2989
+ };
2990
+ interface GradientStop {
2991
+ /** 0.0–1.0 */
2992
+ position: number;
2993
+ /** hex 6-char */
2994
+ color: string;
2995
+ }
2996
+ interface FieldRun {
2997
+ /** "page" | "numPages" | "other" */
2998
+ fieldType: string;
2999
+ instruction: string;
3000
+ fallbackText: string;
3001
+ bold: boolean;
3002
+ italic: boolean;
2501
3003
  underline: boolean;
2502
3004
  strikethrough: boolean;
2503
3005
  fontSize: number;
@@ -2624,7 +3126,8 @@ interface DocxTextRun {
2624
3126
  * width, not the gap between glyphs. Absent ⇒ 100%. */
2625
3127
  charScale?: number;
2626
3128
  /** ECMA-376 §17.3.2.24 `<w:position w:val>` — baseline raise (positive) /
2627
- * lower (negative) in POINTS, without changing the font size or line box.
3129
+ * lower (negative) in POINTS, without changing the font size. The shifted
3130
+ * ink still participates in the surrounding line's visible extent.
2628
3131
  * Absent ⇒ no shift. */
2629
3132
  position?: number;
2630
3133
  /** ECMA-376 §17.3.2.19 `<w:kern w:val>` — font-kerning threshold in POINTS
@@ -3032,7 +3535,7 @@ interface DocxTextRunInfo {
3032
3535
  eastAsianVert?: boolean;
3033
3536
  }
3034
3537
  //#endregion
3035
- //#region dist/.types-work/docx-VDKt2JLe.d.ts
3538
+ //#region dist/.types-work/docx-KCG5Gwuy.d.ts
3036
3539
  //#region packages/docx/src/worker-protocol.d.ts
3037
3540
  /** Serializable subset of RenderPageOptions (callbacks cannot cross the wire). */
3038
3541
  type WireRenderPageOptions = Omit<RenderPageOptions, 'onTextRun'>;
@@ -3742,929 +4245,457 @@ declare class DocxScrollViewer implements ZoomableViewer {
3742
4245
  private _wheelListener;
3743
4246
  /** Gesture-only pointer anchor for the NEXT `setScale`, in scrollHost-viewport
3744
4247
  * px (`{ x, y }` from the wheel event, relative to the scroll host's top-left).
3745
- * Set by the Ctrl/⌘+wheel handler right before it calls `setScale` so the zoom
3746
- * pivots on the cursor ("zoom toward the pointer") in BOTH axes; consumed and
3747
- * cleared by `setScale`. `null` for every non-gesture source (the public
3748
- * `setScale`, the +/- steppers, `fitWidth`/`fitPage`, the resize re-fit), which
3749
- * keep the historical viewport-TOP re-anchor so their behaviour is unchanged. */
3750
- private _pendingZoomAnchor;
3751
- /** Observes the container so a width change re-fits the base scale. Disconnected
3752
- * in `destroy()`. */
3753
- private _resizeObserver;
3754
- /** The base fit scale at the last established/re-fit layout. `_onResize` divides
3755
- * `_scale` by this to recover the current zoom multiplier so a width change
3756
- * re-fits the base while preserving the user's zoom (design §11). */
3757
- private _prevBase;
3758
- /** The fit width (px) the base scale was last established at. Lets `_onResize`
3759
- * skip the re-fit when only the height changed (a ResizeObserver fires on ANY
3760
- * box change, but only a WIDTH change alters the fit-to-width base scale). */
3761
- private _lastFitWidth;
3762
- /** Resolved page-canvas `box-shadow` (design: the recipe drop shadow by
3763
- * default). Resolved ONCE with `??` — NOT `||` — so `pageShadow: false`
3764
- * survives as the "no shadow" sentinel (a `||` would treat `false` as absent
3765
- * and wrongly re-apply the default). Applied by `_applyPageShadow` at EVERY
3766
- * canvas-creation site (`_acquireSlot` and the double-buffer spare in
3767
- * `_settleSlot`) so a recycled/re-mounted slot and a settle-swapped spare all
3768
- * carry it. */
3769
- private readonly _pageShadow;
3770
- private readonly _find;
3771
- private _findActive;
3772
- constructor(container: HTMLElement, opts?: DocxScrollViewerOptions);
3773
- /**
3774
- * Load a DOCX from URL or ArrayBuffer and render the first window.
3775
- * UNSUPPORTED when an engine was injected via `opts.document` (throws) — the
3776
- * caller already owns the parsed engine.
3777
- */
3778
- load(source: string | ArrayBuffer): Promise<void>;
3779
- get pageCount(): number;
3780
- /** CSS px width of page `i` at the current scale. */
3781
- private _pageWidthPx;
3782
- /** CSS px height of page `i` at the current scale. */
3783
- private _pageHeightPx;
3784
- /** The fit width (px), deferring when the container is unlaid-out. An EXPLICIT
3785
- * `opts.width` is the page's CSS-width contract and is returned UNCHANGED (the
3786
- * gutters still apply around placement, not to the width). The container-derived
3787
- * default instead targets `containerWidth − padL − padR` so a page sits INSIDE
3788
- * the horizontal gutters at 100%. A non-positive result (gutters wider than the
3789
- * container) is treated as unlaid-out — the same deferral as a zero-width box. */
3790
- private _fitWidthPx;
3791
- /** Base scale: first page's width fit to the fit-width. Returns 0 when the
3792
- * container has no width yet (deferral). */
3793
- private _baseScale;
3794
- /**
3795
- * Recompute per-page heights + the spacer and re-mount the visible window.
3796
- *
3797
- * The viewer already calls this automatically after `load()`, an injected
3798
- * engine, a container resize, and a zoom, so most integrations never need it.
3799
- * It is public as a deliberate escape hatch: if the host mutates the layout in
3800
- * a way the `ResizeObserver` cannot observe (e.g. a CSS change on an ancestor
3801
- * that resizes the container without a box-size event, or a font that finishes
3802
- * loading after first paint), call `relayout()` to force a re-fit. Idempotent —
3803
- * safe to call repeatedly, and a no-op while the container has zero width (the
3804
- * fit is deferred until width appears, design §11).
3805
- */
3806
- relayout(): void;
3807
- private _recomputeHeights;
3808
- private _gap;
3809
- private _overscan;
3810
- /** Desk padding fed to `computeVisibleRange`: `paddingTop`/`paddingBottom`,
3811
- * each defaulting to `gap` (uniform rhythm). Resolved here (not stored) to
3812
- * mirror `_gap()`/`_overscan()`, and consumed at EVERY `computeVisibleRange`
3813
- * call site so the padded offsets are the single source of geometry. */
3814
- private _pad;
3815
- /** Horizontal desk gutters: `paddingLeft`/`paddingRight`, each defaulting to
3816
- * `gap` (uniform rhythm — the horizontal gutters match the vertical padding).
3817
- * Consumed by `_fitWidthPx` (to shrink the container-derived fit), by
3818
- * `_positionSlot` (the flush-left floor), and by `_syncSpacer` (the spacer
3819
- * width). Resolved here (not stored) to mirror `_gap()`/`_pad()`. */
3820
- private _padH;
3821
- /** Index of the page whose slot spans content-offset `y` (largest `i` with
3822
- * `offsets[i] <= y`), for the pointer-anchored zoom re-anchor. Mirrors the
3823
- * `topIndex` search `computeVisibleRange` runs for the scrollTop, but for an
3824
- * ARBITRARY content-y (the pointer, not the viewport top). Clamped into
3825
- * `[0, n-1]`; a `y` below the first page (inside the leading pad) yields 0. */
3826
- private _pageIndexAtOffset;
3827
- private _range;
3828
- private _syncSpacer;
3829
- /** Horizontal scroll extent: the widest page (docx pages can differ in width)
3830
- * plus both gutters. A spacer NARROWER than the container never creates a
3831
- * scrollbar (scrollWidth = max(clientWidth, content)), so it is always safe to
3832
- * set — it only matters when a zoomed-in page grows past the viewport, where it
3833
- * gives the gutters something to scroll to on either side. Max over per-page
3834
- * widths so the extent covers the widest page in the document. Called from
3835
- * `_syncSpacer` and after every scale change (zoom / resize re-fit) so the
3836
- * extent tracks the current page px width. */
3837
- private _syncSpacerWidth;
3838
- private _onScroll;
3839
- /** Mount/recycle slots for the current visible window. */
3840
- private _mountVisible;
3841
- /** Apply the resolved page-canvas shadow (design: recipe drop shadow by
3842
- * default, `false` ⇒ none). Single source so `_acquireSlot` and the
3843
- * double-buffer spare in `_settleSlot` stay in lock-step — a spare that missed
3844
- * this would lose the shadow on the settle swap. `box-shadow` never affects
3845
- * layout, so this is safe to (re)set on a live/pooled canvas without shifting
3846
- * any offset. */
3847
- private _applyPageShadow;
3848
- private _acquireSlot;
3849
- private _recycleSlot;
3850
- private _positionSlot;
3851
- /** Device-pixel ratio for a render (opts override → window → 1). */
3852
- private _dpr;
3853
- /**
3854
- * Render page `i` into `slot`. Routes strictly on the constructor-resolved
3855
- * `_mode` (design §11 — no probing, no silent mis-pathing): `main` ⇒ paint the
3856
- * slot's canvas directly via `renderPage`; `worker` ⇒ transfer an ImageBitmap
3857
- * from `renderPageToBitmap`.
3858
- *
3859
- * Slot-identity guard: a slot recycled to a DIFFERENT page while a previous
3860
- * render is in flight must not repaint the stale page. `slot.renderedPage`
3861
- * tracks the page this slot is committed to; we stamp it up-front and bail on
3862
- * resolution if it changed (the engine's own token guard is per-canvas; this is
3863
- * the viewer's per-slot page-identity check).
3864
- *
3865
- * Render epoch (main path): pixel staleness after a mid-flight `setScale` is
3866
- * already handled by the engine's per-canvas token (the newer renderPage on the
3867
- * same canvas wins) — `setScale` recycles + re-mounts, and the re-mount always
3868
- * re-dispatches `renderPage` (renderedPage reset to -1), so a fresh render is
3869
- * always issued. But the viewer-side side effects of a STALE resolution — the
3870
- * text-layer build (its run geometry is at the OLD scale) and the renderedPage
3871
- * bookkeeping — must NOT run, or a superseded render would rebuild the overlay
3872
- * with stale x/y/w/h (the pool reuses slot objects, so the identity check alone
3873
- * can pass for an old-epoch resolution). We gate them on the captured epoch.
3874
- */
3875
- private _renderSlot;
3876
- /**
3877
- * IX1/IX-nav — the click handler passed to the text-layer overlay. When the
3878
- * caller supplied `onHyperlinkClick`, it fully owns the behaviour (the default
3879
- * is suppressed). Otherwise the built-in default is: an external link opens in
3880
- * a new tab through core `openExternalHyperlink` (URL sanitised against the
3881
- * safe scheme allowlist, `noopener,noreferrer`); an internal `<w:anchor>` link
3882
- * resolves its bookmark name to its destination page via
3883
- * {@link DocxDocument.getBookmarkPage} (ECMA-376 §17.16.23) and scrolls there
3884
- * with {@link scrollToPage}. An anchor naming no known bookmark is a safe no-op
3885
- * rather than a scroll to a guessed page.
3886
- *
3887
- * IX1 — returns `undefined` when `enableHyperlinks` is `false`, the single gate
3888
- * that disables hyperlink interactivity: {@link buildDocxTextLayer} treats a
3889
- * missing handler as "render link runs like plain runs", so no hit region,
3890
- * cursor, tooltip, listener, or navigation is wired (a custom
3891
- * `onHyperlinkClick` is suppressed too).
3892
- */
3893
- private _hyperlinkHandler;
3894
- /** A width-measurer primed with a run's `font` — used ONLY to clamp a §17.3.2.10
3895
- * 縦中横 selection span to its drawn one-em cell (#836). Mirrors DocxViewer's
3896
- * `_measureForFont`. Returns a length-based fallback when canvas metrics are
3897
- * unavailable so the caller still gets a callable (the overlay then sees scale
3898
- * 1 and leaves the span un-clamped). */
3899
- private _measureForFont;
3900
- /** A canvas's intended CSS box in px (the % denominators the overlay builders
3901
- * expect). Reads the inline `style.width`/`height` set by the render path,
3902
- * falling back to the backing-store size when unset; tolerates the `px` suffix. */
3903
- private _canvasCssPx;
3904
- /** Route an async render failure to `onError`, or `console.error` when none is
3905
- * set (so failures are never fully silent), and never after teardown. */
3906
- private _reportRenderError;
3907
- /**
3908
- * Worker-mode slot render: dispatch `renderPageToBitmap`, transfer the result
3909
- * via a per-slot `bitmaprenderer` context, and manage the ImageBitmap lifecycle.
3910
- *
3911
- * Coalescing / drop-stale (design §11):
3912
- * - Skip if page `i` is already in flight (a scroll storm won't double-dispatch).
3913
- * - Skip if page `i` already left the mounted window before dispatch.
3914
- * - On resolution, if `slot` is no longer THIS page's live slot (it recycled to
3915
- * another page, or page `i` re-mounted onto a DIFFERENT slot while this render
3916
- * was in flight), close the orphan bitmap and skip the paint. In that
3917
- * re-mount case a live slot for `i` still awaits a render, so once we clear
3918
- * the in-flight guard we re-dispatch it — a page that recycled and re-mounted
3919
- * mid-flight must never stay blank.
3920
- * - RENDER EPOCH: the dispatch captures `this._renderEpoch`. `setScale` bumps
3921
- * the epoch, so a resolution whose captured epoch ≠ the live epoch is STALE
3922
- * even when the SAME slot object is still mounted for page `i` (the pool
3923
- * reuses slot objects, so the identity check alone can't catch a zoom that
3924
- * happened mid-flight). A moved epoch ⇒ close the orphan + re-dispatch the
3925
- * live slot at the new scale, never paint the old-scale bitmap.
3926
- */
3927
- private _renderSlotBitmap;
3928
- /**
3929
- * Set the absolute px-per-pt zoom scale, clamped inline to
3930
- * `[zoomMin ?? 0.1, zoomMax ?? 4]` (absolute bounds, XlsxViewer convention — NOT
3931
- * multiples of the base fit; design §3 keeps the clamp in the viewer, not core),
3932
- * then re-anchor VERTICALLY so the page currently under the viewport top stays
3933
- * fixed. A no-op when the clamped scale is unchanged. Called BEFORE the doc is
3934
- * loaded / the base fit is established, the clamped factor is LATCHED (IX9 F1,
3935
- * family-unified with the single-canvas viewers) and applied by `relayout()`
3936
- * once the layout establishes — `onScaleChange` fires then.
3937
- *
3938
- * FLICKER-FREE (design §7): this does NOT re-render the visible pages inline.
3939
- * It shows an immediate CSS preview (stretch the existing bitmaps, scale the
3940
- * overlays) and DEBOUNCES a full-resolution settle re-render for ZOOM_SETTLE_MS,
3941
- * so a wheel/pinch burst never blanks a page and coalesces into one crisp render.
3942
- *
3943
- * Re-anchor (written from scratch — XlsxViewer only re-anchors horizontally):
3944
- * capture `top = topIndex` and the intra-page fraction `intraFrac` from the
3945
- * CURRENT range BEFORE rescale; after recomputing heights at the new scale,
3946
- * `newScrollTop = offsets'[top] + intraFrac × heights'[top]`, clamped to
3947
- * `[0, totalHeight' − viewportHeight]`. Because a page's height scales linearly
3948
- * with `_scale`, the same fractional position maps exactly to the new geometry.
3949
- *
3950
- * CAVEAT — base fit below the floor: `relayout()` sets `_scale = base` WITHOUT
3951
- * clamping to `[zoomMin, zoomMax]`. If the base fit is below `zoomMin` (a wide
3952
- * page in a narrow container), the initial scale sits under the floor, but once
3953
- * the user zooms via `setScale` the clamp pins the minimum to `zoomMin`, so they
3954
- * can no longer return below the floor to the original base fit through this API.
3955
- */
3956
- setScale(scale: number): void;
3957
- /** IX9 {@link ZoomableViewer} — the current zoom factor, where `1` = 100% (a
3958
- * page at its natural pt→px width). This is the viewer's absolute `_scale`
3959
- * (`widthPt × PT_TO_PX × _scale` is the drawn width), so it reads `1` at true
3960
- * 100% and, after the initial fit-to-width, the base fit factor. Before the
3961
- * fit is established it reports a latched pre-load `setScale` (IX9 F1) if one
3962
- * is pending — matching what a single-canvas viewer would show — else `1`. */
3963
- getScale(): number;
3964
- /** IX9 {@link ZoomableViewer} — step up to the next rung of the shared zoom
3965
- * ladder above the current factor (clamped to `zoomMax` by {@link setScale}). */
3966
- zoomIn(): void;
3967
- /** IX9 {@link ZoomableViewer} — step down to the next lower ladder rung. */
3968
- zoomOut(): void;
3969
- /**
3970
- * IX9 {@link ZoomableViewer} — fit a page's WIDTH to the container (the classic
3971
- * continuous-scroll "fit width"). Sets the scale to the width-fit base for the
3972
- * current container, then re-anchors + re-renders via {@link setScale}. Defers
3973
- * (no-op) while the container is unlaid-out. Note the `zoomMin`/`zoomMax` clamp
3974
- * still applies, so a fit below `zoomMin` pins to `zoomMin`.
3975
- */
3976
- fitWidth(): void;
3977
- /**
3978
- * IX9 {@link ZoomableViewer} — fit a WHOLE page (width and height) inside the
3979
- * container so one page is visible without scrolling; takes the tighter of the
3980
- * width/height fit. Uses the FIRST page's size (the continuous viewer's fit
3981
- * reference, matching the base-fit convention). Defers while unlaid-out.
3982
- */
3983
- fitPage(): void;
3984
- /** Shared fit for {@link fitWidth}/{@link fitPage}: the width-fit factor is the
3985
- * established base (`_baseScale`); the page-fit additionally bounds by the
3986
- * container height against the first page's height. Applies via {@link setScale}
3987
- * so the flicker-free re-anchor / settle path and `onScaleChange` all run. */
3988
- private _fit;
3989
- /**
3990
- * CSS preview of the visible window at the current `_scale` (design §7
3991
- * mechanism 1), WITHOUT re-rendering. Slots leaving the window recycle normally;
3992
- * slots ENTERING the window mount fresh (rendered at the current scale directly,
3993
- * so they never need a preview); slots that STAY are repositioned and their
3994
- * canvas + text overlay are CSS-transformed to the new size (the device buffer
3995
- * is untouched — that is the whole point: no synchronous clear, no blank frame).
3996
- */
3997
- private _previewVisible;
3998
- /**
3999
- * CSS-preview a single already-mounted slot at the new geometry (design §7): the
4000
- * wrapper is repositioned + sized (via `_positionSlot`), the canvas bitmap is
4001
- * STRETCHED to the new CSS size (no `canvas.width` — the device buffer, and thus
4002
- * the drawn pixels, are left intact, just scaled by the browser), and the text
4003
- * overlay is scaled by `newScale / renderedScale` so it tracks the stretched
4004
- * page. `renderedScale <= 0` means the slot's first render hasn't resolved yet
4005
- * (nothing to stretch); the pending render captured the current scale, so it
4006
- * lands correct and no preview is needed.
4007
- */
4008
- private _previewSlot;
4009
- /** (Re)schedule the debounced settle re-render (design §7 mechanism 2). Resets
4010
- * the timer on every call so a burst of `setScale` dispatches ONE settle
4011
- * ZOOM_SETTLE_MS after the LAST call. Cleared in `destroy()`. */
4012
- private _scheduleSettle;
4013
- /** Full-resolution settle re-render of the visible window (design §7 mechanisms
4014
- * 2+3). Re-renders each mounted slot at the current scale via the double-buffer
4015
- * swap (main) / same-canvas transfer (worker). Both modes rebuild the text
4016
- * overlay from the fresh render's run geometry (IX6 — worker mode collects the
4017
- * runs off-thread via `_renderSlotBitmap`) and clear the preview transform.
4018
- * Dispatched at the CURRENT epoch; the existing epoch gate discards it if a
4019
- * later `setScale` supersedes it mid-render. */
4020
- private _settleRender;
4021
- /**
4022
- * Settle-render one slot at the current scale (design §7 mechanism 3).
4023
- *
4024
- * WORKER: re-dispatch the bitmap render into the SAME canvas. The worker path
4025
- * sizes the device buffer and `transferFromImageBitmap`s it in ONE synchronous
4026
- * step (no await between `canvas.width = …` and the transfer), so the browser
4027
- * never composites an intermediate blank frame — no spare canvas is needed. The
4028
- * `renderedScale === _scale` gate in `_settleRender` plus the epoch gate inside
4029
- * `_renderSlotBitmap` keep this correct and idempotent.
4030
- *
4031
- * MAIN: `renderPage` (via renderDocumentToCanvas) synchronously sets
4032
- * `canvas.width = …` (which CLEARS the backing store to blank) BEFORE its first
4033
- * await and paints AFTER — so rendering into the on-screen canvas would flash it
4034
- * white. Render into a SPARE off-DOM canvas instead; only once it resolves at the
4035
- * current epoch do we swap it into the wrapper (replacing the old canvas, which is
4036
- * DISCARDED — the pooled unit is the slot, not the canvas). The old canvas keeps
4037
- * showing the stretched preview until the instant of the swap — blank-free.
4038
- */
4039
- private _settleSlot;
4040
- /**
4041
- * Scroll so page `index`'s top edge sits at the viewport top. Clamps `index` to
4042
- * `[0, pageCount-1]` (the pager convention) and the resulting scrollTop to
4043
- * `[0, totalHeight − viewportHeight]` so the last pages don't scroll past the
4044
- * end. A no-op when nothing is loaded or the document is empty.
4045
- *
4046
- * `opts.behavior` ('auto' | 'smooth', default 'auto') is honoured via
4047
- * `scrollHost.scrollTo({ top, behavior })` when the host supports it (a real
4048
- * browser); the stub-DOM has no `scrollTo`, so the fallback sets `scrollTop`
4049
- * directly (which is what the tests assert). We then call `_mountVisible` once.
4050
- *
4051
- * MOUNTING CAVEAT: synchronous mounting of the target page is guaranteed only on
4052
- * the DEFAULT/'auto' path — there `scrollTop` has already jumped to `top`, so the
4053
- * `_mountVisible` call reads the final scroll position and the target page's slots
4054
- * exist immediately. With `behavior: 'smooth'` the scroll animates ASYNCHRONOUSLY:
4055
- * `scrollTop` is still near the old position when `_mountVisible` runs, so the
4056
- * target page mounts lazily via the animation's subsequent `scroll` events, not
4057
- * from this call.
4058
- */
4059
- scrollToPage(index: number, opts?: {
4060
- behavior?: 'auto' | 'smooth';
4061
- }): void;
4062
- /** Search the complete document, including pages outside the virtualized
4063
- * mounted window. Matching is case-insensitive by default. */
4064
- findText(query: string, opts?: FindMatchesOptions): Promise<FindMatch<DocxMatchLocation>[]>;
4065
- /** Activate and reveal the next match, wrapping at the end. */
4066
- findNext(): Promise<FindMatch<DocxMatchLocation> | null>;
4067
- /** Activate and reveal the previous match, wrapping at the beginning. */
4068
- findPrev(): Promise<FindMatch<DocxMatchLocation> | null>;
4069
- /** Clear the current query and every mounted highlight. */
4070
- clearFind(): void;
4071
- private _activateMatch;
4072
- private _collectPageRuns;
4073
- private _redrawHighlights;
4074
- private _refreshFindRuns;
4075
- private _redrawSlotHighlights;
4076
- /**
4077
- * Re-fit the base scale on a container resize while PRESERVING the current zoom
4078
- * multiplier (design §11), then re-anchor + re-render. A `ResizeObserver` fires
4079
- * on any box change, but only a WIDTH change alters the fit-to-width base scale;
4080
- * a height-only change skips the re-fit yet STILL re-mounts the visible window
4081
- * (via `_mountVisible`), because a taller viewport reveals rows that were below
4082
- * the fold and would otherwise stay blank until the next scroll. Empty/unloaded
4083
- * ⇒ no-op; a still-zero width ⇒ defer.
4084
- *
4085
- * Zero-width recovery: a container that was 0-wide at construction never
4086
- * established a scale (`_scaleEstablished` is false), so the first non-zero
4087
- * resize establishes it here via `relayout()` — completing the T2 deferral.
4088
- *
4089
- * Re-fit math (zoom multiplier preserved):
4090
- * mult = _scale / _prevBase (the user's zoom over the old base)
4091
- * newScale = newBase × mult
4092
- * Routing through `setScale(newScale)` bumps `_renderEpoch` (resize IS an epoch
4093
- * event — T4 banner) and re-anchors + CSS-previews + debounces a settle re-render
4094
- * of every slot at the new geometry, exactly like a zoom (design §7 flicker-free
4095
- * path — a rapid ResizeObserver burst therefore also coalesces into one settle).
4096
- * `setScale`'s clamp/no-op guards apply: an unchanged newScale (identical width)
4097
- * is a no-op there — so we short-circuit BEFORE it when the fit-width is
4098
- * unchanged (mounting the revealed window without a needless re-render), and
4099
- * after it we call `_mountVisible` again to cover the case where the clamp made
4100
- * `setScale` no-op yet the viewport still grew.
4101
- */
4102
- private _onResize;
4103
- get topVisiblePage(): number;
4104
- /** Return the owning engine's latest content-free package-usage snapshot. */
4105
- getResourceMetrics(): Promise<OoxmlResourceMetrics>;
4106
- /**
4107
- * Tear down the viewer: remove the DOM subtree and (only for a self-loaded
4108
- * engine) destroy the engine. An injected engine is left intact — the caller
4109
- * owns its lifecycle. Per-slot worker ImageBitmaps are closed on recycle.
4110
- */
4111
- destroy(): void;
4112
- }
4113
- //#endregion
4114
- //#region packages/docx/src/text-layer.d.ts
4115
- /**
4116
- * Build the transparent text-selection overlay for a rendered docx page: one
4117
- * absolutely-positioned, color-transparent `<span>` per {@link DocxTextRunInfo}
4118
- * (emitted by `renderPage`'s `onTextRun`), so the browser's native selection
4119
- * lands on the drawn glyphs. Extracted verbatim from `DocxViewer._buildTextLayer`
4120
- * so both the pager (DocxViewer) and the continuous-scroll viewer (DocxScrollViewer)
4121
- * share one implementation; also public API for integrators building their own
4122
- * overlay (design §10). IX6 — usable in BOTH render modes: worker mode collects
4123
- * the same `DocxTextRunInfo[]` off-thread and ships it back beside the bitmap, so
4124
- * the overlay is built from identical geometry regardless of thread.
4125
- *
4126
- * Every span is positioned as a PERCENTAGE of `cssWidth`/`cssHeight` (the page's
4127
- * intended CSS-px box), never literal px, and the container's own width/height are
4128
- * left untouched (the caller sizes it `width:100%;height:100%`). This lets the
4129
- * overlay track the canvas's ACTUAL rendered box even when a consumer scales the
4130
- * canvas down with external CSS (`width:100%!important; height:auto`): the
4131
- * `display:inline-block` wrapper shrinks with the canvas, the `100%` container
4132
- * follows, and every `%`-placed span scales with it, so nothing overflows the
4133
- * wrapper into an ancestor's scroll area.
4134
- *
4135
- * @param layer the overlay div (sized `width:100%;height:100%` by the caller).
4136
- * @param runs per-run geometry from `renderPage({ onTextRun })`.
4137
- * @param cssWidth the page's intended CSS width (px, number) — the %
4138
- * denominator for the x axis.
4139
- * @param cssHeight the page's intended CSS height (px, number) — the %
4140
- * denominator for the y axis.
4141
- * @param onHyperlinkClick IX1 — invoked when a run carrying a resolved
4142
- * {@link HyperlinkTarget} is clicked. A hyperlink run's
4143
- * span keeps its transparent glyphs (the visible link
4144
- * colour/underline is already drawn on the canvas) but
4145
- * gains `cursor:pointer`, a `title` tooltip (the URL or
4146
- * bookmark ref) and this click handler. A plain
4147
- * `<span>` — not an `<a href>` — is used deliberately so
4148
- * the browser's own navigation can never bypass the
4149
- * caller's URL sanitisation. When omitted, link runs are
4150
- * rendered exactly like plain runs (no click affordance).
4151
- * @param measureForFont optional width-measurer factory (primed with a run's
4152
- * `font`), used ONLY to clamp a §17.3.2.10 縦中横
4153
- * (eastAsianVert) span to its drawn one-em cell (#836):
4154
- * the span composes a `scaleX(run.w / naturalWidth)` so
4155
- * its selection extent matches the compressed glyphs
4156
- * instead of the run's natural ~2× width. When omitted,
4157
- * a 縦中横 span keeps the bare rotate (no regression for
4158
- * callers that do not thread a measurer).
4159
- */
4160
- declare function buildDocxTextLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], cssWidth: number, cssHeight: number, onHyperlinkClick?: (target: HyperlinkTarget) => void, measureForFont?: (font: string) => (s: string) => number): void;
4161
- //#endregion
4162
- //#region packages/docx/src/find-highlight-layer.d.ts
4163
- /** One page's highlight input: the run-slices a match covers, and whether that
4164
- * match is the active one (emphasis colour). */
4165
- interface DocxHighlightMatch {
4166
- slices: MatchRunSlice[];
4167
- active: boolean;
4168
- }
4169
- /** Format-specific compatibility alias for the shared colour contract. */
4170
- type DocxHighlightColors = FindHighlightColors;
4171
- /**
4172
- * Populate a highlight overlay layer with one box per matched run-slice.
4173
- *
4174
- * Every box is positioned as a PERCENTAGE of `cssWidth`/`cssHeight`, and the
4175
- * container's own size is left untouched (`width:100%;height:100%` from the
4176
- * caller), so the highlights track the canvas's ACTUAL rendered box even when a
4177
- * consumer scales the canvas down with external CSS — mirroring
4178
- * {@link buildDocxTextLayer}.
4179
- *
4180
- * @param layer the overlay div (cleared here; sized `100%` by the caller).
4181
- * @param runs the page's runs (same array the page was rendered/text-layered from).
4182
- * @param matches the page's matches (run-slices + active flag).
4183
- * @param cssWidth the page's intended CSS width (px, number) — the x-axis % denominator.
4184
- * @param cssHeight the page's intended CSS height (px, number) — the y-axis % denominator.
4185
- * @param measureForFont returns a width-measurer primed with a run's `font`
4186
- * (the viewer closes over a canvas 2d context). Kept as a
4187
- * factory so the font is set once per run, not per glyph.
4188
- * @param colors optional colour overrides.
4189
- */
4190
- declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], matches: DocxHighlightMatch[], cssWidth: number, cssHeight: number, measureForFont: (font: string) => (s: string) => number, colors?: DocxHighlightColors): void;
4191
- declare namespace docx_d_exports {
4192
- export { AnchorHostMetrics, AutoResizeOptions, BodyElement, BorderSpec, CellBorders, CellElement, ChartRun, ColSpec, ColumnsSpec, DocComment, DocNote, DocParagraph, DocRevision, DocRun, DocSettings, DocTable, DocTableCell, DocTableRow, DocxDocument, DocxDocumentModel, DocxHighlightColors, DocxHighlightMatch, DocxMatchLocation, DocxRunBorder, DocxScrollViewer, DocxScrollViewerOptions, DocxTextRun, DocxTextRunInfo, DocxViewer, DocxViewerOptions, EmbeddedFontRef, FieldRun, FindHighlightColors, FindMatch, FindMatchesOptions, FramePr, GradientStop$1 as GradientStop, HeaderFooter, HeadersFooters, HyperlinkTarget, ImageRun, LineEnd, LineNumbering, LineSpacing, LoadOptions$2 as LoadOptions, NoteRef, NumberingInfo, OoxmlDecodedImageLimitError, OoxmlDecodedImageLimitMetric, OoxmlError, OoxmlErrorCode, OoxmlErrorSource, OoxmlErrorStage, OoxmlFormat, OoxmlResourceLimit, OoxmlResourceLimitError, OoxmlResourceLimitErrorDetails, OoxmlResourceLimits, OoxmlResourceMetric, OoxmlResourceMetrics, OoxmlResourceMetricsCheckpoint, OoxmlResourceName, OoxmlResourcePolicySnapshot, OoxmlResourceUsageSnapshot, OoxmlResourceViolation, PTabRun, PageBorderEdge, PageBorders, PageNumType, ParaBorderEdge, ParagraphBorders, PathCmd$2 as PathCmd, RenderPageOptions, RenderPageToBitmapOptions, RubyAnnotation, RunRevision, SectionGeom, SectionProps, ShapeRun, ShapeText$1 as ShapeText, ShapeTextRun$1 as ShapeTextRun, TabStop$1 as TabStop, TableBorders, TblpPr, TextPath, WireRenderPageOptions, autoResize, buildDocxHighlightLayer, buildDocxTextLayer, isOoxmlDecodedImageLimitError, noteText, openExternalHyperlink };
4193
- }
4194
- //#endregion
4195
- //#region dist/.types-work/common-Bgczc_Eb.d.ts
4196
- //#region packages/core/src/types/common.d.ts
4197
- type PathCmd$1 = {
4198
- cmd: 'moveTo';
4199
- x: number;
4200
- y: number;
4201
- } | {
4202
- cmd: 'lineTo';
4203
- x: number;
4204
- y: number;
4205
- } | {
4206
- cmd: 'cubicBezTo';
4207
- x1: number;
4208
- y1: number;
4209
- x2: number;
4210
- y2: number;
4211
- x: number;
4212
- y: number;
4213
- } | {
4214
- cmd: 'arcTo';
4215
- wr: number;
4216
- hr: number;
4217
- stAng: number;
4218
- swAng: number;
4219
- } | {
4220
- cmd: 'close';
4221
- };
4222
- type Fill = SolidFill | NoFill | GradientFill | PatternFill | ImageFill;
4223
- interface SolidFill {
4224
- fillType: 'solid';
4225
- color: string;
4226
- }
4227
- interface NoFill {
4228
- fillType: 'none';
4229
- }
4230
- interface GradientStop {
4231
- position: number;
4232
- color: string;
4233
- }
4234
- interface GradientFill {
4235
- fillType: 'gradient';
4236
- stops: GradientStop[];
4237
- /** degrees: 0 = left→right, 90 = top→bottom */
4238
- angle: number;
4239
- /** 'linear' | 'radial' */
4240
- gradType: string;
4241
- }
4242
- /**
4243
- * Preset pattern fill — ECMA-376 §20.1.8.40 (CT_PatternFillProperties)
4244
- * with `preset` drawn from §20.1.10.59 (ST_PresetPatternVal).
4245
- */
4246
- interface PatternFill {
4247
- fillType: 'pattern';
4248
- /** Foreground hex colour — used for the "1" pixels of the preset bitmap. */
4249
- fg: string;
4250
- /** Background hex colour — used for the "0" pixels. */
4251
- bg: string;
4252
- /** Preset name, e.g. "pct25", "horz", "diagCross", "lgGrid". */
4253
- preset: string;
4254
- }
4255
- /**
4256
- * ECMA-376 §20.1.8.30 (CT_RelativeRect) — the destination rectangle a stretched
4257
- * blip is mapped into, as edge insets relative to the fill region. Values are
4258
- * fractions (ST_Percentage / 100000); **negative values let the image bleed
4259
- * past the box (overscan)**. Absent edges default to 0.
4260
- */
4261
- interface FillRect {
4262
- l?: number;
4263
- t?: number;
4264
- r?: number;
4265
- b?: number;
4266
- }
4267
- /**
4268
- * ECMA-376 §20.1.8.58 (CT_TileInfoProperties) — tiled blip-fill placement.
4269
- * The blip repeats at its native size (scaled by sx/sy) across the fill box.
4270
- * Mutually exclusive with {@link ImageFill.fillRect} (the `stretch` mode).
4271
- */
4272
- interface TileInfo {
4273
- /** Horizontal offset of the first tile, in EMU (`tx`). Default 0. */
4274
- tx: number;
4275
- /** Vertical offset of the first tile, in EMU (`ty`). Default 0. */
4276
- ty: number;
4277
- /** Horizontal tile scale as a fraction (`sx` / 100000). Default 1.0. */
4278
- sx: number;
4279
- /** Vertical tile scale as a fraction (`sy` / 100000). Default 1.0. */
4280
- sy: number;
4281
- /** Mirror mode: `'none' | 'x' | 'y' | 'xy'` (`flip`). Default `'none'`. */
4282
- flip: string;
4283
- /**
4284
- * Anchor corner the tile grid registers against:
4285
- * `tl|t|tr|l|ctr|r|bl|b|br` (`algn`). Default `'tl'`.
4286
- */
4287
- algn: string;
4288
- }
4289
- /**
4290
- * Image fill — ECMA-376 §20.1.8.14 (CT_BlipFillProperties). The embedded blip
4291
- * is carried as a zip path + MIME; the renderer fetches the bytes on demand via
4292
- * {@link RenderOptions.fetchImage} (no base64 inlined at parse time). Both
4293
- * fill-modes are modelled and mutually exclusive: `stretch` (§20.1.8.56) carries
4294
- * {@link ImageFill.fillRect}; `tile` (§20.1.8.58) carries {@link ImageFill.tile}.
4295
- */
4296
- interface ImageFill {
4297
- fillType: 'image';
4298
- /**
4299
- * Embedded zip path of the blip (e.g. "word/media/image1.png"), for the lazy
4300
- * byte-on-demand pipeline. The renderer fetches the bytes via a path-keyed
4301
- * loader ({@link RenderOptions.fetchImage}) instead of inlining base64.
4302
- */
4303
- imagePath: string;
4304
- /** MIME type of the blip at {@link ImageFill.imagePath} (e.g. `image/png`). */
4305
- mimeType: string;
4306
- /**
4307
- * `<a:stretch><a:fillRect>` insets. Absent → fills the whole box (or the
4308
- * fill is tiled — see {@link ImageFill.tile}).
4309
- */
4310
- fillRect?: FillRect;
4311
- /**
4312
- * `<a:tile>` descriptor. Present only when the blipFill is tiled; mutually
4313
- * exclusive with {@link ImageFill.fillRect}.
4314
- */
4315
- tile?: TileInfo;
4316
- /** `a:blip > a:alphaModFix@amt` as a fraction (0.0–1.0). Absent = opaque. */
4317
- alpha?: number;
4318
- /**
4319
- * ECMA-376 §20.1.8.23 `<a:duotone>` recolour, resolved to its two endpoint
4320
- * colours (through the slide theme). Absent ⇒ no duotone. When present the
4321
- * renderer maps the blip's luminance ramp between the two colours (core
4322
- * `applyDuotone`) — the same recolour a `<p:pic>` duotone applies, wired onto
4323
- * the picture-FILL path (§20.1.8.14) by issue #889.
4324
- */
4325
- duotone?: Duotone$1;
4326
- }
4327
- interface Shadow {
4328
- color: string;
4329
- alpha: number;
4330
- blur: number;
4331
- dist: number;
4332
- /** degrees clockwise from East */
4333
- dir: number;
4334
- }
4335
- /** ECMA-376 §20.1.8.17 (CT_GlowEffect) — coloured halo with blur radius. */
4336
- interface Glow {
4337
- color: string;
4338
- alpha: number;
4339
- /** Blur radius in EMU. */
4340
- radius: number;
4341
- }
4342
- /** ECMA-376 §20.1.8.31 (CT_SoftEdgesEffect) — feather radius in EMU. */
4343
- interface SoftEdge {
4344
- radius: number;
4345
- }
4346
- /** ECMA-376 §20.1.8.27 (CT_ReflectionEffect) — mirrored copy below the
4347
- * shape with a linear alpha gradient. Carries the spec attributes whose
4348
- * defaults the renderer needs to interpret correctly. */
4349
- interface Reflection {
4350
- blur: number;
4351
- dist: number;
4352
- /** Direction in degrees, clockwise from East. */
4353
- dir: number;
4354
- /** Start alpha (0–1). Default 1.0. */
4355
- stA: number;
4356
- /** Start position along the gradient (0–1). Default 0. */
4357
- stPos: number;
4358
- /** End alpha. Default 0. */
4359
- endA: number;
4360
- /** End position. Default 1.0. */
4361
- endPos: number;
4362
- /** Horizontal scale (1.0 = same width). */
4363
- sx: number;
4364
- /** Vertical scale (-1.0 = full mirror). */
4365
- sy: number;
4366
- }
4367
- interface ArrowEnd {
4368
- /** OOXML type: "none" | "triangle" | "stealth" | "diamond" | "oval" | "arrow" */
4369
- type: string;
4370
- /** Width multiplier: "sm" | "med" | "lg" */
4371
- w: string;
4372
- /** Length multiplier: "sm" | "med" | "lg" */
4373
- len: string;
4374
- }
4375
- interface Stroke {
4376
- color: string;
4377
- /** Width in EMU */
4378
- width: number;
4379
- /** Authored non-solid DrawingML line paint. Solid lines use `color`. */
4380
- fill?: Exclude<Fill, {
4381
- fillType: 'image';
4382
- } | {
4383
- fillType: 'none';
4384
- }>;
4385
- /** OOXML prstDash value: "dash", "dot", "dashDot", "lgDash", "lgDashDot", etc. */
4386
- dashStyle?: string;
4387
- /** Canvas line cap normalized from DrawingML/VML (`flat` → `butt`). */
4388
- lineCap?: CanvasLineCap;
4389
- /** Arrow head at the start of the line */
4390
- headEnd?: ArrowEnd;
4391
- /** Arrow head at the end of the line */
4392
- tailEnd?: ArrowEnd;
4393
- /**
4394
- * ECMA-376 §20.1.8.42 ST_CompoundLine. "sng" (default) | "dbl" |
4395
- * "thinThick" | "thickThin" | "tri". Absent means single line.
4396
- */
4397
- cmpd?: string;
4398
- }
4399
- interface TextBody$1 {
4400
- /** Vertical anchor: "t" | "ctr" | "b" */
4401
- verticalAnchor: string;
4402
- paragraphs: Paragraph$1[];
4403
- /** Default pt size from lstStyle (overrides renderer default when present) */
4404
- defaultFontSize: number | null;
4405
- /** Inherited bold from layout/master defRPr (null = not set, use false as final default) */
4406
- defaultBold: boolean | null;
4407
- /** Inherited italic from layout/master defRPr (null = not set, use false as final default) */
4408
- defaultItalic: boolean | null;
4409
- /** Text insets in EMU (defaults: lIns=rIns=91440, tIns=bIns=45720) */
4410
- lIns: number;
4411
- rIns: number;
4412
- tIns: number;
4413
- bIns: number;
4414
- /** "square" = wrap, "none" = no wrap */
4415
- wrap: string;
4416
- /** Text direction: "horz" | "vert" | "vert270" | "eaVert" etc. */
4417
- vert: string;
4418
- /** Auto-fit: "sp" = shape grows to fit text, "norm" = font shrinks, "none" = no fit */
4419
- autoFit: string;
4420
- /**
4421
- * `<a:normAutofit fontScale>` (ECMA-376 §21.1.2.1.3) — PowerPoint's stored,
4422
- * pre-computed font-shrink ratio for `autoFit === "norm"`, as a fraction
4423
- * (e.g. 0.625 for `fontScale="62500"`). Null/absent when PowerPoint stored no
4424
- * scale; the renderer then re-derives one. Applying the stored value matches
4425
- * PowerPoint exactly instead of guessing from our own text metrics.
4426
- */
4427
- fontScale?: number | null;
4428
- /** `<a:normAutofit lnSpcReduction>` — stored line-spacing reduction fraction
4429
- * (e.g. 0.20 for `lnSpcReduction="20000"`). Null/absent when not stored. */
4430
- lnSpcReduction?: number | null;
4431
- /**
4432
- * `<a:bodyPr numCol>` (ECMA-376 §20.1.10.34) — number of text columns inside
4433
- * the shape. Defaults to 1; values > 1 cause the renderer to flow paragraphs
4434
- * across N columns left-to-right, top-to-bottom.
4435
- */
4436
- numCol?: number;
4437
- /** `<a:bodyPr spcCol>` — gap between columns in EMU. Default 0. */
4438
- spcCol?: number;
4439
- }
4440
- type SpaceLine = {
4441
- type: 'pct';
4442
- val: number;
4443
- } | {
4444
- type: 'pts';
4445
- val: number;
4446
- };
4447
- /**
4448
- * A paragraph's bullet marker. For `char`, the marker size is EITHER `sizePct`
4449
- * (a percentage of the run size — ECMA-376 §21.1.2.4.9 `<a:buSzPct>`) OR `sizePts`
4450
- * (an absolute size in points — §21.1.2.4.10 `<a:buSzPts>`), never both: they are
4451
- * the one `EG_TextBulletSize` xsd:choice. `sizePts` is optional (absent when no
4452
- * `<a:buSzPts>` was declared); when present it takes precedence over `sizePct`.
4453
- */
4454
- type Bullet$1 = {
4455
- type: 'none';
4456
- } | {
4457
- type: 'inherit';
4458
- } | {
4459
- type: 'char';
4460
- char: string;
4461
- color: string | null;
4462
- sizePct: number | null;
4463
- sizePts?: number;
4464
- fontFamily: string | null;
4465
- } | {
4466
- type: 'autoNum';
4467
- numType: string;
4468
- startAt: number | null;
4469
- color: string | null;
4470
- };
4471
- interface TabStop {
4472
- /** Position in EMU from the LEADING text-inset edge of the text area —
4473
- * logical, not physical (ECMA-376 §21.1.2.1): the left edge (after lIns)
4474
- * in an LTR paragraph, the right edge (before rIns) in an RTL
4475
- * (`<a:pPr rtl="1">`) paragraph. */
4476
- pos: number;
4477
- /** Alignment: "l" | "r" | "ctr" | "dec" */
4478
- algn: string;
4479
- }
4480
- interface Paragraph$1 {
4481
- /** Alignment: "l" | "ctr" | "r" | "just" */
4482
- alignment: string;
4483
- /** Left margin in EMU */
4484
- marL: number;
4485
- /** Right margin in EMU */
4486
- marR: number;
4487
- /** First-line indent in EMU (negative = hanging indent) */
4488
- indent: number;
4489
- spaceBefore: number | null;
4490
- spaceAfter: number | null;
4491
- spaceLine: SpaceLine | null;
4492
- /** List nesting level (0–8) */
4493
- lvl: number;
4494
- bullet: Bullet$1;
4495
- defFontSize: number | null;
4496
- defColor: string | null;
4497
- defBold: boolean | null;
4498
- defItalic: boolean | null;
4499
- defFontFamily: string | null;
4500
- /** Tab stops from pPr > tabLst */
4501
- tabStops: TabStop[];
4502
- /**
4503
- * `<a:pPr rtl="1">` — right-to-left paragraph (ECMA-376 §21.1.2.2.7).
4504
- * When true and no explicit `algn`, the parser-side default flips from
4505
- * "l" to "r"; renderers can also use this flag to flow runs RTL.
4506
- */
4507
- rtl?: boolean;
4508
- runs: TextRun[];
4509
- }
4510
- type TextRun = TextRunData | LineBreak | EquationRun;
4511
- /**
4512
- * An OMML equation embedded in a paragraph (ECMA-376 §22.1). Parsed into the
4513
- * shared math AST and rendered by `@silurus/ooxml-core`'s math engine.
4514
- * PowerPoint stores these as `a14:m` inside `mc:AlternateContent`.
4515
- */
4516
- interface EquationRun {
4517
- type: 'math';
4518
- /** Parsed OMML node list. */
4519
- nodes: MathNode[];
4520
- /** True for block (`m:oMathPara`) math, false for inline (`m:oMath`). */
4521
- display: boolean;
4522
- /** Paragraph default run size in pt, if declared; absent → renderer inherits. */
4523
- fontSize?: number | null;
4524
- /** Equation colour (hex, no '#') from the math run's rPr; absent → inherit. */
4525
- color?: string | null;
4526
- }
4527
- interface TextRunData {
4528
- type: 'text';
4529
- text: string;
4530
- /** null = not set, inherit from paragraph/body defaults */
4531
- bold: boolean | null;
4532
- /** null = not set, inherit from paragraph/body defaults */
4533
- italic: boolean | null;
4534
- underline: boolean;
4535
- /**
4536
- * Specific underline style when not the default single line. Values come
4537
- * from ECMA-376 §21.1.2.3.16 (ST_TextUnderlineType): "dbl", "heavy",
4538
- * "dotted", "dottedHeavy", "dash", "dashHeavy", "dashLong",
4539
- * "dashLongHeavy", "dotDash", "dotDashHeavy", "dotDotDash",
4540
- * "dotDotDashHeavy", "wavy", "wavyHeavy", "wavyDbl". Absent means either
4541
- * no underline (when `underline` is false) or the default single line.
4542
- */
4543
- underlineStyle?: string;
4248
+ * Set by the Ctrl/⌘+wheel handler right before it calls `setScale` so the zoom
4249
+ * pivots on the cursor ("zoom toward the pointer") in BOTH axes; consumed and
4250
+ * cleared by `setScale`. `null` for every non-gesture source (the public
4251
+ * `setScale`, the +/- steppers, `fitWidth`/`fitPage`, the resize re-fit), which
4252
+ * keep the historical viewport-TOP re-anchor so their behaviour is unchanged. */
4253
+ private _pendingZoomAnchor;
4254
+ /** Observes the container so a width change re-fits the base scale. Disconnected
4255
+ * in `destroy()`. */
4256
+ private _resizeObserver;
4257
+ /** The base fit scale at the last established/re-fit layout. `_onResize` divides
4258
+ * `_scale` by this to recover the current zoom multiplier so a width change
4259
+ * re-fits the base while preserving the user's zoom (design §11). */
4260
+ private _prevBase;
4261
+ /** The fit width (px) the base scale was last established at. Lets `_onResize`
4262
+ * skip the re-fit when only the height changed (a ResizeObserver fires on ANY
4263
+ * box change, but only a WIDTH change alters the fit-to-width base scale). */
4264
+ private _lastFitWidth;
4265
+ /** Resolved page-canvas `box-shadow` (design: the recipe drop shadow by
4266
+ * default). Resolved ONCE with `??` — NOT `||` — so `pageShadow: false`
4267
+ * survives as the "no shadow" sentinel (a `||` would treat `false` as absent
4268
+ * and wrongly re-apply the default). Applied by `_applyPageShadow` at EVERY
4269
+ * canvas-creation site (`_acquireSlot` and the double-buffer spare in
4270
+ * `_settleSlot`) so a recycled/re-mounted slot and a settle-swapped spare all
4271
+ * carry it. */
4272
+ private readonly _pageShadow;
4273
+ private readonly _find;
4274
+ private _findActive;
4275
+ constructor(container: HTMLElement, opts?: DocxScrollViewerOptions);
4544
4276
  /**
4545
- * Underline-only colour from rPr > uFill (ECMA-376 §21.1.2.3.20). Absent
4546
- * means the underline follows the text colour (uFillTx default).
4277
+ * Load a DOCX from URL or ArrayBuffer and render the first window.
4278
+ * UNSUPPORTED when an engine was injected via `opts.document` (throws) — the
4279
+ * caller already owns the parsed engine.
4547
4280
  */
4548
- underlineColor?: string;
4549
- /** True when rPr strike is sngStrike or dblStrike. */
4550
- strikethrough: boolean;
4281
+ load(source: string | ArrayBuffer): Promise<void>;
4282
+ get pageCount(): number;
4283
+ /** CSS px width of page `i` at the current scale. */
4284
+ private _pageWidthPx;
4285
+ /** CSS px height of page `i` at the current scale. */
4286
+ private _pageHeightPx;
4287
+ /** The fit width (px), deferring when the container is unlaid-out. An EXPLICIT
4288
+ * `opts.width` is the page's CSS-width contract and is returned UNCHANGED (the
4289
+ * gutters still apply around placement, not to the width). The container-derived
4290
+ * default instead targets `containerWidth − padL − padR` so a page sits INSIDE
4291
+ * the horizontal gutters at 100%. A non-positive result (gutters wider than the
4292
+ * container) is treated as unlaid-out — the same deferral as a zero-width box. */
4293
+ private _fitWidthPx;
4294
+ /** Base scale: first page's width fit to the fit-width. Returns 0 when the
4295
+ * container has no width yet (deferral). */
4296
+ private _baseScale;
4551
4297
  /**
4552
- * True only when rPr strike = "dblStrike". Lets the renderer draw two parallel
4553
- * lines instead of one. ECMA-376 §21.1.2.3.10 (ST_TextStrikeType).
4298
+ * Recompute per-page heights + the spacer and re-mount the visible window.
4299
+ *
4300
+ * The viewer already calls this automatically after `load()`, an injected
4301
+ * engine, a container resize, and a zoom, so most integrations never need it.
4302
+ * It is public as a deliberate escape hatch: if the host mutates the layout in
4303
+ * a way the `ResizeObserver` cannot observe (e.g. a CSS change on an ancestor
4304
+ * that resizes the container without a box-size event, or a font that finishes
4305
+ * loading after first paint), call `relayout()` to force a re-fit. Idempotent —
4306
+ * safe to call repeatedly, and a no-op while the container has zero width (the
4307
+ * fit is deferred until width appears, design §11).
4554
4308
  */
4555
- strikeDouble?: boolean;
4556
- /** Font size in points */
4557
- fontSize: number | null;
4558
- color: string | null;
4559
- fontFamily: string | null;
4309
+ relayout(): void;
4310
+ private _recomputeHeights;
4311
+ private _gap;
4312
+ private _overscan;
4313
+ /** Desk padding fed to `computeVisibleRange`: `paddingTop`/`paddingBottom`,
4314
+ * each defaulting to `gap` (uniform rhythm). Resolved here (not stored) to
4315
+ * mirror `_gap()`/`_overscan()`, and consumed at EVERY `computeVisibleRange`
4316
+ * call site so the padded offsets are the single source of geometry. */
4317
+ private _pad;
4318
+ /** Horizontal desk gutters: `paddingLeft`/`paddingRight`, each defaulting to
4319
+ * `gap` (uniform rhythm — the horizontal gutters match the vertical padding).
4320
+ * Consumed by `_fitWidthPx` (to shrink the container-derived fit), by
4321
+ * `_positionSlot` (the flush-left floor), and by `_syncSpacer` (the spacer
4322
+ * width). Resolved here (not stored) to mirror `_gap()`/`_pad()`. */
4323
+ private _padH;
4324
+ /** Index of the page whose slot spans content-offset `y` (largest `i` with
4325
+ * `offsets[i] <= y`), for the pointer-anchored zoom re-anchor. Mirrors the
4326
+ * `topIndex` search `computeVisibleRange` runs for the scrollTop, but for an
4327
+ * ARBITRARY content-y (the pointer, not the viewport top). Clamped into
4328
+ * `[0, n-1]`; a `y` below the first page (inside the leading pad) yields 0. */
4329
+ private _pageIndexAtOffset;
4330
+ private _range;
4331
+ private _syncSpacer;
4332
+ /** Horizontal scroll extent: the widest page (docx pages can differ in width)
4333
+ * plus both gutters. A spacer NARROWER than the container never creates a
4334
+ * scrollbar (scrollWidth = max(clientWidth, content)), so it is always safe to
4335
+ * set — it only matters when a zoomed-in page grows past the viewport, where it
4336
+ * gives the gutters something to scroll to on either side. Max over per-page
4337
+ * widths so the extent covers the widest page in the document. Called from
4338
+ * `_syncSpacer` and after every scale change (zoom / resize re-fit) so the
4339
+ * extent tracks the current page px width. */
4340
+ private _syncSpacerWidth;
4341
+ private _onScroll;
4342
+ /** Mount/recycle slots for the current visible window. */
4343
+ private _mountVisible;
4344
+ /** Apply the resolved page-canvas shadow (design: recipe drop shadow by
4345
+ * default, `false` ⇒ none). Single source so `_acquireSlot` and the
4346
+ * double-buffer spare in `_settleSlot` stay in lock-step — a spare that missed
4347
+ * this would lose the shadow on the settle swap. `box-shadow` never affects
4348
+ * layout, so this is safe to (re)set on a live/pooled canvas without shifting
4349
+ * any offset. */
4350
+ private _applyPageShadow;
4351
+ private _acquireSlot;
4352
+ private _recycleSlot;
4353
+ private _positionSlot;
4354
+ /** Device-pixel ratio for a render (opts override → window → 1). */
4355
+ private _dpr;
4560
4356
  /**
4561
- * East Asian font family from rPr > a:ea (ECMA-376 §21.1.2.3.7),
4562
- * resolved through the theme. Renderer uses this for CJK glyphs when
4563
- * present; absent means CJK falls back to fontFamily.
4357
+ * Render page `i` into `slot`. Routes strictly on the constructor-resolved
4358
+ * `_mode` (design §11 no probing, no silent mis-pathing): `main` ⇒ paint the
4359
+ * slot's canvas directly via `renderPage`; `worker` transfer an ImageBitmap
4360
+ * from `renderPageToBitmap`.
4361
+ *
4362
+ * Slot-identity guard: a slot recycled to a DIFFERENT page while a previous
4363
+ * render is in flight must not repaint the stale page. `slot.renderedPage`
4364
+ * tracks the page this slot is committed to; we stamp it up-front and bail on
4365
+ * resolution if it changed (the engine's own token guard is per-canvas; this is
4366
+ * the viewer's per-slot page-identity check).
4367
+ *
4368
+ * Render epoch (main path): pixel staleness after a mid-flight `setScale` is
4369
+ * already handled by the engine's per-canvas token (the newer renderPage on the
4370
+ * same canvas wins) — `setScale` recycles + re-mounts, and the re-mount always
4371
+ * re-dispatches `renderPage` (renderedPage reset to -1), so a fresh render is
4372
+ * always issued. But the viewer-side side effects of a STALE resolution — the
4373
+ * text-layer build (its run geometry is at the OLD scale) and the renderedPage
4374
+ * bookkeeping — must NOT run, or a superseded render would rebuild the overlay
4375
+ * with stale x/y/w/h (the pool reuses slot objects, so the identity check alone
4376
+ * can pass for an old-epoch resolution). We gate them on the captured epoch.
4564
4377
  */
4565
- fontFamilyEa?: string;
4378
+ private _renderSlot;
4566
4379
  /**
4567
- * Symbol font family from rPr > a:sym (ECMA-376 §21.1.2.3.10), resolved
4568
- * through the theme. PowerPoint stores symbol-font glyphs as Private-Use
4569
- * codepoints U+F020–U+F0FF; the renderer uses this font to resolve them.
4570
- * Absent means no symbol font was declared.
4380
+ * IX1/IX-nav the click handler passed to the text-layer overlay. When the
4381
+ * caller supplied `onHyperlinkClick`, it fully owns the behaviour (the default
4382
+ * is suppressed). Otherwise the built-in default is: an external link opens in
4383
+ * a new tab through core `openExternalHyperlink` (URL sanitised against the
4384
+ * safe scheme allowlist, `noopener,noreferrer`); an internal `<w:anchor>` link
4385
+ * resolves its bookmark name to its destination page via
4386
+ * {@link DocxDocument.getBookmarkPage} (ECMA-376 §17.16.23) and scrolls there
4387
+ * with {@link scrollToPage}. An anchor naming no known bookmark is a safe no-op
4388
+ * rather than a scroll to a guessed page.
4389
+ *
4390
+ * IX1 — returns `undefined` when `enableHyperlinks` is `false`, the single gate
4391
+ * that disables hyperlink interactivity: {@link buildDocxTextLayer} treats a
4392
+ * missing handler as "render link runs like plain runs", so no hit region,
4393
+ * cursor, tooltip, listener, or navigation is wired (a custom
4394
+ * `onHyperlinkClick` is suppressed too).
4571
4395
  */
4572
- fontFamilySym?: string;
4573
- /** Baseline shift in thousandths of a point. Positive = superscript, negative = subscript. */
4574
- baseline?: number;
4396
+ private _hyperlinkHandler;
4397
+ /** A width-measurer primed with a run's `font` used ONLY to clamp a §17.3.2.10
4398
+ * 縦中横 selection span to its drawn one-em cell (#836). Mirrors DocxViewer's
4399
+ * `_measureForFont`. Returns a length-based fallback when canvas metrics are
4400
+ * unavailable so the caller still gets a callable (the overlay then sees scale
4401
+ * 1 and leaves the span un-clamped). */
4402
+ private _measureForFont;
4403
+ /** A canvas's intended CSS box in px (the % denominators the overlay builders
4404
+ * expect). Reads the inline `style.width`/`height` set by the render path,
4405
+ * falling back to the backing-store size when unset; tolerates the `px` suffix. */
4406
+ private _canvasCssPx;
4407
+ /** Route an async render failure to `onError`, or `console.error` when none is
4408
+ * set (so failures are never fully silent), and never after teardown. */
4409
+ private _reportRenderError;
4575
4410
  /**
4576
- * Capitalisation transform ECMA-376 §21.1.2.3.13 (ST_TextCapsType).
4577
- * 'all' renders text in upper case; 'small' uses small caps (rendered as
4578
- * upper case at ~80% size when no smcp font feature is available).
4579
- * 'none' or omitted leaves the text unchanged.
4411
+ * Worker-mode slot render: dispatch `renderPageToBitmap`, transfer the result
4412
+ * via a per-slot `bitmaprenderer` context, and manage the ImageBitmap lifecycle.
4413
+ *
4414
+ * Coalescing / drop-stale (design §11):
4415
+ * - Skip if page `i` is already in flight (a scroll storm won't double-dispatch).
4416
+ * - Skip if page `i` already left the mounted window before dispatch.
4417
+ * - On resolution, if `slot` is no longer THIS page's live slot (it recycled to
4418
+ * another page, or page `i` re-mounted onto a DIFFERENT slot while this render
4419
+ * was in flight), close the orphan bitmap and skip the paint. In that
4420
+ * re-mount case a live slot for `i` still awaits a render, so once we clear
4421
+ * the in-flight guard we re-dispatch it — a page that recycled and re-mounted
4422
+ * mid-flight must never stay blank.
4423
+ * - RENDER EPOCH: the dispatch captures `this._renderEpoch`. `setScale` bumps
4424
+ * the epoch, so a resolution whose captured epoch ≠ the live epoch is STALE
4425
+ * even when the SAME slot object is still mounted for page `i` (the pool
4426
+ * reuses slot objects, so the identity check alone can't catch a zoom that
4427
+ * happened mid-flight). A moved epoch ⇒ close the orphan + re-dispatch the
4428
+ * live slot at the new scale, never paint the old-scale bitmap.
4580
4429
  */
4581
- caps?: 'none' | 'small' | 'all';
4430
+ private _renderSlotBitmap;
4582
4431
  /**
4583
- * Inter-character spacing in 100ths of a point ECMA-376 §21.1.2.3.5
4584
- * (rPr @spc). Positive values add space, negative values tighten.
4432
+ * Set the absolute px-per-pt zoom scale, clamped inline to
4433
+ * `[zoomMin ?? 0.1, zoomMax ?? 4]` (absolute bounds, XlsxViewer convention — NOT
4434
+ * multiples of the base fit; design §3 keeps the clamp in the viewer, not core),
4435
+ * then re-anchor VERTICALLY so the page currently under the viewport top stays
4436
+ * fixed. A no-op when the clamped scale is unchanged. Called BEFORE the doc is
4437
+ * loaded / the base fit is established, the clamped factor is LATCHED (IX9 F1,
4438
+ * family-unified with the single-canvas viewers) and applied by `relayout()`
4439
+ * once the layout establishes — `onScaleChange` fires then.
4440
+ *
4441
+ * FLICKER-FREE (design §7): this does NOT re-render the visible pages inline.
4442
+ * It shows an immediate CSS preview (stretch the existing bitmaps, scale the
4443
+ * overlays) and DEBOUNCES a full-resolution settle re-render for ZOOM_SETTLE_MS,
4444
+ * so a wheel/pinch burst never blanks a page and coalesces into one crisp render.
4445
+ *
4446
+ * Re-anchor (written from scratch — XlsxViewer only re-anchors horizontally):
4447
+ * capture `top = topIndex` and the intra-page fraction `intraFrac` from the
4448
+ * CURRENT range BEFORE rescale; after recomputing heights at the new scale,
4449
+ * `newScrollTop = offsets'[top] + intraFrac × heights'[top]`, clamped to
4450
+ * `[0, totalHeight' − viewportHeight]`. Because a page's height scales linearly
4451
+ * with `_scale`, the same fractional position maps exactly to the new geometry.
4452
+ *
4453
+ * CAVEAT — base fit below the floor: `relayout()` sets `_scale = base` WITHOUT
4454
+ * clamping to `[zoomMin, zoomMax]`. If the base fit is below `zoomMin` (a wide
4455
+ * page in a narrow container), the initial scale sits under the floor, but once
4456
+ * the user zooms via `setScale` the clamp pins the minimum to `zoomMin`, so they
4457
+ * can no longer return below the floor to the original base fit through this API.
4585
4458
  */
4586
- letterSpacing?: number;
4587
- /** Set for OOXML field runs (e.g. "slidenum"). When set, renderer replaces text with field value. */
4588
- fieldType?: string;
4459
+ setScale(scale: number): void;
4460
+ /** IX9 {@link ZoomableViewer} the current zoom factor, where `1` = 100% (a
4461
+ * page at its natural pt→px width). This is the viewer's absolute `_scale`
4462
+ * (`widthPt × PT_TO_PX × _scale` is the drawn width), so it reads `1` at true
4463
+ * 100% and, after the initial fit-to-width, the base fit factor. Before the
4464
+ * fit is established it reports a latched pre-load `setScale` (IX9 F1) if one
4465
+ * is pending — matching what a single-canvas viewer would show — else `1`. */
4466
+ getScale(): number;
4467
+ /** IX9 {@link ZoomableViewer} — step up to the next rung of the shared zoom
4468
+ * ladder above the current factor (clamped to `zoomMax` by {@link setScale}). */
4469
+ zoomIn(): void;
4470
+ /** IX9 {@link ZoomableViewer} — step down to the next lower ladder rung. */
4471
+ zoomOut(): void;
4589
4472
  /**
4590
- * Hyperlink target resolved from rPr > a:hlinkClick @r:id via the slide's _rels.
4591
- * For an external link this is the URL; for an internal slide jump it is the
4592
- * resolved internal part name (e.g. "../slides/slide3.xml"). Undefined for runs
4593
- * without a hyperlink. ECMA-376 §21.1.2.3.5 (CT_Hyperlink).
4473
+ * IX9 {@link ZoomableViewer} fit a page's WIDTH to the container (the classic
4474
+ * continuous-scroll "fit width"). Sets the scale to the width-fit base for the
4475
+ * current container, then re-anchors + re-renders via {@link setScale}. Defers
4476
+ * (no-op) while the container is unlaid-out. Note the `zoomMin`/`zoomMax` clamp
4477
+ * still applies, so a fit below `zoomMin` pins to `zoomMin`.
4594
4478
  */
4595
- hyperlink?: string;
4479
+ fitWidth(): void;
4596
4480
  /**
4597
- * Raw `<a:hlinkClick @action>` string (e.g. "ppaction://hlinksldjump") when
4598
- * present its presence marks {@link hyperlink} as an INTERNAL PowerPoint
4599
- * action (slide jump / first / last …) rather than an external URL. Undefined
4600
- * when the hlinkClick has no @action. ECMA-376 §21.1.2.3.5. (IX1)
4481
+ * IX9 {@link ZoomableViewer} — fit a WHOLE page (width and height) inside the
4482
+ * container so one page is visible without scrolling; takes the tighter of the
4483
+ * width/height fit. Uses the FIRST page's size (the continuous viewer's fit
4484
+ * reference, matching the base-fit convention). Defers while unlaid-out.
4601
4485
  */
4602
- hyperlinkAction?: string;
4486
+ fitPage(): void;
4487
+ /** Shared fit for {@link fitWidth}/{@link fitPage}: the width-fit factor is the
4488
+ * established base (`_baseScale`); the page-fit additionally bounds by the
4489
+ * container height against the first page's height. Applies via {@link setScale}
4490
+ * so the flicker-free re-anchor / settle path and `onScaleChange` all run. */
4491
+ private _fit;
4603
4492
  /**
4604
- * Run-level drop shadow on glyphs (`<a:rPr><a:effectLst><a:outerShdw>`),
4605
- * ECMA-376 §20.1.8.45. Independent of the shape-level shadow on `spPr`.
4606
- * Absent means no run-level shadow.
4493
+ * CSS preview of the visible window at the current `_scale` (design §7
4494
+ * mechanism 1), WITHOUT re-rendering. Slots leaving the window recycle normally;
4495
+ * slots ENTERING the window mount fresh (rendered at the current scale directly,
4496
+ * so they never need a preview); slots that STAY are repositioned and their
4497
+ * canvas + text overlay are CSS-transformed to the new size (the device buffer
4498
+ * is untouched — that is the whole point: no synchronous clear, no blank frame).
4607
4499
  */
4608
- shadow?: Shadow;
4500
+ private _previewVisible;
4609
4501
  /**
4610
- * Run-level glyph outline (`<a:rPr><a:ln w="..">`), ECMA-376 §20.1.2.2.24
4611
- * (CT_TextOutlineEffect). Renderer strokes each glyph with the given
4612
- * width / colour in addition to the normal fill. Absent means glyphs are
4613
- * fill-only.
4502
+ * CSS-preview a single already-mounted slot at the new geometry (design §7): the
4503
+ * wrapper is repositioned + sized (via `_positionSlot`), the canvas bitmap is
4504
+ * STRETCHED to the new CSS size (no `canvas.width` the device buffer, and thus
4505
+ * the drawn pixels, are left intact, just scaled by the browser), and the text
4506
+ * overlay is scaled by `newScale / renderedScale` so it tracks the stretched
4507
+ * page. `renderedScale <= 0` means the slot's first render hasn't resolved yet
4508
+ * (nothing to stretch); the pending render captured the current scale, so it
4509
+ * lands correct and no preview is needed.
4614
4510
  */
4615
- outline?: TextOutline;
4511
+ private _previewSlot;
4512
+ /** (Re)schedule the debounced settle re-render (design §7 mechanism 2). Resets
4513
+ * the timer on every call so a burst of `setScale` dispatches ONE settle
4514
+ * ZOOM_SETTLE_MS after the LAST call. Cleared in `destroy()`. */
4515
+ private _scheduleSettle;
4516
+ /** Full-resolution settle re-render of the visible window (design §7 mechanisms
4517
+ * 2+3). Re-renders each mounted slot at the current scale via the double-buffer
4518
+ * swap (main) / same-canvas transfer (worker). Both modes rebuild the text
4519
+ * overlay from the fresh render's run geometry (IX6 — worker mode collects the
4520
+ * runs off-thread via `_renderSlotBitmap`) and clear the preview transform.
4521
+ * Dispatched at the CURRENT epoch; the existing epoch gate discards it if a
4522
+ * later `setScale` supersedes it mid-render. */
4523
+ private _settleRender;
4616
4524
  /**
4617
- * Run-level text highlight / marker colour (`<a:rPr><a:highlight>`),
4618
- * ECMA-376 §21.1.2.3.4. In DrawingML this is a full CT_Color (any
4619
- * srgbClr / schemeClr / sysClr / prstClr + transforms), unlike
4620
- * WordprocessingML's fixed 16-name highlight enum so the parser already
4621
- * resolves it through the theme/clrMap. The value is a hex string without
4622
- * `#` (6-char opaque, or 8-char RRGGBBAA when an alpha transform applies);
4623
- * the renderer paints a background rectangle behind the run's glyphs.
4624
- * Absent means no highlight.
4525
+ * Settle-render one slot at the current scale (design §7 mechanism 3).
4526
+ *
4527
+ * WORKER: re-dispatch the bitmap render into the SAME canvas. The worker path
4528
+ * sizes the device buffer and `transferFromImageBitmap`s it in ONE synchronous
4529
+ * step (no await between `canvas.width = …` and the transfer), so the browser
4530
+ * never composites an intermediate blank frame no spare canvas is needed. The
4531
+ * `renderedScale === _scale` gate in `_settleRender` plus the epoch gate inside
4532
+ * `_renderSlotBitmap` keep this correct and idempotent.
4533
+ *
4534
+ * MAIN: `renderPage` (via renderDocumentToCanvas) synchronously sets
4535
+ * `canvas.width = …` (which CLEARS the backing store to blank) BEFORE its first
4536
+ * await and paints AFTER — so rendering into the on-screen canvas would flash it
4537
+ * white. Render into a SPARE off-DOM canvas instead; only once it resolves at the
4538
+ * current epoch do we swap it into the wrapper (replacing the old canvas, which is
4539
+ * DISCARDED — the pooled unit is the slot, not the canvas). The old canvas keeps
4540
+ * showing the stretched preview until the instant of the swap — blank-free.
4625
4541
  */
4626
- highlight?: string;
4627
- }
4628
- /** Run-level glyph outline. Width is in OOXML EMU (12700 EMU = 1 pt). */
4629
- interface TextOutline {
4630
- width: number;
4631
- /** Hex without '#'. Absent = inherit from text fill colour. */
4632
- color?: string;
4633
- }
4634
- interface LineBreak {
4635
- type: 'break';
4636
- }
4637
- interface RenderOptions {
4638
- width?: number;
4639
- defaultTextColor?: string | null;
4640
- dpr?: number;
4641
- majorFont?: string | null;
4642
- minorFont?: string | null;
4643
- /** Theme hyperlink colour (hex 6 chars). Used to colour hyperlink runs without an explicit colour. */
4644
- hlinkColor?: string | null;
4542
+ private _settleSlot;
4645
4543
  /**
4646
- * Lazily resolve an archive-internal asset (by zip path) to a Blob. The
4647
- * renderer uses this to fetch posters and other large embedded assets on
4648
- * demand, keeping the parse output free of inlined base64.
4544
+ * Scroll so page `index`'s top edge sits at the viewport top. Clamps `index` to
4545
+ * `[0, pageCount-1]` (the pager convention) and the resulting scrollTop to
4546
+ * `[0, totalHeight − viewportHeight]` so the last pages don't scroll past the
4547
+ * end. A no-op when nothing is loaded or the document is empty.
4548
+ *
4549
+ * `opts.behavior` ('auto' | 'smooth', default 'auto') is honoured via
4550
+ * `scrollHost.scrollTo({ top, behavior })` when the host supports it (a real
4551
+ * browser); the stub-DOM has no `scrollTo`, so the fallback sets `scrollTop`
4552
+ * directly (which is what the tests assert). We then call `_mountVisible` once.
4553
+ *
4554
+ * MOUNTING CAVEAT: synchronous mounting of the target page is guaranteed only on
4555
+ * the DEFAULT/'auto' path — there `scrollTop` has already jumped to `top`, so the
4556
+ * `_mountVisible` call reads the final scroll position and the target page's slots
4557
+ * exist immediately. With `behavior: 'smooth'` the scroll animates ASYNCHRONOUSLY:
4558
+ * `scrollTop` is still near the old position when `_mountVisible` runs, so the
4559
+ * target page mounts lazily via the animation's subsequent `scroll` events, not
4560
+ * from this call.
4649
4561
  */
4650
- fetchMedia?: (path: string) => Promise<Blob>;
4562
+ scrollToPage(index: number, opts?: {
4563
+ behavior?: 'auto' | 'smooth';
4564
+ }): void;
4565
+ /** Search the complete document, including pages outside the virtualized
4566
+ * mounted window. Matching is case-insensitive by default. */
4567
+ findText(query: string, opts?: FindMatchesOptions): Promise<FindMatch<DocxMatchLocation>[]>;
4568
+ /** Activate and reveal the next match, wrapping at the end. */
4569
+ findNext(): Promise<FindMatch<DocxMatchLocation> | null>;
4570
+ /** Activate and reveal the previous match, wrapping at the beginning. */
4571
+ findPrev(): Promise<FindMatch<DocxMatchLocation> | null>;
4572
+ /** Clear the current query and every mounted highlight. */
4573
+ clearFind(): void;
4574
+ private _activateMatch;
4575
+ private _collectPageRuns;
4576
+ private _redrawHighlights;
4577
+ private _refreshFindRuns;
4578
+ private _redrawSlotHighlights;
4651
4579
  /**
4652
- * Lazily resolve an embedded image (by zip path + MIME) to a Blob. Twin of
4653
- * {@link RenderOptions.fetchMedia} for pictures and blip fills: the renderer
4654
- * fetches raster/SVG bytes on demand and decodes them (`createImageBitmap` /
4655
- * path-keyed `<img>`), so the parse output carries only paths, never base64.
4580
+ * Re-fit the base scale on a container resize while PRESERVING the current zoom
4581
+ * multiplier (design §11), then re-anchor + re-render. A `ResizeObserver` fires
4582
+ * on any box change, but only a WIDTH change alters the fit-to-width base scale;
4583
+ * a height-only change skips the re-fit yet STILL re-mounts the visible window
4584
+ * (via `_mountVisible`), because a taller viewport reveals rows that were below
4585
+ * the fold and would otherwise stay blank until the next scroll. Empty/unloaded
4586
+ * ⇒ no-op; a still-zero width ⇒ defer.
4587
+ *
4588
+ * Zero-width recovery: a container that was 0-wide at construction never
4589
+ * established a scale (`_scaleEstablished` is false), so the first non-zero
4590
+ * resize establishes it here via `relayout()` — completing the T2 deferral.
4591
+ *
4592
+ * Re-fit math (zoom multiplier preserved):
4593
+ * mult = _scale / _prevBase (the user's zoom over the old base)
4594
+ * newScale = newBase × mult
4595
+ * Routing through `setScale(newScale)` bumps `_renderEpoch` (resize IS an epoch
4596
+ * event — T4 banner) and re-anchors + CSS-previews + debounces a settle re-render
4597
+ * of every slot at the new geometry, exactly like a zoom (design §7 flicker-free
4598
+ * path — a rapid ResizeObserver burst therefore also coalesces into one settle).
4599
+ * `setScale`'s clamp/no-op guards apply: an unchanged newScale (identical width)
4600
+ * is a no-op there — so we short-circuit BEFORE it when the fit-width is
4601
+ * unchanged (mounting the revealed window without a needless re-render), and
4602
+ * after it we call `_mountVisible` again to cover the case where the clamp made
4603
+ * `setScale` no-op yet the viewport still grew.
4656
4604
  */
4657
- fetchImage?: (path: string, mimeType: string) => Promise<Blob>;
4605
+ private _onResize;
4606
+ get topVisiblePage(): number;
4607
+ /** Return the owning engine's latest content-free package-usage snapshot. */
4608
+ getResourceMetrics(): Promise<OoxmlResourceMetrics>;
4658
4609
  /**
4659
- * When true, renderMedia draws only the poster frame play/pause badges
4660
- * and progress bars are left to the caller. Set by the pptx presentSlide
4661
- * API so its interactive handle can own all control chrome without
4662
- * the static renderer drawing a duplicate play badge.
4610
+ * Tear down the viewer: remove the DOM subtree and (only for a self-loaded
4611
+ * engine) destroy the engine. An injected engine is left intact the caller
4612
+ * owns its lifecycle. Per-slot worker ImageBitmaps are closed on recycle.
4663
4613
  */
4664
- skipMediaControls?: boolean;
4614
+ destroy(): void;
4615
+ }
4616
+ //#endregion
4617
+ //#region packages/docx/src/text-layer.d.ts
4618
+ /**
4619
+ * Build the transparent text-selection overlay for a rendered docx page: one
4620
+ * absolutely-positioned, color-transparent `<span>` per {@link DocxTextRunInfo}
4621
+ * (emitted by `renderPage`'s `onTextRun`), so the browser's native selection
4622
+ * lands on the drawn glyphs. Extracted verbatim from `DocxViewer._buildTextLayer`
4623
+ * so both the pager (DocxViewer) and the continuous-scroll viewer (DocxScrollViewer)
4624
+ * share one implementation; also public API for integrators building their own
4625
+ * overlay (design §10). IX6 — usable in BOTH render modes: worker mode collects
4626
+ * the same `DocxTextRunInfo[]` off-thread and ships it back beside the bitmap, so
4627
+ * the overlay is built from identical geometry regardless of thread.
4628
+ *
4629
+ * Every span is positioned as a PERCENTAGE of `cssWidth`/`cssHeight` (the page's
4630
+ * intended CSS-px box), never literal px, and the container's own width/height are
4631
+ * left untouched (the caller sizes it `width:100%;height:100%`). This lets the
4632
+ * overlay track the canvas's ACTUAL rendered box even when a consumer scales the
4633
+ * canvas down with external CSS (`width:100%!important; height:auto`): the
4634
+ * `display:inline-block` wrapper shrinks with the canvas, the `100%` container
4635
+ * follows, and every `%`-placed span scales with it, so nothing overflows the
4636
+ * wrapper into an ancestor's scroll area.
4637
+ *
4638
+ * @param layer the overlay div (sized `width:100%;height:100%` by the caller).
4639
+ * @param runs per-run geometry from `renderPage({ onTextRun })`.
4640
+ * @param cssWidth the page's intended CSS width (px, number) — the %
4641
+ * denominator for the x axis.
4642
+ * @param cssHeight the page's intended CSS height (px, number) — the %
4643
+ * denominator for the y axis.
4644
+ * @param onHyperlinkClick IX1 — invoked when a run carrying a resolved
4645
+ * {@link HyperlinkTarget} is clicked. A hyperlink run's
4646
+ * span keeps its transparent glyphs (the visible link
4647
+ * colour/underline is already drawn on the canvas) but
4648
+ * gains `cursor:pointer`, a `title` tooltip (the URL or
4649
+ * bookmark ref) and this click handler. A plain
4650
+ * `<span>` — not an `<a href>` — is used deliberately so
4651
+ * the browser's own navigation can never bypass the
4652
+ * caller's URL sanitisation. When omitted, link runs are
4653
+ * rendered exactly like plain runs (no click affordance).
4654
+ * @param measureForFont optional width-measurer factory (primed with a run's
4655
+ * `font`), used ONLY to clamp a §17.3.2.10 縦中横
4656
+ * (eastAsianVert) span to its drawn one-em cell (#836):
4657
+ * the span composes a `scaleX(run.w / naturalWidth)` so
4658
+ * its selection extent matches the compressed glyphs
4659
+ * instead of the run's natural ~2× width. When omitted,
4660
+ * a 縦中横 span keeps the bare rotate (no regression for
4661
+ * callers that do not thread a measurer).
4662
+ */
4663
+ declare function buildDocxTextLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], cssWidth: number, cssHeight: number, onHyperlinkClick?: (target: HyperlinkTarget) => void, measureForFont?: (font: string) => (s: string) => number): void;
4664
+ //#endregion
4665
+ //#region packages/docx/src/find-highlight-layer.d.ts
4666
+ /** One page's highlight input: the run-slices a match covers, and whether that
4667
+ * match is the active one (emphasis colour). */
4668
+ interface DocxHighlightMatch {
4669
+ slices: MatchRunSlice[];
4670
+ active: boolean;
4671
+ }
4672
+ /** Format-specific compatibility alias for the shared colour contract. */
4673
+ type DocxHighlightColors = FindHighlightColors;
4674
+ /**
4675
+ * Populate a highlight overlay layer with one box per matched run-slice.
4676
+ *
4677
+ * Every box is positioned as a PERCENTAGE of `cssWidth`/`cssHeight`, and the
4678
+ * container's own size is left untouched (`width:100%;height:100%` from the
4679
+ * caller), so the highlights track the canvas's ACTUAL rendered box even when a
4680
+ * consumer scales the canvas down with external CSS — mirroring
4681
+ * {@link buildDocxTextLayer}.
4682
+ *
4683
+ * @param layer the overlay div (cleared here; sized `100%` by the caller).
4684
+ * @param runs the page's runs (same array the page was rendered/text-layered from).
4685
+ * @param matches the page's matches (run-slices + active flag).
4686
+ * @param cssWidth the page's intended CSS width (px, number) — the x-axis % denominator.
4687
+ * @param cssHeight the page's intended CSS height (px, number) — the y-axis % denominator.
4688
+ * @param measureForFont returns a width-measurer primed with a run's `font`
4689
+ * (the viewer closes over a canvas 2d context). Kept as a
4690
+ * factory so the font is set once per run, not per glyph.
4691
+ * @param colors optional colour overrides.
4692
+ */
4693
+ declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], matches: DocxHighlightMatch[], cssWidth: number, cssHeight: number, measureForFont: (font: string) => (s: string) => number, colors?: DocxHighlightColors): void;
4694
+ declare namespace docx_d_exports {
4695
+ export { AnchorHostMetrics, AutoResizeOptions, BodyElement, BorderSpec, CellBorders, CellElement, ChartRun, ColSpec, ColumnsSpec, DocComment, DocNote, DocParagraph, DocRevision, DocRun, DocSettings, DocTable, DocTableCell, DocTableRow, DocxDocument, DocxDocumentModel, DocxHighlightColors, DocxHighlightMatch, DocxMatchLocation, DocxRunBorder, DocxScrollViewer, DocxScrollViewerOptions, DocxTextRun, DocxTextRunInfo, DocxViewer, DocxViewerOptions, EmbeddedFontRef, FieldRun, FindHighlightColors, FindMatch, FindMatchesOptions, FramePr, GradientStop, HeaderFooter, HeadersFooters, HyperlinkTarget, ImageRun, LineEnd, LineNumbering, LineSpacing, LoadOptions$2 as LoadOptions, NoteRef, NumberingInfo, OoxmlDecodedImageLimitError, OoxmlDecodedImageLimitMetric, OoxmlError, OoxmlErrorCode, OoxmlErrorSource, OoxmlErrorStage, OoxmlFormat, OoxmlResourceLimit, OoxmlResourceLimitError, OoxmlResourceLimitErrorDetails, OoxmlResourceLimits, OoxmlResourceMetric, OoxmlResourceMetrics, OoxmlResourceMetricsCheckpoint, OoxmlResourceName, OoxmlResourcePolicySnapshot, OoxmlResourceUsageSnapshot, OoxmlResourceViolation, PTabRun, PageBorderEdge, PageBorders, PageNumType, ParaBorderEdge, ParagraphBorders, PathCmd$1 as PathCmd, RenderPageOptions, RenderPageToBitmapOptions, RubyAnnotation, RunRevision, SectionGeom, SectionProps, ShapeRun, ShapeText$1 as ShapeText, ShapeTextRun$1 as ShapeTextRun, TabStop, TableBorders, TblpPr, TextPath, WireRenderPageOptions, autoResize, buildDocxHighlightLayer, buildDocxTextLayer, isOoxmlDecodedImageLimitError, noteText, openExternalHyperlink };
4665
4696
  }
4666
4697
  //#endregion
4667
- //#region dist/.types-work/pptx-B9ExAlcU.d.ts
4698
+ //#region dist/.types-work/pptx-BVpCWU0A.d.ts
4668
4699
  //#region packages/pptx/src/types.d.ts
4669
4700
  /**
4670
4701
  * Picture bullet — ECMA-376 §21.1.2.4.2 `<a:buBlip><a:blip r:embed>`. The
@@ -4892,7 +4923,7 @@ interface ShapeElement {
4892
4923
  defaultTextColor: string | null;
4893
4924
  /** Custom geometry sub-paths (set only when geometry === "custGeom").
4894
4925
  * Outer array: one entry per <a:path>; inner: path commands with coords in [0,1]. */
4895
- custGeom: PathCmd$1[][] | null;
4926
+ custGeom: PathCmd$2[][] | null;
4896
4927
  /** First adjustment value from prstGeom avLst (e.g. trapezoid inset). Range 0–100000. */
4897
4928
  adj: number | null;
4898
4929
  /** Second adjustment value from prstGeom avLst (e.g. arrow head width). Range 0–100000. */
@@ -5192,7 +5223,7 @@ interface PictureElement {
5192
5223
  * Path2D and `ctx.clip()` before drawing the bitmap so the image is
5193
5224
  * trimmed to the laptop / device silhouette declared in the file.
5194
5225
  */
5195
- custGeom?: PathCmd$1[][] | null;
5226
+ custGeom?: PathCmd$2[][] | null;
5196
5227
  /**
5197
5228
  * Drop shadow from `spPr > effectLst > outerShdw`. A `p:pic`'s `spPr` is
5198
5229
  * `CT_ShapeProperties` (ECMA-376 §19.3.1.37), so the same effects shapes
@@ -6596,10 +6627,10 @@ type PptxHighlightColors = FindHighlightColors;
6596
6627
  */
6597
6628
  declare function buildPptxHighlightLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], matches: PptxHighlightMatch[], cssWidth: number, cssHeight: number, measureForFont: (font: string) => (s: string) => number, colors?: PptxHighlightColors): void;
6598
6629
  declare namespace pptx_d_exports {
6599
- export { AutoResizeOptions, Bevel3d, BlipBullet, Bullet, Camera3d, ChartElement, ChartModel, ChartSeries, DimOptions, Fill, FillRect, FindHighlightColors, FindMatch, FindMatchesOptions, Glow, GradientFill, GradientStop, HiddenSlideMode, HyperlinkTarget, ImageFill, LightRig, LineBreak, LoadOptions$1 as LoadOptions, MediaElement, NoFill, OoxmlDecodedImageLimitError, OoxmlDecodedImageLimitMetric, OoxmlError, OoxmlErrorCode, OoxmlErrorSource, OoxmlErrorStage, OoxmlFormat, OoxmlResourceLimit, OoxmlResourceLimitError, OoxmlResourceLimitErrorDetails, OoxmlResourceLimits, OoxmlResourceMetric, OoxmlResourceMetrics, OoxmlResourceMetricsCheckpoint, OoxmlResourceName, OoxmlResourcePolicySnapshot, OoxmlResourceUsageSnapshot, OoxmlResourceViolation, Paragraph, PathCmd$1 as PathCmd, PictureElement, PptxComment, PptxHighlightColors, PptxHighlightMatch, PptxMatchLocation, PptxPresentation, PptxScrollViewer, PptxScrollViewerOptions, PptxTextRunInfo, PptxViewer, PptxViewerOptions, Presentation, PresentationHandle, Reflection, RenderOptions, RenderSlideOptions, RenderSlideToBitmapOptions, Rot3d, Scene3d, Shadow, ShapeElement, Slide, SlideElement, SoftEdge, SolidFill, Sp3d, SpaceLine, Stroke, TabStop, TableCell, TableElement, TableRow, TextBody, TextRect, TextRun, TextRunCallback, TextRunData, TileInfo, autoResize, buildPptxHighlightLayer, buildPptxTextLayer, isOoxmlDecodedImageLimitError, openExternalHyperlink, renderSlide };
6630
+ export { AutoResizeOptions, Bevel3d, BlipBullet, Bullet, Camera3d, ChartElement, ChartModel, ChartSeries, DimOptions, Fill, FillRect, FindHighlightColors, FindMatch, FindMatchesOptions, Glow, GradientFill, GradientStop$1 as GradientStop, HiddenSlideMode, HyperlinkTarget, ImageFill, LightRig, LineBreak, LoadOptions$1 as LoadOptions, MediaElement, NoFill, OoxmlDecodedImageLimitError, OoxmlDecodedImageLimitMetric, OoxmlError, OoxmlErrorCode, OoxmlErrorSource, OoxmlErrorStage, OoxmlFormat, OoxmlResourceLimit, OoxmlResourceLimitError, OoxmlResourceLimitErrorDetails, OoxmlResourceLimits, OoxmlResourceMetric, OoxmlResourceMetrics, OoxmlResourceMetricsCheckpoint, OoxmlResourceName, OoxmlResourcePolicySnapshot, OoxmlResourceUsageSnapshot, OoxmlResourceViolation, Paragraph, PathCmd$2 as PathCmd, PictureElement, PptxComment, PptxHighlightColors, PptxHighlightMatch, PptxMatchLocation, PptxPresentation, PptxScrollViewer, PptxScrollViewerOptions, PptxTextRunInfo, PptxViewer, PptxViewerOptions, Presentation, PresentationHandle, Reflection, RenderOptions, RenderSlideOptions, RenderSlideToBitmapOptions, Rot3d, Scene3d, Shadow, ShapeElement, Slide, SlideElement, SoftEdge, SolidFill, Sp3d, SpaceLine, Stroke, TabStop$1 as TabStop, TableCell, TableElement, TableRow, TextBody, TextRect, TextRun, TextRunCallback, TextRunData, TileInfo, autoResize, buildPptxHighlightLayer, buildPptxTextLayer, isOoxmlDecodedImageLimitError, openExternalHyperlink, renderSlide };
6600
6631
  }
6601
6632
  //#endregion
6602
- //#region dist/.types-work/xlsx-DJYn-XbI.d.ts
6633
+ //#region dist/.types-work/xlsx-Dyp8TxbZ.d.ts
6603
6634
  //#region packages/xlsx/src/types.d.ts
6604
6635
  interface Workbook {
6605
6636
  sheets: SheetMeta[];