forked from erp-dev/erp
110 lines
3.9 KiB
Python
110 lines
3.9 KiB
Python
import re
|
||
import zipfile
|
||
from pathlib import Path
|
||
|
||
from django.conf import settings
|
||
from django.test import SimpleTestCase
|
||
|
||
|
||
class DocxPaginationTest(SimpleTestCase):
|
||
def _read_document_xml(self, docx_path: str) -> str:
|
||
with zipfile.ZipFile(docx_path, "r") as zf:
|
||
return zf.read("word/document.xml").decode("utf-8", errors="replace")
|
||
|
||
def _extract_pages_text(self, docx_path: str) -> list[str]:
|
||
"""
|
||
Extract per-page plain text by walking body elements and splitting on page breaks.
|
||
|
||
Important: we extract w:t texts, so assertions won't be broken by XML tag boundaries.
|
||
"""
|
||
import xml.etree.ElementTree as ET
|
||
|
||
xml = self._read_document_xml(docx_path)
|
||
ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
|
||
root = ET.fromstring(xml)
|
||
|
||
body = root.find("w:body", ns)
|
||
if body is None:
|
||
return []
|
||
|
||
pages: list[list[str]] = [[]]
|
||
|
||
def has_page_break(el) -> bool:
|
||
for br in el.findall(".//w:br", ns):
|
||
if br.attrib.get(f"{{{ns['w']}}}type") == "page":
|
||
return True
|
||
return False
|
||
|
||
def collect_text(el) -> str:
|
||
ts = [(t.text or "") for t in el.findall(".//w:t", ns)]
|
||
return "".join(ts)
|
||
|
||
for child in list(body):
|
||
text = collect_text(child)
|
||
if text:
|
||
pages[-1].append(text)
|
||
if has_page_break(child):
|
||
pages.append([])
|
||
|
||
return ["".join(p) for p in pages if "".join(p).strip()]
|
||
|
||
def test_render_pages_per_page_totals(self):
|
||
template = str(Path(settings.BASE_DIR) / "print-template.docx")
|
||
out = str(Path(settings.BASE_DIR) / ".tmp_docx" / "rendered-pages-test.docx")
|
||
Path(out).parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
from printing.docx_template import render_docx_template_pages
|
||
|
||
prefix = "打印-销售单(8*9)."
|
||
|
||
# Page 1: two non-empty qty cells => 匹数=2, 总数量=111.11
|
||
page1 = {
|
||
f"{prefix}当前页码": "1",
|
||
f"{prefix}总页数": "2",
|
||
f"{prefix}匹数": "2",
|
||
f"{prefix}总数量": "111.11",
|
||
# Put a couple of quantities too (indices exist 1..72)
|
||
f"{prefix}销售数量1": "11.11",
|
||
f"{prefix}销售数量2": "100",
|
||
# Fill one product line (index 1 exists for product fields)
|
||
f"{prefix}产品名称1": "P1-产品",
|
||
f"{prefix}幅宽1": "150cm",
|
||
f"{prefix}规格1": "规格A",
|
||
f"{prefix}单位1": "米",
|
||
}
|
||
|
||
# Page 2: one non-empty qty cell => 匹数=1, 总数量=222.22
|
||
page2 = {
|
||
f"{prefix}当前页码": "2",
|
||
f"{prefix}总页数": "2",
|
||
f"{prefix}匹数": "1",
|
||
f"{prefix}总数量": "222.22",
|
||
f"{prefix}销售数量1": "222.22",
|
||
f"{prefix}产品名称1": "P2-产品",
|
||
f"{prefix}幅宽1": "160cm",
|
||
f"{prefix}规格1": "规格B",
|
||
f"{prefix}单位1": "码",
|
||
}
|
||
|
||
render_docx_template_pages(
|
||
template_path=template,
|
||
output_path=out,
|
||
pages_contexts=[page1, page2],
|
||
clear_unresolved=True,
|
||
)
|
||
|
||
pages_text = self._extract_pages_text(out)
|
||
self.assertGreaterEqual(len(pages_text), 2, msg="未生成两页文本(可能分页符缺失)")
|
||
|
||
p1, p2 = pages_text[0], pages_text[1]
|
||
self.assertIn("P1-产品", p1)
|
||
self.assertIn("111.11", p1)
|
||
self.assertIn("P2-产品", p2)
|
||
self.assertIn("222.22", p2)
|
||
|
||
# No raw placeholders should remain (check on plain text concatenation)
|
||
all_text = "".join(pages_text)
|
||
self.assertIsNone(re.search(r"#\{.{1,300}?\}", all_text, flags=re.S))
|
||
self.assertIsNone(re.search(r"(?<!#)\{[^{}\r\n]{1,200}\}", all_text))
|
||
|