fix: Kasten-CSS des Pandoc-Filters wirkt auch mit -H und in EPUB-Kapiteln

- CSS in pandoc/callouts.css (eine Quelle), vom Filter neben sich gelesen
- HTML: <style> am Dokumentanfang statt header-includes, das ein -H auf der
  Kommandozeile ersetzt hat (PDF über weasyprint mit Druck-CSS: Kästen ohne Rahmen)
- EPUB: Kasten-CSS als Stylesheet aller Kapitel (zusammen mit Pandocs
  Standard-epub.css oder zu eigenen css:-Angaben); vorher nur auf der Titelseite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-10-06 05:55:25 +02:00
commit c761a53728
4 changed files with 79 additions and 21 deletions

View file

@ -10,6 +10,13 @@ Versionsnummern nach [Semantic Versioning](https://semver.org/lang/de/).
- Seitenmarken (`--page-markers`) aus einem Kastentitel stehen jetzt vor dem Kasten statt im fetten Titel,
wie bei Überschriften.
### Behoben
- `pandoc/callouts.lua`: Die Kästen blieben ohne Rahmen, wenn man Pandoc zusätzlich `-H`/`--include-in-header`
übergab, und im EPUB hatten nur die Titelseite, nicht die Kapitel, das Kasten-CSS. Das CSS steht jetzt in
`pandoc/callouts.css`; der Filter setzt es bei HTML an den Anfang des Dokuments und fügt es bei EPUB den
Stylesheets hinzu (zusammen mit Pandocs Standard-CSS bzw. zu eigenen `css:`-Angaben).
### Dokumentation
- `--no-css-emphasis` lässt `<mark>`-Markierungen bestehen; diese steuert `--styles`.

View file

@ -437,7 +437,8 @@ epub2md/
├── models.py Dataclasses (Kapitel, Medien, Noten, Metadaten …)
└── utils.py Slugs, YAML-Emitter, Pfadhelfer
pandoc/
└── callouts.lua Pandoc-Filter: '> [!TIP]'-Kästen → gerahmte Kästen (HTML/EPUB) bzw. Zitatblock mit Titel
├── callouts.lua Pandoc-Filter: '> [!TIP]'-Kästen → gerahmte Kästen (HTML/EPUB) bzw. Zitatblock mit Titel
└── callouts.css Gestaltung der Kästen (vom Filter eingebunden; muss neben callouts.lua liegen)
```
Statt BeautifulSoup wird direkt lxml verwendet: Es ist für die XHTML-Bäume ausreichend, schneller und

8
pandoc/callouts.css Normal file
View file

@ -0,0 +1,8 @@
/* Info boxes written by epub2md (--boxes callout/html), used by pandoc/callouts.lua. */
.callout, .box { border: 1px solid #c8ccd2; border-left: 0.35em solid #4a7bd0; border-radius: 0.3em;
background: #f5f8fc; padding: 0.4em 1em; margin: 1em 0; }
.callout > p:first-child { margin-top: 0.4em; }
.callout-tip { border-left-color: #2e9a4f; background: #f3faf5; }
.callout-important { border-left-color: #8250df; background: #f8f5fe; }
.callout-warning { border-left-color: #c58a00; background: #fffaf0; }
.callout-caution { border-left-color: #cf222e; background: #fff5f5; }

View file

@ -11,7 +11,9 @@ epub2md writes boxes as GitHub alerts / Obsidian callouts:
Pandoc < 3.1.10 does not know this syntax and prints "[!TIP]" literally. This filter turns such
block quotes into real boxes:
* HTML, EPUB: <div class="callout callout-tip"> with a bold label and a small stylesheet
* HTML, EPUB: <div class="callout callout-tip"> with a bold label, styled by callouts.css
(next to this filter; HTML: <style> at the start of the body, so -H cannot drop it;
EPUB: added to the book's stylesheets together with Pandoc's default epub.css)
* other formats (LaTeX/PDF, DOCX, ODT …): block quote with a bold label "Tipp: Title"
It also handles <div class="callout …"> coming back from HTML (md -> html -> pdf route) and styles
@ -26,17 +28,40 @@ local LABELS = {
en = { NOTE = "Note", TIP = "Tip", IMPORTANT = "Important", WARNING = "Warning", CAUTION = "Caution" },
}
local CSS = [[
<style>
.callout, .box { border: 1px solid #c8ccd2; border-left: 0.35em solid #4a7bd0; border-radius: 0.3em;
background: #f5f8fc; padding: 0.4em 1em; margin: 1em 0; }
.callout > p:first-child { margin-top: 0.4em; }
.callout-tip { border-left-color: #2e9a4f; background: #f3faf5; }
.callout-important { border-left-color: #8250df; background: #f8f5fe; }
.callout-warning { border-left-color: #c58a00; background: #fffaf0; }
.callout-caution { border-left-color: #cf222e; background: #fff5f5; }
</style>
]]
-- callouts.css next to this filter is the single source of the box styles.
local function read_file(path)
local fh = io.open(path, "r")
if not fh then
return nil
end
local text = fh:read("a")
fh:close()
return text
end
local CSS_PATH = (PANDOC_SCRIPT_FILE and PANDOC_SCRIPT_FILE:match("^(.*[/\\])") or "") .. "callouts.css"
local CSS = read_file(CSS_PATH)
if not CSS then
io.stderr:write("callouts.lua: " .. CSS_PATH .. " nicht gefunden – Kästen ohne Rahmen\n")
end
-- EPUB: a stylesheet in metadata `css` replaces Pandoc's default epub.css, so combine both in a temp file.
local function epub_stylesheet()
local default = ""
local pipe = io.popen("pandoc --print-default-data-file epub.css 2>/dev/null")
if pipe then
default = pipe:read("a") or ""
pipe:close()
end
local path = os.tmpname() -- small temp file; Pandoc reads it after the filter has finished
local fh = io.open(path, "w")
if not fh then
return nil
end
fh:write(default, "\n", CSS)
fh:close()
return path
end
local lang = "de"
@ -79,15 +104,23 @@ local function Meta(meta)
lang = l
end
end
if is_html() then
local includes = meta["header-includes"]
if includes == nil then
includes = pandoc.MetaList({})
elseif includes.t ~= "MetaList" then
includes = pandoc.MetaList({ includes })
if CSS and FORMAT:match("epub") then
local css = meta.css
local path = nil
if css == nil then -- no own stylesheet: default epub.css + ours; otherwise just add ours
path = epub_stylesheet()
end
includes:insert(pandoc.MetaBlocks({ pandoc.RawBlock("html", CSS) }))
meta["header-includes"] = includes
if css == nil then
css = pandoc.List({})
elseif pandoc.utils.type(css) ~= "List" then
css = pandoc.List({ css })
end
if path then
css:insert(pandoc.MetaString(path))
else
css:insert(pandoc.MetaString(CSS_PATH))
end
meta.css = css
end
return meta
end
@ -126,7 +159,16 @@ local function Div(div)
return pandoc.BlockQuote(div.content)
end
-- HTML: the stylesheet goes into the body, because a command-line -H replaces header-includes from a filter.
local function Pandoc(doc)
if CSS and FORMAT:match("html") and not FORMAT:match("epub") then
doc.blocks:insert(1, pandoc.RawBlock("html", "<style>\n" .. CSS .. "</style>"))
end
return doc
end
return {
{ Meta = Meta },
{ BlockQuote = BlockQuote, Div = Div },
{ Pandoc = Pandoc },
}