Word Numbering Format Resolution (w:lvlText)#
What It Is#
In Word's Open XML format (OOXML), auto-generated list numbering is not stored as literal text in the paragraph. Instead, numbering.xml holds a w:lvlText element per level that defines a format template — e.g., 第%1条, (%1), or %1. — where %N tokens are placeholders resolved at render time by substituting the current counter value for level N. When MinerU converts a DOCX file, it never expands these templates, so the rendered prefix (e.g., "第三十六条") is entirely absent from the output.
How OOXML Numbering Works#
Every numbered paragraph carries a w:numPr reference:
w:numPr → w:numId + w:ilvl
→ numbering.xml/w:num[@w:numId] → w:abstractNum → w:lvl[@w:ilvl]
├── w:numFmt (decimal, lowerRoman, …)
├── w:start (start value)
└── w:lvlText (format template, e.g. "第%1条")
Word evaluates w:lvlText at display time by:
- Counting how many times this level has appeared under its parent (the counter).
- Converting that integer to the numeral style specified by
w:numFmt(decimal → Arabic, lowerRoman → roman, etc.; Chinese ordinal formats likechineseCountingalso exist). - Substituting
%1(level 1's counter),%2(level 2's counter), etc., into the template string.
MinerU's Current Behavior#
MinerU's DocxConverter reads numbering metadata but stops short of resolving w:lvlText:
| What it does | Code location |
|---|---|
Loads word/numbering.xml | _get_numbering_root() |
Resolves w:num → w:abstractNum | _get_abstract_numbering_element() |
Fetches the w:lvl element for a (numId, ilvl) pair | _get_numbering_level_definition() |
Reads w:numFmt to classify ordered vs. unordered | _is_numbered_list() |
Advances an integer counter per (numId, ilvl) | _advance_list_counter() |
Reads w:lvlText and expands %N tokens | ❌ Not implemented |
Because w:lvlText is never parsed or expanded, the format prefix is lost. The list item's output contains only the paragraph body text (e.g., "本制度由行政部负责解释。"), stripped of its auto-generated label. The list block is emitted as ordered or unordered based on numFmt, and a start counter is tracked , but no prefix string is prepended to the content .
Observable Impact#
- Numbered list items lose their prefix entirely. A paragraph like "第三十六条 本制度…" renders as
- 本制度…in Markdown. - Both simple and compound templates are affected —
第%1条,(%1),%1.%2., and any locale-specific patterns. - The bug is not limited to Chinese. Any
w:lvlTextcontaining a%Nplaceholder is unresolved.
This was confirmed as an architectural limitation in issue #5356 (August 2026). A related discussion (#5271) also surfaced the symptom that middle.json lacks numbering prefixes while the Markdown renderer may add its own counters separately.
What a Fix Would Require#
To properly resolve numbering labels, the converter would need to:
- Read
w:lvlTextfrom the resolvedw:lvlelement (already fetched by_get_numbering_level_definition()). - For each
%Nplaceholder, look up the counter for levelN-1and convert its integer value to the numeral style given by that level'sw:numFmt(handlingdecimal,lowerRoman,upperRoman,lowerLetter,upperLetter,chineseCounting,chineseCountingThousand, andordinalat minimum). - Prepend the resolved label string to the list item's
content_textbefore appending the block in_add_list_item().
Key Source Files#
| File | Purpose |
|---|---|
mineru/model/docx/docx_converter.py | All DOCX conversion logic; numbering infrastructure at lines 2239–2483 |