diff options
| author | Michael Williamson <mike@zwobble.org> | 2013-12-01 12:22:12 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2013-12-01 12:22:12 +0000 |
| commit | 0c47a825bae96f0737f6d81a78b2138ababce646 (patch) | |
| tree | 28d68ddae77ea4481bc62b4651141335916e20ed | |
| parent | d5acaf1071564a9997a330315ea99cdc1017bae7 (diff) | |
Add parsing for numbering.xml
| -rw-r--r-- | mammoth/docx/numbering_xml.py | 62 | ||||
| -rw-r--r-- | mammoth/docx/xmlparser.py | 3 | ||||
| -rw-r--r-- | tests/docx/numbering_xml_tests.py | 43 |
3 files changed, 108 insertions, 0 deletions
diff --git a/mammoth/docx/numbering_xml.py b/mammoth/docx/numbering_xml.py new file mode 100644 index 0000000..77400c1 --- /dev/null +++ b/mammoth/docx/numbering_xml.py @@ -0,0 +1,62 @@ +def read_numbering_xml_element(element): + abstract_nums = _read_abstract_nums(element) + nums = _read_nums(element, abstract_nums) + return Numbering(nums) + + +def _read_abstract_nums(element): + abstract_num_elements = element.find_children("w:abstractNum") + return dict(map(_read_abstract_num, abstract_num_elements)) + + +def _read_abstract_num(element): + abstract_num_id = element.attributes.get("w:abstractNumId") + levels = _read_abstract_num_levels(element) + return abstract_num_id, levels + + +def _read_abstract_num_levels(element): + levels = map(_read_abstract_num_level, element.find_children("w:lvl")) + return dict( + (level.level_index, level) + for level in levels + ) + + +def _read_abstract_num_level(element): + level_index = element.attributes["w:ilvl"] + num_fmt = element.find_child_or_null("w:numFmt").attributes.get("w:val") + is_ordered = num_fmt != "bullet" + return NumberingLevel(level_index, is_ordered) + + +def _read_nums(element, abstract_nums): + num_elements = element.find_children("w:num") + return dict( + _read_num(num_element, abstract_nums) + for num_element in num_elements + ) + + +def _read_num(element, abstract_nums): + num_id = element.attributes.get("w:numId") + abstract_num_id = element.find_child_or_null("w:abstractNumId").attributes["w:val"] + return num_id, abstract_nums[abstract_num_id] + + +class Numbering(object): + def __init__(self, nums): + self._nums = nums + + def find_level(self, num_id, level): + num = self._nums.get(num_id) + if num is None: + return None + else: + return num[level] + + +class NumberingLevel(object): + def __init__(self, level_index, is_ordered): + self.level_index = level_index + self.is_ordered = is_ordered diff --git a/mammoth/docx/xmlparser.py b/mammoth/docx/xmlparser.py index d58ea75..efeafcf 100644 --- a/mammoth/docx/xmlparser.py +++ b/mammoth/docx/xmlparser.py @@ -9,6 +9,9 @@ class XmlElement(XmlElementBase): if child.name == name: return child return _null_xml_element + + def find_children(self, name): + return filter(lambda child: child.name == name, self.children) class NullXmlElement(object): diff --git a/tests/docx/numbering_xml_tests.py b/tests/docx/numbering_xml_tests.py new file mode 100644 index 0000000..6f4e9ac --- /dev/null +++ b/tests/docx/numbering_xml_tests.py @@ -0,0 +1,43 @@ +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 + + +@istest +def find_level_returns_none_if_num_with_id_cannot_be_found(): + numbering = read_numbering_xml_element(xml_element("w:numbering")) + assert_equal(None, numbering.find_level("47", "0")) + + +_sample_numbering_xml = xml_element("w:numbering", {}, [ + xml_element("w:abstractNum", {"w:abstractNumId": "42"}, [ + xml_element("w:lvl", {"w:ilvl": "0"}, [ + xml_element("w:numFmt", {"w:val": "bullet"}) + ]), + xml_element("w:lvl", {"w:ilvl": "1"}, [ + xml_element("w:numFmt", {"w:val": "decimal"}) + ]) + ]), + xml_element("w:num", {"w:numId": "47"}, [ + xml_element("w:abstractNumId", {"w:val": "42"}) + ]) +]) + + +@istest +def level_includes_level_index(): + numbering = read_numbering_xml_element(_sample_numbering_xml) + assert_equal("0", numbering.find_level("47", "0").level_index) + assert_equal("1", numbering.find_level("47", "1").level_index) + + +@istest +def list_is_not_ordered_if_formatted_as_bullet(): + numbering = read_numbering_xml_element(_sample_numbering_xml) + assert_equal(False, numbering.find_level("47", "0").is_ordered) + +@istest +def list_is_ordered_if_formatted_as_decimal(): + numbering = read_numbering_xml_element(_sample_numbering_xml) + assert_equal(True, numbering.find_level("47", "1").is_ordered) |
