diff --git a/src/function.js b/src/function.js index 5f5ff7d83..96d8a50f2 100644 --- a/src/function.js +++ b/src/function.js @@ -360,7 +360,7 @@ var PDFFunction = (function PDFFunctionClosure() { var range = IR[2]; var code = IR[3]; var numOutputs = range.length / 2; - var evaluator = new PostScriptEvaluator(code[0], code[1]); + var evaluator = new PostScriptEvaluator(code); // Cache the values for a big speed up, the cache size is limited though // since the number of possible values can be huge from a PS function. var cache = new FunctionCache(); @@ -393,6 +393,8 @@ var PDFFunction = (function PDFFunctionClosure() { })(); var FunctionCache = (function FunctionCache() { + // Of 10 PDF's with type4 functions the maxium number of distinct values seen + // was 256. This still may need some tweaking in the future though. var MAX_CACHE_SIZE = 1024; function FunctionCache() { this.cache = {}; @@ -441,8 +443,8 @@ var PostScriptStack = (function PostScriptStack() { index: function index(n) { this.push(this.stack[this.stack.length - n - 1]); }, + // rotate the last n stack elements p times roll: function roll(n, p) { - // rotate the last n stack elements p times var a = this.stack.splice(this.stack.length - n, n); // algorithm from http://jsfromhell.com/array/rotate var l = a.length, p = (Math.abs(p) >= l && (p %= l), @@ -464,27 +466,26 @@ var PostScriptEvaluator = (function PostScriptEvaluator() { var stack = new PostScriptStack(initialStack); var counter = 0; var operators = this.operators; - var operands = this.operands; var length = operators.length; - var a, b, operand; + var operator, a, b; while (counter < length) { - var operator = operators[counter]; - ++counter; + operator = operators[counter++]; + if (typeof operator == 'number') { + // Operator is really an operand and should be pushed to the stack. + stack.push(operator); + continue; + } switch (operator) { // non standard ps operators - case 'push': - operand = operands[counter - 1]; - stack.push(operand); - break; case 'jz': // jump if false - operand = operands[counter - 1]; + b = stack.pop(); a = stack.pop(); if (!a) - counter = operand; + counter = b; break; case 'j': // jump - operand = operands[counter - 1]; - counter = operand; + a = stack.pop(); + counter = a; break; // all ps operators in alphabetical order (excluding if/ifelse) @@ -696,7 +697,6 @@ var PostScriptParser = (function PostScriptParser() { function PostScriptParser(lexer) { this.lexer = lexer; this.operators = []; - this.operands = []; this.token; this.prev; } @@ -723,16 +723,14 @@ var PostScriptParser = (function PostScriptParser() { this.expect(PostScriptTokenTypes.LBRACE); this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); - return [this.operators, this.operands]; + return this.operators; }, parseBlock: function parseBlock() { while (true) { if (this.accept(PostScriptTokenTypes.NUMBER)) { - this.operators.push('push'); - this.operands.push(this.prev.value); + this.operators.push(this.prev.value); } else if (this.accept(PostScriptTokenTypes.OPERATOR)) { this.operators.push(this.prev.value); - this.operands.push(null); } else if (this.accept(PostScriptTokenTypes.LBRACE)) { this.parseCondition(); } else { @@ -743,31 +741,29 @@ var PostScriptParser = (function PostScriptParser() { parseCondition: function parseCondition() { // Add two place holders that will be updated later var conditionLocation = this.operators.length; - this.operators.push(null); - this.operands.push(null); + this.operators.push(null, null); this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); if (this.accept(PostScriptTokenTypes.IF)) { // The true block is right after the 'if' so it just falls through on // true else it jumps and skips the true block. - this.operators[conditionLocation] = 'jz'; - this.operands[conditionLocation] = this.operators.length; + this.operators[conditionLocation] = this.operators.length; + this.operators[conditionLocation + 1] = 'jz'; } else if (this.accept(PostScriptTokenTypes.LBRACE)) { var jumpLocation = this.operators.length; - this.operators.push(null); - this.operands.push(null); + this.operators.push(null, null); var endOfTrue = this.operators.length; this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); this.expect(PostScriptTokenTypes.IFELSE); // The jump is added at the end of the true block to skip the false // block. - this.operators[jumpLocation] = 'j'; - this.operands[jumpLocation] = this.operands.length; + this.operators[jumpLocation] = this.operators.length; + this.operators[jumpLocation + 1] = 'j'; - this.operators[conditionLocation] = 'jz'; - this.operands[conditionLocation] = endOfTrue; + this.operators[conditionLocation] = endOfTrue; + this.operators[conditionLocation + 1] = 'jz'; } else { error('PS Function: error parsing conditional.'); } diff --git a/test/unit/function_spec.js b/test/unit/function_spec.js index 0ea0bf313..c54f3ac29 100644 --- a/test/unit/function_spec.js +++ b/test/unit/function_spec.js @@ -38,51 +38,39 @@ describe('function', function() { } it('parses empty programs', function() { var output = parse('{}'); - expect(output[0].length).toEqual(0); + expect(output.length).toEqual(0); }); it('parses positive numbers', function() { var number = 999; var program = parse('{ ' + number + ' }'); - var expectedProgram = [ - ['push'], [number] - ]; + var expectedProgram = [number]; expect(program).toMatchArray(expectedProgram); }); it('parses negative numbers', function() { var number = -999; var program = parse('{ ' + number + ' }'); - var expectedProgram = [ - ['push'], [number] - ]; + var expectedProgram = [number]; expect(program).toMatchArray(expectedProgram); }); it('parses negative floats', function() { var number = 3.3; var program = parse('{ ' + number + ' }'); - var expectedProgram = [ - ['push'], [number] - ]; + var expectedProgram = [number]; expect(program).toMatchArray(expectedProgram); }); it('parses operators', function() { var program = parse('{ sub }'); - var expectedProgram = [ - ['sub'], [null] - ]; + var expectedProgram = ['sub']; expect(program).toMatchArray(expectedProgram); }); it('parses if statements', function() { var program = parse('{ { 99 } if }'); - var expectedProgram = [ - ['jz', 'push'], [2, 99] - ]; + var expectedProgram = [3, 'jz', 99]; expect(program).toMatchArray(expectedProgram); }); it('parses ifelse statements', function() { var program = parse('{ { 99 } { 44 } ifelse }'); - var expectedProgram = [ - ['jz', 'push', 'j', 'push'], [3, 99, 4, 44] - ]; + var expectedProgram = [5, 'jz', 99, 6, 'j', 44]; expect(program).toMatchArray(expectedProgram); }); it('handles missing brackets', function() { @@ -96,10 +84,11 @@ describe('function', function() { var stream = new StringStream(program); var parser = new PostScriptParser(new PostScriptLexer(stream)); var code = parser.parse(); - var evaluator = new PostScriptEvaluator(code[0], code[1]); + var evaluator = new PostScriptEvaluator(code); var output = evaluator.execute(); return output; } + it('pushes stack', function() { var stack = evaluate('{ 99 }'); var expectedStack = [99];