From b65bc063d98c2ddb1dc4cc9b4dbcdc1aef23d5aa Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 3 Aug 2025 08:05:02 +0100 Subject: Ignore style definitions using a style ID that has already been used --- NEWS | 4 +++ .../zwobble/mammoth/internal/docx/StylesXml.java | 7 ++-- .../org/zwobble/mammoth/internal/util/Maps.java | 16 +++++++++ .../zwobble/mammoth/tests/docx/StylesXmlTests.java | 40 ++++++++++++++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 6224299..ed883f1 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +# 1.11.0 + +* Ignore style definitions using a style ID that has already been used. + # 1.10.0 * Add "Heading" and "Body" styles, as found in documents created by Apple Pages, diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/StylesXml.java b/src/main/java/org/zwobble/mammoth/internal/docx/StylesXml.java index 656b0dd..31d55b7 100644 --- a/src/main/java/org/zwobble/mammoth/internal/docx/StylesXml.java +++ b/src/main/java/org/zwobble/mammoth/internal/docx/StylesXml.java @@ -9,8 +9,7 @@ import java.util.Map; import java.util.Optional; import static org.zwobble.mammoth.internal.util.Iterables.lazyFilter; -import static org.zwobble.mammoth.internal.util.Maps.entry; -import static org.zwobble.mammoth.internal.util.Maps.toMap; +import static org.zwobble.mammoth.internal.util.Maps.*; public class StylesXml { public static Styles readStylesXmlElement(XmlElement element) { @@ -24,7 +23,7 @@ public class StylesXml { } private static Map readStyles(XmlElementList styleElements, String styleType) { - return toMap( + return toMapPreferFirst( styleElementsOfType(styleElements, styleType), StylesXml::readStyle ); @@ -37,7 +36,7 @@ public class StylesXml { } private static Map readNumberingStyles(XmlElementList styleElements) { - return toMap( + return toMapPreferFirst( styleElementsOfType(styleElements, "numbering"), StylesXml::readNumberingStyle ); diff --git a/src/main/java/org/zwobble/mammoth/internal/util/Maps.java b/src/main/java/org/zwobble/mammoth/internal/util/Maps.java index e053c90..72d9fd5 100644 --- a/src/main/java/org/zwobble/mammoth/internal/util/Maps.java +++ b/src/main/java/org/zwobble/mammoth/internal/util/Maps.java @@ -63,6 +63,22 @@ public class Maps { return map; } + /** + * Convert an iterable to map using the given mapping function. If the + * mapping function generates entries with the same key, only the first + * entry will be used, and subsequent entries will be ignored. + */ + public static Map toMapPreferFirst(Iterable iterable, Function> function) { + Map map = new HashMap<>(); + for (T element : iterable) { + Map.Entry entry = function.apply(element); + if (!map.containsKey(entry.getKey())) { + map.put(entry.getKey(), entry.getValue()); + } + } + return map; + } + public static Map> toMultiMapWithKey(Iterable iterable, Function function) { return toMultiMap(iterable, element -> entry(function.apply(element), element)); } diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/StylesXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/StylesXmlTests.java index 89a7d1a..c6ccc82 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/StylesXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/StylesXmlTests.java @@ -118,6 +118,46 @@ public class StylesXmlTests { assertEquals(Optional.of("42"), styles.findNumberingStyleById("List1").get().getNumId()); } + @Test + public void whenMultipleNonNumberingStyleElementsHaveSameStyleIdThenOnlyFirstElementIsUsed() { + XmlElement element = element("w:styles", list( + element("w:style", map("w:type", "table", "w:styleId", "TableNormal"), list( + nameElement("Normal Table") + )), + element("w:style", map("w:type", "table", "w:styleId", "TableNormal"), list( + nameElement("Table Normal") + )) + )); + + Styles styles = readStylesXmlElement(element); + + assertEquals(Optional.of("Normal Table"), styles.findTableStyleById("TableNormal").get().getName()); + } + + @Test + public void whenMultipleNumberingStyleElementsHaveSameStyleIdThenOnlyFirstElementIsUsed() { + XmlElement element = element("w:styles", list( + element("w:style", map("w:type", "numbering", "w:styleId", "List1"), list( + element("w:pPr", list( + element("w:numPr", list( + element("w:numId", map("w:val", "42")) + )) + )) + )), + element("w:style", map("w:type", "numbering", "w:styleId", "List1"), list( + element("w:pPr", list( + element("w:numPr", list( + element("w:numId", map("w:val", "43")) + )) + )) + )) + )); + + Styles styles = readStylesXmlElement(element); + + assertEquals(Optional.of("42"), styles.findNumberingStyleById("List1").get().getNumId()); + } + private XmlElement nameElement(String name) { return element("w:name", map("w:val", name)); } -- cgit v1.2.3