@office-kit/pptx 0.15.1 → 0.16.1

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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,26 @@
1
- # pptx-kit
1
+ # @office-kit/pptx
2
+
3
+ ## 0.16.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b8adc13: Duplicated slides now have independent charts, embedded workbooks and notes.
8
+ Editing a duplicate no longer changes the original's chart or notes. Unknown
9
+ owned dependency parts are copied too, including cyclic relationships. Layouts,
10
+ masters, themes and media remain shared. Invalid missing dependencies fail before
11
+ parts are added.
12
+
13
+ ## 0.16.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 22f1f30: feat: author and read the complex-script typeface (`<a:cs>`), and align the blank deck's font scheme with Office's
18
+
19
+ - `TextFormat.fontComplexScript` sets and reads `<a:cs typeface="…"/>`, the third typeface slot next to `font` (`<a:latin>`) and `fontEastAsian` (`<a:ea>`). It works everywhere the other two already did: `setShapeRunFormat` / `setShapeTextFormat`, `setShapeParagraphs` and `setTableCellParagraphs` (runs and `endFormat`), and it comes back from `getShapeRunFormat`, `getShapeRunFormatEffective` (including `+mj-cs` / `+mn-cs` and the theme's complex-script fallback), `getShapeParagraphElements`, `getParagraphEndFormat` and `getTableCellParagraphs`.
20
+ - `ChartTextStyle.fontComplexScript` writes `<a:cs>` on a chart title / axis / data-label / legend style, and the chart reader returns it. `font` keeps filling the Latin and East Asian slots only, so a chart can name a complex-script face on the legend alone.
21
+ - The three slots stay independent: setting one never changes the other two. Rewriting a slot writes its `typeface` attribute alone, so a `pitchFamily` / `charset` the source file carried on that element is dropped — as was already the case for `<a:latin>` and `<a:ea>`.
22
+ - `createPresentation`'s blank deck now carries the standard per-script font list (`<a:font script="Thai" typeface="Cordia New"/>` and its 46 siblings) in both `majorFont` and `minorFont`, the same list Office's own default theme ships. It is what resolves a face for text in a script no run names one for. Every deck `createPresentation()` produces therefore has a larger `theme1.xml` than before — its bytes change — and text in a script with no explicit typeface now resolves through that list.
23
+ - Chart titles and axis titles no longer carry a `lang="en-US"` language tag on their run. The chart-level tag is still `ChartSpec.language` (`<c:lang>`).
2
24
 
3
25
  ## 0.15.1
4
26
 
package/README.md CHANGED
@@ -1,72 +1,218 @@
1
1
  # @office-kit/pptx
2
2
 
3
- Generate and edit `.pptx` (PowerPoint / Office Open XML Presentation) files
4
- from TypeScript — in **Node.js or the browser**, from a single ESM bundle.
5
-
6
- > **Status: 0.x — pre-1.0, public API still evolving.** The capabilities in
7
- > the table below are exercised against real PPTX fixtures, and emitted XML is
8
- > checked against the ECMA-376 schemas with `xmllint` where it is available on
9
- > the machine running the tests. Until the 1.0 release the public API is not
10
- > frozen — breaking changes can land in a minor (`0.x`) release, so pin a
3
+ [![npm](https://img.shields.io/npm/v/@office-kit/pptx)](https://www.npmjs.com/package/@office-kit/pptx)
4
+ [![CI](https://github.com/office-kit/pptx/actions/workflows/ci.yml/badge.svg)](https://github.com/office-kit/pptx/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/@office-kit/pptx)](./LICENSE)
6
+
7
+ Read, edit, and write `.pptx` (PowerPoint / Office Open XML Presentation) files
8
+ from TypeScript, in **Node.js and the browser**, from a single ESM bundle.
9
+
10
+ **[Documentation](https://office-kit.github.io/pptx/)** ·
11
+ **[Playground](https://office-kit.github.io/pptx/playground)** (inspect a deck in your browser) ·
12
+ **[REPL](https://office-kit.github.io/pptx/repl)** (write code, watch the deck redraw)
13
+
14
+ ```ts
15
+ import { loadPresentation, replaceTokensInPresentation, savePresentation } from '@office-kit/pptx';
16
+
17
+ // A designer makes template.pptx in PowerPoint. Your code fills it in.
18
+ const pres = await loadPresentation(templateBytes);
19
+ replaceTokensInPresentation(pres, { name: 'Alice', event: 'Re:Invent', date: '2026-12-01' });
20
+ const out: Uint8Array = await savePresentation(pres);
21
+ ```
22
+
23
+ > **Status: 0.x, pre-1.0.** The capabilities in the [scope table](#scope) are
24
+ > exercised against real PPTX fixtures and validated in CI (see
25
+ > [How output is checked](#how-output-is-checked)). Until 1.0 the public API is
26
+ > not frozen: a breaking change can land in a minor (`0.x`) release, so pin a
11
27
  > version or an exact range.
12
28
 
13
- ## Why
29
+ ## Why this library
30
+
31
+ For declarative TSX authoring, use the companion
32
+ [`@office-kit/pptx-dsl`](packages/dsl) and
33
+ [`@office-kit/pptx-dev`](packages/dev) packages. They provide typed elements,
34
+ template editing, a local watch preview and PPTX export, with no React or Vue
35
+ runtime. The dev package initializes a project with VSCode tasks and a
36
+ `CLAUDE.md` authoring guide. Start with the
37
+ [step-by-step authoring guide](https://office-kit.github.io/pptx/docs/authoring):
38
+ the CLI and DSL are not yet published to npm, so it includes a verified
39
+ source-checkout installation.
40
+
41
+ - **It reads as well as it writes.** Open a deck made in PowerPoint, Keynote,
42
+ or Google Slides, change it, and save it. Every setter has a getter, and
43
+ there are deck-wide queries (find every hyperlink, every comment by an
44
+ author, every slide with an empty title).
45
+ - **Parts it does not model survive the round trip.** SmartArt, OLE objects,
46
+ video, modern threaded comments, and vendor extensions are carried through
47
+ untouched. The library never silently strips what it does not understand.
48
+ - **The output is valid, not "valid enough".** Microsoft's own
49
+ `OpenXmlValidator` gates every CI run. A file that opens in PowerPoint but
50
+ breaks Keynote is treated as a bug.
51
+ - **One ESM bundle for Node and the browser.** No `fs`, `Buffer`, or `zlib` on
52
+ the hot path, and one runtime dependency ([fflate](https://github.com/101arrowz/fflate), for ZIP).
53
+ - **You ship only what you import.** The API is 451 side-effect-free functions.
54
+ A `load → save` bundle is about 56 KB unminified; the entire API is about
55
+ 142 KB. CI fails if either grows past its cap (`test/tree-shake.test.ts`).
56
+ - **Types follow the spec.** The model mirrors ECMA-376 Part 1 §19
57
+ (PresentationML). Positions are branded `Emu` numbers, so inches and points
58
+ cannot be mixed up by accident.
59
+
60
+ ## How it differs from PptxGenJS
61
+
62
+ [PptxGenJS](https://github.com/gitbrent/PptxGenJS) is the established way to
63
+ generate a deck in JavaScript, and it is good at that job. The difference is
64
+ direction: **PptxGenJS writes new files; `@office-kit/pptx` reads, edits, and
65
+ writes.** If your deck starts from a template, or from a file a person made,
66
+ PptxGenJS cannot open it.
67
+
68
+ Compared against PptxGenJS 4.0.1:
69
+
70
+ | | `@office-kit/pptx` | PptxGenJS |
71
+ | --------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
72
+ | Open and edit an existing `.pptx` | ✅ Load, change, save; unknown parts are preserved | ❌ Creates new files only |
73
+ | Templates | Any `.pptx` a designer made in PowerPoint | Slide masters defined in code (`defineSlideMaster`) |
74
+ | Read back what is in a deck | ✅ Every setter has a getter, plus deck-wide queries | ❌ The API is write-only |
75
+ | API shape | Tree-shakeable functions (`addSlideChart(slide, …)`) | One class with methods (`slide.addChart(…)`) |
76
+ | Module formats | ESM only | ESM, CommonJS, and a script-tag bundle |
77
+ | Runtime dependencies | 1 (`fflate`) | 4 (`jszip`, `image-size`, `https`, `@types/node`) |
78
+ | Slide transitions | ✅ | ❌ |
79
+ | Animations | ✅ Four entrance / exit presets | ❌ |
80
+ | Comments | ✅ | ❌ (speaker notes only) |
81
+ | Chart types you can author | Bar, column, line, pie, doughnut, area; combos, secondary axis, trendlines, per-point labels | Those, plus scatter, bubble, radar, and 3D bar |
82
+ | Audio, video, YouTube embeds | ❌ Not yet (media already in a deck is preserved) | ✅ |
83
+ | HTML `<table>` to slides | ❌ | ✅ With automatic paging |
84
+ | Render a slide to an image | ✅ SVG and PNG, via [`@office-kit/pptx-preview`](packages/preview) | ❌ |
85
+ | How output is checked | Open XML SDK validator and ECMA-376 XSDs, in CI | Manual runs in PowerPoint and other apps before a release |
86
+
87
+ **Pick PptxGenJS** if you only ever generate new decks and need video,
88
+ scatter / bubble / radar charts, HTML-table import, CommonJS, or a
89
+ `<script>`-tag build.
90
+
91
+ **Pick `@office-kit/pptx`** if a template, an existing deck, or a validation
92
+ requirement is involved, or if you need to read a deck as well as write one.
93
+
94
+ The two also work together. Decks written by PptxGenJS are part of this
95
+ library's test fixtures (`test/pptxgenjs-compat.test.ts`), so you can generate
96
+ with PptxGenJS and post-process the result here.
97
+
98
+ ### The same slide in both
14
99
 
15
- The JavaScript ecosystem has several PPTX libraries, but they typically pick
16
- one trade-off:
100
+ ```ts
101
+ // PptxGenJS
102
+ import pptxgen from 'pptxgenjs';
103
+
104
+ const pptx = new pptxgen();
105
+ const slide = pptx.addSlide();
106
+ slide.addText('Q3 Review', { x: 1, y: 0.5, w: 8, h: 1, fontSize: 28, bold: true });
107
+ slide.addChart(
108
+ pptx.ChartType.bar,
109
+ [{ name: 'Revenue', labels: ['Q1', 'Q2'], values: [120, 180] }],
110
+ {
111
+ x: 1,
112
+ y: 1.5,
113
+ w: 8,
114
+ h: 4,
115
+ barDir: 'col',
116
+ },
117
+ );
118
+ await pptx.writeFile({ fileName: 'out.pptx' });
119
+ ```
17
120
 
18
- - **Node-only** with a Buffer-shaped API → does not work in the browser.
19
- - **Browser-only** wrapping a fixed template → cannot author from scratch.
20
- - **Loose XML strings** that "usually open" → break in Keynote / Google Slides
21
- / the Open XML SDK validator.
121
+ ```ts
122
+ // @office-kit/pptx
123
+ import {
124
+ addBlankSlide,
125
+ addSlideChart,
126
+ addSlideTextBox,
127
+ createPresentation,
128
+ inches,
129
+ setShapeTextFormat,
130
+ } from '@office-kit/pptx';
131
+ import { savePresentationToFile } from '@office-kit/pptx/node';
22
132
 
23
- `@office-kit/pptx` is built around a different stance:
133
+ const pres = createPresentation();
134
+ const slide = addBlankSlide(pres);
135
+ const title = addSlideTextBox(slide, {
136
+ x: inches(1),
137
+ y: inches(0.5),
138
+ w: inches(8),
139
+ h: inches(1),
140
+ text: 'Q3 Review',
141
+ });
142
+ setShapeTextFormat(title, { size: 28, bold: true });
143
+ addSlideChart(slide, {
144
+ x: inches(1),
145
+ y: inches(1.5),
146
+ w: inches(8),
147
+ h: inches(4),
148
+ spec: {
149
+ kind: 'column',
150
+ categories: ['Q1', 'Q2'],
151
+ series: [{ name: 'Revenue', values: [120, 180] }],
152
+ },
153
+ });
154
+ await savePresentationToFile(pres, 'out.pptx');
155
+ ```
24
156
 
25
- - One ESM bundle that runs in **Node and the browser**.
26
- - A typed object model that mirrors the **OOXML PresentationML** spec
27
- (ECMA-376 Part 1, §19). When the spec says something is a choice, our types
28
- say it is a discriminated union.
29
- - Output that passes Microsoft's
30
- [Open XML SDK Productivity Tool](https://github.com/dotnet/Open-XML-SDK)
31
- validator, not just PowerPoint's "open and pray."
32
- - Two complementary paths: **author from scratch** _or_ **edit a template**.
157
+ Two differences show up straight away. Positions are explicit units
158
+ (`inches(1)`, `cm(2.5)`, `pt(12)`) rather than bare numbers, and everything is
159
+ a function that takes the thing it changes, which is what lets a bundler drop
160
+ the ones you do not call.
161
+
162
+ ## How output is checked
163
+
164
+ - **Open XML SDK.** A CI job generates the sample decks and runs Microsoft's
165
+ `OpenXmlValidator` over them (`tools/ooxml-validate`). Any validation error
166
+ fails the build.
167
+ - **ECMA-376 XSDs.** Emitted XML is validated against the official schemas
168
+ with `xmllint` in the test suite (these tests skip on a machine without
169
+ `xmllint`).
170
+ - **Real files and a parity corpus.** Fixture tests round-trip decks written
171
+ by python-pptx and PptxGenJS, and `test/corpus` authors the same slide once
172
+ with PptxGenJS and once with this library, then diffs the two drawing trees.
173
+ The suite runs on Node 22, 24, and 26.
174
+ - **At runtime.** `validatePresentation(pres)` checks package invariants
175
+ (missing relationships, dangling slide ids, duplicate shape ids, layouts
176
+ without masters) in Node and the browser.
33
177
 
34
178
  ## Scope
35
179
 
36
- The work is split into four levels of completeness. The current `0.x` line
37
- covers levels 1-3 in full and level 4 in part; the table tracks where each
38
- capability stands today. Items marked "post-1.0" are not implemented yet:
180
+ The work is split into four levels. The `0.x` line covers levels 1–3 and part
181
+ of level 4. Items marked "post-1.0" are not implemented yet:
39
182
 
40
183
  | Level | Capability | 0.x |
41
184
  | ----- | ------------------------------------------------------------------- | ------------------------------- |
42
185
  | L1 | Read an existing PPTX, save it back without corruption | ✅ |
43
- | L2 | Template edit — text replacement, image swap, add slide from layout | ✅ |
44
- | L3 | Authoring — shapes, text, tables, fills, effects, transforms | ✅ |
186
+ | L2 | Template edit: text replacement, image swap, add slide from layout | ✅ |
187
+ | L3 | Authoring: shapes, text, tables, fills, effects, transforms, groups | ✅ |
45
188
  | L3 | Authoring on top of existing themes / masters / layouts | ✅ |
189
+ | L3 | Rebranding a deck: theme colors and theme fonts | ✅ |
46
190
  | L3 | Constructing new themes / masters / layouts from scratch | ❌ post-1.0 |
47
- | L3 | Charts (all common types) with embedded data | ✅ |
191
+ | L3 | Charts: bar, column, line, pie, doughnut, area, and combos | ✅ |
192
+ | L3 | Charts: scatter, radar, bubble | Read + preview only |
48
193
  | L4 | Notes, comments, transitions | ✅ |
49
- | L4 | Simple animations (entrance / exit / emphasis presets) | ✅ |
194
+ | L4 | Simple animations (entrance / exit presets) | ✅ |
195
+ | L4 | Audio / video authoring | ❌ post-1.0 (read pass-through) |
50
196
  | L4 | SmartArt authoring | ❌ post-1.0 (read pass-through) |
51
197
  | L4 | Complex animation timing trees | ❌ post-1.0 |
52
198
  | L4 | OLE / ActiveX authoring | ❌ post-1.0 (read pass-through) |
53
199
  | L4 | Document encryption (read + write) | ❌ post-1.0 |
54
200
 
55
- Out-of-scope content is still **preserved on round-trip** — `@office-kit/pptx` will
56
- never silently strip parts it doesn't model. That's the L1 contract.
201
+ Out-of-scope content is still **preserved on round-trip**. That is the L1
202
+ contract.
57
203
 
58
204
  When NOT to use this:
59
205
 
60
206
  - You need a **pixel-perfect** PPTX rendering (print, archival). The
61
207
  companion [`@office-kit/pptx-preview`](packages/preview) package renders slides to
62
- SVG in the browser and to PNG on the server — its closeness to LibreOffice
63
- is measured per slide and gated in CI (`site/fidelity`) — but it is a
208
+ SVG in the browser and to PNG on the server, and its closeness to LibreOffice
209
+ is measured per slide and gated in CI (`site/fidelity`). It is a
64
210
  high-fidelity preview, not a spec-complete paint engine. For
65
211
  pixel-authoritative output, use PowerPoint itself or LibreOffice headless.
66
- - You need a thin DSL for one-off "report" slides and do not care about
67
- schema validity. A simpler library will be lighter.
212
+ - You only generate new decks and need a feature in the PptxGenJS column
213
+ above. Use PptxGenJS.
68
214
  - You want to convert PPTX to another format (Keynote, ODP). Out of scope
69
- forever — that's a renderer's job.
215
+ forever; that is a renderer's job.
70
216
 
71
217
  ## Install
72
218
 
@@ -83,7 +229,8 @@ yarn add @office-kit/pptx
83
229
  @office-kit/pptx exposes a single tree-shakeable free-function API. Every
84
230
  capability is a named export — `loadPresentation`, `savePresentation`,
85
231
  `addSlideTextBox`, `setShapeFill`, etc. Bundlers drop every entry you
86
- don't import, so the minimal `load → save` bundle is **~60 KB**.
232
+ don't import, so the minimal `load → save` bundle is **about 56 KB**
233
+ unminified. CI enforces the bound in `test/tree-shake.test.ts`.
87
234
 
88
235
  ```ts
89
236
  import {
@@ -110,8 +257,6 @@ declaring a deck done. Its [worked example](skill/examples/business-deck.md) is
110
257
  exercised by the test suite, so the code there is known to produce a
111
258
  schema-valid deck.
112
259
 
113
- CI enforces the tree-shake bound in `test/tree-shake.test.ts`.
114
-
115
260
  ## Usage
116
261
 
117
262
  ### Edit a template
@@ -184,7 +329,7 @@ setShapeText(findSlidePlaceholder(slide, 'ctrTitle')!, 'Authored with @office-ki
184
329
  const out: Uint8Array = await savePresentation(pres);
185
330
  ```
186
331
 
187
- ### Build a deck from a blank template
332
+ ### Add slides to your own template
188
333
 
189
334
  ```ts
190
335
  import {
@@ -201,7 +346,8 @@ import {
201
346
  setShapeText,
202
347
  } from '@office-kit/pptx';
203
348
 
204
- const pres = await loadPresentation(await fetch('/blank.pptx').then((r) => r.arrayBuffer()));
349
+ // Layout names come from the template; list them with getSlideLayouts(pres).
350
+ const pres = await loadPresentation(await fetch('/template.pptx').then((r) => r.arrayBuffer()));
205
351
 
206
352
  const titleLayout = findSlideLayout(pres, 'Title Slide')!;
207
353
  const slide1 = addSlide(pres, { layout: titleLayout });
@@ -277,7 +423,7 @@ addSlideChart(slide!, {
277
423
  w: inches(8),
278
424
  h: inches(4.5),
279
425
  spec: {
280
- kind: 'column', // bar | column | line | pie | doughnut | area
426
+ kind: 'column', // bar | column | line | pie | doughnut | area (combine per series with `chartKind`)
281
427
  categories: ['Q1', 'Q2', 'Q3', 'Q4'],
282
428
  series: [
283
429
  { name: 'Revenue', values: [120, 180, 240, 300] },
@@ -378,28 +524,63 @@ shown together.
378
524
  | Shape removal | `removeShape` |
379
525
  | Tables | `getTableCell` / `getTableCells`, `setTableCellText` / `getTableCellText`, `setTableCellParagraphs` / `getTableCellParagraphs`, `setTableCellFill` / `clearTableCellFill`, `setTableCellAlignment`, `setTableCellTextFormat`, `insertTableRow` / `removeTableRow`, `insertTableColumn` / `removeTableColumn`, `mergeTableCells` / `getTableCellSpan` |
380
526
  | Charts | `addSlideChart`, `getSlideCharts`, `setChartSpec` — kinds: `bar`, `column`, `line`, `pie`, `doughnut`, `area`; axis tick labels via `categoryAxisTickLabelPos` / `valueAxisTickLabelPos` / `secondaryValueAxis.tickLabelPos`; axis line / gridline widths via `valueAxisLineWidthEmu`, `valueAxisMajorGridlineWidthEmu` and their category / secondary-axis mirrors; series `lineColor` / `markerColor` / `markerLineColor`; `dataLabels.showLeaderLines` |
381
- | Theme | `getPresentationTheme` — color scheme (`accent1`..`accent6`, `dark1`, `light1`, `hyperlink`, ...) |
527
+ | Theme | `getPresentationTheme` / `setPresentationTheme` — color scheme (`accent1`..`accent6`, `dark1`, `light1`, `hyperlink`, ...); `getPresentationFonts` / `setPresentationFonts` — major / minor Latin, East Asian, and complex-script faces |
528
+ | Groups | `groupShapes`, `ungroupShapes`, `getGroupChildren`, `getGroupTransform` |
529
+ | Text autofit | `setShapeTextAutoFit` / `getShapeTextAutoFit`, `getShapeTextAutoFitParams` |
530
+ | Preset adjustments | `setShapeAdjustValues`; `getShapeCustomGeometry` (custom geometry is read-only) |
531
+ | Document properties | `getCoreProperties` / `setCoreProperties`, `touchModified`, `getThumbnail` / `setThumbnail` / `removeThumbnail` |
532
+ | Package inspection | `listPackageParts`, `readPackagePart`, `getMediaParts`, `getOrphanMediaPartNames`, `getPackageSize`, `compactPackage` |
382
533
  | Validation | `validatePresentation(pres)` — invariant checks, returns `ValidationIssue[]` |
383
534
  | Units | `inches(n)`, `cm(n)`, `mm(n)`, `pt(n)`, `emu(n)` — return branded `Emu` numbers |
384
535
 
385
- Text formats (`TextFormat`, including a paragraph's `endFormat`) cover the Latin and East Asian typefaces (`font`, `fontEastAsian`). A complex-script typeface (`<a:cs>`) is neither read nor authored: it survives a load / save untouched, but is not carried over when paragraphs are rebuilt from what the getters return.
536
+ Text formats (`TextFormat`, including a paragraph's `endFormat`) cover the Latin, East Asian and complex-script typefaces (`font`, `fontEastAsian`, `fontComplexScript` — `<a:latin>`, `<a:ea>`, `<a:cs>`). Each is authored and read on its own; setting one leaves the others as they were. Chart labels carry the Latin / East Asian pair and the complex-script slot on `ChartTextStyle`: `font` fills `<a:latin>` and `<a:ea>`, `fontComplexScript` fills `<a:cs>`, and neither implies the other. Rebuilding a typeface writes the `typeface` attribute only, so any `pitchFamily` / `charset` the source file carried on that element is dropped.
537
+
538
+ Runs that name no face fall back to the theme's font scheme, which is also where a per-script list (`<a:font script="Thai" typeface="Cordia New"/>` and 46 siblings) lives. `createPresentation`'s blank deck carries Office's own list in both `majorFont` and `minorFont`.
386
539
 
387
540
  Authored XML text and attribute values must contain only XML 1.0 characters. Illegal C0 controls (except tab, LF, and CR), U+FFFE, U+FFFF, and unpaired UTF-16 surrogates throw an error identifying the code point. Remove these characters before authoring; valid supplementary characters such as emoji are preserved.
388
541
 
542
+ ## Preview and text-overflow checks
543
+
544
+ [`@office-kit/pptx-preview`](packages/preview) is a companion package that
545
+ renders a slide to SVG (browser and Node) or to PNG (Node, via resvg, with no
546
+ headless Office). `auditTextLayout` reports text that overflows its box or
547
+ wraps unexpectedly, which is how an automated pipeline catches a broken slide
548
+ before a person sees it.
549
+
550
+ ```ts
551
+ import { getSlides } from '@office-kit/pptx';
552
+ import { renderSlideToSvg } from '@office-kit/pptx-preview';
553
+
554
+ const svg = renderSlideToSvg(pres, getSlides(pres)[0]!);
555
+ ```
556
+
557
+ ## Office Kit
558
+
559
+ `@office-kit/pptx` is one of three libraries built on the same rules: the
560
+ ECMA-376 spec is the source of truth, output has to validate, and one ESM
561
+ build has to run everywhere.
562
+
563
+ | Package | Files |
564
+ | -------------------------------------------------------- | ------------------ |
565
+ | [`@office-kit/pptx`](https://github.com/office-kit/pptx) | PowerPoint `.pptx` |
566
+ | [`@office-kit/xlsx`](https://github.com/office-kit/xlsx) | Excel `.xlsx` |
567
+ | [`@office-kit/docx`](https://github.com/office-kit/docx) | Word `.docx` |
568
+
389
569
  ## Compatibility
390
570
 
391
- - **Node**: >= 24.16.
571
+ - **Node**: >= 22.18 (CI runs 22, 24, and 26).
392
572
  - **Browsers**: current and current-1 of Chrome, Firefox, Safari, Edge.
393
573
  - **TypeScript**: >= 5.4 (for strict `satisfies` and `const` type parameters).
394
- - **Output**: PPTX files checked against the ECMA-376 schemas with `xmllint`
395
- where it is available, and smoke-tested against PowerPoint (current),
396
- Keynote (current), Google Slides, and LibreOffice Impress.
574
+ - **Output**: validated with the Open XML SDK and the ECMA-376 schemas (see
575
+ [How output is checked](#how-output-is-checked)), and smoke-tested against
576
+ PowerPoint (current), Keynote (current), Google Slides, and LibreOffice
577
+ Impress.
397
578
 
398
579
  ## Development
399
580
 
400
581
  ```sh
401
582
  git clone --recurse-submodules git@github.com:office-kit/pptx.git
402
- cd @office-kit/pptx
583
+ cd pptx
403
584
  pnpm install
404
585
  pnpm test
405
586
  ```