\n, \a, \t and \f are now accepted in simple context string
This commit is contained in:
parent
b616daf50a
commit
44a0819625
22
CHANGES.rst
22
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)
|
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)
|
- Remove extension testing (#297)
|
||||||
|
|
||||||
0.10.0 (2020-05-25)
|
|
||||||
-------------------
|
|
||||||
- Fix spaces missing in some cases (#116, #227)
|
- Fix spaces missing in some cases (#116, #227)
|
||||||
|
|
||||||
0.9.2 (2020-04-26)
|
0.9.2 (2020-04-26)
|
||||||
@ -19,16 +12,9 @@
|
|||||||
- Fix #271
|
- Fix #271
|
||||||
- Code styling
|
- Code styling
|
||||||
|
|
||||||
0.9.0 (2020-04-15)
|
|
||||||
-------------------
|
|
||||||
- New syntax : {%- and -%} to merge lines/paragraphs
|
|
||||||
|
|
||||||
0.8.1 (2020-04-14)
|
0.8.1 (2020-04-14)
|
||||||
-------------------
|
-------------------
|
||||||
- fix #266
|
- fix #266
|
||||||
|
|
||||||
0.8.0 (2020-04-10)
|
|
||||||
-------------------
|
|
||||||
- docxtpl is now able to use latest python-docx (0.8.10). Thanks to Dutchy-@github.
|
- docxtpl is now able to use latest python-docx (0.8.10). Thanks to Dutchy-@github.
|
||||||
|
|
||||||
0.7.0 (2020-04-09)
|
0.7.0 (2020-04-09)
|
||||||
|
|||||||
@ -142,6 +142,8 @@ As part of jinja2, one can used double braces::
|
|||||||
|
|
||||||
{{ <var> }}
|
{{ <var> }}
|
||||||
|
|
||||||
|
if ``<var>`` is a string, ``\n``, ``\a``, ``\t`` and ``\f`` will be translated respectively into newlines, new paragraphs, tabs and page breaks
|
||||||
|
|
||||||
But if ``<var>`` is a RichText_ object, you must specify that you are changing the actual 'run'::
|
But if ``<var>`` is a RichText_ object, you must specify that you are changing the actual 'run'::
|
||||||
|
|
||||||
{{r <var> }}
|
{{r <var> }}
|
||||||
@ -240,7 +242,7 @@ especially ``<``, ``>`` and ``&``. In order to use them, you must escape them. T
|
|||||||
* ``context = { 'var':escape('my text')}`` and ``{{ <var> }}`` in the template.
|
* ``context = { 'var':escape('my text')}`` and ``{{ <var> }}`` in the template.
|
||||||
* enable autoescaping when calling render method: ``tpl.render(context, autoescape=True)`` (default is autoescape=False)
|
* 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.
|
text, they will be converted accordingly.
|
||||||
|
|
||||||
See tests/escape.py example for more informations.
|
See tests/escape.py example for more informations.
|
||||||
|
|||||||
@ -4,11 +4,10 @@ Created : 2015-03-12
|
|||||||
|
|
||||||
@author: Eric Lapouyade
|
@author: Eric Lapouyade
|
||||||
'''
|
'''
|
||||||
|
__version__ = '0.11.0'
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import io
|
import io
|
||||||
|
|
||||||
__version__ = '0.10.5'
|
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from docx import Document
|
from docx import Document
|
||||||
from docx.opc.oxml import parse_xml
|
from docx.opc.oxml import parse_xml
|
||||||
@ -28,11 +27,6 @@ import binascii
|
|||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
NEWLINE_XML = '</w:t><w:br/><w:t xml:space="preserve">'
|
|
||||||
NEWPARAGRAPH_XML = '</w:t></w:r></w:p><w:p><w:r><w:t xml:space="preserve">'
|
|
||||||
TAB_XML = '</w:t></w:r><w:r><w:tab/></w:r><w:r><w:t xml:space="preserve">'
|
|
||||||
PAGE_BREAK = '</w:t><w:br w:type="page"/><w:t xml:space="preserve">'
|
|
||||||
|
|
||||||
|
|
||||||
class DocxTemplate(object):
|
class DocxTemplate(object):
|
||||||
""" Class for managing docx files as they were jinja2 templates """
|
""" Class for managing docx files as they were jinja2 templates """
|
||||||
@ -234,8 +228,42 @@ class DocxTemplate(object):
|
|||||||
.replace('}_}', '}}')
|
.replace('}_}', '}}')
|
||||||
.replace('{_%', '{%')
|
.replace('{_%', '{%')
|
||||||
.replace('%_}', '%}'))
|
.replace('%_}', '%}'))
|
||||||
|
dst_xml = self.resolve_listing(dst_xml)
|
||||||
return dst_xml
|
return dst_xml
|
||||||
|
|
||||||
|
def resolve_listing(self, xml):
|
||||||
|
|
||||||
|
def resolve_text(run_properties, paragraph_properties, m):
|
||||||
|
xml = m[0].replace('\t', '</w:t></w:r>'
|
||||||
|
'<w:r>%s<w:tab/></w:r>'
|
||||||
|
'<w:r>%s<w:t xml:space="preserve">' % (run_properties, run_properties))
|
||||||
|
xml = xml.replace('\a', '</w:t></w:r></w:p>'
|
||||||
|
'<w:p>%s<w:r>%s<w:t xml:space="preserve">' % (paragraph_properties, run_properties))
|
||||||
|
xml = xml.replace('\n', '</w:t><w:br/><w:t xml:space="preserve">')
|
||||||
|
xml = xml.replace('\f', '</w:t></w:r></w:p>'
|
||||||
|
'<w:p><w:r><w:br w:type="page"/></w:r></w:p>'
|
||||||
|
'<w:p>%s<w:r>%s<w:t xml:space="preserve">' % (paragraph_properties, run_properties))
|
||||||
|
return xml
|
||||||
|
|
||||||
|
def resolve_run(paragraph_properties, m):
|
||||||
|
run_properties = re.search(r'<w:rPr>.*</w:rPr>', 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'<w:t(?:[^>]*)?>.*?</w:t>', p_resolve_text, m[0], flags=re.DOTALL)
|
||||||
|
|
||||||
|
def resolve_paragraph(m):
|
||||||
|
paragraph_properties = re.search(r'<w:pPr>.*</w:pPr>', 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'<w:r(?:[^>]*)?>.*?</w:r>', p_resolve_run, m[0], flags=re.DOTALL)
|
||||||
|
|
||||||
|
xml = re.sub(r'<w:p(?:[^>]*)?>.*?</w:p>', resolve_paragraph, xml, flags=re.DOTALL)
|
||||||
|
|
||||||
|
return xml
|
||||||
|
|
||||||
def build_xml(self, context, jinja_env=None):
|
def build_xml(self, context, jinja_env=None):
|
||||||
xml = self.get_xml()
|
xml = self.get_xml()
|
||||||
xml = self.patch_xml(xml)
|
xml = self.patch_xml(xml)
|
||||||
@ -684,11 +712,7 @@ class RichText(object):
|
|||||||
text = six.text_type(text)
|
text = six.text_type(text)
|
||||||
if not isinstance(text, six.text_type):
|
if not isinstance(text, six.text_type):
|
||||||
text = text.decode('utf-8', errors='ignore')
|
text = text.decode('utf-8', errors='ignore')
|
||||||
text = (escape(text)
|
text = escape(text)
|
||||||
.replace('\n', NEWLINE_XML)
|
|
||||||
.replace('\a', NEWPARAGRAPH_XML)
|
|
||||||
.replace('\t', TAB_XML)
|
|
||||||
.replace('\f', PAGE_BREAK))
|
|
||||||
|
|
||||||
prop = u''
|
prop = u''
|
||||||
|
|
||||||
@ -756,11 +780,7 @@ class Listing(object):
|
|||||||
# If not a string : cast to string (ex: int, dict etc...)
|
# If not a string : cast to string (ex: int, dict etc...)
|
||||||
if not isinstance(text, (six.text_type, six.binary_type)):
|
if not isinstance(text, (six.text_type, six.binary_type)):
|
||||||
text = six.text_type(text)
|
text = six.text_type(text)
|
||||||
self.xml = (escape(text)
|
self.xml = escape(text)
|
||||||
.replace('\n', NEWLINE_XML)
|
|
||||||
.replace('\a', NEWPARAGRAPH_XML)
|
|
||||||
.replace('\t', TAB_XML)
|
|
||||||
.replace('\f', PAGE_BREAK))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.xml
|
return self.xml
|
||||||
|
|||||||
@ -7,13 +7,20 @@ context = {
|
|||||||
'"less than" must be escaped : <, this can be done with RichText() or R()'
|
'"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 : < ',
|
'myescvar': 'It can be escaped with a "|e" jinja filter in the template too : < ',
|
||||||
'nlnp': R(
|
'nlnp': R('Here is a multiple\nlines\nstring\aand some\aother\aparagraphs',
|
||||||
'Here is a multiple\nlines\nstring\aand some\aother\aparagraphs\aNOTE: the current character styling is removed'
|
color='#ff00ff'),
|
||||||
),
|
|
||||||
'mylisting': Listing(
|
'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'),
|
'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)
|
tpl.render(context)
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user