レベル:
2025年7月15日
•約8分で読めます
pptxgenjsライブラリを使用してNext.jsでマークダウンテキストをPowerPointプレゼンテーションに自動変換するWebツールの実装方法を詳しく解説します。ファイルアップロード、動的インポート、エラーハンドリングまで実践的なコード例付きで紹介。
プレゼンテーション資料の作成は多くのビジネスパーソンにとって日常業務の一部ですが、PowerPointでのレイアウト調整や書式設定に時間を取られることが多々あります。一方で、マークダウンは軽量で書きやすく、多くの開発者や技術者に愛用されています。
この記事では、マークダウンテキストを自動でPowerPointプレゼンテーションに変換するWebツールをNext.js + pptxgenjsで作成する方法を詳しく解説します。
以下のような機能を持つツールを作成します:
まず、必要なライブラリをインストールします:
npm install pptxgenjs marked @types/marked
各パッケージの役割:
今回は以下の構造でファイルを作成します:
src/app/office-ai/tools/markdown-to-pptx/
├── page.tsx # メタデータとページコンポーネント
└── MarkdownToPptxClient.tsx # メインのクライアントコンポーネント
まず、SEO対策とページ情報を設定するためのpage.tsx
を作成します:
import type { Metadata } from "next";
import MarkdownToPptxClient from "./MarkdownToPptxClient";
export const metadata: Metadata = {
title: "マークダウン → PowerPoint 変換ツール|オフィス AI",
description: "マークダウンファイルを美しいPowerPointプレゼンテーションに自動変換。テキスト入力またはファイルアップロードで手軽に使えるWebツール。",
keywords: [
"マークダウン PowerPoint 変換",
"Markdown PPTX",
"プレゼンテーション 自動生成",
"pptxgenjs",
],
};
export default function MarkdownToPptxPage() {
return <MarkdownToPptxClient />;
}
クライアントコンポーネントでは、以下のstateを管理します:
"use client";
import { useState } from "react";
export default function MarkdownToPptxClient() {
const [markdownText, setMarkdownText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
// 実装続く...
}
マークダウンファイル(.md)をアップロードして内容を読み取る機能を実装します:
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
if (file.type === "text/markdown" || file.name.endsWith(".md")) {
const reader = new FileReader();
reader.onload = (e) => {
const content = e.target?.result as string;
setMarkdownText(content);
setSuccess("ファイルを読み込みました");
setError(null);
};
reader.readAsText(file);
} else {
setError("マークダウンファイル(.md)を選択してください");
setSuccess(null);
}
}
};
最も重要な変換処理を実装します。動的インポートを使用してクライアントサイドでのみライブラリを読み込みます:
const handleConvert = async () => {
if (!markdownText.trim()) {
setError("マークダウンテキストを入力してください");
return;
}
setIsLoading(true);
setError(null);
setSuccess(null);
try {
// 動的インポート(クライアントサイドでのみ実行)
const PptxGenJS = (await import("pptxgenjs")).default;
const { marked } = await import("marked");
// PowerPointプレゼンテーションを作成
const pptx = new PptxGenJS();
// マークダウンをパース
const tokens = marked.lexer(markdownText);
let currentSlide = pptx.addSlide();
let slideContent: Array<{ text: string; options: any }> = [];
let hasTitle = false;
for (const token of tokens) {
if (token.type === "heading") {
// 見出しレベル1は新しいスライドのタイトル
if (token.depth === 1) {
if (hasTitle) {
// 前のスライドにコンテンツを追加
if (slideContent.length > 0) {
currentSlide.addText(slideContent, {
x: 0.5, y: 1.5, w: 9, h: 5,
fontSize: 18, color: "363636"
});
}
// 新しいスライドを作成
currentSlide = pptx.addSlide();
slideContent = [];
}
// タイトルを追加
currentSlide.addText(token.text, {
x: 0.5, y: 0.3, w: 9, h: 1,
fontSize: 32, bold: true, color: "1976d2"
});
hasTitle = true;
} else {
// その他の見出しはコンテンツとして追加
slideContent.push({
text: token.text,
options: {
fontSize: token.depth === 2 ? 24 : 20,
bold: true, color: "1976d2", bullet: false
}
});
}
} else if (token.type === "paragraph") {
slideContent.push({
text: token.text,
options: {
fontSize: 18, color: "363636", bullet: false
}
});
} else if (token.type === "list") {
token.items.forEach((item: any) => {
slideContent.push({
text: item.text,
options: {
fontSize: 18, color: "363636", bullet: true
}
});
});
}
}
// 最後のスライドのコンテンツを追加
if (slideContent.length > 0) {
currentSlide.addText(slideContent, {
x: 0.5, y: hasTitle ? 1.5 : 0.5,
w: 9, h: hasTitle ? 5 : 6.5,
fontSize: 18, color: "363636"
});
}
// ファイルを保存
const fileName = `markdown-presentation-${new Date().toISOString().slice(0, 10)}.pptx`;
await pptx.writeFile({ fileName });
setSuccess("PowerPointファイルが作成されました!");
} catch (err: any) {
console.error("変換エラー:", err);
setError("変換中にエラーが発生しました: " + (err.message || "不明なエラー"));
} finally {
setIsLoading(false);
}
};
ユーザーフレンドリーなインターフェースを作成するため、Material-UIコンポーネントを活用します:
// ファイルアップロードボタン
<input
accept=".md,.markdown"
style={{ display: "none" }}
id="markdown-file-upload"
type="file"
onChange={handleFileUpload}
/>
<label htmlFor="markdown-file-upload">
<Button
variant="outlined"
component="span"
startIcon={<Upload />}
sx={{
borderColor: "#1976d2",
color: "#1976d2",
"&:hover": {
borderColor: "#1565c0",
backgroundColor: "#e3f2fd",
},
}}
>
.mdファイルをアップロード
</Button>
</label>
// テキスト入力エリア
<TextField
fullWidth
multiline
rows={12}
value={markdownText}
onChange={(e) => setMarkdownText(e.target.value)}
placeholder="マークダウンテキストを貼り付けてください..."
variant="outlined"
sx={{
"& .MuiOutlinedInput-root": {
fontFamily: "monospace",
fontSize: "14px",
},
}}
/>
適切なフィードバックを提供するため、Alertコンポーネントでエラーと成功メッセージを表示します:
{error && (
<Alert severity="error" sx={{ mb: 3 }}>
{error}
</Alert>
)}
{success && (
<Alert severity="success" sx={{ mb: 3 }}>
{success}
</Alert>
)}
マークダウンの見出しレベルに基づいて、以下のルールでスライドを生成します:
PowerPointスライドの座標系を理解した適切なレイアウトを設計:
// タイトルの配置
currentSlide.addText(token.text, {
x: 0.5, // 左からの位置(インチ)
y: 0.3, // 上からの位置(インチ)
w: 9, // 幅(インチ)
h: 1, // 高さ(インチ)
fontSize: 32,
bold: true,
color: "1976d2"
});
企業ブランドに合わせたテーマを設定できます:
// 企業カラーの設定
const brandColors = {
primary: "#1976d2",
secondary: "#666666",
accent: "#ff9800"
};
// テンプレートスライドの追加
pptx.defineSlideMaster({
title: "MASTER_SLIDE",
background: { color: "F1F1F1" },
objects: [
{
placeholder: {
options: { name: "title", type: "title", x: 0.6, y: 0.2, w: 8.5, h: 1.5 },
text: "[(企業名)]"
}
}
]
});
技術資料でよく使われるコードブロックにも対応可能:
else if (token.type === "code") {
slideContent.push({
text: token.text,
options: {
fontSize: 14,
fontFace: "Courier New",
color: "333333",
fill: "F5F5F5",
bullet: false
}
});
}
1. 動的インポートが失敗する場合
// 型エラーを回避する方法
const PptxGenJS = (await import("pptxgenjs")) as any;
const pptx = new PptxGenJS.default();
2. ファイルダウンロードが機能しない場合
ブラウザのセキュリティ設定を確認し、必要に応じてhttpsでアクセスしてください。
3. 日本語フォントが正しく表示されない場合
currentSlide.addText(text, {
fontFace: "メイリオ", // 日本語対応フォントを指定
// その他のオプション...
});
この記事では、Next.js + pptxgenjsを使用してマークダウンからPowerPointへの変換ツールを作成する方法を詳しく解説しました。
このツールを基盤として、さらなるカスタマイズや機能追加を行い、業務効率化を実現してください。プレゼンテーション作成の時間を大幅に短縮し、コンテンツの質向上に集中できるようになるでしょう。