146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
# Copyright (c) 2026 PowerOn AG
|
|
# All rights reserved.
|
|
"""A3: layout primitives (cover_page, image_grid).
|
|
|
|
Covers the markdown authoring conventions (fenced ```cover_page / ```image_grid
|
|
blocks) and the PDF/DOCX renderer handlers, using real reportlab / python-docx.
|
|
"""
|
|
|
|
import base64
|
|
import io
|
|
|
|
|
|
from modules.serviceCenter.services.serviceGeneration.styleDefaults import resolveStyle
|
|
from modules.serviceCenter.services.serviceGeneration.renderers.rendererPdf import RendererPdf
|
|
from modules.serviceCenter.services.serviceGeneration.renderers.rendererDocx import RendererDocx
|
|
from modules.serviceCenter.services.serviceGeneration.subDocumentUtility import markdownToDocumentJson
|
|
from modules.datamodels.datamodelJson import supportedSectionTypes
|
|
|
|
|
|
def _tinyPngB64() -> str:
|
|
from PIL import Image as PILImage
|
|
|
|
buf = io.BytesIO()
|
|
PILImage.new("RGB", (8, 8), (30, 120, 200)).save(buf, format="PNG")
|
|
return base64.b64encode(buf.getvalue()).decode("ascii")
|
|
|
|
|
|
def _imgContent():
|
|
return {"base64Data": _tinyPngB64(), "altText": "Pic"}
|
|
|
|
|
|
# ── datamodel ───────────────────────────────────────────────────────
|
|
|
|
def test_layout_types_are_registered():
|
|
assert "cover_page" in supportedSectionTypes
|
|
assert "image_grid" in supportedSectionTypes
|
|
|
|
|
|
# ── markdown authoring ──────────────────────────────────────────────
|
|
|
|
def test_markdown_parses_cover_page_block():
|
|
md = (
|
|
"```cover_page\n"
|
|
"title: Klageschrift\n"
|
|
"subtitle: In Sachen A gegen B\n"
|
|
"author: Dr. Muster\n"
|
|
"date: 2026-06-02\n"
|
|
"logo: file:logo-1\n"
|
|
"```\n"
|
|
)
|
|
doc = markdownToDocumentJson(md, "T")
|
|
sections = doc["documents"][0]["sections"]
|
|
assert len(sections) == 1
|
|
sec = sections[0]
|
|
assert sec["content_type"] == "cover_page"
|
|
content = sec["elements"][0]["content"]
|
|
assert content["title"] == "Klageschrift"
|
|
assert content["subtitle"] == "In Sachen A gegen B"
|
|
assert content["author"] == "Dr. Muster"
|
|
assert content["date"] == "2026-06-02"
|
|
assert content["_fileRef"] == "logo-1"
|
|
|
|
|
|
def test_markdown_parses_image_grid_block():
|
|
md = (
|
|
"```image_grid\n"
|
|
"columns: 3\n"
|
|
'\n'
|
|
"file:img-2\n"
|
|
"https://example.com/x.png\n"
|
|
"```\n"
|
|
)
|
|
doc = markdownToDocumentJson(md, "T")
|
|
sec = doc["documents"][0]["sections"][0]
|
|
assert sec["content_type"] == "image_grid"
|
|
content = sec["elements"][0]["content"]
|
|
assert content["columns"] == 3
|
|
assert len(content["images"]) == 3
|
|
assert content["images"][0]["_fileRef"] == "img-1"
|
|
assert content["images"][0]["widthPt"] == 200
|
|
assert content["images"][1]["_fileRef"] == "img-2"
|
|
assert content["images"][2]["_srcUrl"] == "https://example.com/x.png"
|
|
|
|
|
|
def test_markdown_plain_code_block_still_works():
|
|
md = "```python\nprint('x')\n```\n"
|
|
doc = markdownToDocumentJson(md, "T")
|
|
sec = doc["documents"][0]["sections"][0]
|
|
assert sec["content_type"] == "code_block"
|
|
assert sec["elements"][0]["content"]["language"] == "python"
|
|
|
|
|
|
# ── PDF handlers ────────────────────────────────────────────────────
|
|
|
|
def test_pdf_cover_page_emits_page_break():
|
|
from reportlab.platypus import PageBreak
|
|
|
|
r = RendererPdf()
|
|
styles = r._convertUnifiedStyleToInternal(resolveStyle(None))
|
|
el = {"content": {"title": "Report", "subtitle": "2026", "author": "PM"}}
|
|
flowables = r._renderCoverPage(el, styles)
|
|
assert any(isinstance(f, PageBreak) for f in flowables)
|
|
assert len(flowables) >= 2
|
|
|
|
|
|
def test_pdf_image_grid_builds_table():
|
|
from reportlab.platypus import Table
|
|
|
|
r = RendererPdf()
|
|
r._tempImageFiles = []
|
|
styles = r._convertUnifiedStyleToInternal(resolveStyle(None))
|
|
el = {"content": {"columns": 2, "images": [_imgContent(), _imgContent(), _imgContent()]}}
|
|
flowables = r._renderImageGrid(el, styles)
|
|
tables = [f for f in flowables if isinstance(f, Table)]
|
|
assert len(tables) == 1
|
|
# 3 images, 2 columns -> 2 rows (last cell padded).
|
|
assert len(tables[0]._cellvalues) == 2
|
|
r._cleanupTempImageFiles()
|
|
|
|
|
|
# ── DOCX handlers ───────────────────────────────────────────────────
|
|
|
|
def test_docx_cover_page_renders_and_breaks():
|
|
from docx import Document
|
|
|
|
r = RendererDocx()
|
|
doc = Document()
|
|
before = len(doc.paragraphs)
|
|
r._renderCoverPage(doc, {"content": {"title": "Klageschrift", "author": "Dr. M"}}, {})
|
|
# New paragraphs were added (spacers + title + author) and a page break exists.
|
|
assert len(doc.paragraphs) > before
|
|
xml = doc.element.xml
|
|
assert "w:br" in xml and 'type="page"' in xml
|
|
|
|
|
|
def test_docx_image_grid_builds_table_with_pictures():
|
|
from docx import Document
|
|
|
|
r = RendererDocx()
|
|
doc = Document()
|
|
r._renderImageGrid(doc, {"content": {"columns": 2, "images": [_imgContent(), _imgContent(), _imgContent()]}}, {})
|
|
assert len(doc.tables) == 1
|
|
table = doc.tables[0]
|
|
assert len(table.columns) == 2
|
|
assert len(table.rows) == 2
|
|
assert len(doc.inline_shapes) == 3
|