Generic build of PDF.js library.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

964 lines
31 KiB

/* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Parser = exports.Linearization = exports.Lexer = undefined;
var _stream = require('./stream');
var _util = require('../shared/util');
var _primitives = require('./primitives');
var MAX_LENGTH_TO_CACHE = 1000;
var Parser = function ParserClosure() {
function Parser(lexer, allowStreams, xref, recoveryMode) {
this.lexer = lexer;
this.allowStreams = allowStreams;
this.xref = xref;
this.recoveryMode = recoveryMode || false;
this.imageCache = Object.create(null);
this.refill();
}
Parser.prototype = {
refill: function Parser_refill() {
this.buf1 = this.lexer.getObj();
this.buf2 = this.lexer.getObj();
},
shift: function Parser_shift() {
if ((0, _primitives.isCmd)(this.buf2, 'ID')) {
this.buf1 = this.buf2;
this.buf2 = null;
} else {
this.buf1 = this.buf2;
this.buf2 = this.lexer.getObj();
}
},
tryShift: function Parser_tryShift() {
try {
this.shift();
return true;
} catch (e) {
if (e instanceof _util.MissingDataException) {
throw e;
}
return false;
}
},
getObj: function Parser_getObj(cipherTransform) {
var buf1 = this.buf1;
this.shift();
if (buf1 instanceof _primitives.Cmd) {
switch (buf1.cmd) {
case 'BI':
return this.makeInlineImage(cipherTransform);
case '[':
var array = [];
while (!(0, _primitives.isCmd)(this.buf1, ']') && !(0, _primitives.isEOF)(this.buf1)) {
array.push(this.getObj(cipherTransform));
}
if ((0, _primitives.isEOF)(this.buf1)) {
if (!this.recoveryMode) {
(0, _util.error)('End of file inside array');
}
return array;
}
this.shift();
return array;
case '<<':
var dict = new _primitives.Dict(this.xref);
while (!(0, _primitives.isCmd)(this.buf1, '>>') && !(0, _primitives.isEOF)(this.buf1)) {
if (!(0, _primitives.isName)(this.buf1)) {
(0, _util.info)('Malformed dictionary: key must be a name object');
this.shift();
continue;
}
var key = this.buf1.name;
this.shift();
if ((0, _primitives.isEOF)(this.buf1)) {
break;
}
dict.set(key, this.getObj(cipherTransform));
}
if ((0, _primitives.isEOF)(this.buf1)) {
if (!this.recoveryMode) {
(0, _util.error)('End of file inside dictionary');
}
return dict;
}
if ((0, _primitives.isCmd)(this.buf2, 'stream')) {
return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict;
}
this.shift();
return dict;
default:
return buf1;
}
}
if ((0, _util.isInt)(buf1)) {
var num = buf1;
if ((0, _util.isInt)(this.buf1) && (0, _primitives.isCmd)(this.buf2, 'R')) {
var ref = new _primitives.Ref(num, this.buf1);
this.shift();
this.shift();
return ref;
}
return num;
}
if ((0, _util.isString)(buf1)) {
var str = buf1;
if (cipherTransform) {
str = cipherTransform.decryptString(str);
}
return str;
}
return buf1;
},
findDefaultInlineStreamEnd: function Parser_findDefaultInlineStreamEnd(stream) {
var E = 0x45,
I = 0x49,
SPACE = 0x20,
LF = 0xA,
CR = 0xD;
var startPos = stream.pos,
state = 0,
ch,
i,
n,
followingBytes;
while ((ch = stream.getByte()) !== -1) {
if (state === 0) {
state = ch === E ? 1 : 0;
} else if (state === 1) {
state = ch === I ? 2 : 0;
} else {
(0, _util.assert)(state === 2);
if (ch === SPACE || ch === LF || ch === CR) {
n = 5;
followingBytes = stream.peekBytes(n);
for (i = 0; i < n; i++) {
ch = followingBytes[i];
if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7F)) {
state = 0;
break;
}
}
if (state === 2) {
break;
}
} else {
state = 0;
}
}
}
return stream.pos - 4 - startPos;
},
findDCTDecodeInlineStreamEnd: function Parser_findDCTDecodeInlineStreamEnd(stream) {
var startPos = stream.pos,
foundEOI = false,
b,
markerLength,
length;
while ((b = stream.getByte()) !== -1) {
if (b !== 0xFF) {
continue;
}
switch (stream.getByte()) {
case 0x00:
break;
case 0xFF:
stream.skip(-1);
break;
case 0xD9:
foundEOI = true;
break;
case 0xC0:
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC5:
case 0xC6:
case 0xC7:
case 0xC9:
case 0xCA:
case 0xCB:
case 0xCD:
case 0xCE:
case 0xCF:
case 0xC4:
case 0xCC:
case 0xDA:
case 0xDB:
case 0xDC:
case 0xDD:
case 0xDE:
case 0xDF:
case 0xE0:
case 0xE1:
case 0xE2:
case 0xE3:
case 0xE4:
case 0xE5:
case 0xE6:
case 0xE7:
case 0xE8:
case 0xE9:
case 0xEA:
case 0xEB:
case 0xEC:
case 0xED:
case 0xEE:
case 0xEF:
case 0xFE:
markerLength = stream.getUint16();
if (markerLength > 2) {
stream.skip(markerLength - 2);
} else {
stream.skip(-2);
}
break;
}
if (foundEOI) {
break;
}
}
length = stream.pos - startPos;
if (b === -1) {
(0, _util.warn)('Inline DCTDecode image stream: ' + 'EOI marker not found, searching for /EI/ instead.');
stream.skip(-length);
return this.findDefaultInlineStreamEnd(stream);
}
this.inlineStreamSkipEI(stream);
return length;
},
findASCII85DecodeInlineStreamEnd: function Parser_findASCII85DecodeInlineStreamEnd(stream) {
var TILDE = 0x7E,
GT = 0x3E;
var startPos = stream.pos,
ch,
length;
while ((ch = stream.getByte()) !== -1) {
if (ch === TILDE && stream.peekByte() === GT) {
stream.skip();
break;
}
}
length = stream.pos - startPos;
if (ch === -1) {
(0, _util.warn)('Inline ASCII85Decode image stream: ' + 'EOD marker not found, searching for /EI/ instead.');
stream.skip(-length);
return this.findDefaultInlineStreamEnd(stream);
}
this.inlineStreamSkipEI(stream);
return length;
},
findASCIIHexDecodeInlineStreamEnd: function Parser_findASCIIHexDecodeInlineStreamEnd(stream) {
var GT = 0x3E;
var startPos = stream.pos,
ch,
length;
while ((ch = stream.getByte()) !== -1) {
if (ch === GT) {
break;
}
}
length = stream.pos - startPos;
if (ch === -1) {
(0, _util.warn)('Inline ASCIIHexDecode image stream: ' + 'EOD marker not found, searching for /EI/ instead.');
stream.skip(-length);
return this.findDefaultInlineStreamEnd(stream);
}
this.inlineStreamSkipEI(stream);
return length;
},
inlineStreamSkipEI: function Parser_inlineStreamSkipEI(stream) {
var E = 0x45,
I = 0x49;
var state = 0,
ch;
while ((ch = stream.getByte()) !== -1) {
if (state === 0) {
state = ch === E ? 1 : 0;
} else if (state === 1) {
state = ch === I ? 2 : 0;
} else if (state === 2) {
break;
}
}
},
makeInlineImage: function Parser_makeInlineImage(cipherTransform) {
var lexer = this.lexer;
var stream = lexer.stream;
var dict = new _primitives.Dict(this.xref);
while (!(0, _primitives.isCmd)(this.buf1, 'ID') && !(0, _primitives.isEOF)(this.buf1)) {
if (!(0, _primitives.isName)(this.buf1)) {
(0, _util.error)('Dictionary key must be a name object');
}
var key = this.buf1.name;
this.shift();
if ((0, _primitives.isEOF)(this.buf1)) {
break;
}
dict.set(key, this.getObj(cipherTransform));
}
var filter = dict.get('Filter', 'F'),
filterName;
if ((0, _primitives.isName)(filter)) {
filterName = filter.name;
} else if ((0, _util.isArray)(filter)) {
var filterZero = this.xref.fetchIfRef(filter[0]);
if ((0, _primitives.isName)(filterZero)) {
filterName = filterZero.name;
}
}
var startPos = stream.pos,
length,
i,
ii;
if (filterName === 'DCTDecode' || filterName === 'DCT') {
length = this.findDCTDecodeInlineStreamEnd(stream);
} else if (filterName === 'ASCII85Decode' || filterName === 'A85') {
length = this.findASCII85DecodeInlineStreamEnd(stream);
} else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') {
length = this.findASCIIHexDecodeInlineStreamEnd(stream);
} else {
length = this.findDefaultInlineStreamEnd(stream);
}
var imageStream = stream.makeSubStream(startPos, length, dict);
var adler32;
if (length < MAX_LENGTH_TO_CACHE) {
var imageBytes = imageStream.getBytes();
imageStream.reset();
var a = 1;
var b = 0;
for (i = 0, ii = imageBytes.length; i < ii; ++i) {
a += imageBytes[i] & 0xff;
b += a;
}
adler32 = b % 65521 << 16 | a % 65521;
if (this.imageCache.adler32 === adler32) {
this.buf2 = _primitives.Cmd.get('EI');
this.shift();
this.imageCache[adler32].reset();
return this.imageCache[adler32];
}
}
if (cipherTransform) {
imageStream = cipherTransform.createStream(imageStream, length);
}
imageStream = this.filter(imageStream, dict, length);
imageStream.dict = dict;
if (adler32 !== undefined) {
imageStream.cacheKey = 'inline_' + length + '_' + adler32;
this.imageCache[adler32] = imageStream;
}
this.buf2 = _primitives.Cmd.get('EI');
this.shift();
return imageStream;
},
makeStream: function Parser_makeStream(dict, cipherTransform) {
var lexer = this.lexer;
var stream = lexer.stream;
lexer.skipToNextLine();
var pos = stream.pos - 1;
var length = dict.get('Length');
if (!(0, _util.isInt)(length)) {
(0, _util.info)('Bad ' + length + ' attribute in stream');
length = 0;
}
stream.pos = pos + length;
lexer.nextChar();
if (this.tryShift() && (0, _primitives.isCmd)(this.buf2, 'endstream')) {
this.shift();
} else {
stream.pos = pos;
var SCAN_BLOCK_SIZE = 2048;
var ENDSTREAM_SIGNATURE_LENGTH = 9;
var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D];
var skipped = 0,
found = false,
i,
j;
while (stream.pos < stream.end) {
var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE);
var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH;
if (scanLength <= 0) {
break;
}
found = false;
i = 0;
while (i < scanLength) {
j = 0;
while (j < ENDSTREAM_SIGNATURE_LENGTH && scanBytes[i + j] === ENDSTREAM_SIGNATURE[j]) {
j++;
}
if (j >= ENDSTREAM_SIGNATURE_LENGTH) {
found = true;
break;
}
i++;
}
if (found) {
skipped += i;
stream.pos += i;
break;
}
skipped += scanLength;
stream.pos += scanLength;
}
if (!found) {
(0, _util.error)('Missing endstream');
}
length = skipped;
lexer.nextChar();
this.shift();
this.shift();
}
this.shift();
stream = stream.makeSubStream(pos, length, dict);
if (cipherTransform) {
stream = cipherTransform.createStream(stream, length);
}
stream = this.filter(stream, dict, length);
stream.dict = dict;
return stream;
},
filter: function Parser_filter(stream, dict, length) {
var filter = dict.get('Filter', 'F');
var params = dict.get('DecodeParms', 'DP');
if ((0, _primitives.isName)(filter)) {
if ((0, _util.isArray)(params)) {
params = this.xref.fetchIfRef(params[0]);
}
return this.makeFilter(stream, filter.name, length, params);
}
var maybeLength = length;
if ((0, _util.isArray)(filter)) {
var filterArray = filter;
var paramsArray = params;
for (var i = 0, ii = filterArray.length; i < ii; ++i) {
filter = this.xref.fetchIfRef(filterArray[i]);
if (!(0, _primitives.isName)(filter)) {
(0, _util.error)('Bad filter name: ' + filter);
}
params = null;
if ((0, _util.isArray)(paramsArray) && i in paramsArray) {
params = this.xref.fetchIfRef(paramsArray[i]);
}
stream = this.makeFilter(stream, filter.name, maybeLength, params);
maybeLength = null;
}
}
return stream;
},
makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) {
if (maybeLength === 0) {
(0, _util.warn)('Empty "' + name + '" stream.');
return new _stream.NullStream(stream);
}
try {
var xrefStreamStats = this.xref.stats.streamTypes;
if (name === 'FlateDecode' || name === 'Fl') {
xrefStreamStats[_util.StreamType.FLATE] = true;
if (params) {
return new _stream.PredictorStream(new _stream.FlateStream(stream, maybeLength), maybeLength, params);
}
return new _stream.FlateStream(stream, maybeLength);
}
if (name === 'LZWDecode' || name === 'LZW') {
xrefStreamStats[_util.StreamType.LZW] = true;
var earlyChange = 1;
if (params) {
if (params.has('EarlyChange')) {
earlyChange = params.get('EarlyChange');
}
return new _stream.PredictorStream(new _stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
}
return new _stream.LZWStream(stream, maybeLength, earlyChange);
}
if (name === 'DCTDecode' || name === 'DCT') {
xrefStreamStats[_util.StreamType.DCT] = true;
return new _stream.JpegStream(stream, maybeLength, stream.dict, params);
}
if (name === 'JPXDecode' || name === 'JPX') {
xrefStreamStats[_util.StreamType.JPX] = true;
return new _stream.JpxStream(stream, maybeLength, stream.dict, params);
}
if (name === 'ASCII85Decode' || name === 'A85') {
xrefStreamStats[_util.StreamType.A85] = true;
return new _stream.Ascii85Stream(stream, maybeLength);
}
if (name === 'ASCIIHexDecode' || name === 'AHx') {
xrefStreamStats[_util.StreamType.AHX] = true;
return new _stream.AsciiHexStream(stream, maybeLength);
}
if (name === 'CCITTFaxDecode' || name === 'CCF') {
xrefStreamStats[_util.StreamType.CCF] = true;
return new _stream.CCITTFaxStream(stream, maybeLength, params);
}
if (name === 'RunLengthDecode' || name === 'RL') {
xrefStreamStats[_util.StreamType.RL] = true;
return new _stream.RunLengthStream(stream, maybeLength);
}
if (name === 'JBIG2Decode') {
xrefStreamStats[_util.StreamType.JBIG] = true;
return new _stream.Jbig2Stream(stream, maybeLength, stream.dict, params);
}
(0, _util.warn)('filter "' + name + '" not supported yet');
return stream;
} catch (ex) {
if (ex instanceof _util.MissingDataException) {
throw ex;
}
(0, _util.warn)('Invalid stream: \"' + ex + '\"');
return new _stream.NullStream(stream);
}
}
};
return Parser;
}();
var Lexer = function LexerClosure() {
function Lexer(stream, knownCommands) {
this.stream = stream;
this.nextChar();
this.strBuf = [];
this.knownCommands = knownCommands;
}
var specialChars = [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
function toHexDigit(ch) {
if (ch >= 0x30 && ch <= 0x39) {
return ch & 0x0F;
}
if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {
return (ch & 0x0F) + 9;
}
return -1;
}
Lexer.prototype = {
nextChar: function Lexer_nextChar() {
return this.currentChar = this.stream.getByte();
},
peekChar: function Lexer_peekChar() {
return this.stream.peekByte();
},
getNumber: function Lexer_getNumber() {
var ch = this.currentChar;
var eNotation = false;
var divideBy = 0;
var sign = 1;
if (ch === 0x2D) {
sign = -1;
ch = this.nextChar();
if (ch === 0x2D) {
ch = this.nextChar();
}
} else if (ch === 0x2B) {
ch = this.nextChar();
}
if (ch === 0x2E) {
divideBy = 10;
ch = this.nextChar();
}
if (ch === 0x0A || ch === 0x0D) {
do {
ch = this.nextChar();
} while (ch === 0x0A || ch === 0x0D);
}
if (ch < 0x30 || ch > 0x39) {
(0, _util.error)('Invalid number: ' + String.fromCharCode(ch) + ' (charCode ' + ch + ')');
}
var baseValue = ch - 0x30;
var powerValue = 0;
var powerValueSign = 1;
while ((ch = this.nextChar()) >= 0) {
if (0x30 <= ch && ch <= 0x39) {
var currentDigit = ch - 0x30;
if (eNotation) {
powerValue = powerValue * 10 + currentDigit;
} else {
if (divideBy !== 0) {
divideBy *= 10;
}
baseValue = baseValue * 10 + currentDigit;
}
} else if (ch === 0x2E) {
if (divideBy === 0) {
divideBy = 1;
} else {
break;
}
} else if (ch === 0x2D) {
(0, _util.warn)('Badly formatted number');
} else if (ch === 0x45 || ch === 0x65) {
ch = this.peekChar();
if (ch === 0x2B || ch === 0x2D) {
powerValueSign = ch === 0x2D ? -1 : 1;
this.nextChar();
} else if (ch < 0x30 || ch > 0x39) {
break;
}
eNotation = true;
} else {
break;
}
}
if (divideBy !== 0) {
baseValue /= divideBy;
}
if (eNotation) {
baseValue *= Math.pow(10, powerValueSign * powerValue);
}
return sign * baseValue;
},
getString: function Lexer_getString() {
var numParen = 1;
var done = false;
var strBuf = this.strBuf;
strBuf.length = 0;
var ch = this.nextChar();
while (true) {
var charBuffered = false;
switch (ch | 0) {
case -1:
(0, _util.warn)('Unterminated string');
done = true;
break;
case 0x28:
++numParen;
strBuf.push('(');
break;
case 0x29:
if (--numParen === 0) {
this.nextChar();
done = true;
} else {
strBuf.push(')');
}
break;
case 0x5C:
ch = this.nextChar();
switch (ch) {
case -1:
(0, _util.warn)('Unterminated string');
done = true;
break;
case 0x6E:
strBuf.push('\n');
break;
case 0x72:
strBuf.push('\r');
break;
case 0x74:
strBuf.push('\t');
break;
case 0x62:
strBuf.push('\b');
break;
case 0x66:
strBuf.push('\f');
break;
case 0x5C:
case 0x28:
case 0x29:
strBuf.push(String.fromCharCode(ch));
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
var x = ch & 0x0F;
ch = this.nextChar();
charBuffered = true;
if (ch >= 0x30 && ch <= 0x37) {
x = (x << 3) + (ch & 0x0F);
ch = this.nextChar();
if (ch >= 0x30 && ch <= 0x37) {
charBuffered = false;
x = (x << 3) + (ch & 0x0F);
}
}
strBuf.push(String.fromCharCode(x));
break;
case 0x0D:
if (this.peekChar() === 0x0A) {
this.nextChar();
}
break;
case 0x0A:
break;
default:
strBuf.push(String.fromCharCode(ch));
break;
}
break;
default:
strBuf.push(String.fromCharCode(ch));
break;
}
if (done) {
break;
}
if (!charBuffered) {
ch = this.nextChar();
}
}
return strBuf.join('');
},
getName: function Lexer_getName() {
var ch, previousCh;
var strBuf = this.strBuf;
strBuf.length = 0;
while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
if (ch === 0x23) {
ch = this.nextChar();
if (specialChars[ch]) {
(0, _util.warn)('Lexer_getName: ' + 'NUMBER SIGN (#) should be followed by a hexadecimal number.');
strBuf.push('#');
break;
}
var x = toHexDigit(ch);
if (x !== -1) {
previousCh = ch;
ch = this.nextChar();
var x2 = toHexDigit(ch);
if (x2 === -1) {
(0, _util.warn)('Lexer_getName: Illegal digit (' + String.fromCharCode(ch) + ') in hexadecimal number.');
strBuf.push('#', String.fromCharCode(previousCh));
if (specialChars[ch]) {
break;
}
strBuf.push(String.fromCharCode(ch));
continue;
}
strBuf.push(String.fromCharCode(x << 4 | x2));
} else {
strBuf.push('#', String.fromCharCode(ch));
}
} else {
strBuf.push(String.fromCharCode(ch));
}
}
if (strBuf.length > 127) {
(0, _util.warn)('name token is longer than allowed by the spec: ' + strBuf.length);
}
return _primitives.Name.get(strBuf.join(''));
},
getHexString: function Lexer_getHexString() {
var strBuf = this.strBuf;
strBuf.length = 0;
var ch = this.currentChar;
var isFirstHex = true;
var firstDigit;
var secondDigit;
while (true) {
if (ch < 0) {
(0, _util.warn)('Unterminated hex string');
break;
} else if (ch === 0x3E) {
this.nextChar();
break;
} else if (specialChars[ch] === 1) {
ch = this.nextChar();
continue;
} else {
if (isFirstHex) {
firstDigit = toHexDigit(ch);
if (firstDigit === -1) {
(0, _util.warn)('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue;
}
} else {
secondDigit = toHexDigit(ch);
if (secondDigit === -1) {
(0, _util.warn)('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue;
}
strBuf.push(String.fromCharCode(firstDigit << 4 | secondDigit));
}
isFirstHex = !isFirstHex;
ch = this.nextChar();
}
}
return strBuf.join('');
},
getObj: function Lexer_getObj() {
var comment = false;
var ch = this.currentChar;
while (true) {
if (ch < 0) {
return _primitives.EOF;
}
if (comment) {
if (ch === 0x0A || ch === 0x0D) {
comment = false;
}
} else if (ch === 0x25) {
comment = true;
} else if (specialChars[ch] !== 1) {
break;
}
ch = this.nextChar();
}
switch (ch | 0) {
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x2B:
case 0x2D:
case 0x2E:
return this.getNumber();
case 0x28:
return this.getString();
case 0x2F:
return this.getName();
case 0x5B:
this.nextChar();
return _primitives.Cmd.get('[');
case 0x5D:
this.nextChar();
return _primitives.Cmd.get(']');
case 0x3C:
ch = this.nextChar();
if (ch === 0x3C) {
this.nextChar();
return _primitives.Cmd.get('<<');
}
return this.getHexString();
case 0x3E:
ch = this.nextChar();
if (ch === 0x3E) {
this.nextChar();
return _primitives.Cmd.get('>>');
}
return _primitives.Cmd.get('>');
case 0x7B:
this.nextChar();
return _primitives.Cmd.get('{');
case 0x7D:
this.nextChar();
return _primitives.Cmd.get('}');
case 0x29:
this.nextChar();
(0, _util.error)('Illegal character: ' + ch);
break;
}
var str = String.fromCharCode(ch);
var knownCommands = this.knownCommands;
var knownCommandFound = knownCommands && knownCommands[str] !== undefined;
while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
var possibleCommand = str + String.fromCharCode(ch);
if (knownCommandFound && knownCommands[possibleCommand] === undefined) {
break;
}
if (str.length === 128) {
(0, _util.error)('Command token too long: ' + str.length);
}
str = possibleCommand;
knownCommandFound = knownCommands && knownCommands[str] !== undefined;
}
if (str === 'true') {
return true;
}
if (str === 'false') {
return false;
}
if (str === 'null') {
return null;
}
return _primitives.Cmd.get(str);
},
skipToNextLine: function Lexer_skipToNextLine() {
var ch = this.currentChar;
while (ch >= 0) {
if (ch === 0x0D) {
ch = this.nextChar();
if (ch === 0x0A) {
this.nextChar();
}
break;
} else if (ch === 0x0A) {
this.nextChar();
break;
}
ch = this.nextChar();
}
}
};
return Lexer;
}();
var Linearization = {
create: function LinearizationCreate(stream) {
function getInt(name, allowZeroValue) {
var obj = linDict.get(name);
if ((0, _util.isInt)(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {
return obj;
}
throw new Error('The "' + name + '" parameter in the linearization ' + 'dictionary is invalid.');
}
function getHints() {
var hints = linDict.get('H'),
hintsLength,
item;
if ((0, _util.isArray)(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) {
for (var index = 0; index < hintsLength; index++) {
if (!((0, _util.isInt)(item = hints[index]) && item > 0)) {
throw new Error('Hint (' + index + ') in the linearization dictionary is invalid.');
}
}
return hints;
}
throw new Error('Hint array in the linearization dictionary is invalid.');
}
var parser = new Parser(new Lexer(stream), false, null);
var obj1 = parser.getObj();
var obj2 = parser.getObj();
var obj3 = parser.getObj();
var linDict = parser.getObj();
var obj, length;
if (!((0, _util.isInt)(obj1) && (0, _util.isInt)(obj2) && (0, _primitives.isCmd)(obj3, 'obj') && (0, _primitives.isDict)(linDict) && (0, _util.isNum)(obj = linDict.get('Linearized')) && obj > 0)) {
return null;
} else if ((length = getInt('L')) !== stream.length) {
throw new Error('The "L" parameter in the linearization dictionary ' + 'does not equal the stream length.');
}
return {
length: length,
hints: getHints(),
objectNumberFirst: getInt('O'),
endFirst: getInt('E'),
numPages: getInt('N'),
mainXRefEntriesOffset: getInt('T'),
pageFirst: linDict.has('P') ? getInt('P', true) : 0
};
}
};
exports.Lexer = Lexer;
exports.Linearization = Linearization;
exports.Parser = Parser;