Fallback to the built-in JPEG decoder if 'JpegStream', in src/display/api.js, fails to load the image

This works by making `PartialEvaluator.buildPaintImageXObject` wait for the success/failure of `loadJpegStream` on the API side *before* parsing continues.

Please note that in practice, it should be quite rare for the browser to fail loading/decoding of a JPEG image. In the general case, it should thus not be completely surprising if even `src/core/jpg.js` will fail to decode the image.
This commit is contained in:
Jonas Jenwald 2018-02-01 16:43:10 +01:00
parent 76afe1018b
commit 80441346a3
2 changed files with 43 additions and 19 deletions

View File

@ -349,7 +349,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
buildPaintImageXObject({ resources, image, isInline = false, operatorList,
cacheKey, imageCache, }) {
cacheKey, imageCache,
forceDisableNativeImageDecoder = false, }) {
var dict = image.dict;
var w = dict.get('Width', 'W');
var h = dict.get('Height', 'H');
@ -419,28 +420,47 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return Promise.resolve();
}
var nativeImageDecoderSupport = this.options.nativeImageDecoderSupport;
const nativeImageDecoderSupport = forceDisableNativeImageDecoder ?
NativeImageDecoding.NONE : this.options.nativeImageDecoderSupport;
// If there is no imageMask, create the PDFImage and a lot
// of image processing can be done here.
var objId = 'img_' + this.idFactory.createObjId();
operatorList.addDependency(objId);
args = [objId, w, h];
if (nativeImageDecoderSupport !== NativeImageDecoding.NONE &&
!softMask && !mask && image instanceof JpegStream &&
NativeImageDecoder.isSupported(image, this.xref, resources,
this.pdfFunctionFactory)) {
// These JPEGs don't need any more processing so we can just send it.
operatorList.addOp(OPS.paintJpegXObject, args);
this.handler.send('obj', [objId, this.pageIndex, 'JpegStream',
image.getIR(this.options.forceDataSchema)]);
if (cacheKey) {
imageCache[cacheKey] = {
fn: OPS.paintJpegXObject,
args,
};
}
return Promise.resolve();
return this.handler.sendWithPromise('obj', [
objId, this.pageIndex, 'JpegStream',
image.getIR(this.options.forceDataSchema)
]).then(function() {
// Only add the dependency once we know that the native JPEG decoding
// succeeded, to ensure that rendering will always complete.
operatorList.addDependency(objId);
args = [objId, w, h];
operatorList.addOp(OPS.paintJpegXObject, args);
if (cacheKey) {
imageCache[cacheKey] = {
fn: OPS.paintJpegXObject,
args,
};
}
}, (reason) => {
warn('Native JPEG decoding failed -- trying to recover: ' +
(reason && reason.message));
// Try to decode the JPEG image with the built-in decoder instead.
return this.buildPaintImageXObject({
resources,
image,
isInline,
operatorList,
cacheKey,
imageCache,
forceDisableNativeImageDecoder: true,
});
});
}
// Creates native image decoder only if a JPEG image or mask is present.
@ -457,6 +477,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
});
}
// Ensure that the dependency is added before the image is decoded.
operatorList.addDependency(objId);
args = [objId, w, h];
PDFImage.buildImage({
handler: this.handler,
xref: this.xref,

View File

@ -1817,22 +1817,22 @@ var WorkerTransport = (function WorkerTransportClosure() {
switch (type) {
case 'JpegStream':
imageData = data[3];
new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = function() {
reject(new Error('Error during JPEG image loading'));
// Note that when the browser image loading/decoding fails,
// we'll fallback to the built-in PDF.js JPEG decoder; see
// `PartialEvaluator.buildPaintImageXObject` in the
// `src/core/evaluator.js` file.
};
img.src = imageData;
}).then((img) => {
pageProxy.objs.resolve(id, img);
}, (reason) => {
warn(reason);
pageProxy.objs.resolve(id, null);
});
break;
case 'Image':
imageData = data[3];
pageProxy.objs.resolve(id, imageData);