tech-book-labs
データユーティリティ · 最終検証 2026-09-14 · pptxgenjs 4.0.1 · 初公開 2026-05-10

PptxGenJS の使い方: ブラウザだけで PowerPoint(.pptx)を生成する

PptxGenJS v4 でできることと使い方を、その場で .pptx を作れる demo で確認。テキスト・表・グラフ・画像の最小コード、React / TypeScript での import からダウンロードまで、ブラウザと Node の出力の違い、要素ごとの生成時間とサイズの実測。

pptxgenjs powerpoint report-generation browser

検証日: 2026-09-14(初版 2026-05-10)

使用バージョン: pptxgenjs@4.0.1

対象: 売上レポートや週次レポートを 手で PowerPoint に貼り直す 運用を、ブラウザ(またはサーバ)で自動生成に置き換えたい人

PptxGenJS v4 で .pptx ファイルをクライアント完結で生成 するパターン。addSlide / addText / addTable / addChart の最小構成、レイアウトサイズ(LAYOUT_WIDE)とテンプレ運用、ブラウザ ↔ Node の出力切替、画像 / SVG 埋め込みの注意を動く demo で確認します。

触って試す

タイトル / サブタイトル / 作者を入れて生成すると、3 スライド構成(タイトル / 表 / 棒グラフ)の .pptx がブラウザだけで作られダウンロードされる。

この実装を体系立てて学ぶ本(ブラウザでファイルを作る処理は、Blob / File API と非同期処理の理解で安定する)

tech-book.net /books/9784297132880

改訂3版JavaScript本格入門

山田 祥寛 · 技術評論社 · 2023年

ES2022対応の文法基礎からオブジェクト指向構文まで一冊で体系的に押さえる

この本が役立つ理由 — Blob・File・非同期処理(Promise / async)を基礎から。ブラウザ完結のファイル生成で詰まる箇所がこの本の範囲
詳細を tech-book.net で見る
tech-book.net /books/9784297129569

実践Node.js入門ー基礎・開発・運用

伊藤康太 · 技術評論社 · 2023年

Node.jsの基礎文法から非同期処理・CLI・Express実装まで一気通貫で学ぶ。フロントエンド開発の経験があり、Node.jsでのサーバーサイド開発を体系的に学び直したい人向け。

この本が役立つ理由 — 同じ生成処理を Node.js のサーバ側でも動かすとき(Buffer と Stream の扱い)
詳細を tech-book.net で見る

0. PptxGenJS とは、何ができるか(30 秒版)

PptxGenJS は、JavaScript だけで PowerPoint ファイル(.pptx)を 新しく作る ライブラリ。ブラウザでも Node.js でも動き、スライドに 文字・表・グラフ・画像・図形 を座標指定で置き、スライドマスター(共通の背景やロゴ)、発表者ノート、16:9 / 4:3 の レイアウト まで指定できる。グラフは PowerPoint 側で編集できるネイティブのグラフとして埋め込まれ、画像化はされない。

できないこと(4.0.1 時点):既存の .pptx を開いて編集する ことはできない。テンプレートを使いたい場合は、マスターを JS 側で定義して毎回生成する形になる(9. テンプレ駆動)。PDF 出力も無いので、PDF が要るなら生成した .pptx を LibreOffice などで変換する。

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_16x10
  • writeFile はブラウザでは「ダウンロード」、Node では「ファイル保存」と環境で挙動が変わる
.pptx 生成パイプライン — 内部は OOXML を zip にまとめる、最後の出力先は環境で切替 (クリックで拡大)

2b. React / TypeScript で import からダウンロードまで

ボタン 1 つで .pptx を落とすところまでの最小コンポーネント。ライブラリが 300 KB 超あるので、押されたときに import() で読み込む。

import { useState } from "react";

export function ExportButton({ title, rows }: { title: string; rows: { label: string; value: number }[] }) {
  const [busy, setBusy] = useState(false);
  const onClick = async () => {
    setBusy(true);
    try {
      const { default: PptxGenJS } = await import("pptxgenjs");   // 初回クリック時だけ読み込む
      const pptx = new PptxGenJS();
      pptx.layout = "LAYOUT_WIDE";
      const slide = pptx.addSlide();
      slide.addText(title, { x: 0.5, y: 0.4, w: 12, h: 0.8, fontSize: 28, bold: true });
      slide.addChart(pptx.ChartType.bar,
        [{ name: "値", labels: rows.map((r) => r.label), values: rows.map((r) => r.value) }],
        { x: 0.5, y: 1.4, w: 12, h: 5.5 });
      await pptx.writeFile({ fileName: `${title}.pptx` });          // ブラウザではダウンロードを発火
    } finally {
      setBusy(false);
    }
  };
  return <button onClick={onClick} disabled={busy}>{busy ? "生成中…" : "PowerPoint を書き出す"}</button>;
}

TypeScript で詰まりやすい点は 2 つ。pptxgenjs は default export なので import PptxGenJS from "pptxgenjs"(名前付き import は無い)。グラフ種別は pptx.ChartType.bar のように インスタンス経由 で参照する(型は PptxGenJS.CHART_NAME)。

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複数軸の比較
bubble3 軸(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_16x910 x 5.625 inch標準 16:9
LAYOUT_WIDE13.333 x 7.5 inch16:9 ワイド(現代の社内テンプレ)
LAYOUT_4x310 x 7.5 inch古い 4:3 プロジェクター
LAYOUT_16x1010 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 を返す
画像 pathURL or dataURLURL 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 は同一なので、ブラウザでプレビュー → サーバで定期生成を同じコードベースで運用できる。

compression: true が効く経路と効かない経路(4.0.1 実測)

pptxgenjs は 既定で ZIP を非圧縮(Stored)で書く。compression: true を渡すと Deflate になるが、4.0.1 では出力メソッドによって無視される。10 枚のスライドに 12 行 × 4 列の表を置いたデッキで実測:

つまり ブラウザでダウンロードさせるなら writeFile({ fileName, compression: true })、Node で Buffer が欲しいなら stream({ compression: true })(Buffer が返る)。write({ outputType: "nodebuffer" }) で受けたい場合は、返った Buffer を JSZip 等で作り直すか、非圧縮を受け入れる。

8b. スライド 1 枚あたりのサイズ(実測)

50 枚のデッキを内容別に生成し、1 枚あたりの増分を出した(Node 22.17、3 回の中央値。ZIP 非圧縮の既定値):

同じ画像を複数スライドに貼ると、枚数ぶん埋め込まれる(50 枚で ppt/media/image-N-1.png が 50 個、実測)。ロゴなど共通画像はマスター(defineSlideMaster)側に 1 回置く。

8c. 要素の種類ごとの生成時間とサイズ(1 枚 / 10 枚、実測)

「文字・表・画像・グラフのどれが重いか」を、1 枚と 10 枚のデッキで測った(Node 22.17、pptxgenjs@4.0.1、非圧縮の既定、3 回の中央値、2026-09-14。計測スクリプト)。生成時間はどれも 15 ms 以下で、差が出るのはサイズの方。

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(# なし)の文字列配列。配列要素数 < シリーズ数だと循環
  • ファイルが大きい:既定は ZIP 非圧縮。ブラウザは writeFile({ compression: true })、Node は stream({ compression: true }) で 1/10(上の実測)。write({ outputType }) では効かない
  • 同じ画像を貼るたびに太る:画像は dedupe されず枚数ぶん埋め込まれる。共通画像はスライドマスターに
  • 生成が重い:実測では 50 枚 + 画像 50 枚でも 30 ms(Node)。重いのは大きな画像の base64 化と、非圧縮 pptx のダウンロード側。まず compression、次に画像の縮小
  • TypeScript の型補完が弱い:pptx.ChartType.bar は型が string 扱い。as const でリテラルを保つか、定数オブジェクトを別途定義
  • fontFace が反映されない:閲覧端末にそのフォントが入っていない場合、PowerPoint は代替フォントで表示。ブランドフォントは 画像化 か PowerPoint テンプレに埋め込み

関連 Topic / 関連書籍

この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:

tech-book.net /books/9784297132880

改訂3版JavaScript本格入門

山田 祥寛 · 技術評論社 · 2023年

ES2022対応の文法基礎からオブジェクト指向構文まで一冊で体系的に押さえる

この本が役立つ理由 — Blob・File・非同期処理を基礎から。ブラウザ完結のファイル生成で詰まる箇所がこの本の範囲
詳細を tech-book.net で見る
tech-book.net /books/9784297129569

実践Node.js入門ー基礎・開発・運用

伊藤康太 · 技術評論社 · 2023年

Node.jsの基礎文法から非同期処理・CLI・Express実装まで一気通貫で学ぶ。フロントエンド開発の経験があり、Node.jsでのサーバーサイド開発を体系的に学び直したい人向け。

この本が役立つ理由 — 同じ生成処理を Node.js のサーバ側でも動かすとき(Buffer と Stream)
詳細を tech-book.net で見る
tech-book.net /books/9784297127473

プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで

鈴木 僚太 · 技術評論社 · 2022年

TypeScriptの型システムを基礎から高度な表現力まで体系的に習得する

この本が役立つ理由 — 型付きで書くなら。ライブラリの型定義を読み解く力がつく
詳細を tech-book.net で見る
tech-book.net /books/9784627857216

はじめてのWebデザイン&プログラミング : HTML、CSS、JavaScript、PHPの基本

村上 祐治 · 森北出版 · 2023年

HTML・CSS・JavaScript・PHP を横断してWebの全体像を最短で把握する

詳細を tech-book.net で見る
tech-book.net /books/9784297138714

フロントエンドの知識地図ーー 一冊でHTML/CSS/JavaScriptの開発技術が…

株式会社ICS 池田 泰延/西原 翼/松本 ゆき

フロントエンド技術の全体像を俯瞰し、学習の優先順位を自分で判断できるようにする

詳細を tech-book.net で見る