summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--mammoth/docx/numbering_xml.py23
-rw-r--r--tests/docx/numbering_xml_tests.py17
3 files changed, 35 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index 66e134e..cce165e 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@
* Fix: on Windows, when an image's content type includes a backslash in the
subpart, files may be written outside of the directory set by --output-dir.
+* Detect and ignore numbering levels that use numStyleLink to refer to
+ themselves.
+
# 1.12.0
* Handle hyperlinked wp:anchor and wp:inline elements.
diff --git a/mammoth/docx/numbering_xml.py b/mammoth/docx/numbering_xml.py
index 0bd1952..22fa28f 100644
--- a/mammoth/docx/numbering_xml.py
+++ b/mammoth/docx/numbering_xml.py
@@ -101,18 +101,25 @@ class Numbering(object):
self._styles = styles
def find_level(self, num_id, level):
+ return self._find_level_with_seen_num_ids(num_id, level, seen_num_ids=set())
+
+ def _find_level_with_seen_num_ids(self, num_id, level, seen_num_ids):
+ if num_id in seen_num_ids:
+ return None
+ seen_num_ids.add(num_id)
+
num = self._nums.get(num_id)
if num is None:
return None
+
+ abstract_num = self._abstract_nums.get(num.abstract_num_id)
+ if abstract_num is None:
+ return None
+ elif abstract_num.num_style_link is None:
+ return self._to_numbering_level(abstract_num.levels.get(level))
else:
- abstract_num = self._abstract_nums.get(num.abstract_num_id)
- if abstract_num is None:
- return None
- elif abstract_num.num_style_link is None:
- return self._to_numbering_level(abstract_num.levels.get(level))
- else:
- style = self._styles.find_numbering_style_by_id(abstract_num.num_style_link)
- return self.find_level(style.num_id, level)
+ style = self._styles.find_numbering_style_by_id(abstract_num.num_style_link)
+ return self._find_level_with_seen_num_ids(style.num_id, level, seen_num_ids=seen_num_ids)
def find_level_by_paragraph_style_id(self, style_id):
return self._levels_by_paragraph_style_id.get(style_id)
diff --git a/tests/docx/numbering_xml_tests.py b/tests/docx/numbering_xml_tests.py
index 6cc779f..52aad5d 100644
--- a/tests/docx/numbering_xml_tests.py
+++ b/tests/docx/numbering_xml_tests.py
@@ -154,6 +154,23 @@ def test_when_abstract_num_has_num_style_link_then_style_is_used_to_find_num():
assert_equal(True, numbering.find_level("201", "0").is_ordered)
+def test_when_abstract_num_has_self_recursive_num_style_link_then_level_is_not_found():
+ numbering = _read_numbering_xml_element(
+ xml_element("w:numbering", {}, [
+ xml_element("w:abstractNum", {"w:abstractNumId": "100"}, [
+ xml_element("w:numStyleLink", {"w:val": "List1"}),
+ ]),
+ xml_element("w:num", {"w:numId": "200"}, [
+ xml_element("w:abstractNumId", {"w:val": "100"}),
+ ]),
+ ]),
+ styles=Styles.create(numbering_styles={
+ "List1": NumberingStyle(style_id="List1", num_id="200"),
+ }),
+ )
+ assert_equal(None, numbering.find_level("200", "0"))
+
+
# See: 17.9.23 pStyle (Paragraph Style's Associated Numbering Level) in ECMA-376, 4th Edition
def test_numbering_level_can_be_found_by_paragraph_style_id():
numbering = _read_numbering_xml_element(