pptxgenjs でブラウザだけで .pptx を生成する
PptxGenJS v4 を使った PowerPoint ファイル(.pptx)のクライアント生成パターン — addSlide / addText / addTable / addChart の最小構成、レイアウトサイズ(LAYOUT_WIDE)とテンプレ運用、ブラウザの writeFile / Node の writeFile / stream を出力先別に整理、画像/SVG 埋め込みの注意を触れる demo 付きで実装メモ化。
検証日: 2026-05-10
使用バージョン:
pptxgenjs@4.x対象: 売上レポートや週次レポートを 手で PowerPoint に貼り直す 運用を、ブラウザ(またはサーバ)で自動生成に置き換えたい人
PptxGenJS v4 で .pptx ファイルをクライアント完結で生成 するパターン。addSlide / addText / addTable / addChart の最小構成、レイアウトサイズ(LAYOUT_WIDE)とテンプレ運用、ブラウザ ↔ Node の出力切替、画像 / SVG 埋め込みの注意を動く demo で確認します。
触って試す
タイトル / サブタイトル / 作者を入れて生成すると、3 スライド構成(タイトル / 表 / 棒グラフ)の .pptx がブラウザだけで作られダウンロードされる。
1. なぜ pptxgenjs か
「データから PowerPoint を自動生成」したい時の選択肢:
pptxgenjs:JS 製、ブラウザ + Node 両対応、グラフ / 表 / 画像 / SVG 全部入り- python-pptx:Python 製、サーバ運用に向く。ブラウザでは動かない
- Apache POI:Java 製、エンタープライズ用、運用がやや重い
- office.js / Microsoft Graph API:認証必要、PowerPoint アプリと連携する用途
- 手動 ZIP + XML:
.pptxは Open XML(zip)なので作れるが、検証が地獄
「ブラウザ完結で .pptx を作れる」「Node でも同じ API」のは pptxgenjs が事実上唯一の選択肢。
2. 最小構成
pnpm add で導入 → 1 つのスライドを作って書き出す最小コード:
pnpm add pptxgenjs
import PptxGenJS from "pptxgenjs";
const pptx = new PptxGenJS();
pptx.layout = "LAYOUT_WIDE"; // 16:9 の 13.333 x 7.5 inch
pptx.author = "Editorial";
pptx.title = "Q2 Report";
const slide = pptx.addSlide();
slide.background = { color: "0F172A" };
slide.addText("Q2 Report", {
x: 0.5, y: 1.6, w: 12, h: 1.2,
fontSize: 40, bold: true, color: "FFFFFF",
});
// ブラウザ:ダウンロードを発火
await pptx.writeFile({ fileName: "report.pptx" });
// Node:ファイル保存
// await pptx.writeFile({ fileName: "/tmp/report.pptx" });
// 共通:バイナリで取り出し(Blob / Buffer / Uint8Array)
const blob = await pptx.write({ outputType: "blob" });
ポイント:
- 座標は inch 単位(
x: 0.5= 0.5 インチ ≒ 1.27cm) - 色は HEX 文字列の
#抜き("FFFFFF"で白) pptx.layoutで画面サイズが切り替わる:LAYOUT_WIDE/LAYOUT_16x9/LAYOUT_4x3/LAYOUT_16x10writeFileはブラウザでは「ダウンロード」、Node では「ファイル保存」と環境で挙動が変わる
3. テキスト / 段落
addText は 1 つのテキストボックスを作る。文字列を直接渡せば単純テキスト、配列にすると 段落配列(部分書式を混在可能):
slide.addText("見出し", { x: 0.5, y: 0.3, w: 12, h: 0.6, fontSize: 28, bold: true });
// 段落配列(複数段落の混在)
slide.addText(
[
{ text: "重要:", options: { bold: true, color: "DC2626" } },
{ text: " 売上は前年比 +18%、目標は達成。\n" },
{ text: "ただし KPI の達成度は地域差が大きい。" },
],
{ x: 0.5, y: 1.5, w: 12, h: 2, fontSize: 16 },
);
ポイント:
addTextは 1 つのテキストボックスを作る。複数段落を入れたいなら配列で渡す{ text, options }の配列で部分的に色やサイズを変えられる- 改行は
\n(段落区切り)、太字はbold: true
4. 表(table)
addTable に 2 次元配列(各セルは文字列 or { text, options })を渡す。ヘッダ行は太字 + 塗りつぶしで強調:
slide.addTable(
[
// ヘッダ行
[
{ text: "地域", options: { bold: true, fill: { color: "1E293B" }, color: "FFFFFF" } },
{ text: "売上", options: { bold: true, fill: { color: "1E293B" }, color: "FFFFFF" } },
],
["関東", "12,400"],
["関西", "7,800"],
],
{
x: 0.5, y: 1.2, w: 12,
fontSize: 14,
border: { type: "solid", color: "CBD5E1", pt: 0.75 },
rowH: 0.4, // 行高(inch)
},
);
ポイント:
- セルは 文字列 or
{ text, options }(部分書式) fill: { color: "..." }で背景色、borderで罫線- 列幅は
colW: [1, 2, 1.5](inch 配列) か w を指定して自動分割
5. グラフ(chart)
データから直接グラフを描ける(Excel に出力されるのと同じネイティブグラフ):
slide.addChart(
pptx.ChartType.bar,
[
{
name: "売上",
labels: ["関東", "関西", "中部", "九州", "北海道"],
values: [12400, 7800, 5200, 3100, 1800],
},
],
{
x: 0.5, y: 1.0, w: 12, h: 5.5,
chartColors: ["3B82F6"],
showLegend: false,
showValue: true,
catAxisLabelFontSize: 12,
valAxisLabelFontSize: 10,
},
);
種類:
ChartType.* | 用途 |
|---|---|
bar / bar3D | カテゴリ別の量比較 |
line / line3D | 時系列 |
pie / doughnut | 構成比(2-5 カテゴリまで) |
area / area3D | 累積、stacked 推移 |
scatter | 相関 |
radar | 複数軸の比較 |
bubble | 3 軸(x, y, size) |
複数シリーズ:
slide.addChart(pptx.ChartType.line, [
{ name: "売上", labels, values: [...] },
{ name: "目標", labels, values: [...] },
], { x, y, w, h, chartColors: ["3B82F6", "EF4444"] });
「色 / 凡例 / 軸ラベル」のオプションが多いので、実物は pptx.ChartType.* の TypeScript 型で補完を見ながら詰める。
6. 画像(image)
URL / dataURL / SVG 文字列を addImage に渡す。ブラウザでは CORS の制約があるため、外部画像は dataURL に変換してから渡すと安全:
// URL から
slide.addImage({ path: "https://example.com/logo.png", x: 0.5, y: 0.3, w: 1.5, h: 0.5 });
// data URL
slide.addImage({ data: "data:image/png;base64,iVBOR...", x: 0.5, y: 1, w: 4, h: 3 });
// SVG(中身を文字列で)
slide.addImage({ data: `data:image/svg+xml;base64,${btoa(svgString)}`, x: 0.5, y: 1, w: 4, h: 3 });
ポイント:
- CORS 制限:外部 URL の画像は CORS 設定が無いと読めないことがある。dataURL に変換(canvas → toDataURL)してから渡すと安全
- SVG は base64 で
data:image/svg+xml:そのままpathで渡すとブラウザによっては失敗 - 大きい画像はファイルサイズに直結:5MB 級の写真を 10 枚入れると .pptx が 50MB 超に
7. レイアウトサイズ / マスター
pptx.layout | サイズ | 用途 |
|---|---|---|
LAYOUT_16x9 | 10 x 5.625 inch | 標準 16:9 |
LAYOUT_WIDE | 13.333 x 7.5 inch | 16:9 ワイド(現代の社内テンプレ) |
LAYOUT_4x3 | 10 x 7.5 inch | 古い 4:3 プロジェクター |
LAYOUT_16x10 | 10 x 6.25 inch | 一部 LCD |
カスタム:
pptx.defineLayout({ name: "CUSTOM", width: 16, height: 9 });
pptx.layout = "CUSTOM";
マスターで「全スライド共通の背景 / ロゴ / フッター」を定義:
pptx.defineSlideMaster({
title: "BRAND",
background: { color: "FFFFFF" },
objects: [
{ image: { x: 0.2, y: 0.2, w: 1, h: 0.4, path: "/logo.png" } },
{ text: { text: "© 2026 Editorial", options: { x: 0.5, y: 7.0, w: 12, h: 0.3, fontSize: 9, color: "94A3B8" } } },
],
slideNumber: { x: 12.5, y: 7.0, fontSize: 10, color: "94A3B8" },
});
const slide = pptx.addSlide({ masterName: "BRAND" });
「会社テンプレに合わせる」のはマスター + addText の組合せで作り込む。
8. ブラウザ vs Node の違い
| ブラウザ | Node | |
|---|---|---|
writeFile({fileName}) | ダウンロードを発火 | ファイル書込 |
write({outputType: "blob"}) | Blob を返す | Buffer を返す(outputType: "nodebuffer" で明示) |
stream() | Blob を返す | Readable を返す |
| 画像 path | URL or dataURL | URL or filesystem path |
サーバ生成 → email 添付の例(Node):
const pptx = new PptxGenJS();
// ... compose slides
const buf = await pptx.write({ outputType: "nodebuffer" });
await transporter.sendMail({
to: "user@example.com",
attachments: [{ filename: "report.pptx", content: buf }],
});
API は同一なので、ブラウザでプレビュー → サーバで定期生成を同じコードベースで運用できる。
9. テンプレ駆動 + データ駆動の構成パターン
「データ → スライド組立」を関数化して、入力データ別に同じテンプレを当てる構成。複数顧客 / 月次レポートの 量産 に強い:
type ReportData = { region: string; total: number; growth: number }[];
function buildReport(data: ReportData, opts: { title: string; author: string }) {
const pptx = new PptxGenJS();
pptx.layout = "LAYOUT_WIDE";
pptx.author = opts.author;
pptx.title = opts.title;
buildTitleSlide(pptx, opts.title);
buildTableSlide(pptx, data);
buildChartSlide(pptx, data);
return pptx;
}
function buildTitleSlide(pptx: PptxGenJS, title: string) { /* ... */ }
function buildTableSlide(pptx: PptxGenJS, data: ReportData) { /* ... */ }
function buildChartSlide(pptx: PptxGenJS, data: ReportData) { /* ... */ }
ポイント:
- 「スライド 1 つ = 関数 1 つ」 で分割すると、テンプレ変更が局所化する
- データを引数に(計算済を渡す)。スライド関数は表示だけに専念
- 共通ヘルパ:色、フォント、padding を
const COLORS = {...}/const FONT = {...}で共有
つまずいたポイント
- 画像が出ない:外部 URL は CORS、relative path はブラウザでは効かない。dataURL に統一すると安全
- .pptx を Keynote で開くとレイアウトが崩れる:Keynote の互換は Office よりルーズ。社外配布は PDF も同梱
- チャートの色が反映されない:
chartColorsは HEX(#なし)の文字列配列。配列要素数 < シリーズ数だと循環 - 生成が重い:画像を多く貼ると blob 化に数秒〜。ブラウザでは Worker 化 か 「生成中…」UI を出す
- TypeScript の型補完が弱い:
pptx.ChartType.barは型がstring扱い。as constでリテラルを保つか、定数オブジェクトを別途定義 - fontFace が反映されない:閲覧端末にそのフォントが入っていない場合、PowerPoint は代替フォントで表示。ブランドフォントは 画像化 か PowerPoint テンプレに埋め込み
関連 Topic / 関連書籍
この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:
JavaScriptによるはじめてのアルゴリズム入門
Python と JavaScriptではじめるデータビジュアライゼーション
React Native+Expoではじめるスマホアプリ開発 : JavaScriptによるアプリ構築の実際
「React Native」は、Facebookが開発しているスマートフォンアプリ向けの開発環境です。ほとんどのコードをJav…