diff --git a/CLAUDE.md b/CLAUDE.md index e8a90eb..2745926 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,6 +29,11 @@ bilder_04/ Quelle: Diagramme + erzeuge_*.py- web_04/ Quelle: site.css, site.js, icons.svg, plotly.min.js, katex/ - die statischen Bestandteile der Website +pandoc/, pandoc-defaults-basis.yaml, pandoc-defaults-buch.yaml PDF-Konfiguration + (pandoc/emoji_fallback.lua schaltet Zeichen, + die Times New Roman fehlt, auf Symbola um - + siehe PROGRESS.md, "Sonderzeichen in der + PDF-Fassung") Operations_Research_mit_Python_Version_04.md generiert: Gesamtdokument Operations_Research_mit_Python_Version_04.pdf generiert: PDF (xelatex) OR_HTML_04/ generiert: Mehrseiten-Website diff --git a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf index 4f10097..9556d9b 100644 Binary files a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf and b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf differ diff --git a/Operations_Research_mit_Python_Version_04.pdf b/Operations_Research_mit_Python_Version_04.pdf index 4f10097..9556d9b 100644 Binary files a/Operations_Research_mit_Python_Version_04.pdf and b/Operations_Research_mit_Python_Version_04.pdf differ diff --git a/Operations_Research_mit_Python_Version_04/build_version_04.py b/Operations_Research_mit_Python_Version_04/build_version_04.py index 34b9854..d8eacd9 100644 --- a/Operations_Research_mit_Python_Version_04/build_version_04.py +++ b/Operations_Research_mit_Python_Version_04/build_version_04.py @@ -1194,6 +1194,21 @@ def baue_pdf() -> None: print(f"PDF wurde nicht erzeugt - LaTeX-Protokoll in " f"{BUILD_BASISNAME}.log pruefen.") + # Bewachung des Sonderzeichen-Fallbacks: xelatex verschweigt fehlende + # Glyphen im Druckbild (nur "Missing character" im Protokoll). Der Filter + # pandoc/emoji_fallback.lua deckt die bekannten Zeichen ab; taucht hier + # eine Zahl auf, kennt Times New Roman ein Zeichen, das der Filter noch + # nicht umschaltet - Zeichenliste im Filter ergaenzen. + protokoll = os.path.join(BASIS, BUILD_BASISNAME + ".log") + if os.path.exists(protokoll): + with open(protokoll, encoding="utf-8", errors="replace") as datei: + fehlend = sum(1 for zeile in datei if "Missing character" in zeile) + if fehlend: + print(f" WARNUNG: {fehlend} fehlende Zeichen im LaTeX-Protokoll " + f"(pandoc/emoji_fallback.lua erweitern)") + else: + print(" Kein fehlendes Zeichen - Font-Fallback deckt alles ab.") + for endung in (".tex", ".aux", ".log", ".out", ".toc", ".idx", ".ind", ".ilg"): pfad = os.path.join(BASIS, BUILD_BASISNAME + endung) if os.path.exists(pfad) and endung != ".log": diff --git a/PROGRESS.md b/PROGRESS.md index a98eff4..33f8662 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2592,6 +2592,45 @@ eine **doppelte Nummer** — „Abbildung 21: Abb. 7.2: …". Die erste vergibt `nummeriere_marken()`. Das betrifft alle 48 Abbildungen und besteht seit Langem; behoben wäre es dadurch, dass die LaTeX-Nummerierung der Gleitumgebung unterdrückt wird. +### Sonderzeichen in der PDF-Fassung — ein stiller Font-Fehler behoben ✅ + +In der gesamten PDF-Fassung fehlten alle Zeichen, die Times New Roman nicht mitbringt: die +Lernelemente-Symbole (⭐ ✏ ❓ 🎯 🚀 …), aber auch die Sub-/Superskripte (₀ ₁ ₂ ₜ ₙ ⁶ ᵀ) und die +langen Pfeile (⟹ ⟶ ⟵ ⇒). Das alte LaTeX-Protokoll enthielt **rund 2 070** +`Missing character`-Meldungen; die Zeichen fielen stillschweigend aus dem Druckbild. +Zwei Ursachen, beide lange unentdeckt, weil die Website die Zeichen korrekt zeigt +(der Browser weicht selbst auf eine Emoji-Schrift aus): + +* xelatex hat keine automatische Font-Fallback-Kette — fehlt einem Zeichen der Glyph, wird + es weggelassen, ohne dass der Bau fehlschlägt. +* Die pandoc-Variable `emoji-font: "Noto Color Emoji"` wertet der LaTeX-Writer + (pandoc 3.1.3) **nicht aus** — sie hat seit ihrer Einführung still nichts getan. + +**Die Lösung ist zweistufig**, weil die Zeichen auf zwei Wegen in den PDF gelangen: + +1. **`pandoc/emoji_fallback.lua`** (neu) — ein pandoc-Filter, der die betreffenden Zeichen + im Fließtext in `{\texorpdfstring{\symfont{…}}{…}}` einschüttet. `\symfont` ist + **Symbola** — die einzige Schrift auf dem Bausystem, die das komplette Set abdeckt + (Noto Color Emoji funktioniert unter XeTeX nicht: die Zeichen landen im `nullfont`); + monochrom, für den Drucksatz ohnehin passend. Der `\texorpdfstring`-Antil hält die + PDF-Lesezeichen ruhig und erhalten. Variationsselektoren (U+FE0F) werden entfernt. + Zwei pandoc-3.1.3-Eigenheiten unterwegs gefunden: Filterhandler funktionieren **nur als + globale Funktionen** — ein vom Filter zurückgegebenes Table wird stillschweigend + ignoriert; und der Einschub muss `RawInline` sein, als `Str` würde pandoc die + Backslashes escapen. +2. **`ucharclasses`** in `pandoc/header-includes.tex` — für den **Diagrammtext**: Das Paket + `svg` exportiert die SVG-Texte (x₁, σᵀ, ∈ in Achsen- und Knotenbeschriftungen) als + LaTeX-Text, dorthin reicht der AST-Filter nicht. Die Zurück-Umschaltung ist bewusst + `\fontfamily{\familydefault}\selectfont` statt `\normalfont` — `\normalfont` würde den + Fettdruck hinter dem Stern einer Aufgabentitel-Zeile zerstören (gemessen mit + pdfminer: „Titel“ bleibt Bold). + +Die tote `emoji-font`-Variable ist aus `pandoc-defaults-basis.yaml` entfernt (mit +Begründung), und `baue_pdf()` zählt die `Missing character`-Meldungen im Protokoll: +Der neue Lauf meldet **null fehlende Zeichen**; taucht die Warnung wieder auf, kennt Times +New Roman ein Zeichen, das der Filter noch nicht umschaltet. Symbola ist damit eingebettet, +der Buchumfang bleibt bei 792 Seiten; alle HTML-Seiten sind byteidentisch geblieben. + --- ## 8. Commit-Historie des V04-Strangs diff --git a/pandoc-defaults-basis.yaml b/pandoc-defaults-basis.yaml index 62c16aa..8ef38d7 100644 --- a/pandoc-defaults-basis.yaml +++ b/pandoc-defaults-basis.yaml @@ -39,6 +39,15 @@ metadata: include-in-header: - pandoc/header-includes.tex +# Sonderzeichen, die Times New Roman fehlen (Emoji, Lernelemente-Symbole, +# Sub-/Superskripte, lange Pfeile), auf \symfont (= Symbola, definiert in +# pandoc/header-includes.tex) umschalten. Ohne diesen Filter liess xelatex +# alle betreffenden Zeichen in der PDF-Fassung stillschweigend weg — +# die Website zeigte sie, das PDF nicht. Nur für den PDF-Bau; der HTML-Bau +# benutzt diese Defaults nicht. +filters: + - pandoc/emoji_fallback.lua + # -------------------------------- # Fonts & Layout Variables # -------------------------------- @@ -48,7 +57,9 @@ variables: monofont: "DejaVu Sans Mono" # sansfont: "DejaVu Sans" sansfont: "Arial" # Sans-Serif - emoji-font: "Noto Color Emoji" + # "emoji-font" hier bewusst NICHT gesetzt: Die Variable wertet der + # LaTeX-Writer (pandoc 3.1.3) nicht aus, sie hat jahrelang still nichts + # getan. Die Emoji-Abdeckung läuft über pandoc/emoji_fallback.lua. mathfont: "STIX Two Math" monofontoptions: "Scale=MatchLowercase" papersize: a4 diff --git a/pandoc/emoji_fallback.lua b/pandoc/emoji_fallback.lua new file mode 100644 index 0000000..5c43cf1 --- /dev/null +++ b/pandoc/emoji_fallback.lua @@ -0,0 +1,91 @@ +-- emoji_fallback.lua +-- Pandoc-Filter für den PDF-Bau (nur `-t latex`, nur PDF, nicht für die Website). +-- +-- Problem: Der Haupttext steht in Times New Roman — diese Schrift kennt weder +-- Emoji noch die Darstellungssymbole der Lernelemente (⭐ ✏ ❓ 🧠 …) und auch +-- keine hoch-/tiefgestellten Ziffern. XeLaTeX besitzt keine automatische +-- Font-Fallback-Kette: Fehlt einem Zeichen der Glyph, wird es stillschweigend +-- weggelassen ("Missing character" nur im Protokoll). Die pandoc-Variable +-- "emoji-font" wird vom LaTeX-Writer (pandoc 3.1.3) nicht ausgewertet — die +-- Zeichen fehlten daher in der gesamten PDF-Fassung, während die Website sie +-- über den Browser-Fontstack korrekt zeigte. +-- +-- Lösung: Dieser Filter wickelt alle betreffenden Zeichen in +-- {\texorpdfstring{\symfont{}}{}} +-- ein. \symfont ist in pandoc/header-includes.tex auf Symbola gesetzt — die +-- einzige auf dem Bausystem vorhandene Schrift, die das komplette Set abdeckt +-- (Noto Color Emoji funktioniert unter XeTeX nicht, die Zeichen landen im +-- nullfont). Symbola ist monochrom — für den Drucksatz ohnehin passend. +-- +-- Der \texorpdfstring-Anteil hält hyperref ruhig: In PDF-Lesezeichen und im +-- Inhaltsverzeichnis steht das schlichte Zeichen statt des Fontwechsels. +-- +-- Codeblöcke werden nicht angetastet (Code/CodeBlock sind eigene +-- AST-Elemente): Der Mono-Font DejaVu Sans Mono deckt die dort vorkommenden +-- Symbole (✓ ✗ ★, Sub-/Superskripte) selbst ab. + +local function braucht_symfont(cp) + return (cp >= 0x1F000 and cp <= 0x1FAFF) -- Emoji-Blöcke (🎯 📌 🚀 …) + or (cp >= 0x2600 and cp <= 0x27BF) -- Misc Symbols + Dingbats (★ ⚠ ✅ ✏ ✓ ✗ ❌ ❓) + or (cp >= 0x2B00 and cp <= 0x2BFF) -- Misc Symbols and Arrows (⭐) + or (cp >= 0x27F5 and cp <= 0x27FF) -- Supplemental Arrows-A (⟵ ⟶ ⟹) + or (cp >= 0x2070 and cp <= 0x209F) -- hoch-/tiefgestellte Ziffern (⁶ ₀ ₁ ₂ ₜ ₙ) + or cp == 0x21D2 -- ⇒ + or cp == 0x1D40 -- ᵀ (Modifier Letter Capital T) +end + +local function ersetze(text) + local teile = {} + local normal = {} + local symbole = nil -- Puffer für aufeinanderfolgende Fallback-Zeichen + + local function flush_normal() + if #normal > 0 then + teile[#teile + 1] = pandoc.Str(table.concat(normal)) + normal = {} + end + end + + local function flush_symbole() + if symbole and #symbole > 0 then + local zeichen = table.concat(symbole) + teile[#teile + 1] = pandoc.RawInline("latex", + "{\\texorpdfstring{\\symfont{" .. zeichen .. "}}{" + .. zeichen .. "}}") + symbole = nil + end + end + + for _, cp in utf8.codes(text) do + if cp == 0xFE0F then + -- Variationsselektor (Emoji- vs. Textdarstellung): für das PDF ohne + -- Bedeutung, Symbola enthält keinen sichtbaren Glyphen — weglassen. + elseif braucht_symfont(cp) then + flush_normal() + symbole = symbole or {} + symbole[#symbole + 1] = utf8.char(cp) + else + flush_symbole() + normal[#normal + 1] = utf8.char(cp) + end + end + flush_normal() + flush_symbole() + + return teile +end + +-- pandoc 3.1.3 ruft Filterhandler ausschließlich als globale Funktionen auf; +-- ein zurückgegebenes Handler-Table würde stillschweigend ignoriert. +function Str(el) + -- Schneller Ausstieg: nur anfassen, wenn überhaupt Multibyte drin ist. + if not el.text:find("[\194-\244]") then + return nil + end + local ok, teile = pcall(ersetze, el.text) + if not ok then + -- Kaputtes UTF-8 o. ä.: Element unverändert durchreichen. + return nil + end + return teile +end diff --git a/pandoc/header-includes.tex b/pandoc/header-includes.tex index 51a4db6..4ea5273 100644 --- a/pandoc/header-includes.tex +++ b/pandoc/header-includes.tex @@ -2,5 +2,41 @@ \setmonofont{DejaVu Sans Mono}[ Scale=MatchLowercase ] + +% --- Symbol-/Emoji-Fallback ------------------------------------------------ +% Times New Roman (Haupttext) kennt weder Emoji noch die Symbole der +% Lernelemente und keine hoch-/tiefgestellten Ziffern. XeLaTeX hat keine +% automatische Fallback-Kette — fehlende Zeichen fielen stillschweigend weg. +% Der Filter pandoc/emoji_fallback.lua schaltet die betreffenden Zeichen +% deshalb auf \symfont um. Symbola ist die einzige Schrift auf dem +% Bausystem, die das komplette Set abdeckt; Noto Color Emoji funktioniert +% unter XeTeX nicht (die Zeichen landen im nullfont). Monochrom — für den +% Drucksatz ohnehin passend. +\newfontfamily\symfont{Symbola} + +% --- Automatische Umschaltung für den Diagrammtext -------------------------- +% Die SVG-Diagramme werden vom Paket "svg" mit Inkscape nach LaTeX +% exportiert; deren Texte (Achsenbeschriftungen, Knotenlabels wie x₁, σᵀ) +% setzt LaTeX selbst — da erreicht der pandoc-Filter nicht mehr hin. Für +% diese Ebene übernimmt ucharclasses die Umschaltung auf \symfont je +% Unicode-Block. Die Zurück-Umschaltung ist bewusst +% \fontfamily{\familydefault}\selectfont und NICHT \normalfont: +% \normalfont würde auch Fettdruck und Kursiv zurücksetzen — "Aufgabe ⭐ — +% Titel" wäre nach dem Stern nur noch mager. Im Fließtext schaltet der +% pandoc-Filter bereits in geschlossenen Gruppen um; ucharclasses greift +% dort nur innerhalb dieser Gruppen und bleibt unsichtbar. +\usepackage[quiet]{ucharclasses} +\setTransitionsFor{SuperscriptsAndSubscripts}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{PhoneticExtensions}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{MathematicalOperators}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{Arrows}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{SupplementalArrowsA}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{Dingbats}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{MiscellaneousSymbols}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{MiscellaneousSymbolsAndArrows}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{MiscellaneousSymbolsAndPictographs}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{Emoticons}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{TransportAndMapSymbols}{\symfont}{\fontfamily{\familydefault}\selectfont} +\setTransitionsFor{SupplementalSymbolsAndPictographs}{\symfont}{\fontfamily{\familydefault}\selectfont} \usepackage{microtype} \usepackage{pifont}