Add support for appendices: landscape diagrams, tables, and images; implement Kroki URL configurability; enhance directive parsing logic.

This commit is contained in:
Sebastian Unterschütz
2026-05-12 21:44:37 +02:00
parent 436cdcc516
commit 67f9d63f24
12 changed files with 1025 additions and 221 deletions
+74 -9
View File
@@ -3,6 +3,7 @@ package main
import (
"bytes"
"fmt"
"log"
"os"
"strings"
@@ -43,9 +44,15 @@ func ParseMarkdown(mdPath string) (Config, ast.Node, []byte, error) {
// parserState tracks transient state during the AST walk.
type parserState struct {
nextCodeIsAppendix bool
appendixTitle string
listStack []listFrame // stack for nested list tracking
nextCodeIsAppendix bool
nextAppendixLandscape bool // set by @AnhangUMLQuer: — landscape for diagram appendix
appendixTitle string
nextTableCaption string // set by @Tabelle: directive
nextTableIsAppendix bool // set by @TabelleAnhang: or @TabelleAnhangQuer:
nextTableIsLandscape bool // set by @TabelleAnhangQuer:
nextDiagramLandscape bool // set by @DiagrammQuer: directive
nextDiagramCaption string // caption for the landscape diagram page
listStack []listFrame // stack for nested list tracking
}
// listFrame tracks the type and item counter for one list nesting level.
@@ -107,13 +114,29 @@ func RenderAST(doc ast.Node, content []byte, r *IHKRenderer) error {
if lang == "mermaid" || lang == "plantuml" || lang == "puml" {
imgPath, err := RenderDiagramViaKroki(lang, code)
if err != nil {
log.Printf("warning: diagram render failed (%s): %v — falling back to code block", lang, err)
state.nextDiagramLandscape = false
state.nextDiagramCaption = ""
state.nextCodeIsAppendix = false
state.nextAppendixLandscape = false
}
if err == nil {
caption := lang
if state.nextCodeIsAppendix {
r.AddAppendix(state.appendixTitle + " | " + imgPath)
switch {
case state.nextDiagramLandscape:
r.RenderLandscapeDiagram(imgPath, state.nextDiagramCaption)
state.nextDiagramLandscape = false
state.nextDiagramCaption = ""
case state.nextCodeIsAppendix:
if state.nextAppendixLandscape {
r.AddLandscapeAppendix(state.appendixTitle + " | " + imgPath)
state.nextAppendixLandscape = false
} else {
r.AddAppendix(state.appendixTitle + " | " + imgPath)
}
state.nextCodeIsAppendix = false
} else {
r.RenderImage(imgPath, "Diagram ("+caption+")")
default:
r.RenderImage(imgPath, "Diagram ("+lang+")")
}
return ast.WalkSkipChildren, nil
}
@@ -162,6 +185,11 @@ func RenderAST(doc ast.Node, content []byte, r *IHKRenderer) error {
if len(state.listStack) > 0 {
state.listStack = state.listStack[:len(state.listStack)-1]
}
// Add breathing room after the outermost list so the next
// paragraph is not glued to the last bullet.
if len(state.listStack) == 0 {
r.pdf.Ln(dinSpaceAfterList)
}
}
return ast.WalkContinue, nil
@@ -192,7 +220,20 @@ func RenderAST(doc ast.Node, content []byte, r *IHKRenderer) error {
}
tableData = append(tableData, rowData)
}
r.RenderTable(tableData, "")
if state.nextTableIsAppendix {
if state.nextTableIsLandscape {
r.AddTableAppendixLandscape(state.nextTableCaption, tableData)
state.nextTableIsLandscape = false
} else {
r.AddTableAppendix(state.nextTableCaption, tableData)
}
state.nextTableIsAppendix = false
state.nextTableCaption = ""
} else {
caption := state.nextTableCaption
state.nextTableCaption = ""
r.RenderTable(tableData, caption)
}
return ast.WalkSkipChildren, nil
}
@@ -226,10 +267,34 @@ func handleDirectives(text string, state *parserState, r *IHKRenderer) bool {
case strings.HasPrefix(line, "@Anhang:"):
r.AddAppendix(strings.TrimSpace(strings.TrimPrefix(line, "@Anhang:")))
handled = true
case strings.HasPrefix(line, "@AnhangBildQuer:"):
r.AddLandscapeAppendix(strings.TrimSpace(strings.TrimPrefix(line, "@AnhangBildQuer:")))
handled = true
case strings.HasPrefix(line, "@AnhangUMLQuer:"):
state.nextCodeIsAppendix = true
state.nextAppendixLandscape = true
state.appendixTitle = strings.TrimSpace(strings.TrimPrefix(line, "@AnhangUMLQuer:"))
handled = true
case strings.HasPrefix(line, "@AnhangUML:"):
state.nextCodeIsAppendix = true
state.appendixTitle = strings.TrimSpace(strings.TrimPrefix(line, "@AnhangUML:"))
handled = true
case strings.HasPrefix(line, "@TabelleAnhangQuer:"):
state.nextTableIsAppendix = true
state.nextTableIsLandscape = true
state.nextTableCaption = strings.TrimSpace(strings.TrimPrefix(line, "@TabelleAnhangQuer:"))
handled = true
case strings.HasPrefix(line, "@TabelleAnhang:"):
state.nextTableIsAppendix = true
state.nextTableCaption = strings.TrimSpace(strings.TrimPrefix(line, "@TabelleAnhang:"))
handled = true
case strings.HasPrefix(line, "@Tabelle:"):
state.nextTableCaption = strings.TrimSpace(strings.TrimPrefix(line, "@Tabelle:"))
handled = true
case strings.HasPrefix(line, "@DiagrammQuer:"):
state.nextDiagramLandscape = true
state.nextDiagramCaption = strings.TrimSpace(strings.TrimPrefix(line, "@DiagrammQuer:"))
handled = true
}
}
return handled