mcpField
G
glmv-pdf-to-ppt

glmv-pdf-to-ppt

github.com/zai-orgドキュメント47558 回インストール28 週間前に更新
リポジトリ

glmv-pdf-to-ppt について

glmv-pdf-to-pptは、PDFを複数枚のHTMLプレゼンテーションに変換するスキルです。論文などの資料からスライドを作成する際に、ページを順に読み、構成を計画して、画像のローカル切り抜き、HTML生成、概要作成を行います。

主な機能

  1. 01PDFページの順次読み込み
  2. 02構造化されたスライド構成案の作成
  3. 03PDF画像のローカル切り抜き
  4. 04HTMLスライドの個別生成
  5. 05Markdown概要の作成

活用シーン

  1. 01論文PDFからプレゼンテーションを作成
  2. 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_Store
scripts/
crop.py
generate_slide.py
pdf_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 ならスライドと概要も中国語、英語なら英語にします。言語を混在させないでください。

コメント