From acdd49f48097ffcb27332673a8b51cd653f6de88 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 27 Oct 2015 10:07:20 -0500 Subject: [PATCH 1/3] Adds peer communication between MessageHandlers. --- src/core/worker.js | 81 +++++++++++++++++++++++++++------------------- src/display/api.js | 68 ++++++++++++++++++++++++++++++++------ src/shared/util.js | 37 +++++++++++++-------- 3 files changed, 129 insertions(+), 57 deletions(-) diff --git a/src/core/worker.js b/src/core/worker.js index 1e80d5aab..82a4946f0 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -51,12 +51,49 @@ var WorkerTask = (function WorkerTaskClosure() { })(); var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { - setup: function wphSetup(handler) { + setup: function wphSetup(handler, port) { + handler.on('test', function wphSetupTest(data) { + // check if Uint8Array can be sent to worker + if (!(data instanceof Uint8Array)) { + handler.send('test', 'main', false); + return; + } + // making sure postMessage transfers are working + var supportTransfers = data[0] === 255; + handler.postMessageTransfers = supportTransfers; + // check if the response property is supported by xhr + var xhr = new XMLHttpRequest(); + var responseExists = 'response' in xhr; + // check if the property is actually implemented + try { + var dummy = xhr.responseType; + } catch (e) { + responseExists = false; + } + if (!responseExists) { + handler.send('test', false); + return; + } + handler.send('test', { + supportTypedArray: true, + supportTransfers: supportTransfers + }); + }); + + handler.on('GetDocRequest', function wphSetupDoc(data) { + return WorkerMessageHandler.createDocumentHandler(data, port); + }); + }, + createDocumentHandler: function wphCreateDocumentHandler(data, port) { var pdfManager; var terminated = false; var cancelXHRs = null; var WorkerTasks = []; + var mainHandlerName = data.docId; + var workerHandlerName = data.docId + '_worker'; + var handler = new MessageHandler(workerHandlerName, mainHandlerName, port); + function ensureNotTerminated() { if (terminated) { throw new Error('Worker was terminated'); @@ -262,35 +299,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { return pdfManagerCapability.promise; } - handler.on('test', function wphSetupTest(data) { - // check if Uint8Array can be sent to worker - if (!(data instanceof Uint8Array)) { - handler.send('test', false); - return; - } - // making sure postMessage transfers are working - var supportTransfers = data[0] === 255; - handler.postMessageTransfers = supportTransfers; - // check if the response property is supported by xhr - var xhr = new XMLHttpRequest(); - var responseExists = 'response' in xhr; - // check if the property is actually implemented - try { - var dummy = xhr.responseType; - } catch (e) { - responseExists = false; - } - if (!responseExists) { - handler.send('test', false); - return; - } - handler.send('test', { - supportTypedArray: true, - supportTransfers: supportTransfers - }); - }); - - handler.on('GetDocRequest', function wphSetupDoc(data) { + var setupDoc = function(data) { var onSuccess = function(doc) { ensureNotTerminated(); handler.send('GetDoc', { pdfInfo: doc }); @@ -366,7 +375,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); }, onFailure); }, onFailure); - }); + }; handler.on('GetPage', function wphSetupGetPage(data) { return pdfManager.getPage(data.pageIndex).then(function(page) { @@ -548,6 +557,9 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { return Promise.all(waitOn).then(function () {}); }); + + setupDoc(data); + return workerHandlerName; } }; @@ -557,6 +569,7 @@ var workerConsole = { log: function log() { var args = Array.prototype.slice.call(arguments); globalScope.postMessage({ + targetName: 'main', action: 'console_log', data: args }); @@ -565,6 +578,7 @@ var workerConsole = { error: function error() { var args = Array.prototype.slice.call(arguments); globalScope.postMessage({ + targetName: 'main', action: 'console_error', data: args }); @@ -594,11 +608,12 @@ if (typeof window === 'undefined') { // Listen for unsupported features so we can pass them on to the main thread. PDFJS.UnsupportedManager.listen(function (msg) { globalScope.postMessage({ + targetName: 'main', action: '_unsupported_feature', data: msg }); }); - var handler = new MessageHandler('worker_processor', this); - WorkerMessageHandler.setup(handler); + var handler = new MessageHandler('worker', 'main', this); + WorkerMessageHandler.setup(handler, this); } diff --git a/src/display/api.js b/src/display/api.js index c9034fb48..7e65ba6f2 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -17,7 +17,7 @@ Promise, PasswordResponses, PasswordException, InvalidPDFException, MissingPDFException, UnknownErrorException, FontFaceObject, loadJpegStream, createScratchCanvas, CanvasGraphics, stringToBytes, - UnexpectedResponseException, deprecated */ + UnexpectedResponseException, deprecated, UnsupportedManager */ 'use strict'; @@ -353,6 +353,12 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { this._capability = createPromiseCapability(); this._transport = null; + /** + * 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 @@ -383,6 +389,10 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { * is completed. */ destroy: function () { + this.destroyed = true; + if (!this._transport) { + return Promise.resolve(); + } return this._transport.destroy(); }, @@ -1042,8 +1052,7 @@ var WorkerTransport = (function WorkerTransportClosure() { // 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); - this.messageHandler = messageHandler; + var messageHandler = new MessageHandler('main', 'worker', worker); messageHandler.on('test', function transportTest(data) { var supportTypedArray = data && data.supportTypedArray; @@ -1052,13 +1061,23 @@ var WorkerTransport = (function WorkerTransportClosure() { if (!data.supportTransfers) { PDFJS.postMessageTransfers = false; } - this.setupMessageHandler(messageHandler); + this.setupMainMessageHandler(messageHandler, worker); workerInitializedCapability.resolve(); } else { this.setupFakeWorker(); } }.bind(this)); + messageHandler.on('console_log', function (data) { + console.log.apply(console, data); + }); + messageHandler.on('console_error', function (data) { + console.error.apply(console, data); + }); + messageHandler.on('_unsupported_feature', function (data) { + UnsupportedManager.notify(data); + }); + var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); // Some versions of Opera throw a DATA_CLONE_ERR on serializing the // typed array. Also, checking if we can use transfers. @@ -1141,23 +1160,41 @@ var WorkerTransport = (function WorkerTransportClosure() { warn('Setting up fake worker.'); // If we don't use a worker, just post/sendMessage to the main thread. var fakeWorker = { + _listeners: [], postMessage: function WorkerTransport_postMessage(obj) { - fakeWorker.onmessage({data: obj}); + var e = {data: obj}; + this._listeners.forEach(function (listener) { + listener.call(this, e); + }, 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 WorkerTransport_terminate() {} }; - var messageHandler = new MessageHandler('main', fakeWorker); - this.setupMessageHandler(messageHandler); + var messageHandler = new MessageHandler('main', 'worker', fakeWorker); + this.setupMainMessageHandler(messageHandler, fakeWorker); // If the main thread is our worker, setup the handling for the messages // the main thread sends to it self. - PDFJS.WorkerMessageHandler.setup(messageHandler); + var workerHandler = new MessageHandler('worker', 'main', fakeWorker); + PDFJS.WorkerMessageHandler.setup(workerHandler, fakeWorker); this.workerInitializedCapability.resolve(); }.bind(this)); }, + setupMainMessageHandler: + function WorkerTransport_setupMainMessageHandler(messageHandler, port) { + this.mainMessageHandler = messageHandler; + this.port = port; + }, + setupMessageHandler: function WorkerTransport_setupMessageHandler(messageHandler) { this.messageHandler = messageHandler; @@ -1441,7 +1478,9 @@ var WorkerTransport = (function WorkerTransportClosure() { source.length = this.pdfDataRangeTransport.length; source.initialData = this.pdfDataRangeTransport.initialData; } - this.messageHandler.send('GetDocRequest', { + var docId = 'doc'; + this.mainMessageHandler.sendWithPromise('GetDocRequest', { + docId: docId, source: source, disableRange: PDFJS.disableRange, maxImageSize: PDFJS.maxImageSize, @@ -1450,7 +1489,16 @@ var WorkerTransport = (function WorkerTransportClosure() { disableFontFace: PDFJS.disableFontFace, disableCreateObjectURL: PDFJS.disableCreateObjectURL, verbosity: PDFJS.verbosity - }); + }).then(function (workerId) { + if (this.destroyed) { + loadingTask._capability.reject(new Error('Loading aborted')); + this.destroyCapability.resolve(); + return; + } + + var messageHandler = new MessageHandler(docId, workerId, this.port); + this.setupMessageHandler(messageHandler); + }.bind(this), loadingTask._capability.reject); }, getData: function WorkerTransport_getData() { diff --git a/src/shared/util.js b/src/shared/util.js index 6845a5fd4..299baf2d7 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -1518,26 +1518,20 @@ PDFJS.createObjectURL = (function createObjectURLClosure() { }; })(); -function MessageHandler(name, comObj) { - this.name = name; +function MessageHandler(sourceName, targetName, comObj) { + this.sourceName = sourceName; + this.targetName = targetName; this.comObj = comObj; this.callbackIndex = 1; this.postMessageTransfers = true; var callbacksCapabilities = this.callbacksCapabilities = {}; var ah = this.actionHandler = {}; - ah['console_log'] = [function ahConsoleLog(data) { - console.log.apply(console, data); - }]; - ah['console_error'] = [function ahConsoleError(data) { - console.error.apply(console, data); - }]; - ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) { - UnsupportedManager.notify(data); - }]; - - comObj.onmessage = function messageHandlerComObjOnMessage(event) { + this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { var data = event.data; + if (data.targetName !== this.sourceName) { + return; + } if (data.isReply) { var callbackId = data.callbackId; if (data.callbackId in callbacksCapabilities) { @@ -1554,10 +1548,14 @@ function MessageHandler(name, comObj) { } else if (data.action in ah) { var action = ah[data.action]; if (data.callbackId) { + var sourceName = this.sourceName; + var targetName = data.sourceName; Promise.resolve().then(function () { return action[0].call(action[1], data.data); }).then(function (result) { comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, isReply: true, callbackId: data.callbackId, data: result @@ -1568,6 +1566,8 @@ function MessageHandler(name, comObj) { reason = reason + ''; } comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, isReply: true, callbackId: data.callbackId, error: reason @@ -1579,7 +1579,8 @@ function MessageHandler(name, comObj) { } else { error('Unknown action from worker: ' + data.action); } - }; + }.bind(this); + comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { @@ -1598,6 +1599,8 @@ MessageHandler.prototype = { */ send: function messageHandlerSend(actionName, data, transfers) { var message = { + sourceName: this.sourceName, + targetName: this.targetName, action: actionName, data: data }; @@ -1615,6 +1618,8 @@ MessageHandler.prototype = { function messageHandlerSendWithPromise(actionName, data, transfers) { var callbackId = this.callbackIndex++; var message = { + sourceName: this.sourceName, + targetName: this.targetName, action: actionName, data: data, callbackId: callbackId @@ -1640,6 +1645,10 @@ MessageHandler.prototype = { } else { this.comObj.postMessage(message); } + }, + + destroy: function () { + this.comObj.removeEventListener('message', this._onComObjOnMessage); } }; From 09772e1e1543445ceb9ba68bc3ae346ebcfc7e82 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 27 Oct 2015 12:55:15 -0500 Subject: [PATCH 2/3] Creates PDFWorker, separates fetchDocument from transport. --- src/core/worker.js | 9 +- src/display/api.js | 475 ++++++++++++++++++++++++++---------------- test/unit/api_spec.js | 70 ++++++- 3 files changed, 370 insertions(+), 184 deletions(-) diff --git a/src/core/worker.js b/src/core/worker.js index 82a4946f0..8474e97a9 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -85,6 +85,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); }, createDocumentHandler: function wphCreateDocumentHandler(data, port) { + // This context is actually holds references on pdfManager and handler, + // until the latter is destroyed. var pdfManager; var terminated = false; var cancelXHRs = null; @@ -555,7 +557,12 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { task.terminate(); }); - return Promise.all(waitOn).then(function () {}); + return Promise.all(waitOn).then(function () { + // Notice that even if we destroying handler, resolved response promise + // must be sent back. + handler.destroy(); + handler = null; + }); }); setupDoc(data); diff --git a/src/display/api.js b/src/display/api.js index 7e65ba6f2..68eeab9a2 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -222,6 +222,8 @@ PDFJS.isEvalSupported = (PDFJS.isEvalSupported === undefined ? * @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. */ /** @@ -284,7 +286,6 @@ PDFJS.getDocument = function getDocument(src, task.onPassword = passwordCallback || null; task.onProgress = progressCallback || null; - var workerInitializedCapability, transport; var source; if (typeof src === 'string') { source = { url: src }; @@ -305,12 +306,18 @@ PDFJS.getDocument = function getDocument(src, } var params = {}; + var rangeTransport = null; + var worker = null; for (var key in source) { if (key === 'url' && typeof window !== 'undefined') { // The full path is required in the 'url' field. params[key] = combineUrl(window.location.href, source[key]); 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. @@ -331,27 +338,91 @@ PDFJS.getDocument = function getDocument(src, params[key] = source[key]; } - params.rangeChunkSize = source.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE; + params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE; - workerInitializedCapability = createPromiseCapability(); - transport = new WorkerTransport(workerInitializedCapability, source.range); - workerInitializedCapability.promise.then(function transportInitialized() { - transport.fetchDocument(task, params); - }); - task._transport = transport; + 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; + }); + }, task._capability.reject); return task; }; +/** + * 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 = PDFJS.disableAutoFetch; + source.disableStream = PDFJS.disableStream; + source.chunkedViewerLoading = !!pdfDataRangeTransport; + if (pdfDataRangeTransport) { + source.length = pdfDataRangeTransport.length; + source.initialData = pdfDataRangeTransport.initialData; + } + return worker.messageHandler.sendWithPromise('GetDocRequest', { + docId: docId, + source: source, + disableRange: PDFJS.disableRange, + maxImageSize: PDFJS.maxImageSize, + cMapUrl: PDFJS.cMapUrl, + cMapPacked: PDFJS.cMapPacked, + disableFontFace: PDFJS.disableFontFace, + disableCreateObjectURL: PDFJS.disableCreateObjectURL, + verbosity: PDFJS.verbosity + }).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. @@ -390,10 +461,16 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { */ destroy: function () { this.destroyed = true; - if (!this._transport) { - return Promise.resolve(); - } - return this._transport.destroy(); + + 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)); }, /** @@ -622,7 +699,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * Destroys current document instance and terminates worker. */ destroy: function PDFDocumentProxy_destroy() { - return this.transport.destroy(); + return this.loadingTask.destroy(); } }; return PDFDocumentProxy; @@ -1018,17 +1095,211 @@ var PDFPageProxy = (function PDFPageProxyClosure() { return PDFPageProxy; })(); +/** + * 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 + */ +var PDFWorker = (function PDFWorkerClosure() { + var nextFakeWorkerId = 0; + + // Loads worker code into main thread. + function setupFakeWorkerGlobal() { + if (!PDFJS.fakeWorkerFilesLoadedCapability) { + PDFJS.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 !PRODUCTION + Util.loadScript(PDFJS.workerSrc); +//#endif +//#if PRODUCTION && SINGLE_FILE +// PDFJS.fakeWorkerFilesLoadedCapability.resolve(); +//#endif +//#if PRODUCTION && !SINGLE_FILE +// Util.loadScript(PDFJS.workerSrc, function() { +// PDFJS.fakeWorkerFilesLoadedCapability.resolve(); +// }); +//#endif + } + return PDFJS.fakeWorkerFilesLoadedCapability.promise; + } + + 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 fullfills + // 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 !SINGLE_FILE + if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { + var workerSrc = PDFJS.workerSrc; + if (!workerSrc) { + error('No PDFJS.workerSrc specified'); + } + + try { + // 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); + + messageHandler.on('test', function PDFWorker_test(data) { + if (this.destroyed) { + this._readyCapability.reject(new Error('Worker was destroyed')); + messageHandler.destroy(); + worker.terminate(); + return; // worker was destroyed + } + var supportTypedArray = data && data.supportTypedArray; + if (supportTypedArray) { + this._messageHandler = messageHandler; + this._port = worker; + this._webWorker = worker; + if (!data.supportTransfers) { + PDFJS.postMessageTransfers = false; + } + this._readyCapability.resolve(); + } 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); + }); + messageHandler.on('_unsupported_feature', function (data) { + UnsupportedManager.notify(data); + }); + + var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); + // 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); + } + return; + } catch (e) { + info('The worker has been disabled.'); + } + } +//#endif + // Either workers are disabled, not supported or have thrown an exception. + // Thus, we fallback to a faked worker. + this._setupFakeWorker(); + }, + + _setupFakeWorker: function PDFWorker_setupFakeWorker() { + warn('Setting up fake worker.'); + globalScope.PDFJS.disableWorker = true; + + setupFakeWorkerGlobal().then(function () { + if (this.destroyed) { + this._readyCapability.reject(new Error('Worker was destroyed')); + return; + } + + // If we don't use a worker, just post/sendMessage to the main thread. + var port = { + _listeners: [], + postMessage: function (obj) { + var e = {data: obj}; + this._listeners.forEach(function (listener) { + listener.call(this, e); + }, 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._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); + PDFJS.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; + } + } + }; + + return PDFWorker; +})(); +PDFJS.PDFWorker = PDFWorker; + /** * For internal use only. * @ignore */ var WorkerTransport = (function WorkerTransportClosure() { - function WorkerTransport(workerInitializedCapability, pdfDataRangeTransport) { + function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport) { + this.messageHandler = messageHandler; + this.loadingTask = loadingTask; this.pdfDataRangeTransport = pdfDataRangeTransport; - this.workerInitializedCapability = workerInitializedCapability; this.commonObjs = new PDFObjects(); - this.loadingTask = null; this.destroyed = false; this.destroyCapability = null; @@ -1036,67 +1307,7 @@ var WorkerTransport = (function WorkerTransportClosure() { this.pagePromises = []; this.downloadInfoCapability = createPromiseCapability(); - // If worker support isn't disabled explicit and the browser has worker - // support, create a new web worker and test if it/the browser fullfills - // 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 version 15. -//#if !SINGLE_FILE - if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { - var workerSrc = PDFJS.workerSrc; - if (!workerSrc) { - error('No PDFJS.workerSrc specified'); - } - - try { - // 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); - - messageHandler.on('test', function transportTest(data) { - var supportTypedArray = data && data.supportTypedArray; - if (supportTypedArray) { - this.worker = worker; - if (!data.supportTransfers) { - PDFJS.postMessageTransfers = false; - } - this.setupMainMessageHandler(messageHandler, worker); - workerInitializedCapability.resolve(); - } else { - this.setupFakeWorker(); - } - }.bind(this)); - - messageHandler.on('console_log', function (data) { - console.log.apply(console, data); - }); - messageHandler.on('console_error', function (data) { - console.error.apply(console, data); - }); - messageHandler.on('_unsupported_feature', function (data) { - UnsupportedManager.notify(data); - }); - - var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); - // 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); - } - return; - } catch (e) { - info('The worker has been disabled.'); - } - } -//#endif - // Either workers are disabled, not supported or have thrown an exception. - // Thus, we fallback to a faked worker. - this.setupFakeWorker(); + this.setupMessageHandler(); } WorkerTransport.prototype = { destroy: function WorkerTransport_destroy() { @@ -1123,81 +1334,22 @@ var WorkerTransport = (function WorkerTransportClosure() { waitOn.push(terminated); Promise.all(waitOn).then(function () { FontLoader.clear(); - if (self.worker) { - self.worker.terminate(); - } if (self.pdfDataRangeTransport) { self.pdfDataRangeTransport.abort(); self.pdfDataRangeTransport = null; } - self.messageHandler = null; + if (self.messageHandler) { + self.messageHandler.destroy(); + self.messageHandler = null; + } self.destroyCapability.resolve(); }, this.destroyCapability.reject); return this.destroyCapability.promise; }, - setupFakeWorker: function WorkerTransport_setupFakeWorker() { - globalScope.PDFJS.disableWorker = true; - - if (!PDFJS.fakeWorkerFilesLoadedCapability) { - PDFJS.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 !PRODUCTION - Util.loadScript(PDFJS.workerSrc); -//#endif -//#if PRODUCTION && SINGLE_FILE -// PDFJS.fakeWorkerFilesLoadedCapability.resolve(); -//#endif -//#if PRODUCTION && !SINGLE_FILE -// Util.loadScript(PDFJS.workerSrc, function() { -// PDFJS.fakeWorkerFilesLoadedCapability.resolve(); -// }); -//#endif - } - PDFJS.fakeWorkerFilesLoadedCapability.promise.then(function () { - warn('Setting up fake worker.'); - // If we don't use a worker, just post/sendMessage to the main thread. - var fakeWorker = { - _listeners: [], - postMessage: function WorkerTransport_postMessage(obj) { - var e = {data: obj}; - this._listeners.forEach(function (listener) { - listener.call(this, e); - }, 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 WorkerTransport_terminate() {} - }; - - var messageHandler = new MessageHandler('main', 'worker', fakeWorker); - this.setupMainMessageHandler(messageHandler, fakeWorker); - - // If the main thread is our worker, setup the handling for the messages - // the main thread sends to it self. - var workerHandler = new MessageHandler('worker', 'main', fakeWorker); - PDFJS.WorkerMessageHandler.setup(workerHandler, fakeWorker); - - this.workerInitializedCapability.resolve(); - }.bind(this)); - }, - - setupMainMessageHandler: - function WorkerTransport_setupMainMessageHandler(messageHandler, port) { - this.mainMessageHandler = messageHandler; - this.port = port; - }, - setupMessageHandler: - function WorkerTransport_setupMessageHandler(messageHandler) { - this.messageHandler = messageHandler; + function WorkerTransport_setupMessageHandler() { + var messageHandler = this.messageHandler; function updatePassword(password) { messageHandler.send('UpdatePassword', password); @@ -1462,45 +1614,6 @@ var WorkerTransport = (function WorkerTransportClosure() { }, this); }, - fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) { - if (this.destroyed) { - loadingTask._capability.reject(new Error('Loading aborted')); - this.destroyCapability.resolve(); - return; - } - - this.loadingTask = loadingTask; - - source.disableAutoFetch = PDFJS.disableAutoFetch; - source.disableStream = PDFJS.disableStream; - source.chunkedViewerLoading = !!this.pdfDataRangeTransport; - if (this.pdfDataRangeTransport) { - source.length = this.pdfDataRangeTransport.length; - source.initialData = this.pdfDataRangeTransport.initialData; - } - var docId = 'doc'; - this.mainMessageHandler.sendWithPromise('GetDocRequest', { - docId: docId, - source: source, - disableRange: PDFJS.disableRange, - maxImageSize: PDFJS.maxImageSize, - cMapUrl: PDFJS.cMapUrl, - cMapPacked: PDFJS.cMapPacked, - disableFontFace: PDFJS.disableFontFace, - disableCreateObjectURL: PDFJS.disableCreateObjectURL, - verbosity: PDFJS.verbosity - }).then(function (workerId) { - if (this.destroyed) { - loadingTask._capability.reject(new Error('Loading aborted')); - this.destroyCapability.resolve(); - return; - } - - var messageHandler = new MessageHandler(docId, workerId, this.port); - this.setupMessageHandler(messageHandler); - }.bind(this), loadingTask._capability.reject); - }, - getData: function WorkerTransport_getData() { return this.messageHandler.sendWithPromise('GetData', null); }, diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 1bfa02f86..9749942ff 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -77,8 +77,7 @@ describe('api', function() { var loadingTask = PDFJS.getDocument(basicApiUrl); // This can be somewhat random -- we cannot guarantee perfect // 'Terminate' message to the worker before/after setting up pdfManager. - var destroyed = loadingTask._transport.workerInitializedCapability. - promise.then(function () { + var destroyed = loadingTask._worker.promise.then(function () { return loadingTask.destroy(); }); waitsForPromiseResolved(destroyed, function (data) { @@ -208,6 +207,73 @@ describe('api', function() { }); }); }); + describe('PDFWorker', function() { + it('worker created or destroyed', function () { + var worker = new PDFJS.PDFWorker('test1'); + waitsForPromiseResolved(worker.promise, function () { + expect(worker.name).toEqual('test1'); + expect(!!worker.port).toEqual(true); + expect(worker.destroyed).toEqual(false); + expect(!!worker._webWorker).toEqual(true); + expect(worker.port === worker._webWorker).toEqual(true); + + worker.destroy(); + expect(!!worker.port).toEqual(false); + expect(worker.destroyed).toEqual(true); + }); + }); + it('worker created or destroyed by getDocument', function () { + var loadingTask = PDFJS.getDocument(basicApiUrl); + var worker; + waitsForPromiseResolved(loadingTask.promise, function () { + worker = loadingTask._worker; + expect(!!worker).toEqual(true); + }); + + var destroyPromise = loadingTask.promise.then(function () { + return loadingTask.destroy(); + }); + waitsForPromiseResolved(destroyPromise, function () { + var destroyedWorker = loadingTask._worker; + expect(!!destroyedWorker).toEqual(false); + expect(worker.destroyed).toEqual(true); + }); + }); + it('worker created and can be used in getDocument', function () { + var worker = new PDFJS.PDFWorker('test1'); + var loadingTask = PDFJS.getDocument({url: basicApiUrl, worker: worker}); + waitsForPromiseResolved(loadingTask.promise, function () { + var docWorker = loadingTask._worker; + expect(!!docWorker).toEqual(false); + // checking is the same port is used in the MessageHandler + var messageHandlerPort = loadingTask._transport.messageHandler.comObj; + expect(messageHandlerPort === worker.port).toEqual(true); + }); + + var destroyPromise = loadingTask.promise.then(function () { + return loadingTask.destroy(); + }); + waitsForPromiseResolved(destroyPromise, function () { + expect(worker.destroyed).toEqual(false); + worker.destroy(); + }); + }); + it('creates more than one worker', function () { + var worker1 = new PDFJS.PDFWorker('test1'); + var worker2 = new PDFJS.PDFWorker('test2'); + var worker3 = new PDFJS.PDFWorker('test3'); + var ready = Promise.all([worker1.promise, worker2.promise, + worker3.promise]); + waitsForPromiseResolved(ready, function () { + expect(worker1.port !== worker2.port && + worker1.port !== worker3.port && + worker2.port !== worker3.port).toEqual(true); + worker1.destroy(); + worker2.destroy(); + worker3.destroy(); + }); + }); + }); describe('PDFDocument', function() { var promise = PDFJS.getDocument(basicApiUrl); var doc; From 06c190467563da0d672a933805cc63ccf6fc64f8 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 27 Oct 2015 17:48:10 -0500 Subject: [PATCH 3/3] Refactors FontLoader to group fonts per document. --- examples/node/domstubs.js | 37 ++++++---- src/core/evaluator.js | 2 +- src/core/pdf_manager.js | 11 ++- src/core/worker.js | 12 +-- src/display/api.js | 7 +- src/display/font_loader.js | 146 +++++++++++++++++++------------------ 6 files changed, 117 insertions(+), 98 deletions(-) diff --git a/examples/node/domstubs.js b/examples/node/domstubs.js index 4918a4af9..5eb186b8d 100644 --- a/examples/node/domstubs.js +++ b/examples/node/domstubs.js @@ -1,17 +1,6 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -var sheet = { - cssRules: [], - insertRule: function(rule) { - this.cssRules.push(rule); - }, -}; - -var style = { - sheet: sheet, -}; - function xmlEncode(s){ var i = 0, ch; s = String(s); @@ -75,6 +64,15 @@ function DOMElement(name) { this.childNodes = []; this.attributes = {}; this.textContent = ''; + + if (name === 'style') { + this.sheet = { + cssRules: [], + insertRule: function (rule) { + this.cssRules.push(rule); + }, + }; + } } DOMElement.prototype = { @@ -125,14 +123,23 @@ DOMElement.prototype = { global.document = { childNodes : [], - getElementById: function (id) { - if (id === 'PDFJS_FONT_STYLE_TAG') { - return style; - } + get documentElement() { + return this; }, createElementNS: function (NS, element) { var elObject = new DOMElement(element); return elObject; }, + + createElement: function (element) { + return this.createElementNS('', element); + }, + + getElementsByTagName: function (element) { + if (element === 'head') { + return [this.head || (this.head = new DOMElement('head'))]; + } + return []; + } }; diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 51ff19741..7e80ecf42 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -529,7 +529,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Keep track of each font we translated so the caller can // load them asynchronously before calling display on a page. - font.loadedName = 'g_font_' + (fontRefIsDict ? + font.loadedName = 'g_' + this.pdfManager.docId + '_f' + (fontRefIsDict ? fontName.replace(/\W/g, '') : fontID); font.translated = fontCapability.promise; diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index 7fbe57732..90e3d78e2 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -24,6 +24,10 @@ var BasePdfManager = (function BasePdfManagerClosure() { } BasePdfManager.prototype = { + get docId() { + return this._docId; + }, + onLoadedStream: function BasePdfManager_onLoadedStream() { throw new NotImplementedException(); }, @@ -85,7 +89,8 @@ var BasePdfManager = (function BasePdfManagerClosure() { })(); var LocalPdfManager = (function LocalPdfManagerClosure() { - function LocalPdfManager(data, password) { + function LocalPdfManager(docId, data, password) { + this._docId = docId; var stream = new Stream(data); this.pdfDocument = new PDFDocument(this, stream, password); this._loadedStreamCapability = createPromiseCapability(); @@ -136,8 +141,8 @@ var LocalPdfManager = (function LocalPdfManagerClosure() { })(); var NetworkPdfManager = (function NetworkPdfManagerClosure() { - function NetworkPdfManager(args, msgHandler) { - + function NetworkPdfManager(docId, args, msgHandler) { + this._docId = docId; this.msgHandler = msgHandler; var params = { diff --git a/src/core/worker.js b/src/core/worker.js index 8474e97a9..08fa18981 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -92,9 +92,9 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var cancelXHRs = null; var WorkerTasks = []; - var mainHandlerName = data.docId; + var docId = data.docId; var workerHandlerName = data.docId + '_worker'; - var handler = new MessageHandler(workerHandlerName, mainHandlerName, port); + var handler = new MessageHandler(workerHandlerName, docId, port); function ensureNotTerminated() { if (terminated) { @@ -153,7 +153,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var disableRange = data.disableRange; if (source.data) { try { - pdfManager = new LocalPdfManager(source.data, source.password); + pdfManager = new LocalPdfManager(docId, source.data, source.password); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -161,7 +161,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { return pdfManagerCapability.promise; } else if (source.chunkedViewerLoading) { try { - pdfManager = new NetworkPdfManager(source, handler); + pdfManager = new NetworkPdfManager(docId, source, handler); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -218,7 +218,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } try { - pdfManager = new NetworkPdfManager(source, handler); + pdfManager = new NetworkPdfManager(docId, source, handler); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -263,7 +263,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // the data is array, instantiating directly from it try { - pdfManager = new LocalPdfManager(pdfFile, source.password); + pdfManager = new LocalPdfManager(docId, pdfFile, source.password); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); diff --git a/src/display/api.js b/src/display/api.js index 68eeab9a2..970481dd9 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1299,6 +1299,7 @@ var WorkerTransport = (function WorkerTransportClosure() { this.loadingTask = loadingTask; this.pdfDataRangeTransport = pdfDataRangeTransport; this.commonObjs = new PDFObjects(); + this.fontLoader = new FontLoader(loadingTask.docId); this.destroyed = false; this.destroyCapability = null; @@ -1333,7 +1334,7 @@ var WorkerTransport = (function WorkerTransportClosure() { var terminated = this.messageHandler.sendWithPromise('Terminate', null); waitOn.push(terminated); Promise.all(waitOn).then(function () { - FontLoader.clear(); + self.fontLoader.clear(); if (self.pdfDataRangeTransport) { self.pdfDataRangeTransport.abort(); self.pdfDataRangeTransport = null; @@ -1489,7 +1490,7 @@ var WorkerTransport = (function WorkerTransportClosure() { font = new FontFaceObject(exportedData); } - FontLoader.bind( + this.fontLoader.bind( [font], function fontReady(fontObjs) { this.commonObjs.resolve(id, font); @@ -1697,7 +1698,7 @@ var WorkerTransport = (function WorkerTransportClosure() { } } this.commonObjs.clear(); - FontLoader.clear(); + this.fontLoader.clear(); }.bind(this)); } }; diff --git a/src/display/font_loader.js b/src/display/font_loader.js index b800bf012..73f902fba 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -19,12 +19,24 @@ PDFJS.disableFontFace = false; -var FontLoader = { +function FontLoader(docId) { + this.docId = docId; + this.styleElement = null; +//#if !(MOZCENTRAL) + this.nativeFontFaces = []; + this.loadTestFontId = 0; + this.loadingContext = { + requests: [], + nextRequestId: 0 + }; +//#endif +} +FontLoader.prototype = { insertRule: function fontLoaderInsertRule(rule) { - var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); + var styleElement = this.styleElement; if (!styleElement) { - styleElement = document.createElement('style'); - styleElement.id = 'PDFJS_FONT_STYLE_TAG'; + styleElement = this.styleElement = document.createElement('style'); + styleElement.id = 'PDFJS_FONT_STYLE_TAG_' + this.docId; document.documentElement.getElementsByTagName('head')[0].appendChild( styleElement); } @@ -34,7 +46,7 @@ var FontLoader = { }, clear: function fontLoaderClear() { - var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); + var styleElement = this.styleElement; if (styleElement) { styleElement.parentNode.removeChild(styleElement); } @@ -75,49 +87,6 @@ var FontLoader = { )); }, - get isEvalSupported() { - var evalSupport = false; - if (PDFJS.isEvalSupported) { - try { - /* jshint evil: true */ - new Function(''); - evalSupport = true; - } catch (e) {} - } - return shadow(this, 'isEvalSupported', evalSupport); - }, - - loadTestFontId: 0, - - loadingContext: { - requests: [], - nextRequestId: 0 - }, - - isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() { - if (isWorker) { - return false; - } - - // User agent string sniffing is bad, but there is no reliable way to tell - // if font is fully loaded and ready to be used with canvas. - var userAgent = window.navigator.userAgent; - var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent); - if (m && m[1] >= 14) { - return true; - } - // TODO other browsers - if (userAgent === 'node') { - return true; - } - return false; - })(), - - nativeFontFaces: [], - - isFontLoadingAPISupported: (!isWorker && typeof document !== 'undefined' && - !!document.fonts), - addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) { this.nativeFontFaces.push(nativeFontFace); document.fonts.add(nativeFontFace); @@ -146,27 +115,29 @@ var FontLoader = { } font.attached = true; - if (this.isFontLoadingAPISupported) { + if (FontLoader.isFontLoadingAPISupported) { var nativeFontFace = font.createNativeFontFace(); if (nativeFontFace) { + this.addNativeFontFace(nativeFontFace); fontLoadPromises.push(getNativeFontPromise(nativeFontFace)); } } else { - var rule = font.bindDOM(); + var rule = font.createFontFaceRule(); if (rule) { + this.insertRule(rule); rules.push(rule); fontsToLoad.push(font); } } } - var request = FontLoader.queueLoadingCallback(callback); - if (this.isFontLoadingAPISupported) { + var request = this.queueLoadingCallback(callback); + if (FontLoader.isFontLoadingAPISupported) { Promise.all(fontLoadPromises).then(function() { request.complete(); }); - } else if (rules.length > 0 && !this.isSyncFontLoadingSupported) { - FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); + } else if (rules.length > 0 && !FontLoader.isSyncFontLoadingSupported) { + this.prepareFontLoadEvent(rules, fontsToLoad, request); } else { request.complete(); } @@ -184,7 +155,7 @@ var FontLoader = { } } - var context = FontLoader.loadingContext; + var context = this.loadingContext; var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++); var request = { id: requestId, @@ -271,7 +242,7 @@ var FontLoader = { var url = 'url(data:font/opentype;base64,' + btoa(data) + ');'; var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' + url + '}'; - FontLoader.insertRule(rule); + this.insertRule(rule); var names = []; for (i = 0, ii = fonts.length; i < ii; i++) { @@ -316,19 +287,56 @@ var FontLoader = { //} //#endif }; +//#if !(MOZCENTRAL) +FontLoader.isFontLoadingAPISupported = (!isWorker && + typeof document !== 'undefined' && !!document.fonts); +//#endif +//#if !(MOZCENTRAL || CHROME) +Object.defineProperty(FontLoader, 'isSyncFontLoadingSupported', { + get: function () { + var supported = false; + + // User agent string sniffing is bad, but there is no reliable way to tell + // if font is fully loaded and ready to be used with canvas. + var userAgent = window.navigator.userAgent; + var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent); + if (m && m[1] >= 14) { + supported = true; + } + // TODO other browsers + if (userAgent === 'node') { + supported = true; + } + return shadow(FontLoader, 'isSyncFontLoadingSupported', supported); + }, + enumerable: true, + configurable: true +}); +//#endif var FontFaceObject = (function FontFaceObjectClosure() { - function FontFaceObject(name, file, properties) { + function FontFaceObject(translatedData) { this.compiledGlyphs = {}; - if (arguments.length === 1) { - // importing translated data - var data = arguments[0]; - for (var i in data) { - this[i] = data[i]; - } - return; + // importing translated data + for (var i in translatedData) { + this[i] = translatedData[i]; } } + Object.defineProperty(FontFaceObject, 'isEvalSupported', { + get: function () { + var evalSupport = false; + if (PDFJS.isEvalSupported) { + try { + /* jshint evil: true */ + new Function(''); + evalSupport = true; + } catch (e) {} + } + return shadow(this, 'isEvalSupported', evalSupport); + }, + enumerable: true, + configurable: true + }); FontFaceObject.prototype = { //#if !(MOZCENTRAL) createNativeFontFace: function FontFaceObject_createNativeFontFace() { @@ -343,8 +351,6 @@ var FontFaceObject = (function FontFaceObjectClosure() { var nativeFontFace = new FontFace(this.loadedName, this.data, {}); - FontLoader.addNativeFontFace(nativeFontFace); - if (PDFJS.pdfBug && 'FontInspector' in globalScope && globalScope['FontInspector'].enabled) { globalScope['FontInspector'].fontAdded(this); @@ -353,7 +359,7 @@ var FontFaceObject = (function FontFaceObjectClosure() { }, //#endif - bindDOM: function FontFaceObject_bindDOM() { + createFontFaceRule: function FontFaceObject_createFontFaceRule() { if (!this.data) { return null; } @@ -370,7 +376,6 @@ var FontFaceObject = (function FontFaceObjectClosure() { var url = ('url(data:' + this.mimetype + ';base64,' + window.btoa(data) + ');'); var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}'; - FontLoader.insertRule(rule); if (PDFJS.pdfBug && 'FontInspector' in globalScope && globalScope['FontInspector'].enabled) { @@ -380,13 +385,14 @@ var FontFaceObject = (function FontFaceObjectClosure() { return rule; }, - getPathGenerator: function FontLoader_getPathGenerator(objs, character) { + getPathGenerator: + function FontFaceObject_getPathGenerator(objs, character) { if (!(character in this.compiledGlyphs)) { var cmds = objs.get(this.loadedName + '_path_' + character); var current, i, len; // If we can, compile cmds into JS for MAXIMUM SPEED - if (FontLoader.isEvalSupported) { + if (FontFaceObject.isEvalSupported) { var args, js = ''; for (i = 0, len = cmds.length; i < len; i++) { current = cmds[i];