/** * @licstart The following is the entire license notice for the * JavaScript code in this page * * Copyright 2022 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. * * @licend The above is the entire license notice for the * JavaScript code in this page */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = exports.Linearization = exports.Lexer = void 0; var _util = require("../shared/util.js"); var _primitives = require("./primitives.js"); var _core_utils = require("./core_utils.js"); var _ascii_85_stream = require("./ascii_85_stream.js"); var _ascii_hex_stream = require("./ascii_hex_stream.js"); var _ccitt_stream = require("./ccitt_stream.js"); var _flate_stream = require("./flate_stream.js"); var _jbig2_stream = require("./jbig2_stream.js"); var _jpeg_stream = require("./jpeg_stream.js"); var _jpx_stream = require("./jpx_stream.js"); var _lzw_stream = require("./lzw_stream.js"); var _stream = require("./stream.js"); var _predictor_stream = require("./predictor_stream.js"); var _run_length_stream = require("./run_length_stream.js"); const MAX_LENGTH_TO_CACHE = 1000; const MAX_ADLER32_LENGTH = 5552; function computeAdler32(bytes) { const bytesLength = bytes.length; let a = 1, b = 0; for (let i = 0; i < bytesLength; ++i) { a += bytes[i] & 0xff; b += a; } return b % 65521 << 16 | a % 65521; } class Parser { constructor({ lexer, xref, allowStreams = false, recoveryMode = false }) { this.lexer = lexer; this.xref = xref; this.allowStreams = allowStreams; this.recoveryMode = recoveryMode; this.imageCache = Object.create(null); this.refill(); } refill() { this.buf1 = this.lexer.getObj(); this.buf2 = this.lexer.getObj(); } shift() { if (this.buf2 instanceof _primitives.Cmd && this.buf2.cmd === "ID") { this.buf1 = this.buf2; this.buf2 = null; } else { this.buf1 = this.buf2; this.buf2 = this.lexer.getObj(); } } tryShift() { try { this.shift(); return true; } catch (e) { if (e instanceof _core_utils.MissingDataException) { throw e; } return false; } } getObj(cipherTransform = null) { const buf1 = this.buf1; this.shift(); if (buf1 instanceof _primitives.Cmd) { switch (buf1.cmd) { case "BI": return this.makeInlineImage(cipherTransform); case "[": const array = []; while (!(0, _primitives.isCmd)(this.buf1, "]") && this.buf1 !== _primitives.EOF) { array.push(this.getObj(cipherTransform)); } if (this.buf1 === _primitives.EOF) { if (this.recoveryMode) { return array; } throw new _core_utils.ParserEOFException("End of file inside array."); } this.shift(); return array; case "<<": const dict = new _primitives.Dict(this.xref); while (!(0, _primitives.isCmd)(this.buf1, ">>") && this.buf1 !== _primitives.EOF) { if (!(this.buf1 instanceof _primitives.Name)) { (0, _util.info)("Malformed dictionary: key must be a name object"); this.shift(); continue; } const key = this.buf1.name; this.shift(); if (this.buf1 === _primitives.EOF) { break; } dict.set(key, this.getObj(cipherTransform)); } if (this.buf1 === _primitives.EOF) { if (this.recoveryMode) { return dict; } throw new _core_utils.ParserEOFException("End of file inside dictionary."); } if ((0, _primitives.isCmd)(this.buf2, "stream")) { return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict; } this.shift(); return dict; default: return buf1; } } if (Number.isInteger(buf1)) { if (Number.isInteger(this.buf1) && (0, _primitives.isCmd)(this.buf2, "R")) { const ref = _primitives.Ref.get(buf1, this.buf1); this.shift(); this.shift(); return ref; } return buf1; } if (typeof buf1 === "string") { if (cipherTransform) { return cipherTransform.decryptString(buf1); } return buf1; } return buf1; } findDefaultInlineStreamEnd(stream) { const E = 0x45, I = 0x49, SPACE = 0x20, LF = 0xa, CR = 0xd, NUL = 0x0; const lexer = this.lexer, startPos = stream.pos, n = 10; let state = 0, ch, maybeEIPos; 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, "findDefaultInlineStreamEnd - invalid state."); if (ch === SPACE || ch === LF || ch === CR) { maybeEIPos = stream.pos; const followingBytes = stream.peekBytes(n); for (let i = 0, ii = followingBytes.length; i < ii; i++) { ch = followingBytes[i]; if (ch === NUL && followingBytes[i + 1] !== NUL) { continue; } if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7f)) { state = 0; break; } } if (state !== 2) { continue; } if (lexer.knownCommands) { const nextObj = lexer.peekObj(); if (nextObj instanceof _primitives.Cmd && !lexer.knownCommands[nextObj.cmd]) { state = 0; } } else { (0, _util.warn)("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined."); } if (state === 2) { break; } } else { state = 0; } } } if (ch === -1) { (0, _util.warn)("findDefaultInlineStreamEnd: " + "Reached the end of the stream without finding a valid EI marker"); if (maybeEIPos) { (0, _util.warn)('... trying to recover by using the last "EI" occurrence.'); stream.skip(-(stream.pos - maybeEIPos)); } } let endOffset = 4; stream.skip(-endOffset); ch = stream.peekByte(); stream.skip(endOffset); if (!(0, _core_utils.isWhiteSpace)(ch)) { endOffset--; } return stream.pos - endOffset - startPos; } findDCTDecodeInlineStreamEnd(stream) { const startPos = stream.pos; let foundEOI = false, b, markerLength; 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; } } const 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(stream) { const TILDE = 0x7e, GT = 0x3e; const startPos = stream.pos; let ch; while ((ch = stream.getByte()) !== -1) { if (ch === TILDE) { const tildePos = stream.pos; ch = stream.peekByte(); while ((0, _core_utils.isWhiteSpace)(ch)) { stream.skip(); ch = stream.peekByte(); } if (ch === GT) { stream.skip(); break; } if (stream.pos > tildePos) { const maybeEI = stream.peekBytes(2); if (maybeEI[0] === 0x45 && maybeEI[1] === 0x49) { break; } } } } const 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(stream) { const GT = 0x3e; const startPos = stream.pos; let ch; while ((ch = stream.getByte()) !== -1) { if (ch === GT) { break; } } const 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(stream) { const E = 0x45, I = 0x49; let 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(cipherTransform) { const lexer = this.lexer; const stream = lexer.stream; const dict = new _primitives.Dict(this.xref); let dictLength; while (!(0, _primitives.isCmd)(this.buf1, "ID") && this.buf1 !== _primitives.EOF) { if (!(this.buf1 instanceof _primitives.Name)) { throw new _util.FormatError("Dictionary key must be a name object"); } const key = this.buf1.name; this.shift(); if (this.buf1 === _primitives.EOF) { break; } dict.set(key, this.getObj(cipherTransform)); } if (lexer.beginInlineImagePos !== -1) { dictLength = stream.pos - lexer.beginInlineImagePos; } const filter = dict.get("F", "Filter"); let filterName; if (filter instanceof _primitives.Name) { filterName = filter.name; } else if (Array.isArray(filter)) { const filterZero = this.xref.fetchIfRef(filter[0]); if (filterZero instanceof _primitives.Name) { filterName = filterZero.name; } } const startPos = stream.pos; let length; switch (filterName) { case "DCT": case "DCTDecode": length = this.findDCTDecodeInlineStreamEnd(stream); break; case "A85": case "ASCII85Decode": length = this.findASCII85DecodeInlineStreamEnd(stream); break; case "AHx": case "ASCIIHexDecode": length = this.findASCIIHexDecodeInlineStreamEnd(stream); break; default: length = this.findDefaultInlineStreamEnd(stream); } let imageStream = stream.makeSubStream(startPos, length, dict); let cacheKey; if (length < MAX_LENGTH_TO_CACHE && dictLength < MAX_ADLER32_LENGTH) { const imageBytes = imageStream.getBytes(); imageStream.reset(); const initialStreamPos = stream.pos; stream.pos = lexer.beginInlineImagePos; const dictBytes = stream.getBytes(dictLength); stream.pos = initialStreamPos; cacheKey = computeAdler32(imageBytes) + "_" + computeAdler32(dictBytes); const cacheEntry = this.imageCache[cacheKey]; if (cacheEntry !== undefined) { this.buf2 = _primitives.Cmd.get("EI"); this.shift(); cacheEntry.reset(); return cacheEntry; } } if (cipherTransform) { imageStream = cipherTransform.createStream(imageStream, length); } imageStream = this.filter(imageStream, dict, length); imageStream.dict = dict; if (cacheKey !== undefined) { imageStream.cacheKey = `inline_${length}_${cacheKey}`; this.imageCache[cacheKey] = imageStream; } this.buf2 = _primitives.Cmd.get("EI"); this.shift(); return imageStream; } _findStreamLength(startPos, signature) { const { stream } = this.lexer; stream.pos = startPos; const SCAN_BLOCK_LENGTH = 2048; const signatureLength = signature.length; while (stream.pos < stream.end) { const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH); const scanLength = scanBytes.length - signatureLength; if (scanLength <= 0) { break; } let pos = 0; while (pos < scanLength) { let j = 0; while (j < signatureLength && scanBytes[pos + j] === signature[j]) { j++; } if (j >= signatureLength) { stream.pos += pos; return stream.pos - startPos; } pos++; } stream.pos += scanLength; } return -1; } makeStream(dict, cipherTransform) { const lexer = this.lexer; let stream = lexer.stream; lexer.skipToNextLine(); const startPos = stream.pos - 1; let length = dict.get("Length"); if (!Number.isInteger(length)) { (0, _util.info)(`Bad length "${length && length.toString()}" in stream.`); length = 0; } stream.pos = startPos + length; lexer.nextChar(); if (this.tryShift() && (0, _primitives.isCmd)(this.buf2, "endstream")) { this.shift(); } else { const ENDSTREAM_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d]); let actualLength = this._findStreamLength(startPos, ENDSTREAM_SIGNATURE); if (actualLength < 0) { const MAX_TRUNCATION = 1; for (let i = 1; i <= MAX_TRUNCATION; i++) { const end = ENDSTREAM_SIGNATURE.length - i; const TRUNCATED_SIGNATURE = ENDSTREAM_SIGNATURE.slice(0, end); const maybeLength = this._findStreamLength(startPos, TRUNCATED_SIGNATURE); if (maybeLength >= 0) { const lastByte = stream.peekBytes(end + 1)[end]; if (!(0, _core_utils.isWhiteSpace)(lastByte)) { break; } (0, _util.info)(`Found "${(0, _util.bytesToString)(TRUNCATED_SIGNATURE)}" when ` + "searching for endstream command."); actualLength = maybeLength; break; } } if (actualLength < 0) { throw new _util.FormatError("Missing endstream command."); } } length = actualLength; lexer.nextChar(); this.shift(); this.shift(); } this.shift(); stream = stream.makeSubStream(startPos, length, dict); if (cipherTransform) { stream = cipherTransform.createStream(stream, length); } stream = this.filter(stream, dict, length); stream.dict = dict; return stream; } filter(stream, dict, length) { let filter = dict.get("F", "Filter"); let params = dict.get("DP", "DecodeParms"); if (filter instanceof _primitives.Name) { if (Array.isArray(params)) { (0, _util.warn)("/DecodeParms should not be an Array, when /Filter is a Name."); } return this.makeFilter(stream, filter.name, length, params); } let maybeLength = length; if (Array.isArray(filter)) { const filterArray = filter; const paramsArray = params; for (let i = 0, ii = filterArray.length; i < ii; ++i) { filter = this.xref.fetchIfRef(filterArray[i]); if (!(filter instanceof _primitives.Name)) { throw new _util.FormatError(`Bad filter name "${filter}"`); } params = null; if (Array.isArray(paramsArray) && i in paramsArray) { params = this.xref.fetchIfRef(paramsArray[i]); } stream = this.makeFilter(stream, filter.name, maybeLength, params); maybeLength = null; } } return stream; } makeFilter(stream, name, maybeLength, params) { if (maybeLength === 0) { (0, _util.warn)(`Empty "${name}" stream.`); return new _stream.NullStream(); } const xrefStats = this.xref.stats; try { switch (name) { case "Fl": case "FlateDecode": xrefStats.addStreamType(_util.StreamType.FLATE); if (params) { return new _predictor_stream.PredictorStream(new _flate_stream.FlateStream(stream, maybeLength), maybeLength, params); } return new _flate_stream.FlateStream(stream, maybeLength); case "LZW": case "LZWDecode": xrefStats.addStreamType(_util.StreamType.LZW); let earlyChange = 1; if (params) { if (params.has("EarlyChange")) { earlyChange = params.get("EarlyChange"); } return new _predictor_stream.PredictorStream(new _lzw_stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params); } return new _lzw_stream.LZWStream(stream, maybeLength, earlyChange); case "DCT": case "DCTDecode": xrefStats.addStreamType(_util.StreamType.DCT); return new _jpeg_stream.JpegStream(stream, maybeLength, params); case "JPX": case "JPXDecode": xrefStats.addStreamType(_util.StreamType.JPX); return new _jpx_stream.JpxStream(stream, maybeLength, params); case "A85": case "ASCII85Decode": xrefStats.addStreamType(_util.StreamType.A85); return new _ascii_85_stream.Ascii85Stream(stream, maybeLength); case "AHx": case "ASCIIHexDecode": xrefStats.addStreamType(_util.StreamType.AHX); return new _ascii_hex_stream.AsciiHexStream(stream, maybeLength); case "CCF": case "CCITTFaxDecode": xrefStats.addStreamType(_util.StreamType.CCF); return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params); case "RL": case "RunLengthDecode": xrefStats.addStreamType(_util.StreamType.RLX); return new _run_length_stream.RunLengthStream(stream, maybeLength); case "JBIG2Decode": xrefStats.addStreamType(_util.StreamType.JBIG); return new _jbig2_stream.Jbig2Stream(stream, maybeLength, params); } (0, _util.warn)(`Filter "${name}" is not supported.`); return stream; } catch (ex) { if (ex instanceof _core_utils.MissingDataException) { throw ex; } (0, _util.warn)(`Invalid stream: "${ex}"`); return new _stream.NullStream(); } } } exports.Parser = Parser; const 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; } class Lexer { constructor(stream, knownCommands = null) { this.stream = stream; this.nextChar(); this.strBuf = []; this.knownCommands = knownCommands; this._hexStringNumWarn = 0; this.beginInlineImagePos = -1; } nextChar() { return this.currentChar = this.stream.getByte(); } peekChar() { return this.stream.peekByte(); } getNumber() { let ch = this.currentChar; let eNotation = false; let divideBy = 0; let sign = 0; if (ch === 0x2d) { sign = -1; ch = this.nextChar(); if (ch === 0x2d) { ch = this.nextChar(); } } else if (ch === 0x2b) { sign = 1; ch = this.nextChar(); } if (ch === 0x0a || ch === 0x0d) { do { ch = this.nextChar(); } while (ch === 0x0a || ch === 0x0d); } if (ch === 0x2e) { divideBy = 10; ch = this.nextChar(); } if (ch < 0x30 || ch > 0x39) { if ((0, _core_utils.isWhiteSpace)(ch) || ch === -1) { if (divideBy === 10 && sign === 0) { (0, _util.warn)("Lexer.getNumber - treating a single decimal point as zero."); return 0; } if (divideBy === 0 && sign === -1) { (0, _util.warn)("Lexer.getNumber - treating a single minus sign as zero."); return 0; } } throw new _util.FormatError(`Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`); } sign = sign || 1; let baseValue = ch - 0x30; let powerValue = 0; let powerValueSign = 1; while ((ch = this.nextChar()) >= 0) { if (ch >= 0x30 && ch <= 0x39) { const 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: minus sign in the middle"); } 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 *= 10 ** (powerValueSign * powerValue); } return sign * baseValue; } getString() { let numParen = 1; let done = false; const strBuf = this.strBuf; strBuf.length = 0; let ch = this.nextChar(); while (true) { let 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: let 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() { let ch, previousCh; const 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; } const x = toHexDigit(ch); if (x !== -1) { previousCh = ch; ch = this.nextChar(); const 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("")); } _hexStringWarn(ch) { const MAX_HEX_STRING_NUM_WARN = 5; if (this._hexStringNumWarn++ === MAX_HEX_STRING_NUM_WARN) { (0, _util.warn)("getHexString - ignoring additional invalid characters."); return; } if (this._hexStringNumWarn > MAX_HEX_STRING_NUM_WARN) { return; } (0, _util.warn)(`getHexString - ignoring invalid character: ${ch}`); } getHexString() { const strBuf = this.strBuf; strBuf.length = 0; let ch = this.currentChar; let isFirstHex = true; let firstDigit, secondDigit; this._hexStringNumWarn = 0; 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) { this._hexStringWarn(ch); ch = this.nextChar(); continue; } } else { secondDigit = toHexDigit(ch); if (secondDigit === -1) { this._hexStringWarn(ch); ch = this.nextChar(); continue; } strBuf.push(String.fromCharCode(firstDigit << 4 | secondDigit)); } isFirstHex = !isFirstHex; ch = this.nextChar(); } } return strBuf.join(""); } getObj() { let comment = false; let 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(); throw new _util.FormatError(`Illegal character: ${ch}`); } let str = String.fromCharCode(ch); if (ch < 0x20 || ch > 0x7f) { const nextCh = this.peekChar(); if (nextCh >= 0x20 && nextCh <= 0x7f) { this.nextChar(); return _primitives.Cmd.get(str); } } const knownCommands = this.knownCommands; let knownCommandFound = knownCommands && knownCommands[str] !== undefined; while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { const possibleCommand = str + String.fromCharCode(ch); if (knownCommandFound && knownCommands[possibleCommand] === undefined) { break; } if (str.length === 128) { throw new _util.FormatError(`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; } if (str === "BI") { this.beginInlineImagePos = this.stream.pos; } return _primitives.Cmd.get(str); } peekObj() { const streamPos = this.stream.pos, currentChar = this.currentChar, beginInlineImagePos = this.beginInlineImagePos; let nextObj; try { nextObj = this.getObj(); } catch (ex) { if (ex instanceof _core_utils.MissingDataException) { throw ex; } (0, _util.warn)(`peekObj: ${ex}`); } this.stream.pos = streamPos; this.currentChar = currentChar; this.beginInlineImagePos = beginInlineImagePos; return nextObj; } skipToNextLine() { let 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(); } } } exports.Lexer = Lexer; class Linearization { static create(stream) { function getInt(linDict, name, allowZeroValue = false) { const obj = linDict.get(name); if (Number.isInteger(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) { return obj; } throw new Error(`The "${name}" parameter in the linearization ` + "dictionary is invalid."); } function getHints(linDict) { const hints = linDict.get("H"); let hintsLength; if (Array.isArray(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) { for (let index = 0; index < hintsLength; index++) { const hint = hints[index]; if (!(Number.isInteger(hint) && hint > 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."); } const parser = new Parser({ lexer: new Lexer(stream), xref: null }); const obj1 = parser.getObj(); const obj2 = parser.getObj(); const obj3 = parser.getObj(); const linDict = parser.getObj(); let obj, length; if (!(Number.isInteger(obj1) && Number.isInteger(obj2) && (0, _primitives.isCmd)(obj3, "obj") && linDict instanceof _primitives.Dict && typeof (obj = linDict.get("Linearized")) === "number" && obj > 0)) { return null; } else if ((length = getInt(linDict, "L")) !== stream.length) { throw new Error('The "L" parameter in the linearization dictionary ' + "does not equal the stream length."); } return { length, hints: getHints(linDict), objectNumberFirst: getInt(linDict, "O"), endFirst: getInt(linDict, "E"), numPages: getInt(linDict, "N"), mainXRefEntriesOffset: getInt(linDict, "T"), pageFirst: linDict.has("P") ? getInt(linDict, "P", true) : 0 }; } } exports.Linearization = Linearization;