From 98c98549a237be0db4689bf38436a24fc30ac111 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 11 Sep 2025 15:37:06 +0100 Subject: Handle numbering levels defined without an index --- .../mammoth/internal/docx/NumberingXml.java | 36 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java') diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java b/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java index 5f811db..012d548 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java @@ -3,6 +3,7 @@ package org.zwobble.mammoth.internal.docx; import org.zwobble.mammoth.internal.xml.XmlElement; import org.zwobble.mammoth.internal.xml.XmlElementList; +import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -31,15 +32,42 @@ public class NumberingXml { } private static Map readAbstractNumLevels(XmlElement element) { - return toMap(element.findChildren("w:lvl"), NumberingXml::readAbstractNumLevel); + Map levels = new HashMap<>(); + + // Some malformed documents define numbering levels without an index, and + // reference the numbering using a w:numPr element without a w:ilvl child. + // To handle such cases, we assume a level of 0 as a fallback. + Optional levelWithoutIndex = Optional.empty(); + + for (XmlElement levelElement : element.findChildren("w:lvl")) { + Map.Entry, Numbering.AbstractNumLevel> entry = + readAbstractNumLevel(levelElement); + + if (entry.getKey().isPresent()) { + levels.put(entry.getKey().get(), entry.getValue()); + } else { + levelWithoutIndex = Optional.of(entry.getValue()); + } + } + + if (levelWithoutIndex.isPresent() && !levels.containsKey(levelWithoutIndex.get().levelIndex())) { + levels.put(levelWithoutIndex.get().levelIndex(), levelWithoutIndex.get()); + } + + return levels; } - private static Map.Entry readAbstractNumLevel(XmlElement element) { - String levelIndex = element.getAttribute("w:ilvl"); + private static Map.Entry, Numbering.AbstractNumLevel> readAbstractNumLevel(XmlElement element) { + Optional levelIndex = element.getAttributeOrNone("w:ilvl"); Optional numFmt = element.findChildOrEmpty("w:numFmt").getAttributeOrNone("w:val"); boolean isOrdered = !numFmt.equals(Optional.of("bullet")); Optional paragraphStyleId = element.findChildOrEmpty("w:pStyle").getAttributeOrNone("w:val"); - return entry(levelIndex, new Numbering.AbstractNumLevel(levelIndex, isOrdered, paragraphStyleId)); + Numbering.AbstractNumLevel abstractNumLevel = new Numbering.AbstractNumLevel( + levelIndex.orElse("0"), + isOrdered, + paragraphStyleId + ); + return entry(levelIndex, abstractNumLevel); } private static Map readNums(XmlElementList numElements) { -- cgit v1.2.3