diff --git a/src/api.js b/src/api.js index 18644ebe6..3d97dacd2 100644 --- a/src/api.js +++ b/src/api.js @@ -133,6 +133,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { this.stats = new StatTimer(); this.stats.enabled = !!globalScope.PDFJS.enableStats; this.objs = transport.objs; + this.renderInProgress = false; } PDFPageProxy.prototype = { /** @@ -198,6 +199,8 @@ var PDFPageProxy = (function PDFPageProxyClosure() { * rendering. */ render: function(params) { + this.renderInProgress = true; + var promise = new Promise(); var stats = this.stats; stats.time('Overall'); @@ -205,6 +208,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // requested before. Make the request and create the promise. if (!this.displayReadyPromise) { this.displayReadyPromise = new Promise(); + this.destroyed = false; this.stats.time('Page Request'); this.transport.messageHandler.send('RenderPageRequest', { @@ -212,7 +216,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }); } + var self = this; function complete(error) { + self.renderInProgress = false; + if (self.destroyed) { + delete self.operatorList; + delete self.displayReadyPromise; + } + if (error) promise.reject(error); else @@ -222,6 +233,11 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // Once the operatorList and fonts are loaded, do the actual rendering. this.displayReadyPromise.then( function pageDisplayReadyPromise() { + if (self.destroyed) { + complete(); + return; + } + var gfx = new CanvasGraphics(params.canvasContext, this.objs, params.textLayer); try { @@ -305,7 +321,6 @@ var PDFPageProxy = (function PDFPageProxyClosure() { gfx.executeOperatorList(operatorList, startIdx, next, stepper); if (startIdx == length) { gfx.endDrawing(); - delete this.operatorList; stats.timeEnd('Rendering'); stats.timeEnd('Overall'); if (callback) callback(); @@ -333,6 +348,17 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }; promise.resolve(operationList); return promise; + }, + /** + * Destroys resources allocated by the page. + */ + destroy: function() { + this.destroyed = true; + + if (!this.renderInProgress) { + delete this.operatorList; + delete this.displayReadyPromise; + } } }; return PDFPageProxy; @@ -463,6 +489,8 @@ var WorkerTransport = (function WorkerTransportClosure() { messageHandler.on('obj', function transportObj(data) { var id = data[0]; var type = data[1]; + if (this.objs.hasData(id)) + return; switch (type) { case 'JpegStream': diff --git a/src/core.js b/src/core.js index 41f9a9c61..2734d0eef 100644 --- a/src/core.js +++ b/src/core.js @@ -132,20 +132,18 @@ var Page = (function PageClosure() { }, getOperatorList: function Page_getOperatorList(handler, dependency) { - if (this.operatorList) { - // content was compiled - return this.operatorList; - } - var xref = this.xref; var content = this.content; var resources = this.resources; if (isArray(content)) { // fetching items + var streams = []; var i, n = content.length; for (i = 0; i < n; ++i) - content[i] = xref.fetchIfRef(content[i]); - content = new StreamsSequenceStream(content); + streams.push(xref.fetchIfRef(content[i])); + content = new StreamsSequenceStream(streams); + } else if (isStream(content)) { + content.reset(); } else if (!content) { // replacing non-existent page content with empty one content = new Stream(new Uint8Array(0)); @@ -154,8 +152,7 @@ var Page = (function PageClosure() { var pe = this.pe = new PartialEvaluator( xref, handler, 'p' + this.pageNumber + '_'); - this.operatorList = pe.getOperatorList(content, resources, dependency); - return this.operatorList; + return pe.getOperatorList(content, resources, dependency); }, getLinks: function Page_getLinks() { diff --git a/src/evaluator.js b/src/evaluator.js index c57e291c0..e07394201 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -153,13 +153,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { font = xref.fetchIfRef(font) || fontRes.get(fontName); assertWellFormed(isDict(font)); + ++self.objIdCounter; if (!font.translated) { font.translated = self.translateFont(font, xref, resources, dependency); if (font.translated) { // keep track of each font we translated so the caller can // load them asynchronously before calling display on a page - loadedName = 'font_' + uniquePrefix + (++self.objIdCounter); + loadedName = 'font_' + uniquePrefix + self.objIdCounter; font.translated.properties.loadedName = loadedName; font.loadedName = loadedName; diff --git a/test/driver.js b/test/driver.js index 26c5a156a..cd5ea49e7 100644 --- a/test/driver.js +++ b/test/driver.js @@ -194,11 +194,15 @@ function nextPage(task, loadError) { textLayer: textLayerBuilder, viewport: viewport }; + var completeRender = (function(error) { + page.destroy(); + snapshotCurrentPage(task, error); + }); page.render(renderContext).then(function() { - snapshotCurrentPage(task, false); + completeRender(false); }, function(error) { - snapshotCurrentPage(task, 'render : ' + error); + completeRender('render : ' + error); }); }, function(error) { diff --git a/web/viewer.js b/web/viewer.js index 68f0a6a33..3233371cc 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -32,7 +32,7 @@ var Cache = function cacheCache(size) { data.splice(i); data.push(view); if (data.length > size) - data.shift().update(); + data.shift().destroy(); }; }; @@ -743,6 +743,11 @@ var PageView = function pageView(container, pdfPage, id, scale, container.appendChild(anchor); container.appendChild(div); + this.destroy = function pageViewDestroy() { + this.update(); + this.pdfPage.destroy(); + }; + this.update = function pageViewUpdate(scale) { this.scale = scale || this.scale; var viewport = this.pdfPage.getViewport(this.scale);