For images that include SMask/Mask entries, ignore an SMask defined in the current graphics state
From section [11.6.4.3 Mask Shape and Opacity](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf#G10.4848628) in the PDF specification: - An image XObject may contain its own *soft-mask image* in the form of a subsidiary image XObject in the `SMask` entry of the image dictionary (see "Image Dictionaries"). This mask, if present, shall override any explicit or colour key mask specified by the image dictionary's `Mask` entry. Either form of mask in the image dictionary shall override the current soft mask in the graphics state.
This commit is contained in:
parent
8a50d2d302
commit
20d5332009
@ -175,7 +175,7 @@ function addLocallyCachedImageOps(opList, data) {
|
|||||||
if (data.objId) {
|
if (data.objId) {
|
||||||
opList.addDependency(data.objId);
|
opList.addDependency(data.objId);
|
||||||
}
|
}
|
||||||
opList.addImageOps(data.fn, data.args, data.optionalContent);
|
opList.addImageOps(data.fn, data.args, data.optionalContent, data.hasMask);
|
||||||
|
|
||||||
if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) {
|
if (data.fn === OPS.paintImageMaskXObject && data.args[0]?.count > 0) {
|
||||||
data.args[0].count++;
|
data.args[0].count++;
|
||||||
@ -730,13 +730,9 @@ class PartialEvaluator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SMALL_IMAGE_DIMENSIONS = 200;
|
const SMALL_IMAGE_DIMENSIONS = 200;
|
||||||
|
const hasMask = dict.has("SMask") || dict.has("Mask");
|
||||||
// Inlining small images into the queue as RGB data
|
// Inlining small images into the queue as RGB data
|
||||||
if (
|
if (isInline && w + h < SMALL_IMAGE_DIMENSIONS && !hasMask) {
|
||||||
isInline &&
|
|
||||||
w + h < SMALL_IMAGE_DIMENSIONS &&
|
|
||||||
!dict.has("SMask") &&
|
|
||||||
!dict.has("Mask")
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const imageObj = new PDFImage({
|
const imageObj = new PDFImage({
|
||||||
xref: this.xref,
|
xref: this.xref,
|
||||||
@ -793,7 +789,12 @@ class PartialEvaluator {
|
|||||||
// Ensure that the dependency is added before the image is decoded.
|
// Ensure that the dependency is added before the image is decoded.
|
||||||
operatorList.addDependency(objId);
|
operatorList.addDependency(objId);
|
||||||
args = [objId, w, h];
|
args = [objId, w, h];
|
||||||
operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent);
|
operatorList.addImageOps(
|
||||||
|
OPS.paintImageXObject,
|
||||||
|
args,
|
||||||
|
optionalContent,
|
||||||
|
hasMask
|
||||||
|
);
|
||||||
|
|
||||||
if (cacheGlobally) {
|
if (cacheGlobally) {
|
||||||
if (this.globalImageCache.hasDecodeFailed(imageRef)) {
|
if (this.globalImageCache.hasDecodeFailed(imageRef)) {
|
||||||
@ -802,6 +803,7 @@ class PartialEvaluator {
|
|||||||
fn: OPS.paintImageXObject,
|
fn: OPS.paintImageXObject,
|
||||||
args,
|
args,
|
||||||
optionalContent,
|
optionalContent,
|
||||||
|
hasMask,
|
||||||
byteSize: 0, // Data is `null`, since decoding failed previously.
|
byteSize: 0, // Data is `null`, since decoding failed previously.
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -812,7 +814,7 @@ class PartialEvaluator {
|
|||||||
// For large (at least 500x500) or more complex images that we'll cache
|
// For large (at least 500x500) or more complex images that we'll cache
|
||||||
// globally, check if the image is still cached locally on the main-thread
|
// globally, check if the image is still cached locally on the main-thread
|
||||||
// to avoid having to re-parse the image (since that can be slow).
|
// to avoid having to re-parse the image (since that can be slow).
|
||||||
if (w * h > 250000 || dict.has("SMask") || dict.has("Mask")) {
|
if (w * h > 250000 || hasMask) {
|
||||||
const localLength = await this.handler.sendWithPromise("commonobj", [
|
const localLength = await this.handler.sendWithPromise("commonobj", [
|
||||||
objId,
|
objId,
|
||||||
"CopyLocalImage",
|
"CopyLocalImage",
|
||||||
@ -825,6 +827,7 @@ class PartialEvaluator {
|
|||||||
fn: OPS.paintImageXObject,
|
fn: OPS.paintImageXObject,
|
||||||
args,
|
args,
|
||||||
optionalContent,
|
optionalContent,
|
||||||
|
hasMask,
|
||||||
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
byteSize: 0, // Temporary entry, to avoid `setData` returning early.
|
||||||
});
|
});
|
||||||
this.globalImageCache.addByteSize(imageRef, localLength);
|
this.globalImageCache.addByteSize(imageRef, localLength);
|
||||||
@ -872,6 +875,7 @@ class PartialEvaluator {
|
|||||||
fn: OPS.paintImageXObject,
|
fn: OPS.paintImageXObject,
|
||||||
args,
|
args,
|
||||||
optionalContent,
|
optionalContent,
|
||||||
|
hasMask,
|
||||||
};
|
};
|
||||||
localImageCache.set(cacheKey, imageRef, cacheData);
|
localImageCache.set(cacheKey, imageRef, cacheData);
|
||||||
|
|
||||||
@ -884,6 +888,7 @@ class PartialEvaluator {
|
|||||||
fn: OPS.paintImageXObject,
|
fn: OPS.paintImageXObject,
|
||||||
args,
|
args,
|
||||||
optionalContent,
|
optionalContent,
|
||||||
|
hasMask,
|
||||||
byteSize: 0, // Temporary entry, note `addByteSize` above.
|
byteSize: 0, // Temporary entry, note `addByteSize` above.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1814,7 +1819,8 @@ class PartialEvaluator {
|
|||||||
operatorList.addImageOps(
|
operatorList.addImageOps(
|
||||||
globalImage.fn,
|
globalImage.fn,
|
||||||
globalImage.args,
|
globalImage.args,
|
||||||
globalImage.optionalContent
|
globalImage.optionalContent,
|
||||||
|
globalImage.hasMask
|
||||||
);
|
);
|
||||||
|
|
||||||
resolveXObject();
|
resolveXObject();
|
||||||
|
|||||||
@ -636,7 +636,11 @@ class OperatorList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addImageOps(fn, args, optionalContent) {
|
addImageOps(fn, args, optionalContent, hasMask = false) {
|
||||||
|
if (hasMask) {
|
||||||
|
this.addOp(OPS.save);
|
||||||
|
this.addOp(OPS.setGState, [[["SMask", false]]]);
|
||||||
|
}
|
||||||
if (optionalContent !== undefined) {
|
if (optionalContent !== undefined) {
|
||||||
this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
|
this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
|
||||||
}
|
}
|
||||||
@ -646,6 +650,9 @@ class OperatorList {
|
|||||||
if (optionalContent !== undefined) {
|
if (optionalContent !== undefined) {
|
||||||
this.addOp(OPS.endMarkedContent, []);
|
this.addOp(OPS.endMarkedContent, []);
|
||||||
}
|
}
|
||||||
|
if (hasMask) {
|
||||||
|
this.addOp(OPS.restore);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addDependency(dependency) {
|
addDependency(dependency) {
|
||||||
|
|||||||
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -232,6 +232,7 @@
|
|||||||
!issue5954.pdf
|
!issue5954.pdf
|
||||||
!issue6612.pdf
|
!issue6612.pdf
|
||||||
!alphatrans.pdf
|
!alphatrans.pdf
|
||||||
|
!issue14200.pdf
|
||||||
!pattern_text_embedded_font.pdf
|
!pattern_text_embedded_font.pdf
|
||||||
!devicen.pdf
|
!devicen.pdf
|
||||||
!cmykjpeg.pdf
|
!cmykjpeg.pdf
|
||||||
|
|||||||
BIN
test/pdfs/issue14200.pdf
Normal file
BIN
test/pdfs/issue14200.pdf
Normal file
Binary file not shown.
@ -5255,6 +5255,13 @@
|
|||||||
"link": true,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "issue14200",
|
||||||
|
"file": "pdfs/issue14200.pdf",
|
||||||
|
"md5": "4dba2cde1c6e65abe53e66eefc97a7f1",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "jbig2_huffman_1",
|
"id": "jbig2_huffman_1",
|
||||||
"file": "pdfs/jbig2_huffman_1.pdf",
|
"file": "pdfs/jbig2_huffman_1.pdf",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user