feat: topology on its own landscape page, as large as possible

The topology image was shrunk inside the flex container used for rack
images. Each site's topology now gets a dedicated A4 landscape page
(named @page in PDF, landscape section in Word) with the section and
site headings, scaled to the full usable width or height depending on
its aspect ratio. The document switches back to portrait only when more
content follows, so no blank pages are produced. The TOC includes
headings on landscape pages.

Also fix Word exports without a template using US Letter instead of A4.

Bump version to 0.6.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:03:11 +02:00
co-authored by Claude Opus 5.5
parent 062413a2cd
commit e3c31b6312
7 changed files with 70 additions and 11 deletions
+36 -1
View File
@@ -61,6 +61,7 @@ def render_docx(doc):
# Ohne Vorlage: Standarddokument mit Farben/Schrift aus dem Branding
d = Docx()
sec = d.sections[0]
sec.page_width, sec.page_height = Cm(21), Cm(29.7) # A4 statt US-Letter aus der python-docx-Vorlage
sec.left_margin = sec.right_margin = Cm(2)
sec.top_margin = sec.bottom_margin = Cm(2)
d.styles["Normal"].font.name = b["font_family"]
@@ -116,8 +117,42 @@ def render_docx(doc):
d.settings.element.append(upd)
d.add_page_break()
from docx.enum.section import WD_ORIENT, WD_SECTION
portrait = d.sections[-1]
portrait_size = (portrait.page_width, portrait.page_height)
landscape_open = False
def new_section(landscape):
s = d.add_section(WD_SECTION.NEW_PAGE)
w, h = portrait_size
s.orientation = WD_ORIENT.LANDSCAPE if landscape else WD_ORIENT.PORTRAIT
s.page_width, s.page_height = (h, w) if landscape else (w, h)
return s
for blk in doc.blocks:
if blk.kind == "heading":
if landscape_open and blk.kind != "landscape":
# erst beim nächsten Inhalt zurück ins Hochformat – vermeidet leere Seiten
new_section(landscape=False)
landscape_open = False
if blk.kind == "landscape":
s = new_section(landscape=True)
landscape_open = True
for h in blk.rows:
d.add_heading(h.text, level=h.level)
png = blk.images[0]["png"]
from PIL import Image
px_w, px_h = Image.open(BytesIO(png)).size
avail_w = s.page_width - s.left_margin - s.right_margin
avail_h = s.page_height - s.top_margin - s.bottom_margin - Cm(1.4 * len(blk.rows) + 0.8)
para = d.add_paragraph()
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
if px_w / px_h >= avail_w / avail_h:
para.add_run().add_picture(BytesIO(png), width=avail_w)
else:
para.add_run().add_picture(BytesIO(png), height=avail_h)
elif blk.kind == "heading":
d.add_heading(blk.text, level=min(blk.level, 4))
elif blk.kind == "paragraph":
d.add_paragraph(blk.text)