Separate page objects/images from the fonts; does not store large images

This commit is contained in:
Yury Delendik 2012-10-28 15:10:34 -05:00
parent d2c1868497
commit 69b72078c0
7 changed files with 97 additions and 73 deletions

View File

@ -172,8 +172,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
this.transport = transport; this.transport = transport;
this.stats = new StatTimer(); this.stats = new StatTimer();
this.stats.enabled = !!globalScope.PDFJS.enableStats; this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.objs = transport.objs; this.commonObjs = transport.commonObjs;
this.objs = new PDFObjects();
this.renderInProgress = false; this.renderInProgress = false;
this.cleanupAfterRender = false;
} }
PDFPageProxy.prototype = { PDFPageProxy.prototype = {
/** /**
@ -263,9 +265,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
var self = this; var self = this;
function complete(error) { function complete(error) {
self.renderInProgress = false; self.renderInProgress = false;
if (self.destroyed) { if (self.destroyed || self.cleanupAfterRender) {
delete self.operatorList;
delete self.displayReadyPromise; delete self.displayReadyPromise;
delete self.operatorList;
self.objs.clear();
} }
if (error) if (error)
@ -283,7 +286,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
return; return;
} }
var gfx = new CanvasGraphics(params.canvasContext, var gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
this.objs, params.textLayer); this.objs, params.textLayer);
try { try {
this.display(gfx, params.viewport, complete, continueCallback); this.display(gfx, params.viewport, complete, continueCallback);
@ -329,7 +332,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
// Convert the font names to the corresponding font obj. // Convert the font names to the corresponding font obj.
var fontObjs = []; var fontObjs = [];
for (var i = 0, ii = fonts.length; i < ii; i++) { for (var i = 0, ii = fonts.length; i < ii; i++) {
var obj = this.objs.objs[fonts[i]].data; var obj = this.commonObjs.getData(fonts[i]);
if (obj.error) { if (obj.error) {
warn('Error during font loading: ' + obj.error); warn('Error during font loading: ' + obj.error);
continue; continue;
@ -423,6 +426,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
if (!this.renderInProgress) { if (!this.renderInProgress) {
delete this.operatorList; delete this.operatorList;
delete this.displayReadyPromise; delete this.displayReadyPromise;
this.objs.clear();
} }
} }
}; };
@ -434,7 +438,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
var WorkerTransport = (function WorkerTransportClosure() { var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport(workerInitializedPromise, workerReadyPromise) { function WorkerTransport(workerInitializedPromise, workerReadyPromise) {
this.workerReadyPromise = workerReadyPromise; this.workerReadyPromise = workerReadyPromise;
this.objs = new PDFObjects(); this.commonObjs = new PDFObjects();
this.pageCache = []; this.pageCache = [];
this.pagePromises = []; this.pagePromises = [];
@ -569,21 +573,13 @@ var WorkerTransport = (function WorkerTransportClosure() {
page.startRenderingFromOperatorList(data.operatorList, depFonts); page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this); }, this);
messageHandler.on('obj', function transportObj(data) { messageHandler.on('commonobj', function transportObj(data) {
var id = data[0]; var id = data[0];
var type = data[1]; var type = data[1];
if (this.objs.hasData(id)) if (this.commonObjs.hasData(id))
return; return;
switch (type) { switch (type) {
case 'JpegStream':
var imageData = data[2];
loadJpegStream(id, imageData, this.objs);
break;
case 'Image':
var imageData = data[2];
this.objs.resolve(id, imageData);
break;
case 'Font': case 'Font':
var exportedData = data[2]; var exportedData = data[2];
@ -594,10 +590,39 @@ var WorkerTransport = (function WorkerTransportClosure() {
font = new ErrorFont(exportedData.error); font = new ErrorFont(exportedData.error);
else else
font = new Font(exportedData); font = new Font(exportedData);
this.objs.resolve(id, font); this.commonObjs.resolve(id, font);
break; break;
default: default:
error('Got unkown object type ' + type); error('Got unknown common object type ' + type);
}
}, this);
messageHandler.on('obj', function transportObj(data) {
var id = data[0];
var pageIndex = data[1];
var type = data[2];
var pageProxy = this.pageCache[pageIndex];
if (pageProxy.objs.hasData(id))
return;
switch (type) {
case 'JpegStream':
var imageData = data[3];
loadJpegStream(id, imageData, pageProxy.objs);
break;
case 'Image':
var 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 ('data' in imageData &&
imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
pageProxy.cleanupAfterRender = true;
}
break;
default:
error('Got unknown object type ' + type);
} }
}, this); }, this);

View File

@ -208,13 +208,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// before it stops and shedules a continue of execution. // before it stops and shedules a continue of execution.
var kExecutionTime = 15; var kExecutionTime = 15;
function CanvasGraphics(canvasCtx, objs, textLayer) { function CanvasGraphics(canvasCtx, commonObjs, objs, textLayer) {
this.ctx = canvasCtx; this.ctx = canvasCtx;
this.current = new CanvasExtraState(); this.current = new CanvasExtraState();
this.stateStack = []; this.stateStack = [];
this.pendingClip = null; this.pendingClip = null;
this.res = null; this.res = null;
this.xobjs = null; this.xobjs = null;
this.commonObjs = commonObjs;
this.objs = objs; this.objs = objs;
this.textLayer = textLayer; this.textLayer = textLayer;
if (canvasCtx) { if (canvasCtx) {
@ -283,6 +284,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var executionEndIdx; var executionEndIdx;
var endTime = Date.now() + kExecutionTime; var endTime = Date.now() + kExecutionTime;
var commonObjs = this.commonObjs;
var objs = this.objs; var objs = this.objs;
var fnName; var fnName;
var slowCommands = this.slowCommands; var slowCommands = this.slowCommands;
@ -301,13 +303,18 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var deps = argsArray[i]; var deps = argsArray[i];
for (var n = 0, nn = deps.length; n < nn; n++) { for (var n = 0, nn = deps.length; n < nn; n++) {
var depObjId = deps[n]; var depObjId = deps[n];
var common = depObjId.substring(0, 2) == 'g_';
// If the promise isn't resolved yet, add the continueCallback // If the promise isn't resolved yet, add the continueCallback
// to the promise and bail out. // to the promise and bail out.
if (!objs.isResolved(depObjId)) { if (!common && !objs.isResolved(depObjId)) {
objs.get(depObjId, continueCallback); objs.get(depObjId, continueCallback);
return i; return i;
} }
if (common && !commonObjs.isResolved(depObjId)) {
commonObjs.get(depObjId, continueCallback);
return i;
}
} }
} }
@ -620,7 +627,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.current.leading = -leading; this.current.leading = -leading;
}, },
setFont: function CanvasGraphics_setFont(fontRefName, size) { setFont: function CanvasGraphics_setFont(fontRefName, size) {
var fontObj = this.objs.get(fontRefName); var fontObj = this.commonObjs.get(fontRefName);
var current = this.current; var current = this.current;
if (!fontObj) if (!fontObj)

View File

@ -97,8 +97,8 @@ globalScope.PDFJS.getPdf = getPdf;
globalScope.PDFJS.pdfBug = false; globalScope.PDFJS.pdfBug = false;
var Page = (function PageClosure() { var Page = (function PageClosure() {
function Page(xref, pageNumber, pageDict, ref) { function Page(xref, pageIndex, pageDict, ref) {
this.pageNumber = pageNumber; this.pageIndex = pageIndex;
this.pageDict = pageDict; this.pageDict = pageDict;
this.xref = xref; this.xref = xref;
this.ref = ref; this.ref = ref;
@ -167,14 +167,11 @@ var Page = (function PageClosure() {
} }
return shadow(this, 'rotate', rotate); return shadow(this, 'rotate', rotate);
}, },
getContentStream: function Page_getContentStream() {
getOperatorList: function Page_getOperatorList(handler, dependency) {
var xref = this.xref;
var content = this.content; var content = this.content;
var resources = this.resources;
if (isArray(content)) { if (isArray(content)) {
// fetching items // fetching items
var streams = []; var xref = this.xref;
var i, n = content.length; var i, n = content.length;
var streams = []; var streams = [];
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
@ -184,13 +181,19 @@ var Page = (function PageClosure() {
content.reset(); content.reset();
} else if (!content) { } else if (!content) {
// replacing non-existent page content with empty one // replacing non-existent page content with empty one
content = new Stream(new Uint8Array(0)); content = new NullStream();
} }
return content;
},
getOperatorList: function Page_getOperatorList(handler, dependency) {
var xref = this.xref;
var contentStream = this.getContentStream();
var resources = this.resources;
var pe = this.pe = new PartialEvaluator( var pe = this.pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_'); xref, handler, this.pageIndex,
'p' + this.pageIndex + '_');
return pe.getOperatorList(content, resources, dependency); return pe.getOperatorList(contentStream, resources, dependency);
}, },
extractTextContent: function Page_extractTextContent() { extractTextContent: function Page_extractTextContent() {
var handler = { var handler = {
@ -199,40 +202,13 @@ var Page = (function PageClosure() {
}; };
var xref = this.xref; var xref = this.xref;
var content = xref.fetchIfRef(this.content); var contentStream = this.getContentStream();
var resources = xref.fetchIfRef(this.resources); var resources = xref.fetchIfRef(this.resources);
if (isArray(content)) {
// fetching items
var i, n = content.length;
var streams = [];
for (i = 0; i < n; ++i)
streams.push(xref.fetchIfRef(content[i]));
content = new StreamsSequenceStream(streams);
} else if (isStream(content)) {
content.reset();
}
var pe = new PartialEvaluator( var pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_'); xref, handler, this.pageIndex,
return pe.getTextContent(content, resources); 'p' + this.pageIndex + '_');
}, return pe.getTextContent(contentStream, resources);
ensureFonts: function Page_ensureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this)
);
}, },
getLinks: function Page_getLinks() { getLinks: function Page_getLinks() {
var links = []; var links = [];

View File

@ -18,12 +18,13 @@
'use strict'; 'use strict';
var PartialEvaluator = (function PartialEvaluatorClosure() { var PartialEvaluator = (function PartialEvaluatorClosure() {
function PartialEvaluator(xref, handler, uniquePrefix) { function PartialEvaluator(xref, handler, pageIndex, uniquePrefix) {
this.state = new EvalState(); this.state = new EvalState();
this.stateStack = []; this.stateStack = [];
this.xref = xref; this.xref = xref;
this.handler = handler; this.handler = handler;
this.pageIndex = pageIndex;
this.uniquePrefix = uniquePrefix; this.uniquePrefix = uniquePrefix;
this.objIdCounter = 0; this.objIdCounter = 0;
this.fontIdCounter = 0; this.fontIdCounter = 0;
@ -151,7 +152,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!isDict(font)) { if (!isDict(font)) {
return { return {
translated: new ErrorFont('Font ' + fontName + ' is not available'), translated: new ErrorFont('Font ' + fontName + ' is not available'),
loadedName: 'font_' + this.uniquePrefix + this.fontIdCounter loadedName: 'g_font_' + this.uniquePrefix + this.fontIdCounter
}; };
} }
@ -159,7 +160,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!loadedName) { if (!loadedName) {
// keep track of each font we translated so the caller can // keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page // load them asynchronously before calling display on a page
loadedName = 'font_' + this.uniquePrefix + this.fontIdCounter; loadedName = 'g_font_' + this.uniquePrefix + this.fontIdCounter;
font.loadedName = loadedName; font.loadedName = loadedName;
var translated; var translated;
@ -197,6 +198,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var self = this; var self = this;
var xref = this.xref; var xref = this.xref;
var handler = this.handler; var handler = this.handler;
var pageIndex = this.pageIndex;
var uniquePrefix = this.uniquePrefix || ''; var uniquePrefix = this.uniquePrefix || '';
function insertDependency(depList) { function insertDependency(depList) {
@ -217,7 +219,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!font.sent) { if (!font.sent) {
var data = font.translated.exportData(); var data = font.translated.exportData();
handler.send('obj', [ handler.send('commonobj', [
loadedName, loadedName,
'Font', 'Font',
data data
@ -271,7 +273,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
image.isNativelySupported(xref, resources)) { image.isNativelySupported(xref, resources)) {
// These JPEGs don't need any more processing so we can just send it. // These JPEGs don't need any more processing so we can just send it.
fn = 'paintJpegXObject'; fn = 'paintJpegXObject';
handler.send('obj', [objId, 'JpegStream', image.getIR()]); handler.send('obj', [objId, pageIndex, 'JpegStream', image.getIR()]);
return; return;
} }
@ -287,7 +289,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}; };
var pixels = imgData.data; var pixels = imgData.data;
imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight); imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight);
handler.send('obj', [objId, 'Image', imgData]); handler.send('obj', [objId, pageIndex, 'Image', imgData]);
}, handler, xref, resources, image, inline); }, handler, xref, resources, image, inline);
} }

View File

@ -754,8 +754,6 @@ var PDFObjects = (function PDFObjectsClosure() {
} }
PDFObjects.prototype = { PDFObjects.prototype = {
objs: null,
/** /**
* Internal function. * Internal function.
* Ensures there is an object defined for `objId`. Stores `data` on the * Ensures there is an object defined for `objId`. Stores `data` on the
@ -832,6 +830,18 @@ var PDFObjects = (function PDFObjectsClosure() {
} }
}, },
/**
* Returns the data of `objId` if object exists, null otherwise.
*/
getData: function PDFObjects_getData(objId) {
var objs = this.objs;
if (!objs[objId] || !objs[objId].hasData) {
return null;
} else {
return objs[objId].data;
}
},
/** /**
* Sets the data of an object but *doesn't* resolve it. * Sets the data of an object but *doesn't* resolve it.
*/ */
@ -839,6 +849,10 @@ var PDFObjects = (function PDFObjectsClosure() {
// Watchout! If you call `this.ensureObj(objId, data)` you're going to // Watchout! If you call `this.ensureObj(objId, data)` you're going to
// create a *resolved* promise which shouldn't be the case! // create a *resolved* promise which shouldn't be the case!
this.ensureObj(objId).data = data; this.ensureObj(objId).data = data;
},
clear: function PDFObjects_clear() {
this.objs = {};
} }
}; };
return PDFObjects; return PDFObjects;

View File

@ -286,7 +286,7 @@ var TilingPattern = (function TilingPatternClosure() {
// set the new canvas element context as the graphics context // set the new canvas element context as the graphics context
var tmpCtx = tmpCanvas.getContext('2d'); var tmpCtx = tmpCanvas.getContext('2d');
var graphics = new CanvasGraphics(tmpCtx, objs); var graphics = new CanvasGraphics(tmpCtx, null, objs);
switch (paintType) { switch (paintType) {
case PaintType.COLORED: case PaintType.COLORED:

View File

@ -259,7 +259,7 @@ var WorkerMessageHandler = {
var fonts = {}; var fonts = {};
for (var i = 0, ii = dependency.length; i < ii; i++) { for (var i = 0, ii = dependency.length; i < ii; i++) {
var dep = dependency[i]; var dep = dependency[i];
if (dep.indexOf('font_') == 0) { if (dep.indexOf('g_font_') == 0) {
fonts[dep] = true; fonts[dep] = true;
} }
} }