diff --git a/docxtpl/subdoc.py b/docxtpl/subdoc.py index e8da093..f4ea439 100644 --- a/docxtpl/subdoc.py +++ b/docxtpl/subdoc.py @@ -7,6 +7,7 @@ from docx import Document from docx.oxml import CT_SectPr +from docx.oxml.ns import nsmap from docx.opc.constants import RELATIONSHIP_TYPE as RT from docxcompose.properties import CustomProperties from docxcompose.utils import xpath @@ -16,6 +17,12 @@ import re +# docxcompose changes python-docx's process-wide ``cp`` prefix to the custom +# properties namespace. Restore its standard meaning so python-docx updates +# existing core properties instead of adding conflicting elements. +nsmap["cp"] = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties" + + class SubdocComposer(Composer): def attach_parts(self, doc, remove_property_fields=True): """Attach docx parts instead of appending the whole document diff --git a/tests/core_properties.py b/tests/core_properties.py new file mode 100644 index 0000000..0cd3a57 --- /dev/null +++ b/tests/core_properties.py @@ -0,0 +1,27 @@ +from zipfile import ZipFile + +from lxml import etree + +from docxtpl import DocxTemplate + +doctemplate = r"templates/doc_properties_tpl.docx" +output = "output/core_properties.docx" + +tpl = DocxTemplate(doctemplate) +tpl.render({"test": "HelloWorld"}) +tpl.docx.core_properties.keywords = "rendered by docxtpl" +tpl.save(output) + +with ZipFile(output) as archive: + core_properties = etree.fromstring(archive.read("docProps/core.xml")) + +keywords = [ + element + for element in core_properties + if etree.QName(element).localname == "keywords" +] +assert len(keywords) == 1 +assert etree.QName(keywords[0]).namespace == ( + "http://schemas.openxmlformats.org/package/2006/metadata/core-properties" +) +assert keywords[0].text == "rendered by docxtpl"