- Add replace_embedded() method (useful for embedding docx)
- Add replace_media() method (useful for header/footer images)
This commit is contained in:
parent
b7947ca10c
commit
be149b367d
@ -1,6 +1,7 @@
|
||||
0.4.0 (2017-09-03)
|
||||
0.4.0 (2017-09-09)
|
||||
------------------
|
||||
- Add replace_medias() utility (useful for header/footer images)
|
||||
- Add replace_media() method (useful for header/footer images)
|
||||
- Add replace_embedded() method (useful for embedding docx)
|
||||
|
||||
0.3.9 (2017-06-27)
|
||||
------------------
|
||||
|
||||
@ -21,6 +21,11 @@ You save the document as a .docx file (xml format) : it will be your .docx templ
|
||||
|
||||
Now you can use python-docx-template to generate as many word documents you want from this .docx template and context variables you will associate.
|
||||
|
||||
Share
|
||||
-----
|
||||
|
||||
If you like this project, please rate and share it here : http://rate.re/github/elapouya/python-docx-template
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
|
||||
@ -172,13 +172,30 @@ It is not possible to dynamically add images in header/footer, but you can chang
|
||||
The idea is to put a dummy picture in your template, render the template as usual, then replace the dummy picture with another one.
|
||||
You can do that for all medias at the same time.
|
||||
Note: for images, the aspect ratio will be the same as the replaced image
|
||||
Note2 : it is important to have the source media files as they are required to calculate their CRC to find them in the docx
|
||||
Note2 : it is important to have the source media files as they are required to calculate their CRC to find them in the docx.
|
||||
(dummy file name is not important)
|
||||
|
||||
Syntax to replace dummy_header_pic.jpg and dummy2.jpg in mydoc.docx::
|
||||
Syntax to replace dummy_header_pic.jpg::
|
||||
|
||||
replace_medias('mydoc.docx',[('dummy_header_pic.jpg','header_pic_i_want.jpg'),('dummy2.png','mycompany.png')])
|
||||
tpl.replace_media('dummy_header_pic.jpg','header_pic_i_want.jpg')
|
||||
|
||||
|
||||
dummy_header_pic.jpg must exist in the template directory when rendering and saving the generated docx. It must be the same
|
||||
file as the one inserted manually in the docx template.
|
||||
|
||||
Replace embedded objects
|
||||
------------------------
|
||||
|
||||
It works like medias replacement, except it is for embedded objects like embedded docx.
|
||||
|
||||
Syntax to replace embedded_dummy.docx::
|
||||
|
||||
tpl.replace_embedded('embdded_dummy.docx','embdded_docx_i_want.docx')
|
||||
|
||||
|
||||
embdded_dummy.docx must exist in the template directory when rendering and saving the generated docx. It must be the same
|
||||
file as the one inserted manually in the docx template.
|
||||
|
||||
Jinja custom filters
|
||||
--------------------
|
||||
|
||||
@ -200,7 +217,10 @@ Examples
|
||||
|
||||
The best way to see how it works is to read examples, they are located in `tests/` directory. Templates and generated .docx files are in `tests/test_files/`.
|
||||
|
||||
Share
|
||||
-----
|
||||
|
||||
If you like this project, please rate and share it here : http://rate.re/github/elapouya/python-docx-template
|
||||
|
||||
|
||||
.. rubric:: Functions index
|
||||
|
||||
@ -29,6 +29,8 @@ class DocxTemplate(object):
|
||||
|
||||
def __init__(self, docx):
|
||||
self.docx = Document(docx)
|
||||
self.crc_to_new_media = {}
|
||||
self.crc_to_new_embedded = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.docx, name)
|
||||
@ -184,6 +186,65 @@ class DocxTemplate(object):
|
||||
def new_subdoc(self):
|
||||
return Subdoc(self)
|
||||
|
||||
@staticmethod
|
||||
def get_file_crc(filename):
|
||||
with open(filename, 'rb') as fh:
|
||||
buf = fh.read()
|
||||
crc = (binascii.crc32(buf) & 0xFFFFFFFF)
|
||||
return crc
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
"""
|
||||
with open(dst_file, 'rb') as fh:
|
||||
crc = self.get_file_crc(src_file)
|
||||
self.crc_to_new_media[crc] = fh.read()
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
"""
|
||||
with open(dst_file, 'rb') as fh:
|
||||
crc = self.get_file_crc(src_file)
|
||||
self.crc_to_new_embedded[crc] = fh.read()
|
||||
|
||||
def post_processing(self,docx_filename):
|
||||
if self.crc_to_new_media or self.crc_to_new_embedded:
|
||||
backup_filename = '%s_docxtpl_before_replace_medias' % docx_filename
|
||||
os.rename(docx_filename,backup_filename)
|
||||
|
||||
with zipfile.ZipFile(backup_filename) as zin:
|
||||
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:
|
||||
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:
|
||||
zout.writestr(item, self.crc_to_new_embedded[item.CRC])
|
||||
else:
|
||||
zout.writestr(item, buf)
|
||||
|
||||
os.remove(backup_filename)
|
||||
|
||||
def save(self,filename,*args,**kwargs):
|
||||
self.docx.save(filename,*args,**kwargs)
|
||||
self.post_processing(filename)
|
||||
|
||||
|
||||
class Subdoc(object):
|
||||
""" Class for subdocument to insert into master document """
|
||||
def __init__(self, tpl):
|
||||
@ -298,37 +359,4 @@ class InlineImage(object):
|
||||
return self.xml
|
||||
|
||||
def __str__(self):
|
||||
return self.xml
|
||||
|
||||
def replace_medias(docx_filename,src_dst_lst):
|
||||
"""Utility function replace any media by another 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
|
||||
|
||||
Syntax: replace_medias('mydoc.docx',[('dummy_header_pic.jpg','header_pic_i_want.jpg'),('dummy2.png','mycompany.png')])
|
||||
|
||||
Note: for images, the aspect ratio will be the same as the replaced image
|
||||
Note2 : it is important to have the source media files as they are required to calculate their CRC to find them in the docx
|
||||
"""
|
||||
crc_to_new_media = {}
|
||||
for src,dst in src_dst_lst:
|
||||
with open(src,'rb') as fh:
|
||||
buf = fh.read()
|
||||
crc = (binascii.crc32(buf) & 0xFFFFFFFF)
|
||||
with open(dst,'rb') as fh:
|
||||
crc_to_new_media[crc] = fh.read()
|
||||
|
||||
backup_filename = '%s_before_replace_medias' % docx_filename
|
||||
os.rename(docx_filename,backup_filename)
|
||||
|
||||
with zipfile.ZipFile(backup_filename) as zin:
|
||||
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 crc_to_new_media:
|
||||
zout.writestr(item, crc_to_new_media[item.CRC])
|
||||
else:
|
||||
zout.writestr(item, buf)
|
||||
|
||||
os.remove(backup_filename)
|
||||
return self.xml
|
||||
29
tests/embedded.py
Normal file
29
tests/embedded.py
Normal file
@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Created : 2017-09-09
|
||||
|
||||
@author: Eric Lapouyade
|
||||
'''
|
||||
|
||||
from docxtpl import DocxTemplate
|
||||
|
||||
# rendering the "dynamic embedded docx":
|
||||
embedded_docx_tpl=DocxTemplate('test_files/embedded_embedded_docx_tpl.docx')
|
||||
context = {
|
||||
'name' : 'John Doe',
|
||||
}
|
||||
embedded_docx_tpl.render(context)
|
||||
embedded_docx_tpl.save('test_files/embedded_embedded_docx.docx')
|
||||
|
||||
|
||||
# rendring the main document :
|
||||
tpl=DocxTemplate('test_files/embedded_main_tpl.docx')
|
||||
|
||||
context = {
|
||||
'name' : 'John Doe',
|
||||
}
|
||||
|
||||
tpl.replace_embedded('test_files/embedded_dummy.docx','test_files/embedded_static_docx.docx')
|
||||
tpl.replace_embedded('test_files/embedded_dummy2.docx','test_files/embedded_embedded_docx.docx')
|
||||
tpl.render(context)
|
||||
tpl.save('test_files/embedded.docx')
|
||||
@ -5,7 +5,7 @@ Created : 2017-09-03
|
||||
@author: Eric Lapouyade
|
||||
'''
|
||||
|
||||
from docxtpl import DocxTemplate, replace_medias
|
||||
from docxtpl import DocxTemplate
|
||||
|
||||
DEST_FILE = 'test_files/header_footer_image.docx'
|
||||
|
||||
@ -14,7 +14,6 @@ tpl=DocxTemplate('test_files/header_footer_image_tpl.docx')
|
||||
context = {
|
||||
'mycompany' : 'The World Wide company',
|
||||
}
|
||||
|
||||
tpl.replace_media('test_files/dummy_pic_for_header.png','test_files/python.png')
|
||||
tpl.render(context)
|
||||
tpl.save(DEST_FILE)
|
||||
replace_medias(DEST_FILE,[('test_files/dummy_pic_for_header.png','test_files/python.png')])
|
||||
tpl.save(DEST_FILE)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user