# スライドの組み方 — .pptx と HTML デッキの実装ガイド

デザインの数値と禁止事項は [README.md](README.md)（共通）と `systems/*.md`（システム固有）が正本。このファイルは「その数値をどうやってファイルにするか」だけを書く。進め方は [workflow.md](workflow.md)。

どの AI・どの環境でも、**.pptx は python-pptx＋部品集 `tools/pptx_kit.py` で組む**のが最短で最も崩れにくい。部品集を使えない環境（コード実行なし）では HTML デッキ（§3）にする。

---

## 1. 部品集 `tools/pptx_kit.py` で組む（推奨）

python-pptx をそのまま使うと、AI が毎回同じところでつまずく（§2）。部品集はそれを吸収し、色・サイズ・座標を `systems/<system>.md` の表から直接読む。規定を直せば部品集の値も変わる。

```python
import sys; sys.path.insert(0, "slides/tools")   # リポジトリの外なら pptx_kit.py の置き場所を足す
from pptx_kit import Deck

d = Deck("navy")                                   # システム名は slides/README.md §1 の表（navy / report / academic …）
s = d.slide()                                      # 本文ページ（地の色を敷いた白紙）
d.eyebrow(s, "03 ｜ 推移")
x, y, w, h = d.title(s, "処理件数は導入 3 年目に 2.4 倍へ伸びる")   # 戻り値は CONTENT ゾーン
d.bar_chart(s, x, y, w * 0.64, h, ["現状", "1 年目", "2 年目", "3 年目"], [10, 14.5, 19.8, 24],
            highlight=3, unit="万件", number_format="0.0")
d.text(s, x + w * 0.64 + 0.4, y + 0.8, w * 0.36 - 0.4, 0.9, "2.4 倍", size=48, color="title", bold=True, font="head")
d.footer(s, "試算: メーカー実績値")                 # 出典キャプション＋ページ番号
d.notes(s, "話す内容はノートへ")
d.save("deck.pptx")
```

| 部品 | 役割 |
|------|------|
| `Deck(system)` | 16:9・テーマ既定フォントの置き換えまで済んだ空のデッキ |
| `slide()` | ページを足して地の色を敷く。地は表紙から締めまで全ページ同じ（[README §2.3](README.md)）なので引数は通常付けない |
| `text(s, x, y, w, h, 内容, size=, color=, bold=, font="head"/"body", align=, anchor=, alpha=)` | 文字枠。内側余白 0・自動縮小なし・行間 pt 固定・和欧両方のフォント指定。溢れと泣き別れを警告する |
| `eyebrow` / `title` / `footer` / `notes` | 定位置の要素。`title` は 1 行に入らなければ 2 行用のサイズ・座標に切り替え、CONTENT ゾーンを返す |
| `rect` / `oval` / `line` | 面・丸・罫線。テーマの影や青い塗りを外してある。`rect` の角はシステムの RADIUS に従う |
| `bar_chart` / `line_chart` | ネイティブのグラフ。目盛線・凡例なし、値を直接ラベル、`highlight` の 1 点だけスパーク色。`bar_chart` は `{系列名: 値}` で複数系列、`stacked=True` で積み上げ。系列名は凡例の代わりに `text()` でグラフ脇に書く |
| `table(s, x, y, w, rows, align=, highlight_cells=, bold_row=)` | 行の罫線だけの表（格子・縞なし）。数字の列は `align="right"`。スパーク色は決めの 1 セルだけ（`highlight_cells=[(行, 列)]`）、推奨案の行は `bold_row` で太字 |

- **色**は定数名（`"NAVY"`）か役割名で指定する。役割名は `title` `text` `sub` `line` `card` `tint` `accent` `spark` `dark` `on_dark` `num_mute` `bg`。役割名で書けば、同じコードで全システムを切り替えられる。
- **サイズ**は定数名（`"BODY"` `"LEAD"` …）か pt の数値。
- **段落の中で色・大きさを変える**ときは段落を `[(文字列, {"color": "spark", "size": 28}), …]` のリストで渡す（決め数字と単位を 1 つの枠に入れるときもこれ。幅は文字ごとの pt で見積もる）。改行位置は `\n`（または段落のリスト）で自分で決める。
- 全テンプレートの組み方の実例は [tools/sample_deck.py](tools/sample_deck.py)（表紙・目次・キーメッセージ・節区切り・3 分割・ビッグメトリック・グラフ・表・2×2・期間表・締め）。新しい型を組む前に読む。
- 部品集を直したら `python3 slides/tools/pptx_kit.py --selftest` と、全システムの見本デッキで `check_pptx.py` が 0 件になることを確かめる。

**リポジトリの外（claude.ai・ChatGPT のコード実行など）で使うとき:** `pptx_kit.py` と `check_pptx.py`、使うシステムの `.md` を同じフォルダに置き、`pptx_kit.SYSTEMS_DIR` をそのフォルダに向ける（`pptx_kit.SYSTEMS_DIR = Path(".")`）。

---

## 2. python-pptx で素のまま組むときの落とし穴

部品集を使わない場合も、次は必ず守る。どれも「コードは動くのに紙面が崩れる」種類の欠陥で、機械チェックでも拾いきれないものがある。

| 落とし穴 | 起きること | 対処 |
|----------|-----------|------|
| `font.name` だけ設定する | 欧文（latin）にしか効かず、和文はテーマ既定（Aptos・MS ゴシック等）になる | `rPr` に `<a:ea typeface="Meiryo"/>` を足す。テーマの major/minor フォントも置き換える |
| 行間を倍数（`line_spacing = 1.25`）で指定 | 倍数は書体の行高に掛かる。メイリオは行高が 1.5 em あるので、実際は pt × 1.9 前後になり、文字数バジェットの見積もりより高くなって下の要素と重なる | **pt 固定**で指定する（`line_spacing = Pt(pt × 1.25)`） |
| 文字枠の既定値のまま | 内側余白 0.1"・自動縮小・折り返しなしで、座標どおりに並ばない | `margin_* = 0`、`auto_size = NONE`、`word_wrap = True` |
| `add_shape` の図形 | `p:style` を引き継ぎ、テーマの青いグラデーションの塗り・青い枠・下向きの影が付く（PowerPoint でも LibreOffice でも出る。2026-09-29 に PowerPoint で確認） | 塗りと線を明示し、`p:style` 要素を外す |
| `add_connector` の直線 | 同上（テーマの青い線に薄い影が付く） | 線の色・太さを明示し、`p:style` を外す |
| `add_table` の表 | テーマの表スタイル（見出しの青・縞・格子）が付く | 表スタイルを「No Style, No Grid」（`{2D5ABB26-0587-4C30-8999-92F81FD0307C}`）にし、`bandRow` 等を 0、罫線は各セルの `lnB` だけ |
| `add_chart` のグラフ | 系列が既定のアクセント色（青・橙）、目盛線・凡例付き | 系列・点ごとに色を指定し、目盛線・凡例を消し、データラベルを付ける。グラフの文字にも ea フォントを入れる |
| 角丸の `ROUNDED_RECTANGLE` | 角の半径が図形サイズに比例して変わり、カードごとに丸みが違う | `adjustments[0] = RADIUS ÷ min(幅, 高さ)` で半径を揃える |
| スライドサイズ未指定 | 4:3（10" × 7.5"）になる | `slide_width = Inches(13.333)`、`slide_height = Inches(7.5)` |
| レイアウトのプレースホルダー | 空の「タイトルを入力」が残る | 白紙レイアウト（`slide_layouts[6]`）に自分で置く |
| 透明度付きの文字に字間 | PowerPoint では正しく描かれる（2026-09-29 に欧文・和文とも確認）。LibreOffice だけ最後の 1 字が消えて描画されるので、PowerPoint の無い環境の画像で欠けて見えても本番の欠陥ではない | 本番を PowerPoint で開くなら外さなくてよい。LibreOffice・Google スライドで開く資料ならどちらかを外す |

---

## 3. HTML デッキで組むとき

コードを実行できない環境（ブラウザ上のチャットなど）や、ブラウザで投影するだけの用途では HTML デッキにする。寸法は **1 インチ ＝ 96px、1pt ＝ 4/3 px**（README §2.6）。スライド 1 枚は 1280 × 720px。色・書体・禁止事項は .pptx と同じ。

```html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>〇〇のご提案</title>
<style>
  /* navy の定数（systems/navy.md §1）。システムを変えるときはここだけ差し替える */
  :root{ --navy:#16284D; --blue:#2D6BE0; --amber:#F0A93C; --white:#FFFFFF; --ink:#1C2638;
         --slate:#5C6880; --hairline:#DDE3ED; --cloud:#F5F7FB; --sky:#EAF1FC; }
  *{margin:0; padding:0; box-sizing:border-box;}
  body{background:#E5E7EB; font-family:"Meiryo","メイリオ","Hiragino Sans","Noto Sans JP",sans-serif; color:var(--ink);}
  .slide{width:1280px; height:720px; position:relative; overflow:hidden; background:var(--white);
         margin:24px auto; line-height:1.25;}
  /* 座標は §2 の値 × 96px。EYEBROW y=0.55" → 53px、TITLE y=0.90" → 86px、SLIDE_TITLE 36pt → 48px */
  .eyebrow{position:absolute; left:72px; top:53px; font-size:16px; color:var(--slate); letter-spacing:.05em;}
  .title{position:absolute; left:72px; top:86px; width:1136px; font-size:48px; font-weight:700; color:var(--navy);}
  .content{position:absolute; left:72px; top:182px; width:1136px; height:461px;}
  .caption{position:absolute; left:72px; top:662px; font-size:14.67px; color:var(--slate);}
  .page{position:absolute; right:72px; top:662px; font-size:14.67px; color:var(--slate);}
  @media print{ body{background:none;} .slide{margin:0; page-break-after:always;} @page{size:1280px 720px; margin:0;} }
</style>
</head>
<body>
<section class="slide">
  <p class="eyebrow">03 ｜ 推移</p>
  <h2 class="title">処理件数は導入 3 年目に 2.4 倍へ伸びる</h2>
  <div class="content"><!-- 主役の図解（SVG か CSS で組む。グラフは SVG で直接ラベル） --></div>
  <p class="caption">試算: メーカー実績値</p>
  <p class="page">6</p>
</section>
</body>
</html>
```

- 1 ページ ＝ 1 つの `section.slide`。要素は `position:absolute` で座標どおりに置く（流し込みのレイアウトにすると、文字量でページごとに位置がずれる）。
- 文字数バジェットは .pptx と同じ式で検算する（1 行の全角文字数 ＝ 幅 px ÷ 文字サイズ px）。
- 検証はブラウザで全ページのスクリーンショットを撮って目視する。PDF にするときは印刷で「背景のグラフィック」を ON にする。
- アニメーション・スクロール演出・グラデーション背景は使わない（紙の資料として成立させる）。

---

## 4. 画像にして目視する

```bash
python3 slides/tools/check_pptx.py deck.pptx --system navy   # 機械チェック
slides/tools/render_pptx.sh deck.pptx out/                    # 全ページを PNG に（out/deck-01.png …）
```

`render_pptx.sh` は、Mac に Microsoft PowerPoint が入っていれば PowerPoint 自身に PDF を書き出させる（本番と同じ描画・同じメイリオ。起動していなかった PowerPoint は変換後に終了する）。PowerPoint の無い環境（Linux のサンドボックスなど）では LibreOffice で変換する。LibreOffice は透明度付きの文字の字間などを PowerPoint と違う形で描くことがあり、メイリオが無い環境では和文が豆腐（□）や空白で描かれるので、スクリプトが一時的に「メイリオ → Noto Sans CJK JP」の置き換え表を入れてから変換する。Mac では LibreOffice がシステムのフォントを見ないため、Homebrew の fontconfig 設定を渡している（スクリプト冒頭のコメント参照）。置き換え後は本番と字幅がわずかに違うので、収まりの最終判定は文字数バジェットで行う。

準備（初回だけ）:

```bash
# mac
brew install poppler fontconfig   # Microsoft PowerPoint が入っていればこれだけでよい
# PowerPoint の無い mac だけ: brew install --cask libreoffice font-noto-sans-cjk-jp font-noto-serif-cjk-jp font-carlito font-caladea
# linux（Codex などのサンドボックス）
apt-get install -y libreoffice poppler-utils fonts-noto-cjk fonts-crosextra-carlito fonts-crosextra-caladea
```
