diff options
| -rw-r--r-- | mammoth/docx/__init__.py | 40 | ||||
| -rw-r--r-- | mammoth/docx/document_xml.py | 16 | ||||
| -rw-r--r-- | mammoth/docx/notes_xml.py | 26 | ||||
| -rw-r--r-- | tests/docx/document_xml_tests.py | 6 | ||||
| -rw-r--r-- | tests/docx/notes_xml_tests.py | 16 | ||||
| -rw-r--r-- | tests/mammoth_tests.py | 12 | ||||
| -rwxr-xr-x | tests/test-data/footnote-hyperlink.docx | bin | 0 -> 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 Binary files differnew file mode 100755 index 0000000..208f3dc --- /dev/null +++ b/tests/test-data/footnote-hyperlink.docx |
