: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
separateCanvas: isUsingOwnCanvas
async save(evaluator, task, annotationStorage) {
async extractTextContent(evaluator, task, viewBox) {
const resources = await this.loadResources(["ExtGState", "Font", "Properties", "XObject"], this.appearance);
let firstPosition = null;
desiredSize: Math.Infinity,
for (const item of chunk.items) {
if (item.str === undefined) {
firstPosition ||= item.transform.slice(-2);
text.push(buffer.join("").trimEnd());
await evaluator.getTextContent({
includeMarkedContent: true,
text.push(buffer.join("").trimEnd());
if (text.length > 1 || text[0]) {
const appearanceDict = this.appearance.dict;
const bbox = lookupRect(appearanceDict.getArray("BBox"), null);
const matrix = lookupMatrix(appearanceDict.getArray("Matrix"), null);
this.data.textPosition = this._transformPoint(firstPosition, bbox, matrix);
this.data.textContent = text;
_transformPoint(coords, bbox, matrix) {
matrix ||= [1, 0, 0, 1, 0, 0];
const transform = getTransformMatrix(rect, bbox, matrix);
coords = Util.applyTransform(coords, transform);
return Util.applyTransform(coords, matrix);
actions: this.data.actions,
name: this.data.fieldName,
strokeColor: this.data.borderColor,
fillColor: this.data.backgroundColor,
kidIds: this.data.kidIds,
page: this.data.pageIndex,
for (const stream of this._streams) {
_constructFieldName(dict) {
if (!dict.has("T") && !dict.has("Parent")) {
warn("Unknown field name, falling back to empty field name.");
if (!dict.has("Parent")) {
return stringToPDFString(dict.get("T"));
fieldName.unshift(stringToPDFString(dict.get("T")));
const visited = new RefSet();
while (loopDict.has("Parent")) {
loopDict = loopDict.get("Parent");
if (!(loopDict instanceof Dict) || loopDict.objId && visited.has(loopDict.objId)) {
visited.put(loopDict.objId);
fieldName.unshift(stringToPDFString(loopDict.get("T")));
return fieldName.join(".");
class AnnotationBorderStyle {
this.style = AnnotationBorderStyleType.SOLID;
this.horizontalCornerRadius = 0;
this.verticalCornerRadius = 0;
setWidth(width, rect = [0, 0, 0, 0]) {
if (width instanceof Name) {
if (typeof width === "number") {
const maxWidth = (rect[2] - rect[0]) / 2;
const maxHeight = (rect[3] - rect[1]) / 2;
if (maxWidth > 0 && maxHeight > 0 && (width > maxWidth || width > maxHeight)) {
warn(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`);
if (!(style instanceof Name)) {
this.style = AnnotationBorderStyleType.SOLID;
this.style = AnnotationBorderStyleType.DASHED;
this.style = AnnotationBorderStyleType.BEVELED;
this.style = AnnotationBorderStyleType.INSET;
this.style = AnnotationBorderStyleType.UNDERLINE;
setDashArray(dashArray, forceStyle = false) {
if (Array.isArray(dashArray)) {
for (const element of dashArray) {
const validNumber = +element >= 0;
} else if (element > 0) {
if (dashArray.length === 0 || isValid && !allZeros) {
this.dashArray = dashArray;
this.setStyle(Name.get("D"));
setHorizontalCornerRadius(radius) {
if (Number.isInteger(radius)) {
this.horizontalCornerRadius = radius;
setVerticalCornerRadius(radius) {
if (Number.isInteger(radius)) {
this.verticalCornerRadius = radius;
class MarkupAnnotation extends Annotation {
const rawIRT = dict.getRaw("IRT");
this.data.inReplyTo = rawIRT instanceof Ref ? rawIRT.toString() : null;
const rt = dict.get("RT");
this.data.replyType = rt instanceof Name ? rt.name : AnnotationReplyType.REPLY;
if (this.data.replyType === AnnotationReplyType.GROUP) {
const parent = dict.get("IRT");
this.setTitle(parent.get("T"));
this.data.titleObj = this._title;
this.setContents(parent.get("Contents"));
this.data.contentsObj = this._contents;
if (!parent.has("CreationDate")) {
this.data.creationDate = null;
this.setCreationDate(parent.get("CreationDate"));
this.data.creationDate = this.creationDate;
this.data.modificationDate = null;
this.setModificationDate(parent.get("M"));
this.data.modificationDate = this.modificationDate;
popupRef = parent.getRaw("Popup");
this.setColor(parent.getArray("C"));
this.data.color = this.color;
this.data.titleObj = this._title;
this.setCreationDate(dict.get("CreationDate"));
this.data.creationDate = this.creationDate;
popupRef = dict.getRaw("Popup");
this.data.popupRef = popupRef instanceof Ref ? popupRef.toString() : null;
this.data.richText = XFAFactory.getRichTextAsHtml(dict.get("RC"));
setCreationDate(creationDate) {
this.creationDate = typeof creationDate === "string" ? creationDate : null;
let minX = Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
let maxX = Number.MIN_VALUE;
let maxY = Number.MIN_VALUE;
buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`);
buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
let pointsArray = this.data.quadPoints;
for (const points of pointsArray) {
const [mX, MX, mY, MY] = pointsCallback(buffer, points);
minX = Math.min(minX, mX);
maxX = Math.max(maxX, MX);
minY = Math.min(minY, mY);
maxY = Math.max(maxY, MY);
const formDict = new Dict(xref);
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("Subtype", Name.get("Form"));
const appearanceStream = new StringStream(buffer.join(" "));
appearanceStream.dict = appearanceStreamDict;
formDict.set("Fm0", appearanceStream);
const gsDict = new Dict(xref);
gsDict.set("BM", Name.get(blendMode));
if (typeof strokeAlpha === "number") {
gsDict.set("CA", strokeAlpha);
if (typeof fillAlpha === "number") {
gsDict.set("ca", fillAlpha);
const stateDict = new Dict(xref);
stateDict.set("GS0", gsDict);
const resources = new Dict(xref);
resources.set("ExtGState", stateDict);
resources.set("XObject", formDict);
const appearanceDict = new Dict(xref);
appearanceDict.set("Resources", resources);
const bbox = this.data.rect = [minX, minY, maxX, maxY];
appearanceDict.set("BBox", bbox);
this.appearance = new StringStream("/GS0 gs /Fm0 Do");
this.appearance.dict = appearanceDict;
this._streams.push(this.appearance, appearanceStream);
static async createNewAnnotation(xref, annotation, dependencies, params) {
const annotationRef = annotation.ref ||= xref.getNewTemporaryRef();
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const apRef = xref.getNewTemporaryRef();
annotationDict = this.createNewDict(annotation, xref, {
await writeObject(apRef, ap, buffer, xref);
annotationDict = this.createNewDict(annotation, xref, {});
if (Number.isInteger(annotation.parentTreeId)) {
annotationDict.set("StructParent", annotation.parentTreeId);
await writeObject(annotationRef, annotationDict, buffer, xref);
static async createNewPrintAnnotation(annotationGlobals, xref, annotation, params) {
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const annotationDict = this.createNewDict(annotation, xref, {
const newAnnotation = new this.prototype.constructor({
evaluatorOptions: params.evaluatorOptions
newAnnotation.ref = newAnnotation.refToReplace = annotation.ref;
class WidgetAnnotation extends Annotation {
this._needAppearances = params.needAppearances;
data.annotationType = AnnotationType.WIDGET;
if (data.fieldName === undefined) {
data.fieldName = this._constructFieldName(dict);
if (data.actions === undefined) {
data.actions = collectActions(xref, dict, AnnotationActionEventType);
let fieldValue = getInheritableProperty({
data.fieldValue = this._decodeFormValue(fieldValue);
const defaultFieldValue = getInheritableProperty({
data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);
if (fieldValue === undefined && annotationGlobals.xfaDatasets) {
const path = this._title.str;
this._hasValueFromXFA = true;
data.fieldValue = fieldValue = annotationGlobals.xfaDatasets.getValue(path);
if (fieldValue === undefined && data.defaultFieldValue !== null) {
data.fieldValue = data.defaultFieldValue;
data.alternativeText = stringToPDFString(dict.get("TU") || "");
this.setDefaultAppearance(params);
data.hasAppearance ||= this._needAppearances && data.fieldValue !== undefined && data.fieldValue !== null;
const fieldType = getInheritableProperty({
data.fieldType = fieldType instanceof Name ? fieldType.name : null;
const localResources = getInheritableProperty({
const acroFormResources = annotationGlobals.acroForm.get("DR");
const appearanceResources = this.appearance?.dict.get("Resources");
mergedResources: Dict.merge({
dictArray: [localResources, appearanceResources, acroFormResources],
data.fieldFlags = getInheritableProperty({
if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {
data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
data.required = this.hasFieldFlag(AnnotationFieldFlag.REQUIRED);
data.hidden = this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN) || this._hasFlag(data.annotationFlags, AnnotationFlag.NOVIEW);
_decodeFormValue(formValue) {
if (Array.isArray(formValue)) {
return formValue.filter(item => typeof item === "string").map(item => stringToPDFString(item));
} else if (formValue instanceof Name) {
return stringToPDFString(formValue.name);
} else if (typeof formValue === "string") {
return stringToPDFString(formValue);
return !!(this.data.fieldFlags & flag);
mustBeViewed(annotationStorage, renderForms) {
return super.mustBeViewed(annotationStorage, renderForms) && !this._hasFlag(this.flags, AnnotationFlag.NOVIEW);
getRotationMatrix(annotationStorage) {
let rotation = annotationStorage?.get(this.data.id)?.rotation;
if (rotation === undefined) {
rotation = this.rotation;
const width = this.data.rect[2] - this.data.rect[0];
const height = this.data.rect[3] - this.data.rect[1];
return getRotationMatrix(rotation, width, height);
getBorderAndBackgroundAppearances(annotationStorage) {
let rotation = annotationStorage?.get(this.data.id)?.rotation;
if (rotation === undefined) {
rotation = this.rotation;
if (!this.backgroundColor && !this.borderColor) {