diff options
| -rw-r--r-- | NEWS | 2 | ||||
| -rw-r--r-- | pom.xml | 2 | ||||
| -rw-r--r-- | src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java | 41 | ||||
| -rw-r--r-- | src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java | 8 | ||||
| -rw-r--r-- | src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java | 13 | ||||
| -rw-r--r-- | src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java | 37 | ||||
| -rw-r--r-- | src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java | 22 |
7 files changed, 110 insertions, 15 deletions
@@ -6,6 +6,8 @@ * Add style mapping for all caps. +* Support numbering defined by paragraph style. + # 1.4.1 * Read the children of v:rect elements. @@ -108,7 +108,7 @@ </profiles> <properties> - <junit.jupiter.version>5.0.2</junit.jupiter.version> + <junit.jupiter.version>5.7.0</junit.jupiter.version> <junit.platform.version>1.0.2</junit.platform.version> </properties> diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java b/src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java index 90ea5c1..8d29393 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java @@ -4,21 +4,47 @@ import org.zwobble.mammoth.internal.documents.NumberingLevel; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static org.zwobble.mammoth.internal.util.Maps.lookup; import static org.zwobble.mammoth.internal.util.Maps.map; public class Numbering { + public static class AbstractNum { - private final Map<String, NumberingLevel> levels; + private final Map<String, AbstractNumLevel> levels; private final Optional<String> numStyleLink; - public AbstractNum(Map<String, NumberingLevel> levels, Optional<String> numStyleLink) { + public AbstractNum(Map<String, AbstractNumLevel> levels, Optional<String> numStyleLink) { this.levels = levels; this.numStyleLink = numStyleLink; } } + public static class AbstractNumLevel { + public static AbstractNumLevel ordered(String levelIndex) { + return new AbstractNumLevel(levelIndex, true, Optional.empty()); + } + + public static AbstractNumLevel unordered(String levelIndex) { + return new AbstractNumLevel(levelIndex, false, Optional.empty()); + } + + private final String levelIndex; + private final boolean isOrdered; + private final Optional<String> paragraphStyleId; + + public AbstractNumLevel(String levelIndex, boolean isOrdered, Optional<String> paragraphStyleId) { + this.levelIndex = levelIndex; + this.isOrdered = isOrdered; + this.paragraphStyleId = paragraphStyleId; + } + + public NumberingLevel toNumberingLevel() { + return new NumberingLevel(levelIndex, isOrdered); + } + } + public static class Num { private final Optional<String> abstractNumId; @@ -30,6 +56,7 @@ public class Numbering { public static final Numbering EMPTY = new Numbering(map(), map(), Styles.EMPTY); private final Map<String, AbstractNum> abstractNums; + private final Map<String, AbstractNumLevel> levelsByParagraphStyleId; private final Map<String, Num> nums; private final Styles styles; @@ -39,6 +66,10 @@ public class Numbering { Styles styles ) { this.abstractNums = abstractNums; + this.levelsByParagraphStyleId = abstractNums.values().stream() + .flatMap(abstractNum -> abstractNum.levels.values().stream()) + .filter(level -> level.paragraphStyleId.isPresent()) + .collect(Collectors.toMap(level -> level.paragraphStyleId.get(), level -> level)); this.nums = nums; this.styles = styles; } @@ -54,8 +85,12 @@ public class Numbering { .flatMap(style -> style.getNumId()) .flatMap(linkedNumId -> findLevel(linkedNumId, level)); } else { - return lookup(abstractNum.levels, level); + return lookup(abstractNum.levels, level).map(value -> value.toNumberingLevel()); } }); } + + public Optional<NumberingLevel> findLevelByParagraphStyleId(String styleId) { + return lookup(levelsByParagraphStyleId, styleId).map(value -> value.toNumberingLevel()); + } } 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 b25ffdd..5f811db 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java @@ -1,6 +1,5 @@ package org.zwobble.mammoth.internal.docx; -import org.zwobble.mammoth.internal.documents.NumberingLevel; import org.zwobble.mammoth.internal.xml.XmlElement; import org.zwobble.mammoth.internal.xml.XmlElementList; @@ -31,15 +30,16 @@ public class NumberingXml { return entry(abstractNumId, abstractNum); } - private static Map<String, NumberingLevel> readAbstractNumLevels(XmlElement element) { + private static Map<String, Numbering.AbstractNumLevel> readAbstractNumLevels(XmlElement element) { return toMap(element.findChildren("w:lvl"), NumberingXml::readAbstractNumLevel); } - private static Map.Entry<String, NumberingLevel> readAbstractNumLevel(XmlElement element) { + private static Map.Entry<String, Numbering.AbstractNumLevel> readAbstractNumLevel(XmlElement element) { String levelIndex = element.getAttribute("w:ilvl"); Optional<String> numFmt = element.findChildOrEmpty("w:numFmt").getAttributeOrNone("w:val"); boolean isOrdered = !numFmt.equals(Optional.of("bullet")); - return entry(levelIndex, new NumberingLevel(levelIndex, isOrdered)); + Optional<String> paragraphStyleId = element.findChildOrEmpty("w:pStyle").getAttributeOrNone("w:val"); + return entry(levelIndex, new Numbering.AbstractNumLevel(levelIndex, isOrdered, paragraphStyleId)); } private static Map<String, Numbering.Num> readNums(XmlElementList numElements) { diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java index 9c270de..0cf141e 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java @@ -256,12 +256,11 @@ class StatefulBodyXmlReader { private ReadResult readParagraph(XmlElement element) { XmlElementLike properties = element.findChildOrEmpty("w:pPr"); - Optional<NumberingLevel> numbering = readNumbering(properties); ParagraphIndent indent = readParagraphIndent(properties); return ReadResult.map( readParagraphStyle(properties), readElements(element.getChildren()), - (style, children) -> new Paragraph(style, numbering, indent, children)).appendExtra(); + (style, children) -> new Paragraph(style, readNumbering(style, properties), indent, children)).appendExtra(); } private ReadResult readFieldChar(XmlElement element) { @@ -328,7 +327,15 @@ class StatefulBodyXmlReader { } - private Optional<NumberingLevel> readNumbering(XmlElementLike properties) { + private Optional<NumberingLevel> readNumbering(Optional<Style> style, XmlElementLike properties) { + if (style.isPresent()) { + String styleId = style.get().getStyleId(); + Optional<NumberingLevel> level = numbering.findLevelByParagraphStyleId(styleId); + if (level.isPresent()) { + return level; + } + } + XmlElementLike numberingProperties = properties.findChildOrEmpty("w:numPr"); return Optionals.flatMap( readVal(numberingProperties, "w:numId"), diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java index 076cba4..fffc901 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java @@ -198,7 +198,7 @@ public class BodyXmlTests { element("w:ilvl", map("w:val", "1")), element("w:numId", map("w:val", "42")))))))); - Numbering numbering = numberingMap(map("42", map("1", NumberingLevel.ordered("1")))); + Numbering numbering = numberingMap(map("42", map("1", Numbering.AbstractNumLevel.ordered("1")))); assertThat( readSuccess(bodyReader(numbering), element), @@ -206,6 +206,35 @@ public class BodyXmlTests { } @Test + public void numberingOnParagraphStyleTakesPrecedenceOverNumPr() { + XmlElement element = paragraphXml(list( + element("w:pPr", list( + element("w:pStyle", map("w:val", "List")), + element("w:numPr", map(), list( + element("w:ilvl", map("w:val", "1")), + element("w:numId", map("w:val", "42")) + )) + )) + )); + + Numbering numbering = numberingMap(map( + "42", map("1", new Numbering.AbstractNumLevel("1", false, Optional.empty())), + "43", map("1", new Numbering.AbstractNumLevel("1", true, Optional.of("List"))) + )); + Styles styles = new Styles( + map("List", new Style("List", Optional.empty())), + map(), + map(), + map() + ); + + assertThat( + readSuccess(bodyReader(numbering, styles), element), + hasNumbering(NumberingLevel.ordered("1")) + ); + } + + @Test public void numberingPropertiesAreIgnoredIfLevelIsMissing() { // TODO: emit warning XmlElement element = paragraphXml(list( @@ -213,7 +242,7 @@ public class BodyXmlTests { element("w:numPr", map(), list( element("w:numId", map("w:val", "42")))))))); - Numbering numbering = numberingMap(map("42", map("1", NumberingLevel.ordered("1")))); + Numbering numbering = numberingMap(map("42", map("1", Numbering.AbstractNumLevel.ordered("1")))); assertThat( readSuccess(bodyReader(numbering), element), @@ -228,7 +257,7 @@ public class BodyXmlTests { element("w:numPr", map(), list( element("w:ilvl", map("w:val", "1")))))))); - Numbering numbering = numberingMap(map("42", map("1", NumberingLevel.ordered("1")))); + Numbering numbering = numberingMap(map("42", map("1", Numbering.AbstractNumLevel.ordered("1")))); assertThat( readSuccess(bodyReader(numbering), element), @@ -1492,7 +1521,7 @@ public class BodyXmlTests { return new Relationship(relationshipId, target, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"); } - private static Numbering numberingMap(Map<String, Map<String, NumberingLevel>> numbering) { + private static Numbering numberingMap(Map<String, Map<String, Numbering.AbstractNumLevel>> numbering) { return new Numbering( numbering.entrySet().stream().collect(Collectors.toMap( entry -> entry.getKey(), diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java index a94f8e8..8f4e5ba 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java @@ -78,6 +78,28 @@ public class NumberingXmlTests { assertEquals(true, numbering.findLevel("201", "0").get().isOrdered()); } + // See: 17.9.23 pStyle (Paragraph Style's Associated Numbering Level) in ECMA-376, 4th Edition + @Test + public void numberingLevelCanBeFoundByParagraphStyleId() { + Numbering numbering = readNumbering( + element("w:numbering", list( + element("w:abstractNum", map("w:abstractNumId", "42"), list( + element("w:lvl", map("w:ilvl", "0"), list( + element("w:numFmt", map("w:val", "bullet")) + )) + )), + element("w:abstractNum", map("w:abstractNumId", "43"), list( + element("w:lvl", map("w:ilvl", "0"), list( + element("w:pStyle", map("w:val", "List")), + element("w:numFmt", map("w:val", "decimal")) + )) + )) + )) + ); + assertEquals(true, numbering.findLevelByParagraphStyleId("List").get().isOrdered()); + assertEquals(false, numbering.findLevelByParagraphStyleId("Paragraph").isPresent()); + } + private Numbering readNumbering(XmlElement element) { return readNumberingXmlElement(element, Styles.EMPTY); |
