Merge pull request #3913 from brendandahl/int-ops

Reduce the memory usage of the operator list.
This commit is contained in:
Yury Delendik 2013-11-13 13:14:59 -08:00
commit 516d2e79be
8 changed files with 288 additions and 155 deletions

View File

@ -184,7 +184,6 @@ var Page = (function PageClosure() {
var annotations = datas[1]; var annotations = datas[1];
if (annotations.length === 0) { if (annotations.length === 0) {
PartialEvaluator.optimizeQueue(pageOpList);
pageOpList.flush(true); pageOpList.flush(true);
promise.resolve(pageOpList); promise.resolve(pageOpList);
return; return;
@ -193,7 +192,6 @@ var Page = (function PageClosure() {
var annotationsReadyPromise = Annotation.appendToOperatorList( var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageOpList, pdfManager, partialEvaluator); annotations, pageOpList, pdfManager, partialEvaluator);
annotationsReadyPromise.then(function () { annotationsReadyPromise.then(function () {
PartialEvaluator.optimizeQueue(pageOpList);
pageOpList.flush(true); pageOpList.flush(true);
promise.resolve(pageOpList); promise.resolve(pageOpList);
}, reject); }, reject);

View File

@ -20,7 +20,7 @@
isStream, isString, JpegStream, Lexer, Metrics, Name, Parser, isStream, isString, JpegStream, Lexer, Metrics, Name, Parser,
Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts, Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts,
TilingPattern, TODO, warn, Util, Promise, TilingPattern, TODO, warn, Util, Promise,
RefSetCache, isRef, TextRenderingMode, CMapFactory */ RefSetCache, isRef, TextRenderingMode, CMapFactory, OPS */
'use strict'; 'use strict';
@ -45,98 +45,99 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// If variableArgs === false: exactly `numArgs` expected // If variableArgs === false: exactly `numArgs` expected
var OP_MAP = { var OP_MAP = {
// Graphic state // Graphic state
w: { fnName: 'setLineWidth', numArgs: 1, variableArgs: false }, w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false },
J: { fnName: 'setLineCap', numArgs: 1, variableArgs: false }, J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false },
j: { fnName: 'setLineJoin', numArgs: 1, variableArgs: false }, j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false },
M: { fnName: 'setMiterLimit', numArgs: 1, variableArgs: false }, M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false },
d: { fnName: 'setDash', numArgs: 2, variableArgs: false }, d: { id: OPS.setDash, numArgs: 2, variableArgs: false },
ri: { fnName: 'setRenderingIntent', numArgs: 1, variableArgs: false }, ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false },
i: { fnName: 'setFlatness', numArgs: 1, variableArgs: false }, i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false },
gs: { fnName: 'setGState', numArgs: 1, variableArgs: false }, gs: { id: OPS.setGState, numArgs: 1, variableArgs: false },
q: { fnName: 'save', numArgs: 0, variableArgs: false }, q: { id: OPS.save, numArgs: 0, variableArgs: false },
Q: { fnName: 'restore', numArgs: 0, variableArgs: false }, Q: { id: OPS.restore, numArgs: 0, variableArgs: false },
cm: { fnName: 'transform', numArgs: 6, variableArgs: false }, cm: { id: OPS.transform, numArgs: 6, variableArgs: false },
// Path // Path
m: { fnName: 'moveTo', numArgs: 2, variableArgs: false }, m: { id: OPS.moveTo, numArgs: 2, variableArgs: false },
l: { fnName: 'lineTo', numArgs: 2, variableArgs: false }, l: { id: OPS.lineTo, numArgs: 2, variableArgs: false },
c: { fnName: 'curveTo', numArgs: 6, variableArgs: false }, c: { id: OPS.curveTo, numArgs: 6, variableArgs: false },
v: { fnName: 'curveTo2', numArgs: 4, variableArgs: false }, v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false },
y: { fnName: 'curveTo3', numArgs: 4, variableArgs: false }, y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false },
h: { fnName: 'closePath', numArgs: 0, variableArgs: false }, h: { id: OPS.closePath, numArgs: 0, variableArgs: false },
re: { fnName: 'rectangle', numArgs: 4, variableArgs: false }, re: { id: OPS.rectangle, numArgs: 4, variableArgs: false },
S: { fnName: 'stroke', numArgs: 0, variableArgs: false }, S: { id: OPS.stroke, numArgs: 0, variableArgs: false },
s: { fnName: 'closeStroke', numArgs: 0, variableArgs: false }, s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false },
f: { fnName: 'fill', numArgs: 0, variableArgs: false }, f: { id: OPS.fill, numArgs: 0, variableArgs: false },
F: { fnName: 'fill', numArgs: 0, variableArgs: false }, F: { id: OPS.fill, numArgs: 0, variableArgs: false },
'f*': { fnName: 'eoFill', numArgs: 0, variableArgs: false }, 'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false },
B: { fnName: 'fillStroke', numArgs: 0, variableArgs: false }, B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false },
'B*': { fnName: 'eoFillStroke', numArgs: 0, variableArgs: false }, 'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false },
b: { fnName: 'closeFillStroke', numArgs: 0, variableArgs: false }, b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false },
'b*': { fnName: 'closeEOFillStroke', numArgs: 0, variableArgs: false }, 'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false },
n: { fnName: 'endPath', numArgs: 0, variableArgs: false }, n: { id: OPS.endPath, numArgs: 0, variableArgs: false },
// Clipping // Clipping
W: { fnName: 'clip', numArgs: 0, variableArgs: false }, W: { id: OPS.clip, numArgs: 0, variableArgs: false },
'W*': { fnName: 'eoClip', numArgs: 0, variableArgs: false }, 'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false },
// Text // Text
BT: { fnName: 'beginText', numArgs: 0, variableArgs: false }, BT: { id: OPS.beginText, numArgs: 0, variableArgs: false },
ET: { fnName: 'endText', numArgs: 0, variableArgs: false }, ET: { id: OPS.endText, numArgs: 0, variableArgs: false },
Tc: { fnName: 'setCharSpacing', numArgs: 1, variableArgs: false }, Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false },
Tw: { fnName: 'setWordSpacing', numArgs: 1, variableArgs: false }, Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false },
Tz: { fnName: 'setHScale', numArgs: 1, variableArgs: false }, Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false },
TL: { fnName: 'setLeading', numArgs: 1, variableArgs: false }, TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false },
Tf: { fnName: 'setFont', numArgs: 2, variableArgs: false }, Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false },
Tr: { fnName: 'setTextRenderingMode', numArgs: 1, variableArgs: false }, Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false },
Ts: { fnName: 'setTextRise', numArgs: 1, variableArgs: false }, Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false },
Td: { fnName: 'moveText', numArgs: 2, variableArgs: false }, Td: { id: OPS.moveText, numArgs: 2, variableArgs: false },
TD: { fnName: 'setLeadingMoveText', numArgs: 2, variableArgs: false }, TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false },
Tm: { fnName: 'setTextMatrix', numArgs: 6, variableArgs: false }, Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false },
'T*': { fnName: 'nextLine', numArgs: 0, variableArgs: false }, 'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false },
Tj: { fnName: 'showText', numArgs: 1, variableArgs: false }, Tj: { id: OPS.showText, numArgs: 1, variableArgs: false },
TJ: { fnName: 'showSpacedText', numArgs: 1, variableArgs: false }, TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false },
'\'': { fnName: 'nextLineShowText', numArgs: 1, variableArgs: false }, '\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false },
'"': { fnName: 'nextLineSetSpacingShowText', numArgs: 3, '"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3,
variableArgs: false }, variableArgs: false },
// Type3 fonts // Type3 fonts
d0: { fnName: 'setCharWidth', numArgs: 2, variableArgs: false }, d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false },
d1: { fnName: 'setCharWidthAndBounds', numArgs: 6, variableArgs: false }, d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false },
// Color // Color
CS: { fnName: 'setStrokeColorSpace', numArgs: 1, variableArgs: false }, CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false },
cs: { fnName: 'setFillColorSpace', numArgs: 1, variableArgs: false }, cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false },
SC: { fnName: 'setStrokeColor', numArgs: 4, variableArgs: true }, SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true },
SCN: { fnName: 'setStrokeColorN', numArgs: 33, variableArgs: true }, SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true },
sc: { fnName: 'setFillColor', numArgs: 4, variableArgs: true }, sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true },
scn: { fnName: 'setFillColorN', numArgs: 33, variableArgs: true }, scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true },
G: { fnName: 'setStrokeGray', numArgs: 1, variableArgs: false }, G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false },
g: { fnName: 'setFillGray', numArgs: 1, variableArgs: false }, g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false },
RG: { fnName: 'setStrokeRGBColor', numArgs: 3, variableArgs: false }, RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false },
rg: { fnName: 'setFillRGBColor', numArgs: 3, variableArgs: false }, rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false },
K: { fnName: 'setStrokeCMYKColor', numArgs: 4, variableArgs: false }, K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false },
k: { fnName: 'setFillCMYKColor', numArgs: 4, variableArgs: false }, k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false },
// Shading // Shading
sh: { fnName: 'shadingFill', numArgs: 1, variableArgs: false }, sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false },
// Images // Images
BI: { fnName: 'beginInlineImage', numArgs: 0, variableArgs: false }, BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false },
ID: { fnName: 'beginImageData', numArgs: 0, variableArgs: false }, ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false },
EI: { fnName: 'endInlineImage', numArgs: 1, variableArgs: false }, EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false },
// XObjects // XObjects
Do: { fnName: 'paintXObject', numArgs: 1, variableArgs: false }, Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false },
MP: { fnName: 'markPoint', numArgs: 1, variableArgs: false }, MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false },
DP: { fnName: 'markPointProps', numArgs: 2, variableArgs: false }, DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false },
BMC: { fnName: 'beginMarkedContent', numArgs: 1, variableArgs: false }, BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false },
BDC: { fnName: 'beginMarkedContentProps', numArgs: 2, variableArgs: false }, BDC: { id: OPS.beginMarkedContentProps, numArgs: 2,
EMC: { fnName: 'endMarkedContent', numArgs: 0, variableArgs: false }, variableArgs: false },
EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false },
// Compatibility // Compatibility
BX: { fnName: 'beginCompat', numArgs: 0, variableArgs: false }, BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false },
EX: { fnName: 'endCompat', numArgs: 0, variableArgs: false }, EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false },
// (reserved partial commands for the lexer) // (reserved partial commands for the lexer)
BM: null, BM: null,
@ -218,17 +219,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// There is also a group colorspace, but since we put everything in // There is also a group colorspace, but since we put everything in
// RGB I'm not sure we need it. // RGB I'm not sure we need it.
} }
operatorList.addOp('beginGroup', [groupOptions]); operatorList.addOp(OPS.beginGroup, [groupOptions]);
} }
operatorList.addOp('paintFormXObjectBegin', [matrix, bbox]); operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
this.getOperatorList(xobj, xobj.dict.get('Resources') || resources, this.getOperatorList(xobj, xobj.dict.get('Resources') || resources,
operatorList); operatorList);
operatorList.addOp('paintFormXObjectEnd', []); operatorList.addOp(OPS.paintFormXObjectEnd, []);
if (group) { if (group) {
operatorList.addOp('endGroup', [groupOptions]); operatorList.addOp(OPS.endGroup, [groupOptions]);
} }
}, },
@ -259,7 +260,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var decode = dict.get('Decode', 'D'); var decode = dict.get('Decode', 'D');
var inverseDecode = !!decode && decode[0] > 0; var inverseDecode = !!decode && decode[0] > 0;
operatorList.addOp('paintImageMaskXObject', operatorList.addOp(OPS.paintImageMaskXObject,
[PDFImage.createMask(imgArray, width, height, [PDFImage.createMask(imgArray, width, height,
inverseDecode)] inverseDecode)]
); );
@ -277,7 +278,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var imageObj = new PDFImage(this.xref, resources, image, var imageObj = new PDFImage(this.xref, resources, image,
inline, null, null); inline, null, null);
var imgData = imageObj.getImageData(); var imgData = imageObj.getImageData();
operatorList.addOp('paintInlineImageXObject', [imgData]); operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
return; return;
} }
@ -291,7 +292,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!softMask && !mask && image instanceof JpegStream && if (!softMask && !mask && image instanceof JpegStream &&
image.isNativelySupported(this.xref, resources)) { image.isNativelySupported(this.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.
operatorList.addOp('paintJpegXObject', args); operatorList.addOp(OPS.paintJpegXObject, args);
this.handler.send( this.handler.send(
'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]); 'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]);
return; return;
@ -303,7 +304,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]); self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]);
}, self.handler, self.xref, resources, image, inline); }, self.handler, self.xref, resources, image, inline);
operatorList.addOp('paintImageXObject', args); operatorList.addOp(OPS.paintImageXObject, args);
}, },
handleTilingType: function PartialEvaluator_handleTilingType( handleTilingType: function PartialEvaluator_handleTilingType(
@ -442,7 +443,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
setGStateForKey(gStateObj, key, value); setGStateForKey(gStateObj, key, value);
} }
operatorList.addOp('setGState', [gStateObj]); operatorList.addOp(OPS.setGState, [gStateObj]);
}, },
loadFont: function PartialEvaluator_loadFont(fontName, font, xref, loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
@ -557,7 +558,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
continue; continue;
} }
var fn = opSpec.fnName; var fn = opSpec.id;
// Validate the number of arguments for the command // Validate the number of arguments for the command
if (opSpec.variableArgs) { if (opSpec.variableArgs) {
@ -640,7 +641,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var loadedName = self.handleSetFont(resources, args, null, var loadedName = self.handleSetFont(resources, args, null,
operatorList); operatorList);
operatorList.addDependency(loadedName); operatorList.addDependency(loadedName);
fn = 'setFont'; fn = OPS.setFont;
args[0] = loadedName; args[0] = loadedName;
} else if (cmd == 'EI') { } else if (cmd == 'EI') {
self.buildPaintImageXObject(resources, args[0], true, operatorList); self.buildPaintImageXObject(resources, args[0], true, operatorList);
@ -675,11 +676,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
switch (fn) { switch (fn) {
// Parse the ColorSpace data to a raw format. // Parse the ColorSpace data to a raw format.
case 'setFillColorSpace': case OPS.setFillColorSpace:
case 'setStrokeColorSpace': case OPS.setStrokeColorSpace:
args = [ColorSpace.parseToIR(args[0], xref, resources)]; args = [ColorSpace.parseToIR(args[0], xref, resources)];
break; break;
case 'shadingFill': case OPS.shadingFill:
var shadingRes = resources.get('Shading'); var shadingRes = resources.get('Shading');
if (!shadingRes) if (!shadingRes)
error('No shading resource found'); error('No shading resource found');
@ -692,9 +693,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
shading, null, xref, resources); shading, null, xref, resources);
var patternIR = shadingFill.getIR(); var patternIR = shadingFill.getIR();
args = [patternIR]; args = [patternIR];
fn = 'shadingFill'; fn = OPS.shadingFill;
break; break;
case 'setGState': case OPS.setGState:
var dictName = args[0]; var dictName = args[0];
var extGState = resources.get('ExtGState'); var extGState = resources.get('ExtGState');
@ -1330,6 +1331,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
PartialEvaluator.optimizeQueue = PartialEvaluator.optimizeQueue =
function PartialEvaluator_optimizeQueue(queue) { function PartialEvaluator_optimizeQueue(queue) {
function squash(array, index, howMany, element) {
if (isArray(array)) {
array.splice(index, howMany, element);
} else {
// Replace the element.
array[index] = element;
// Shift everything after the element up.
var sub = array.subarray(index + howMany);
array.set(sub, index + 1);
}
}
var fnArray = queue.fnArray, argsArray = queue.argsArray; var fnArray = queue.fnArray, argsArray = queue.argsArray;
// grouping paintInlineImageXObject's into paintInlineImageXObjectGroup // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup
// searching for (save, transform, paintInlineImageXObject, restore)+ // searching for (save, transform, paintInlineImageXObject, restore)+
@ -1337,10 +1350,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200; var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
var MAX_WIDTH = 1000; var MAX_WIDTH = 1000;
var IMAGE_PADDING = 1; var IMAGE_PADDING = 1;
for (var i = 0, ii = fnArray.length; i < ii; i++) { for (var i = 0, ii = argsArray.length; i < ii; i++) {
if (fnArray[i] === 'paintInlineImageXObject' && if (fnArray[i] === OPS.paintInlineImageXObject &&
fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' && fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform &&
fnArray[i + 1] === 'restore') { fnArray[i + 1] === OPS.restore) {
var j = i - 2; var j = i - 2;
for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) {
} }
@ -1405,21 +1418,21 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
} }
// replacing queue items // replacing queue items
fnArray.splice(j, count * 4, ['paintInlineImageXObjectGroup']); squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup);
argsArray.splice(j, count * 4, argsArray.splice(j, count * 4,
[{width: imgWidth, height: imgHeight, data: imgData}, map]); [{width: imgWidth, height: imgHeight, data: imgData}, map]);
i = j; i = j;
ii = fnArray.length; ii = argsArray.length;
} }
} }
// grouping paintImageMaskXObject's into paintImageMaskXObjectGroup // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup
// searching for (save, transform, paintImageMaskXObject, restore)+ // searching for (save, transform, paintImageMaskXObject, restore)+
var MIN_IMAGES_IN_MASKS_BLOCK = 10; var MIN_IMAGES_IN_MASKS_BLOCK = 10;
var MAX_IMAGES_IN_MASKS_BLOCK = 100; var MAX_IMAGES_IN_MASKS_BLOCK = 100;
for (var i = 0, ii = fnArray.length; i < ii; i++) { for (var i = 0, ii = argsArray.length; i < ii; i++) {
if (fnArray[i] === 'paintImageMaskXObject' && if (fnArray[i] === OPS.paintImageMaskXObject &&
fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' && fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform &&
fnArray[i + 1] === 'restore') { fnArray[i + 1] === OPS.restore) {
var j = i - 2; var j = i - 2;
for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) {
} }
@ -1436,10 +1449,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
height: maskParams.height, transform: transform}); height: maskParams.height, transform: transform});
} }
// replacing queue items // replacing queue items
fnArray.splice(j, count * 4, ['paintImageMaskXObjectGroup']); squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup);
argsArray.splice(j, count * 4, [images]); argsArray.splice(j, count * 4, [images]);
i = j; i = j;
ii = fnArray.length; ii = argsArray.length;
} }
} }
}; };
@ -1448,25 +1461,40 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return PartialEvaluator; return PartialEvaluator;
})(); })();
var OperatorList = (function OperatorListClosure() { var OperatorList = (function OperatorListClosure() {
var CHUNK_SIZE = 100; var CHUNK_SIZE = 100;
function OperatorList(messageHandler, pageIndex) { function OperatorList(messageHandler, pageIndex) {
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
// When there isn't a message handler the fn array needs to be able to grow
// since we can't flush the operators.
if (messageHandler) {
this.fnArray = new Uint8Array(CHUNK_SIZE);
} else {
this.fnArray = []; this.fnArray = [];
}
this.argsArray = []; this.argsArray = [];
this.dependencies = {}, this.dependencies = {},
this.pageIndex = pageIndex; this.pageIndex = pageIndex;
this.fnIndex = 0;
} }
OperatorList.prototype = { OperatorList.prototype = {
get length() {
return this.argsArray.length;
},
addOp: function(fn, args) { addOp: function(fn, args) {
if (this.messageHandler) {
this.fnArray[this.fnIndex++] = fn;
this.argsArray.push(args);
if (this.fnIndex >= CHUNK_SIZE) {
this.flush();
}
} else {
this.fnArray.push(fn); this.fnArray.push(fn);
this.argsArray.push(args); this.argsArray.push(args);
if (this.messageHandler && this.fnArray.length >= CHUNK_SIZE) {
this.flush();
} }
}, },
@ -1475,7 +1503,7 @@ var OperatorList = (function OperatorListClosure() {
return; return;
} }
this.dependencies[dependency] = true; this.dependencies[dependency] = true;
this.addOp('dependency', [dependency]); this.addOp(OPS.dependency, [dependency]);
}, },
addDependencies: function(dependencies) { addDependencies: function(dependencies) {
@ -1485,15 +1513,17 @@ var OperatorList = (function OperatorListClosure() {
}, },
addOpList: function(opList) { addOpList: function(opList) {
Util.concatenateToArray(this.fnArray, opList.fnArray);
Util.concatenateToArray(this.argsArray, opList.argsArray);
Util.extendObj(this.dependencies, opList.dependencies); Util.extendObj(this.dependencies, opList.dependencies);
for (var i = 0, ii = opList.length; i < ii; i++) {
this.addOp(opList.fnArray[i], opList.argsArray[i]);
}
}, },
getIR: function() { getIR: function() {
return { return {
fnArray: this.fnArray, fnArray: this.fnArray,
argsArray: this.argsArray argsArray: this.argsArray,
length: this.length
}; };
}, },
@ -1503,12 +1533,13 @@ var OperatorList = (function OperatorListClosure() {
operatorList: { operatorList: {
fnArray: this.fnArray, fnArray: this.fnArray,
argsArray: this.argsArray, argsArray: this.argsArray,
lastChunk: lastChunk lastChunk: lastChunk,
length: this.length
}, },
pageIndex: this.pageIndex pageIndex: this.pageIndex
}); });
this.dependencies = []; this.dependencies = [];
this.fnArray = []; this.fnIndex = 0;
this.argsArray = []; this.argsArray = [];
} }
}; };

View File

@ -481,10 +481,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
*/ */
_renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) { _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) {
// Add the new chunk to the current operator list. // Add the new chunk to the current operator list.
Util.concatenateToArray(this.operatorList.fnArray, for (var i = 0, ii = operatorListChunk.length; i < ii; i++) {
operatorListChunk.fnArray); this.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
Util.concatenateToArray(this.operatorList.argsArray, this.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
operatorListChunk.argsArray); }
this.operatorList.lastChunk = operatorListChunk.lastChunk; this.operatorList.lastChunk = operatorListChunk.lastChunk;
// Notify all the rendering tasks there are more operators to be consumed. // Notify all the rendering tasks there are more operators to be consumed.
@ -1094,7 +1094,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() {
this.operatorListIdx, this.operatorListIdx,
this._continue.bind(this), this._continue.bind(this),
this.stepper); this.stepper);
if (this.operatorListIdx === this.operatorList.fnArray.length) { if (this.operatorListIdx === this.operatorList.argsArray.length) {
this.running = false; this.running = false;
if (this.operatorList.lastChunk) { if (this.operatorList.lastChunk) {
this.gfx.endDrawing(); this.gfx.endDrawing();

View File

@ -17,7 +17,7 @@
/* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error, /* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error,
FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, isArray, isNum, FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, isArray, isNum,
Pattern, TilingPattern, TODO, Util, warn, assert, info, Pattern, TilingPattern, TODO, Util, warn, assert, info,
TextRenderingMode */ TextRenderingMode, OPS */
'use strict'; 'use strict';
@ -538,7 +538,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var commonObjs = this.commonObjs; var commonObjs = this.commonObjs;
var objs = this.objs; var objs = this.objs;
var fnName; var fnId;
var slowCommands = this.slowCommands; var slowCommands = this.slowCommands;
while (true) { while (true) {
@ -547,10 +547,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
return i; return i;
} }
fnName = fnArray[i]; fnId = fnArray[i];
if (fnName !== 'dependency') { if (fnId !== OPS.dependency) {
this[fnName].apply(this, argsArray[i]); this[fnId].apply(this, argsArray[i]);
} else { } else {
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++) {
@ -580,7 +580,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// If the execution took longer then a certain amount of time, shedule // If the execution took longer then a certain amount of time, shedule
// to continue exeution after a short delay. // to continue exeution after a short delay.
// However, this is only possible if a 'continueCallback' is passed in. // However, this is only possible if a 'continueCallback' is passed in.
if (continueCallback && slowCommands[fnName] && Date.now() > endTime) { if (continueCallback && slowCommands[fnId] && Date.now() > endTime) {
setTimeout(continueCallback, 0); setTimeout(continueCallback, 0);
return i; return i;
} }
@ -1867,6 +1867,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
} }
}; };
for (var op in OPS) {
CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
}
return CanvasGraphics; return CanvasGraphics;
})(); })();

View File

@ -16,7 +16,7 @@
*/ */
/* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream, /* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream,
stringToBytes, PDFJS, isWorker, assert, NotImplementedException, stringToBytes, PDFJS, isWorker, assert, NotImplementedException,
Promise, isArray, ObjectLoader, isValidUrl, OperatorList */ Promise, isArray, ObjectLoader, isValidUrl, OperatorList, OPS */
'use strict'; 'use strict';
@ -188,9 +188,9 @@ var Annotation = (function AnnotationClosure() {
resourcesPromise.then(function(resources) { resourcesPromise.then(function(resources) {
var opList = new OperatorList(); var opList = new OperatorList();
opList.addOp('beginAnnotation', [data.rect, transform, matrix]); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
evaluator.getOperatorList(this.appearance, resources, opList); evaluator.getOperatorList(this.appearance, resources, opList);
opList.addOp('endAnnotation', []); opList.addOp(OPS.endAnnotation, []);
promise.resolve(opList); promise.resolve(opList);
}.bind(this)); }.bind(this));
@ -284,12 +284,12 @@ var Annotation = (function AnnotationClosure() {
annotationPromises.push(annotations[i].getOperatorList(partialEvaluator)); annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
} }
Promise.all(annotationPromises).then(function(datas) { Promise.all(annotationPromises).then(function(datas) {
opList.addOp('beginAnnotations', []); opList.addOp(OPS.beginAnnotations, []);
for (var i = 0, n = datas.length; i < n; ++i) { for (var i = 0, n = datas.length; i < n; ++i) {
var annotOpList = datas[i]; var annotOpList = datas[i];
opList.addOpList(annotOpList); opList.addOpList(annotOpList);
} }
opList.addOp('endAnnotations', []); opList.addOp(OPS.endAnnotations, []);
annotationsReadyPromise.resolve(); annotationsReadyPromise.resolve();
}, reject); }, reject);
@ -465,10 +465,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
data.rgb = [0, 0, 0]; data.rgb = [0, 0, 0];
// TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY! // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
for (var i = 0, n = fnArray.length; i < n; ++i) { for (var i = 0, n = fnArray.length; i < n; ++i) {
var fnName = appearanceFnArray[i]; var fnId = appearanceFnArray[i];
var args = appearanceArgsArray[i]; var args = appearanceArgsArray[i];
if (fnName === 'setFont') { if (fnId === OPS.setFont) {
data.fontRefName = args[0]; data.fontRefName = args[0];
var size = args[1]; var size = args[1];
if (size < 0) { if (size < 0) {
@ -478,9 +478,9 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
data.fontDirection = 1; data.fontDirection = 1;
data.fontSize = size; data.fontSize = size;
} }
} else if (fnName === 'setFillRGBColor') { } else if (fnId === OPS.setFillRGBColor) {
data.rgb = args; data.rgb = args;
} else if (fnName === 'setFillGray') { } else if (fnId === OPS.setFillGray) {
var rgbValue = args[0] * 255; var rgbValue = args[0] * 255;
data.rgb = [rgbValue, rgbValue, rgbValue]; data.rgb = [rgbValue, rgbValue, rgbValue];
} }

View File

@ -49,6 +49,98 @@ if (!globalScope.PDFJS) {
globalScope.PDFJS.pdfBug = false; globalScope.PDFJS.pdfBug = false;
// All the possible operations for an operator list.
var OPS = PDFJS.OPS = {
// Intentially start from 1 so it is easy to spot bad operators that will be
// 0's.
dependency: 1,
setLineWidth: 2,
setLineCap: 3,
setLineJoin: 4,
setMiterLimit: 5,
setDash: 6,
setRenderingIntent: 7,
setFlatness: 8,
setGState: 9,
save: 10,
restore: 11,
transform: 12,
moveTo: 13,
lineTo: 14,
curveTo: 15,
curveTo2: 16,
curveTo3: 17,
closePath: 18,
rectangle: 19,
stroke: 20,
closeStroke: 21,
fill: 22,
eoFill: 23,
fillStroke: 24,
eoFillStroke: 25,
closeFillStroke: 26,
closeEOFillStroke: 27,
endPath: 28,
clip: 29,
eoClip: 30,
beginText: 31,
endText: 32,
setCharSpacing: 33,
setWordSpacing: 34,
setHScale: 35,
setLeading: 36,
setFont: 37,
setTextRenderingMode: 38,
setTextRise: 39,
moveText: 40,
setLeadingMoveText: 41,
setTextMatrix: 42,
nextLine: 43,
showText: 44,
showSpacedText: 45,
nextLineShowText: 46,
nextLineSetSpacingShowText: 47,
setCharWidth: 48,
setCharWidthAndBounds: 49,
setStrokeColorSpace: 50,
setFillColorSpace: 51,
setStrokeColor: 52,
setStrokeColorN: 53,
setFillColor: 54,
setFillColorN: 55,
setStrokeGray: 56,
setFillGray: 57,
setStrokeRGBColor: 58,
setFillRGBColor: 59,
setStrokeCMYKColor: 60,
setFillCMYKColor: 61,
shadingFill: 62,
beginInlineImage: 63,
beginImageData: 64,
endInlineImage: 65,
paintXObject: 66,
markPoint: 67,
markPointProps: 68,
beginMarkedContent: 69,
beginMarkedContentProps: 70,
endMarkedContent: 71,
beginCompat: 72,
endCompat: 73,
paintFormXObjectBegin: 74,
paintFormXObjectEnd: 75,
beginGroup: 76,
endGroup: 77,
beginAnnotations: 78,
endAnnotations: 79,
beginAnnotation: 80,
endAnnotation: 81,
paintJpegXObject: 82,
paintImageMaskXObject: 83,
paintImageMaskXObjectGroup: 84,
paintImageXObject: 85,
paintInlineImageXObject: 86,
paintInlineImageXObjectGroup: 87
};
// Use only for debugging purposes. This should not be used in any code that is // Use only for debugging purposes. This should not be used in any code that is
// in mozilla master. // in mozilla master.

View File

@ -1,6 +1,6 @@
/* -*- 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 */ /* globals expect, it, describe, PartialEvaluator, StringStream, OPS */
'use strict'; 'use strict';
@ -39,7 +39,7 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1); expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual('save'); expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.argsArray[0].length).toEqual(0); expect(result.argsArray[0].length).toEqual(0);
}); });
@ -51,7 +51,7 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1); expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual('restore'); expect(result.fnArray[0]).toEqual(OPS.restore);
}); });
it('should handle two glued operations', function() { it('should handle two glued operations', function() {
@ -64,8 +64,8 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, resources); var result = evaluator.getOperatorList(stream, resources);
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('paintXObject'); expect(result.fnArray[0]).toEqual(OPS.paintXObject);
expect(result.fnArray[1]).toEqual('restore'); expect(result.fnArray[1]).toEqual(OPS.restore);
}); });
it('should handle tree glued operations', function() { it('should handle tree glued operations', function() {
@ -76,9 +76,9 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
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('save'); expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.fnArray[1]).toEqual('save'); expect(result.fnArray[1]).toEqual(OPS.save);
expect(result.fnArray[2]).toEqual('save'); expect(result.fnArray[2]).toEqual(OPS.save);
}); });
it('should handle three glued operations #2', function() { it('should handle three glued operations #2', function() {
@ -91,9 +91,9 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, resources); var result = evaluator.getOperatorList(stream, resources);
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('eoFillStroke'); expect(result.fnArray[0]).toEqual(OPS.eoFillStroke);
expect(result.fnArray[1]).toEqual('fillStroke'); expect(result.fnArray[1]).toEqual(OPS.fillStroke);
expect(result.fnArray[2]).toEqual('eoFill'); expect(result.fnArray[2]).toEqual(OPS.eoFill);
}); });
it('should handle glued operations and operands', function() { it('should handle glued operations and operands', function() {
@ -104,8 +104,8 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
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('save'); expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.fnArray[1]).toEqual('setTextRise'); expect(result.fnArray[1]).toEqual(OPS.setTextRise);
expect(result.argsArray.length).toEqual(2); expect(result.argsArray.length).toEqual(2);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(5); expect(result.argsArray[1][0]).toEqual(5);
@ -119,9 +119,9 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
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('setFlatness'); expect(result.fnArray[0]).toEqual(OPS.setFlatness);
expect(result.fnArray[1]).toEqual('setRenderingIntent'); expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
expect(result.fnArray[2]).toEqual('save'); expect(result.fnArray[2]).toEqual(OPS.save);
expect(result.argsArray.length).toEqual(3); expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[0][0]).toEqual(true); expect(result.argsArray[0][0]).toEqual(true);
@ -141,7 +141,7 @@ describe('evaluator', function() {
var result = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
expect(result.argsArray[0][0]).toEqual(5); expect(result.argsArray[0][0]).toEqual(5);
expect(result.argsArray[0][1]).toEqual(1); expect(result.argsArray[0][1]).toEqual(1);
expect(result.fnArray[0]).toEqual('setCharWidth'); 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(),
@ -152,7 +152,7 @@ describe('evaluator', function() {
expect(result.argsArray[0][0]).toEqual(5); expect(result.argsArray[0][0]).toEqual(5);
expect(result.argsArray[0][1]).toEqual(1); expect(result.argsArray[0][1]).toEqual(1);
expect(result.argsArray[0][2]).toEqual(4); expect(result.argsArray[0][2]).toEqual(4);
expect(result.fnArray[0]).toEqual('setCharWidth'); expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
}); });
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(),

View File

@ -240,6 +240,8 @@ var Stepper = (function StepperClosure() {
return out; return out;
} }
var opMap = null;
var glyphCommands = { var glyphCommands = {
'showText': 0, 'showText': 0,
'showSpacedText': 0, 'showSpacedText': 0,
@ -271,6 +273,12 @@ var Stepper = (function StepperClosure() {
headerRow.appendChild(c('th', 'args')); headerRow.appendChild(c('th', 'args'));
panel.appendChild(content); panel.appendChild(content);
this.table = table; this.table = table;
if (!opMap) {
opMap = Object.create(null);
for (var key in PDFJS.OPS) {
opMap[PDFJS.OPS[key]] = key;
}
}
}, },
updateOperatorList: function updateOperatorList(operatorList) { updateOperatorList: function updateOperatorList(operatorList) {
var self = this; var self = this;
@ -300,7 +308,7 @@ var Stepper = (function StepperClosure() {
breakCell.appendChild(cbox); breakCell.appendChild(cbox);
line.appendChild(breakCell); line.appendChild(breakCell);
line.appendChild(c('td', i.toString())); line.appendChild(c('td', i.toString()));
var fn = operatorList.fnArray[i]; var fn = opMap[operatorList.fnArray[i]];
var decArgs = args; var decArgs = args;
if (fn in glyphCommands) { if (fn in glyphCommands) {
var glyphIndex = glyphCommands[fn]; var glyphIndex = glyphCommands[fn];