Python 第三方库之 docxtpl (处理word文档)

项目官方文档

项目官方git

docxtpl 软件包使用两个主要软件包:

  • python docx,用于读取、写入和创建子文档
  • jinja2用于管理插入到模板docx中的标记。jinja官网, jinja中文网站

简单示例

from docxtpl import DocxTemplate

tpl = DocxTemplate('test.docx')

context = {
    'template': '123'
}

tpl.render(context)
tpl.save('test1.docx')

安装

pip install docxtpl


导言

python-docx-template被开发出来,是因为python-docx的强大在于创建文档,而不是修改它们。

这个想法是开始于创建一个需要用MicrosoftWord生成的文档的示例,它可以像我们想要的那样复杂:如图片、索引表、页脚、页眉、变量,以及我们可以用Word做的任何事情。然后,当我们使用MicrosoftWord编辑文档时,直接在文档中插入类似于Jinja 2的标记。将文档保存为.docx文件(XML格式):它将是.docx模板文件。

现在,我们可以使用python-docx-template从这个.docx模板和将关联的上下文变量生成您想要的多个Word文档。

注意:python-docx-template是测试的是MS Word 97格式,它可能不适用于其他版本.

Jinja 2类语法

因Jinja 2包被使用,可以使用Word文档中的所有Jinja 2标记和过滤器。然而,要使它在Word文档中工作,进行了些限制和扩展:

限制

通常的Jinja 2标签,只能在同一个段落的同一个‘’run’中使用,它不能跨几个段落使用,表行、‘run’。如果您想要管理段落、表行和整个‘run’的样式,您必须使用特殊的标记语法,如下一章所述。

注意:MicrosoftWord的“Run”是一个具有相同风格的字符序列。例如,如果您创建一个具有相同风格的所有字符的段落:Word将在内部创建一个段落中的“Run”。现在,如果你在这段中间加了一个粗体,单词会把前面的“Run”转换成3‘Run’(普通-粗体-正常)。

扩展

为了管理段落、表行、表列、run,必须使用特殊的语法。(区别于jinja2)

{%p jinja2_tag %} for paragraphs
{%tr jinja2_tag %} for table rows
{%tc jinja2_tag %} for table columns
{%r jinja2_tag %} for runs

通过使用这些标记,python-docx-template将真正的Jinja 2标记放入文档的XML源代码中的正确位置。此外,这些标记还告诉python-docx-template移除位于开始和结束标记的位置的段落、表行、表列或run,只注意中间的位置。

重要*不要使用2次{%p, {%tr、`{%tc‘、{%r在同一段落、行、列或run中。例子:

不要用这个

{%p if display_paragraph %}Here is my paragraph {%p endif %}

而是使用

{%p if display_paragraph %}
Here is my paragraph
{%p endif %}

这种语法是可能是因为Microsoft word将每一行都看作是一个新的段落,{%p标签不在同一段中

显示变量

作为Jinja 2的一部分,可以使用双括弧:{ { }}。但如果 是RichText对象,必须指定要更改实际的“Run”。{ {r }}注意’r紧跟左括弧。

重要*不要使用 r 变量出现在模板中,因为{ {r}} 可以解释为 { {r 没有指定变量。不过,您可以使用以‘r’开头的更大的变量名。例如{ {render_color}}将被解释为{ { render_color }}不像{ {r ender_color}}.

重要*不要使用2次 { {r 在同一run上。使用RichText.add()方法在python端连接多个字符串和样式,并且只有一个字符串和样式 { {r 在模板边。

单元格颜色

当您想要更改表格单元格的背景色时,有一种特殊情况,您必须在单元格的开头放置以下标记

{% cellbg %}

必须包含颜色的十六进制代码无散列符号

列跨越

如果您想在多个列上动态地跨越一个表单元格(当您有一个具有动态列计数的表时,这是很有用的),您必须在单元格的最开始处放置下面的标记来跨越

{% colspan %}

必须包含要跨越的列数的整数。有关示例,请参见下一篇示例操作

Escaping

为了展示{%, %}, { {或}},你可以用

{ %, %}, { { or }}

RichText

当你使用{ { }}标记,它将被包含在 var 变量的字符串替换。但它将保持目前的格式。如果要添加动态变化的样式,则必须同时使用以下两种方式:{ {r }}标签一个 RichText对象在 var 变量。您可以更改颜色、粗体、斜体、大小等,但最好的方法是使用microsoft word来定义自己的字符样式(主页选项卡->修改样式->管理样式按钮->新建样式,在窗体中选择“字符样式”),请参见tests/richtext.py,除了使用RichText(),您可以使用它的快捷方式:R()

重要*当你使用{ {r }}它从docx模板中移除当前字符样式,这意味着如果你没有在RichText()设置字体样式,样式将返回到Microsoft Word默认样式。这只会影响字符样式,而不会影响段落样式(MSWord管理的这2种样式)。

可以通过Richtext将超链接添加到文本中。

tpl=DocxTemplate(‘your_template.docx’)
rt = RichText('You can add an hyperlink, here to ')
rt.add(‘google’,url_id=tpl.build_url_id(‘http://google.com’))

将rt放在您的语境中,然后使用{ {r rt}}在你的模板中

内嵌图像

您可以动态地将一个或多个图像添加到文档中(使用JPEG和PNG文件进行测试)。只要在你的模板中加上{ { }},标记是doxtpl.inlineImage的实例。

myimage=InlineImage(tpl,‘test_files/python_logo.png’,width=Mm(20))

您只需指定模板对象、图像文件路径和可选宽度和/或高度即可。对于高度和宽度,您必须使用毫米(毫米),英寸(英寸)或点(Pt)类。有关示例,请参见test/inlineImage.py。

子文件

模板变量可以包含使用python-docxWord文档从头开始构建的复杂变量。为此,首先从模板对象获取子文档对象,并将其用作python-docx文档对象,参见tests/subdoc.py.

转义,换行符,新段落,列表
当您使用{ { }},您正在修改XMLWord文档,这意味着您不能使用所有字符,特别是<, >和&。为了使用它们,你必须转义它们。有四种方式:

context = { ‘var’:R(‘my text’) }和在模板中{ {r }}(注意r),
context = { ‘var’:‘my text’}和在模板中{ { |e }}
context = { ‘var’:escape(‘my text’)}和在模板中{ { }}。
在调用呈现方法时启用自动转义:tpl.render(context, autoescape=True)(默认值autoescape=false)
RichText()或R()提供换行符和新段落功能:只需使用\n或\a在文本中,它们将作相应的转换。

有关更多信息,请参见tests/escape.py 。

另一个解决方案,如果要将列表包含到文档中,即转义文本并管理n和a,则可以使用’Listing’ class:

在python代码中

context = { 'mylisting':Listing('the listing\nwith\nsome\nlines \a and some paragraph \a and special chars : <>&') }

在docx模板中只需使用{ { mylisting }}带着Listing(),您将保持当前字符的样式(除非在一个\a之后你开始新的一段落)。

替换docx图片

在页眉/页脚中动态添加图像是不可能的,但您可以更改它们。这样做的目的是在模板中放置一个虚拟图片,像往常一样渲染模板,然后用另一个模板替换虚拟图片。你可以同时为所有媒体做到这一点。Note1:纵横比将与替换的图像相同 Note 2:指定用于在docx模板中插入图像的文件名(只要其basename,而不是完整路径)。

替换dummy_header_pic.jpg的语法:

tpl.replace_pic('dummy_header_pic.jpg','header_pic_i_want.jpg')

替换发生在页眉、页脚和整个文档正文中。

替换docx媒体

在页眉/页脚中动态添加图片以外的其他媒体是不可能的,但您可以更改它们。这样做的目的是在模板中放置一个虚拟媒体,像往常一样呈现模板,然后用另一个模板替换虚拟媒体。你可以同时为所有媒体做到这一点。注意:对于图像,高宽比将与替换的图像注释2相同:重要的是要有源媒体文件,因为它们需要计算它们的CRC才能在docx中找到它们。(虚拟文件名不重要)

替换dummy_header_pic.jpg的语法:

tpl.replace_media('dummy_header_pic.jpg','header_pic_i_want.jpg')

警告:与replace_pic() 方法不同,dummy_header_pic.jpg 必须存在模板目录中,当在渲染和保存生成docx时。它必须与手动插入在docx模板的文件相同。替换可以发生在页眉、页脚和整个文档正文中。

替换嵌入对象

它的工作方式类似于媒体替换,只是它适用于嵌入式docx这样的嵌入式对象。

替换embedded_dummy.docx的语法:

tpl.replace_embedded('embdded_dummy.docx','embdded_docx_i_want.docx')

警告:与REPLE_PIC()方法不同,embdded_dumy.docx必须存在于模板目录中,在渲染和保存生成的docx时。它必须与手动插入docx模板的文件相同。替换发生在页眉、页脚和整个文档正文中。

Microsoft Word 2016特例

MS Word 2016将忽略表格的 \t。这对这个版本来说是特别的。但是LibreOffice或Wordpad没有这个问题。对于以jinja2标记产生空格来开头的行,也会出现同样的情况:它们将被忽略。要解决这些问题,解决方案是使用Richtext:

tpl.render({
    'test_space_r' : RichText('          '),
    'test_tabs_r': RichText(5*'\t'),
})

在模板中,使用 {r 表示法:

{ {r test_space_r}} Spaces will be preserved
{ {r test_tabs_r}} Tabs will be displayed

表格

可以两种方式水平地跨表格单元格,一是利用colspan标记(请参阅tests/dynamic_table.py):

{% colspan %}

或者在for循环中(请参阅tests/horizontal_merge.py)):

{% hm %}

还可以在for循环中垂直合并单元格(请参阅 tests/vertical_merge.py):

{% vm %}

Jinja自定义过滤器

render() 接受 jinja_env 选项参数:您可以传递一个jinja环境对象。通过这种方式,您可以添加一些定制的jinja过滤器:

from docxtpl import DocxTemplate
import jinja2
def multiply_by(value, by):
   return value * by

doc = DocxTemplate("my_word_template.docx")
context = { 'price_dollars' : 5.00 }
jinja_env = jinja2.Environment()
jinja_env.filters['multiply_by'] = multiply_by
doc.render(context,jinja_env)
doc.save("generated_doc.docx")

然后在模板中,您将能够使用

Euros price : { { price_dollars|multiply_by(0.88) }}

例子

查看其工作原理的最佳方法是阅读示例,它们位于 tests/ 目录。Docx测试模板在tests/templates/目录。要生成最终的docx文件:

cd tests/
python runtests.py

生成的文件位于tests/output 目录。

想要真正学会对应包,赶紧自己下载,根据测试文件测试吧!

你可能感兴趣的:(Python)