From a3a55be9cdebd517f4dce9ff66aeda78b136b003 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 24 Dec 2017 19:18:52 -0500 Subject: Correct errors in PNG filters and make use from library --- libqpdf/Pl_PNGFilter.cc | 89 ++++++++++++++++++++++++++++---------------- libqpdf/QPDF.cc | 4 +- libqpdf/QPDFWriter.cc | 2 +- libqpdf/QPDF_Stream.cc | 64 ++++++++++++++++++++++--------- libqpdf/qpdf/Pl_PNGFilter.hh | 7 ++-- libqpdf/qpdf/QPDF_Stream.hh | 8 +++- 6 files changed, 116 insertions(+), 58 deletions(-) (limited to 'libqpdf') diff --git a/libqpdf/Pl_PNGFilter.cc b/libqpdf/Pl_PNGFilter.cc index edcc34cb..5504112e 100644 --- a/libqpdf/Pl_PNGFilter.cc +++ b/libqpdf/Pl_PNGFilter.cc @@ -2,32 +2,54 @@ #include #include #include -#include 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(); } diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 88117222..33847a45 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -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 bp = xref_obj.getStreamData(); + PointerHolder 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 offsets; - PointerHolder bp = obj_stream.getStreamData(); + PointerHolder bp = obj_stream.getStreamData(qpdf_dl_specialized); PointerHolder input = new BufferInputSource( "object stream " + QUtil::int_to_string(obj_stream_number), bp.getPointer()); diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index d4e13310..63d8c07c 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -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) diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index cf26f001..62d6a553 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -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 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& 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& 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& 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 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); diff --git a/libqpdf/qpdf/Pl_PNGFilter.hh b/libqpdf/qpdf/Pl_PNGFilter.hh index 504988eb..b5fcf536 100644 --- a/libqpdf/qpdf/Pl_PNGFilter.hh +++ b/libqpdf/qpdf/Pl_PNGFilter.hh @@ -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; }; diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index 50771627..5350fc0d 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -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& 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; -- cgit v1.2.3-70-g09d2