"""投标函字段填充不得把混合格式段落压平到第一个 run。""" import os import tempfile import unittest import xml.etree.ElementTree as ET from docx import Document from docx.enum.text import WD_UNDERLINE from models import Chapter from step4_writing import _fill_docx_paragraph_text, _save_chapter_docx, _w_tag class Step4UnderlineTests(unittest.TestCase): def test_cross_run_placeholder_keeps_untouched_run_properties(self): doc = Document() p = doc.add_paragraph("参加") for value in ("%%项", "目名称%%"): run = p.add_run(value) run.underline = WD_UNDERLINE.DOUBLE run.bold = True p.add_run(",承诺") p.add_run("固定下划线文字").underline = True p.add_run("。") xml = ET.fromstring(p._p.xml) properties = [ET.tostring(r.find(_w_tag("rPr"))) if r.find(_w_tag("rPr")) is not None else None for r in xml.iter(_w_tag("r"))] _fill_docx_paragraph_text(xml, "参加示例项目,承诺固定下划线文字。") texts = [t.text or "" for t in xml.iter(_w_tag("t"))] self.assertEqual(texts, ["参加", "示例项目", "", ",承诺", "固定下划线文字", "。"], "字段应在原下划线run内替换,禁止整段压平") self.assertEqual(properties, [ET.tostring(r.find(_w_tag("rPr"))) if r.find(_w_tag("rPr")) is not None else None for r in xml.iter(_w_tag("r"))]) def test_deletion_numeric_update_and_non_text_nodes_survive(self): p = Document().add_paragraph("比例") p.add_run("15%").underline = True p.add_run(",包号:%%包号%%") p.add_run().add_break() p.add_run("签署:____").italic = True xml = ET.fromstring(p._p.xml) _fill_docx_paragraph_text(xml, "比例20%签署:____") self.assertEqual("".join(t.text or "" for t in xml.iter(_w_tag("t"))), "比例20%签署:____") self.assertEqual(len(list(xml.iter(_w_tag("br")))), 1) underlined = [r for r in xml.iter(_w_tag("r")) if r.find(_w_tag("rPr") + '/' + _w_tag("u")) is not None] self.assertEqual("".join(t.text or "" for r in underlined for t in r.iter(_w_tag("t"))), "20%") def test_native_chapter_docx_preserves_letter_and_cell_underlines(self): with tempfile.TemporaryDirectory() as directory: template = Document() template.add_paragraph("第一章 投标人资格、资信证明", style="Heading 1") template.add_paragraph("投标函") paragraphs = [template.add_paragraph(), template.add_table(rows=1, cols=1).cell(0, 0).paragraphs[0]] for p in paragraphs: p.add_run("参加") p.add_run("%%项目").underline = True p.add_run("名称%%").underline = True p.add_run(",编号:") p.add_run("%%项目编号%%").underline = WD_UNDERLINE.DOUBLE p.add_run(";签署:") p.add_run(" ").underline = True p.add_run("。") template.add_paragraph("第二章 报价", style="Heading 1") template_path = os.path.join(directory, "template.docx") template.save(template_path) path = _save_chapter_docx( Chapter(id="1", title="投标人资格、资信证明", template_chapter_id="1"), directory, template_path=template_path, placeholder_map={"项目名称": "示例项目", "项目编号": "TEST-001"}, ) result = Document(path) paragraphs = [next(p for p in result.paragraphs if p.text.startswith("参加")), result.tables[0].cell(0, 0).paragraphs[0]] for p in paragraphs: self.assertEqual(p.text, "参加示例项目,编号:TEST-001;签署: 。") self.assertEqual([(r.text, r.underline) for r in p.runs if r.text], [ ("参加", None), ("示例项目", True), (",编号:", None), ("TEST-001", WD_UNDERLINE.DOUBLE), (";签署:", None), (" ", True), ("。", None), ], "原生段落/单元格填充必须保留字段及签署空白下划线") if __name__ == "__main__": unittest.main()