python

您所在的位置:网站首页 docx文档行距很大无法调整 python

python

2024-06-30 15:57| 来源: 网络整理| 查看: 265

 

python-docx模块是处理word的利器,希望通过调用模块生成预定格式的文件,word本身自带的模板使用不太方便,而工作中对文档格式要求很高(~~)

借助一个富文本编辑器,可以将文档内容输出为word,存在几类问题,

字体大小、字号、加粗等,这些直接调用styles即可实现,python-docx对字体支持还是很完善,

另外一个问题是段落间距问题,折腾了一天在抓狂的状态下终于解决,原因还是因为对xml原理不熟悉

以下介绍不改变原文件的方法,修改间距为“行”“自动”的方法:

1.如果段间距是以“磅”作为单位,那么对应的python中即是Pt单位,通过以下直接对读出的段落进行修改: 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"]进行类修改,网上资料较多,修改pt值即可实现段前断后间距;

2.如果段间距是以“行”作为单位,pydocx模块内置不能识别该格式,导致失效。

用xml查看会发现spacing设置“1行”的参数名称:

而源码文件parfmt.py中class CT_Spacing(BaseOxmlElement)只定义了四种类型:after,before,line,lineRule,虽然在word中看到的段前断后选1行和选1磅仍然在同一个框中,但是实际的数据类型已经是另外的了。所以要对pydocx源码进行修改。

2.1在oxml/text/parfmt.py的CT_Spacing新增两种类型:afterLines和beforeLines class CT_Spacing(BaseOxmlElement): """ ```` element, specifying paragraph spacing attributes such as space 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函数 def spacing_after(self): """ The value of `w:spacing/@w:after` or |None| if not present. """ spacing = self.spacing if spacing is None: return None if spacing.afterLines is not None: return spacing.afterLines if spacing.after is not None: return spacing.after @spacing_after.setter def spacing_after(self, value): if value is None and self.spacing is None: return if self.spacing.afterLines is not None: self.get_or_add_spacing().afterLines = value return if self.spacing.after is not None: self.get_or_add_spacing().after = value return @property

一个是子函数,一个是设置参数,设置需要调用子函数,所以都要改。

首先判断spacing是不是空接着判断是否有行参数,如果有则返回行参数或者修改行参数如果行参数不存在,则按原来的pt等值进行修改

(到这猜测pydocx为什么没声明这种类型,国内用的word可能是微软针对国内word再加工,国外的统一用pt等度量值,可能没行单位,或者就是pydocx有需要优化的地方)

spacing_before也用同样道理修改:

def spacing_before(self): """ The value of `w:spacing/@w:before` or |None| if not present. """ spacing = self.spacing if spacing is None: return None if spacing.beforeLines is not None: return spacing.beforeLines if spacing.before is not None: return spacing.before #return spacing.before @spacing_before.setter def spacing_before(self, value): if value is None and self.spacing is None: return if self.spacing.beforeLines is not None: self.get_or_add_spacing().beforeLines = value return if self.spacing.before is not None: self.get_or_add_spacing().before = value @property 2.3这样可以试一下读一个采用1行间距的值大小

读出的值应该是63500,而一个1磅的值应该是6350,差了10倍。

解决了“行”的问题,本来以为可以愉快玩耍了,但是遇到了下一个问题,“自动”

3.行间距为“自动”的间距设置

有些富文本编辑器导出的行间距沿用上一段或者沿用一种style的模板,所以行间距在word中显示自动,用上面改行的方法,对自动行虽然读出值是显示改了,但是实际效果并没有改,但是不怕,因为通过2已经大概了解ms的套路。

3.1同样用xml查看“自动”行

很明显,又多了一个beforeAutospacing类型,而且value值是1,所以推断它是一种布尔变量。

3.2在parfmt.py的CT_Spacing中再增加两个类型,最终: class CT_Spacing(BaseOxmlElement): """ ```` element, specifying paragraph spacing attributes such as space 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的修改

当我们要调整格式的时候,显然不希望间距是自动,所以删除这种类型最好,但是找了几分钟get_or_add_spacing这个函数在pydocx中信息较少,只有个相关方法的注册,再继续研究又会陷入另外一个坑,所以转换一下思路。

既然修改的时候肯定不是自动,那么就保留它,而把布尔值设为0,另外再新增需要的间距值就行。

所以不用管子函数函数,只用改“设置”的那个函数,在设置间距之前先将“自动”赋0,所以after和before的修改如下:

def spacing_after(self, value): if value is None and self.spacing is None: return self.spacing.beforeAutospacing=None self.spacing.afterAutospacing = None if self.spacing.afterLines is not None: self.get_or_add_spacing().afterLines = value return if self.spacing.after is not None: self.get_or_add_spacing().after = value return @property def spacing_before(self, value): if value is None and self.spacing is None: return self.spacing.beforeAutospacing=None self.spacing.afterAutospacing = None if self.spacing.beforeLines is not None: self.get_or_add_spacing().beforeLines = value return if self.spacing.before is not None: self.get_or_add_spacing().before = value @property

将自动强制设为None.

3.4测试

将word找个行调整为自动或者X行,python程序设为Pt(0),运行一下,发现完美实现间距为0。

检查:将word导出为xml,结果发现,将beforeAutospacing,afterAutospacing两个变量设置为None后,xml实际删除了auto的变量类型,最终也实现了我想要的结果。

终于又可以愉快的玩耍了。

还有一个思路是打开原WORD,读出paragraph列表了之后,用循环将内容复制到另外一个word,用add_paragraph来设置间距、字体等。但是因为我有图有表,段落列表、图列表和表格列表一次出来后存在定位问题,又需要用xml去定位,坑可能更大,所以又返回来用修改原文件的方法。

介绍得有点啰嗦,主要说明思路,希望有所帮助。

TIPs:notepad等工具修改源码会使tab的空格发生变化而报错,所以还是用pycharm等工具修改源码较好。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3