python-docx 不改变原文件调整段落行间距的问题
?
python-docx模塊是處理word的利器,希望通過調(diào)用模塊生成預(yù)定格式的文件,word本身自帶的模板使用不太方便,而工作中對文檔格式要求很高(~~)
借助一個富文本編輯器,可以將文檔內(nèi)容輸出為word,存在幾類問題,
-
字體大小、字號、加粗等,這些直接調(diào)用styles即可實(shí)現(xiàn),python-docx對字體支持還是很完善,
-
另外一個問題是段落間距問題,折騰了一天在抓狂的狀態(tài)下終于解決,原因還是因?yàn)閷ml原理不熟悉
以下介紹不改變原文件的方法,修改間距為“行”“自動”的方法:
1.如果段間距是以“磅”作為單位,那么對應(yīng)的python中即是Pt單位,通過以下直接對讀出的段落進(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)置不能識別該格式,導(dǎo)致失效。
用xml查看會發(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磅仍然在同一個框中,但是實(shí)際的數(shù)據(jù)類型已經(jīng)是另外的了。所以要對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一個是子函數(shù),一個是設(shè)置參數(shù),設(shè)置需要調(diào)用子函數(shù),所以都要改。
- 首先判斷spacing是不是空
- 接著判斷是否有行參數(shù),如果有則返回行參數(shù)或者修改行參數(shù)
- 如果行參數(shù)不存在,則按原來的pt等值進(jìn)行修改
(到這猜測pydocx為什么沒聲明這種類型,國內(nèi)用的word可能是微軟針對國內(nèi)word再加工,國外的統(tǒng)一用pt等度量值,可能沒行單位,或者就是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這樣可以試一下讀一個采用1行間距的值大小
讀出的值應(yīng)該是63500,而一個1磅的值應(yīng)該是6350,差了10倍。
解決了“行”的問題,本來以為可以愉快玩耍了,但是遇到了下一個問題,“自動”
3.行間距為“自動”的間距設(shè)置
有些富文本編輯器導(dǎo)出的行間距沿用上一段或者沿用一種style的模板,所以行間距在word中顯示自動,用上面改行的方法,對自動行雖然讀出值是顯示改了,但是實(shí)際效果并沒有改,但是不怕,因?yàn)橥ㄟ^2已經(jīng)大概了解ms的套路。
3.1同樣用xml查看“自動”行
<w:spacing w:before="100" w:beforeAutospacing="1" w:after="100" w:afterAutospacing="1"/>很明顯,又多了一個beforeAutospacing類型,而且value值是1,所以推斷它是一種布爾變量。
3.2在parfmt.py的CT_Spacing中再增加兩個類型,最終:
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)整格式的時候,顯然不希望間距是自動,所以刪除這種類型最好,但是找了幾分鐘get_or_add_spacing這個函數(shù)在pydocx中信息較少,只有個相關(guān)方法的注冊,再繼續(xù)研究又會陷入另外一個坑,所以轉(zhuǎn)換一下思路。
既然修改的時候肯定不是自動,那么就保留它,而把布爾值設(shè)為0,另外再新增需要的間距值就行。
所以不用管子函數(shù)函數(shù),只用改“設(shè)置”的那個函數(shù),在設(shè)置間距之前先將“自動”賦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將自動強(qiáng)制設(shè)為None.
3.4測試
將word找個行調(diào)整為自動或者X行,python程序設(shè)為Pt(0),運(yùn)行一下,發(fā)現(xiàn)完美實(shí)現(xiàn)間距為0。
檢查:將word導(dǎo)出為xml,結(jié)果發(fā)現(xiàn),將beforeAutospacing,afterAutospacing兩個變量設(shè)置為None后,xml實(shí)際刪除了auto的變量類型,最終也實(shí)現(xiàn)了我想要的結(jié)果。
終于又可以愉快的玩耍了。
還有一個思路是打開原WORD,讀出paragraph列表了之后,用循環(huán)將內(nèi)容復(fù)制到另外一個word,用add_paragraph來設(shè)置間距、字體等。但是因?yàn)槲矣袌D有表,段落列表、圖列表和表格列表一次出來后存在定位問題,又需要用xml去定位,坑可能更大,所以又返回來用修改原文件的方法。
介紹得有點(diǎn)啰嗦,主要說明思路,希望有所幫助。
TIPs:notepad等工具修改源碼會使tab的空格發(fā)生變化而報錯,所以還是用pycharm等工具修改源碼較好。
總結(jié)
以上是生活随笔為你收集整理的python-docx 不改变原文件调整段落行间距的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阻止事件冒泡 -- 在antd-mo
- 下一篇: leetcode刷题之旅(5) Long