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:
parent
f39219cd45
commit
6c229dffb1
@ -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] &&
|
||||
|
@ -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;
|
||||
|
1
test/pdfs/issue14824.pdf.link
Normal file
1
test/pdfs/issue14824.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
https://github.com/mozilla/pdf.js/files/8540275/PDF.pdf
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user