: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
class CircleAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.CIRCLE;
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeAlpha = dict.get("CA");
const interiorColor = getRgbColor(dict.getArray("IC"), null);
const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;
const fillAlpha = fillColor ? strokeAlpha : null;
if (this.borderStyle.width === 0 && !fillColor) {
const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));
this._setDefaultAppearance({
extra: `${this.borderStyle.width} w`,
pointsCallback: (buffer, points) => {
const x0 = points[0].x + this.borderStyle.width / 2;
const y0 = points[0].y - this.borderStyle.width / 2;
const x1 = points[3].x - this.borderStyle.width / 2;
const y1 = points[3].y + this.borderStyle.width / 2;
const xMid = x0 + (x1 - x0) / 2;
const yMid = y0 + (y1 - y0) / 2;
const xOffset = (x1 - x0) / 2 * controlPointsDistance;
const yOffset = (y1 - y0) / 2 * controlPointsDistance;
buffer.push(`${xMid} ${y1} m`, `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`, `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`, `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`, `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`, "h");
return [points[0].x, points[1].x, points[3].y, points[1].y];
class PolylineAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.POLYLINE;
this.data.hasOwnCanvas = this.data.noRotate;
this.data.noHTML = false;
if (!(this instanceof PolygonAnnotation)) {
this.setLineEndings(dict.getArray("LE"));
this.data.lineEndings = this.lineEndings;
const rawVertices = dict.getArray("Vertices");
if (!isNumberArray(rawVertices, null)) {
for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
this.data.vertices.push({
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeAlpha = dict.get("CA");
const borderWidth = this.borderStyle.width || 1,
borderAdjust = 2 * borderWidth;
const bbox = [Infinity, Infinity, -Infinity, -Infinity];
for (const vertex of this.data.vertices) {
bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
if (!Util.intersect(this.rectangle, bbox)) {
this._setDefaultAppearance({
extra: `${borderWidth} w`,
pointsCallback: (buffer, points) => {
const vertices = this.data.vertices;
for (let i = 0, ii = vertices.length; i < ii; i++) {
buffer.push(`${vertices[i].x} ${vertices[i].y} ${i === 0 ? "m" : "l"}`);
return [points[0].x, points[1].x, points[3].y, points[1].y];
class PolygonAnnotation extends PolylineAnnotation {
this.data.annotationType = AnnotationType.POLYGON;
class CaretAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.CARET;
class InkAnnotation extends MarkupAnnotation {
this.data.hasOwnCanvas = this.data.noRotate;
this.data.noHTML = false;
this.data.annotationType = AnnotationType.INK;
const rawInkLists = dict.getArray("InkList");
if (!Array.isArray(rawInkLists)) {
for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {
this.data.inkLists.push([]);
if (!Array.isArray(rawInkLists[i])) {
for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {
const x = xref.fetchIfRef(rawInkLists[i][j]),
y = xref.fetchIfRef(rawInkLists[i][j + 1]);
if (typeof x === "number" && typeof y === "number") {
this.data.inkLists[i].push({
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeAlpha = dict.get("CA");
const borderWidth = this.borderStyle.width || 1,
borderAdjust = 2 * borderWidth;
const bbox = [Infinity, Infinity, -Infinity, -Infinity];
for (const inkLists of this.data.inkLists) {
for (const vertex of inkLists) {
bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
if (!Util.intersect(this.rectangle, bbox)) {
this._setDefaultAppearance({
extra: `${borderWidth} w`,
pointsCallback: (buffer, points) => {
for (const inkList of this.data.inkLists) {
for (let i = 0, ii = inkList.length; i < ii; i++) {
buffer.push(`${inkList[i].x} ${inkList[i].y} ${i === 0 ? "m" : "l"}`);
return [points[0].x, points[1].x, points[3].y, points[1].y];
static createNewDict(annotation, xref, {
const ink = new Dict(xref);
ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink"));
ink.set("CreationDate", `D:${getModificationDate()}`);
ink.set("InkList", outlines?.points || paths.map(p => p.points));
ink.set("Rotate", rotation);
ink.set("IT", Name.get("InkHighlight"));
const bs = new Dict(xref);
ink.set("C", Array.from(color, c => c / 255));
const n = new Dict(xref);
static async createNewAppearanceStream(annotation, xref, params) {
if (annotation.outlines) {
return this.createNewAppearanceStreamForHighlight(annotation, xref, params);
const appearanceBuffer = [`${thickness} w 1 J 1 j`, `${getPdfColor(color, false)}`];
appearanceBuffer.push("/R0 gs");
buffer.push(`${numberToString(bezier[0])} ${numberToString(bezier[1])} m`);
if (bezier.length === 2) {
buffer.push(`${numberToString(bezier[0])} ${numberToString(bezier[1])} l S`);
for (let i = 2, ii = bezier.length; i < ii; i += 6) {
const curve = bezier.slice(i, i + 6).map(numberToString).join(" ");
buffer.push(`${curve} c`);
appearanceBuffer.push(buffer.join("\n"));
const appearance = appearanceBuffer.join("\n");
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
const resources = new Dict(xref);
const extGState = new Dict(xref);
const r0 = new Dict(xref);
r0.set("Type", Name.get("ExtGState"));
resources.set("ExtGState", extGState);
appearanceStreamDict.set("Resources", resources);
const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;
static async createNewAppearanceStreamForHighlight(annotation, xref, params) {
const appearanceBuffer = [`${getPdfColor(color, true)}`, "/R0 gs"];
appearanceBuffer.push(`${numberToString(outline[4])} ${numberToString(outline[5])} m`);
for (let i = 6, ii = outline.length; i < ii; i += 6) {
if (isNaN(outline[i]) || outline[i] === null) {
appearanceBuffer.push(`${numberToString(outline[i + 4])} ${numberToString(outline[i + 5])} l`);
const curve = outline.slice(i, i + 6).map(numberToString).join(" ");
appearanceBuffer.push(`${curve} c`);
appearanceBuffer.push("h f");
const appearance = appearanceBuffer.join("\n");
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
const resources = new Dict(xref);
const extGState = new Dict(xref);
resources.set("ExtGState", extGState);
appearanceStreamDict.set("Resources", resources);
const r0 = new Dict(xref);
r0.set("BM", Name.get("Multiply"));
r0.set("Type", Name.get("ExtGState"));
const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;
class HighlightAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.HIGHLIGHT;
const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);
const resources = this.appearance?.dict.get("Resources");
if (!this.appearance || !resources?.has("ExtGState")) {
warn("HighlightAnnotation - ignoring built-in appearance stream.");
const fillColor = this.color ? getPdfColorArray(this.color) : [1, 1, 0];
const fillAlpha = dict.get("CA");
this._setDefaultAppearance({
pointsCallback: (buffer, points) => {
buffer.push(`${points[0].x} ${points[0].y} m`, `${points[1].x} ${points[1].y} l`, `${points[3].x} ${points[3].y} l`, `${points[2].x} ${points[2].y} l`, "f");
return [points[0].x, points[1].x, points[3].y, points[1].y];
this.data.popupRef = null;
static createNewDict(annotation, xref, {
const highlight = new Dict(xref);
highlight.set("Type", Name.get("Annot"));
highlight.set("Subtype", Name.get("Highlight"));
highlight.set("CreationDate", `D:${getModificationDate()}`);
highlight.set("Rect", rect);
highlight.set("Border", [0, 0, 0]);
highlight.set("Rotate", rotation);
highlight.set("QuadPoints", quadPoints);
highlight.set("C", Array.from(color, c => c / 255));
highlight.set("CA", opacity);
highlight.set("T", isAscii(user) ? user : stringToUTF16String(user, true));
const n = new Dict(xref);
static async createNewAppearanceStream(annotation, xref, params) {
const appearanceBuffer = [`${getPdfColor(color, true)}`, "/R0 gs"];
for (const outline of outlines) {
buffer.push(`${numberToString(outline[0])} ${numberToString(outline[1])} m`);
for (let i = 2, ii = outline.length; i < ii; i += 2) {
buffer.push(`${numberToString(outline[i])} ${numberToString(outline[i + 1])} l`);
appearanceBuffer.push(buffer.join("\n"));
appearanceBuffer.push("f*");
const appearance = appearanceBuffer.join("\n");
const appearanceStreamDict = new Dict(xref);
appearanceStreamDict.set("FormType", 1);
appearanceStreamDict.set("Subtype", Name.get("Form"));
appearanceStreamDict.set("Type", Name.get("XObject"));
appearanceStreamDict.set("BBox", rect);
appearanceStreamDict.set("Length", appearance.length);
const resources = new Dict(xref);
const extGState = new Dict(xref);
resources.set("ExtGState", extGState);
appearanceStreamDict.set("Resources", resources);
const r0 = new Dict(xref);
r0.set("BM", Name.get("Multiply"));
r0.set("Type", Name.get("ExtGState"));
const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;
class UnderlineAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.UNDERLINE;
const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeAlpha = dict.get("CA");
this._setDefaultAppearance({
pointsCallback: (buffer, points) => {
buffer.push(`${points[2].x} ${points[2].y + 1.3} m`, `${points[3].x} ${points[3].y + 1.3} l`, "S");
return [points[0].x, points[1].x, points[3].y, points[1].y];
this.data.popupRef = null;
class SquigglyAnnotation extends MarkupAnnotation {
this.data.annotationType = AnnotationType.SQUIGGLY;
const quadPoints = this.data.quadPoints = getQuadPoints(dict, null);
const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];
const strokeAlpha = dict.get("CA");
this._setDefaultAppearance({
pointsCallback: (buffer, points) => {
const dy = (points[0].y - points[2].y) / 6;
const xEnd = points[3].x;
buffer.push(`${x} ${y + shift} m`);
shift = shift === 0 ? dy : 0;
buffer.push(`${x} ${y + shift} l`);
return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy];
this.data.popupRef = null;
class StrikeOutAnnotation extends MarkupAnnotation {