2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 19:08:59 +00:00

Correct errors in PNG filters and make use from library

This commit is contained in:
Jay Berkenbilt 2017-12-24 19:18:52 -05:00
parent 9a48720246
commit a3a55be9cd
7 changed files with 117 additions and 59 deletions

View File

@ -2,32 +2,54 @@
#include <stdexcept>
#include <string.h>
#include <limits.h>
#include <math.h>
Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next,
action_e action, unsigned int columns,
unsigned int bytes_per_pixel) :
unsigned int samples_per_pixel,
unsigned int bits_per_sample) :
Pipeline(identifier, next),
action(action),
columns(columns),
cur_row(0),
prev_row(0),
buf1(0),
buf2(0),
bytes_per_pixel(bytes_per_pixel),
pos(0)
{
if ((columns == 0) || (columns > UINT_MAX - 1))
if ((samples_per_pixel < 1) || (samples_per_pixel > 4))
{
throw std::runtime_error(
"PNGFilter created with invalid samples_per_pixel not from 1 to 4");
}
if (! ((bits_per_sample == 1) ||
(bits_per_sample == 2) ||
(bits_per_sample == 4) ||
(bits_per_sample == 8) ||
(bits_per_sample == 16)))
{
throw std::runtime_error(
"PNGFilter created with invalid bits_per_sample not"
" 1, 2, 4, 8, or 16");
}
this->bytes_per_pixel = ((bits_per_sample * samples_per_pixel) + 7) / 8;
unsigned long long bpr =
((columns * bits_per_sample * samples_per_pixel) + 7) / 8;
if ((bpr == 0) || (bpr > (UINT_MAX - 1)))
{
throw std::runtime_error(
"PNGFilter created with invalid columns value");
}
this->buf1 = new unsigned char[columns + 1];
this->buf2 = new unsigned char[columns + 1];
this->cur_row = buf1;
this->bytes_per_row = bpr & UINT_MAX;
this->buf1 = new unsigned char[this->bytes_per_row + 1];
this->buf2 = new unsigned char[this->bytes_per_row + 1];
memset(this->buf1, 0, this->bytes_per_row + 1);
memset(this->buf2, 0, this->bytes_per_row + 1);
this->cur_row = this->buf1;
this->prev_row = this->buf2;
// number of bytes per incoming row
this->incoming = (action == a_encode ? columns : columns + 1);
this->incoming = (action == a_encode ?
this->bytes_per_row :
this->bytes_per_row + 1);
}
Pl_PNGFilter::~Pl_PNGFilter()
@ -54,7 +76,7 @@ Pl_PNGFilter::write(unsigned char* data, size_t len)
unsigned char* t = this->prev_row;
this->prev_row = this->cur_row;
this->cur_row = t ? t : this->buf2;
memset(this->cur_row, 0, this->columns + 1);
memset(this->cur_row, 0, this->bytes_per_row + 1);
left = this->incoming;
this->pos = 0;
}
@ -106,16 +128,16 @@ Pl_PNGFilter::decodeRow()
}
}
getNext()->write(this->cur_row + 1, this->columns);
getNext()->write(this->cur_row + 1, this->bytes_per_row);
}
void
Pl_PNGFilter::decodeSub()
{
unsigned char* buffer = this->cur_row + 1;
unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
unsigned int bpp = this->bytes_per_pixel;
for (unsigned int i = 0; i < this->columns; ++i)
for (unsigned int i = 0; i < this->bytes_per_row; ++i)
{
unsigned char left = 0;
@ -134,7 +156,7 @@ Pl_PNGFilter::decodeUp()
unsigned char* buffer = this->cur_row + 1;
unsigned char* above_buffer = this->prev_row + 1;
for (unsigned int i = 0; i < this->columns; ++i)
for (unsigned int i = 0; i < this->bytes_per_row; ++i)
{
unsigned char up = above_buffer[i];
buffer[i] += up;
@ -144,13 +166,14 @@ Pl_PNGFilter::decodeUp()
void
Pl_PNGFilter::decodeAverage()
{
unsigned char* buffer = this->cur_row+1;
unsigned char* above_buffer = this->prev_row+1;
unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
unsigned char* buffer = this->cur_row + 1;
unsigned char* above_buffer = this->prev_row + 1;
unsigned int bpp = this->bytes_per_pixel;
for (unsigned int i = 0; i < this->columns; ++i)
for (unsigned int i = 0; i < this->bytes_per_row; ++i)
{
int left = 0, up = 0;
int left = 0;
int up = 0;
if (i >= bpp)
{
@ -158,22 +181,22 @@ Pl_PNGFilter::decodeAverage()
}
up = above_buffer[i];
buffer[i] += floor((left+up) / 2);
buffer[i] += (left+up) / 2;
}
}
void
Pl_PNGFilter::decodePaeth()
{
unsigned char* buffer = this->cur_row+1;
unsigned char* above_buffer = this->prev_row+1;
unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
unsigned char* buffer = this->cur_row + 1;
unsigned char* above_buffer = this->prev_row + 1;
unsigned int bpp = this->bytes_per_pixel;
for (unsigned int i = 0; i < this->columns; ++i)
for (unsigned int i = 0; i < this->bytes_per_row; ++i)
{
int left = 0,
up = above_buffer[i],
upper_left = 0;
int left = 0;
int up = above_buffer[i];
int upper_left = 0;
if (i >= bpp)
{
@ -193,10 +216,12 @@ Pl_PNGFilter::PaethPredictor(int a, int b, int c)
int pb = std::abs(p - b);
int pc = std::abs(p - c);
if (pa <= pb && pa <= pc) {
if (pa <= pb && pa <= pc)
{
return a;
}
if (pb <= pc) {
if (pb <= pc)
{
return b;
}
return c;
@ -210,7 +235,7 @@ Pl_PNGFilter::encodeRow()
getNext()->write(&ch, 1);
if (this->prev_row)
{
for (unsigned int i = 0; i < this->columns; ++i)
for (unsigned int i = 0; i < this->bytes_per_row; ++i)
{
ch = this->cur_row[i] - this->prev_row[i];
getNext()->write(&ch, 1);
@ -218,7 +243,7 @@ Pl_PNGFilter::encodeRow()
}
else
{
getNext()->write(this->cur_row, this->columns);
getNext()->write(this->cur_row, this->bytes_per_row);
}
}
@ -233,7 +258,7 @@ Pl_PNGFilter::finish()
this->prev_row = 0;
this->cur_row = buf1;
this->pos = 0;
memset(this->cur_row, 0, this->columns + 1);
memset(this->cur_row, 0, this->bytes_per_row + 1);
getNext()->finish();
}

View File

@ -1006,7 +1006,7 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
// that this multiplication does not cause an overflow.
size_t expected_size = entry_size * num_entries;
PointerHolder<Buffer> bp = xref_obj.getStreamData();
PointerHolder<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized);
size_t actual_size = bp->getSize();
if (expected_size != actual_size)
@ -1837,7 +1837,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
std::map<int, int> offsets;
PointerHolder<Buffer> bp = obj_stream.getStreamData();
PointerHolder<Buffer> bp = obj_stream.getStreamData(qpdf_dl_specialized);
PointerHolder<InputSource> input = new BufferInputSource(
"object stream " + QUtil::int_to_string(obj_stream_number),
bp.getPointer());

View File

@ -2684,7 +2684,7 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset,
}
p = pushPipeline(
new Pl_PNGFilter(
"pngify xref", p, Pl_PNGFilter::a_encode, esize, 0));
"pngify xref", p, Pl_PNGFilter::a_encode, esize));
}
activatePipelineStack();
for (int i = first; i <= last; ++i)

View File

@ -114,7 +114,9 @@ QPDF_Stream::getRawStreamData()
bool
QPDF_Stream::understandDecodeParams(
std::string const& filter, QPDFObjectHandle decode_obj,
int& predictor, int& columns, bool& early_code_change)
int& predictor, int& columns,
int& colors, int& bits_per_component,
bool& early_code_change)
{
bool filterable = true;
std::set<std::string> keys = decode_obj.getKeys();
@ -122,13 +124,15 @@ QPDF_Stream::understandDecodeParams(
iter != keys.end(); ++iter)
{
std::string const& key = *iter;
if ((filter == "/FlateDecode") && (key == "/Predictor"))
if (((filter == "/FlateDecode") || (filter == "/LZWDecode")) &&
(key == "/Predictor"))
{
QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
if (predictor_obj.isInteger())
{
predictor = predictor_obj.getIntValue();
if (! ((predictor == 1) || (predictor == 12)))
if (! ((predictor == 1) ||
((predictor >= 10) && (predictor <= 15))))
{
filterable = false;
}
@ -155,12 +159,26 @@ QPDF_Stream::understandDecodeParams(
filterable = false;
}
}
else if (key == "/Columns")
else if ((key == "/Columns") ||
(key == "/Colors") ||
(key == "/BitsPerComponent"))
{
QPDFObjectHandle columns_obj = decode_obj.getKey(key);
if (columns_obj.isInteger())
QPDFObjectHandle param_obj = decode_obj.getKey(key);
if (param_obj.isInteger())
{
columns = columns_obj.getIntValue();
int val = param_obj.getIntValue();
if (key == "/Columns")
{
columns = val;
}
else if (key == "/Colors")
{
colors = val;
}
else if (key == "/BitsPerComponent")
{
bits_per_component = val;
}
}
else
{
@ -190,6 +208,7 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
bool& specialized_compression,
bool& lossy_compression,
int& predictor, int& columns,
int& colors, int& bits_per_component,
bool& early_code_change)
{
if (filter_abbreviations.empty())
@ -295,6 +314,8 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
// Initialize values to their defaults as per the PDF spec
predictor = 1;
columns = 0;
colors = 1;
bits_per_component = 8;
early_code_change = true;
// See if we can support any decode parameters that are specified.
@ -344,7 +365,8 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
{
if (! understandDecodeParams(
filters.at(i), decode_item,
predictor, columns, early_code_change))
predictor, columns, colors, bits_per_component,
early_code_change))
{
filterable = false;
}
@ -378,6 +400,8 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
std::vector<std::string> filters;
int predictor = 1;
int columns = 0;
int colors = 1;
int bits_per_component = 8;
bool early_code_change = true;
bool specialized_compression = false;
bool lossy_compression = false;
@ -385,7 +409,9 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
if (filter)
{
filter = filterable(filters, specialized_compression, lossy_compression,
predictor, columns, early_code_change);
predictor, columns,
colors, bits_per_component,
early_code_change);
if ((decode_level < qpdf_dl_all) && lossy_compression)
{
filter = false;
@ -430,21 +456,23 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
iter != filters.rend(); ++iter)
{
std::string const& filter = *iter;
if (((filter == "/FlateDecode") || (filter == "/LZWDecode")) &&
((predictor >= 10) && (predictor <= 15)))
{
QTC::TC("qpdf", "QPDF_Stream PNG filter");
pipeline = new Pl_PNGFilter(
"png decode", pipeline, Pl_PNGFilter::a_decode,
columns, colors, bits_per_component);
to_delete.push_back(pipeline);
}
if (filter == "/Crypt")
{
// Ignore -- handled by pipeStreamData
}
else if (filter == "/FlateDecode")
{
if (predictor == 12)
{
QTC::TC("qpdf", "QPDF_Stream PNG filter");
pipeline = new Pl_PNGFilter(
"png decode", pipeline, Pl_PNGFilter::a_decode,
columns, 0 /* not used */);
to_delete.push_back(pipeline);
}
pipeline = new Pl_Flate("stream inflate",
pipeline, Pl_Flate::a_inflate);
to_delete.push_back(pipeline);

View File

@ -18,7 +18,8 @@ class Pl_PNGFilter: public Pipeline
QPDF_DLL
Pl_PNGFilter(char const* identifier, Pipeline* next,
action_e action, unsigned int columns,
unsigned int bytes_per_pixel);
unsigned int samples_per_pixel = 1,
unsigned int bits_per_sample = 8);
QPDF_DLL
virtual ~Pl_PNGFilter();
@ -38,12 +39,12 @@ class Pl_PNGFilter: public Pipeline
int PaethPredictor(int a, int b, int c);
action_e action;
unsigned int columns;
unsigned int bytes_per_row;
unsigned int bytes_per_pixel;
unsigned char* cur_row;
unsigned char* prev_row;
unsigned char* buf1;
unsigned char* buf2;
unsigned int bytes_per_pixel;
size_t pos;
size_t incoming;
};

View File

@ -54,10 +54,14 @@ class QPDF_Stream: public QPDFObject
size_t length);
bool understandDecodeParams(
std::string const& filter, QPDFObjectHandle decode_params,
int& predictor, int& columns, bool& early_code_change);
int& predictor, int& columns,
int& colors, int& bits_per_component,
bool& early_code_change);
bool filterable(std::vector<std::string>& filters,
bool& specialized_compression, bool& lossy_compression,
int& predictor, int& columns, bool& early_code_change);
int& predictor, int& columns,
int& colors, int& bits_per_component,
bool& early_code_change);
void warn(QPDFExc const& e);
QPDF* qpdf;

View File

@ -17,7 +17,7 @@ void run(char const* filename, bool encode, unsigned int columns)
Pipeline* pl = new Pl_PNGFilter(
"png", out,
encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode,
columns, 0 /* not used */);
columns);
assert((2 * (columns + 1)) < 1024);
unsigned char buf[1024];
size_t len;