aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2013-12-01 01:06:03 +0000
committerMichael Williamson <mike@zwobble.org>2013-12-01 01:06:03 +0000
commitf9148d6f5dcf1e8188411c800daeb0ab493be19b (patch)
treea8fddd8f839b5519f93979d9533eeb60d336e0fc
parent1ca475c19254ebb8e3c453bab8599c19305e45a5 (diff)
Add basic docx file reading
-rw-r--r--mammoth/docx.py78
-rw-r--r--tests/docx_tests.py79
2 files changed, 157 insertions, 0 deletions
diff --git a/mammoth/docx.py b/mammoth/docx.py
new file mode 100644
index 0000000..9a3fe36
--- /dev/null
+++ b/mammoth/docx.py
@@ -0,0 +1,78 @@
+import zipfile
+
+from . import documents
+from .results import Result
+from .xmlparser import parse_xml, node_types
+
+
+_namespaces = [
+ ("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"),
+]
+
+
+def read(fileobj):
+ zip_file = zipfile.ZipFile(fileobj)
+ document_fileobj = zip_file.open("word/document.xml")
+ document_xml = parse_xml(document_fileobj, _namespaces)
+ return Result(read_xml_element(document_xml), [])
+
+
+_handlers = {}
+
+
+def handler(name):
+ def add(func):
+ _handlers[name] = func
+
+ return add
+
+
+def read_xml_element(element):
+ handler = _handlers.get(element.name)
+ if handler is None:
+ return None
+ else:
+ return handler(element)
+
+
+@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):
+ return documents.Paragraph(_read_xml_elements(element.children))
+
+
+@handler("w:body")
+def paragraph(element):
+ return _read_xml_elements(element.children)
+
+
+@handler("w:document")
+def paragraph(element):
+ body_element = _find(lambda child: child.name == "w:body", element.children)
+ return documents.Document(_read_xml_elements(body_element.children))
+
+
+def _find(predicate, iterable):
+ for item in iterable:
+ if predicate(item):
+ return item
+
+
+def _read_xml_elements(elements):
+ return filter(None, map(read_xml_element, elements))
+
+
+def _inner_text(node):
+ if node.node_type == node_types.text:
+ return node.value
+ else:
+ return "".join(_inner_text(child) for child in node.children)
diff --git a/tests/docx_tests.py b/tests/docx_tests.py
new file mode 100644
index 0000000..55a2222
--- /dev/null
+++ b/tests/docx_tests.py
@@ -0,0 +1,79 @@
+from nose.tools import istest, assert_equal
+
+from mammoth import docx, documents
+from mammoth.xmlparser import element as xml_element, text as xml_text
+from .testing import test_path
+
+@istest
+class ReadTests(object):
+ @istest
+ def can_read_document_with_single_paragraph_with_single_run_of_text(self):
+ with open(test_path("single-paragraph.docx")) as fileobj:
+ result = docx.read(fileobj=fileobj)
+ expected_document = documents.Document([
+ documents.Paragraph([
+ documents.Run([
+ documents.Text("Walking on imported air")
+ ])
+ ])
+ ])
+ assert_equal(expected_document, result.value)
+
+
+@istest
+class ReadXmlElementTests(object):
+ @istest
+ def text_from_text_element_is_read(self):
+ element = _text_element("Hello!")
+ assert_equal(documents.Text("Hello!"), docx.read_xml_element(element))
+
+ @istest
+ def can_read_text_within_run(self):
+ element = _run_element_with_text("Hello!")
+ assert_equal(
+ documents.Run([documents.Text("Hello!")]),
+ docx.read_xml_element(element)
+ )
+
+ @istest
+ def can_read_text_within_paragraph(self):
+ element = _paragraph_element_with_text("Hello!")
+ assert_equal(
+ documents.Paragraph([documents.Run([documents.Text("Hello!")])]),
+ docx.read_xml_element(element)
+ )
+
+ @istest
+ def can_read_text_within_paragraph(self):
+ element = _paragraph_element_with_text("Hello!")
+ assert_equal(
+ documents.Paragraph([documents.Run([documents.Text("Hello!")])]),
+ docx.read_xml_element(element)
+ )
+
+ @istest
+ def can_read_text_within_document(self):
+ element = _document_element_with_text("Hello!")
+ assert_equal(
+ documents.Document([documents.Paragraph([documents.Run([documents.Text("Hello!")])])]),
+ docx.read_xml_element(element)
+ )
+
+
+def _document_element_with_text(text):
+ return xml_element("w:document", {}, [
+ xml_element("w:body", {}, [_paragraph_element_with_text(text)])
+ ])
+
+
+def _paragraph_element_with_text(text):
+ return xml_element("w:p", {}, [_run_element_with_text(text)])
+
+
+def _run_element_with_text(text):
+ return xml_element("w:r", {}, [_text_element(text)])
+
+
+def _text_element(value):
+ return xml_element("w:t", {}, [xml_text(value)])
+