aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mammoth/documents.py6
-rw-r--r--mammoth/docx/document_xml.py103
-rw-r--r--mammoth/docx/xmlparser.py8
-rw-r--r--tests/docx/document_xml_tests.py22
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):