Merge pull request #4770 from yurydelendik/promise-operationlist

Adds Promises to getOperatorList
This commit is contained in:
Jonas Jenwald 2014-05-19 23:22:40 +02:00
commit 7079992d89
6 changed files with 610 additions and 489 deletions

View File

@ -156,13 +156,6 @@ var Page = (function PageClosure() {
getOperatorList: function Page_getOperatorList(handler, intent) { getOperatorList: function Page_getOperatorList(handler, intent) {
var self = this; var self = this;
var capability = createPromiseCapability();
function reject(e) {
capability.reject(e);
}
var pageListCapability = createPromiseCapability();
var pdfManager = this.pdfManager; var pdfManager = this.pdfManager;
var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', var contentStreamPromise = pdfManager.ensure(this, 'getContentStream',
@ -184,9 +177,8 @@ var Page = (function PageClosure() {
this.idCounters, this.idCounters,
this.fontCache); this.fontCache);
var dataPromises = Promise.all([contentStreamPromise, resourcesPromise], var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
reject); var pageListPromise = dataPromises.then(function(data) {
dataPromises.then(function(data) {
var contentStream = data[0]; var contentStream = data[0];
var opList = new OperatorList(intent, handler, self.pageIndex); var opList = new OperatorList(intent, handler, self.pageIndex);
@ -195,31 +187,30 @@ var Page = (function PageClosure() {
pageIndex: self.pageIndex, pageIndex: self.pageIndex,
intent: intent intent: intent
}); });
partialEvaluator.getOperatorList(contentStream, self.resources, opList); return partialEvaluator.getOperatorList(contentStream, self.resources,
pageListCapability.resolve(opList); opList).then(function () {
return opList;
});
}); });
var annotationsPromise = pdfManager.ensure(this, 'annotations'); var annotationsPromise = pdfManager.ensure(this, 'annotations');
Promise.all([pageListCapability.promise, annotationsPromise]).then( return Promise.all([pageListPromise, annotationsPromise]).then(
function(datas) { function(datas) {
var pageOpList = datas[0]; var pageOpList = datas[0];
var annotations = datas[1]; var annotations = datas[1];
if (annotations.length === 0) { if (annotations.length === 0) {
pageOpList.flush(true); pageOpList.flush(true);
capability.resolve(pageOpList); return pageOpList;
return;
} }
var annotationsReadyPromise = Annotation.appendToOperatorList( var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageOpList, pdfManager, partialEvaluator, intent); annotations, pageOpList, pdfManager, partialEvaluator, intent);
annotationsReadyPromise.then(function () { return annotationsReadyPromise.then(function () {
pageOpList.flush(true); pageOpList.flush(true);
capability.resolve(pageOpList); return pageOpList;
}, reject); });
}, reject); });
return capability.promise;
}, },
extractTextContent: function Page_extractTextContent() { extractTextContent: function Page_extractTextContent() {

View File

@ -22,7 +22,7 @@
stdFontMap, symbolsFonts, getTilingPatternIR, warn, Util, Promise, stdFontMap, symbolsFonts, getTilingPatternIR, warn, Util, Promise,
RefSetCache, isRef, TextRenderingMode, CMapFactory, OPS, RefSetCache, isRef, TextRenderingMode, CMapFactory, OPS,
UNSUPPORTED_FEATURES, UnsupportedManager, NormalizedUnicodes, UNSUPPORTED_FEATURES, UnsupportedManager, NormalizedUnicodes,
IDENTITY_MATRIX, reverseIfRtl */ IDENTITY_MATRIX, reverseIfRtl, createPromiseCapability */
'use strict'; 'use strict';
@ -38,6 +38,28 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
this.fontCache = fontCache; this.fontCache = fontCache;
} }
// Trying to minimize Date.now() usage and check every 100 time
var TIME_SLOT_DURATION_MS = 20;
var CHECK_TIME_EVERY = 100;
function TimeSlotManager() {
this.reset();
}
TimeSlotManager.prototype = {
check: function TimeSlotManager_check() {
if (++this.checked < CHECK_TIME_EVERY) {
return false;
}
this.checked = 0;
return this.endTime <= Date.now();
},
reset: function TimeSlotManager_reset() {
this.endTime = Date.now() + TIME_SLOT_DURATION_MS;
this.checked = 0;
}
};
var deferred = Promise.resolve();
var TILING_PATTERN = 1, SHADING_PATTERN = 2; var TILING_PATTERN = 1, SHADING_PATTERN = 2;
PartialEvaluator.prototype = { PartialEvaluator.prototype = {
@ -121,13 +143,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
this.getOperatorList(xobj, (xobj.dict.get('Resources') || resources), return this.getOperatorList(xobj,
operatorList, initialState); (xobj.dict.get('Resources') || resources), operatorList, initialState).
operatorList.addOp(OPS.paintFormXObjectEnd, []); then(function () {
operatorList.addOp(OPS.paintFormXObjectEnd, []);
if (group) { if (group) {
operatorList.addOp(OPS.endGroup, [groupOptions]); operatorList.addOp(OPS.endGroup, [groupOptions]);
} }
});
}, },
buildPaintImageXObject: buildPaintImageXObject:
@ -236,7 +260,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
subtype: smask.get('S').name, subtype: smask.get('S').name,
backdrop: smask.get('BC') backdrop: smask.get('BC')
}; };
this.buildFormXObject(resources, smaskContent, smaskOptions, return this.buildFormXObject(resources, smaskContent, smaskOptions,
operatorList, stateManager.state.clone()); operatorList, stateManager.state.clone());
}, },
@ -245,15 +269,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
pattern, patternDict, pattern, patternDict,
operatorList) { operatorList) {
// Create an IR of the pattern code. // Create an IR of the pattern code.
var tilingOpList = this.getOperatorList(pattern, var tilingOpList = new OperatorList();
(patternDict.get('Resources') || resources)); return this.getOperatorList(pattern,
// Add the dependencies to the parent operator list so they are resolved (patternDict.get('Resources') || resources), tilingOpList).
// before sub operator list is executed synchronously. then(function () {
operatorList.addDependencies(tilingOpList.dependencies); // Add the dependencies to the parent operator list so they are
operatorList.addOp(fn, getTilingPatternIR({ // resolved before sub operator list is executed synchronously.
fnArray: tilingOpList.fnArray, operatorList.addDependencies(tilingOpList.dependencies);
argsArray: tilingOpList.argsArray operatorList.addOp(fn, getTilingPatternIR({
}, patternDict, args)); fnArray: tilingOpList.fnArray,
argsArray: tilingOpList.argsArray
}, patternDict, args));
});
}, },
handleSetFont: handleSetFont:
@ -266,22 +293,23 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
fontName = fontArgs[0].name; fontName = fontArgs[0].name;
} }
var self = this; var self = this;
var font = this.loadFont(fontName, fontRef, this.xref, resources, return this.loadFont(fontName, fontRef, this.xref, resources,
operatorList); operatorList).then(function (font) {
state.font = font; state.font = font;
var loadedName = font.loadedName; var loadedName = font.loadedName;
if (!font.sent) { if (!font.sent) {
var fontData = font.translated.exportData(); var fontData = font.translated.exportData();
self.handler.send('commonobj', [ self.handler.send('commonobj', [
loadedName, loadedName,
'Font', 'Font',
fontData fontData
]); ]);
font.sent = true; font.sent = true;
} }
return loadedName; return loadedName;
});
}, },
handleText: function PartialEvaluator_handleText(chars, state) { handleText: function PartialEvaluator_handleText(chars, state) {
@ -324,7 +352,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList, xref, operatorList, xref,
stateManager) { stateManager) {
var self = this;
// TODO(mack): This should be rewritten so that this function returns // TODO(mack): This should be rewritten so that this function returns
// what should be added to the queue during each iteration // what should be added to the queue during each iteration
function setGStateForKey(gStateObj, key, value) { function setGStateForKey(gStateObj, key, value) {
@ -343,11 +370,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
gStateObj.push([key, value]); gStateObj.push([key, value]);
break; break;
case 'Font': case 'Font':
var loadedName = self.handleSetFont(resources, null, value[0], promise = promise.then(function () {
operatorList, return self.handleSetFont(resources, null, value[0],
stateManager.state); operatorList, stateManager.state).
operatorList.addDependency(loadedName); then(function (loadedName) {
gStateObj.push([key, [loadedName, value[1]]]); operatorList.addDependency(loadedName);
gStateObj.push([key, [loadedName, value[1]]]);
});
});
break; break;
case 'BM': case 'BM':
gStateObj.push([key, value]); gStateObj.push([key, value]);
@ -359,7 +389,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
var dict = xref.fetchIfRef(value); var dict = xref.fetchIfRef(value);
if (isDict(dict)) { if (isDict(dict)) {
self.handleSMask(dict, resources, operatorList, stateManager); promise = promise.then(function () {
return self.handleSMask(dict, resources, operatorList,
stateManager);
});
gStateObj.push([key, true]); gStateObj.push([key, true]);
} else { } else {
warn('Unsupported SMask type'); warn('Unsupported SMask type');
@ -394,12 +427,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// This array holds the converted/processed state data. // This array holds the converted/processed state data.
var gStateObj = []; var gStateObj = [];
var gStateMap = gState.map; var gStateMap = gState.map;
var self = this;
var promise = Promise.resolve();
for (var key in gStateMap) { for (var key in gStateMap) {
var value = gStateMap[key]; var value = gStateMap[key];
setGStateForKey(gStateObj, key, value); setGStateForKey(gStateObj, key, value);
} }
return promise.then(function () {
operatorList.addOp(OPS.setGState, [gStateObj]); operatorList.addOp(OPS.setGState, [gStateObj]);
});
}, },
loadFont: function PartialEvaluator_loadFont(fontName, font, xref, loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
@ -407,10 +443,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
parentOperatorList) { parentOperatorList) {
function errorFont() { function errorFont() {
return { return Promise.resolve({
translated: new ErrorFont('Font ' + fontName + ' is not available'), translated: new ErrorFont('Font ' + fontName + ' is not available'),
loadedName: 'g_font_error' loadedName: 'g_font_error'
}; });
} }
var fontRef; var fontRef;
@ -435,6 +471,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return errorFont(); return errorFont();
} }
var fontCapability = createPromiseCapability();
var preEvaluatedFont = this.preEvaluateFont(font, xref); var preEvaluatedFont = this.preEvaluateFont(font, xref);
var descriptor = preEvaluatedFont.descriptor; var descriptor = preEvaluatedFont.descriptor;
var fontID = fontRef.num + '_' + fontRef.gen; var fontID = fontRef.num + '_' + fontRef.gen;
@ -471,7 +509,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// fontName in font.loadedName below. // fontName in font.loadedName below.
var fontRefIsDict = isDict(fontRef); var fontRefIsDict = isDict(fontRef);
if (!fontRefIsDict) { if (!fontRefIsDict) {
this.fontCache.put(fontRef, font); this.fontCache.put(fontRef, fontCapability.promise);
} }
// Keep track of each font we translated so the caller can // Keep track of each font we translated so the caller can
@ -490,27 +528,38 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
font.translated = translated; font.translated = translated;
} }
var loadCharProcsPromise = Promise.resolve();
if (font.translated.loadCharProcs) { if (font.translated.loadCharProcs) {
var charProcs = font.get('CharProcs').getAll(); var charProcs = font.get('CharProcs').getAll();
var fontResources = (font.get('Resources') || resources); var fontResources = (font.get('Resources') || resources);
var charProcKeys = Object.keys(charProcs); var charProcKeys = Object.keys(charProcs);
var charProcOperatorList = {}; var charProcOperatorList = {};
for (var i = 0, n = charProcKeys.length; i < n; ++i) { for (var i = 0, n = charProcKeys.length; i < n; ++i) {
var key = charProcKeys[i]; loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
var glyphStream = charProcs[key]; var glyphStream = charProcs[key];
var operatorList = this.getOperatorList(glyphStream, fontResources); var operatorList = new OperatorList();
charProcOperatorList[key] = operatorList.getIR(); return this.getOperatorList(glyphStream, fontResources,
if (!parentOperatorList) { operatorList).
continue; then(function () {
} charProcOperatorList[key] = operatorList.getIR();
// Add the dependencies to the parent operator list so they are
// resolved before sub operator list is executed synchronously. // Add the dependencies to the parent operator list so they are
parentOperatorList.addDependencies(charProcOperatorList.dependencies); // resolved before sub operator list is executed synchronously.
if (parentOperatorList) {
parentOperatorList.addDependencies(operatorList.dependencies);
}
});
}.bind(this, charProcKeys[i]));
} }
font.translated.charProcOperatorList = charProcOperatorList; loadCharProcsPromise = loadCharProcsPromise.then(function () {
font.translated.charProcOperatorList = charProcOperatorList;
});
} }
font.loaded = true; return loadCharProcsPromise.then(function () {
return font; font.loaded = true;
fontCapability.resolve(font);
return fontCapability.promise;
}, fontCapability.reject);
}, },
buildPath: function PartialEvaluator_buildPath(operatorList, fn, args) { buildPath: function PartialEvaluator_buildPath(operatorList, fn, args) {
@ -534,182 +583,196 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var xref = this.xref; var xref = this.xref;
var imageCache = {}; var imageCache = {};
operatorList = (operatorList || new OperatorList()); assert(operatorList);
resources = (resources || Dict.empty); resources = (resources || Dict.empty);
var xobjs = (resources.get('XObject') || Dict.empty); var xobjs = (resources.get('XObject') || Dict.empty);
var patterns = (resources.get('Pattern') || Dict.empty); var patterns = (resources.get('Pattern') || Dict.empty);
var stateManager = new StateManager(initialState || new EvalState()); var stateManager = new StateManager(initialState || new EvalState());
var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
var shading;
var timeSlotManager = new TimeSlotManager();
var operation, i, ii; return new Promise(function next(resolve, reject) {
while ((operation = preprocessor.read())) { timeSlotManager.reset();
var args = operation.args; var stop, operation, i, ii;
var fn = operation.fn; while (!(stop = timeSlotManager.check()) &&
var shading; (operation = preprocessor.read())) {
var args = operation.args;
var fn = operation.fn;
switch (fn | 0) { switch (fn | 0) {
case OPS.setStrokeColorN: case OPS.setStrokeColorN:
case OPS.setFillColorN: case OPS.setFillColorN:
if (args[args.length - 1].code) { if (args[args.length - 1].code) {
break;
}
// compile tiling patterns
var patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors
var pattern;
if (isName(patternName) &&
(pattern = patterns.get(patternName.name))) {
var dict = (isStream(pattern) ? pattern.dict : pattern);
var typeNum = dict.get('PatternType');
if (typeNum == TILING_PATTERN) {
return self.handleTilingType(fn, args, resources, pattern,
dict, operatorList).then(
function() {
next(resolve, reject);
}, reject);
} else if (typeNum == SHADING_PATTERN) {
shading = dict.get('Shading');
var matrix = dict.get('Matrix');
pattern = Pattern.parseShading(shading, matrix, xref,
resources);
args = pattern.getIR();
} else {
error('Unknown PatternType ' + typeNum);
}
}
break; break;
} case OPS.paintXObject:
// compile tiling patterns if (args[0].code) {
var patternName = args[args.length - 1]; break;
// SCN/scn applies patterns along with normal colors }
var pattern; // eagerly compile XForm objects
if (isName(patternName) && var name = args[0].name;
(pattern = patterns.get(patternName.name))) { if (imageCache.key === name) {
var dict = (isStream(pattern) ? pattern.dict : pattern); operatorList.addOp(imageCache.fn, imageCache.args);
var typeNum = dict.get('PatternType');
if (typeNum == TILING_PATTERN) {
self.handleTilingType(fn, args, resources, pattern, dict,
operatorList);
args = []; args = [];
continue; continue;
} else if (typeNum == SHADING_PATTERN) {
shading = dict.get('Shading');
var matrix = dict.get('Matrix');
pattern = Pattern.parseShading(shading, matrix, xref,
resources);
args = pattern.getIR();
} else {
error('Unkown PatternType ' + typeNum);
} }
}
break; var xobj = xobjs.get(name);
case OPS.paintXObject: if (xobj) {
if (args[0].code) { assert(isStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assert(isName(type),
'XObject should have a Name subtype');
if ('Form' == type.name) {
stateManager.save();
return self.buildFormXObject(resources, xobj, null,
operatorList,
stateManager.state.clone()).
then(function () {
stateManager.restore();
next(resolve, reject);
}, reject);
} else if ('Image' == type.name) {
self.buildPaintImageXObject(resources, xobj, false,
operatorList, name, imageCache);
args = [];
continue;
} else {
error('Unhandled XObject subtype ' + type.name);
}
}
break; break;
} case OPS.setFont:
// eagerly compile XForm objects var fontSize = args[1];
var name = args[0].name; // eagerly collect all fonts
if (imageCache.key === name) { return self.handleSetFont(resources, args, null,
operatorList.addOp(imageCache.fn, imageCache.args); operatorList, stateManager.state).
then(function (loadedName) {
operatorList.addDependency(loadedName);
operatorList.addOp(OPS.setFont, [loadedName, fontSize]);
next(resolve, reject);
}, reject);
case OPS.endInlineImage:
var cacheKey = args[0].cacheKey;
if (cacheKey && imageCache.key === cacheKey) {
operatorList.addOp(imageCache.fn, imageCache.args);
args = [];
continue;
}
self.buildPaintImageXObject(resources, args[0], true,
operatorList, cacheKey, imageCache);
args = []; args = [];
continue; continue;
} case OPS.showText:
args[0] = self.handleText(args[0], stateManager.state);
var xobj = xobjs.get(name);
if (xobj) {
assert(isStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assert(isName(type),
'XObject should have a Name subtype');
if ('Form' == type.name) {
stateManager.save();
self.buildFormXObject(resources, xobj, null, operatorList,
stateManager.state.clone());
args = [];
stateManager.restore();
continue;
} else if ('Image' == type.name) {
self.buildPaintImageXObject(resources, xobj, false,
operatorList, name, imageCache);
args = [];
continue;
} else {
error('Unhandled XObject subtype ' + type.name);
}
}
break;
case OPS.setFont:
// eagerly collect all fonts
var loadedName = self.handleSetFont(resources, args, null,
operatorList,
stateManager.state);
operatorList.addDependency(loadedName);
args[0] = loadedName;
break;
case OPS.endInlineImage:
var cacheKey = args[0].cacheKey;
if (cacheKey && imageCache.key === cacheKey) {
operatorList.addOp(imageCache.fn, imageCache.args);
args = [];
continue;
}
self.buildPaintImageXObject(resources, args[0], true,
operatorList, cacheKey, imageCache);
args = [];
continue;
case OPS.showText:
args[0] = this.handleText(args[0], stateManager.state);
break;
case OPS.showSpacedText:
var arr = args[0];
var arrLength = arr.length;
for (i = 0; i < arrLength; ++i) {
if (isString(arr[i])) {
arr[i] = this.handleText(arr[i], stateManager.state);
}
}
break;
case OPS.nextLineShowText:
args[0] = this.handleText(args[0], stateManager.state);
break;
case OPS.nextLineSetSpacingShowText:
args[2] = this.handleText(args[2], stateManager.state);
break;
case OPS.setTextRenderingMode:
stateManager.state.textRenderingMode = args[0];
break;
// Parse the ColorSpace data to a raw format.
case OPS.setFillColorSpace:
case OPS.setStrokeColorSpace:
args = [ColorSpace.parseToIR(args[0], xref, resources)];
break;
case OPS.shadingFill:
var shadingRes = resources.get('Shading');
if (!shadingRes) {
error('No shading resource found');
}
shading = shadingRes.get(args[0].name);
if (!shading) {
error('No shading object found');
}
var shadingFill = Pattern.parseShading(shading, null, xref,
resources);
var patternIR = shadingFill.getIR();
args = [patternIR];
fn = OPS.shadingFill;
break;
case OPS.setGState:
var dictName = args[0];
var extGState = resources.get('ExtGState');
if (!isDict(extGState) || !extGState.has(dictName.name)) {
break; break;
} case OPS.showSpacedText:
var arr = args[0];
var arrLength = arr.length;
for (i = 0; i < arrLength; ++i) {
if (isString(arr[i])) {
arr[i] = self.handleText(arr[i], stateManager.state);
}
}
break;
case OPS.nextLineShowText:
args[0] = self.handleText(args[0], stateManager.state);
break;
case OPS.nextLineSetSpacingShowText:
args[2] = self.handleText(args[2], stateManager.state);
break;
case OPS.setTextRenderingMode:
stateManager.state.textRenderingMode = args[0];
break;
// Parse the ColorSpace data to a raw format.
case OPS.setFillColorSpace:
case OPS.setStrokeColorSpace:
args = [ColorSpace.parseToIR(args[0], xref, resources)];
break;
case OPS.shadingFill:
var shadingRes = resources.get('Shading');
if (!shadingRes) {
error('No shading resource found');
}
var gState = extGState.get(dictName.name); shading = shadingRes.get(args[0].name);
self.setGState(resources, gState, operatorList, xref, if (!shading) {
stateManager); error('No shading object found');
args = []; }
continue;
case OPS.moveTo: var shadingFill = Pattern.parseShading(shading, null, xref,
case OPS.lineTo: resources);
case OPS.curveTo: var patternIR = shadingFill.getIR();
case OPS.curveTo2: args = [patternIR];
case OPS.curveTo3: fn = OPS.shadingFill;
case OPS.closePath: break;
self.buildPath(operatorList, fn, args); case OPS.setGState:
continue; var dictName = args[0];
var extGState = resources.get('ExtGState');
if (!isDict(extGState) || !extGState.has(dictName.name)) {
break;
}
var gState = extGState.get(dictName.name);
return self.setGState(resources, gState, operatorList, xref,
stateManager).then(function() {
next(resolve, reject);
}, reject);
case OPS.moveTo:
case OPS.lineTo:
case OPS.curveTo:
case OPS.curveTo2:
case OPS.curveTo3:
case OPS.closePath:
self.buildPath(operatorList, fn, args);
continue;
}
operatorList.addOp(fn, args);
} }
operatorList.addOp(fn, args); if (stop) {
} deferred.then(function () {
next(resolve, reject);
// Some PDFs don't close all restores inside object/form. });
// Closing those for them. return;
for (i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { }
operatorList.addOp(OPS.restore, []); // Some PDFs don't close all restores inside object/form.
} // Closing those for them.
for (i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {
return operatorList; operatorList.addOp(OPS.restore, []);
}
resolve();
});
}, },
getTextContent: function PartialEvaluator_getTextContent(stream, resources, getTextContent: function PartialEvaluator_getTextContent(stream, resources,
@ -767,10 +830,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
function handleSetFont(fontName, fontRef) { function handleSetFont(fontName, fontRef) {
var font = textState.font = self.loadFont(fontName, fontRef, xref, return self.loadFont(fontName, fontRef, xref, resources, null).
resources, null).translated; then(function (font) {
textState.fontMatrix = font.fontMatrix ? font.fontMatrix : var fontTranslated = textState.font = font.translated;
FONT_IDENTITY_MATRIX; textState.fontMatrix = fontTranslated.fontMatrix ?
fontTranslated.fontMatrix :
FONT_IDENTITY_MATRIX;
});
} }
function buildTextGeometry(chars, textChunk) { function buildTextGeometry(chars, textChunk) {
@ -865,171 +931,192 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return textChunk; return textChunk;
} }
while ((operation = preprocessor.read())) { var timeSlotManager = new TimeSlotManager();
textState = stateManager.state;
var fn = operation.fn; return new Promise(function next(resolve, reject) {
var args = operation.args; timeSlotManager.reset();
switch (fn | 0) { var stop;
case OPS.setFont: while (!(stop = timeSlotManager.check()) &&
handleSetFont(args[0].name); (operation = preprocessor.read())) {
textState.fontSize = args[1]; textState = stateManager.state;
break; var fn = operation.fn;
case OPS.setTextRise: var args = operation.args;
textState.textRise = args[0]; switch (fn | 0) {
break; case OPS.setFont:
case OPS.setHScale: textState.fontSize = args[1];
textState.textHScale = args[0] / 100; return handleSetFont(args[0].name).then(function() {
break; next(resolve, reject);
case OPS.setLeading: }, reject);
textState.leading = args[0]; case OPS.setTextRise:
break; textState.textRise = args[0];
case OPS.moveText: break;
textState.translateTextLineMatrix(args[0], args[1]); case OPS.setHScale:
textState.textMatrix = textState.textLineMatrix.slice(); textState.textHScale = args[0] / 100;
break; break;
case OPS.setLeadingMoveText: case OPS.setLeading:
textState.leading = -args[1]; textState.leading = args[0];
textState.translateTextLineMatrix(args[0], args[1]); break;
textState.textMatrix = textState.textLineMatrix.slice(); case OPS.moveText:
break; textState.translateTextLineMatrix(args[0], args[1]);
case OPS.nextLine: textState.textMatrix = textState.textLineMatrix.slice();
textState.carriageReturn(); break;
break; case OPS.setLeadingMoveText:
case OPS.setTextMatrix: textState.leading = -args[1];
textState.setTextMatrix(args[0], args[1], args[2], args[3], textState.translateTextLineMatrix(args[0], args[1]);
args[4], args[5]); textState.textMatrix = textState.textLineMatrix.slice();
textState.setTextLineMatrix(args[0], args[1], args[2], args[3], break;
args[4], args[5]); case OPS.nextLine:
break; textState.carriageReturn();
case OPS.setCharSpacing: break;
textState.charSpacing = args[0]; case OPS.setTextMatrix:
break; textState.setTextMatrix(args[0], args[1], args[2], args[3],
case OPS.setWordSpacing: args[4], args[5]);
textState.wordSpacing = args[0]; textState.setTextLineMatrix(args[0], args[1], args[2], args[3],
break; args[4], args[5]);
case OPS.beginText: break;
textState.textMatrix = IDENTITY_MATRIX.slice(); case OPS.setCharSpacing:
textState.textLineMatrix = IDENTITY_MATRIX.slice(); textState.charSpacing = args[0];
break; break;
case OPS.showSpacedText: case OPS.setWordSpacing:
var items = args[0]; textState.wordSpacing = args[0];
var textChunk = newTextChunk(); break;
var offset; case OPS.beginText:
for (var j = 0, jj = items.length; j < jj; j++) { textState.textMatrix = IDENTITY_MATRIX.slice();
if (typeof items[j] === 'string') { textState.textLineMatrix = IDENTITY_MATRIX.slice();
buildTextGeometry(items[j], textChunk); break;
} else { case OPS.showSpacedText:
var val = items[j] / 1000; var items = args[0];
if (!textState.font.vertical) { var textChunk = newTextChunk();
offset = -val * textState.fontSize * textState.textHScale * var offset;
textState.textMatrix[0]; for (var j = 0, jj = items.length; j < jj; j++) {
textState.translateTextMatrix(offset, 0); if (typeof items[j] === 'string') {
textChunk.width += offset; buildTextGeometry(items[j], textChunk);
} else { } else {
offset = -val * textState.fontSize * textState.textMatrix[3]; var val = items[j] / 1000;
textState.translateTextMatrix(0, offset); if (!textState.font.vertical) {
textChunk.height += offset; offset = -val * textState.fontSize * textState.textHScale *
} textState.textMatrix[0];
if (items[j] < 0 && textState.font.spaceWidth > 0) { textState.translateTextMatrix(offset, 0);
var fakeSpaces = -items[j] / textState.font.spaceWidth; textChunk.width += offset;
if (fakeSpaces > MULTI_SPACE_FACTOR) { } else {
fakeSpaces = Math.round(fakeSpaces); offset = -val * textState.fontSize *
while (fakeSpaces--) { textState.textMatrix[3];
textState.translateTextMatrix(0, offset);
textChunk.height += offset;
}
if (items[j] < 0 && textState.font.spaceWidth > 0) {
var fakeSpaces = -items[j] / textState.font.spaceWidth;
if (fakeSpaces > MULTI_SPACE_FACTOR) {
fakeSpaces = Math.round(fakeSpaces);
while (fakeSpaces--) {
textChunk.str += ' ';
}
} else if (fakeSpaces > SPACE_FACTOR) {
textChunk.str += ' '; textChunk.str += ' ';
} }
} else if (fakeSpaces > SPACE_FACTOR) {
textChunk.str += ' ';
} }
} }
} }
} bidiTexts.push(runBidi(textChunk));
bidiTexts.push(runBidi(textChunk));
break;
case OPS.showText:
bidiTexts.push(runBidi(buildTextGeometry(args[0])));
break;
case OPS.nextLineShowText:
textState.carriageReturn();
bidiTexts.push(runBidi(buildTextGeometry(args[0])));
break;
case OPS.nextLineSetSpacingShowText:
textState.wordSpacing = args[0];
textState.charSpacing = args[1];
textState.carriageReturn();
bidiTexts.push(runBidi(buildTextGeometry(args[2])));
break;
case OPS.paintXObject:
if (args[0].code) {
break; break;
} case OPS.showText:
bidiTexts.push(runBidi(buildTextGeometry(args[0])));
break;
case OPS.nextLineShowText:
textState.carriageReturn();
bidiTexts.push(runBidi(buildTextGeometry(args[0])));
break;
case OPS.nextLineSetSpacingShowText:
textState.wordSpacing = args[0];
textState.charSpacing = args[1];
textState.carriageReturn();
bidiTexts.push(runBidi(buildTextGeometry(args[2])));
break;
case OPS.paintXObject:
if (args[0].code) {
break;
}
if (!xobjs) { if (!xobjs) {
xobjs = (resources.get('XObject') || Dict.empty); xobjs = (resources.get('XObject') || Dict.empty);
} }
var name = args[0].name; var name = args[0].name;
if (xobjsCache.key === name) { if (xobjsCache.key === name) {
if (xobjsCache.texts) { if (xobjsCache.texts) {
Util.concatenateToArray(bidiTexts, xobjsCache.texts.items); Util.concatenateToArray(bidiTexts, xobjsCache.texts.items);
Util.extendObj(textContent.styles, xobjsCache.texts.styles); Util.extendObj(textContent.styles, xobjsCache.texts.styles);
}
break;
}
var xobj = xobjs.get(name);
if (!xobj) {
break;
}
assert(isStream(xobj), 'XObject should be a stream');
var type = xobj.dict.get('Subtype');
assert(isName(type),
'XObject should have a Name subtype');
if ('Form' !== type.name) {
xobjsCache.key = name;
xobjsCache.texts = null;
break;
}
stateManager.save();
var matrix = xobj.dict.get('Matrix');
if (isArray(matrix) && matrix.length === 6) {
stateManager.transform(matrix);
}
return self.getTextContent(xobj,
xobj.dict.get('Resources') || resources, stateManager).
then(function (formTextContent) {
Util.concatenateToArray(bidiTexts, formTextContent.items);
Util.extendObj(textContent.styles, formTextContent.styles);
stateManager.restore();
xobjsCache.key = name;
xobjsCache.texts = formTextContent;
next(resolve, reject);
}, reject);
case OPS.setGState:
var dictName = args[0];
var extGState = resources.get('ExtGState');
if (!isDict(extGState) || !extGState.has(dictName.name)) {
break;
}
var gsStateMap = extGState.get(dictName.name);
var gsStateFont = null;
for (var key in gsStateMap) {
if (key === 'Font') {
assert(!gsStateFont);
gsStateFont = gsStateMap[key];
}
}
if (gsStateFont) {
textState.fontSize = gsStateFont[1];
return handleSetFont(gsStateFont[0]).then(function() {
next(resolve, reject);
}, reject);
} }
break; break;
} } // switch
} // while
var xobj = xobjs.get(name); if (stop) {
if (!xobj) { deferred.then(function () {
break; next(resolve, reject);
} });
assert(isStream(xobj), 'XObject should be a stream'); return;
}
var type = xobj.dict.get('Subtype'); resolve(textContent);
assert(isName(type), });
'XObject should have a Name subtype');
if ('Form' !== type.name) {
xobjsCache.key = name;
xobjsCache.texts = null;
break;
}
stateManager.save();
var matrix = xobj.dict.get('Matrix');
if (isArray(matrix) && matrix.length === 6) {
stateManager.transform(matrix);
}
var formTextContent = this.getTextContent(
xobj,
xobj.dict.get('Resources') || resources,
stateManager
);
Util.concatenateToArray(bidiTexts, formTextContent.items);
Util.extendObj(textContent.styles, formTextContent.styles);
stateManager.restore();
xobjsCache.key = name;
xobjsCache.texts = formTextContent;
break;
case OPS.setGState:
var dictName = args[0];
var extGState = resources.get('ExtGState');
if (!isDict(extGState) || !extGState.has(dictName.name)) {
break;
}
var gsState = extGState.get(dictName.name);
for (var i = 0; i < gsState.length; i++) {
if (gsState[i] === 'Font') {
handleSetFont(args[0].name);
}
}
break;
} // switch
} // while
return textContent;
}, },
extractDataStructures: function extractDataStructures: function

View File

@ -507,11 +507,17 @@ var Catalog = (function CatalogClosure() {
}, },
cleanup: function Catalog_cleanup() { cleanup: function Catalog_cleanup() {
this.fontCache.forEach(function (font) { var promises = [];
delete font.sent; this.fontCache.forEach(function (promise) {
delete font.translated; promises.push(promise);
}); });
this.fontCache.clear(); return Promise.all(promises).then(function (fonts) {
for (var i = 0, ii = fonts.length; i < ii; i++) {
delete fonts[i].sent;
delete fonts[i].translated;
}
this.fontCache.clear();
}.bind(this));
}, },
getPage: function Catalog_getPage(pageIndex) { getPage: function Catalog_getPage(pageIndex) {

View File

@ -387,8 +387,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}); });
handler.on('Cleanup', function wphCleanup(data) { handler.on('Cleanup', function wphCleanup(data) {
pdfManager.cleanup(); return pdfManager.cleanup();
return true;
}); });
handler.on('Terminate', function wphTerminate(data) { handler.on('Terminate', function wphTerminate(data) {

View File

@ -219,11 +219,9 @@ var Annotation = (function AnnotationClosure() {
}, },
getOperatorList: function Annotation_getOperatorList(evaluator) { getOperatorList: function Annotation_getOperatorList(evaluator) {
var capability = createPromiseCapability();
if (!this.appearance) { if (!this.appearance) {
capability.resolve(new OperatorList()); return Promise.resolve(new OperatorList());
return capability.promise;
} }
var data = this.data; var data = this.data;
@ -242,18 +240,18 @@ var Annotation = (function AnnotationClosure() {
var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1]; var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
var transform = getTransformMatrix(data.rect, bbox, matrix); var transform = getTransformMatrix(data.rect, bbox, matrix);
var self = this;
resourcesPromise.then(function(resources) { return resourcesPromise.then(function(resources) {
var opList = new OperatorList(); var opList = new OperatorList();
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
evaluator.getOperatorList(this.appearance, resources, opList); return evaluator.getOperatorList(self.appearance, resources, opList).
opList.addOp(OPS.endAnnotation, []); then(function () {
capability.resolve(opList); opList.addOp(OPS.endAnnotation, []);
self.appearance.reset();
this.appearance.reset(); return opList;
}.bind(this), capability.reject); });
});
return capability.promise;
} }
}; };
@ -512,8 +510,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
} }
var stream = new Stream(stringToBytes(data.defaultAppearance)); var stream = new Stream(stringToBytes(data.defaultAppearance));
evaluator.getOperatorList(stream, this.fieldResources, opList); return evaluator.getOperatorList(stream, this.fieldResources, opList).
return Promise.resolve(opList); then(function () {
return opList;
});
} }
}); });

View File

@ -1,6 +1,7 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* globals expect, it, describe, PartialEvaluator, StringStream, OPS */ /* globals expect, it, describe, PartialEvaluator, StringStream, OPS,
OperatorList, waitsFor, runs */
'use strict'; 'use strict';
@ -30,17 +31,34 @@ describe('evaluator', function() {
function PdfManagerMock() { } function PdfManagerMock() { }
function runOperatorListCheck(evaluator, stream, resources, check) {
var done = false;
runs(function () {
var result = new OperatorList();
evaluator.getOperatorList(stream, resources, result).then(function () {
check(result);
done = true;
});
});
waitsFor(function () {
return done;
});
}
describe('splitCombinedOperations', function() { describe('splitCombinedOperations', function() {
it('should reject unknown operations', function() { it('should reject unknown operations', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('fTT'); var stream = new StringStream('fTT');
var result = evaluator.getOperatorList(stream, new ResourcesMock());
expect(!!result.fnArray && !!result.argsArray).toEqual(true); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(result.fnArray.length).toEqual(1); function(result) {
expect(result.fnArray[0]).toEqual(OPS.fill); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.argsArray[0].length).toEqual(0); expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.argsArray[0].length).toEqual(0);
});
}); });
it('should handle one operations', function() { it('should handle one operations', function() {
@ -48,10 +66,12 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('Q'); var stream = new StringStream('Q');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(!!result.fnArray && !!result.argsArray).toEqual(true); function(result) {
expect(result.fnArray.length).toEqual(1); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray[0]).toEqual(OPS.restore); expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.restore);
});
}); });
it('should handle two glued operations', function() { it('should handle two glued operations', function() {
@ -61,11 +81,12 @@ describe('evaluator', function() {
var resources = new ResourcesMock(); var resources = new ResourcesMock();
resources.Res1 = {}; resources.Res1 = {};
var stream = new StringStream('/Res1 DoQ'); var stream = new StringStream('/Res1 DoQ');
var result = evaluator.getOperatorList(stream, resources); runOperatorListCheck(evaluator, stream, resources, function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(2); expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[0]).toEqual(OPS.paintXObject); expect(result.fnArray[0]).toEqual(OPS.paintXObject);
expect(result.fnArray[1]).toEqual(OPS.restore); expect(result.fnArray[1]).toEqual(OPS.restore);
});
}); });
it('should handle tree glued operations', function() { it('should handle tree glued operations', function() {
@ -73,12 +94,14 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('fff'); var stream = new StringStream('fff');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(!!result.fnArray && !!result.argsArray).toEqual(true); function (result) {
expect(result.fnArray.length).toEqual(3); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray[0]).toEqual(OPS.fill); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[1]).toEqual(OPS.fill); expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[2]).toEqual(OPS.fill); expect(result.fnArray[1]).toEqual(OPS.fill);
expect(result.fnArray[2]).toEqual(OPS.fill);
});
}); });
it('should handle three glued operations #2', function() { it('should handle three glued operations #2', function() {
@ -88,12 +111,13 @@ describe('evaluator', function() {
var resources = new ResourcesMock(); var resources = new ResourcesMock();
resources.Res1 = {}; resources.Res1 = {};
var stream = new StringStream('B*Bf*'); var stream = new StringStream('B*Bf*');
var result = evaluator.getOperatorList(stream, resources); runOperatorListCheck(evaluator, stream, resources, function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.eoFillStroke); expect(result.fnArray[0]).toEqual(OPS.eoFillStroke);
expect(result.fnArray[1]).toEqual(OPS.fillStroke); expect(result.fnArray[1]).toEqual(OPS.fillStroke);
expect(result.fnArray[2]).toEqual(OPS.eoFill); expect(result.fnArray[2]).toEqual(OPS.eoFill);
});
}); });
it('should handle glued operations and operands', function() { it('should handle glued operations and operands', function() {
@ -101,14 +125,16 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('f5 Ts'); var stream = new StringStream('f5 Ts');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(!!result.fnArray && !!result.argsArray).toEqual(true); function (result) {
expect(result.fnArray.length).toEqual(2); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray[0]).toEqual(OPS.fill); expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[1]).toEqual(OPS.setTextRise); expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.argsArray.length).toEqual(2); expect(result.fnArray[1]).toEqual(OPS.setTextRise);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray.length).toEqual(2);
expect(result.argsArray[1][0]).toEqual(5); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(5);
});
}); });
it('should handle glued operations and literals', function() { it('should handle glued operations and literals', function() {
@ -116,18 +142,20 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('trueifalserinulln'); var stream = new StringStream('trueifalserinulln');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(!!result.fnArray && !!result.argsArray).toEqual(true); function (result) {
expect(result.fnArray.length).toEqual(3); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray[0]).toEqual(OPS.setFlatness); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent); expect(result.fnArray[0]).toEqual(OPS.setFlatness);
expect(result.fnArray[2]).toEqual(OPS.endPath); expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
expect(result.argsArray.length).toEqual(3); expect(result.fnArray[2]).toEqual(OPS.endPath);
expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0][0]).toEqual(true); expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[0][0]).toEqual(true);
expect(result.argsArray[1][0]).toEqual(false); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[2].length).toEqual(0); expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[2].length).toEqual(0);
});
}); });
}); });
@ -138,57 +166,67 @@ describe('evaluator', function() {
'prefix'); 'prefix');
var stream = new StringStream('5 1 d0'); var stream = new StringStream('5 1 d0');
console.log('here!'); console.log('here!');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(result.argsArray[0][0]).toEqual(5); function (result) {
expect(result.argsArray[0][1]).toEqual(1); expect(result.argsArray[0][0]).toEqual(5);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth); expect(result.argsArray[0][1]).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
});
}); });
it('should execute if too many arguments', function() { it('should execute if too many arguments', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('5 1 4 d0'); var stream = new StringStream('5 1 4 d0');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(result.argsArray[0][0]).toEqual(1); function (result) {
expect(result.argsArray[0][1]).toEqual(4); expect(result.argsArray[0][0]).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth); expect(result.argsArray[0][1]).toEqual(4);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
});
}); });
it('should execute if nested commands', function() { it('should execute if nested commands', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('/F2 /GS2 gs 5.711 Tf'); var stream = new StringStream('/F2 /GS2 gs 5.711 Tf');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(result.fnArray.length).toEqual(3); function (result) {
expect(result.fnArray[0]).toEqual(OPS.setGState); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[1]).toEqual(OPS.dependency); expect(result.fnArray[0]).toEqual(OPS.setGState);
expect(result.fnArray[2]).toEqual(OPS.setFont); expect(result.fnArray[1]).toEqual(OPS.dependency);
expect(result.argsArray.length).toEqual(3); expect(result.fnArray[2]).toEqual(OPS.setFont);
expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[2].length).toEqual(2); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[2].length).toEqual(2);
});
}); });
it('should skip if too few arguments', function() { it('should skip if too few arguments', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('5 d0'); var stream = new StringStream('5 d0');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(result.argsArray).toEqual([]); function (result) {
expect(result.fnArray).toEqual([]); expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
});
}); });
it('should close opened saves', function() { it('should close opened saves', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('qq'); var stream = new StringStream('qq');
var result = evaluator.getOperatorList(stream, new ResourcesMock()); runOperatorListCheck(evaluator, stream, new ResourcesMock(),
expect(!!result.fnArray && !!result.argsArray).toEqual(true); function (result) {
expect(result.fnArray.length).toEqual(4); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray[0]).toEqual(OPS.save); expect(result.fnArray.length).toEqual(4);
expect(result.fnArray[1]).toEqual(OPS.save); expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.fnArray[2]).toEqual(OPS.restore); expect(result.fnArray[1]).toEqual(OPS.save);
expect(result.fnArray[3]).toEqual(OPS.restore); expect(result.fnArray[2]).toEqual(OPS.restore);
expect(result.fnArray[3]).toEqual(OPS.restore);
});
}); });
}); });
}); });