fix #392
This commit is contained in:
parent
d22aeb41db
commit
589262664f
17
CHANGES.rst
17
CHANGES.rst
@ -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)
|
0.14.1 (2021-10-01)
|
||||||
-------------------
|
-------------------
|
||||||
- One can now use python -m docxtpl on command line
|
- One can now use python -m docxtpl on command line
|
||||||
@ -22,21 +27,9 @@
|
|||||||
0.11.5 (2021-05-09)
|
0.11.5 (2021-05-09)
|
||||||
-------------------
|
-------------------
|
||||||
- PR #351
|
- PR #351
|
||||||
|
|
||||||
0.11.4 (2021-04-06)
|
|
||||||
-------------------
|
|
||||||
- It is now possible to put InlineImage in header/footer
|
- It is now possible to put InlineImage in header/footer
|
||||||
|
|
||||||
0.11.2 (2020-11-09)
|
|
||||||
-------------------
|
|
||||||
- fix #323
|
- fix #323
|
||||||
|
|
||||||
0.11.1 (2020-10-27)
|
|
||||||
-------------------
|
|
||||||
- fix #320
|
- fix #320
|
||||||
|
|
||||||
0.11.0 (2020-10-19)
|
|
||||||
-------------------
|
|
||||||
- \\n, \\a, \\t and \\f are now accepted in simple context string. Thanks to chabErch@github
|
- \\n, \\a, \\t and \\f are now accepted in simple context string. Thanks to chabErch@github
|
||||||
|
|
||||||
0.10.5 (2020-10-15)
|
0.10.5 (2020-10-15)
|
||||||
|
|||||||
@ -4,7 +4,7 @@ Created : 2015-03-12
|
|||||||
|
|
||||||
@author: Eric Lapouyade
|
@author: Eric Lapouyade
|
||||||
"""
|
"""
|
||||||
__version__ = '0.14.2'
|
__version__ = '0.15.0'
|
||||||
|
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
from .inline_image import InlineImage
|
from .inline_image import InlineImage
|
||||||
|
|||||||
@ -34,15 +34,24 @@ class DocxTemplate(object):
|
|||||||
HEADER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
|
HEADER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
|
||||||
FOOTER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
|
FOOTER_URI = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
|
||||||
|
|
||||||
def __init__(self, docx):
|
def __init__(self, template_file):
|
||||||
self.docx = Document(docx)
|
self.template_file = template_file
|
||||||
self.crc_to_new_media = {}
|
self.reset_replacements()
|
||||||
self.crc_to_new_embedded = {}
|
self.docx = None
|
||||||
self.zipname_to_replace = {}
|
self.is_rendered = False
|
||||||
self.pics_to_replace = {}
|
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.pic_map = {}
|
||||||
self.current_rendering_part = None
|
self.current_rendering_part = None
|
||||||
self.docx_ids_index = 1000
|
self.docx_ids_index = 1000
|
||||||
|
self.is_saved = False
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self.docx, name)
|
return getattr(self.docx, name)
|
||||||
@ -53,6 +62,7 @@ class DocxTemplate(object):
|
|||||||
return etree.tostring(xml, encoding='unicode', pretty_print=False)
|
return etree.tostring(xml, encoding='unicode', pretty_print=False)
|
||||||
|
|
||||||
def get_docx(self):
|
def get_docx(self):
|
||||||
|
self.init_docx()
|
||||||
return self.docx
|
return self.docx
|
||||||
|
|
||||||
def get_xml(self):
|
def get_xml(self):
|
||||||
@ -307,6 +317,9 @@ class DocxTemplate(object):
|
|||||||
self.docx._part._rels[relKey]._target = new_part
|
self.docx._part._rels[relKey]._target = new_part
|
||||||
|
|
||||||
def render(self, context, jinja_env=None, autoescape=False):
|
def render(self, context, jinja_env=None, autoescape=False):
|
||||||
|
# init template working attributes
|
||||||
|
self.render_init()
|
||||||
|
|
||||||
if autoescape:
|
if autoescape:
|
||||||
if not jinja_env:
|
if not jinja_env:
|
||||||
jinja_env = Environment(autoescape=autoescape)
|
jinja_env = Environment(autoescape=autoescape)
|
||||||
@ -337,6 +350,9 @@ class DocxTemplate(object):
|
|||||||
for relKey, xml in footers:
|
for relKey, xml in footers:
|
||||||
self.map_headers_footers_xml(relKey, xml)
|
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
|
# 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.
|
# correspond to real count of columns in row. This function is able to fix it.
|
||||||
def fix_tables(self, xml):
|
def fix_tables(self, xml):
|
||||||
@ -432,6 +448,7 @@ class DocxTemplate(object):
|
|||||||
elt.attrib['id'] = str(self.docx_ids_index)
|
elt.attrib['id'] = str(self.docx_ids_index)
|
||||||
|
|
||||||
def new_subdoc(self, docpath=None):
|
def new_subdoc(self, docpath=None):
|
||||||
|
self.init_docx()
|
||||||
return Subdoc(self, docpath)
|
return Subdoc(self, docpath)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -540,6 +557,27 @@ class DocxTemplate(object):
|
|||||||
with open(dst_file, 'rb') as fh:
|
with open(dst_file, 'rb') as fh:
|
||||||
self.zipname_to_replace[zipname] = fh.read()
|
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):
|
def post_processing(self, docx_file):
|
||||||
if (self.crc_to_new_media or
|
if (self.crc_to_new_media or
|
||||||
self.crc_to_new_embedded or
|
self.crc_to_new_embedded or
|
||||||
@ -662,13 +700,19 @@ class DocxTemplate(object):
|
|||||||
self.pic_map.update(part_map)
|
self.pic_map.update(part_map)
|
||||||
|
|
||||||
def build_url_id(self, url):
|
def build_url_id(self, url):
|
||||||
|
self.init_docx()
|
||||||
return self.docx._part.relate_to(url, REL_TYPE.HYPERLINK,
|
return self.docx._part.relate_to(url, REL_TYPE.HYPERLINK,
|
||||||
is_external=True)
|
is_external=True)
|
||||||
|
|
||||||
def save(self, filename, *args, **kwargs):
|
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.pre_processing()
|
||||||
self.docx.save(filename, *args, **kwargs)
|
self.docx.save(filename, *args, **kwargs)
|
||||||
self.post_processing(filename)
|
self.post_processing(filename)
|
||||||
|
self.is_saved = True
|
||||||
|
|
||||||
def get_undeclared_template_variables(self, jinja_env=None):
|
def get_undeclared_template_variables(self, jinja_env=None):
|
||||||
xml = self.get_xml()
|
xml = self.get_xml()
|
||||||
|
|||||||
40
tests/multi_rendering.py
Normal file
40
tests/multi_rendering.py
Normal 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}')
|
||||||
BIN
tests/templates/multi_rendering_tpl.docx
Normal file
BIN
tests/templates/multi_rendering_tpl.docx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user