diff --git a/.project b/.project new file mode 100644 index 0000000..ddf0128 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + python-docx-template + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 0000000..40e9f40 --- /dev/null +++ b/.pydevproject @@ -0,0 +1,5 @@ + + +Default +python 2.7 + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..add17fd --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//docxtpl/__init__.py=utf-8 diff --git a/docxtpl/__init__.py b/docxtpl/__init__.py index c1ce39c..c882275 100644 --- a/docxtpl/__init__.py +++ b/docxtpl/__init__.py @@ -7,10 +7,64 @@ Created : 2015-03-12 __version__ = '0.1.1' +from lxml import etree +from docx import Document +from jinja2 import Template +import copy +import re class DocxTemplate(object): """ Class for managing docx files as they were jinja2 templates """ - pass + def __init__(self, docx): + self.docx = Document(docx) + + def __getattr__(self, name): + return getattr(self.docx, name) + + def build_xml(self,context): + src_xml = etree.tostring(self.docx._element.body, pretty_print=True) + + with open('/tmp/docx1.xml','w') as fh: + fh.write(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('.*?(|]*>)','',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) + cell_xml = re.sub(r'](?:(?!]).)*.*?','',cell_xml,flags=re.DOTALL) + return re.sub(r'()',r'\1{{ %s}}\2' % m.group(2), cell_xml) + src_xml = re.sub(r'(](?:(?!]).)*){%\s*cellbg\s+([^%]*)\s*%}(.*?)',cellbg,src_xml,flags=re.DOTALL) + + # replace xml code corresponding to the paragraph containing {{{ xxx }}} by {{ xxx }} + src_xml = re.sub(r'](?:(?!]).)*{{{([^}]*)}}}.*?',r'{{\1}}',src_xml,flags=re.DOTALL) + + # replace xml code corresponding to the row containing {% tr-xxx template tag by {% xxx template tag itself + src_xml = re.sub(r'](?:(?!]).)*{%\s*tr-([^%]*%}).*?',r'{% \1',src_xml,flags=re.DOTALL) + + # replace xml code corresponding to the paragraph containing {% p-xxx template tag by {% xxx template tag itself + src_xml = re.sub(r'](?:(?!]).)*{%\s*p-([^%]*%}).*?',r'{% \1',src_xml,flags=re.DOTALL) + + with open('/tmp/docx2.xml','w') as fh: + fh.write(src_xml) + + template = Template(src_xml) + dst_xml = template.render(context) + + return dst_xml + + def map_xml(self,xml): + root = self.docx._element + body = root.body + root.replace(body,etree.fromstring(xml)) + + def render(self,context): + xml = self.build_xml(context) + self.map_xml(xml) class Subdoc(object): """ Class for subdocumentation insertion into master document """ diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/order.py b/tests/order.py new file mode 100644 index 0000000..a7d90ee --- /dev/null +++ b/tests/order.py @@ -0,0 +1,19 @@ +from docxtpl import DocxTemplate + +tpl=DocxTemplate('test_files/order_tpl.docx') + +context = { + 'customer_name' : 'Eric', + 'items' : [ + {'desc' : 'Python interpreters', 'qty' : 2, 'price' : 'FREE' }, + {'desc' : 'Django projects', 'qty' : 5403, 'price' : 'FREE' }, + {'desc' : 'Guido', 'qty' : 1, 'price' : '100,000,000.00' }, + ], + 'in_europe' : True, + 'is_paid': False, + 'company_name' : 'The World Wide company', + 'total_price' : '100,000,000.00' +} + +tpl.render(context) +tpl.save('test_files/order.docx') diff --git a/tests/test_files/order.docx b/tests/test_files/order.docx new file mode 100644 index 0000000..a4874bc Binary files /dev/null and b/tests/test_files/order.docx differ diff --git a/tests/test_files/order_tpl.docx b/tests/test_files/order_tpl.docx new file mode 100644 index 0000000..04e3fa3 Binary files /dev/null and b/tests/test_files/order_tpl.docx differ