glmv-pdf-to-ppt について
glmv-pdf-to-pptは、PDFを複数枚のHTMLプレゼンテーションに変換するスキルです。論文などの資料からスライドを作成する際に、ページを順に読み、構成を計画して、画像のローカル切り抜き、HTML生成、概要作成を行います。
主な機能
- 01PDFページの順次読み込み
- 02構造化されたスライド構成案の作成
- 03PDF画像のローカル切り抜き
- 04HTMLスライドの個別生成
- 05Markdown概要の作成
活用シーン
- 01論文PDFからプレゼンテーションを作成
- 02資料の内容をHTMLスライドに変換
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zai-org/ glm-skills --skill glmv-pdf-to-pptインストール後、Claude Code で/glmv-pdf-to-pptを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.DS_Storescripts/crop.pygenerate_slide.pypdf_to_images.py詳細
PDF → HTML PPT スキル
任意の PDF を複数スライドの HTML プレゼンテーションに変換します。ページは DPI 120 で画像に変換し、内容を理解するために順番に読み込んだあと、構造化した outline.json を保存します。画像はローカルで切り抜き(クラウドへのアップロードなし)、スライドを 1 枚ずつレンダリングし、最後に summary.md を生成します。
スクリプトの保存先: {SKILL_DIR}/scripts/
依存関係
Python パッケージ(初回のみインストール):
pip install pymupdf pillow
システムツール:curl(macOS/Linux にはプリインストールされています)。
使用するタイミング
ユーザーが PDF からスライドやプレゼンテーションを作成するよう求めたときに実行します。たとえば、次のような表現が含まれます。 「PDF から PPT を作成」「PDF をスライドに変換」「この論文からプレゼンテーションを作成」「根据pdf做ppt」「根据论文做幻灯片」「做PPT」「做幻灯片」「生成演示文稿」「把这个pdf转成ppt」、または中国語・英語での同様の意図。
出力ディレクトリの規則
すべての出力は {WORKSPACE}/ppt/<pdf_stem>_<timestamp>/ 以下に保存します。
ppt/
└── <pdf_stem>_<timestamp>/
├── outline.json ← structured slide plan (SlidesPlan schema)
├── crops/ ← locally-saved cropped images
│ ├── slide3_method_crop.png
│ └── slide5_results_crop.png
├── slide_01.html
├── slide_02.html
├── ...
└── summary.md ← final summary document
<pdf_stem>= 拡張子を除いた PDF ファイル名<timestamp>=YYYYMMDD_HHMMSS形式(例:20240119_143022)- 切り抜いた画像は
crops/サブフォルダーに保存します - 各スライドの HTML は、相対パス
crops/<name>.pngで画像を参照します
入力
$ARGUMENTS は PDF ファイルのパス(ローカル)または HTTP/HTTPS URL です。
- ユーザーが URL を指定した場合:最初に curl でダウンロードしてから変換します
- ユーザーが ローカル PDF パス を指定した場合:そのまま変換します
ワークフロー
フェーズ 0 — 出力ディレクトリの作成
出力パスを計算します。
import os, datetime
pdf_stem = os.path.splitext(os.path.basename(pdf_path))[0]
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
out_dir = os.path.join(workspace, "ppt", f"{pdf_stem}_{timestamp}")
ただちに作成します。
mkdir -p "<out_dir>/crops"
out_dir を記録し、以降のすべてのフェーズで使用します。
フェーズ 1 — PDF の各ページを画像に変換(DPI 120)
入力が URL の場合は、最初にダウンロードします。
pdf_stem=$(basename "$ARGUMENTS" .pdf)
curl -L -o "/tmp/${pdf_stem}.pdf" "$ARGUMENTS"
次に変換します(ダウンロードしたパス、または元のローカルパスのどちらかを渡します)。
python {SKILL_DIR}/scripts/pdf_to_images.py "<pdf_path>" --dpi 120
標準出力に JSON が出力されます。
[{"page": 1, "path": "/abs/path/page_001.png"}, ...]
page → path の完全なマップを解析して保存します。これらのローカルパスは、ページの表示と crop.py の --path 入力に使用します。
フェーズ 2 — すべてのページを順番に読む
何かを計画する前に、すべてのページ画像を順番に確認します。ここでは理解に専念し、ドキュメントの全体構成、内容、図、論旨を把握します。
読みながら、次の点を記録します。
- どのページに図、グラフ、表があるか
- 全体の展開(論文の場合は導入 → 手法 → 結果 → 結論、その他のドキュメントの場合は論理的な構成)
- スライドに使うために切り抜く候補となるビジュアル(ページ番号 + おおよその領域)
スライドの計画や作成はまだ行わないでください。まず、すべてのページを読んで理解してください。
フェーズ 3 — アウトラインを計画して outline.json を保存
すべてのページを読んだあと、8~15 枚のスライドを計画します(学術文書以外の場合は柔軟に調整してください)。
| スライド | 一般的な目的 |
|---|---|
| 1 | タイトル、著者、所属、発表先/年 |
| 2 | 動機/問題の説明 |
| 3 | 関連研究(簡潔に) |
| 4~N-2 | 手法/主要な貢献(1 枚につき 1 つの概念) |
| N-1 | 結果と実験 |
| N | 結論と今後の課題 |
ビジュアルが必要なスライドごとに、次を特定します。
- 使用元のページ(フェーズ 1 のローカルページパス)
- ビジュアルが何を示すか、またこのスライドに適している理由の説明
アウトラインを <out_dir>/outline.json に、次のスキーマと完全に同じ形式で保存します。
{
"presentation_title": "Paper Title Here",
"lang": "Chinese",
"total_slides": 10,
"slides_plan": [
{
"slide_index": 1,
"title": "Slide Title",
"main_content": "Key points and text content for this slide",
"template_id": null,
"required_crops": [
{
"url": "<page_image_url_from_phase1>",
"visual_description": "Figure 3: architecture diagram showing encoder-decoder",
"usage_reason": "Illustrates the core model structure for slide 4"
}
]
}
]
}
各フィールドの説明:
-
lang:PDF の言語に合わせて"Chinese"または"English" -
template_id:常にnull -
required_crops:このスライドに画像が不要な場合は空の配列[] -
各 crop の
url:元ページ画像のローカルファイルパス(フェーズ 1 のpathフィールド)。crop.py はこのパスを開いて切り抜きます -
visual_description:ビジュアルの内容。図表番号がある場合はそれも含めます -
usage_reason:このビジュアルがこのスライドに適している理由 -
切り抜きが必要な画像については、おおよその領域を記録します。正確な切り抜き範囲はフェーズ 4 で決定します
Write ツールを使用して、outline.json を <out_dir>/outline.json に書き込みます。
フェーズ 4 — 必要な画像を切り抜く(グラウンディング+サブエージェント)
重要:すべての切り抜き処理を、Agent ツールを使って新しいサブエージェントに委任する必要があります。 このフェーズまでにコンテキストが非常に長くなっており(すべてのページ画像+アウトライン)、視覚的な座標の精度が低下します。対象画像だけを渡した新しいサブエージェントを使うと、より正確な座標を得られます。
重要:すべての画像の切り抜きには、必ず提供されている {SKILL_DIR}/scripts/crop.py スクリプトを使用してください。独自の切り抜きコードを書いたり、PIL/Pillow を直接使ったり、ほかの方法を使ったりしないでください。
outline.json を読み込みます。必要な切り抜きをすべて集めてから、ページごとに 1 つのサブエージェントを起動します(ページが異なる場合は、切り抜きごとに 1 つ起動します)。サブエージェントはグラウンディング方式の位置特定を行います。画像を見て対象要素を特定し、正規化された 0~999 の座標で正確なバウンディングボックスを出力します。
Agent ツールは次のように使用します。
Agent tool call:
description: "Grounding crop page N"
prompt: |
You are a visual grounding and cropping assistant. Your task is to precisely
locate specified visual elements in a page image and crop them out.
## Grounding method
Use visual grounding to locate each target:
1. Read the source image using the Read tool to view it
2. Identify the target element described below
3. Determine its bounding box as normalized coordinates in the 0–999 range:
- 0 = left/top edge of the image
- 999 = right/bottom edge of the image
- These are thousandths, NOT pixels, NOT percentages (0–100)
- Format: [x1, y1, x2, y2] where (x1,y1) is top-left, (x2,y2) is bottom-right
- Example: [0, 0, 500, 500] = top-left quarter of the image
4. Be precise: tightly bound the target element with a small margin (~10–20 units)
around it. Do NOT crop too wide or too narrow.
## Source image
<page_image_path>
## Crops needed
For each crop below, first do grounding (locate the element), then crop:
1. Name: "slide<N>_<descriptive_name>"
Target: "<visual_description from outline.json>"
Context: "<usage_reason from outline.json>"
## Crop command
After determining the bounding box [X1, Y1, X2, Y2] for each target, run:
```bash
python <SKILL_DIR>/scripts/crop.py \
--path "<page_image_path>" \
--box X1 Y1 X2 Y2 \
--name "<crop_name>" \
--out-dir "<out_dir>/crops"
```
## Verification
After each crop, READ the output image to visually verify the correct region
was captured. If the crop missed the target or is too wide/narrow, adjust the
coordinates and re-run crop.py.
## Output
Report the final results as a list:
- crop_name: <name>, file: <output_filename>, box: [X1, Y1, X2, Y2]
<page_image_path>、<SKILL_DIR>、<out_dir>、切り抜きの詳細を、コンテキスト内の実際の値に置き換えます。
crop.py スクリプトは JSON を出力します:{"path": "/abs/path/slide3_method_crop.png"}
すべてのサブエージェントからの結果を集め、HTML で参照するための対応表 slide_index → [crop filename, ...] を作成します。ファイル名は <name>_crop.png になります。
可能であれば、独立したページのサブエージェントを並行して起動します。次に進む前に、すべての処理が完了するまで待ちます。
フェーズ 5 — 切り抜いた画像のサイズを測定する
切り抜き後、ピクセル寸法を取得します。
python3 -c "
from PIL import Image; import os, json
d = '<out_dir>/crops'
sizes = {}
for f in sorted(os.listdir(d)):
if f.endswith('.png'):
w, h = Image.open(os.path.join(d, f)).size
sizes[f] = {'width': w, 'height': h, 'aspect': round(w/h, 2)}
print(json.dumps(sizes, indent=2))
"
アスペクト比を使って、各スライドのレイアウトを選びます。
| アスペクト比 | レイアウトの推奨 |
|---|---|
| < 0.7(縦長/幅が狭い) | text + image を横に並べる — 画像に max-height: 600px を指定 |
| 0.7 – 1.3(正方形に近い) | text + image — 画像の幅は約 50% |
| > 1.3(横長) | 画像を上または下に配置し、テキストをその下または上に配置 |
| > 2.0(非常に横長。表など) | full-image — 幅 1280px 全体に広げ、キャプションを下に配置 |
フェーズ 6 — スライドを 1 枚ずつ生成する
各スライドの HTML を作成して一時ファイルに保存し、generate_slide.py を呼び出します。
ステップ A — HTML を作成する /tmp/slide_N.html に保存します
- すべての
<img src="...">には相対パスを使用します:crops/<name>_crop.png - 切り抜いた画像に絶対パスや URL を使用しないでください
- ナビゲーションはクリック領域方式です。ボタンは不要です:
- スライドの左半分をクリックすると、前のスライドに移動します
- スライドの右半分をクリックすると、次のスライドに移動します
- スライド 1 では左クリックは何もせず、最後のスライドでは右クリックは何もしません
- キーボードの
←/→矢印キーでも移動できます - スライドキャンバス全体に重ねて配置する、各半分を覆う透明な
<div>オーバーレイを 2 つ実装します。位置は absolute にします
ステップ B — スライドを保存する:
python {SKILL_DIR}/scripts/generate_slide.py \
--html-file /tmp/slide_N.html \
--index N \
--total <total> \
--title "<presentation title>" \
--out-dir "<out_dir>/"
すべてのスライドを保存するまで繰り返します。
フェーズ 7 — summary.md を生成する
スライドと同じ言語で(outline.json の lang に従って)<out_dir>/summary.md を作成します。
以下を含めます。
- ドキュメントのタイトルと基本情報(該当する場合は著者、掲載先、年)
- 簡潔な要旨/概要(2~3 文)
- スライドごとの内訳表:スライド番号、タイトル、1~2 文の概要
- 主な貢献または要点(箇条書き)
- 最初のスライドを開くための
slide_01.htmlへのリンク
構成例:
# [Presentation Title]
> **来源 / Source:** [PDF filename] | **语言 / Language:** Chinese | **幻灯片数 / Slides:** 10
## 摘要
[2-3 sentence overview]
## 幻灯片概览
| # | 标题 | 主要内容 |
|---|------|---------|
| 1 | 标题页 | ... |
...
## 主要贡献
- ...
## 📂 打开演示文稿
[▶ 开始播放](slide_01.html)
HTML スライド仕様
各スライドは独立した HTML ファイルです。CSS を埋め込んだ完全な <html>…</html> にしてください。
キャンバス: 固定サイズ 1280 × 720 px、overflow: hidden — スクロールは発生しません。
すべてのスライドで一貫したデザイン:
- ドキュメントの分野やトーンに合ったビジュアルスタイルを選んでください。固定のカラーパレットやフォントはありません
- ユーザーがスタイルを指定した場合は、その指定に正確に従ってください。指定がない場合は、コンテンツから判断してください(例:ML 論文 → モダンで洗練されたデザイン、歴史レポート → エディトリアルなセリフ体、製品紹介 → 大胆でブランド感のあるデザイン)
- すべてのスライドで同じフォント、色、余白のルールを統一して適用してください
- すべてのスライドに、スライドタイトル、ページ番号(右下隅)、プレゼンテーションタイトル(控えめなフッター)を表示してください
各スライドのナビゲーション:
- 透明なクリック領域を2つ設け、スライドの高さ全体を覆うようにします。左半分 → 前のスライド、右半分 → 次のスライド
- 1枚目では左側の領域は動作しません。最後のスライドでは右側の領域は動作しません
- キーボードの
←/→矢印キーでも移動できます - 表示されるボタンは不要です。必要であれば、端に控えめな
‹/›のヒントを表示し、ホバー時にフェードインさせてもかまいません
レイアウトパターン:
title-card— 中央に配置するヒーロー、タイトルを大きく表示し、その下に著者/発表先を配置text-only— 構造化した箇条書き、最大 5~6 項目、余白を十分に確保text + image— 画像を右または左に配置し、反対側にテキストを配置full-image— キャンバスいっぱいに画像を表示し、テキストの重ね合わせは最小限にするgrid— 図を 2×2 または 3 列で配置し、キャプションを付ける
画像:
- 相対パスを使用します:
crops/<name>_crop.png style="object-fit: contain; max-width: 100%; max-height: 100%;"を追加します- 下に小さなイタリック体でキャプションを付けます
禁止事項:
- 外部 JavaScript フレームワークやアイコン CDN を使用しない
- プレースホルダー画像やストック画像を使用しない。切り抜いた PDF の画像のみを使用する
- 汎用的な白地に紫のグラデーションを使ったスライドを生成しない
- コンテンツを 720px の高さからはみ出させない
品質チェックリスト
- 出力ディレクトリ名を
<pdf_stem>_<timestamp>/にする - 有効な SlidesPlan スキーマで
outline.jsonを保存する - すべての切り抜き画像を
crops/に保存する(ローカルのみ。クラウドにはアップロードしない) - 各スライドを 1280×720 に収め、はみ出しがないようにする
- すべてのスライドでテーマを統一する
- 切り抜き画像を相対パス
crops/<name>_crop.pngで参照する - すべてのスライドにスライド番号とプレゼンテーションタイトルを表示する
- 左右のクリック領域によるナビゲーションと、キーボードの矢印キーが動作する
- 正しい言語で
summary.mdを作成し、slide_01.htmlへのリンクを含める
言語
PDF の言語に合わせてください。中国語の PDF ならスライドと概要も中国語、英語なら英語にします。言語を混在させないでください。