This commit is contained in:
Eric Lapouyade 2021-12-20 12:07:12 +01:00
parent d22aeb41db
commit 589262664f
5 changed files with 96 additions and 19 deletions

View File

@ -1,3 +1,8 @@
0.15.0 (2021-12-20)
-------------------
- Multi-rendering with same DocxTemplate object is now possible
see tests/multi_rendering.py
0.14.1 (2021-10-01)
-------------------
- One can now use python -m docxtpl on command line
@ -22,21 +27,9 @@
0.11.5 (2021-05-09)
-------------------
- PR #351
0.11.4 (2021-04-06)
-------------------
- It is now possible to put InlineImage in header/footer
0.11.2 (2020-11-09)
-------------------
- fix #323
0.11.1 (2020-10-27)
-------------------
- fix #320
0.11.0 (2020-10-19)
-------------------
- \\n, \\a, \\t and \\f are now accepted in simple context string. Thanks to chabErch@github
0.10.5 (2020-10-15)

View File

@ -4,7 +4,7 @@ Created : 2015-03-12
@author: Eric Lapouyade
"""
__version__ = '0.14.2'
__version__ = '0.15.0'
# flake8: noqa
from .inline_image import InlineImage

View File

@ -34,15 +34,24 @@ class DocxTemplate(object):
HEADER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
FOOTER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
def __init__(self, docx):
self.docx = Document(docx)
self.crc_to_new_media = {}
self.crc_to_new_embedded = {}
self.zipname_to_replace = {}
self.pics_to_replace = {}
def __init__(self, template_file):
self.template_file = template_file
self.reset_replacements()
self.docx = None
self.is_rendered = False
self.is_saved = False
def init_docx(self):
if not self.docx or self.is_rendered:
self.docx = Document(self.template_file)
self.is_rendered = False
def render_init(self):
self.init_docx()
self.pic_map = {}
self.current_rendering_part = None
self.docx_ids_index = 1000
self.is_saved = False
def __getattr__(self, name):
return getattr(self.docx, name)
@ -53,6 +62,7 @@ class DocxTemplate(object):
return etree.tostring(xml, encoding='unicode', pretty_print=False)
def get_docx(self):
self.init_docx()
return self.docx
def get_xml(self):
@ -307,6 +317,9 @@ class DocxTemplate(object):
self.docx._part._rels[relKey]._target = new_part
def render(self, context, jinja_env=None, autoescape=False):
# init template working attributes
self.render_init()
if autoescape:
if not jinja_env:
jinja_env = Environment(autoescape=autoescape)
@ -337,6 +350,9 @@ class DocxTemplate(object):
for relKey, xml in footers:
self.map_headers_footers_xml(relKey, xml)
# set rendered flag
self.is_rendered = True
# using of TC tag in for cycle can cause that count of columns does not
# correspond to real count of columns in row. This function is able to fix it.
def fix_tables(self, xml):
@ -432,6 +448,7 @@ class DocxTemplate(object):
elt.attrib['id'] = str(self.docx_ids_index)
def new_subdoc(self, docpath=None):
self.init_docx()
return Subdoc(self, docpath)
@staticmethod
@ -540,6 +557,27 @@ class DocxTemplate(object):
with open(dst_file, 'rb') as fh:
self.zipname_to_replace[zipname] = fh.read()
def reset_replacements(self):
"""Reset replacement dictionnaries
This will reset data for image/embedded/zipname replacement
This is useful when calling several times render() with different
image/embedded/zipname replacements without re-instantiating
DocxTemplate object.
In this case, the right sequence for each rendering will be :
- reset_replacements(...)
- replace_zipname(...), replace_media(...) and/or replace_embedded(...),
- render(...)
If you instantiate DocxTemplate object before each render(),
this method is useless.
"""
self.crc_to_new_media = {}
self.crc_to_new_embedded = {}
self.zipname_to_replace = {}
self.pics_to_replace = {}
def post_processing(self, docx_file):
if (self.crc_to_new_media or
self.crc_to_new_embedded or
@ -662,13 +700,19 @@ class DocxTemplate(object):
self.pic_map.update(part_map)
def build_url_id(self, url):
self.init_docx()
return self.docx._part.relate_to(url, REL_TYPE.HYPERLINK,
is_external=True)
def save(self, filename, *args, **kwargs):
# case where save() is called without doing rendering
# ( user wants only to replace image/embedded/zipname )
if not self.is_saved and not self.is_rendered:
self.docx = Document(self.template_file)
self.pre_processing()
self.docx.save(filename, *args, **kwargs)
self.post_processing(filename)
self.is_saved = True
def get_undeclared_template_variables(self, jinja_env=None):
xml = self.get_xml()

40
tests/multi_rendering.py Normal file
View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
'''
Created : 2021-12-20
@author: Eric Lapouyade
'''
from docxtpl import DocxTemplate
tpl = DocxTemplate('templates/multi_rendering_tpl.docx')
documents_data = [
{
'dest_file': 'multi_render1.docx',
'context': {
'title': 'Title ONE',
'body': 'This is the body for first document'
}
},
{
'dest_file': 'multi_render2.docx',
'context': {
'title': 'Title TWO',
'body': 'This is the body for second document'
}
},
{
'dest_file': 'multi_render3.docx',
'context': {
'title': 'Title THREE',
'body': 'This is the body for third document'
}
},
]
for document_data in documents_data:
dest_file = document_data['dest_file']
context = document_data['context']
tpl.render(context)
tpl.save(f'output/{dest_file}')

Binary file not shown.