: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const DEFAULT_USER_UNIT = 1.0;
const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
this.pdfManager = pdfManager;
this.pageIndex = pageIndex;
this.pageDict = pageDict;
this.fontCache = fontCache;
this.builtInCMapCache = builtInCMapCache;
this.standardFontDataCache = standardFontDataCache;
this.globalImageCache = globalImageCache;
this.systemFontCache = systemFontCache;
this.nonBlendModesSet = nonBlendModesSet;
this.evaluatorOptions = pdfManager.evaluatorOptions;
this.resourcesPromise = null;
this.xfaFactory = xfaFactory;
this._localIdFactory = class extends globalIdFactory {
return `p${pageIndex}_${++idCounters.obj}`;
return `p${ref.toString()}`;
_getInheritableProperty(key, getArray = false) {
const value = getInheritableProperty({
if (!Array.isArray(value)) {
if (value.length === 1 || !(value[0] instanceof Dict)) {
return this.pageDict.getArray("Contents");
const resources = this._getInheritableProperty("Resources");
return shadow(this, "resources", resources instanceof Dict ? resources : Dict.empty);
return this.xfaData.bbox;
const box = lookupNormalRect(this._getInheritableProperty(name, true), null);
if (box[2] - box[0] > 0 && box[3] - box[1] > 0) {
warn(`Empty, or invalid, /${name} entry.`);
return shadow(this, "mediaBox", this._getBoundingBox("MediaBox") || LETTER_SIZE_MEDIABOX);
return shadow(this, "cropBox", this._getBoundingBox("CropBox") || this.mediaBox);
let obj = this.pageDict.get("UserUnit");
if (typeof obj !== "number" || obj <= 0) {
return shadow(this, "userUnit", obj);
if (cropBox !== mediaBox && !isArrayEqual(cropBox, mediaBox)) {
const box = Util.intersect(cropBox, mediaBox);
if (box && box[2] - box[0] > 0 && box[3] - box[1] > 0) {
return shadow(this, "view", box);
warn("Empty /CropBox and /MediaBox intersection.");
return shadow(this, "view", mediaBox);
let rotate = this._getInheritableProperty("Rotate") || 0;
} else if (rotate >= 360) {
rotate = (rotate % 360 + 360) % 360;
return shadow(this, "rotate", rotate);
_onSubStreamError(reason, objId) {
if (this.evaluatorOptions.ignoreErrors) {
warn(`getContentStream - ignoring sub-stream (${objId}): "${reason}".`);
return this.pdfManager.ensure(this, "content").then(content => {
if (content instanceof BaseStream) {
if (Array.isArray(content)) {
return new StreamsSequenceStream(content, this._onSubStreamError.bind(this));
return shadow(this, "xfaData", this.xfaFactory ? {
bbox: this.xfaFactory.getBoundingBox(this.pageIndex)
#replaceIdByRef(annotations, deletedAnnotations, existingAnnotations) {
for (const annotation of annotations) {
const ref = Ref.fromString(annotation.id);
warn(`A non-linked annotation cannot be modified: ${annotation.id}`);
if (annotation.deleted) {
deletedAnnotations.put(ref, ref);
existingAnnotations?.put(ref);
async saveNewAnnotations(handler, task, annotations, imagePromises) {
throw new Error("XFA: Cannot save new annotations.");
const partialEvaluator = new PartialEvaluator({
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions
const deletedAnnotations = new RefSetCache();
const existingAnnotations = new RefSet();
this.#replaceIdByRef(annotations, deletedAnnotations, existingAnnotations);
const pageDict = this.pageDict;
const annotationsArray = this.annotations.filter(a => !(a instanceof Ref && deletedAnnotations.has(a)));
const newData = await AnnotationFactory.saveNewAnnotations(partialEvaluator, task, annotations, imagePromises);
} of newData.annotations) {
if (ref instanceof Ref && !existingAnnotations.has(ref)) {
annotationsArray.push(ref);
const savedDict = pageDict.get("Annots");
pageDict.set("Annots", annotationsArray);
await writeObject(this.ref, pageDict, buffer, this.xref);
pageDict.set("Annots", savedDict);
const objects = newData.dependencies;
}, ...newData.annotations);
for (const deletedRef of deletedAnnotations) {
save(handler, task, annotationStorage) {
const partialEvaluator = new PartialEvaluator({
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions
return this._parsedAnnotations.then(function (annotations) {
const newRefsPromises = [];
for (const annotation of annotations) {
if (!annotation.mustBePrinted(annotationStorage)) {
newRefsPromises.push(annotation.save(partialEvaluator, task, annotationStorage).catch(function (reason) {
warn("save - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
return Promise.all(newRefsPromises).then(function (newRefs) {
return newRefs.filter(newRef => !!newRef);
this.resourcesPromise ||= this.pdfManager.ensure(this, "resources");
return this.resourcesPromise.then(() => {
const objectLoader = new ObjectLoader(this.resources, keys, this.xref);
return objectLoader.load();
const contentStreamPromise = this.getContentStream();
const resourcesPromise = this.loadResources(["ColorSpace", "ExtGState", "Font", "Pattern", "Properties", "Shading", "XObject"]);
const partialEvaluator = new PartialEvaluator({
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions
const newAnnotsByPage = !this.xfaFactory ? getNewAnnotationsMap(annotationStorage) : null;
const newAnnots = newAnnotsByPage?.get(this.pageIndex);
let newAnnotationsPromise = Promise.resolve(null);
let deletedAnnotations = null;
const annotationGlobalsPromise = this.pdfManager.ensureDoc("annotationGlobals");
const missingBitmaps = new Set();
if (bitmapId && !bitmap && !missingBitmaps.has(bitmapId)) {
missingBitmaps.add(bitmapId);
isOffscreenCanvasSupported
} = this.evaluatorOptions;
if (missingBitmaps.size > 0) {
const annotationWithBitmaps = newAnnots.slice();
for (const [key, annotation] of annotationStorage) {
if (!key.startsWith(AnnotationEditorPrefix)) {
if (annotation.bitmap && missingBitmaps.has(annotation.bitmapId)) {
annotationWithBitmaps.push(annotation);
imagePromises = AnnotationFactory.generateImages(annotationWithBitmaps, this.xref, isOffscreenCanvasSupported);
imagePromises = AnnotationFactory.generateImages(newAnnots, this.xref, isOffscreenCanvasSupported);
deletedAnnotations = new RefSet();
this.#replaceIdByRef(newAnnots, deletedAnnotations, null);
newAnnotationsPromise = annotationGlobalsPromise.then(annotationGlobals => {
if (!annotationGlobals) {
return AnnotationFactory.printNewAnnotations(annotationGlobals, partialEvaluator, task, newAnnots, imagePromises);
const pageListPromise = Promise.all([contentStreamPromise, resourcesPromise]).then(([contentStream]) => {
const opList = new OperatorList(intent, sink);
handler.send("StartRenderPage", {
transparency: partialEvaluator.hasBlendModes(this.resources, this.nonBlendModesSet),
pageIndex: this.pageIndex,
return partialEvaluator.getOperatorList({
resources: this.resources,
return Promise.all([pageListPromise, this._parsedAnnotations, newAnnotationsPromise]).then(function ([pageOpList, annotations, newAnnotations]) {
annotations = annotations.filter(a => !(a.ref && deletedAnnotations.has(a.ref)));
for (let i = 0, ii = newAnnotations.length; i < ii; i++) {
const newAnnotation = newAnnotations[i];
if (newAnnotation.refToReplace) {
const j = annotations.findIndex(a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace));
annotations.splice(j, 1, newAnnotation);
newAnnotations.splice(i--, 1);
annotations = annotations.concat(newAnnotations);
if (annotations.length === 0 || intent & RenderingIntentFlag.ANNOTATIONS_DISABLE) {
length: pageOpList.totalLength
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
const opListPromises = [];
for (const annotation of annotations) {
if (intentAny || intentDisplay && annotation.mustBeViewed(annotationStorage, renderForms) || intentPrint && annotation.mustBePrinted(annotationStorage)) {
opListPromises.push(annotation.getOperatorList(partialEvaluator, task, intent, renderForms, annotationStorage).catch(function (reason) {
warn("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
return Promise.all(opListPromises).then(function (opLists) {
pageOpList.addOpList(opList);
canvas ||= separateCanvas;
length: pageOpList.totalLength
async extractTextContent({
const contentStreamPromise = this.getContentStream();
const resourcesPromise = this.loadResources(["ExtGState", "Font", "Properties", "XObject"]);
const langPromise = this.pdfManager.ensureCatalog("lang");
const [contentStream,, lang] = await Promise.all([contentStreamPromise, resourcesPromise, langPromise]);
const partialEvaluator = new PartialEvaluator({
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions
return partialEvaluator.getTextContent({
resources: this.resources,
const structTreeRoot = await this.pdfManager.ensureCatalog("structTreeRoot");
await this._parsedAnnotations;
const structTree = await this.pdfManager.ensure(this, "_parseStructTree", [structTreeRoot]);
return structTree.serializable;
_parseStructTree(structTreeRoot) {
const tree = new StructTreePage(structTreeRoot, this.pageDict);
async getAnnotationsData(handler, task, intent) {
const annotations = await this._parsedAnnotations;
if (annotations.length === 0) {
const annotationsData = [],
textContentPromises = [];
const intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
for (const annotation of annotations) {
const isVisible = intentAny || intentDisplay && annotation.viewable;
if (isVisible || intentPrint && annotation.printable) {
annotationsData.push(annotation.data);
if (annotation.hasTextContent && isVisible) {
partialEvaluator ||= new PartialEvaluator({
pageIndex: this.pageIndex,
idFactory: this._localIdFactory,
fontCache: this.fontCache,
builtInCMapCache: this.builtInCMapCache,
standardFontDataCache: this.standardFontDataCache,
globalImageCache: this.globalImageCache,
systemFontCache: this.systemFontCache,
options: this.evaluatorOptions
textContentPromises.push(annotation.extractTextContent(partialEvaluator, task, [-Infinity, -Infinity, Infinity, Infinity]).catch(function (reason) {
warn(`getAnnotationsData - ignoring textContent during "${task.name}" task: "${reason}".`);
await Promise.all(textContentPromises);
const annots = this._getInheritableProperty("Annots");
return shadow(this, "annotations", Array.isArray(annots) ? annots : []);
get _parsedAnnotations() {
const promise = this.pdfManager.ensure(this, "annotations").then(async annots => {
if (annots.length === 0) {
const annotationGlobals = await this.pdfManager.ensureDoc("annotationGlobals");
if (!annotationGlobals) {
const annotationPromises = [];
for (const annotationRef of annots) {
annotationPromises.push(AnnotationFactory.create(this.xref, annotationRef, annotationGlobals, this._localIdFactory, false, this.ref).catch(function (reason) {
warn(`_parsedAnnotations: "${reason}".`);