Skip to content

Fix list numbering counters and per level indentation - #16

Merged
mantissaman merged 2 commits into
mainfrom
fix/list-numbering-and-indent
Jul 29, 2026
Merged

Fix list numbering counters and per level indentation#16
mantissaman merged 2 commits into
mainfrom
fix/list-numbering-and-indent

Conversation

@mantissaman

Copy link
Copy Markdown
Contributor

Closes #12 and closes #13. Both were found rendering the file attached to #3, and both are verified by rendering it again.

Numbering counters, #12

Counters were keyed on num_id. That is the numbering instance, not the list.

Pandoc and LibreOffice emit a separate w:num per list block and point them all at a single w:abstractNum. The file in #3 is exactly that:

numId 4 -> abstract 1     <- "List item 1"
numId 1 -> abstract 1     <- "List item 2"

Two instances, one list. Keying on the instance gave each its own counter, so both items rendered as 1..

Counters are now keyed on the abstract definition. Lists that really do have separate abstract definitions still restart independently, and there is a test for each direction so the fix cannot drift into the opposite bug.

Level indentation, #13

A w:lvl carries its own w:pPr, and that is where the per level indent lives. We parsed it into CT_Lvl::ppr and then never looked at it, so every level of a list drew at the same left position. In the #3 file the levels ask for 720 and 1440 twips and both rendered at the same place.

Level properties are now merged between the paragraph style and direct formatting, which is where they belong in the property chain. A level indent applies unless the paragraph sets its own.

Result

Rendering office2pdf_test.docx from #3.

Before:

1.    List item 1
1.    List item 2
o     List item
o     List item

After:

    1.    List item 1
    2.    List item 2
            o    List item
            o    List item

That matches the LibreOffice reference in #3.

Checks

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings and the full suite all pass. 336 tests, up from 333, with three added here.

Scope

Deliberately does not touch #10 or #11, the other two halves of #3. #10 is in the same function PR #2 is rewriting and should wait for that to land.

Two unrelated list bugs, both visible in the file attached to #3.

Numbering counters were keyed on num_id. Pandoc and LibreOffice emit a
separate w:num for each list block and point them all at one
w:abstractNum, so a plain two item list arrives as numId 4 and numId 1
both mapping to abstract 1. Keying on the instance gave each block its
own counter and every item rendered as "1.". Counters are now keyed on
the abstract definition, which is what the count actually belongs to.
Lists with genuinely separate abstract definitions still restart, and
there is a test for each direction.

Indentation for a numbering level was parsed and then never used. A
w:lvl carries its own w:pPr, which is where the per level indent lives,
and nothing merged it into the paragraph properties. Every level of a
list drew at the same left position. The level properties are now
merged between the paragraph style and direct formatting, which is
where they sit in the property chain, so a level indent applies unless
the paragraph overrides it.

Rendering the file from #3 now gives "1." and "2." for the two numbered
items, with the nested bullets stepped in one level, which matches the
LibreOffice reference in that issue.

Closes #12. Closes #13.
Applying a numbering level's own indent moves list markers, so the
page-one PNG for letter and for contract both change.

Verified by rendering both samples before and after. The only visual
difference is list markers moving to their level indent, with wrapped
text now aligning under the text start instead of returning to the
margin. Nothing else on either page moved.

The docx part hashes are untouched, including numbering.xml, which
confirms this is a rendering change and not a change to the OOXML we
write.
@mantissaman
mantissaman force-pushed the fix/list-numbering-and-indent branch from 7b8c0d4 to c895a9e Compare July 29, 2026 21:38
@mantissaman

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and added the baseline update, because the Output stability job had not run on this PR when it was opened. It landed on main with the sprint merge afterwards.

It does fail on the original commit, as expected. Applying a numbering level's own indent moves list markers, so letter:page1.png and contract:page1.png both change.

I checked the delta rather than just regenerating. Rendering both samples before and after, the only visual difference is list markers moving to their level indent, with wrapped text now aligning under the text start instead of returning to the margin. Nothing else on either page moved.

One detail worth recording: the docx part hashes are untouched, numbering.xml included. So this is purely a rendering change and does not alter the OOXML we write, which is what you would want from a fix in the layout engine.

Baseline updated with an audit reason. All seven checks now pass.

@mantissaman
mantissaman merged commit 2167103 into main Jul 29, 2026
7 checks passed
@mantissaman
mantissaman deleted the fix/list-numbering-and-indent branch July 29, 2026 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested list levels are not indented Numbered list items all render as 1.

1 participant