Update RichText

This commit is contained in:
Eric Lapouyade 2015-03-26 11:23:05 +01:00
parent 28f7f6b6dd
commit 1483bae331
8 changed files with 94 additions and 50 deletions

View File

@ -2,4 +2,5 @@ eclipse.preferences.version=1
encoding//docxtpl/__init__.py=utf-8
encoding//tests/cellbg.py=utf-8
encoding//tests/order.py=utf-8
encoding//tests/richtext.py=utf-8
encoding//tests/subdoc.py=utf-8

View File

@ -10,6 +10,7 @@ __version__ = '0.1.1'
from lxml import etree
from docx import Document
from jinja2 import Template
from cgi import escape
import copy
import re
@ -17,27 +18,27 @@ class DocxTemplate(object):
""" Class for managing docx files as they were jinja2 templates """
def __init__(self, docx):
self.docx = Document(docx)
def __getattr__(self, name):
def __getattr__(self, name):
return getattr(self.docx, name)
def get_docx(self):
return self.docx
def get_xml(self):
return etree.tostring(self.docx._element.body, pretty_print=True)
def write_xml(self,filename):
with open(filename,'w') as fh:
fh.write(self.get_xml())
def patch_xml(self,src_xml):
# strip all xml tags inside {% %} and {{ }}
# that Microsoft word can insert into xml code for this part of the document
def striptags(m):
return re.sub('</w:t>.*?(<w:t>|<w:t [^>]*>)','',m.group(0),flags=re.DOTALL)
src_xml = re.sub(r'{%(?:(?!%}).)*|{{(?:(?!}}).)*',striptags,src_xml,flags=re.DOTALL)
# manage table cell background color
def cellbg(m):
cell_xml = m.group(1) + m.group(3)
@ -45,19 +46,20 @@ class DocxTemplate(object):
cell_xml = re.sub(r'<w:shd[^/]*/>','', cell_xml, count=1)
return re.sub(r'(<w:tcPr[^>]*>)',r'\1<w:shd w:val="clear" w:color="auto" w:fill="{{%s}}"/>' % m.group(2), cell_xml)
src_xml = re.sub(r'(<w:tc[ >](?:(?!<w:tc[ >]).)*){%\s*cellbg\s+([^%]*)\s*%}(.*?</w:tc>)',cellbg,src_xml,flags=re.DOTALL)
for y in ['tr', '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 xml tags :
# This is mandatory to have jinja2 generating correct xml code
pat = r'<w:%(y)s[ >](?:(?!<w:%(y)s[ >]).)*({%%|{{)%(y)s ([^}%%]*(?:%%}|}})).*?</w:%(y)s>' % {'y':y}
src_xml = re.sub(pat, r'\1 \2',src_xml,flags=re.DOTALL)
return src_xml
def render_xml(self,src_xml,context):
template = Template(src_xml)
dst_xml = template.render(context)
dst_xml = template.render(context)
dst_xml = dst_xml.replace('{_{','{{').replace('}_}','}}').replace('{_%','{%').replace('%_}','%}')
return dst_xml
def build_xml(self,context):
@ -65,7 +67,7 @@ class DocxTemplate(object):
xml = self.patch_xml(xml)
xml = self.render_xml(xml, context)
return xml
def map_xml(self,xml):
root = self.docx._element
body = root.body
@ -74,7 +76,7 @@ class DocxTemplate(object):
def render(self,context):
xml = self.build_xml(context)
self.map_xml(xml)
def new_subdoc(self):
return Subdoc(self)
@ -86,66 +88,71 @@ class Subdoc(object):
self.subdocx = Document()
self.subdocx._part = self.docx._part
def __getattr__(self, name):
def __getattr__(self, name):
return getattr(self.subdocx, name)
def __unicode__(self):
xml = ''
for p in self.paragraphs:
xml += '<w:p>\n' + re.sub(r'^.*\n', '', etree.tostring(p._element,pretty_print=True))
return xml
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 = ''
if text:
self.add_run(text, **text_prop)
def add(self, text, style=None,
color=None,
highlight=None,
size=None,
bold=False,
italic=False,
underline=False,
self.add(text, **text_prop)
def add(self, text, style=None,
color=None,
highlight=None,
size=None,
bold=False,
italic=False,
underline=False,
strike=False):
prop = ''
if not isinstance(text, unicode):
text = text.decode('utf-8',errors='ignore')
text = escape(text).replace('\n','<w:br/>')
prop = u''
if style:
prop += '<w:rStyle w:val="%s"/>' % style
prop += u'<w:rStyle w:val="%s"/>' % style
if color:
if color[0] == '#':
color = color[1:]
prop += '<w:color w:val="%s"/>' % color
prop += u'<w:color w:val="%s"/>' % color
if highlight:
if highlight[0] == '#':
highlight = highlight[1:]
prop += '<w:highlight w:val="%s"/>' % highlight
prop += u'<w:highlight w:val="%s"/>' % highlight
if size:
prop += '<w:sz w:val="%s"/>' % size
prop += '<w:szCs w:val="%s"/>' % size
prop += u'<w:sz w:val="%s"/>' % size
prop += u'<w:szCs w:val="%s"/>' % size
if bold:
prop += '<w:b/>'
prop += u'<w:b/>'
if italic:
prop += '<w:i/>'
prop += u'<w:i/>'
if underline:
if underline not in ['single','double']:
underline = 'single'
prop += '<w:u w:val="%s"/>' % underline
prop += u'<w:u w:val="%s"/>' % underline
if strike:
prop += '<w:strike/>'
self.xml += '<w:r>'
prop += u'<w:strike/>'
self.xml += u'<w:r>'
if prop:
self.xml += '<w:rPr>%s</w:rPr>' % prop
self.xml += '<w:t>%s</w:t></w:r>\n' % text
self.xml += u'<w:rPr>%s</w:rPr>' % prop
self.xml += u'<w:t xml:space="preserve">%s</w:t></w:r>\n' % text
def __unicode__(self):
return xml
return self.xml

View File

@ -5,17 +5,17 @@ Created : 2015-03-12
@author: Eric Lapouyade
'''
from docxtpl import DocxTemplate
from docxtpl import DocxTemplate, RichText
tpl=DocxTemplate('test_files/cellbg_tpl.docx')
context = {
context = {
'alerts' : [
{'date' : '2015-03-10', 'desc' : 'Very critical alert', 'type' : 'CRITICAL', 'bg': 'FF0000' },
{'date' : '2015-03-11', 'desc' : 'Just a warning', 'type' : 'WARNING', 'bg': 'FFDD00' },
{'date' : '2015-03-12', 'desc' : 'Information', 'type' : 'INFO', 'bg': '8888FF' },
{'date' : '2015-03-13', 'desc' : 'Debug trace', 'type' : 'DEBUG', 'bg': 'FF00FF' },
],
{'date' : '2015-03-10', 'desc' : RichText('Very critical alert',color='FF0000', bold=True), 'type' : 'CRITICAL', 'bg': 'FF0000' },
{'date' : '2015-03-11', 'desc' : RichText('Just a warning'), 'type' : 'WARNING', 'bg': 'FFDD00' },
{'date' : '2015-03-12', 'desc' : RichText('Information'), 'type' : 'INFO', 'bg': '8888FF' },
{'date' : '2015-03-13', 'desc' : RichText('Debug trace'), 'type' : 'DEBUG', 'bg': 'FF00FF' },
],
}
tpl.render(context)

36
tests/richtext.py Normal file
View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
'''
Created : 2015-03-26
@author: Eric Lapouyade
'''
from docxtpl import DocxTemplate, RichText
tpl=DocxTemplate('test_files/richtext_tpl.docx')
rt = RichText('an exemple of ')
rt.add('a rich text', style='myrichtextstyle')
rt.add(' with ')
rt.add('some italic', italic=True)
rt.add(' and ')
rt.add('some violet', color='#ff00ff')
rt.add(' and ')
rt.add('some striked', strike=True)
rt.add(' and ')
rt.add('some small', size=14)
rt.add(' or ')
rt.add('big', size=60)
rt.add(' text.')
rt.add(' Et voilà ! ')
rt.add('\n1st line')
rt.add('\n2nd line')
rt.add('\n3rd line')
rt.add('\n\n<cool>')
context = {
'example' : rt,
}
tpl.render(context)
tpl.save('test_files/richtext.docx')

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.