diff options
| author | Michael Williamson <mike@zwobble.org> | 2015-07-20 18:57:46 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2015-07-20 18:58:15 +0100 |
| commit | 61cf58387f239633bf1b81f4e8252459152d2598 (patch) | |
| tree | 99aaf0469c35a330a973618dc5f7cb176cb14e18 /mammoth | |
| parent | 3d20b52a4105d48e319493adf2481cdb5985f72d (diff) | |
Support linked images
Diffstat (limited to 'mammoth')
| -rw-r--r-- | mammoth/docx/__init__.py | 7 | ||||
| -rw-r--r-- | mammoth/docx/body_xml.py | 45 | ||||
| -rw-r--r-- | mammoth/docx/content_types_xml.py | 43 | ||||
| -rw-r--r-- | mammoth/docx/files.py | 25 |
4 files changed, 104 insertions, 16 deletions
diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py index ec8d363..1ca8cb0 100644 --- a/mammoth/docx/__init__.py +++ b/mammoth/docx/__init__.py @@ -1,5 +1,6 @@ import zipfile import contextlib +import os from .. import results, lists from .xmlparser import parse_xml @@ -9,6 +10,7 @@ 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 create_footnotes_reader, create_endnotes_reader +from .files import Files from . import body_xml @@ -26,7 +28,7 @@ _namespaces = [ def read(fileobj): zip_file = zipfile.ZipFile(fileobj) - body_readers = _body_readers(zip_file) + body_readers = _body_readers(getattr(fileobj, "name"), zip_file) return _read_notes(zip_file, body_readers).bind(lambda notes: _read_document(zip_file, body_readers, notes)) @@ -55,7 +57,7 @@ def _read_document(zip_file, body_readers, notes): ) -def _body_readers(zip_file): +def _body_readers(document_path, zip_file): with _open_entry(zip_file, "[Content_Types].xml") as content_types_fileobj: content_types = read_content_types_xml_element(_parse_docx_xml(content_types_fileobj)) @@ -77,6 +79,7 @@ def _body_readers(zip_file): relationships=relationships, styles=styles, docx_file=zip_file, + files=Files(os.path.dirname(document_path)), ) return for_name diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py index cb42f05..54f9dd3 100644 --- a/mammoth/docx/body_xml.py +++ b/mammoth/docx/body_xml.py @@ -10,7 +10,8 @@ def reader(numbering=None, content_types=None, relationships=None, styles=None, - docx_file=None): + docx_file=None, + files=None): read, read_all = _create_reader( numbering=numbering, @@ -18,6 +19,7 @@ def reader(numbering=None, relationships=relationships, styles=styles, docx_file=docx_file, + files=files, ) return _BodyReader(read, read_all) @@ -37,7 +39,7 @@ class _BodyReader(object): return results.Result(result.value, result.messages) -def _create_reader(numbering, content_types, relationships, styles, docx_file): +def _create_reader(numbering, content_types, relationships, styles, docx_file, files): _handlers = {} _ignored_elements = set([ "w:bookmarkStart", @@ -221,17 +223,9 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file): return _combine_results(map(lambda blip: _read_blip(blip, alt_text), blips)) def _read_blip(element, alt_text): - relationship_id = element.attributes["r:embed"] - image_path = "word/" + relationships[relationship_id].target.lstrip("/") + image_path, open_image = _read_blip_image(element) content_type = content_types.find_content_type(image_path) - def open_image(): - image_file = docx_file.open(image_path) - if hasattr(image_file, "__exit__"): - return image_file - else: - return contextlib.closing(image_file) - image = documents.image(alt_text=alt_text, content_type=content_type, open=open_image) if content_type in ["image/png", "image/gif", "image/jpeg", "image/svg+xml", "image/tiff"]: @@ -241,6 +235,35 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file): return _element_result_with_messages(image, messages) + def _read_blip_image(element): + embed_relationship_id = element.attributes.get("r:embed") + link_relationship_id = element.attributes.get("r:link") + if embed_relationship_id is not None: + return _read_embedded_blip_image(embed_relationship_id) + elif link_relationship_id is not None: + return _read_linked_blip_image(link_relationship_id) + + def _read_embedded_blip_image(relationship_id): + image_path = "word/" + relationships[relationship_id].target.lstrip("/") + + def open_image(): + image_file = docx_file.open(image_path) + if hasattr(image_file, "__exit__"): + return image_file + else: + return contextlib.closing(image_file) + + return image_path, open_image + + + def _read_linked_blip_image(relationship_id): + image_path = relationships[relationship_id].target + + def open_image(): + return files.open(image_path) + + return image_path, open_image + def note_reference_reader(note_type): @handler("w:{0}Reference".format(note_type)) def note_reference(element): diff --git a/mammoth/docx/content_types_xml.py b/mammoth/docx/content_types_xml.py index a2eb14c..f0406b7 100644 --- a/mammoth/docx/content_types_xml.py +++ b/mammoth/docx/content_types_xml.py @@ -7,7 +7,10 @@ def read_content_types_xml_element(element): _read_override, element.find_children("content-types:Override") )) - return ContentTypes(extension_defaults, overrides) + return _ContentTypes([ + _XmlContentTypes(extension_defaults, overrides), + _FallbackContentTypes(), + ]) def _read_default(element): @@ -22,7 +25,37 @@ def _read_override(element): return part_name.lstrip("/"), content_type -class ContentTypes(object): +class _ContentTypes(object): + def __init__(self, finders): + self._finders = finders + + def find_content_type(self, path): + for finder in self._finders: + content_type = finder.find_content_type(path) + if content_type is not None: + return content_type + + +class _FallbackContentTypes(object): + _image_content_types = { + "png": "png", + "gif": "gif", + "jpeg": "jpeg", + "jpg": "jpeg", + "tif": "tiff", + "tiff": "tiff", + "bmp": "bmp", + } + + def find_content_type(self, path): + extension = _get_extension(path).lower() + if extension in self._image_content_types: + return "image/" + self._image_content_types[extension] + else: + return None + + +class _XmlContentTypes(object): def __init__(self, extension_defaults, overrides): self._extension_defaults = extension_defaults self._overrides = overrides @@ -31,5 +64,9 @@ class ContentTypes(object): if path in self._overrides: return self._overrides[path] else: - extension = path.rpartition(".")[2] + extension = _get_extension(path) return self._extension_defaults.get(extension) + + +def _get_extension(path): + return path.rpartition(".")[2] diff --git a/mammoth/docx/files.py b/mammoth/docx/files.py new file mode 100644 index 0000000..d919de9 --- /dev/null +++ b/mammoth/docx/files.py @@ -0,0 +1,25 @@ +import os +import contextlib +try: + from urllib2 import urlopen +except ImportError: + from urllib.request import urlopen +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse + + +class Files(object): + def __init__(self, base): + self._base = base + + def open(self, uri): + if _is_absolute(uri): + return contextlib.closing(urlopen(uri)) + else: + return open(os.path.join(self._base, uri), "rb") + + +def _is_absolute(url): + return urlparse(url).scheme != "" |
