: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas);
const drawnWidth = Math.round(maxX - minX) || 1;
const drawnHeight = Math.round(maxY - minY) || 1;
const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight);
const fillCtx = fillCanvas.context;
fillCtx.translate(-offsetX, -offsetY);
fillCtx.transform(...maskToCanvas);
scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx));
if (cache && isPatternFill) {
cache.set(cacheKey, scaled);
fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate);
drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);
fillCtx.globalCompositeOperation = "source-in";
const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);
fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor;
fillCtx.fillRect(0, 0, width, height);
if (cache && !isPatternFill) {
this.cachedCanvases.delete("fillCanvas");
cache.set(cacheKey, fillCanvas.canvas);
canvas: fillCanvas.canvas,
offsetX: Math.round(offsetX),
offsetY: Math.round(offsetY)
if (width !== this.current.lineWidth) {
this._cachedScaleForStroking[0] = -1;
this.current.lineWidth = width;
this.ctx.lineWidth = width;
this.ctx.lineCap = LINE_CAP_STYLES[style];
this.ctx.lineJoin = LINE_JOIN_STYLES[style];
this.ctx.miterLimit = limit;
setDash(dashArray, dashPhase) {
if (ctx.setLineDash !== undefined) {
ctx.setLineDash(dashArray);
ctx.lineDashOffset = dashPhase;
setRenderingIntent(intent) {}
for (const [key, value] of states) {
this.setLineWidth(value);
this.setMiterLimit(value);
this.setDash(value[0], value[1]);
this.setRenderingIntent(value);
this.setFont(value[0], value[1]);
this.current.strokeAlpha = value;
this.current.fillAlpha = value;
this.ctx.globalAlpha = value;
this.ctx.globalCompositeOperation = value;
this.current.activeSMask = value ? this.tempSMask : null;
this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);
return !!this.suspendedCtx;
const inSMaskMode = this.inSMaskMode;
if (this.current.activeSMask && !inSMaskMode) {
} else if (!this.current.activeSMask && inSMaskMode) {
throw new Error("beginSMaskMode called while already in smask mode");
const drawnWidth = this.ctx.canvas.width;
const drawnHeight = this.ctx.canvas.height;
const cacheId = "smaskGroupAt" + this.groupLevel;
const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
this.suspendedCtx = this.ctx;
this.ctx = scratchCanvas.context;
ctx.setTransform(...getCurrentTransform(this.suspendedCtx));
copyCtxState(this.suspendedCtx, ctx);
mirrorContextOperations(ctx, this.suspendedCtx);
this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
throw new Error("endSMaskMode called while not in smask mode");
this.ctx._removeMirroring();
copyCtxState(this.ctx, this.suspendedCtx);
this.ctx = this.suspendedCtx;
this.suspendedCtx = null;
if (!this.current.activeSMask) {
dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
dirtyBox[0] = Math.floor(dirtyBox[0]);
dirtyBox[1] = Math.floor(dirtyBox[1]);
dirtyBox[2] = Math.ceil(dirtyBox[2]);
dirtyBox[3] = Math.ceil(dirtyBox[3]);
const smask = this.current.activeSMask;
const suspendedCtx = this.suspendedCtx;
this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
composeSMask(ctx, smask, layerCtx, layerBox) {
const layerOffsetX = layerBox[0];
const layerOffsetY = layerBox[1];
const layerWidth = layerBox[2] - layerOffsetX;
const layerHeight = layerBox[3] - layerOffsetY;
if (layerWidth === 0 || layerHeight === 0) {
this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);
ctx.globalCompositeOperation = "source-over";
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.drawImage(layerCtx.canvas, 0, 0);
genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {
let maskCanvas = maskCtx.canvas;
let maskX = layerOffsetX - maskOffsetX;
let maskY = layerOffsetY - maskOffsetY;
if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) {
const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height);
const ctx = canvas.context;
ctx.drawImage(maskCanvas, -maskX, -maskY);
if (backdrop.some(c => c !== 0)) {
ctx.globalCompositeOperation = "destination-atop";
ctx.fillStyle = Util.makeHexColor(...backdrop);
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "source-over";
maskCanvas = canvas.canvas;
} else if (backdrop.some(c => c !== 0)) {
maskCtx.setTransform(1, 0, 0, 1, 0, 0);
const clip = new Path2D();
clip.rect(maskX, maskY, width, height);
maskCtx.globalCompositeOperation = "destination-atop";
maskCtx.fillStyle = Util.makeHexColor(...backdrop);
maskCtx.fillRect(maskX, maskY, width, height);
layerCtx.globalAlpha = 1;
layerCtx.setTransform(1, 0, 0, 1, 0, 0);
if (subtype === "Alpha" && transferMap) {
layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap);
} else if (subtype === "Luminosity") {
layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap);
const clip = new Path2D();
clip.rect(layerOffsetX, layerOffsetY, width, height);
layerCtx.globalCompositeOperation = "destination-in";
layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height);
copyCtxState(this.ctx, this.suspendedCtx);
this.suspendedCtx.save();
const old = this.current;
this.stateStack.push(old);
this.current = old.clone();
if (this.stateStack.length === 0 && this.inSMaskMode) {
if (this.stateStack.length !== 0) {
this.current = this.stateStack.pop();
this.suspendedCtx.restore();
copyCtxState(this.suspendedCtx, this.ctx);
this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null;
transform(a, b, c, d, e, f) {
this.ctx.transform(a, b, c, d, e, f);
this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null;
constructPath(ops, args, minMax) {
const current = this.current;
const currentTransform = getCurrentTransform(ctx);
const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0;
const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
const height = args[j++];
if (width === 0 || height === 0) {
current.updateRectMinMax(currentTransform, [x, y, xw, yh]);
current.updatePathMinMax(currentTransform, x, y);
current.updatePathMinMax(currentTransform, x, y);
ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier);
ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier);
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier);
current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
current.setCurrentPoint(x, y);
stroke(consumePath = true) {
const strokeColor = this.current.strokeColor;
ctx.globalAlpha = this.current.strokeAlpha;
if (this.contentVisible) {
if (typeof strokeColor === "object" && strokeColor?.getPattern) {
ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE);
this.rescaleAndStroke(false);
this.rescaleAndStroke(true);
this.consumePath(this.current.getClippedPathBoundingBox());
ctx.globalAlpha = this.current.fillAlpha;
fill(consumePath = true) {
const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill;
ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL);
const intersect = this.current.getClippedPathBoundingBox();
if (this.contentVisible && intersect !== null) {
if (this.pendingEOFill) {
this.pendingEOFill = false;
this.consumePath(intersect);
this.pendingEOFill = true;
this.pendingEOFill = true;
this.pendingEOFill = true;
this.pendingClip = NORMAL_CLIP;
this.pendingClip = EO_CLIP;
this.current.textMatrix = IDENTITY_MATRIX;
this.current.textMatrixScale = 1;
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
const paths = this.pendingTextPaths;
if (paths === undefined) {
for (const path of paths) {
ctx.setTransform(...path.transform);
ctx.translate(path.x, path.y);
path.addToPath(ctx, path.fontSize);
delete this.pendingTextPaths;
setCharSpacing(spacing) {
this.current.charSpacing = spacing;
setWordSpacing(spacing) {
this.current.wordSpacing = spacing;
this.current.textHScale = scale / 100;
this.current.leading = -leading;
setFont(fontRefName, size) {
const fontObj = this.commonObjs.get(fontRefName);
const current = this.current;
throw new Error(`Can't find font for ${fontRefName}`);
current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;
if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
warn("Invalid font matrix for font " + fontRefName);
current.fontDirection = -1;
current.fontDirection = 1;
this.current.font = fontObj;
this.current.fontSize = size;
if (fontObj.isType3Font) {
const name = fontObj.loadedName || "sans-serif";
const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`;
} else if (fontObj.bold) {
const italic = fontObj.italic ? "italic" : "normal";
let browserFontSize = size;
if (size < MIN_FONT_SIZE) {
browserFontSize = MIN_FONT_SIZE;
} else if (size > MAX_FONT_SIZE) {
browserFontSize = MAX_FONT_SIZE;
this.current.fontSizeScale = size / browserFontSize;
this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;
setTextRenderingMode(mode) {