pptx-kit 0.0.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1657 -0
- package/LICENSE +21 -0
- package/README.md +405 -3
- package/dist/index.d.ts +5118 -0
- package/dist/index.js +13377 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +14 -0
- package/dist/node.js +13389 -0
- package/dist/node.js.map +1 -0
- package/package.json +75 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuichiro Yamashita
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,408 @@
|
|
|
1
1
|
# pptx-kit
|
|
2
2
|
|
|
3
|
-
Generate and edit `.pptx` (
|
|
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.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
> **Status: 1.0 — public API stabilized.** Every capability in the table
|
|
7
|
+
> below works end-to-end against real PPTX fixtures, with every emitted XML
|
|
8
|
+
> part validated against the ECMA-376 schemas via `xmllint` in CI. Future
|
|
9
|
+
> 1.x releases are SemVer-compatible.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
The JavaScript ecosystem has several PPTX libraries, but they typically pick
|
|
14
|
+
one trade-off:
|
|
15
|
+
|
|
16
|
+
- **Node-only** with a Buffer-shaped API → does not work in the browser.
|
|
17
|
+
- **Browser-only** wrapping a fixed template → cannot author from scratch.
|
|
18
|
+
- **Loose XML strings** that "usually open" → break in Keynote / Google Slides
|
|
19
|
+
/ the Open XML SDK validator.
|
|
20
|
+
|
|
21
|
+
`pptx-kit` is built around a different stance:
|
|
22
|
+
|
|
23
|
+
- One ESM bundle that runs in **Node and the browser**.
|
|
24
|
+
- A typed object model that mirrors the **OOXML PresentationML** spec
|
|
25
|
+
(ECMA-376 Part 1, §19). When the spec says something is a choice, our types
|
|
26
|
+
say it is a discriminated union.
|
|
27
|
+
- Output that passes Microsoft's
|
|
28
|
+
[Open XML SDK Productivity Tool](https://github.com/dotnet/Open-XML-SDK)
|
|
29
|
+
validator, not just PowerPoint's "open and pray."
|
|
30
|
+
- Two complementary paths: **author from scratch** _or_ **edit a template**.
|
|
31
|
+
|
|
32
|
+
## Scope
|
|
33
|
+
|
|
34
|
+
The work is split into four levels of completeness. The v1.0 release targets
|
|
35
|
+
levels 1-3 in full and level 4 in part:
|
|
36
|
+
|
|
37
|
+
| Level | Capability | v1.0 |
|
|
38
|
+
| ----- | ------------------------------------------------------------------- | ------------------------------- |
|
|
39
|
+
| L1 | Read an existing PPTX, save it back without corruption | ✅ |
|
|
40
|
+
| L2 | Template edit — text replacement, image swap, add slide from layout | ✅ |
|
|
41
|
+
| L3 | Authoring — shapes, text, tables, fills, effects, transforms | ✅ |
|
|
42
|
+
| L3 | Authoring on top of existing themes / masters / layouts | ✅ |
|
|
43
|
+
| L3 | Constructing new themes / masters / layouts from scratch | ❌ post-1.0 |
|
|
44
|
+
| L3 | Charts (all common types) with embedded data | ✅ |
|
|
45
|
+
| L4 | Notes, comments, transitions | ✅ |
|
|
46
|
+
| L4 | Simple animations (entrance / exit / emphasis presets) | ✅ |
|
|
47
|
+
| L4 | SmartArt authoring | ❌ post-1.0 (read pass-through) |
|
|
48
|
+
| L4 | Complex animation timing trees | ❌ post-1.0 |
|
|
49
|
+
| L4 | OLE / ActiveX authoring | ❌ post-1.0 (read pass-through) |
|
|
50
|
+
| L4 | Document encryption (read + write) | ❌ post-1.0 |
|
|
51
|
+
|
|
52
|
+
Out-of-scope content is still **preserved on round-trip** — `pptx-kit` will
|
|
53
|
+
never silently strip parts it doesn't model. That's the L1 contract.
|
|
54
|
+
|
|
55
|
+
When NOT to use this:
|
|
56
|
+
|
|
57
|
+
- You need a **pixel-perfect** PPTX rendering (print, archival). The
|
|
58
|
+
companion [`pptx-kit-preview`](packages/preview) package renders slides to
|
|
59
|
+
SVG in the browser and to PNG on the server — its closeness to LibreOffice
|
|
60
|
+
is measured per slide and gated in CI (`site/fidelity`) — but it is a
|
|
61
|
+
high-fidelity preview, not a spec-complete paint engine. For
|
|
62
|
+
pixel-authoritative output, use PowerPoint itself or LibreOffice headless.
|
|
63
|
+
- You need a thin DSL for one-off "report" slides and do not care about
|
|
64
|
+
schema validity. A simpler library will be lighter.
|
|
65
|
+
- You want to convert PPTX to another format (Keynote, ODP). Out of scope
|
|
66
|
+
forever — that's a renderer's job.
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm install pptx-kit
|
|
72
|
+
# or
|
|
73
|
+
pnpm add pptx-kit
|
|
74
|
+
# or
|
|
75
|
+
yarn add pptx-kit
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## One API
|
|
79
|
+
|
|
80
|
+
pptx-kit exposes a single tree-shakeable free-function API. Every
|
|
81
|
+
capability is a named export — `loadPresentation`, `savePresentation`,
|
|
82
|
+
`addSlideTextBox`, `setShapeFill`, etc. Bundlers drop every entry you
|
|
83
|
+
don't import, so the minimal `load → save` bundle is **~60 KB**.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {
|
|
87
|
+
findSlidePlaceholder,
|
|
88
|
+
getSlides,
|
|
89
|
+
loadPresentation,
|
|
90
|
+
savePresentation,
|
|
91
|
+
setShapeText,
|
|
92
|
+
} from 'pptx-kit';
|
|
93
|
+
|
|
94
|
+
const pres = await loadPresentation(bytes);
|
|
95
|
+
const title = findSlidePlaceholder(getSlides(pres)[0]!, 'title');
|
|
96
|
+
if (title) setShapeText(title, 'Hello');
|
|
97
|
+
const out = await savePresentation(pres);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
CI enforces the tree-shake bound in `test/tree-shake.test.ts`.
|
|
101
|
+
|
|
102
|
+
## Usage
|
|
103
|
+
|
|
104
|
+
### Edit a template
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import {
|
|
108
|
+
findSlidePlaceholder,
|
|
109
|
+
getSlides,
|
|
110
|
+
loadPresentation,
|
|
111
|
+
savePresentation,
|
|
112
|
+
setShapeText,
|
|
113
|
+
} from 'pptx-kit';
|
|
114
|
+
|
|
115
|
+
const pres = await loadPresentation(existingPptxBytes);
|
|
116
|
+
const cover = getSlides(pres)[0]!;
|
|
117
|
+
const title = findSlidePlaceholder(cover, 'title');
|
|
118
|
+
if (title) setShapeText(title, 'Q3 Review');
|
|
119
|
+
const body = findSlidePlaceholder(cover, 'body');
|
|
120
|
+
if (body) setShapeText(body, 'Numbers up and to the right.');
|
|
121
|
+
|
|
122
|
+
const out: Uint8Array = await savePresentation(pres);
|
|
123
|
+
// Node: fs.writeFile('out.pptx', out)
|
|
124
|
+
// Browser: new Blob([out], { type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Token-based template fill
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { loadPresentation, replaceTokensInPresentation, savePresentation } from 'pptx-kit';
|
|
131
|
+
|
|
132
|
+
const pres = await loadPresentation(templateBytes);
|
|
133
|
+
// Replaces `{{name}}`, `{{event}}`, `{{date}}` across every slide.
|
|
134
|
+
replaceTokensInPresentation(pres, { name: 'Alice', event: 'Re:Invent', date: '2026-12-01' });
|
|
135
|
+
const out = await savePresentation(pres);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Build a deck from scratch (no template file)
|
|
139
|
+
|
|
140
|
+
`createPresentation()` returns an immediately-authorable deck — a slide
|
|
141
|
+
master, the Office theme, and three layouts (`Blank`, `Title Slide`,
|
|
142
|
+
`Title and Content`) — with no slides yet. No `.pptx` template needed.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import {
|
|
146
|
+
addContentSlide,
|
|
147
|
+
addTitleSlide,
|
|
148
|
+
createPresentation,
|
|
149
|
+
findSlideLayoutByType,
|
|
150
|
+
addSlide,
|
|
151
|
+
findSlidePlaceholder,
|
|
152
|
+
savePresentation,
|
|
153
|
+
setShapeText,
|
|
154
|
+
} from 'pptx-kit';
|
|
155
|
+
|
|
156
|
+
// Defaults to 16:9; pass { size: '4:3' } for the classic ratio.
|
|
157
|
+
const pres = createPresentation();
|
|
158
|
+
|
|
159
|
+
// Sugar helpers pick the right layout by its locale-stable type token.
|
|
160
|
+
addTitleSlide(pres, 'Q3 Business Review');
|
|
161
|
+
addContentSlide(pres, { title: 'Agenda', body: 'Highlights and risks' });
|
|
162
|
+
|
|
163
|
+
// Or bind a layout explicitly. Prefer findSlideLayoutByType — it matches
|
|
164
|
+
// the `type` token (`'title'`, `'obj'`, `'blank'`), which is stable
|
|
165
|
+
// across PowerPoint UI languages. findSlideLayout(pres, 'Blank') matches
|
|
166
|
+
// the user-visible name, which is case-sensitive and localized.
|
|
167
|
+
const titleLayout = findSlideLayoutByType(pres, 'title')!;
|
|
168
|
+
const slide = addSlide(pres, { layout: titleLayout });
|
|
169
|
+
setShapeText(findSlidePlaceholder(slide, 'ctrTitle')!, 'Authored with pptx-kit');
|
|
170
|
+
|
|
171
|
+
const out: Uint8Array = await savePresentation(pres);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Build a deck from a blank template
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import {
|
|
178
|
+
addSlide,
|
|
179
|
+
addSlideImage,
|
|
180
|
+
addSlideTextBox,
|
|
181
|
+
duplicateSlide,
|
|
182
|
+
findSlideLayout,
|
|
183
|
+
findSlidePlaceholder,
|
|
184
|
+
inches,
|
|
185
|
+
loadPresentation,
|
|
186
|
+
moveSlide,
|
|
187
|
+
savePresentation,
|
|
188
|
+
setShapeText,
|
|
189
|
+
} from 'pptx-kit';
|
|
190
|
+
|
|
191
|
+
const pres = await loadPresentation(await fetch('/blank.pptx').then((r) => r.arrayBuffer()));
|
|
192
|
+
|
|
193
|
+
const titleLayout = findSlideLayout(pres, 'Title Slide')!;
|
|
194
|
+
const slide1 = addSlide(pres, { layout: titleLayout });
|
|
195
|
+
setShapeText(findSlidePlaceholder(slide1, 'ctrTitle')!, 'pptx-kit demo');
|
|
196
|
+
setShapeText(findSlidePlaceholder(slide1, 'subTitle')!, 'an OOXML library for TypeScript');
|
|
197
|
+
|
|
198
|
+
const blank = findSlideLayout(pres, 'Blank')!;
|
|
199
|
+
const slide2 = addSlide(pres, { layout: blank });
|
|
200
|
+
addSlideTextBox(slide2, {
|
|
201
|
+
x: inches(1),
|
|
202
|
+
y: inches(1),
|
|
203
|
+
w: inches(8),
|
|
204
|
+
h: inches(1),
|
|
205
|
+
text: 'Free-form text box',
|
|
206
|
+
});
|
|
207
|
+
addSlideImage(slide2, imageBytes, { x: inches(1), y: inches(3), w: inches(3), h: inches(3) });
|
|
208
|
+
|
|
209
|
+
const dup = duplicateSlide(pres, slide2);
|
|
210
|
+
moveSlide(pres, dup, 0);
|
|
211
|
+
|
|
212
|
+
const out: Uint8Array = await savePresentation(pres);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Replace an image in place
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import {
|
|
219
|
+
getShapeKind,
|
|
220
|
+
getShapeName,
|
|
221
|
+
getSlideShapes,
|
|
222
|
+
getSlides,
|
|
223
|
+
loadPresentation,
|
|
224
|
+
savePresentation,
|
|
225
|
+
setShapeImage,
|
|
226
|
+
} from 'pptx-kit';
|
|
227
|
+
|
|
228
|
+
const pres = await loadPresentation(templateBytes);
|
|
229
|
+
for (const slide of getSlides(pres)) {
|
|
230
|
+
for (const shape of getSlideShapes(slide)) {
|
|
231
|
+
if (getShapeKind(shape) === 'picture' && getShapeName(shape) === 'Logo') {
|
|
232
|
+
setShapeImage(shape, newLogoBytes); // format auto-detected; geometry preserved
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const out = await savePresentation(pres);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Node convenience entry
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { loadPresentationFile, savePresentationToFile } from 'pptx-kit/node';
|
|
243
|
+
|
|
244
|
+
const pres = await loadPresentationFile('./template.pptx');
|
|
245
|
+
await savePresentationToFile(pres, './out.pptx');
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Charts
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { addSlideChart, getSlides, loadPresentation, savePresentation, inches } from 'pptx-kit';
|
|
252
|
+
|
|
253
|
+
const pres = await loadPresentation(templateBytes);
|
|
254
|
+
const slide = getSlides(pres)[0];
|
|
255
|
+
addSlideChart(slide!, {
|
|
256
|
+
x: inches(0.5),
|
|
257
|
+
y: inches(0.5),
|
|
258
|
+
w: inches(8),
|
|
259
|
+
h: inches(4.5),
|
|
260
|
+
spec: {
|
|
261
|
+
kind: 'column', // bar | column | line | pie | doughnut | area
|
|
262
|
+
categories: ['Q1', 'Q2', 'Q3', 'Q4'],
|
|
263
|
+
series: [
|
|
264
|
+
{ name: 'Revenue', values: [120, 180, 240, 300] },
|
|
265
|
+
{ name: 'Cost', values: [80, 90, 130, 160] },
|
|
266
|
+
],
|
|
267
|
+
title: 'FY26 plan',
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await savePresentation(pres);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The embedded xlsx that PowerPoint requires for "Edit data" is generated
|
|
275
|
+
automatically. Inline `<c:strCache>` / `<c:numCache>` caches mean the
|
|
276
|
+
chart renders without opening the workbook.
|
|
277
|
+
|
|
278
|
+
### Animations
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import { setShapeAnimation, getSlideShapes, getSlides } from 'pptx-kit';
|
|
282
|
+
|
|
283
|
+
const slide = getSlides(pres)[0]!;
|
|
284
|
+
const shape = getSlideShapes(slide)[0]!;
|
|
285
|
+
setShapeAnimation(shape, { effect: 'fadeIn', durationMs: 800 });
|
|
286
|
+
// effects: 'fadeIn' | 'fadeOut' | 'appear' | 'disappear'
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Comments
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
import { addSlideComment, getSlides } from 'pptx-kit';
|
|
293
|
+
|
|
294
|
+
const slide = getSlides(pres)[0]!;
|
|
295
|
+
addSlideComment(slide, {
|
|
296
|
+
author: { name: 'Reviewer A' },
|
|
297
|
+
text: 'Punch up the numbers here.',
|
|
298
|
+
position: { x: 1_000_000, y: 1_000_000 }, // optional EMU coords
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Gradient fills
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
import { setShapeGradientFill } from 'pptx-kit';
|
|
306
|
+
|
|
307
|
+
setShapeGradientFill(shape, {
|
|
308
|
+
stops: [
|
|
309
|
+
{ offset: 0, color: '#FF0000' },
|
|
310
|
+
{ offset: 1, color: '#0000FF' },
|
|
311
|
+
],
|
|
312
|
+
angleDeg: 90, // top → bottom
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Validation
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
import { validatePresentation } from 'pptx-kit';
|
|
320
|
+
|
|
321
|
+
const issues = validatePresentation(pres);
|
|
322
|
+
for (const i of issues) console.error(i.severity, i.message);
|
|
323
|
+
// Catches missing rels, dangling slide ids, layouts without masters, etc.
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### API surface (current state)
|
|
327
|
+
|
|
328
|
+
Each row lists the free-function entry points. Read/write pairs are
|
|
329
|
+
shown together.
|
|
330
|
+
|
|
331
|
+
| Capability | API |
|
|
332
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
333
|
+
| Load / save | `loadPresentation(input)`, `savePresentation(pres)`, `loadPresentationFile(path)` (node), `savePresentationToFile(pres, path)` (node) |
|
|
334
|
+
| Create | `createPresentation({ size?: '16:9' \| '4:3' })` — blank deck with master + theme + `Blank` / `Title Slide` / `Title and Content` layouts |
|
|
335
|
+
| Slide CRUD | `getSlides`, `getSlideAt`, `getSlideIndex`, `addSlide`, `removeSlide`, `moveSlide`, `duplicateSlide`, `clearSlideShapes` |
|
|
336
|
+
| Slide layout | `getSlideLayouts`, `findSlideLayout` (by name — case-sensitive, exact; pass a `RegExp` for case-insensitive), `findSlideLayoutByType` (by locale-stable `type` token — preferred), `getSlideLayout(slide)`, `setSlideLayout(slide, layout)`, `getSlideLayoutName`, `getSlideLayoutType` |
|
|
337
|
+
| Slide metadata | `getSlideTitle` / `setSlideTitle`, `getSlideSize` / `setSlideSize`, `isSlideHidden` / `setSlideHidden`, `getSlideText` |
|
|
338
|
+
| Slide sections | `getSlideSections`, `setSlideSections` (p14 sectionLst) |
|
|
339
|
+
| Placeholders | `findSlidePlaceholder(slide, 'title' \| 'body' \| ...)` |
|
|
340
|
+
| Token / text replace | `replaceTokensInPresentation`, `replaceTokensInSlide`, `replaceTextInPresentation`, `replaceTextInSlide` |
|
|
341
|
+
| Background | `getSlideBackground` / `setSlideBackground` / `clearSlideBackground` |
|
|
342
|
+
| Notes | `getSlideNotes` / `setSlideNotes` |
|
|
343
|
+
| Transitions | `getSlideTransition` / `setSlideTransition` / `clearSlideTransition` |
|
|
344
|
+
| Animations | `getShapeAnimation` / `setShapeAnimation` (`fadeIn` / `fadeOut` / `appear` / `disappear`), `clearSlideAnimations` |
|
|
345
|
+
| Comments | `addSlideComment`, `getSlideComments`, `removeSlideComment`, `getCommentAuthors`, `getCommentText` / `getCommentAuthor` / `getCommentPosition` |
|
|
346
|
+
| Shape authoring | `addSlideTextBox`, `addSlideShape`, `addSlideLine`, `addSlideTable`, `addSlideImage`, `addSlideChart` |
|
|
347
|
+
| Shape lookup | `findShapeByName`, `findShapesByName`, `findShapesByKind`, `findShapeInPresentation`, `getAllShapes`, `getSlideShapes` |
|
|
348
|
+
| Shape text | `setShapeText`, `setShapeBullets`, `setShapeAlignment`, `setShapeTextFormat`, `setShapeHyperlink` / `getShapeHyperlink` |
|
|
349
|
+
| Per-paragraph | `setParagraphAlignment` / `getParagraphAlignment`, `setParagraphLevel` / `getParagraphLevel`, `setParagraphBullet` / `getParagraphBullet` |
|
|
350
|
+
| Per-run text | `setShapeRunText` / `getShapeRunText`, `setShapeRunFormat` / `getShapeRunFormat`, `getShapeParagraphCount`, `getShapeRunCount` |
|
|
351
|
+
| Text frame | `setShapeTextAnchor` / `getShapeTextAnchor`, `setShapeTextMargins` / `getShapeTextMargins` |
|
|
352
|
+
| Fill | `setShapeFill` / `getShapeFill`, `setShapeGradientFill`, `setShapePatternFill`, `setShapeImageFill`, `setShapeNoFill`, `clearShapeFill` |
|
|
353
|
+
| Stroke | `setShapeStroke` / `getShapeStroke`, `setShapeStrokeDash` / `getShapeStrokeDash`, `setShapeStrokeArrow` / `getShapeStrokeArrow`, `…NoStroke` |
|
|
354
|
+
| Effects | `setShapeShadow` / `setShapeGlow` / `getShapeEffect`, `clearShapeEffects` |
|
|
355
|
+
| Geometry | `setShapePosition`, `setShapeSize`, `setShapeRotation`, `setShapeFlip`, `setShapeBounds` / `getShapeBounds` |
|
|
356
|
+
| Pictures | `setShapeImage`, `setShapeImageCrop` / `getShapeImageCrop`, `setShapeImageOpacity` / `getShapeImageOpacity`, `setShapeImageBrightness`, `…Contrast` |
|
|
357
|
+
| Z-order | `bringShapeToFront`, `sendShapeToBack`, `bringShapeForward`, `sendShapeBackward` |
|
|
358
|
+
| Click actions | `setShapeClickAction` / `getShapeClickAction` (`url` / `slide` / `nextSlide` / `prevSlide` / `firstSlide` / `lastSlide`) |
|
|
359
|
+
| Shape removal | `removeShape` |
|
|
360
|
+
| Tables | `getTableCell` / `getTableCells`, `setTableCellText` / `getTableCellText`, `setTableCellFill` / `clearTableCellFill`, `setTableCellAlignment`, `setTableCellTextFormat`, `insertTableRow` / `removeTableRow`, `insertTableColumn` / `removeTableColumn` |
|
|
361
|
+
| Charts | `addSlideChart`, `getSlideCharts`, `setChartSpec` — kinds: `bar`, `column`, `line`, `pie`, `doughnut`, `area` |
|
|
362
|
+
| Theme | `getPresentationTheme` — color scheme (`accent1`..`accent6`, `dark1`, `light1`, `hyperlink`, ...) |
|
|
363
|
+
| Validation | `validatePresentation(pres)` — invariant checks, returns `ValidationIssue[]` |
|
|
364
|
+
| Units | `inches(n)`, `cm(n)`, `mm(n)`, `pt(n)`, `emu(n)` — return branded `Emu` numbers |
|
|
365
|
+
|
|
366
|
+
## Compatibility
|
|
367
|
+
|
|
368
|
+
- **Node**: >= 20.
|
|
369
|
+
- **Browsers**: current and current-1 of Chrome, Firefox, Safari, Edge.
|
|
370
|
+
- **TypeScript**: >= 5.4 (for strict `satisfies` and `const` type parameters).
|
|
371
|
+
- **Output**: PPTX files validated against ECMA-376 schemas, smoke-tested
|
|
372
|
+
against PowerPoint (current), Keynote (current), Google Slides, and
|
|
373
|
+
LibreOffice Impress.
|
|
374
|
+
|
|
375
|
+
## Development
|
|
376
|
+
|
|
377
|
+
```sh
|
|
378
|
+
git clone --recurse-submodules git@github.com:baseballyama/pptx-kit.git
|
|
379
|
+
cd pptx-kit
|
|
380
|
+
pnpm install
|
|
381
|
+
pnpm test
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
If you already cloned without submodules:
|
|
385
|
+
|
|
386
|
+
```sh
|
|
387
|
+
git submodule update --init --recursive --depth 1
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
`references/` holds reference implementations and spec material we read
|
|
391
|
+
while building this library. See `references/README.md`.
|
|
392
|
+
|
|
393
|
+
## Contributing
|
|
394
|
+
|
|
395
|
+
Before opening an issue or PR, please read `CLAUDE.md` — it documents the
|
|
396
|
+
project's design rules, the "one way to do one thing" policy, and what
|
|
397
|
+
counts as a real bug report vs. a low-effort AI-generated one.
|
|
398
|
+
|
|
399
|
+
PRs are expected to:
|
|
400
|
+
|
|
401
|
+
- Follow the template (`.github/pull_request_template.md`).
|
|
402
|
+
- Include a failing test in the same PR that the change makes pass.
|
|
403
|
+
- Add a changeset (`pnpm changeset`) for user-visible changes.
|
|
404
|
+
- Pass `pnpm typecheck`, `pnpm lint`, and `pnpm test`.
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
[MIT](./LICENSE)
|