: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const editToolbar = this.#toolbar = document.createElement("div");
editToolbar.className = "editToolbar";
editToolbar.setAttribute("role", "toolbar");
editToolbar.addEventListener("contextmenu", noContextMenu);
const buttons = this.#buttons = document.createElement("div");
buttons.className = "buttons";
editToolbar.append(buttons);
this.#addHighlightButton();
#getLastPoint(boxes, isLTR) {
for (const box of boxes) {
const y = box.y + box.height;
const x = box.x + (isLTR ? box.width : 0);
return [isLTR ? 1 - lastX : lastX, lastY];
show(parent, boxes, isLTR) {
const [x, y] = this.#getLastPoint(boxes, isLTR);
} = this.#toolbar ||= this.#render();
parent.append(this.#toolbar);
style.insetInlineEnd = `${100 * x}%`;
style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`;
const button = document.createElement("button");
button.className = "highlightButton";
button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`);
const span = document.createElement("span");
span.className = "visuallyHidden";
span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label");
button.addEventListener("contextmenu", noContextMenu);
button.addEventListener("click", () => {
this.#uiManager.highlightSelection("floating_button");
this.#buttons.append(button);
;// CONCATENATED MODULE: ./src/display/editor/tools.js
function bindEvents(obj, element, names) {
for (const name of names) {
element.addEventListener(name, obj[name].bind(obj));
function opacityToHex(opacity) {
return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, "0");
return `${AnnotationEditorPrefix}${this.#id++}`;
static get _isSVGFittingCanvas() {
const svg = `data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 1 1" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><rect width="1" height="1" style="fill:red;"/></svg>`;
const canvas = new OffscreenCanvas(1, 3);
const ctx = canvas.getContext("2d");
const image = new Image();
const promise = image.decode().then(() => {
ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3);
return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0;
return shadow(this, "_isSVGFittingCanvas", promise);
async #get(key, rawData) {
this.#cache ||= new Map();
let data = this.#cache.get(key);
id: `image_${this.#baseId}_${this.#id++}`,
if (typeof rawData === "string") {
image = await fetchData(rawData, "blob");
image = data.file = rawData;
if (image.type === "image/svg+xml") {
const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas;
const fileReader = new FileReader();
const imageElement = new Image();
const imagePromise = new Promise((resolve, reject) => {
imageElement.onload = () => {
data.bitmap = imageElement;
fileReader.onload = async () => {
const url = data.svgUrl = fileReader.result;
imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url;
imageElement.onerror = fileReader.onerror = reject;
fileReader.readAsDataURL(image);
data.bitmap = await createImageBitmap(image);
this.#cache.set(key, data);
this.#cache.set(data.id, data);
async getFromFile(file) {
return this.#get(`${lastModified}_${name}_${size}_${type}`, file);
return this.#get(url, url);
this.#cache ||= new Map();
const data = this.#cache.get(id);
return this.getFromFile(data.file);
return this.getFromUrl(data.url);
const data = this.#cache.get(id);
this.#cache ||= new Map();
const data = this.#cache.get(id);
if (data.refCounter !== 0) {
return id.startsWith(`image_${this.#baseId}_`);
constructor(maxSize = 128) {
overwriteIfSameType = false,
if (this.#position === -1) {
if (this.#commands.length > 0) {
this.#commands.length = 0;
this.#commands.push(save);
if (overwriteIfSameType && this.#commands[this.#position].type === type) {
save.undo = this.#commands[this.#position].undo;
this.#commands[this.#position] = save;
const next = this.#position + 1;
if (next === this.#maxSize) {
this.#commands.splice(0, 1);
if (next < this.#commands.length) {
this.#commands.splice(next);
this.#commands.push(save);
if (this.#position === -1) {
} = this.#commands[this.#position];
if (this.#position < this.#commands.length - 1) {
} = this.#commands[this.#position];
return this.#position !== -1;
return this.#position < this.#commands.length - 1;
this.callbacks = new Map();
this.allKeys = new Set();
} = util_FeatureTest.platform;
for (const [keys, callback, options = {}] of callbacks) {
for (const key of keys) {
const isMacKey = key.startsWith("mac+");
this.callbacks.set(key.slice(4), {
this.allKeys.add(key.split("+").at(-1));
} else if (!isMac && !isMacKey) {
this.callbacks.set(key, {
this.allKeys.add(key.split("+").at(-1));
this.buffer.push("ctrl");
this.buffer.push("meta");
this.buffer.push("shift");
this.buffer.push(event.key);
const str = this.buffer.join("+");
if (!this.allKeys.has(event.key)) {
const info = this.callbacks.get(this.#serialize(event));
if (checker && !checker(self, event)) {
callback.bind(self, ...args, event)();
static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]);
const colors = new Map([["CanvasText", null], ["Canvas", null]]);
return shadow(this, "_colors", colors);
const rgb = getRGB(color);
if (!window.matchMedia("(forced-colors: active)").matches) {
for (const [name, RGB] of this._colors) {
if (RGB.every((x, i) => x === rgb[i])) {
return ColorManager._colorsMapping.get(name);
const rgb = this._colors.get(name);
return Util.makeHexColor(...rgb);
class AnnotationEditorUIManager {
#annotationStorage = null;
#changedExistingAnnotations = null;
#commandManager = new CommandManager();
#deletedAnnotationsElementIds = new Set();
#editorsToRescale = new Set();
#enableHighlightFloatingButton = false;
#focusMainContainerTimeoutId = null;
#highlightWhenShiftUp = false;
#highlightToolbar = null;
#idManager = new IdManager();
#lastActiveElement = null;
#mainHighlightColorPicker = null;
#mode = AnnotationEditorType.NONE;
#selectedEditors = new Set();
#selectedTextNode = null;
#boundBlur = this.blur.bind(this);
#boundFocus = this.focus.bind(this);
#boundCopy = this.copy.bind(this);
#boundCut = this.cut.bind(this);
#boundPaste = this.paste.bind(this);
#boundKeydown = this.keydown.bind(this);
#boundKeyup = this.keyup.bind(this);
#boundOnEditingAction = this.onEditingAction.bind(this);
#boundOnPageChanging = this.onPageChanging.bind(this);
#boundOnScaleChanging = this.onScaleChanging.bind(this);
#boundSelectionChange = this.#selectionChange.bind(this);
#boundOnRotationChanging = this.onRotationChanging.bind(this);
hasSomethingToUndo: false,
hasSomethingToRedo: false,
hasSelectedEditor: false,
#translationTimeoutId = null;
static TRANSLATE_SMALL = 1;
static TRANSLATE_BIG = 10;
static get _keyboardManager() {
const proto = AnnotationEditorUIManager.prototype;
const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl();
const textInputChecker = (_self, {
if (el instanceof HTMLInputElement) {
return type !== "text" && type !== "number";
const small = this.TRANSLATE_SMALL;
const big = this.TRANSLATE_BIG;
return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, {
checker: textInputChecker
}], [["ctrl+z", "mac+meta+z"], proto.undo, {
checker: textInputChecker
}], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, {
checker: textInputChecker
}], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, {
checker: textInputChecker
}], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, {
}) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled
}], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, {