Files
demo-epb/tools/render_plantuml.py
Stefan Lohmaier c81121c3d5
Validate / build-test (macos-latest) (push) Failing after 2s
Validate / build-test (ubuntu-latest) (push) Successful in 17s
Validate / build-test (windows-latest) (push) Failing after 18s
Validate / reports (push) Successful in 52s
feat(i18n): remaining German comments + CI strings in English
Final residual translations found in code/comments/CI:
- .doorstop.yml: config comments, traceability mapping comments
- Doxyfile: header comment
- tools/render_plantuml.py: docstring
- tools/generate_test_report.py: docstring
- tests/unit_test_framework.h: doxygen brief + body
- tests/unit/test_safety_manager.c: section comment
- src/stubs/*.h: doxygen briefs for diag/display/inclinometer/logger/service/wheel-speed
- .gitea/workflows/release.yml: release notes 'Statische Analyse' + deploy error message
2026-05-12 06:14:23 -07:00

107 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""
Render all @startuml ... @enduml blocks from arch/**/*.md as SVG.
Uses a reachable PlantUML HTTP server (default: www.plantuml.com).
In CI the server URL can be overridden via PLANTUML_SERVER.
Output: docs/diagrams/<file>-<index>.svg
Run:
python3 tools/render_plantuml.py
PLANTUML_SERVER=http://plantuml-server:8080 python3 tools/render_plantuml.py
"""
from __future__ import annotations
import os
import re
import sys
import urllib.request
import zlib
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
SERVER = os.environ.get("PLANTUML_SERVER", "https://www.plantuml.com/plantuml")
# Map standard base64 to PlantUML's base64 alphabet
_B64 = (
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"-_"
)
def _encode_3bytes(b1: int, b2: int, b3: int) -> str:
c1 = b1 >> 2
c2 = ((b1 & 0x3) << 4) | (b2 >> 4)
c3 = ((b2 & 0xF) << 2) | (b3 >> 6)
c4 = b3 & 0x3F
return _B64[c1] + _B64[c2] + _B64[c3] + _B64[c4]
def plantuml_encode(text: str) -> str:
"""Encode PlantUML source to its URL-safe representation."""
compressed = zlib.compress(text.encode("utf-8"), 9)
# Strip zlib header (2 bytes) and Adler32 trailer (4 bytes)
raw = compressed[2:-4]
out = ""
i = 0
while i < len(raw):
b1 = raw[i]
b2 = raw[i + 1] if i + 1 < len(raw) else 0
b3 = raw[i + 2] if i + 2 < len(raw) else 0
out += _encode_3bytes(b1, b2, b3)
i += 3
return out
BLOCK_RE = re.compile(r"```plantuml\s*\n(@startuml.*?@enduml)\s*\n```",
re.DOTALL)
def extract_blocks(md: Path) -> list[str]:
return BLOCK_RE.findall(md.read_text())
def render_one(src: str, out_path: Path) -> None:
encoded = plantuml_encode(src)
url = f"{SERVER.rstrip('/')}/svg/{encoded}"
req = urllib.request.Request(url, headers={"User-Agent": "demo-epb/1.0"})
with urllib.request.urlopen(req, timeout=30) as resp:
data = resp.read()
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_bytes(data)
def main() -> int:
out_dir = REPO / "docs" / "diagrams"
out_dir.mkdir(parents=True, exist_ok=True)
md_files = sorted((REPO / "arch").rglob("*.md"))
if not md_files:
print("Keine arch/**/*.md gefunden.")
return 0
total = 0
for md in md_files:
rel = md.relative_to(REPO)
blocks = extract_blocks(md)
if not blocks:
continue
for i, block in enumerate(blocks, start=1):
stem = md.stem
target = out_dir / f"{stem}-{i}.svg"
try:
render_one(block, target)
print(f" {rel} block {i} -> {target.relative_to(REPO)}")
total += 1
except Exception as e:
print(f" WARN: {rel} block {i} render failed: {e}")
print(f"\nDone: {total} Diagrams rendered to {out_dir.relative_to(REPO)}/")
return 0
if __name__ == "__main__":
sys.exit(main())