Merge pull request #3424 from yurydelendik/lookChar

lookChar refactoring
This commit is contained in:
Brendan Dahl 2013-07-11 09:08:59 -07:00
commit 5dcc4cd1b4
8 changed files with 251 additions and 272 deletions

View File

@ -118,7 +118,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
getByte: function ChunkedStream_getByte() { getByte: function ChunkedStream_getByte() {
var pos = this.pos; var pos = this.pos;
if (pos >= this.end) { if (pos >= this.end) {
return null; return -1;
} }
this.ensureRange(pos, pos + 1); this.ensureRange(pos, pos + 1);
return this.bytes[this.pos++]; return this.bytes[this.pos++];
@ -156,22 +156,6 @@ var ChunkedStream = (function ChunkedStreamClosure() {
return this.bytes.subarray(begin, end); return this.bytes.subarray(begin, end);
}, },
lookChar: function ChunkedStream_lookChar() {
var pos = this.pos;
if (pos >= this.end)
return null;
this.ensureRange(pos, pos + 1);
return String.fromCharCode(this.bytes[pos]);
},
getChar: function ChunkedStream_getChar() {
var pos = this.pos;
if (pos >= this.end)
return null;
this.ensureRange(pos, pos + 1);
return String.fromCharCode(this.bytes[this.pos++]);
},
skip: function ChunkedStream_skip(n) { skip: function ChunkedStream_skip(n) {
if (!n) if (!n)
n = 1; n = 1;

View File

@ -313,7 +313,7 @@ var PDFDocument = (function PDFDocumentClosure() {
if (pos + limit > end) if (pos + limit > end)
limit = end - pos; limit = end - pos;
for (var n = 0; n < limit; ++n) for (var n = 0; n < limit; ++n)
str += stream.getChar(); str += String.fromCharCode(stream.getByte());
stream.pos = pos; stream.pos = pos;
var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle); var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
if (index == -1) if (index == -1)
@ -392,12 +392,12 @@ var PDFDocument = (function PDFDocumentClosure() {
stream.skip(9); stream.skip(9);
var ch; var ch;
do { do {
ch = stream.getChar(); ch = stream.getByte();
} while (Lexer.isSpace(ch)); } while (Lexer.isSpace(ch));
var str = ''; var str = '';
while ((ch - '0') <= 9) { while (ch >= 0x20 && ch <= 0x39) { // < '9'
str += ch; str += String.fromCharCode(ch);
ch = stream.getChar(); ch = stream.getByte();
} }
startXRef = parseInt(str, 10); startXRef = parseInt(str, 10);
if (isNaN(startXRef)) if (isNaN(startXRef))
@ -426,11 +426,11 @@ var PDFDocument = (function PDFDocumentClosure() {
// Reading file format version // Reading file format version
var MAX_VERSION_LENGTH = 12; var MAX_VERSION_LENGTH = 12;
var version = '', ch; var version = '', ch;
while ((ch = stream.getChar()) > ' ') { while ((ch = stream.getByte()) > 0x20) { // SPACE
if (version.length >= MAX_VERSION_LENGTH) { if (version.length >= MAX_VERSION_LENGTH) {
break; break;
} }
version += ch; version += String.fromCharCode(ch);
} }
// removing "%PDF-"-prefix // removing "%PDF-"-prefix
this.pdfFormatVersion = version.substring(5); this.pdfFormatVersion = version.substring(5);

View File

@ -3562,8 +3562,9 @@ var Font = (function FontClosure() {
while (font.pos < end) { while (font.pos < end) {
var stringLength = font.getByte(); var stringLength = font.getByte();
var string = ''; var string = '';
for (var i = 0; i < stringLength; ++i) for (var i = 0; i < stringLength; ++i) {
string += font.getChar(); string += String.fromCharCode(font.getByte());
}
customNames.push(string); customNames.push(string);
} }
glyphNames = []; glyphNames = [];
@ -5170,10 +5171,10 @@ var Type1Parser = (function Type1ParserClosure() {
} }
function isSpecial(c) { function isSpecial(c) {
return c === '/' || return c === 0x2F || // '/'
c === '[' || c === ']' || c === 0x5B || c === 0x5D || // '[', ']'
c === '{' || c === '}' || c === 0x7B || c === 0x7D || // '{', '}'
c === '(' || c === ')'; c === 0x28 || c === 0x29; // '(', ')'
} }
function Type1Parser(stream, encrypted) { function Type1Parser(stream, encrypted) {
@ -5181,6 +5182,7 @@ var Type1Parser = (function Type1ParserClosure() {
stream = new Stream(decrypt(stream.getBytes(), EEXEC_ENCRYPT_KEY, 4)); stream = new Stream(decrypt(stream.getBytes(), EEXEC_ENCRYPT_KEY, 4));
} }
this.stream = stream; this.stream = stream;
this.nextChar();
} }
Type1Parser.prototype = { Type1Parser.prototype = {
@ -5216,36 +5218,39 @@ var Type1Parser = (function Type1ParserClosure() {
return token === 'true' ? 1 : 0; return token === 'true' ? 1 : 0;
}, },
nextChar : function Type1_nextChar() {
return (this.currentChar = this.stream.getByte());
},
getToken: function Type1Parser_getToken() { getToken: function Type1Parser_getToken() {
// Eat whitespace and comments. // Eat whitespace and comments.
var comment = false; var comment = false;
var ch; var ch = this.currentChar;
var stream = this.stream;
while (true) { while (true) {
if ((ch = stream.lookChar()) === null) if (ch === -1) {
return null; return null;
}
if (comment) { if (comment) {
if (ch === '\x0a' || ch === '\x0d') { if (ch === 0x0A || ch === 0x0D) {
comment = false; comment = false;
} }
} else if (ch === '%') { } else if (ch === 0x25) { // '%'
comment = true; comment = true;
} else if (!Lexer.isSpace(ch)) { } else if (!Lexer.isSpace(ch)) {
break; break;
} }
stream.skip(); ch = this.nextChar();
} }
if (isSpecial(ch)) { if (isSpecial(ch)) {
stream.skip(); this.nextChar();
return ch; return String.fromCharCode(ch);
} }
var token = ''; var token = '';
do { do {
token += ch; token += String.fromCharCode(ch);
stream.skip(); ch = this.nextChar();
ch = stream.lookChar(); } while (ch >= 0 && !Lexer.isSpace(ch) && !isSpecial(ch));
} while (ch !== null && !Lexer.isSpace(ch) && !isSpecial(ch));
return token; return token;
}, },
@ -5292,12 +5297,13 @@ var Type1Parser = (function Type1ParserClosure() {
var glyph = this.getToken(); var glyph = this.getToken();
var length = this.readInt(); var length = this.readInt();
this.getToken(); // read in 'RD' or '-|' this.getToken(); // read in 'RD' or '-|'
var data = stream.makeSubStream(stream.pos + 1, length); var data = stream.makeSubStream(stream.pos, length);
var lenIV = program.properties.privateData['lenIV']; var lenIV = program.properties.privateData['lenIV'];
var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY,
lenIV); lenIV);
// Skip past the required space and binary data. // Skip past the required space and binary data.
stream.skip(1 + length); stream.skip(length);
this.nextChar();
token = this.getToken(); // read in 'ND' or '|-' token = this.getToken(); // read in 'ND' or '|-'
if (token === 'noaccess') { if (token === 'noaccess') {
this.getToken(); // read in 'def' this.getToken(); // read in 'def'
@ -5315,12 +5321,13 @@ var Type1Parser = (function Type1ParserClosure() {
var index = this.readInt(); var index = this.readInt();
var length = this.readInt(); var length = this.readInt();
this.getToken(); // read in 'RD' or '-|' this.getToken(); // read in 'RD' or '-|'
var data = stream.makeSubStream(stream.pos + 1, length); var data = stream.makeSubStream(stream.pos, length);
var lenIV = program.properties.privateData['lenIV']; var lenIV = program.properties.privateData['lenIV'];
var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY,
lenIV); lenIV);
// Skip past the required space and binary data. // Skip past the required space and binary data.
stream.skip(1 + length); stream.skip(length);
this.nextChar();
token = this.getToken(); // read in 'NP' or '|' token = this.getToken(); // read in 'NP' or '|'
if (token === 'noaccess') { if (token === 'noaccess') {
this.getToken(); // read in 'put' this.getToken(); // read in 'put'

View File

@ -816,53 +816,54 @@ var PostScriptToken = (function PostScriptTokenClosure() {
var PostScriptLexer = (function PostScriptLexerClosure() { var PostScriptLexer = (function PostScriptLexerClosure() {
function PostScriptLexer(stream) { function PostScriptLexer(stream) {
this.stream = stream; this.stream = stream;
this.nextChar();
} }
PostScriptLexer.prototype = { PostScriptLexer.prototype = {
nextChar: function PostScriptLexer_nextChar() {
return (this.currentChar = this.stream.getByte());
},
getToken: function PostScriptLexer_getToken() { getToken: function PostScriptLexer_getToken() {
var s = ''; var s = '';
var ch;
var comment = false; var comment = false;
var stream = this.stream; var ch = this.currentChar;
// skip comments // skip comments
while (true) { while (true) {
if (!(ch = stream.getChar())) if (ch < 0) {
return EOF; return EOF;
}
if (comment) { if (comment) {
if (ch == '\x0a' || ch == '\x0d') if (ch === 0x0A || ch === 0x0D) {
comment = false; comment = false;
} else if (ch == '%') { }
} else if (ch == 0x25) { // '%'
comment = true; comment = true;
} else if (!Lexer.isSpace(ch)) { } else if (!Lexer.isSpace(ch)) {
break; break;
} }
ch = this.nextChar();
} }
switch (ch) { switch (ch | 0) {
case '0': case '1': case '2': case '3': case '4': case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
case '5': case '6': case '7': case '8': case '9': case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
case '+': case '-': case '.': case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
return new PostScriptToken(PostScriptTokenTypes.NUMBER, return new PostScriptToken(PostScriptTokenTypes.NUMBER,
this.getNumber(ch)); this.getNumber());
case '{': case 0x7B: // '{'
this.nextChar();
return PostScriptToken.LBRACE; return PostScriptToken.LBRACE;
case '}': case 0x7D: // '}'
this.nextChar();
return PostScriptToken.RBRACE; return PostScriptToken.RBRACE;
} }
// operator // operator
var str = ch.toLowerCase(); var str = String.fromCharCode(ch);
while (true) { while ((ch = this.nextChar()) >= 0 && // and 'A'-'Z', 'a'-'z'
ch = stream.lookChar(); ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) {
if (ch === null) str += String.fromCharCode(ch);
break;
ch = ch.toLowerCase();
if (ch >= 'a' && ch <= 'z')
str += ch;
else
break;
stream.skip();
} }
switch (str) { switch (str.toLowerCase()) {
case 'if': case 'if':
return PostScriptToken.IF; return PostScriptToken.IF;
case 'ifelse': case 'ifelse':
@ -871,16 +872,16 @@ var PostScriptLexer = (function PostScriptLexerClosure() {
return PostScriptToken.getOperator(str); return PostScriptToken.getOperator(str);
} }
}, },
getNumber: function PostScriptLexer_getNumber(ch) { getNumber: function PostScriptLexer_getNumber() {
var str = ch; var ch = this.currentChar;
var stream = this.stream; var str = String.fromCharCode(ch);
while (true) { while ((ch = this.nextChar()) >= 0) {
ch = stream.lookChar(); if ((ch >= 0x30 && ch <= 0x39) || // '0'-'9'
if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.') ch === 0x2D || ch === 0x2E) { // '-', '.'
str += ch; str += String.fromCharCode(ch);
else } else {
break; break;
stream.skip(); }
} }
var value = parseFloat(str); var value = parseFloat(str);
if (isNaN(value)) if (isNaN(value))

View File

@ -59,8 +59,6 @@ var Parser = (function ParserClosure() {
if (isCmd(this.buf2, 'ID')) { if (isCmd(this.buf2, 'ID')) {
this.buf1 = this.buf2; this.buf1 = this.buf2;
this.buf2 = null; this.buf2 = null;
// skip byte after ID
this.lexer.skip();
} else { } else {
this.buf1 = this.buf2; this.buf1 = this.buf2;
this.buf2 = this.lexer.getObj(); this.buf2 = this.lexer.getObj();
@ -155,9 +153,8 @@ var Parser = (function ParserClosure() {
// searching for the /EI\s/ // searching for the /EI\s/
var state = 0, ch, i, ii; var state = 0, ch, i, ii;
while (state != 4 && while (state != 4 && (ch = stream.getByte()) !== -1) {
(ch = stream.getByte()) !== null && ch !== undefined) { switch (ch | 0) {
switch (ch) {
case 0x20: case 0x20:
case 0x0D: case 0x0D:
case 0x0A: case 0x0A:
@ -165,7 +162,8 @@ var Parser = (function ParserClosure() {
var followingBytes = stream.peekBytes(5); var followingBytes = stream.peekBytes(5);
for (i = 0, ii = followingBytes.length; i < ii; i++) { for (i = 0, ii = followingBytes.length; i < ii; i++) {
ch = followingBytes[i]; ch = followingBytes[i];
if (ch !== 0x0A && ch != 0x0D && (ch < 0x20 || ch > 0x7F)) { if (ch !== 0x0A && ch !== 0x0D && (ch < 0x20 || ch > 0x7F)) {
// not a LF, CR, SPACE or any visible ASCII character
state = 0; state = 0;
break; // some binary stuff found, resetting the state break; // some binary stuff found, resetting the state
} }
@ -206,7 +204,7 @@ var Parser = (function ParserClosure() {
// get stream start position // get stream start position
lexer.skipToNextLine(); lexer.skipToNextLine();
var pos = stream.pos; var pos = stream.pos - 1;
// get length // get length
var length = this.fetchIfRef(dict.get('Length')); var length = this.fetchIfRef(dict.get('Length'));
@ -215,6 +213,8 @@ var Parser = (function ParserClosure() {
// skip over the stream data // skip over the stream data
stream.pos = pos + length; stream.pos = pos + length;
lexer.nextChar();
this.shift(); // '>>' this.shift(); // '>>'
this.shift(); // 'stream' this.shift(); // 'stream'
if (!isCmd(this.buf1, 'endstream')) { if (!isCmd(this.buf1, 'endstream')) {
@ -254,6 +254,8 @@ var Parser = (function ParserClosure() {
error('Missing endstream'); error('Missing endstream');
} }
length = skipped; length = skipped;
lexer.nextChar();
this.shift(); this.shift();
this.shift(); this.shift();
} }
@ -344,6 +346,8 @@ var Parser = (function ParserClosure() {
var Lexer = (function LexerClosure() { var Lexer = (function LexerClosure() {
function Lexer(stream, knownCommands) { function Lexer(stream, knownCommands) {
this.stream = stream; this.stream = stream;
this.nextChar();
// The PDFs might have "glued" commands with other commands, operands or // The PDFs might have "glued" commands with other commands, operands or
// literals, e.g. "q1". The knownCommands is a dictionary of the valid // literals, e.g. "q1". The knownCommands is a dictionary of the valid
// commands and their prefixes. The prefixes are built the following way: // commands and their prefixes. The prefixes are built the following way:
@ -355,7 +359,8 @@ var Lexer = (function LexerClosure() {
} }
Lexer.isSpace = function Lexer_isSpace(ch) { Lexer.isSpace = function Lexer_isSpace(ch) {
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a'; // space is one of the following characters: SPACE, TAB, CR, or LF
return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
}; };
// A '1' in this array means the character is white space. A '1' or // A '1' in this array means the character is white space. A '1' or
@ -380,36 +385,40 @@ var Lexer = (function LexerClosure() {
]; ];
function toHexDigit(ch) { function toHexDigit(ch) {
if (ch >= '0' && ch <= '9') if (ch >= 0x30 && ch <= 0x39) { // '0'-'9'
return ch.charCodeAt(0) - 48; return ch & 0x0F;
ch = ch.toUpperCase(); }
if (ch >= 'A' && ch <= 'F') if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) {
return ch.charCodeAt(0) - 55; // 'A'-'F', 'a'-'f'
return (ch & 0x0F) + 9;
}
return -1; return -1;
} }
Lexer.prototype = { Lexer.prototype = {
getNumber: function Lexer_getNumber(ch) { nextChar: function Lexer_nextChar() {
return (this.currentChar = this.stream.getByte());
},
getNumber: function Lexer_getNumber() {
var floating = false; var floating = false;
var str = ch; var ch = this.currentChar;
var stream = this.stream; var str = String.fromCharCode(ch);
while ((ch = stream.lookChar())) { while ((ch = this.nextChar()) >= 0) {
if (ch == '.' && !floating) { if (ch === 0x2E && !floating) { // '.'
str += ch; str += '.';
floating = true; floating = true;
} else if (ch == '-') { } else if (ch === 0x2D) { // '-'
// ignore minus signs in the middle of numbers to match // ignore minus signs in the middle of numbers to match
// Adobe's behavior // Adobe's behavior
warn('Badly formated number'); warn('Badly formated number');
} else if (ch >= '0' && ch <= '9') { } else if (ch >= 0x30 && ch <= 0x39) { // '0'-'9'
str += ch; str += String.fromCharCode(ch);
} else if (ch == 'e' || ch == 'E') { } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e'
floating = true; floating = true;
} else { } else {
// the last character doesn't belong to us // the last character doesn't belong to us
break; break;
} }
stream.skip();
} }
var value = parseFloat(str); var value = parseFloat(str);
if (isNaN(value)) if (isNaN(value))
@ -420,148 +429,150 @@ var Lexer = (function LexerClosure() {
var numParen = 1; var numParen = 1;
var done = false; var done = false;
var str = ''; var str = '';
var stream = this.stream;
var ch; var ch = this.nextChar();
do { while (true) {
ch = stream.getChar(); var charBuffered = false;
switch (ch) { switch (ch | 0) {
case null: case -1:
case undefined:
warn('Unterminated string'); warn('Unterminated string');
done = true; done = true;
break; break;
case '(': case 0x28: // '('
++numParen; ++numParen;
str += ch; str += '(';
break; break;
case ')': case 0x29: // ')'
if (--numParen === 0) { if (--numParen === 0) {
this.nextChar(); // consume strings ')'
done = true; done = true;
} else { } else {
str += ch; str += ')';
} }
break; break;
case '\\': case 0x5C: // '\\'
ch = stream.getChar(); ch = this.nextChar();
switch (ch) { switch (ch) {
case null: case -1:
case undefined:
warn('Unterminated string'); warn('Unterminated string');
done = true; done = true;
break; break;
case 'n': case 0x6E: // 'n'
str += '\n'; str += '\n';
break; break;
case 'r': case 0x72: // 'r'
str += '\r'; str += '\r';
break; break;
case 't': case 0x74: // 't'
str += '\t'; str += '\t';
break; break;
case 'b': case 0x62: // 'b'
str += '\b'; str += '\b';
break; break;
case 'f': case 0x66: // 'f'
str += '\f'; str += '\f';
break; break;
case '\\': case 0x5C: // '\'
case '(': case 0x28: // '('
case ')': case 0x29: // ')'
str += ch; str += String.fromCharCode(ch);
break; break;
case '0': case '1': case '2': case '3': case 0x30: case 0x31: case 0x32: case 0x33: // '0'-'3'
case '4': case '5': case '6': case '7': case 0x34: case 0x35: case 0x36: case 0x37: // '4'-'7'
var x = ch - '0'; var x = ch & 0x0F;
ch = stream.lookChar(); ch = this.nextChar();
if (ch >= '0' && ch <= '7') { charBuffered = true;
stream.skip(); if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
x = (x << 3) + (ch - '0'); x = (x << 3) + (ch & 0x0F);
ch = stream.lookChar(); ch = this.nextChar();
if (ch >= '0' && ch <= '7') { if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
stream.skip(); charBuffered = false;
x = (x << 3) + (ch - '0'); x = (x << 3) + (ch & 0x0F);
} }
} }
str += String.fromCharCode(x); str += String.fromCharCode(x);
break; break;
case '\r': case 0x0A: case 0x0D: // LF, CR
ch = stream.lookChar();
if (ch == '\n')
stream.skip();
break;
case '\n':
break; break;
default: default:
str += ch; str += String.fromCharCode(ch);
break; break;
} }
break; break;
default: default:
str += ch; str += String.fromCharCode(ch);
break; break;
} }
} while (!done); if (done) {
break;
}
if (!charBuffered) {
ch = this.nextChar();
}
}
return str; return str;
}, },
getName: function Lexer_getName(ch) { getName: function Lexer_getName() {
var str = ''; var str = '', ch;
var stream = this.stream; while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) { if (ch === 0x23) { // '#'
stream.skip(); ch = this.nextChar();
if (ch == '#') {
ch = stream.lookChar();
var x = toHexDigit(ch); var x = toHexDigit(ch);
if (x != -1) { if (x != -1) {
stream.skip(); var x2 = toHexDigit(this.nextChar());
var x2 = toHexDigit(stream.getChar());
if (x2 == -1) if (x2 == -1)
error('Illegal digit in hex char in name: ' + x2); error('Illegal digit in hex char in name: ' + x2);
str += String.fromCharCode((x << 4) | x2); str += String.fromCharCode((x << 4) | x2);
} else { } else {
str += '#'; str += '#';
str += ch; str += String.fromCharCode(ch);
} }
} else { } else {
str += ch; str += String.fromCharCode(ch);
} }
} }
if (str.length > 128) if (str.length > 128) {
error('Warning: name token is longer than allowed by the spec: ' + error('Warning: name token is longer than allowed by the spec: ' +
str.length); str.length);
}
return new Name(str); return new Name(str);
}, },
getHexString: function Lexer_getHexString(ch) { getHexString: function Lexer_getHexString() {
var str = ''; var str = '';
var stream = this.stream; var ch = this.currentChar;
var isFirstHex = true; var isFirstHex = true;
var firstDigit; var firstDigit;
var secondDigit; var secondDigit;
while (true) { while (true) {
ch = stream.getChar(); if (ch < 0) {
if (!ch) {
warn('Unterminated hex string'); warn('Unterminated hex string');
break; break;
} else if (ch === '>') { } else if (ch === 0x3E) { // '>'
this.nextChar();
break; break;
} else if (specialChars[ch.charCodeAt(0)] === 1) { } else if (specialChars[ch] === 1) {
ch = this.nextChar();
continue; continue;
} else { } else {
if (isFirstHex) { if (isFirstHex) {
firstDigit = toHexDigit(ch); firstDigit = toHexDigit(ch);
if (firstDigit === -1) { if (firstDigit === -1) {
warn('Ignoring invalid character "' + ch + '" in hex string'); warn('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue; continue;
} }
} else { } else {
secondDigit = toHexDigit(ch); secondDigit = toHexDigit(ch);
if (secondDigit === -1) { if (secondDigit === -1) {
warn('Ignoring invalid character "' + ch + '" in hex string'); warn('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue; continue;
} }
str += String.fromCharCode((firstDigit << 4) | secondDigit); str += String.fromCharCode((firstDigit << 4) | secondDigit);
} }
isFirstHex = !isFirstHex; isFirstHex = !isFirstHex;
ch = this.nextChar();
} }
} }
return str; return str;
@ -569,73 +580,81 @@ var Lexer = (function LexerClosure() {
getObj: function Lexer_getObj() { getObj: function Lexer_getObj() {
// skip whitespace and comments // skip whitespace and comments
var comment = false; var comment = false;
var stream = this.stream; var ch = this.currentChar;
var ch;
while (true) { while (true) {
if (!(ch = stream.getChar())) if (ch < 0) {
return EOF; return EOF;
}
if (comment) { if (comment) {
if (ch == '\r' || ch == '\n') if (ch === 0x0A || ch == 0x0D) // LF, CR
comment = false; comment = false;
} else if (ch == '%') { } else if (ch === 0x25) { // '%'
comment = true; comment = true;
} else if (specialChars[ch.charCodeAt(0)] != 1) { } else if (specialChars[ch] !== 1) {
break; break;
} }
ch = this.nextChar();
} }
// start reading token // start reading token
switch (ch) { switch (ch | 0) {
case '0': case '1': case '2': case '3': case '4': case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
case '5': case '6': case '7': case '8': case '9': case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
case '+': case '-': case '.': case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
return this.getNumber(ch); return this.getNumber();
case '(': case 0x28: // '('
return this.getString(); return this.getString();
case '/': case 0x2F: // '/'
return this.getName(ch); return this.getName();
// array punctuation // array punctuation
case '[': case 0x5B: // '['
case ']': this.nextChar();
return Cmd.get(ch); return Cmd.get('[');
case 0x5D: // ']'
this.nextChar();
return Cmd.get(']');
// hex string or dict punctuation // hex string or dict punctuation
case '<': case 0x3C: // '<'
ch = stream.lookChar(); ch = this.nextChar();
if (ch == '<') { if (ch === 0x3C) {
// dict punctuation // dict punctuation
stream.skip(); this.nextChar();
return Cmd.get('<<'); return Cmd.get('<<');
} }
return this.getHexString(ch); return this.getHexString();
// dict punctuation // dict punctuation
case '>': case 0x3E: // '>'
ch = stream.lookChar(); ch = this.nextChar();
if (ch == '>') { if (ch === 0x3E) {
stream.skip(); this.nextChar();
return Cmd.get('>>'); return Cmd.get('>>');
} }
return Cmd.get(ch); return Cmd.get('>');
case '{': case 0x7B: // '{'
case '}': this.nextChar();
return Cmd.get(ch); return Cmd.get('{');
// fall through case 0x7D: // '}'
case ')': this.nextChar();
return Cmd.get('}');
case 0x29: // ')'
error('Illegal character: ' + ch); error('Illegal character: ' + ch);
break;
} }
// command // command
var str = ch; var str = String.fromCharCode(ch);
var knownCommands = this.knownCommands; var knownCommands = this.knownCommands;
var knownCommandFound = knownCommands && (str in knownCommands); var knownCommandFound = knownCommands && (str in knownCommands);
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) { while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
// stop if known command is found and next character does not make // stop if known command is found and next character does not make
// the str a command // the str a command
if (knownCommandFound && !((str + ch) in knownCommands)) var possibleCommand = str + String.fromCharCode(ch);
if (knownCommandFound && !(possibleCommand in knownCommands)) {
break; break;
stream.skip(); }
if (str.length == 128) if (str.length == 128)
error('Command token too long: ' + str.length); error('Command token too long: ' + str.length);
str += ch; str = possibleCommand;
knownCommandFound = knownCommands && (str in knownCommands); knownCommandFound = knownCommands && (str in knownCommands);
} }
if (str == 'true') if (str == 'true')
@ -648,19 +667,20 @@ var Lexer = (function LexerClosure() {
}, },
skipToNextLine: function Lexer_skipToNextLine() { skipToNextLine: function Lexer_skipToNextLine() {
var stream = this.stream; var stream = this.stream;
while (true) { var ch = this.currentChar;
var ch = stream.getChar(); while (ch >= 0) {
if (!ch || ch == '\n') if (ch === 0x0D) { // CR
return; ch = this.nextChar();
if (ch == '\r') { if (ch === 0x0A) { // LF
if ((ch = stream.lookChar()) == '\n') this.nextChar();
stream.skip(); }
return; break;
} else if (ch === 0x0A) { // LF
this.nextChar();
break;
} }
ch = this.nextChar();
} }
},
skip: function Lexer_skip() {
this.stream.skip();
} }
}; };

View File

@ -37,7 +37,7 @@ var Stream = (function StreamClosure() {
}, },
getByte: function Stream_getByte() { getByte: function Stream_getByte() {
if (this.pos >= this.end) if (this.pos >= this.end)
return null; return -1;
return this.bytes[this.pos++]; return this.bytes[this.pos++];
}, },
// returns subarray of original buffer // returns subarray of original buffer
@ -62,16 +62,6 @@ var Stream = (function StreamClosure() {
this.pos -= bytes.length; this.pos -= bytes.length;
return bytes; return bytes;
}, },
lookChar: function Stream_lookChar() {
if (this.pos >= this.end)
return null;
return String.fromCharCode(this.bytes[this.pos]);
},
getChar: function Stream_getChar() {
if (this.pos >= this.end)
return null;
return String.fromCharCode(this.bytes[this.pos++]);
},
skip: function Stream_skip(n) { skip: function Stream_skip(n) {
if (!n) if (!n)
n = 1; n = 1;
@ -133,7 +123,7 @@ var DecodeStream = (function DecodeStreamClosure() {
var pos = this.pos; var pos = this.pos;
while (this.bufferLength <= pos) { while (this.bufferLength <= pos) {
if (this.eof) if (this.eof)
return null; return -1;
this.readBlock(); this.readBlock();
} }
return this.buffer[this.pos++]; return this.buffer[this.pos++];
@ -171,31 +161,13 @@ var DecodeStream = (function DecodeStreamClosure() {
this.pos -= bytes.length; this.pos -= bytes.length;
return bytes; return bytes;
}, },
lookChar: function DecodeStream_lookChar() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return null;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos]);
},
getChar: function DecodeStream_getChar() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return null;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos++]);
},
makeSubStream: function DecodeStream_makeSubStream(start, length, dict) { makeSubStream: function DecodeStream_makeSubStream(start, length, dict) {
var end = start + length; var end = start + length;
while (this.bufferLength <= end && !this.eof) while (this.bufferLength <= end && !this.eof)
this.readBlock(); this.readBlock();
return new Stream(this.buffer, start, length, dict); return new Stream(this.buffer, start, length, dict);
}, },
skip: function DecodeStream_skip(n) { skip: function Stream_skip(n) {
if (!n) if (!n)
n = 1; n = 1;
this.pos += n; this.pos += n;
@ -901,9 +873,6 @@ var JpegStream = (function JpegStreamClosure() {
JpegStream.prototype.getIR = function JpegStream_getIR() { JpegStream.prototype.getIR = function JpegStream_getIR() {
return bytesToString(this.bytes); return bytesToString(this.bytes);
}; };
JpegStream.prototype.getChar = function JpegStream_getChar() {
error('internal error: getChar is not valid on JpegStream');
};
/** /**
* Checks if the image can be decoded and displayed by the browser without any * Checks if the image can be decoded and displayed by the browser without any
* further processing such as color space conversions. * further processing such as color space conversions.
@ -1031,9 +1000,6 @@ var JpxStream = (function JpxStreamClosure() {
this.bufferLength = data.length; this.bufferLength = data.length;
this.eof = true; this.eof = true;
}; };
JpxStream.prototype.getChar = function JpxStream_getChar() {
error('internal error: getChar is not valid on JpxStream');
};
return JpxStream; return JpxStream;
})(); })();
@ -1076,9 +1042,6 @@ var Jbig2Stream = (function Jbig2StreamClosure() {
this.bufferLength = dataLength; this.bufferLength = dataLength;
this.eof = true; this.eof = true;
}; };
Jbig2Stream.prototype.getChar = function Jbig2Stream_getChar() {
error('internal error: getChar is not valid on Jbig2Stream');
};
return Jbig2Stream; return Jbig2Stream;
})(); })();
@ -1139,15 +1102,18 @@ var Ascii85Stream = (function Ascii85StreamClosure() {
Ascii85Stream.prototype = Object.create(DecodeStream.prototype); Ascii85Stream.prototype = Object.create(DecodeStream.prototype);
Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() { Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() {
var tildaCode = '~'.charCodeAt(0); var TILDA_CHAR = 0x7E; // '~'
var zCode = 'z'.charCodeAt(0); var Z_LOWER_CHAR = 0x7A; // 'z'
var EOF = -1;
var str = this.str; var str = this.str;
var c = str.getByte(); var c = str.getByte();
while (Lexer.isSpace(String.fromCharCode(c))) while (Lexer.isSpace(c)) {
c = str.getByte(); c = str.getByte();
}
if (!c || c === tildaCode) { if (c === EOF || c === TILDA_CHAR) {
this.eof = true; this.eof = true;
return; return;
} }
@ -1155,7 +1121,7 @@ var Ascii85Stream = (function Ascii85StreamClosure() {
var bufferLength = this.bufferLength, buffer; var bufferLength = this.bufferLength, buffer;
// special code for z // special code for z
if (c == zCode) { if (c == Z_LOWER_CHAR) {
buffer = this.ensureBuffer(bufferLength + 4); buffer = this.ensureBuffer(bufferLength + 4);
for (var i = 0; i < 4; ++i) for (var i = 0; i < 4; ++i)
buffer[bufferLength + i] = 0; buffer[bufferLength + i] = 0;
@ -1165,12 +1131,13 @@ var Ascii85Stream = (function Ascii85StreamClosure() {
input[0] = c; input[0] = c;
for (var i = 1; i < 5; ++i) { for (var i = 1; i < 5; ++i) {
c = str.getByte(); c = str.getByte();
while (Lexer.isSpace(String.fromCharCode(c))) while (Lexer.isSpace(c)) {
c = str.getByte(); c = str.getByte();
}
input[i] = c; input[i] = c;
if (!c || c == tildaCode) if (c === EOF || c == TILDA_CHAR)
break; break;
} }
buffer = this.ensureBuffer(bufferLength + i - 1); buffer = this.ensureBuffer(bufferLength + i - 1);
@ -2258,7 +2225,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) { CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) {
var c; var c;
while (this.inputBits < n) { while (this.inputBits < n) {
if ((c = this.str.getByte()) === null || c === undefined) { if ((c = this.str.getByte()) === -1) {
if (this.inputBits === 0) if (this.inputBits === 0)
return EOF; return EOF;
return ((this.inputBuf << (n - this.inputBits)) & return ((this.inputBuf << (n - this.inputBits)) &
@ -2312,7 +2279,7 @@ var LZWStream = (function LZWStreamClosure() {
var cachedData = this.cachedData; var cachedData = this.cachedData;
while (bitsCached < n) { while (bitsCached < n) {
var c = this.str.getByte(); var c = this.str.getByte();
if (c === null || c === undefined) { if (c === -1) {
this.eof = true; this.eof = true;
return null; return null;
} }

View File

@ -623,7 +623,7 @@ function isArray(v) {
function isStream(v) { function isStream(v) {
return typeof v == 'object' && v !== null && v !== undefined && return typeof v == 'object' && v !== null && v !== undefined &&
('getChar' in v); ('getBytes' in v);
} }
function isArrayBuffer(v) { function isArrayBuffer(v) {

View File

@ -7,20 +7,20 @@
describe('parser', function() { describe('parser', function() {
describe('Lexer', function() { describe('Lexer', function() {
it('should stop parsing numbers at the end of stream', function() { it('should stop parsing numbers at the end of stream', function() {
var input = new StringStream('1.234'); var input = new StringStream('11.234');
var lexer = new Lexer(input); var lexer = new Lexer(input);
var result = lexer.getNumber('1'); var result = lexer.getNumber();
expect(result).toEqual(11.234); expect(result).toEqual(11.234);
}); });
it('should stop parsing strings at the end of stream', function() { it('should stop parsing strings at the end of stream', function() {
var input = new StringStream('1$4)'); var input = new StringStream('(1$4)');
input.getChar = function(super_getChar) { input.getByte = function(super_getByte) {
// simulating end of file using null (see issue 2766) // simulating end of file using null (see issue 2766)
var ch = super_getChar.call(input); var ch = super_getByte.call(input);
return ch == '$' ? null : ch; return ch === 0x24 /* '$' */ ? -1 : ch;
}.bind(input, input.getChar); }.bind(input, input.getByte);
var lexer = new Lexer(input); var lexer = new Lexer(input);
var result = lexer.getString(); var result = lexer.getString();
@ -31,9 +31,9 @@ describe('parser', function() {
// '8 0 2 15 5 2 2 2 4 3 2 4' // '8 0 2 15 5 2 2 2 4 3 2 4'
// should be parsed as // should be parsed as
// '80 21 55 22 24 32' // '80 21 55 22 24 32'
var input = new StringStream('7 0 2 15 5 2 2 2 4 3 2 4>'); var input = new StringStream('<7 0 2 15 5 2 2 2 4 3 2 4>');
var lexer = new Lexer(input); var lexer = new Lexer(input);
var result = lexer.getHexString('<'); var result = lexer.getHexString();
expect(result).toEqual('p!U"$2'); expect(result).toEqual('p!U"$2');
}); });