UNPKG

42.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.Catalog = void 0;
28
29var _core_utils = require("./core_utils.js");
30
31var _util = require("../shared/util.js");
32
33var _primitives = require("./primitives.js");
34
35var _name_number_tree = require("./name_number_tree.js");
36
37var _base_stream = require("./base_stream.js");
38
39var _cleanup_helper = require("./cleanup_helper.js");
40
41var _colorspace = require("./colorspace.js");
42
43var _file_spec = require("./file_spec.js");
44
45var _image_utils = require("./image_utils.js");
46
47var _metadata_parser = require("./metadata_parser.js");
48
49var _struct_tree = require("./struct_tree.js");
50
51function fetchDestination(dest) {
52 if (dest instanceof _primitives.Dict) {
53 dest = dest.get("D");
54 }
55
56 return Array.isArray(dest) ? dest : null;
57}
58
59class Catalog {
60 constructor(pdfManager, xref) {
61 this.pdfManager = pdfManager;
62 this.xref = xref;
63 this._catDict = xref.getCatalogObj();
64
65 if (!(this._catDict instanceof _primitives.Dict)) {
66 throw new _util.FormatError("Catalog object is not a dictionary.");
67 }
68
69 this.toplevelPagesDict;
70 this._actualNumPages = null;
71 this.fontCache = new _primitives.RefSetCache();
72 this.builtInCMapCache = new Map();
73 this.standardFontDataCache = new Map();
74 this.globalImageCache = new _image_utils.GlobalImageCache();
75 this.pageKidsCountCache = new _primitives.RefSetCache();
76 this.pageIndexCache = new _primitives.RefSetCache();
77 this.nonBlendModesSet = new _primitives.RefSet();
78 }
79
80 get version() {
81 const version = this._catDict.get("Version");
82
83 return (0, _util.shadow)(this, "version", version instanceof _primitives.Name ? version.name : null);
84 }
85
86 get lang() {
87 const lang = this._catDict.get("Lang");
88
89 return (0, _util.shadow)(this, "lang", typeof lang === "string" ? (0, _util.stringToPDFString)(lang) : null);
90 }
91
92 get needsRendering() {
93 const needsRendering = this._catDict.get("NeedsRendering");
94
95 return (0, _util.shadow)(this, "needsRendering", typeof needsRendering === "boolean" ? needsRendering : false);
96 }
97
98 get collection() {
99 let collection = null;
100
101 try {
102 const obj = this._catDict.get("Collection");
103
104 if (obj instanceof _primitives.Dict && obj.size > 0) {
105 collection = obj;
106 }
107 } catch (ex) {
108 if (ex instanceof _core_utils.MissingDataException) {
109 throw ex;
110 }
111
112 (0, _util.info)("Cannot fetch Collection entry; assuming no collection is present.");
113 }
114
115 return (0, _util.shadow)(this, "collection", collection);
116 }
117
118 get acroForm() {
119 let acroForm = null;
120
121 try {
122 const obj = this._catDict.get("AcroForm");
123
124 if (obj instanceof _primitives.Dict && obj.size > 0) {
125 acroForm = obj;
126 }
127 } catch (ex) {
128 if (ex instanceof _core_utils.MissingDataException) {
129 throw ex;
130 }
131
132 (0, _util.info)("Cannot fetch AcroForm entry; assuming no forms are present.");
133 }
134
135 return (0, _util.shadow)(this, "acroForm", acroForm);
136 }
137
138 get acroFormRef() {
139 const value = this._catDict.getRaw("AcroForm");
140
141 return (0, _util.shadow)(this, "acroFormRef", value instanceof _primitives.Ref ? value : null);
142 }
143
144 get metadata() {
145 const streamRef = this._catDict.getRaw("Metadata");
146
147 if (!(streamRef instanceof _primitives.Ref)) {
148 return (0, _util.shadow)(this, "metadata", null);
149 }
150
151 let metadata = null;
152
153 try {
154 const suppressEncryption = !(this.xref.encrypt && this.xref.encrypt.encryptMetadata);
155 const stream = this.xref.fetch(streamRef, suppressEncryption);
156
157 if (stream instanceof _base_stream.BaseStream && stream.dict instanceof _primitives.Dict) {
158 const type = stream.dict.get("Type");
159 const subtype = stream.dict.get("Subtype");
160
161 if ((0, _primitives.isName)(type, "Metadata") && (0, _primitives.isName)(subtype, "XML")) {
162 const data = (0, _util.stringToUTF8String)(stream.getString());
163
164 if (data) {
165 metadata = new _metadata_parser.MetadataParser(data).serializable;
166 }
167 }
168 }
169 } catch (ex) {
170 if (ex instanceof _core_utils.MissingDataException) {
171 throw ex;
172 }
173
174 (0, _util.info)(`Skipping invalid Metadata: "${ex}".`);
175 }
176
177 return (0, _util.shadow)(this, "metadata", metadata);
178 }
179
180 get markInfo() {
181 let markInfo = null;
182
183 try {
184 markInfo = this._readMarkInfo();
185 } catch (ex) {
186 if (ex instanceof _core_utils.MissingDataException) {
187 throw ex;
188 }
189
190 (0, _util.warn)("Unable to read mark info.");
191 }
192
193 return (0, _util.shadow)(this, "markInfo", markInfo);
194 }
195
196 _readMarkInfo() {
197 const obj = this._catDict.get("MarkInfo");
198
199 if (!(obj instanceof _primitives.Dict)) {
200 return null;
201 }
202
203 const markInfo = {
204 Marked: false,
205 UserProperties: false,
206 Suspects: false
207 };
208
209 for (const key in markInfo) {
210 const value = obj.get(key);
211
212 if (typeof value === "boolean") {
213 markInfo[key] = value;
214 }
215 }
216
217 return markInfo;
218 }
219
220 get structTreeRoot() {
221 let structTree = null;
222
223 try {
224 structTree = this._readStructTreeRoot();
225 } catch (ex) {
226 if (ex instanceof _core_utils.MissingDataException) {
227 throw ex;
228 }
229
230 (0, _util.warn)("Unable read to structTreeRoot info.");
231 }
232
233 return (0, _util.shadow)(this, "structTreeRoot", structTree);
234 }
235
236 _readStructTreeRoot() {
237 const obj = this._catDict.get("StructTreeRoot");
238
239 if (!(obj instanceof _primitives.Dict)) {
240 return null;
241 }
242
243 const root = new _struct_tree.StructTreeRoot(obj);
244 root.init();
245 return root;
246 }
247
248 get toplevelPagesDict() {
249 const pagesObj = this._catDict.get("Pages");
250
251 if (!(pagesObj instanceof _primitives.Dict)) {
252 throw new _util.FormatError("Invalid top-level pages dictionary.");
253 }
254
255 return (0, _util.shadow)(this, "toplevelPagesDict", pagesObj);
256 }
257
258 get documentOutline() {
259 let obj = null;
260
261 try {
262 obj = this._readDocumentOutline();
263 } catch (ex) {
264 if (ex instanceof _core_utils.MissingDataException) {
265 throw ex;
266 }
267
268 (0, _util.warn)("Unable to read document outline.");
269 }
270
271 return (0, _util.shadow)(this, "documentOutline", obj);
272 }
273
274 _readDocumentOutline() {
275 let obj = this._catDict.get("Outlines");
276
277 if (!(obj instanceof _primitives.Dict)) {
278 return null;
279 }
280
281 obj = obj.getRaw("First");
282
283 if (!(obj instanceof _primitives.Ref)) {
284 return null;
285 }
286
287 const root = {
288 items: []
289 };
290 const queue = [{
291 obj,
292 parent: root
293 }];
294 const processed = new _primitives.RefSet();
295 processed.put(obj);
296 const xref = this.xref,
297 blackColor = new Uint8ClampedArray(3);
298
299 while (queue.length > 0) {
300 const i = queue.shift();
301 const outlineDict = xref.fetchIfRef(i.obj);
302
303 if (outlineDict === null) {
304 continue;
305 }
306
307 if (!outlineDict.has("Title")) {
308 throw new _util.FormatError("Invalid outline item encountered.");
309 }
310
311 const data = {
312 url: null,
313 dest: null
314 };
315 Catalog.parseDestDictionary({
316 destDict: outlineDict,
317 resultObj: data,
318 docBaseUrl: this.pdfManager.docBaseUrl
319 });
320 const title = outlineDict.get("Title");
321 const flags = outlineDict.get("F") || 0;
322 const color = outlineDict.getArray("C");
323 const count = outlineDict.get("Count");
324 let rgbColor = blackColor;
325
326 if (Array.isArray(color) && color.length === 3 && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {
327 rgbColor = _colorspace.ColorSpace.singletons.rgb.getRgb(color, 0);
328 }
329
330 const outlineItem = {
331 dest: data.dest,
332 url: data.url,
333 unsafeUrl: data.unsafeUrl,
334 newWindow: data.newWindow,
335 title: (0, _util.stringToPDFString)(title),
336 color: rgbColor,
337 count: Number.isInteger(count) ? count : undefined,
338 bold: !!(flags & 2),
339 italic: !!(flags & 1),
340 items: []
341 };
342 i.parent.items.push(outlineItem);
343 obj = outlineDict.getRaw("First");
344
345 if (obj instanceof _primitives.Ref && !processed.has(obj)) {
346 queue.push({
347 obj,
348 parent: outlineItem
349 });
350 processed.put(obj);
351 }
352
353 obj = outlineDict.getRaw("Next");
354
355 if (obj instanceof _primitives.Ref && !processed.has(obj)) {
356 queue.push({
357 obj,
358 parent: i.parent
359 });
360 processed.put(obj);
361 }
362 }
363
364 return root.items.length > 0 ? root.items : null;
365 }
366
367 get permissions() {
368 let permissions = null;
369
370 try {
371 permissions = this._readPermissions();
372 } catch (ex) {
373 if (ex instanceof _core_utils.MissingDataException) {
374 throw ex;
375 }
376
377 (0, _util.warn)("Unable to read permissions.");
378 }
379
380 return (0, _util.shadow)(this, "permissions", permissions);
381 }
382
383 _readPermissions() {
384 const encrypt = this.xref.trailer.get("Encrypt");
385
386 if (!(encrypt instanceof _primitives.Dict)) {
387 return null;
388 }
389
390 let flags = encrypt.get("P");
391
392 if (typeof flags !== "number") {
393 return null;
394 }
395
396 flags += 2 ** 32;
397 const permissions = [];
398
399 for (const key in _util.PermissionFlag) {
400 const value = _util.PermissionFlag[key];
401
402 if (flags & value) {
403 permissions.push(value);
404 }
405 }
406
407 return permissions;
408 }
409
410 get optionalContentConfig() {
411 let config = null;
412
413 try {
414 const properties = this._catDict.get("OCProperties");
415
416 if (!properties) {
417 return (0, _util.shadow)(this, "optionalContentConfig", null);
418 }
419
420 const defaultConfig = properties.get("D");
421
422 if (!defaultConfig) {
423 return (0, _util.shadow)(this, "optionalContentConfig", null);
424 }
425
426 const groupsData = properties.get("OCGs");
427
428 if (!Array.isArray(groupsData)) {
429 return (0, _util.shadow)(this, "optionalContentConfig", null);
430 }
431
432 const groups = [];
433 const groupRefs = [];
434
435 for (const groupRef of groupsData) {
436 if (!(groupRef instanceof _primitives.Ref)) {
437 continue;
438 }
439
440 groupRefs.push(groupRef);
441 const group = this.xref.fetchIfRef(groupRef);
442 groups.push({
443 id: groupRef.toString(),
444 name: typeof group.get("Name") === "string" ? (0, _util.stringToPDFString)(group.get("Name")) : null,
445 intent: typeof group.get("Intent") === "string" ? (0, _util.stringToPDFString)(group.get("Intent")) : null
446 });
447 }
448
449 config = this._readOptionalContentConfig(defaultConfig, groupRefs);
450 config.groups = groups;
451 } catch (ex) {
452 if (ex instanceof _core_utils.MissingDataException) {
453 throw ex;
454 }
455
456 (0, _util.warn)(`Unable to read optional content config: ${ex}`);
457 }
458
459 return (0, _util.shadow)(this, "optionalContentConfig", config);
460 }
461
462 _readOptionalContentConfig(config, contentGroupRefs) {
463 function parseOnOff(refs) {
464 const onParsed = [];
465
466 if (Array.isArray(refs)) {
467 for (const value of refs) {
468 if (!(value instanceof _primitives.Ref)) {
469 continue;
470 }
471
472 if (contentGroupRefs.includes(value)) {
473 onParsed.push(value.toString());
474 }
475 }
476 }
477
478 return onParsed;
479 }
480
481 function parseOrder(refs, nestedLevels = 0) {
482 if (!Array.isArray(refs)) {
483 return null;
484 }
485
486 const order = [];
487
488 for (const value of refs) {
489 if (value instanceof _primitives.Ref && contentGroupRefs.includes(value)) {
490 parsedOrderRefs.put(value);
491 order.push(value.toString());
492 continue;
493 }
494
495 const nestedOrder = parseNestedOrder(value, nestedLevels);
496
497 if (nestedOrder) {
498 order.push(nestedOrder);
499 }
500 }
501
502 if (nestedLevels > 0) {
503 return order;
504 }
505
506 const hiddenGroups = [];
507
508 for (const groupRef of contentGroupRefs) {
509 if (parsedOrderRefs.has(groupRef)) {
510 continue;
511 }
512
513 hiddenGroups.push(groupRef.toString());
514 }
515
516 if (hiddenGroups.length) {
517 order.push({
518 name: null,
519 order: hiddenGroups
520 });
521 }
522
523 return order;
524 }
525
526 function parseNestedOrder(ref, nestedLevels) {
527 if (++nestedLevels > MAX_NESTED_LEVELS) {
528 (0, _util.warn)("parseNestedOrder - reached MAX_NESTED_LEVELS.");
529 return null;
530 }
531
532 const value = xref.fetchIfRef(ref);
533
534 if (!Array.isArray(value)) {
535 return null;
536 }
537
538 const nestedName = xref.fetchIfRef(value[0]);
539
540 if (typeof nestedName !== "string") {
541 return null;
542 }
543
544 const nestedOrder = parseOrder(value.slice(1), nestedLevels);
545
546 if (!nestedOrder || !nestedOrder.length) {
547 return null;
548 }
549
550 return {
551 name: (0, _util.stringToPDFString)(nestedName),
552 order: nestedOrder
553 };
554 }
555
556 const xref = this.xref,
557 parsedOrderRefs = new _primitives.RefSet(),
558 MAX_NESTED_LEVELS = 10;
559 return {
560 name: typeof config.get("Name") === "string" ? (0, _util.stringToPDFString)(config.get("Name")) : null,
561 creator: typeof config.get("Creator") === "string" ? (0, _util.stringToPDFString)(config.get("Creator")) : null,
562 baseState: config.get("BaseState") instanceof _primitives.Name ? config.get("BaseState").name : null,
563 on: parseOnOff(config.get("ON")),
564 off: parseOnOff(config.get("OFF")),
565 order: parseOrder(config.get("Order")),
566 groups: null
567 };
568 }
569
570 setActualNumPages(num = null) {
571 this._actualNumPages = num;
572 }
573
574 get hasActualNumPages() {
575 return this._actualNumPages !== null;
576 }
577
578 get _pagesCount() {
579 const obj = this.toplevelPagesDict.get("Count");
580
581 if (!Number.isInteger(obj)) {
582 throw new _util.FormatError("Page count in top-level pages dictionary is not an integer.");
583 }
584
585 return (0, _util.shadow)(this, "_pagesCount", obj);
586 }
587
588 get numPages() {
589 return this.hasActualNumPages ? this._actualNumPages : this._pagesCount;
590 }
591
592 get destinations() {
593 const obj = this._readDests(),
594 dests = Object.create(null);
595
596 if (obj instanceof _name_number_tree.NameTree) {
597 for (const [key, value] of obj.getAll()) {
598 const dest = fetchDestination(value);
599
600 if (dest) {
601 dests[(0, _util.stringToPDFString)(key)] = dest;
602 }
603 }
604 } else if (obj instanceof _primitives.Dict) {
605 obj.forEach(function (key, value) {
606 const dest = fetchDestination(value);
607
608 if (dest) {
609 dests[key] = dest;
610 }
611 });
612 }
613
614 return (0, _util.shadow)(this, "destinations", dests);
615 }
616
617 getDestination(id) {
618 const obj = this._readDests();
619
620 if (obj instanceof _name_number_tree.NameTree) {
621 const dest = fetchDestination(obj.get(id));
622
623 if (dest) {
624 return dest;
625 }
626
627 const allDest = this.destinations[id];
628
629 if (allDest) {
630 (0, _util.warn)(`Found "${id}" at an incorrect position in the NameTree.`);
631 return allDest;
632 }
633 } else if (obj instanceof _primitives.Dict) {
634 const dest = fetchDestination(obj.get(id));
635
636 if (dest) {
637 return dest;
638 }
639 }
640
641 return null;
642 }
643
644 _readDests() {
645 const obj = this._catDict.get("Names");
646
647 if (obj && obj.has("Dests")) {
648 return new _name_number_tree.NameTree(obj.getRaw("Dests"), this.xref);
649 } else if (this._catDict.has("Dests")) {
650 return this._catDict.get("Dests");
651 }
652
653 return undefined;
654 }
655
656 get pageLabels() {
657 let obj = null;
658
659 try {
660 obj = this._readPageLabels();
661 } catch (ex) {
662 if (ex instanceof _core_utils.MissingDataException) {
663 throw ex;
664 }
665
666 (0, _util.warn)("Unable to read page labels.");
667 }
668
669 return (0, _util.shadow)(this, "pageLabels", obj);
670 }
671
672 _readPageLabels() {
673 const obj = this._catDict.getRaw("PageLabels");
674
675 if (!obj) {
676 return null;
677 }
678
679 const pageLabels = new Array(this.numPages);
680 let style = null,
681 prefix = "";
682 const numberTree = new _name_number_tree.NumberTree(obj, this.xref);
683 const nums = numberTree.getAll();
684 let currentLabel = "",
685 currentIndex = 1;
686
687 for (let i = 0, ii = this.numPages; i < ii; i++) {
688 const labelDict = nums.get(i);
689
690 if (labelDict !== undefined) {
691 if (!(labelDict instanceof _primitives.Dict)) {
692 throw new _util.FormatError("PageLabel is not a dictionary.");
693 }
694
695 if (labelDict.has("Type") && !(0, _primitives.isName)(labelDict.get("Type"), "PageLabel")) {
696 throw new _util.FormatError("Invalid type in PageLabel dictionary.");
697 }
698
699 if (labelDict.has("S")) {
700 const s = labelDict.get("S");
701
702 if (!(s instanceof _primitives.Name)) {
703 throw new _util.FormatError("Invalid style in PageLabel dictionary.");
704 }
705
706 style = s.name;
707 } else {
708 style = null;
709 }
710
711 if (labelDict.has("P")) {
712 const p = labelDict.get("P");
713
714 if (typeof p !== "string") {
715 throw new _util.FormatError("Invalid prefix in PageLabel dictionary.");
716 }
717
718 prefix = (0, _util.stringToPDFString)(p);
719 } else {
720 prefix = "";
721 }
722
723 if (labelDict.has("St")) {
724 const st = labelDict.get("St");
725
726 if (!(Number.isInteger(st) && st >= 1)) {
727 throw new _util.FormatError("Invalid start in PageLabel dictionary.");
728 }
729
730 currentIndex = st;
731 } else {
732 currentIndex = 1;
733 }
734 }
735
736 switch (style) {
737 case "D":
738 currentLabel = currentIndex;
739 break;
740
741 case "R":
742 case "r":
743 currentLabel = (0, _core_utils.toRomanNumerals)(currentIndex, style === "r");
744 break;
745
746 case "A":
747 case "a":
748 const LIMIT = 26;
749 const A_UPPER_CASE = 0x41,
750 A_LOWER_CASE = 0x61;
751 const baseCharCode = style === "a" ? A_LOWER_CASE : A_UPPER_CASE;
752 const letterIndex = currentIndex - 1;
753 const character = String.fromCharCode(baseCharCode + letterIndex % LIMIT);
754 currentLabel = character.repeat(Math.floor(letterIndex / LIMIT) + 1);
755 break;
756
757 default:
758 if (style) {
759 throw new _util.FormatError(`Invalid style "${style}" in PageLabel dictionary.`);
760 }
761
762 currentLabel = "";
763 }
764
765 pageLabels[i] = prefix + currentLabel;
766 currentIndex++;
767 }
768
769 return pageLabels;
770 }
771
772 get pageLayout() {
773 const obj = this._catDict.get("PageLayout");
774
775 let pageLayout = "";
776
777 if (obj instanceof _primitives.Name) {
778 switch (obj.name) {
779 case "SinglePage":
780 case "OneColumn":
781 case "TwoColumnLeft":
782 case "TwoColumnRight":
783 case "TwoPageLeft":
784 case "TwoPageRight":
785 pageLayout = obj.name;
786 }
787 }
788
789 return (0, _util.shadow)(this, "pageLayout", pageLayout);
790 }
791
792 get pageMode() {
793 const obj = this._catDict.get("PageMode");
794
795 let pageMode = "UseNone";
796
797 if (obj instanceof _primitives.Name) {
798 switch (obj.name) {
799 case "UseNone":
800 case "UseOutlines":
801 case "UseThumbs":
802 case "FullScreen":
803 case "UseOC":
804 case "UseAttachments":
805 pageMode = obj.name;
806 }
807 }
808
809 return (0, _util.shadow)(this, "pageMode", pageMode);
810 }
811
812 get viewerPreferences() {
813 const obj = this._catDict.get("ViewerPreferences");
814
815 if (!(obj instanceof _primitives.Dict)) {
816 return (0, _util.shadow)(this, "viewerPreferences", null);
817 }
818
819 let prefs = null;
820
821 for (const key of obj.getKeys()) {
822 const value = obj.get(key);
823 let prefValue;
824
825 switch (key) {
826 case "HideToolbar":
827 case "HideMenubar":
828 case "HideWindowUI":
829 case "FitWindow":
830 case "CenterWindow":
831 case "DisplayDocTitle":
832 case "PickTrayByPDFSize":
833 if (typeof value === "boolean") {
834 prefValue = value;
835 }
836
837 break;
838
839 case "NonFullScreenPageMode":
840 if (value instanceof _primitives.Name) {
841 switch (value.name) {
842 case "UseNone":
843 case "UseOutlines":
844 case "UseThumbs":
845 case "UseOC":
846 prefValue = value.name;
847 break;
848
849 default:
850 prefValue = "UseNone";
851 }
852 }
853
854 break;
855
856 case "Direction":
857 if (value instanceof _primitives.Name) {
858 switch (value.name) {
859 case "L2R":
860 case "R2L":
861 prefValue = value.name;
862 break;
863
864 default:
865 prefValue = "L2R";
866 }
867 }
868
869 break;
870
871 case "ViewArea":
872 case "ViewClip":
873 case "PrintArea":
874 case "PrintClip":
875 if (value instanceof _primitives.Name) {
876 switch (value.name) {
877 case "MediaBox":
878 case "CropBox":
879 case "BleedBox":
880 case "TrimBox":
881 case "ArtBox":
882 prefValue = value.name;
883 break;
884
885 default:
886 prefValue = "CropBox";
887 }
888 }
889
890 break;
891
892 case "PrintScaling":
893 if (value instanceof _primitives.Name) {
894 switch (value.name) {
895 case "None":
896 case "AppDefault":
897 prefValue = value.name;
898 break;
899
900 default:
901 prefValue = "AppDefault";
902 }
903 }
904
905 break;
906
907 case "Duplex":
908 if (value instanceof _primitives.Name) {
909 switch (value.name) {
910 case "Simplex":
911 case "DuplexFlipShortEdge":
912 case "DuplexFlipLongEdge":
913 prefValue = value.name;
914 break;
915
916 default:
917 prefValue = "None";
918 }
919 }
920
921 break;
922
923 case "PrintPageRange":
924 if (Array.isArray(value) && value.length % 2 === 0) {
925 const isValid = value.every((page, i, arr) => {
926 return Number.isInteger(page) && page > 0 && (i === 0 || page >= arr[i - 1]) && page <= this.numPages;
927 });
928
929 if (isValid) {
930 prefValue = value;
931 }
932 }
933
934 break;
935
936 case "NumCopies":
937 if (Number.isInteger(value) && value > 0) {
938 prefValue = value;
939 }
940
941 break;
942
943 default:
944 (0, _util.warn)(`Ignoring non-standard key in ViewerPreferences: ${key}.`);
945 continue;
946 }
947
948 if (prefValue === undefined) {
949 (0, _util.warn)(`Bad value, for key "${key}", in ViewerPreferences: ${value}.`);
950 continue;
951 }
952
953 if (!prefs) {
954 prefs = Object.create(null);
955 }
956
957 prefs[key] = prefValue;
958 }
959
960 return (0, _util.shadow)(this, "viewerPreferences", prefs);
961 }
962
963 get openAction() {
964 const obj = this._catDict.get("OpenAction");
965
966 const openAction = Object.create(null);
967
968 if (obj instanceof _primitives.Dict) {
969 const destDict = new _primitives.Dict(this.xref);
970 destDict.set("A", obj);
971 const resultObj = {
972 url: null,
973 dest: null,
974 action: null
975 };
976 Catalog.parseDestDictionary({
977 destDict,
978 resultObj
979 });
980
981 if (Array.isArray(resultObj.dest)) {
982 openAction.dest = resultObj.dest;
983 } else if (resultObj.action) {
984 openAction.action = resultObj.action;
985 }
986 } else if (Array.isArray(obj)) {
987 openAction.dest = obj;
988 }
989
990 return (0, _util.shadow)(this, "openAction", (0, _util.objectSize)(openAction) > 0 ? openAction : null);
991 }
992
993 get attachments() {
994 const obj = this._catDict.get("Names");
995
996 let attachments = null;
997
998 if (obj instanceof _primitives.Dict && obj.has("EmbeddedFiles")) {
999 const nameTree = new _name_number_tree.NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
1000
1001 for (const [key, value] of nameTree.getAll()) {
1002 const fs = new _file_spec.FileSpec(value, this.xref);
1003
1004 if (!attachments) {
1005 attachments = Object.create(null);
1006 }
1007
1008 attachments[(0, _util.stringToPDFString)(key)] = fs.serializable;
1009 }
1010 }
1011
1012 return (0, _util.shadow)(this, "attachments", attachments);
1013 }
1014
1015 get xfaImages() {
1016 const obj = this._catDict.get("Names");
1017
1018 let xfaImages = null;
1019
1020 if (obj instanceof _primitives.Dict && obj.has("XFAImages")) {
1021 const nameTree = new _name_number_tree.NameTree(obj.getRaw("XFAImages"), this.xref);
1022
1023 for (const [key, value] of nameTree.getAll()) {
1024 if (!xfaImages) {
1025 xfaImages = new _primitives.Dict(this.xref);
1026 }
1027
1028 xfaImages.set((0, _util.stringToPDFString)(key), value);
1029 }
1030 }
1031
1032 return (0, _util.shadow)(this, "xfaImages", xfaImages);
1033 }
1034
1035 _collectJavaScript() {
1036 const obj = this._catDict.get("Names");
1037
1038 let javaScript = null;
1039
1040 function appendIfJavaScriptDict(name, jsDict) {
1041 if (!(jsDict instanceof _primitives.Dict)) {
1042 return;
1043 }
1044
1045 if (!(0, _primitives.isName)(jsDict.get("S"), "JavaScript")) {
1046 return;
1047 }
1048
1049 let js = jsDict.get("JS");
1050
1051 if (js instanceof _base_stream.BaseStream) {
1052 js = js.getString();
1053 } else if (typeof js !== "string") {
1054 return;
1055 }
1056
1057 if (javaScript === null) {
1058 javaScript = new Map();
1059 }
1060
1061 js = (0, _util.stringToPDFString)(js).replace(/\u0000/g, "");
1062 javaScript.set(name, js);
1063 }
1064
1065 if (obj instanceof _primitives.Dict && obj.has("JavaScript")) {
1066 const nameTree = new _name_number_tree.NameTree(obj.getRaw("JavaScript"), this.xref);
1067
1068 for (const [key, value] of nameTree.getAll()) {
1069 appendIfJavaScriptDict((0, _util.stringToPDFString)(key), value);
1070 }
1071 }
1072
1073 const openAction = this._catDict.get("OpenAction");
1074
1075 if (openAction) {
1076 appendIfJavaScriptDict("OpenAction", openAction);
1077 }
1078
1079 return javaScript;
1080 }
1081
1082 get javaScript() {
1083 const javaScript = this._collectJavaScript();
1084
1085 return (0, _util.shadow)(this, "javaScript", javaScript ? [...javaScript.values()] : null);
1086 }
1087
1088 get jsActions() {
1089 const javaScript = this._collectJavaScript();
1090
1091 let actions = (0, _core_utils.collectActions)(this.xref, this._catDict, _util.DocumentActionEventType);
1092
1093 if (javaScript) {
1094 if (!actions) {
1095 actions = Object.create(null);
1096 }
1097
1098 for (const [key, val] of javaScript) {
1099 if (key in actions) {
1100 actions[key].push(val);
1101 } else {
1102 actions[key] = [val];
1103 }
1104 }
1105 }
1106
1107 return (0, _util.shadow)(this, "jsActions", actions);
1108 }
1109
1110 async fontFallback(id, handler) {
1111 const translatedFonts = await Promise.all(this.fontCache);
1112
1113 for (const translatedFont of translatedFonts) {
1114 if (translatedFont.loadedName === id) {
1115 translatedFont.fallback(handler);
1116 return;
1117 }
1118 }
1119 }
1120
1121 async cleanup(manuallyTriggered = false) {
1122 (0, _cleanup_helper.clearGlobalCaches)();
1123 this.globalImageCache.clear(manuallyTriggered);
1124 this.pageKidsCountCache.clear();
1125 this.pageIndexCache.clear();
1126 this.nonBlendModesSet.clear();
1127 const translatedFonts = await Promise.all(this.fontCache);
1128
1129 for (const {
1130 dict
1131 } of translatedFonts) {
1132 delete dict.cacheKey;
1133 }
1134
1135 this.fontCache.clear();
1136 this.builtInCMapCache.clear();
1137 this.standardFontDataCache.clear();
1138 }
1139
1140 async getPageDict(pageIndex) {
1141 const nodesToVisit = [this.toplevelPagesDict];
1142 const visitedNodes = new _primitives.RefSet();
1143
1144 const pagesRef = this._catDict.getRaw("Pages");
1145
1146 if (pagesRef instanceof _primitives.Ref) {
1147 visitedNodes.put(pagesRef);
1148 }
1149
1150 const xref = this.xref,
1151 pageKidsCountCache = this.pageKidsCountCache,
1152 pageIndexCache = this.pageIndexCache;
1153 let currentPageIndex = 0;
1154
1155 while (nodesToVisit.length) {
1156 const currentNode = nodesToVisit.pop();
1157
1158 if (currentNode instanceof _primitives.Ref) {
1159 const count = pageKidsCountCache.get(currentNode);
1160
1161 if (count >= 0 && currentPageIndex + count <= pageIndex) {
1162 currentPageIndex += count;
1163 continue;
1164 }
1165
1166 if (visitedNodes.has(currentNode)) {
1167 throw new _util.FormatError("Pages tree contains circular reference.");
1168 }
1169
1170 visitedNodes.put(currentNode);
1171 const obj = await xref.fetchAsync(currentNode);
1172
1173 if (obj instanceof _primitives.Dict) {
1174 let type = obj.getRaw("Type");
1175
1176 if (type instanceof _primitives.Ref) {
1177 type = await xref.fetchAsync(type);
1178 }
1179
1180 if ((0, _primitives.isName)(type, "Page") || !obj.has("Kids")) {
1181 if (!pageKidsCountCache.has(currentNode)) {
1182 pageKidsCountCache.put(currentNode, 1);
1183 }
1184
1185 if (!pageIndexCache.has(currentNode)) {
1186 pageIndexCache.put(currentNode, currentPageIndex);
1187 }
1188
1189 if (currentPageIndex === pageIndex) {
1190 return [obj, currentNode];
1191 }
1192
1193 currentPageIndex++;
1194 continue;
1195 }
1196 }
1197
1198 nodesToVisit.push(obj);
1199 continue;
1200 }
1201
1202 if (!(currentNode instanceof _primitives.Dict)) {
1203 throw new _util.FormatError("Page dictionary kid reference points to wrong type of object.");
1204 }
1205
1206 const {
1207 objId
1208 } = currentNode;
1209 let count = currentNode.getRaw("Count");
1210
1211 if (count instanceof _primitives.Ref) {
1212 count = await xref.fetchAsync(count);
1213 }
1214
1215 if (Number.isInteger(count) && count >= 0) {
1216 if (objId && !pageKidsCountCache.has(objId)) {
1217 pageKidsCountCache.put(objId, count);
1218 }
1219
1220 if (currentPageIndex + count <= pageIndex) {
1221 currentPageIndex += count;
1222 continue;
1223 }
1224 }
1225
1226 let kids = currentNode.getRaw("Kids");
1227
1228 if (kids instanceof _primitives.Ref) {
1229 kids = await xref.fetchAsync(kids);
1230 }
1231
1232 if (!Array.isArray(kids)) {
1233 let type = currentNode.getRaw("Type");
1234
1235 if (type instanceof _primitives.Ref) {
1236 type = await xref.fetchAsync(type);
1237 }
1238
1239 if ((0, _primitives.isName)(type, "Page") || !currentNode.has("Kids")) {
1240 if (currentPageIndex === pageIndex) {
1241 return [currentNode, null];
1242 }
1243
1244 currentPageIndex++;
1245 continue;
1246 }
1247
1248 throw new _util.FormatError("Page dictionary kids object is not an array.");
1249 }
1250
1251 for (let last = kids.length - 1; last >= 0; last--) {
1252 nodesToVisit.push(kids[last]);
1253 }
1254 }
1255
1256 throw new Error(`Page index ${pageIndex} not found.`);
1257 }
1258
1259 async getAllPageDicts(recoveryMode = false) {
1260 const queue = [{
1261 currentNode: this.toplevelPagesDict,
1262 posInKids: 0
1263 }];
1264 const visitedNodes = new _primitives.RefSet();
1265
1266 const pagesRef = this._catDict.getRaw("Pages");
1267
1268 if (pagesRef instanceof _primitives.Ref) {
1269 visitedNodes.put(pagesRef);
1270 }
1271
1272 const map = new Map(),
1273 xref = this.xref,
1274 pageIndexCache = this.pageIndexCache;
1275 let pageIndex = 0;
1276
1277 function addPageDict(pageDict, pageRef) {
1278 if (pageRef && !pageIndexCache.has(pageRef)) {
1279 pageIndexCache.put(pageRef, pageIndex);
1280 }
1281
1282 map.set(pageIndex++, [pageDict, pageRef]);
1283 }
1284
1285 function addPageError(error) {
1286 if (error instanceof _core_utils.XRefEntryException && !recoveryMode) {
1287 throw error;
1288 }
1289
1290 map.set(pageIndex++, [error, null]);
1291 }
1292
1293 while (queue.length > 0) {
1294 const queueItem = queue.at(-1);
1295 const {
1296 currentNode,
1297 posInKids
1298 } = queueItem;
1299 let kids = currentNode.getRaw("Kids");
1300
1301 if (kids instanceof _primitives.Ref) {
1302 try {
1303 kids = await xref.fetchAsync(kids);
1304 } catch (ex) {
1305 addPageError(ex);
1306 break;
1307 }
1308 }
1309
1310 if (!Array.isArray(kids)) {
1311 addPageError(new _util.FormatError("Page dictionary kids object is not an array."));
1312 break;
1313 }
1314
1315 if (posInKids >= kids.length) {
1316 queue.pop();
1317 continue;
1318 }
1319
1320 const kidObj = kids[posInKids];
1321 let obj;
1322
1323 if (kidObj instanceof _primitives.Ref) {
1324 if (visitedNodes.has(kidObj)) {
1325 addPageError(new _util.FormatError("Pages tree contains circular reference."));
1326 break;
1327 }
1328
1329 visitedNodes.put(kidObj);
1330
1331 try {
1332 obj = await xref.fetchAsync(kidObj);
1333 } catch (ex) {
1334 addPageError(ex);
1335 break;
1336 }
1337 } else {
1338 obj = kidObj;
1339 }
1340
1341 if (!(obj instanceof _primitives.Dict)) {
1342 addPageError(new _util.FormatError("Page dictionary kid reference points to wrong type of object."));
1343 break;
1344 }
1345
1346 let type = obj.getRaw("Type");
1347
1348 if (type instanceof _primitives.Ref) {
1349 try {
1350 type = await xref.fetchAsync(type);
1351 } catch (ex) {
1352 addPageError(ex);
1353 break;
1354 }
1355 }
1356
1357 if ((0, _primitives.isName)(type, "Page") || !obj.has("Kids")) {
1358 addPageDict(obj, kidObj instanceof _primitives.Ref ? kidObj : null);
1359 } else {
1360 queue.push({
1361 currentNode: obj,
1362 posInKids: 0
1363 });
1364 }
1365
1366 queueItem.posInKids++;
1367 }
1368
1369 return map;
1370 }
1371
1372 getPageIndex(pageRef) {
1373 const cachedPageIndex = this.pageIndexCache.get(pageRef);
1374
1375 if (cachedPageIndex !== undefined) {
1376 return Promise.resolve(cachedPageIndex);
1377 }
1378
1379 const xref = this.xref;
1380
1381 function pagesBeforeRef(kidRef) {
1382 let total = 0,
1383 parentRef;
1384 return xref.fetchAsync(kidRef).then(function (node) {
1385 if ((0, _primitives.isRefsEqual)(kidRef, pageRef) && !(0, _primitives.isDict)(node, "Page") && !(node instanceof _primitives.Dict && !node.has("Type") && node.has("Contents"))) {
1386 throw new _util.FormatError("The reference does not point to a /Page dictionary.");
1387 }
1388
1389 if (!node) {
1390 return null;
1391 }
1392
1393 if (!(node instanceof _primitives.Dict)) {
1394 throw new _util.FormatError("Node must be a dictionary.");
1395 }
1396
1397 parentRef = node.getRaw("Parent");
1398 return node.getAsync("Parent");
1399 }).then(function (parent) {
1400 if (!parent) {
1401 return null;
1402 }
1403
1404 if (!(parent instanceof _primitives.Dict)) {
1405 throw new _util.FormatError("Parent must be a dictionary.");
1406 }
1407
1408 return parent.getAsync("Kids");
1409 }).then(function (kids) {
1410 if (!kids) {
1411 return null;
1412 }
1413
1414 const kidPromises = [];
1415 let found = false;
1416
1417 for (let i = 0, ii = kids.length; i < ii; i++) {
1418 const kid = kids[i];
1419
1420 if (!(kid instanceof _primitives.Ref)) {
1421 throw new _util.FormatError("Kid must be a reference.");
1422 }
1423
1424 if ((0, _primitives.isRefsEqual)(kid, kidRef)) {
1425 found = true;
1426 break;
1427 }
1428
1429 kidPromises.push(xref.fetchAsync(kid).then(function (obj) {
1430 if (!(obj instanceof _primitives.Dict)) {
1431 throw new _util.FormatError("Kid node must be a dictionary.");
1432 }
1433
1434 if (obj.has("Count")) {
1435 total += obj.get("Count");
1436 } else {
1437 total++;
1438 }
1439 }));
1440 }
1441
1442 if (!found) {
1443 throw new _util.FormatError("Kid reference not found in parent's kids.");
1444 }
1445
1446 return Promise.all(kidPromises).then(function () {
1447 return [total, parentRef];
1448 });
1449 });
1450 }
1451
1452 let total = 0;
1453
1454 const next = ref => pagesBeforeRef(ref).then(args => {
1455 if (!args) {
1456 this.pageIndexCache.put(pageRef, total);
1457 return total;
1458 }
1459
1460 const [count, parentRef] = args;
1461 total += count;
1462 return next(parentRef);
1463 });
1464
1465 return next(pageRef);
1466 }
1467
1468 get baseUrl() {
1469 const uri = this._catDict.get("URI");
1470
1471 if (uri instanceof _primitives.Dict) {
1472 const base = uri.get("Base");
1473
1474 if (typeof base === "string") {
1475 const absoluteUrl = (0, _util.createValidAbsoluteUrl)(base, null, {
1476 tryConvertEncoding: true
1477 });
1478
1479 if (absoluteUrl) {
1480 return (0, _util.shadow)(this, "baseUrl", absoluteUrl.href);
1481 }
1482 }
1483 }
1484
1485 return (0, _util.shadow)(this, "baseUrl", null);
1486 }
1487
1488 static parseDestDictionary(params) {
1489 const destDict = params.destDict;
1490
1491 if (!(destDict instanceof _primitives.Dict)) {
1492 (0, _util.warn)("parseDestDictionary: `destDict` must be a dictionary.");
1493 return;
1494 }
1495
1496 const resultObj = params.resultObj;
1497
1498 if (typeof resultObj !== "object") {
1499 (0, _util.warn)("parseDestDictionary: `resultObj` must be an object.");
1500 return;
1501 }
1502
1503 const docBaseUrl = params.docBaseUrl || null;
1504 let action = destDict.get("A"),
1505 url,
1506 dest;
1507
1508 if (!(action instanceof _primitives.Dict)) {
1509 if (destDict.has("Dest")) {
1510 action = destDict.get("Dest");
1511 } else {
1512 action = destDict.get("AA");
1513
1514 if (action instanceof _primitives.Dict) {
1515 if (action.has("D")) {
1516 action = action.get("D");
1517 } else if (action.has("U")) {
1518 action = action.get("U");
1519 }
1520 }
1521 }
1522 }
1523
1524 if (action instanceof _primitives.Dict) {
1525 const actionType = action.get("S");
1526
1527 if (!(actionType instanceof _primitives.Name)) {
1528 (0, _util.warn)("parseDestDictionary: Invalid type in Action dictionary.");
1529 return;
1530 }
1531
1532 const actionName = actionType.name;
1533
1534 switch (actionName) {
1535 case "ResetForm":
1536 const flags = action.get("Flags");
1537 const include = ((typeof flags === "number" ? flags : 0) & 1) === 0;
1538 const fields = [];
1539 const refs = [];
1540
1541 for (const obj of action.get("Fields") || []) {
1542 if (obj instanceof _primitives.Ref) {
1543 refs.push(obj.toString());
1544 } else if (typeof obj === "string") {
1545 fields.push((0, _util.stringToPDFString)(obj));
1546 }
1547 }
1548
1549 resultObj.resetForm = {
1550 fields,
1551 refs,
1552 include
1553 };
1554 break;
1555
1556 case "URI":
1557 url = action.get("URI");
1558
1559 if (url instanceof _primitives.Name) {
1560 url = "/" + url.name;
1561 }
1562
1563 break;
1564
1565 case "GoTo":
1566 dest = action.get("D");
1567 break;
1568
1569 case "Launch":
1570 case "GoToR":
1571 const urlDict = action.get("F");
1572
1573 if (urlDict instanceof _primitives.Dict) {
1574 url = urlDict.get("F") || null;
1575 } else if (typeof urlDict === "string") {
1576 url = urlDict;
1577 }
1578
1579 let remoteDest = action.get("D");
1580
1581 if (remoteDest) {
1582 if (remoteDest instanceof _primitives.Name) {
1583 remoteDest = remoteDest.name;
1584 }
1585
1586 if (typeof url === "string") {
1587 const baseUrl = url.split("#")[0];
1588
1589 if (typeof remoteDest === "string") {
1590 url = baseUrl + "#" + remoteDest;
1591 } else if (Array.isArray(remoteDest)) {
1592 url = baseUrl + "#" + JSON.stringify(remoteDest);
1593 }
1594 }
1595 }
1596
1597 const newWindow = action.get("NewWindow");
1598
1599 if (typeof newWindow === "boolean") {
1600 resultObj.newWindow = newWindow;
1601 }
1602
1603 break;
1604
1605 case "Named":
1606 const namedAction = action.get("N");
1607
1608 if (namedAction instanceof _primitives.Name) {
1609 resultObj.action = namedAction.name;
1610 }
1611
1612 break;
1613
1614 case "JavaScript":
1615 const jsAction = action.get("JS");
1616 let js;
1617
1618 if (jsAction instanceof _base_stream.BaseStream) {
1619 js = jsAction.getString();
1620 } else if (typeof jsAction === "string") {
1621 js = jsAction;
1622 }
1623
1624 const jsURL = js && (0, _core_utils.recoverJsURL)((0, _util.stringToPDFString)(js));
1625
1626 if (jsURL) {
1627 url = jsURL.url;
1628 resultObj.newWindow = jsURL.newWindow;
1629 break;
1630 }
1631
1632 default:
1633 if (actionName === "JavaScript" || actionName === "SubmitForm") {
1634 break;
1635 }
1636
1637 (0, _util.warn)(`parseDestDictionary - unsupported action: "${actionName}".`);
1638 break;
1639 }
1640 } else if (destDict.has("Dest")) {
1641 dest = destDict.get("Dest");
1642 }
1643
1644 if (typeof url === "string") {
1645 const absoluteUrl = (0, _util.createValidAbsoluteUrl)(url, docBaseUrl, {
1646 addDefaultProtocol: true,
1647 tryConvertEncoding: true
1648 });
1649
1650 if (absoluteUrl) {
1651 resultObj.url = absoluteUrl.href;
1652 }
1653
1654 resultObj.unsafeUrl = url;
1655 }
1656
1657 if (dest) {
1658 if (dest instanceof _primitives.Name) {
1659 dest = dest.name;
1660 }
1661
1662 if (typeof dest === "string") {
1663 resultObj.dest = (0, _util.stringToPDFString)(dest);
1664 } else if (Array.isArray(dest)) {
1665 resultObj.dest = dest;
1666 }
1667 }
1668 }
1669
1670}
1671
1672exports.Catalog = Catalog;
\No newline at end of file