Skip to content

Commit 1e6d9ba

Browse files
author
Steve Canny
committed
img: add Jfif.from_stream()
1 parent 395243f commit 1e6d9ba

2 files changed

Lines changed: 134 additions & 0 deletions

File tree

docx/image/jpeg.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# encoding: utf-8
22

3+
"""
4+
Objects related to parsing headers of JPEG image streams, both JFIF and Exif
5+
sub-formats.
6+
"""
7+
38
from __future__ import absolute_import, division, print_function
49

510
from .image import Image
@@ -21,3 +26,63 @@ class Jfif(Jpeg):
2126
"""
2227
Image header parser for JFIF image format
2328
"""
29+
def __init__(self, blob, filename, cx, cy, horz_dpi, vert_dpi):
30+
super(Jfif, self).__init__(blob, filename, cx, cy, attrs={})
31+
self._horz_dpi = horz_dpi
32+
self._vert_dpi = vert_dpi
33+
34+
@classmethod
35+
def from_stream(cls, stream, blob, filename):
36+
"""
37+
Return a |Jfif| instance having header properties parsed from image
38+
in *stream*.
39+
"""
40+
markers = _JfifMarkers.from_stream(stream)
41+
sof, app0 = markers.sof, markers.app0
42+
cx, cy = sof.px_width, sof.px_height
43+
horz_dpi, vert_dpi = app0.horz_dpi, app0.vert_dpi
44+
return cls(blob, filename, cx, cy, horz_dpi, vert_dpi)
45+
46+
@property
47+
def horz_dpi(self):
48+
"""
49+
Integer dots per inch for the width of this image. Defaults to 72
50+
when not present in the file, as is often the case.
51+
"""
52+
return self._horz_dpi
53+
54+
@property
55+
def vert_dpi(self):
56+
"""
57+
Integer dots per inch for the height of this image. Defaults to 72
58+
when not present in the file, as is often the case.
59+
"""
60+
return self._vert_dpi
61+
62+
63+
class _JfifMarkers(object):
64+
"""
65+
Sequence of markers in a JPEG file, perhaps truncated at first SOS marker
66+
for performance reasons.
67+
"""
68+
@classmethod
69+
def from_stream(cls, stream):
70+
"""
71+
Return a |_JfifMarkers| instance containing a |_JfifMarker| instance
72+
for each marker in *stream*.
73+
"""
74+
raise NotImplementedError
75+
76+
@property
77+
def app0(self):
78+
"""
79+
First APP0 marker in image markers.
80+
"""
81+
raise NotImplementedError
82+
83+
@property
84+
def sof(self):
85+
"""
86+
First start of frame (SOFn) marker in this sequence.
87+
"""
88+
raise NotImplementedError

tests/image/test_jpeg.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# encoding: utf-8
2+
3+
"""
4+
Test suite for docx.image.jpeg module
5+
"""
6+
7+
from __future__ import absolute_import, print_function
8+
9+
import pytest
10+
11+
from docx.compat import BytesIO
12+
from docx.image.jpeg import Jfif, _JfifMarkers
13+
14+
from ..unitutil import class_mock, instance_mock
15+
16+
17+
class DescribeJfif(object):
18+
19+
def it_can_construct_from_a_jfif_stream(self, from_stream_fixture):
20+
# fixture ----------------------
21+
(stream_, blob_, filename_, _JfifMarkers_, px_width, px_height,
22+
horz_dpi, vert_dpi) = from_stream_fixture
23+
# exercise ---------------------
24+
jfif = Jfif.from_stream(stream_, blob_, filename_)
25+
# verify -----------------------
26+
_JfifMarkers_.from_stream.assert_called_once_with(stream_)
27+
assert isinstance(jfif, Jfif)
28+
assert jfif.px_width == px_width
29+
assert jfif.px_height == px_height
30+
assert jfif.horz_dpi == horz_dpi
31+
assert jfif.vert_dpi == vert_dpi
32+
33+
# fixtures -------------------------------------------------------
34+
35+
@pytest.fixture
36+
def blob_(self, request):
37+
return instance_mock(request, bytes)
38+
39+
@pytest.fixture
40+
def filename_(self, request):
41+
return instance_mock(request, str)
42+
43+
@pytest.fixture
44+
def from_stream_fixture(
45+
self, stream_, blob_, filename_, _JfifMarkers_, jfif_markers_):
46+
px_width, px_height = 111, 222
47+
horz_dpi, vert_dpi = 333, 444
48+
jfif_markers_.sof.px_width = px_width
49+
jfif_markers_.sof.px_height = px_height
50+
jfif_markers_.app0.horz_dpi = horz_dpi
51+
jfif_markers_.app0.vert_dpi = vert_dpi
52+
return (
53+
stream_, blob_, filename_, _JfifMarkers_, px_width, px_height,
54+
horz_dpi, vert_dpi
55+
)
56+
57+
@pytest.fixture
58+
def _JfifMarkers_(self, request, jfif_markers_):
59+
_JfifMarkers_ = class_mock(request, 'docx.image.jpeg._JfifMarkers')
60+
_JfifMarkers_.from_stream.return_value = jfif_markers_
61+
return _JfifMarkers_
62+
63+
@pytest.fixture
64+
def jfif_markers_(self, request):
65+
return instance_mock(request, _JfifMarkers)
66+
67+
@pytest.fixture
68+
def stream_(self, request):
69+
return instance_mock(request, BytesIO)

0 commit comments

Comments
 (0)