Skip to content

Make numbering definitions writable - define a list from scratch #98

Closed

Description

@toxicphreAK

#73 gave us a good read side for numbering — Paragraph.numbering, Paragraph.list_number, Document.list_numbers, NumberingLevel.number_format, .level_text, .start, format_number() — plus set_numbering(), remove_numbering() and restart_numbering() on the write side.

What is missing is the ability to define a list. NumberingLevel is read-only: start, num_fmt, lvl_text, lvl_restart, is_lgl and p_style on CT_Lvl are all getters with no setters, and Numbering has no add_definition(). CT_Numbering.add_num() exists but only takes an abstractNum_id that must already be there.

So you can apply a list definition the document already has, and you can restart one, but you cannot create a numbered list with a format the template does not already contain — no 1) when the template only has 1., no custom bullet character, no per-level indent. In practice people fall back to hand-building w:abstractNum XML and appending it, which is exactly the kind of thing the library should be doing for them.

Where the prior art is

jlovegren0/python-docx, branch master (and footnotes), is the most complete numbering-definition layer of any of the forks. It adds constructors and writable attributes for CT_AbstractNum, CT_Lvl, CT_NumFmt, CT_LevelText, CT_LevelSuffix, CT_MultiLevelType, CT_LvlLegacy and CT_NumPicBullet, along with the ST_NumberFormat, ST_LevelSuffix and ST_MultiLevelType simple types in docx/oxml/simpletypes.py and an abstractNum_having_abstractNumId() lookup.

Our CT_AbstractNum and CT_Lvl already exist with the read accessors and lvl_having_ilvl(); WD_NUMBER_FORMAT already covers ST_NumberFormat. This is filling in the write side of classes we have, not new ones.

Yiqing-Zhou/python-docx and revvsales/python-docx-1 (Document.get_new_list(abstractNumId)) both do smaller versions of the same thing.

Shape

  • Setters on CT_Lvl for start, numFmt, lvlText, lvlRestart, suff, lvlJc and the level w:pPr indent
  • w:suff (ST_LevelSuffix: tab, space, nothing) is not modelled at all — it is what controls the gap between the number and the text, and is a common ask
  • Numbering.add_definition(levels) -> NumberingDefinition, allocating a free abstractNumId and numId and wiring the w:num -> w:abstractNum link
  • A NumberingLevel write side matching the existing read properties
  • Probably a helper for the two common cases, a bulleted list and a decimal-with-sublevels list, since assembling nine levels by hand is the tedious part

Watch out for

  • CT_Numbering is an xsd:sequence — all w:abstractNum elements precede all w:num. _tag_seq already reflects this; new definitions must be placed accordingly and the placement asserted in a test.
  • Word rejects a w:abstractNum whose w:lvl elements are not in ascending w:ilvl order.
  • w:nsid and w:tmpl are what Word uses to recognise a list definition as one of its own. Check ref/xsd/wml.xsd and decide whether to emit them; documents open fine without them but the list may not round-trip through Word's list gallery.

Activity

  1. toxicphreAK commented on Aug 5, 2026

    @toxicphreAK
    OwnerAuthor

    Landed in #139. Setters on CT_Lvl for start, numFmt, lvlText, lvlRestart, suff, lvlJc, pStyle and isLgl, each placing its child in schema order; w:suff was not modelled at all and is now. Numbering.add_definition(), plus add_numbered_definition() and add_bulleted_definition() for the tedious nine-level part, and NumberingLevel.set().

    All three warnings are handled: the abstract definition goes before every w:num, levels are kept in ascending w:ilvl, and on w:nsid/w:tmpl — they are deliberately not written, since they are what Word uses to claim a definition as one of its own gallery entries and inventing values would assert a provenance the definition does not have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions