diff options
| -rw-r--r-- | NEWS | 4 | ||||
| -rw-r--r-- | mammoth/docx/__init__.py | 2 | ||||
| -rw-r--r-- | mammoth/docx/numbering_xml.py | 19 | ||||
| -rw-r--r-- | tests/docx/numbering_xml_tests.py | 26 |
4 files changed, 45 insertions, 6 deletions
@@ -1,3 +1,7 @@ +# 1.4.9 + +* Improve list support by following w:numStyleLink in w:abstractNum. + # 1.4.8 * Preserve empty table rows. diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py index 63a634c..fcb5da5 100644 --- a/mammoth/docx/__init__.py +++ b/mammoth/docx/__init__.py @@ -153,7 +153,7 @@ def _part_with_body_reader(document_path, zip_file, part_paths): zip_file, part_paths.numbering, lambda element: read_numbering_xml_element(element, styles=styles), - default=Numbering(abstract_nums={}, nums={}), + default=Numbering.EMPTY, ) def read_part(name, reader, default=_undefined): diff --git a/mammoth/docx/numbering_xml.py b/mammoth/docx/numbering_xml.py index 3a0645e..af3d1d5 100644 --- a/mammoth/docx/numbering_xml.py +++ b/mammoth/docx/numbering_xml.py @@ -1,12 +1,13 @@ import cobble from ..documents import numbering_level +from .styles_xml import Styles def read_numbering_xml_element(element, styles): abstract_nums = _read_abstract_nums(element) nums = _read_nums(element) - return Numbering(abstract_nums=abstract_nums, nums=nums) + return Numbering(abstract_nums=abstract_nums, nums=nums, styles=styles) def _read_abstract_nums(element): @@ -17,12 +18,14 @@ def _read_abstract_nums(element): def _read_abstract_num(element): abstract_num_id = element.attributes.get("w:abstractNumId") levels = _read_abstract_num_levels(element) - return abstract_num_id, _AbstractNum(levels=levels) + num_style_link = element.find_child_or_null("w:numStyleLink").attributes.get("w:val") + return abstract_num_id, _AbstractNum(levels=levels, num_style_link=num_style_link) @cobble.data class _AbstractNum(object): levels = cobble.field() + num_style_link = cobble.field() def _read_abstract_num_levels(element): @@ -60,9 +63,10 @@ class _Num(object): class Numbering(object): - def __init__(self, abstract_nums, nums): + def __init__(self, abstract_nums, nums, styles): self._abstract_nums = abstract_nums self._nums = nums + self._styles = styles def find_level(self, num_id, level): num = self._nums.get(num_id) @@ -70,4 +74,11 @@ class Numbering(object): return None else: abstract_num = self._abstract_nums[num.abstract_num_id] - return abstract_num.levels.get(level) + if abstract_num.num_style_link is None: + return 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) + + +Numbering.EMPTY = Numbering(abstract_nums={}, nums={}, styles=Styles.EMPTY) diff --git a/tests/docx/numbering_xml_tests.py b/tests/docx/numbering_xml_tests.py index 467d406..b707ba1 100644 --- a/tests/docx/numbering_xml_tests.py +++ b/tests/docx/numbering_xml_tests.py @@ -2,7 +2,7 @@ from nose.tools import istest, assert_equal from mammoth.docx.xmlparser import element as xml_element from mammoth.docx.numbering_xml import read_numbering_xml_element -from mammoth.docx.styles_xml import Styles +from mammoth.docx.styles_xml import NumberingStyle, Styles @istest @@ -50,6 +50,30 @@ def find_level_returns_none_if_level_cannot_be_found(): assert_equal(None, numbering.find_level("47", "2")) +@istest +def when_abstract_num_has_num_style_link_then_style_is_used_to_find_num(): + numbering = _read_numbering_xml_element( + xml_element("w:numbering", {}, [ + xml_element("w:abstractNum", {"w:abstractNumId": "100"}, [ + xml_element("w:lvl", {"w:ilvl": "0"}, [ + xml_element("w:numFmt", {"w:val": "decimal"}), + ]), + ]), + xml_element("w:abstractNum", {"w:abstractNumId": "101"}, [ + xml_element("w:numStyleLink", {"w:val": "List1"}), + ]), + xml_element("w:num", {"w:numId": "200"}, [ + xml_element("w:abstractNumId", {"w:val": "100"}), + ]), + xml_element("w:num", {"w:numId": "201"}, [ + xml_element("w:abstractNumId", {"w:val": "101"}), + ]) + ]), + styles=Styles.create(numbering_styles={"List1": NumberingStyle(num_id="200")}), + ) + assert_equal(True, numbering.find_level("201", "0").is_ordered) + + def _read_numbering_xml_element(element, styles=None): if styles is None: styles = Styles.EMPTY |
