diff --git a/src/core/colorspace.js b/src/core/colorspace.js index 081d489db..7b60a0348 100644 --- a/src/core/colorspace.js +++ b/src/core/colorspace.js @@ -492,7 +492,7 @@ class AlternateCS extends ColorSpace { alpha01); } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -565,8 +565,19 @@ class IndexedCS extends ColorSpace { return this.base.getOutputLength(inputLength * this.base.numComps, alpha01); } - isDefaultDecode(decodeMap) { - return true; // Indexed color maps shouldn't be changed. + isDefaultDecode(decodeMap, bpc) { + if (!Array.isArray(decodeMap)) { + return true; + } + if (decodeMap.length !== 2) { + warn('Decode map length is not correct'); + return true; + } + if (!Number.isInteger(bpc) || bpc < 1) { + warn('Bits per component is not correct'); + return true; + } + return decodeMap[0] === 0 && decodeMap[1] === (1 << bpc) - 1; } } @@ -609,7 +620,7 @@ class DeviceGrayCS extends ColorSpace { return inputLength * (3 + alpha01); } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -661,7 +672,7 @@ class DeviceRgbCS extends ColorSpace { return bits === 8; } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -744,7 +755,7 @@ const DeviceCmykCS = (function DeviceCmykCSClosure() { return (inputLength / 4 * (3 + alpha01)) | 0; } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -847,7 +858,7 @@ const CalGrayCS = (function CalGrayCSClosure() { return inputLength * (3 + alpha01); } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -1129,7 +1140,7 @@ const CalRGBCS = (function CalRGBCSClosure() { return (inputLength * (3 + alpha01) / 3) | 0; } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); } } @@ -1280,7 +1291,7 @@ const LabCS = (function LabCSClosure() { return (inputLength * (3 + alpha01) / 3) | 0; } - isDefaultDecode(decodeMap) { + isDefaultDecode(decodeMap, bpc) { // XXX: Decoding is handled with the lab conversion because of the strange // ranges that are used. return true; diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 24942512d..7599c1c40 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -100,6 +100,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res, pdfFunctionFactory); + // isDefaultDecode() of DeviceGray and DeviceRGB needs no `bpc` argument. return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') && cs.isDefaultDecode(dict.getArray('Decode', 'D')); }; @@ -114,8 +115,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res, pdfFunctionFactory); + const bpc = dict.get('BitsPerComponent', 'BPC') || 1; return (cs.numComps === 1 || cs.numComps === 3) && - cs.isDefaultDecode(dict.getArray('Decode', 'D')); + cs.isDefaultDecode(dict.getArray('Decode', 'D'), bpc); }; function PartialEvaluator({ pdfManager, xref, handler, pageIndex, idFactory, diff --git a/src/core/image.js b/src/core/image.js index c402a5208..b9029235a 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -169,18 +169,21 @@ var PDFImage = (function PDFImageClosure() { this.decode = dict.getArray('Decode', 'D'); this.needsDecode = false; if (this.decode && - ((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) || + ((this.colorSpace && + !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent)) || (isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) { this.needsDecode = true; // Do some preprocessing to avoid more math. var max = (1 << bitsPerComponent) - 1; this.decodeCoefficients = []; this.decodeAddends = []; + const isIndexed = this.colorSpace && this.colorSpace.name === 'Indexed'; for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) { var dmin = this.decode[i]; var dmax = this.decode[i + 1]; - this.decodeCoefficients[j] = dmax - dmin; - this.decodeAddends[j] = max * dmin; + this.decodeCoefficients[j] = isIndexed ? ((dmax - dmin) / max) : + (dmax - dmin); + this.decodeAddends[j] = isIndexed ? dmin : (max * dmin); } } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index ad6cec7f2..a6bbdc3b5 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -251,6 +251,7 @@ !bug878026.pdf !issue1045.pdf !issue5010.pdf +!issue10339_reduced.pdf !issue4934.pdf !issue4650.pdf !issue6721_reduced.pdf diff --git a/test/pdfs/issue10339_reduced.pdf b/test/pdfs/issue10339_reduced.pdf new file mode 100644 index 000000000..438779f5a Binary files /dev/null and b/test/pdfs/issue10339_reduced.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index 60c981d2b..f9bb996cc 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -2643,6 +2643,12 @@ "link": false, "type": "eq" }, + { "id": "issue10339", + "file": "pdfs/issue10339_reduced.pdf", + "md5": "e34ef74f188080f8194c7d8e8b68c562", + "rounds": 1, + "type": "eq" + }, { "id": "issue1721", "file": "pdfs/issue1721.pdf", "md5": "b47177f9e5197a76ec498733ecab60e6", diff --git a/test/unit/colorspace_spec.js b/test/unit/colorspace_spec.js index c3c6f9488..43845430b 100644 --- a/test/unit/colorspace_spec.js +++ b/test/unit/colorspace_spec.js @@ -496,7 +496,7 @@ describe('colorspace', function () { expect(colorSpace.getRgb([2], 0)).toEqual( new Uint8ClampedArray([255, 109, 70])); expect(colorSpace.isPassthrough(8)).toBeFalsy(); - expect(colorSpace.isDefaultDecode([0, 1])).toBeTruthy(); + expect(colorSpace.isDefaultDecode([0, 1], 1)).toBeTruthy(); expect(testDest).toEqual(expectedDest); }); });