Skip to content

Commit 116ba19

Browse files
committed
Respect the 'ColorTransform' entry in the image dictionary when decoding JPEG images (bug 956965, issue 6574)
Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=956965. Fixes 6574.
1 parent 6c263c1 commit 116ba19

File tree

6 files changed

+56
-15
lines changed

6 files changed

+56
-15
lines changed

src/core/evaluator.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,26 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
146146
*/
147147
NativeImageDecoder.isSupported =
148148
function NativeImageDecoder_isSupported(image, xref, res) {
149-
var cs = ColorSpace.parse(image.dict.get('ColorSpace', 'CS'), xref, res);
149+
var dict = image.dict;
150+
if (dict.has('DecodeParms') || dict.has('DP')) {
151+
return false;
152+
}
153+
var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
150154
return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') &&
151-
cs.isDefaultDecode(image.dict.getArray('Decode', 'D'));
155+
cs.isDefaultDecode(dict.getArray('Decode', 'D'));
152156
};
153157
/**
154158
* Checks if the image can be decoded by the browser.
155159
*/
156160
NativeImageDecoder.isDecodable =
157161
function NativeImageDecoder_isDecodable(image, xref, res) {
158-
var cs = ColorSpace.parse(image.dict.get('ColorSpace', 'CS'), xref, res);
162+
var dict = image.dict;
163+
if (dict.has('DecodeParms') || dict.has('DP')) {
164+
return false;
165+
}
166+
var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
159167
return (cs.numComps === 1 || cs.numComps === 3) &&
160-
cs.isDefaultDecode(image.dict.getArray('Decode', 'D'));
168+
cs.isDefaultDecode(dict.getArray('Decode', 'D'));
161169
};
162170

163171
function PartialEvaluator(pdfManager, xref, handler, pageIndex,

src/core/jpg.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var error = sharedUtil.error;
4040
* (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)
4141
*/
4242

43-
var JpegImage = (function jpegImage() {
43+
var JpegImage = (function JpegImageClosure() {
4444
var dctZigZag = new Uint8Array([
4545
0,
4646
1, 8,
@@ -68,7 +68,9 @@ var JpegImage = (function jpegImage() {
6868
var dctSqrt2 = 5793; // sqrt(2)
6969
var dctSqrt1d2 = 2896; // sqrt(2) / 2
7070

71-
function constructor() {
71+
function JpegImage() {
72+
this.decodeTransform = null;
73+
this.colorTransform = -1;
7274
}
7375

7476
function buildHuffmanTable(codeLengths, values) {
@@ -585,7 +587,7 @@ var JpegImage = (function jpegImage() {
585587
return a <= 0 ? 0 : a >= 255 ? 255 : a;
586588
}
587589

588-
constructor.prototype = {
590+
JpegImage.prototype = {
589591
parse: function parse(data) {
590592

591593
function readUint16() {
@@ -902,8 +904,20 @@ var JpegImage = (function jpegImage() {
902904
// The adobe transform marker overrides any previous setting
903905
return true;
904906
} else if (this.numComponents === 3) {
907+
if (!this.adobe && this.colorTransform === 0) {
908+
// If the Adobe transform marker is not present and the image
909+
// dictionary has a 'ColorTransform' entry, explicitly set to `0`,
910+
// then the colours should *not* be transformed.
911+
return false;
912+
}
905913
return true;
906-
} else {
914+
} else { // `this.numComponents !== 3`
915+
if (!this.adobe && this.colorTransform === 1) {
916+
// If the Adobe transform marker is not present and the image
917+
// dictionary has a 'ColorTransform' entry, explicitly set to `1`,
918+
// then the colours should be transformed.
919+
return true;
920+
}
907921
return false;
908922
}
909923
},
@@ -1045,7 +1059,7 @@ var JpegImage = (function jpegImage() {
10451059
rgbData[offset++] = grayColor;
10461060
}
10471061
return rgbData;
1048-
} else if (this.numComponents === 3) {
1062+
} else if (this.numComponents === 3 && this._isColorConversionNeeded()) {
10491063
return this._convertYccToRgb(data);
10501064
} else if (this.numComponents === 4) {
10511065
if (this._isColorConversionNeeded()) {
@@ -1062,7 +1076,7 @@ var JpegImage = (function jpegImage() {
10621076
}
10631077
};
10641078

1065-
return constructor;
1079+
return JpegImage;
10661080
})();
10671081

10681082
exports.JpegImage = JpegImage;

src/core/parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ var Parser = (function ParserClosure() {
596596
}
597597
if (name === 'DCTDecode' || name === 'DCT') {
598598
xrefStreamStats[StreamType.DCT] = true;
599-
return new JpegStream(stream, maybeLength, stream.dict, this.xref);
599+
return new JpegStream(stream, maybeLength, stream.dict);
600600
}
601601
if (name === 'JPXDecode' || name === 'JPX') {
602602
xrefStreamStats[StreamType.JPX] = true;

src/core/stream.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
var Util = sharedUtil.Util;
3535
var error = sharedUtil.error;
3636
var info = sharedUtil.info;
37+
var isInt = sharedUtil.isInt;
3738
var isArray = sharedUtil.isArray;
3839
var createObjectURL = sharedUtil.createObjectURL;
3940
var shadow = sharedUtil.shadow;
@@ -891,7 +892,7 @@ var PredictorStream = (function PredictorStreamClosure() {
891892
* DecodeStreams.
892893
*/
893894
var JpegStream = (function JpegStreamClosure() {
894-
function JpegStream(stream, maybeLength, dict, xref) {
895+
function JpegStream(stream, maybeLength, dict) {
895896
// Some images may contain 'junk' before the SOI (start-of-image) marker.
896897
// Note: this seems to mainly affect inline images.
897898
var ch;
@@ -925,8 +926,8 @@ var JpegStream = (function JpegStreamClosure() {
925926
var jpegImage = new JpegImage();
926927

927928
// Checking if values need to be transformed before conversion.
928-
if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) {
929-
var decodeArr = this.dict.getArray('Decode');
929+
var decodeArr = this.dict.getArray('Decode', 'D');
930+
if (this.forceRGB && isArray(decodeArr)) {
930931
var bitsPerComponent = this.dict.get('BitsPerComponent') || 8;
931932
var decodeArrLength = decodeArr.length;
932933
var transform = new Int32Array(decodeArrLength);
@@ -943,6 +944,14 @@ var JpegStream = (function JpegStreamClosure() {
943944
jpegImage.decodeTransform = transform;
944945
}
945946
}
947+
// Fetching the 'ColorTransform' entry, if it exists.
948+
var decodeParams = this.dict.get('DecodeParms', 'DP');
949+
if (isDict(decodeParams)) {
950+
var colorTransform = decodeParams.get('ColorTransform');
951+
if (isInt(colorTransform)) {
952+
jpegImage.colorTransform = colorTransform;
953+
}
954+
}
946955

947956
jpegImage.parse(this.bytes);
948957
var data = jpegImage.getData(this.drawWidth, this.drawHeight,
@@ -1064,7 +1073,7 @@ var Jbig2Stream = (function Jbig2StreamClosure() {
10641073
var jbig2Image = new Jbig2Image();
10651074

10661075
var chunks = [];
1067-
var decodeParams = this.dict.getArray('DecodeParms');
1076+
var decodeParams = this.dict.getArray('DecodeParms', 'DP');
10681077

10691078
// According to the PDF specification, DecodeParms can be either
10701079
// a dictionary, or an array whose elements are dictionaries.

test/pdfs/bug956965.pdf.link

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
http://web.archive.org/web/20160414174617/http://cache.lego.com/bigdownloads/buildinginstructions/6030672.pdf

test/test_manifest.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,15 @@
11561156
"lastPage": 1,
11571157
"type": "eq"
11581158
},
1159+
{ "id": "bug956965",
1160+
"file": "pdfs/bug956965.pdf",
1161+
"md5": "9b2f1176c797ee84e989a507e745f89d",
1162+
"rounds": 1,
1163+
"link": true,
1164+
"firstPage": 33,
1165+
"lastPage": 33,
1166+
"type": "eq"
1167+
},
11591168
{ "id": "smaskdim",
11601169
"file": "pdfs/smaskdim.pdf",
11611170
"md5": "de80aeca7cbf79940189fd34d59671ee",

0 commit comments

Comments
 (0)