mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Correct errors in PNG filters and make use from library
This commit is contained in:
parent
9a48720246
commit
a3a55be9cd
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user