numbering
numbering
¶
The list-numbering API — reading the number a list paragraph displays, and restarting.
The number a reader sees against a list paragraph — "1.", "a)", "iii." — is nowhere in
the document body. Word computes it from numbering.xml at display time, so anything
converting a document to text, Markdown or HTML has to compute it too. That computation
is compute_list_numbers, and it is the part of this module worth being careful
about; the rest is a straightforward model over the numbering part.
The model mirrors the two-level indirection described in docx.oxml.numbering: a
NumberingDefinition is a w:num, a concrete list, and it resolves each of its nine
NumberingLevel objects from the w:abstractNum it points at, with any w:lvlOverride
of its own applied on top.
Restarting a list means creating a second w:num on the same w:abstractNum carrying a
w:startOverride, not resetting a counter; see Paragraph.restart_numbering.
What is not computed here: a level whose format is one of the locale-specific ones — Japanese counting, Korean chosung and the rest — falls back to decimal, because rendering those correctly is a localisation problem rather than a document-model one. NumberingLevel.is_renderable says which is which.
NumberingLevel
¶
NumberingLevel(
ilvl: int,
lvl: CT_Lvl | None,
start_override: int | None = None,
)
One of the nine levels of a list, with any instance overrides applied.
Reached through NumberingDefinition.levels or NumberingDefinition.level.
Source code in src/docx/numbering.py
is_renderable
property
¶
True when format_number can render this level's format faithfully.
False for the locale-specific formats, where format_number falls back to
decimal. Worth checking before presenting a computed number as authoritative.
level_text
property
¶
The pattern this level displays, e.g. "%1.".
A %n is the counter of one-based level n. Defaults to "%{ilvl+1}.", which
is what Word shows for a level that does not say.
number_format
property
¶
number_format: WD_NUMBER_FORMAT
Member of WdNumberFormat this level renders its counter as.
DECIMAL when the level does not say, which is Word's default.
restart_after_level
property
¶
The one-based level whose increment restarts this one.
None means Word's default: restart whenever any higher level increments. 0
means never restart.
start
property
¶
The number this level counts from.
A w:startOverride on the concrete list wins over the abstract definition's
w:start, which is how a restarted list begins again at 1. Defaults to 1.
style_id
property
¶
The paragraph style linked to this level, or None.
A paragraph with this style takes this level even with no w:numPr of its own.
is_legal
property
¶
True when this level renders every placeholder as decimal.
Word's "legal numbering" option, which turns "1.a.i" into "1.1.1".
suffix
property
¶
What separates the number from the text: "tab", "space" or "nothing".
"tab" when the level does not say, which is Word's default.
indent
property
¶
indent: Length | None
The left indent this level applies, or None when it sets none.
hanging_indent
property
¶
hanging_indent: Length | None
The hanging indent this level applies, or None when it sets none.
This is what keeps the wrapped text of a list item lined up under the first line rather than under the bullet.
set
¶
set(
*,
start: int | None = None,
number_format: WD_NUMBER_FORMAT | str | None = None,
level_text: str | None = None,
suffix: str | None = None,
alignment: str | None = None,
indent: Length | None = None,
hanging_indent: Length | None = None,
restart_after_level: int | None = None,
style_id: str | None = None,
is_legal: bool | None = None,
) -> NumberingLevel
Change this level's definition; return self for chaining.
Only the arguments given are written, so a call sets what it names and leaves the rest of the level alone:
The change is made to the abstract definition, which is shared: every list pointing at it changes with it. Use Numbering.add_definition for a list of your own rather than editing a definition the document already had.
Raises ValueError for a level that has no definition to write to — one of the
nine levels an abstract definition does not define.
Source code in src/docx/numbering.py
format_number
¶
value rendered in this level's number format.
Falls back to decimal for a format this library does not render; see is_renderable.
Source code in src/docx/numbering.py
NumberingDefinition
¶
A concrete list — a w:num — and the levels it resolves to.
Two definitions pointing at the same abstract definition are two independent sequences that happen to look alike; that is how a restarted list is represented.
Source code in src/docx/numbering.py
abstract_num_id
property
¶
The id of the abstract definition this list takes its formatting from.
level
¶
level(ilvl: int) -> NumberingLevel
The level ilvl of this list, with any instance override applied.
A level the definition says nothing about is still returned, carrying Word's defaults, because a paragraph can legitimately refer to it.
Source code in src/docx/numbering.py
Numbering
¶
Numbering(numbering: CT_Numbering, part: DocumentPart)
Bases: ElementProxy
The numbering definitions of a document.
Reached through Document.numbering. Supports len(), iteration over the
concrete list definitions, and lookup by num_id.
Source code in src/docx/numbering.py
get
¶
get(num_id: int) -> NumberingDefinition | None
The list definition with num_id, or None when there is none.
Source code in src/docx/numbering.py
add_definition
¶
add_definition(
levels: Sequence[Mapping[str, object]] | None = None,
*,
multi_level_type: str | None = None,
) -> NumberingDefinition
Define a new list and return it.
Until now a list could be applied and restarted but not defined, so a format
the template did not already contain — 1) where the template has 1., a
custom bullet character, a particular per-level indent — meant hand-building
w:abstractNum XML:
definition = document.numbering.add_definition([
{"number_format": WD_NUMBER_FORMAT.DECIMAL, "level_text": "%1)"},
{"number_format": WD_NUMBER_FORMAT.LOWER_LETTER, "level_text": "%2)"},
])
paragraph.set_numbering(definition.num_id, level=0)
levels is one mapping per level, outermost first, of the keyword arguments
NumberingLevel.set takes. A level given as an empty mapping takes the
defaults. With levels of None the definition gets nine decimal levels, which
is what Word's plain numbered list is; add_bulleted_definition and
add_numbered_definition are the shorthands for the two common cases.
multi_level_type is written to w:multiLevelType when given; Word uses it to
decide how to present the list in its gallery and is content without it.
A fresh w:abstractNum and a w:num pointing at it are created, both with ids
free in this document. w:nsid and w:tmpl are deliberately not written — they
are what Word uses to recognise a definition as one of its own gallery entries,
and inventing values would claim a provenance this definition does not have.
Raises ValueError for more than nine levels, which is all OOXML admits.
Source code in src/docx/numbering.py
add_numbered_definition
¶
add_numbered_definition(
depth: int = 9,
*,
formats: Sequence[WD_NUMBER_FORMAT] | None = None,
indent_step: Length | None = None,
) -> NumberingDefinition
Define a decimal-with-sublevels list and return it.
The tedious half of add_definition is assembling nine levels by hand, so
this does it: each level shows its own counter followed by a period, in the
format formats gives it, indented indent_step further than the level above.
formats cycles when it is shorter than depth; the default of decimal, lower
letter and lower roman is Word's familiar 1. / a. / i. alternation. For the
cumulative "1.1.1" style, pass levels to add_definition with
level_text of "%1.%2." and so on. indent_step defaults to a quarter
inch, which is what Word uses.
Raises ValueError for a depth above nine, as add_definition does.
Source code in src/docx/numbering.py
add_bulleted_definition
¶
add_bulleted_definition(
depth: int = 9,
*,
bullets: Sequence[str] = ("•", "o", "§"),
indent_step: Length | None = None,
) -> NumberingDefinition
Define a bulleted list and return it.
bullets cycles when it is shorter than depth; the default is Word's own
bullet, circle and square sequence. indent_step defaults to a quarter inch.
Raises ValueError for a depth above nine.
Note the bullet characters Word writes are glyphs of the Symbol and Wingdings fonts rather than the Unicode characters they resemble. The defaults here are the Unicode ones, which render in whatever font the paragraph uses and so do not depend on a font being installed.
Source code in src/docx/numbering.py
restart
¶
restart(
num_id: int, ilvl: int = 0, start: int = 1
) -> NumberingDefinition
Return a new list definition restarting the list num_id at start.
The new definition points at the same abstract definition, so it looks identical,
and carries a w:startOverride for level ilvl. Assign its num_id to a
paragraph to make the list begin again there; Paragraph.restart_numbering
does that in one step.
Raises KeyError if num_id names no list.
Source code in src/docx/numbering.py
ParagraphNumbering
¶
ParagraphNumbering(
num_id: int,
level: int,
numbering: Numbering,
from_style: bool,
)
The list membership of a paragraph — which list it is in, and at what level.
Reached through Paragraph.numbering, which is None for a paragraph that
is not in a list at all.
Source code in src/docx/numbering.py
definition
property
¶
definition: NumberingDefinition | None
The list this paragraph belongs to, None if num_id names none.
from_style
property
¶
True when this numbering comes from the paragraph's style, not the paragraph.
A paragraph numbered through its style has no w:numPr of its own, so changing
its level means giving it one.
level_definition
property
¶
level_definition: NumberingLevel | None
The NumberingLevel governing this paragraph, None if unresolvable.
get_paragraph_numbering
¶
get_paragraph_numbering(
p: CT_P, part: DocumentPart
) -> Tuple[int, int, bool] | None
(num_id, ilvl, from_style) for p, or None when it is not in a list.
A direct w:pPr/w:numPr on the paragraph wins. Failing that the paragraph's style
hierarchy is walked — a style's own w:numPr, then the style it is based on — which
is how the built-in "List Number" and "List Bullet" styles number a paragraph that
carries no numbering markup at all.
w:numId of 0 means "explicitly not numbered" and is honoured as such: Word uses it
to switch numbering off for a paragraph whose style would otherwise apply it.
Source code in src/docx/numbering.py
compute_list_numbers
¶
compute_list_numbers(
paragraphs: Iterator[CT_P], part: DocumentPart
) -> List[Tuple[CT_P, str]]
(paragraph_element, number) for each numbered paragraph, in document order.
paragraphs must be every paragraph of the story in document order, since a list
number depends on everything before it. Paragraphs that are not in a list contribute
no entry.
The elements are returned rather than used as dictionary keys because an lxml element
proxy is created on demand and may be collected and its id() reused; holding the
element in the result is what keeps the association valid.
The counters follow ISO/IEC 29500 §17.9. For each numbered paragraph the counter of
its own level increments, and every deeper level is restarted — unless its
w:lvlRestart says otherwise, where 0 means never restart and n means restart only
when the one-based level n increments. Counters are kept per w:num rather than
per abstract definition, which is what makes two lists sharing a definition count
independently and what makes a w:startOverride restart work.
Source code in src/docx/numbering.py
iter_story_paragraphs
¶
iter_story_paragraphs(element) -> Iterator[CT_P]
Generate every w:p in element in document order, tables included.
List numbering counts paragraphs in the order Word lays them out, and a numbered paragraph inside a table cell counts towards the same list as one outside it.