diff --git a/AUTHORS b/AUTHORS index 47ed8dacd..d29c5ce97 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ Jonas Jenwald Julian Viereck Justin D'Arcangelo Kalervo Kujala +Ophir Lojkine <@lovasoa> Rob Wu Shaon Barman Tim van der Meij diff --git a/src/core/parser.js b/src/core/parser.js index 28e081630..32968d206 100644 --- a/src/core/parser.js +++ b/src/core/parser.js @@ -393,47 +393,85 @@ var Lexer = (function LexerClosure() { nextChar: function Lexer_nextChar() { return (this.currentChar = this.stream.getByte()); }, + peekChar: function Lexer_peekChar() { + return this.stream.peekBytes(1)[0]; + }, getNumber: function Lexer_getNumber() { - var floating = false; var ch = this.currentChar; - var allDigits = ch >= 0x30 && ch <= 0x39; - var strBuf = this.strBuf; - strBuf.length = 0; - strBuf.push(String.fromCharCode(ch)); + var eNotation = false; + var divideBy = 0; // different from 0 if it's a floating point value + + var sign = 1; + + + if (ch === 0x2D) { // '-' + sign = -1; + ch = this.nextChar(); + } else if (ch === 0x2B) { // '+' + ch = this.nextChar(); + } + if (ch === 0x2E) { // '.' + divideBy = 10; + ch = this.nextChar(); + } + + if (ch < 0x30 || ch > 0x39) { // '0' - '9' + error('Invalid number: ' + String.fromCharCode(ch)); + return 0; + } + + var baseValue = ch - 0x30; // '0' + var powerValue = 0; + var powerValueSign = 1; + while ((ch = this.nextChar()) >= 0) { - if (ch >= 0x30 && ch <= 0x39) { // '0'-'9' - strBuf.push(String.fromCharCode(ch)); - } else if (ch === 0x2E && !floating) { // '.' - strBuf.push('.'); - floating = true; - allDigits = false; + if (0x30 <= ch && ch <= 0x39) { // '0' - '9' + var currentDigit = ch - 0x30; // '0' + if (eNotation) { // We are after an 'e' or 'E' + powerValue = powerValue * 10 + currentDigit; + } else { + if (divideBy !== 0) { // We are after a point + divideBy *= 10; + } + baseValue = baseValue * 10 + currentDigit; + } + } else if (ch === 0x2E) { // '.' + if (divideBy === 0) { + divideBy = 1; + } else { + // A number can have only one '.' + break; + } } else if (ch === 0x2D) { // '-' // ignore minus signs in the middle of numbers to match // Adobe's behavior warn('Badly formated number'); - allDigits = false; } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e' - floating = true; - allDigits = false; + // 'E' can be either a scientific notation or the beginning of a new + // operator + var hasE = true; + ch = this.peekChar(); + if (ch === 0x2B || ch === 0x2D) { // '+', '-' + powerValueSign = (ch === 0x2D) ? -1 : 1; + this.nextChar(); // Consume the sign character + } else if (ch < 0x30 || ch > 0x39) { // '0' - '9' + // The 'E' must be the beginning of a new operator + break; + } + eNotation = true; } else { // the last character doesn't belong to us break; } } - var value; - if (allDigits) { - value = 0; - var charCodeOfZero = 48; // '0' - for (var i = 0, ii = strBuf.length; i < ii; i++) { - value = value * 10 + (strBuf[i].charCodeAt(0) - charCodeOfZero); - } - } else { - value = parseFloat(strBuf.join('')); - if (isNaN(value)) { - error('Invalid floating point number: ' + value); - } + + if (divideBy !== 0) { + baseValue /= divideBy; } - return value; + if (eNotation) { + baseValue *= Math.pow(10, powerValueSign * powerValue); + } + return sign * baseValue; }, getString: function Lexer_getString() { var numParen = 1; diff --git a/test/unit/parser_spec.js b/test/unit/parser_spec.js index 53aadb315..03d700a40 100644 --- a/test/unit/parser_spec.js +++ b/test/unit/parser_spec.js @@ -14,6 +14,30 @@ describe('parser', function() { expect(result).toEqual(11.234); }); + it('should parse PostScript numbers', function() { + var numbers = ['-.002', '34.5', '-3.62', '123.6e10', '1E-5', '-1.', '0.0', + '123', '-98', '43445', '0', '+17']; + for (var i=0, ii=numbers.length; i