Skip to content

Commit e72d14d

Browse files
author
Steve Canny
committed
img: add _IfdEntries.from_stream()
1 parent 86ea283 commit e72d14d

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

docx/image/tiff.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,44 @@ def from_stream(cls, stream, offset):
113113
Return a new |_IfdEntries| instance parsed from *stream* starting at
114114
*offset*.
115115
"""
116+
ifd_parser = _IfdParser(stream, offset)
117+
entries = dict((e.tag, e.value) for e in ifd_parser.iter_entries())
118+
return cls(entries)
119+
120+
121+
class _IfdParser(object):
122+
"""
123+
Service object that knows how to extract directory entries from an Image
124+
File Directory (IFD)
125+
"""
126+
def __init__(self, stream_rdr, offset):
127+
super(_IfdParser, self).__init__()
128+
self._stream_rdr = stream_rdr
129+
self._offset = offset
130+
131+
def iter_entries(self):
132+
"""
133+
Generate an |_IfdEntry| instance corresponding to each entry in the
134+
directory.
135+
"""
136+
raise NotImplementedError
137+
138+
139+
class _IfdEntry(object):
140+
"""
141+
Base class for IFD entry classes. Subclasses are differentiated by value
142+
type, e.g. ASCII, long int, etc.
143+
"""
144+
@property
145+
def tag(self):
146+
"""
147+
Short int code that identifies this IFD entry
148+
"""
149+
raise NotImplementedError
150+
151+
@property
152+
def value(self):
153+
"""
154+
Value of this tag, its type being dependent on the tag.
155+
"""
116156
raise NotImplementedError

tests/image/test_tiff.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
from docx.compat import BytesIO
1212
from docx.image.helpers import BIG_ENDIAN, LITTLE_ENDIAN, StreamReader
13-
from docx.image.tiff import _IfdEntries, Tiff, _TiffParser
13+
from docx.image.tiff import (
14+
_IfdEntries, _IfdEntry, _IfdParser, Tiff, _TiffParser
15+
)
1416

1517
from ..unitutil import (
1618
initializer_mock, class_mock, instance_mock, method_mock
@@ -153,3 +155,55 @@ def stream_rdr_(self, request, ifd0_offset_):
153155
@pytest.fixture
154156
def _TiffParser__init_(self, request):
155157
return initializer_mock(request, _TiffParser)
158+
159+
160+
class Describe_IfdEntries(object):
161+
162+
def it_can_construct_from_a_stream_and_offset(self, from_stream_fixture):
163+
stream_, offset_, _IfdParser_, _IfdEntries__init_, entries_ = (
164+
from_stream_fixture
165+
)
166+
ifd_entries = _IfdEntries.from_stream(stream_, offset_)
167+
_IfdParser_.assert_called_once_with(stream_, offset_)
168+
_IfdEntries__init_.assert_called_once_with(entries_)
169+
assert isinstance(ifd_entries, _IfdEntries)
170+
171+
# fixtures -------------------------------------------------------
172+
173+
@pytest.fixture
174+
def from_stream_fixture(
175+
self, stream_, offset_, _IfdParser_, ifd_parser_,
176+
_IfdEntries__init_, ifd_entry_, ifd_entry_2_):
177+
ifd_parser_.iter_entries.return_value = [ifd_entry_, ifd_entry_2_]
178+
entries_ = {1: 42, 2: 24}
179+
return stream_, offset_, _IfdParser_, _IfdEntries__init_, entries_
180+
181+
@pytest.fixture
182+
def ifd_entry_(self, request):
183+
return instance_mock(request, _IfdEntry, tag=1, value=42)
184+
185+
@pytest.fixture
186+
def ifd_entry_2_(self, request):
187+
return instance_mock(request, _IfdEntry, tag=2, value=24)
188+
189+
@pytest.fixture
190+
def _IfdEntries__init_(self, request):
191+
return initializer_mock(request, _IfdEntries)
192+
193+
@pytest.fixture
194+
def _IfdParser_(self, request, ifd_parser_):
195+
return class_mock(
196+
request, 'docx.image.tiff._IfdParser', return_value=ifd_parser_
197+
)
198+
199+
@pytest.fixture
200+
def ifd_parser_(self, request):
201+
return instance_mock(request, _IfdParser)
202+
203+
@pytest.fixture
204+
def offset_(self, request):
205+
return instance_mock(request, int)
206+
207+
@pytest.fixture
208+
def stream_(self, request):
209+
return instance_mock(request, BytesIO)

0 commit comments

Comments
 (0)