Skip to content

numbering

numbering

Custom element classes related to the numbering part.

The numbering model has two levels of indirection, and getting them the wrong way round is what most reimplementations of it do:

  • A paragraph's w:numPr/w:numId names a w:num, a concrete list instance.
  • The w:num names a w:abstractNum through w:abstractNumId. The abstract definition holds the formatting of each of the nine levels: the start value, the number format and the level text.
  • The w:num may carry w:lvlOverride children that override parts of the abstract definition for this instance, w:startOverride in particular.

Two w:num elements pointing at the same w:abstractNum are two independent sequences that happen to look alike. That is precisely how Word restarts a list: it does not reset a counter, it creates a second w:num with a w:startOverride.

Leaf values here are read from their w:val attribute rather than through registered element classes, because the tag names are reused elsewhere in the schema with other types — w:start is a table-cell border, and lxml resolves an element class by tag name alone, so registering it would silently change the type of every table border in the document.

CT_Lvl

Bases: BaseOxmlElement

w:lvl element, the definition of one of the nine levels of a list.

start property writable

start: int | None

The number this level counts from, or None when it does not say.

Word treats an unspecified start as 1.

num_fmt property writable

num_fmt: WD_NUMBER_FORMAT | None

Member of WdNumberFormat this level renders its counter as.

None when the level does not say, or when it names a format outside the enumeration — Word accepts vendor extensions here and a document using one should still be readable.

lvl_restart property writable

lvl_restart: int | None

The one-based level whose increment restarts this one, or None.

None means the default: this level restarts whenever any higher level increments. A value of 0 means it never restarts.

lvl_text property writable

lvl_text: str | None

The pattern this level displays, e.g. "%1." or "%1.%2", or None.

A %n placeholder is replaced by the counter of the one-based level n. For a bullet level the text is the bullet character itself and holds no placeholder.

is_lgl property writable

is_lgl: bool

True when this level renders all its placeholders as decimal.

The "legal numbering" option, which turns "1.a.i" into "1.1.1" without changing the underlying formats.

p_style property writable

p_style: str | None

The style id this level is linked to, or None.

A paragraph with this style takes this numbering level even without its own w:numPr, which is how the built-in "List Number" styles work.

suffix property writable

suffix: str | None

What separates the number from the text: "tab", "space" or "nothing".

None when the level does not say, which Word treats as "tab". This is the gap between the bullet or number and the paragraph text, and setting it to "space" is the usual way to tighten up a compact list.

jc property writable

jc: str | None

Alignment of the number within its indent: "left", "center", "right".

None when the level does not say, which Word treats as left.

pPr property

pPr: BaseOxmlElement | None

The w:pPr of this level, or None when it has none.

This is where a level's indent lives, as an ordinary w:ind.

get_or_add_pPr

get_or_add_pPr() -> BaseOxmlElement

The w:pPr of this level, added in schema order if not already there.

Source code in src/docx/oxml/numbering.py
def get_or_add_pPr(self) -> BaseOxmlElement:
    """The `w:pPr` of this level, added in schema order if not already there."""
    pPr = self.pPr
    if pPr is None:
        pPr = OxmlElement("w:pPr")
        _insert_in_order(self, pPr, "w:pPr", self._tag_seq)
    return pPr

new classmethod

new(ilvl: int) -> CT_Lvl

A new empty w:lvl for level ilvl.

Source code in src/docx/oxml/numbering.py
@classmethod
def new(cls, ilvl: int) -> CT_Lvl:
    """A new empty `w:lvl` for level `ilvl`."""
    lvl = OxmlElement("w:lvl")
    lvl.set(qn("w:ilvl"), str(ilvl))
    return lvl  # pyright: ignore[reportReturnType]

CT_AbstractNum

Bases: BaseOxmlElement

w:abstractNum element, the shared definition behind one or more w:num.

multi_level_type property writable

multi_level_type: str | None

"singleLevel", "multilevel" or "hybridMultilevel", or None.

num_style_link: str | None

The style id this definition defers to, or None.

An abstract definition carrying this holds no levels of its own; the numbering actually comes from the definition the named style points at. Word writes this for a list style shared between several lists.

style_link: str | None

The style id this definition is the numbering for, or None.

new classmethod

new(abstract_num_id: int) -> CT_AbstractNum

A new empty w:abstractNum with abstract_num_id.

w:nsid and w:tmpl are deliberately not written. They are what Word uses to recognise a definition as one of its own list-gallery entries; inventing values for them would claim a provenance this definition does not have, and Word opens a document without them perfectly well.

Source code in src/docx/oxml/numbering.py
@classmethod
def new(cls, abstract_num_id: int) -> CT_AbstractNum:
    """A new empty `w:abstractNum` with `abstract_num_id`.

    `w:nsid` and `w:tmpl` are deliberately not written. They are what Word uses to
    recognise a definition as one of its own list-gallery entries; inventing values
    for them would claim a provenance this definition does not have, and Word opens
    a document without them perfectly well.
    """
    abstractNum = OxmlElement("w:abstractNum")
    abstractNum.set(qn("w:abstractNumId"), str(abstract_num_id))
    return abstractNum  # pyright: ignore[reportReturnType]

add_level

add_level(ilvl: int) -> CT_Lvl

A w:lvl for level ilvl, newly added in ascending w:ilvl order.

Word rejects an abstract definition whose levels are out of order.

Source code in src/docx/oxml/numbering.py
def add_level(self, ilvl: int) -> CT_Lvl:
    """A `w:lvl` for level `ilvl`, newly added in ascending `w:ilvl` order.

    Word rejects an abstract definition whose levels are out of order.
    """
    existing = self.lvl_having_ilvl(ilvl)
    if existing is not None:
        return existing
    lvl = CT_Lvl.new(ilvl)
    for sibling in self.lvl_lst:
        if sibling.ilvl > ilvl:
            sibling.addprevious(lvl)
            return lvl
    _insert_in_order(self, lvl, "w:lvl", self._tag_seq)
    return lvl

lvl_having_ilvl

lvl_having_ilvl(ilvl: int) -> CT_Lvl | None

The w:lvl child for level ilvl, or None when it has none.

Source code in src/docx/oxml/numbering.py
def lvl_having_ilvl(self, ilvl: int) -> CT_Lvl | None:
    """The `w:lvl` child for level `ilvl`, or |None| when it has none."""
    return next(iter(self.xpath('./w:lvl[@w:ilvl="%d"]' % ilvl)), None)

CT_Num

Bases: BaseOxmlElement

<w:num> element, which represents a concrete list definition instance, having a required child that references an abstract numbering definition that defines most of the formatting details.

add_lvlOverride

add_lvlOverride(ilvl)

Return a newly added CT_NumLvl () element having its ilvl attribute set to ilvl.

Source code in src/docx/oxml/numbering.py
def add_lvlOverride(self, ilvl):
    """Return a newly added CT_NumLvl (<w:lvlOverride>) element having its ``ilvl``
    attribute set to `ilvl`."""
    return self._add_lvlOverride(ilvl=ilvl)

lvlOverride_having_ilvl

lvlOverride_having_ilvl(ilvl: int) -> CT_NumLvl | None

The w:lvlOverride child for level ilvl, or None when there is none.

Source code in src/docx/oxml/numbering.py
def lvlOverride_having_ilvl(self, ilvl: int) -> CT_NumLvl | None:
    """The `w:lvlOverride` child for level `ilvl`, or |None| when there is none."""
    return next(iter(self.xpath('./w:lvlOverride[@w:ilvl="%d"]' % ilvl)), None)

new classmethod

new(num_id, abstractNum_id)

Return a new <w:num> element having numId of num_id and having a <w:abstractNumId> child with val attribute set to abstractNum_id.

Source code in src/docx/oxml/numbering.py
@classmethod
def new(cls, num_id, abstractNum_id):
    """Return a new ``<w:num>`` element having numId of `num_id` and having a
    ``<w:abstractNumId>`` child with val attribute set to `abstractNum_id`."""
    num = OxmlElement("w:num")
    num.numId = num_id
    abstractNumId = CT_DecimalNumber.new("w:abstractNumId", abstractNum_id)
    num.append(abstractNumId)
    return num

CT_NumLvl

Bases: BaseOxmlElement

<w:lvlOverride> element, which identifies a level in a list definition to override with settings it contains.

lvl property

lvl: CT_Lvl | None

The w:lvl override of this level, or None when it overrides only start.

start_override property

start_override: int | None

The number this level counts from in this instance, or None.

add_startOverride

add_startOverride(val)

Return a newly added CT_DecimalNumber element having tagname w:startOverride and val attribute set to val.

Source code in src/docx/oxml/numbering.py
def add_startOverride(self, val):
    """Return a newly added CT_DecimalNumber element having tagname
    ``w:startOverride`` and ``val`` attribute set to `val`."""
    return self._add_startOverride(val=val)

CT_NumPr

Bases: BaseOxmlElement

A <w:numPr> element, a container for numbering properties applied to a paragraph.

ilvl_val property writable

ilvl_val: int | None

Value of w:ilvl/@w:val, or None when absent.

numId_val property writable

numId_val: int | None

Value of w:numId/@w:val, or None when absent.

CT_Numbering

Bases: BaseOxmlElement

<w:numbering> element, the root element of a numbering part, i.e. numbering.xml.

add_num

add_num(abstractNum_id)

Return a newly added CT_Num () element referencing the abstract numbering definition identified by abstractNum_id.

Source code in src/docx/oxml/numbering.py
def add_num(self, abstractNum_id):
    """Return a newly added CT_Num (<w:num>) element referencing the abstract
    numbering definition identified by `abstractNum_id`."""
    next_num_id = self._next_numId
    num = CT_Num.new(next_num_id, abstractNum_id)
    return self._insert_num(num)

abstractNum_having_abstractNumId

abstractNum_having_abstractNumId(
    abstractNumId: int,
) -> CT_AbstractNum | None

The w:abstractNum child with abstractNumId, or None if not found.

Source code in src/docx/oxml/numbering.py
def abstractNum_having_abstractNumId(self, abstractNumId: int) -> CT_AbstractNum | None:
    """The `w:abstractNum` child with `abstractNumId`, or |None| if not found."""
    xpath = './w:abstractNum[@w:abstractNumId="%d"]' % abstractNumId
    return next(iter(self.xpath(xpath)), None)

num_having_numId

num_having_numId(numId)

Return the <w:num> child element having numId attribute matching numId.

Source code in src/docx/oxml/numbering.py
def num_having_numId(self, numId):
    """Return the ``<w:num>`` child element having ``numId`` attribute matching
    `numId`."""
    xpath = './w:num[@w:numId="%d"]' % numId
    try:
        return self.xpath(xpath)[0]
    except IndexError:
        raise KeyError("no <w:num> element with numId %d" % numId)