Merge pull request #4119 from yurydelendik/preprocessor
Extracts evaluator preprocessor, refactor text extraction and paintFormXObject
This commit is contained in:
commit
070d77a332
@ -40,119 +40,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
this.fontCache = fontCache;
|
||||
}
|
||||
|
||||
// Specifies properties for each command
|
||||
//
|
||||
// If variableArgs === true: [0, `numArgs`] expected
|
||||
// If variableArgs === false: exactly `numArgs` expected
|
||||
var OP_MAP = {
|
||||
// Graphic state
|
||||
w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false },
|
||||
J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false },
|
||||
j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false },
|
||||
M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false },
|
||||
d: { id: OPS.setDash, numArgs: 2, variableArgs: false },
|
||||
ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false },
|
||||
i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false },
|
||||
gs: { id: OPS.setGState, numArgs: 1, variableArgs: false },
|
||||
q: { id: OPS.save, numArgs: 0, variableArgs: false },
|
||||
Q: { id: OPS.restore, numArgs: 0, variableArgs: false },
|
||||
cm: { id: OPS.transform, numArgs: 6, variableArgs: false },
|
||||
|
||||
// Path
|
||||
m: { id: OPS.moveTo, numArgs: 2, variableArgs: false },
|
||||
l: { id: OPS.lineTo, numArgs: 2, variableArgs: false },
|
||||
c: { id: OPS.curveTo, numArgs: 6, variableArgs: false },
|
||||
v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false },
|
||||
y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false },
|
||||
h: { id: OPS.closePath, numArgs: 0, variableArgs: false },
|
||||
re: { id: OPS.rectangle, numArgs: 4, variableArgs: false },
|
||||
S: { id: OPS.stroke, numArgs: 0, variableArgs: false },
|
||||
s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false },
|
||||
f: { id: OPS.fill, numArgs: 0, variableArgs: false },
|
||||
F: { id: OPS.fill, numArgs: 0, variableArgs: false },
|
||||
'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false },
|
||||
B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false },
|
||||
'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false },
|
||||
b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false },
|
||||
'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false },
|
||||
n: { id: OPS.endPath, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Clipping
|
||||
W: { id: OPS.clip, numArgs: 0, variableArgs: false },
|
||||
'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Text
|
||||
BT: { id: OPS.beginText, numArgs: 0, variableArgs: false },
|
||||
ET: { id: OPS.endText, numArgs: 0, variableArgs: false },
|
||||
Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false },
|
||||
Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false },
|
||||
Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false },
|
||||
TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false },
|
||||
Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false },
|
||||
Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false },
|
||||
Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false },
|
||||
Td: { id: OPS.moveText, numArgs: 2, variableArgs: false },
|
||||
TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false },
|
||||
Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false },
|
||||
'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false },
|
||||
Tj: { id: OPS.showText, numArgs: 1, variableArgs: false },
|
||||
TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false },
|
||||
'\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false },
|
||||
'"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3,
|
||||
variableArgs: false },
|
||||
|
||||
// Type3 fonts
|
||||
d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false },
|
||||
d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false },
|
||||
|
||||
// Color
|
||||
CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false },
|
||||
cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false },
|
||||
SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true },
|
||||
SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true },
|
||||
sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true },
|
||||
scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true },
|
||||
G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false },
|
||||
g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false },
|
||||
RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false },
|
||||
rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false },
|
||||
K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false },
|
||||
k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false },
|
||||
|
||||
// Shading
|
||||
sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false },
|
||||
|
||||
// Images
|
||||
BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false },
|
||||
ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false },
|
||||
EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false },
|
||||
|
||||
// XObjects
|
||||
Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false },
|
||||
MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false },
|
||||
DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false },
|
||||
BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false },
|
||||
BDC: { id: OPS.beginMarkedContentProps, numArgs: 2,
|
||||
variableArgs: false },
|
||||
EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Compatibility
|
||||
BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false },
|
||||
EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false },
|
||||
|
||||
// (reserved partial commands for the lexer)
|
||||
BM: null,
|
||||
BD: null,
|
||||
'true': null,
|
||||
fa: null,
|
||||
fal: null,
|
||||
fals: null,
|
||||
'false': null,
|
||||
nu: null,
|
||||
nul: null,
|
||||
'null': null
|
||||
};
|
||||
|
||||
var TILING_PATTERN = 1, SHADING_PATTERN = 2;
|
||||
|
||||
PartialEvaluator.prototype = {
|
||||
@ -198,7 +85,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
|
||||
xobj, smask,
|
||||
operatorList) {
|
||||
operatorList,
|
||||
state) {
|
||||
var self = this;
|
||||
|
||||
var matrix = xobj.dict.get('Matrix');
|
||||
@ -226,7 +114,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
|
||||
|
||||
this.getOperatorList(xobj, xobj.dict.get('Resources') || resources,
|
||||
operatorList);
|
||||
operatorList, state);
|
||||
operatorList.addOp(OPS.paintFormXObjectEnd, []);
|
||||
|
||||
if (group) {
|
||||
@ -532,7 +420,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
getOperatorList: function PartialEvaluator_getOperatorList(stream,
|
||||
resources,
|
||||
operatorList) {
|
||||
operatorList,
|
||||
evaluatorState) {
|
||||
|
||||
var self = this;
|
||||
var xref = this.xref;
|
||||
@ -543,54 +432,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
resources = resources || new Dict();
|
||||
var xobjs = resources.get('XObject') || new Dict();
|
||||
var patterns = resources.get('Pattern') || new Dict();
|
||||
// TODO(mduan): pass array of knownCommands rather than OP_MAP
|
||||
// dictionary
|
||||
var parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
|
||||
var preprocessor = new EvaluatorPreprocessor(stream, xref);
|
||||
if (evaluatorState) {
|
||||
preprocessor.setState(evaluatorState);
|
||||
}
|
||||
|
||||
var promise = new LegacyPromise();
|
||||
var args = [];
|
||||
while (true) {
|
||||
|
||||
var obj = parser.getObj();
|
||||
|
||||
if (isEOF(obj)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isCmd(obj)) {
|
||||
var cmd = obj.cmd;
|
||||
|
||||
// Check that the command is valid
|
||||
var opSpec = OP_MAP[cmd];
|
||||
if (!opSpec) {
|
||||
warn('Unknown command "' + cmd + '"');
|
||||
continue;
|
||||
}
|
||||
|
||||
var fn = opSpec.id;
|
||||
|
||||
// Validate the number of arguments for the command
|
||||
if (opSpec.variableArgs) {
|
||||
if (args.length > opSpec.numArgs) {
|
||||
info('Command ' + fn + ': expected [0,' + opSpec.numArgs +
|
||||
'] args, but received ' + args.length + ' args');
|
||||
}
|
||||
} else {
|
||||
if (args.length < opSpec.numArgs) {
|
||||
// If we receive too few args, it's not possible to possible
|
||||
// to execute the command, so skip the command
|
||||
info('Command ' + fn + ': because expected ' +
|
||||
opSpec.numArgs + ' args, but received ' + args.length +
|
||||
' args; skipping');
|
||||
args = [];
|
||||
continue;
|
||||
} else if (args.length > opSpec.numArgs) {
|
||||
info('Command ' + fn + ': expected ' + opSpec.numArgs +
|
||||
' args, but received ' + args.length + ' args');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out how to type-check vararg functions
|
||||
var operation;
|
||||
while ((operation = preprocessor.read())) {
|
||||
var args = operation.args;
|
||||
var fn = operation.fn;
|
||||
|
||||
switch (fn) {
|
||||
case OPS.setStrokeColorN:
|
||||
@ -642,7 +493,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
);
|
||||
|
||||
if ('Form' == type.name) {
|
||||
self.buildFormXObject(resources, xobj, null, operatorList);
|
||||
self.buildFormXObject(resources, xobj, null, operatorList,
|
||||
preprocessor.getState());
|
||||
args = [];
|
||||
continue;
|
||||
} else if ('Image' == type.name) {
|
||||
@ -733,12 +585,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
} // switch
|
||||
|
||||
operatorList.addOp(fn, args);
|
||||
args = [];
|
||||
parser.saveState();
|
||||
} else if (obj !== null && obj !== undefined) {
|
||||
args.push(obj instanceof Dict ? obj.getAll() : obj);
|
||||
assertWellFormed(args.length <= 33, 'Too many arguments');
|
||||
}
|
||||
}
|
||||
|
||||
// some pdf don't close all restores inside object/form
|
||||
// closing those for them
|
||||
for (var i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) {
|
||||
operatorList.addOp(OPS.restore, []);
|
||||
}
|
||||
|
||||
return operatorList;
|
||||
@ -775,65 +627,55 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
// The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
|
||||
var xobjs = null;
|
||||
|
||||
var parser = new Parser(new Lexer(stream), false);
|
||||
var preprocessor = new EvaluatorPreprocessor(stream, xref);
|
||||
var res = resources;
|
||||
var args = [], obj;
|
||||
|
||||
var chunk = '';
|
||||
var font = null;
|
||||
var charSpace = 0, wordSpace = 0;
|
||||
while (!isEOF(obj = parser.getObj())) {
|
||||
if (isCmd(obj)) {
|
||||
var cmd = obj.cmd;
|
||||
switch (cmd) {
|
||||
var operation;
|
||||
while ((operation = preprocessor.read())) {
|
||||
var fn = operation.fn;
|
||||
var args = operation.args;
|
||||
switch (fn) {
|
||||
// TODO: Add support for SAVE/RESTORE and XFORM here.
|
||||
case 'Tf':
|
||||
case OPS.setFont:
|
||||
font = handleSetFont(args[0].name).translated;
|
||||
textState.fontSize = args[1];
|
||||
break;
|
||||
case 'Ts':
|
||||
case OPS.setTextRise:
|
||||
textState.textRise = args[0];
|
||||
break;
|
||||
case 'Tz':
|
||||
case OPS.setHScale:
|
||||
textState.textHScale = args[0] / 100;
|
||||
break;
|
||||
case 'TL':
|
||||
case OPS.setLeading:
|
||||
textState.leading = args[0];
|
||||
break;
|
||||
case 'Td':
|
||||
case OPS.moveText:
|
||||
textState.translateTextMatrix(args[0], args[1]);
|
||||
break;
|
||||
case 'TD':
|
||||
case OPS.setLeadingMoveText:
|
||||
textState.leading = -args[1];
|
||||
textState.translateTextMatrix(args[0], args[1]);
|
||||
break;
|
||||
case 'T*':
|
||||
case OPS.nextLine:
|
||||
textState.translateTextMatrix(0, -textState.leading);
|
||||
break;
|
||||
case 'Tm':
|
||||
case OPS.setTextMatrix:
|
||||
textState.setTextMatrix(args[0], args[1],
|
||||
args[2], args[3], args[4], args[5]);
|
||||
break;
|
||||
case 'Tc':
|
||||
case OPS.setCharSpacing:
|
||||
charSpace = args[0];
|
||||
break;
|
||||
case 'Tw':
|
||||
case OPS.setWordSpacing:
|
||||
wordSpace = args[0];
|
||||
break;
|
||||
case 'q':
|
||||
textState.push();
|
||||
break;
|
||||
case 'Q':
|
||||
textState.pop();
|
||||
break;
|
||||
case 'BT':
|
||||
case OPS.beginText:
|
||||
textState.initialiseTextObj();
|
||||
break;
|
||||
case 'cm':
|
||||
textState.transformCTM(args[0], args[1], args[2],
|
||||
args[3], args[4], args[5]);
|
||||
break;
|
||||
case 'TJ':
|
||||
case OPS.showSpacedText:
|
||||
var items = args[0];
|
||||
for (var j = 0, jj = items.length; j < jj; j++) {
|
||||
if (typeof items[j] === 'string') {
|
||||
@ -851,20 +693,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Tj':
|
||||
case OPS.showText:
|
||||
chunk += fontCharsToUnicode(args[0], font);
|
||||
break;
|
||||
case '\'':
|
||||
case OPS.nextLineShowText:
|
||||
// For search, adding a extra white space for line breaks would be
|
||||
// better here, but that causes too much spaces in the
|
||||
// text-selection divs.
|
||||
chunk += fontCharsToUnicode(args[0], font);
|
||||
break;
|
||||
case '"':
|
||||
case OPS.nextLineSetSpacingShowText:
|
||||
// Note comment in "'"
|
||||
chunk += fontCharsToUnicode(args[2], font);
|
||||
break;
|
||||
case 'Do':
|
||||
case OPS.paintXObject:
|
||||
// Set the chunk such that the following if won't add something
|
||||
// to the state.
|
||||
chunk = '';
|
||||
@ -898,7 +740,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
state
|
||||
);
|
||||
break;
|
||||
case 'gs':
|
||||
case OPS.setGState:
|
||||
var dictName = args[0];
|
||||
var extGState = resources.get('ExtGState');
|
||||
|
||||
@ -917,7 +759,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
if (chunk !== '') {
|
||||
var bidiText = PDFJS.bidi(chunk, -1, font.vertical);
|
||||
var renderParams = textState.calcRenderParams();
|
||||
var renderParams = textState.calcRenderParams(preprocessor.ctm);
|
||||
bidiText.x = renderParams.renderMatrix[4] - (textState.fontSize *
|
||||
renderParams.vScale * Math.sin(renderParams.angle));
|
||||
bidiText.y = renderParams.renderMatrix[5] + (textState.fontSize *
|
||||
renderParams.vScale * Math.cos(renderParams.angle));
|
||||
var fontHeight = textState.fontSize * renderParams.vScale;
|
||||
var fontAscent = font.ascent ? font.ascent * fontHeight :
|
||||
font.descent ? (1 + font.descent) * fontHeight : fontHeight;
|
||||
@ -933,12 +779,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
chunk = '';
|
||||
}
|
||||
|
||||
args = [];
|
||||
} else if (obj !== null && obj !== undefined) {
|
||||
assertWellFormed(args.length <= 33, 'Too many arguments');
|
||||
args.push(obj);
|
||||
}
|
||||
} // while
|
||||
|
||||
return state;
|
||||
@ -1597,7 +1437,6 @@ var OperatorList = (function OperatorListClosure() {
|
||||
var TextState = (function TextStateClosure() {
|
||||
function TextState() {
|
||||
this.fontSize = 0;
|
||||
this.ctm = [1, 0, 0, 1, 0, 0];
|
||||
this.textMatrix = [1, 0, 0, 1, 0, 0];
|
||||
this.stateStack = [];
|
||||
//textState variables
|
||||
@ -1606,15 +1445,6 @@ var TextState = (function TextStateClosure() {
|
||||
this.textRise = 0;
|
||||
}
|
||||
TextState.prototype = {
|
||||
push: function TextState_push() {
|
||||
this.stateStack.push(this.ctm.slice());
|
||||
},
|
||||
pop: function TextState_pop() {
|
||||
var prev = this.stateStack.pop();
|
||||
if (prev) {
|
||||
this.ctm = prev;
|
||||
}
|
||||
},
|
||||
initialiseTextObj: function TextState_initialiseTextObj() {
|
||||
var m = this.textMatrix;
|
||||
m[0] = 1, m[1] = 0, m[2] = 0, m[3] = 1, m[4] = 0, m[5] = 0;
|
||||
@ -1623,24 +1453,13 @@ var TextState = (function TextStateClosure() {
|
||||
var m = this.textMatrix;
|
||||
m[0] = a, m[1] = b, m[2] = c, m[3] = d, m[4] = e, m[5] = f;
|
||||
},
|
||||
transformCTM: function TextState_transformCTM(a, b, c, d, e, f) {
|
||||
var m = this.ctm;
|
||||
var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5];
|
||||
m[0] = m0 * a + m2 * b;
|
||||
m[1] = m1 * a + m3 * b;
|
||||
m[2] = m0 * c + m2 * d;
|
||||
m[3] = m1 * c + m3 * d;
|
||||
m[4] = m0 * e + m2 * f + m4;
|
||||
m[5] = m1 * e + m3 * f + m5;
|
||||
},
|
||||
translateTextMatrix: function TextState_translateTextMatrix(x, y) {
|
||||
var m = this.textMatrix;
|
||||
m[4] = m[0] * x + m[2] * y + m[4];
|
||||
m[5] = m[1] * x + m[3] * y + m[5];
|
||||
},
|
||||
calcRenderParams: function TextState_calcRenderingParams() {
|
||||
calcRenderParams: function TextState_calcRenderingParams(cm) {
|
||||
var tm = this.textMatrix;
|
||||
var cm = this.ctm;
|
||||
var a = this.fontSize;
|
||||
var b = a * this.textHScale;
|
||||
var c = this.textRise;
|
||||
@ -1683,3 +1502,218 @@ var EvalState = (function EvalStateClosure() {
|
||||
return EvalState;
|
||||
})();
|
||||
|
||||
var EvaluatorPreprocessor = (function EvaluatorPreprocessor() {
|
||||
// Specifies properties for each command
|
||||
//
|
||||
// If variableArgs === true: [0, `numArgs`] expected
|
||||
// If variableArgs === false: exactly `numArgs` expected
|
||||
var OP_MAP = {
|
||||
// Graphic state
|
||||
w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false },
|
||||
J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false },
|
||||
j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false },
|
||||
M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false },
|
||||
d: { id: OPS.setDash, numArgs: 2, variableArgs: false },
|
||||
ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false },
|
||||
i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false },
|
||||
gs: { id: OPS.setGState, numArgs: 1, variableArgs: false },
|
||||
q: { id: OPS.save, numArgs: 0, variableArgs: false },
|
||||
Q: { id: OPS.restore, numArgs: 0, variableArgs: false },
|
||||
cm: { id: OPS.transform, numArgs: 6, variableArgs: false },
|
||||
|
||||
// Path
|
||||
m: { id: OPS.moveTo, numArgs: 2, variableArgs: false },
|
||||
l: { id: OPS.lineTo, numArgs: 2, variableArgs: false },
|
||||
c: { id: OPS.curveTo, numArgs: 6, variableArgs: false },
|
||||
v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false },
|
||||
y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false },
|
||||
h: { id: OPS.closePath, numArgs: 0, variableArgs: false },
|
||||
re: { id: OPS.rectangle, numArgs: 4, variableArgs: false },
|
||||
S: { id: OPS.stroke, numArgs: 0, variableArgs: false },
|
||||
s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false },
|
||||
f: { id: OPS.fill, numArgs: 0, variableArgs: false },
|
||||
F: { id: OPS.fill, numArgs: 0, variableArgs: false },
|
||||
'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false },
|
||||
B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false },
|
||||
'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false },
|
||||
b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false },
|
||||
'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false },
|
||||
n: { id: OPS.endPath, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Clipping
|
||||
W: { id: OPS.clip, numArgs: 0, variableArgs: false },
|
||||
'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Text
|
||||
BT: { id: OPS.beginText, numArgs: 0, variableArgs: false },
|
||||
ET: { id: OPS.endText, numArgs: 0, variableArgs: false },
|
||||
Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false },
|
||||
Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false },
|
||||
Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false },
|
||||
TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false },
|
||||
Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false },
|
||||
Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false },
|
||||
Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false },
|
||||
Td: { id: OPS.moveText, numArgs: 2, variableArgs: false },
|
||||
TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false },
|
||||
Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false },
|
||||
'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false },
|
||||
Tj: { id: OPS.showText, numArgs: 1, variableArgs: false },
|
||||
TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false },
|
||||
'\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false },
|
||||
'"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3,
|
||||
variableArgs: false },
|
||||
|
||||
// Type3 fonts
|
||||
d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false },
|
||||
d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false },
|
||||
|
||||
// Color
|
||||
CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false },
|
||||
cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false },
|
||||
SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true },
|
||||
SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true },
|
||||
sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true },
|
||||
scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true },
|
||||
G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false },
|
||||
g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false },
|
||||
RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false },
|
||||
rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false },
|
||||
K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false },
|
||||
k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false },
|
||||
|
||||
// Shading
|
||||
sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false },
|
||||
|
||||
// Images
|
||||
BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false },
|
||||
ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false },
|
||||
EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false },
|
||||
|
||||
// XObjects
|
||||
Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false },
|
||||
MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false },
|
||||
DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false },
|
||||
BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false },
|
||||
BDC: { id: OPS.beginMarkedContentProps, numArgs: 2,
|
||||
variableArgs: false },
|
||||
EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false },
|
||||
|
||||
// Compatibility
|
||||
BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false },
|
||||
EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false },
|
||||
|
||||
// (reserved partial commands for the lexer)
|
||||
BM: null,
|
||||
BD: null,
|
||||
'true': null,
|
||||
fa: null,
|
||||
fal: null,
|
||||
fals: null,
|
||||
'false': null,
|
||||
nu: null,
|
||||
nul: null,
|
||||
'null': null
|
||||
};
|
||||
|
||||
function EvaluatorPreprocessor(stream, xref) {
|
||||
// TODO(mduan): pass array of knownCommands rather than OP_MAP
|
||||
// dictionary
|
||||
this.parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
|
||||
this.ctm = new Float32Array([1, 0, 0, 1, 0, 0]);
|
||||
this.savedStates = [];
|
||||
}
|
||||
EvaluatorPreprocessor.prototype = {
|
||||
get savedStatesDepth() {
|
||||
return this.savedStates.length;
|
||||
},
|
||||
read: function EvaluatorPreprocessor_read() {
|
||||
var args = [];
|
||||
while (true) {
|
||||
var obj = this.parser.getObj();
|
||||
if (isEOF(obj)) {
|
||||
return null; // no more commands
|
||||
}
|
||||
if (!isCmd(obj)) {
|
||||
// argument
|
||||
if (obj !== null && obj !== undefined) {
|
||||
args.push(obj instanceof Dict ? obj.getAll() : obj);
|
||||
assertWellFormed(args.length <= 33, 'Too many arguments');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var cmd = obj.cmd;
|
||||
// Check that the command is valid
|
||||
var opSpec = OP_MAP[cmd];
|
||||
if (!opSpec) {
|
||||
warn('Unknown command "' + cmd + '"');
|
||||
continue;
|
||||
}
|
||||
|
||||
var fn = opSpec.id;
|
||||
|
||||
// Validate the number of arguments for the command
|
||||
if (opSpec.variableArgs) {
|
||||
if (args.length > opSpec.numArgs) {
|
||||
info('Command ' + fn + ': expected [0,' + opSpec.numArgs +
|
||||
'] args, but received ' + args.length + ' args');
|
||||
}
|
||||
} else {
|
||||
if (args.length < opSpec.numArgs) {
|
||||
// If we receive too few args, it's not possible to possible
|
||||
// to execute the command, so skip the command
|
||||
info('Command ' + fn + ': because expected ' +
|
||||
opSpec.numArgs + ' args, but received ' + args.length +
|
||||
' args; skipping');
|
||||
args = [];
|
||||
continue;
|
||||
} else if (args.length > opSpec.numArgs) {
|
||||
info('Command ' + fn + ': expected ' + opSpec.numArgs +
|
||||
' args, but received ' + args.length + ' args');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out how to type-check vararg functions
|
||||
|
||||
this.preprocessCommand(fn, args);
|
||||
|
||||
return {fn: fn, args: args};
|
||||
}
|
||||
},
|
||||
getState: function EvaluatorPreprocessor_getState() {
|
||||
return {
|
||||
ctm: this.ctm
|
||||
};
|
||||
},
|
||||
setState: function EvaluatorPreprocessor_setState(state) {
|
||||
this.ctm = state.ctm;
|
||||
},
|
||||
preprocessCommand: function EvaluatorPreprocessor_preprocessCommand(fn,
|
||||
args) {
|
||||
switch (fn | 0) {
|
||||
case OPS.save:
|
||||
this.savedStates.push(this.getState());
|
||||
break;
|
||||
case OPS.restore:
|
||||
var previousState = this.savedStates.pop();
|
||||
if (previousState) {
|
||||
this.setState(previousState);
|
||||
}
|
||||
break;
|
||||
case OPS.transform:
|
||||
var ctm = this.ctm;
|
||||
var m = new Float32Array(6);
|
||||
m[0] = ctm[0] * args[0] + ctm[2] * args[1];
|
||||
m[1] = ctm[1] * args[0] + ctm[3] * args[1];
|
||||
m[2] = ctm[0] * args[2] + ctm[2] * args[3];
|
||||
m[3] = ctm[1] * args[2] + ctm[3] * args[3];
|
||||
m[4] = ctm[0] * args[4] + ctm[2] * args[5] + ctm[4];
|
||||
m[5] = ctm[1] * args[4] + ctm[3] * args[5] + ctm[5];
|
||||
this.ctm = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
return EvaluatorPreprocessor;
|
||||
})();
|
||||
|
@ -36,21 +36,6 @@ var Parser = (function ParserClosure() {
|
||||
}
|
||||
|
||||
Parser.prototype = {
|
||||
saveState: function Parser_saveState() {
|
||||
this.state = {
|
||||
buf1: this.buf1,
|
||||
buf2: this.buf2,
|
||||
streamPos: this.lexer.stream.pos
|
||||
};
|
||||
},
|
||||
|
||||
restoreState: function Parser_restoreState() {
|
||||
var state = this.state;
|
||||
this.buf1 = state.buf1;
|
||||
this.buf2 = state.buf2;
|
||||
this.lexer.stream.pos = state.streamPos;
|
||||
},
|
||||
|
||||
refill: function Parser_refill() {
|
||||
this.buf1 = this.lexer.getObj();
|
||||
this.buf2 = this.lexer.getObj();
|
||||
|
@ -376,7 +376,6 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||
this.fillAlpha = 1;
|
||||
this.strokeAlpha = 1;
|
||||
this.lineWidth = 1;
|
||||
this.paintFormXObjectDepth = 0;
|
||||
|
||||
this.old = old;
|
||||
}
|
||||
@ -1453,7 +1452,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
|
||||
bbox) {
|
||||
this.save();
|
||||
this.current.paintFormXObjectDepth++;
|
||||
this.baseTransformStack.push(this.baseTransform);
|
||||
|
||||
if (matrix && isArray(matrix) && 6 == matrix.length)
|
||||
@ -1471,12 +1469,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
},
|
||||
|
||||
paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
|
||||
var depth = this.current.paintFormXObjectDepth;
|
||||
do {
|
||||
this.restore();
|
||||
// some pdf don't close all restores inside object
|
||||
// closing those for them
|
||||
} while (this.current.paintFormXObjectDepth >= depth);
|
||||
this.restore();
|
||||
this.baseTransform = this.baseTransformStack.pop();
|
||||
},
|
||||
|
||||
|
@ -35,11 +35,11 @@ describe('evaluator', function() {
|
||||
var evaluator = new PartialEvaluator(new PdfManagerMock(),
|
||||
new XrefMock(), new HandlerMock(),
|
||||
'prefix');
|
||||
var stream = new StringStream('qTT');
|
||||
var stream = new StringStream('fTT');
|
||||
var result = evaluator.getOperatorList(stream, new ResourcesMock());
|
||||
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
|
||||
expect(result.fnArray.length).toEqual(1);
|
||||
expect(result.fnArray[0]).toEqual(OPS.save);
|
||||
expect(result.fnArray[0]).toEqual(OPS.fill);
|
||||
expect(result.argsArray[0].length).toEqual(0);
|
||||
});
|
||||
|
||||
@ -72,13 +72,13 @@ describe('evaluator', function() {
|
||||
var evaluator = new PartialEvaluator(new PdfManagerMock(),
|
||||
new XrefMock(), new HandlerMock(),
|
||||
'prefix');
|
||||
var stream = new StringStream('qqq');
|
||||
var stream = new StringStream('fff');
|
||||
var result = evaluator.getOperatorList(stream, new ResourcesMock());
|
||||
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
|
||||
expect(result.fnArray.length).toEqual(3);
|
||||
expect(result.fnArray[0]).toEqual(OPS.save);
|
||||
expect(result.fnArray[1]).toEqual(OPS.save);
|
||||
expect(result.fnArray[2]).toEqual(OPS.save);
|
||||
expect(result.fnArray[0]).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() {
|
||||
@ -100,11 +100,11 @@ describe('evaluator', function() {
|
||||
var evaluator = new PartialEvaluator(new PdfManagerMock(),
|
||||
new XrefMock(), new HandlerMock(),
|
||||
'prefix');
|
||||
var stream = new StringStream('q5 Ts');
|
||||
var stream = new StringStream('f5 Ts');
|
||||
var result = evaluator.getOperatorList(stream, new ResourcesMock());
|
||||
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
|
||||
expect(result.fnArray.length).toEqual(2);
|
||||
expect(result.fnArray[0]).toEqual(OPS.save);
|
||||
expect(result.fnArray[0]).toEqual(OPS.fill);
|
||||
expect(result.fnArray[1]).toEqual(OPS.setTextRise);
|
||||
expect(result.argsArray.length).toEqual(2);
|
||||
expect(result.argsArray[1].length).toEqual(1);
|
||||
@ -115,13 +115,13 @@ describe('evaluator', function() {
|
||||
var evaluator = new PartialEvaluator(new PdfManagerMock(),
|
||||
new XrefMock(), new HandlerMock(),
|
||||
'prefix');
|
||||
var stream = new StringStream('trueifalserinullq');
|
||||
var stream = new StringStream('trueifalserinullh');
|
||||
var result = evaluator.getOperatorList(stream, new ResourcesMock());
|
||||
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
|
||||
expect(result.fnArray.length).toEqual(3);
|
||||
expect(result.fnArray[0]).toEqual(OPS.setFlatness);
|
||||
expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
|
||||
expect(result.fnArray[2]).toEqual(OPS.save);
|
||||
expect(result.fnArray[2]).toEqual(OPS.closePath);
|
||||
expect(result.argsArray.length).toEqual(3);
|
||||
expect(result.argsArray[0].length).toEqual(1);
|
||||
expect(result.argsArray[0][0]).toEqual(true);
|
||||
@ -163,6 +163,19 @@ describe('evaluator', function() {
|
||||
expect(result.argsArray).toEqual([]);
|
||||
expect(result.fnArray).toEqual([]);
|
||||
});
|
||||
it('should close opened saves', function() {
|
||||
var evaluator = new PartialEvaluator(new PdfManagerMock(),
|
||||
new XrefMock(), new HandlerMock(),
|
||||
'prefix');
|
||||
var stream = new StringStream('qq');
|
||||
var result = evaluator.getOperatorList(stream, new ResourcesMock());
|
||||
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
|
||||
expect(result.fnArray.length).toEqual(4);
|
||||
expect(result.fnArray[0]).toEqual(OPS.save);
|
||||
expect(result.fnArray[1]).toEqual(OPS.save);
|
||||
expect(result.fnArray[2]).toEqual(OPS.restore);
|
||||
expect(result.fnArray[3]).toEqual(OPS.restore);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user