Files
MarkdownToIHKChemnits/README.md
T

346 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MarkdownToIHKChemnitz
A Go CLI tool that converts a single Markdown file into a print-ready PDF compliant with the **IHK Chemnitz project documentation guidelines** (Verordnung 2020) and **DIN 5008** formatting rules.
## Features
- DIN 5008 page layout — correct margins, font, line spacing, Blocksatz
- Two-pass rendering for a page-accurate table of contents
- Roman/Arabic page numbering with automatic section detection
- Multi-line table cells with uniform row height per row
- Named tables (*Tabellenverzeichnis*) and captioned figures (*Abbildungsverzeichnis*)
- Numbered code blocks with line-number gutter and long-line wrapping
- Diagram rendering via [Kroki](https://kroki.io) — Mermaid, PlantUML, and more
- Inline landscape diagram pages (`@DiagrammQuer:`)
- Appendices in portrait **and landscape** — images, tables, and diagrams
- Abbreviation list and glossary from YAML front matter
- Bibliography sorted alphabetically from inline `@Quelle:` directives
- Declaration of authenticity page (pre-written legal text)
---
## Prerequisites
- Go 1.22 or later
- Internet access **or** a local Kroki instance for diagram rendering (see below)
---
## Build & Run
```bash
go build -o ihk-pdf .
./ihk-pdf -i report.md -o projektarbeit.pdf
```
Or without building:
```bash
go run . -i report.md -o projektarbeit.pdf
```
---
## CLI Flags
| Flag | Default | Description |
|------|---------|-------------|
| `-i` | `report.md` | Input Markdown file |
| `-o` | `projektarbeit.pdf` | Output PDF file |
| `-kroki` | `https://kroki.io` | Kroki base URL for diagram rendering |
---
## Diagram Rendering with Kroki
Mermaid and PlantUML fenced code blocks are rendered server-side via [Kroki](https://kroki.io). The resulting PNG is cached locally using a SHA-256 hash of the diagram source, so unchanged diagrams are not re-fetched.
By default the public instance at `https://kroki.io` is used. If your network blocks it, run a local instance with Docker Compose:
**`docker-compose.yml`**
```yaml
services:
kroki:
image: yuzutech/kroki
environment:
- KROKI_MERMAID_HOST=mermaid
ports:
- "8000:8000"
depends_on:
- mermaid
mermaid:
image: yuzutech/kroki-mermaid
expose:
- "8002"
```
```bash
docker compose up -d
go run . -i report.md -kroki http://localhost:8000 -o projektarbeit.pdf
```
> **Note:** The base `yuzutech/kroki` image does not include a Mermaid renderer. The companion `yuzutech/kroki-mermaid` container is required, wired via `KROKI_MERMAID_HOST=mermaid`.
---
## YAML Front Matter
Every input file begins with a YAML block delimited by `---`. All fields are optional except where noted.
```yaml
---
student:
name: "Max Mustermann" # required
profession: "Fachinformatiker Fachrichtung Anwendungsentwicklung"
company: "Musterfirma GmbH"
supervisor: "Sabine Supervisor"
project:
title: "Titel der Projektarbeit" # required
subtitle: "Optionaler Untertitel" # optional
period: "Sommer 2026"
abbreviations: # optional → Abkürzungsverzeichnis
- abbr: "API"
meaning: "Application Programming Interface"
- abbr: "IHK"
meaning: "Industrie- und Handelskammer"
glossary: # optional → Glossar (after appendices)
- term: "Goldmark"
definition: "Ein CommonMark-konformer Markdown-Parser für Go."
---
```
---
## Document Structure
The generated PDF follows the mandatory IHK Chemnitz order:
| # | Section | Page Numbering |
|---|---------|----------------|
| 1 | Title page | none |
| 2 | Table of contents | Roman (II, III, …) |
| 3 | Abbreviation list | Roman (from YAML, optional) |
| 4 | Foreword / front-matter sections | Roman |
| 5 | Main body chapters | Arabic (1, 2, …) |
| 6 | Bibliography | Arabic |
| 7 | List of tables | Arabic |
| 8 | List of figures | Arabic |
| 9 | Appendices | Arabic |
| 10 | Glossary | Arabic (from YAML, optional) |
| 11 | Declaration of authenticity | none |
### Front-matter detection
Level-1 headings named `Vorwort`, `Einleitung`, or `Abkürzungsverzeichnis` stay in the Roman-numbered front matter. Every other level-1 heading triggers the switch to Arabic numbering.
---
## DIN 5008 / IHK Formatting Rules
| Property | Value |
|----------|-------|
| Paper | A4 |
| Left margin | 30 mm |
| Right margin (Korrekturrand) | 40 mm |
| Top margin | 20 mm |
| Bottom margin | 25 mm |
| Body font | Helvetica (Arial) 12 pt, black |
| Body line spacing | 1.5× → 6.35 mm |
| Heading font | Helvetica Bold 14 pt |
| Caption / footnote | Helvetica 10 pt |
| List line spacing | 1.2× → 5.0 mm |
| Alignment | Justified (Blocksatz) |
| Page number position | Centered at bottom |
---
## Directives
Directives are plain paragraphs starting with `@`. They are consumed by the parser and never appear in the PDF as raw text. Multiple directives may appear in a single paragraph, one per line.
---
### `@Quelle:` — Bibliography entry
```
@Quelle: Autor, Titel, Verlag, Jahr
```
Registers a bibliography entry. All entries are collected and rendered alphabetically at the end of the document in the *Literaturverzeichnis*. May appear anywhere in the document, any number of times.
---
### `@Tabelle:` — Named table
Place immediately before a Markdown table to assign it a name and record it in the *Tabellenverzeichnis*:
```
@Tabelle: Übersicht der Programmiersprachen
| Sprache | Paradigma | Typsystem |
|---------|-----------|-----------|
| Go | Imperativ | Statisch |
| Python | Multi | Dynamisch |
```
---
### `@TabelleAnhang:` — Table as portrait appendix
Sends the following table to the appendix on a **portrait** A4 page as a numbered *Anlage*:
```
@TabelleAnhang: Vollständige Fehlerliste
| Code | Beschreibung | Schwere |
|------|--------------------|----------|
| E001 | Datei nicht gefunden | Kritisch |
```
---
### `@TabelleAnhangQuer:` — Table as landscape appendix
Same as `@TabelleAnhang:` but placed on a **landscape** A4 page (297 × 210 mm, 15 mm symmetric margins). Use for wide tables that do not fit in portrait:
```
@TabelleAnhangQuer: Breite Vergleichsmatrix
| Kriterium | Option A | Option B | Option C | Option D |
|-----------|----------|----------|----------|----------|
| Leistung | Gut | Sehr gut | Befriedigend | Gut |
```
---
### `@Anhang:` — Image as portrait appendix
```
@Anhang: Netzwerkdiagramm | diagrams/network.png
```
Adds an image file as a numbered *Anlage* on a **portrait** appendix page. The format is `Title | relative/path/to/image`.
---
### `@AnhangBildQuer:` — Image as landscape appendix
```
@AnhangBildQuer: Großes Architekturdiagramm | diagrams/arch.png
```
Same as `@Anhang:` but placed on a **landscape** A4 page. Useful for wide images.
---
### `@AnhangUML:` — Diagram as portrait appendix
Renders the immediately following Mermaid or PlantUML code block via Kroki and places the result as a numbered *Anlage* on a **portrait** appendix page:
```
@AnhangUML: Datenbankschema
` ``plantuml
@startuml
entity User {
+ id : int
+ name : string
}
@enduml
` ``
```
---
### `@AnhangUMLQuer:` — Diagram as landscape appendix
Same as `@AnhangUML:` but placed on a **landscape** A4 page with 15 mm symmetric margins. Use for complex diagrams that need more horizontal space:
```
@AnhangUMLQuer: Vollständige Modulübersicht
` ``mermaid
graph TD
A --> B --> C
` ``
```
---
### `@DiagrammQuer:` — Inline landscape diagram page
Renders the following diagram on a **dedicated landscape page inline** in the document (not in the appendix). A figure caption is added and the figure is recorded in the *Abbildungsverzeichnis*. A fresh portrait page opens automatically afterwards:
```
@DiagrammQuer: Systemarchitektur Zwei-Pass-Rendering
` ``mermaid
graph LR
MD[report.md] --> Parser --> AST --> Renderer --> PDF
` ``
```
---
## Directive Summary Table
| Directive | Format | Placement | Orientation |
|-----------|--------|-----------|-------------|
| `@Quelle:` | `@Quelle: Text` | Inline anywhere | — |
| `@Tabelle:` | `@Tabelle: Name` | Before a table | — |
| `@TabelleAnhang:` | `@TabelleAnhang: Name` | Before a table | Portrait appendix |
| `@TabelleAnhangQuer:` | `@TabelleAnhangQuer: Name` | Before a table | **Landscape** appendix |
| `@Anhang:` | `@Anhang: Title \| path` | Standalone | Portrait appendix |
| `@AnhangBildQuer:` | `@AnhangBildQuer: Title \| path` | Standalone | **Landscape** appendix |
| `@AnhangUML:` | `@AnhangUML: Title` | Before diagram block | Portrait appendix |
| `@AnhangUMLQuer:` | `@AnhangUMLQuer: Title` | Before diagram block | **Landscape** appendix |
| `@DiagrammQuer:` | `@DiagrammQuer: Caption` | Before diagram block | **Landscape** inline page |
---
## File Structure
```
.
├── main.go # Entry point, CLI flags, two-pass pipeline
├── config.go # Config struct matching the YAML front matter
├── markdown_parser.go # Goldmark AST walker, directive handling
├── pdf_renderer.go # IHKRenderer struct, DIN 5008 constants, appendix registry
├── pdf_content.go # Paragraphs, lists, tables, images, code blocks
├── pdf_toc.go # TOC, list of tables, list of figures
├── pdf_pages.go # Title page, declaration, bibliography, appendices, glossary
├── pdf_numbering.go # Roman/Arabic page numbering helpers
├── diagram.go # Kroki HTTP client, SHA-256 image cache
├── docker-compose.yml # Local Kroki + kroki-mermaid containers
└── report.md # Sample document demonstrating all features
```
---
## Two-Pass Rendering
The tool renders the document twice:
1. **Pass 1** — full render into a scratch PDF to collect the page number of every heading, table, and figure.
2. **Pass 2** — final render using the TOC index from pass 1, producing the output PDF.
Only `tocItems` are transferred between passes. `tableItems` and `figureItems` are rebuilt during pass 2 to avoid duplicates in the respective lists.
---
## Landscape Pages — Technical Notes
- Landscape appendices and `@DiagrammQuer:` use `fpdf.AddPageFormat("L", {Wd:297, Ht:210})` with 15 mm symmetric margins. No 40 mm Korrekturrand — examiners do not annotate diagram or data pages.
- After any landscape page, `AddPage()` reverts to the default portrait orientation (A4 "P") set at construction time — no explicit orientation restore is needed for subsequent content.
- Portrait margins (30/20/40 mm) are restored immediately after the landscape page is closed so all following content is formatted correctly.
---
## License
MIT