Skip to content

Commit 07f2dea

Browse files
author
Christopher Egner
committed
fix: byte math errors for decoding bitmap pngs
1 parent 18a2627 commit 07f2dea

1 file changed

Lines changed: 43 additions & 29 deletions

File tree

PyPDF2/filters.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@
3939
from .utils import PdfReadError, ord_, chr_, paethPredictor
4040
from sys import version_info
4141
if version_info < ( 3, 0 ):
42-
from cStringIO import StringIO
42+
from cStringIO import StringIO as BytesIO
43+
bytearray = buffer
4344
else:
44-
from io import StringIO
45+
from io import BytesIO
4546
import struct
4647

4748
try:
@@ -123,39 +124,52 @@ def decode(data, decodeParms):
123124
columns = decodeParms["/Columns"]
124125
# PNG prediction:
125126
if predictor >= 10 and predictor <= 15:
126-
output = StringIO()
127-
# PNG prediction can vary from row to row
128-
rowlength = columns + 1
129-
assert len(data) % rowlength == 0
127+
bitsPerComponent = decodeParms.get("/BitsPerComponent")
128+
output = BytesIO()
129+
130+
# PNG predictor can vary by row and so is the lead byte on each row
131+
rowlength = math.ceil(columns * bitsPerComponent / 8) + 1 # number of bytes
132+
if len(data) % rowlength != 0:
133+
raise PdfReadError("Image data is not rectangular")
134+
130135
prev_rowdata = (0,) * rowlength
131136
for row in range(len(data) // rowlength):
132137
rowdata = [ord_(x) for x in data[(row*rowlength):((row+1)*rowlength)]]
133138
filterByte = rowdata[0]
134-
if filterByte == 0:
135-
pass
136-
elif filterByte == 1:
137-
for i in range(2, rowlength):
138-
rowdata[i] = (rowdata[i] + rowdata[i-1]) % 256
139-
elif filterByte == 2:
140-
for i in range(1, rowlength):
141-
rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256
142-
elif filterByte == 3:
143-
for i in range(1, rowlength):
144-
left = rowdata[i-1] if i > 1 else 0
145-
floor = math.floor(left + prev_rowdata[i])/2
146-
rowdata[i] = (rowdata[i] + int(floor)) % 256
147-
elif filterByte == 4:
148-
for i in range(1, rowlength):
149-
left = rowdata[i - 1] if i > 1 else 0
150-
up = prev_rowdata[i]
151-
up_left = prev_rowdata[i - 1] if i > 1 else 0
152-
paeth = paethPredictor(left, up, up_left)
153-
rowdata[i] = (rowdata[i] + paeth) % 256
139+
140+
if bitsPerComponent == 1:
141+
# bitmap data, filters don't make sense but the filter byte is still present
142+
if filterByte != 0:
143+
raise PdfReadError("Unsupported filter byte in bitmap mode: %r" % (filterByte,))
144+
154145
else:
155-
# unsupported PNG filter
156-
raise PdfReadError("Unsupported PNG filter %r" % filterByte)
146+
if filterByte == 0:
147+
pass
148+
elif filterByte == 1:
149+
for i in range(2, rowlength):
150+
rowdata[i] = (rowdata[i] + rowdata[i-1]) % 256
151+
elif filterByte == 2:
152+
for i in range(1, rowlength):
153+
rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256
154+
elif filterByte == 3:
155+
for i in range(1, rowlength):
156+
left = rowdata[i-1] if i > 1 else 0
157+
floor = math.floor(left + prev_rowdata[i])/2
158+
rowdata[i] = (rowdata[i] + int(floor)) % 256
159+
elif filterByte == 4:
160+
for i in range(1, rowlength):
161+
left = rowdata[i - 1] if i > 1 else 0
162+
up = prev_rowdata[i]
163+
up_left = prev_rowdata[i - 1] if i > 1 else 0
164+
paeth = paethPredictor(left, up, up_left)
165+
rowdata[i] = (rowdata[i] + paeth) % 256
166+
167+
else:
168+
# unsupported PNG filter
169+
raise PdfReadError("Unsupported PNG filter %r" % filterByte)
170+
157171
prev_rowdata = rowdata
158-
output.write(''.join([chr(x) for x in rowdata[1:]]))
172+
output.write(bytearray(rowdata[1:]))
159173
data = output.getvalue()
160174
else:
161175
# unsupported predictor

0 commit comments

Comments
 (0)