summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-07-04 13:52:28 +0100
committerMichael Williamson <mike@zwobble.org>2015-07-04 13:52:28 +0100
commit3a363314e6149361b16959d4e3c2ebb6cd97ab1f (patch)
tree1ce2159a0ccdd9d0ceac90ecbda6cdc924254f27
parent044398430b84b73fa6fce57405254f4a63c42424 (diff)
Handle relationships in notes
-rw-r--r--mammoth/docx/__init__.py40
-rw-r--r--mammoth/docx/document_xml.py16
-rw-r--r--mammoth/docx/notes_xml.py26
-rw-r--r--tests/docx/document_xml_tests.py6
-rw-r--r--tests/docx/notes_xml_tests.py16
-rw-r--r--tests/mammoth_tests.py12
-rwxr-xr-xtests/test-data/footnote-hyperlink.docxbin0 -> 16271 bytes
7 files changed, 68 insertions, 48 deletions
diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py
index 3e7c0e0..ec8d363 100644
--- a/mammoth/docx/__init__.py
+++ b/mammoth/docx/__init__.py
@@ -1,13 +1,14 @@
import zipfile
import contextlib
+from .. import results, lists
from .xmlparser import parse_xml
from .document_xml import read_document_xml_element
from .content_types_xml import read_content_types_xml_element
-from .relationships_xml import read_relationships_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 .notes_xml import read_footnotes_xml_element, read_endnotes_xml_element
+from .notes_xml import create_footnotes_reader, create_endnotes_reader
from . import body_xml
@@ -25,23 +26,32 @@ _namespaces = [
def read(fileobj):
zip_file = zipfile.ZipFile(fileobj)
-
body_readers = _body_readers(zip_file)
- body_reader = body_readers("document")
- footnote_elements = _try_read_entry_or_default(
- zip_file, "word/footnotes.xml", read_footnotes_xml_element, default=[])
+ return _read_notes(zip_file, body_readers).bind(lambda notes:
+ _read_document(zip_file, body_readers, notes))
+
+
+def _read_notes(zip_file, body_readers):
+ empty_result = results.success([])
+
+ read_footnotes_xml = create_footnotes_reader(body_readers("footnotes"))
+ footnotes = _try_read_entry_or_default(
+ zip_file, "word/footnotes.xml", read_footnotes_xml, default=empty_result)
+
+ read_endnotes_xml = create_endnotes_reader(body_readers("endnotes"))
+ endnotes = _try_read_entry_or_default(
+ zip_file, "word/endnotes.xml", read_endnotes_xml, default=empty_result)
- endnote_elements = _try_read_entry_or_default(
- zip_file, "word/endnotes.xml", read_endnotes_xml_element, default=[])
+ return results.combine([footnotes, endnotes]).map(lists.collect)
+def _read_document(zip_file, body_readers, notes):
with _open_entry(zip_file, "word/document.xml") as document_fileobj:
document_xml = _parse_docx_xml(document_fileobj)
return read_document_xml_element(
document_xml,
- body_reader=body_reader,
- footnote_elements=footnote_elements,
- endnote_elements=endnote_elements,
+ body_reader=body_readers("document"),
+ notes=notes,
)
@@ -56,9 +66,11 @@ def _body_readers(zip_file):
styles = read_styles_xml_element(_parse_docx_xml(styles_fileobj))
def for_name(name):
- with _open_entry(zip_file, "word/_rels/{0}.xml.rels".format(name)) as relationships_fileobj:
- relationships = read_relationships_xml_element(_parse_docx_xml(relationships_fileobj))
-
+ 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({}))
+
return body_xml.reader(
numbering=numbering,
content_types=content_types,
diff --git a/mammoth/docx/document_xml.py b/mammoth/docx/document_xml.py
index 3cdbec2..1c15001 100644
--- a/mammoth/docx/document_xml.py
+++ b/mammoth/docx/document_xml.py
@@ -1,19 +1,13 @@
from .. import documents
-from .. import results
def read_document_xml_element(element,
body_reader,
- footnote_elements=None,
- endnote_elements=None):
+ notes=None):
- note_elements = (footnote_elements or []) + (endnote_elements or [])
+ if notes is None:
+ notes = []
- def _read_note(element):
- return body_reader.read_all(element.body).map(
- lambda body: documents.note(element.note_type, element.id, body))
-
body_element = element.find_child("w:body")
- children_result = body_reader.read_all(body_element.children)
- notes_result = results.combine(map(_read_note, note_elements)).map(documents.notes)
- return results.map(documents.document, children_result, notes_result)
+ return body_reader.read_all(body_element.children) \
+ .map(lambda children: documents.document(children, documents.notes(notes)))
diff --git a/mammoth/docx/notes_xml.py b/mammoth/docx/notes_xml.py
index b9b0362..f1072f9 100644
--- a/mammoth/docx/notes_xml.py
+++ b/mammoth/docx/notes_xml.py
@@ -1,13 +1,17 @@
+import functools
+
from .. import lists
+from .. import documents
+from .. import results
-def create_reader(note_type):
+def create_reader(note_type, body_reader):
def read_notes_xml_element(element):
note_elements = lists.filter(
_is_note_element,
element.find_children("w:" + note_type),
)
- return lists.map(_read_note_element, note_elements)
+ return results.combine(lists.map(_read_note_element, note_elements))
def _is_note_element(element):
@@ -15,16 +19,14 @@ def create_reader(note_type):
def _read_note_element(element):
- return NoteElement(note_type, element.attributes["w:id"], element.children)
+ return body_reader.read_all(element.children).map(lambda body:
+ documents.note(
+ note_type=note_type,
+ note_id=element.attributes["w:id"],
+ body=body
+ ))
return read_notes_xml_element
-class NoteElement(object):
- def __init__(self, note_type, id, body):
- self.note_type = note_type
- self.id = id
- self.body = body
-
-
-read_footnotes_xml_element = create_reader("footnote")
-read_endnotes_xml_element = create_reader("endnote")
+create_footnotes_reader = functools.partial(create_reader, "footnote")
+create_endnotes_reader = functools.partial(create_reader, "endnote")
diff --git a/tests/docx/document_xml_tests.py b/tests/docx/document_xml_tests.py
index b09f481..fa63775 100644
--- a/tests/docx/document_xml_tests.py
+++ b/tests/docx/document_xml_tests.py
@@ -3,7 +3,6 @@ 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.notes_xml import NoteElement
from mammoth.docx import body_xml
@@ -19,13 +18,12 @@ class ReadXmlElementTests(object):
@istest
def footnotes_of_document_are_read(self):
- paragraph_xml = xml_element("w:p")
- footnotes = [NoteElement("footnote", "4", [paragraph_xml])]
+ notes = [documents.note("footnote", "4", [documents.paragraph([])])]
body_xml = xml_element("w:body")
document_xml = xml_element("w:document", {}, [body_xml])
- document = _read_and_get_document_xml_element(document_xml, footnote_elements=footnotes)
+ document = _read_and_get_document_xml_element(document_xml, notes=notes)
footnote = document.notes.find_note("footnote", "4")
assert_equal("4", footnote.note_id)
assert isinstance(footnote.body[0], documents.Paragraph)
diff --git a/tests/docx/notes_xml_tests.py b/tests/docx/notes_xml_tests.py
index 90de8f8..b912991 100644
--- a/tests/docx/notes_xml_tests.py
+++ b/tests/docx/notes_xml_tests.py
@@ -1,18 +1,20 @@
from nose.tools import istest, assert_equal
+from mammoth import documents
from mammoth.docx.xmlparser import element as xml_element
-from mammoth.docx.notes_xml import read_footnotes_xml_element
+from mammoth.docx.notes_xml import create_footnotes_reader
+from mammoth.docx import body_xml
@istest
def id_and_body_of_footnote_are_read():
footnote_body = [xml_element("w:p")]
- footnotes = read_footnotes_xml_element(xml_element("w:footnotes", {}, [
+ footnotes = create_footnotes_reader(body_xml.reader())(xml_element("w:footnotes", {}, [
xml_element("w:footnote", {"w:id": "1"}, footnote_body),
]))
- assert_equal(1, len(footnotes))
- assert footnote_body is footnotes[0].body
- assert_equal("1", footnotes[0].id)
+ assert_equal(1, len(footnotes.value))
+ assert isinstance(footnotes.value[0].body[0], documents.Paragraph)
+ assert_equal("1", footnotes.value[0].note_id)
@istest
@@ -27,8 +29,8 @@ def separator_is_ignored():
def _assert_footnote_type_is_ignored(footnote_type):
footnote_body = [xml_element("w:p")]
- footnotes = read_footnotes_xml_element(xml_element("w:footnotes", {}, [
+ footnotes = create_footnotes_reader(None)(xml_element("w:footnotes", {}, [
xml_element("w:footnote", {"w:id": "1", "w:type": footnote_type}, footnote_body),
]))
- assert_equal(0, len(footnotes))
+ assert_equal(0, len(footnotes.value))
diff --git a/tests/mammoth_tests.py b/tests/mammoth_tests.py
index 9f37db0..2c7262c 100644
--- a/tests/mammoth_tests.py
+++ b/tests/mammoth_tests.py
@@ -103,6 +103,18 @@ def endnotes_are_appended_to_text():
@istest
+def relationships_are_handled_properly_in_footnotes():
+ expected_html = (
+ '<p><sup><a href="#doc-42-footnote-1" id="doc-42-footnote-ref-1">[1]</a></sup></p>' +
+ '<ol><li id="doc-42-footnote-1"><p> <a href="http://www.example.com">Example</a> <a href="#doc-42-footnote-ref-1">↑</a></p></li></ol>')
+
+ with open(test_path("footnote-hyperlink.docx"), "rb") as fileobj:
+ result = mammoth.convert_to_html(fileobj=fileobj, id_prefix="doc-42")
+ assert_equal([], result.messages)
+ assert_equal(expected_html, result.value)
+
+
+@istest
def text_boxes_are_read():
with open(test_path("text-box.docx"), "rb") as fileobj:
result = mammoth.convert_to_html(fileobj=fileobj)
diff --git a/tests/test-data/footnote-hyperlink.docx b/tests/test-data/footnote-hyperlink.docx
new file mode 100755
index 0000000..208f3dc
--- /dev/null
+++ b/tests/test-data/footnote-hyperlink.docx
Binary files differ