• pythondocx生成多级编号数字列表


    前言

    入职了一家开发流程极其规范的公司,每个项目都要编写很多格式基本相同,但内容完全不同的文档。

    在编写过程中,发现其中一份文档50%的内容来自于源代码,而且是纯复制粘贴+稍微润色的活儿。

    秉承着重复性强的手工作业就要交给机器去办的个人原则,使用python开发了个工具,自动生成文档内容。

    顺利的开发过程

    开发环境使用miniconda里的python3,使用open读取源代码全部内容,正则匹配出合适代码块,提取其中的关键信息。最后用python-docx将结果导出到docx文件。

    用gooey给工具加个GUI界面,增强易用性。

    多级编号列表断档问题

    在使用过程中,发现生成出来的编号列表与文档模板的编号列表不符,直接复制粘贴会导致编号列表的序号断档,但是再一个一个手动的赋予样式又会产生新的重复劳动。

    公司文档模板

    使用工具生成的部分相当于是文档模板中4.2里面,多级编号列表的标号从4.2.1开始递推。

    我的py脚本里使用的是docx自带的样式“List Number 3”,从生成结果看,怎么都是单级编号列表。

     1 from docx import Document
     2 from docx.oxml.shared import qn
     3 from docx.shared import Pt
     4 
     5 
     6 doc = Document()
     7 para = doc.add_paragraph('', style='List Number 3')
     8 para.paragraph_format.space_after = Pt(0)
     9 run = para.add_run('表内容1')
    10 run.font.name = '宋体'
    11 run.font.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    12 run.font.bold = True
    13 run.font.size = Pt(14)

    将生成结果复制到文档模板中,样式和大纲标题均无法连续。

    分析docx的xml

    将docx解压可以得到类似如下的目录结构

    文档内容在word/document.xml

    文档样式在word/styles.xml

    多级列表属性在word/numbering.xml

    分析document.xml,使用样式“List Number 3”生成的列表段落节点如下:

    <w:p w14:paraId="696A4A66" w14:textId="7E41BC18" w:rsidR="005A47E3" w:rsidRDefault="005A47E3" w:rsidP="005A47E3">
        <w:pPr>
            <w:pStyle w:val="a3"/>
            <w:numPr>
                <w:ilvl w:val="0"/>
                <w:numId w:val="2"/>
            </w:numPr>
            <w:ind w:firstLineChars="0"/>
        </w:pPr>
        <w:r>
            <w:rPr>
                <w:rFonts w:hint="eastAsia"/>
            </w:rPr>
            <w:t>提取内容1-1</w:t>
        </w:r>
    </w:p>

    根据查阅文档可得,<w:pPr>下的<w:pStyle>节点储存的是样式的styleId,<w:numPr>下的<w:ilvl>节点存的是缩进等级,而<w:numId>节点存的是数字等级。

    但使用新建OxmlElement对pPr进行手动节点添加后,发现生成的docx中,数字编号依然只有一级。

    解决方案

    在对xml反复研究测试了2天后,发现靠手动的节点添加和修改无法实现多级数字编号。

    【插入反杠声明】

    而从修改<w:pStyle>节点的styleId实现多级数字列表这一解决方向出现了突破口。

    分析styles.xml,在<w:latentStyles>节点的后面,是许多自定义的样式。例如:

    <w:style w:type="paragraph" w:customStyle="1" w:styleId="lv2">
        <w:name w:val="lv2"/>
        <w:basedOn w:val="a5"/>
        <w:link w:val="lv20"/>
        <w:qFormat/>
        <w:rsid w:val="00762E5E"/>
        <w:pPr>
            <w:numPr>
                <w:ilvl w:val="1"/>
                <w:numId w:val="2"/>
            </w:numPr>
            <w:ind w:firstLineChars="0"/>
        </w:pPr>
        <w:rPr>
            <w:b/>
            <w:bCs/>
            <w:sz w:val="24"/>
            <w:szCs w:val="28"/>
        </w:rPr>
    </w:style>

    这是一个自定义的二级数字编号列表样式,run为粗体,继承于a5,styleId是lv2。

    之前的脚本中使用的是python-docx模块自带的default.docx作为基础模板。

    而解决编号列表断档问题需要制作一个专门的模板,从文档模板中把需要生成的部分拷入新模板中,这样源样式也会一起拷进来,样式可以不用重命名。

    将新模板解压,查看其中的styles.xml,找到数字编号列表样式的w:styleId,把值记下。

    在py脚本中,初始化文档时,指定模板。在添加段落时,对pPr进行自定义节点添加。

     1 from docx import Document
     2 from docx.oxml.shared import OxmlElement, qn
     3 from docx.shared import Pt
     4 
     5 
     6 doc = Document('template.docx')
     7 para = doc.add_paragraph()
     8 pPr = para._element.get_or_add_pPr()
     9 pStyle = OxmlElement('w:pStyle', {qn('w:val'), 'lv3'}) # lv3 是从styles.xml里找到的对应样式w:styleId的值
    10 pPr.append(pStyle)
    11 para.add_run('表内容1')

    最终生成的结果符合预期,全选复制到源文档模板内能够达到编号连续的效果。

    相关学习文档

    Python操纵Word神器——python-docx大全

    DocumentFormat.OpenXml

  • 相关阅读:
    Java接口(interface),扫盲贴
    Java抽象类,扫盲贴
    Java类的继承、super关键字、复写
    Java内部类,扫盲贴
    数据结构学习笔记1--简单排序
    7.1 通用的职责分配软件原则 GRASP原则一: 创建者 Creator
    6.6 面向对象设计
    6.5 开始进入设计 … Transition to Design
    6.4 操作契约 Operation Contracts
    6.3 契约式设计
  • 原文地址:https://www.cnblogs.com/waltersgarden/p/16312598.html
Copyright © 2020-2023  润新知