# @office-kit/pptx — full documentation This file is the concatenation of every page on @office-kit/pptx's docs site, intended for LLM ingestion. Page boundaries are marked with H1 headings prefixed by the source path. Source repo: https://github.com/office-kit/pptx --- # Install > Add @office-kit/pptx to a Node or browser project. # Install `@office-kit/pptx` ships as ESM-only. Pick the entry that matches your runtime. ## Package install ```sh pnpm add @office-kit/pptx # or npm install @office-kit/pptx # or bun add @office-kit/pptx ``` Requires **Node `>= 20`** for the built-in `Web Streams`, `Blob`, and `fetch` globals. Modern browsers (current and current-1 of Chromium, Firefox, Safari) work with the default entry — no Node built-ins are pulled in. ## Subpath entries | Import | Use case | |--------|----------| | `@office-kit/pptx` | Full library: load / save, slides, shapes, charts, tables, comments, themes. Runs in Node **and** the browser. | | `@office-kit/pptx/node` | Adds `loadPresentationFile` / `savePresentationToFile` on top of the full lib. Node-only. | Bundle budgets, unminified: - Minimal `loadPresentation → savePresentation`: **~61 KB** - Full fn-API (every export imported): **~122 KB** All exports are side-effect-free (`"sideEffects": false`), so unused chart / table / animation code drops out under any modern bundler. A CI test guards the bundle ceiling. ## TypeScript Types are bundled. `tsconfig.json` should have `"moduleResolution": "bundler"` (or `"node16"` / `"nodenext"`) so the `@office-kit/pptx/node` subpath resolves. ```json { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true } } ``` ## What's not in the box (yet) - **New theme / master / layout authoring from scratch** — at 1.0 you can author slides on top of any layout in your template, but constructing a fresh master and theme is post-1.0. - **SmartArt authoring** — preserved verbatim on round-trip but not synthesizable. - **Complex animation timing-tree authoring** — entrance / exit / emphasis presets (`fadeIn`, `fadeOut`, `appear`, `disappear`) are exposed; deeper timing trees are deferred. - **Document encryption (read + write)** — encrypted `.pptx` files are detected and rejected with a clear error. Next: Getting started → --- # Getting started > Read, edit, and write your first .pptx presentation. # Getting started This page walks the canonical @office-kit/pptx workflows: **fill a template**, **build a deck from scratch**, **edit images in place**. Every snippet below is a real `.ts` file in this repo — `svelte-check` compiles them against the live `@office-kit/pptx` types on every build, so when this page renders, it's already proven that the code typechecks. ## Token-based template fill The most common @office-kit/pptx use case: take a designer-authored template, swap `{{tokens}}` with your data, save. Formatting on the surrounding runs is preserved. ```ts title="site/src/lib/examples/template-fill.ts" // Open a template .pptx, replace `{{tokens}}` everywhere, save it back. // // This file is imported as ?raw into the docs site so the snippet shown // to readers is exactly what svelte-check / tsc compiled — if an API // rename breaks this import, the docs build fails before deploy. import { readFile, writeFile } from 'node:fs/promises'; import { loadPresentation, replaceTokensInPresentation, savePresentation } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('template.pptx')); replaceTokensInPresentation(pres, { name: 'Yamashita', event: 'Re:Invent', date: '2026-12-01', }); await writeFile('out.pptx', await savePresentation(pres)); ``` `loadPresentation` accepts a `Uint8Array`, `ArrayBuffer`, or `Blob` — anything you can `fetch` or `readFile`. `replaceTokensInPresentation` walks every text run in every slide (and the speaker notes) replacing exact-match `{{name}}` tokens. ## Build a deck from a blank template For authoring from scratch you still need at least one master + layout — the simplest path is to ship a small `blank.pptx` alongside your app. Then add slides on its layouts and fill them in with the typed `addSlide*` helpers: ```ts title="site/src/lib/examples/build-deck.ts" // Build a deck from a blank template: add slides on a layout, drop in a // text box, an image, and a chart, then save. import { readFile, writeFile } from 'node:fs/promises'; import { addSlide, addSlideChart, addSlideImage, addSlideTextBox, findSlideLayout, findSlidePlaceholder, inches, loadPresentation, savePresentation, setShapeText, } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('blank.pptx')); const titleLayout = findSlideLayout(pres, 'Title Slide'); if (!titleLayout) throw new Error('no Title Slide layout'); const cover = addSlide(pres, { layout: titleLayout }); const title = findSlidePlaceholder(cover, 'ctrTitle') ?? findSlidePlaceholder(cover, 'title'); if (title) setShapeText(title, 'Q3 review'); const blank = findSlideLayout(pres, 'Blank') ?? titleLayout; const slide = addSlide(pres, { layout: blank }); addSlideTextBox(slide, { x: inches(0.7), y: inches(0.5), w: inches(9), h: inches(0.7), text: 'Numbers up and to the right', }); addSlideChart(slide, { x: inches(0.7), y: inches(1.5), w: inches(8), h: inches(4.5), spec: { kind: 'column', categories: ['Q1', 'Q2', 'Q3', 'Q4'], series: [{ name: 'Revenue', values: [120, 180, 240, 300] }], title: 'FY26', }, }); addSlideImage(slide, await readFile('logo.png'), { x: inches(8), y: inches(0.4), w: inches(1.5), h: inches(1), }); await writeFile('out.pptx', await savePresentation(pres)); ``` `inches(n)`, `cm(n)`, `pt(n)`, and `emu(n)` all return a branded `Emu` number — internally @office-kit/pptx only sees EMUs, so unit confusion is caught at the type level. ## Replace an image, preserve geometry A common L2 (template-edit) workflow: a designer drops a placeholder image into the template, your code swaps in the real one without disturbing crop, position, or scaling. ```ts title="site/src/lib/examples/image-replace.ts" // Swap an image in a template, preserving its crop / transform / sizing. // The format is auto-detected from the new bytes. import { readFile, writeFile } from 'node:fs/promises'; import { getShapeKind, getShapeName, getSlideShapes, getSlides, loadPresentation, savePresentation, setShapeImage, } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('template.pptx')); const newLogo = await readFile('new-logo.png'); for (const slide of getSlides(pres)) { for (const shape of getSlideShapes(slide)) { if (getShapeKind(shape) === 'picture' && getShapeName(shape) === 'Logo') { setShapeImage(shape, newLogo); } } } await writeFile('out.pptx', await savePresentation(pres)); ``` `setShapeImage` auto-detects the new image's format (PNG / JPEG / GIF / SVG / BMP / TIFF) from magic bytes — pass `options.format` to override. ## Direct fs helpers (Node) `@office-kit/pptx/node` exposes `loadPresentationFile` / `savePresentationToFile` so you can skip the `readFile` / `writeFile` glue: ```ts title="site/src/lib/examples/node-fs.ts" // One-shot read + save direct from / to disk via the @office-kit/pptx/node // helpers, no manual fs glue needed. import { loadPresentationFile, savePresentationToFile } from '@office-kit/pptx/node'; const pres = await loadPresentationFile('input.pptx'); // ...mutate pres... await savePresentationToFile(pres, 'output.pptx'); ``` ## Browser via fetch The default entry is browser-safe (no `node:fs`). Load straight from a `fetch` Response: ```ts title="site/src/lib/examples/browser-fetch.ts" // Browser: fetch a .pptx and load it. loadPresentation accepts any of // Uint8Array / ArrayBuffer / Blob — `await response.arrayBuffer()` is the // most browser-portable form. import { getSlides, getSlideTitle, loadPresentation } from '@office-kit/pptx'; const response = await fetch('/template.pptx'); const pres = await loadPresentation(await response.arrayBuffer()); for (const slide of getSlides(pres)) { console.log(getSlideTitle(slide)); } ``` This works in any environment with `fetch`: modern browsers, Bun, Deno, Cloudflare Workers, edge runtimes. ## What's next - Recipes — copy-pasteable code for the most common tasks (charts, tables, comments, animations, transitions, validation). - Cheatsheet — one-page lookup: "I want to do X" → exact functions to call. - API reference — every export, organized by section. - **GitHub:** [`office-kit/pptx`](https://github.com/office-kit/pptx) --- # Recipes > Working code for the most common tasks — load / edit / build / charts / images / notes / validate. # Recipes Working code for the things people actually want to do with @office-kit/pptx — fill a template, build a deck on a blank layout, add shapes / charts / tables / images, set up notes / comments / transitions / animations, validate before saving. Every snippet below is a real `.ts` file in the repo, type-checked against `@office-kit/pptx` on every build. ## Basics ### Open a presentation and walk every slide Load an existing .pptx, iterate slides, and read title + text content. ```ts title="site/src/lib/examples/recipes/open-and-iterate.ts" // Load a presentation, walk every slide, print titles and text length. import { readFile } from 'node:fs/promises'; import { getSlideText, getSlideTextLength, getSlideTitle, getSlides, loadPresentation, } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('input.pptx')); for (const slide of getSlides(pres)) { console.log(`${getSlideTitle(slide) ?? '(no title)'} — ${getSlideTextLength(slide)} chars`); console.log(getSlideText(slide)); } ``` - getSlideTitle returns the title placeholder text or null if the slide has no title placeholder. - getSlideText concatenates every text body on the slide; getSlideOutline gives a structured outline if you need paragraph hierarchy. *Related API: `loadPresentation`, `getSlides`, `getSlideTitle`, `getSlideText`* ### Token-based template fill The canonical L2 workflow: open a designer-authored template, swap `{{tokens}}`, save. ```ts title="site/src/lib/examples/template-fill.ts" // Open a template .pptx, replace `{{tokens}}` everywhere, save it back. // // This file is imported as ?raw into the docs site so the snippet shown // to readers is exactly what svelte-check / tsc compiled — if an API // rename breaks this import, the docs build fails before deploy. import { readFile, writeFile } from 'node:fs/promises'; import { loadPresentation, replaceTokensInPresentation, savePresentation } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('template.pptx')); replaceTokensInPresentation(pres, { name: 'Yamashita', event: 'Re:Invent', date: '2026-12-01', }); await writeFile('out.pptx', await savePresentation(pres)); ``` - Tokens are exact-match: {{name}} replaces only the literal `{{name}}` string in any text run. - replaceTokensInPresentation also walks speaker notes; use replaceTokensInSlide for a single slide. *Related API: `loadPresentation`, `replaceTokensInPresentation`, `replaceTokensInSlide`, `savePresentation`* ### Build a deck on a blank template Start from a tiny blank.pptx, add slides on its layouts, and save. ```ts title="site/src/lib/examples/recipes/build-from-scratch.ts" // Build a deck on top of a blank template: add a title slide and a content // slide, then save. import { readFile, writeFile } from 'node:fs/promises'; import { addSlide, addSlideTextBox, findSlideLayout, findSlidePlaceholder, inches, loadPresentation, savePresentation, setShapeText, } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('blank.pptx')); const titleLayout = findSlideLayout(pres, 'Title Slide'); if (titleLayout) { const cover = addSlide(pres, { layout: titleLayout }); const t = findSlidePlaceholder(cover, 'ctrTitle') ?? findSlidePlaceholder(cover, 'title'); if (t) setShapeText(t, '@office-kit/pptx demo'); const sub = findSlidePlaceholder(cover, 'subTitle'); if (sub) setShapeText(sub, 'an OOXML library for TypeScript'); } const blank = findSlideLayout(pres, 'Blank') ?? titleLayout; if (blank) { const body = addSlide(pres, { layout: blank }); addSlideTextBox(body, { x: inches(1), y: inches(1), w: inches(8), h: inches(1), text: 'Free-form text box', }); } await writeFile('out.pptx', await savePresentation(pres)); ``` *Related API: `addSlide`, `findSlideLayout`, `findSlidePlaceholder`, `setShapeText`* ### Brand a deck’s color scheme + fonts setPresentationTheme / setPresentationFonts patch the theme’s color and font scheme — no template required. ```ts title="site/src/lib/examples/recipes/theme-branding.ts" // Brand a from-scratch deck's color scheme and typography — no template // required. setPresentationTheme / setPresentationFonts patch only the // slots you name; every other theme slot keeps its default. import { createPresentation, setPresentationFonts, setPresentationTheme, type PresentationData, } from '@office-kit/pptx'; const pres: PresentationData = createPresentation(); setPresentationTheme(pres, { name: 'Consulting Navy', dark1: '#0B1F3A', accent1: '#00A9E0', accent2: '#FDB913', accent3: '#6E7B8B', }); setPresentationFonts(pres, { majorLatin: 'Georgia', minorLatin: 'Calibri', }); // Every shape added from here on inherits the new scheme via the // scheme-color tokens (accent1, tx1, ...) it was already using. ``` - Only the slots you pass are overwritten; every other color/typeface keeps its default. - Colors are #RRGGBB strings; every theme slot is always a plain srgbClr, never a scheme-color reference. *Related API: `setPresentationTheme`, `setPresentationFonts`, `getPresentationTheme`, `getPresentationFonts`* ### Direct fs helpers (Node) loadPresentationFile / savePresentationToFile skip the manual fs glue. ```ts title="site/src/lib/examples/node-fs.ts" // One-shot read + save direct from / to disk via the @office-kit/pptx/node // helpers, no manual fs glue needed. import { loadPresentationFile, savePresentationToFile } from '@office-kit/pptx/node'; const pres = await loadPresentationFile('input.pptx'); // ...mutate pres... await savePresentationToFile(pres, 'output.pptx'); ``` *Related API: `loadPresentationFile`, `savePresentationToFile`* ## Authoring shapes ### Per-run text formatting Address text by (paragraph, run) indices and apply font, size, color, bold / italic / underline. ```ts title="site/src/lib/examples/recipes/text-formatting.ts" // Per-run formatting: bold, size, color, font on a single paragraph. // // Index a shape's text by (paragraphIndex, runIndex). Each \n in the // `text:` argument starts a new paragraph; the first run of each is // index 0. import { addSlideTextBox, inches, setParagraphAlignment, setShapeRunFormat, type SlideData, } from '@office-kit/pptx'; declare const slide: SlideData; const box = addSlideTextBox(slide, { x: inches(0.7), y: inches(1.5), w: inches(9), h: inches(2.5), text: 'Default text\nBold red 24pt Calibri\nItalic underline 18pt', }); setShapeRunFormat(box, 1, 0, { bold: true, size: 24, color: '#C00000', font: 'Calibri', }); setShapeRunFormat(box, 2, 0, { italic: true, underline: true, size: 18, color: '#1F4E79', }); setParagraphAlignment(box, 2, 'ctr'); ``` - TextFormat.size is in points (number), not EMU. Color accepts #RRGGBB, RRGGBB, or a scheme token like "accent1". - setShapeTextFormat (no index) applies the same format to every run in the shape. *Related API: `setShapeRunFormat`, `setShapeTextFormat`, `setParagraphAlignment`* ### Preset shapes (180+ presets) addSlideShape ships every ECMA-376 preset geometry — pass the token as `preset`. ```ts title="site/src/lib/examples/recipes/preset-shapes.ts" // Drop in a few preset shapes from the 180+ DrawingML preset library. import { addSlideShape, inches, type SlideData } from '@office-kit/pptx'; declare const slide: SlideData; addSlideShape(slide, { preset: 'star5', x: inches(1), y: inches(2), w: inches(2), h: inches(2), text: '★', }); addSlideShape(slide, { preset: 'rightArrow', x: inches(4), y: inches(2.4), w: inches(2.5), h: inches(1.2), text: 'next', }); addSlideShape(slide, { preset: 'roundRect', x: inches(7), y: inches(2), w: inches(2.5), h: inches(2), text: 'callout', }); ``` - Use PresetShape from the public types for autocompletion. Unknown strings pass through to verbatim. *Related API: `addSlideShape`, `PresetShape`* ### Fills, shadows, glows Solid / gradient / pattern fills plus the two effect helpers. ```ts title="site/src/lib/examples/recipes/fills-and-effects.ts" // Solid / gradient / pattern fills + a drop shadow and an outer glow. import { addSlideShape, inches, pt, setShapeFill, setShapeGlow, setShapeGradientFill, setShapePatternFill, setShapeShadow, type SlideData, } from '@office-kit/pptx'; declare const slide: SlideData; const solid = addSlideShape(slide, { preset: 'rect', x: inches(0.7), y: inches(1), w: inches(2.5), h: inches(2), text: 'Solid', }); setShapeFill(solid, '#C00000'); setShapeShadow(solid, { blurEmu: pt(8), offsetEmu: pt(4), angleDeg: 45, color: '#000000', opacity: 0.5, }); const grad = addSlideShape(slide, { preset: 'rect', x: inches(3.5), y: inches(1), w: inches(2.5), h: inches(2), text: 'Gradient', }); setShapeGradientFill(grad, { stops: [ { offset: 0, color: '#FFD966' }, { offset: 1, color: '#C00000' }, ], angleDeg: 45, }); const pat = addSlideShape(slide, { preset: 'rect', x: inches(6.3), y: inches(1), w: inches(2.5), h: inches(2), text: 'Pattern', }); setShapePatternFill(pat, { preset: 'pct50', foreground: '#1F4E79', background: '#FFFFFF' }); setShapeGlow(pat, { radiusEmu: pt(12), color: '#2E75B6' }); ``` *Related API: `setShapeFill`, `setShapeGradientFill`, `setShapePatternFill`, `setShapeShadow`, `setShapeGlow`* ### Group shapes into one component Compose a rectangle + label into a "KPI card"; move/resize the group as one unit. ```ts title="site/src/lib/examples/recipes/group-shapes.ts" // Compose a rectangle + label into a single "KPI card" component, then // move/resize it as one unit — the members scale with it. import { addSlideShape, addSlideTextBox, groupShapes, inches, setShapeFill, setShapePosition, setShapeSize, type SlideData, } from '@office-kit/pptx'; declare const slide: SlideData; const card = addSlideShape(slide, { preset: 'roundRect', x: inches(1), y: inches(1), w: inches(2.5), h: inches(1.2), }); setShapeFill(card, '#0B1F3A'); const label = addSlideTextBox(slide, { x: inches(1.2), y: inches(1.3), w: inches(2.1), h: inches(0.6), text: 'Revenue +12%', }); const kpiCard = groupShapes([card, label], { name: 'KPI Card' }); // Move + resize the group; the rectangle and label scale together. setShapePosition(kpiCard, inches(5), inches(2)); setShapeSize(kpiCard, inches(3.75), inches(1.8)); ``` - groupShapes needs every member to have an explicit position/size — placeholders that inherit geometry from the layout can't be grouped directly. - ungroupShapes reverses it, rescaling each member if the group itself was moved/resized in between. *Related API: `groupShapes`, `ungroupShapes`, `getGroupChildren`, `getGroupTransform`* ## Tables, charts, images ### Add a table A 2D array of cell strings, plus firstRow / bandRow flags for header styling. ```ts title="site/src/lib/examples/recipes/add-table.ts" // Add a 5×4 table with a header row and banded styling. import { addSlideTable, inches, type SlideData } from '@office-kit/pptx'; declare const slide: SlideData; addSlideTable(slide, { x: inches(0.7), y: inches(1.5), w: inches(9), h: inches(3), rows: [ ['Quarter', 'Revenue', 'Cost', 'Margin'], ['Q1', '$1.2M', '$0.8M', '33%'], ['Q2', '$1.8M', '$0.9M', '50%'], ['Q3', '$2.4M', '$1.3M', '46%'], ['Q4', '$3.0M', '$1.6M', '47%'], ], firstRow: true, bandRow: true, }); ``` - Per-cell control after the fact: getTableCell + setTableCellText / setTableCellFill / setTableCellAlignment. - insertTableRow / removeTableRow / insertTableColumn / removeTableColumn for structural changes. *Related API: `addSlideTable`, `getTableCell`, `setTableCellText`* ### Add a chart addSlideChart writes the chart XML, the drawing rels, and an embedded xlsx so "Edit data" works in PowerPoint. ```ts title="site/src/lib/examples/recipes/add-chart.ts" // Column chart with two series. addSlideChart writes the chart XML, the // drawing rels, and an embedded xlsx so PowerPoint's "Edit data" works. import { addSlideChart, inches, type SlideData } from '@office-kit/pptx'; declare const slide: SlideData; addSlideChart(slide, { x: inches(0.5), y: inches(1.5), w: inches(9), h: inches(4.5), spec: { kind: 'column', categories: ['Q1', 'Q2', 'Q3', 'Q4'], series: [ { name: 'Revenue', values: [120, 180, 240, 300] }, { name: 'Cost', values: [80, 90, 130, 160] }, ], title: 'FY26', }, }); ``` - Chart kinds: bar, column, line, pie, doughnut, area. Each takes the same { categories, series } shape. - setChartSpec updates an existing chart in place — useful for re-rendering a template chart with new data. *Related API: `addSlideChart`, `setChartSpec`, `getShapeChartSpec`* ### Insert an image Format auto-detected from magic bytes. Reuse setShapeImage to swap an existing picture. ```ts title="site/src/lib/examples/recipes/add-image.ts" // Insert an image. setShapeImage / addSlideImage detect the format // (PNG / JPEG / GIF / SVG / BMP / TIFF) from magic bytes. import { readFile } from 'node:fs/promises'; import { addSlideImage, inches, type SlideData } from '@office-kit/pptx'; declare const slide: SlideData; const logo = await readFile('logo.png'); addSlideImage(slide, logo, { x: inches(8), y: inches(0.4), w: inches(1.5), h: inches(1), }); ``` *Related API: `addSlideImage`, `setShapeImage`* ### Replace a template image in place Find a placeholder picture by name and swap its bytes — geometry / crop preserved. ```ts title="site/src/lib/examples/image-replace.ts" // Swap an image in a template, preserving its crop / transform / sizing. // The format is auto-detected from the new bytes. import { readFile, writeFile } from 'node:fs/promises'; import { getShapeKind, getShapeName, getSlideShapes, getSlides, loadPresentation, savePresentation, setShapeImage, } from '@office-kit/pptx'; const pres = await loadPresentation(await readFile('template.pptx')); const newLogo = await readFile('new-logo.png'); for (const slide of getSlides(pres)) { for (const shape of getSlideShapes(slide)) { if (getShapeKind(shape) === 'picture' && getShapeName(shape) === 'Logo') { setShapeImage(shape, newLogo); } } } await writeFile('out.pptx', await savePresentation(pres)); ``` *Related API: `setShapeImage`, `getShapeKind`, `getShapeName`* ## L4: notes, comments, transitions, animations ### Speaker notes + review comments Notes live on the slide; comments are separate parts with author metadata and an EMU position. ```ts title="site/src/lib/examples/recipes/notes-and-comments.ts" // Add speaker notes and two review comments to a slide. import { addSlideComment, cm, setSlideNotes, type SlideData } from '@office-kit/pptx'; declare const slide: SlideData; setSlideNotes(slide, 'Open the comments pane to see the review thread.'); addSlideComment(slide, { author: { name: 'Reviewer A', initials: 'RA' }, text: 'Tighten the headline.', position: { x: cm(2), y: cm(2) }, }); addSlideComment(slide, { author: { name: 'Reviewer B', initials: 'RB' }, text: 'Numbers look strong.', position: { x: cm(10), y: cm(6) }, }); ``` *Related API: `setSlideNotes`, `getSlideNotes`, `addSlideComment`, `getSlideComments`* ### Slide transitions + shape animations Per-slide transition effect plus entrance / exit animations on individual shapes. ```ts title="site/src/lib/examples/recipes/transitions-animations.ts" // Slide transitions + shape entrance / exit animations. import { addSlideShape, inches, setShapeAnimation, setShapeFill, setSlideTransition, type SlideData, } from '@office-kit/pptx'; declare const slide: SlideData; setSlideTransition(slide, { effect: 'fade', speed: 'med' }); const a = addSlideShape(slide, { preset: 'roundRect', x: inches(1), y: inches(2), w: inches(3), h: inches(1.5), text: 'fadeIn', }); setShapeFill(a, '#2E75B6'); setShapeAnimation(a, { effect: 'fadeIn', durationMs: 700 }); const b = addSlideShape(slide, { preset: 'roundRect', x: inches(5), y: inches(2), w: inches(3), h: inches(1.5), text: 'fadeOut', }); setShapeFill(b, '#C00000'); setShapeAnimation(b, { effect: 'fadeOut', durationMs: 700 }); ``` - Animation effects: fadeIn, fadeOut, appear, disappear. Deeper timing-tree authoring is post-1.0. *Related API: `setSlideTransition`, `setShapeAnimation`* ### Hyperlinks + click-to-slide External URL on a text run, and a click action that jumps to another slide. ```ts title="site/src/lib/examples/recipes/hyperlinks.ts" // External URL hyperlink + an in-deck click action (jump to slide 2). import { addSlideTextBox, getSlides, inches, setShapeClickAction, setShapeHyperlink, setShapeTextFormat, type PresentationData, type SlideData, } from '@office-kit/pptx'; declare const pres: PresentationData; declare const slide: SlideData; const link = addSlideTextBox(slide, { x: inches(1), y: inches(2), w: inches(6), h: inches(0.6), text: 'Open the docs', }); setShapeHyperlink(link, 'https://github.com/office-kit/pptx'); setShapeTextFormat(link, { color: '#0563C1', underline: true }); const nav = addSlideTextBox(slide, { x: inches(1), y: inches(3), w: inches(6), h: inches(0.6), text: 'Jump to slide 2', }); const slide2 = getSlides(pres)[1]; if (slide2) setShapeClickAction(nav, { kind: 'slide', slide: slide2 }); ``` *Related API: `setShapeHyperlink`, `setShapeClickAction`* ## Diagnostics ### Validate the package validatePresentation returns invariant violations: dangling rels, missing parts, off-spec IDs. ```ts title="site/src/lib/examples/recipes/validate.ts" // Run the invariant checker before saving — useful in CI to catch // dangling rels / missing parts / off-spec IDs. import { type PresentationData, validatePresentation } from '@office-kit/pptx'; declare const pres: PresentationData; const issues: ReturnType = validatePresentation(pres); for (const issue of issues) { console.error(`[${issue.severity}] ${issue.message}`); } if (issues.some((i) => i.severity === 'error')) { throw new Error(`@office-kit/pptx: ${issues.length} validation issue(s)`); } ``` *Related API: `validatePresentation`* ### Inspect raw OPC parts listPackageParts + readPackagePart let you peek at the underlying XML without dropping to the internal class. ```ts title="site/src/lib/examples/recipes/inspect-parts.ts" // Inspect the raw OPC parts in a package without dropping to the // internal OpcPackage class. import { type PresentationData, listPackageParts, readPackagePart } from '@office-kit/pptx'; declare const pres: PresentationData; for (const part of listPackageParts(pres)) { console.log(`${part.name} (${part.contentType}, ${part.byteLength} bytes)`); } const themeBytes = readPackagePart(pres, '/ppt/theme/theme1.xml'); if (themeBytes) console.log(new TextDecoder().decode(themeBytes)); ``` *Related API: `listPackageParts`, `readPackagePart`, `getMediaParts`* ## Browser ### Browser: load via fetch loadPresentation accepts ArrayBuffer / Uint8Array / Blob — pipe a fetch Response straight in. ```ts title="site/src/lib/examples/browser-fetch.ts" // Browser: fetch a .pptx and load it. loadPresentation accepts any of // Uint8Array / ArrayBuffer / Blob — `await response.arrayBuffer()` is the // most browser-portable form. import { getSlides, getSlideTitle, loadPresentation } from '@office-kit/pptx'; const response = await fetch('/template.pptx'); const pres = await loadPresentation(await response.arrayBuffer()); for (const slide of getSlides(pres)) { console.log(getSlideTitle(slide)); } ``` *Related API: `loadPresentation`, `getSlides`, `getSlideTitle`* --- # Cheatsheet > One-page lookup of task → exact functions to import + call. The shortest path from "I want to do X" to working code. # Cheatsheet The shortest path from "I want to do X" to working code. Each row names the exact function(s) you import. The snippets below the index expand the most common patterns. For prose context see Recipes; for every export see API reference. > Everything lives under the single `@office-kit/pptx` entry (or `@office-kit/pptx/node` for > the Node fs helpers). The library is fn-only — no classes — so once you > know the function name you know how to import it. ## Index ### Load / save | Task | Functions | |------|-----------| | Load a `.pptx` (any env) → `PresentationData` | `loadPresentation` | | Save a `PresentationData` → `Uint8Array` | `savePresentation` | | Node convenience: read from / write to disk | `loadPresentationFile` + `savePresentationToFile` (from `@office-kit/pptx/node`) | | Create an empty package (advanced) | `createPresentation` | ### Slides | Task | Functions | |------|-----------| | Enumerate slides | `getSlides` (or `getSlideAt(pres, i)`) | | Add a slide on a layout | `findSlideLayout` + `addSlide` | | Quick blank / title / content slide | `addBlankSlide` / `addTitleSlide` / `addContentSlide` | | Duplicate, move, swap, reverse | `duplicateSlide` + `moveSlide` + `swapSlides` + `reverseSlides` | | Remove or sort | `removeSlide` + `sortSlides` | | Title / hidden flag / layout | `setSlideTitle` + `setSlideHidden` + `setSlideLayout` | | Solid background color | `setSlideBackground` (or `setSlideBackgroundImage`) | | Speaker notes | `setSlideNotes` + `getSlideNotes` | | Transition | `setSlideTransition` | ### Placeholders & text | Task | Functions | |------|-----------| | Find a placeholder by type | `findSlidePlaceholder(slide, 'title')` | | Bulk-fill placeholders | `setSlidePlaceholders(slide, { title, body, ... })` | | Token replace across deck or slide | `replaceTokensInPresentation` / `replaceTokensInSlide` | | Free text replace | `replaceTextInPresentation` / `replaceTextInSlide` | | Set / get a shape's text | `setShapeText` + `getShapeText` | | Per-run formatting (bold, size, color) | `setShapeRunFormat` | | Per-paragraph alignment / level / bullet | `setParagraphAlignment` + `setParagraphLevel` + `setParagraphBullet` | | Bullets across the whole shape | `setShapeBullets` | ### Shapes | Task | Functions | |------|-----------| | Add a free-form text box | `addSlideTextBox` | | Add a preset shape (180+ presets) | `addSlideShape` | | Add a line / connector / arrow | `addSlideLine` + `setShapeStrokeArrow` | | Add a picture | `addSlideImage` | | Solid fill / gradient / pattern / image fill | `setShapeFill` + `setShapeGradientFill` + `setShapePatternFill` + `setShapeImageFill` | | Stroke color / width / dash | `setShapeStroke` + `setShapeStrokeDash` | | Shadow / glow | `setShapeShadow` + `setShapeGlow` | | Move / resize / rotate / flip | `setShapePosition` + `setShapeSize` + `setShapeRotation` + `setShapeFlip` | | Z-order | `bringShapeToFront` + `sendShapeToBack` + `bringShapeForward` + `sendShapeBackward` | | Hyperlink / click action | `setShapeHyperlink` + `setShapeClickAction` | | Animation (entrance / exit) | `setShapeAnimation({ effect: 'fadeIn', durationMs: 700 })` | | Remove a shape | `removeShape` | ### Tables | Task | Functions | |------|-----------| | Add a table | `addSlideTable` | | Get / set a cell's text | `getTableCellText` + `setTableCellText` | | Cell fill / alignment / text format | `setTableCellFill` + `setTableCellAlignment` + `setTableCellTextFormat` | | Insert / remove a row or column | `insertTableRow` + `removeTableRow` + `insertTableColumn` + `removeTableColumn` | | Resize a row or column | `setTableRowHeight` + `setTableColumnWidth` | ### Charts | Task | Functions | |------|-----------| | Add a chart with embedded data | `addSlideChart({ spec: { kind, categories, series } })` | | Update an existing chart's data | `getShapeChartSpec` + `setChartSpec` | | Enumerate every chart | `getAllCharts` | ### Images | Task | Functions | |------|-----------| | Replace an image's bytes | `setShapeImage` | | Crop / opacity / brightness / contrast | `setShapeImageCrop` + `setShapeImageOpacity` + `setShapeImageBrightness` + `setShapeImageContrast` | ### Document metadata | Task | Functions | |------|-----------| | Title / Author / Subject / Keywords | `setCoreProperties` | | App / Company / Manager | `setExtendedProperties` | | Thumbnail image | `setThumbnail` | | Bump revision on save | `incrementRevision` + `touchModified` | ### Comments | Task | Functions | |------|-----------| | Add / remove a comment | `addSlideComment` + `removeSlideComment` | | List comments | `getSlideComments` (or `getAllComments`) | ### Diagnostics | Task | Functions | |------|-----------| | Validate the package | `validatePresentation(pres)` | | Inspect parts list | `listPackageParts` + `readPackagePart` | | Strip unused media | `compactPackage` | ## Snippets Each snippet below corresponds to one row above. Imports are explicit so you can copy-paste a single block into your file. ### Load + save round-trip ```ts import { loadPresentation, savePresentation } from '@office-kit/pptx'; const pres = await loadPresentation(bytes); // ...mutate pres... const out = await savePresentation(pres); ``` ### Token-based template fill ```ts import { loadPresentation, replaceTokensInPresentation, savePresentation } from '@office-kit/pptx'; const pres = await loadPresentation(templateBytes); replaceTokensInPresentation(pres, { name: 'Alice', date: '2026-12-01' }); const out = await savePresentation(pres); ``` ### Add a title slide ```ts import { addSlide, findSlideLayout, findSlidePlaceholder, loadPresentation, savePresentation, setShapeText, } from '@office-kit/pptx'; const pres = await loadPresentation(blankBytes); const layout = findSlideLayout(pres, 'Title Slide'); if (layout) { const slide = addSlide(pres, { layout }); const t = findSlidePlaceholder(slide, 'ctrTitle') ?? findSlidePlaceholder(slide, 'title'); if (t) setShapeText(t, 'Hello'); } const out = await savePresentation(pres); ``` ### Add a free-form text box with formatting ```ts import { addSlideTextBox, inches, setShapeRunFormat, } from '@office-kit/pptx'; const box = addSlideTextBox(slide, { x: inches(1), y: inches(1), w: inches(5), h: inches(1), text: 'Hello, @office-kit/pptx', }); setShapeRunFormat(box, 0, 0, { bold: true, size: 28, color: '#C00000', font: 'Calibri' }); ``` ### Add a preset shape with a fill + shadow ```ts import { addSlideShape, inches, pt, setShapeFill, setShapeShadow, } from '@office-kit/pptx'; const star = addSlideShape(slide, { preset: 'star5', x: inches(1), y: inches(2), w: inches(2), h: inches(2), text: '★', }); setShapeFill(star, '#FFD966'); setShapeShadow(star, { blurEmu: pt(6), offsetEmu: pt(3), angleDeg: 45, color: '#000000', opacity: 0.4, }); ``` ### Add a column chart ```ts import { addSlideChart, inches } from '@office-kit/pptx'; addSlideChart(slide, { x: inches(1), y: inches(1.5), w: inches(8), h: inches(4.5), spec: { kind: 'column', categories: ['Q1', 'Q2', 'Q3', 'Q4'], series: [{ name: 'Revenue', values: [120, 180, 240, 300] }], title: 'FY26', }, }); ``` ### Add a table with header + banded rows ```ts import { addSlideTable, inches } from '@office-kit/pptx'; addSlideTable(slide, { x: inches(0.7), y: inches(1.5), w: inches(9), h: inches(3), rows: [ ['Quarter', 'Revenue', 'Margin'], ['Q1', '$1.2M', '33%'], ['Q2', '$1.8M', '50%'], ], firstRow: true, bandRow: true, }); ``` ### Insert an image at a fixed size ```ts import { addSlideImage, inches } from '@office-kit/pptx'; addSlideImage(slide, pngBytes, { x: inches(1), y: inches(1), w: inches(3), h: inches(2), }); ``` ### Replace an image's bytes in place ```ts import { getShapeKind, getShapeName, getSlideShapes, getSlides, setShapeImage } from '@office-kit/pptx'; for (const slide of getSlides(pres)) { for (const shape of getSlideShapes(slide)) { if (getShapeKind(shape) === 'picture' && getShapeName(shape) === 'Logo') { setShapeImage(shape, newLogoBytes); } } } ``` ### Add a hyperlink on text ```ts import { setShapeHyperlink, setShapeTextFormat } from '@office-kit/pptx'; setShapeHyperlink(linkShape, 'https://example.com'); setShapeTextFormat(linkShape, { color: '#0563C1', underline: true }); ``` ### Set a transition + animation ```ts import { setShapeAnimation, setSlideTransition } from '@office-kit/pptx'; setSlideTransition(slide, { effect: 'fade', speed: 'med' }); setShapeAnimation(shape, { effect: 'fadeIn', durationMs: 700 }); ``` ### Validate before saving ```ts import { validatePresentation } from '@office-kit/pptx'; const issues = validatePresentation(pres); for (const i of issues) console.error(i.severity, i.message); ``` ### Speaker notes ```ts import { setSlideNotes } from '@office-kit/pptx'; setSlideNotes(slide, 'Open the comments pane to see review.'); ``` ### Comments ```ts import { addSlideComment, cm } from '@office-kit/pptx'; addSlideComment(slide, { author: { name: 'Reviewer', initials: 'R' }, text: 'Tighten the headline.', position: { x: cm(2), y: cm(2) }, }); ``` --- # API overview > Public surface: free-function API + units + the Node convenience entry. # API overview The full TypeScript API is bundled with the npm package — your editor will autocomplete every export. This page is the **map**: where to look for each kind of thing. > The full, section-organized reference lives at /api — every export rendered with its signature, parameters, and source link. This page is the conceptual map you reach for first. ## One entry, fn-only Unlike xlsx-kit which splits the public surface across many subpaths, **@office-kit/pptx ships a single entry** (`@office-kit/pptx`) plus a Node convenience subpath (`@office-kit/pptx/node`). Every export is a free function — no classes — so tree-shaking is straightforward. ```ts // Load, save, create — the only mandatory imports. import { loadPresentation, savePresentation, createPresentation } from '@office-kit/pptx'; // Slides + layouts. import { addSlide, findSlideLayout, getSlides, duplicateSlide } from '@office-kit/pptx'; // Template fill. import { replaceTokensInPresentation } from '@office-kit/pptx'; // Shapes (180+ presets, lines, tables, charts, images). import { addSlideShape, addSlideLine, addSlideTable, addSlideChart, addSlideImage, addSlideTextBox, } from '@office-kit/pptx'; // Per-shape mutations (fills, strokes, effects, text formatting). import { setShapeFill, setShapeStroke, setShapeShadow, setShapeRunFormat, setShapeAnimation, } from '@office-kit/pptx'; // Units: inches / cm / mm / pt / emu — all return branded Emu numbers. import { inches, cm, pt, emu } from '@office-kit/pptx'; // Node fs glue. import { loadPresentationFile, savePresentationToFile } from '@office-kit/pptx/node'; ``` Bundle budgets enforced by CI: - **Minimal `load → save`**: ~61 KB unminified - **Full fn-API**: ~122 KB unminified All exports are side-effect-free (`"sideEffects": false`). Bundlers drop every function you do not import. ## The model The library exposes three opaque, branded handle types — `PresentationData`, `SlideData`, `SlideShapeData` — and a fourth, `SlideLayoutData`, for the layout templates the deck ships with. You never construct these directly; they come back from `loadPresentation` / `createPresentation` / `addSlide` / `addSlideShape` / etc. Every mutator takes one of them as its first argument: ```ts const pres: PresentationData = await loadPresentation(bytes); const slide: SlideData = addSlide(pres, { layout }); const shape: SlideShapeData = addSlideShape(slide, { preset: 'rect', ... }); setShapeFill(shape, '#C00000'); ``` This is what lets the API be tree-shakable — there is no method dispatch, so the bundler can see exactly which functions you reach for and drop the rest. ## Units Everything OOXML measures in EMUs (1 inch = 914 400 EMU, 1 cm = 360 000 EMU, 1 pt = 12 700 EMU). @office-kit/pptx brands `Emu` as a nominal type and forces callers to be explicit: ```ts import { inches, cm, pt, emu, type Emu } from '@office-kit/pptx'; addSlideShape(slide, { preset: 'rect', x: inches(1), // ✅ Emu y: cm(3), // ✅ Emu w: emu(914400), // ✅ Emu — escape hatch h: 100, // ❌ type error: number is not assignable to Emu }); ``` `TextFormat.size` is in **points** (a plain `number`), not EMU — only positions and sizes are EMU-branded. ## Discriminated unions Where the OOXML spec gives a choice, @office-kit/pptx exposes a discriminated union: ```ts // Fill: solid | gradient | pattern | image | none type ShapeFill = | { kind: 'solid'; color: string } | { kind: 'gradient'; stops: GradientStop[]; angleDeg: number } | { kind: 'pattern'; preset: PatternPreset; foreground: string; background: string } | { kind: 'image'; partName: string } | { kind: 'none' }; // Click action: url | slide | nextSlide | prevSlide | firstSlide | lastSlide type ShapeClickAction = | { kind: 'url'; href: string } | { kind: 'slide'; target: SlideData } | { kind: 'nextSlide' | 'prevSlide' | 'firstSlide' | 'lastSlide' }; ``` `getShapeFill(shape)` returns this union; you narrow on `kind` rather than threading `null`-checks through every accessor. ## Errors - **Malformed package** — `loadPresentation` throws a descriptive `Error` naming the missing or malformed OPC part. - **Encrypted .pptx** — detected by CFB Compound Document magic and rejected up-front; decrypt with [`officecrypto-tool`](https://github.com/zurmokeeper/officecrypto-tool) first. - **Unknown image format** — `addSlideImage` / `setShapeImage` throw if the magic bytes don't match a supported format (PNG / JPEG / GIF / SVG / BMP / TIFF). Pass `options.format` to override detection. - **Off-spec IDs** — `validatePresentation(pres)` returns the list of invariant violations as `ValidationIssue[]` (severity + message). Run it before saving in CI. ## Stability 1.0 freezes the public surface under SemVer. Any function listed at /api is contract; the `_internalPackageOf` escape hatch is intentionally underscore-prefixed and may change at any minor release.