From 62e007546f112dd683125c445a1d8e7839b1958a Mon Sep 17 00:00:00 2001 From: Perr Hoov <187721938+aka-debug-jie@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:46:00 +0800 Subject: [PATCH] Fix Pandoc subdocument XML namespaces (#620) --- docxtpl/subdoc.py | 11 +++------- tests/pandoc_subdoc.py | 32 +++++++++++++++++++++++++++++ tests/templates/pandoc_subdoc.docx | Bin 0 -> 10418 bytes 3 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 tests/pandoc_subdoc.py create mode 100644 tests/templates/pandoc_subdoc.docx diff --git a/docxtpl/subdoc.py b/docxtpl/subdoc.py index e8da093..994a16b 100644 --- a/docxtpl/subdoc.py +++ b/docxtpl/subdoc.py @@ -13,7 +13,6 @@ from docxcompose.composer import Composer from docxcompose.utils import NS from lxml import etree -import re class SubdocComposer(Composer): @@ -84,14 +83,10 @@ def __getattr__(self, name): def _get_xml(self): if self.subdocx.element.body.sectPr is not None: self.subdocx.element.body.remove(self.subdocx.element.body.sectPr) - xml = re.sub( - r"]*>", - "", - etree.tostring( - self.subdocx.element.body, encoding="unicode", pretty_print=False - ), + return "".join( + etree.tostring(element, encoding="unicode", pretty_print=False) + for element in self.subdocx.element.body ) - return xml def __unicode__(self): return self._get_xml() diff --git a/tests/pandoc_subdoc.py b/tests/pandoc_subdoc.py new file mode 100644 index 0000000..5aa2379 --- /dev/null +++ b/tests/pandoc_subdoc.py @@ -0,0 +1,32 @@ +import io +import zipfile + +from docx import Document +from lxml import etree + +from docxtpl import DocxTemplate + + +main_template = Document() +main_template.add_paragraph("{{p subdoc }}") +main_template_stream = io.BytesIO() +main_template.save(main_template_stream) +main_template_stream.seek(0) + +template = DocxTemplate(main_template_stream) +subdoc = template.new_subdoc("templates/pandoc_subdoc.docx") +template.render({"subdoc": subdoc}) +output_stream = io.BytesIO() +template.save(output_stream) + +output_stream.seek(0) +document = Document(output_stream) +assert len(document.inline_shapes) == 1 + +output_stream.seek(0) +with zipfile.ZipFile(output_stream) as archive: + document_xml = archive.read("word/document.xml") + +root = etree.fromstring(document_xml) +assert root.find(".//{http://schemas.openxmlformats.org/drawingml/2006/main}graphic") is not None +assert root.find(".//{http://schemas.openxmlformats.org/drawingml/2006/picture}pic") is not None diff --git a/tests/templates/pandoc_subdoc.docx b/tests/templates/pandoc_subdoc.docx new file mode 100644 index 0000000000000000000000000000000000000000..0816b384337ac866459f0f57b7b5dabdcc29845f GIT binary patch literal 10418 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 Cd*@UD literal 0 HcmV?d00001