report-to-pdf – Markdown-Bericht zu poliertem PDF
Deterministischer, skriptgesteuerter Markdown-zu-PDF-Renderer. Er parst einen
eingeschränkten „Report"-Markdown-Subset mit einem kleinen handgeschriebenen Block-
Parser (keine externe Markdown-Bibliothek) und setzt ihn über fpdf2 mit
den gleichen visuellen Konventionen wie der ColeteOnline-Magento-Modul-
build-user-guide.py-Generator um – Titelseite, Kopf-/Fußzeile, TOC,
Akzentbalken-Abschnittsüberschriften, schattierte Code-Blöcke, umrandete/Zebrastreifen-
Tabellen. Das Layout ändert sich nie zwischen Durchläufen mit demselben Input:
Gleicher Bericht rein, gleiches PDF raus, jedes Mal – kein LLM im
Render-Loop.
Wann verwenden
- „Wandel diesen Markdown-Review in ein PDF für den Mentor um.“
- „Exportiere meinen Bericht als poliertes PDF.“
- „Ich möchte, dass das jedes Mal gleich aussieht, nicht von einem LLM bei jedem Durchlauf neu formatiert.“
- Jeder
.md-Report/-Text, der Überschriften, Listen, Code-Blöcke oder Tabellen enthält und eine Client-/Mentor-/Markenqualitäts-PDF-Version benötigt.
So führen Sie es aus
python3 <skill-verz>/scripts/build_report_pdf.py <eingabe.md> [optionen]
(<skill-verz> ist das Basisverzeichnis dieses Skills, der Ordner, der diese SKILL.md enthält.)
Optionen:
-o / --output <pfad>– Ausgabe-PDF-Pfad (Standard:<eingabe>.pdfneben der Quelldatei).--logo <pfad>– optionales Logobild (PNG/WebP/JPEG, alles, was Pillow öffnen kann) auf der Titelseite. Wiederholen Sie das Flag für mehrere Logos, die nebeneinander in einer Reihe gezeichnet werden (auch über Frontmatterlogo: a.png, b.pngeinstellbar, Pfade relativ zur.md-Datei).--title,--subtitle,--version,--vendor,--author– Überschreiben der entsprechenden Frontmatter-Werte (siehe unten); CLI-Flags gewinnen.--cover center|left– Titelseitenlayout. Der gesamte Titelblock (Logo, Titel, Untertitel, Autor, Version) wird in beiden Modi vertikal zentriert;center(Standard) zentriert ihn auch horizontal,leftlinksbündig. Auch über Frontmattercover:einstellbar.--no-header– Lässt den laufenden Kopf und den dafür reservierten Platz weg, sodass der Inhalt jeder Seite am gleichen oberen Rand beginnt (auch über Frontmatterheader: falseeinstellbar).--accent "R,G,B"– Überschreibt die Akzentfarbe für den oberen Balken der Titelseite und die Akzentbalken der Abschnittsüberschriften (Standard220,50,50, ColeteOnline-Rot).--dark– Rendert mit dem dunklen Farbschema anstelle des standardmäßigen hellen (auch über Frontmattertheme: darkeinstellbar;--darkgewinnt, wenn beide vorhanden sind). Siehe „Dark theme" unten.
Das Skript installiert automatisch fpdf2, Pillow und Pygments via
pip install --user, falls ein Import fehlschlägt, und spiegelt damit die
Pillow-Autoinstallation des Referenzskripts wider. Alle drei waren importierbar bestätigt und das Skript wurde während der Entwicklung End-to-End ausgeführt (siehe Testing unten).
Markdown-Konventionen, die es erwartet
Optionaler Frontmatter-Block ganz oben in der Datei:
---
title: Mein Berichtstitel
subtitle: Ein einzeiliger Untertitel
version: 1.0
vendor: ColeteOnline SRL
accent: 220,50,50
---
Alle Schlüssel sind optional und fallen sinnvoll zurück:
title– fällt auf die erste# H1-Überschrift im Textkörper zurück, dann auf den Eingabedateinamen.subtitle– fällt auf leer zurück (wird auf der Titelseite weggelassen).version– fällt auf das heutige Datum zurück (YYYY-MM-DD); setzen Sieversion: none, um die Versionszeile auszublenden.vendor– fällt auf leer zurück (Copyright-Zeile wird in der Fußzeile der Titelseite weggelassen, wenn nicht gesetzt).author– Autorenname(n), werden auf dem Cover unter dem Untertitel angezeigt (z. B.author: Jane Doe, John Smith). Wird weggelassen, wenn nicht gesetzt.cover–center(Standard) oderleft; siehe--coveroben.header–falselässt den laufenden Kopf weg; siehe--no-header.justify–truesorgt für Blocksatz im Textkörper, Überschriften und Tabellenzellen; der Standard ist linksbündig (Blocksatz dehnt Wortabstände bei kurzen umbrochenen Zeilen, z. B. einer zweizeiligen Überschrift).accent– fällt auf das ColeteOnline-Markenrot220,50,50zurück.
Textkörper-Markdown und wie jedes Element gerendert wird:
| Markdown | Wird gerendert als |
|---|---|
# Titel | Das allererste # in der Datei ist nur der Titel der Titelseite (verbraucht, nicht im Textkörper wiederholt). Jedes spätere # ist eine echte Hauptüberschrift (siehe unten). |
# Hauptabschnitt (nach dem ersten #) | Hauptüberschrift. Beginnt immer auf einer eigenen frischen Seite (außer der Cursor ist bereits oben auf einer leeren Seite), großer farbiger linker Balken, fett 16pt, schwarzer Text – und erhält einen TOC-Eintrag. Nur für echte Top-Level-Abschnitte verwenden; jeder kostet eine Seite. |
## Unterabschnitt | Zwischenüberschrift. Fett 13pt, Akzentfarb-Text mit einer kurzen akzentfarbigen Unterstreichung (visuell unterschiedlich zu beiden Ebenen darüber und darunter) – erzwingt keinen Seitenumbruch, setzt den Fluss fort. Erhält trotzdem einen TOC-Eintrag. Hat eine eigene Überprüfung, dass eine Überschrift nicht ganz unten auf einer Seite hängen bleibt. |
### Unter-Unterabschnitt (und ####+) | Tertiäre Überschrift. Fett 12pt, neutrales Grau, dünne durchgehende Linie, kein Seitenumbruch, kein TOC-Eintrag (hält das TOC überschaubar). |
| Absatztext | Linksbündiger Fließtextabsatz, 10pt. |
- item / * item | Aufzählungsliste (•). |
1. item | Nummerierte Liste. |
```sprache ... ``` | Schattierter, umrandeter Monospace-Codeblock; der Sprach-Tag (falls vorhanden) wird als kleine kursive Bezeichnung über dem Block gedruckt und steuert das echte Pygments-Syntax-Highlighting – siehe unten. |
| ` | a |
--- / *** / ___ in einer eigenen Zeile | Vertikaler Abstandshalter (kein Seitenumbruch). |
<br> in einer eigenen Zeile | Eine explizite leere Textzeile (5,5 mm). Stapeln Sie mehrere für mehr Platz. Der Renderer fügt oben auf einer Seite keine eigenen Abstände hinzu, also verwenden Sie dies, wenn Sie zusätzlichen Platz wünschen. |
<!-- pagebreak --> in einer eigenen Zeile | Beginnt eine neue Seite (kein Effekt, wenn bereits oben auf einer). |
 in einer eigenen Zeile | Zentriertes, umrandetes Bild, skaliert, um zu passen; relative Pfade werden relativ zum Ordner der .md-Datei aufgelöst. PNG/JPEG/WebP und SVG (siehe „Abbildungen, Boxen und Mathematik"). Ein reines Rasterbild unter 900 px Breite wird auf ~130x105mm begrenzt; eine Abbildung (Bild gefolgt von einer Bildunterschrift), jedes SVG und jedes Bild mit 900 px oder mehr Breite darf die volle Textbreite nutzen (max. Höhe 120mm). Eine Abbildung, die nicht in den verbleibenden Platz auf der Seite passt, wird auf den Platz verkleinert (bis auf 70 % ihrer Höhe), anstatt auf die nächste Seite zu springen und eine Lücke zu hinterlassen. |
*Abbildung: ...* (eine ganze kursive Zeile direkt nach einem Bild) | Die Bildunterschrift des Bildes: klein, zentriert, kursiv, auf derselben Seite wie das Bild gehalten. Alt-Text wird nie gedruckt. |
> text Zeilen | Definitionsbox: eine umrandete, leicht schattierte Box pro Blockquote (fetter Begriff und Inline-Mathematik funktionieren darin; eine >-Zeile ohne weiteren Inhalt beginnt einen neuen Absatz in derselben Box). |
$...$ | Inline-Mathematik, in Unicode-Text umgewandelt (griechische Buchstaben, ≤ ≥ × ·, tief-/hochgestellte Zeichen, \frac{a}{b} -> (a)/b, \sqrt, \text{}, Akzente). Ein Preis wie $5 ist keine Mathematik. |
$$...$$ (eine Zeile oder in eigenen Zeilen) | Display-Mathematik, ein zentriertes Bild vom ersten funktionierenden Mathe-Backend (siehe „Abbildungen, Boxen und Mathematik"). |
| 4-Leerzeichen eingerückter Block nach einer leeren Zeile | Codeblock ohne Sprach-Tag (Notebook-Exporte drucken Zellenausgabe auf diese Weise). Wird nicht direkt nach einer Liste angewendet. |
**fett** | Echter fetter Text, über fpdf2s integrierten markdown=True-Renderer. |
[text](url) | Ein echter, klickbarer PDF-Link-Annotation – nur der Anzeigetext wird gezeigt, nicht die rohe URL – in einer deutlichen Linkfarbe gestaltet. |
*kursiv* / _kursiv_ | Gerendert kursiv (intern in fpdf2s native __italic__-Form gefaltet). |
`code` | Inline-Code-Backticks werden entfernt – keine Monospace-Inline-Unterstützung, wird als Klartext gerendert. |
Überschriften, Absätze, Aufzählungen, nummerierte Elemente und Tabellenzellen werden alle über fpdf2s markdown=True-Pfad gerendert, daher funktionieren fett und [text](url)-Links überall, wo Text erscheint, einschließlich in Überschriften und Tabellenzellen. Fenced-Code-Blöcke sind die eine Ausnahme – ihr Inhalt wird vollständig wörtlich gerendert (keine Markdown-Analyse), daher wird ein Codebeispiel, das wörtliche **, __ oder []() enthält, nicht falsch interpretiert.
Abbildungen, Boxen und Mathematik
Geschrieben, damit der Markdown-Begleiter eines Notebooks (nbconvert-Ausgabe) direkt rendert: report-to-pdf notebook.md ersetzt einen separaten Chrome-basierten PDF-Schritt.
SVG-Bilder.  funktioniert. fpdf2s eigenes SVG-Zeichnen überspringt <marker>-Pfeilspitzen, Filter und Masken, zeichnet Text in einer Kernschriftart (kein Griechisch) und färbt Text ohne fill falsch, daher wird jedes SVG mit 2,5x gerastert und als PNG eingebettet, wobei in dieser Reihenfolge verwendet wird: ein kopfloser Chrome/Edge (automatisch gefunden; setzen Sie REPORT_PDF_BROWSER auf einen Browserpfad, um zu überschreiben), dann cairosvg, falls importierbar. Ohne beides wird das SVG nativ von fpdf2 gezeichnet und eine Warnung sagt, dass es unvollständig sein könnte. Das Rastern wird pro Durchlauf zwischengespeichert.
Display-Mathematik-Backends (--math auto|latex|mathtext|unicode oder Frontmatter math:; Standard auto). Das Skript gibt aus, welches Backend verwendet wurde.
latex: echtes LaTeX, wennpdflatex,xelatexodertectonicim PATH ist (pluspdftocairo, PyMuPDF oderdvisvgm, um das Bild zu erstellen), oderlatex+dvipng. Jede Formel wird mitstandalone,amsmath,amssymbundxcolorin 12 pt kompiliert und dann auf ein transparentes 300-dpi-PNG in der Textfarbe zugeschnitten. Installation: MiKTeX (Windows), TeX Live (apt install texlive-latex-base texlive-latex-extra poppler-utils, macOSbrew install --cask mactex-no-gui) odertectonic(einzelne Binärdatei, lädt Pakete bei Bedarf herunter;conda install -c conda-forge tectonicoderscoop install tectonic; End-to-End auf Windows mit PyMuPDF als Bildkonverter getestet).mathtext: matplotlibs LaTeX-Teilmenge (Brüche, Summen, Griechisch,\text{}wird auf\mathrmabgebildet). Kein TeX nötig, nur matplotlib. Umgebungen wiealignedwerden nicht unterstützt und fallen durch.unicode: die gleiche Textumwandlung wie Inline-Mathematik, zentriert.
Ein Backend, das fehlt oder fehlschlägt (z. B. eine Formel, die TeX ablehnt), fällt stillschweigend auf das nächste zurück; --math latex degradiert auf die gleiche Weise.
Test: python tests/run_checks.py (Mathematik-Konvertierung, Parser, SVG-Routing, Backend-Fallback mit gemocktem PATH, End-to-End-Erstellung von tests/fixture.md). Rendern eines Begleiters: python scripts/build_report_pdf.py notebook.md -o notebook.pdf.
Syntax-Highlighting
Fenced-Code-Blöcke werden mit echten Pygments-Lexern tokenisiert (kein
handgestrickter Regex-Highlighter – Code ist genau die Art von Ding, die ein kleiner
Ad-hoc-Parser subtil falsch versteht: String-Escapes, verschachtelte
Kommentare, Template-Literale). Der Sprach-Tag auf der Abgrenzung (```typescript, ```sql, ```python, ...)
wählt den Lexer aus; eine Handvoll gängiger Aliase werden normalisiert (ts/
tsx → typescript, js/jsx → javascript, sh/shell/zsh →
bash, yml → yaml, prisma → graphql – der nächstgelegene eingebaute
Lexer und eine gute Übereinstimmung in der Praxis, da Prismas @attribute(...)-
Syntax und GraphQLs @directive(...)-Syntax fast identisch tokenisieren).
Highlighting ist ein Bonus, niemals eine harte Anforderung zum Rendern eines Code- Blocks – ein fehlender Sprach-Tag, eine Sprache, die Pygments nicht erkennt (selbst nach der Alias-Tabelle), oder Pygments selbst, das nicht importierbar ist, fallen alle auf das exakte Verhalten vor dem Highlighting zurück: der gesamte Block in einer flachen Farbe, kein Absturz, kein fehlender Inhalt.
Farbpalette (gewählt für Lesbarkeit vor dem hellgrauen Hintergrund des Codeblocks, nicht an ein bestimmtes Pygments-Theme gebunden):
| Token-Kategorie | Farbe |
|---|---|
Schlüsselwörter (interface, async, const, NOT NULL, Typ-Schlüsselwörter wie string/number) | Magenta |
| Zeichenketten | Grün |
| Zahlen | Braun/Orange |
| Kommentare | Grau |
Funktions-/Klassennamen, Dekorateure/Attribute (@map, @relation) | Blau / Braun |
Builtins (Promise usw.) | Blaugrün |
| Alles andere (Bezeichner, Satzzeichen, Operatoren) | Die Basis-Code-Farbe |
Ein Token-Typ ohne exakten Eintrag in der Palette wird aufgelöst, indem die Hierarchie der Pygments-Token-Typen durchlaufen wird (z. B. Keyword.Declaration hat keinen eigenen Eintrag, aber sein Elternteil Keyword schon), bis ein Treffer gefunden wird – die nackte Token-Wurzel stimmt immer, daher schlägt dies nie fehl, irgendeine Farbe aufzulösen.
Die Monospace-Shrink-to-Fit-Logik (eine Codezeile, die zu breit für die Seite ist, verkleinert gleichmäßig die Schriftgröße des gesamten Blocks, anstatt überzulaufen) wird durch Highlighting nicht beeinflusst – sie misst einfache Zeichenbreiten, die sich nicht ändern, je nachdem, welche Farbe ein Token letztendlich erhält.
Dark theme
Übergeben Sie --dark oder setzen Sie theme: dark im Frontmatter, um das gesamte
Dokument in einem dunklen Theme anstelle des standardmäßig hellen zu rendern.
Jede Farbe im Dokument wechselt zu einem dunkel-angepassten Gegenstück –
nicht nur die Code-Blöcke:
- Der Seitenhintergrund selbst wird dunkel gefüllt (fpdf2 hat keine native
„Seitenhintergrundfarbe"-Einstellung, daher wird dies durch Füllen der vollen Seite
im
header()-Hook erreicht, bevor irgendetwas anderes darauf gezeichnet wird – auch auf der Titelseite). - Textkörper, Überschriften (alle drei Ebenen), dezenter/footer-Text, Regel- linien, Tabellen-Zebrastreifen und die laufende Kopf-/Fußzeile verwenden alle einen separaten Dark-Mode-Farbsatz, nicht nur eine gedimmte helle Version.
- Syntax-Highlighting verwendet seine eigene Dark-Mode-Palette (hellere Pink-/Grün-/Blautöne für Kontrast vor dem dunklen Code- Hintergrund), nicht die hellen Farben abgedimmt.
- Linkfarbe (sowohl
[text](url)-Markdown-Links als auch die Sprung-zu-Seiten-Links des TOC) wechselt zu einem helleren Blau, das auf dunklem Hintergrund lesbar bleibt.
Die --accent-Farbe (der obere Balken, der linke Balken der Hauptüberschrift und der Unterüberschrift-Text/Unterstreichung) wird in beiden Themes unverändert verwendet – wählen Sie eine aus, die sowohl vor einem weißen als auch vor einem fast schwarzen Hintergrund funktioniert, wenn Sie denselben Bericht in beiden Themes generieren möchten, oder überschreiben Sie --accent pro Theme, wenn nicht.
Inhaltsverzeichnis
Das rückwirkend befüllte TOC (siehe „Wie das rendert" oben) wird …
[Rest des Inhalts gekürzt für die Übersetzung]