Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[api-minor] Decode all JPEG images with the built-in PDF.js decoder in src/core/jpg.js #11601

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions examples/node/pdf2svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ function writeSvgToFile(svgElement, filePath) {
var loadingTask = pdfjsLib.getDocument({
data: data,
fontExtraProperties: true,
// Try to export JPEG images directly if they don't need any further
// processing.
nativeImageDecoderSupport: pdfjsLib.NativeImageDecoding.DISPLAY,
});
loadingTask.promise
.then(function (doc) {
Expand Down
8 changes: 0 additions & 8 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,14 +563,6 @@ gulp.task("default_preferences-pre", function () {
};
var preprocessor2 = require("./external/builder/preprocessor2.js");
return merge([
gulp.src(
[
"src/{display,shared}/*.js",
"!src/shared/{cffStandardStrings,fonts_utils}.js",
"src/pdf.js",
],
{ base: "src/" }
),
gulp.src(["web/{app_options,viewer_compatibility}.js"], {
base: ".",
}),
Expand Down
106 changes: 1 addition & 105 deletions src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
isArrayEqual,
isNum,
isString,
NativeImageDecoding,
OPS,
stringToPDFString,
TextRenderingMode,
Expand Down Expand Up @@ -80,18 +79,14 @@ import { DecodeStream } from "./stream.js";
import { getGlyphsUnicode } from "./glyphlist.js";
import { getMetrics } from "./metrics.js";
import { isPDFFunction } from "./function.js";
import { JpegStream } from "./jpeg_stream.js";
import { MurmurHash3_64 } from "./murmurhash3.js";
import { NativeImageDecoder } from "./image_utils.js";
import { OperatorList } from "./operator_list.js";
import { PDFImage } from "./image.js";

var PartialEvaluator = (function PartialEvaluatorClosure() {
const DefaultPartialEvaluatorOptions = {
forceDataSchema: false,
maxImageSize: -1,
disableFontFace: false,
nativeImageDecoderSupport: NativeImageDecoding.DECODE,
ignoreErrors: false,
isEvalSupported: true,
fontExtraProperties: false,
Expand Down Expand Up @@ -450,7 +445,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList,
cacheKey,
imageCache,
forceDisableNativeImageDecoder = false,
}) {
var dict = image.dict;
const imageRef = dict.objId;
Expand Down Expand Up @@ -510,13 +504,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {

var SMALL_IMAGE_DIMENSIONS = 200;
// Inlining small images into the queue as RGB data
if (
isInline &&
!softMask &&
!mask &&
!(image instanceof JpegStream) &&
w + h < SMALL_IMAGE_DIMENSIONS
) {
if (isInline && !softMask && !mask && w + h < SMALL_IMAGE_DIMENSIONS) {
const imageObj = new PDFImage({
xref: this.xref,
res: resources,
Expand All @@ -531,20 +519,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return undefined;
}

let 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.
let objId = `img_${this.idFactory.createObjId()}`,
cacheGlobally = false;

if (this.parsingType3Font) {
assert(
nativeImageDecoderSupport === NativeImageDecoding.NONE,
"Type3 image resources should be completely decoded in the worker."
);

objId = `${this.idFactory.getDocId()}_type3res_${objId}`;
} else if (imageRef) {
cacheGlobally = this.globalImageCache.shouldCache(
Expand All @@ -553,102 +533,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
);

if (cacheGlobally) {
// Ensure that the image is *completely* decoded on the worker-thread,
// in order to simplify the caching/rendering code on the main-thread.
nativeImageDecoderSupport = NativeImageDecoding.NONE;

objId = `${this.idFactory.getDocId()}_${objId}`;
}
}

if (
nativeImageDecoderSupport !== NativeImageDecoding.NONE &&
!softMask &&
!mask &&
image instanceof JpegStream &&
image.maybeValidDimensions &&
NativeImageDecoder.isSupported(
image,
this.xref,
resources,
this.pdfFunctionFactory
)
) {
// These JPEGs don't need any more processing so we can just send it.
return this.handler
.sendWithPromise("obj", [
objId,
this.pageIndex,
"JpegStream",
image.getIR(this.options.forceDataSchema),
])
.then(
() => {
// 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,
};

if (imageRef) {
this.globalImageCache.addPageIndex(imageRef, this.pageIndex);
}
}
},
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.
var nativeImageDecoder = null;
if (
nativeImageDecoderSupport === NativeImageDecoding.DECODE &&
(image instanceof JpegStream ||
mask instanceof JpegStream ||
softMask instanceof JpegStream)
) {
nativeImageDecoder = new NativeImageDecoder({
xref: this.xref,
resources,
handler: this.handler,
forceDataSchema: this.options.forceDataSchema,
pdfFunctionFactory: this.pdfFunctionFactory,
});
}

// Ensure that the dependency is added before the image is decoded.
operatorList.addDependency(objId);
args = [objId, w, h];

const imgPromise = PDFImage.buildImage({
handler: this.handler,
xref: this.xref,
res: resources,
image,
isInline,
nativeDecoder: nativeImageDecoder,
pdfFunctionFactory: this.pdfFunctionFactory,
})
.then(imageObj => {
Expand Down Expand Up @@ -3393,7 +3290,6 @@ class TranslatedFont {
// the rendering code on the main-thread (see issue10717.pdf).
var type3Options = Object.create(evaluator.options);
type3Options.ignoreErrors = false;
type3Options.nativeImageDecoderSupport = NativeImageDecoding.NONE;
var type3Evaluator = evaluator.clone(type3Options);
type3Evaluator.parsingType3Font = true;

Expand Down
70 changes: 20 additions & 50 deletions src/core/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,6 @@ import { JpegStream } from "./jpeg_stream.js";
import { JpxImage } from "./jpx.js";

var PDFImage = (function PDFImageClosure() {
/**
* Decodes the image using native decoder if possible. Resolves the promise
* when the image data is ready.
*/
function handleImageData(image, nativeDecoder) {
if (nativeDecoder && nativeDecoder.canDecode(image)) {
return nativeDecoder.decode(image).catch(reason => {
warn(
"Native image decoding failed -- trying to recover: " +
(reason && reason.message)
);
return image;
});
}
return Promise.resolve(image);
}

/**
* Decode and clamp a value. The formula is different from the spec because we
* don't decode to float range [0,1], we decode it in the [0,max] range.
Expand Down Expand Up @@ -266,51 +249,38 @@ var PDFImage = (function PDFImageClosure() {
* with a PDFImage when the image is ready to be used.
*/
PDFImage.buildImage = function ({
handler,
xref,
res,
image,
isInline = false,
nativeDecoder = null,
pdfFunctionFactory,
}) {
var imagePromise = handleImageData(image, nativeDecoder);
var smaskPromise;
var maskPromise;
const imageData = image;
let smaskData = null;
let maskData = null;

var smask = image.dict.get("SMask");
var mask = image.dict.get("Mask");
const smask = image.dict.get("SMask");
const mask = image.dict.get("Mask");

if (smask) {
smaskPromise = handleImageData(smask, nativeDecoder);
maskPromise = Promise.resolve(null);
} else {
smaskPromise = Promise.resolve(null);
if (mask) {
if (isStream(mask)) {
maskPromise = handleImageData(mask, nativeDecoder);
} else if (Array.isArray(mask)) {
maskPromise = Promise.resolve(mask);
} else {
warn("Unsupported mask format.");
maskPromise = Promise.resolve(null);
}
smaskData = smask;
} else if (mask) {
if (isStream(mask) || Array.isArray(mask)) {
maskData = mask;
} else {
maskPromise = Promise.resolve(null);
warn("Unsupported mask format.");
}
}
return Promise.all([imagePromise, smaskPromise, maskPromise]).then(
function ([imageData, smaskData, maskData]) {
return new PDFImage({
xref,
res,
image: imageData,
isInline,
smask: smaskData,
mask: maskData,
pdfFunctionFactory,
});
}
return Promise.resolve(
new PDFImage({
xref,
res,
image: imageData,
isInline,
smask: smaskData,
mask: maskData,
pdfFunctionFactory,
})
);
};

Expand Down
Loading