report-to-pdf — Markdown レポートを洗練された PDF に
決定論的、スクリプト駆動の Markdown から PDF へのレンダラ。制約された「レポート」用の Markdown サブセットを、手作りの小さなブロックパーサー(外部 Markdown ライブラリ不使用)で解析し、fpdf2 を通じて、ColeteOnline Magento モジュールの build-user-guide.py ジェネレータと同じ視覚的慣習でレイアウトします — タイトルページ、ヘッダー/フッター、目次、アクセントバーのセクション見出し、網掛けコードブロック、境界線付き/ゼブラストライプ表。レイアウトロジックは同じ入力に対して実行間で変化しません。同じレポートを入力すると、毎回同じ PDF が出力されます。レンダリングループに LLM は関与しません。
使用するタイミング
- 「この Markdown レビューをメンターに提出する PDF にして。」
- 「レポートを洗練された PDF にエクスポートして。」
- 「毎回同じ見た目にしたい。LLM が毎回再フォーマットしないように。」
- 見出し、リスト、コードブロック、表を含み、クライアント/メンター/ブランド品質の PDF バージョンが必要な
.mdレポート/レポート。
実行方法
python3 <skill-dir>/scripts/build_report_pdf.py <input.md> [options]
(<skill-dir> はこのスキルのベースディレクトリ、この SKILL.md を含むフォルダです。)
オプション:
-o / --output <path>— 出力 PDF パス(デフォルト:<input>.pdfをソースファイルの隣に作成)。--logo <path>— タイトルページのオプションのロゴ画像(PNG/WebP/JPEG、Pillow が開けるものなら何でも)。複数のロゴを並べて表示するにはフラグを繰り返します(フロントマターlogo: a.png, b.pngでも設定可能。パスは.mdファイルからの相対パス)。--title,--subtitle,--version,--vendor,--author— 対応するフロントマター値を上書き(下記参照)。CLI フラグが優先されます。--cover center|left— カバーページのレイアウト。タイトルブロック全体(ロゴ、タイトル、サブタイトル、著者、バージョン)は両モードで垂直方向に中央揃え。center(デフォルト)では水平方向も中央揃え、leftでは左揃え。フロントマターcover:でも設定可能。--no-header— ランニングヘッダーとその予約スペースを省略。すべてのページのコンテンツが同じ上部マージンから開始されます(フロントマターheader: falseでも設定可能)。--accent "R,G,B"— タイトルページのトップバーとセクション見出しのアクセントバーに使用するアクセントカラーを上書き(デフォルト220,50,50、ColeteOnline レッド)。--dark— デフォルトのライトテーマの代わりにダークテーマでレンダリング(フロントマターtheme: darkでも設定可能。両方存在する場合は--darkが優先)。「ダークテーマ」を参照。
スクリプトは、任意のインポートが失敗した場合、pip install --user で fpdf2、Pillow、Pygments を自動インストールします。これはリファレンススクリプトの Pillow 自動インストールを模倣しています。開発中に 3 つすべてがインポート可能であり、スクリプトがエンドツーエンドで実行されることを確認済みです(下記「テスト」を参照)。
想定する Markdown の慣習
ファイルの先頭にあるオプションのフロントマターブロック:
---
title: My Report Title
subtitle: A one-line subtitle
version: 1.0
vendor: ColeteOnline SRL
accent: 220,50,50
---
すべてのキーはオプションであり、適切にフォールバックします:
title— 本文で見つかった最初の# H1見出しにフォールバック、さらにファイル名にフォールバック。subtitle— 空の場合(タイトルページから省略)。version— 今日の日付(YYYY-MM-DD)にフォールバック。version: noneを設定するとバージョン行を非表示に。vendor— 空の場合(設定されていない場合、タイトルページのフッターから著作権行を省略)。author— 著者名。サブタイトルの下の表紙に表示されます(例:author: Jane Doe, John Smith)。設定されていない場合は省略。cover—center(デフォルト)またはleft。上記の--coverを参照。header—falseでランニングヘッダーを省略。--no-headerを参照。justify—trueで本文テキスト、見出し、表セルを両端揃えに。デフォルトは左揃え(両端揃えテキストは短い折り返し行で単語間のギャップが伸びます。例えば 2 行の見出し)。accent— ColeteOnline ブランドレッド220,50,50にフォールバック。
本文 Markdown と各要素のレンダリング方法:
| Markdown | レンダリング結果 |
|---|---|
# Title | ファイル内の 最初の # はタイトルページのタイトルのみ(消費され、本文では繰り返されません)。それ以降の # は実際の大見出し(下記参照)。 |
# Major Section(最初の # の後) | 大見出し。 常に新しいページから開始(カーソルが既に空白ページの先頭にある場合を除く)、大きな色付き左バー、太字 16pt、黒テキスト — 目次エントリを取得。真のトップレベルの区切りにのみ使用。それぞれ 1 ページ消費されます。 |
## Subsection | 小見出し。 太字 13pt、アクセントカラーのテキストと短いアクセントカラーの下線(上下の階層と視覚的に区別可能) — 改ページは強制せず、フローを続行。目次エントリを取得。ページ下部に見出しが孤立しないための独自のチェックがあります。 |
### Sub-subsection(および ####+) | 三次見出し。 太字 12pt、ニュートラルグレー、全幅細線、改ページなし、目次エントリなし(目次をスキャンしやすく保つ)。 |
| 段落テキスト | 両端揃えの本文段落、10pt。 |
- item / * item | 箇条書き(•)。 |
1. item | 番号付きリスト。 |
```lang ... ``` | 網掛け、境界線付き等幅コードブロック。言語タグ(存在する場合)はブロックの上に小さな斜体ラベルとして表示され、実際の Pygments シンタックスハイライトを駆動(下記参照)。 |
| ` | a |
--- / *** / ___ 単独行 | 垂直スペーサー(改ページではありません)。 |
<br> 単独行 | 明示的な空白本文行 1 行(5.5mm)。複数積み重ねてスペースを追加。レンダラはページ上部に独自のギャップを追加しないので、余分なスペースが必要な場合に使用。 |
<!-- pagebreak --> 単独行 | 新しいページを開始(既にページの先頭にある場合は何もしない)。 |
 単独行 | 中央揃え、境界線付き画像、フィットするように拡大縮小。相対パスは .md ファイルのフォルダに対して解決。PNG/JPEG/WebP および SVG(「図、ボックス、数式」を参照)。900 ピクセル未満の生ラスター画像は約 130x105mm に制限。図(画像の後にキャプションが続くもの)、SVG、および 900 ピクセル以上の画像はテキスト幅全体を使用可能(最大高さ 120mm)。ページに残されたスペースに収まらない図は、次のページにジャンプしてギャップを残す代わりに、収まるように縮小されます(高さの 70% まで)。 |
*Figure: ...*(画像の直後の斜体行全体) | 画像のキャプション: 小さく、中央揃え、斜体、画像と同じページに保持。alt テキストは印刷されません。 |
> text 行 | 定義ボックス: ブロック引用ごとに 1 つの境界線付き、薄く網掛けされたボックス(太字用語とインライン数式は内部で機能。> 行の後に何もない場合、同じボックス内で新しい段落が開始)。 |
$...$ | インライン数式。Unicode テキストに変換(ギリシャ文字、≤ ≥ × ·、下付き/上付き文字、\frac{a}{b} -> (a)/b、\sqrt、\text{}、アクセント)。$5 のような価格は数式ではありません。 |
$$...$$(1 行、または独自の行) | ディスプレイ数式。最初に動作する数式バックエンドからの中央揃え画像(「図、ボックス、数式」を参照)。 |
| 空白行の後の 4 スペースインデントブロック | 言語タグなしのコードブロック(ノートブックエクスポートはセル出力をこの方法で出力)。リストの直後には適用されません。 |
**bold** | 実際の太字テキスト。fpdf2 の組み込み markdown=True レンダラを使用。 |
[text](url) | 実際のクリック可能な PDF リンク注釈 — 生の URL ではなく表示テキストのみが表示され、明確なリンク色でスタイル設定。 |
*italic* / _italic_ | 斜体でレンダリング(内部的に fpdf2 のネイティブ __italic__ 形式に変換)。 |
`code` | インラインコードのバックティックは削除 — 等幅インラインのサポートなし、プレーンテキストとしてレンダリング。 |
見出し、段落、箇条書き、番号付きアイテム、表セルはすべて fpdf2 の markdown=True パスを通じてレンダリングされるため、太字 と [text](url) リンクは、見出しや表セルなど、テキストが表示されるすべての場所で機能します。フェンス付きコードブロックは唯一の例外で、その内容は完全にリテラルにレンダリングされます(Markdown 解析なし)。したがって、リテラルな **、__、または []() を含むコードサンプルが誤解釈されることはありません。
図、ボックス、数式
ノートブックの Markdown コンパニオン(nbconvert 出力)を直接レンダリングするように記述されています。report-to-pdf notebook.md は、別の Chrome ベースの PDF ステップを置き換えます。
SVG 画像。  が機能します。fpdf2 独自の SVG 描画は <marker> 矢印、フィルター、マスクをスキップし、テキストをコアフォント(ギリシャ文字なし)で描画し、fill なしのテキストの色を誤って処理するため、すべての SVG は 2.5 倍でラスタライズされ、PNG として埋め込まれます。使用順序は次のとおり: ヘッドレス Chrome/Edge(自動検出。REPORT_PDF_BROWSER をブラウザパスに設定して上書き可能)、次に cairosvg(インポート可能な場合)。どちらもない場合、SVG は fpdf2 によってネイティブに描画され、不完全である可能性があるという警告が表示されます。ラスタライズは実行ごとにキャッシュされます。
ディスプレイ数式バックエンド(--math auto|latex|mathtext|unicode、またはフロントマター math:。デフォルト auto)。スクリプトは使用されたバックエンドを出力します。
latex:pdflatex、xelatex、またはtectonicが PATH 上にある場合(さらにpdftocairo、PyMuPDF、またはdvisvgmを使用して画像を作成)、またはlatex+dvipngがある場合、実際の LaTeX。各式はstandalone、amsmath、amssymb、xcolorで 12pt でコンパイルされ、テキスト色の透明な 300 dpi PNG にクロップされます。インストール: MiKTeX(Windows)、TeX Live(apt install texlive-latex-base texlive-latex-extra poppler-utils、macOSbrew install --cask mactex-no-gui)、またはtectonic(単一バイナリ、オンデマンドでパッケージをダウンロード。conda install -c conda-forge tectonicまたはscoop install tectonic。画像コンバーターとして PyMuPDF を使用して Windows でエンドツーエンドでテスト済み)。mathtext: matplotlib の LaTeX サブセット(分数、総和、ギリシャ文字、\text{}は\mathrmにマッピング)。TeX は不要、matplotlib のみ必要。alignedなどの環境はサポートされておらず、フォールスルーします。unicode: インライン数式と同じテキスト変換、中央揃え。
欠落している、または失敗するバックエンド(たとえば TeX が拒否する数式)は、次のバックエンドに静かにフォールスルーします。--math latex も同じように劣化します。
テスト: python tests/run_checks.py(数式変換、パーサー、SVG ルーティング、モック PATH によるバックエンドフォールバック、tests/fixture.md のエンドツーエンドビルド)。コンパニオンをレンダリング: python scripts/build_report_pdf.py notebook.md -o notebook.pdf。
シンタックスハイライト
フェンス付きコードブロックは、実際の Pygments レクサーでトークン化されます(手作りの正規表現ハイライターではありません。コードは小さなアドホックパーサーが微妙に間違える種類のものです: 文字列エスケープ、ネストされたコメント、テンプレートリテラル)。フェンスの言語タグ(```typescript、```sql、```python...)がレクサーを選択します。一般的なエイリアスの一部は正規化されます(ts/tsx → typescript、js/jsx → javascript、sh/shell/zsh → bash、yml → yaml、prisma → graphql — 最も近い組み込みレクサー。Prisma の @attribute(...) 構文と GraphQL の @directive(...) 構文はほぼ同じようにトークン化されるため、実際には良い一致です)。
ハイライトはボーナスであり、コードブロックをレンダリングするための必須要件ではありません。言語タグがない場合、Pygments が認識しない言語(エイリアステーブル後も)、または Pygments 自体がインポートできない場合、すべてハイライト前の正確な動作にフォールバックします: ブロック全体が 1 つのフラットな色で表示され、クラッシュもコンテンツの欠落もありません。
カラーパレット(コードブロックの明るい灰色の背景に対する読みやすさのために選択され、特定の Pygments テーマに関連付けられていません):
| トークンカテゴリ | 色 |
|---|---|
キーワード(interface、async、const、NOT NULL、string/number などの型キーワード) | マゼンタ |
| 文字列 | 緑 |
| 数値 | 茶/オレンジ |
| コメント | グレー |
関数/クラス名、デコレータ/属性(@map、@relation) | 青 / 茶 |
組み込み(Promise など) | ティール |
| その他すべて(識別子、句読点、演算子) | ベースコード色 |
パレットに正確なエントリがないトークンタイプは、Pygments のトークンタイプ階層を上にたどって(例: Keyword.Declaration には独自のエントリがありませんが、その親 Keyword にはあります)一致が見つかるまで解決します。裸の Token ルートは常に一致するため、これが 何らかの 色に解決できないことはありません。
等幅の縮小フィットロジック(ページに対して広すぎるコード行は、オーバーフローする代わりにブロック全体のフォントを均一に縮小)はハイライトの影響を受けません。これはプレーンな文字幅を測定するものであり、トークンが描画される色によって変化しません。
ダークテーマ
--dark を渡すか、フロントマターで theme: dark を設定すると、デフォルトのライトテーマの代わりにダークテーマでドキュメント全体がレンダリングされます。ドキュメント内のすべての色が、ダークに適した対応色に変わります。コードブロックだけではありません:
- ページ背景自体が暗く塗りつぶされます(fpdf2 にはネイティブの「ページ背景色」設定がないため、これは
header()フックでページ全体を塗りつぶすことで行われ、他の描画より前に、タイトルページも含めて行われます)。 - 本文テキスト、見出し(3 階層すべて)、薄暗いテキスト/フッターテキスト、罫線、表のゼブラストライプ、ランニングヘッダー/フッターはすべて、ライトテーマの色を暗くしたものではなく、別のダークモードの色セットを使用します。
- シンタックスハイライトは、独自のダークモードパレット(暗いコード背景に対してコントラストが高くなるよう選択された、より明るいピンク/緑/青)を使用します。ライトモードの色を暗くしたものではありません。
- リンク色(
[text](url)マークダウンリンクと TOC のページジャンプリンクの両方)は、暗い背景でも読みやすい明るい青に切り替わります。
--accent 色(トップバー、大見出しの左バー、小見出しのテキスト/下線)は両方のテーマでそのまま使用されます。両方のテーマで同じレポートを生成する予定がある場合は、白とほぼ黒の両方の背景に対して機能するものを選択し、そうでない場合はテーマごとに --accent を上書きします。
目次
バックフィルされた目次(「レンダリング方法」を参照...
...(元のスキルは truncate されました)