python-openxml / python-docx

Create and modify Word documents with Python
MIT License
4.51k stars 1.11k forks source link

Automatically update table of contents #1207

Closed jadeid91 closed 12 months ago

jadeid91 commented 1 year ago

Hello,

Is there any solution to automatically update the table of contents before a word document is opened? I checked other solutions like aspose-words and it works there: https://blog.aspose.com/words/create-word-documents-using-python/

scanny commented 12 months ago

Closing as stale/inactive.

suryanshagnihotri commented 7 months ago

Is there any solution for this? if doc is updated via python-docx, it need us to manually click "update table or update field " in word doc...

albcunha commented 7 months ago

As this is a hot topic, one alternative way to solve is to use libreoffice cli + a macro to update the table of contents. you would run the macro like this:

def update_toc_from_docx_file(self, file_path):
        subprocess.call(
            [
                "libreoffice",
                "--headless",
                f"macro:///Standard.Module1.UpdateTOC({file_path})",
            ]
        )

Check where your vba macros are. On Module1.xba, you overwrite it with this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">REM  *****  BASIC  *****

Sub UpdateTOC(path As String)
     '''Update indexes, such as for the table of contents''' 
     Dim doc As Object
     Dim args()

     doc = StarDesktop.loadComponentFromUrl(convertToUrl(path), "_default", 0, args())

     Dim i As Integer

     With doc ' Only process Writer documents
     If .supportsService("com.sun.star.text.GenericTextDocument") Then
         For i = 0 To .getDocumentIndexes().count - 1
         .getDocumentIndexes().getByIndex(i).update()
         Next i
     End If
     End With ' ThisComponent

     doc.store()
     doc.close(True)
end sub

</script:module>

I should note this macro does not raise any error if it fails. the variable file_path should be an absolute path.