@silurus/ooxml 0.71.0 → 0.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,11 +61,18 @@ declare interface BlipBullet {
61
61
  /** MIME type of the blip at {@link BlipBullet.imagePath} (e.g. `image/png`). */
62
62
  mimeType: string;
63
63
  /**
64
- * `<a:buSzPct val>` (ECMA-376 §21.1.2.4.3) as a percentage of the text size
64
+ * `<a:buSzPct val>` (ECMA-376 §21.1.2.4.9) as a percentage of the text size
65
65
  * (100 = same size). `null` when no explicit `<a:buSzPct>` is present, in
66
66
  * which case the renderer uses the spec default of 100%.
67
67
  */
68
68
  sizePct: number | null;
69
+ /**
70
+ * `<a:buSzPts val>` (ECMA-376 §21.1.2.4.10) — an ABSOLUTE marker size in
71
+ * points, independent of the text size. Mutually exclusive with {@link
72
+ * BlipBullet.sizePct} (both are the one `EG_TextBulletSize` choice). Omitted
73
+ * when no explicit `<a:buSzPts>` is present; when present it takes precedence.
74
+ */
75
+ sizePts?: number;
69
76
  }
70
77
 
71
78
  declare type BodyElement = {
@@ -114,6 +121,14 @@ declare type BodyElement = {
114
121
  * separately from `geom` because a section may inherit its geometry yet
115
122
  * still restart / re-format its page numbers. */
116
123
  pageNumType?: PageNumType | null;
124
+ /** ECMA-376 §17.6.20 `<w:textDirection w:val>` — this ENDING section's
125
+ * flow direction (TRANSITIONAL ST_TextDirection, same enum and semantics
126
+ * as {@link SectionProps.textDirection}), so a vertical (tbRl/btLr)
127
+ * non-final section can coexist with a horizontal final section (issue
128
+ * #1000). Absent ⇒ horizontal ("lrTb" is collapsed by the parser).
129
+ * Carried separately from `geom` (like `pageNumType`) because a section
130
+ * may inherit its page geometry yet still set its own flow direction. */
131
+ textDirection?: string | null;
117
132
  };
118
133
 
119
134
  declare interface Border {
@@ -145,17 +160,23 @@ declare interface BorderSpec {
145
160
  /**
146
161
  * Populate a highlight overlay layer with one box per matched run-slice.
147
162
  *
148
- * @param layer the overlay div (cleared and re-sized to the canvas here).
163
+ * Every box is positioned as a PERCENTAGE of `cssWidth`/`cssHeight`, and the
164
+ * container's own size is left untouched (`width:100%;height:100%` from the
165
+ * caller), so the highlights track the canvas's ACTUAL rendered box even when a
166
+ * consumer scales the canvas down with external CSS — mirroring
167
+ * {@link buildDocxTextLayer}.
168
+ *
169
+ * @param layer the overlay div (cleared here; sized `100%` by the caller).
149
170
  * @param runs the page's runs (same array the page was rendered/text-layered from).
150
171
  * @param matches the page's matches (run-slices + active flag).
151
- * @param canvasCssWidth the rendered canvas's CSS width (e.g. `"700px"`).
152
- * @param canvasCssHeight the rendered canvas's CSS height.
172
+ * @param cssWidth the page's intended CSS width (px, number) — the x-axis % denominator.
173
+ * @param cssHeight the page's intended CSS height (px, number) — the y-axis % denominator.
153
174
  * @param measureForFont returns a width-measurer primed with a run's `font`
154
175
  * (the viewer closes over a canvas 2d context). Kept as a
155
176
  * factory so the font is set once per run, not per glyph.
156
177
  * @param colors optional colour overrides.
157
178
  */
158
- declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], matches: DocxHighlightMatch[], canvasCssWidth: string, canvasCssHeight: string, measureForFont: (font: string) => (s: string) => number, colors?: DocxHighlightColors): void;
179
+ declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], matches: DocxHighlightMatch[], cssWidth: number, cssHeight: number, measureForFont: (font: string) => (s: string) => number, colors?: DocxHighlightColors): void;
159
180
 
160
181
  /**
161
182
  * Build the transparent text-selection overlay for a rendered docx page: one
@@ -168,11 +189,21 @@ declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRu
168
189
  * the same `DocxTextRunInfo[]` off-thread and ships it back beside the bitmap, so
169
190
  * the overlay is built from identical geometry regardless of thread.
170
191
  *
171
- * @param layer the overlay div (position:relative parent expected).
192
+ * Every span is positioned as a PERCENTAGE of `cssWidth`/`cssHeight` (the page's
193
+ * intended CSS-px box), never literal px, and the container's own width/height are
194
+ * left untouched (the caller sizes it `width:100%;height:100%`). This lets the
195
+ * overlay track the canvas's ACTUAL rendered box even when a consumer scales the
196
+ * canvas down with external CSS (`width:100%!important; height:auto`): the
197
+ * `display:inline-block` wrapper shrinks with the canvas, the `100%` container
198
+ * follows, and every `%`-placed span scales with it, so nothing overflows the
199
+ * wrapper into an ancestor's scroll area.
200
+ *
201
+ * @param layer the overlay div (sized `width:100%;height:100%` by the caller).
172
202
  * @param runs per-run geometry from `renderPage({ onTextRun })`.
173
- * @param canvasCssWidth the rendered canvas's CSS width (e.g. `"700px"`), used
174
- * to size the overlay to match the canvas.
175
- * @param canvasCssHeight the rendered canvas's CSS height.
203
+ * @param cssWidth the page's intended CSS width (px, number) — the %
204
+ * denominator for the x axis.
205
+ * @param cssHeight the page's intended CSS height (px, number) — the %
206
+ * denominator for the y axis.
176
207
  * @param onHyperlinkClick IX1 — invoked when a run carrying a resolved
177
208
  * {@link HyperlinkTarget} is clicked. A hyperlink run's
178
209
  * span keeps its transparent glyphs (the visible link
@@ -192,18 +223,23 @@ declare function buildDocxHighlightLayer(layer: HTMLDivElement, runs: DocxTextRu
192
223
  * a 縦中横 span keeps the bare rotate (no regression for
193
224
  * callers that do not thread a measurer).
194
225
  */
195
- declare function buildDocxTextLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], canvasCssWidth: string, canvasCssHeight: string, onHyperlinkClick?: (target: HyperlinkTarget) => void, measureForFont?: (font: string) => (s: string) => number): void;
226
+ declare function buildDocxTextLayer(layer: HTMLDivElement, runs: DocxTextRunInfo[], cssWidth: number, cssHeight: number, onHyperlinkClick?: (target: HyperlinkTarget) => void, measureForFont?: (font: string) => (s: string) => number): void;
196
227
 
197
228
  /**
198
229
  * Populate a highlight overlay layer with a box per matched run-slice, grouped
199
230
  * by shape frame (with the shape's rotation) so each box lands on the drawn
200
231
  * glyphs.
201
232
  *
202
- * @param layer the overlay div (cleared + re-sized here).
233
+ * All coordinates are PERCENTAGES of `cssWidth`/`cssHeight`, and the container's
234
+ * own size is left untouched (`width:100%;height:100%` from the caller), so the
235
+ * highlights track the canvas's ACTUAL rendered box even when a consumer scales
236
+ * the canvas down with external CSS — mirroring {@link buildPptxTextLayer}.
237
+ *
238
+ * @param layer the overlay div (cleared here; sized `100%` by the caller).
203
239
  * @param runs the slide's runs (same array the slide was rendered from).
204
240
  * @param matches the slide's matches (run-slices + active flag).
205
- * @param cssWidth rendered canvas CSS width (px, number).
206
- * @param cssHeight rendered canvas CSS height (px, number).
241
+ * @param cssWidth the slide's intended CSS width (px, number) — the % denominator.
242
+ * @param cssHeight the slide's intended CSS height (px, number) — the % denominator.
207
243
  * @param measureForFont returns a width-measurer primed with a run's font.
208
244
  * @param colors optional colour overrides.
209
245
  */
@@ -228,16 +264,32 @@ declare function buildPptxHighlightLayer(layer: HTMLDivElement, runs: PptxTextRu
228
264
  * (no hyperlink) is byte-identical to before. A JS click handler is used rather
229
265
  * than an `<a href>` so the URL never bypasses the viewer's sanitisation.
230
266
  *
231
- * @param layer the overlay div.
267
+ * The overlay's coordinates are all PERCENTAGES of `cssWidth`/`cssHeight` (the
268
+ * slide's intended CSS-px box), never literal px, and the container's own
269
+ * `width`/`height` are left untouched (the caller sizes it `width:100%;
270
+ * height:100%` so it fills the wrapper). This lets the overlay track the canvas's
271
+ * ACTUAL rendered box even when a consumer scales the canvas down with external
272
+ * CSS (e.g. `width:100%!important;height:auto`): the wrapper (and therefore the
273
+ * `100%` container) shrinks with the canvas, and every `%`-placed child scales
274
+ * with it, so nothing overflows the wrapper into an ancestor's scroll area.
275
+ *
276
+ * @param layer the overlay div (sized `width:100%;height:100%` by the caller).
232
277
  * @param runs per-run + per-shape geometry from `renderSlide({ onTextRun })`.
233
- * @param cssWidth the rendered canvas's CSS width (px, number).
234
- * @param cssHeight the rendered canvas's CSS height (px, number).
278
+ * @param cssWidth the slide's intended CSS width (px, number) — the % denominator.
279
+ * @param cssHeight the slide's intended CSS height (px, number) — the % denominator.
235
280
  * @param onHyperlinkClick called with the run's resolved {@link HyperlinkTarget}
236
281
  * when a hyperlink span is clicked. Omit to leave links
237
282
  * non-interactive (spans stay plain, selectable text).
238
283
  */
239
284
  declare function buildPptxTextLayer(layer: HTMLDivElement, runs: PptxTextRunInfo[], cssWidth: number, cssHeight: number, onHyperlinkClick?: (target: HyperlinkTarget) => void): void;
240
285
 
286
+ /**
287
+ * A paragraph's bullet marker. For `char`, the marker size is EITHER `sizePct`
288
+ * (a percentage of the run size — ECMA-376 §21.1.2.4.9 `<a:buSzPct>`) OR `sizePts`
289
+ * (an absolute size in points — §21.1.2.4.10 `<a:buSzPts>`), never both: they are
290
+ * the one `EG_TextBulletSize` xsd:choice. `sizePts` is optional (absent when no
291
+ * `<a:buSzPts>` was declared); when present it takes precedence over `sizePct`.
292
+ */
241
293
  declare type Bullet = {
242
294
  type: 'none';
243
295
  } | {
@@ -247,11 +299,13 @@ declare type Bullet = {
247
299
  char: string;
248
300
  color: string | null;
249
301
  sizePct: number | null;
302
+ sizePts?: number;
250
303
  fontFamily: string | null;
251
304
  } | {
252
305
  type: 'autoNum';
253
306
  numType: string;
254
307
  startAt: number | null;
308
+ color: string | null;
255
309
  };
256
310
 
257
311
  /**
@@ -678,6 +732,14 @@ declare interface ChartModel {
678
732
  title: string | null;
679
733
  categories: string[];
680
734
  series: ChartSeries[];
735
+ /**
736
+ * §21.2.2.227 `<c:varyColors val="1"/>` on a SINGLE-series bar/column chart:
737
+ * color each data point (bar) from the theme/palette sequence and list one
738
+ * legend entry per point, matching Office. Set by the shared parser ONLY for
739
+ * that non-pie, single-series case (pie/doughnut already vary by point via
740
+ * `chartType` + `dataPointColors`); absent/false otherwise.
741
+ */
742
+ varyColors?: boolean | null;
681
743
  /** Show data labels on bars / points / slices. */
682
744
  showDataLabels: boolean;
683
745
  /** Explicit Y-axis minimum (OOXML `<c:valAx><c:min>`). */
@@ -1056,8 +1118,9 @@ declare interface ChartModel {
1056
1118
  * core `renderChart` consumes (identical to what pptx/xlsx pass), so a docx
1057
1119
  * chart draws at the same quality through the same code path. `widthPt`/
1058
1120
  * `heightPt` are the `<wp:extent>` natural size. An inline chart flows as an
1059
- * inline box of that size; an anchored chart (§20.4.2.3) is painted at its
1060
- * absolute page box by `renderAnchorImages` — both via `renderChart`. */
1121
+ * inline box of that size; an anchored chart (§20.4.2.3) is painted via
1122
+ * `registerAnchorFloats` when it wraps text, or by `renderAnchorImages` for
1123
+ * wrapNone/no-wrap anchors — all paths use `renderChart`. */
1061
1124
  declare interface ChartRun {
1062
1125
  chart: ChartModel;
1063
1126
  widthPt: number;
@@ -1069,6 +1132,39 @@ declare interface ChartRun {
1069
1132
  anchorYPt?: number;
1070
1133
  anchorXFromMargin?: boolean;
1071
1134
  anchorYFromPara?: boolean;
1135
+ /**
1136
+ * Wrap mode for anchored charts (ECMA-376 §20.4.2.16/.17):
1137
+ * "square" | "topAndBottom" | "none" | "tight" | "through"
1138
+ * Inline charts and undetermined cases leave this undefined. The renderer
1139
+ * treats "tight" and "through" as "square", matching anchored images.
1140
+ */
1141
+ wrapMode?: string;
1142
+ /** Padding top (pt). Anchor-only (ECMA-376 §20.4.2.16/.17). */
1143
+ distTop?: number;
1144
+ /** Padding bottom (pt). Anchor-only (ECMA-376 §20.4.2.16/.17). */
1145
+ distBottom?: number;
1146
+ /** Padding left (pt). Anchor-only (ECMA-376 §20.4.2.16/.17). */
1147
+ distLeft?: number;
1148
+ /** Padding right (pt). Anchor-only (ECMA-376 §20.4.2.16/.17). */
1149
+ distRight?: number;
1150
+ /** wrapText attribute: "bothSides" | "left" | "right" | "largest". */
1151
+ wrapSide?: string;
1152
+ /**
1153
+ * ECMA-376 §20.4.2.3 `wp:anchor/@allowOverlap`. The parser omits this
1154
+ * field when true, so renderers must read it as `allowOverlap ?? true`.
1155
+ */
1156
+ allowOverlap?: boolean;
1157
+ /** ECMA-376 §20.4.3.1 wp:align horizontal: "left" | "center" | "right" |
1158
+ * "inside" | "outside". */
1159
+ anchorXAlign?: string | null;
1160
+ /** Vertical equivalent of anchorXAlign: "top" | "center" | "bottom". */
1161
+ anchorYAlign?: string | null;
1162
+ /**
1163
+ * ECMA-376 §20.4.3.2 `<wp:positionH/@relativeFrom>` / §20.4.3.5
1164
+ * `<wp:positionV/@relativeFrom>` — the raw anchor placement containers.
1165
+ */
1166
+ anchorXRelativeFrom?: string | null;
1167
+ anchorYRelativeFrom?: string | null;
1072
1168
  }
1073
1169
 
1074
1170
  declare interface ChartSeries {
@@ -1393,12 +1489,20 @@ declare interface DocParagraph {
1393
1489
  shading?: string | null;
1394
1490
  /** Force a page break before this paragraph (w:pageBreakBefore) */
1395
1491
  pageBreakBefore?: boolean;
1396
- /** Suppress spacing between adjacent same-style paragraphs (w:contextualSpacing) */
1492
+ /** ECMA-376 §17.3.1.9 `<w:contextualSpacing>` — between adjacent SAME-style
1493
+ * paragraphs, a toggling paragraph drops its OWN contribution to the
1494
+ * collapsed inter-paragraph gap (per-side, Word-adjudicated — issue #1015;
1495
+ * see the renderer's `contextualSpacingAdjust`). */
1397
1496
  contextualSpacing?: boolean;
1398
1497
  /** Keep paragraph on same page as the next paragraph (w:keepNext) */
1399
1498
  keepNext?: boolean;
1400
1499
  /** Keep all lines of this paragraph on the same page (w:keepLines) */
1401
1500
  keepLines?: boolean;
1501
+ /** ECMA-376 §17.3.1.29 + §17.3.2.41 — the paragraph MARK's resolved `w:vanish`
1502
+ * (hidden text). An inkless paragraph whose mark is vanished collapses to zero
1503
+ * height in the normal/print view (hidden-text off), the same way the parser
1504
+ * strips hidden runs; the paginator drops it whole. Absent = mark is visible. */
1505
+ markVanish?: boolean;
1402
1506
  /** Widow/orphan control (w:widowControl). ECMA-376 default is true. */
1403
1507
  widowControl?: boolean;
1404
1508
  /** Paragraph borders (w:pBdr) */
@@ -1410,6 +1514,17 @@ declare interface DocParagraph {
1410
1514
  /** Default font family resolved from the style chain. Used to size empty
1411
1515
  * paragraphs (no runs) with the intended font's line metrics. */
1412
1516
  defaultFontFamily?: string | null;
1517
+ /** Default East Asian font family resolved from the style chain. Empty /
1518
+ * anchor-only paragraph marks in East Asian documents use this axis for line
1519
+ * metrics instead of the ASCII fallback. */
1520
+ defaultFontFamilyEastAsia?: string | null;
1521
+ /** ECMA-376 §17.3.1.29 — the paragraph MARK run's resolved `w:color` (direct
1522
+ * pPr/rPr → pStyle chain → docDefaults; hex 6 without `#`, lowercased; an
1523
+ * explicit `auto` surfaces as absent, §17.3.2.6). Word formats a numbering
1524
+ * marker with the level rPr (§17.9.24) layered over the mark's run
1525
+ * properties, so the renderer uses this as the marker-color fallback when
1526
+ * {@link NumberingInfo.color} is absent. */
1527
+ paragraphMarkColor?: string | null;
1413
1528
  /**
1414
1529
  * ECMA-376 §17.3.1.6 `<w:bidi>` — right-to-left paragraph. `true` = RTL,
1415
1530
  * `false` = explicitly LTR, absent = unspecified (inherit). The renderer uses
@@ -1487,6 +1602,17 @@ declare interface DocSettings {
1487
1602
  * tab stops generated after all custom stops. `undefined` ⇒ the renderer
1488
1603
  * uses the spec default of 720 twips (36pt). */
1489
1604
  defaultTabStop?: number;
1605
+ /** §17.15.1.18 `w:characterSpacingControl@w:val` — East Asian punctuation /
1606
+ * character-spacing control. */
1607
+ characterSpacingControl?: string;
1608
+ /** §17.15.3.1 `w:compat/w:useFELayout` — Far East layout compatibility. */
1609
+ useFeLayout?: boolean;
1610
+ /** §17.15.3.1 `w:compat/w:balanceSingleByteDoubleByteWidth` — balance
1611
+ * single-byte and double-byte widths for East Asian layout. */
1612
+ balanceSingleByteDoubleByteWidth?: boolean;
1613
+ /** §17.15.3.1 `w:compat/w:adjustLineHeightInTable` — apply the section
1614
+ * document-grid line pitch to text in table cells. */
1615
+ adjustLineHeightInTable?: boolean;
1490
1616
  }
1491
1617
 
1492
1618
  declare interface DocTable {
@@ -1568,6 +1694,9 @@ declare interface DocTableRow {
1568
1694
  * lower bound; "exact" = fixed clip. */
1569
1695
  rowHeightRule: 'auto' | 'atLeast' | 'exact' | string;
1570
1696
  isHeader: boolean;
1697
+ /** ECMA-376 §17.4.6 `<w:cantSplit>` — when true, the row must not be split
1698
+ * across page boundaries. Omitted/false rows may split at page boundaries. */
1699
+ cantSplit?: boolean;
1571
1700
  }
1572
1701
 
1573
1702
  export declare namespace docx {
@@ -1829,6 +1958,16 @@ declare interface DocxDocumentModel {
1829
1958
  * entry is absent or classified as "auto".
1830
1959
  */
1831
1960
  fontFamilyClasses?: Record<string, string>;
1961
+ /**
1962
+ * ECMA-376 §17.8.3.29 — per-font pitch from `word/fontTable.xml`
1963
+ * (`<w:pitch>`, ST_Pitch §17.18.66): font name → "fixed" | "variable" |
1964
+ * "default". Present only for fonts that declare `<w:pitch>`. The renderer
1965
+ * pairs this with {@link fontFamilyClasses}: a `family="modern"` face is
1966
+ * treated as monospace ONLY when its pitch is "fixed"; "variable" /
1967
+ * "default" / absent fall through to name-pattern / CJK-sans classification
1968
+ * (§17.8.3.10 `family` classifies the design, not the pitch — issue #855).
1969
+ */
1970
+ fontFamilyPitches?: Record<string, string>;
1832
1971
  /** ECMA-376 §17.8.3.3-.6 — embedded fonts from `word/fontTable.xml`, resolved
1833
1972
  * to their `.odttf` part paths + fontKey. The viewer de-obfuscates (§17.8.1)
1834
1973
  * and registers each as a FontFace before pagination so text measures/draws
@@ -2131,6 +2270,12 @@ declare class DocxScrollViewer implements ZoomableViewer {
2131
2270
  * {@link DocxDocument.getBookmarkPage} (ECMA-376 §17.16.23) and scrolls there
2132
2271
  * with {@link scrollToPage}. An anchor naming no known bookmark is a safe no-op
2133
2272
  * rather than a scroll to a guessed page.
2273
+ *
2274
+ * IX1 — returns `undefined` when `enableHyperlinks` is `false`, the single gate
2275
+ * that disables hyperlink interactivity: {@link buildDocxTextLayer} treats a
2276
+ * missing handler as "render link runs like plain runs", so no hit region,
2277
+ * cursor, tooltip, listener, or navigation is wired (a custom
2278
+ * `onHyperlinkClick` is suppressed too).
2134
2279
  */
2135
2280
  private _hyperlinkHandler;
2136
2281
  /** A width-measurer primed with a run's `font` — used ONLY to clamp a §17.3.2.10
@@ -2139,6 +2284,10 @@ declare class DocxScrollViewer implements ZoomableViewer {
2139
2284
  * unavailable so the caller still gets a callable (the overlay then sees scale
2140
2285
  * 1 and leaves the span un-clamped). */
2141
2286
  private _measureForFont;
2287
+ /** A canvas's intended CSS box in px (the % denominators the overlay builders
2288
+ * expect). Reads the inline `style.width`/`height` set by the render path,
2289
+ * falling back to the backing-store size when unset; tolerates the `px` suffix. */
2290
+ private _canvasCssPx;
2142
2291
  /** Route an async render failure to `onError`, or `console.error` when none is
2143
2292
  * set (so failures are never fully silent), and never after teardown. */
2144
2293
  private _reportRenderError;
@@ -2435,6 +2584,13 @@ declare interface DocxScrollViewerOptions extends Omit<RenderPageOptions, 'onTex
2435
2584
  * new tab via core `openExternalHyperlink` (sanitised, noopener,noreferrer);
2436
2585
  * internal → jump to the page whose text contains the bookmark (best-effort). */
2437
2586
  onHyperlinkClick?: (target: HyperlinkTarget) => void;
2587
+ /** IX1 — master switch for hyperlink interactivity. Default `true`. When
2588
+ * `false`, the hyperlink machinery is not wired at all: no overlay hit region
2589
+ * is installed for link runs, so there is no pointer cursor, no title tooltip,
2590
+ * no default navigation (external new-tab / internal bookmark jump), and
2591
+ * `onHyperlinkClick` is never called. Links still render exactly as authored
2592
+ * but are inert, like plain text. */
2593
+ enableHyperlinks?: boolean;
2438
2594
  /** Error callback. When set, `load()` invokes it and resolves (otherwise the
2439
2595
  * error is rethrown — shared viewer error contract). It ALSO fires for async
2440
2596
  * per-slot render failures (both main `renderPage` and worker
@@ -2533,12 +2689,22 @@ declare interface DocxTextRun {
2533
2689
  /** ECMA-376 §17.3.2.20 `<w:lang w:bidi>` — complex-script (RTL) language tag,
2534
2690
  * lower-cased (e.g. "ar-sa", "ae-ar"). Drives Word's AN digit ordering. */
2535
2691
  langBidi?: string;
2692
+ /** ECMA-376 §17.3.2.34 `<w:snapToGrid>` — false opts this run out of the
2693
+ * section character grid; absent inherits participation. */
2694
+ snapToGrid?: boolean;
2536
2695
  /** ECMA-376 §17.3.2.35 `<w:spacing w:val>` — character-spacing adjustment in
2537
2696
  * POINTS (signed): the extra pitch added after each character before the next
2538
2697
  * is rendered. The renderer feeds it to `ctx.letterSpacing` on BOTH the
2539
2698
  * measure and paint passes so line breaking / pagination stay consistent.
2540
2699
  * Absent ⇒ no extra pitch. */
2541
2700
  charSpacing?: number;
2701
+ /** ECMA-376 §17.3.2.14 `<w:fitText>` — manual run-width target in TWIPS
2702
+ * (`w:val`, 1/20 pt) plus the optional `w:id` that links consecutive runs
2703
+ * into one region. The arbitrary-precision XSD integer id is serialized as a
2704
+ * string; numeric synthetic inputs remain supported for layout tests and
2705
+ * direct model construction. An id-less run is always standalone. */
2706
+ fitTextVal?: number;
2707
+ fitTextId?: string | number;
2542
2708
  /** ECMA-376 §17.3.2.43 `<w:w w:val>` — horizontal text scale as a FRACTION of
2543
2709
  * normal character width (0.67 = 67%, 2.0 = 200%). Stretches each glyph's
2544
2710
  * width, not the gap between glyphs. Absent ⇒ 100%. */
@@ -2592,6 +2758,9 @@ declare interface DocxTextRunInfo {
2592
2758
  fontSize: number;
2593
2759
  /** CSS `font` shorthand used for canvas drawing (e.g. `"bold 16px Arial"`). */
2594
2760
  font: string;
2761
+ /** Uniform per-code-point pitch in CSS px used to draw a horizontal run.
2762
+ * Absent when the pitch is zero or the run uses vertical / 縦中横 paint. */
2763
+ letterSpacingPx?: number;
2595
2764
  /** ECMA-376 §17.6.20 (tbRl) — when the page is vertical the canvas is the
2596
2765
  * physical landscape page rotated +90° at paint, so this run's `x`/`y` are the
2597
2766
  * PHYSICAL top-left the overlay span must sit at, and `transform` is the CSS
@@ -2801,6 +2970,11 @@ declare class DocxViewer implements ZoomableViewer {
2801
2970
  /** Draw the find-highlight boxes for the current page from its runs. Clears
2802
2971
  * the overlay when there is no active find. */
2803
2972
  private _buildHighlightLayer;
2973
+ /** The canvas's intended CSS box in px (the % denominators the overlay builders
2974
+ * expect). Reads the inline `style.width`/`height` set by the render path
2975
+ * (which mirror the render's logical size), falling back to the backing-store
2976
+ * dimensions when unset. Parsing tolerates the trailing `px`. */
2977
+ private _canvasCssPx;
2804
2978
  /** A width-measurer primed with `font`, backed by a private 1×1 canvas so it
2805
2979
  * never disturbs the visible canvas's context state. */
2806
2980
  private _measureForFont;
@@ -2819,6 +2993,12 @@ declare class DocxViewer implements ZoomableViewer {
2819
2993
  * {@link DocxDocument.getBookmarkPage} (ECMA-376 §17.16.23) and jumps there
2820
2994
  * with {@link goToPage}. An anchor naming no known bookmark is a safe no-op
2821
2995
  * rather than a jump to a guessed page.
2996
+ *
2997
+ * IX1 — returns `undefined` when `enableHyperlinks` is `false`, the single gate
2998
+ * that disables hyperlink interactivity: {@link buildDocxTextLayer} treats a
2999
+ * missing handler as "render link runs like plain runs", so no hit region,
3000
+ * cursor, tooltip, listener, or navigation is wired (a custom
3001
+ * `onHyperlinkClick` is suppressed too).
2822
3002
  */
2823
3003
  private _hyperlinkHandler;
2824
3004
  }
@@ -2848,6 +3028,15 @@ declare interface DocxViewerOptions extends RenderPageOptions, LoadOptions_4 {
2848
3028
  * new tab via core `openExternalHyperlink` (sanitised, noopener,noreferrer);
2849
3029
  * internal → jump to the page whose text contains the bookmark (best-effort). */
2850
3030
  onHyperlinkClick?: (target: HyperlinkTarget) => void;
3031
+ /** IX1 — master switch for hyperlink interactivity. Default `true`. When
3032
+ * `false`, the hyperlink machinery is not wired at all: no overlay hit region
3033
+ * is installed for link runs, so there is no pointer cursor, no title tooltip,
3034
+ * no default navigation (external new-tab / internal bookmark jump), and
3035
+ * `onHyperlinkClick` is never called. Links still render exactly as authored
3036
+ * (their colour/underline are painted on the canvas) but are inert, like plain
3037
+ * text. Set it to disable clickable links entirely — e.g. in a preview where
3038
+ * navigation must not leave the current view. */
3039
+ enableHyperlinks?: boolean;
2851
3040
  /** Called on parse or render errors. */
2852
3041
  onError?: (err: Error) => void;
2853
3042
  }
@@ -3245,6 +3434,14 @@ declare interface ImageFill {
3245
3434
  tile?: TileInfo;
3246
3435
  /** `a:blip > a:alphaModFix@amt` as a fraction (0.0–1.0). Absent = opaque. */
3247
3436
  alpha?: number;
3437
+ /**
3438
+ * ECMA-376 §20.1.8.23 `<a:duotone>` recolour, resolved to its two endpoint
3439
+ * colours (through the slide theme). Absent ⇒ no duotone. When present the
3440
+ * renderer maps the blip's luminance ramp between the two colours (core
3441
+ * `applyDuotone`) — the same recolour a `<p:pic>` duotone applies, wired onto
3442
+ * the picture-FILL path (§20.1.8.14) by issue #889.
3443
+ */
3444
+ duotone?: Duotone;
3248
3445
  }
3249
3446
 
3250
3447
  declare interface ImageRun {
@@ -3846,6 +4043,19 @@ declare interface NumberingInfo {
3846
4043
  * this family. Absent ⇒ the renderer falls back to
3847
4044
  * {@link NumberingInfo.fontFamily}. */
3848
4045
  fontFamilyEastAsia?: string | null;
4046
+ /** ECMA-376 §17.9.24 — the numbering level rPr's `w:color` (hex 6 without
4047
+ * `#`, lowercased). Colors the marker glyph only, never the paragraph's
4048
+ * runs. Absent ⇒ the renderer falls back to
4049
+ * {@link DocParagraph.paragraphMarkColor} (§17.3.1.29 — Word layers the
4050
+ * level rPr over the paragraph mark's run properties) and finally to its
4051
+ * default ink. An explicit `w:val="auto"` is absent here + {@link colorAuto}. */
4052
+ color?: string | null;
4053
+ /** ECMA-376 §17.3.2.6 / ST_HexColorAuto (§17.18.39) — true when the level
4054
+ * rPr carries an EXPLICIT `w:color w:val="auto"`. Auto names no concrete
4055
+ * color but is not "unset": it breaks the paragraph-mark fallback, so the
4056
+ * marker draws the automatic (default) ink instead of
4057
+ * {@link DocParagraph.paragraphMarkColor}. */
4058
+ colorAuto?: boolean;
3849
4059
  /** ECMA-376 §17.9.9/§17.9.20 — when the level uses a `<w:lvlPicBulletId>`,
3850
4060
  * the marker is this image (zip path, e.g. `word/media/image1.gif`), drawn in
3851
4061
  * place of {@link NumberingInfo.text}. Absent ⇒ ordinary text/glyph marker. */
@@ -4043,6 +4253,14 @@ declare interface Paragraph_2 extends Paragraph {
4043
4253
  * emits an effective boolean.
4044
4254
  */
4045
4255
  eaLnBrk: boolean;
4256
+ /**
4257
+ * `<a:pPr defTabSz>` (ECMA-376 §21.1.2.2.7) — the default tab-stop interval in
4258
+ * EMU. When a `\t` has no reachable explicit `a:tabLst` stop, it advances to
4259
+ * the next multiple of this grid (issue #1006). Absent ⇒ the renderer uses the
4260
+ * PowerPoint universal default of 914400 EMU (1 inch). Omitted from JSON when
4261
+ * the parser found no explicit value.
4262
+ */
4263
+ defTabSz?: number;
4046
4264
  }
4047
4265
 
4048
4266
  declare interface ParagraphBorders {
@@ -5007,6 +5225,15 @@ declare class PptxScrollViewer implements ZoomableViewer {
5007
5225
  * to the target slide via {@link scrollToSlide} once the action resolves to a
5008
5226
  * slide index (a jump resolving to no reachable slide is a safe no-op).
5009
5227
  */
5228
+ /**
5229
+ * IX1 — the click handler passed to the text-layer overlay, or `undefined` when
5230
+ * `enableHyperlinks` is `false`. This is the single gate that disables hyperlink
5231
+ * interactivity: {@link buildPptxTextLayer} renders link runs exactly like plain
5232
+ * runs when no handler is supplied, so no hit region, cursor, tooltip, listener,
5233
+ * or navigation is wired (a custom `onHyperlinkClick` is suppressed too). When
5234
+ * enabled, the returned handler dispatches through {@link _onHyperlinkClick}.
5235
+ */
5236
+ private _hyperlinkHandler;
5010
5237
  private _onHyperlinkClick;
5011
5238
  /** Populate an internal {@link HyperlinkTarget}'s `slideIndex` from its `ref`
5012
5239
  * via the engine's stamped part names. Relative `hlinkshowjump` verbs are
@@ -5173,6 +5400,13 @@ declare interface PptxScrollViewerOptions extends Omit<RenderSlideOptions, 'onTe
5173
5400
  * instead and takes NO default action.
5174
5401
  */
5175
5402
  onHyperlinkClick?: (target: HyperlinkTarget) => void;
5403
+ /** IX1 — master switch for hyperlink interactivity. Default `true`. When
5404
+ * `false`, the hyperlink machinery is not wired at all: the overlay's link
5405
+ * spans are non-interactive, so there is no pointer cursor, no title tooltip,
5406
+ * no default navigation (external new-tab / internal slide jump), and
5407
+ * `onHyperlinkClick` is never called. Links still render exactly as authored
5408
+ * but are inert, like plain text. */
5409
+ enableHyperlinks?: boolean;
5176
5410
  }
5177
5411
 
5178
5412
  /** Information about a rendered text segment for building a transparent selection overlay. */
@@ -5412,6 +5646,15 @@ declare class PptxViewer implements ZoomableViewer {
5412
5646
  /** Rebuild the highlight overlay for the current slide from cached runs. */
5413
5647
  private _redrawHighlights;
5414
5648
  private _buildTextLayer;
5649
+ /**
5650
+ * IX1 — the click handler passed to the text-layer overlay, or `undefined` when
5651
+ * `enableHyperlinks` is `false`. This is the single gate that disables hyperlink
5652
+ * interactivity: {@link buildPptxTextLayer} renders link runs exactly like plain
5653
+ * runs when no handler is supplied, so no hit region, cursor, tooltip, listener,
5654
+ * or navigation is wired (a custom `onHyperlinkClick` is suppressed too). When
5655
+ * enabled, the returned handler dispatches through {@link _onHyperlinkClick}.
5656
+ */
5657
+ private _hyperlinkHandler;
5415
5658
  /**
5416
5659
  * IX1/IX-nav hyperlink click dispatch. An internal target is first *enriched*
5417
5660
  * with its resolved 0-based `slideIndex` (via
@@ -5508,6 +5751,14 @@ declare interface PptxViewerOptions extends RenderOptions, LoadOptions {
5508
5751
  * viewer calls this instead and takes NO default action.
5509
5752
  */
5510
5753
  onHyperlinkClick?: (target: HyperlinkTarget) => void;
5754
+ /** IX1 — master switch for hyperlink interactivity. Default `true`. When
5755
+ * `false`, the hyperlink machinery is not wired at all: the overlay's link
5756
+ * spans are non-interactive, so there is no pointer cursor, no title tooltip,
5757
+ * no default navigation (external new-tab / internal slide jump), and
5758
+ * `onHyperlinkClick` is never called. Links still render exactly as authored
5759
+ * (theme `hlink` colour + underline are painted on the canvas) but are inert,
5760
+ * like plain text. */
5761
+ enableHyperlinks?: boolean;
5511
5762
  }
5512
5763
 
5513
5764
  declare interface Presentation {
@@ -5794,6 +6045,10 @@ declare interface RubyAnnotation {
5794
6045
  text: string;
5795
6046
  /** Annotation font size in pt. Word stores this as half-points in `<w:hps>`. */
5796
6047
  fontSizePt: number;
6048
+ /** Distance “between the phonetic guide base text and the phonetic guide
6049
+ * text” in pt. Word stores this as half-points in `<w:hpsRaise>`
6050
+ * (ECMA-376 §17.3.3.12). */
6051
+ hpsRaisePt?: number;
5797
6052
  }
5798
6053
 
5799
6054
  declare interface Run {
@@ -6067,6 +6322,14 @@ declare interface ShapeElement {
6067
6322
  /** `<a:sp3d>` 3D shape properties (ECMA-376 §20.1.5.12). Parsed but not
6068
6323
  * rendered in Phase A. */
6069
6324
  sp3d?: Sp3d;
6325
+ /** `<p:nvSpPr><p:cNvPr @id>` — DrawingML cNvPr id. Present for file-authored
6326
+ * shapes (the attribute is schema-required); absent on parser-synthesized
6327
+ * shapes such as the SmartArt data-model fallback. */
6328
+ id?: string;
6329
+ /** `<p:nvSpPr><p:cNvPr @name>` — author-visible shape name (e.g. "Title 1").
6330
+ * The SmartArt data-model fallback synthesizes `name: "SmartArt"` with no
6331
+ * {@link ShapeElement.id} (see smartart-fallback-contrast.ts). */
6332
+ name?: string;
6070
6333
  /** Shape-level hyperlink target resolved from `<p:cNvPr><a:hlinkClick @r:id>`
6071
6334
  * via slide _rels (ECMA-376 §21.1.2.3.5). For an external link this is the
6072
6335
  * URL; for an internal slide jump it is the resolved internal part name.
@@ -6216,7 +6479,7 @@ declare interface ShapeRun {
6216
6479
  groupHeightPt?: number | null;
6217
6480
  /** Draw behind text when true (wp:anchor behindDoc="1"). */
6218
6481
  behindDoc?: boolean;
6219
- /** Document-order index within a group; lower values render first. */
6482
+ /** ECMA-376 §20.4.2.3 `wp:anchor/@relativeHeight`: lower values render first. */
6220
6483
  zOrder: number;
6221
6484
  /** Normalized [0,1] custom-geometry sub-paths. Empty when `presetGeometry`
6222
6485
  * is set; the renderer chooses between buildCustomPath and buildShapePath. */
@@ -6224,8 +6487,10 @@ declare interface ShapeRun {
6224
6487
  /** OOXML <a:prstGeom prst> name (e.g. "rect", "ellipse", "rtTriangle").
6225
6488
  * When set the renderer calls core's buildShapePath with `adjValues`. */
6226
6489
  presetGeometry?: string | null;
6227
- /** Up to four <a:gd name="adj{n}"> values from prstGeom/avLst (0–100000). */
6228
- adjValues?: number[];
6490
+ /** <a:gd name="adj{n}"> values from prstGeom/avLst in adj1..adj8 order.
6491
+ * `null` preserves omitted named guides so the preset engine can use the
6492
+ * geometry's default for that index. */
6493
+ adjValues?: Array<number | null>;
6229
6494
  fill: ShapeFill | null;
6230
6495
  stroke: string | null;
6231
6496
  strokeWidth?: number;
@@ -6275,6 +6540,14 @@ declare interface ShapeRun {
6275
6540
  textInsetT?: number;
6276
6541
  textInsetR?: number;
6277
6542
  textInsetB?: number;
6543
+ /** ECMA-376 §20.1.10.83 ST_TextVerticalType — the text-body flow direction from
6544
+ * `<wps:bodyPr vert>` / `<a:bodyPr vert>`. Recognised vertical values:
6545
+ * "vert" (all glyphs 90° CW, chars T→B, lines R→L), "vert270" (all glyphs 270°
6546
+ * CW = 90° CCW, chars B→T, lines L→R), and "eaVert" (East-Asian upright: CJK
6547
+ * stands upright, non-EA rotated 90°, chars T→B, lines R→L). "horz"/absent ⇒
6548
+ * horizontal (unchanged). Unrecognised values ("mongolianVert", "wordArtVert",
6549
+ * …) fall back to horizontal until implemented. */
6550
+ textVert?: string | null;
6278
6551
  /** ECMA-376 Part 4 §19.1.2.23 `<v:textpath>` — WordArt text laid on the
6279
6552
  * shape path (a text watermark). When set the renderer draws this string,
6280
6553
  * scaled to fill the box (`fitshape`), rotated by {@link ShapeRun.rotation},
@@ -6330,6 +6603,8 @@ declare interface ShapeText_2 {
6330
6603
  * (image blocks / legacy single-format paragraphs). Absent for image-only
6331
6604
  * paragraphs. */
6332
6605
  runs?: ShapeTextRun_2[];
6606
+ /** ECMA-376 §17.9 paragraph numbering for text-box paragraphs. */
6607
+ numbering?: NumberingInfo | null;
6333
6608
  alignment: string;
6334
6609
  /** ECMA-376 §17.3.1.33 `<w:spacing w:before>` of this text-box paragraph, in
6335
6610
  * pt — reserved ABOVE the paragraph inside the box. Absent/0 ⇒ no offset. */
@@ -6367,6 +6642,19 @@ declare interface ShapeText_2 {
6367
6642
  * base direction for the UAX#9 reordering pass (the body renderer reads the
6368
6643
  * identical field). */
6369
6644
  bidi?: boolean;
6645
+ /** ECMA-376 §17.3.1.9 `<w:contextualSpacing>` — resolved through the style
6646
+ * chain in the parser. When set, this text-box paragraph drops its OWN
6647
+ * contribution to the collapsed gap against an ADJACENT paragraph that
6648
+ * shares its {@link ShapeText.styleId} (per-side, Word-adjudicated — issue
6649
+ * #1015; identical to {@link DocParagraph.contextualSpacing} via the
6650
+ * renderer's `contextualSpacingAdjust`). Absent ⇒ no suppression. */
6651
+ contextualSpacing?: boolean;
6652
+ /** Resolved paragraph style id of this text-box paragraph — the explicit
6653
+ * `<w:pStyle>`, else the document default paragraph style, else "Normal" (the
6654
+ * same stable id {@link DocParagraph.styleId} carries). Paired with
6655
+ * {@link ShapeText.contextualSpacing} to group adjacent same-style paragraphs
6656
+ * for §17.3.1.9. */
6657
+ styleId?: string | null;
6370
6658
  /** Zip path of an inline image inside this text-box paragraph
6371
6659
  * (`<w:drawing><wp:inline><a:blip r:embed>`), e.g. `word/media/image1.emf`.
6372
6660
  * Absent for a text-only paragraph. */
@@ -6438,6 +6726,8 @@ declare interface ShapeTextRun_2 {
6438
6726
  fontFamilyEastAsia?: string | null;
6439
6727
  bold?: boolean;
6440
6728
  italic?: boolean;
6729
+ /** ECMA-376 §17.3.3.25 ruby annotation (furigana) for text-box runs. */
6730
+ ruby?: RubyAnnotation | null;
6441
6731
  }
6442
6732
 
6443
6733
  declare interface SharedString {
@@ -6766,7 +7056,10 @@ declare interface TableRow {
6766
7056
  }
6767
7057
 
6768
7058
  declare interface TabStop {
6769
- /** Position in EMU from the left edge of the text area (after lIns) */
7059
+ /** Position in EMU from the LEADING text-inset edge of the text area
7060
+ * logical, not physical (ECMA-376 §21.1.2.1): the left edge (after lIns)
7061
+ * in an LTR paragraph, the right edge (before rIns) in an RTL
7062
+ * (`<a:pPr rtl="1">`) paragraph. */
6770
7063
  pos: number;
6771
7064
  /** Alignment: "l" | "r" | "ctr" | "dec" */
6772
7065
  algn: string;
@@ -7803,7 +8096,13 @@ declare class XlsxViewer implements ZoomableViewer {
7803
8096
  * match the renderer's `hyperlinkMap` exactly (`${hl.row}:${hl.col}`). */
7804
8097
  private buildHyperlinkMap;
7805
8098
  /** IX1 — the hyperlink at a cell, or null. `getCellAt` returns 1-based
7806
- * {row,col}, matching the parser/renderer keying. */
8099
+ * {row,col}, matching the parser/renderer keying.
8100
+ *
8101
+ * Returns null unconditionally when `enableHyperlinks` is `false`: this is the
8102
+ * single gate that disables hyperlink interactivity. Both consumers — the
8103
+ * pointermove pointer-cursor affordance and the click dispatch
8104
+ * ({@link dispatchHyperlink}) — funnel through this hit-test, so a null result
8105
+ * means no cursor change, no default navigation, and no `onHyperlinkClick`. */
7807
8106
  private hyperlinkAtCell;
7808
8107
  /**
7809
8108
  * IX1 — dispatch a click on a hyperlinked cell. Builds a
@@ -7990,6 +8289,13 @@ declare interface XlsxViewerOptions extends LoadOptions_2 {
7990
8289
  * default handler's job, so a blocked scheme still reaches a custom callback).
7991
8290
  */
7992
8291
  onHyperlinkClick?: (target: HyperlinkTarget) => void;
8292
+ /** IX1 — master switch for hyperlink interactivity. Default `true`. When
8293
+ * `false`, the cell hit-test reports no hyperlink under any cell, so hyperlink
8294
+ * interactivity is disabled entirely: no pointer cursor over a link, no default
8295
+ * navigation (external new-tab / internal sheet jump), and `onHyperlinkClick`
8296
+ * is never called. Hyperlinked cells still render exactly as authored but are
8297
+ * inert. */
8298
+ enableHyperlinks?: boolean;
7993
8299
  /**
7994
8300
  * Color of the cell-selection highlight. A single CSS color drives both the
7995
8301
  * selection rectangle's border (drawn in this color) and its fill (the same
@@ -8135,6 +8441,14 @@ declare class XlsxWorkbook {
8135
8441
  * validation-list expansion use). Returns `''` before the workbook is loaded.
8136
8442
  */
8137
8443
  cellText(ws: Worksheet, cell: Cell): string;
8444
+ /**
8445
+ * Render a sheet viewport into `target`. Note: `opts.fetchImage` is ignored
8446
+ * here — image bytes always come from this workbook's own archive through its
8447
+ * stable per-instance loader, whose closure identity keys the shared decoded
8448
+ * caches, the render-pass lease, and {@link destroy}'s cache drops. Callers
8449
+ * needing a custom byte source should use the standalone
8450
+ * `renderWorksheetViewport` orchestrator directly.
8451
+ */
8138
8452
  renderViewport(target: HTMLCanvasElement | OffscreenCanvas, sheetIndex: number, viewport: ViewportRange, opts?: RenderViewportOptions): Promise<void>;
8139
8453
  /**
8140
8454
  * Render a sheet viewport and return it as an ImageBitmap (both modes; in