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().
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.
import { PptxPresentation } from '@silurus/ooxml/pptx';
// Headless engine — render every slide into a canvas you control.
const doc = await PptxPresentation.load('/sample.pptx');
for (let i = 0; i < doc.slideCount; i++) {
const canvas = document.createElement('canvas');
scroller.appendChild(canvas);
await doc.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.
import { PptxPresentation } from '@silurus/ooxml/pptx';
// Render each slide small, wire up navigation.
const doc = await PptxPresentation.load('/sample.pptx');
for (let i = 0; i < doc.slideCount; i++) {
const thumb = document.createElement('canvas');
thumb.addEventListener('click', () => open(i));
grid.appendChild(thumb);
await doc.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().
import { PptxPresentation, PptxViewer } from '@silurus/ooxml/pptx';
// A large preview viewer on the right…
const viewer = new PptxViewer(detailCanvas, { width: 960, enableTextSelection: true });
// …and a thumbnail rail on the left, sharing the same file.
const [doc] = await Promise.all([
PptxPresentation.load('/sample.pptx'),
viewer.load('/sample.pptx'),
]);
for (let i = 0; i < doc.slideCount; i++) {
const thumb = document.createElement('canvas');
thumb.addEventListener('click', () => viewer.goToSlide(i)); // jump the preview
rail.appendChild(thumb);
await doc.renderSlide(thumb, i, { width: 200 });
} In your framework
Mount it in a canvas
The viewer is a plain TypeScript class — no framework runtime, no peer deps. Create it on mount,
call load(), and let it go on unmount.
import { useEffect, useRef } from 'react';
import { PptxViewer } from '@silurus/ooxml/pptx';
export function Viewer({ src }: { src: string }) {
const ref = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = ref.current;
if (!canvas) return;
const viewer = new PptxViewer(canvas, { width: 960 });
void viewer.load(src);
return () => viewer.destroy();
}, [src]);
return <canvas ref={ref} />;
} <script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { PptxViewer } from '@silurus/ooxml/pptx';
const props = defineProps<{ src: string }>();
const canvas = ref<HTMLCanvasElement>();
let viewer: PptxViewer | undefined;
onMounted(() => {
viewer = new PptxViewer(canvas.value as HTMLCanvasElement, { width: 960 });
void viewer.load(props.src);
});
onBeforeUnmount(() => viewer?.destroy());
</script>
<template>
<canvas ref="canvas" />
</template> <script lang="ts">
import { onMount } from 'svelte';
import { PptxViewer } from '@silurus/ooxml/pptx';
export let src: string;
let canvas: HTMLCanvasElement;
onMount(() => {
const viewer = new PptxViewer(canvas, { width: 960 });
void viewer.load(src);
return () => viewer.destroy();
});
</script>
<canvas bind:this={canvas}></canvas> import { PptxViewer } from '@silurus/ooxml/pptx';
const canvas = document.getElementById('viewer') as HTMLCanvasElement;
const viewer = new PptxViewer(canvas, { width: 960 });
await viewer.load('/sample.pptx'); 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.
PptxViewer
Opinionated single-canvas viewer. Hand it a <canvas>; it owns parsing, rendering and the current slide.
new PptxViewer(canvas: HTMLCanvasElement, options?: PptxViewerOptions) Options
| Option | Type | Default | Description |
|---|---|---|---|
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 from Google Fonts so layout matches Office without the fonts installed. Off by default for privacy. |
enableTextSelection | boolean | false | Overlay a transparent text layer so users can select & copy slide text. |
enableMediaPlayback | boolean | false | Make embedded audio/video interactive (the viewer draws its own play chrome). |
maxZipEntryBytes | number | 512 MiB | Per-entry ZIP decompression cap (zip-bomb guard). Lower it for untrusted input. |
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 — the engine tree-shakes away entirely. |
onSlideChange | (index: number, total: number) => void | — | Called after a slide finishes rendering. |
onError | (err: Error) => void | — | Called on parse or render errors. |
Methods
load(source: string | ArrayBuffer): Promise<void> | Load from a 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. |
get slideIndex(): number | Current slide index. |
get slideCount(): number | Total slides (0 until loaded). |
get canvasElement(): HTMLCanvasElement | The underlying canvas. |
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
| Option | Type | Default | Description |
|---|---|---|---|
useGoogleFonts | boolean | false | Load metric-compatible webfonts from Google Fonts so layout matches Office without the fonts installed. Off by default for privacy. |
maxZipEntryBytes | number | 512 MiB | Per-entry ZIP decompression cap (zip-bomb guard). Lower it for untrusted input. |
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?, math? }): Promise<void> | Render one slide into the given canvas at the given width. Pass `math` (from `@silurus/ooxml/math`) to render OMML equations; omit it and equations are skipped. |
destroy(): void | Release the worker. |