diff --git a/src/display/api.js b/src/display/api.js index a72fc525f..9f34e916f 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1629,8 +1629,8 @@ var PDFWorker = (function PDFWorkerClosure() { * For internal use only. * @ignore */ -var WorkerTransport = (function WorkerTransportClosure() { - function WorkerTransport(messageHandler, loadingTask, networkStream, params) { +class WorkerTransport { + constructor(messageHandler, loadingTask, networkStream, params) { this.messageHandler = messageHandler; this.loadingTask = loadingTask; this.commonObjs = new PDFObjects(); @@ -1655,553 +1655,531 @@ var WorkerTransport = (function WorkerTransportClosure() { this.setupMessageHandler(); } - WorkerTransport.prototype = { - destroy: function WorkerTransport_destroy() { - if (this.destroyCapability) { - return this.destroyCapability.promise; - } - this.destroyed = true; - this.destroyCapability = createPromiseCapability(); - - if (this._passwordCapability) { - this._passwordCapability.reject( - new Error('Worker was destroyed during onPassword callback')); - } - - var waitOn = []; - // We need to wait for all renderings to be completed, e.g. - // timeout/rAF can take a long time. - this.pageCache.forEach(function (page) { - if (page) { - waitOn.push(page._destroy()); - } - }); - this.pageCache = []; - this.pagePromises = []; - // We also need to wait for the worker to finish its long running tasks. - var terminated = this.messageHandler.sendWithPromise('Terminate', null); - waitOn.push(terminated); - Promise.all(waitOn).then(() => { - this.fontLoader.clear(); - if (this._networkStream) { - this._networkStream.cancelAllRequests(); - } - - if (this.messageHandler) { - this.messageHandler.destroy(); - this.messageHandler = null; - } - this.destroyCapability.resolve(); - }, this.destroyCapability.reject); + destroy() { + if (this.destroyCapability) { return this.destroyCapability.promise; - }, + } - setupMessageHandler: function WorkerTransport_setupMessageHandler() { - var messageHandler = this.messageHandler; - var loadingTask = this.loadingTask; + this.destroyed = true; + this.destroyCapability = createPromiseCapability(); - messageHandler.on('GetReader', function(data, sink) { - assert(this._networkStream); - this._fullReader = this._networkStream.getFullReader(); - this._fullReader.onProgress = (evt) => { - this._lastProgress = { - loaded: evt.loaded, - total: evt.total, - }; + if (this._passwordCapability) { + this._passwordCapability.reject( + new Error('Worker was destroyed during onPassword callback')); + } + + const waitOn = []; + // We need to wait for all renderings to be completed, e.g. + // timeout/rAF can take a long time. + this.pageCache.forEach(function(page) { + if (page) { + waitOn.push(page._destroy()); + } + }); + this.pageCache = []; + this.pagePromises = []; + // We also need to wait for the worker to finish its long running tasks. + const terminated = this.messageHandler.sendWithPromise('Terminate', null); + waitOn.push(terminated); + Promise.all(waitOn).then(() => { + this.fontLoader.clear(); + if (this._networkStream) { + this._networkStream.cancelAllRequests(); + } + + if (this.messageHandler) { + this.messageHandler.destroy(); + this.messageHandler = null; + } + this.destroyCapability.resolve(); + }, this.destroyCapability.reject); + return this.destroyCapability.promise; + } + + setupMessageHandler() { + const { messageHandler, loadingTask, } = this; + + messageHandler.on('GetReader', function(data, sink) { + assert(this._networkStream); + this._fullReader = this._networkStream.getFullReader(); + this._fullReader.onProgress = (evt) => { + this._lastProgress = { + loaded: evt.loaded, + total: evt.total, }; - sink.onPull = () => { - this._fullReader.read().then(function({ value, done, }) { - if (done) { - sink.close(); - return; - } - assert(isArrayBuffer(value)); - // Enqueue data chunk into sink, and transfer it - // to other side as `Transferable` object. - sink.enqueue(new Uint8Array(value), 1, [value]); - }).catch((reason) => { - sink.error(reason); - }); - }; - - sink.onCancel = (reason) => { - this._fullReader.cancel(reason); - }; - }, this); - - messageHandler.on('ReaderHeadersReady', function(data) { - let headersCapability = createPromiseCapability(); - let fullReader = this._fullReader; - fullReader.headersReady.then(() => { - // If stream or range are disabled, it's our only way to report - // loading progress. - if (!fullReader.isStreamingSupported || - !fullReader.isRangeSupported) { - if (this._lastProgress) { - let loadingTask = this.loadingTask; - if (loadingTask.onProgress) { - loadingTask.onProgress(this._lastProgress); - } - } - fullReader.onProgress = (evt) => { - let loadingTask = this.loadingTask; - if (loadingTask.onProgress) { - loadingTask.onProgress({ - loaded: evt.loaded, - total: evt.total, - }); - } - }; + }; + sink.onPull = () => { + this._fullReader.read().then(function({ value, done, }) { + if (done) { + sink.close(); + return; } + assert(isArrayBuffer(value)); + // Enqueue data chunk into sink, and transfer it + // to other side as `Transferable` object. + sink.enqueue(new Uint8Array(value), 1, [value]); + }).catch((reason) => { + sink.error(reason); + }); + }; - headersCapability.resolve({ - isStreamingSupported: fullReader.isStreamingSupported, - isRangeSupported: fullReader.isRangeSupported, - contentLength: fullReader.contentLength, - }); - }, headersCapability.reject); + sink.onCancel = (reason) => { + this._fullReader.cancel(reason); + }; + }, this); - return headersCapability.promise; - }, this); - - messageHandler.on('GetRangeReader', function(data, sink) { - assert(this._networkStream); - let _rangeReader = - this._networkStream.getRangeReader(data.begin, data.end); - - sink.onPull = () => { - _rangeReader.read().then(function({ value, done, }) { - if (done) { - sink.close(); - return; + messageHandler.on('ReaderHeadersReady', function(data) { + const headersCapability = createPromiseCapability(); + const fullReader = this._fullReader; + fullReader.headersReady.then(() => { + // If stream or range are disabled, it's our only way to report + // loading progress. + if (!fullReader.isStreamingSupported || + !fullReader.isRangeSupported) { + if (this._lastProgress) { + if (loadingTask.onProgress) { + loadingTask.onProgress(this._lastProgress); } - assert(isArrayBuffer(value)); - sink.enqueue(new Uint8Array(value), 1, [value]); - }).catch((reason) => { - sink.error(reason); - }); - }; - - sink.onCancel = (reason) => { - _rangeReader.cancel(reason); - }; - }, this); - - messageHandler.on('GetDoc', function transportDoc({ pdfInfo, }) { - this.numPages = pdfInfo.numPages; - var loadingTask = this.loadingTask; - var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask); - this.pdfDocument = pdfDocument; - loadingTask._capability.resolve(pdfDocument); - }, this); - - messageHandler.on('PasswordRequest', - function transportPasswordRequest(exception) { - this._passwordCapability = createPromiseCapability(); - - if (loadingTask.onPassword) { - var updatePassword = (password) => { - this._passwordCapability.resolve({ - password, - }); - }; - try { - loadingTask.onPassword(updatePassword, exception.code); - } catch (ex) { - this._passwordCapability.reject(ex); } - } else { - this._passwordCapability.reject( - new PasswordException(exception.message, exception.code)); + fullReader.onProgress = (evt) => { + if (loadingTask.onProgress) { + loadingTask.onProgress({ + loaded: evt.loaded, + total: evt.total, + }); + } + }; } - return this._passwordCapability.promise; - }, this); - messageHandler.on('PasswordException', - function transportPasswordException(exception) { - loadingTask._capability.reject( + headersCapability.resolve({ + isStreamingSupported: fullReader.isStreamingSupported, + isRangeSupported: fullReader.isRangeSupported, + contentLength: fullReader.contentLength, + }); + }, headersCapability.reject); + + return headersCapability.promise; + }, this); + + messageHandler.on('GetRangeReader', function(data, sink) { + assert(this._networkStream); + const rangeReader = + this._networkStream.getRangeReader(data.begin, data.end); + + sink.onPull = () => { + rangeReader.read().then(function({ value, done, }) { + if (done) { + sink.close(); + return; + } + assert(isArrayBuffer(value)); + sink.enqueue(new Uint8Array(value), 1, [value]); + }).catch((reason) => { + sink.error(reason); + }); + }; + + sink.onCancel = (reason) => { + rangeReader.cancel(reason); + }; + }, this); + + messageHandler.on('GetDoc', function({ pdfInfo, }) { + this.numPages = pdfInfo.numPages; + this.pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask); + loadingTask._capability.resolve(this.pdfDocument); + }, this); + + messageHandler.on('PasswordRequest', function(exception) { + this._passwordCapability = createPromiseCapability(); + + if (loadingTask.onPassword) { + const updatePassword = (password) => { + this._passwordCapability.resolve({ + password, + }); + }; + try { + loadingTask.onPassword(updatePassword, exception.code); + } catch (ex) { + this._passwordCapability.reject(ex); + } + } else { + this._passwordCapability.reject( new PasswordException(exception.message, exception.code)); - }, this); + } + return this._passwordCapability.promise; + }, this); - messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) { - this.loadingTask._capability.reject( - new InvalidPDFException(exception.message)); - }, this); + messageHandler.on('PasswordException', function(exception) { + loadingTask._capability.reject( + new PasswordException(exception.message, exception.code)); + }, this); - messageHandler.on('MissingPDF', function transportMissingPDF(exception) { - this.loadingTask._capability.reject( - new MissingPDFException(exception.message)); - }, this); + messageHandler.on('InvalidPDF', function(exception) { + loadingTask._capability.reject( + new InvalidPDFException(exception.message)); + }, this); - messageHandler.on('UnexpectedResponse', - function transportUnexpectedResponse(exception) { - this.loadingTask._capability.reject( - new UnexpectedResponseException(exception.message, exception.status)); - }, this); + messageHandler.on('MissingPDF', function(exception) { + loadingTask._capability.reject( + new MissingPDFException(exception.message)); + }, this); - messageHandler.on('UnknownError', - function transportUnknownError(exception) { - this.loadingTask._capability.reject( - new UnknownErrorException(exception.message, exception.details)); - }, this); + messageHandler.on('UnexpectedResponse', function(exception) { + loadingTask._capability.reject( + new UnexpectedResponseException(exception.message, exception.status)); + }, this); - messageHandler.on('DataLoaded', function transportPage(data) { - this.downloadInfoCapability.resolve(data); - }, this); + messageHandler.on('UnknownError', function(exception) { + loadingTask._capability.reject( + new UnknownErrorException(exception.message, exception.details)); + }, this); - messageHandler.on('StartRenderPage', function transportRender(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - var page = this.pageCache[data.pageIndex]; + messageHandler.on('DataLoaded', function(data) { + this.downloadInfoCapability.resolve(data); + }, this); - page._stats.timeEnd('Page Request'); - page._startRenderPage(data.transparency, data.intent); - }, this); - - messageHandler.on('RenderPageChunk', function transportRender(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - var page = this.pageCache[data.pageIndex]; - - page._renderPageChunk(data.operatorList, data.intent); - }, this); - - messageHandler.on('commonobj', function transportObj(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - - var id = data[0]; - var type = data[1]; - if (this.commonObjs.hasData(id)) { - return; - } - - switch (type) { - case 'Font': - var exportedData = data[2]; - let params = this._params; - - if ('error' in exportedData) { - var exportedError = exportedData.error; - warn('Error during font loading: ' + exportedError); - this.commonObjs.resolve(id, exportedError); - break; - } - var fontRegistry = null; - if (params.pdfBug && globalScope.FontInspector && - globalScope.FontInspector.enabled) { - fontRegistry = { - registerFont(font, url) { - globalScope['FontInspector'].fontAdded(font, url); - }, - }; - } - var font = new FontFaceObject(exportedData, { - isEvalSupported: params.isEvalSupported, - disableFontFace: params.disableFontFace, - ignoreErrors: params.ignoreErrors, - onUnsupportedFeature: this._onUnsupportedFeature.bind(this), - fontRegistry, - }); - var fontReady = (fontObjs) => { - this.commonObjs.resolve(id, font); - }; - - this.fontLoader.bind([font], fontReady); - break; - case 'FontPath': - this.commonObjs.resolve(id, data[2]); - break; - default: - throw new Error(`Got unknown common object type ${type}`); - } - }, this); - - messageHandler.on('obj', function transportObj(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - - var id = data[0]; - var pageIndex = data[1]; - var type = data[2]; - var pageProxy = this.pageCache[pageIndex]; - var imageData; - if (pageProxy.objs.hasData(id)) { - return; - } - - switch (type) { - case 'JpegStream': - imageData = data[3]; - 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); - }); - case 'Image': - imageData = data[3]; - pageProxy.objs.resolve(id, imageData); - - // heuristics that will allow not to store large data - var MAX_IMAGE_SIZE_TO_STORE = 8000000; - if (imageData && 'data' in imageData && - imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) { - pageProxy.cleanupAfterRender = true; - } - break; - default: - throw new Error(`Got unknown object type ${type}`); - } - }, this); - - messageHandler.on('DocProgress', function transportDocProgress(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - - var loadingTask = this.loadingTask; - if (loadingTask.onProgress) { - loadingTask.onProgress({ - loaded: data.loaded, - total: data.total, - }); - } - }, this); - - messageHandler.on('PageError', function transportError(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - - var page = this.pageCache[data.pageNum - 1]; - var intentState = page.intentStates[data.intent]; - - if (intentState.displayReadyCapability) { - intentState.displayReadyCapability.reject(data.error); - } else { - throw new Error(data.error); - } - - if (intentState.operatorList) { - // Mark operator list as complete. - intentState.operatorList.lastChunk = true; - for (var i = 0; i < intentState.renderTasks.length; i++) { - intentState.renderTasks[i].operatorListChanged(); - } - } - }, this); - - messageHandler.on('UnsupportedFeature', this._onUnsupportedFeature, this); - - messageHandler.on('JpegDecode', function(data) { - 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.')); - } - - var imageUrl = data[0]; - var components = data[1]; - 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) { - var img = new Image(); - img.onload = function () { - var width = img.width; - var height = img.height; - var size = width * height; - var rgbaLength = size * 4; - var buf = new Uint8ClampedArray(size * components); - var tmpCanvas = document.createElement('canvas'); - tmpCanvas.width = width; - tmpCanvas.height = height; - var tmpCtx = tmpCanvas.getContext('2d'); - tmpCtx.drawImage(img, 0, 0); - var data = tmpCtx.getImageData(0, 0, width, height).data; - var i, j; - - if (components === 3) { - for (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 (i = 0, j = 0; i < rgbaLength; i += 4, j++) { - buf[j] = data[i]; - } - } - resolve({ data: buf, width, height, }); - }; - img.onerror = function () { - reject(new Error('JpegDecode failed to load image')); - }; - img.src = imageUrl; - }); - }, this); - - messageHandler.on('FetchBuiltInCMap', function (data) { - if (this.destroyed) { - return Promise.reject(new Error('Worker was destroyed')); - } - return this.CMapReaderFactory.fetch({ - name: data.name, - }); - }, this); - }, - - _onUnsupportedFeature({ featureId, }) { + messageHandler.on('StartRenderPage', function(data) { if (this.destroyed) { return; // Ignore any pending requests if the worker was terminated. } - let loadingTask = this.loadingTask; - if (loadingTask.onUnsupportedFeature) { - loadingTask.onUnsupportedFeature(featureId); - } - }, - getData: function WorkerTransport_getData() { - return this.messageHandler.sendWithPromise('GetData', null); - }, + const page = this.pageCache[data.pageIndex]; + page._stats.timeEnd('Page Request'); + page._startRenderPage(data.transparency, data.intent); + }, this); - getPage(pageNumber) { - if (!Number.isInteger(pageNumber) || - pageNumber <= 0 || pageNumber > this.numPages) { - return Promise.reject(new Error('Invalid page request')); + messageHandler.on('RenderPageChunk', function(data) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. } - var pageIndex = pageNumber - 1; - if (pageIndex in this.pagePromises) { - return this.pagePromises[pageIndex]; + const page = this.pageCache[data.pageIndex]; + page._renderPageChunk(data.operatorList, data.intent); + }, this); + + messageHandler.on('commonobj', function(data) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. } - var promise = this.messageHandler.sendWithPromise('GetPage', { - pageIndex, - }).then((pageInfo) => { - if (this.destroyed) { - throw new Error('Transport destroyed'); - } - let page = new PDFPageProxy(pageIndex, pageInfo, this, - this._params.pdfBug); - this.pageCache[pageIndex] = page; - return page; - }); - this.pagePromises[pageIndex] = promise; - return promise; - }, - getPageIndex: function WorkerTransport_getPageIndexByRef(ref) { - return this.messageHandler.sendWithPromise('GetPageIndex', { - ref, - }).catch(function (reason) { - return Promise.reject(new Error(reason)); - }); - }, - - getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) { - return this.messageHandler.sendWithPromise('GetAnnotations', { - pageIndex, - intent, - }); - }, - - getDestinations: function WorkerTransport_getDestinations() { - return this.messageHandler.sendWithPromise('GetDestinations', null); - }, - - getDestination: function WorkerTransport_getDestination(id) { - if (typeof id !== 'string') { - return Promise.reject(new Error('Invalid destination request.')); + const [id, type, exportedData] = data; + if (this.commonObjs.hasData(id)) { + return; } - return this.messageHandler.sendWithPromise('GetDestination', { - id, - }); - }, - getPageLabels: function WorkerTransport_getPageLabels() { - return this.messageHandler.sendWithPromise('GetPageLabels', null); - }, + switch (type) { + case 'Font': + const params = this._params; - getPageMode() { - return this.messageHandler.sendWithPromise('GetPageMode', null); - }, - - getAttachments: function WorkerTransport_getAttachments() { - return this.messageHandler.sendWithPromise('GetAttachments', null); - }, - - getJavaScript: function WorkerTransport_getJavaScript() { - return this.messageHandler.sendWithPromise('GetJavaScript', null); - }, - - getOutline: function WorkerTransport_getOutline() { - return this.messageHandler.sendWithPromise('GetOutline', null); - }, - - getMetadata: function WorkerTransport_getMetadata() { - return this.messageHandler.sendWithPromise('GetMetadata', null). - then((results) => { - return { - info: results[0], - metadata: (results[1] ? new Metadata(results[1]) : null), - contentDispositionFilename: (this._fullReader ? - this._fullReader.filename : null), - }; - }); - }, - - getStats: function WorkerTransport_getStats() { - return this.messageHandler.sendWithPromise('GetStats', null); - }, - - startCleanup: function WorkerTransport_startCleanup() { - this.messageHandler.sendWithPromise('Cleanup', null).then(() => { - for (var i = 0, ii = this.pageCache.length; i < ii; i++) { - var page = this.pageCache[i]; - if (page) { - page.cleanup(); + if ('error' in exportedData) { + const exportedError = exportedData.error; + warn(`Error during font loading: ${exportedError}`); + this.commonObjs.resolve(id, exportedError); + break; } + + let fontRegistry = null; + if (params.pdfBug && globalScope.FontInspector && + globalScope.FontInspector.enabled) { + fontRegistry = { + registerFont(font, url) { + globalScope['FontInspector'].fontAdded(font, url); + }, + }; + } + const font = new FontFaceObject(exportedData, { + isEvalSupported: params.isEvalSupported, + disableFontFace: params.disableFontFace, + ignoreErrors: params.ignoreErrors, + onUnsupportedFeature: this._onUnsupportedFeature.bind(this), + fontRegistry, + }); + const fontReady = (fontObjs) => { + this.commonObjs.resolve(id, font); + }; + + this.fontLoader.bind([font], fontReady); + break; + case 'FontPath': + this.commonObjs.resolve(id, exportedData); + break; + default: + throw new Error(`Got unknown common object type ${type}`); + } + }, this); + + messageHandler.on('obj', function(data) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. + } + + const [id, pageIndex, type, imageData] = data; + const pageProxy = this.pageCache[pageIndex]; + if (pageProxy.objs.hasData(id)) { + return; + } + + switch (type) { + case 'JpegStream': + 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); + }); + case 'Image': + pageProxy.objs.resolve(id, imageData); + + // Heuristic that will allow us not to store large data. + const MAX_IMAGE_SIZE_TO_STORE = 8000000; + if (imageData && 'data' in imageData && + imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) { + pageProxy.cleanupAfterRender = true; + } + break; + default: + throw new Error(`Got unknown object type ${type}`); + } + }, this); + + messageHandler.on('DocProgress', function(data) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. + } + + if (loadingTask.onProgress) { + loadingTask.onProgress({ + loaded: data.loaded, + total: data.total, + }); + } + }, this); + + messageHandler.on('PageError', function(data) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. + } + + const page = this.pageCache[data.pageNum - 1]; + const intentState = page.intentStates[data.intent]; + + if (intentState.displayReadyCapability) { + intentState.displayReadyCapability.reject(data.error); + } else { + throw new Error(data.error); + } + + if (intentState.operatorList) { + // Mark operator list as complete. + intentState.operatorList.lastChunk = true; + for (let i = 0; i < intentState.renderTasks.length; i++) { + intentState.renderTasks[i].operatorListChanged(); } - this.commonObjs.clear(); - this.fontLoader.clear(); - }); - }, + } + }, this); - get loadingParams() { - let params = this._params; - return shadow(this, 'loadingParams', { - disableAutoFetch: params.disableAutoFetch, - disableCreateObjectURL: params.disableCreateObjectURL, - disableFontFace: params.disableFontFace, - nativeImageDecoderSupport: params.nativeImageDecoderSupport, - }); - }, - }; - return WorkerTransport; + messageHandler.on('UnsupportedFeature', this._onUnsupportedFeature, this); -})(); + messageHandler.on('JpegDecode', function(data) { + 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.')); + } + + const [imageUrl, components] = data; + 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 = img.width; + const height = img.height; + const size = width * height; + const rgbaLength = size * 4; + const buf = new Uint8ClampedArray(size * components); + const tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = width; + tmpCanvas.height = height; + const 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, }); + }; + img.onerror = function () { + reject(new Error('JpegDecode failed to load image')); + }; + img.src = imageUrl; + }); + }, this); + + messageHandler.on('FetchBuiltInCMap', function(data) { + if (this.destroyed) { + return Promise.reject(new Error('Worker was destroyed')); + } + return this.CMapReaderFactory.fetch({ + name: data.name, + }); + }, this); + } + + _onUnsupportedFeature({ featureId, }) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. + } + if (this.loadingTask.onUnsupportedFeature) { + this.loadingTask.onUnsupportedFeature(featureId); + } + } + + getData() { + return this.messageHandler.sendWithPromise('GetData', null); + } + + getPage(pageNumber) { + if (!Number.isInteger(pageNumber) || + pageNumber <= 0 || pageNumber > this.numPages) { + return Promise.reject(new Error('Invalid page request')); + } + + const pageIndex = pageNumber - 1; + if (pageIndex in this.pagePromises) { + return this.pagePromises[pageIndex]; + } + const promise = this.messageHandler.sendWithPromise('GetPage', { + pageIndex, + }).then((pageInfo) => { + if (this.destroyed) { + throw new Error('Transport destroyed'); + } + const page = new PDFPageProxy(pageIndex, pageInfo, this, + this._params.pdfBug); + this.pageCache[pageIndex] = page; + return page; + }); + this.pagePromises[pageIndex] = promise; + return promise; + } + + getPageIndex(ref) { + return this.messageHandler.sendWithPromise('GetPageIndex', { + ref, + }).catch(function(reason) { + return Promise.reject(new Error(reason)); + }); + } + + getAnnotations(pageIndex, intent) { + return this.messageHandler.sendWithPromise('GetAnnotations', { + pageIndex, + intent, + }); + } + + getDestinations() { + return this.messageHandler.sendWithPromise('GetDestinations', null); + } + + getDestination(id) { + if (typeof id !== 'string') { + return Promise.reject(new Error('Invalid destination request.')); + } + return this.messageHandler.sendWithPromise('GetDestination', { + id, + }); + } + + getPageLabels() { + return this.messageHandler.sendWithPromise('GetPageLabels', null); + } + + getPageMode() { + return this.messageHandler.sendWithPromise('GetPageMode', null); + } + + getAttachments() { + return this.messageHandler.sendWithPromise('GetAttachments', null); + } + + getJavaScript() { + return this.messageHandler.sendWithPromise('GetJavaScript', null); + } + + getOutline() { + return this.messageHandler.sendWithPromise('GetOutline', null); + } + + getMetadata() { + return this.messageHandler.sendWithPromise('GetMetadata', null). + then((results) => { + return { + info: results[0], + metadata: (results[1] ? new Metadata(results[1]) : null), + contentDispositionFilename: (this._fullReader ? + this._fullReader.filename : null), + }; + }); + } + + getStats() { + return this.messageHandler.sendWithPromise('GetStats', null); + } + + startCleanup() { + this.messageHandler.sendWithPromise('Cleanup', null).then(() => { + for (let i = 0, ii = this.pageCache.length; i < ii; i++) { + const page = this.pageCache[i]; + if (page) { + page.cleanup(); + } + } + this.commonObjs.clear(); + this.fontLoader.clear(); + }); + } + + get loadingParams() { + const params = this._params; + return shadow(this, 'loadingParams', { + disableAutoFetch: params.disableAutoFetch, + disableCreateObjectURL: params.disableCreateObjectURL, + disableFontFace: params.disableFontFace, + nativeImageDecoderSupport: params.nativeImageDecoderSupport, + }); + } +} /** * A PDF document and page is built of many objects. E.g. there are objects