From 0c47a825bae96f0737f6d81a78b2138ababce646 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 1 Dec 2013 12:22:12 +0000 Subject: Add parsing for numbering.xml --- mammoth/docx/numbering_xml.py | 62 +++++++++++++++++++++++++++++++++++++++ mammoth/docx/xmlparser.py | 3 ++ tests/docx/numbering_xml_tests.py | 43 +++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 mammoth/docx/numbering_xml.py create mode 100644 tests/docx/numbering_xml_tests.py 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) -- cgit v1.2.3