: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
let p0 = scaleFactor * (bezier[0] - padding);
let p1 = scaleFactor * (bezier[1] - padding);
for (let i = 2, ii = bezier.length; i < ii; i += 6) {
const p10 = scaleFactor * (bezier[i] - padding);
const p11 = scaleFactor * (bezier[i + 1] - padding);
const p20 = scaleFactor * (bezier[i + 2] - padding);
const p21 = scaleFactor * (bezier[i + 3] - padding);
const p30 = scaleFactor * (bezier[i + 4] - padding);
const p31 = scaleFactor * (bezier[i + 5] - padding);
path.push([[p0, p1], [p10, p11], [p20, p21], [p30, p31]]);
const path2D = this.#buildPath2D(path);
editor.bezierPath2D.push(path2D);
const bbox = editor.#getBbox();
editor.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);
editor.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);
editor.#setScaleFactor(width, height);
const rect = this.getRect(0, 0);
const color = AnnotationEditor._colorManager.convert(this.ctx.strokeStyle);
annotationType: AnnotationEditorType.INK,
thickness: this.thickness,
paths: this.#serializePaths(this.scaleFactor / this.parentScale, this.translationX, this.translationY, rect),
pageIndex: this.pageIndex,
structTreeParentId: this._structTreeParentId
;// CONCATENATED MODULE: ./src/display/editor/stamp.js
class StampEditor extends AnnotationEditor {
#hasBeenAddedInUndoStack = false;
static _editorType = AnnotationEditorType.STAMP;
this.#bitmapUrl = params.bitmapUrl;
this.#bitmapFile = params.bitmapFile;
static initialize(l10n, uiManager) {
AnnotationEditor.initialize(l10n, uiManager);
static get supportedTypes() {
const types = ["apng", "avif", "bmp", "gif", "jpeg", "png", "svg+xml", "webp", "x-icon"];
return shadow(this, "supportedTypes", types.map(type => `image/${type}`));
static get supportedTypesStr() {
return shadow(this, "supportedTypesStr", this.supportedTypes.join(","));
static isHandlingMimeForPasting(mime) {
return this.supportedTypes.includes(mime);
static paste(item, parent) {
parent.pasteEditor(AnnotationEditorType.STAMP, {
bitmapFile: item.getAsFile()
#getBitmapFetched(data, fromId = false) {
this.#bitmap = data.bitmap;
this.#bitmapId = data.id;
this.#isSvg = data.isSvg;
this.#bitmapFileName = data.file.name;
this.#bitmapPromise = null;
this._uiManager.enableWaiting(false);
this._uiManager.enableWaiting(true);
this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone());
const url = this.#bitmapUrl;
this._uiManager.enableWaiting(true);
this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
const file = this.#bitmapFile;
this._uiManager.enableWaiting(true);
this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
const input = document.createElement("input");
input.accept = StampEditor.supportedTypesStr;
this.#bitmapPromise = new Promise(resolve => {
input.addEventListener("change", async () => {
if (!input.files || input.files.length === 0) {
this._uiManager.enableWaiting(true);
const data = await this._uiManager.imageManager.getFromFile(input.files[0]);
this.#getBitmapFetched(data);
input.addEventListener("cancel", () => {
}).finally(() => this.#getBitmapDone());
this._uiManager.imageManager.deleteId(this.#bitmapId);
this.#observer?.disconnect();
if (this.#resizeTimeoutId) {
clearTimeout(this.#resizeTimeoutId);
this.#resizeTimeoutId = null;
if (this.#bitmapId && this.#canvas === null) {
if (!this.isAttachedToDOM) {
this._isDraggable = true;
return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId);
const [parentWidth, parentHeight] = this.parentDimensions;
this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
const [pageWidth, pageHeight] = this.pageDimensions;
width = this.width * pageWidth;
height = this.height * pageHeight;
} else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) {
const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height);
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight);
this._uiManager.enableWaiting(false);
const canvas = this.#canvas = document.createElement("canvas");
this.#drawBitmap(width, height);
if (!this.#hasBeenAddedInUndoStack) {
this.parent.addUndoableEditor(this);
this.#hasBeenAddedInUndoStack = true;
if (this.#bitmapFileName) {
canvas.setAttribute("aria-label", this.#bitmapFileName);
#setDimensions(width, height) {
const [parentWidth, parentHeight] = this.parentDimensions;
this.width = width / parentWidth;
this.height = height / parentHeight;
this.setDims(width, height);
if (this._initialOptions?.isCentered) {
this.fixAndSetPosition();
this._initialOptions = null;
if (this.#resizeTimeoutId !== null) {
clearTimeout(this.#resizeTimeoutId);
const TIME_TO_WAIT = 200;
this.#resizeTimeoutId = setTimeout(() => {
this.#resizeTimeoutId = null;
this.#drawBitmap(width, height);
#scaleBitmap(width, height) {
let newWidth = bitmapWidth;
let newHeight = bitmapHeight;
let bitmap = this.#bitmap;
while (newWidth > 2 * width || newHeight > 2 * height) {
const prevWidth = newWidth;
const prevHeight = newHeight;
if (newWidth > 2 * width) {
newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2);
if (newHeight > 2 * height) {
newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2);
const offscreen = new OffscreenCanvas(newWidth, newHeight);
const ctx = offscreen.getContext("2d");
ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight);
bitmap = offscreen.transferToImageBitmap();
#drawBitmap(width, height) {
width = Math.ceil(width);
height = Math.ceil(height);
const canvas = this.#canvas;
if (!canvas || canvas.width === width && canvas.height === height) {
const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(width, height);
if (this._uiManager.hasMLManager && !this.hasAltText()) {
const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext("2d");
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height);
this._uiManager.mlGuess({
service: "image-to-text",
data: ctx.getImageData(0, 0, width, height).data,
const altText = response?.output || "";
if (this.parent && altText && !this.hasAltText()) {
const ctx = canvas.getContext("2d");
ctx.filter = this._uiManager.hcmFilter;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height);
#serializeBitmap(toUrl) {
const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.drawImage(this.#bitmap, 0, 0);
return canvas.toDataURL();
const [pageWidth, pageHeight] = this.pageDimensions;
const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS);
const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS);
const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext("2d");
ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height);
return offscreen.transferToImageBitmap();
return structuredClone(this.#bitmap);
this.#observer = new ResizeObserver(entries => {
const rect = entries[0].contentRect;
if (rect.width && rect.height) {
this.#setDimensions(rect.width, rect.height);
this.#observer.observe(this.div);
static deserialize(data, parent, uiManager) {
if (data instanceof StampAnnotationElement) {
const editor = super.deserialize(data, parent, uiManager);
if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) {
editor.#bitmapId = bitmapId;
editor.#bitmapUrl = bitmapUrl;
const [parentWidth, parentHeight] = editor.pageDimensions;
editor.width = (rect[2] - rect[0]) / parentWidth;
editor.height = (rect[3] - rect[1]) / parentHeight;
editor.altTextData = accessibilityData;
serialize(isForCopying = false, context = null) {
annotationType: AnnotationEditorType.STAMP,
bitmapId: this.#bitmapId,
pageIndex: this.pageIndex,
rect: this.getRect(0, 0),
structTreeParentId: this._structTreeParentId
serialized.bitmapUrl = this.#serializeBitmap(true);
serialized.accessibilityData = this.altTextData;
if (!decorative && altText) {
serialized.accessibilityData = {
context.stamps ||= new Map();
const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null;
if (!context.stamps.has(this.#bitmapId)) {
context.stamps.set(this.#bitmapId, {
serialized.bitmap = this.#serializeBitmap(false);
} else if (this.#isSvg) {
const prevData = context.stamps.get(this.#bitmapId);
if (area > prevData.area) {
prevData.serialized.bitmap.close();
prevData.serialized.bitmap = this.#serializeBitmap(false);
;// CONCATENATED MODULE: ./src/display/editor/annotation_editor_layer.js
class AnnotationEditorLayer {
#boundPointerdown = null;
#boundTextLayerPointerDown = null;
#editorFocusTimeoutId = null;
static _initialized = false;
static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [type._editorType, type]));
const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()];
if (!AnnotationEditorLayer._initialized) {
AnnotationEditorLayer._initialized = true;
for (const editorType of editorTypes) {