python-docx 不改变原文件调整段落行间距的问题
?
python-docx模塊是處理word的利器,希望通過(guò)調(diào)用模塊生成預(yù)定格式的文件,word本身自帶的模板使用不太方便,而工作中對(duì)文檔格式要求很高(~~)
借助一個(gè)富文本編輯器,可以將文檔內(nèi)容輸出為word,存在幾類問(wèn)題,
-
字體大小、字號(hào)、加粗等,這些直接調(diào)用styles即可實(shí)現(xiàn),python-docx對(duì)字體支持還是很完善,
-
另外一個(gè)問(wèn)題是段落間距問(wèn)題,折騰了一天在抓狂的狀態(tài)下終于解決,原因還是因?yàn)閷?duì)xml原理不熟悉
以下介紹不改變?cè)募姆椒?#xff0c;修改間距為“行”“自動(dòng)”的方法:
1.如果段間距是以“磅”作為單位,那么對(duì)應(yīng)的python中即是Pt單位,通過(guò)以下直接對(duì)讀出的段落進(jìn)行修改:
pars=doc1.paragraphs for par in pars:if par.style.name=="Heading 1":par.paragraph_format.space_before = Pt(0)par.paragraph_format.space_after =Pt(0)也可以使用doc.styles["Heading 1"]進(jìn)行類修改,網(wǎng)上資料較多,修改pt值即可實(shí)現(xiàn)段前斷后間距;
2.如果段間距是以“行”作為單位,pydocx模塊內(nèi)置不能識(shí)別該格式,導(dǎo)致失效。
用xml查看會(huì)發(fā)現(xiàn)spacing設(shè)置“1行”的參數(shù)名稱:
<w:spacing w:beforeLines="100" w:afterLines="100"/>而源碼文件parfmt.py中class CT_Spacing(BaseOxmlElement)只定義了四種類型:after,before,line,lineRule,雖然在word中看到的段前斷后選1行和選1磅仍然在同一個(gè)框中,但是實(shí)際的數(shù)據(jù)類型已經(jīng)是另外的了。所以要對(duì)pydocx源碼進(jìn)行修改。
2.1在oxml/text/parfmt.py的CT_Spacing新增兩種類型:afterLines和beforeLines
class CT_Spacing(BaseOxmlElement):"""``<w:spacing>`` element, specifying paragraph spacing attributes such asspace before and line spacing."""after = OptionalAttribute('w:after', ST_TwipsMeasure)before = OptionalAttribute('w:before', ST_TwipsMeasure)line = OptionalAttribute('w:line', ST_SignedTwipsMeasure)lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING)afterLines = OptionalAttribute('w:afterLines', ST_TwipsMeasure)beforeLines = OptionalAttribute('w:beforeLines', ST_TwipsMeasure)2.2在parfmt.py修改spacing_after和spacing_before函數(shù)
def spacing_after(self):"""The value of `w:spacing/@w:after` or |None| if not present."""spacing = self.spacingif spacing is None:return Noneif spacing.afterLines is not None:return spacing.afterLinesif spacing.after is not None:return spacing.after@spacing_after.setterdef spacing_after(self, value):if value is None and self.spacing is None:returnif self.spacing.afterLines is not None:self.get_or_add_spacing().afterLines = valuereturnif self.spacing.after is not None:self.get_or_add_spacing().after = valuereturn@property一個(gè)是子函數(shù),一個(gè)是設(shè)置參數(shù),設(shè)置需要調(diào)用子函數(shù),所以都要改。
- 首先判斷spacing是不是空
- 接著判斷是否有行參數(shù),如果有則返回行參數(shù)或者修改行參數(shù)
- 如果行參數(shù)不存在,則按原來(lái)的pt等值進(jìn)行修改
(到這猜測(cè)pydocx為什么沒(méi)聲明這種類型,國(guó)內(nèi)用的word可能是微軟針對(duì)國(guó)內(nèi)word再加工,國(guó)外的統(tǒng)一用pt等度量值,可能沒(méi)行單位,或者就是pydocx有需要優(yōu)化的地方)
spacing_before也用同樣道理修改:
def spacing_before(self):"""The value of `w:spacing/@w:before` or |None| if not present."""spacing = self.spacingif spacing is None:return Noneif spacing.beforeLines is not None:return spacing.beforeLinesif spacing.before is not None:return spacing.before#return spacing.before@spacing_before.setterdef spacing_before(self, value):if value is None and self.spacing is None:returnif self.spacing.beforeLines is not None:self.get_or_add_spacing().beforeLines = valuereturnif self.spacing.before is not None:self.get_or_add_spacing().before = value@property2.3這樣可以試一下讀一個(gè)采用1行間距的值大小
讀出的值應(yīng)該是63500,而一個(gè)1磅的值應(yīng)該是6350,差了10倍。
解決了“行”的問(wèn)題,本來(lái)以為可以愉快玩耍了,但是遇到了下一個(gè)問(wèn)題,“自動(dòng)”
3.行間距為“自動(dòng)”的間距設(shè)置
有些富文本編輯器導(dǎo)出的行間距沿用上一段或者沿用一種style的模板,所以行間距在word中顯示自動(dòng),用上面改行的方法,對(duì)自動(dòng)行雖然讀出值是顯示改了,但是實(shí)際效果并沒(méi)有改,但是不怕,因?yàn)橥ㄟ^(guò)2已經(jīng)大概了解ms的套路。
3.1同樣用xml查看“自動(dòng)”行
<w:spacing w:before="100" w:beforeAutospacing="1" w:after="100" w:afterAutospacing="1"/>很明顯,又多了一個(gè)beforeAutospacing類型,而且value值是1,所以推斷它是一種布爾變量。
3.2在parfmt.py的CT_Spacing中再增加兩個(gè)類型,最終:
class CT_Spacing(BaseOxmlElement):"""``<w:spacing>`` element, specifying paragraph spacing attributes such asspace before and line spacing."""after = OptionalAttribute('w:after', ST_TwipsMeasure)before = OptionalAttribute('w:before', ST_TwipsMeasure)line = OptionalAttribute('w:line', ST_SignedTwipsMeasure)lineRule = OptionalAttribute('w:lineRule', WD_LINE_SPACING)afterLines = OptionalAttribute('w:afterLines', ST_TwipsMeasure)beforeLines = OptionalAttribute('w:beforeLines', ST_TwipsMeasure)beforeAutospacing=OptionalAttribute('w:beforeAutospacing', ST_TwipsMeasure)afterAutospacing = OptionalAttribute('w:afterAutospacing', ST_TwipsMeasure)3.3spacing_after和spacing_before的修改
當(dāng)我們要調(diào)整格式的時(shí)候,顯然不希望間距是自動(dòng),所以刪除這種類型最好,但是找了幾分鐘get_or_add_spacing這個(gè)函數(shù)在pydocx中信息較少,只有個(gè)相關(guān)方法的注冊(cè),再繼續(xù)研究又會(huì)陷入另外一個(gè)坑,所以轉(zhuǎn)換一下思路。
既然修改的時(shí)候肯定不是自動(dòng),那么就保留它,而把布爾值設(shè)為0,另外再新增需要的間距值就行。
所以不用管子函數(shù)函數(shù),只用改“設(shè)置”的那個(gè)函數(shù),在設(shè)置間距之前先將“自動(dòng)”賦0,所以after和before的修改如下:
def spacing_after(self, value):if value is None and self.spacing is None:returnself.spacing.beforeAutospacing=Noneself.spacing.afterAutospacing = Noneif self.spacing.afterLines is not None:self.get_or_add_spacing().afterLines = valuereturnif self.spacing.after is not None:self.get_or_add_spacing().after = valuereturn@property def spacing_before(self, value):if value is None and self.spacing is None:returnself.spacing.beforeAutospacing=Noneself.spacing.afterAutospacing = Noneif self.spacing.beforeLines is not None:self.get_or_add_spacing().beforeLines = valuereturnif self.spacing.before is not None:self.get_or_add_spacing().before = value@property將自動(dòng)強(qiáng)制設(shè)為None.
3.4測(cè)試
將word找個(gè)行調(diào)整為自動(dòng)或者X行,python程序設(shè)為Pt(0),運(yùn)行一下,發(fā)現(xiàn)完美實(shí)現(xiàn)間距為0。
檢查:將word導(dǎo)出為xml,結(jié)果發(fā)現(xiàn),將beforeAutospacing,afterAutospacing兩個(gè)變量設(shè)置為None后,xml實(shí)際刪除了auto的變量類型,最終也實(shí)現(xiàn)了我想要的結(jié)果。
終于又可以愉快的玩耍了。
還有一個(gè)思路是打開(kāi)原WORD,讀出paragraph列表了之后,用循環(huán)將內(nèi)容復(fù)制到另外一個(gè)word,用add_paragraph來(lái)設(shè)置間距、字體等。但是因?yàn)槲矣袌D有表,段落列表、圖列表和表格列表一次出來(lái)后存在定位問(wèn)題,又需要用xml去定位,坑可能更大,所以又返回來(lái)用修改原文件的方法。
介紹得有點(diǎn)啰嗦,主要說(shuō)明思路,希望有所幫助。
TIPs:notepad等工具修改源碼會(huì)使tab的空格發(fā)生變化而報(bào)錯(cuò),所以還是用pycharm等工具修改源碼較好。
總結(jié)
以上是生活随笔為你收集整理的python-docx 不改变原文件调整段落行间距的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阻止事件冒泡 -- 在antd-mo
- 下一篇: 哈罗python的学费多少-数据分析面试