md2pdf-training-system
v0.3.5
Published
CLI tool for generating PDFs from training materials with multi-variant support
Maintainers
Readme
md2pdf - Training Materials PDF Generator
研修教材のMarkdownファイルからPDFを生成するCLIツール。複数バリアント対応、テンプレート変数展開、プレビュー機能を備えています。
特徴
- ✅ マルチバリアント対応: フレームワーク、環境、解答有無などの複数の軸でPDFを生成
- ✅ 3段階優先度解決: Combination > Dimension > Base の優先順位でファイルを解決
- ✅ インラインバリアント:
{{variant:framework=spring}}...{{end}}タグで条件付きコンテンツ - ✅ PlantUMLダイアグラム:
```plantumlコードブロックを自動的にSVGに変換 - ✅ テンプレート変数展開:
{courseId},{framework},{format}などの変数をサポート - ✅ 複数フォーマット: PDF/HTML/両方の出力に対応
- ✅ プレビューモード: ライブリロード付きHTTPサーバー
- ✅ 設定バリデーション: YAML設定ファイルの自動検証
- ✅ Docker対応: CI/CD統合のためのコンテナ化
インストール
ローカル開発
cd md2pdf
pnpm install
pnpm buildDocker
# イメージをビルド
./docker-build.sh
# または手動で
docker build -t md2pdf:latest .使い方
ローカル実行
# PDF生成
md2pdf generate -c config.yaml
# 特定のタイプのみ生成
md2pdf generate -c config.yaml -t text
# 特定のバリアントのみ生成
md2pdf generate -c config.yaml -v "framework=spring"
# 設定ファイルの検証
md2pdf validate -c config.yaml
# プレビューモード
md2pdf preview -c config.yaml
# シンプルモード(設定ファイルなし)
md2pdf generate -i ./testdoc --output ./sample-pdf --title 'サンプル'Docker実行(CI/CD統合用)
画像最適化
docker run --rm \
-v $(pwd):/workspace \
-e IMAGE_QUALITY=80 \
md2pdf:latest optimize-images \
--course-dir /workspace \
--output /workspace/.tmp-optimized-images \
--mapping-output /workspace/optimized-images-map.json全バリアントPDF生成
docker run --rm \
-v $(pwd):/workspace \
md2pdf:latest generate-all-pdfs \
--config /workspace/md2pdf-config.yaml \
--course-dir /workspace \
--output /workspace/.tmp-pdf \
--use-optimized-images /workspace/.tmp-optimized-images環境変数
| 変数名 | 説明 | デフォルト |
|--------|------|-----------|
| IMAGE_QUALITY | JPEG品質 (1-100) | 60 |
| PDF_QUALITY | PDF品質 | - |
| VIVLIOSTYLE_TIMEOUT | Vivliostyleタイムアウト秒数 | 120 |
設定ファイル
設定ファイルは任意の名前(例: config.yaml, materials.yaml)で作成できます。設定ファイルのパスは-c, --configオプションで指定します。
設定ファイルの例
globals:
specFile: specs/spec.json # コース仕様ファイル(相対パス)
theme: default # デフォルトテーマ
cleanup: true # 生成後の一時ファイル削除
materials:
text:
inputDir: text # 入力ディレクトリ(相対パス)
outDir: out/text # 出力ディレクトリ(相対パス)
sectionNumber: true # セクション番号を付与
tocLevel: 2 # 目次の深さ
variants:
- selection:
framework: spring
outputPattern: "text-{framework}.pdf"
- selection:
framework: express
outputPattern: "text-{framework}.pdf"
slides:
inputDir: slides
outDir: out/slides
format: both # pdf, html, or both
theme: gaia
variants:
- selection:
framework: spring
outputPattern: "slides-{framework}.{format}"
preview:
autoOpenBrowser: true
portRange: [3000, 3100]spec.json (メタデータとバリアント定義)
コースのメタデータとバリアント定義はspec.jsonに一元化されています:
{
"course_id": "ddd-tactical-design",
"training_name": "DDD戦術設計",
"training_description": "ドメイン駆動設計の戦術パターンを学ぶ",
"variants": {
"dimensions": [
{
"id": "framework",
"name": "フレームワーク",
"options": [
{
"id": "java-spring",
"name": "Java + Spring Boot"
},
{
"id": "typescript-nestjs",
"name": "TypeScript + NestJS"
}
]
}
],
"defaultSelections": {
"framework": "java-spring"
}
}
}注意: バリアント定義はspec.jsonのvariantsフィールドに含まれます。variants.jsonは使用されません。
ディレクトリ構造
training-materials/
└── ddd-tactical-design/
├── config.yaml # 設定ファイル
├── specs/
│ └── spec.json # コースメタデータとバリアント定義
├── text/
│ ├── chapter-01.md # ベースファイル
│ └── _variants/
│ ├── framework=spring/
│ │ └── chapter-01.md # Dimension variant
│ └── framework=spring,answer=yes/
│ └── chapter-01.md # Combination variant
└── out/
└── text/
├── text-spring.pdf
└── text-express.pdfバリアント解決の優先順位
- Combination (複数ディメンション):
_variants/framework=spring,answer=yes/chapter-01.md - Dimension (単一ディメンション):
_variants/framework=spring/chapter-01.md - Base (デフォルト):
chapter-01.md
インラインバリアントタグ
Markdownファイル内で条件付きコンテンツを記述できます:
# Chapter 1
{{variant:framework=spring}}
## Spring Framework での実装
このセクションはSpringを選択した場合のみ表示されます。
{{end}}
{{variant:framework=express}}
## Express での実装
このセクションはExpressを選択した場合のみ表示されます。
{{end}}
## 共通セクション
このセクションは常に表示されます。テンプレート変数
出力パターンで使用できる変数:
{courseId}- コースID (spec.jsonのcourse_id){type}- マテリアルタイプ (text, practice, quiz, slides){framework}- フレームワーク (spring, express, etc.){answer}- 解答表示 (yes, no){format}- 出力フォーマット (pdf, html)
{format} が both の場合、配列として展開されます:
slides-{courseId}.{format}
→ ["slides-ddd.pdf", "slides-ddd.html"]開発
テスト実行
# 全テスト実行
pnpm test
# 特定のテスト実行
pnpm test ConfigLoader
pnpm test Strategy
pnpm test e2e
# カバレッジ確認
pnpm test:coverage
# ウォッチモード
pnpm test:watch
# UIモード
pnpm test:ui型チェック
pnpm type-checkビルド
pnpm buildテーマシステム
md2pdfは、プロジェクトルートのthemes/ディレクトリからテーマを読み込みます。テーマはエンジン(vivliostyle/marp/web)ごとに管理され、各エンジンごとに独立したテーマディレクトリを持ちます。
テーマのディレクトリ構造
themes/
├── vivliostyle/ # PDF生成用(Vivliostyle)
│ ├── default/ # デフォルトテーマ
│ │ ├── index.html
│ │ ├── page.html
│ │ ├── postscript.html
│ │ ├── style.css
│ │ └── scss/ # SCSS ソース
│ └── quiz/ # クイズ専用テーマ
│ └── ...
├── marp/ # スライド生成用(Marp)
│ ├── default/ # デフォルトテーマ
│ │ └── theme.css # Marpテーマファイル
│ └── [custom]/ # カスタムテーマ
│ └── theme.css
└── web/ # Web素材生成用
└── default/
├── templates/
├── css/
└── js/テーマの優先順位
テーマは以下の優先順位で決定されます:
- CLIオプション (
--theme) - 最優先 - Material設定 (
materials.{type}.theme) - 各materialタイプごとの設定 - グローバル設定 (
globals.theme) - デフォルト値
globals:
theme: default # グローバルなデフォルトテーマ
materials:
text:
theme: custom # textタイプのみcustomテーマを使用
slides:
theme: gaia # slidesタイプのみgaiaテーマを使用
# themeが指定されない場合はglobals.themeが使用されるVivliostyleテーマ (テキスト/演習/クイズ)
Vivliostyleテーマは、PDF生成時に使用されるテーマです。
themes/vivliostyle/
├── default/ # デフォルトテーマ
│ ├── index.html # 表紙・目次ページ
│ ├── page.html # 本文ページ
│ ├── postscript.html # 奥付ページ
│ ├── style.css # コンパイル済みCSS
│ └── scss/ # SCSS ソース
└── quiz/ # クイズ専用テーマ
└── ...新しいVivliostyleテーマの追加方法
- テーマディレクトリを作成:
themes/vivliostyle/my-theme/ - 必要なファイルを配置:
index.html- 表紙・目次ページテンプレートpage.html- 本文ページテンプレートpostscript.html- 奥付ページテンプレート(オプション)style.css- スタイルシート
- 設定ファイルで
theme: my-themeを指定
globals:
theme: my-theme # themes/vivliostyle/my-theme/ が使用される
# または、materialごとに指定
materials:
text:
theme: my-themeMarpテーマ (スライド)
Marpテーマは、スライド生成時に使用されるテーマです。
themes/marp/
├── default/ # デフォルトテーマ
│ └── theme.css # Marpテーマファイル
└── [custom]/ # カスタムテーマ
└── theme.css新しいMarpテーマの追加方法
- テーマディレクトリを作成:
themes/marp/my-theme/ theme.cssを配置し、@theme my-themeを定義- 設定ファイルで
theme: my-themeを指定
slides:
theme: my-theme # themes/marp/my-theme/theme.css が使用されるMarpエンジン設定
全テーマ共通の設定は config/marp-engine.mjs で管理されます:
- HTML タグの有効化 (
html: true) - コードハイライト (
markdown-it-highlight-lines)
Webテーマ (Web素材生成)
Webテーマは、Web素材生成時に使用されるテーマです。
themes/web/
└── default/
├── templates/
│ └── page.html # ページテンプレート
├── css/
│ ├── theme.css
│ ├── theme-dark.css
│ └── highlight-*.css
└── js/
├── main.js
├── dark-mode.js
└── search.js新しいWebテーマの追加方法
- テーマディレクトリを作成:
themes/web/my-theme/ - 必要なファイルを配置:
templates/page.html- ページテンプレートcss/- スタイルシートjs/- JavaScriptファイル(オプション)
- 設定ファイルで
theme: my-themeを指定
materials:
text:
format: web
theme: my-theme # themes/web/my-theme/ が使用されるテーマのフォールバック動作
指定されたテーマが見つからない場合、以下のフォールバック動作が行われます:
- Vivliostyle/Web:
defaultテーマにフォールバック - Marp: エラーが発生(Marpはフォールバック機能なし)
CLIオプションでのテーマ指定
シンプルモードでは、--themeオプションでテーマを指定できます(実装予定):
md2pdf generate -i ./docs --output ./docs.pdf --theme customアーキテクチャ
md2pdfは4層アーキテクチャ(ヘキサゴナルアーキテクチャ)を採用しています:
src/
├── cli/ # CLI層(Primary Adapter)
│ ├── index.ts # CLIエントリーポイント
│ └── commands/
│ ├── generate.ts # PDF生成コマンド(レガシー)
│ ├── ConfigBasedGenerateCommand.ts # 設定ファイルベース生成
│ ├── SimpleGenerateCommand.ts # シンプルモード生成
│ ├── GenerateCommandFactory.ts # コマンドファクトリ
│ ├── PreviewCommand.ts # プレビューコマンド
│ ├── validate.ts # 設定検証コマンド
│ └── optimize-images.ts # 画像最適化コマンド
├── application/ # アプリケーション層(Use Cases)
│ └── services/
│ ├── ConfigService.ts # 設定管理サービス
│ ├── FileProcessingService.ts # ファイル処理サービス
│ ├── PreviewService.ts # プレビューサービス
│ └── ImageService.ts # 画像処理サービス
├── domain/ # ドメイン層(Business Logic)
│ ├── config/ # 設定管理
│ │ ├── ConfigLoader.ts
│ │ ├── SimpleConfigLoader.ts
│ │ └── ConfigValidator.ts
│ ├── generator/ # PDF生成(Strategy Pattern)
│ │ └── strategies/
│ │ ├── TextStrategy.ts
│ │ ├── PracticeStrategy.ts
│ │ ├── QuizStrategy.ts
│ │ └── SlidesStrategy.ts
│ ├── strategy/ # 戦略ファクトリ
│ │ └── StrategyFactory.ts
│ ├── markdown/ # Markdown処理
│ ├── html/ # HTML生成
│ ├── publication/ # publication.json生成
│ ├── variant/ # バリアント解決
│ │ ├── VariantResolver.ts
│ │ └── VariantProcessor.ts
│ ├── template/ # テンプレート変数展開
│ ├── preview/ # プレビューシステム
│ └── web/ # Web素材生成
├── infrastructure/ # インフラ層(Secondary Adapter)
│ └── adapters/
│ ├── VivliostyleAdapter.ts # Vivliostyle連携
│ └── MarpAdapter.ts # Marp連携
├── presentation/ # プレゼンテーション層
│ └── themes/
│ └── ThemeManager.ts # テーマ管理
├── di/ # 依存性注入
│ ├── container.ts # DIコンテナ
│ └── types.ts # サービス識別子
└── types/ # 型定義(インターフェース)
├── cli/
├── application/
├── domain/
└── infrastructure/