開発eurekapu-nuxt4

決算書の読み方教材: PDF199ページから47枚のSVG図解作成と表示確認の落とし穴

1ヶ月前に「いつかやる」と棚に置いた決算書教材のPDFを、今日ようやく開いた。199ページの分厚いやつだ。「これを全部SVG図解に置き換える」と決めて、pdftoppmにPDFを食わせた瞬間からセッションが走り出した。

夜になる頃には、preview.htmlに47枚のSVGが章ごとに並んでいた。途中で踏んだ地雷と、svg-diagramスキルに追加した新ルールを残しておく。

1. PDF199ページを画像化して、図インベントリを作る

最初にやったのは「どのページに図があるか」のマッピング。これをサボると、後で「あれ、この図やった?」と何度も聞き直すことになる。

pdftoppm -r 150 input.pdf p -png
# → p-001.png 〜 p-199.png

全199ページが画像になった。次に Claude Code に「ページごとに図あり/なしを判定して、図のタイプ(フロー、概念図、ツリー、表)と一行サマリを書いて」と頼んだ。

最初は memo/2026-05-11/figure-pages-1to50.md に1〜50ページ分だけ作った。中身を見て「これでいける」と判断してから、全199ページ版に拡張した。インベントリができた段階で「SVG化対象は47ページ」と確定した。

2. 「1ページ=1図」「下回らない・改善OKルール」で書く

方針は2つだけ決めた。

  • 1ページ=1図: ページをまたいで複合図にしない。教材として「このページの図」と参照しやすくする
  • 下回らない・改善OKルール: 書籍の図を再現するのが最低ライン。情報量を削らない。ただし配置や表現で改善できるならOK

この方針が後で効いた。サブエージェントに丸投げするときに「元の図と同じ情報を全部入れて、改善は任せる」と一言で渡せた。

3. svg-diagramスキルを起動してバッチ並列で47枚

svg-diagramスキルにはグレー8段階の濃淡、文字サイズ、レイアウト規則、マゼンタ強調のパターンが定義してある。これに従ってサブエージェントを並列起動した。

  • 第1バッチ: 22枚を並列生成
  • 第2バッチ: 25枚を並列生成
  • 合計47枚

人間がやったのは「方針を一言で渡す」「バッチを切る」だけ。実装はサブエージェントが回した。

4. ここから地雷踏みのターン

47枚生成して「できた」と提出したら、ユーザーから「表示確認をちゃんと」と一刺しされた。確かに、preview.htmlで開いてもいない。慌ててChrome DevToolsでpreviewを開いた瞬間、画面が崩れていた。ここから3つのバグを順に潰した。

バグ1: 「セブン&アイ」のXMLエスケープ漏れ

p020あたりのSVGで、テキストがそこから先全部消えていた。SVGをエディタで開いて目で追ったら、<text>セブン&アイ</text> と書いてあった。& は XML予約文字なので &amp; に書かないとパーサーが死ぬ。

3箇所ヒットした。&&amp; に置換して直した。

<!-- NG -->
<text>セブン&アイ・ホールディングス</text>

<!-- OK -->
<text>セブン&amp;アイ・ホールディングス</text>

バグ2: 反復ラベルの太字が「内容差分」をぼかす

対比図(左に「日本企業A」、右に「日本企業B」のような構造)で、両側に同じ「売上高」「営業利益」というラベルが並ぶ。これを両方とも太字にしていた。

並べてみると、視線がラベルの太字に吸われて、本来見せたい右側の数字差分が頭に入ってこない。「対比図では反復するラベルは太字にしない」とその場で気づいた。

svg-diagramスキルに新ルールを追加した:

## 対比図での反復ラベル太字禁止

左右や上下で対比する図では、両側に同じラベル(「売上高」「営業利益」等)が並ぶ。
このとき反復するラベルを太字にすると、視線がラベルに吸われて
本来見せたい「内容の差分」がぼやける。

- 反復ラベル: regular weight
- 差分の数値・キーワード: bold

ルール追加後、9ファイル35箇所の font-weight="bold"font-weight="normal" に書き換えた。preview.htmlを再描画したら、視線が数字に向くようになった。

バグ3: テキストはみ出し(p026の現金化スピード列、p050の「。」見切れ)

これが一番やっかいだった。Chrome DevToolsで開いて初めて「右端の文字が切れている」「最下部の句点が見切れている」と気づくケース。SVGのviewBox内に収まっているように見えても、<text> のサイズ計算が甘いと枠外に飛び出す。

Pythonで全47枚を厳密チェックするスクリプトを書いた。

# 各 <text> 要素の x + 文字数 * フォントサイズ係数 を計算し、
# viewBox の幅を超えるテキストを検出

スクリプトで弾かれた候補を、Chrome DevToolsで16枚実画面確認した。p026の「現金化スピード」列のヘッダーが幅をはみ出していたのと、p050の本文最下行の「。」が下にはみ出していたのを修正した。

5. マゼンタ強調を「ここだけ見ればいい」1〜2箇所に追加

47枚の構造修正が落ち着いた後、各SVGに「読者がここだけ見ればこのページの言いたいことが伝わる」という1〜2箇所をマゼンタで強調した。svg-diagramスキルにあるパターンを素直に適用しただけだが、preview.htmlで一気にスクロールすると、マゼンタの位置だけ目で拾える。教材としての「視線の導線」が一段強くなった。

6. preview.htmlで章別に並べる

最後に preview.html を作って、47枚のSVGを章ごとに整理した。全部一画面でスクロールできるので、構成のバランス(第3章だけ図が多すぎないか等)が一目で分かる。

ついでに「サブメッセージ以下は+16pxシフト」の調整も入れた。各図の説明文がメインメッセージに寄りすぎていて読みづらかったので、垂直方向の余白を広げた。

7. 引き継ぎドキュメント session-handover.md

セッションが長くなってきたので、明日以降の続きのために session-handover.md を作った。

  • どのページが完了したか(47/47)
  • どのバグを潰したか
  • svg-diagramスキルに追加した新ルール
  • preview.htmlの場所と開き方

明日 /clear してもこのファイルから再開できる。

今日の学び

  • インベントリを先に作る: 199ページを「全部見る」のは無理。図あり/なしを先に判定して、対象を47枚に絞ってから着手したのが正解だった
  • 「表示確認をちゃんと」は毎回言われる前にやる: 生成して終わり、ではなく Chrome DevTools で1枚ずつ画面で見る。これをサボったので3バグ連続で踏んだ
  • 対比図の反復ラベルは太字にしない: スキルに追加した。次回以降のSVG生成でこの罠を踏まなくなる
  • XMLエスケープは & だけで十分死ぬ: <> 以外にも & がトラップ。対象テキストに会社名が出てきたら最初に grep で & を探す癖をつけたい

明日やること

  • preview.html を本番ページに組み込む(/financial-statements-textbook のような専用ページに配置)
  • 47枚のSVGに alt テキストを追加してアクセシビリティ対応
  • 図の下に出典ページ番号(p.026 等)を小さく入れて、原書との突合を可能にする
  • svg-diagramスキルの新ルール「対比図での反復ラベル太字禁止」を、過去に作った他のSVG資産にも適用するか検討