Skip to content

Commit ea1c348

Browse files
committed
Always prefer abbreviated keys, over full ones, when doing any dictionary lookups (issue 14256)
Note that issue 14256 was specifically about *inline* images, please refer to: - https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf#G7.1852045 - https://www.pdfa.org/safedocs-unearths-pdf-inline-image-issue/ - https://pdf-issues.pdfa.org/32000-2-2020/clause08.html#H8.9.7 However, during review of the initial PR in #14257 (comment), it was suggested that we instead do this *unconditionally for all* dictionary lookups. In addition to re-ordering the existing call-sites in the `src/core`-code, and adding non-PRODUCTION/TESTING asserts to catch future errors, for consistency a number of existing `if`/`switch`-blocks were re-factored to also check the abbreviated keys first.
1 parent 4ee906a commit ea1c348

12 files changed

+154
-104
lines changed

src/core/colorspace.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,14 @@ class ColorSpace {
379379
cs = xref.fetchIfRef(cs);
380380
if (isName(cs)) {
381381
switch (cs.name) {
382-
case "DeviceGray":
383382
case "G":
383+
case "DeviceGray":
384384
return this.singletons.gray;
385-
case "DeviceRGB":
386385
case "RGB":
386+
case "DeviceRGB":
387387
return this.singletons.rgb;
388-
case "DeviceCMYK":
389388
case "CMYK":
389+
case "DeviceCMYK":
390390
return this.singletons.cmyk;
391391
case "Pattern":
392392
return new PatternCS(/* baseCS = */ null);
@@ -417,14 +417,14 @@ class ColorSpace {
417417
let params, numComps, baseCS, whitePoint, blackPoint, gamma;
418418

419419
switch (mode) {
420-
case "DeviceGray":
421420
case "G":
421+
case "DeviceGray":
422422
return this.singletons.gray;
423-
case "DeviceRGB":
424423
case "RGB":
424+
case "DeviceRGB":
425425
return this.singletons.rgb;
426-
case "DeviceCMYK":
427426
case "CMYK":
427+
case "DeviceCMYK":
428428
return this.singletons.cmyk;
429429
case "CalGray":
430430
params = xref.fetchIfRef(cs[1]);
@@ -467,8 +467,8 @@ class ColorSpace {
467467
baseCS = this._parse(baseCS, xref, resources, pdfFunctionFactory);
468468
}
469469
return new PatternCS(baseCS);
470-
case "Indexed":
471470
case "I":
471+
case "Indexed":
472472
baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory);
473473
const hiVal = xref.fetchIfRef(cs[2]) + 1;
474474
const lookup = xref.fetchIfRef(cs[3]);

src/core/evaluator.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,8 @@ class PartialEvaluator {
580580
}) {
581581
const dict = image.dict;
582582
const imageRef = dict.objId;
583-
const w = dict.get("Width", "W");
584-
const h = dict.get("Height", "H");
583+
const w = dict.get("W", "Width");
584+
const h = dict.get("H", "Height");
585585

586586
if (!(w && isNum(w)) || !(h && isNum(h))) {
587587
warn("Image dimensions are missing, or not numbers.");
@@ -604,29 +604,26 @@ class PartialEvaluator {
604604
operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
605605
}
606606

607-
const imageMask = dict.get("ImageMask", "IM") || false;
608-
const interpolate = dict.get("Interpolate", "I");
607+
const imageMask = dict.get("IM", "ImageMask") || false;
608+
const interpolate = dict.get("I", "Interpolate");
609609
let imgData, args;
610610
if (imageMask) {
611611
// This depends on a tmpCanvas being filled with the
612612
// current fillStyle, such that processing the pixel
613613
// data can't be done here. Instead of creating a
614614
// complete PDFImage, only read the information needed
615615
// for later.
616-
617-
const width = dict.get("Width", "W");
618-
const height = dict.get("Height", "H");
619-
const bitStrideLength = (width + 7) >> 3;
616+
const bitStrideLength = (w + 7) >> 3;
620617
const imgArray = image.getBytes(
621-
bitStrideLength * height,
618+
bitStrideLength * h,
622619
/* forceClamped = */ true
623620
);
624-
const decode = dict.getArray("Decode", "D");
621+
const decode = dict.getArray("D", "Decode");
625622

626623
imgData = PDFImage.createMask({
627624
imgArray,
628-
width,
629-
height,
625+
width: w,
626+
height: h,
630627
imageIsFromDecodeStream: image instanceof DecodeStream,
631628
inverseDecode: !!decode && decode[0] > 0,
632629
interpolate,
@@ -648,7 +645,7 @@ class PartialEvaluator {
648645
return;
649646
}
650647

651-
const softMask = dict.get("SMask", "SM") || false;
648+
const softMask = dict.get("SM", "SMask") || false;
652649
const mask = dict.get("Mask") || false;
653650

654651
const SMALL_IMAGE_DIMENSIONS = 200;

src/core/image.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class PDFImage {
9393
this.image = image;
9494
const dict = image.dict;
9595

96-
const filter = dict.get("Filter");
96+
const filter = dict.get("F", "Filter");
9797
if (isName(filter)) {
9898
switch (filter.name) {
9999
case "JPXDecode":
@@ -114,8 +114,8 @@ class PDFImage {
114114
}
115115
// TODO cache rendered images?
116116

117-
let width = dict.get("Width", "W");
118-
let height = dict.get("Height", "H");
117+
let width = dict.get("W", "Width");
118+
let height = dict.get("H", "Height");
119119

120120
if (
121121
Number.isInteger(image.width) &&
@@ -139,13 +139,13 @@ class PDFImage {
139139
this.width = width;
140140
this.height = height;
141141

142-
this.interpolate = dict.get("Interpolate", "I");
143-
this.imageMask = dict.get("ImageMask", "IM") || false;
142+
this.interpolate = dict.get("I", "Interpolate");
143+
this.imageMask = dict.get("IM", "ImageMask") || false;
144144
this.matte = dict.get("Matte") || false;
145145

146146
let bitsPerComponent = image.bitsPerComponent;
147147
if (!bitsPerComponent) {
148-
bitsPerComponent = dict.get("BitsPerComponent", "BPC");
148+
bitsPerComponent = dict.get("BPC", "BitsPerComponent");
149149
if (!bitsPerComponent) {
150150
if (this.imageMask) {
151151
bitsPerComponent = 1;
@@ -159,7 +159,7 @@ class PDFImage {
159159
this.bpc = bitsPerComponent;
160160

161161
if (!this.imageMask) {
162-
let colorSpace = dict.getRaw("ColorSpace") || dict.getRaw("CS");
162+
let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace");
163163
if (!colorSpace) {
164164
info("JPX images (which do not require color spaces)");
165165
switch (image.numComps) {
@@ -174,8 +174,7 @@ class PDFImage {
174174
break;
175175
default:
176176
throw new Error(
177-
`JPX images with ${image.numComps} ` +
178-
"color components not supported."
177+
`JPX images with ${image.numComps} color components not supported.`
179178
);
180179
}
181180
}
@@ -189,7 +188,7 @@ class PDFImage {
189188
this.numComps = this.colorSpace.numComps;
190189
}
191190

192-
this.decode = dict.getArray("Decode", "D");
191+
this.decode = dict.getArray("D", "Decode");
193192
this.needsDecode = false;
194193
if (
195194
this.decode &&
@@ -226,7 +225,7 @@ class PDFImage {
226225
} else if (mask) {
227226
if (isStream(mask)) {
228227
const maskDict = mask.dict,
229-
imageMask = maskDict.get("ImageMask", "IM");
228+
imageMask = maskDict.get("IM", "ImageMask");
230229
if (!imageMask) {
231230
warn("Ignoring /Mask in image without /ImageMask.");
232231
} else {

src/core/jpeg_stream.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class JpegStream extends DecodeStream {
6262
};
6363

6464
// Checking if values need to be transformed before conversion.
65-
const decodeArr = this.dict.getArray("Decode", "D");
65+
const decodeArr = this.dict.getArray("D", "Decode");
6666
if (this.forceRGB && Array.isArray(decodeArr)) {
67-
const bitsPerComponent = this.dict.get("BitsPerComponent") || 8;
67+
const bitsPerComponent = this.dict.get("BPC", "BitsPerComponent") || 8;
6868
const decodeArrLength = decodeArr.length;
6969
const transform = new Int32Array(decodeArrLength);
7070
let transformNeeded = false;

src/core/parser.js

+72-67
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ class Parser {
513513
}
514514

515515
// Extract the name of the first (i.e. the current) image filter.
516-
const filter = dict.get("Filter", "F");
516+
const filter = dict.get("F", "Filter");
517517
let filterName;
518518
if (isName(filter)) {
519519
filterName = filter.name;
@@ -527,14 +527,21 @@ class Parser {
527527
// Parse image stream.
528528
const startPos = stream.pos;
529529
let length;
530-
if (filterName === "DCTDecode" || filterName === "DCT") {
531-
length = this.findDCTDecodeInlineStreamEnd(stream);
532-
} else if (filterName === "ASCII85Decode" || filterName === "A85") {
533-
length = this.findASCII85DecodeInlineStreamEnd(stream);
534-
} else if (filterName === "ASCIIHexDecode" || filterName === "AHx") {
535-
length = this.findASCIIHexDecodeInlineStreamEnd(stream);
536-
} else {
537-
length = this.findDefaultInlineStreamEnd(stream);
530+
switch (filterName) {
531+
case "DCT":
532+
case "DCTDecode":
533+
length = this.findDCTDecodeInlineStreamEnd(stream);
534+
break;
535+
case "A85":
536+
case "ASCII85Decode":
537+
length = this.findASCII85DecodeInlineStreamEnd(stream);
538+
break;
539+
case "AHx":
540+
case "ASCIIHexDecode":
541+
length = this.findASCIIHexDecodeInlineStreamEnd(stream);
542+
break;
543+
default:
544+
length = this.findDefaultInlineStreamEnd(stream);
538545
}
539546
let imageStream = stream.makeSubStream(startPos, length, dict);
540547

@@ -694,15 +701,12 @@ class Parser {
694701
}
695702

696703
filter(stream, dict, length) {
697-
let filter = dict.get("Filter", "F");
698-
let params = dict.get("DecodeParms", "DP");
704+
let filter = dict.get("F", "Filter");
705+
let params = dict.get("DP", "DecodeParms");
699706

700707
if (isName(filter)) {
701708
if (Array.isArray(params)) {
702-
warn(
703-
"/DecodeParms should not contain an Array, " +
704-
"when /Filter contains a Name."
705-
);
709+
warn("/DecodeParms should not be an Array, when /Filter is a Name.");
706710
}
707711
return this.makeFilter(stream, filter.name, length, params);
708712
}
@@ -740,59 +744,60 @@ class Parser {
740744

741745
try {
742746
const xrefStreamStats = this.xref.stats.streamTypes;
743-
if (name === "FlateDecode" || name === "Fl") {
744-
xrefStreamStats[StreamType.FLATE] = true;
745-
if (params) {
746-
return new PredictorStream(
747-
new FlateStream(stream, maybeLength),
748-
maybeLength,
749-
params
750-
);
751-
}
752-
return new FlateStream(stream, maybeLength);
753-
}
754-
if (name === "LZWDecode" || name === "LZW") {
755-
xrefStreamStats[StreamType.LZW] = true;
756-
let earlyChange = 1;
757-
if (params) {
758-
if (params.has("EarlyChange")) {
759-
earlyChange = params.get("EarlyChange");
747+
switch (name) {
748+
case "Fl":
749+
case "FlateDecode":
750+
xrefStreamStats[StreamType.FLATE] = true;
751+
if (params) {
752+
return new PredictorStream(
753+
new FlateStream(stream, maybeLength),
754+
maybeLength,
755+
params
756+
);
760757
}
761-
return new PredictorStream(
762-
new LZWStream(stream, maybeLength, earlyChange),
763-
maybeLength,
764-
params
765-
);
766-
}
767-
return new LZWStream(stream, maybeLength, earlyChange);
768-
}
769-
if (name === "DCTDecode" || name === "DCT") {
770-
xrefStreamStats[StreamType.DCT] = true;
771-
return new JpegStream(stream, maybeLength, params);
772-
}
773-
if (name === "JPXDecode" || name === "JPX") {
774-
xrefStreamStats[StreamType.JPX] = true;
775-
return new JpxStream(stream, maybeLength, params);
776-
}
777-
if (name === "ASCII85Decode" || name === "A85") {
778-
xrefStreamStats[StreamType.A85] = true;
779-
return new Ascii85Stream(stream, maybeLength);
780-
}
781-
if (name === "ASCIIHexDecode" || name === "AHx") {
782-
xrefStreamStats[StreamType.AHX] = true;
783-
return new AsciiHexStream(stream, maybeLength);
784-
}
785-
if (name === "CCITTFaxDecode" || name === "CCF") {
786-
xrefStreamStats[StreamType.CCF] = true;
787-
return new CCITTFaxStream(stream, maybeLength, params);
788-
}
789-
if (name === "RunLengthDecode" || name === "RL") {
790-
xrefStreamStats[StreamType.RLX] = true;
791-
return new RunLengthStream(stream, maybeLength);
792-
}
793-
if (name === "JBIG2Decode") {
794-
xrefStreamStats[StreamType.JBIG] = true;
795-
return new Jbig2Stream(stream, maybeLength, params);
758+
return new FlateStream(stream, maybeLength);
759+
case "LZW":
760+
case "LZWDecode":
761+
xrefStreamStats[StreamType.LZW] = true;
762+
let earlyChange = 1;
763+
if (params) {
764+
if (params.has("EarlyChange")) {
765+
earlyChange = params.get("EarlyChange");
766+
}
767+
return new PredictorStream(
768+
new LZWStream(stream, maybeLength, earlyChange),
769+
maybeLength,
770+
params
771+
);
772+
}
773+
return new LZWStream(stream, maybeLength, earlyChange);
774+
case "DCT":
775+
case "DCTDecode":
776+
xrefStreamStats[StreamType.DCT] = true;
777+
return new JpegStream(stream, maybeLength, params);
778+
case "JPX":
779+
case "JPXDecode":
780+
xrefStreamStats[StreamType.JPX] = true;
781+
return new JpxStream(stream, maybeLength, params);
782+
case "A85":
783+
case "ASCII85Decode":
784+
xrefStreamStats[StreamType.A85] = true;
785+
return new Ascii85Stream(stream, maybeLength);
786+
case "AHx":
787+
case "ASCIIHexDecode":
788+
xrefStreamStats[StreamType.AHX] = true;
789+
return new AsciiHexStream(stream, maybeLength);
790+
case "CCF":
791+
case "CCITTFaxDecode":
792+
xrefStreamStats[StreamType.CCF] = true;
793+
return new CCITTFaxStream(stream, maybeLength, params);
794+
case "RL":
795+
case "RunLengthDecode":
796+
xrefStreamStats[StreamType.RLX] = true;
797+
return new RunLengthStream(stream, maybeLength);
798+
case "JBIG2Decode":
799+
xrefStreamStats[StreamType.JBIG] = true;
800+
return new Jbig2Stream(stream, maybeLength, params);
796801
}
797802
warn(`Filter "${name}" is not supported.`);
798803
return stream;

src/core/pattern.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class RadialAxialShading extends BaseShading {
117117
this.coordsArr = dict.getArray("Coords");
118118
this.shadingType = dict.get("ShadingType");
119119
const cs = ColorSpace.parse({
120-
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
120+
cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
121121
xref,
122122
resources,
123123
pdfFunctionFactory,
@@ -415,7 +415,7 @@ class MeshShading extends BaseShading {
415415
this.bbox = null;
416416
}
417417
const cs = ColorSpace.parse({
418-
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
418+
cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
419419
xref,
420420
resources,
421421
pdfFunctionFactory,

src/core/predictor_stream.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class PredictorStream extends DecodeStream {
4343
this.dict = str.dict;
4444

4545
const colors = (this.colors = params.get("Colors") || 1);
46-
const bits = (this.bits = params.get("BitsPerComponent") || 8);
46+
const bits = (this.bits = params.get("BPC", "BitsPerComponent") || 8);
4747
const columns = (this.columns = params.get("Columns") || 1);
4848

4949
this.pixBytes = (colors * bits + 7) >> 3;

0 commit comments

Comments
 (0)