diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index eb77c5d..c54043f 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,14 +4,10 @@
-
+
-
-
-
-
@@ -23,7 +19,7 @@
-
+
@@ -103,7 +99,10 @@
-
+
+
+
+
diff --git a/MarkdownToIHKChemnits b/MarkdownToIHKChemnits
index 1ae9db0..5281f2e 100755
Binary files a/MarkdownToIHKChemnits and b/MarkdownToIHKChemnits differ
diff --git a/markdown_parser.go b/markdown_parser.go
index 2b3f032..be73a5d 100644
--- a/markdown_parser.go
+++ b/markdown_parser.go
@@ -44,15 +44,17 @@ func ParseMarkdown(mdPath string) (Config, ast.Node, []byte, error) {
// parserState tracks transient state during the AST walk.
type parserState struct {
- 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
+ nextCodeIsAppendix bool
+ nextAppendixLandscape bool // set by @AnhangUMLQuer: — landscape for diagram appendix
+ appendixTitle string
+ nextCodeBlockAppendix bool // set by @AnhangCode: — next non-diagram code block → appendix
+ codeBlockAppendixTitle 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.
@@ -142,6 +144,12 @@ func RenderAST(doc ast.Node, content []byte, r *IHKRenderer) error {
}
// Fall through: render as plain code block on error
}
+ if state.nextCodeBlockAppendix {
+ r.AddCodeAppendix(state.codeBlockAppendixTitle, lang, code)
+ state.nextCodeBlockAppendix = false
+ state.codeBlockAppendixTitle = ""
+ return ast.WalkSkipChildren, nil
+ }
// Render as a numbered code block (gutter + monospace body).
r.RenderCodeBlock(lang, code)
return ast.WalkSkipChildren, nil
@@ -264,6 +272,10 @@ func handleDirectives(text string, state *parserState, r *IHKRenderer) bool {
case strings.HasPrefix(line, "@Quelle:"):
r.AddSource(strings.TrimSpace(strings.TrimPrefix(line, "@Quelle:")))
handled = true
+ case strings.HasPrefix(line, "@AnhangCode:"):
+ state.nextCodeBlockAppendix = true
+ state.codeBlockAppendixTitle = strings.TrimSpace(strings.TrimPrefix(line, "@AnhangCode:"))
+ handled = true
case strings.HasPrefix(line, "@Anhang:"):
r.AddAppendix(strings.TrimSpace(strings.TrimPrefix(line, "@Anhang:")))
handled = true
diff --git a/pdf_pages.go b/pdf_pages.go
index 4c49373..5b7cbfd 100644
--- a/pdf_pages.go
+++ b/pdf_pages.go
@@ -171,8 +171,9 @@ func (r *IHKRenderer) RenderAppendices() {
case AppendixKindImage:
r.renderAppendixImage(app.Path)
case AppendixKindTable:
- // Render without numbering/recording; the annex header already identifies the table.
r.renderTableBody(app.TableData, "")
+ case AppendixKindCode:
+ r.RenderCodeBlock(app.Lang, app.Code)
}
if app.Landscape {
diff --git a/pdf_renderer.go b/pdf_renderer.go
index 45b5d19..f426e7e 100644
--- a/pdf_renderer.go
+++ b/pdf_renderer.go
@@ -38,10 +38,8 @@ const (
dinSpaceAfterHeading = dinLineHtBody // ≈ 6.35 mm
dinSpaceAfterParagraph = 4.0 // between body paragraphs
- // List items use tighter line spacing than body text (1.2× instead of 1.5×).
- // This keeps lists compact while remaining legible at 12 pt.
- dinLineHtList = 5.0 // 12 pt × 1.2 ≈ 5.08 mm, rounded to 5.0
- dinSpaceAfterList = 3.0 // gap inserted after the outermost list exits
+ dinLineHtList = dinLineHtBody // 1.5× IHK standard, same as body text
+ dinSpaceAfterList = 3.0 // gap inserted after the outermost list exits
)
// AppendixKind distinguishes between image and table annexes.
@@ -50,6 +48,7 @@ type AppendixKind int
const (
AppendixKindImage AppendixKind = iota
AppendixKindTable
+ AppendixKindCode
)
// Appendix holds the content for one annex entry.
@@ -59,6 +58,8 @@ type Appendix struct {
Title string
Path string // image path (Kind == AppendixKindImage)
TableData [][]string // table rows (Kind == AppendixKindTable)
+ Lang string // language label (Kind == AppendixKindCode)
+ Code string // source code (Kind == AppendixKindCode)
}
// IHKRenderer is the central PDF generator for IHK Chemnitz project documentation.
@@ -183,6 +184,16 @@ func (r *IHKRenderer) AddTableAppendix(title string, data [][]string) {
})
}
+// AddCodeAppendix registers a source-code annex rendered with line-number gutter.
+func (r *IHKRenderer) AddCodeAppendix(title, lang, code string) {
+ r.appendices = append(r.appendices, Appendix{
+ Kind: AppendixKindCode,
+ Title: title,
+ Lang: lang,
+ Code: code,
+ })
+}
+
// AddLandscapeAppendix registers an image annex in "Title | /path/to/image" format
// that will be rendered on a landscape A4 page with 15 mm symmetric margins.
func (r *IHKRenderer) AddLandscapeAppendix(titlePath string) {
diff --git a/projektarbeit.pdf b/projektarbeit.pdf
index 8076355..32b0903 100644
Binary files a/projektarbeit.pdf and b/projektarbeit.pdf differ
diff --git a/report.md b/report.md
index dd3e333..b1e678b 100644
--- a/report.md
+++ b/report.md
@@ -282,6 +282,38 @@ eine konfigurierbare Spaltenbreite für Tabellen umfassen.
| Bildanhang | @Anhang: Titel \| Pfad | Fügt ein Bild als nummerierte Anlage in den Anhang ein |
| Diagrammanhang | @AnhangUML: Titel | Rendert den folgenden Mermaid/PlantUML-Block als Anlage |
+@AnhangCode: Implementierung – Tabellen-Renderer (prepareRow)
+
+```go
+func (r *IHKRenderer) prepareRow(rawCells []string, numCols int,
+ colW, lineHt float64, bold bool) tableRowData {
+
+ r.pdf.SetFont("Helvetica", map[bool]string{true: "B", false: ""}[bold], dinFontCaption)
+
+ cells := make([][]string, numCols)
+ maxLines := 0
+ for j := 0; j < numCols; j++ {
+ raw := ""
+ if j < len(rawCells) {
+ raw = rawCells[j]
+ }
+ split := r.pdf.SplitLines([]byte(r.tr(raw)), colW-2)
+ lines := make([]string, len(split))
+ for k, b := range split {
+ lines[k] = string(b)
+ }
+ if len(lines) == 0 {
+ lines = []string{""}
+ }
+ cells[j] = lines
+ if len(lines) > maxLines {
+ maxLines = len(lines)
+ }
+ }
+ return tableRowData{cells: cells, height: float64(maxLines) * lineHt}
+}
+```
+
@AnhangUML: Systemarchitektur – Datenflussdiagramm
```mermaid