operations_research/bilder_04/erzeuge_titelseite.py

350 lines
16 KiB
Python
Raw Permalink Normal View History

Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
#!/usr/bin/env python3
# erzeuge_titelseite.py
"""
Baut die Titelseite bilder_04/titelseite.svg (und die HTML-Kopie
OR_HTML_04/bilder_04/titelseite.svg) als SVG mit matplotlib. Das Datum im Fußband
('Stand: <datum> v<Std>.<Min>') wird dynamisch aus datetime.now() gesetzt,
damit es nach jedem Build den aktuellen Stand zeigt.
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
Die Koordinaten unten sind SVG-Punkte (595.44 x 841.68, A4 @ 72 dpi). Die
y-Achse wird invertiert (set_ylim(841.68, 0)), sodass y von oben zählt und
die Werte direkt mit der alten, von Hand erzeugten titelseite.svg
übereinstimmen.
Design (aus der Vorgängerversion abgeleitet):
- weißer Hintergrund
- 5 farbige Akzentbalken oben
- 7 konzentrische Ringe oben rechts (indigo, transparent)
- violette Trennlinie unter dem Untertitel
- grüne, konkave 'Optimierungskurve' mit rotem Stern-Marker (Optimum)
- große Solver-Karte (hell, violetter Rand) mit vier kleinen farbigen
Solver-Karten (HiGHS indigo, SciPy grün, Google OR-Tools cyan, CVXPY violett)
- indigo Fußband unten mit Autor (weiß) und Stand-Datum (hellviolett)
Aufruf:
python3 bilder_04/erzeuge_titelseite.py
(wird vom Build-Skript build_version_03.py vor dem PDF-/HTML-Bau aufgerufen).
"""
import os
Titeltext: eine Quelle fuer PDF-Titelseite und Startseite Die Startseite zeigte einen anderen Titeltext als das PDF. Der Text stand VIERMAL im Repository: NEUER_TITEL.md, die Konstanten in bilder_04/erzeuge_titelseite.py, der Titelblock des Vorworts - und noch einmal fest einprogrammiert im Hero-Block von baue_landingpage(). Die Ursache ist messbar: Die drei Commits, die den Titel ueberarbeitet haben (ca5621f, 199dd3b, 1659fb9 im Ursprungs-Repository), fassten jedes Mal dieselben drei Dateien an. build_version_04.py war in keinem davon, und OR_HTML_04/index.html taucht in ihren Dateilisten gar nicht auf. Online stand deshalb: Unterzeile "Von den mathematischen Grundlagen zur quantitativen Portfolio-Optimierung an den Finanzmaerkten." Kompendium "Ein vollstaendiges Kompendium mit Algorithmen ..." Anwendungen fehlte ganz Behandlung wie bei den neun Nummern-Funden zuvor: nicht die Kopie korrigieren, sondern die Kopie abschaffen. Das neue titeltexte_04.py zerlegt NEUER_TITEL.md in Titel, Untertitel, Kompendium- und Anwendungen-Absatz; PDF-Titelseite und Startseite lesen dieselbe Funktion. Der Autor bleibt bewusst Konstante im Generator - die Vorlage beschreibt das Buch, nicht sein Impressum. Der entscheidende Beleg: Die neu erzeugte titelseite.svg ist BYTE-IDENTISCH (97fb40d7...) zur vorherigen - der Umbau hat am PDF nachweislich nichts veraendert. Auch nicht am Umbruch: Die frueher handgesetzten drei Zeilen entstehen jetzt automatisch aus _umbrechen(), und zwar exakt so. Ein Textvergleich ueber das SVG war uebrigens nicht moeglich - es enthaelt kein einziges <text>-Element, matplotlib zeichnet alle 726 Buchstaben als Kurven. Das Vorwort bleibt von Hand gepflegter Buchtext, wird aber von pruefe_titeltexte() bei jedem --check gegen die Quelle gestellt. Gleich beim ersten Lauf fand es einen echten Unterschied: Das Vorwort setzte einen Geviertstrich, die Vorlage einen Halbgeviertstrich. Im Deutschen ist der Halbgeviertstrich der richtige - das Vorwort folgt jetzt der Quelle. Veroeffentlicht: 5 Dateien uebertragen, alle vierzehn HTTP-Proben richtig. Gegenprobe vom Server: alle drei Absaetze der Startseite sind zeichengenau identisch mit NEUER_TITEL.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:39:49 +02:00
import sys
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
from datetime import datetime
Titeltext: eine Quelle fuer PDF-Titelseite und Startseite Die Startseite zeigte einen anderen Titeltext als das PDF. Der Text stand VIERMAL im Repository: NEUER_TITEL.md, die Konstanten in bilder_04/erzeuge_titelseite.py, der Titelblock des Vorworts - und noch einmal fest einprogrammiert im Hero-Block von baue_landingpage(). Die Ursache ist messbar: Die drei Commits, die den Titel ueberarbeitet haben (ca5621f, 199dd3b, 1659fb9 im Ursprungs-Repository), fassten jedes Mal dieselben drei Dateien an. build_version_04.py war in keinem davon, und OR_HTML_04/index.html taucht in ihren Dateilisten gar nicht auf. Online stand deshalb: Unterzeile "Von den mathematischen Grundlagen zur quantitativen Portfolio-Optimierung an den Finanzmaerkten." Kompendium "Ein vollstaendiges Kompendium mit Algorithmen ..." Anwendungen fehlte ganz Behandlung wie bei den neun Nummern-Funden zuvor: nicht die Kopie korrigieren, sondern die Kopie abschaffen. Das neue titeltexte_04.py zerlegt NEUER_TITEL.md in Titel, Untertitel, Kompendium- und Anwendungen-Absatz; PDF-Titelseite und Startseite lesen dieselbe Funktion. Der Autor bleibt bewusst Konstante im Generator - die Vorlage beschreibt das Buch, nicht sein Impressum. Der entscheidende Beleg: Die neu erzeugte titelseite.svg ist BYTE-IDENTISCH (97fb40d7...) zur vorherigen - der Umbau hat am PDF nachweislich nichts veraendert. Auch nicht am Umbruch: Die frueher handgesetzten drei Zeilen entstehen jetzt automatisch aus _umbrechen(), und zwar exakt so. Ein Textvergleich ueber das SVG war uebrigens nicht moeglich - es enthaelt kein einziges <text>-Element, matplotlib zeichnet alle 726 Buchstaben als Kurven. Das Vorwort bleibt von Hand gepflegter Buchtext, wird aber von pruefe_titeltexte() bei jedem --check gegen die Quelle gestellt. Gleich beim ersten Lauf fand es einen echten Unterschied: Das Vorwort setzte einen Geviertstrich, die Vorlage einen Halbgeviertstrich. Im Deutschen ist der Halbgeviertstrich der richtige - das Vorwort folgt jetzt der Quelle. Veroeffentlicht: 5 Dateien uebertragen, alle vierzehn HTTP-Proben richtig. Gegenprobe vom Server: alle drei Absaetze der Startseite sind zeichengenau identisch mit NEUER_TITEL.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:39:49 +02:00
# Der Titeltext kommt aus NEUER_TITEL.md, gelesen von titeltexte_04.py. Das
# Modul liegt im Quellverzeichnis des Buchs, dieses Skript in bilder_04/ -
# daher der Pfadeintrag. Ein zweiter Parser hier waere genau die Dopplung,
# die dieses Modul beseitigt.
_BASIS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(_BASIS,
"Operations_Research_mit_Python_Version_04"))
from titeltexte_04 import lies_titeltexte
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyBboxPatch, Rectangle
# Reproduzierbare SVG-Ausgabe. Ohne diese beiden Vorkehrungen unterscheiden sich
# zwei Laeufe - und sogar die zwei Kopien AUS DEMSELBEN Lauf - in zwei Punkten:
# * svg.hashsalt: matplotlib wuerfelt sonst die clip-path-IDs je Figure neu.
# * metadata={"Date": None} beim Speichern: sonst steht der Zeitstempel des
# Schreibvorgangs (auf die Mikrosekunde) in den SVG-Metadaten.
# Beides ist unsichtbar, macht aber jeden git-diff unlesbar und die beiden
# Kopien bilder_04/ und OR_HTML_04/bilder_04/ dauerhaft verschieden. Die Datei
# aendert sich jetzt bei jedem Lauf mit neuem Datum oder neuer Uhrzeit.
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
plt.rcParams["svg.hashsalt"] = "or-mit-python-v04"
# Hardcodierte deutsche Monatsnamen statt strftime("%B") - so ist das Datum
# unabhängig von der gesetzten Locale (de_DE.UTF-8) immer korrekt geschrieben,
# auch in CI-/Docker-/Headless-Umgebungen ohne deutsche Locale.
MONATE = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"]
# A4 bei 72 dpi -> 595.44 x 841.68 pt (matplotlib figure-Koordinaten).
BREITE, HOEHE = 595.44, 841.68
# Geometrie des Mittelteils. _design() zeichnet danach, _texte() setzt danach -
# beide lesen aus DIESEN Werten, damit Kasten und Text nie auseinanderlaufen.
# Trennlinie und Kartenoberkante sind gegenueber dem urspruenglichen Entwurf
# (336.7 / 368.7) nach oben gerueckt: Der Untertitel braucht seit der
# Titelaenderung nur noch zwei statt vier Zeilen, und der gewonnene Platz kommt
# der Solver-Karte zugute, die jetzt zwei Absaetze traegt.
TRENNLINIE_Y = 308.0
KARTE_X0, KARTE_BREITE = 64.30752, 466.82496
KARTE_Y0, KARTE_Y1 = 340.0, 481.44096
# Solver-Karten (x0, Breite, Farbe, Label) - bewusst NICHT gleich breit:
# "Google OR-Tools" ist deutlich laenger als die anderen Labels und liefe bei
# einheitlicher Breite ueber die Karte hinaus - da das Label weiss ist, wuerde
# der ueberstehende Teil auf dem weissen Seitenhintergrund unsichtbar. Reihe
# beginnt bei x=71.4528 (linker Rand wie Titel/Untertitel) und endet bei
# x=523.9872 (wie die violette Trennlinie), dazwischen 3 Luecken von je 10pt.
KARTEN_Y0, KARTEN_Y1 = 494.06616, 541.20024
KARTEN = [
(71.4528, 94.0, "#4338ca", "HiGHS"),
(175.4528, 94.0, "#059669", "SciPy"),
(279.4528, 140.5344, "#0891b2", "Google OR-Tools"),
(429.9872, 94.0, "#7c3aed", "CVXPY"),
]
def datum_string() -> str:
"""Heutiges Datum+Version als '5. September 2026 v11.55' (locale-unabhängig).
Die Versionsnummer v< Stunde>.<Minute> (24er Format, zweistellig) macht
jeden Übersetzungslauf unterscheidbar nicht nur einen pro Tag.
"""
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
n = datetime.now()
return f"{n.day}. {MONATE[n.month - 1]} {n.year} v{n.hour:02d}.{n.minute:02d}"
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
def _ring(ax, x, y, r):
ax.add_patch(Circle((x, y), r, fill=False, ec="#4338ca", alpha=0.1,
lw=1.4, clip_on=True))
def _design(ax):
# Weißer Hintergrund.
ax.add_patch(Rectangle((0, 0), BREITE, HOEHE, facecolor="white",
edgecolor="none", zorder=0))
# 5 farbige Akzentbalken oben (Leiste), y 34.51..46.29, je 90.51 breit.
balken_farben = ["#4338ca", "#0891b2", "#7c3aed", "#059669", "#d97706"]
for i, farbe in enumerate(balken_farben):
ax.add_patch(Rectangle((71.4528 + i * 90.50688, 34.50888),
90.50688, 11.78352, facecolor=farbe,
edgecolor="none", zorder=1))
# 7 konzentrische Ringe oben rechts, Zentrum (553.76, 84.17).
for r in (42.084, 77.154, 112.224, 147.294, 182.364, 217.434, 252.504):
_ring(ax, 553.7592, 84.168, r)
# Große Solver-Karte (abgerundet): (64.31, 368.66) bis (531.13, 481.44),
# Radius ~11.91 (Q-Offset in der Original-SVG).
ax.add_patch(FancyBboxPatch(
(KARTE_X0, KARTE_Y0), KARTE_BREITE, KARTE_Y1 - KARTE_Y0,
boxstyle="round,pad=0,rounding_size=11.9098",
facecolor="#f5f3ff", edgecolor="#7c3aed", lw=1.6, zorder=1))
# Vier farbige Solver-Karten (HiGHS, SciPy, Google OR-Tools, CVXPY),
# individuelle Breiten (siehe KARTEN), y 494.07..541.20.
for x0, breite, farbe, _label in KARTEN:
ax.add_patch(FancyBboxPatch(
(x0, KARTEN_Y0), breite, KARTEN_Y1 - KARTEN_Y0,
boxstyle="round,pad=0,rounding_size=11.9098",
facecolor=farbe, edgecolor="none", zorder=2))
# Violette Trennlinie unter dem Untertitel, x 71.45..523.99.
ax.plot([71.4528, 523.9872], [TRENNLINIE_Y, TRENNLINIE_Y], color="#7c3aed",
lw=2.5, solid_capstyle="butt", zorder=3)
# Indigo Fußband unten: (0, 757.51) bis (595.44, 841.68).
ax.add_patch(Rectangle((0, 757.512), BREITE, HOEHE - 757.512,
facecolor="#4338ca", edgecolor="none", zorder=1))
# Grüne 'Optimierungskurve' (konkav, abflachend) von (71.45, 673.34) nach
# (523.99, 639.68) - approximiert die dekorative Kurve der Vorgängerversion
# (dort 99 geglättete Stützpunkte, hier eine konkave Wurzelfunktion, die
# denselben visuellen Eindruck erzeugt).
import numpy as np
xs = np.linspace(71.4528, 523.9872, 100)
t = (xs - 71.4528) / (523.9872 - 71.4528)
ys = 673.344 - 33.664 * np.sqrt(t) # konkav: steiler Start, flaches Ende
ax.plot(xs, ys, color="#059669", lw=3, solid_capstyle="butt", zorder=3)
# Roter Stern-Marker ('Optimum') bei (446.28, 641.67) auf der Kurve.
ax.plot([446.279273], [641.670502], marker="*", color="#e11d48",
markersize=12, mec="#e11d48", mfc="#e11d48", zorder=4)
# --- Textumbruch: gemessen statt geschaetzt -------------------------------
# Bis zur Titelaenderung standen alle Textzeilen mit von Hand vermessenen
# Koordinaten hier - uebernommen aus der urspruenglichen, handgezeichneten SVG.
# Das traegt genau so lange, wie sich der Text nicht aendert: Der neue Titel ist
# knapp dreimal so lang wie der alte, der Untertitel mehr als doppelt so lang.
# Deshalb werden Umbruch und Schriftgroesse jetzt GEMESSEN. Wer den Titel das
# naechste Mal aendert, aendert nur noch die Zeichenketten weiter unten.
def _textbreite(ax, text, groesse, fett=False) -> float:
"""Breite eines Textes in Seitenpunkten - ueber den echten Renderer."""
marke = ax.text(0, 0, text, fontsize=groesse,
fontweight="bold" if fett else "normal")
kasten = marke.get_window_extent(
renderer=ax.figure.canvas.get_renderer()).transformed(
ax.transData.inverted())
marke.remove()
return abs(kasten.width)
def _umbrechen(ax, text, groesse, maxbreite, fett=False) -> list[str]:
"""Bricht 'text' so um, dass keine Zeile breiter als 'maxbreite' wird."""
zeilen, zeile = [], ""
for wort in text.split():
probe = f"{zeile} {wort}".strip()
if not zeile or _textbreite(ax, probe, groesse, fett) <= maxbreite:
zeile = probe
else:
zeilen.append(zeile)
zeile = wort
if zeile:
zeilen.append(zeile)
return zeilen
def _setze_block(ax, zeilen, x, y0, groesse, durchschuss, **stil) -> float:
"""Setzt die Zeilen ab Grundlinie y0 und liefert die Grundlinie danach."""
for i, zeile in enumerate(zeilen):
ax.text(x, y0 + i * groesse * durchschuss, zeile,
fontsize=groesse, zorder=5, **stil)
return y0 + (len(zeilen) - 1) * groesse * durchschuss
# --- Die Texte der Titelseite ----------------------------------------------
# Nur diese Zeichenketten aendern, wenn das Buch anders heissen soll. Der Text
NEUER_TITEL.md zu den Quellen, Kritik-Verzeichnis ganz heraus Der erste Anlauf schloss Kritik_und_Verbesserungsvorschlaege/ mit einer Gegenausnahme aus, weil NEUER_TITEL.md darin lag und der Bau ohne diese Datei abbricht: Kritik_und_Verbesserungsvorschlaege/* !Kritik_und_Verbesserungsvorschlaege/NEUER_TITEL.md Das funktionierte, war aber die schlechtere Loesung: Eine Bauquelle blieb in einem Verzeichnis liegen, das aus dem Repository verschwinden soll. Jetzt steht sie als Operations_Research_mit_Python_Version_04/NEUER_TITEL.md bei den uebrigen Quellen, und .gitignore braucht nur noch eine Zeile. Eine Stelle im Build musste mit: pruefe_dateien() meldet jede .md im Quellverzeichnis, die weder in DATEIEN steht noch mit README beginnt, als "nicht eingebundene Datei". Die bis dahin leere Menge KEINE_KAPITELDATEIEN fuehrt jetzt NEUER_TITEL.md - eine Quelle, aber keine Kapiteldatei. Der Nachweis, dass der Umzug nichts veraendert hat, brauchte einen Umweg: Die Pruefsumme von titelseite.svg aenderte sich - aber nur, weil das Fussband das Tagesdatum traegt und inzwischen der 9. September war. Mit dem Datum des Vortags erzeugt, ist die Datei byte-identisch zum committeten Stand (1b076697...). Dass der Text unveraendert ist, zeigt schon der Strukturvergleich: 721 Glyphenverweise und 81 Pfaddefinitionen in beiden Fassungen. Gegengeprueft mit einem simulierten Klon (git checkout-index in ein leeres Verzeichnis): Das Kritik-Verzeichnis fehlt dort vollstaendig, NEUER_TITEL.md liegt im Quellverzeichnis, und --check liefert dieselben Zahlen wie lokal. Was das NICHT leistet: Die fuenf Dateien verschwinden aus kuenftigen Klons, nicht aus der Historie. Wer einen alten Commit auscheckt, bekommt sie weiterhin. Veroeffentlicht: 5 Dateien uebertragen, PDF unveraendert 781 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 00:31:16 +02:00
# stammt aus Operations_Research_mit_Python_Version_04/NEUER_TITEL.md; die beiden
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
# Absaetze KOMPENDIUM und ANWENDUNGEN
# stehen gemeinsam in der grossen Solver-Karte.
#
Titeltext: eine Quelle fuer PDF-Titelseite und Startseite Die Startseite zeigte einen anderen Titeltext als das PDF. Der Text stand VIERMAL im Repository: NEUER_TITEL.md, die Konstanten in bilder_04/erzeuge_titelseite.py, der Titelblock des Vorworts - und noch einmal fest einprogrammiert im Hero-Block von baue_landingpage(). Die Ursache ist messbar: Die drei Commits, die den Titel ueberarbeitet haben (ca5621f, 199dd3b, 1659fb9 im Ursprungs-Repository), fassten jedes Mal dieselben drei Dateien an. build_version_04.py war in keinem davon, und OR_HTML_04/index.html taucht in ihren Dateilisten gar nicht auf. Online stand deshalb: Unterzeile "Von den mathematischen Grundlagen zur quantitativen Portfolio-Optimierung an den Finanzmaerkten." Kompendium "Ein vollstaendiges Kompendium mit Algorithmen ..." Anwendungen fehlte ganz Behandlung wie bei den neun Nummern-Funden zuvor: nicht die Kopie korrigieren, sondern die Kopie abschaffen. Das neue titeltexte_04.py zerlegt NEUER_TITEL.md in Titel, Untertitel, Kompendium- und Anwendungen-Absatz; PDF-Titelseite und Startseite lesen dieselbe Funktion. Der Autor bleibt bewusst Konstante im Generator - die Vorlage beschreibt das Buch, nicht sein Impressum. Der entscheidende Beleg: Die neu erzeugte titelseite.svg ist BYTE-IDENTISCH (97fb40d7...) zur vorherigen - der Umbau hat am PDF nachweislich nichts veraendert. Auch nicht am Umbruch: Die frueher handgesetzten drei Zeilen entstehen jetzt automatisch aus _umbrechen(), und zwar exakt so. Ein Textvergleich ueber das SVG war uebrigens nicht moeglich - es enthaelt kein einziges <text>-Element, matplotlib zeichnet alle 726 Buchstaben als Kurven. Das Vorwort bleibt von Hand gepflegter Buchtext, wird aber von pruefe_titeltexte() bei jedem --check gegen die Quelle gestellt. Gleich beim ersten Lauf fand es einen echten Unterschied: Das Vorwort setzte einen Geviertstrich, die Vorlage einen Halbgeviertstrich. Im Deutschen ist der Halbgeviertstrich der richtige - das Vorwort folgt jetzt der Quelle. Veroeffentlicht: 5 Dateien uebertragen, alle vierzehn HTTP-Proben richtig. Gegenprobe vom Server: alle drei Absaetze der Startseite sind zeichengenau identisch mit NEUER_TITEL.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:39:49 +02:00
# Sie stehen NICHT hier, sondern in NEUER_TITEL.md - derselben Datei, aus der
# auch die Startseite der Website ihren Hero-Block speist. Vorher trug dieses
# Skript eigene Konstanten, und die Startseite noch einmal eigene; drei
# Titelueberarbeitungen zogen diese hier nach und liessen jene stehen.
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
#
Titeltext: eine Quelle fuer PDF-Titelseite und Startseite Die Startseite zeigte einen anderen Titeltext als das PDF. Der Text stand VIERMAL im Repository: NEUER_TITEL.md, die Konstanten in bilder_04/erzeuge_titelseite.py, der Titelblock des Vorworts - und noch einmal fest einprogrammiert im Hero-Block von baue_landingpage(). Die Ursache ist messbar: Die drei Commits, die den Titel ueberarbeitet haben (ca5621f, 199dd3b, 1659fb9 im Ursprungs-Repository), fassten jedes Mal dieselben drei Dateien an. build_version_04.py war in keinem davon, und OR_HTML_04/index.html taucht in ihren Dateilisten gar nicht auf. Online stand deshalb: Unterzeile "Von den mathematischen Grundlagen zur quantitativen Portfolio-Optimierung an den Finanzmaerkten." Kompendium "Ein vollstaendiges Kompendium mit Algorithmen ..." Anwendungen fehlte ganz Behandlung wie bei den neun Nummern-Funden zuvor: nicht die Kopie korrigieren, sondern die Kopie abschaffen. Das neue titeltexte_04.py zerlegt NEUER_TITEL.md in Titel, Untertitel, Kompendium- und Anwendungen-Absatz; PDF-Titelseite und Startseite lesen dieselbe Funktion. Der Autor bleibt bewusst Konstante im Generator - die Vorlage beschreibt das Buch, nicht sein Impressum. Der entscheidende Beleg: Die neu erzeugte titelseite.svg ist BYTE-IDENTISCH (97fb40d7...) zur vorherigen - der Umbau hat am PDF nachweislich nichts veraendert. Auch nicht am Umbruch: Die frueher handgesetzten drei Zeilen entstehen jetzt automatisch aus _umbrechen(), und zwar exakt so. Ein Textvergleich ueber das SVG war uebrigens nicht moeglich - es enthaelt kein einziges <text>-Element, matplotlib zeichnet alle 726 Buchstaben als Kurven. Das Vorwort bleibt von Hand gepflegter Buchtext, wird aber von pruefe_titeltexte() bei jedem --check gegen die Quelle gestellt. Gleich beim ersten Lauf fand es einen echten Unterschied: Das Vorwort setzte einen Geviertstrich, die Vorlage einen Halbgeviertstrich. Im Deutschen ist der Halbgeviertstrich der richtige - das Vorwort folgt jetzt der Quelle. Veroeffentlicht: 5 Dateien uebertragen, alle vierzehn HTTP-Proben richtig. Gegenprobe vom Server: alle drei Absaetze der Startseite sind zeichengenau identisch mit NEUER_TITEL.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:39:49 +02:00
# Der TITEL kommt einzeilig an und wird von _umbrechen() auf den Satzspiegel
# verteilt. Das ergibt dieselben drei Zeilen wie die frueher handgesetzten
# ("Optimierte" / "Entscheidungsfindung" / "mit Python") - und das ist kein
# Zufall: "Entscheidungsfindung" ist bei 34 pt schon 429 pt breit und damit
# fast so breit wie der ganze Satzspiegel (453 pt). Jede zweizeilige Aufteilung
# braeuchte mindestens 649 pt und passt bei keiner lesbaren Schriftgroesse.
_TEXTE = lies_titeltexte()
TITEL = _TEXTE["titel"]
UNTERTITEL = _TEXTE["untertitel"]
KOMPENDIUM = _TEXTE["kompendium"]
ANWENDUNGEN = _TEXTE["anwendungen"]
# Der Autor steht nicht in der Titelvorlage - sie beschreibt das Buch, nicht
# sein Impressum.
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
AUTOR = "Autor / Herausgeber: Dieter Schlüter"
# Satzspiegel: linker Rand und rechte Kante wie Trennlinie und Kartenreihe.
LINKS, RECHTS = 71.4528, 523.9872
SATZBREITE = RECHTS - LINKS
# Schriftgroessen und Durchschuss (Faktor auf die Schriftgroesse). Nachgemessen,
# nicht geraten: Bei 34 pt ist "Entscheidungsfindung" 429 pt breit und bleibt
# damit im Satzspiegel von 453 pt; bei 36 pt waeren es 454 pt - eines zu viel.
TITEL_GROESSE, TITEL_DURCHSCHUSS = 34, 1.40
# Untertitel 14 statt 15 pt: Bei 15 pt braucht er drei Zeilen, und die dritte
# traegt nur noch das Wort "Finanzmärkte" - eine Hurenkind-Zeile mitten auf dem
# Titelblatt. Bei 14 pt sind es zwei ausgeglichene Zeilen.
UNTER_GROESSE, UNTER_DURCHSCHUSS = 14, 1.23
# Kartentext 11.5 statt 12 pt: Der Text ist auf neun Zeilen gewachsen; bei 12 pt
# blieben nur noch 9 pt Luft ueber und unter dem Block, und der Text geriete in
# die abgerundeten Kartenecken (Radius 11.9 pt).
KOMP_GROESSE, KOMP_DURCHSCHUSS = 11.5, 1.27
KOMP_BREITE = 440.0 # nutzbare Textbreite in der Karte
# Anker des Satzes.
TITEL_START = 135.0 # erste Grundlinie des Haupttitels
ABSTAND_TITEL_UNTER = 42.0 # Luft zwischen Titel- und Untertitelblock
def _texte(ax, datum):
# Haupttitel: die letzte Zeile ("mit Python") in Anthrazit statt Indigo -
# so bleibt die Zweifarbigkeit des urspruenglichen Entwurfs erhalten, in
# dem "Operations Research" indigo und "mit Python" dunkel gesetzt war.
# Ein \n im TITEL heisst: hier wird umbrochen. Sonst automatisch.
if "\n" in TITEL:
titelzeilen = TITEL.split("\n")
zu_breit = [z for z in titelzeilen
if _textbreite(ax, z, TITEL_GROESSE, True) > SATZBREITE]
if zu_breit:
raise SystemExit(
f"Titelblatt: Diese Titelzeile passt bei {TITEL_GROESSE} pt "
f"nicht in den Satzspiegel ({SATZBREITE:.0f} pt): {zu_breit[0]!r}")
else:
titelzeilen = _umbrechen(ax, TITEL, TITEL_GROESSE, SATZBREITE, fett=True)
for i, zeile in enumerate(titelzeilen):
ax.text(LINKS, TITEL_START + i * TITEL_GROESSE * TITEL_DURCHSCHUSS,
zeile, fontsize=TITEL_GROESSE, fontweight="bold", zorder=5,
color="#1a1a2e" if i == len(titelzeilen) - 1 else "#4338ca")
titel_ende = TITEL_START + (len(titelzeilen) - 1) * TITEL_GROESSE * TITEL_DURCHSCHUSS
# Untertitel direkt darunter; die Zeilenzahl ergibt sich aus dem Umbruch.
unterzeilen = _umbrechen(ax, UNTERTITEL, UNTER_GROESSE, SATZBREITE)
unter_ende = _setze_block(ax, unterzeilen, LINKS,
titel_ende + ABSTAND_TITEL_UNTER,
UNTER_GROESSE, UNTER_DURCHSCHUSS, color="#334155")
# Sicherung: Der Untertitel darf die Trennlinie nicht beruehren. Lieber ein
# Abbruch beim Erzeugen als ein Titelblatt, auf dem Text auf der Linie liegt.
if unter_ende > TRENNLINIE_Y - 8:
raise SystemExit(
f"Titelblatt: Der Untertitel reicht bis y={unter_ende:.1f} und "
f"kollidiert mit der Trennlinie bei y={TRENNLINIE_Y}. "
f"Schriftgroesse oder Startpunkt anpassen.")
# KOMPENDIUM-Label
ax.text(LINKS, 84.168, "K O M P E N D I U M",
fontsize=13, color="#0891b2", zorder=5)
# Die beiden Absaetze in der grossen Solver-Karte, durch eine Leerzeile
# getrennt und als Block vertikal zentriert. Zentriert gesetzt statt links,
# weil die Karte breiter ist als der Satzspiegel.
kartenmitte_x = KARTE_X0 + KARTE_BREITE / 2
kartenzeilen = (_umbrechen(ax, KOMPENDIUM, KOMP_GROESSE, KOMP_BREITE)
+ [""]
+ _umbrechen(ax, ANWENDUNGEN, KOMP_GROESSE, KOMP_BREITE))
hoehe = (len(kartenzeilen) - 1) * KOMP_GROESSE * KOMP_DURCHSCHUSS
start = (KARTE_Y0 + KARTE_Y1) / 2 - hoehe / 2 + KOMP_GROESSE * 0.35
# Sicherung: Der Block darf nicht ueber den Kartenrand hinauslaufen.
if start - KOMP_GROESSE < KARTE_Y0 or start + hoehe > KARTE_Y1 - 4:
raise SystemExit(
f"Titelblatt: Der Kartentext ({len(kartenzeilen)} Zeilen) passt "
f"nicht in die Karte ({KARTE_Y1 - KARTE_Y0:.0f} pt hoch). "
f"Schriftgroesse verkleinern oder Karte vergroessern.")
_setze_block(ax, kartenzeilen, kartenmitte_x, start,
KOMP_GROESSE, KOMP_DURCHSCHUSS, color="#1a1a2e", ha="center")
# Autor (im Fußband, weiß)
ax.text(LINKS, 793.402715, AUTOR,
fontsize=14, color="#ffffff", zorder=5)
# text_7: Stand-Datum (im Fußband, hellviolett) - DYNAMISCH
ax.text(71.4528, 818.05749, f"Stand: {datum}",
fontsize=12, color="#c7d2fe", zorder=5)
# Solver-Namen, zentriert in ihrer Karte (weiß). ha/va="center"
# macht die Zentrierung unabhängig von Schriftmetriken - robuster als
# von Hand vermessene x/y-Offsets.
mitte_y = (KARTEN_Y0 + KARTEN_Y1) / 2
for x0, breite, _farbe, label in KARTEN:
ax.text(x0 + breite / 2, mitte_y, label,
fontsize=13, color="#ffffff", ha="center", va="center",
zorder=5)
def main() -> int:
basis = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ausgaben = [
os.path.join(basis, "bilder_04", "titelseite.svg"),
os.path.join(basis, "OR_HTML_04", "bilder_04", "titelseite.svg"),
]
d = datum_string()
fig = plt.figure(figsize=(8.27, 11.69))
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, BREITE)
ax.set_ylim(HOEHE, 0) # invertiert: y zählt von oben (wie SVG)
ax.axis("off")
_design(ax)
_texte(ax, d)
for pfad in ausgaben:
os.makedirs(os.path.dirname(pfad), exist_ok=True)
fig.savefig(pfad, format="svg", metadata={"Date": None})
plt.close(fig)
print(f"Titelseite erzeugt ({d}): {', '.join(ausgaben)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())