← All formats

PPTX

Render PPTX files in the browser with JavaScript.

The examples below render a sample PowerPoint deck (.pptx) in your browser and show the corresponding TypeScript.

Story · Demo

Single viewer with navigation

Hand PptxViewer a canvas and it manages parsing, rendering and the current slide. Step through with the built-in nextSlide() / prevSlide().

sample-1.pptx live · WASM
import { PptxViewer } from '@silurus/ooxml/pptx';

// The built-in viewer tracks the current slide for you.
const viewer = new PptxViewer(canvas, { width: 960, useGoogleFonts: true });
await viewer.load('/sample.pptx');

nextBtn.addEventListener('click', () => viewer.nextSlide());
prevBtn.addEventListener('click', () => viewer.prevSlide());

Story · ScrollView

Scroll through every slide

Drive the headless PptxPresentation engine to render each slide into its own canvas, stacked in a scroll container — no built-in viewer required.

sample-1.pptx live · WASM
import { PptxPresentation } from '@silurus/ooxml/pptx';

// Headless engine — render every slide into a canvas you control.
const presentation = await PptxPresentation.load('/sample.pptx');

for (let i = 0; i < presentation.slideCount; i++) {
  const canvas = document.createElement('canvas');
  scroller.appendChild(canvas);
  await presentation.renderSlide(canvas, i, { width: 1100 });
}

Story · ThumbnailGrid

Thumbnail overview

The same engine renders slides at any size. Drop them into a grid at thumbnail width and wire up click handlers for navigation.

sample-1.pptx live · WASM
import { PptxPresentation } from '@silurus/ooxml/pptx';

// Render each slide small, wire up navigation.
const presentation = await PptxPresentation.load('/sample.pptx');

for (let i = 0; i < presentation.slideCount; i++) {
  const thumb = document.createElement('canvas');
  thumb.addEventListener('click', () => open(i));
  grid.appendChild(thumb);
  await presentation.renderSlide(thumb, i, { width: 320 });
}

Story · MasterDetail

Thumbnail rail + large preview

Combine both: a PptxPresentation for the thumbnail rail and a PptxViewer for the detail pane. Click a thumbnail to jump the preview with goToSlide().

sample-1.pptx live · WASM
import { PptxPresentation, PptxViewer } from '@silurus/ooxml/pptx';

// Parse once, then lend the loaded engine to every view that needs it.
const presentation = await PptxPresentation.load('/sample.pptx');

// A large preview on the right borrows the engine and cannot acquire another source.
const viewer = PptxViewer.fromPresentation(detailCanvas, presentation, {
  width: 960,
  enableTextSelection: true,
});
await viewer.goToSlide(0);

// The thumbnail rail on the left renders from that same engine.
for (let i = 0; i < presentation.slideCount; i++) {
  const thumb = document.createElement('canvas');
  thumb.addEventListener('click', () => viewer.goToSlide(i));  // jump the preview
  rail.appendChild(thumb);
  await presentation.renderSlide(thumb, i, { width: 200 });
}

window.addEventListener('pagehide', () => {
  viewer.destroy();
  presentation.destroy(); // borrowed engines remain caller-owned
}, { once: true });

API reference

Options & methods

Every public option and method, straight from the source. Types omitted for brevity are exported from the package — your editor will autocomplete the rest.

Choose one loading mode

Normal case: construct the Viewer, then call viewer.load(source). The Viewer owns the parsed engine, supports replacement loads, and destroys it during teardown.

Shared-engine case: load PptxPresentation once and create each view with fromPresentation(). This is mutually exclusive with Viewer loading: load() is unavailable, and the caller must destroy the borrowed engine after every view has been destroyed.

Loading ownership and execution mode are separate choices. Both loading modes support both execution modes:

Loading ownershipmode: 'main'mode: 'worker'
Viewer-owned Pass mode: 'main' to the Viewer, then call load(source). Pass mode: 'worker' to the Viewer, then call load(source).
Shared engine PptxPresentation.load(source, { mode: 'main' }), then fromPresentation(). PptxPresentation.load(source, { mode: 'worker' }), then fromPresentation().

On the shared-engine path, the engine's mode is authoritative: fromPresentation() deliberately does not accept a mode and cannot switch an already-loaded engine. Here, main means parsing in a Worker and rendering on the main thread; worker means both parsing and rendering in a Worker. Worker rendering requires Worker and OffscreenCanvas support and may have format-specific feature limitations.

Error handling

Typed container, resource-limit and decoded-image failures expose stable fields. Other failures remain ordinary JavaScript errors. The callback and Promise delivery rules are part of the public contract.

Open the error reference →

Selection context for AI and external tools

Viewer selection remains read-only UI state. getSelectionContext() turns the current focus into a detached, resource-bounded snapshot; every format can notify you through onSelectionContextChange when that snapshot may have changed.

Open the selection & AI guide →

PptxViewer

Opinionated single-canvas viewer. Hand it a <canvas>; it owns parsing, rendering and the current slide.

new PptxViewer(canvas: HTMLCanvasElement, options?: PptxViewerOptions)

Options

NameTypeDefaultDescription
Properties
width number 960 Canvas CSS width in px; height is derived from the slide aspect ratio.
dpr number devicePixelRatio Device pixel ratio for the backing store (crispness on HiDPI).
useGoogleFonts boolean false Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy.
password string undefined Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded.
enableTextSelection boolean false Overlay a transparent text layer so users can select & copy slide text.
enableElementSelection boolean false Enable read-only slide-element selection with a non-editable outline and element context; no editor model is exposed.
elementHitTolerance number 6 Straight-line hit tolerance in CSS pixels for element context clicks.
findHighlightColors { match?: string; active?: string } yellow / orange CSS backgrounds for ordinary and active find matches. Values are applied verbatim; use an alpha color to keep the canvas text visible through the overlay.
enableMediaPlayback boolean false Make embedded audio/video interactive (the viewer draws its own play chrome).
hiddenSlideMode 'show' | 'skip' | 'dim' 'show' How hidden slides (<p:sld show="0">, §19.3.1.38) are presented. show draws them like any other slide; skip makes sequential navigation (nextSlide/prevSlide and the initial load) jump over them while keeping absolute indices unchanged (an explicit goToSlide to a hidden slide is still honored); dim draws them under a translucent overlay (the PowerPoint thumbnail look).
hiddenSlideDim Partial<DimOptions> { color: '#ffffff', opacity: 0.6 } Overrides for the dim overlay, merged over the default white 60% wash. A partial so it stays in sync if DimOptions gains a field.
maxZipEntryBytes number resource policy default Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration →
resourceLimits OoxmlResourceLimits 128 MiB per entry / 256 MiB distinct total / 4,096 entries Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields →
debug boolean false Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection.
math MathRenderer undefined Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation.
mode 'main' | 'worker' 'main' 'main' renders on the main thread (default). 'worker' renders the viewer off the main thread and paints transferred ImageBitmaps, improving UI responsiveness and containing parser/renderer state away from Window. It is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Scroll, tabs, zoom, selection and find remain available; equations require 'main'. Requires Worker + OffscreenCanvas, and frame transfer can add latency.
zoomMin / zoomMax number 0.1 / 4 Zoom factor bounds for setScale / fitWidth / fitPage (10%–400%).
enableHyperlinks boolean true Master switch for hyperlink interactivity. Set false to disable it entirely: no hit-testing, no pointer cursor over links, no default navigation, and onHyperlinkClick is never called. Links still render exactly as authored but are inert, like plain text.
Event handlers
onSelectionContextChange (context: PptxSelectionContext | null) => void Receive bounded detached text-selection or element-click context for AI/MCP handoff. Text selection takes precedence; this callback does not enable element hit-testing.
onContextMenu (event: ViewerContextMenuEvent<PptxSelectionContext>) => void Called synchronously for the native contextmenu event. Use originalEvent.preventDefault() before returning to replace the browser menu; getContext() starts one memoized target lookup on first call. Omit the callback to keep native browser behavior unchanged.
onResourceMetrics (metrics: OoxmlResourceMetrics) => void Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results.
onScaleChange (scale: number) => void Called when the zoom factor changes (setScale / fitWidth / fitPage / zoomIn / zoomOut), with the clamped factor (1 = 100%).
onHyperlinkClick (target: HyperlinkTarget) => void Called when a hyperlink is clicked. target is { kind: 'external', url } or { kind: 'internal', ref, slideIndex? }. When supplied, the callback fully owns the click (the default external-open / internal-navigation is not run). External URLs are scheme-sanitized (http / https / mailto / tel only); internal targets resolve to a docx bookmark / pptx slide jump / xlsx defined name or cell.
onSlideChange (index: number, total: number) => void Called after a slide finishes rendering.
onError (err: Error) => void Receives Viewer-managed failures that have no directly awaitable result, such as virtualized rendering or embedded-media playback. load(), navigation, and other awaitable operations reject their own Promise whether or not this callback is supplied; the same failure is never delivered twice. Background failures are logged with console.error when the callback is omitted. Narrow stable cases with OoxmlError, OoxmlResourceLimitError or OoxmlDecodedImageLimitError; other failures remain Error values and message text is not a stable discriminator. Error reference →

Methods

static fromPresentation(canvas, presentation, options?): Omit<PptxViewer, "load"> Synchronously create a Viewer that borrows an already-loaded presentation. Render with goToSlide(); destroy() leaves the presentation open.
load(source: string | ArrayBuffer): Promise<void> Load a Viewer-owned URL or ArrayBuffer and render the first slide.
goToSlide(index: number): Promise<void> Render a specific slide (0-indexed, clamped).
nextSlide(): Promise<void> Advance one slide.
prevSlide(): Promise<void> Go back one slide.
getScale(): number The current zoom factor (1 = 100%).
setScale(scale: number): Promise<void> Set the absolute zoom factor (1 = 100%), clamped to [zoomMin, zoomMax]; re-renders at the new size and fires onScaleChange when it changes. View-only.
fitWidth(): Promise<void> Fit the content WIDTH to the host container and re-render (routes through setScale). Defers when nothing is loaded or the container is unlaid-out.
fitPage(): Promise<void> Fit the WHOLE content (width and height) inside the container so it is visible without scrolling — takes the tighter of the two fits. Defers when unloaded / unlaid-out.
findText(query: string, opts?: { caseSensitive?: boolean }): Promise<FindMatch<PptxMatchLocation>[]> Full-text search across the whole document; highlights every hit and returns them in document order. Each match carries matchIndex, the matched text, and its location. Case-insensitive by default.
findNext(): Promise<FindMatch<PptxMatchLocation> | null> Move to the next match (wrap-around), navigate to it if needed, and draw it in the active-match colour. Returns the now-active match, or null when there are none. Call findText first.
findPrev(): Promise<FindMatch<PptxMatchLocation> | null> Move to the previous match (wrap-around from first to last).
clearFind(): void Clear all highlights and reset the find state.
get slideIndex(): number Current slide index.
get slideCount(): number Total slides (0 until loaded).
get hiddenSlideMode(): "show" | "skip" | "dim" The current hidden-slide mode.
setHiddenSlideMode(mode: "show" | "skip" | "dim"): Promise<void> Switch the hidden-slide mode at runtime and re-render. Entering skip while on a hidden slide advances to the nearest visible slide.
get visibleSlideCount(): number Number of non-hidden slides (the absolute slideCount is unchanged).
getNotes(slideIndex: number): string | null Speaker-notes text for a slide (0-based); null when the slide has no notes part or the index is out of range.
get canvasElement(): HTMLCanvasElement The underlying canvas.
getSelectionContext(options?: PptxSelectionContextOptions): PptxSelectionContext | null Return the current bounded, JSON-serializable text or element focus snapshot. Throws after destroy().
getResourceMetrics(): Promise<OoxmlResourceMetrics> Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output.
destroy(): void Tear down the worker and release resources.

PptxPresentation

Headless engine — parse once, render any slide into any canvas you supply (scroll views, thumbnail grids, master–detail).

await PptxPresentation.load(source, options?)

Options

NameTypeDefaultDescription
Properties
useGoogleFonts boolean false Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy.
password string undefined Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded.
wasmUrl string | URL bundled asset Override the URL the parser worker fetches the WebAssembly module from. By default each format resolves the *_parser_bg.wasm asset that ships next to its bundle (relative to the module URL); set this to serve it from a CDN or a self-hosted path instead (a relative value resolves against the document URL). Pointing it at a mismatched or missing file makes load() reject when the worker instantiates it.
maxZipEntryBytes number resource policy default Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration →
resourceLimits OoxmlResourceLimits 128 MiB per entry / 256 MiB distinct total / 4,096 entries Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields →
debug boolean false Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection.
workerTimeoutMs number unlimited Reject the parse if the worker does not answer within this many ms — an opt-in safety net for a wedged / crashed worker that would otherwise leave load() pending forever. Unlimited by default (a large document with heavy media can legitimately take tens of seconds). A worker that throws or fails to load already rejects immediately regardless; this only covers the "silent, never-responds" case.
math MathRenderer undefined Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation.
mode 'main' | 'worker' 'main' 'main' parses in a worker and renders on the main thread (default). 'worker' also renders inside the worker; the main thread only paints the returned ImageBitmap. This contains parser/renderer state and many failures away from Window, but a Worker is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Requires Worker + OffscreenCanvas. Canvas-target render methods are unavailable in 'worker' mode, equations require 'main', and transferring each frame can add latency.
Event handlers
onResourceMetrics (metrics: OoxmlResourceMetrics) => void Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results.

Methods

static load(source, options?): Promise<PptxPresentation> Parse a deck from a URL or ArrayBuffer.
get slideCount(): number Total slides.
renderSlide(canvas, index, opts?: { width?, dpr?, onTextRun?, dim? }): Promise<void> Render one slide into the given canvas at the given width. onTextRun receives each rendered segment as PptxTextRunInfo, including the source shape’s slide-local shapeId when authored, so callers can build a transparent selection overlay or stable shape mapping; dim (a DimOptions) paints a translucent wash over the finished slide (hidden-slide dimming). Equations render when a math engine was passed to load. Unavailable in mode: "worker" — use renderSlideToBitmap.
renderSlideToBitmap(index, opts?: { width?, dpr?, dim? }): Promise<ImageBitmap> Render one slide and return it as an ImageBitmap (both modes; in worker mode the render runs off the main thread). dim paints a translucent overlay over the slide (hidden-slide dimming). Equations are skipped in mode: "worker" (they require mode: "main"). The bitmap is caller-owned: pass it to transferFromImageBitmap (which consumes it) or call bitmap.close().
presentSlide(canvas, index, opts?: PresentSlideOptions): Promise<PresentationHandle> Render a slide and attach canvas-native audio/video playback, returning a handle with play() / pause() / destroy(). Initial render and media acquisition failures reject this Promise. PresentSlideOptions.onError observes decode or playback failures that occur only after the handle has been returned. Works in both modes — in mode: "worker" the base slide and text-run geometry are produced off-thread and the video overlay is composited on the main thread.
getNotes(slideIndex: number): string | null Speaker-notes text for a slide (0-based; ECMA-376 §13.3.5). Returns null when the slide has no notes part or the index is out of range.
get slideWidth(): number Slide width in EMU (0 until loaded).
get slideHeight(): number Slide height in EMU (0 until loaded).
get mode(): "main" | "worker" The render mode this engine was loaded with. A borrowed engine’s mode decides whether slides render via renderSlide (main) or renderSlideToBitmap (worker).
getElementContextAt(slideIndex, point, options?): Promise<PptxElementContext | null> Return compact context for the topmost transformed element frame at a slide-EMU point in either mode (line segments use tolerance). Includes master/layout/slide provenance, never editor tree indexes or mutable elements.
getResourceMetrics(): Promise<OoxmlResourceMetrics> Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output.
destroy(): void Release the worker.

PptxScrollViewer

Container-owning continuous-scroll viewer. Takes a <div> (not a canvas) and renders the whole deck as one vertically-scrolling, virtualized surface (only the visible window + overscan is mounted). Zoom is view-only.

new PptxScrollViewer(container: HTMLElement, options?: PptxScrollViewerOptions)

Options

NameTypeDefaultDescription
Properties
width number container width Base fit width in CSS px. Default: the container width at first non-zero layout.
gap number 16 Vertical gap (px) between consecutive slides.
paddingTop / paddingBottom number gap Desk padding (px) above the first slide / below the last. Pass 0 for a flush edge.
paddingLeft / paddingRight number gap Horizontal desk gutters (px); also shrink the container-derived fit width so a slide sits inside them at 100%. Pass 0 for a flush edge.
overscan number 1 Slides kept mounted beyond the viewport on each side.
background string undefined CSS background for the scroll surface (the desk behind/between slides). Default transparent (the container shows through).
pageShadow string | false '0 1px 3px rgba(0,0,0,0.2)' CSS box-shadow painted on every slide canvas. A spread-only ring (e.g. 0 0 0 1px #c8ccd0) gives a crisp 1px border look. false disables it (flat slides).
enableZoom boolean true Enable Ctrl/⌘ + wheel (and trackpad pinch) zoom. View-only.
zoomMin / zoomMax number 0.1 / 4 Absolute zoom scale bounds (10%–400%).
refitOnResize boolean true Re-fit to the container width when it resizes. Set false to preserve an absolute scale independently of viewport width; explicit fitWidth() / fitPage() still work.
enableTextSelection boolean false Overlay a transparent, selectable text layer per slide for native copy in both render modes.
enableElementSelection boolean false Enable read-only element selection on mounted slide canvases with a non-editable outline and element context.
elementHitTolerance number 6 Straight-line hit tolerance in CSS pixels.
findHighlightColors { match?: string; active?: string } yellow / orange CSS backgrounds for ordinary and active find matches. Values are applied verbatim; use an alpha color to keep the canvas text visible through the overlay.
enableMediaPlayback boolean false Make embedded audio/video interactive inside the real viewport plus mediaOverscan. Other mounted slides remain static and selectable without allocating media blobs or RAF loops.
mediaOverscan number 1 Slides beyond the real viewport that may keep interactive media handles. Independent from the general overscan used for mounted canvases/text overlays.
enableHyperlinks boolean true Master switch for hyperlink interactivity. Set false to disable it entirely: no hit-testing, no pointer cursor over links, no default navigation, and onHyperlinkClick is never called. Links still render exactly as authored but are inert, like plain text.
useGoogleFonts boolean false Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy.
password string undefined Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded.
maxZipEntryBytes number resource policy default Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration →
resourceLimits OoxmlResourceLimits 128 MiB per entry / 256 MiB distinct total / 4,096 entries Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields →
debug boolean false Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection.
math MathRenderer undefined Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation.
dpr number devicePixelRatio Device pixel ratio for the backing store (crispness on HiDPI).
mode 'main' | 'worker' 'main' 'main' parses in a worker and renders on the main thread (default). 'worker' also renders inside the worker; the main thread only paints the returned ImageBitmap. This contains parser/renderer state and many failures away from Window, but a Worker is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Requires Worker + OffscreenCanvas. Canvas-target render methods are unavailable in 'worker' mode, equations require 'main', and transferring each frame can add latency.
Event handlers
onSelectionContextChange (context: PptxSelectionContext | null) => void Receive bounded detached text or element context for external AI/MCP integrations. This callback does not enable element hit-testing.
onContextMenu (event: ViewerContextMenuEvent<PptxSelectionContext>) => void Called synchronously for the native contextmenu event. Use originalEvent.preventDefault() before returning to replace the browser menu; getContext() starts one memoized target lookup on first call. Omit the callback to keep native browser behavior unchanged.
onHyperlinkClick (target: HyperlinkTarget) => void Called when a hyperlink is clicked. target is { kind: 'external', url } or { kind: 'internal', ref, slideIndex? }. When supplied, the callback fully owns the click (the default external-open / internal-navigation is not run). External URLs are scheme-sanitized (http / https / mailto / tel only); internal targets resolve to a docx bookmark / pptx slide jump / xlsx defined name or cell.
onResourceMetrics (metrics: OoxmlResourceMetrics) => void Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results.
onVisibleSlideChange (topIndex: number, total: number) => void Fires when the top-most visible slide changes.
onError (err: Error) => void Receives Viewer-managed failures that have no directly awaitable result, such as virtualized rendering or embedded-media playback. load(), navigation, and other awaitable operations reject their own Promise whether or not this callback is supplied; the same failure is never delivered twice. Background failures are logged with console.error when the callback is omitted. Narrow stable cases with OoxmlError, OoxmlResourceLimitError or OoxmlDecodedImageLimitError; other failures remain Error values and message text is not a stable discriminator. Error reference →

Methods

static fromPresentation(container, presentation, options?): Omit<PptxScrollViewer, "load"> Synchronously create a Scroll Viewer that borrows one loaded presentation and lays out its initial virtual window.
load(source: string | ArrayBuffer): Promise<void> Load a Viewer-owned deck and render the first window.
scrollToSlide(index: number, opts?: { behavior?: "auto" | "smooth" }): void Scroll so slide index’s top edge sits at the viewport top (index clamped).
findText(query: string, opts?: { caseSensitive?: boolean }): Promise<FindMatch<PptxMatchLocation>[]> Full-text search across the whole document; highlights every hit and returns them in document order. Each match carries matchIndex, the matched text, and its location. Case-insensitive by default.
findNext(): Promise<FindMatch<PptxMatchLocation> | null> Move to the next match (wrap-around), navigate to it if needed, and draw it in the active-match colour. Returns the now-active match, or null when there are none. Call findText first.
findPrev(): Promise<FindMatch<PptxMatchLocation> | null> Move to the previous match (wrap-around from first to last).
clearFind(): void Clear all highlights and reset the find state.
setScale(scale: number): void Set the absolute zoom scale at runtime (clamped to zoomMin/zoomMax). Flicker-free. View-only.
relayout(): void Force a re-fit + re-mount of the visible window. Called automatically after load / resize / zoom; use it when the container resizes in a way a ResizeObserver cannot observe (e.g. a late web-font load). Idempotent.
get slideCount(): number Total slides (0 until loaded).
get topVisibleSlide(): number Index of the top-most visible slide.
getSelectionContext(options?: PptxSelectionContextOptions): PptxSelectionContext | null Return the current mounted text selection or clicked-element context.
getResourceMetrics(): Promise<OoxmlResourceMetrics> Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output.
destroy(): void Tear down the DOM subtree. Destroys a self-loaded engine; a borrowed one is left intact.