1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.XRef = void 0;
|
28 |
|
29 | var _util = require("../shared/util.js");
|
30 |
|
31 | var _primitives = require("./primitives.js");
|
32 |
|
33 | var _core_utils = require("./core_utils.js");
|
34 |
|
35 | var _parser = require("./parser.js");
|
36 |
|
37 | var _base_stream = require("./base_stream.js");
|
38 |
|
39 | var _crypto = require("./crypto.js");
|
40 |
|
41 | class XRef {
|
42 | constructor(stream, pdfManager) {
|
43 | this.stream = stream;
|
44 | this.pdfManager = pdfManager;
|
45 | this.entries = [];
|
46 | this.xrefstms = Object.create(null);
|
47 | this._cacheMap = new Map();
|
48 | this._pendingRefs = new _primitives.RefSet();
|
49 | this.stats = new _core_utils.DocStats(pdfManager.msgHandler);
|
50 | this._newRefNum = null;
|
51 | }
|
52 |
|
53 | getNewRef() {
|
54 | if (this._newRefNum === null) {
|
55 | this._newRefNum = this.entries.length || 1;
|
56 | }
|
57 |
|
58 | return _primitives.Ref.get(this._newRefNum++, 0);
|
59 | }
|
60 |
|
61 | resetNewRef() {
|
62 | this._newRefNum = null;
|
63 | }
|
64 |
|
65 | setStartXRef(startXRef) {
|
66 | this.startXRefQueue = [startXRef];
|
67 | }
|
68 |
|
69 | parse(recoveryMode = false) {
|
70 | let trailerDict;
|
71 |
|
72 | if (!recoveryMode) {
|
73 | trailerDict = this.readXRef();
|
74 | } else {
|
75 | (0, _util.warn)("Indexing all PDF objects");
|
76 | trailerDict = this.indexObjects();
|
77 | }
|
78 |
|
79 | trailerDict.assignXref(this);
|
80 | this.trailer = trailerDict;
|
81 | let encrypt;
|
82 |
|
83 | try {
|
84 | encrypt = trailerDict.get("Encrypt");
|
85 | } catch (ex) {
|
86 | if (ex instanceof _core_utils.MissingDataException) {
|
87 | throw ex;
|
88 | }
|
89 |
|
90 | (0, _util.warn)(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
|
91 | }
|
92 |
|
93 | if (encrypt instanceof _primitives.Dict) {
|
94 | const ids = trailerDict.get("ID");
|
95 | const fileId = ids && ids.length ? ids[0] : "";
|
96 | encrypt.suppressEncryption = true;
|
97 | this.encrypt = new _crypto.CipherTransformFactory(encrypt, fileId, this.pdfManager.password);
|
98 | }
|
99 |
|
100 | let root;
|
101 |
|
102 | try {
|
103 | root = trailerDict.get("Root");
|
104 | } catch (ex) {
|
105 | if (ex instanceof _core_utils.MissingDataException) {
|
106 | throw ex;
|
107 | }
|
108 |
|
109 | (0, _util.warn)(`XRef.parse - Invalid "Root" reference: "${ex}".`);
|
110 | }
|
111 |
|
112 | if (root instanceof _primitives.Dict) {
|
113 | try {
|
114 | const pages = root.get("Pages");
|
115 |
|
116 | if (pages instanceof _primitives.Dict) {
|
117 | this.root = root;
|
118 | return;
|
119 | }
|
120 | } catch (ex) {
|
121 | if (ex instanceof _core_utils.MissingDataException) {
|
122 | throw ex;
|
123 | }
|
124 |
|
125 | (0, _util.warn)(`XRef.parse - Invalid "Pages" reference: "${ex}".`);
|
126 | }
|
127 | }
|
128 |
|
129 | if (!recoveryMode) {
|
130 | throw new _core_utils.XRefParseException();
|
131 | }
|
132 |
|
133 | throw new _util.InvalidPDFException("Invalid Root reference.");
|
134 | }
|
135 |
|
136 | processXRefTable(parser) {
|
137 | if (!("tableState" in this)) {
|
138 | this.tableState = {
|
139 | entryNum: 0,
|
140 | streamPos: parser.lexer.stream.pos,
|
141 | parserBuf1: parser.buf1,
|
142 | parserBuf2: parser.buf2
|
143 | };
|
144 | }
|
145 |
|
146 | const obj = this.readXRefTable(parser);
|
147 |
|
148 | if (!(0, _primitives.isCmd)(obj, "trailer")) {
|
149 | throw new _util.FormatError("Invalid XRef table: could not find trailer dictionary");
|
150 | }
|
151 |
|
152 | let dict = parser.getObj();
|
153 |
|
154 | if (!(dict instanceof _primitives.Dict) && dict.dict) {
|
155 | dict = dict.dict;
|
156 | }
|
157 |
|
158 | if (!(dict instanceof _primitives.Dict)) {
|
159 | throw new _util.FormatError("Invalid XRef table: could not parse trailer dictionary");
|
160 | }
|
161 |
|
162 | delete this.tableState;
|
163 | return dict;
|
164 | }
|
165 |
|
166 | readXRefTable(parser) {
|
167 | const stream = parser.lexer.stream;
|
168 | const tableState = this.tableState;
|
169 | stream.pos = tableState.streamPos;
|
170 | parser.buf1 = tableState.parserBuf1;
|
171 | parser.buf2 = tableState.parserBuf2;
|
172 | let obj;
|
173 |
|
174 | while (true) {
|
175 | if (!("firstEntryNum" in tableState) || !("entryCount" in tableState)) {
|
176 | if ((0, _primitives.isCmd)(obj = parser.getObj(), "trailer")) {
|
177 | break;
|
178 | }
|
179 |
|
180 | tableState.firstEntryNum = obj;
|
181 | tableState.entryCount = parser.getObj();
|
182 | }
|
183 |
|
184 | let first = tableState.firstEntryNum;
|
185 | const count = tableState.entryCount;
|
186 |
|
187 | if (!Number.isInteger(first) || !Number.isInteger(count)) {
|
188 | throw new _util.FormatError("Invalid XRef table: wrong types in subsection header");
|
189 | }
|
190 |
|
191 | for (let i = tableState.entryNum; i < count; i++) {
|
192 | tableState.streamPos = stream.pos;
|
193 | tableState.entryNum = i;
|
194 | tableState.parserBuf1 = parser.buf1;
|
195 | tableState.parserBuf2 = parser.buf2;
|
196 | const entry = {};
|
197 | entry.offset = parser.getObj();
|
198 | entry.gen = parser.getObj();
|
199 | const type = parser.getObj();
|
200 |
|
201 | if (type instanceof _primitives.Cmd) {
|
202 | switch (type.cmd) {
|
203 | case "f":
|
204 | entry.free = true;
|
205 | break;
|
206 |
|
207 | case "n":
|
208 | entry.uncompressed = true;
|
209 | break;
|
210 | }
|
211 | }
|
212 |
|
213 | if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {
|
214 | throw new _util.FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`);
|
215 | }
|
216 |
|
217 | if (i === 0 && entry.free && first === 1) {
|
218 | first = 0;
|
219 | }
|
220 |
|
221 | if (!this.entries[i + first]) {
|
222 | this.entries[i + first] = entry;
|
223 | }
|
224 | }
|
225 |
|
226 | tableState.entryNum = 0;
|
227 | tableState.streamPos = stream.pos;
|
228 | tableState.parserBuf1 = parser.buf1;
|
229 | tableState.parserBuf2 = parser.buf2;
|
230 | delete tableState.firstEntryNum;
|
231 | delete tableState.entryCount;
|
232 | }
|
233 |
|
234 | if (this.entries[0] && !this.entries[0].free) {
|
235 | throw new _util.FormatError("Invalid XRef table: unexpected first object");
|
236 | }
|
237 |
|
238 | return obj;
|
239 | }
|
240 |
|
241 | processXRefStream(stream) {
|
242 | if (!("streamState" in this)) {
|
243 | const streamParameters = stream.dict;
|
244 | const byteWidths = streamParameters.get("W");
|
245 | let range = streamParameters.get("Index");
|
246 |
|
247 | if (!range) {
|
248 | range = [0, streamParameters.get("Size")];
|
249 | }
|
250 |
|
251 | this.streamState = {
|
252 | entryRanges: range,
|
253 | byteWidths,
|
254 | entryNum: 0,
|
255 | streamPos: stream.pos
|
256 | };
|
257 | }
|
258 |
|
259 | this.readXRefStream(stream);
|
260 | delete this.streamState;
|
261 | return stream.dict;
|
262 | }
|
263 |
|
264 | readXRefStream(stream) {
|
265 | const streamState = this.streamState;
|
266 | stream.pos = streamState.streamPos;
|
267 | const [typeFieldWidth, offsetFieldWidth, generationFieldWidth] = streamState.byteWidths;
|
268 | const entryRanges = streamState.entryRanges;
|
269 |
|
270 | while (entryRanges.length > 0) {
|
271 | const [first, n] = entryRanges;
|
272 |
|
273 | if (!Number.isInteger(first) || !Number.isInteger(n)) {
|
274 | throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
|
275 | }
|
276 |
|
277 | if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {
|
278 | throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
|
279 | }
|
280 |
|
281 | for (let i = streamState.entryNum; i < n; ++i) {
|
282 | streamState.entryNum = i;
|
283 | streamState.streamPos = stream.pos;
|
284 | let type = 0,
|
285 | offset = 0,
|
286 | generation = 0;
|
287 |
|
288 | for (let j = 0; j < typeFieldWidth; ++j) {
|
289 | const typeByte = stream.getByte();
|
290 |
|
291 | if (typeByte === -1) {
|
292 | throw new _util.FormatError("Invalid XRef byteWidths 'type'.");
|
293 | }
|
294 |
|
295 | type = type << 8 | typeByte;
|
296 | }
|
297 |
|
298 | if (typeFieldWidth === 0) {
|
299 | type = 1;
|
300 | }
|
301 |
|
302 | for (let j = 0; j < offsetFieldWidth; ++j) {
|
303 | const offsetByte = stream.getByte();
|
304 |
|
305 | if (offsetByte === -1) {
|
306 | throw new _util.FormatError("Invalid XRef byteWidths 'offset'.");
|
307 | }
|
308 |
|
309 | offset = offset << 8 | offsetByte;
|
310 | }
|
311 |
|
312 | for (let j = 0; j < generationFieldWidth; ++j) {
|
313 | const generationByte = stream.getByte();
|
314 |
|
315 | if (generationByte === -1) {
|
316 | throw new _util.FormatError("Invalid XRef byteWidths 'generation'.");
|
317 | }
|
318 |
|
319 | generation = generation << 8 | generationByte;
|
320 | }
|
321 |
|
322 | const entry = {};
|
323 | entry.offset = offset;
|
324 | entry.gen = generation;
|
325 |
|
326 | switch (type) {
|
327 | case 0:
|
328 | entry.free = true;
|
329 | break;
|
330 |
|
331 | case 1:
|
332 | entry.uncompressed = true;
|
333 | break;
|
334 |
|
335 | case 2:
|
336 | break;
|
337 |
|
338 | default:
|
339 | throw new _util.FormatError(`Invalid XRef entry type: ${type}`);
|
340 | }
|
341 |
|
342 | if (!this.entries[first + i]) {
|
343 | this.entries[first + i] = entry;
|
344 | }
|
345 | }
|
346 |
|
347 | streamState.entryNum = 0;
|
348 | streamState.streamPos = stream.pos;
|
349 | entryRanges.splice(0, 2);
|
350 | }
|
351 | }
|
352 |
|
353 | indexObjects() {
|
354 | const TAB = 0x9,
|
355 | LF = 0xa,
|
356 | CR = 0xd,
|
357 | SPACE = 0x20;
|
358 | const PERCENT = 0x25,
|
359 | LT = 0x3c;
|
360 |
|
361 | function readToken(data, offset) {
|
362 | let token = "",
|
363 | ch = data[offset];
|
364 |
|
365 | while (ch !== LF && ch !== CR && ch !== LT) {
|
366 | if (++offset >= data.length) {
|
367 | break;
|
368 | }
|
369 |
|
370 | token += String.fromCharCode(ch);
|
371 | ch = data[offset];
|
372 | }
|
373 |
|
374 | return token;
|
375 | }
|
376 |
|
377 | function skipUntil(data, offset, what) {
|
378 | const length = what.length,
|
379 | dataLength = data.length;
|
380 | let skipped = 0;
|
381 |
|
382 | while (offset < dataLength) {
|
383 | let i = 0;
|
384 |
|
385 | while (i < length && data[offset + i] === what[i]) {
|
386 | ++i;
|
387 | }
|
388 |
|
389 | if (i >= length) {
|
390 | break;
|
391 | }
|
392 |
|
393 | offset++;
|
394 | skipped++;
|
395 | }
|
396 |
|
397 | return skipped;
|
398 | }
|
399 |
|
400 | const objRegExp = /^(\d+)\s+(\d+)\s+obj\b/;
|
401 | const endobjRegExp = /\bendobj[\b\s]$/;
|
402 | const nestedObjRegExp = /\s+(\d+\s+\d+\s+obj[\b\s<])$/;
|
403 | const CHECK_CONTENT_LENGTH = 25;
|
404 | const trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
|
405 | const startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);
|
406 | const objBytes = new Uint8Array([111, 98, 106]);
|
407 | const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
|
408 | this.entries.length = 0;
|
409 |
|
410 | this._cacheMap.clear();
|
411 |
|
412 | const stream = this.stream;
|
413 | stream.pos = 0;
|
414 | const buffer = stream.getBytes(),
|
415 | length = buffer.length;
|
416 | let position = stream.start;
|
417 | const trailers = [],
|
418 | xrefStms = [];
|
419 |
|
420 | while (position < length) {
|
421 | let ch = buffer[position];
|
422 |
|
423 | if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {
|
424 | ++position;
|
425 | continue;
|
426 | }
|
427 |
|
428 | if (ch === PERCENT) {
|
429 | do {
|
430 | ++position;
|
431 |
|
432 | if (position >= length) {
|
433 | break;
|
434 | }
|
435 |
|
436 | ch = buffer[position];
|
437 | } while (ch !== LF && ch !== CR);
|
438 |
|
439 | continue;
|
440 | }
|
441 |
|
442 | const token = readToken(buffer, position);
|
443 | let m;
|
444 |
|
445 | if (token.startsWith("xref") && (token.length === 4 || /\s/.test(token[4]))) {
|
446 | position += skipUntil(buffer, position, trailerBytes);
|
447 | trailers.push(position);
|
448 | position += skipUntil(buffer, position, startxrefBytes);
|
449 | } else if (m = objRegExp.exec(token)) {
|
450 | const num = m[1] | 0,
|
451 | gen = m[2] | 0;
|
452 | let contentLength,
|
453 | startPos = position + token.length,
|
454 | updateEntries = false;
|
455 |
|
456 | if (!this.entries[num]) {
|
457 | updateEntries = true;
|
458 | } else if (this.entries[num].gen === gen) {
|
459 | try {
|
460 | const parser = new _parser.Parser({
|
461 | lexer: new _parser.Lexer(stream.makeSubStream(startPos))
|
462 | });
|
463 | parser.getObj();
|
464 | updateEntries = true;
|
465 | } catch (ex) {
|
466 | if (ex instanceof _core_utils.ParserEOFException) {
|
467 | (0, _util.warn)(`indexObjects -- checking object (${token}): "${ex}".`);
|
468 | } else {
|
469 | updateEntries = true;
|
470 | }
|
471 | }
|
472 | }
|
473 |
|
474 | if (updateEntries) {
|
475 | this.entries[num] = {
|
476 | offset: position - stream.start,
|
477 | gen,
|
478 | uncompressed: true
|
479 | };
|
480 | }
|
481 |
|
482 | while (startPos < buffer.length) {
|
483 | const endPos = startPos + skipUntil(buffer, startPos, objBytes) + 4;
|
484 | contentLength = endPos - position;
|
485 | const checkPos = Math.max(endPos - CHECK_CONTENT_LENGTH, startPos);
|
486 | const tokenStr = (0, _util.bytesToString)(buffer.subarray(checkPos, endPos));
|
487 |
|
488 | if (endobjRegExp.test(tokenStr)) {
|
489 | break;
|
490 | } else {
|
491 | const objToken = nestedObjRegExp.exec(tokenStr);
|
492 |
|
493 | if (objToken && objToken[1]) {
|
494 | (0, _util.warn)('indexObjects: Found new "obj" inside of another "obj", ' + 'caused by missing "endobj" -- trying to recover.');
|
495 | contentLength -= objToken[1].length;
|
496 | break;
|
497 | }
|
498 | }
|
499 |
|
500 | startPos = endPos;
|
501 | }
|
502 |
|
503 | const content = buffer.subarray(position, position + contentLength);
|
504 | const xrefTagOffset = skipUntil(content, 0, xrefBytes);
|
505 |
|
506 | if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {
|
507 | xrefStms.push(position - stream.start);
|
508 | this.xrefstms[position - stream.start] = 1;
|
509 | }
|
510 |
|
511 | position += contentLength;
|
512 | } else if (token.startsWith("trailer") && (token.length === 7 || /\s/.test(token[7]))) {
|
513 | trailers.push(position);
|
514 | position += skipUntil(buffer, position, startxrefBytes);
|
515 | } else {
|
516 | position += token.length + 1;
|
517 | }
|
518 | }
|
519 |
|
520 | for (let i = 0, ii = xrefStms.length; i < ii; ++i) {
|
521 | this.startXRefQueue.push(xrefStms[i]);
|
522 | this.readXRef(true);
|
523 | }
|
524 |
|
525 | let trailerDict;
|
526 |
|
527 | for (let i = 0, ii = trailers.length; i < ii; ++i) {
|
528 | stream.pos = trailers[i];
|
529 | const parser = new _parser.Parser({
|
530 | lexer: new _parser.Lexer(stream),
|
531 | xref: this,
|
532 | allowStreams: true,
|
533 | recoveryMode: true
|
534 | });
|
535 | const obj = parser.getObj();
|
536 |
|
537 | if (!(0, _primitives.isCmd)(obj, "trailer")) {
|
538 | continue;
|
539 | }
|
540 |
|
541 | const dict = parser.getObj();
|
542 |
|
543 | if (!(dict instanceof _primitives.Dict)) {
|
544 | continue;
|
545 | }
|
546 |
|
547 | try {
|
548 | const rootDict = dict.get("Root");
|
549 |
|
550 | if (!(rootDict instanceof _primitives.Dict)) {
|
551 | continue;
|
552 | }
|
553 |
|
554 | const pagesDict = rootDict.get("Pages");
|
555 |
|
556 | if (!(pagesDict instanceof _primitives.Dict)) {
|
557 | continue;
|
558 | }
|
559 |
|
560 | const pagesCount = pagesDict.get("Count");
|
561 |
|
562 | if (!Number.isInteger(pagesCount)) {
|
563 | continue;
|
564 | }
|
565 | } catch (ex) {
|
566 | continue;
|
567 | }
|
568 |
|
569 | if (dict.has("ID")) {
|
570 | return dict;
|
571 | }
|
572 |
|
573 | trailerDict = dict;
|
574 | }
|
575 |
|
576 | if (trailerDict) {
|
577 | return trailerDict;
|
578 | }
|
579 |
|
580 | if (this.topDict) {
|
581 | return this.topDict;
|
582 | }
|
583 |
|
584 | throw new _util.InvalidPDFException("Invalid PDF structure.");
|
585 | }
|
586 |
|
587 | readXRef(recoveryMode = false) {
|
588 | const stream = this.stream;
|
589 | const startXRefParsedCache = new Set();
|
590 |
|
591 | try {
|
592 | while (this.startXRefQueue.length) {
|
593 | const startXRef = this.startXRefQueue[0];
|
594 |
|
595 | if (startXRefParsedCache.has(startXRef)) {
|
596 | (0, _util.warn)("readXRef - skipping XRef table since it was already parsed.");
|
597 | this.startXRefQueue.shift();
|
598 | continue;
|
599 | }
|
600 |
|
601 | startXRefParsedCache.add(startXRef);
|
602 | stream.pos = startXRef + stream.start;
|
603 | const parser = new _parser.Parser({
|
604 | lexer: new _parser.Lexer(stream),
|
605 | xref: this,
|
606 | allowStreams: true
|
607 | });
|
608 | let obj = parser.getObj();
|
609 | let dict;
|
610 |
|
611 | if ((0, _primitives.isCmd)(obj, "xref")) {
|
612 | dict = this.processXRefTable(parser);
|
613 |
|
614 | if (!this.topDict) {
|
615 | this.topDict = dict;
|
616 | }
|
617 |
|
618 | obj = dict.get("XRefStm");
|
619 |
|
620 | if (Number.isInteger(obj)) {
|
621 | const pos = obj;
|
622 |
|
623 | if (!(pos in this.xrefstms)) {
|
624 | this.xrefstms[pos] = 1;
|
625 | this.startXRefQueue.push(pos);
|
626 | }
|
627 | }
|
628 | } else if (Number.isInteger(obj)) {
|
629 | if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), "obj") || !((obj = parser.getObj()) instanceof _base_stream.BaseStream)) {
|
630 | throw new _util.FormatError("Invalid XRef stream");
|
631 | }
|
632 |
|
633 | dict = this.processXRefStream(obj);
|
634 |
|
635 | if (!this.topDict) {
|
636 | this.topDict = dict;
|
637 | }
|
638 |
|
639 | if (!dict) {
|
640 | throw new _util.FormatError("Failed to read XRef stream");
|
641 | }
|
642 | } else {
|
643 | throw new _util.FormatError("Invalid XRef stream header");
|
644 | }
|
645 |
|
646 | obj = dict.get("Prev");
|
647 |
|
648 | if (Number.isInteger(obj)) {
|
649 | this.startXRefQueue.push(obj);
|
650 | } else if (obj instanceof _primitives.Ref) {
|
651 | this.startXRefQueue.push(obj.num);
|
652 | }
|
653 |
|
654 | this.startXRefQueue.shift();
|
655 | }
|
656 |
|
657 | return this.topDict;
|
658 | } catch (e) {
|
659 | if (e instanceof _core_utils.MissingDataException) {
|
660 | throw e;
|
661 | }
|
662 |
|
663 | (0, _util.info)("(while reading XRef): " + e);
|
664 | this.startXRefQueue.shift();
|
665 | }
|
666 |
|
667 | if (recoveryMode) {
|
668 | return undefined;
|
669 | }
|
670 |
|
671 | throw new _core_utils.XRefParseException();
|
672 | }
|
673 |
|
674 | getEntry(i) {
|
675 | const xrefEntry = this.entries[i];
|
676 |
|
677 | if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
|
678 | return xrefEntry;
|
679 | }
|
680 |
|
681 | return null;
|
682 | }
|
683 |
|
684 | fetchIfRef(obj, suppressEncryption = false) {
|
685 | if (obj instanceof _primitives.Ref) {
|
686 | return this.fetch(obj, suppressEncryption);
|
687 | }
|
688 |
|
689 | return obj;
|
690 | }
|
691 |
|
692 | fetch(ref, suppressEncryption = false) {
|
693 | if (!(ref instanceof _primitives.Ref)) {
|
694 | throw new Error("ref object is not a reference");
|
695 | }
|
696 |
|
697 | const num = ref.num;
|
698 |
|
699 | const cacheEntry = this._cacheMap.get(num);
|
700 |
|
701 | if (cacheEntry !== undefined) {
|
702 | if (cacheEntry instanceof _primitives.Dict && !cacheEntry.objId) {
|
703 | cacheEntry.objId = ref.toString();
|
704 | }
|
705 |
|
706 | return cacheEntry;
|
707 | }
|
708 |
|
709 | let xrefEntry = this.getEntry(num);
|
710 |
|
711 | if (xrefEntry === null) {
|
712 | this._cacheMap.set(num, xrefEntry);
|
713 |
|
714 | return xrefEntry;
|
715 | }
|
716 |
|
717 | if (this._pendingRefs.has(ref)) {
|
718 | this._pendingRefs.remove(ref);
|
719 |
|
720 | (0, _util.warn)(`Ignoring circular reference: ${ref}.`);
|
721 | return _primitives.CIRCULAR_REF;
|
722 | }
|
723 |
|
724 | this._pendingRefs.put(ref);
|
725 |
|
726 | try {
|
727 | if (xrefEntry.uncompressed) {
|
728 | xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
|
729 | } else {
|
730 | xrefEntry = this.fetchCompressed(ref, xrefEntry, suppressEncryption);
|
731 | }
|
732 |
|
733 | this._pendingRefs.remove(ref);
|
734 | } catch (ex) {
|
735 | this._pendingRefs.remove(ref);
|
736 |
|
737 | throw ex;
|
738 | }
|
739 |
|
740 | if (xrefEntry instanceof _primitives.Dict) {
|
741 | xrefEntry.objId = ref.toString();
|
742 | } else if (xrefEntry instanceof _base_stream.BaseStream) {
|
743 | xrefEntry.dict.objId = ref.toString();
|
744 | }
|
745 |
|
746 | return xrefEntry;
|
747 | }
|
748 |
|
749 | fetchUncompressed(ref, xrefEntry, suppressEncryption = false) {
|
750 | const gen = ref.gen;
|
751 | let num = ref.num;
|
752 |
|
753 | if (xrefEntry.gen !== gen) {
|
754 | throw new _core_utils.XRefEntryException(`Inconsistent generation in XRef: ${ref}`);
|
755 | }
|
756 |
|
757 | const stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start);
|
758 | const parser = new _parser.Parser({
|
759 | lexer: new _parser.Lexer(stream),
|
760 | xref: this,
|
761 | allowStreams: true
|
762 | });
|
763 | const obj1 = parser.getObj();
|
764 | const obj2 = parser.getObj();
|
765 | const obj3 = parser.getObj();
|
766 |
|
767 | if (obj1 !== num || obj2 !== gen || !(obj3 instanceof _primitives.Cmd)) {
|
768 | throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
|
769 | }
|
770 |
|
771 | if (obj3.cmd !== "obj") {
|
772 | if (obj3.cmd.startsWith("obj")) {
|
773 | num = parseInt(obj3.cmd.substring(3), 10);
|
774 |
|
775 | if (!Number.isNaN(num)) {
|
776 | return num;
|
777 | }
|
778 | }
|
779 |
|
780 | throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
|
781 | }
|
782 |
|
783 | if (this.encrypt && !suppressEncryption) {
|
784 | xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen));
|
785 | } else {
|
786 | xrefEntry = parser.getObj();
|
787 | }
|
788 |
|
789 | if (!(xrefEntry instanceof _base_stream.BaseStream)) {
|
790 | this._cacheMap.set(num, xrefEntry);
|
791 | }
|
792 |
|
793 | return xrefEntry;
|
794 | }
|
795 |
|
796 | fetchCompressed(ref, xrefEntry, suppressEncryption = false) {
|
797 | const tableOffset = xrefEntry.offset;
|
798 | const stream = this.fetch(_primitives.Ref.get(tableOffset, 0));
|
799 |
|
800 | if (!(stream instanceof _base_stream.BaseStream)) {
|
801 | throw new _util.FormatError("bad ObjStm stream");
|
802 | }
|
803 |
|
804 | const first = stream.dict.get("First");
|
805 | const n = stream.dict.get("N");
|
806 |
|
807 | if (!Number.isInteger(first) || !Number.isInteger(n)) {
|
808 | throw new _util.FormatError("invalid first and n parameters for ObjStm stream");
|
809 | }
|
810 |
|
811 | let parser = new _parser.Parser({
|
812 | lexer: new _parser.Lexer(stream),
|
813 | xref: this,
|
814 | allowStreams: true
|
815 | });
|
816 | const nums = new Array(n);
|
817 | const offsets = new Array(n);
|
818 |
|
819 | for (let i = 0; i < n; ++i) {
|
820 | const num = parser.getObj();
|
821 |
|
822 | if (!Number.isInteger(num)) {
|
823 | throw new _util.FormatError(`invalid object number in the ObjStm stream: ${num}`);
|
824 | }
|
825 |
|
826 | const offset = parser.getObj();
|
827 |
|
828 | if (!Number.isInteger(offset)) {
|
829 | throw new _util.FormatError(`invalid object offset in the ObjStm stream: ${offset}`);
|
830 | }
|
831 |
|
832 | nums[i] = num;
|
833 | offsets[i] = offset;
|
834 | }
|
835 |
|
836 | const start = (stream.start || 0) + first;
|
837 | const entries = new Array(n);
|
838 |
|
839 | for (let i = 0; i < n; ++i) {
|
840 | const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined;
|
841 |
|
842 | if (length < 0) {
|
843 | throw new _util.FormatError("Invalid offset in the ObjStm stream.");
|
844 | }
|
845 |
|
846 | parser = new _parser.Parser({
|
847 | lexer: new _parser.Lexer(stream.makeSubStream(start + offsets[i], length, stream.dict)),
|
848 | xref: this,
|
849 | allowStreams: true
|
850 | });
|
851 | const obj = parser.getObj();
|
852 | entries[i] = obj;
|
853 |
|
854 | if (obj instanceof _base_stream.BaseStream) {
|
855 | continue;
|
856 | }
|
857 |
|
858 | const num = nums[i],
|
859 | entry = this.entries[num];
|
860 |
|
861 | if (entry && entry.offset === tableOffset && entry.gen === i) {
|
862 | this._cacheMap.set(num, obj);
|
863 | }
|
864 | }
|
865 |
|
866 | xrefEntry = entries[xrefEntry.gen];
|
867 |
|
868 | if (xrefEntry === undefined) {
|
869 | throw new _core_utils.XRefEntryException(`Bad (compressed) XRef entry: ${ref}`);
|
870 | }
|
871 |
|
872 | return xrefEntry;
|
873 | }
|
874 |
|
875 | async fetchIfRefAsync(obj, suppressEncryption) {
|
876 | if (obj instanceof _primitives.Ref) {
|
877 | return this.fetchAsync(obj, suppressEncryption);
|
878 | }
|
879 |
|
880 | return obj;
|
881 | }
|
882 |
|
883 | async fetchAsync(ref, suppressEncryption) {
|
884 | try {
|
885 | return this.fetch(ref, suppressEncryption);
|
886 | } catch (ex) {
|
887 | if (!(ex instanceof _core_utils.MissingDataException)) {
|
888 | throw ex;
|
889 | }
|
890 |
|
891 | await this.pdfManager.requestRange(ex.begin, ex.end);
|
892 | return this.fetchAsync(ref, suppressEncryption);
|
893 | }
|
894 | }
|
895 |
|
896 | getCatalogObj() {
|
897 | return this.root;
|
898 | }
|
899 |
|
900 | }
|
901 |
|
902 | exports.XRef = XRef; |
\ | No newline at end of file |