From 0061b556ec3b3a3346ef2edd4f3a84efa642c74f Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Tue, 17 Sep 2024 15:09:29 +0200 Subject: [PATCH 1/8] [WIP] Support rendering variables in footnotes --- docxtpl/template.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docxtpl/template.py b/docxtpl/template.py index d6d28ec..b421e64 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -350,6 +350,23 @@ def render_properties( template = jinja_env.from_string(initial) rendered = template.render(context) setattr(self.docx.core_properties, prop, rendered) + + def render_footnotes( + self, context: Dict[str, Any], jinja_env: Optional[Environment] = None + ) -> None: + if jinja_env is None: + jinja_env = Environment() + + for k, v in self.docx.sections[0].part.related_parts.items(): + if v.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': + import xml.etree.ElementTree as ET + tree = ET.fromstring(v.blob) + for footnote in tree.findall('.//w:t', docx.oxml.ns.nsmap): + if hasattr(footnote, 'text'): + footnote.text = jinja_env.from_string(footnote.text).render(context) + for part in self.docx.sections[0].part.related_parts[k].package.parts: + if part.partname == v.partname: + part._blob = ET.tostring(tree, encoding='unicode').encode('utf8') def resolve_listing(self, xml): @@ -483,6 +500,8 @@ def render( self.render_properties(context, jinja_env) + self.render_footnotes(context, jinja_env) + # set rendered flag self.is_rendered = True From 2886a851e03d319935477dc5f7bf4040a807d332 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Tue, 17 Sep 2024 21:07:21 +0200 Subject: [PATCH 2/8] Change the XML library to lxml to avoid namespace renaming and other XML problems --- docxtpl/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docxtpl/template.py b/docxtpl/template.py index b421e64..20bfb4d 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -359,7 +359,7 @@ def render_footnotes( for k, v in self.docx.sections[0].part.related_parts.items(): if v.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': - import xml.etree.ElementTree as ET + from lxml import etree as ET tree = ET.fromstring(v.blob) for footnote in tree.findall('.//w:t', docx.oxml.ns.nsmap): if hasattr(footnote, 'text'): From 2812487300baf7aa37cd5641420c04d4c5b94b05 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Tue, 17 Sep 2024 21:19:19 +0200 Subject: [PATCH 3/8] Simplify the code --- docxtpl/template.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docxtpl/template.py b/docxtpl/template.py index 20bfb4d..29e1b08 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -357,16 +357,13 @@ def render_footnotes( if jinja_env is None: jinja_env = Environment() - for k, v in self.docx.sections[0].part.related_parts.items(): - if v.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': - from lxml import etree as ET - tree = ET.fromstring(v.blob) + for part in self.docx.sections[0].part.package.parts: + if part.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': + tree = etree.fromstring(part.blob) for footnote in tree.findall('.//w:t', docx.oxml.ns.nsmap): if hasattr(footnote, 'text'): footnote.text = jinja_env.from_string(footnote.text).render(context) - for part in self.docx.sections[0].part.related_parts[k].package.parts: - if part.partname == v.partname: - part._blob = ET.tostring(tree, encoding='unicode').encode('utf8') + part._blob = etree.tostring(tree) def resolve_listing(self, xml): From a449f01f3674e25e33ed7b34d160fb90102bcc3b Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Wed, 18 Sep 2024 07:35:12 +0000 Subject: [PATCH 4/8] Fix flake8 --- docxtpl/template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docxtpl/template.py b/docxtpl/template.py index 29e1b08..6babb73 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -350,10 +350,10 @@ def render_properties( template = jinja_env.from_string(initial) rendered = template.render(context) setattr(self.docx.core_properties, prop, rendered) - + def render_footnotes( self, context: Dict[str, Any], jinja_env: Optional[Environment] = None - ) -> None: + ) -> None: if jinja_env is None: jinja_env = Environment() From 1cca257016c97fa1be4f6aa888d8ed44b2eca073 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Mon, 23 Sep 2024 06:24:41 +0000 Subject: [PATCH 5/8] Add a test and use existing XML patching method --- docxtpl/template.py | 13 ++++++------- tests/footnotes.py | 19 +++++++++++++++++++ tests/templates/footnotes_tpl.docx | Bin 0 -> 15991 bytes 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 tests/footnotes.py create mode 100644 tests/templates/footnotes_tpl.docx diff --git a/docxtpl/template.py b/docxtpl/template.py index 6babb73..a37dd71 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -357,13 +357,12 @@ def render_footnotes( if jinja_env is None: jinja_env = Environment() - for part in self.docx.sections[0].part.package.parts: - if part.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': - tree = etree.fromstring(part.blob) - for footnote in tree.findall('.//w:t', docx.oxml.ns.nsmap): - if hasattr(footnote, 'text'): - footnote.text = jinja_env.from_string(footnote.text).render(context) - part._blob = etree.tostring(tree) + for section in self.docx.sections: + for part in section.part.package.parts: + if part.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': + xml = self.patch_xml(part.blob.decode('utf8')) + xml = self.render_xml_part(xml, part, context, jinja_env) + part._blob = xml def resolve_listing(self, xml): diff --git a/tests/footnotes.py b/tests/footnotes.py new file mode 100644 index 0000000..f80e1c6 --- /dev/null +++ b/tests/footnotes.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" +Created : 2024-09-23 + +@author: Bart Broere +""" + +from docxtpl import DocxTemplate + +DEST_FILE = "output/footnotes.docx" + +tpl = DocxTemplate("templates/footnotes_tpl.docx") + +context = { + "a_jinja_variable": "A Jinja variable!" +} + +tpl.render(context) +tpl.save(DEST_FILE) \ No newline at end of file diff --git a/tests/templates/footnotes_tpl.docx b/tests/templates/footnotes_tpl.docx new file mode 100644 index 0000000000000000000000000000000000000000..838c002f89cb40a43fc4bdee18638f5b88c4132e GIT binary patch literal 15991 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 literal 0 HcmV?d00001 From 9124386b4cc4cb9e88301dc6412945f32c4fc48e Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Mon, 23 Sep 2024 06:25:22 +0000 Subject: [PATCH 6/8] Fix flake8 --- tests/footnotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/footnotes.py b/tests/footnotes.py index f80e1c6..c6ec59c 100644 --- a/tests/footnotes.py +++ b/tests/footnotes.py @@ -16,4 +16,4 @@ } tpl.render(context) -tpl.save(DEST_FILE) \ No newline at end of file +tpl.save(DEST_FILE) From 0d616add8454db28e178459a2904e03a98ef1dc0 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Thu, 24 Oct 2024 07:27:59 +0200 Subject: [PATCH 7/8] Apply suggestion since part.blob changes type in the loop Co-authored-by: Chatnoir Miki --- docxtpl/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docxtpl/template.py b/docxtpl/template.py index a37dd71..d37911f 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -360,7 +360,7 @@ def render_footnotes( for section in self.docx.sections: for part in section.part.package.parts: if part.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': - xml = self.patch_xml(part.blob.decode('utf8')) + xml = self.patch_xml(part.blob.decode('utf-8') if type(part.blob) is bytes else part.blob) xml = self.render_xml_part(xml, part, context, jinja_env) part._blob = xml From 0ef74a740ef9378c3bc0b8a0b5d19720262fb411 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Thu, 24 Oct 2024 07:28:49 +0200 Subject: [PATCH 8/8] Change to isinstance for type checking --- docxtpl/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docxtpl/template.py b/docxtpl/template.py index d37911f..2a442fa 100644 --- a/docxtpl/template.py +++ b/docxtpl/template.py @@ -360,7 +360,7 @@ def render_footnotes( for section in self.docx.sections: for part in section.part.package.parts: if part.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml': - xml = self.patch_xml(part.blob.decode('utf-8') if type(part.blob) is bytes else part.blob) + xml = self.patch_xml(part.blob.decode('utf-8') if isinstance(part.blob, bytes) else part.blob) xml = self.render_xml_part(xml, part, context, jinja_env) part._blob = xml