Commit a3a55be9cdebd517f4dce9ff66aeda78b136b003

Authored by Jay Berkenbilt
1 parent 9a487202

Correct errors in PNG filters and make use from library

libqpdf/Pl_PNGFilter.cc
... ... @@ -2,32 +2,54 @@
2 2 #include <stdexcept>
3 3 #include <string.h>
4 4 #include <limits.h>
5   -#include <math.h>
6 5  
7 6 Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next,
8 7 action_e action, unsigned int columns,
9   - unsigned int bytes_per_pixel) :
  8 + unsigned int samples_per_pixel,
  9 + unsigned int bits_per_sample) :
10 10 Pipeline(identifier, next),
11 11 action(action),
12   - columns(columns),
13 12 cur_row(0),
14 13 prev_row(0),
15 14 buf1(0),
16 15 buf2(0),
17   - bytes_per_pixel(bytes_per_pixel),
18 16 pos(0)
19 17 {
20   - if ((columns == 0) || (columns > UINT_MAX - 1))
  18 + if ((samples_per_pixel < 1) || (samples_per_pixel > 4))
  19 + {
  20 + throw std::runtime_error(
  21 + "PNGFilter created with invalid samples_per_pixel not from 1 to 4");
  22 + }
  23 + if (! ((bits_per_sample == 1) ||
  24 + (bits_per_sample == 2) ||
  25 + (bits_per_sample == 4) ||
  26 + (bits_per_sample == 8) ||
  27 + (bits_per_sample == 16)))
  28 + {
  29 + throw std::runtime_error(
  30 + "PNGFilter created with invalid bits_per_sample not"
  31 + " 1, 2, 4, 8, or 16");
  32 + }
  33 + this->bytes_per_pixel = ((bits_per_sample * samples_per_pixel) + 7) / 8;
  34 + unsigned long long bpr =
  35 + ((columns * bits_per_sample * samples_per_pixel) + 7) / 8;
  36 + if ((bpr == 0) || (bpr > (UINT_MAX - 1)))
21 37 {
22 38 throw std::runtime_error(
23 39 "PNGFilter created with invalid columns value");
24 40 }
25   - this->buf1 = new unsigned char[columns + 1];
26   - this->buf2 = new unsigned char[columns + 1];
27   - this->cur_row = buf1;
  41 + this->bytes_per_row = bpr & UINT_MAX;
  42 + this->buf1 = new unsigned char[this->bytes_per_row + 1];
  43 + this->buf2 = new unsigned char[this->bytes_per_row + 1];
  44 + memset(this->buf1, 0, this->bytes_per_row + 1);
  45 + memset(this->buf2, 0, this->bytes_per_row + 1);
  46 + this->cur_row = this->buf1;
  47 + this->prev_row = this->buf2;
28 48  
29 49 // number of bytes per incoming row
30   - this->incoming = (action == a_encode ? columns : columns + 1);
  50 + this->incoming = (action == a_encode ?
  51 + this->bytes_per_row :
  52 + this->bytes_per_row + 1);
31 53 }
32 54  
33 55 Pl_PNGFilter::~Pl_PNGFilter()
... ... @@ -54,7 +76,7 @@ Pl_PNGFilter::write(unsigned char* data, size_t len)
54 76 unsigned char* t = this->prev_row;
55 77 this->prev_row = this->cur_row;
56 78 this->cur_row = t ? t : this->buf2;
57   - memset(this->cur_row, 0, this->columns + 1);
  79 + memset(this->cur_row, 0, this->bytes_per_row + 1);
58 80 left = this->incoming;
59 81 this->pos = 0;
60 82 }
... ... @@ -106,16 +128,16 @@ Pl_PNGFilter::decodeRow()
106 128 }
107 129 }
108 130  
109   - getNext()->write(this->cur_row + 1, this->columns);
  131 + getNext()->write(this->cur_row + 1, this->bytes_per_row);
110 132 }
111 133  
112 134 void
113 135 Pl_PNGFilter::decodeSub()
114 136 {
115 137 unsigned char* buffer = this->cur_row + 1;
116   - unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  138 + unsigned int bpp = this->bytes_per_pixel;
117 139  
118   - for (unsigned int i = 0; i < this->columns; ++i)
  140 + for (unsigned int i = 0; i < this->bytes_per_row; ++i)
119 141 {
120 142 unsigned char left = 0;
121 143  
... ... @@ -134,7 +156,7 @@ Pl_PNGFilter::decodeUp()
134 156 unsigned char* buffer = this->cur_row + 1;
135 157 unsigned char* above_buffer = this->prev_row + 1;
136 158  
137   - for (unsigned int i = 0; i < this->columns; ++i)
  159 + for (unsigned int i = 0; i < this->bytes_per_row; ++i)
138 160 {
139 161 unsigned char up = above_buffer[i];
140 162 buffer[i] += up;
... ... @@ -144,13 +166,14 @@ Pl_PNGFilter::decodeUp()
144 166 void
145 167 Pl_PNGFilter::decodeAverage()
146 168 {
147   - unsigned char* buffer = this->cur_row+1;
148   - unsigned char* above_buffer = this->prev_row+1;
149   - unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  169 + unsigned char* buffer = this->cur_row + 1;
  170 + unsigned char* above_buffer = this->prev_row + 1;
  171 + unsigned int bpp = this->bytes_per_pixel;
150 172  
151   - for (unsigned int i = 0; i < this->columns; ++i)
  173 + for (unsigned int i = 0; i < this->bytes_per_row; ++i)
152 174 {
153   - int left = 0, up = 0;
  175 + int left = 0;
  176 + int up = 0;
154 177  
155 178 if (i >= bpp)
156 179 {
... ... @@ -158,22 +181,22 @@ Pl_PNGFilter::decodeAverage()
158 181 }
159 182  
160 183 up = above_buffer[i];
161   - buffer[i] += floor((left+up) / 2);
  184 + buffer[i] += (left+up) / 2;
162 185 }
163 186 }
164 187  
165 188 void
166 189 Pl_PNGFilter::decodePaeth()
167 190 {
168   - unsigned char* buffer = this->cur_row+1;
169   - unsigned char* above_buffer = this->prev_row+1;
170   - unsigned int bpp = this->bytes_per_pixel != 0 ? this->bytes_per_pixel : 1;
  191 + unsigned char* buffer = this->cur_row + 1;
  192 + unsigned char* above_buffer = this->prev_row + 1;
  193 + unsigned int bpp = this->bytes_per_pixel;
171 194  
172   - for (unsigned int i = 0; i < this->columns; ++i)
  195 + for (unsigned int i = 0; i < this->bytes_per_row; ++i)
173 196 {
174   - int left = 0,
175   - up = above_buffer[i],
176   - upper_left = 0;
  197 + int left = 0;
  198 + int up = above_buffer[i];
  199 + int upper_left = 0;
177 200  
178 201 if (i >= bpp)
179 202 {
... ... @@ -193,10 +216,12 @@ Pl_PNGFilter::PaethPredictor(int a, int b, int c)
193 216 int pb = std::abs(p - b);
194 217 int pc = std::abs(p - c);
195 218  
196   - if (pa <= pb && pa <= pc) {
  219 + if (pa <= pb && pa <= pc)
  220 + {
197 221 return a;
198 222 }
199   - if (pb <= pc) {
  223 + if (pb <= pc)
  224 + {
200 225 return b;
201 226 }
202 227 return c;
... ... @@ -210,7 +235,7 @@ Pl_PNGFilter::encodeRow()
210 235 getNext()->write(&ch, 1);
211 236 if (this->prev_row)
212 237 {
213   - for (unsigned int i = 0; i < this->columns; ++i)
  238 + for (unsigned int i = 0; i < this->bytes_per_row; ++i)
214 239 {
215 240 ch = this->cur_row[i] - this->prev_row[i];
216 241 getNext()->write(&ch, 1);
... ... @@ -218,7 +243,7 @@ Pl_PNGFilter::encodeRow()
218 243 }
219 244 else
220 245 {
221   - getNext()->write(this->cur_row, this->columns);
  246 + getNext()->write(this->cur_row, this->bytes_per_row);
222 247 }
223 248 }
224 249  
... ... @@ -233,7 +258,7 @@ Pl_PNGFilter::finish()
233 258 this->prev_row = 0;
234 259 this->cur_row = buf1;
235 260 this->pos = 0;
236   - memset(this->cur_row, 0, this->columns + 1);
  261 + memset(this->cur_row, 0, this->bytes_per_row + 1);
237 262  
238 263 getNext()->finish();
239 264 }
... ...
libqpdf/QPDF.cc
... ... @@ -1006,7 +1006,7 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1006 1006 // that this multiplication does not cause an overflow.
1007 1007 size_t expected_size = entry_size * num_entries;
1008 1008  
1009   - PointerHolder<Buffer> bp = xref_obj.getStreamData();
  1009 + PointerHolder<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized);
1010 1010 size_t actual_size = bp->getSize();
1011 1011  
1012 1012 if (expected_size != actual_size)
... ... @@ -1837,7 +1837,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1837 1837  
1838 1838 std::map<int, int> offsets;
1839 1839  
1840   - PointerHolder<Buffer> bp = obj_stream.getStreamData();
  1840 + PointerHolder<Buffer> bp = obj_stream.getStreamData(qpdf_dl_specialized);
1841 1841 PointerHolder<InputSource> input = new BufferInputSource(
1842 1842 "object stream " + QUtil::int_to_string(obj_stream_number),
1843 1843 bp.getPointer());
... ...
libqpdf/QPDFWriter.cc
... ... @@ -2684,7 +2684,7 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset,
2684 2684 }
2685 2685 p = pushPipeline(
2686 2686 new Pl_PNGFilter(
2687   - "pngify xref", p, Pl_PNGFilter::a_encode, esize, 0));
  2687 + "pngify xref", p, Pl_PNGFilter::a_encode, esize));
2688 2688 }
2689 2689 activatePipelineStack();
2690 2690 for (int i = first; i <= last; ++i)
... ...
libqpdf/QPDF_Stream.cc
... ... @@ -114,7 +114,9 @@ QPDF_Stream::getRawStreamData()
114 114 bool
115 115 QPDF_Stream::understandDecodeParams(
116 116 std::string const& filter, QPDFObjectHandle decode_obj,
117   - int& predictor, int& columns, bool& early_code_change)
  117 + int& predictor, int& columns,
  118 + int& colors, int& bits_per_component,
  119 + bool& early_code_change)
118 120 {
119 121 bool filterable = true;
120 122 std::set<std::string> keys = decode_obj.getKeys();
... ... @@ -122,13 +124,15 @@ QPDF_Stream::understandDecodeParams(
122 124 iter != keys.end(); ++iter)
123 125 {
124 126 std::string const& key = *iter;
125   - if ((filter == "/FlateDecode") && (key == "/Predictor"))
  127 + if (((filter == "/FlateDecode") || (filter == "/LZWDecode")) &&
  128 + (key == "/Predictor"))
126 129 {
127 130 QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
128 131 if (predictor_obj.isInteger())
129 132 {
130 133 predictor = predictor_obj.getIntValue();
131   - if (! ((predictor == 1) || (predictor == 12)))
  134 + if (! ((predictor == 1) ||
  135 + ((predictor >= 10) && (predictor <= 15))))
132 136 {
133 137 filterable = false;
134 138 }
... ... @@ -155,12 +159,26 @@ QPDF_Stream::understandDecodeParams(
155 159 filterable = false;
156 160 }
157 161 }
158   - else if (key == "/Columns")
  162 + else if ((key == "/Columns") ||
  163 + (key == "/Colors") ||
  164 + (key == "/BitsPerComponent"))
159 165 {
160   - QPDFObjectHandle columns_obj = decode_obj.getKey(key);
161   - if (columns_obj.isInteger())
  166 + QPDFObjectHandle param_obj = decode_obj.getKey(key);
  167 + if (param_obj.isInteger())
162 168 {
163   - columns = columns_obj.getIntValue();
  169 + int val = param_obj.getIntValue();
  170 + if (key == "/Columns")
  171 + {
  172 + columns = val;
  173 + }
  174 + else if (key == "/Colors")
  175 + {
  176 + colors = val;
  177 + }
  178 + else if (key == "/BitsPerComponent")
  179 + {
  180 + bits_per_component = val;
  181 + }
164 182 }
165 183 else
166 184 {
... ... @@ -190,6 +208,7 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
190 208 bool& specialized_compression,
191 209 bool& lossy_compression,
192 210 int& predictor, int& columns,
  211 + int& colors, int& bits_per_component,
193 212 bool& early_code_change)
194 213 {
195 214 if (filter_abbreviations.empty())
... ... @@ -295,6 +314,8 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
295 314 // Initialize values to their defaults as per the PDF spec
296 315 predictor = 1;
297 316 columns = 0;
  317 + colors = 1;
  318 + bits_per_component = 8;
298 319 early_code_change = true;
299 320  
300 321 // See if we can support any decode parameters that are specified.
... ... @@ -344,7 +365,8 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
344 365 {
345 366 if (! understandDecodeParams(
346 367 filters.at(i), decode_item,
347   - predictor, columns, early_code_change))
  368 + predictor, columns, colors, bits_per_component,
  369 + early_code_change))
348 370 {
349 371 filterable = false;
350 372 }
... ... @@ -378,6 +400,8 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
378 400 std::vector<std::string> filters;
379 401 int predictor = 1;
380 402 int columns = 0;
  403 + int colors = 1;
  404 + int bits_per_component = 8;
381 405 bool early_code_change = true;
382 406 bool specialized_compression = false;
383 407 bool lossy_compression = false;
... ... @@ -385,7 +409,9 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
385 409 if (filter)
386 410 {
387 411 filter = filterable(filters, specialized_compression, lossy_compression,
388   - predictor, columns, early_code_change);
  412 + predictor, columns,
  413 + colors, bits_per_component,
  414 + early_code_change);
389 415 if ((decode_level < qpdf_dl_all) && lossy_compression)
390 416 {
391 417 filter = false;
... ... @@ -430,21 +456,23 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline,
430 456 iter != filters.rend(); ++iter)
431 457 {
432 458 std::string const& filter = *iter;
  459 +
  460 + if (((filter == "/FlateDecode") || (filter == "/LZWDecode")) &&
  461 + ((predictor >= 10) && (predictor <= 15)))
  462 + {
  463 + QTC::TC("qpdf", "QPDF_Stream PNG filter");
  464 + pipeline = new Pl_PNGFilter(
  465 + "png decode", pipeline, Pl_PNGFilter::a_decode,
  466 + columns, colors, bits_per_component);
  467 + to_delete.push_back(pipeline);
  468 + }
  469 +
433 470 if (filter == "/Crypt")
434 471 {
435 472 // Ignore -- handled by pipeStreamData
436 473 }
437 474 else if (filter == "/FlateDecode")
438 475 {
439   - if (predictor == 12)
440   - {
441   - QTC::TC("qpdf", "QPDF_Stream PNG filter");
442   - pipeline = new Pl_PNGFilter(
443   - "png decode", pipeline, Pl_PNGFilter::a_decode,
444   - columns, 0 /* not used */);
445   - to_delete.push_back(pipeline);
446   - }
447   -
448 476 pipeline = new Pl_Flate("stream inflate",
449 477 pipeline, Pl_Flate::a_inflate);
450 478 to_delete.push_back(pipeline);
... ...
libqpdf/qpdf/Pl_PNGFilter.hh
... ... @@ -18,7 +18,8 @@ class Pl_PNGFilter: public Pipeline
18 18 QPDF_DLL
19 19 Pl_PNGFilter(char const* identifier, Pipeline* next,
20 20 action_e action, unsigned int columns,
21   - unsigned int bytes_per_pixel);
  21 + unsigned int samples_per_pixel = 1,
  22 + unsigned int bits_per_sample = 8);
22 23 QPDF_DLL
23 24 virtual ~Pl_PNGFilter();
24 25  
... ... @@ -38,12 +39,12 @@ class Pl_PNGFilter: public Pipeline
38 39 int PaethPredictor(int a, int b, int c);
39 40  
40 41 action_e action;
41   - unsigned int columns;
  42 + unsigned int bytes_per_row;
  43 + unsigned int bytes_per_pixel;
42 44 unsigned char* cur_row;
43 45 unsigned char* prev_row;
44 46 unsigned char* buf1;
45 47 unsigned char* buf2;
46   - unsigned int bytes_per_pixel;
47 48 size_t pos;
48 49 size_t incoming;
49 50 };
... ...
libqpdf/qpdf/QPDF_Stream.hh
... ... @@ -54,10 +54,14 @@ class QPDF_Stream: public QPDFObject
54 54 size_t length);
55 55 bool understandDecodeParams(
56 56 std::string const& filter, QPDFObjectHandle decode_params,
57   - int& predictor, int& columns, bool& early_code_change);
  57 + int& predictor, int& columns,
  58 + int& colors, int& bits_per_component,
  59 + bool& early_code_change);
58 60 bool filterable(std::vector<std::string>& filters,
59 61 bool& specialized_compression, bool& lossy_compression,
60   - int& predictor, int& columns, bool& early_code_change);
  62 + int& predictor, int& columns,
  63 + int& colors, int& bits_per_component,
  64 + bool& early_code_change);
61 65 void warn(QPDFExc const& e);
62 66  
63 67 QPDF* qpdf;
... ...
libtests/png_filter.cc
... ... @@ -17,7 +17,7 @@ void run(char const* filename, bool encode, unsigned int columns)
17 17 Pipeline* pl = new Pl_PNGFilter(
18 18 "png", out,
19 19 encode ? Pl_PNGFilter::a_encode : Pl_PNGFilter::a_decode,
20   - columns, 0 /* not used */);
  20 + columns);
21 21 assert((2 * (columns + 1)) < 1024);
22 22 unsigned char buf[1024];
23 23 size_t len;
... ...