Ensure that worker-thread image caching doesn't break optional content (issue 14824)

Currently we only insert optionalContent-data into the operatorList the first time that an image is parsed, which will (in hindsight) obviously cause problems for cached images.
Hence we also need to insert the optionalContent-data in the various worker-thread image caches, such that it can be accessed in the fast-paths that are used to skip re-parsing of images.

In order to reduce the amount of repeated code, this patch also adds a new `OperatorList`-method that takes care of inserting the necessary data in the operatorList.
This commit is contained in:
Jonas Jenwald 2022-04-22 14:30:25 +02:00
parent f39219cd45
commit 6c229dffb1
4 changed files with 78 additions and 33 deletions

View File

@ -592,12 +592,8 @@ class PartialEvaluator {
resources
);
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
}
const imageMask = dict.get("IM", "ImageMask") || false;
const interpolate = dict.get("I", "Interpolate");
let imgData, args;
if (imageMask) {
// This depends on a tmpCanvas being filled with the
@ -605,6 +601,7 @@ class PartialEvaluator {
// data can't be done here. Instead of creating a
// complete PDFImage, only read the information needed
// for later.
const interpolate = dict.get("I", "Interpolate");
const bitStrideLength = (w + 7) >> 3;
const imgArray = image.getBytes(
bitStrideLength * h,
@ -625,17 +622,19 @@ class PartialEvaluator {
imgData.cached = !!cacheKey;
args = [imgData];
operatorList.addOp(OPS.paintImageMaskXObject, args);
operatorList.addImageOps(
OPS.paintImageMaskXObject,
args,
optionalContent
);
if (cacheKey) {
localImageCache.set(cacheKey, imageRef, {
fn: OPS.paintImageMaskXObject,
args,
optionalContent,
});
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
}
return;
}
@ -651,17 +650,19 @@ class PartialEvaluator {
if (imgData.isSingleOpaquePixel) {
// Handles special case of mainly LaTeX documents which use image
// masks to draw lines with the current fill style.
operatorList.addOp(OPS.paintSolidColorImageMask, []);
operatorList.addImageOps(
OPS.paintSolidColorImageMask,
[],
optionalContent
);
if (cacheKey) {
localImageCache.set(cacheKey, imageRef, {
fn: OPS.paintSolidColorImageMask,
args: [],
optionalContent,
});
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
}
return;
}
@ -678,18 +679,19 @@ class PartialEvaluator {
count: 1,
},
];
operatorList.addImageOps(
OPS.paintImageMaskXObject,
args,
optionalContent
);
operatorList.addOp(OPS.paintImageMaskXObject, args);
if (cacheKey) {
localImageCache.set(cacheKey, imageRef, {
fn: OPS.paintImageMaskXObject,
args,
optionalContent,
});
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
}
return;
}
@ -710,11 +712,11 @@ class PartialEvaluator {
// We force the use of RGBA_32BPP images here, because we can't handle
// any other kind.
imgData = imageObj.createImageData(/* forceRGBA = */ true);
operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
}
operatorList.addImageOps(
OPS.paintInlineImageXObject,
[imgData],
optionalContent
);
return;
}
@ -762,11 +764,13 @@ class PartialEvaluator {
return this._sendImgData(objId, /* imgData = */ null, cacheGlobally);
});
operatorList.addOp(OPS.paintImageXObject, args);
operatorList.addImageOps(OPS.paintImageXObject, args, optionalContent);
if (cacheKey) {
localImageCache.set(cacheKey, imageRef, {
fn: OPS.paintImageXObject,
args,
optionalContent,
});
if (imageRef) {
@ -778,15 +782,12 @@ class PartialEvaluator {
objId,
fn: OPS.paintImageXObject,
args,
optionalContent,
byteSize: 0, // Temporary entry, note `addByteSize` above.
});
}
}
}
if (optionalContent !== undefined) {
operatorList.addOp(OPS.endMarkedContent, []);
}
}
handleSMask(
@ -1700,7 +1701,12 @@ class PartialEvaluator {
if (isValidName) {
const localImage = localImageCache.getByName(name);
if (localImage) {
operatorList.addOp(localImage.fn, localImage.args);
operatorList.addImageOps(
localImage.fn,
localImage.args,
localImage.optionalContent
);
if (
localImage.fn === OPS.paintImageMaskXObject &&
localImage.args[0] &&
@ -1723,7 +1729,12 @@ class PartialEvaluator {
if (xobj instanceof Ref) {
const localImage = localImageCache.getByRef(xobj);
if (localImage) {
operatorList.addOp(localImage.fn, localImage.args);
operatorList.addImageOps(
localImage.fn,
localImage.args,
localImage.optionalContent
);
if (
localImage.fn === OPS.paintImageMaskXObject &&
localImage.args[0] &&
@ -1741,7 +1752,11 @@ class PartialEvaluator {
);
if (globalImage) {
operatorList.addDependency(globalImage.objId);
operatorList.addOp(globalImage.fn, globalImage.args);
operatorList.addImageOps(
globalImage.fn,
globalImage.args,
globalImage.optionalContent
);
resolveXObject();
return;
@ -1846,7 +1861,12 @@ class PartialEvaluator {
if (cacheKey) {
const localImage = localImageCache.getByName(cacheKey);
if (localImage) {
operatorList.addOp(localImage.fn, localImage.args);
operatorList.addImageOps(
localImage.fn,
localImage.args,
localImage.optionalContent
);
if (
localImage.fn === OPS.paintImageMaskXObject &&
localImage.args[0] &&

View File

@ -622,6 +622,18 @@ class OperatorList {
}
}
addImageOps(fn, args, optionalContent) {
if (optionalContent !== undefined) {
this.addOp(OPS.beginMarkedContentProps, ["OC", optionalContent]);
}
this.addOp(fn, args);
if (optionalContent !== undefined) {
this.addOp(OPS.endMarkedContent, []);
}
}
addDependency(dependency) {
if (this.dependencies.has(dependency)) {
return;

View File

@ -0,0 +1 @@
https://github.com/mozilla/pdf.js/files/8540275/PDF.pdf

View File

@ -2965,6 +2965,18 @@
"annotations": true,
"about": "LinkAnnotation with a relative link, and a /Catalog Base-URI."
},
{ "id": "issue14824",
"file": "pdfs/issue14824.pdf",
"md5": "7b8d061ab0a342e3606a3b3ba1925d5b",
"rounds": 1,
"link": true,
"lastPage": 4,
"type": "eq",
"optionalContent": {
"7R": false
},
"about": "Need to test *at least* three pages, since the `GlobalImageCache` is involved."
},
{ "id": "issue1127-text",
"file": "pdfs/issue1127.pdf",
"md5": "4fb2be5ffefeafda4ba977de2a1bb4d8",