pdf.js/src/display/api.js

2178 lines
74 KiB
JavaScript
Raw Normal View History

2012-09-01 07:48:21 +09:00
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals pdfjsFilePath, pdfjsVersion, pdfjsBuild, requirejs, pdfjsLibs,
Switch to using ESLint, instead of JSHint, for linting *Please note that most of the necessary code adjustments were made in PR 7890.* ESLint has a number of advantageous properties, compared to JSHint. Among those are: - The ability to find subtle bugs, thanks to more rules (e.g. PR 7881). - Much more customizable in general, and many rules allow fine-tuned behaviour rather than the just the on/off rules in JSHint. - Many more rules that can help developers avoid bugs, and a lot of rules that can be used to enforce a consistent coding style. The latter should be particularily useful for new contributors (and reduce the amount of stylistic review comments necessary). - The ability to easily specify exactly what rules to use/not to use, as opposed to JSHint which has a default set. *Note:* in future JSHint version some of the rules we depend on will be removed, according to warnings in http://jshint.com/docs/options/, so we wouldn't be able to update without losing lint coverage. - More easily disable one, or more, rules temporarily. In JSHint this requires using a numeric code, which isn't very user friendly, whereas in ESLint the rule name is simply used instead. By default there's no rules enabled in ESLint, but there are some default rule sets available. However, to prevent linting failures if we update ESLint in the future, it seemed easier to just explicitly specify what rules we want. Obviously this makes the ESLint config file somewhat bigger than the old JSHint config file, but given how rarely that one has been updated over the years I don't think that matters too much. I've tried, to the best of my ability, to ensure that we enable the same rules for ESLint that we had for JSHint. Furthermore, I've also enabled a number of rules that seemed to make sense, both to catch possible errors *and* various style guide violations. Despite the ESLint README claiming that it's slower that JSHint, https://github.com/eslint/eslint#how-does-eslint-performance-compare-to-jshint, locally this patch actually reduces the runtime for `gulp` lint (by approximately 20-25%). A couple of stylistic rules that would have been nice to enable, but where our code currently differs to much to make it feasible: - `comma-dangle`, controls trailing commas in Objects and Arrays (among others). - `object-curly-spacing`, controls spacing inside of Objects. - `spaced-comment`, used to enforce spaces after `//` and `/*. (This is made difficult by the fact that there's still some usage of the old preprocessor left.) Rules that I indend to look into possibly enabling in follow-ups, if it seems to make sense: `no-else-return`, `no-lonely-if`, `brace-style` with the `allowSingleLine` parameter removed. Useful links: - http://eslint.org/docs/user-guide/configuring - http://eslint.org/docs/rules/
2016-12-15 23:52:29 +09:00
__webpack_require__ */
2012-04-10 14:20:57 +09:00
2013-10-16 19:07:27 +09:00
'use strict';
2012-09-05 02:22:32 +09:00
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define('pdfjs/display/api', ['exports', 'pdfjs/shared/util',
'pdfjs/display/font_loader', 'pdfjs/display/canvas',
'pdfjs/display/metadata', 'pdfjs/display/dom_utils',
'require'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports, require('../shared/util.js'), require('./font_loader.js'),
require('./canvas.js'), require('./metadata.js'),
require('./dom_utils.js'));
} else {
factory((root.pdfjsDisplayAPI = {}), root.pdfjsSharedUtil,
root.pdfjsDisplayFontLoader, root.pdfjsDisplayCanvas,
root.pdfjsDisplayMetadata, root.pdfjsDisplayDOMUtils);
}
}(this, function (exports, sharedUtil, displayFontLoader, displayCanvas,
displayMetadata, displayDOMUtils, amdRequire) {
var InvalidPDFException = sharedUtil.InvalidPDFException;
var MessageHandler = sharedUtil.MessageHandler;
var MissingPDFException = sharedUtil.MissingPDFException;
var PageViewport = sharedUtil.PageViewport;
var PasswordResponses = sharedUtil.PasswordResponses;
var PasswordException = sharedUtil.PasswordException;
var StatTimer = sharedUtil.StatTimer;
var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
var UnknownErrorException = sharedUtil.UnknownErrorException;
var Util = sharedUtil.Util;
var createPromiseCapability = sharedUtil.createPromiseCapability;
var error = sharedUtil.error;
var deprecated = sharedUtil.deprecated;
var getVerbosityLevel = sharedUtil.getVerbosityLevel;
var info = sharedUtil.info;
var isInt = sharedUtil.isInt;
var isArray = sharedUtil.isArray;
var isArrayBuffer = sharedUtil.isArrayBuffer;
var isSameOrigin = sharedUtil.isSameOrigin;
var loadJpegStream = sharedUtil.loadJpegStream;
var stringToBytes = sharedUtil.stringToBytes;
var globalScope = sharedUtil.globalScope;
var warn = sharedUtil.warn;
var FontFaceObject = displayFontLoader.FontFaceObject;
var FontLoader = displayFontLoader.FontLoader;
var CanvasGraphics = displayCanvas.CanvasGraphics;
var createScratchCanvas = displayCanvas.createScratchCanvas;
var Metadata = displayMetadata.Metadata;
var getDefaultSetting = displayDOMUtils.getDefaultSetting;
var DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
var isWorkerDisabled = false;
var workerSrc;
var isPostMessageTransfersDisabled = false;
var fakeWorkerFilesLoader = null;
var useRequireEnsure = false;
if (typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('GENERIC && !SINGLE_FILE')) {
// For GENERIC build we need add support of different fake file loaders
// for different frameworks.
if (typeof window === 'undefined') {
// node.js - disable worker and set require.ensure.
isWorkerDisabled = true;
if (typeof require.ensure === 'undefined') {
require.ensure = require('node-ensure');
}
useRequireEnsure = true;
}
if (typeof __webpack_require__ !== 'undefined') {
useRequireEnsure = true;
}
if (typeof requirejs !== 'undefined' && requirejs.toUrl) {
workerSrc = requirejs.toUrl('pdfjs-dist/build/pdf.worker.js');
}
var dynamicLoaderSupported =
typeof requirejs !== 'undefined' && requirejs.load;
fakeWorkerFilesLoader = useRequireEnsure ? (function (callback) {
require.ensure([], function () {
var worker = require('./pdf.worker.js');
callback(worker.WorkerMessageHandler);
});
}) : dynamicLoaderSupported ? (function (callback) {
requirejs(['pdfjs-dist/build/pdf.worker'], function (worker) {
callback(worker.WorkerMessageHandler);
});
}) : null;
}
/**
* Document initialization / loading parameters object.
*
* @typedef {Object} DocumentInitParameters
* @property {string} url - The URL of the PDF.
* @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays
* (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded,
* use atob() to convert it to a binary string first.
* @property {Object} httpHeaders - Basic authentication headers.
* @property {boolean} withCredentials - Indicates whether or not cross-site
* Access-Control requests should be made using credentials such as cookies
* or authorization headers. The default is false.
* @property {string} password - For decrypting password-protected PDFs.
* @property {TypedArray} initialData - A typed array with the first portion or
* all of the pdf data. Used by the extension since some data is already
* loaded before the switch to range requests.
* @property {number} length - The PDF file length. It's used for progress
* reports and range requests operations.
* @property {PDFDataRangeTransport} range
* @property {number} rangeChunkSize - Optional parameter to specify
* maximum number of bytes fetched per range request. The default value is
* 2^16 = 65536.
* @property {PDFWorker} worker - The worker that will be used for the loading
* and parsing of the PDF data.
* @property {string} docBaseUrl - (optional) 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.
*/
/**
* @typedef {Object} PDFDocumentStats
* @property {Array} streamTypes - Used stream types in the document (an item
* is set to true if specific stream ID was used in the document).
* @property {Array} fontTypes - Used font type in the document (an item is set
* to true if specific font ID was used in the document).
*/
2012-04-14 01:25:08 +09:00
/**
* This is the main entry point for loading a PDF and interacting with it.
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
* is used, which means it must follow the same origin rules that any XHR does
* e.g. No cross domain requests without CORS.
*
* @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src
* Can be a url to where a PDF is located, a typed array (Uint8Array)
* already populated with data or parameter object.
*
* @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used
* if you want to manually serve range requests for data in the PDF.
2013-02-07 08:19:29 +09:00
*
* @param {function} passwordCallback (deprecated) It is used to request a
* password if wrong or no password was provided. The callback receives two
* parameters: function that needs to be called with new password and reason
* (see {PasswordResponses}).
*
* @param {function} progressCallback (deprecated) It is used to be able to
* monitor the loading progress of the PDF file (necessary to implement e.g.
* a loading bar). The callback receives an {Object} with the properties:
* {number} loaded and {number} total.
*
* @return {PDFDocumentLoadingTask}
2012-04-14 01:25:08 +09:00
*/
function getDocument(src, pdfDataRangeTransport,
passwordCallback, progressCallback) {
var task = new PDFDocumentLoadingTask();
// Support of the obsolete arguments (for compatibility with API v1.0)
if (arguments.length > 1) {
deprecated('getDocument is called with pdfDataRangeTransport, ' +
'passwordCallback or progressCallback argument');
}
if (pdfDataRangeTransport) {
if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) {
// Not a PDFDataRangeTransport instance, trying to add missing properties.
pdfDataRangeTransport = Object.create(pdfDataRangeTransport);
pdfDataRangeTransport.length = src.length;
pdfDataRangeTransport.initialData = src.initialData;
if (!pdfDataRangeTransport.abort) {
pdfDataRangeTransport.abort = function () {};
}
}
src = Object.create(src);
src.range = pdfDataRangeTransport;
2012-06-28 19:33:32 +09:00
}
task.onPassword = passwordCallback || null;
task.onProgress = progressCallback || null;
var source;
if (typeof src === 'string') {
source = { url: src };
} else if (isArrayBuffer(src)) {
source = { data: src };
} else if (src instanceof PDFDataRangeTransport) {
source = { range: src };
} else {
if (typeof src !== 'object') {
error('Invalid parameter in getDocument, need either Uint8Array, ' +
'string or a parameter object');
}
if (!src.url && !src.data && !src.range) {
error('Invalid parameter object: need either .data, .range or .url');
}
2012-06-28 19:33:32 +09:00
source = src;
2014-03-14 21:24:04 +09:00
}
2012-06-24 04:48:33 +09:00
2012-07-27 02:11:28 +09:00
var params = {};
var rangeTransport = null;
var worker = null;
2012-07-27 02:11:28 +09:00
for (var key in source) {
if (key === 'url' && typeof window !== 'undefined') {
// The full path is required in the 'url' field.
params[key] = new URL(source[key], window.location).href;
2012-07-27 02:11:28 +09:00
continue;
} else if (key === 'range') {
rangeTransport = source[key];
continue;
} else if (key === 'worker') {
worker = source[key];
continue;
} else if (key === 'data' && !(source[key] instanceof Uint8Array)) {
// Converting string or array-like data to Uint8Array.
var pdfBytes = source[key];
if (typeof pdfBytes === 'string') {
params[key] = stringToBytes(pdfBytes);
} else if (typeof pdfBytes === 'object' && pdfBytes !== null &&
!isNaN(pdfBytes.length)) {
params[key] = new Uint8Array(pdfBytes);
} else if (isArrayBuffer(pdfBytes)) {
params[key] = new Uint8Array(pdfBytes);
} else {
error('Invalid PDF binary data: either typed array, string or ' +
'array-like object is expected in the data property.');
}
continue;
2012-07-27 02:11:28 +09:00
}
params[key] = source[key];
}
params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
if (!worker) {
// Worker was not provided -- creating and owning our own.
worker = new PDFWorker();
task._worker = worker;
}
var docId = task.docId;
worker.promise.then(function () {
if (task.destroyed) {
throw new Error('Loading aborted');
}
return _fetchDocument(worker, params, rangeTransport, docId).then(
function (workerId) {
if (task.destroyed) {
throw new Error('Loading aborted');
}
var messageHandler = new MessageHandler(docId, workerId, worker.port);
var transport = new WorkerTransport(messageHandler, task, rangeTransport);
task._transport = transport;
messageHandler.send('Ready', null);
});
2015-12-17 09:37:43 +09:00
}).catch(task._capability.reject);
return task;
}
2012-04-13 04:11:22 +09:00
/**
* Starts fetching of specified PDF document/data.
* @param {PDFWorker} worker
* @param {Object} source
* @param {PDFDataRangeTransport} pdfDataRangeTransport
* @param {string} docId Unique document id, used as MessageHandler id.
* @returns {Promise} The promise, which is resolved when worker id of
* MessageHandler is known.
* @private
*/
function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
if (worker.destroyed) {
return Promise.reject(new Error('Worker was destroyed'));
}
source.disableAutoFetch = getDefaultSetting('disableAutoFetch');
source.disableStream = getDefaultSetting('disableStream');
source.chunkedViewerLoading = !!pdfDataRangeTransport;
if (pdfDataRangeTransport) {
source.length = pdfDataRangeTransport.length;
source.initialData = pdfDataRangeTransport.initialData;
}
return worker.messageHandler.sendWithPromise('GetDocRequest', {
docId: docId,
source: source,
disableRange: getDefaultSetting('disableRange'),
maxImageSize: getDefaultSetting('maxImageSize'),
cMapUrl: getDefaultSetting('cMapUrl'),
cMapPacked: getDefaultSetting('cMapPacked'),
disableFontFace: getDefaultSetting('disableFontFace'),
disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'),
postMessageTransfers: getDefaultSetting('postMessageTransfers') &&
!isPostMessageTransfersDisabled,
docBaseUrl: source.docBaseUrl,
}).then(function (workerId) {
if (worker.destroyed) {
throw new Error('Worker was destroyed');
}
return workerId;
});
}
/**
* PDF document loading operation.
* @class
* @alias PDFDocumentLoadingTask
*/
var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
var nextDocumentId = 0;
/** @constructs PDFDocumentLoadingTask */
function PDFDocumentLoadingTask() {
this._capability = createPromiseCapability();
this._transport = null;
this._worker = null;
/**
* Unique document loading task id -- used in MessageHandlers.
* @type {string}
*/
this.docId = 'd' + (nextDocumentId++);
/**
* Shows if loading task is destroyed.
* @type {boolean}
*/
this.destroyed = false;
/**
* Callback to request a password if wrong or no password was provided.
* The callback receives two parameters: function that needs to be called
* with new password and reason (see {PasswordResponses}).
*/
this.onPassword = null;
/**
* Callback to be able to monitor the loading progress of the PDF file
* (necessary to implement e.g. a loading bar). The callback receives
* an {Object} with the properties: {number} loaded and {number} total.
*/
this.onProgress = null;
/**
* Callback to when unsupported feature is used. The callback receives
* an {UNSUPPORTED_FEATURES} argument.
*/
this.onUnsupportedFeature = null;
}
PDFDocumentLoadingTask.prototype =
/** @lends PDFDocumentLoadingTask.prototype */ {
/**
* @return {Promise}
*/
get promise() {
return this._capability.promise;
},
/**
* Aborts all network requests and destroys worker.
* @return {Promise} A promise that is resolved after destruction activity
* is completed.
*/
destroy: function () {
this.destroyed = true;
var transportDestroyed = !this._transport ? Promise.resolve() :
this._transport.destroy();
return transportDestroyed.then(function () {
this._transport = null;
if (this._worker) {
this._worker.destroy();
this._worker = null;
}
}.bind(this));
},
/**
* Registers callbacks to indicate the document loading completion.
*
* @param {function} onFulfilled The callback for the loading completion.
* @param {function} onRejected The callback for the loading failure.
* @return {Promise} A promise that is resolved after the onFulfilled or
* onRejected callback.
*/
then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
return this.promise.then.apply(this.promise, arguments);
}
};
return PDFDocumentLoadingTask;
})();
/**
* Abstract class to support range requests file loading.
* @class
* @alias PDFDataRangeTransport
* @param {number} length
* @param {Uint8Array} initialData
*/
var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() {
function PDFDataRangeTransport(length, initialData) {
this.length = length;
this.initialData = initialData;
this._rangeListeners = [];
this._progressListeners = [];
this._progressiveReadListeners = [];
this._readyCapability = createPromiseCapability();
}
PDFDataRangeTransport.prototype =
/** @lends PDFDataRangeTransport.prototype */ {
addRangeListener:
function PDFDataRangeTransport_addRangeListener(listener) {
this._rangeListeners.push(listener);
},
addProgressListener:
function PDFDataRangeTransport_addProgressListener(listener) {
this._progressListeners.push(listener);
},
addProgressiveReadListener:
function PDFDataRangeTransport_addProgressiveReadListener(listener) {
this._progressiveReadListeners.push(listener);
},
onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
var listeners = this._rangeListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](begin, chunk);
}
},
onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
this._readyCapability.promise.then(function () {
var listeners = this._progressListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](loaded);
}
}.bind(this));
},
onDataProgressiveRead:
function PDFDataRangeTransport_onDataProgress(chunk) {
this._readyCapability.promise.then(function () {
var listeners = this._progressiveReadListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](chunk);
}
}.bind(this));
},
transportReady: function PDFDataRangeTransport_transportReady() {
this._readyCapability.resolve();
},
requestDataRange:
function PDFDataRangeTransport_requestDataRange(begin, end) {
throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
},
abort: function PDFDataRangeTransport_abort() {
}
};
return PDFDataRangeTransport;
})();
2012-04-14 01:25:08 +09:00
/**
* Proxy to a PDFDocument in the worker thread. Also, contains commonly used
* properties that can be read synchronously.
* @class
* @alias PDFDocumentProxy
2012-04-14 01:25:08 +09:00
*/
2012-05-02 02:54:16 +09:00
var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
function PDFDocumentProxy(pdfInfo, transport, loadingTask) {
2012-04-13 04:11:22 +09:00
this.pdfInfo = pdfInfo;
this.transport = transport;
this.loadingTask = loadingTask;
2012-04-13 04:11:22 +09:00
}
PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ {
2012-04-14 01:25:08 +09:00
/**
* @return {number} Total number of pages the PDF contains.
*/
2012-04-13 04:11:22 +09:00
get numPages() {
return this.pdfInfo.numPages;
},
2012-04-14 01:25:08 +09:00
/**
* @return {string} A unique ID to identify a PDF. Not guaranteed to be
* unique.
*/
2012-04-13 04:11:22 +09:00
get fingerprint() {
return this.pdfInfo.fingerprint;
},
2012-04-14 01:25:08 +09:00
/**
* @param {number} pageNumber The page number to get. The first page is 1.
* @return {Promise} A promise that is resolved with a {@link PDFPageProxy}
2012-04-14 01:25:08 +09:00
* object.
*/
getPage: function PDFDocumentProxy_getPage(pageNumber) {
return this.transport.getPage(pageNumber);
2012-04-13 04:11:22 +09:00
},
/**
* @param {{num: number, gen: number}} ref The page reference. Must have
* the 'num' and 'gen' properties.
* @return {Promise} A promise that is resolved with the page index that is
* associated with the reference.
*/
getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
return this.transport.getPageIndex(ref);
},
2012-04-14 01:25:08 +09:00
/**
* @return {Promise} A promise that is resolved with a lookup table for
* mapping named destinations to reference numbers.
*
* This can be slow for large documents: use getDestination instead
2012-04-14 01:25:08 +09:00
*/
2012-05-02 02:48:07 +09:00
getDestinations: function PDFDocumentProxy_getDestinations() {
2013-02-07 08:19:29 +09:00
return this.transport.getDestinations();
2012-04-13 04:11:22 +09:00
},
/**
* @param {string} id The named destination to get.
* @return {Promise} A promise that is resolved with all information
* of the given named destination.
*/
getDestination: function PDFDocumentProxy_getDestination(id) {
return this.transport.getDestination(id);
},
/**
* @return {Promise} A promise that is resolved with:
* an Array containing the pageLabels that correspond to the pageIndexes,
* or `null` when no pageLabels are present in the PDF file.
*/
getPageLabels: function PDFDocumentProxy_getPageLabels() {
return this.transport.getPageLabels();
},
/**
* @return {Promise} A promise that is resolved with a lookup table for
* mapping named attachments to their content.
*/
getAttachments: function PDFDocumentProxy_getAttachments() {
return this.transport.getAttachments();
},
/**
* @return {Promise} A promise that is resolved with an array of all the
* JavaScript strings in the name tree.
*/
getJavaScript: function PDFDocumentProxy_getJavaScript() {
2014-05-08 04:15:34 +09:00
return this.transport.getJavaScript();
},
2012-04-14 01:25:08 +09:00
/**
* @return {Promise} A promise that is resolved with an {Array} that is a
2012-04-14 01:25:08 +09:00
* tree outline (if it has one) of the PDF. The tree is in the format of:
* [
* {
* title: string,
* bold: boolean,
* italic: boolean,
* color: rgb Uint8Array,
2012-04-14 01:25:08 +09:00
* dest: dest obj,
* url: string,
2012-04-14 01:25:08 +09:00
* items: array of more items like this
* },
* ...
* ].
*/
2012-05-02 02:48:07 +09:00
getOutline: function PDFDocumentProxy_getOutline() {
2014-05-08 04:06:44 +09:00
return this.transport.getOutline();
2012-04-13 04:11:22 +09:00
},
2012-04-14 01:25:08 +09:00
/**
* @return {Promise} A promise that is resolved with an {Object} that has
* info and metadata properties. Info is an {Object} filled with anything
2012-04-14 01:25:08 +09:00
* available in the information dictionary and similarly metadata is a
* {Metadata} object with information from the metadata section of the PDF.
*/
2012-05-02 02:48:07 +09:00
getMetadata: function PDFDocumentProxy_getMetadata() {
return this.transport.getMetadata();
2012-04-13 04:11:22 +09:00
},
/**
* @return {Promise} A promise that is resolved with a TypedArray that has
* the raw data from the PDF.
*/
getData: function PDFDocumentProxy_getData() {
return this.transport.getData();
},
2013-02-07 08:19:29 +09:00
/**
* @return {Promise} A promise that is resolved when the document's data
* is loaded. It is resolved with an {Object} that contains the length
* property that indicates size of the PDF data in bytes.
2013-02-07 08:19:29 +09:00
*/
getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() {
2014-04-30 00:07:05 +09:00
return this.transport.downloadInfoCapability.promise;
2013-02-07 08:19:29 +09:00
},
/**
* @return {Promise} A promise this is resolved with current stats about
* document structures (see {@link PDFDocumentStats}).
*/
getStats: function PDFDocumentProxy_getStats() {
return this.transport.getStats();
},
/**
* Cleans up resources allocated by the document, e.g. created @font-face.
*/
cleanup: function PDFDocumentProxy_cleanup() {
this.transport.startCleanup();
},
/**
* Destroys current document instance and terminates worker.
*/
2012-05-02 02:48:07 +09:00
destroy: function PDFDocumentProxy_destroy() {
return this.loadingTask.destroy();
2012-04-13 04:11:22 +09:00
}
};
return PDFDocumentProxy;
})();
/**
* Page getTextContent parameters.
*
* @typedef {Object} getTextContentParameters
* @property {boolean} normalizeWhitespace - replaces all occurrences of
* whitespace with standard spaces (0x20). The default value is `false`.
* @property {boolean} disableCombineTextItems - do not attempt to combine
* same line {@link TextItem}'s. The default value is `false`.
*/
/**
* Page text content.
*
* @typedef {Object} TextContent
* @property {array} items - array of {@link TextItem}
* @property {Object} styles - {@link TextStyles} objects, indexed by font name.
*/
/**
* Page text content part.
*
* @typedef {Object} TextItem
* @property {string} str - text content.
* @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'.
* @property {array} transform - transformation matrix.
* @property {number} width - width in device space.
* @property {number} height - height in device space.
* @property {string} fontName - font name used by pdf.js for converted font.
*/
/**
2014-04-12 00:57:48 +09:00
* Text style.
*
* @typedef {Object} TextStyle
* @property {number} ascent - font ascent.
* @property {number} descent - font descent.
* @property {boolean} vertical - text is in vertical mode.
* @property {string} fontFamily - possible font family
*/
/**
* Page annotation parameters.
*
* @typedef {Object} GetAnnotationsParameters
* @property {string} intent - Determines the annotations that will be fetched,
* can be either 'display' (viewable annotations) or 'print'
* (printable annotations).
* If the parameter is omitted, all annotations are fetched.
*/
2014-04-12 00:57:48 +09:00
/**
* Page render parameters.
*
* @typedef {Object} RenderParameters
* @property {Object} canvasContext - A 2D context of a DOM Canvas object.
* @property {PageViewport} viewport - Rendering viewport obtained by
2014-05-06 04:09:47 +09:00
* calling of PDFPage.getViewport method.
2014-04-12 00:57:48 +09:00
* @property {string} intent - Rendering intent, can be 'display' or 'print'
* (default value is 'display').
* @property {boolean} renderInteractiveForms - (optional) Whether or not
* interactive form elements are rendered in the display
* layer. If so, we do not render them on canvas as well.
* @property {Array} transform - (optional) Additional transform, applied
* just before viewport transform.
2014-04-12 00:57:48 +09:00
* @property {Object} imageLayer - (optional) An object that has beginLayout,
* endLayout and appendImage functions.
* @property {function} continueCallback - (deprecated) A function that will be
2014-04-12 00:57:48 +09:00
* called each time the rendering is paused. To continue
* rendering call the function that is the first argument
* to the callback.
*/
2015-02-03 00:12:52 +09:00
2014-06-17 03:35:38 +09:00
/**
* PDF page operator list.
*
* @typedef {Object} PDFOperatorList
* @property {Array} fnArray - Array containing the operator functions.
* @property {Array} argsArray - Array containing the arguments of the
* functions.
*/
2014-04-12 00:57:48 +09:00
/**
* Proxy to a PDFPage in the worker thread.
* @class
* @alias PDFPageProxy
*/
2012-04-13 04:11:22 +09:00
var PDFPageProxy = (function PDFPageProxyClosure() {
function PDFPageProxy(pageIndex, pageInfo, transport) {
this.pageIndex = pageIndex;
2012-04-13 04:11:22 +09:00
this.pageInfo = pageInfo;
this.transport = transport;
2012-04-13 06:07:11 +09:00
this.stats = new StatTimer();
this.stats.enabled = getDefaultSetting('enableStats');
this.commonObjs = transport.commonObjs;
this.objs = new PDFObjects();
this.cleanupAfterRender = false;
this.pendingCleanup = false;
this.intentStates = Object.create(null);
this.destroyed = false;
2012-04-13 04:11:22 +09:00
}
PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ {
2012-04-14 01:25:08 +09:00
/**
* @return {number} Page number of the page. First page is 1.
*/
2012-04-13 04:11:22 +09:00
get pageNumber() {
return this.pageIndex + 1;
2012-04-13 04:11:22 +09:00
},
2012-04-14 01:25:08 +09:00
/**
* @return {number} The number of degrees the page is rotated clockwise.
*/
2012-04-13 04:11:22 +09:00
get rotate() {
return this.pageInfo.rotate;
},
2012-04-14 01:25:08 +09:00
/**
* @return {Object} The reference that points to this page. It has 'num' and
2012-04-14 01:25:08 +09:00
* 'gen' properties.
*/
2012-04-13 04:11:22 +09:00
get ref() {
return this.pageInfo.ref;
},
/**
* @return {number} The default size of units in 1/72nds of an inch.
*/
get userUnit() {
return this.pageInfo.userUnit;
},
2012-04-14 01:25:08 +09:00
/**
* @return {Array} An array of the visible portion of the PDF page in the
2012-04-14 01:25:08 +09:00
* user space units - [x1, y1, x2, y2].
*/
2012-04-13 04:11:22 +09:00
get view() {
return this.pageInfo.view;
},
2012-04-14 01:25:08 +09:00
/**
* @param {number} scale The desired scale of the viewport.
* @param {number} rotate Degrees to rotate the viewport. If omitted this
* defaults to the page rotation.
* @return {PageViewport} Contains 'width' and 'height' properties
2014-05-06 04:09:47 +09:00
* along with transforms required for rendering.
2012-04-14 01:25:08 +09:00
*/
2012-05-02 02:48:07 +09:00
getViewport: function PDFPageProxy_getViewport(scale, rotate) {
2014-03-14 21:24:04 +09:00
if (arguments.length < 2) {
2012-04-13 04:11:22 +09:00
rotate = this.rotate;
2014-03-14 21:24:04 +09:00
}
return new PageViewport(this.view, scale, rotate, 0, 0);
2012-04-13 04:11:22 +09:00
},
2012-04-14 01:25:08 +09:00
/**
* @param {GetAnnotationsParameters} params - Annotation parameters.
* @return {Promise} A promise that is resolved with an {Array} of the
2012-04-14 01:25:08 +09:00
* annotation objects.
*/
getAnnotations: function PDFPageProxy_getAnnotations(params) {
var intent = (params && params.intent) || null;
if (!this.annotationsPromise || this.annotationsIntent !== intent) {
this.annotationsPromise = this.transport.getAnnotations(this.pageIndex,
intent);
this.annotationsIntent = intent;
2014-03-14 21:24:04 +09:00
}
2015-07-19 00:52:03 +09:00
return this.annotationsPromise;
2012-04-13 04:11:22 +09:00
},
2012-04-14 01:25:08 +09:00
/**
* Begins the process of rendering a page to the desired context.
2014-04-12 00:57:48 +09:00
* @param {RenderParameters} params Page render parameters.
* @return {RenderTask} An object that contains the promise, which
* is resolved when the page finishes rendering.
2012-04-14 01:25:08 +09:00
*/
2012-05-02 02:48:07 +09:00
render: function PDFPageProxy_render(params) {
2012-04-13 04:11:22 +09:00
var stats = this.stats;
stats.time('Overall');
// If there was a pending destroy cancel it so no cleanup happens during
// this call to render.
this.pendingCleanup = false;
var renderingIntent = (params.intent === 'print' ? 'print' : 'display');
var renderInteractiveForms = (params.renderInteractiveForms === true ?
true : /* Default */ false);
if (!this.intentStates[renderingIntent]) {
this.intentStates[renderingIntent] = Object.create(null);
}
var intentState = this.intentStates[renderingIntent];
2014-04-30 00:07:05 +09:00
// If there's no displayReadyCapability yet, then the operatorList
// was never requested before. Make the request and create the promise.
if (!intentState.displayReadyCapability) {
intentState.receivingOperatorList = true;
2014-04-30 00:07:05 +09:00
intentState.displayReadyCapability = createPromiseCapability();
intentState.operatorList = {
fnArray: [],
argsArray: [],
lastChunk: false
};
2012-04-13 04:11:22 +09:00
this.stats.time('Page Request');
this.transport.messageHandler.send('RenderPageRequest', {
pageIndex: this.pageNumber - 1,
intent: renderingIntent,
renderInteractiveForms: renderInteractiveForms,
2012-04-13 04:11:22 +09:00
});
}
var internalRenderTask = new InternalRenderTask(complete, params,
2014-03-14 21:24:04 +09:00
this.objs,
this.commonObjs,
intentState.operatorList,
this.pageNumber);
2015-05-12 22:44:42 +09:00
internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print';
if (!intentState.renderTasks) {
intentState.renderTasks = [];
}
intentState.renderTasks.push(internalRenderTask);
var renderTask = internalRenderTask.task;
// Obsolete parameter support
if (params.continueCallback) {
deprecated('render is used with continueCallback parameter');
renderTask.onContinue = params.continueCallback;
}
2012-04-13 04:11:22 +09:00
var self = this;
2014-04-30 00:07:05 +09:00
intentState.displayReadyCapability.promise.then(
function pageDisplayReadyPromise(transparency) {
if (self.pendingCleanup) {
complete();
return;
}
stats.time('Rendering');
internalRenderTask.initializeGraphics(transparency);
internalRenderTask.operatorListChanged();
},
2012-04-13 04:11:22 +09:00
function pageDisplayReadPromiseError(reason) {
2012-04-13 06:07:11 +09:00
complete(reason);
2012-04-13 04:11:22 +09:00
}
);
function complete(error) {
var i = intentState.renderTasks.indexOf(internalRenderTask);
if (i >= 0) {
intentState.renderTasks.splice(i, 1);
2012-04-13 04:11:22 +09:00
}
if (self.cleanupAfterRender) {
self.pendingCleanup = true;
}
self._tryCleanup();
if (error) {
2014-04-30 00:07:05 +09:00
internalRenderTask.capability.reject(error);
} else {
2014-04-30 00:07:05 +09:00
internalRenderTask.capability.resolve();
}
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
2012-04-13 04:11:22 +09:00
}
return renderTask;
2012-04-13 04:11:22 +09:00
},
2014-06-17 03:35:38 +09:00
/**
* @return {Promise} A promise resolved with an {@link PDFOperatorList}
* object that represents page's operator list.
*/
getOperatorList: function PDFPageProxy_getOperatorList() {
function operatorListChanged() {
if (intentState.operatorList.lastChunk) {
intentState.opListReadCapability.resolve(intentState.operatorList);
var i = intentState.renderTasks.indexOf(opListTask);
if (i >= 0) {
intentState.renderTasks.splice(i, 1);
}
2014-06-17 03:35:38 +09:00
}
}
var renderingIntent = 'oplist';
if (!this.intentStates[renderingIntent]) {
this.intentStates[renderingIntent] = Object.create(null);
2014-06-17 03:35:38 +09:00
}
var intentState = this.intentStates[renderingIntent];
var opListTask;
2014-06-17 03:35:38 +09:00
if (!intentState.opListReadCapability) {
opListTask = {};
2014-06-17 03:35:38 +09:00
opListTask.operatorListChanged = operatorListChanged;
intentState.receivingOperatorList = true;
intentState.opListReadCapability = createPromiseCapability();
intentState.renderTasks = [];
intentState.renderTasks.push(opListTask);
intentState.operatorList = {
fnArray: [],
argsArray: [],
lastChunk: false
};
this.transport.messageHandler.send('RenderPageRequest', {
pageIndex: this.pageIndex,
intent: renderingIntent
});
}
return intentState.opListReadCapability.promise;
},
2012-04-14 01:25:08 +09:00
/**
* @param {getTextContentParameters} params - getTextContent parameters.
* @return {Promise} That is resolved a {@link TextContent}
* object that represent the page text content.
2012-04-14 01:25:08 +09:00
*/
getTextContent: function PDFPageProxy_getTextContent(params) {
return this.transport.messageHandler.sendWithPromise('GetTextContent', {
pageIndex: this.pageNumber - 1,
normalizeWhitespace: (params && params.normalizeWhitespace === true ?
true : /* Default */ false),
combineTextItems: (params && params.disableCombineTextItems === true ?
false : /* Default */ true),
});
2012-04-13 04:11:22 +09:00
},
/**
* Destroys page object.
*/
_destroy: function PDFPageProxy_destroy() {
this.destroyed = true;
this.transport.pageCache[this.pageIndex] = null;
2015-10-21 10:50:32 +09:00
var waitOn = [];
Object.keys(this.intentStates).forEach(function(intent) {
if (intent === 'oplist') {
// Avoid errors below, since the renderTasks are just stubs.
return;
}
var intentState = this.intentStates[intent];
intentState.renderTasks.forEach(function(renderTask) {
2015-10-21 10:50:32 +09:00
var renderCompleted = renderTask.capability.promise.
catch(function () {}); // ignoring failures
waitOn.push(renderCompleted);
renderTask.cancel();
});
}, this);
this.objs.clear();
this.annotationsPromise = null;
this.pendingCleanup = false;
2015-10-21 10:50:32 +09:00
return Promise.all(waitOn);
},
/**
* Cleans up resources allocated by the page. (deprecated)
*/
destroy: function() {
deprecated('page destroy method, use cleanup() instead');
this.cleanup();
},
/**
* Cleans up resources allocated by the page.
*/
cleanup: function PDFPageProxy_cleanup() {
this.pendingCleanup = true;
this._tryCleanup();
},
/**
* For internal use only. Attempts to clean up if rendering is in a state
* where that's possible.
* @ignore
*/
_tryCleanup: function PDFPageProxy_tryCleanup() {
if (!this.pendingCleanup ||
Object.keys(this.intentStates).some(function(intent) {
var intentState = this.intentStates[intent];
2014-03-14 21:24:04 +09:00
return (intentState.renderTasks.length !== 0 ||
intentState.receivingOperatorList);
}, this)) {
return;
}
Object.keys(this.intentStates).forEach(function(intent) {
delete this.intentStates[intent];
}, this);
this.objs.clear();
this.annotationsPromise = null;
this.pendingCleanup = false;
},
/**
* For internal use only.
* @ignore
*/
_startRenderPage: function PDFPageProxy_startRenderPage(transparency,
intent) {
var intentState = this.intentStates[intent];
2014-06-17 03:35:38 +09:00
// TODO Refactor RenderPageRequest to separate rendering
// and operator list logic
if (intentState.displayReadyCapability) {
intentState.displayReadyCapability.resolve(transparency);
}
},
/**
* For internal use only.
* @ignore
*/
_renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk,
intent) {
var intentState = this.intentStates[intent];
var i, ii;
// Add the new chunk to the current operator list.
for (i = 0, ii = operatorListChunk.length; i < ii; i++) {
intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
intentState.operatorList.argsArray.push(
operatorListChunk.argsArray[i]);
}
intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
// Notify all the rendering tasks there are more operators to be consumed.
for (i = 0; i < intentState.renderTasks.length; i++) {
intentState.renderTasks[i].operatorListChanged();
2012-04-17 04:49:55 +09:00
}
if (operatorListChunk.lastChunk) {
intentState.receivingOperatorList = false;
this._tryCleanup();
}
2012-04-13 04:11:22 +09:00
}
};
return PDFPageProxy;
})();
2012-04-14 01:25:08 +09:00
/**
* PDF.js web worker abstraction, it controls instantiation of PDF documents and
* WorkerTransport for them. If creation of a web worker is not possible,
* a "fake" worker will be used instead.
* @class
2012-04-14 01:25:08 +09:00
*/
var PDFWorker = (function PDFWorkerClosure() {
var nextFakeWorkerId = 0;
function getWorkerSrc() {
if (typeof workerSrc !== 'undefined') {
return workerSrc;
}
if (getDefaultSetting('workerSrc')) {
return getDefaultSetting('workerSrc');
}
if (typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('PRODUCTION && !(MOZCENTRAL || FIREFOX)') &&
pdfjsFilePath) {
return pdfjsFilePath.replace(/\.js$/i, '.worker.js');
}
error('No PDFJS.workerSrc specified');
}
2016-03-03 10:16:38 +09:00
var fakeWorkerFilesLoadedCapability;
// Loads worker code into main thread.
function setupFakeWorkerGlobal() {
2016-03-03 10:16:38 +09:00
var WorkerMessageHandler;
if (fakeWorkerFilesLoadedCapability) {
return fakeWorkerFilesLoadedCapability.promise;
}
fakeWorkerFilesLoadedCapability = createPromiseCapability();
// In the developer build load worker_loader which in turn loads all the
// other files and resolves the promise. In production only the
// pdf.worker.js file is needed.
if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) {
if (typeof amdRequire === 'function') {
2016-03-03 10:16:38 +09:00
amdRequire(['pdfjs/core/network', 'pdfjs/core/worker'],
function (network, worker) {
WorkerMessageHandler = worker.WorkerMessageHandler;
fakeWorkerFilesLoadedCapability.resolve(WorkerMessageHandler);
});
} else if (typeof require === 'function') {
2016-03-03 10:16:38 +09:00
var worker = require('../core/worker.js');
WorkerMessageHandler = worker.WorkerMessageHandler;
fakeWorkerFilesLoadedCapability.resolve(WorkerMessageHandler);
} else {
2015-12-17 09:37:43 +09:00
throw new Error('AMD or CommonJS must be used to load fake worker.');
}
} else if (PDFJSDev.test('SINGLE_FILE')) {
WorkerMessageHandler = pdfjsLibs.pdfjsCoreWorker.WorkerMessageHandler;
fakeWorkerFilesLoadedCapability.resolve(WorkerMessageHandler);
} else {
var loader = fakeWorkerFilesLoader || function (callback) {
Util.loadScript(getWorkerSrc(), function () {
callback(window.pdfjsDistBuildPdfWorker.WorkerMessageHandler);
});
};
loader(fakeWorkerFilesLoadedCapability.resolve);
}
2016-03-03 10:16:38 +09:00
return fakeWorkerFilesLoadedCapability.promise;
}
2012-04-12 07:52:15 +09:00
function FakeWorkerPort(defer) {
this._listeners = [];
this._defer = defer;
this._deferred = Promise.resolve(undefined);
}
FakeWorkerPort.prototype = {
postMessage: function (obj, transfers) {
function cloneValue(value) {
// Trying to perform a structured clone close to the spec, including
// transfers.
if (typeof value !== 'object' || value === null) {
return value;
}
if (cloned.has(value)) { // already cloned the object
return cloned.get(value);
}
var result;
var buffer;
if ((buffer = value.buffer) && isArrayBuffer(buffer)) {
// We found object with ArrayBuffer (typed array).
var transferable = transfers && transfers.indexOf(buffer) >= 0;
if (value === buffer) {
// Special case when we are faking typed arrays in compatibility.js.
result = value;
} else if (transferable) {
result = new value.constructor(buffer, value.byteOffset,
value.byteLength);
} else {
result = new value.constructor(value);
}
cloned.set(value, result);
return result;
}
result = isArray(value) ? [] : {};
cloned.set(value, result); // adding to cache now for cyclic references
// Cloning all value and object properties, however ignoring properties
// defined via getter.
for (var i in value) {
var desc, p = value;
while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
p = Object.getPrototypeOf(p);
}
if (typeof desc.value === 'undefined' ||
typeof desc.value === 'function') {
continue;
}
result[i] = cloneValue(desc.value);
}
return result;
}
if (!this._defer) {
this._listeners.forEach(function (listener) {
listener.call(this, {data: obj});
}, this);
return;
}
var cloned = new WeakMap();
var e = {data: cloneValue(obj)};
this._deferred.then(function () {
this._listeners.forEach(function (listener) {
listener.call(this, e);
}, this);
}.bind(this));
},
addEventListener: function (name, listener) {
this._listeners.push(listener);
},
removeEventListener: function (name, listener) {
var i = this._listeners.indexOf(listener);
this._listeners.splice(i, 1);
},
terminate: function () {
this._listeners = [];
}
};
function createCDNWrapper(url) {
// We will rely on blob URL's property to specify origin.
// We want this function to fail in case if createObjectURL or Blob do not
// exist or fail for some reason -- our Worker creation will fail anyway.
var wrapper = 'importScripts(\'' + url + '\');';
return URL.createObjectURL(new Blob([wrapper]));
}
function PDFWorker(name) {
this.name = name;
this.destroyed = false;
this._readyCapability = createPromiseCapability();
this._port = null;
this._webWorker = null;
this._messageHandler = null;
this._initialize();
}
PDFWorker.prototype = /** @lends PDFWorker.prototype */ {
get promise() {
return this._readyCapability.promise;
},
get port() {
return this._port;
},
get messageHandler() {
return this._messageHandler;
},
_initialize: function PDFWorker_initialize() {
// If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fulfills
// all requirements to run parts of pdf.js in a web worker.
// Right now, the requirement is, that an Uint8Array is still an
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
if ((typeof PDFJSDev === 'undefined' || !PDFJSDev.test('SINGLE_FILE')) &&
!isWorkerDisabled && !getDefaultSetting('disableWorker') &&
typeof Worker !== 'undefined') {
var workerSrc = getWorkerSrc();
2012-04-12 07:52:15 +09:00
try {
// Wraps workerSrc path into blob URL, if the former does not belong
// to the same origin.
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('GENERIC') &&
!isSameOrigin(window.location.href, workerSrc)) {
workerSrc = createCDNWrapper(
new URL(workerSrc, window.location).href);
}
// Some versions of FF can't create a worker on localhost, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
var worker = new Worker(workerSrc);
var messageHandler = new MessageHandler('main', 'worker', worker);
var terminateEarly = function() {
worker.removeEventListener('error', onWorkerError);
messageHandler.destroy();
worker.terminate();
if (this.destroyed) {
this._readyCapability.reject(new Error('Worker was destroyed'));
} else {
// Fall back to fake worker if the termination is caused by an
// error (e.g. NetworkError / SecurityError).
this._setupFakeWorker();
}
}.bind(this);
var onWorkerError = function(event) {
if (!this._webWorker) {
// Worker failed to initialize due to an error. Clean up and fall
// back to the fake worker.
terminateEarly();
}
}.bind(this);
worker.addEventListener('error', onWorkerError);
messageHandler.on('test', function PDFWorker_test(data) {
worker.removeEventListener('error', onWorkerError);
if (this.destroyed) {
terminateEarly();
return; // worker was destroyed
2013-11-12 12:30:26 +09:00
}
var supportTypedArray = data && data.supportTypedArray;
if (supportTypedArray) {
this._messageHandler = messageHandler;
this._port = worker;
this._webWorker = worker;
if (!data.supportTransfers) {
isPostMessageTransfersDisabled = true;
}
this._readyCapability.resolve();
// Send global setting, e.g. verbosity level.
messageHandler.send('configure', {
verbosity: getVerbosityLevel()
});
} else {
this._setupFakeWorker();
messageHandler.destroy();
worker.terminate();
}
}.bind(this));
messageHandler.on('console_log', function (data) {
console.log.apply(console, data);
});
messageHandler.on('console_error', function (data) {
console.error.apply(console, data);
});
2015-12-17 09:37:43 +09:00
messageHandler.on('ready', function (data) {
worker.removeEventListener('error', onWorkerError);
2015-12-17 09:37:43 +09:00
if (this.destroyed) {
terminateEarly();
2015-12-17 09:37:43 +09:00
return; // worker was destroyed
}
try {
sendTest();
} catch (e) {
2015-12-17 09:37:43 +09:00
// We need fallback to a faked worker.
this._setupFakeWorker();
}
}.bind(this));
var sendTest = function () {
var postMessageTransfers =
getDefaultSetting('postMessageTransfers') &&
!isPostMessageTransfersDisabled;
var testObj = new Uint8Array([postMessageTransfers ? 255 : 0]);
2015-12-17 09:37:43 +09:00
// Some versions of Opera throw a DATA_CLONE_ERR on serializing the
// typed array. Also, checking if we can use transfers.
try {
messageHandler.send('test', testObj, [testObj.buffer]);
} catch (ex) {
info('Cannot use postMessage transfers');
testObj[0] = 0;
messageHandler.send('test', testObj);
}
};
// It might take time for worker to initialize (especially when AMD
// loader is used). We will try to send test immediately, and then
// when 'ready' message will arrive. The worker shall process only
// first received 'test'.
sendTest();
return;
} catch (e) {
info('The worker has been disabled.');
}
}
// Either workers are disabled, not supported or have thrown an exception.
// Thus, we fallback to a faked worker.
this._setupFakeWorker();
},
2012-04-12 07:52:15 +09:00
_setupFakeWorker: function PDFWorker_setupFakeWorker() {
if (!isWorkerDisabled && !getDefaultSetting('disableWorker')) {
warn('Setting up fake worker.');
isWorkerDisabled = true;
}
2016-03-03 10:16:38 +09:00
setupFakeWorkerGlobal().then(function (WorkerMessageHandler) {
if (this.destroyed) {
this._readyCapability.reject(new Error('Worker was destroyed'));
return;
2013-11-12 12:30:26 +09:00
}
// We cannot turn on proper fake port simulation (this includes
// structured cloning) when typed arrays are not supported. Relying
// on a chance that messages will be sent in proper order.
var isTypedArraysPresent = Uint8Array !== Float32Array;
var port = new FakeWorkerPort(isTypedArraysPresent);
this._port = port;
// All fake workers use the same port, making id unique.
var id = 'fake' + (nextFakeWorkerId++);
// If the main thread is our worker, setup the handling for the
// messages -- the main thread sends to it self.
var workerHandler = new MessageHandler(id + '_worker', id, port);
2016-03-03 10:16:38 +09:00
WorkerMessageHandler.setup(workerHandler, port);
var messageHandler = new MessageHandler(id, id + '_worker', port);
this._messageHandler = messageHandler;
this._readyCapability.resolve();
}.bind(this));
},
/**
* Destroys the worker instance.
*/
destroy: function PDFWorker_destroy() {
this.destroyed = true;
if (this._webWorker) {
// We need to terminate only web worker created resource.
this._webWorker.terminate();
this._webWorker = null;
}
this._port = null;
if (this._messageHandler) {
this._messageHandler.destroy();
this._messageHandler = null;
2012-04-12 07:52:15 +09:00
}
}
};
return PDFWorker;
})();
2012-04-14 01:25:08 +09:00
/**
* For internal use only.
* @ignore
2012-04-14 01:25:08 +09:00
*/
2012-04-13 04:11:22 +09:00
var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport) {
this.messageHandler = messageHandler;
this.loadingTask = loadingTask;
2013-02-07 08:19:29 +09:00
this.pdfDataRangeTransport = pdfDataRangeTransport;
this.commonObjs = new PDFObjects();
this.fontLoader = new FontLoader(loadingTask.docId);
2012-04-12 07:52:15 +09:00
this.destroyed = false;
this.destroyCapability = null;
2012-04-12 07:52:15 +09:00
this.pageCache = [];
this.pagePromises = [];
2014-04-30 00:07:05 +09:00
this.downloadInfoCapability = createPromiseCapability();
this.setupMessageHandler();
2012-04-12 07:52:15 +09:00
}
WorkerTransport.prototype = {
2012-04-12 09:09:55 +09:00
destroy: function WorkerTransport_destroy() {
if (this.destroyCapability) {
return this.destroyCapability.promise;
}
this.destroyed = true;
this.destroyCapability = createPromiseCapability();
2015-10-21 10:50:32 +09:00
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) {
2015-10-21 10:50:32 +09:00
waitOn.push(page._destroy());
}
});
2012-04-13 02:01:07 +09:00
this.pageCache = [];
this.pagePromises = [];
var self = this;
2015-10-21 10:50:32 +09:00
// 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(function () {
self.fontLoader.clear();
if (self.pdfDataRangeTransport) {
self.pdfDataRangeTransport.abort();
self.pdfDataRangeTransport = null;
}
if (self.messageHandler) {
self.messageHandler.destroy();
self.messageHandler = null;
}
self.destroyCapability.resolve();
}, this.destroyCapability.reject);
return this.destroyCapability.promise;
2012-04-12 09:09:55 +09:00
},
2012-04-12 09:09:55 +09:00
setupMessageHandler:
function WorkerTransport_setupMessageHandler() {
var messageHandler = this.messageHandler;
2012-04-12 07:52:15 +09:00
function updatePassword(password) {
messageHandler.send('UpdatePassword', password);
}
2013-02-07 08:19:29 +09:00
var pdfDataRangeTransport = this.pdfDataRangeTransport;
if (pdfDataRangeTransport) {
pdfDataRangeTransport.addRangeListener(function(begin, chunk) {
2013-02-07 08:19:29 +09:00
messageHandler.send('OnDataRange', {
begin: begin,
chunk: chunk
});
});
pdfDataRangeTransport.addProgressListener(function(loaded) {
messageHandler.send('OnDataProgress', {
loaded: loaded
});
});
pdfDataRangeTransport.addProgressiveReadListener(function(chunk) {
messageHandler.send('OnDataRange', {
chunk: chunk
});
});
2013-02-07 08:19:29 +09:00
messageHandler.on('RequestDataRange',
function transportDataRange(data) {
pdfDataRangeTransport.requestDataRange(data.begin, data.end);
}, this);
}
2012-04-13 04:11:22 +09:00
messageHandler.on('GetDoc', function transportDoc(data) {
2012-04-12 07:52:15 +09:00
var pdfInfo = data.pdfInfo;
2014-02-24 03:16:14 +09:00
this.numPages = data.pdfInfo.numPages;
var loadingTask = this.loadingTask;
var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask);
2012-04-12 07:52:15 +09:00
this.pdfDocument = pdfDocument;
loadingTask._capability.resolve(pdfDocument);
2012-04-12 07:52:15 +09:00
}, this);
messageHandler.on('NeedPassword',
function transportNeedPassword(exception) {
var loadingTask = this.loadingTask;
if (loadingTask.onPassword) {
return loadingTask.onPassword(updatePassword,
PasswordResponses.NEED_PASSWORD);
}
loadingTask._capability.reject(
new PasswordException(exception.message, exception.code));
}, this);
messageHandler.on('IncorrectPassword',
function transportIncorrectPassword(exception) {
var loadingTask = this.loadingTask;
if (loadingTask.onPassword) {
return loadingTask.onPassword(updatePassword,
PasswordResponses.INCORRECT_PASSWORD);
}
loadingTask._capability.reject(
new PasswordException(exception.message, exception.code));
}, this);
messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) {
this.loadingTask._capability.reject(
new InvalidPDFException(exception.message));
}, this);
messageHandler.on('MissingPDF', function transportMissingPDF(exception) {
this.loadingTask._capability.reject(
new MissingPDFException(exception.message));
}, this);
messageHandler.on('UnexpectedResponse',
function transportUnexpectedResponse(exception) {
this.loadingTask._capability.reject(
new UnexpectedResponseException(exception.message, exception.status));
}, this);
messageHandler.on('UnknownError',
function transportUnknownError(exception) {
this.loadingTask._capability.reject(
new UnknownErrorException(exception.message, exception.details));
}, this);
messageHandler.on('DataLoaded', function transportPage(data) {
2014-04-30 00:07:05 +09:00
this.downloadInfoCapability.resolve(data);
}, this);
messageHandler.on('PDFManagerReady', function transportPage(data) {
if (this.pdfDataRangeTransport) {
this.pdfDataRangeTransport.transportReady();
}
}, this);
messageHandler.on('StartRenderPage', function transportRender(data) {
if (this.destroyed) {
return; // Ignore any pending requests if the worker was terminated.
}
2012-04-13 02:01:07 +09:00
var page = this.pageCache[data.pageIndex];
2012-04-12 07:52:15 +09:00
2012-04-13 02:01:07 +09:00
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);
2012-04-12 07:52:15 +09:00
}, this);
messageHandler.on('commonobj', function transportObj(data) {
if (this.destroyed) {
return; // Ignore any pending requests if the worker was terminated.
}
2012-04-12 07:52:15 +09:00
var id = data[0];
var type = data[1];
2014-03-14 21:24:04 +09:00
if (this.commonObjs.hasData(id)) {
return;
2014-03-14 21:24:04 +09:00
}
2012-04-12 07:52:15 +09:00
switch (type) {
case 'Font':
2012-09-13 09:31:04 +09:00
var exportedData = data[2];
2012-04-12 07:52:15 +09:00
if ('error' in exportedData) {
var exportedError = exportedData.error;
warn('Error during font loading: ' + exportedError);
this.commonObjs.resolve(id, exportedError);
break;
}
var fontRegistry = null;
if (getDefaultSetting('pdfBug') && globalScope.FontInspector &&
globalScope['FontInspector'].enabled) {
fontRegistry = {
registerFont: function (font, url) {
globalScope['FontInspector'].fontAdded(font, url);
}
};
}
var font = new FontFaceObject(exportedData, {
isEvalSuported: getDefaultSetting('isEvalSupported'),
disableFontFace: getDefaultSetting('disableFontFace'),
fontRegistry: fontRegistry
});
this.fontLoader.bind(
[font],
function fontReady(fontObjs) {
this.commonObjs.resolve(id, font);
}.bind(this)
);
break;
case 'FontPath':
this.commonObjs.resolve(id, data[2]);
break;
default:
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];
loadJpegStream(id, imageData, pageProxy.objs);
break;
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;
}
2012-04-12 07:52:15 +09:00
break;
default:
error('Got unknown object type ' + type);
2012-04-12 07:52:15 +09:00
}
}, this);
2012-06-24 04:48:33 +09:00
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
});
}
2012-06-24 04:48:33 +09:00
}, this);
messageHandler.on('PageError', function transportError(data) {
if (this.destroyed) {
return; // Ignore any pending requests if the worker was terminated.
}
2012-04-13 07:14:18 +09:00
var page = this.pageCache[data.pageNum - 1];
var intentState = page.intentStates[data.intent];
if (intentState.displayReadyCapability) {
2014-04-30 00:07:05 +09:00
intentState.displayReadyCapability.reject(data.error);
2014-03-14 21:24:04 +09:00
} else {
2012-04-12 07:52:15 +09:00
error(data.error);
2014-03-14 21:24:04 +09:00
}
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();
}
}
2012-04-12 07:52:15 +09:00
}, this);
messageHandler.on('UnsupportedFeature',
function transportUnsupportedFeature(data) {
if (this.destroyed) {
return; // Ignore any pending requests if the worker was terminated.
}
var featureId = data.featureId;
var loadingTask = this.loadingTask;
if (loadingTask.onUnsupportedFeature) {
loadingTask.onUnsupportedFeature(featureId);
}
_UnsupportedManager.notify(featureId);
}, this);
messageHandler.on('JpegDecode', function(data) {
if (this.destroyed) {
return Promise.reject(new Error('Worker was destroyed'));
}
var imageUrl = data[0];
2012-04-12 07:52:15 +09:00
var components = data[1];
if (components !== 3 && components !== 1) {
return Promise.reject(
new Error('Only 3 components or 1 component can be returned'));
2014-03-14 21:24:04 +09:00
}
2012-04-12 07:52:15 +09:00
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 Uint8Array(size * components);
var tmpCanvas = createScratchCanvas(width, 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];
}
2012-04-12 07:52:15 +09:00
}
resolve({ data: buf, width: width, height: height});
};
img.onerror = function () {
reject(new Error('JpegDecode failed to load image'));
};
img.src = imageUrl;
});
}, this);
2012-04-12 07:52:15 +09:00
},
getData: function WorkerTransport_getData() {
return this.messageHandler.sendWithPromise('GetData', null);
},
2014-04-30 00:07:05 +09:00
getPage: function WorkerTransport_getPage(pageNumber, capability) {
if (!isInt(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
return Promise.reject(new Error('Invalid page request'));
2014-02-24 03:16:14 +09:00
}
2012-04-13 02:01:07 +09:00
var pageIndex = pageNumber - 1;
if (pageIndex in this.pagePromises) {
return this.pagePromises[pageIndex];
2014-03-14 21:24:04 +09:00
}
var promise = this.messageHandler.sendWithPromise('GetPage', {
pageIndex: pageIndex
}).then(function (pageInfo) {
if (this.destroyed) {
throw new Error('Transport destroyed');
}
var page = new PDFPageProxy(pageIndex, pageInfo, this);
this.pageCache[pageIndex] = page;
return page;
}.bind(this));
this.pagePromises[pageIndex] = promise;
return promise;
},
getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
return this.messageHandler.sendWithPromise('GetPageIndex', {
ref: ref,
}).catch(function (reason) {
return Promise.reject(new Error(reason));
});
},
getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) {
return this.messageHandler.sendWithPromise('GetAnnotations', {
pageIndex: pageIndex,
intent: intent,
});
2013-02-07 08:19:29 +09:00
},
getDestinations: function WorkerTransport_getDestinations() {
return this.messageHandler.sendWithPromise('GetDestinations', null);
},
getDestination: function WorkerTransport_getDestination(id) {
return this.messageHandler.sendWithPromise('GetDestination', { id: id });
},
getPageLabels: function WorkerTransport_getPageLabels() {
return this.messageHandler.sendWithPromise('GetPageLabels', null);
},
getAttachments: function WorkerTransport_getAttachments() {
return this.messageHandler.sendWithPromise('GetAttachments', null);
},
2014-05-08 04:15:34 +09:00
getJavaScript: function WorkerTransport_getJavaScript() {
return this.messageHandler.sendWithPromise('GetJavaScript', null);
2014-05-08 04:15:34 +09:00
},
2014-05-08 04:06:44 +09:00
getOutline: function WorkerTransport_getOutline() {
return this.messageHandler.sendWithPromise('GetOutline', null);
2014-05-08 04:06:44 +09:00
},
getMetadata: function WorkerTransport_getMetadata() {
return this.messageHandler.sendWithPromise('GetMetadata', null).
then(function transportMetadata(results) {
return {
info: results[0],
metadata: (results[1] ? new Metadata(results[1]) : null)
};
});
},
getStats: function WorkerTransport_getStats() {
return this.messageHandler.sendWithPromise('GetStats', null);
},
startCleanup: function WorkerTransport_startCleanup() {
this.messageHandler.sendWithPromise('Cleanup', null).
then(function endCleanup() {
for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
var page = this.pageCache[i];
if (page) {
page.cleanup();
}
}
this.commonObjs.clear();
this.fontLoader.clear();
}.bind(this));
2012-04-12 07:52:15 +09:00
}
};
2012-04-13 04:11:22 +09:00
return WorkerTransport;
2012-04-12 07:52:15 +09:00
2012-04-10 14:20:57 +09:00
})();
/**
* A PDF document and page is built of many objects. E.g. there are objects
* for fonts, images, rendering code and such. These objects might get processed
* inside of a worker. The `PDFObjects` implements some basic functions to
* manage these objects.
* @ignore
*/
var PDFObjects = (function PDFObjectsClosure() {
function PDFObjects() {
this.objs = Object.create(null);
}
PDFObjects.prototype = {
/**
* Internal function.
* Ensures there is an object defined for `objId`.
*/
ensureObj: function PDFObjects_ensureObj(objId) {
2014-03-14 21:24:04 +09:00
if (this.objs[objId]) {
return this.objs[objId];
2014-03-14 21:24:04 +09:00
}
var obj = {
2014-04-30 00:07:05 +09:00
capability: createPromiseCapability(),
data: null,
resolved: false
};
this.objs[objId] = obj;
return obj;
},
/**
* If called *without* callback, this returns the data of `objId` but the
* object needs to be resolved. If it isn't, this function throws.
*
* If called *with* a callback, the callback is called with the data of the
* object once the object is resolved. That means, if you call this
* function and the object is already resolved, the callback gets called
* right away.
*/
get: function PDFObjects_get(objId, callback) {
// If there is a callback, then the get can be async and the object is
// not required to be resolved right now
if (callback) {
2014-04-30 00:07:05 +09:00
this.ensureObj(objId).capability.promise.then(callback);
return null;
}
// If there isn't a callback, the user expects to get the resolved data
// directly.
var obj = this.objs[objId];
// If there isn't an object yet or the object isn't resolved, then the
// data isn't ready yet!
2014-03-14 21:24:04 +09:00
if (!obj || !obj.resolved) {
error('Requesting object that isn\'t resolved yet ' + objId);
2014-03-14 21:24:04 +09:00
}
return obj.data;
},
/**
* Resolves the object `objId` with optional `data`.
*/
resolve: function PDFObjects_resolve(objId, data) {
var obj = this.ensureObj(objId);
obj.resolved = true;
obj.data = data;
2014-04-30 00:07:05 +09:00
obj.capability.resolve(data);
},
isResolved: function PDFObjects_isResolved(objId) {
var objs = this.objs;
if (!objs[objId]) {
return false;
} else {
return objs[objId].resolved;
}
},
hasData: function PDFObjects_hasData(objId) {
return this.isResolved(objId);
},
/**
* Returns the data of `objId` if object exists, null otherwise.
*/
getData: function PDFObjects_getData(objId) {
var objs = this.objs;
if (!objs[objId] || !objs[objId].resolved) {
return null;
} else {
return objs[objId].data;
}
},
clear: function PDFObjects_clear() {
this.objs = Object.create(null);
}
};
return PDFObjects;
})();
/**
* Allows controlling of the rendering tasks.
* @class
* @alias RenderTask
*/
var RenderTask = (function RenderTaskClosure() {
function RenderTask(internalRenderTask) {
this._internalRenderTask = internalRenderTask;
/**
* Callback for incremental rendering -- a function that will be called
* each time the rendering is paused. To continue rendering call the
* function that is the first argument to the callback.
* @type {function}
*/
this.onContinue = null;
}
RenderTask.prototype = /** @lends RenderTask.prototype */ {
/**
* Promise for rendering task completion.
* @return {Promise}
*/
get promise() {
return this._internalRenderTask.capability.promise;
},
/**
* Cancels the rendering task. If the task is currently rendering it will
* not be cancelled until graphics pauses with a timeout. The promise that
* this object extends will resolved when cancelled.
*/
cancel: function RenderTask_cancel() {
this._internalRenderTask.cancel();
2014-04-12 02:10:42 +09:00
},
/**
* Registers callbacks to indicate the rendering task completion.
2014-04-12 02:10:42 +09:00
*
* @param {function} onFulfilled The callback for the rendering completion.
* @param {function} onRejected The callback for the rendering failure.
* @return {Promise} A promise that is resolved after the onFulfilled or
* onRejected callback.
*/
then: function RenderTask_then(onFulfilled, onRejected) {
return this.promise.then.apply(this.promise, arguments);
}
};
return RenderTask;
})();
/**
* For internal use only.
* @ignore
*/
var InternalRenderTask = (function InternalRenderTaskClosure() {
function InternalRenderTask(callback, params, objs, commonObjs, operatorList,
pageNumber) {
this.callback = callback;
this.params = params;
this.objs = objs;
this.commonObjs = commonObjs;
this.operatorListIdx = null;
this.operatorList = operatorList;
this.pageNumber = pageNumber;
this.running = false;
this.graphicsReadyCallback = null;
this.graphicsReady = false;
2015-05-12 22:44:42 +09:00
this.useRequestAnimationFrame = false;
this.cancelled = false;
2014-04-30 00:07:05 +09:00
this.capability = createPromiseCapability();
this.task = new RenderTask(this);
// caching this-bound methods
this._continueBound = this._continue.bind(this);
this._scheduleNextBound = this._scheduleNext.bind(this);
this._nextBound = this._next.bind(this);
}
InternalRenderTask.prototype = {
initializeGraphics:
function InternalRenderTask_initializeGraphics(transparency) {
if (this.cancelled) {
return;
}
if (getDefaultSetting('pdfBug') && globalScope.StepperManager &&
globalScope.StepperManager.enabled) {
this.stepper = globalScope.StepperManager.create(this.pageNumber - 1);
this.stepper.init(this.operatorList);
this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
}
var params = this.params;
this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
this.objs, params.imageLayer);
this.gfx.beginDrawing(params.transform, params.viewport, transparency);
this.operatorListIdx = 0;
this.graphicsReady = true;
if (this.graphicsReadyCallback) {
this.graphicsReadyCallback();
}
},
cancel: function InternalRenderTask_cancel() {
this.running = false;
this.cancelled = true;
this.callback('cancelled');
},
operatorListChanged: function InternalRenderTask_operatorListChanged() {
if (!this.graphicsReady) {
if (!this.graphicsReadyCallback) {
this.graphicsReadyCallback = this._continueBound;
}
return;
}
if (this.stepper) {
this.stepper.updateOperatorList(this.operatorList);
}
if (this.running) {
return;
}
this._continue();
},
_continue: function InternalRenderTask__continue() {
this.running = true;
if (this.cancelled) {
return;
}
if (this.task.onContinue) {
this.task.onContinue.call(this.task, this._scheduleNextBound);
} else {
this._scheduleNext();
}
},
_scheduleNext: function InternalRenderTask__scheduleNext() {
if (this.useRequestAnimationFrame && typeof window !== 'undefined') {
2015-05-12 22:44:42 +09:00
window.requestAnimationFrame(this._nextBound);
} else {
Promise.resolve(undefined).then(this._nextBound);
}
},
_next: function InternalRenderTask__next() {
if (this.cancelled) {
return;
}
this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
this.operatorListIdx,
this._continueBound,
this.stepper);
if (this.operatorListIdx === this.operatorList.argsArray.length) {
this.running = false;
if (this.operatorList.lastChunk) {
this.gfx.endDrawing();
this.callback();
}
}
}
};
return InternalRenderTask;
})();
/**
* (Deprecated) Global observer of unsupported feature usages. Use
* onUnsupportedFeature callback of the {PDFDocumentLoadingTask} instance.
*/
var _UnsupportedManager = (function UnsupportedManagerClosure() {
var listeners = [];
return {
listen: function (cb) {
deprecated('Global UnsupportedManager.listen is used: ' +
' use PDFDocumentLoadingTask.onUnsupportedFeature instead');
listeners.push(cb);
},
notify: function (featureId) {
for (var i = 0, ii = listeners.length; i < ii; i++) {
listeners[i](featureId);
}
}
};
})();
if (typeof pdfjsVersion !== 'undefined') {
exports.version = pdfjsVersion;
}
if (typeof pdfjsBuild !== 'undefined') {
exports.build = pdfjsBuild;
}
exports.getDocument = getDocument;
exports.PDFDataRangeTransport = PDFDataRangeTransport;
exports.PDFWorker = PDFWorker;
exports.PDFDocumentProxy = PDFDocumentProxy;
exports.PDFPageProxy = PDFPageProxy;
exports._UnsupportedManager = _UnsupportedManager;
}));