operations_research/pandoc/emoji_fallback.lua

91 lines
3.6 KiB
Lua
Raw Normal View History

-- 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{<Zeichen>}}{<Zeichen>}}
-- 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