Skip to content

Commit a2782f5

Browse files
author
Steve Canny
committed
img: add Png._parse_chunk_offsets()
1 parent 2c6d96a commit a2782f5

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

docx/image/png.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,30 @@ def _parse_chunk_offsets(cls, stream):
4242
*stream*. The offsets for a chunk type that may appear more than once
4343
are returned as a list regardless of their actual number in *stream*.
4444
"""
45-
raise NotImplementedError
45+
chunk_offsets = {}
46+
for chunk_type, offset in cls._iter_chunk_offsets(stream):
47+
# this would need to be more sophisticated if we needed any of
48+
# the chunks that can appear multiple times
49+
chunk_offsets[chunk_type] = offset
50+
return chunk_offsets
51+
52+
@staticmethod
53+
def _iter_chunk_offsets(stream):
54+
"""
55+
Generate a (chunk_type, chunk_offset) 2-tuple for each of the chunks
56+
in the PNG image stream. Iteration stops after the IEND chunk is
57+
returned.
58+
"""
59+
chunk_offset = 8
60+
while True:
61+
chunk_data_len = stream.read_long(chunk_offset)
62+
chunk_type = stream.read_str(4, chunk_offset, 4)
63+
data_offset = chunk_offset + 8
64+
yield chunk_type, data_offset
65+
if chunk_type == 'IEND':
66+
break
67+
# incr offset for chunk len long, chunk type, chunk data, and CRC
68+
chunk_offset += (4 + 4 + chunk_data_len + 4)
4669

4770
@classmethod
4871
def _parse_chunks(cls, stream, chunk_offsets):

tests/image/test_png.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
from docx.compat import BytesIO
1212
from docx.image.exceptions import InvalidImageStreamError
13-
from docx.image.helpers import StreamReader
13+
from docx.image.helpers import BIG_ENDIAN, StreamReader
1414
from docx.image.png import Png
1515

1616
from ..unitutil import (
17-
initializer_mock, class_mock, instance_mock, method_mock
17+
initializer_mock, class_mock, instance_mock, method_mock, test_file
1818
)
1919

2020

@@ -45,6 +45,12 @@ def it_raises_on_png_having_no_IHDR_chunk(self, no_IHDR_fixture):
4545
with pytest.raises(InvalidImageStreamError):
4646
Png._parse_png_headers(stream_)
4747

48+
def it_parses_chunk_offsets_to_help_chunk_parser(
49+
self, chunk_offset_fixture):
50+
stream, expected_chunk_offsets = chunk_offset_fixture
51+
chunk_offsets = Png._parse_chunk_offsets(stream)
52+
assert chunk_offsets == expected_chunk_offsets
53+
4854
# fixtures -------------------------------------------------------
4955

5056
@pytest.fixture
@@ -59,6 +65,22 @@ def attrs_(self, request):
5965
def blob_(self, request):
6066
return instance_mock(request, bytes)
6167

68+
@pytest.fixture(params=[
69+
('150-dpi.png', {
70+
'IHDR': 16, 'pHYs': 41, 'iCCP': 62, 'cHRM': 2713, 'IDAT': 2757,
71+
'IEND': 146888}),
72+
('300-dpi.png', {
73+
'IHDR': 16, 'pHYs': 41, 'tEXt': 62, 'IDAT': 99, 'IEND': 39917}),
74+
])
75+
def chunk_offset_fixture(self, request):
76+
filename, expected_chunk_offsets = request.param
77+
path = test_file(filename)
78+
with open(path, 'rb') as f:
79+
blob = f.read()
80+
stream = BytesIO(blob)
81+
stream_rdr = StreamReader(stream, BIG_ENDIAN)
82+
return stream_rdr, expected_chunk_offsets
83+
6284
@pytest.fixture
6385
def chunk_offsets_(self, request):
6486
return dict()

0 commit comments

Comments
 (0)