: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const sortedAnnotations = [];
let popupAnnotations, widgetAnnotations;
for (const annotation of await Promise.all(annotationPromises)) {
if (annotation instanceof WidgetAnnotation) {
(widgetAnnotations ||= []).push(annotation);
if (annotation instanceof PopupAnnotation) {
(popupAnnotations ||= []).push(annotation);
sortedAnnotations.push(annotation);
sortedAnnotations.push(...widgetAnnotations);
sortedAnnotations.push(...popupAnnotations);
return sortedAnnotations;
return shadow(this, "_parsedAnnotations", promise);
const actions = collectActions(this.xref, this.pageDict, PageActionEventType);
return shadow(this, "jsActions", actions);
const PDF_HEADER_SIGNATURE = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]);
const STARTXREF_SIGNATURE = new Uint8Array([0x73, 0x74, 0x61, 0x72, 0x74, 0x78, 0x72, 0x65, 0x66]);
const ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]);
const FINGERPRINT_FIRST_BYTES = 1024;
const EMPTY_FINGERPRINT = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
function find(stream, signature, limit = 1024, backwards = false) {
const signatureLength = signature.length;
const scanBytes = stream.peekBytes(limit);
const scanLength = scanBytes.length - signatureLength;
const signatureEnd = signatureLength - 1;
let pos = scanBytes.length - 1;
while (pos >= signatureEnd) {
while (j < signatureLength && scanBytes[pos - j] === signature[signatureEnd - j]) {
if (j >= signatureLength) {
stream.pos += pos - signatureEnd;
while (pos <= scanLength) {
while (j < signatureLength && scanBytes[pos + j] === signature[j]) {
if (j >= signatureLength) {
constructor(pdfManager, stream) {
if (stream.length <= 0) {
throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");
this.pdfManager = pdfManager;
this.xref = new XRef(stream, pdfManager);
this._pagePromises = new Map();
this._globalIdFactory = class {
return `g_${pdfManager.docId}`;
return `f${++idCounters.font}`;
unreachable("Abstract method `createObjId` called.");
unreachable("Abstract method `getPageObjId` called.");
this.xref.parse(recoveryMode);
this.catalog = new Catalog(this.pdfManager, this.xref);
let linearization = null;
linearization = Linearization.create(this.stream);
if (err instanceof MissingDataException) {
return shadow(this, "linearization", linearization);
const stream = this.stream;
if (this.linearization) {
if (find(stream, ENDOBJ_SIGNATURE)) {
let ch = stream.peekByte();
while (isWhiteSpace(ch)) {
startXRef = stream.pos - stream.start;
const startXRefLength = STARTXREF_SIGNATURE.length;
while (!found && pos > 0) {
pos -= step - startXRefLength;
found = find(stream, STARTXREF_SIGNATURE, step, true);
} while (isWhiteSpace(ch));
while (ch >= 0x20 && ch <= 0x39) {
str += String.fromCharCode(ch);
startXRef = parseInt(str, 10);
return shadow(this, "startXRef", startXRef);
const stream = this.stream;
if (!find(stream, PDF_HEADER_SIGNATURE)) {
stream.skip(PDF_HEADER_SIGNATURE.length);
while ((ch = stream.getByte()) > 0x20 && version.length < 7) {
version += String.fromCharCode(ch);
if (PDF_VERSION_REGEXP.test(version)) {
warn(`Invalid PDF header version: ${version}`);
this.xref.setStartXRef(this.startXRef);
if (this.catalog.hasActualNumPages) {
num = this.catalog.numPages;
} else if (this.xfaFactory) {
num = this.xfaFactory.getNumPages();
} else if (this.linearization) {
num = this.linearization.numPages;
num = this.catalog.numPages;
return shadow(this, "numPages", num);
_hasOnlyDocumentSignatures(fields, recursionDepth = 0) {
const RECURSION_LIMIT = 10;
if (!Array.isArray(fields)) {
return fields.every(field => {
field = this.xref.fetchIfRef(field);
if (!(field instanceof Dict)) {
if (++recursionDepth > RECURSION_LIMIT) {
warn("_hasOnlyDocumentSignatures: maximum recursion depth reached");
return this._hasOnlyDocumentSignatures(field.get("Kids"), recursionDepth);
const isSignature = isName(field.get("FT"), "Sig");
const rectangle = field.get("Rect");
const isInvisible = Array.isArray(rectangle) && rectangle.every(value => value === 0);
return isSignature && isInvisible;
const acroForm = this.catalog.acroForm;
const xfa = acroForm.get("XFA");
if (xfa instanceof BaseStream && !xfa.isEmpty) {
entries["xdp:xdp"] = xfa;
if (!Array.isArray(xfa) || xfa.length === 0) {
for (let i = 0, ii = xfa.length; i < ii; i += 2) {
} else if (i === ii - 2) {
if (!entries.hasOwnProperty(name)) {
const data = this.xref.fetchIfRef(xfa[i + 1]);
if (!(data instanceof BaseStream) || data.isEmpty) {
const streams = this._xfaStreams;
return shadow(this, "xfaDatasets", null);
for (const key of ["datasets", "xdp:xdp"]) {
const stream = streams[key];
const str = stringToUTF8String(stream.getString());
return shadow(this, "xfaDatasets", new DatasetReader(data));
warn("XFA - Invalid utf-8 string.");
return shadow(this, "xfaDatasets", null);
const streams = this._xfaStreams;
const data = Object.create(null);
for (const [key, stream] of Object.entries(streams)) {
data[key] = stringToUTF8String(stream.getString());
warn("XFA - Invalid utf-8 string.");
if (this.pdfManager.enableXfa && this.catalog.needsRendering && this.formInfo.hasXfa && !this.formInfo.hasAcroForm) {
return shadow(this, "xfaFactory", data ? new XFAFactory(data) : null);
return this.xfaFactory ? this.xfaFactory.isValid() : false;
return this.xfaFactory ? this.xfaFactory.getPages() : null;
const xfaImagesDict = await this.pdfManager.ensureCatalog("xfaImages");
const keys = xfaImagesDict.getKeys();
const objectLoader = new ObjectLoader(xfaImagesDict, keys, this.xref);
await objectLoader.load();
const xfaImages = new Map();
for (const key of keys) {
const stream = xfaImagesDict.get(key);
if (stream instanceof BaseStream) {
xfaImages.set(key, stream.getBytes());
this.xfaFactory.setImages(xfaImages);
async loadXfaFonts(handler, task) {
const acroForm = await this.pdfManager.ensureCatalog("acroForm");
const resources = await acroForm.getAsync("DR");
if (!(resources instanceof Dict)) {
const objectLoader = new ObjectLoader(resources, ["Font"], this.xref);
await objectLoader.load();
const fontRes = resources.get("Font");
if (!(fontRes instanceof Dict)) {
const options = Object.assign(Object.create(null), this.pdfManager.evaluatorOptions);
options.useSystemFonts = false;
const partialEvaluator = new PartialEvaluator({
idFactory: this._globalIdFactory,
fontCache: this.catalog.fontCache,
builtInCMapCache: this.catalog.builtInCMapCache,
standardFontDataCache: this.catalog.standardFontDataCache,
const operatorList = new OperatorList();
fontRes.forEach((fontName, font) => {
fonts.set(fontName, font);
for (const [fontName, font] of fonts) {
const descriptor = font.get("FontDescriptor");
if (!(descriptor instanceof Dict)) {
let fontFamily = descriptor.get("FontFamily");
fontFamily = fontFamily.replaceAll(/[ ]+(\d)/g, "$1");
const fontWeight = descriptor.get("FontWeight");
const italicAngle = -descriptor.get("ItalicAngle");
if (!validateCSSFont(cssFontInfo)) {
promises.push(partialEvaluator.handleSetFont(resources, [Name.get(fontName), 1], null, operatorList, task, initialState, null, cssFontInfo).catch(function (reason) {
warn(`loadXfaFonts: "${reason}".`);
await Promise.all(promises);
const missingFonts = this.xfaFactory.setFonts(pdfFonts);
options.ignoreErrors = true;
const reallyMissingFonts = new Set();
for (const missing of missingFonts) {
if (!getXfaFontName(`${missing}-Regular`)) {
reallyMissingFonts.add(missing);
if (reallyMissingFonts.size) {
missingFonts.push("PdfJS-Fallback");
for (const missing of missingFonts) {
if (reallyMissingFonts.has(missing)) {
for (const fontInfo of [{
const name = `${missing}-${fontInfo.name}`;
const dict = getXfaFontDict(name);
promises.push(partialEvaluator.handleSetFont(resources, [Name.get(name), 1], null, operatorList, task, initialState, dict, {
fontWeight: fontInfo.fontWeight,
italicAngle: fontInfo.italicAngle
}).catch(function (reason) {
warn(`loadXfaFonts: "${reason}".`);
await Promise.all(promises);
this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);
async serializeXfaData(annotationStorage) {
return this.xfaFactory ? this.xfaFactory.serializeData(annotationStorage) : null;
return this.catalog.version || this._version;
const acroForm = this.catalog.acroForm;
return shadow(this, "formInfo", formInfo);
const fields = acroForm.get("Fields");
const hasFields = Array.isArray(fields) && fields.length > 0;
formInfo.hasFields = hasFields;
const xfa = acroForm.get("XFA");
formInfo.hasXfa = Array.isArray(xfa) && xfa.length > 0 || xfa instanceof BaseStream && !xfa.isEmpty;
const sigFlags = acroForm.get("SigFlags");
const hasSignatures = !!(sigFlags & 0x1);
const hasOnlyDocumentSignatures = hasSignatures && this._hasOnlyDocumentSignatures(fields);
formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
formInfo.hasSignatures = hasSignatures;
if (ex instanceof MissingDataException) {
warn(`Cannot fetch form information: "${ex}".`);
return shadow(this, "formInfo", formInfo);
PDFFormatVersion: this.version,
Language: this.catalog.lang,
EncryptFilterName: this.xref.encrypt ? this.xref.encrypt.filterName : null,
IsLinearized: !!this.linearization,
IsAcroFormPresent: this.formInfo.hasAcroForm,
IsXFAPresent: this.formInfo.hasXfa,