Skip to content

Commit fdeb44f

Browse files
author
Steve Canny
committed
img: add _TiffParser.horz_dpi and .vert_dpi
1 parent cdb462d commit fdeb44f

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

docx/image/tiff.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,16 @@ def horz_dpi(self):
5353
and ResolutionUnit tags of the IFD; defaults to 72 if those tags are
5454
not present.
5555
"""
56-
raise NotImplementedError
56+
return self._dpi(TIFF_TAG.X_RESOLUTION)
57+
58+
@property
59+
def vert_dpi(self):
60+
"""
61+
The vertical dots per inch value calculated from the XResolution and
62+
ResolutionUnit tags of the IFD; defaults to 72 if those tags are not
63+
present.
64+
"""
65+
return self._dpi(TIFF_TAG.Y_RESOLUTION)
5766

5867
@property
5968
def px_height(self):
@@ -73,15 +82,6 @@ def px_width(self):
7382
"""
7483
return self._ifd_entries.get(TIFF_TAG.IMAGE_WIDTH)
7584

76-
@property
77-
def vert_dpi(self):
78-
"""
79-
The vertical dots per inch value calculated from the XResolution and
80-
ResolutionUnit tags of the IFD; defaults to 72 if those tags are not
81-
present.
82-
"""
83-
raise NotImplementedError
84-
8585
@classmethod
8686
def _detect_endian(cls, stream):
8787
"""
@@ -92,6 +92,23 @@ def _detect_endian(cls, stream):
9292
endian_str = stream.read(2)
9393
return BIG_ENDIAN if endian_str == b'MM' else LITTLE_ENDIAN
9494

95+
def _dpi(self, resolution_tag):
96+
"""
97+
Return the dpi value calculated for *resolution_tag*, which can be
98+
either TIFF_TAG.X_RESOLUTION or TIFF_TAG.Y_RESOLUTION. The
99+
calculation is based on the values of both that tag and the
100+
TIFF_TAG.RESOLUTION_UNIT tag in this parser's |_IfdEntries| instance.
101+
"""
102+
if resolution_tag not in self._ifd_entries:
103+
return 72
104+
resolution_unit = self._ifd_entries[TIFF_TAG.RESOLUTION_UNIT]
105+
if resolution_unit == 1: # aspect ratio only
106+
return 72
107+
# resolution_unit == 2 for inches, 3 for centimeters
108+
units_per_inch = 1 if resolution_unit == 2 else 2.54
109+
dots_per_unit = self._ifd_entries[resolution_tag]
110+
return int(round(dots_per_unit * units_per_inch))
111+
95112
@classmethod
96113
def _make_stream_reader(cls, stream):
97114
"""
@@ -112,6 +129,18 @@ def __init__(self, entries):
112129
super(_IfdEntries, self).__init__()
113130
self._entries = entries
114131

132+
def __contains__(self, key):
133+
"""
134+
Provides ``in`` operator, e.g. ``tag in ifd_entries``
135+
"""
136+
return self._entries.__contains__(key)
137+
138+
def __getitem__(self, key):
139+
"""
140+
Provides indexed access, e.g. ``tag_value = ifd_entries[tag_code]``
141+
"""
142+
return self._entries.__getitem__(key)
143+
115144
@classmethod
116145
def from_stream(cls, stream, offset):
117146
"""

tests/image/test_tiff.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,32 @@ def it_knows_image_width_and_height_after_parsing(self):
110110
assert tiff_parser.px_width == px_width
111111
assert tiff_parser.px_height == px_height
112112

113+
def it_knows_the_horz_and_vert_dpi_after_parsing(self, dpi_fixture):
114+
tiff_parser, expected_horz_dpi, expected_vert_dpi = dpi_fixture
115+
assert tiff_parser.horz_dpi == expected_horz_dpi
116+
assert tiff_parser.vert_dpi == expected_vert_dpi
117+
113118
# fixtures -------------------------------------------------------
114119

120+
@pytest.fixture(params=[
121+
(1, 150, 240, 72, 72),
122+
(2, 42, 24, 42, 24),
123+
(3, 100, 200, 254, 508),
124+
(6, None, None, 72, 72),
125+
])
126+
def dpi_fixture(self, request):
127+
resolution_unit, x_resolution, y_resolution = request.param[:3]
128+
expected_horz_dpi, expected_vert_dpi = request.param[3:]
129+
130+
entries = {TIFF_TAG.RESOLUTION_UNIT: resolution_unit}
131+
if x_resolution is not None:
132+
entries[TIFF_TAG.X_RESOLUTION] = x_resolution
133+
if y_resolution is not None:
134+
entries[TIFF_TAG.Y_RESOLUTION] = y_resolution
135+
136+
tiff_parser = _TiffParser(entries)
137+
return tiff_parser, expected_horz_dpi, expected_vert_dpi
138+
115139
@pytest.fixture
116140
def from_stream_fixture(
117141
self, stream_, _make_stream_reader_, _IfdEntries_, ifd0_offset_,

0 commit comments

Comments
 (0)