diff --git a/examples/node/pdf2svg.js b/examples/node/pdf2svg.js index bce275ce62c23..24ca75ba86173 100644 --- a/examples/node/pdf2svg.js +++ b/examples/node/pdf2svg.js @@ -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) { diff --git a/gulpfile.js b/gulpfile.js index 18a2ab9fac447..120d4fdbad354 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -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: ".", }), diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 7bac0edde38d8..4a307f1425fd7 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -25,7 +25,6 @@ import { isArrayEqual, isNum, isString, - NativeImageDecoding, OPS, stringToPDFString, TextRenderingMode, @@ -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, @@ -450,7 +445,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList, cacheKey, imageCache, - forceDisableNativeImageDecoder = false, }) { var dict = image.dict; const imageRef = dict.objId; @@ -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, @@ -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( @@ -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 => { @@ -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; diff --git a/src/core/image.js b/src/core/image.js index d0cecd766811f..bab5affa0e4f5 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -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. @@ -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, + }) ); }; diff --git a/src/core/image_utils.js b/src/core/image_utils.js index 1ae7bf6ccc71d..0cbdc2c618cf9 100644 --- a/src/core/image_utils.js +++ b/src/core/image_utils.js @@ -15,103 +15,7 @@ /* eslint no-var: error */ import { assert, info, shadow } from "../shared/util.js"; -import { ColorSpace } from "./colorspace.js"; -import { JpegStream } from "./jpeg_stream.js"; import { RefSetCache } from "./primitives.js"; -import { Stream } from "./stream.js"; - -class NativeImageDecoder { - constructor({ - xref, - resources, - handler, - forceDataSchema = false, - pdfFunctionFactory, - }) { - this.xref = xref; - this.resources = resources; - this.handler = handler; - this.forceDataSchema = forceDataSchema; - this.pdfFunctionFactory = pdfFunctionFactory; - } - - canDecode(image) { - return ( - image instanceof JpegStream && - image.maybeValidDimensions && - NativeImageDecoder.isDecodable( - image, - this.xref, - this.resources, - this.pdfFunctionFactory - ) - ); - } - - decode(image) { - // For natively supported JPEGs send them to the main thread for decoding. - const dict = image.dict; - let colorSpace = dict.get("ColorSpace", "CS"); - colorSpace = ColorSpace.parse( - colorSpace, - this.xref, - this.resources, - this.pdfFunctionFactory - ); - - return this.handler - .sendWithPromise("JpegDecode", [ - image.getIR(this.forceDataSchema), - colorSpace.numComps, - ]) - .then(function ({ data, width, height }) { - return new Stream(data, 0, data.length, dict); - }); - } - - /** - * Checks if the image can be decoded and displayed by the browser without any - * further processing such as color space conversions. - */ - static isSupported(image, xref, res, pdfFunctionFactory) { - const dict = image.dict; - if (dict.has("DecodeParms") || dict.has("DP")) { - return false; - } - const cs = ColorSpace.parse( - dict.get("ColorSpace", "CS"), - xref, - res, - pdfFunctionFactory - ); - // isDefaultDecode() of DeviceGray and DeviceRGB needs no `bpc` argument. - return ( - (cs.name === "DeviceGray" || cs.name === "DeviceRGB") && - cs.isDefaultDecode(dict.getArray("Decode", "D")) - ); - } - - /** - * Checks if the image can be decoded by the browser. - */ - static isDecodable(image, xref, res, pdfFunctionFactory) { - const dict = image.dict; - if (dict.has("DecodeParms") || dict.has("DP")) { - return false; - } - const cs = ColorSpace.parse( - dict.get("ColorSpace", "CS"), - xref, - res, - pdfFunctionFactory - ); - const bpc = dict.get("BitsPerComponent", "BPC") || 1; - return ( - (cs.numComps === 1 || cs.numComps === 3) && - cs.isDefaultDecode(dict.getArray("Decode", "D"), bpc) - ); - } -} class GlobalImageCache { static get NUM_PAGES_THRESHOLD() { @@ -207,4 +111,4 @@ class GlobalImageCache { } } -export { NativeImageDecoder, GlobalImageCache }; +export { GlobalImageCache }; diff --git a/src/core/jpeg_stream.js b/src/core/jpeg_stream.js index eb8e9a5f7fbb6..f936626ea3c03 100644 --- a/src/core/jpeg_stream.js +++ b/src/core/jpeg_stream.js @@ -13,17 +13,14 @@ * limitations under the License. */ -import { createObjectURL, shadow } from "../shared/util.js"; import { DecodeStream } from "./stream.js"; import { isDict } from "./primitives.js"; import { JpegImage } from "./jpg.js"; +import { shadow } from "../shared/util.js"; /** - * Depending on the type of JPEG a JpegStream is handled in different ways. For - * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image - * data is stored and then loaded by the browser. For unsupported JPEG's we use - * a library to decode these images and the stream behaves like all the other - * DecodeStreams. + * For JPEG's we use a library to decode these images and the stream behaves + * like all the other DecodeStreams. */ const JpegStream = (function JpegStreamClosure() { // eslint-disable-next-line no-shadow @@ -110,150 +107,6 @@ const JpegStream = (function JpegStreamClosure() { this.eof = true; }; - Object.defineProperty(JpegStream.prototype, "maybeValidDimensions", { - get: function JpegStream_maybeValidDimensions() { - const { dict, stream } = this; - const dictHeight = dict.get("Height", "H"); - const startPos = stream.pos; - - let validDimensions = true, - foundSOF = false, - b; - while ((b = stream.getByte()) !== -1) { - if (b !== 0xff) { - // Not a valid marker. - continue; - } - switch (stream.getByte()) { - case 0xc0: // SOF0 - case 0xc1: // SOF1 - case 0xc2: // SOF2 - // These three SOF{n} markers are the only ones that the built-in - // PDF.js JPEG decoder currently supports. - foundSOF = true; - - stream.pos += 2; // Skip marker length. - stream.pos += 1; // Skip precision. - const scanLines = stream.getUint16(); - const samplesPerLine = stream.getUint16(); - - // Letting the browser handle the JPEG decoding, on the main-thread, - // will cause a *large* increase in peak memory usage since there's - // a handful of short-lived copies of the image data. For very big - // JPEG images, always let the PDF.js image decoder handle them to - // reduce overall memory usage during decoding (see issue 11694). - if (scanLines * samplesPerLine > 1e6) { - validDimensions = false; - break; - } - - // The "normal" case, where the image data and dictionary agrees. - if (scanLines === dictHeight) { - break; - } - // A DNL (Define Number of Lines) marker is expected, - // which browsers (usually) cannot decode natively. - if (scanLines === 0) { - validDimensions = false; - break; - } - // The dimensions of the image, among other properties, should - // always be taken from the image data *itself* rather than the - // XObject dictionary. However there's cases of corrupt images that - // browsers cannot decode natively, for example: - // - JPEG images with DNL markers, where the SOF `scanLines` - // parameter has an unexpected value (see issue 8614). - // - JPEG images with too large SOF `scanLines` parameter, where - // the EOI marker is encountered prematurely (see issue 10880). - // In an attempt to handle these kinds of corrupt images, compare - // the dimensions in the image data with the dictionary and *always* - // let the PDF.js JPEG decoder (rather than the browser) handle the - // image if the difference is larger than one order of magnitude - // (since that would generally suggest that something is off). - if (scanLines > dictHeight * 10) { - validDimensions = false; - break; - } - break; - - case 0xc3: // SOF3 - /* falls through */ - case 0xc5: // SOF5 - case 0xc6: // SOF6 - case 0xc7: // SOF7 - /* falls through */ - case 0xc9: // SOF9 - case 0xca: // SOF10 - case 0xcb: // SOF11 - /* falls through */ - case 0xcd: // SOF13 - case 0xce: // SOF14 - case 0xcf: // SOF15 - foundSOF = true; - break; - - case 0xc4: // DHT - case 0xcc: // DAC - /* falls through */ - case 0xda: // SOS - case 0xdb: // DQT - case 0xdc: // DNL - case 0xdd: // DRI - case 0xde: // DHP - case 0xdf: // EXP - /* falls through */ - case 0xe0: // APP0 - case 0xe1: // APP1 - case 0xe2: // APP2 - case 0xe3: // APP3 - case 0xe4: // APP4 - case 0xe5: // APP5 - case 0xe6: // APP6 - case 0xe7: // APP7 - case 0xe8: // APP8 - case 0xe9: // APP9 - case 0xea: // APP10 - case 0xeb: // APP11 - case 0xec: // APP12 - case 0xed: // APP13 - case 0xee: // APP14 - case 0xef: // APP15 - /* falls through */ - case 0xfe: // COM - const markerLength = stream.getUint16(); - if (markerLength > 2) { - stream.skip(markerLength - 2); // Jump to the next marker. - } else { - // The marker length is invalid, resetting the stream position. - stream.skip(-2); - } - break; - - case 0xff: // Fill byte. - // Avoid skipping a valid marker, resetting the stream position. - stream.skip(-1); - break; - - case 0xd9: // EOI - foundSOF = true; - break; - } - if (foundSOF) { - break; - } - } - // Finally, don't forget to reset the stream position. - stream.pos = startPos; - - return shadow(this, "maybeValidDimensions", validDimensions); - }, - configurable: true, - }); - - JpegStream.prototype.getIR = function (forceDataSchema = false) { - return createObjectURL(this.bytes, "image/jpeg", forceDataSchema); - }; - return JpegStream; })(); diff --git a/src/core/worker.js b/src/core/worker.js index e03c98da2d435..1d1bd9acdbcd7 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -399,10 +399,8 @@ var WorkerMessageHandler = { ensureNotTerminated(); var evaluatorOptions = { - forceDataSchema: data.disableCreateObjectURL, maxImageSize: data.maxImageSize, disableFontFace: data.disableFontFace, - nativeImageDecoderSupport: data.nativeImageDecoderSupport, ignoreErrors: data.ignoreErrors, isEvalSupported: data.isEvalSupported, fontExtraProperties: data.fontExtraProperties, diff --git a/src/display/api.js b/src/display/api.js index a99cffeef896a..3d6ee47cc0c22 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -28,7 +28,6 @@ import { isArrayBuffer, isSameOrigin, MissingPDFException, - NativeImageDecoding, PasswordException, setVerbosityLevel, shadow, @@ -44,7 +43,6 @@ import { DOMCMapReaderFactory, loadScript, PageViewport, - releaseImageResources, RenderingCancelledException, StatTimer, } from "./display_utils.js"; @@ -116,14 +114,6 @@ function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) { * @property {string} [docBaseUrl] - The base URL of the document, * used when attempting to recover valid absolute URLs for annotations, and * outline items, that (incorrectly) only specify relative URLs. - * @property {string} [nativeImageDecoderSupport] - Strategy for - * decoding certain (simple) JPEG images in the browser. This is useful for - * environments without DOM image and canvas support, such as e.g. Node.js. - * Valid values are 'decode', 'display' or 'none'; where 'decode' is intended - * for browsers with full image/canvas support, 'display' for environments - * with limited image support through stubs (useful for SVG conversion), - * and 'none' where JPEG images will be decoded entirely by PDF.js. - * The default value is 'decode'. * @property {string} [cMapUrl] - The URL where the predefined * Adobe CMaps are located. Include trailing slash. * @property {boolean} [cMapPacked] - Specifies if the Adobe CMaps are @@ -164,9 +154,6 @@ function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) { * The default value is `false`. * NOTE: It is also necessary to disable streaming, see above, * in order for disabling of pre-fetching to work correctly. - * @property {boolean} [disableCreateObjectURL] - Disable the use of - * `URL.createObjectURL`, for compatibility with older browsers. - * The default value is `false`. * @property {boolean} [pdfBug] - Enables special hooks for debugging * PDF.js (see `web/debugger.js`). The default value is `false`. */ @@ -260,15 +247,6 @@ function getDocument(src) { params.fontExtraProperties = params.fontExtraProperties === true; params.pdfBug = params.pdfBug === true; - const NativeImageDecoderValues = Object.values(NativeImageDecoding); - if ( - params.nativeImageDecoderSupport === undefined || - !NativeImageDecoderValues.includes(params.nativeImageDecoderSupport) - ) { - params.nativeImageDecoderSupport = - apiCompatibilityParams.nativeImageDecoderSupport || - NativeImageDecoding.DECODE; - } if (!Number.isInteger(params.maxImageSize)) { params.maxImageSize = -1; } @@ -288,10 +266,6 @@ function getDocument(src) { if (typeof params.disableAutoFetch !== "boolean") { params.disableAutoFetch = false; } - if (typeof params.disableCreateObjectURL !== "boolean") { - params.disableCreateObjectURL = - apiCompatibilityParams.disableCreateObjectURL || false; - } // Set the main-thread verbosity level. setVerbosityLevel(params.verbosity); @@ -414,10 +388,8 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { }, maxImageSize: source.maxImageSize, disableFontFace: source.disableFontFace, - disableCreateObjectURL: source.disableCreateObjectURL, postMessageTransfers: worker.postMessageTransfers, docBaseUrl: source.docBaseUrl, - nativeImageDecoderSupport: source.nativeImageDecoderSupport, ignoreErrors: source.ignoreErrors, isEvalSupported: source.isEvalSupported, fontExtraProperties: source.fontExtraProperties, @@ -2309,26 +2281,6 @@ class WorkerTransport { } switch (type) { - case "JpegStream": - return new Promise((resolve, reject) => { - const img = new Image(); - img.onload = function () { - resolve(img); - }; - img.onerror = function () { - // 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. - reject(new Error("Error during JPEG image loading")); - - // Always remember to release the image data if errors occurred. - releaseImageResources(img); - }; - img.src = imageData; - }).then(img => { - pageProxy.objs.resolve(id, img); - }); case "Image": pageProxy.objs.resolve(id, imageData); @@ -2366,69 +2318,6 @@ class WorkerTransport { this._onUnsupportedFeature.bind(this) ); - messageHandler.on("JpegDecode", ([imageUrl, components]) => { - if (this.destroyed) { - return Promise.reject(new Error("Worker was destroyed")); - } - - if (typeof document === "undefined") { - // Make sure that this code is not executing in node.js, as - // it's using DOM image, and there is no library to support that. - return Promise.reject(new Error('"document" is not defined.')); - } - - if (components !== 3 && components !== 1) { - return Promise.reject( - new Error("Only 3 components or 1 component can be returned") - ); - } - - return new Promise(function (resolve, reject) { - const img = new Image(); - img.onload = function () { - const { width, height } = img; - const size = width * height; - const rgbaLength = size * 4; - const buf = new Uint8ClampedArray(size * components); - let tmpCanvas = document.createElement("canvas"); - tmpCanvas.width = width; - tmpCanvas.height = height; - let tmpCtx = tmpCanvas.getContext("2d"); - tmpCtx.drawImage(img, 0, 0); - const data = tmpCtx.getImageData(0, 0, width, height).data; - - if (components === 3) { - for (let i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { - buf[j] = data[i]; - buf[j + 1] = data[i + 1]; - buf[j + 2] = data[i + 2]; - } - } else if (components === 1) { - for (let i = 0, j = 0; i < rgbaLength; i += 4, j++) { - buf[j] = data[i]; - } - } - resolve({ data: buf, width, height }); - - // Immediately release the image data once decoding has finished. - releaseImageResources(img); - // Zeroing the width and height cause Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - tmpCanvas.width = 0; - tmpCanvas.height = 0; - tmpCanvas = null; - tmpCtx = null; - }; - img.onerror = function () { - reject(new Error("JpegDecode failed to load image")); - - // Always remember to release the image data if errors occurred. - releaseImageResources(img); - }; - img.src = imageUrl; - }); - }); - messageHandler.on("FetchBuiltInCMap", (data, sink) => { if (this.destroyed) { sink.error(new Error("Worker was destroyed")); @@ -2608,9 +2497,7 @@ class WorkerTransport { const params = this._params; return shadow(this, "loadingParams", { disableAutoFetch: params.disableAutoFetch, - disableCreateObjectURL: params.disableCreateObjectURL, disableFontFace: params.disableFontFace, - nativeImageDecoderSupport: params.nativeImageDecoderSupport, }); } } @@ -2684,14 +2571,6 @@ class PDFObjects { } clear() { - for (const objId in this._objs) { - const { data } = this._objs[objId]; - - if (typeof Image !== "undefined" && data instanceof Image) { - // Always release the image data when clearing out the cached objects. - releaseImageResources(data); - } - } this._objs = Object.create(null); } } diff --git a/src/display/api_compatibility.js b/src/display/api_compatibility.js index 01c185b991960..d610e3fae43c2 100644 --- a/src/display/api_compatibility.js +++ b/src/display/api_compatibility.js @@ -17,27 +17,11 @@ import { isNodeJS } from "../shared/is_node.js"; const compatibilityParams = Object.create(null); if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { - const userAgent = - (typeof navigator !== "undefined" && navigator.userAgent) || ""; - const isIE = /Trident/.test(userAgent); - const isIOSChrome = /CriOS/.test(userAgent); - - // Checks if possible to use URL.createObjectURL() - // Support: IE, Chrome on iOS - (function checkOnBlobSupport() { - // Sometimes IE and Chrome on iOS losing the data created with - // createObjectURL(), see issues #3977 and #8081. - if (isIE || isIOSChrome) { - compatibilityParams.disableCreateObjectURL = true; - } - })(); - // Support: Node.js - (function checkFontFaceAndImage() { - // Node.js is missing native support for `@font-face` and `Image`. + (function checkFontFace() { + // Node.js is missing native support for `@font-face`. if (isNodeJS) { compatibilityParams.disableFontFace = true; - compatibilityParams.nativeImageDecoderSupport = "none"; } })(); } diff --git a/src/display/canvas.js b/src/display/canvas.js index 0cd2108f34dba..8cae8db99de58 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -2113,46 +2113,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.restore(); }, - paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) { - const domImage = objId.startsWith("g_") - ? this.commonObjs.get(objId) - : this.objs.get(objId); - if (!domImage) { - warn("Dependent image isn't ready yet"); - return; - } - - this.save(); - - var ctx = this.ctx; - // scale the image to the unit square - ctx.scale(1 / w, -1 / h); - - ctx.drawImage( - domImage, - 0, - 0, - domImage.width, - domImage.height, - 0, - -h, - w, - h - ); - if (this.imageLayer) { - var currentTransform = ctx.mozCurrentTransformInverse; - var position = this.getCanvasPosition(0, 0); - this.imageLayer.appendImage({ - objId, - left: position[0], - top: position[1], - width: w / currentTransform[0], - height: h / currentTransform[3], - }); - } - this.restore(); - }, - paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) { var ctx = this.ctx; var width = img.width, @@ -2353,9 +2313,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var paintWidth = width, paintHeight = height; var tmpCanvasId = "prescale1"; - // Vertial or horizontal scaling shall not be more than 2 to not loose the + // Vertical or horizontal scaling shall not be more than 2 to not lose the // pixels during drawImage operation, painting on the temporary canvas(es) - // that are twice smaller in size + // that are twice smaller in size. while ( (widthScale > 2 && paintWidth > 1) || (heightScale > 2 && paintHeight > 1) diff --git a/src/display/display_utils.js b/src/display/display_utils.js index 3be6ba0219608..4578a7d594edd 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -517,20 +517,6 @@ function deprecated(details) { console.log("Deprecated API usage: " + details); } -function releaseImageResources(img) { - assert(img instanceof Image, "Invalid `img` parameter."); - - const url = img.src; - if ( - typeof url === "string" && - url.startsWith("blob:") && - URL.revokeObjectURL - ) { - URL.revokeObjectURL(url); - } - img.removeAttribute("src"); -} - let pdfDateStringRegex; class PDFDateString { @@ -631,6 +617,5 @@ export { isValidFetchUrl, loadScript, deprecated, - releaseImageResources, PDFDateString, }; diff --git a/src/display/svg.js b/src/display/svg.js index 8e27f38d5c1d1..4631b5d4fd08e 100644 --- a/src/display/svg.js +++ b/src/display/svg.js @@ -440,7 +440,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { // eslint-disable-next-line no-shadow SVGGraphics = class SVGGraphics { - constructor(commonObjs, objs, forceDataSchema) { + constructor(commonObjs, objs, forceDataSchema = false) { this.svgFactory = new DOMSVGFactory(); this.current = new SVGExtraState(); @@ -664,9 +664,6 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { case OPS.paintSolidColorImageMask: this.paintSolidColorImageMask(); break; - case OPS.paintJpegXObject: - this.paintJpegXObject(args[0], args[1], args[2]); - break; case OPS.paintImageXObject: this.paintImageXObject(args[0]); break; @@ -1559,23 +1556,6 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { this._ensureTransformGroup().appendChild(rect); } - paintJpegXObject(objId, w, h) { - const imgObj = this.objs.get(objId); - const imgEl = this.svgFactory.createElement("svg:image"); - imgEl.setAttributeNS(XLINK_NS, "xlink:href", imgObj.src); - imgEl.setAttributeNS(null, "width", pf(w)); - imgEl.setAttributeNS(null, "height", pf(h)); - imgEl.setAttributeNS(null, "x", "0"); - imgEl.setAttributeNS(null, "y", pf(-h)); - imgEl.setAttributeNS( - null, - "transform", - `scale(${pf(1 / w)} ${pf(-1 / h)})` - ); - - this._ensureTransformGroup().appendChild(imgEl); - } - paintImageXObject(objId) { const imgData = this.objs.get(objId); if (!imgData) { diff --git a/src/pdf.js b/src/pdf.js index bd6f129f112a9..b318f4675335d 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -39,7 +39,6 @@ import { createValidAbsoluteUrl, InvalidPDFException, MissingPDFException, - NativeImageDecoding, OPS, PasswordResponses, PermissionFlag, @@ -143,7 +142,6 @@ export { createValidAbsoluteUrl, InvalidPDFException, MissingPDFException, - NativeImageDecoding, OPS, PasswordResponses, PermissionFlag, diff --git a/src/shared/util.js b/src/shared/util.js index dcbf5b28dfb7b..eeb666d204c30 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -19,12 +19,6 @@ import "./compatibility.js"; const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; -const NativeImageDecoding = { - NONE: "none", - DECODE: "decode", - DISPLAY: "display", -}; - // Permission flags from Table 22, Section 7.6.3.2 of the PDF specification. const PermissionFlag = { PRINT: 0x04, @@ -917,7 +911,6 @@ export { AbortException, InvalidPDFException, MissingPDFException, - NativeImageDecoding, PasswordException, PasswordResponses, PermissionFlag, diff --git a/test/driver.js b/test/driver.js index 53729a785690f..8a2720892a490 100644 --- a/test/driver.js +++ b/test/driver.js @@ -387,7 +387,6 @@ var Driver = (function DriverClosure() { const loadingTask = pdfjsLib.getDocument({ url: absoluteUrl, password: task.password, - nativeImageDecoderSupport: task.nativeImageDecoderSupport, cMapUrl: CMAP_URL, cMapPacked: CMAP_PACKED, disableRange: task.disableRange, diff --git a/test/test_manifest.json b/test/test_manifest.json index 6c757ede546e7..cb3b99ea7e362 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -1637,8 +1637,7 @@ "link": true, "firstPage": 2, "lastPage": 2, - "type": "eq", - "nativeImageDecoderSupport": "none" + "type": "eq" }, { "id": "issue10529", "file": "pdfs/issue10529.pdf", @@ -4181,22 +4180,12 @@ "link": true, "type": "eq" }, - { "id": "issue4926-built-in-jpg", - "file": "pdfs/issue4926.pdf", - "md5": "ed881c8ea2f9bc4be94ecb7f2b2c149b", - "rounds": 1, - "link": true, - "lastPage": 1, - "type": "eq", - "nativeImageDecoderSupport": "none" - }, { "id": "decodeACSuccessive", "file": "pdfs/decodeACSuccessive.pdf", "md5": "7749c032624fe27ab8e8d7d5e9a4a93f", "rounds": 1, "link": false, - "type": "eq", - "nativeImageDecoderSupport": "none" + "type": "eq" }, { "id": "issue5592", "file": "pdfs/issue5592.pdf", diff --git a/test/unit/display_svg_spec.js b/test/unit/display_svg_spec.js index 0dce86043e443..30eec2000f2ba 100644 --- a/test/unit/display_svg_spec.js +++ b/test/unit/display_svg_spec.js @@ -18,7 +18,6 @@ import { setStubs, unsetStubs } from "../../examples/node/domstubs.js"; import { buildGetDocumentParams } from "./test_utils.js"; import { getDocument } from "../../src/display/api.js"; import { isNodeJS } from "../../src/shared/is_node.js"; -import { NativeImageDecoding } from "../../src/shared/util.js"; import { SVGGraphics } from "../../src/display/svg.js"; const XLINK_NS = "http://www.w3.org/1999/xlink"; @@ -62,11 +61,7 @@ describe("SVGGraphics", function () { var loadingTask; var page; beforeAll(function (done) { - loadingTask = getDocument( - buildGetDocumentParams("xobject-image.pdf", { - nativeImageDecoderSupport: NativeImageDecoding.DISPLAY, - }) - ); + loadingTask = getDocument(buildGetDocumentParams("xobject-image.pdf")); loadingTask.promise.then(function (doc) { doc.getPage(1).then(function (firstPage) { page = firstPage; diff --git a/web/app.js b/web/app.js index f4956dd65b6fa..1d442f0a189aa 100644 --- a/web/app.js +++ b/web/app.js @@ -2223,7 +2223,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { } const file = evt.fileInput.files[0]; - if (URL.createObjectURL && !AppOptions.get("disableCreateObjectURL")) { + if (!AppOptions.get("disableCreateObjectURL")) { let url = URL.createObjectURL(file); if (file.name) { url = { url, originalUrl: file.name }; diff --git a/web/app_options.js b/web/app_options.js index f3ac76d8a2357..8b68d7525c530 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -13,7 +13,6 @@ * limitations under the License. */ -import { apiCompatibilityParams } from "pdfjs-lib"; import { viewerCompatibilityParams } from "./viewer_compatibility.js"; const OptionKind = { @@ -43,6 +42,12 @@ const defaultOptions = { value: "", kind: OptionKind.VIEWER + OptionKind.PREFERENCE, }, + disableCreateObjectURL: { + /** @type {boolean} */ + value: false, + compatibility: viewerCompatibilityParams.disableCreateObjectURL, + kind: OptionKind.VIEWER, + }, disableHistory: { /** @type {boolean} */ value: false, @@ -174,12 +179,6 @@ const defaultOptions = { value: false, kind: OptionKind.API + OptionKind.PREFERENCE, }, - disableCreateObjectURL: { - /** @type {boolean} */ - value: false, - compatibility: apiCompatibilityParams.disableCreateObjectURL, - kind: OptionKind.API, - }, disableFontFace: { /** @type {boolean} */ value: false, diff --git a/web/download_manager.js b/web/download_manager.js index 5c54015503bae..00ec1675d3540 100644 --- a/web/download_manager.js +++ b/web/download_manager.js @@ -13,11 +13,8 @@ * limitations under the License. */ -import { - apiCompatibilityParams, - createObjectURL, - createValidAbsoluteUrl, -} from "pdfjs-lib"; +import { createObjectURL, createValidAbsoluteUrl } from "pdfjs-lib"; +import { viewerCompatibilityParams } from "./viewer_compatibility.js"; if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("CHROME || GENERIC")) { throw new Error( @@ -27,7 +24,7 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("CHROME || GENERIC")) { } const DISABLE_CREATE_OBJECT_URL = - apiCompatibilityParams.disableCreateObjectURL || false; + viewerCompatibilityParams.disableCreateObjectURL || false; function download(blobUrl, filename) { const a = document.createElement("a"); diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index 198162b93009b..529c7712162f4 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -74,14 +74,10 @@ class PDFAttachmentViewer { } /** + * NOTE: Should only be used when `URL.createObjectURL` is natively supported. * @private */ _bindPdfLink(button, content, filename) { - if (this.downloadManager.disableCreateObjectURL) { - throw new Error( - 'bindPdfLink: Unsupported "disableCreateObjectURL" value.' - ); - } let blobUrl; button.onclick = () => { if (!blobUrl) { diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index d6f5b331187e0..d16792c7fac49 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -65,8 +65,7 @@ function PDFPrintService(pdfDocument, pagesOverview, printContainer, l10n) { this.pagesOverview = pagesOverview; this.printContainer = printContainer; this.l10n = l10n || NullL10n; - this.disableCreateObjectURL = - pdfDocument.loadingParams.disableCreateObjectURL; + this.disableCreateObjectURL = AppOptions.get("disableCreateObjectURL"); this.currentPage = -1; // The temporary canvas where renderPage paints one page at a time. this.scratchCanvas = document.createElement("canvas"); diff --git a/web/viewer_compatibility.js b/web/viewer_compatibility.js index db8421a79f330..b998f49ae87b0 100644 --- a/web/viewer_compatibility.js +++ b/web/viewer_compatibility.js @@ -23,9 +23,21 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { (typeof navigator !== "undefined" && navigator.maxTouchPoints) || 1; const isAndroid = /Android/.test(userAgent); + const isIE = /Trident/.test(userAgent); const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || (platform === "MacIntel" && maxTouchPoints > 1); + const isIOSChrome = /CriOS/.test(userAgent); + + // Checks if possible to use URL.createObjectURL() + // Support: IE, Chrome on iOS + (function checkOnBlobSupport() { + // Sometimes IE and Chrome on iOS losing the data created with + // createObjectURL(), see issues #3977 and #8081. + if (isIE || isIOSChrome) { + compatibilityParams.disableCreateObjectURL = true; + } + })(); // Limit canvas size to 5 mega-pixels on mobile. // Support: Android, iOS