作者:小小明
本文鏈接:https://blog.csdn.net/as604049322/article/details/112008531
支持pdf文檔下載:https://download.csdn.net/download/as604049322/13993564
簡介:Pandas數(shù)據(jù)處理專家,10余年編碼經(jīng)驗,。
若你在數(shù)據(jù)處理的問題上遇到什么困難,歡迎與我交流。
大家好,我是小小明,這篇文章將給大家分享一個強大的包docxtpl ,它通過對docx文檔模板加載,使用jinja2網(wǎng)頁模板開發(fā)的語法對其進行修改,。
docxtpl 的簡介
前面我分享過python-docx庫的操作案例,而這次分享的docxtpl 就是基于python-docx和jinja2開發(fā)出來的庫。
docxtpl 的作者開發(fā)出它的原因主要是python-docx擅長創(chuàng)建word文檔,卻不擅長修改,。
對于docxtpl來說,使用MicrosoftWord編輯文檔時,直接在文檔中插入類似于Jinja2的標(biāo)記,。將文檔保存為.docx文件(XML格式):它將是.docx模板文件。
然后使用docxtpl加載這個.docx模板,按照J(rèn)inja2的語法傳入關(guān)聯(lián)的上下文變量,即可生成想要的Word文檔,多次傳入不同的上下文變量即可生成多個基于模板的word文檔,。
docxtpl 主要依賴兩個包
python-docx :讀寫word文檔 jinja2:管理插入到模板中的標(biāo)簽
jinja2語法可參考: http://docs./docs/jinja2/templates.html docxtpl官方文檔: https://docxtpl./en/latest/
安裝:
pip install docxtpl
基本使用示例:
from docxtpl import DocxTemplate
tpl = DocxTemplate( "my_word_template.docx" )
context = { 'company_name' : "World company" }
tpl. render( context)
tpl. save( "generated_doc.docx" )
docxtpl的使用手冊
Jinja2語法
為了掌握docxtpl 的用法,我們需要學(xué)習(xí)或復(fù)習(xí)一下Jinja2的語法,然后再研究docxtpl 特有的類Jinja2語法,。
基本語法:
{% if user %}
< title> hello {{user}} </ title>
{% else %}
< title> welcome to docxtpl </ title>
{% endif %}
< ul>
{% for index in indexs %}
< li> {{ index }} </ li>
{% endfor %}
</ ul>
在模板中{{ variable }}結(jié)構(gòu)表示變量,是一種特殊的占位符,告訴模板引擎這個位置的值,從渲染模板時使用的數(shù)據(jù)中獲取;Jinja2除了能識別基本類型的變量,還能識別字典;
< p> {{mydict['key']}}</ p>
< p> {{mylist[1]}}</ p>
< p> {{mylist[myvariable]}}</ p>
過濾器的本質(zhì)就是函數(shù),使用方式為:變量名 | 過濾器。 過濾器名寫在變量名后面,中間用 | 分隔,。如:{{variable | capitalize}},這個過濾器的作用:把變量variable的值的首字母轉(zhuǎn)換為大寫,其他字母轉(zhuǎn)換為小寫,。 操作列表的常用過濾器如下:
列表操作:
first:取第一個元素
< p> {{ [1,2,3,4,5,6] | first }}</ p>
last:取最后一個元素
< p> {{ [1,2,3,4,5,6] | last }}</ p>
length:獲取列表長度
< p> {{ [1,2,3,4,5,6] | length }}</ p>
sum:列表求和
< p> {{ [1,2,3,4,5,6] | sum }}</ p>
sort:列表排序
< p> {{ [6,2,3,1,5,4] | sort }}</ p>
Jinja自定義過濾器
render() 的 jinja_env 選項參數(shù)可以傳遞一個jinja環(huán)境對象,從而添加一些定制的jinja過濾器:
from docxtpl import DocxTemplate
import jinja2
def multiply_by ( value, by) :
return value * by
tpl = DocxTemplate( "my_word_template.docx" )
context = { 'price_dollars' : 5.00 }
jinja_env = jinja2. Environment( )
jinja_env. filters[ 'multiply_by' ] = multiply_by
tpl. render( context, jinja_env)
tpl. save( "generated_doc.docx" )
然后在模板中能夠使用
Euros price : {{ price_dollars|multiply_by(0.88) }}
類Jinja 2語法
4個重要的專屬標(biāo)簽
為了管理段落、表行,、表列,、run,必須使用特殊的語法:
{%p jinja2_tag %} for paragraphs
{%tr jinja2_tag %} for table rows
{%tc jinja2_tag %} for table columns
{%r jinja2_tag %} for runs
正常的Jinja 2語法只有%的普通標(biāo)簽,而docxtpl的類語法包含%p,%tr,%tc,%r
%p:段落,即docx.text.paragraph.Paragraph對象
%tr:表格中的一行,即docx.table._Row對象
%tc:表格中的一列,即docx.table._Column對象
%r:段落中的一個片段,即docx.text.run.Run對象
通過使用這些標(biāo)記,python-docx-template將真正的Jinja 2標(biāo)記放入文檔的XML源代碼中的正確位置。
另外,需注意,這四種標(biāo)簽,起始標(biāo)簽不能在同一行,必須在不同的行上面,。
例如:
{%p if display_paragraph %}Here is my paragraph {%p endif %}
需改寫成:
{%p if display_paragraph %}
Here is my paragraph
{%p endif %}
否則無法正確渲染,。
長文本拆分
包含jinja2標(biāo)簽的文本如果太長,可能無法讀取:
My house is located {% if living_in_town %} in urban area {% else %} in countryside {% endif %} and I love it.
可以使用 {%-
和-%}
標(biāo)簽來拆分整個文本:
My house is located
{%- if living_in_town -%}
in urban area
{%- else -%}
in countryside
{%- endif -%}
and I love it.
注意: {%- xxx -%}
標(biāo)簽必須在單獨在一行中,不可在之前或之后添加其他的文本。
Jinja 2的語法對變量是使用雙括弧:{{ variable }}
但如果variable 是RichText對象時,必須更改為:{{r variable }}
注意 :
r緊跟左括弧,。variable不能使用r作為變量,因為{{r}} 可以解釋為 {{r 沒有指定變量 不要在同一run上使用2次 {{r
. 使用 RichText.add() 方法在python代碼上連接多個字符串和樣式,在word文檔模板上只需 寫一個 {{r
標(biāo)簽,。
表格處理與合并單元格
列跨越:
如果需要動態(tài)生成一個合并單元格,可以使用colspan標(biāo)記:
{% colspan column_count %}
column_count是一個整數(shù)表示要跨越的列數(shù),。
例如,word模板中:
{% colspan col_labels|count %}Type of thing {%tc for col in col_labels %} {{ col }} {%tc endfor %}
python渲染代碼:
tpl. render( { 'col_labels' : [ 'fruit' , 'vegetable' , 'stone' , 'thing' ] } )
渲染結(jié)果:
水平合并單元格:
或者在for循環(huán)中:
{% hm %}
例如,word模板:
{%tc for x in [1, 2, 3, 4] %} {% hm %}Header 2 {%tc endfor %} {%tc for x in [1, 2, 3, 4] %} Subheader {{ x }} {%tc endfor %}
最終渲染結(jié)果:
垂直合并單元格:
在for循環(huán)中:
{% vm %}
例如,word模板:
Description Quantity Price {%tr for i in items %} {% vm %}{{category}} {{ i.desc }} {{ i.qty }} {{ i.price }} {%tr endfor %} Total {{total_price}}
python渲染代碼:
context = {
'items' : [
{ 'desc' : 'Python interpreters' , 'qty' : 2 , 'price' : 'FREE' } ,
{ 'desc' : 'Django projects' , 'qty' : 5403 , 'price' : 'FREE' } ,
{ 'desc' : 'Guido' , 'qty' : 1 , 'price' : '100,000,000.00' } ,
] ,
'total_price' : '100,000,000.00' ,
'category' : 'Book' ,
}
tpl. render( context)
渲染結(jié)果:
RichText動態(tài)樣式
使用{{ variable }}標(biāo)記,它將保持目前的格式使用variable 變量的值進行字符串替換。但如果要添加動態(tài)變化的樣式,則必須使用{{r variable }}標(biāo)記,同時傳入的variable變量是一個 RichText對象,。RichText對象可以在python代碼中更改顏色,、粗體、斜體,、大小等,。
使用{{r variable }}標(biāo)記時,它在docx模板中原本的樣式將會被刪除,如果沒有在RichText()設(shè)置字體樣式,樣式將返回到Microsoft Word默認(rèn)樣式。
還可以通過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' ) )
RichText('my text’)的簡寫是R('my text’)
python代碼示例:
from docxtpl import DocxTemplate, RichText
tpl = DocxTemplate( 'templates/richtext_and_if_tpl.docx' )
context = { 'foobar' : RichText( 'Foobar!' , color= 'ff0000' ) }
tpl. render( context)
tpl. save( 'output/richtext_and_if.docx' )
richtext_and_if_tpl.docx文件模板內(nèi)容:
{%r if foobar %} {{r foobar }}COUCOU{%r endif %}
渲染結(jié)果:
單元格顏色
需要更改表格單元格的背景色時,必須在單元格的開頭放置以下標(biāo)記
{% cellbg variable %}
variable 必須是顏色的十六進制碼,不支持red等顏色單詞,。
比如模板:
Date Type Alert Description {%tr for a in alerts %} {{ a.date }} {% cellbg a.bg %}{{ a.type }} {{r a.desc }} {%tr endfor %}
python渲染代碼:
context = {
'alerts' : [
{
'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)
渲染結(jié)果:
Escaping和轉(zhuǎn)義
為了展示{%
, %}
, {{
or }}
, 可以使用:
{_%, %_}, {_{ or }_}
傳入 {{ variable }}
的variable 變量不能使用<
, >
和&
等字符,必須轉(zhuǎn)義它們,否則會導(dǎo)致整個文檔錯亂,。
轉(zhuǎn)義方法有 :
context = { 'variable':R('my text') }
和在模板中{{r variable }}
context = { 'variable':'my text'}
和在模板中{{ variable|e }}
context = { 'var':escape('my text')}
和在模板中{{ <var> }}
在調(diào)用渲染方法時啟用自動轉(zhuǎn)義:tpl.render(context, autoescape=True)
(默認(rèn)值autoescape=False)
RichText()或R()支持換行符,新段落,縮進和分頁符功能:只需使用\n
, \a
, \t
或\f
在文本中,它們將作相應(yīng)的轉(zhuǎn)換。
示例:
word模板內(nèi)容:
{{orgin}}
{{r var1}}
{{var2|e}}
{{var3}}
{{ var4}}
python渲染腳本:
context = {
'var1' : R(
'<>&:必須被轉(zhuǎn)義才能顯示, 可以使用RichText() 或 R()'
) ,
'var2' : '或者使用 "|e" jinja 過濾器來轉(zhuǎn)義 <>& ' ,
'var3' : escape( '或者使用escape函數(shù)來轉(zhuǎn)義: <>& ...' ) ,
'var4' : '多行文本\n\ttab縮進和一些段落\n會自動\a被轉(zhuǎn)換' ,
}
tpl. render( context)
渲染結(jié)果:
假如不轉(zhuǎn)義,直接傳入這些特殊字符:
context = {
'orgin' : '直接傳入<>&看看渲染結(jié)果:' ,
'var1' : R(
'<>&:必須被轉(zhuǎn)義才能顯示, 可以使用RichText() 或 R()'
) ,
'var2' : '或者使用 "|e" jinja 過濾器來轉(zhuǎn)義 <>& ' ,
'var3' : escape( '或者使用escape函數(shù)來轉(zhuǎn)義: <>& ...' ) ,
'var4' : '多行文本\n\ttab縮進和一些段落\n會自動\a被轉(zhuǎn)換' ,
}
tpl. render( context)
渲染結(jié)果:
結(jié)果就是會導(dǎo)致其他已經(jīng)轉(zhuǎn)義的<>&
等字符都無法正常顯示,。
如果希望可以直接安全的轉(zhuǎn)入這些字符,可以開啟自動轉(zhuǎn)義:
context = {
'orgin' : '<>&沒有轉(zhuǎn)義的情況下無法顯示' ,
'var1' : R(
'<>&:必須被轉(zhuǎn)義才能顯示, 可以使用RichText() 或 R()'
) ,
'var2' : '或者使用 "|e" jinja 過濾器來轉(zhuǎn)義 <>& ' ,
'var3' : escape( '或者使用escape函數(shù)來轉(zhuǎn)義: <>& ...' ) ,
'var4' : '多行文本\n\ttab縮進和一些段落\n會自動\a被轉(zhuǎn)換' ,
}
tpl. render( context, autoescape= True )
渲染結(jié)果:
docxtpl的2個高級對象
內(nèi)嵌圖像
doxtpl.inlineImage對象可以動態(tài)地將一個或多個圖像添加到文檔中(支持JPEG和PNG格式),。
from docx. shared import Mm
myimage= InlineImage( tpl, 'python_logo.png' , width= Mm( 20 ) )
對于高度和寬度,必須使用毫米(毫米),英寸(英寸)或點(Pt)類。
示例
word模板:
python渲染代碼:
from docxtpl import DocxTemplate, InlineImage
# for height and width you have to use millimeters (Mm), inches or points(Pt) class :
from docx. shared import Mm
import jinja2
tpl = DocxTemplate( 'templates/inline_image_tpl.docx' )
context = {
'myimage' : InlineImage( tpl, 'templates/python_logo.png' , width= Mm( 20 ) ) ,
'myimageratio' : InlineImage(
tpl, 'templates/python_jpeg.jpg' , width= Mm( 30 ) , height= Mm( 60 )
) ,
'frameworks' : [
{
'image' : InlineImage( tpl, 'templates/django.png' , height= Mm( 10 ) ) ,
'desc' : 'The web framework for perfectionists with deadlines' ,
} ,
{
'image' : InlineImage( tpl, 'templates/zope.png' , height= Mm( 10 ) ) ,
'desc' : 'Zope is a leading Open Source Application Server and Content Management Framework' ,
} ,
{
'image' : InlineImage( tpl, 'templates/pyramid.png' , height= Mm( 10 ) ) ,
'desc' : 'Pyramid is a lightweight Python web framework aimed at taking small web apps into big web apps.' ,
} ,
{
'image' : InlineImage( tpl, 'templates/bottle.png' , height= Mm( 10 ) ) ,
'desc' : 'Bottle is a fast, simple and lightweight WSGI micro web-framework for Python' ,
} ,
{
'image' : InlineImage( tpl, 'templates/tornado.png' , height= Mm( 10 ) ) ,
'desc' : 'Tornado is a Python web framework and asynchronous networking library.' ,
} ,
] ,
}
# testing that it works also when autoescape has been forced to True
jinja_env = jinja2. Environment( autoescape= True )
tpl. render( context, jinja_env)
tpl. save( 'output/inline_image.docx' )
渲染結(jié)果:
子文件
Subdoc對象可以作為一個使用python-docx庫從頭開始構(gòu)建word文檔的對象,構(gòu)建的內(nèi)容可以直接嵌入到傳入的變量位置:
sd = tpl. new_subdoc( )
p = sd. add_paragraph( 'This is a sub-document inserted into a bigger one' )
p = sd. add_paragraph( 'It has been ' )
p. add_run( 'dynamically' ) . style = 'dynamic'
p. add_run( ' generated with python by using ' )
p. add_run( 'python-docx' ) . italic = True
p. add_run( ' library' )
context = {
'mysubdoc' : sd,
}
tpl. render( context)
效果:
替換word文檔的圖片或媒體
在頁眉/頁腳中是無法動態(tài)添加圖片和媒體的,但我們可以在模板中放置一個虛擬對象,像往常一樣渲染模板,然后用另一個對象替換虛擬對象,。從而實現(xiàn)圖片和媒體的添加,。
替換可以發(fā)生在頁眉、頁腳和整個文檔正文中,。
替換頁眉/頁腳中的圖片
需要先在頁眉/頁腳中放置一張模板圖片,替換時指定被插入的模板圖片的文件名即可,替換dummy_header_pic.jpg的語法:
tpl. replace_pic( 'dummy_header_pic.jpg' , 'header_pic_i_want.jpg' )
被替換的圖像在word文檔中將保持原始文檔的寬高比,。
替換頁眉/頁腳中的媒體
與替換圖片幾乎一致,替換dummy_header_pic.jpg的語法:
tpl. replace_media( 'dummy_header_pic.jpg' , 'header_pic_i_want.jpg' )
警告:與replace_pic() 方法不同,dummy_header_pic.jpg 必須存在模板目錄中。
替換嵌入對象
它的工作方式類似于媒體替換,只是它適用于嵌入式docx這樣的嵌入式對象,。
替換embedded_dummy.docx的語法:
tpl. replace_embedded( 'embdded_dummy.docx' , 'embdded_docx_i_want.docx' )
警告:與replace_pic()方法不同,embdded_dumy.docx必須存在于模板目錄中,。