UNPKG

34.5 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * JavaScript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.Parser = exports.Linearization = exports.Lexer = void 0;
28
29var _util = require("../shared/util.js");
30
31var _primitives = require("./primitives.js");
32
33var _core_utils = require("./core_utils.js");
34
35var _ascii_85_stream = require("./ascii_85_stream.js");
36
37var _ascii_hex_stream = require("./ascii_hex_stream.js");
38
39var _ccitt_stream = require("./ccitt_stream.js");
40
41var _flate_stream = require("./flate_stream.js");
42
43var _jbig2_stream = require("./jbig2_stream.js");
44
45var _jpeg_stream = require("./jpeg_stream.js");
46
47var _jpx_stream = require("./jpx_stream.js");
48
49var _lzw_stream = require("./lzw_stream.js");
50
51var _stream = require("./stream.js");
52
53var _predictor_stream = require("./predictor_stream.js");
54
55var _run_length_stream = require("./run_length_stream.js");
56
57const MAX_LENGTH_TO_CACHE = 1000;
58const MAX_ADLER32_LENGTH = 5552;
59
60function computeAdler32(bytes) {
61 const bytesLength = bytes.length;
62 let a = 1,
63 b = 0;
64
65 for (let i = 0; i < bytesLength; ++i) {
66 a += bytes[i] & 0xff;
67 b += a;
68 }
69
70 return b % 65521 << 16 | a % 65521;
71}
72
73class Parser {
74 constructor({
75 lexer,
76 xref,
77 allowStreams = false,
78 recoveryMode = false
79 }) {
80 this.lexer = lexer;
81 this.xref = xref;
82 this.allowStreams = allowStreams;
83 this.recoveryMode = recoveryMode;
84 this.imageCache = Object.create(null);
85 this.refill();
86 }
87
88 refill() {
89 this.buf1 = this.lexer.getObj();
90 this.buf2 = this.lexer.getObj();
91 }
92
93 shift() {
94 if (this.buf2 instanceof _primitives.Cmd && this.buf2.cmd === "ID") {
95 this.buf1 = this.buf2;
96 this.buf2 = null;
97 } else {
98 this.buf1 = this.buf2;
99 this.buf2 = this.lexer.getObj();
100 }
101 }
102
103 tryShift() {
104 try {
105 this.shift();
106 return true;
107 } catch (e) {
108 if (e instanceof _core_utils.MissingDataException) {
109 throw e;
110 }
111
112 return false;
113 }
114 }
115
116 getObj(cipherTransform = null) {
117 const buf1 = this.buf1;
118 this.shift();
119
120 if (buf1 instanceof _primitives.Cmd) {
121 switch (buf1.cmd) {
122 case "BI":
123 return this.makeInlineImage(cipherTransform);
124
125 case "[":
126 const array = [];
127
128 while (!(0, _primitives.isCmd)(this.buf1, "]") && this.buf1 !== _primitives.EOF) {
129 array.push(this.getObj(cipherTransform));
130 }
131
132 if (this.buf1 === _primitives.EOF) {
133 if (this.recoveryMode) {
134 return array;
135 }
136
137 throw new _core_utils.ParserEOFException("End of file inside array.");
138 }
139
140 this.shift();
141 return array;
142
143 case "<<":
144 const dict = new _primitives.Dict(this.xref);
145
146 while (!(0, _primitives.isCmd)(this.buf1, ">>") && this.buf1 !== _primitives.EOF) {
147 if (!(this.buf1 instanceof _primitives.Name)) {
148 (0, _util.info)("Malformed dictionary: key must be a name object");
149 this.shift();
150 continue;
151 }
152
153 const key = this.buf1.name;
154 this.shift();
155
156 if (this.buf1 === _primitives.EOF) {
157 break;
158 }
159
160 dict.set(key, this.getObj(cipherTransform));
161 }
162
163 if (this.buf1 === _primitives.EOF) {
164 if (this.recoveryMode) {
165 return dict;
166 }
167
168 throw new _core_utils.ParserEOFException("End of file inside dictionary.");
169 }
170
171 if ((0, _primitives.isCmd)(this.buf2, "stream")) {
172 return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict;
173 }
174
175 this.shift();
176 return dict;
177
178 default:
179 return buf1;
180 }
181 }
182
183 if (Number.isInteger(buf1)) {
184 if (Number.isInteger(this.buf1) && (0, _primitives.isCmd)(this.buf2, "R")) {
185 const ref = _primitives.Ref.get(buf1, this.buf1);
186
187 this.shift();
188 this.shift();
189 return ref;
190 }
191
192 return buf1;
193 }
194
195 if (typeof buf1 === "string") {
196 if (cipherTransform) {
197 return cipherTransform.decryptString(buf1);
198 }
199
200 return buf1;
201 }
202
203 return buf1;
204 }
205
206 findDefaultInlineStreamEnd(stream) {
207 const E = 0x45,
208 I = 0x49,
209 SPACE = 0x20,
210 LF = 0xa,
211 CR = 0xd,
212 NUL = 0x0;
213 const lexer = this.lexer,
214 startPos = stream.pos,
215 n = 10;
216 let state = 0,
217 ch,
218 maybeEIPos;
219
220 while ((ch = stream.getByte()) !== -1) {
221 if (state === 0) {
222 state = ch === E ? 1 : 0;
223 } else if (state === 1) {
224 state = ch === I ? 2 : 0;
225 } else {
226 (0, _util.assert)(state === 2, "findDefaultInlineStreamEnd - invalid state.");
227
228 if (ch === SPACE || ch === LF || ch === CR) {
229 maybeEIPos = stream.pos;
230 const followingBytes = stream.peekBytes(n);
231
232 for (let i = 0, ii = followingBytes.length; i < ii; i++) {
233 ch = followingBytes[i];
234
235 if (ch === NUL && followingBytes[i + 1] !== NUL) {
236 continue;
237 }
238
239 if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7f)) {
240 state = 0;
241 break;
242 }
243 }
244
245 if (state !== 2) {
246 continue;
247 }
248
249 if (lexer.knownCommands) {
250 const nextObj = lexer.peekObj();
251
252 if (nextObj instanceof _primitives.Cmd && !lexer.knownCommands[nextObj.cmd]) {
253 state = 0;
254 }
255 } else {
256 (0, _util.warn)("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");
257 }
258
259 if (state === 2) {
260 break;
261 }
262 } else {
263 state = 0;
264 }
265 }
266 }
267
268 if (ch === -1) {
269 (0, _util.warn)("findDefaultInlineStreamEnd: " + "Reached the end of the stream without finding a valid EI marker");
270
271 if (maybeEIPos) {
272 (0, _util.warn)('... trying to recover by using the last "EI" occurrence.');
273 stream.skip(-(stream.pos - maybeEIPos));
274 }
275 }
276
277 let endOffset = 4;
278 stream.skip(-endOffset);
279 ch = stream.peekByte();
280 stream.skip(endOffset);
281
282 if (!(0, _core_utils.isWhiteSpace)(ch)) {
283 endOffset--;
284 }
285
286 return stream.pos - endOffset - startPos;
287 }
288
289 findDCTDecodeInlineStreamEnd(stream) {
290 const startPos = stream.pos;
291 let foundEOI = false,
292 b,
293 markerLength;
294
295 while ((b = stream.getByte()) !== -1) {
296 if (b !== 0xff) {
297 continue;
298 }
299
300 switch (stream.getByte()) {
301 case 0x00:
302 break;
303
304 case 0xff:
305 stream.skip(-1);
306 break;
307
308 case 0xd9:
309 foundEOI = true;
310 break;
311
312 case 0xc0:
313 case 0xc1:
314 case 0xc2:
315 case 0xc3:
316 case 0xc5:
317 case 0xc6:
318 case 0xc7:
319 case 0xc9:
320 case 0xca:
321 case 0xcb:
322 case 0xcd:
323 case 0xce:
324 case 0xcf:
325 case 0xc4:
326 case 0xcc:
327 case 0xda:
328 case 0xdb:
329 case 0xdc:
330 case 0xdd:
331 case 0xde:
332 case 0xdf:
333 case 0xe0:
334 case 0xe1:
335 case 0xe2:
336 case 0xe3:
337 case 0xe4:
338 case 0xe5:
339 case 0xe6:
340 case 0xe7:
341 case 0xe8:
342 case 0xe9:
343 case 0xea:
344 case 0xeb:
345 case 0xec:
346 case 0xed:
347 case 0xee:
348 case 0xef:
349 case 0xfe:
350 markerLength = stream.getUint16();
351
352 if (markerLength > 2) {
353 stream.skip(markerLength - 2);
354 } else {
355 stream.skip(-2);
356 }
357
358 break;
359 }
360
361 if (foundEOI) {
362 break;
363 }
364 }
365
366 const length = stream.pos - startPos;
367
368 if (b === -1) {
369 (0, _util.warn)("Inline DCTDecode image stream: " + "EOI marker not found, searching for /EI/ instead.");
370 stream.skip(-length);
371 return this.findDefaultInlineStreamEnd(stream);
372 }
373
374 this.inlineStreamSkipEI(stream);
375 return length;
376 }
377
378 findASCII85DecodeInlineStreamEnd(stream) {
379 const TILDE = 0x7e,
380 GT = 0x3e;
381 const startPos = stream.pos;
382 let ch;
383
384 while ((ch = stream.getByte()) !== -1) {
385 if (ch === TILDE) {
386 const tildePos = stream.pos;
387 ch = stream.peekByte();
388
389 while ((0, _core_utils.isWhiteSpace)(ch)) {
390 stream.skip();
391 ch = stream.peekByte();
392 }
393
394 if (ch === GT) {
395 stream.skip();
396 break;
397 }
398
399 if (stream.pos > tildePos) {
400 const maybeEI = stream.peekBytes(2);
401
402 if (maybeEI[0] === 0x45 && maybeEI[1] === 0x49) {
403 break;
404 }
405 }
406 }
407 }
408
409 const length = stream.pos - startPos;
410
411 if (ch === -1) {
412 (0, _util.warn)("Inline ASCII85Decode image stream: " + "EOD marker not found, searching for /EI/ instead.");
413 stream.skip(-length);
414 return this.findDefaultInlineStreamEnd(stream);
415 }
416
417 this.inlineStreamSkipEI(stream);
418 return length;
419 }
420
421 findASCIIHexDecodeInlineStreamEnd(stream) {
422 const GT = 0x3e;
423 const startPos = stream.pos;
424 let ch;
425
426 while ((ch = stream.getByte()) !== -1) {
427 if (ch === GT) {
428 break;
429 }
430 }
431
432 const length = stream.pos - startPos;
433
434 if (ch === -1) {
435 (0, _util.warn)("Inline ASCIIHexDecode image stream: " + "EOD marker not found, searching for /EI/ instead.");
436 stream.skip(-length);
437 return this.findDefaultInlineStreamEnd(stream);
438 }
439
440 this.inlineStreamSkipEI(stream);
441 return length;
442 }
443
444 inlineStreamSkipEI(stream) {
445 const E = 0x45,
446 I = 0x49;
447 let state = 0,
448 ch;
449
450 while ((ch = stream.getByte()) !== -1) {
451 if (state === 0) {
452 state = ch === E ? 1 : 0;
453 } else if (state === 1) {
454 state = ch === I ? 2 : 0;
455 } else if (state === 2) {
456 break;
457 }
458 }
459 }
460
461 makeInlineImage(cipherTransform) {
462 const lexer = this.lexer;
463 const stream = lexer.stream;
464 const dict = new _primitives.Dict(this.xref);
465 let dictLength;
466
467 while (!(0, _primitives.isCmd)(this.buf1, "ID") && this.buf1 !== _primitives.EOF) {
468 if (!(this.buf1 instanceof _primitives.Name)) {
469 throw new _util.FormatError("Dictionary key must be a name object");
470 }
471
472 const key = this.buf1.name;
473 this.shift();
474
475 if (this.buf1 === _primitives.EOF) {
476 break;
477 }
478
479 dict.set(key, this.getObj(cipherTransform));
480 }
481
482 if (lexer.beginInlineImagePos !== -1) {
483 dictLength = stream.pos - lexer.beginInlineImagePos;
484 }
485
486 const filter = dict.get("F", "Filter");
487 let filterName;
488
489 if (filter instanceof _primitives.Name) {
490 filterName = filter.name;
491 } else if (Array.isArray(filter)) {
492 const filterZero = this.xref.fetchIfRef(filter[0]);
493
494 if (filterZero instanceof _primitives.Name) {
495 filterName = filterZero.name;
496 }
497 }
498
499 const startPos = stream.pos;
500 let length;
501
502 switch (filterName) {
503 case "DCT":
504 case "DCTDecode":
505 length = this.findDCTDecodeInlineStreamEnd(stream);
506 break;
507
508 case "A85":
509 case "ASCII85Decode":
510 length = this.findASCII85DecodeInlineStreamEnd(stream);
511 break;
512
513 case "AHx":
514 case "ASCIIHexDecode":
515 length = this.findASCIIHexDecodeInlineStreamEnd(stream);
516 break;
517
518 default:
519 length = this.findDefaultInlineStreamEnd(stream);
520 }
521
522 let imageStream = stream.makeSubStream(startPos, length, dict);
523 let cacheKey;
524
525 if (length < MAX_LENGTH_TO_CACHE && dictLength < MAX_ADLER32_LENGTH) {
526 const imageBytes = imageStream.getBytes();
527 imageStream.reset();
528 const initialStreamPos = stream.pos;
529 stream.pos = lexer.beginInlineImagePos;
530 const dictBytes = stream.getBytes(dictLength);
531 stream.pos = initialStreamPos;
532 cacheKey = computeAdler32(imageBytes) + "_" + computeAdler32(dictBytes);
533 const cacheEntry = this.imageCache[cacheKey];
534
535 if (cacheEntry !== undefined) {
536 this.buf2 = _primitives.Cmd.get("EI");
537 this.shift();
538 cacheEntry.reset();
539 return cacheEntry;
540 }
541 }
542
543 if (cipherTransform) {
544 imageStream = cipherTransform.createStream(imageStream, length);
545 }
546
547 imageStream = this.filter(imageStream, dict, length);
548 imageStream.dict = dict;
549
550 if (cacheKey !== undefined) {
551 imageStream.cacheKey = `inline_${length}_${cacheKey}`;
552 this.imageCache[cacheKey] = imageStream;
553 }
554
555 this.buf2 = _primitives.Cmd.get("EI");
556 this.shift();
557 return imageStream;
558 }
559
560 _findStreamLength(startPos, signature) {
561 const {
562 stream
563 } = this.lexer;
564 stream.pos = startPos;
565 const SCAN_BLOCK_LENGTH = 2048;
566 const signatureLength = signature.length;
567
568 while (stream.pos < stream.end) {
569 const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
570 const scanLength = scanBytes.length - signatureLength;
571
572 if (scanLength <= 0) {
573 break;
574 }
575
576 let pos = 0;
577
578 while (pos < scanLength) {
579 let j = 0;
580
581 while (j < signatureLength && scanBytes[pos + j] === signature[j]) {
582 j++;
583 }
584
585 if (j >= signatureLength) {
586 stream.pos += pos;
587 return stream.pos - startPos;
588 }
589
590 pos++;
591 }
592
593 stream.pos += scanLength;
594 }
595
596 return -1;
597 }
598
599 makeStream(dict, cipherTransform) {
600 const lexer = this.lexer;
601 let stream = lexer.stream;
602 lexer.skipToNextLine();
603 const startPos = stream.pos - 1;
604 let length = dict.get("Length");
605
606 if (!Number.isInteger(length)) {
607 (0, _util.info)(`Bad length "${length && length.toString()}" in stream.`);
608 length = 0;
609 }
610
611 stream.pos = startPos + length;
612 lexer.nextChar();
613
614 if (this.tryShift() && (0, _primitives.isCmd)(this.buf2, "endstream")) {
615 this.shift();
616 } else {
617 const ENDSTREAM_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d]);
618
619 let actualLength = this._findStreamLength(startPos, ENDSTREAM_SIGNATURE);
620
621 if (actualLength < 0) {
622 const MAX_TRUNCATION = 1;
623
624 for (let i = 1; i <= MAX_TRUNCATION; i++) {
625 const end = ENDSTREAM_SIGNATURE.length - i;
626 const TRUNCATED_SIGNATURE = ENDSTREAM_SIGNATURE.slice(0, end);
627
628 const maybeLength = this._findStreamLength(startPos, TRUNCATED_SIGNATURE);
629
630 if (maybeLength >= 0) {
631 const lastByte = stream.peekBytes(end + 1)[end];
632
633 if (!(0, _core_utils.isWhiteSpace)(lastByte)) {
634 break;
635 }
636
637 (0, _util.info)(`Found "${(0, _util.bytesToString)(TRUNCATED_SIGNATURE)}" when ` + "searching for endstream command.");
638 actualLength = maybeLength;
639 break;
640 }
641 }
642
643 if (actualLength < 0) {
644 throw new _util.FormatError("Missing endstream command.");
645 }
646 }
647
648 length = actualLength;
649 lexer.nextChar();
650 this.shift();
651 this.shift();
652 }
653
654 this.shift();
655 stream = stream.makeSubStream(startPos, length, dict);
656
657 if (cipherTransform) {
658 stream = cipherTransform.createStream(stream, length);
659 }
660
661 stream = this.filter(stream, dict, length);
662 stream.dict = dict;
663 return stream;
664 }
665
666 filter(stream, dict, length) {
667 let filter = dict.get("F", "Filter");
668 let params = dict.get("DP", "DecodeParms");
669
670 if (filter instanceof _primitives.Name) {
671 if (Array.isArray(params)) {
672 (0, _util.warn)("/DecodeParms should not be an Array, when /Filter is a Name.");
673 }
674
675 return this.makeFilter(stream, filter.name, length, params);
676 }
677
678 let maybeLength = length;
679
680 if (Array.isArray(filter)) {
681 const filterArray = filter;
682 const paramsArray = params;
683
684 for (let i = 0, ii = filterArray.length; i < ii; ++i) {
685 filter = this.xref.fetchIfRef(filterArray[i]);
686
687 if (!(filter instanceof _primitives.Name)) {
688 throw new _util.FormatError(`Bad filter name "${filter}"`);
689 }
690
691 params = null;
692
693 if (Array.isArray(paramsArray) && i in paramsArray) {
694 params = this.xref.fetchIfRef(paramsArray[i]);
695 }
696
697 stream = this.makeFilter(stream, filter.name, maybeLength, params);
698 maybeLength = null;
699 }
700 }
701
702 return stream;
703 }
704
705 makeFilter(stream, name, maybeLength, params) {
706 if (maybeLength === 0) {
707 (0, _util.warn)(`Empty "${name}" stream.`);
708 return new _stream.NullStream();
709 }
710
711 const xrefStats = this.xref.stats;
712
713 try {
714 switch (name) {
715 case "Fl":
716 case "FlateDecode":
717 xrefStats.addStreamType(_util.StreamType.FLATE);
718
719 if (params) {
720 return new _predictor_stream.PredictorStream(new _flate_stream.FlateStream(stream, maybeLength), maybeLength, params);
721 }
722
723 return new _flate_stream.FlateStream(stream, maybeLength);
724
725 case "LZW":
726 case "LZWDecode":
727 xrefStats.addStreamType(_util.StreamType.LZW);
728 let earlyChange = 1;
729
730 if (params) {
731 if (params.has("EarlyChange")) {
732 earlyChange = params.get("EarlyChange");
733 }
734
735 return new _predictor_stream.PredictorStream(new _lzw_stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
736 }
737
738 return new _lzw_stream.LZWStream(stream, maybeLength, earlyChange);
739
740 case "DCT":
741 case "DCTDecode":
742 xrefStats.addStreamType(_util.StreamType.DCT);
743 return new _jpeg_stream.JpegStream(stream, maybeLength, params);
744
745 case "JPX":
746 case "JPXDecode":
747 xrefStats.addStreamType(_util.StreamType.JPX);
748 return new _jpx_stream.JpxStream(stream, maybeLength, params);
749
750 case "A85":
751 case "ASCII85Decode":
752 xrefStats.addStreamType(_util.StreamType.A85);
753 return new _ascii_85_stream.Ascii85Stream(stream, maybeLength);
754
755 case "AHx":
756 case "ASCIIHexDecode":
757 xrefStats.addStreamType(_util.StreamType.AHX);
758 return new _ascii_hex_stream.AsciiHexStream(stream, maybeLength);
759
760 case "CCF":
761 case "CCITTFaxDecode":
762 xrefStats.addStreamType(_util.StreamType.CCF);
763 return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params);
764
765 case "RL":
766 case "RunLengthDecode":
767 xrefStats.addStreamType(_util.StreamType.RLX);
768 return new _run_length_stream.RunLengthStream(stream, maybeLength);
769
770 case "JBIG2Decode":
771 xrefStats.addStreamType(_util.StreamType.JBIG);
772 return new _jbig2_stream.Jbig2Stream(stream, maybeLength, params);
773 }
774
775 (0, _util.warn)(`Filter "${name}" is not supported.`);
776 return stream;
777 } catch (ex) {
778 if (ex instanceof _core_utils.MissingDataException) {
779 throw ex;
780 }
781
782 (0, _util.warn)(`Invalid stream: "${ex}"`);
783 return new _stream.NullStream();
784 }
785 }
786
787}
788
789exports.Parser = Parser;
790const 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];
791
792function toHexDigit(ch) {
793 if (ch >= 0x30 && ch <= 0x39) {
794 return ch & 0x0f;
795 }
796
797 if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {
798 return (ch & 0x0f) + 9;
799 }
800
801 return -1;
802}
803
804class Lexer {
805 constructor(stream, knownCommands = null) {
806 this.stream = stream;
807 this.nextChar();
808 this.strBuf = [];
809 this.knownCommands = knownCommands;
810 this._hexStringNumWarn = 0;
811 this.beginInlineImagePos = -1;
812 }
813
814 nextChar() {
815 return this.currentChar = this.stream.getByte();
816 }
817
818 peekChar() {
819 return this.stream.peekByte();
820 }
821
822 getNumber() {
823 let ch = this.currentChar;
824 let eNotation = false;
825 let divideBy = 0;
826 let sign = 0;
827
828 if (ch === 0x2d) {
829 sign = -1;
830 ch = this.nextChar();
831
832 if (ch === 0x2d) {
833 ch = this.nextChar();
834 }
835 } else if (ch === 0x2b) {
836 sign = 1;
837 ch = this.nextChar();
838 }
839
840 if (ch === 0x0a || ch === 0x0d) {
841 do {
842 ch = this.nextChar();
843 } while (ch === 0x0a || ch === 0x0d);
844 }
845
846 if (ch === 0x2e) {
847 divideBy = 10;
848 ch = this.nextChar();
849 }
850
851 if (ch < 0x30 || ch > 0x39) {
852 if ((0, _core_utils.isWhiteSpace)(ch) || ch === -1) {
853 if (divideBy === 10 && sign === 0) {
854 (0, _util.warn)("Lexer.getNumber - treating a single decimal point as zero.");
855 return 0;
856 }
857
858 if (divideBy === 0 && sign === -1) {
859 (0, _util.warn)("Lexer.getNumber - treating a single minus sign as zero.");
860 return 0;
861 }
862 }
863
864 throw new _util.FormatError(`Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`);
865 }
866
867 sign = sign || 1;
868 let baseValue = ch - 0x30;
869 let powerValue = 0;
870 let powerValueSign = 1;
871
872 while ((ch = this.nextChar()) >= 0) {
873 if (ch >= 0x30 && ch <= 0x39) {
874 const currentDigit = ch - 0x30;
875
876 if (eNotation) {
877 powerValue = powerValue * 10 + currentDigit;
878 } else {
879 if (divideBy !== 0) {
880 divideBy *= 10;
881 }
882
883 baseValue = baseValue * 10 + currentDigit;
884 }
885 } else if (ch === 0x2e) {
886 if (divideBy === 0) {
887 divideBy = 1;
888 } else {
889 break;
890 }
891 } else if (ch === 0x2d) {
892 (0, _util.warn)("Badly formatted number: minus sign in the middle");
893 } else if (ch === 0x45 || ch === 0x65) {
894 ch = this.peekChar();
895
896 if (ch === 0x2b || ch === 0x2d) {
897 powerValueSign = ch === 0x2d ? -1 : 1;
898 this.nextChar();
899 } else if (ch < 0x30 || ch > 0x39) {
900 break;
901 }
902
903 eNotation = true;
904 } else {
905 break;
906 }
907 }
908
909 if (divideBy !== 0) {
910 baseValue /= divideBy;
911 }
912
913 if (eNotation) {
914 baseValue *= 10 ** (powerValueSign * powerValue);
915 }
916
917 return sign * baseValue;
918 }
919
920 getString() {
921 let numParen = 1;
922 let done = false;
923 const strBuf = this.strBuf;
924 strBuf.length = 0;
925 let ch = this.nextChar();
926
927 while (true) {
928 let charBuffered = false;
929
930 switch (ch | 0) {
931 case -1:
932 (0, _util.warn)("Unterminated string");
933 done = true;
934 break;
935
936 case 0x28:
937 ++numParen;
938 strBuf.push("(");
939 break;
940
941 case 0x29:
942 if (--numParen === 0) {
943 this.nextChar();
944 done = true;
945 } else {
946 strBuf.push(")");
947 }
948
949 break;
950
951 case 0x5c:
952 ch = this.nextChar();
953
954 switch (ch) {
955 case -1:
956 (0, _util.warn)("Unterminated string");
957 done = true;
958 break;
959
960 case 0x6e:
961 strBuf.push("\n");
962 break;
963
964 case 0x72:
965 strBuf.push("\r");
966 break;
967
968 case 0x74:
969 strBuf.push("\t");
970 break;
971
972 case 0x62:
973 strBuf.push("\b");
974 break;
975
976 case 0x66:
977 strBuf.push("\f");
978 break;
979
980 case 0x5c:
981 case 0x28:
982 case 0x29:
983 strBuf.push(String.fromCharCode(ch));
984 break;
985
986 case 0x30:
987 case 0x31:
988 case 0x32:
989 case 0x33:
990 case 0x34:
991 case 0x35:
992 case 0x36:
993 case 0x37:
994 let x = ch & 0x0f;
995 ch = this.nextChar();
996 charBuffered = true;
997
998 if (ch >= 0x30 && ch <= 0x37) {
999 x = (x << 3) + (ch & 0x0f);
1000 ch = this.nextChar();
1001
1002 if (ch >= 0x30 && ch <= 0x37) {
1003 charBuffered = false;
1004 x = (x << 3) + (ch & 0x0f);
1005 }
1006 }
1007
1008 strBuf.push(String.fromCharCode(x));
1009 break;
1010
1011 case 0x0d:
1012 if (this.peekChar() === 0x0a) {
1013 this.nextChar();
1014 }
1015
1016 break;
1017
1018 case 0x0a:
1019 break;
1020
1021 default:
1022 strBuf.push(String.fromCharCode(ch));
1023 break;
1024 }
1025
1026 break;
1027
1028 default:
1029 strBuf.push(String.fromCharCode(ch));
1030 break;
1031 }
1032
1033 if (done) {
1034 break;
1035 }
1036
1037 if (!charBuffered) {
1038 ch = this.nextChar();
1039 }
1040 }
1041
1042 return strBuf.join("");
1043 }
1044
1045 getName() {
1046 let ch, previousCh;
1047 const strBuf = this.strBuf;
1048 strBuf.length = 0;
1049
1050 while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
1051 if (ch === 0x23) {
1052 ch = this.nextChar();
1053
1054 if (specialChars[ch]) {
1055 (0, _util.warn)("Lexer_getName: " + "NUMBER SIGN (#) should be followed by a hexadecimal number.");
1056 strBuf.push("#");
1057 break;
1058 }
1059
1060 const x = toHexDigit(ch);
1061
1062 if (x !== -1) {
1063 previousCh = ch;
1064 ch = this.nextChar();
1065 const x2 = toHexDigit(ch);
1066
1067 if (x2 === -1) {
1068 (0, _util.warn)(`Lexer_getName: Illegal digit (${String.fromCharCode(ch)}) ` + "in hexadecimal number.");
1069 strBuf.push("#", String.fromCharCode(previousCh));
1070
1071 if (specialChars[ch]) {
1072 break;
1073 }
1074
1075 strBuf.push(String.fromCharCode(ch));
1076 continue;
1077 }
1078
1079 strBuf.push(String.fromCharCode(x << 4 | x2));
1080 } else {
1081 strBuf.push("#", String.fromCharCode(ch));
1082 }
1083 } else {
1084 strBuf.push(String.fromCharCode(ch));
1085 }
1086 }
1087
1088 if (strBuf.length > 127) {
1089 (0, _util.warn)(`Name token is longer than allowed by the spec: ${strBuf.length}`);
1090 }
1091
1092 return _primitives.Name.get(strBuf.join(""));
1093 }
1094
1095 _hexStringWarn(ch) {
1096 const MAX_HEX_STRING_NUM_WARN = 5;
1097
1098 if (this._hexStringNumWarn++ === MAX_HEX_STRING_NUM_WARN) {
1099 (0, _util.warn)("getHexString - ignoring additional invalid characters.");
1100 return;
1101 }
1102
1103 if (this._hexStringNumWarn > MAX_HEX_STRING_NUM_WARN) {
1104 return;
1105 }
1106
1107 (0, _util.warn)(`getHexString - ignoring invalid character: ${ch}`);
1108 }
1109
1110 getHexString() {
1111 const strBuf = this.strBuf;
1112 strBuf.length = 0;
1113 let ch = this.currentChar;
1114 let isFirstHex = true;
1115 let firstDigit, secondDigit;
1116 this._hexStringNumWarn = 0;
1117
1118 while (true) {
1119 if (ch < 0) {
1120 (0, _util.warn)("Unterminated hex string");
1121 break;
1122 } else if (ch === 0x3e) {
1123 this.nextChar();
1124 break;
1125 } else if (specialChars[ch] === 1) {
1126 ch = this.nextChar();
1127 continue;
1128 } else {
1129 if (isFirstHex) {
1130 firstDigit = toHexDigit(ch);
1131
1132 if (firstDigit === -1) {
1133 this._hexStringWarn(ch);
1134
1135 ch = this.nextChar();
1136 continue;
1137 }
1138 } else {
1139 secondDigit = toHexDigit(ch);
1140
1141 if (secondDigit === -1) {
1142 this._hexStringWarn(ch);
1143
1144 ch = this.nextChar();
1145 continue;
1146 }
1147
1148 strBuf.push(String.fromCharCode(firstDigit << 4 | secondDigit));
1149 }
1150
1151 isFirstHex = !isFirstHex;
1152 ch = this.nextChar();
1153 }
1154 }
1155
1156 return strBuf.join("");
1157 }
1158
1159 getObj() {
1160 let comment = false;
1161 let ch = this.currentChar;
1162
1163 while (true) {
1164 if (ch < 0) {
1165 return _primitives.EOF;
1166 }
1167
1168 if (comment) {
1169 if (ch === 0x0a || ch === 0x0d) {
1170 comment = false;
1171 }
1172 } else if (ch === 0x25) {
1173 comment = true;
1174 } else if (specialChars[ch] !== 1) {
1175 break;
1176 }
1177
1178 ch = this.nextChar();
1179 }
1180
1181 switch (ch | 0) {
1182 case 0x30:
1183 case 0x31:
1184 case 0x32:
1185 case 0x33:
1186 case 0x34:
1187 case 0x35:
1188 case 0x36:
1189 case 0x37:
1190 case 0x38:
1191 case 0x39:
1192 case 0x2b:
1193 case 0x2d:
1194 case 0x2e:
1195 return this.getNumber();
1196
1197 case 0x28:
1198 return this.getString();
1199
1200 case 0x2f:
1201 return this.getName();
1202
1203 case 0x5b:
1204 this.nextChar();
1205 return _primitives.Cmd.get("[");
1206
1207 case 0x5d:
1208 this.nextChar();
1209 return _primitives.Cmd.get("]");
1210
1211 case 0x3c:
1212 ch = this.nextChar();
1213
1214 if (ch === 0x3c) {
1215 this.nextChar();
1216 return _primitives.Cmd.get("<<");
1217 }
1218
1219 return this.getHexString();
1220
1221 case 0x3e:
1222 ch = this.nextChar();
1223
1224 if (ch === 0x3e) {
1225 this.nextChar();
1226 return _primitives.Cmd.get(">>");
1227 }
1228
1229 return _primitives.Cmd.get(">");
1230
1231 case 0x7b:
1232 this.nextChar();
1233 return _primitives.Cmd.get("{");
1234
1235 case 0x7d:
1236 this.nextChar();
1237 return _primitives.Cmd.get("}");
1238
1239 case 0x29:
1240 this.nextChar();
1241 throw new _util.FormatError(`Illegal character: ${ch}`);
1242 }
1243
1244 let str = String.fromCharCode(ch);
1245
1246 if (ch < 0x20 || ch > 0x7f) {
1247 const nextCh = this.peekChar();
1248
1249 if (nextCh >= 0x20 && nextCh <= 0x7f) {
1250 this.nextChar();
1251 return _primitives.Cmd.get(str);
1252 }
1253 }
1254
1255 const knownCommands = this.knownCommands;
1256 let knownCommandFound = knownCommands && knownCommands[str] !== undefined;
1257
1258 while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
1259 const possibleCommand = str + String.fromCharCode(ch);
1260
1261 if (knownCommandFound && knownCommands[possibleCommand] === undefined) {
1262 break;
1263 }
1264
1265 if (str.length === 128) {
1266 throw new _util.FormatError(`Command token too long: ${str.length}`);
1267 }
1268
1269 str = possibleCommand;
1270 knownCommandFound = knownCommands && knownCommands[str] !== undefined;
1271 }
1272
1273 if (str === "true") {
1274 return true;
1275 }
1276
1277 if (str === "false") {
1278 return false;
1279 }
1280
1281 if (str === "null") {
1282 return null;
1283 }
1284
1285 if (str === "BI") {
1286 this.beginInlineImagePos = this.stream.pos;
1287 }
1288
1289 return _primitives.Cmd.get(str);
1290 }
1291
1292 peekObj() {
1293 const streamPos = this.stream.pos,
1294 currentChar = this.currentChar,
1295 beginInlineImagePos = this.beginInlineImagePos;
1296 let nextObj;
1297
1298 try {
1299 nextObj = this.getObj();
1300 } catch (ex) {
1301 if (ex instanceof _core_utils.MissingDataException) {
1302 throw ex;
1303 }
1304
1305 (0, _util.warn)(`peekObj: ${ex}`);
1306 }
1307
1308 this.stream.pos = streamPos;
1309 this.currentChar = currentChar;
1310 this.beginInlineImagePos = beginInlineImagePos;
1311 return nextObj;
1312 }
1313
1314 skipToNextLine() {
1315 let ch = this.currentChar;
1316
1317 while (ch >= 0) {
1318 if (ch === 0x0d) {
1319 ch = this.nextChar();
1320
1321 if (ch === 0x0a) {
1322 this.nextChar();
1323 }
1324
1325 break;
1326 } else if (ch === 0x0a) {
1327 this.nextChar();
1328 break;
1329 }
1330
1331 ch = this.nextChar();
1332 }
1333 }
1334
1335}
1336
1337exports.Lexer = Lexer;
1338
1339class Linearization {
1340 static create(stream) {
1341 function getInt(linDict, name, allowZeroValue = false) {
1342 const obj = linDict.get(name);
1343
1344 if (Number.isInteger(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {
1345 return obj;
1346 }
1347
1348 throw new Error(`The "${name}" parameter in the linearization ` + "dictionary is invalid.");
1349 }
1350
1351 function getHints(linDict) {
1352 const hints = linDict.get("H");
1353 let hintsLength;
1354
1355 if (Array.isArray(hints) && ((hintsLength = hints.length) === 2 || hintsLength === 4)) {
1356 for (let index = 0; index < hintsLength; index++) {
1357 const hint = hints[index];
1358
1359 if (!(Number.isInteger(hint) && hint > 0)) {
1360 throw new Error(`Hint (${index}) in the linearization dictionary is invalid.`);
1361 }
1362 }
1363
1364 return hints;
1365 }
1366
1367 throw new Error("Hint array in the linearization dictionary is invalid.");
1368 }
1369
1370 const parser = new Parser({
1371 lexer: new Lexer(stream),
1372 xref: null
1373 });
1374 const obj1 = parser.getObj();
1375 const obj2 = parser.getObj();
1376 const obj3 = parser.getObj();
1377 const linDict = parser.getObj();
1378 let obj, length;
1379
1380 if (!(Number.isInteger(obj1) && Number.isInteger(obj2) && (0, _primitives.isCmd)(obj3, "obj") && linDict instanceof _primitives.Dict && typeof (obj = linDict.get("Linearized")) === "number" && obj > 0)) {
1381 return null;
1382 } else if ((length = getInt(linDict, "L")) !== stream.length) {
1383 throw new Error('The "L" parameter in the linearization dictionary ' + "does not equal the stream length.");
1384 }
1385
1386 return {
1387 length,
1388 hints: getHints(linDict),
1389 objectNumberFirst: getInt(linDict, "O"),
1390 endFirst: getInt(linDict, "E"),
1391 numPages: getInt(linDict, "N"),
1392 mainXRefEntriesOffset: getInt(linDict, "T"),
1393 pageFirst: linDict.has("P") ? getInt(linDict, "P", true) : 0
1394 };
1395 }
1396
1397}
1398
1399exports.Linearization = Linearization;
\No newline at end of file