From e5c4c149663dbd38dff85de2fc5e4294c92d5a98 Mon Sep 17 00:00:00 2001 From: Eric Lapouyade Date: Tue, 20 Nov 2018 15:30:12 +0100 Subject: [PATCH] - Smart quotes in jinja tags are now converted into simple quotes - Add custom jinja filter example in tests/ - Reformat the code to be a little more PEP8 compliant --- CHANGES.rst | 6 + docxtpl/__init__.py | 153 ++++++++++++------ tests/custom_jinja_filters.py | 33 ++++ tests/templates/custom_jinja_filters_tpl.docx | Bin 0 -> 20679 bytes 4 files changed, 144 insertions(+), 48 deletions(-) create mode 100644 tests/custom_jinja_filters.py create mode 100644 tests/templates/custom_jinja_filters_tpl.docx diff --git a/CHANGES.rst b/CHANGES.rst index 0975d5c..24c8fcc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +0.5.10 (2018-11-20) +------------------- +- Smart quotes in jinja tags are now converted into simple quotes +- Add custom jinja filter example in tests/ +- Reformat the code to be a little more PEP8 compliant + 0.5.9 (2018-11-18) ------------------ - Add {% hm %} tag for table columns horizontal merging (Thanks to nickgashkov) diff --git a/docxtpl/__init__.py b/docxtpl/__init__.py index 71cac6a..d17cd7a 100644 --- a/docxtpl/__init__.py +++ b/docxtpl/__init__.py @@ -5,7 +5,7 @@ Created : 2015-03-12 @author: Eric Lapouyade ''' -__version__ = '0.5.9' +__version__ = '0.5.10' from lxml import etree from docx import Document @@ -49,7 +49,8 @@ class DocxTemplate(object): return getattr(self.docx, name) def xml_to_string(self, xml, encoding='unicode'): - # Be careful : pretty_print MUST be set to False, otherwise patch_xml() won't work properly + # Be careful : pretty_print MUST be set to False, otherwise patch_xml() + # won't work properly return etree.tostring(xml, encoding='unicode', pretty_print=False) def get_docx(self): @@ -63,34 +64,47 @@ class DocxTemplate(object): fh.write(self.get_xml()) def patch_xml(self,src_xml): - # strip all xml tags inside {% %} and {{ }} that MS word can insert into xml source - # also unescape html entities - src_xml = re.sub(r'(?<={)(<[^>]*>)+(?=[\{%])|(?<=[%\}])(<[^>]*>)+(?=\})','',src_xml,flags=re.DOTALL) + # strip all xml tags inside {% %} and {{ }} that MS word can insert + # into xml source also unescape html entities + src_xml = re.sub(r'(?<={)(<[^>]*>)+(?=[\{%])|(?<=[%\}])(<[^>]*>)+(?=\})','', + src_xml,flags=re.DOTALL) def striptags(m): - return re.sub('.*?(|]*>)','',m.group(0),flags=re.DOTALL) - src_xml = re.sub(r'{%(?:(?!%}).)*|{{(?:(?!}}).)*',striptags,src_xml,flags=re.DOTALL) + return re.sub('.*?(|]*>)','', + m.group(0),flags=re.DOTALL) + src_xml = re.sub(r'{%(?:(?!%}).)*|{{(?:(?!}}).)*',striptags, + src_xml,flags=re.DOTALL) # manage table cell colspan def colspan(m): cell_xml = m.group(1) + m.group(3) - cell_xml = re.sub(r'](?:(?!]).)*.*?','',cell_xml,flags=re.DOTALL) + cell_xml = re.sub(r'](?:(?!]).)*.*?', + '', cell_xml,flags=re.DOTALL) cell_xml = re.sub(r'','', cell_xml, count=1) - return re.sub(r'(]*>)',r'\1' % m.group(2), cell_xml) - src_xml = re.sub(r'(](?:(?!]).)*){%\s*colspan\s+([^%]*)\s*%}(.*?)',colspan,src_xml,flags=re.DOTALL) + return re.sub(r'(]*>)',r'\1' + % m.group(2), cell_xml ) + src_xml = re.sub(r'(](?:(?!]).)*){%\s*colspan\s+([^%]*)\s*%}(.*?)', + colspan,src_xml,flags=re.DOTALL) # manage table cell background color def cellbg(m): cell_xml = m.group(1) + m.group(3) - cell_xml = re.sub(r'](?:(?!]).)*.*?','',cell_xml,flags=re.DOTALL) + cell_xml = re.sub(r'](?:(?!]).)*.*?', + '',cell_xml,flags=re.DOTALL) cell_xml = re.sub(r'','', cell_xml, count=1) - return re.sub(r'(]*>)',r'\1' % m.group(2), cell_xml) - src_xml = re.sub(r'(](?:(?!]).)*){%\s*cellbg\s+([^%]*)\s*%}(.*?)',cellbg,src_xml,flags=re.DOTALL) + return re.sub(r'(]*>)', + r'\1' + % m.group(2), cell_xml) + src_xml = re.sub(r'(](?:(?!]).)*){%\s*cellbg\s+([^%]*)\s*%}(.*?)', + cellbg,src_xml,flags=re.DOTALL) # avoid {{r and {%r tags to strip MS xml tags too far - src_xml = re.sub(r'({{r\s.*?}}|{%r\s.*?%})',r'\1',src_xml,flags=re.DOTALL) + src_xml = re.sub(r'({{r\s.*?}}|{%r\s.*?%})', + r'\1', + src_xml,flags=re.DOTALL) for y in ['tr', 'tc', 'p', 'r']: - # replace into xml code the row/paragraph/run containing {%y xxx %} or {{y xxx}} template tag + # replace into xml code the row/paragraph/run containing + # {%y xxx %} or {{y xxx}} template tag # by {% xxx %} or {{ xx }} without any surronding tags : # This is mandatory to have jinja2 generating correct xml code pat = r'](?:(?!]).)*({%%|{{)%(y)s ([^}%%]*(?:%%}|}})).*?' % {'y':y} @@ -115,7 +129,8 @@ class DocxTemplate(object): m.group(), # Everything between ```` and ```` with ``{% vm %}`` inside. flags=re.DOTALL, ) - src_xml = re.sub(r'](?:(?!]).)*?{%\s*vm\s*%}.*?]', v_merge_tc, src_xml, flags=re.DOTALL) + src_xml = re.sub(r'](?:(?!]).)*?{%\s*vm\s*%}.*?]', + v_merge_tc, src_xml, flags=re.DOTALL) # Use ``{% hm %}`` to make table cell become horizontally merged within # a ``{% for %}``. @@ -165,10 +180,16 @@ class DocxTemplate(object): # Discard every other cell generated in loop. return "{% if loop.first %}" + xml + "{% endif %}" - src_xml = re.sub(r'](?:(?!]).)*?{%\s*hm\s*%}.*?]', h_merge_tc, src_xml, flags=re.DOTALL) + src_xml = re.sub(r'](?:(?!]).)*?{%\s*hm\s*%}.*?]', + h_merge_tc, src_xml, flags=re.DOTALL) def clean_tags(m): - return m.group(0).replace(r"‘","'").replace('<','<').replace('>','>') + return ( m.group(0) + .replace(r"‘","'") + .replace('<','<') + .replace('>','>') + .replace(u"‘",u"'") + .replace(u"’",u"'") ) src_xml = re.sub(r'(?<=\{[\{%])(.*?)(?=[\}%]})',clean_tags,src_xml) return src_xml @@ -184,10 +205,15 @@ class DocxTemplate(object): except TemplateError as exc: if hasattr(exc, 'lineno') and exc.lineno is not None: line_number = max(exc.lineno - 4, 0) - exc.docx_context = map(lambda x: re.sub(r'<[^>]+>', '', x), src_xml.splitlines()[line_number:(line_number + 7)]) + exc.docx_context = map(lambda x: re.sub(r'<[^>]+>', '', x), + src_xml.splitlines()[line_number:(line_number + 7)]) raise exc dst_xml = dst_xml.replace('\n', '') - dst_xml = dst_xml.replace('{_{','{{').replace('}_}','}}').replace('{_%','{%').replace('%_}','%}') + dst_xml = ( dst_xml + .replace('{_{','{{') + .replace('}_}','}}') + .replace('{_%','{%') + .replace('%_}','%}') ) return dst_xml def build_xml(self,context,jinja_env=None): @@ -263,15 +289,19 @@ class DocxTemplate(object): self.map_tree(tree) # Headers - for relKey, xml in self.build_headers_footers_xml(context, self.HEADER_URI, jinja_env): + headers = self.build_headers_footers_xml(context, self.HEADER_URI, + jinja_env) + for relKey, xml in headers: self.map_headers_footers_xml(relKey, xml) # Footers - for relKey, xml in self.build_headers_footers_xml(context, self.FOOTER_URI, jinja_env): + footers = self.build_headers_footers_xml(context, self.FOOTER_URI, + jinja_env) + for relKey, xml in footers: self.map_headers_footers_xml(relKey, xml) - # 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. + # 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): parser = etree.XMLParser(recover=True) tree = etree.fromstring(xml, parser=parser) @@ -302,10 +332,12 @@ class DocxTemplate(object): new_average = width / (len(columns) + to_add) # scale the old columns for c in columns: - c.set(ns+'w', str(int(float(c.get(ns+'w')) * new_average/old_average))) + c.set(ns+'w', str(int(float(c.get(ns+'w')) * + new_average/old_average))) # add new columns for i in range(to_add): - etree.SubElement(tblGrid, ns+'gridCol', {ns+'w': str(int(new_average))}) + etree.SubElement(tblGrid, ns+'gridCol', + {ns+'w': str(int(new_average))}) return tree def new_subdoc(self,docpath=None): @@ -321,13 +353,16 @@ class DocxTemplate(object): def replace_media(self,src_file,dst_file): """Replace one media by another one into a docx - This has been done mainly because it is not possible to add images in docx header/footer. - With this function, put a dummy picture in your header/footer, then specify it with its replacement in this function + This has been done mainly because it is not possible to add images in + docx header/footer. + With this function, put a dummy picture in your header/footer, + then specify it with its replacement in this function Syntax: tpl.replace_media('dummy_media_to_replace.png','media_to_paste.jpg') Note: for images, the aspect ratio will be the same as the replaced image - Note2 : it is important to have the source media file as it is required to calculate its CRC to find them in the docx + Note2 : it is important to have the source media file as it is required + to calculate its CRC to find them in the docx """ with open(dst_file, 'rb') as fh: crc = self.get_file_crc(src_file) @@ -344,7 +379,8 @@ class DocxTemplate(object): in case dst_file is a file-like object, no check is done on format compatibility 2) the aspect ratio will be the same as the replaced image - 3) There is no need to keep the original file (this is not the case for replace_embedded and replace_media) + 3) There is no need to keep the original file (this is not the case + for replace_embedded and replace_media) """ if hasattr(dst_file,'read'): @@ -363,12 +399,15 @@ class DocxTemplate(object): def replace_embedded(self,src_file,dst_file): """Replace one embdded object by another one into a docx - This has been done mainly because it is not possible to add images in docx header/footer. - With this function, put a dummy picture in your header/footer, then specify it with its replacement in this function + This has been done mainly because it is not possible to add images + in docx header/footer. + With this function, put a dummy picture in your header/footer, + then specify it with its replacement in this function Syntax: tpl.replace_embedded('dummy_doc.docx','doc_to_paste.docx') - Note2 : it is important to have the source file as it is required to calculate its CRC to find them in the docx + Note2 : it is important to have the source file as it is required to + calculate its CRC to find them in the docx """ with open(dst_file, 'rb') as fh: crc = self.get_file_crc(src_file) @@ -383,9 +422,11 @@ class DocxTemplate(object): with zipfile.ZipFile(docx_filename, 'w') as zout: for item in zin.infolist(): buf = zin.read(item.filename) - if item.filename.startswith('word/media/') and item.CRC in self.crc_to_new_media: + if ( item.filename.startswith('word/media/') and + item.CRC in self.crc_to_new_media ): zout.writestr(item, self.crc_to_new_media[item.CRC]) - elif item.filename.startswith('word/embeddings/') and item.CRC in self.crc_to_new_embedded: + elif ( item.filename.startswith('word/embeddings/') + and item.CRC in self.crc_to_new_embedded ): zout.writestr(item, self.crc_to_new_embedded[item.CRC]) else: zout.writestr(item, buf) @@ -400,11 +441,13 @@ class DocxTemplate(object): # Do the actual replacement for embedded_file,stream in six.iteritems(self.pic_to_replace): if embedded_file not in self.pic_map: - raise ValueError('Picture "%s" not found in the docx template' % embedded_file) + raise ValueError('Picture "%s" not found in the docx template' + % embedded_file) self.pic_map[embedded_file][1]._blob=stream def build_pic_map(self): - """Searches in docx template all the xml pictures tag and store them in pic_map dict""" + """Searches in docx template all the xml pictures tag and store them + in pic_map dict""" if self.pic_to_replace: # Main document part=self.docx.part @@ -431,7 +474,8 @@ class DocxTemplate(object): try: if gd.attrib['uri']==docx.oxml.ns.nsmap['pic']: # Either PICTURE or LINKED_PICTURE image - blip=gd.xpath('pic:pic/pic:blipFill/a:blip',namespaces=docx.oxml.ns.nsmap)[0] + blip=gd.xpath('pic:pic/pic:blipFill/a:blip', + namespaces=docx.oxml.ns.nsmap)[0] dest=blip.xpath('@r:embed',namespaces=docx.oxml.ns.nsmap) if len(dest)>0: rel=dest[0] @@ -441,9 +485,11 @@ class DocxTemplate(object): continue #title=inl.xpath('wp:docPr/@title',namespaces=docx.oxml.ns.nsmap)[0] - name=gd.xpath('pic:pic/pic:nvPicPr/pic:cNvPr/@name',namespaces=docx.oxml.ns.nsmap)[0] + name=gd.xpath('pic:pic/pic:nvPicPr/pic:cNvPr/@name', + namespaces=docx.oxml.ns.nsmap)[0] - part_map[name]=(doc_part.rels[rel].target_ref,doc_part.rels[rel].target_part) + part_map[name]=(doc_part.rels[rel].target_ref, + doc_part.rels[rel].target_part) except: continue @@ -474,7 +520,8 @@ class Subdoc(object): 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)) + xml = re.sub(r']*>','',etree.tostring( + self.subdocx._element.body, encoding='unicode', pretty_print=False)) return xml def __unicode__(self): @@ -489,7 +536,8 @@ class Subdoc(object): class RichText(object): """ class to generate Rich Text when using templates variables - This is much faster than using Subdoc class, but this only for texts INSIDE an existing paragraph. + This is much faster than using Subdoc class, + but this only for texts INSIDE an existing paragraph. """ def __init__(self, text=None, **text_prop): self.xml = '' @@ -515,7 +563,10 @@ class RichText(object): text = six.text_type(text) if not isinstance(text, six.text_type): text = text.decode('utf-8',errors='ignore') - text = escape(text).replace('\n', NEWLINE_XML).replace('\a', NEWPARAGRAPH_XML).replace('\t',TAB_XML) + text = ( escape(text) + .replace('\n', NEWLINE_XML) + .replace('\a', NEWPARAGRAPH_XML) + .replace('\t',TAB_XML) ) prop = u'' @@ -547,7 +598,8 @@ class RichText(object): if strike: prop += u'' if font: - prop += u''.format(font=font) + prop += ( u'' + .format(font=font) ) xml = u'' @@ -555,7 +607,8 @@ class RichText(object): xml += u'%s' % prop xml += u'%s' % text if url_id: - xml = u'%s' % (url_id, xml) + xml = ( u'%s' + % (url_id, xml) ) self.xml += xml @@ -571,15 +624,19 @@ class RichText(object): R = RichText class Listing(object): - r"""class to manage \n and \a without to use RichText, by this way you keep the current template styling + r"""class to manage \n and \a without to use RichText, + by this way you keep the current template styling - use {{ mylisting }} in your template and context={ mylisting:Listing(the_listing_with_newlines) } + use {{ mylisting }} in your template and + context={ mylisting:Listing(the_listing_with_newlines) } """ def __init__(self, text): # If not a string : cast to string (ex: int, dict etc...) if not isinstance(text, (six.text_type, six.binary_type)): text = six.text_type(text) - self.xml = escape(text).replace('\n', NEWLINE_XML).replace('\a', NEWPARAGRAPH_XML) + self.xml = ( escape(text) + .replace('\n', NEWLINE_XML) + .replace('\a', NEWPARAGRAPH_XML) ) def __unicode__(self): return self.xml diff --git a/tests/custom_jinja_filters.py b/tests/custom_jinja_filters.py new file mode 100644 index 0000000..74db42c --- /dev/null +++ b/tests/custom_jinja_filters.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +''' +Created : 2015-03-12 + +@author: sandeeprah, Eric Lapouyade +''' + +from docxtpl import DocxTemplate +import jinja2 + +jinja_env = jinja2.Environment() + +# to create new filters, first create functions that accept the value to filter +# as first argument, and filter parameters as next arguments +def my_filterA(value, my_string_arg): + return_value = value + ' ' + my_string_arg + return return_value + + +def my_filterB(value, my_float_arg): + return_value = value + my_float_arg + return return_value + +# Then, declare them to jinja like this : +jinja_env.filters['my_filterA'] = my_filterA +jinja_env.filters['my_filterB'] = my_filterB + + +context = {'base_value_string' : ' Hello', 'base_value_float' : 1.5 } + +tpl=DocxTemplate('templates/custom_jinja_filters_tpl.docx') +tpl.render(context, jinja_env) +tpl.save('output/custom_jinja_filters.docx') \ No newline at end of file diff --git a/tests/templates/custom_jinja_filters_tpl.docx b/tests/templates/custom_jinja_filters_tpl.docx new file mode 100644 index 0000000000000000000000000000000000000000..0a20b7a319e5dc1579d537e921e3e350d28fd6ea GIT binary patch literal 20679 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 ffcx~z?erH~