Fields and tables of contents¶
A field is how Word represents anything it works out for itself: page numbers, a table of contents, cross-references, captions that renumber, dates, and references to document properties. Every one of those is the same feature with a different instruction string.
Warning
This library cannot compute a field result, and no API can change that. A table
of contents added here is empty, a PAGE field has no number, and a
cross-reference shows nothing, because all three depend on how Word lays the
document out. Fields are written asking Word to refresh them when it opens the
file — see Getting Word to fill them in.
Adding a field¶
Paragraph.add_field() takes a field code.
The builders in docx.fields write the ones people usually want, so you
rarely need to remember the switch syntax:
from docx import Document, fields
document = Document()
paragraph = document.add_paragraph("Page ")
paragraph.add_field(fields.page_number())
paragraph.add_run(" of ")
paragraph.add_field(fields.page_count())
The builders return the instruction string, not the field, so they compose with anything that takes a field code:
| Builder | Field |
|---|---|
page_number() |
PAGE — the current page |
page_count() |
NUMPAGES — pages in the document |
table_of_contents() |
TOC |
cross_reference() |
REF — text of a bookmark |
page_reference() |
PAGEREF — page a bookmark is on |
sequence() |
SEQ — caption and figure numbering |
date() |
DATE |
doc_property() |
DOCPROPERTY |
styleref() |
STYLEREF — nearest text in a style |
An instruction Word understands but no builder covers can be passed directly:
A table of contents¶
from docx import Document, fields
document = Document()
document.add_paragraph("Contents", style="Heading 1")
toc = document.add_paragraph()
toc.add_field(fields.table_of_contents(levels=(1, 3)))
document.add_page_break()
document.add_heading("Introduction", level=1)
document.add_paragraph("...")
document.settings.update_fields_on_open = True
document.save("with-toc.docx")
levels is the heading range to include. The document opens showing "Update this
document?" and the contents appear once Word rebuilds them.
Cross-references¶
A cross-reference points at a bookmark, so bookmark the target first:
from docx import Document, fields
document = Document()
intro = document.add_heading("Introduction", level=1)
intro.add_bookmark("intro")
body = document.add_paragraph("See ")
body.add_field(fields.cross_reference("intro"))
body.add_run(" on page ")
body.add_field(fields.page_reference("intro"))
cross_reference() writes the bookmark's text and
page_reference() the page number it falls on. See
Bookmarks for the ways to create one.
Captions¶
Document.add_caption() writes the whole thing —
the label, the self-renumbering SEQ field, and the bookmark a cross-reference needs:
document.add_picture("architecture.png")
caption = document.add_caption("Figure", "the architecture")
That gives a paragraph in the "Caption" style that Word renders as "Figure 1 the
architecture", where the number is a field it recomputes whenever figures are inserted or
removed. Read back before Word has computed it, the number is simply missing:
caption.text is "Figure the architecture".
caption.paragraph # -> the Paragraph, for any further formatting
caption.label # -> "Figure"
caption.text # -> the text as the document currently reads it
caption.number # -> the number Word last computed, or None
caption.bookmark_name # -> "_Ref000000001"
number is None for a caption this library has just written: a SEQ field's result is
cached in the document, so it reads back only after a round trip through Word.
Referring to it later is one call:
The bookmark name is the part that matters. Word's cross-reference dialogue offers
only targets whose bookmark name follows its own _Ref convention, so a caption
bookmarked with an arbitrary name works but is invisible in the UI — the user cannot
reference it. add_caption() generates names in that shape. (Word derives its own from a
timestamp; a counter is used here instead, because a timestamp would make the same
document generate different bytes on each run.) That shape also means the bookmark does
not appear in Document.bookmarks, which leaves out
the ones Word maintains for itself; Bookmarks.iter_all() reaches it.
Four keyword arguments adjust the result:
document.add_caption("Table", "Revenue by region", separator=": ")
document.add_caption("Figure", "A cat", restart_at_heading_level=1) # -> "Figure 3-2"
document.add_caption("Figure", "A cat", before=document.paragraphs[0])
document.add_caption("Figure", "A cat", style=None) # -> no style applied
separator is what goes between the number and the text. restart_at_heading_level
starts the numbering over at each chapter, which is the "Figure 3-2" convention.
_Cell.add_caption() does the same inside a table cell.
Each label is its own sequence, so figures, tables and equations number independently.
Assembling one by hand¶
SEQ is the counter underneath, and it is still there if you want to build a caption
yourself:
caption = document.add_paragraph("Figure ", style="Caption")
caption.add_field(fields.sequence("Figure"))
caption.add_run(": the architecture")
This is what add_caption() does, minus the bookmark.
Reading the fields in a document¶
Document.fields returns every field in the body, and
Paragraph.fields the fields of one paragraph:
Field.instruction— the field code, switches and allField.type— the first word of it, e.g."PAGE"Field.result_text— the cached result Word last computed, which is what a reader sawField.is_simple— whether Word stored it as a self-containedw:fldSimpleor spread it across runs
A field's cached result counts towards
Paragraph.text, so the page number a reader saw
is part of the text you read back.
Complex fields nest — a TOC result is full of PAGEREF fields — and each inner field
is a field in its own right.
Getting Word to fill them in¶
Two things ask Word to compute results, and they are not the same:
Settings.update_fields_on_open sets a
document-wide flag; Word prompts on open and refreshes every field. This is what a
generated table of contents needs.
Individual fields are written with w:dirty set, which asks Word to refresh that field
alone. Pass dirty=False to
add_field() to suppress it.
To keep the field from showing as blank before the first refresh, supply cached result
text of your own. That requires simple=True, because only a w:fldSimple stores its
result as plain content — a complex field's result is whatever sits between its
separate and end field characters, which Word writes when it computes it: