Python: создайте оглавление с помощью python-docx / lxml

Я пытаюсь автоматизировать создание файлов .docx (WordML) с помощью python-docx (https://github.com/mikemaccana/python-docx). Мой текущий сценарий создает ToC вручную с помощью следующего цикла:

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

Кто-нибудь знает способ использования "встроенной в слова" ToC-функции, которая автоматически добавляет индекс, а также создает ссылки на абзацы для отдельных глав?

Большое спасибо!


person Joseph jun. Melettukunnel    schedule 03.09.2013    source источник


Ответы (4)


Ключевой проблемой является то, что отображаемый ToC зависит от разбивки на страницы, чтобы знать, какой номер страницы поставить для каждого заголовка. Разбивка на страницы - это функция, предоставляемая механизмом компоновки, очень сложным программным обеспечением, встроенным в клиент Word. Написание движка макета страницы на Python, вероятно, не лучшая идея, определенно не тот проект, которым я планирую заняться в ближайшее время :)

ToC состоит из двух частей:

  1. элемент, определяющий размещение ToC и такие вещи, как уровни заголовков, которые нужно включить.
  2. фактическое видимое содержимое ToC, заголовки и номера страниц с соединяющими их пунктирными линиями.

Создание элемента довольно просто и относительно не требует больших усилий. Для создания фактического видимого содержимого, по крайней мере, если вы хотите, чтобы номера страниц были включены, требуется механизм компоновки Word.

Это варианты:

  1. Просто добавьте тег и несколько других битов, чтобы сообщить Word, что ToC необходимо обновить. При первом открытии документа появляется диалоговое окно с сообщением, что ссылки необходимо обновить. Пользователь нажимает Да, и Боб - ваш дядя. Если пользователь нажимает «Нет», отображается заголовок ToC без содержания под ним, и ToC можно обновить вручную.

  2. Добавьте тег, а затем задействуйте клиент Word с помощью C # или Visual Basic для библиотеки Word Automation, чтобы открыть и сохранить файл; все поля (включая поле ToC) обновляются.

  3. Сделайте то же самое на стороне сервера, если у вас есть экземпляр SharePoint или что-то еще, что может сделать это с помощью Word Automation Services.

  4. Создайте в документе макрос AutoOpen, который автоматически запускает обновление поля при открытии документа. Вероятно, не пройдет много проверок на вирусы и не будет работать с заблокированными сборками Windows, обычными в корпоративной среде.

Вот очень хороший набор скринкасты Эрика Уайта, объясняющие все непростые детали

person scanny    schedule 03.09.2013

Извините за добавление комментариев к старому сообщению, но я думаю, что это может быть полезно. Это не мое решение, но оно было найдено там: https://github.com/python-openxml/python-docx/issues/36 Благодаря https://github.com/mustash и https://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
person Kostiantyn Medvid    schedule 05.02.2018
comment
Ага! См. эту страницу, где @ Sup3rGeo сообщает, что мне тоже пришлось сбежать \ \ o \\ h \\ z \\ u чтобы он работал без ошибок, после чего у меня работает. Я предлагаю вам обновить свой отличный ответ. Вот если бы был способ программно обновить оглавление ;-) - person Mawg says reinstate Monica; 20.12.2018
comment
@Mawg Спасибо за предложение. Обновлю комментарий. - person Kostiantyn Medvid; 03.01.2019
comment
Отлично (+1)! Это, безусловно, поможет другим в будущем (это, безусловно, помогло мне, когда я понял, что такое двойное спасение). Вот если бы был способ программно обновить оглавление ;-) - person Mawg says reinstate Monica; 03.01.2019
comment
@MawgsaysreinstateMonica или кто-нибудь еще, вы нашли в Ubuntu способ обновления оглавления? - person melutovich; 13.08.2020

@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()
person sliceoflive    schedule 14.03.2019
comment
Для этого нужна среда Windows. stackoverflow.com/questions/62158702 / Нужно будет изучить, как сделать этот шаг в средах Ubuntu / Mac. - person rain; 14.04.2021

Для создания автоматического оглавления в Word с помощью python:

#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 

#Спасибо

person Vardhmaan Jain    schedule 04.12.2019
comment
Спасибо за фрагмент кода, но зачем нам обновлять Таблицу содержания вручную? Есть ли способ автоматизировать это в скрипте Python? - person rain; 08.04.2021
comment
Думаю, как автообновление узнал. Добавьте эти строки кода для fldChar3. fldChar3 = OxmlElement ('w: updateFields') fldChar3.set (qn ('w: val'), 'истина') - person rain; 08.04.2021