Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: byte math errors for decoding bitmap pngs #536

Closed
wants to merge 5 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 43 additions & 28 deletions PyPDF2/filters.py
Expand Up @@ -44,12 +44,14 @@
from .utils import PdfReadError, ord_, paethPredictor

if version_info < ( 3, 0 ):
from cStringIO import StringIO
from cStringIO import StringIO as BytesIO
bytearray = buffer
MartinThoma marked this conversation as resolved.
Show resolved Hide resolved
else:
from io import StringIO

import struct


MartinThoma marked this conversation as resolved.
Show resolved Hide resolved
try:
import zlib

Expand Down Expand Up @@ -129,39 +131,52 @@ def decode(data, decodeParms):
columns = decodeParms[LZW.COLUMNS]
# PNG prediction:
if predictor >= 10 and predictor <= 15:
output = StringIO()
# PNG prediction can vary from row to row
rowlength = columns + 1
assert len(data) % rowlength == 0
bitsPerComponent = decodeParms.get("/BitsPerComponent")
output = BytesIO()

# PNG predictor can vary by row and so is the lead byte on each row
rowlength = math.ceil(columns * bitsPerComponent / 8) + 1 # number of bytes
if len(data) % rowlength != 0:
raise PdfReadError("Image data is not rectangular")

prev_rowdata = (0,) * rowlength
for row in range(len(data) // rowlength):
rowdata = [ord_(x) for x in data[(row*rowlength):((row+1)*rowlength)]]
filterByte = rowdata[0]
if filterByte == 0:
pass
elif filterByte == 1:
for i in range(2, rowlength):
rowdata[i] = (rowdata[i] + rowdata[i-1]) % 256
elif filterByte == 2:
for i in range(1, rowlength):
rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256
elif filterByte == 3:
for i in range(1, rowlength):
left = rowdata[i-1] if i > 1 else 0
floor = math.floor(left + prev_rowdata[i])/2
rowdata[i] = (rowdata[i] + int(floor)) % 256
elif filterByte == 4:
for i in range(1, rowlength):
left = rowdata[i - 1] if i > 1 else 0
up = prev_rowdata[i]
up_left = prev_rowdata[i - 1] if i > 1 else 0
paeth = paethPredictor(left, up, up_left)
rowdata[i] = (rowdata[i] + paeth) % 256

if bitsPerComponent == 1:
# bitmap data, filters don't make sense but the filter byte is still present
if filterByte != 0:
raise PdfReadError("Unsupported filter byte in bitmap mode: %r" % (filterByte,))

else:
# unsupported PNG filter
raise PdfReadError("Unsupported PNG filter %r" % filterByte)
if filterByte == 0:
pass
elif filterByte == 1:
for i in range(2, rowlength):
rowdata[i] = (rowdata[i] + rowdata[i-1]) % 256
elif filterByte == 2:
for i in range(1, rowlength):
rowdata[i] = (rowdata[i] + prev_rowdata[i]) % 256
elif filterByte == 3:
for i in range(1, rowlength):
left = rowdata[i-1] if i > 1 else 0
floor = math.floor(left + prev_rowdata[i])/2
rowdata[i] = (rowdata[i] + int(floor)) % 256
elif filterByte == 4:
for i in range(1, rowlength):
left = rowdata[i - 1] if i > 1 else 0
up = prev_rowdata[i]
up_left = prev_rowdata[i - 1] if i > 1 else 0
paeth = paethPredictor(left, up, up_left)
rowdata[i] = (rowdata[i] + paeth) % 256

else:
# unsupported PNG filter
raise PdfReadError("Unsupported PNG filter %r" % filterByte)

prev_rowdata = rowdata
output.write(''.join([chr(x) for x in rowdata[1:]]))
output.write(bytearray(rowdata[1:]))
data = output.getvalue()
else:
# unsupported predictor
Expand Down