Skip to content
pdfconversionapi

Guide5 min read

Table of contents not updating when converting Word to PDF

A Word table of contents is saved text, so the PDF shows it as it was last updated. Set UpdateTableOfContents=true to rebuild it. Before and after, tested.

Written and tested by Zeel Shekhaliya, Founder

Contents (8 sections)

A table of contents in a Word file is a field. Word works out its entries and page numbers when you update it, then saves the result in the file as ordinary text. Our DOCX to PDF prints that saved text unless you ask for the fields to be updated, so a contents list saved before the last round of edits comes out of the PDF with old headings and wrong page numbers. Send UpdateTableOfContents=true and the API rebuilds it from the headings before it writes the PDF.

Shell
curl -sS -X POST https://api.pdfconversionapi.com/v2/convert/docx/to/pdf \
  -H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
  -F File=@field-service-handbook.docx \
  -F UpdateTableOfContents=true \
  -o response.json

jq -r '.Files[0].FileData' response.json | base64 --decode > field-service-handbook.pdf

The same handbook, without and with the option

Our test handbook has five sections, each starting on a new page, so the right page numbers are 2 to 6. We wrote its contents field the way Word leaves it when a document changes after the last update: the saved entries still call section 2 "Setup" (it is now "Installing the unit"), leave out 2.2, 3.1, 3.2 and sections 4 and 5, and give every page number one page too low.

First page of the PDF without the option: six entries, "2 Setup", page numbers 1 to 3
Without the option: the contents as they were saved.
First page of the PDF with UpdateTableOfContents=true: fourteen entries, "2 Installing the unit", page numbers 2 to 6
With UpdateTableOfContents=true: rebuilt from the headings.
In the PDFNo optionUpdateTableOfContents=true
Contents entries614
Section 2's entry"2 Setup", the old name"2 Installing the unit"
Sections 4 and 5MissingListed, with their subsections
Page numbers in the contents1, 1, 1, 2, 2, 3: each one page too low2 to 6, matching the headings
Cross-reference in section 2.2"Error codes on page 4""4 Fault codes on page 5"
Clickable entries614
Footer"Page 1 of 6" to "Page 6 of 6"The same
PDF bookmarks14, to the right pagesThe same

Three things were right even without the option. The footer's page numbers were saved as "Page 1 of 1" and printed correctly on every page. The bookmarks, which come from the headings, were right in both PDFs. And the six stale entries still linked to the right pages (2, 2, 2, 3, 3 and 4): in the Word file each entry links to a bookmark on its heading, and the converter points the link at wherever that heading is. Someone who clicks an entry lands in the right place; someone who reads the printed number and scrolls does not.

The cross-reference in section 2.2 is two fields, one for the heading's text and one for its page. They were saved as "Error codes on page 4"; with the option they show the heading as it is now, "4 Fault codes", on page 5.

UpdateFields, UpdateTableOfContents and UpdateReferences do the same thing

Any one of the three turns on a single pass that refreshes every field the converter can update. In our files that meant the contents and the cross-reference together. Footer page numbers were right with or without it, and the DOCX to PDF page notes the same for page counts and dates. We sent the handbook once with each, and all three returned the same 45,899-byte PDF, identical byte for byte. There is no way to refresh the contents and leave an old cross-reference as it was.

Documents generated by code

Code that writes DOCX files from a template can insert a contents field, but it can't work out page numbers without laying out the pages, so the field can be saved with only a placeholder. Our generated report saves the line "Right-click to update field." in its contents field.

  • Without the option, the PDF prints "Right-click to update field." under "Contents".
  • With UpdateTableOfContents=true, the contents list its 7 headings with page numbers 2 to 4, and each entry links to its page.
  • The same report with Word's "update fields when the file opens" setting (<w:updateFields w:val="true"/> in settings.xml) gave a PDF identical to the one without it. In Word, that setting asks the person opening the file whether to update. The converter doesn't read it, so send the option with the request.

Headings need a heading style or an outline level

The contents are built from paragraphs that have a heading style or an outline level. The headings file has four sections: A and B start with large bold text in the Normal style, and C and D start with the same bold text plus an outline level set on the paragraph (in Word: Paragraph, Outline level).

With UpdateTableOfContents=true, the contents listed C on page 4 and D on page 5, and left out A and B. The PDF's bookmarks follow the same rule, with or without the option: two bookmarks, C and D. If a template formats headings by hand, give them the Heading 1 to Heading 3 styles. Our contents field includes the \u switch, which tells Word to use outline levels as well as heading styles; we didn't test a field without it.

Fix the Word file as well

If you keep or send the Word file too, docx/to/docx takes the same three option names. With UpdateFields=true it returned the handbook as a .docx with the contents and the cross-reference refreshed. Converting that file to PDF afterwards, with no options, gave the same PDF byte for byte as converting the original with the option.

The request in Python and Node.js

Each version writes the 45,899-byte field-service-handbook.pdf from the test file and prints its name and size.

# Needs the requests package: pip install requests
import base64
import os

import requests

with open("field-service-handbook.docx", "rb") as f:
    response = requests.post(
        "https://api.pdfconversionapi.com/v2/convert/docx/to/pdf",
        headers={"Authorization": f"Bearer {os.environ['PDFCONVERSIONAPI_KEY']}"},
        files={"File": f},
        data={"UpdateTableOfContents": "true"},
        timeout=150,
    )
result = response.json()
if not response.ok:
    raise SystemExit(f"{result['Code']}: {result['Message']}")

pdf = result["Files"][0]
with open(pdf["FileName"], "wb") as out:
    out.write(base64.b64decode(pdf["FileData"]))
print(pdf["FileName"], pdf["FileSize"], "bytes")

What the option doesn't change

  • Page numbers come from our layout, not Word's. Fonts the server doesn't have are replaced, where one exists, with a font of the same character widths; the test files use Calibri and Cambria, which were replaced this way, so lines should break in the same places. Our test files start each section on a new page, so their page numbers don't depend on where a page happens to break. In a long document where headings fall mid-page, a heading can land on a different page than in Word if a page breaks differently; we haven't compared that with Word.
  • A contents list typed by hand isn't a field, so the option has nothing to update in it.
  • It's all or nothing. You can't keep one field's saved result while the others are updated.

How we tested

Date
2026-11-05. Every request and result on this page ran that day.
Server
Our API running locally in a container, built from the same source as the hosted API. The commands show the hosted address; we ran them with only the base URL changed.
Files
Four Word files we wrote for this test (download them above). A handbook whose contents and one cross-reference were saved before the last edits; a generated report whose contents field holds only a placeholder; the same report with Word's setting to update fields when the file opens; and a file whose headings are bold text or outline levels instead of heading styles.
Runs
10 conversions to PDF and 1 to DOCX. The handbook without options and with each of the three option names; the generated report and the headings file without and with UpdateTableOfContents; the report with Word's setting, without options; the handbook to DOCX with UpdateFields, and that DOCX back to PDF without options.
Checked with
Page text, links and bookmarks read with PyMuPDF 1.27.2; first pages rendered with our PDF to PNG at 120 DPI and compared by eye.
Clients
Curl 8.7.1, jq 1.8.1, Python 3.14 with requests 2.34.2, Node.js 24.15.0.
Not checked
Microsoft Word itself. We compared page numbers with the pages the headings land on in our own PDFs, not with Word's layout.

Run your own files through the API.