From 44a08196253514c4aab3788a23f6a22c365c71b5 Mon Sep 17 00:00:00 2001 From: Eric Lapouyade Date: Mon, 19 Oct 2020 13:59:36 +0200 Subject: [PATCH] \n, \a, \t and \f are now accepted in simple context string --- CHANGES.rst | 22 +++---------- docs/index.rst | 4 ++- docxtpl/__init__.py | 56 ++++++++++++++++++++++---------- tests/escape.py | 15 ++++++--- tests/templates/escape_tpl.docx | Bin 9506 -> 9910 bytes 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 33b3165..a81d7cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,17 +1,10 @@ +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) ------------------- -- Revert 0.10.4 as the XML code get corrupted - -0.10.4 (2020-10-15) -------------------- -- \\n \\t and \\f are now accepted in simple context string (#307, #312) - -0.10.1 (2020-08-23) -------------------- - Remove extension testing (#297) - -0.10.0 (2020-05-25) -------------------- - Fix spaces missing in some cases (#116, #227) 0.9.2 (2020-04-26) @@ -19,16 +12,9 @@ - Fix #271 - Code styling -0.9.0 (2020-04-15) -------------------- -- New syntax : {%- and -%} to merge lines/paragraphs - 0.8.1 (2020-04-14) ------------------- - fix #266 - -0.8.0 (2020-04-10) -------------------- - docxtpl is now able to use latest python-docx (0.8.10). Thanks to Dutchy-@github. 0.7.0 (2020-04-09) diff --git a/docs/index.rst b/docs/index.rst index 29291fa..16c844e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -142,6 +142,8 @@ As part of jinja2, one can used double braces:: {{ }} +if ```` is a string, ``\n``, ``\a``, ``\t`` and ``\f`` will be translated respectively into newlines, new paragraphs, tabs and page breaks + But if ```` is a RichText_ object, you must specify that you are changing the actual 'run':: {{r }} @@ -240,7 +242,7 @@ especially ``<``, ``>`` and ``&``. In order to use them, you must escape them. T * ``context = { 'var':escape('my text')}`` and ``{{ }}`` in the template. * enable autoescaping when calling render method: ``tpl.render(context, autoescape=True)`` (default is autoescape=False) -The ``RichText()`` or ``R()`` offers newline, new paragraph, and page break features : just use ``\n``, ``\a``, or ``\f`` in the +The ``RichText()`` or ``R()`` offers newline, new paragraph, and page break features : just use ``\n``, ``\a``, ``\t`` or ``\f`` in the text, they will be converted accordingly. See tests/escape.py example for more informations. diff --git a/docxtpl/__init__.py b/docxtpl/__init__.py index 1bef924..51c1646 100644 --- a/docxtpl/__init__.py +++ b/docxtpl/__init__.py @@ -4,11 +4,10 @@ Created : 2015-03-12 @author: Eric Lapouyade ''' +__version__ = '0.11.0' + import functools import io - -__version__ = '0.10.5' - from lxml import etree from docx import Document from docx.opc.oxml import parse_xml @@ -28,11 +27,6 @@ import binascii import os import zipfile -NEWLINE_XML = '' -NEWPARAGRAPH_XML = '' -TAB_XML = '' -PAGE_BREAK = '' - class DocxTemplate(object): """ Class for managing docx files as they were jinja2 templates """ @@ -234,8 +228,42 @@ class DocxTemplate(object): .replace('}_}', '}}') .replace('{_%', '{%') .replace('%_}', '%}')) + dst_xml = self.resolve_listing(dst_xml) return dst_xml + def resolve_listing(self, xml): + + def resolve_text(run_properties, paragraph_properties, m): + xml = m[0].replace('\t', '' + '%s' + '%s' % (run_properties, run_properties)) + xml = xml.replace('\a', '' + '%s%s' % (paragraph_properties, run_properties)) + xml = xml.replace('\n', '') + xml = xml.replace('\f', '' + '' + '%s%s' % (paragraph_properties, run_properties)) + return xml + + def resolve_run(paragraph_properties, m): + run_properties = re.search(r'.*', m[0]) + run_properties = run_properties[0] if run_properties else '' + + p_resolve_text = lambda x:resolve_text(run_properties, paragraph_properties, x) + return re.sub(r']*)?>.*?', p_resolve_text, m[0], flags=re.DOTALL) + + def resolve_paragraph(m): + paragraph_properties = re.search(r'.*', m[0]) + paragraph_properties = paragraph_properties[0] if paragraph_properties else '' + + p_resolve_run = lambda x:resolve_run(paragraph_properties, x) + + return re.sub(r']*)?>.*?', p_resolve_run, m[0], flags=re.DOTALL) + + xml = re.sub(r']*)?>.*?', resolve_paragraph, xml, flags=re.DOTALL) + + return xml + def build_xml(self, context, jinja_env=None): xml = self.get_xml() xml = self.patch_xml(xml) @@ -684,11 +712,7 @@ 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) - .replace('\f', PAGE_BREAK)) + text = escape(text) prop = u'' @@ -756,11 +780,7 @@ class Listing(object): # 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) - .replace('\t', TAB_XML) - .replace('\f', PAGE_BREAK)) + self.xml = escape(text) def __unicode__(self): return self.xml diff --git a/tests/escape.py b/tests/escape.py index a18b926..865d693 100644 --- a/tests/escape.py +++ b/tests/escape.py @@ -7,13 +7,20 @@ context = { '"less than" must be escaped : <, this can be done with RichText() or R()' ), 'myescvar': 'It can be escaped with a "|e" jinja filter in the template too : < ', - 'nlnp': R( - 'Here is a multiple\nlines\nstring\aand some\aother\aparagraphs\aNOTE: the current character styling is removed' - ), + 'nlnp': R('Here is a multiple\nlines\nstring\aand some\aother\aparagraphs', + color='#ff00ff'), 'mylisting': Listing( - 'the listing\nwith\nsome\nlines\nand special chars : <>&\f ... and a page break' + 'the listing\nwith\nsome\nlines\nand special chars : <>& ...' ), 'page_break': R('\f'), + 'new_listing':""" +This is a new listing +Now, does not require Listing() Object +Here is a \t tab\a +Here is a new paragraph\a +Here is a page break : \f +That's it +""", } tpl.render(context) diff --git a/tests/templates/escape_tpl.docx b/tests/templates/escape_tpl.docx index d676492f352ecd382a311cb9cfad367f9311dfa5..d377494f5c3abf9bcbe00d0f79cd80c38dc8f650 100644 GIT binary patch delta 7427 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 delta 6810 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 YXFPwWpWr_%j?#eQ8R$_|=pMEI1r{cF_W%F@