diff options
| -rw-r--r-- | mammoth/documents.py | 6 | ||||
| -rw-r--r-- | mammoth/docx/document_xml.py | 103 | ||||
| -rw-r--r-- | mammoth/docx/xmlparser.py | 8 | ||||
| -rw-r--r-- | tests/docx/document_xml_tests.py | 22 |
4 files changed, 91 insertions, 48 deletions
diff --git a/mammoth/documents.py b/mammoth/documents.py index 58e4f6b..08e3bba 100644 --- a/mammoth/documents.py +++ b/mammoth/documents.py @@ -1,11 +1,11 @@ import collections -def paragraph(children, style_name=None): - return Paragraph(children, style_name) +def paragraph(children, style_name=None, numbering=None): + return Paragraph(children, style_name, numbering) Document = collections.namedtuple("Document", ["children"]) -Paragraph = collections.namedtuple("Paragraph", ["children", "style_name"]) +Paragraph = collections.namedtuple("Paragraph", ["children", "style_name", "numbering"]) Run = collections.namedtuple("Run", ["children"]) Text = collections.namedtuple("Text", ["value"]) diff --git a/mammoth/docx/document_xml.py b/mammoth/docx/document_xml.py index 1c9a43b..425ba37 100644 --- a/mammoth/docx/document_xml.py +++ b/mammoth/docx/document_xml.py @@ -2,54 +2,73 @@ from .. import documents from .xmlparser import node_types -_handlers = {} - - -def handler(name): - def add(func): - _handlers[name] = func +def read_document_xml_element(element, numbering=None): + reader = _create_reader(numbering) + return reader(element) + +def _create_reader(numbering): + _handlers = {} + + def handler(name): + def add(func): + _handlers[name] = func + + return add + + @handler("w:t") + def text(element): + return documents.Text(_inner_text(element)) + + + @handler("w:r") + def run(element): + return documents.Run(_read_xml_elements(element.children)) + + + @handler("w:p") + def paragraph(element): + properties = element.find_child_or_null("w:pPr") + style_name = properties \ + .find_child_or_null("w:pStyle") \ + .attributes.get("w:val") + numbering_properties = properties.find_child("w:numPr") + if numbering_properties is None: + numbering = None + else: + numbering = _read_numbering_properties(numbering_properties) - return add + children = _read_xml_elements(element.children) + return documents.paragraph(children, style_name, numbering) - -def read_document_xml_element(element): - handler = _handlers.get(element.name) - if handler is None: - return None - else: - return handler(element) + def _read_numbering_properties(element): + num_id = element.find_child("w:numId").attributes["w:val"] + level_index = element.find_child("w:ilvl").attributes["w:val"] + return numbering.find_level(num_id, level_index) -@handler("w:t") -def text(element): - return documents.Text(_inner_text(element)) + @handler("w:body") + def body(element): + return _read_xml_elements(element.children) -@handler("w:r") -def run(element): - return documents.Run(_read_xml_elements(element.children)) - - -@handler("w:p") -def paragraph(element): - properties = element.find_child_or_null("w:pPr") - style_name = properties \ - .find_child_or_null("w:pStyle") \ - .attributes.get("w:val") + @handler("w:document") + def document(element): + body_element = _find_child(element, "w:body") + return documents.Document(_read_xml_elements(body_element.children)) - children = _read_xml_elements(element.children) - return documents.paragraph(children, style_name) - - -@handler("w:body") -def body(element): - return _read_xml_elements(element.children) - + + def read(element): + handler = _handlers.get(element.name) + if handler is None: + return None + else: + return handler(element) + -@handler("w:document") -def document(element): - body_element = _find_child(element, "w:body") - return documents.Document(_read_xml_elements(body_element.children)) + def _read_xml_elements(elements): + return filter(None, map(read, elements)) + + return read def _find(predicate, iterable): @@ -62,10 +81,6 @@ def _find_child(element, name): return _find(lambda child: child.name == name, element.children) -def _read_xml_elements(elements): - return filter(None, map(read_document_xml_element, elements)) - - def _inner_text(node): if node.node_type == node_types.text: return node.value diff --git a/mammoth/docx/xmlparser.py b/mammoth/docx/xmlparser.py index efeafcf..50c80db 100644 --- a/mammoth/docx/xmlparser.py +++ b/mammoth/docx/xmlparser.py @@ -5,10 +5,13 @@ import xml.sax XmlElementBase = collections.namedtuple("XmlElement", ["name", "attributes", "children"]) class XmlElement(XmlElementBase): def find_child_or_null(self, name): + return self.find_child(name) or _null_xml_element + + def find_child(self, name): for child in self.children: if child.name == name: return child - return _null_xml_element + def find_children(self, name): return filter(lambda child: child.name == name, self.children) @@ -19,6 +22,9 @@ class NullXmlElement(object): def find_child_or_null(self, name): return self + + def find_child(self, name): + return None _null_xml_element = NullXmlElement() diff --git a/tests/docx/document_xml_tests.py b/tests/docx/document_xml_tests.py index 8178cc9..b31ac9d 100644 --- a/tests/docx/document_xml_tests.py +++ b/tests/docx/document_xml_tests.py @@ -3,6 +3,7 @@ from nose.tools import istest, assert_equal from mammoth import documents from mammoth.docx.xmlparser import element as xml_element, text as xml_text from mammoth.docx.document_xml import read_document_xml_element +from mammoth.docx.numbering_xml import Numbering, NumberingLevel @istest @@ -48,6 +49,27 @@ class ReadXmlElementTests(object): paragraph_xml = xml_element("w:p", {}, [properties_xml]) paragraph = read_document_xml_element(paragraph_xml) assert_equal("Heading1", paragraph.style_name) + + @istest + def paragraph_has_no_numbering_if_it_has_no_numbering_properties(self): + element = xml_element("w:p") + assert_equal(None, read_document_xml_element(element).numbering) + + @istest + def paragraph_has_numbering_properties_from_paragraph_properties_if_present(self): + numbering_properties_xml = xml_element("w:numPr", {}, [ + xml_element("w:ilvl", {"w:val": "1"}), + xml_element("w:numId", {"w:val": "42"}), + ]) + properties_xml = xml_element("w:pPr", {}, [numbering_properties_xml]) + paragraph_xml = xml_element("w:p", {}, [properties_xml]) + + numbering = Numbering({"42": {"1": NumberingLevel("1", True)}}) + paragraph = read_document_xml_element(paragraph_xml, numbering=numbering) + + assert_equal("1", paragraph.numbering.level_index) + assert_equal(True, paragraph.numbering.is_ordered) + @istest def unrecognised_elements_are_ignored(self): |
