diff options
| author | Michael Williamson <mike@zwobble.org> | 2013-12-01 11:47:48 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2013-12-01 11:47:48 +0000 |
| commit | f8d72dbba832db282a3ec04a5108fbd090f49a4f (patch) | |
| tree | e86971aec308963c55afde6263091f6591bb08d8 /mammoth/docx | |
| parent | 833c867189d757efb5b13e1e783869a4039860de (diff) | |
Move docx reading into separate package
Diffstat (limited to 'mammoth/docx')
| -rw-r--r-- | mammoth/docx/__init__.py | 88 | ||||
| -rw-r--r-- | mammoth/docx/xmlparser.py | 99 |
2 files changed, 187 insertions, 0 deletions
diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py new file mode 100644 index 0000000..de2b8a8 --- /dev/null +++ b/mammoth/docx/__init__.py @@ -0,0 +1,88 @@ +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): + properties = element.find_child_or_null("w:pPr") + style_name = properties \ + .find_child_or_null("w:pStyle") \ + .attributes.get("w:val") + + children = _read_xml_elements(element.children) + return documents.paragraph(children, style_name) + + +@handler("w:body") +def body(element): + return _read_xml_elements(element.children) + + +@handler("w:document") +def document(element): + body_element = _find_child(element, "w:body") + return documents.Document(_read_xml_elements(body_element.children)) + + +def _find(predicate, iterable): + for item in iterable: + if predicate(item): + return item + + +def _find_child(element, name): + return _find(lambda child: child.name == name, element.children) + + +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/mammoth/docx/xmlparser.py b/mammoth/docx/xmlparser.py new file mode 100644 index 0000000..d58ea75 --- /dev/null +++ b/mammoth/docx/xmlparser.py @@ -0,0 +1,99 @@ +import collections +import xml.sax + + +XmlElementBase = collections.namedtuple("XmlElement", ["name", "attributes", "children"]) +class XmlElement(XmlElementBase): + def find_child_or_null(self, name): + for child in self.children: + if child.name == name: + return child + return _null_xml_element + + +class NullXmlElement(object): + attributes = {} + + def find_child_or_null(self, name): + return self + + +_null_xml_element = NullXmlElement() + + +XmlText = collections.namedtuple("XmlText", ["value"]) + + +def element(name, attributes=None, children=None): + return XmlElement(name, attributes or {}, children or []) + +text = XmlText + + +class node_types(object): + element = 1 + text = 3 + + +XmlElement.node_type = node_types.element +XmlText.node_type = node_types.text + + + +def parse_xml(fileobj, namespace_mapping=None): + if namespace_mapping is None: + namespace_prefixes = {} + else: + namespace_prefixes = dict((uri, prefix) for prefix, uri in namespace_mapping) + + handler = Handler(namespace_prefixes) + parser = xml.sax.make_parser() + parser.setFeature(xml.sax.handler.feature_namespaces, True) + parser.setContentHandler(handler) + parser.parse(fileobj) + return handler.root() + + +class Handler(xml.sax.handler.ContentHandler): + def __init__(self, namespace_prefixes): + self._namespace_prefixes = namespace_prefixes + self._element_stack = [RootElement()] + self._character_buffer = [] + + def root(self): + return self._element_stack[0].children[0] + + def startElementNS(self, name, qname, attrs): + self._flush_character_buffer() + attributes = dict((self._read_name(key), value) for key, value in attrs.items()) + element = XmlElement(self._read_name(name), attributes, []) + self._element_stack[-1].children.append(element) + self._element_stack.append(element) + + def endElementNS(self, name, qname): + self._flush_character_buffer() + self._element_stack.pop() + + def characters(self, content): + self._character_buffer.append(content) + + def _flush_character_buffer(self): + if self._character_buffer: + text = "".join(self._character_buffer) + self._element_stack[-1].children.append(XmlText(text)) + self._character_buffer = [] + + def _read_name(self, name): + uri, local_name = name + if uri is None: + return local_name + else: + prefix = self._namespace_prefixes.get(uri) + if prefix is None: + return "{%s}%s" % (uri, local_name) + else: + return "%s:%s" % (prefix, local_name) + +class RootElement(object): + def __init__(self): + self.children = [] |
