: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
for (const [key, value] of obj.getAll()) {
const dest = fetchDest(value);
dests[stringToPDFString(key)] = dest;
} else if (obj instanceof Dict) {
obj.forEach(function (key, value) {
const dest = fetchDest(value);
return shadow(this, "destinations", dests);
const obj = this._readDests();
if (obj instanceof NameTree) {
const dest = fetchDest(obj.get(id));
const allDest = this.destinations[id];
warn(`Found "${id}" at an incorrect position in the NameTree.`);
} else if (obj instanceof Dict) {
const dest = fetchDest(obj.get(id));
const obj = this._catDict.get("Names");
return new NameTree(obj.getRaw("Dests"), this.xref);
} else if (this._catDict.has("Dests")) {
return this._catDict.get("Dests");
obj = this._readPageLabels();
if (ex instanceof MissingDataException) {
warn("Unable to read page labels.");
return shadow(this, "pageLabels", obj);
const obj = this._catDict.getRaw("PageLabels");
const pageLabels = new Array(this.numPages);
const numberTree = new NumberTree(obj, this.xref);
const nums = numberTree.getAll();
for (let i = 0, ii = this.numPages; i < ii; i++) {
const labelDict = nums.get(i);
if (labelDict !== undefined) {
if (!(labelDict instanceof Dict)) {
throw new FormatError("PageLabel is not a dictionary.");
if (labelDict.has("Type") && !isName(labelDict.get("Type"), "PageLabel")) {
throw new FormatError("Invalid type in PageLabel dictionary.");
if (labelDict.has("S")) {
const s = labelDict.get("S");
if (!(s instanceof Name)) {
throw new FormatError("Invalid style in PageLabel dictionary.");
if (labelDict.has("P")) {
const p = labelDict.get("P");
if (typeof p !== "string") {
throw new FormatError("Invalid prefix in PageLabel dictionary.");
prefix = stringToPDFString(p);
if (labelDict.has("St")) {
const st = labelDict.get("St");
if (!(Number.isInteger(st) && st >= 1)) {
throw new FormatError("Invalid start in PageLabel dictionary.");
currentLabel = currentIndex;
currentLabel = toRomanNumerals(currentIndex, style === "r");
const A_UPPER_CASE = 0x41,
const baseCharCode = style === "a" ? A_LOWER_CASE : A_UPPER_CASE;
const letterIndex = currentIndex - 1;
const character = String.fromCharCode(baseCharCode + letterIndex % LIMIT);
currentLabel = character.repeat(Math.floor(letterIndex / LIMIT) + 1);
throw new FormatError(`Invalid style "${style}" in PageLabel dictionary.`);
pageLabels[i] = prefix + currentLabel;
const obj = this._catDict.get("PageLayout");
if (obj instanceof Name) {
return shadow(this, "pageLayout", pageLayout);
const obj = this._catDict.get("PageMode");
let pageMode = "UseNone";
if (obj instanceof Name) {
return shadow(this, "pageMode", pageMode);
get viewerPreferences() {
const obj = this._catDict.get("ViewerPreferences");
if (!(obj instanceof Dict)) {
return shadow(this, "viewerPreferences", null);
for (const key of obj.getKeys()) {
const value = obj.get(key);
case "PickTrayByPDFSize":
if (typeof value === "boolean") {
case "NonFullScreenPageMode":
if (value instanceof Name) {
if (value instanceof Name) {
if (value instanceof Name) {
if (value instanceof Name) {
prefValue = "AppDefault";
if (value instanceof Name) {
case "DuplexFlipShortEdge":
case "DuplexFlipLongEdge":
if (Array.isArray(value) && value.length % 2 === 0) {
const isValid = value.every((page, i, arr) => Number.isInteger(page) && page > 0 && (i === 0 || page >= arr[i - 1]) && page <= this.numPages);
if (Number.isInteger(value) && value > 0) {
warn(`Ignoring non-standard key in ViewerPreferences: ${key}.`);
if (prefValue === undefined) {
warn(`Bad value, for key "${key}", in ViewerPreferences: ${value}.`);
prefs = Object.create(null);
return shadow(this, "viewerPreferences", prefs);
const obj = this._catDict.get("OpenAction");
const openAction = Object.create(null);
if (obj instanceof Dict) {
const destDict = new Dict(this.xref);
Catalog.parseDestDictionary({
if (Array.isArray(resultObj.dest)) {
openAction.dest = resultObj.dest;
} else if (resultObj.action) {
openAction.action = resultObj.action;
} else if (Array.isArray(obj)) {
return shadow(this, "openAction", objectSize(openAction) > 0 ? openAction : null);
const obj = this._catDict.get("Names");
if (obj instanceof Dict && obj.has("EmbeddedFiles")) {
const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
for (const [key, value] of nameTree.getAll()) {
const fs = new FileSpec(value, this.xref);
attachments = Object.create(null);
attachments[stringToPDFString(key)] = fs.serializable;
return shadow(this, "attachments", attachments);
const obj = this._catDict.get("Names");
if (obj instanceof Dict && obj.has("XFAImages")) {
const nameTree = new NameTree(obj.getRaw("XFAImages"), this.xref);
for (const [key, value] of nameTree.getAll()) {
xfaImages = new Dict(this.xref);
xfaImages.set(stringToPDFString(key), value);
return shadow(this, "xfaImages", xfaImages);
const obj = this._catDict.get("Names");
function appendIfJavaScriptDict(name, jsDict) {
if (!(jsDict instanceof Dict)) {
if (!isName(jsDict.get("S"), "JavaScript")) {
let js = jsDict.get("JS");
if (js instanceof BaseStream) {
} else if (typeof js !== "string") {
js = stringToPDFString(js).replaceAll("\x00", "");
(javaScript ||= new Map()).set(name, js);
if (obj instanceof Dict && obj.has("JavaScript")) {
const nameTree = new NameTree(obj.getRaw("JavaScript"), this.xref);
for (const [key, value] of nameTree.getAll()) {
appendIfJavaScriptDict(stringToPDFString(key), value);
const openAction = this._catDict.get("OpenAction");
appendIfJavaScriptDict("OpenAction", openAction);
const javaScript = this._collectJavaScript();
let actions = collectActions(this.xref, this._catDict, DocumentActionEventType);
actions ||= Object.create(null);
for (const [key, val] of javaScript) {
return shadow(this, "jsActions", actions);
async fontFallback(id, handler) {
const translatedFonts = await Promise.all(this.fontCache);
for (const translatedFont of translatedFonts) {
if (translatedFont.loadedName === id) {
translatedFont.fallback(handler);
async cleanup(manuallyTriggered = false) {
this.globalImageCache.clear(manuallyTriggered);
this.pageKidsCountCache.clear();
this.pageIndexCache.clear();
this.nonBlendModesSet.clear();
const translatedFonts = await Promise.all(this.fontCache);
this.builtInCMapCache.clear();
this.standardFontDataCache.clear();
this.systemFontCache.clear();
async getPageDict(pageIndex) {
const nodesToVisit = [this.toplevelPagesDict];
const visitedNodes = new RefSet();
const pagesRef = this._catDict.getRaw("Pages");
if (pagesRef instanceof Ref) {
visitedNodes.put(pagesRef);
pageKidsCountCache = this.pageKidsCountCache,
pageIndexCache = this.pageIndexCache;
let currentPageIndex = 0;
while (nodesToVisit.length) {
const currentNode = nodesToVisit.pop();
if (currentNode instanceof Ref) {
const count = pageKidsCountCache.get(currentNode);
if (count >= 0 && currentPageIndex + count <= pageIndex) {
currentPageIndex += count;
if (visitedNodes.has(currentNode)) {
throw new FormatError("Pages tree contains circular reference.");
visitedNodes.put(currentNode);
const obj = await xref.fetchAsync(currentNode);
if (obj instanceof Dict) {
let type = obj.getRaw("Type");
if (type instanceof Ref) {
type = await xref.fetchAsync(type);
if (isName(type, "Page") || !obj.has("Kids")) {
if (!pageKidsCountCache.has(currentNode)) {
pageKidsCountCache.put(currentNode, 1);
if (!pageIndexCache.has(currentNode)) {
pageIndexCache.put(currentNode, currentPageIndex);
if (currentPageIndex === pageIndex) {
return [obj, currentNode];
if (!(currentNode instanceof Dict)) {
throw new FormatError("Page dictionary kid reference points to wrong type of object.");
let count = currentNode.getRaw("Count");
if (count instanceof Ref) {
count = await xref.fetchAsync(count);
if (Number.isInteger(count) && count >= 0) {
if (objId && !pageKidsCountCache.has(objId)) {
pageKidsCountCache.put(objId, count);
if (currentPageIndex + count <= pageIndex) {
currentPageIndex += count;
let kids = currentNode.getRaw("Kids");
if (kids instanceof Ref) {
kids = await xref.fetchAsync(kids);
if (!Array.isArray(kids)) {
let type = currentNode.getRaw("Type");
if (type instanceof Ref) {
type = await xref.fetchAsync(type);
if (isName(type, "Page") || !currentNode.has("Kids")) {
if (currentPageIndex === pageIndex) {
return [currentNode, null];
throw new FormatError("Page dictionary kids object is not an array.");
for (let last = kids.length - 1; last >= 0; last--) {
nodesToVisit.push(kids[last]);