summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2018-01-19 20:12:18 +0000
committerMichael Williamson <mike@zwobble.org>2018-01-19 20:12:18 +0000
commitdf5644cde6ba9b70870ddb01caaee423e00e49ad (patch)
tree393d0be67c91b2ab62f02fd660952a1258ef65c9
parentdcca2c6ffe71e399caeeac6c2d44070c097a9cc5 (diff)
Read document XML path from _rels/.rels
-rw-r--r--mammoth/docx/__init__.py45
-rw-r--r--mammoth/docx/content_types_xml.py1
-rw-r--r--mammoth/docx/relationships_xml.py3
-rw-r--r--tests/docx/docx_tests.py53
4 files changed, 94 insertions, 8 deletions
diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py
index 8fb150a..c4c6877 100644
--- a/mammoth/docx/__init__.py
+++ b/mammoth/docx/__init__.py
@@ -2,10 +2,10 @@ import os
from .. import results, lists
from .document_xml import read_document_xml_element
-from .content_types_xml import read_content_types_xml_element
+from .content_types_xml import empty_content_types, read_content_types_xml_element
from .relationships_xml import read_relationships_xml_element, Relationships
from .numbering_xml import read_numbering_xml_element, Numbering
-from .styles_xml import read_styles_xml_element
+from .styles_xml import read_styles_xml_element, Styles
from .notes_xml import create_footnotes_reader, create_endnotes_reader
from .comments_xml import create_comments_reader
from .files import Files
@@ -50,7 +50,16 @@ def _read_comments(zip_file, body_readers):
def _read_document(zip_file, body_readers, notes, comments):
- with zip_file.open("word/document.xml") as document_fileobj:
+ file_relationships = _try_read_entry_or_default(
+ zip_file,
+ "_rels/.rels",
+ read_relationships_xml_element,
+ default=Relationships.EMPTY,
+ )
+
+ document_filename = _find_document_filename(zip_file, file_relationships)
+
+ with zip_file.open(document_filename) as document_fileobj:
document_xml = office_xml.read(document_fileobj)
return read_document_xml_element(
document_xml,
@@ -60,21 +69,41 @@ def _read_document(zip_file, body_readers, notes, comments):
)
+def _find_document_filename(zip_file, relationships):
+ targets = ["word/document.xml"] + [
+ target.lstrip("/")
+ for target in relationships.find_targets_by_type("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
+ ]
+ valid_targets = list(filter(lambda target: zip_file.exists(target), targets))
+ if len(valid_targets) == 0:
+ return None
+ else:
+ return valid_targets[0]
+
+
def _body_readers(document_path, zip_file):
- with zip_file.open("[Content_Types].xml") as content_types_fileobj:
- content_types = read_content_types_xml_element(office_xml.read(content_types_fileobj))
+ content_types = _try_read_entry_or_default(
+ zip_file,
+ "[Content_Types].xml",
+ read_content_types_xml_element,
+ empty_content_types,
+ )
numbering = _try_read_entry_or_default(
zip_file, "word/numbering.xml", read_numbering_xml_element, default=Numbering({}))
- with zip_file.open("word/styles.xml") as styles_fileobj:
- styles = read_styles_xml_element(office_xml.read(styles_fileobj))
+ styles = _try_read_entry_or_default(
+ zip_file,
+ "word/styles.xml",
+ read_styles_xml_element,
+ Styles.EMPTY,
+ )
def for_name(name):
relationships_path = "word/_rels/{0}.xml.rels".format(name)
relationships = _try_read_entry_or_default(
zip_file, relationships_path, read_relationships_xml_element,
- default=Relationships({}))
+ default=Relationships.EMPTY)
return body_xml.reader(
numbering=numbering,
diff --git a/mammoth/docx/content_types_xml.py b/mammoth/docx/content_types_xml.py
index cdb8f6f..1180458 100644
--- a/mammoth/docx/content_types_xml.py
+++ b/mammoth/docx/content_types_xml.py
@@ -52,6 +52,7 @@ class _ContentTypes(object):
return None
+empty_content_types = _ContentTypes({}, {})
def _get_extension(path):
return path.rpartition(".")[2]
diff --git a/mammoth/docx/relationships_xml.py b/mammoth/docx/relationships_xml.py
index c30c428..555ba2d 100644
--- a/mammoth/docx/relationships_xml.py
+++ b/mammoth/docx/relationships_xml.py
@@ -18,6 +18,9 @@ class Relationships(object):
return self._targets_by_type[relationship_type]
+Relationships.EMPTY = Relationships([])
+
+
Relationship = collections.namedtuple("Relationship", ["relationship_id", "target", "type"])
diff --git a/tests/docx/docx_tests.py b/tests/docx/docx_tests.py
index ea69dc2..fd53d4f 100644
--- a/tests/docx/docx_tests.py
+++ b/tests/docx/docx_tests.py
@@ -1,3 +1,7 @@
+import io
+import textwrap
+import zipfile
+
from nose.tools import istest, assert_equal
from mammoth import docx, documents
@@ -18,3 +22,52 @@ class ReadTests(object):
])
])
assert_equal(expected_document, result.value)
+
+
+_relationship_namespaces = {
+ "r": "http://schemas.openxmlformats.org/package/2006/relationships",
+}
+
+
+@istest
+def when_document_xml_is_not_present_then_document_is_found_using_file_relationships():
+ fileobj = _create_zip({
+ "word/document2.xml": textwrap.dedent("""\
+ <?xml version="1.0" encoding="utf-8" ?>
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
+ <w:body>
+ <w:p>
+ <w:r>
+ <w:t>Hello.</w:t>
+ </w:r>
+ </w:p>
+ </w:body>
+ </w:document>
+ """),
+ "_rels/.rels": textwrap.dedent("""\
+ <?xml version="1.0" encoding="utf-8"?>
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+ <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="/word/document2.xml" Id="rId1"/>
+ </Relationships>
+ """),
+ })
+ result = docx.read(fileobj=fileobj)
+ expected_document = documents.document([
+ documents.paragraph([
+ documents.run([
+ documents.text("Hello.")
+ ])
+ ])
+ ])
+ assert_equal(expected_document, result.value)
+
+
+def _create_zip(files):
+ fileobj = io.BytesIO()
+
+ with zipfile.ZipFile(fileobj, "w") as zip_file:
+ for name, contents in files.items():
+ zip_file.writestr(name, contents)
+
+ fileobj.seek(0)
+ return fileobj