aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2021-01-09 17:25:55 +0000
committerMichael Williamson <mike@zwobble.org>2021-01-09 17:25:55 +0000
commit437a3029d872ec5f2113aa6e074567de7fd2d61f (patch)
tree1ab117b3ca019f6232d842113e45f5097107d419 /src
parente65a011e679931377af77793fc36b671766c4118 (diff)
Support numbering defined by paragraph style
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/docx/Numbering.java41
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/docx/NumberingXml.java8
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/docx/StatefulBodyXmlReader.java13
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java37
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/NumberingXmlTests.java22
5 files changed, 107 insertions, 14 deletions
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);