【问题标题】:Python: Create a "Table Of Contents" with python-docx/lxmlPython:使用 python-docx/lxml 创建“目录”
【发布时间】:2020-06-03 18:01:19
【问题描述】:

我正在尝试借助 python-docx (https://github.com/mikemaccana/python-docx) 自动创建 .docx 文件 (WordML)。我当前的脚本使用以下循环手动创建 ToC:

for chapter in myChapters:
    body.append(paragraph(chapter.text, style='ListNumber'))

有谁知道使用“内置单词”ToC 功能的方法,该功能会自动添加索引并创建各个章节的段落链接?

非常感谢!

【问题讨论】:

    标签: python docx wordml python-docx


    【解决方案1】:

    关键的挑战是渲染的 ToC 依赖于分页来知道为每个标题放置什么页码。分页是布局引擎提供的一个功能,是Word客户端内置的一个非常复杂的软件。用 Python 编写页面布局引擎可能不是一个好主意,绝对不是我打算很快开展的项目:)

    ToC 由两部分组成:

    1. 该元素指定 ToC 位置以及要包含的标题级别等内容。
    2. 实际可见的 ToC 内容、标题和页码,用虚线连接它们。

    创建元素非常简单且相对省力。创建实际可见的内容(至少如果您希望包含页码)需要 Word 布局引擎。

    这些是选项:

    1. 只需添加标签和其他一些位即可向 Word 发出 ToC 需要更新的信号。首次打开文档时,会出现一个对话框,提示需要刷新链接。用户单击是,Bob 是你的叔叔。如果用户单击“否”,则 ToC 标题将出现,其下方没有内容,并且可以手动更新 ToC。

    2. 添加标签,然后使用 Word 客户端,通过 C# 或 Visual Basic 针对 Word 自动化库,打开并保存文件;所有字段(包括 ToC 字段)都会更新。

    3. 如果您有 SharePoint 实例或任何可以使用 Word Automation Services 执行的操作,请在服务器端执行相同的操作。

    4. 在文档中创建一个 AutoOpen 宏,该宏会在文档打开时自动运行字段更新。可能无法通过大量病毒检查程序,并且无法在企业环境中常见的锁定 Windows 版本上运行。

    这里是a very nice set of screencasts by Eric White that explain all the hairy details

    【讨论】:

    • 你能指导我如何使用第一个选项,请使用标签和信号吗?
    【解决方案2】:

    很抱歉将 cmets 添加到旧帖子中,但我认为这可能会有所帮助。 这不是我的解决方案,但已在此处找到:https://github.com/python-openxml/python-docx/issues/36 感谢https://github.com/mustashhttps://github.com/scanny

        from docx.oxml.ns import qn
        from docx.oxml import OxmlElement
    
        paragraph = self.document.add_paragraph()
        run = paragraph.add_run()
        fldChar = OxmlElement('w:fldChar')  # creates a new element
        fldChar.set(qn('w:fldCharType'), 'begin')  # sets attribute on element
        instrText = OxmlElement('w:instrText')
        instrText.set(qn('xml:space'), 'preserve')  # sets attribute on element
        instrText.text = 'TOC \\o "1-3" \\h \\z \\u'   # change 1-3 depending on heading levels you need
    
        fldChar2 = OxmlElement('w:fldChar')
        fldChar2.set(qn('w:fldCharType'), 'separate')
        fldChar3 = OxmlElement('w:t')
        fldChar3.text = "Right-click to update field."
        fldChar2.append(fldChar3)
    
        fldChar4 = OxmlElement('w:fldChar')
        fldChar4.set(qn('w:fldCharType'), 'end')
    
        r_element = run._r
        r_element.append(fldChar)
        r_element.append(instrText)
        r_element.append(fldChar2)
        r_element.append(fldChar4)
        p_element = paragraph._p
    

    【讨论】:

    • instrText.text = 'TOC \o "1-3" \h \z \u' # change 1-3 depending on heading levels you need ^ SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 19-20: truncated \uXXXX escape
    • 啊哈!请参阅this page,其中@Sup3rGeo 说“我还必须转义 \\o \\h \\z \\u 才能正常工作”,之后它对我有用。我建议您更新您原本出色的答案。现在,如果只有某种方法可以以编程方式更新目录;-)
    • @Mawg 谢谢你的建议。将更新评论。
    • 太棒了(+1)!这肯定会在未来帮助其他人(一旦我弄清楚双重转义,它肯定会帮助我)。现在,如果只有某种方法可以以编程方式更新目录;-)
    • @MawgsaysreinstateMonica 或其他任何人你有没有在 ubuntu 上找到更新目录的方法?
    【解决方案3】:

    @Mawg // 更新 ToC

    在更新 ToC 时遇到了同样的问题,并在 Google 上搜索了它。不是我的代码,但它可以工作:

    word = win32com.client.DispatchEx("Word.Application")
    doc = word.Documents.Open(input_file_name)
    doc.TablesOfContents(1).Update()
    doc.Close(SaveChanges=True)
    word.Quit()
    

    【讨论】:

    【解决方案4】:

    使用 python 在 Word 中创建自动目录:

    #First set directory where you want to save the file
    
    import os
    os.chdir("D:/")
    
    #Now import required packages
    
    import docx
    from docx import Document
    from docx.oxml.ns import qn
    from docx.oxml import OxmlElement
    
    #Initialising document to make word file using python
    
    document = Document()
    
    #Code for making Table of Contents
    
    paragraph = document.add_paragraph()
    run = paragraph.add_run()
    fldChar = OxmlElement('w:fldChar')  # creates a new element
    fldChar.set(qn('w:fldCharType'), 'begin')  # sets attribute on element
    instrText = OxmlElement('w:instrText')
    instrText.set(qn('xml:space'), 'preserve')  # sets attribute on element
    instrText.text = 'TOC \\o "1-3" \\h \\z \\u'   # change 1-3 depending on heading levels you need
    
    fldChar2 = OxmlElement('w:fldChar')
    fldChar2.set(qn('w:fldCharType'), 'separate')
    fldChar3 = OxmlElement('w:t')
    fldChar3.text = "Right-click to update field."
    fldChar2.append(fldChar3)
    
    fldChar4 = OxmlElement('w:fldChar')
    fldChar4.set(qn('w:fldCharType'), 'end')
    
    r_element = run._r
    r_element.append(fldChar)
    r_element.append(instrText)
    r_element.append(fldChar2)
    r_element.append(fldChar4)
    p_element = paragraph._p
    
    #Giving headings that need to be included in Table of contents
    
    document.add_heading("Network Connectivity")
    document.add_heading("Weather Stations")
    
    #Saving the word file by giving name to the file
    
    name = "mdh2"
    document.save(name+".docx")
    
    #Now check word file which got created
    
    #Select "Right-click to update field text"
    #Now right click and then select update field option
    #and then click on update entire table
    
    #Now,You will find Automatic Table of Contents 
    

    #谢谢

    【讨论】:

    • 感谢代码 sn-p,但是为什么我们必须手动更新目录。有没有办法在 python 脚本中实现自动化?
    • 我想我发现了如何自动更新。为 fldChar3 添加这些代码行。 fldChar3 = OxmlElement('w:updateFields') fldChar3.set(qn('w:val'), 'true')
    • 当我打开word文档时,它说该文档链接到另一个文档。这是什么原因?