: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (this.#isFreeHighlight) {
angle = (angle - this.rotation + 360) % 360;
box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle);
box = HighlightEditor.#rotateBbox(this, angle);
drawLayer.rotate(this.#id, angle);
drawLayer.rotate(this.#outlineId, angle);
drawLayer.updateBox(this.#id, box);
drawLayer.updateBox(this.#outlineId, HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle));
const div = super.render();
div.setAttribute("aria-label", this.#text);
div.setAttribute("role", "mark");
if (this.#isFreeHighlight) {
div.classList.add("free");
this.div.addEventListener("keydown", this.#boundKeydown);
const highlightDiv = this.#highlightDiv = document.createElement("div");
div.append(highlightDiv);
highlightDiv.setAttribute("aria-hidden", "true");
highlightDiv.className = "internal";
highlightDiv.style.clipPath = this.#clipPathId;
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]);
this.parent.drawLayer.addClass(this.#outlineId, "hovered");
this.parent.drawLayer.removeClass(this.#outlineId, "hovered");
HighlightEditor._keyboardManager.exec(this, event);
this.parent.unselect(this);
const selection = window.getSelection();
selection.setPosition(this.#anchorNode, this.#anchorOffset);
selection.setPosition(this.#focusNode, this.#focusOffset);
this.parent?.drawLayer.removeClass(this.#outlineId, "hovered");
this.parent?.drawLayer.addClass(this.#outlineId, "selected");
this.parent?.drawLayer.removeClass(this.#outlineId, "selected");
if (!this.#isFreeHighlight) {
return !this.#isFreeHighlight;
show(visible = this._isVisible) {
this.parent.drawLayer.show(this.#id, visible);
this.parent.drawLayer.show(this.#outlineId, visible);
return this.#isFreeHighlight ? this.rotation : 0;
if (this.#isFreeHighlight) {
const [pageWidth, pageHeight] = this.pageDimensions;
const boxes = this.#boxes;
const quadPoints = new Array(boxes.length * 8);
const sx = x * pageWidth;
const sy = (1 - y - height) * pageHeight;
quadPoints[i] = quadPoints[i + 4] = sx;
quadPoints[i + 1] = quadPoints[i + 3] = sy;
quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth;
quadPoints[i + 5] = quadPoints[i + 7] = sy + height * pageHeight;
#serializeOutlines(rect) {
return this.#highlightOutlines.serialize(rect, this.#getRotation());
static startHighlighting(parent, isLTR, {
} = textLayer.getBoundingClientRect();
const pointerMove = e => {
this.#highlightMove(parent, e);
const pointerDownOptions = {
const pointerDown = e => {
const pointerUpCallback = e => {
textLayer.removeEventListener("pointermove", pointerMove);
window.removeEventListener("blur", pointerUpCallback);
window.removeEventListener("pointerup", pointerUpCallback);
window.removeEventListener("pointerdown", pointerDown, pointerDownOptions);
window.removeEventListener("contextmenu", noContextMenu);
this.#endHighlight(parent, e);
window.addEventListener("blur", pointerUpCallback);
window.addEventListener("pointerup", pointerUpCallback);
window.addEventListener("pointerdown", pointerDown, pointerDownOptions);
window.addEventListener("contextmenu", noContextMenu);
textLayer.addEventListener("pointermove", pointerMove);
this._freeHighlight = new FreeOutliner({
}, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001);
id: this._freeHighlightId,
clipPathId: this._freeHighlightClipId
} = parent.drawLayer.highlight(this._freeHighlight, this._defaultColor, this._defaultOpacity, true));
static #highlightMove(parent, event) {
if (this._freeHighlight.add(event)) {
parent.drawLayer.updatePath(this._freeHighlightId, this._freeHighlight);
static #endHighlight(parent, event) {
if (!this._freeHighlight.isEmpty()) {
parent.createAndAddNewEditor(event, false, {
highlightId: this._freeHighlightId,
highlightOutlines: this._freeHighlight.getOutlines(),
clipPathId: this._freeHighlightClipId,
methodOfCreation: "main_toolbar"
parent.drawLayer.removeFreeHighlight(this._freeHighlightId);
this._freeHighlightId = -1;
this._freeHighlight = null;
this._freeHighlightClipId = "";
static deserialize(data, parent, uiManager) {
const editor = super.deserialize(data, parent, uiManager);
rect: [blX, blY, trX, trY],
editor.color = Util.makeHexColor(...color);
editor.#opacity = data.opacity;
const [pageWidth, pageHeight] = editor.pageDimensions;
editor.width = (trX - blX) / pageWidth;
editor.height = (trY - blY) / pageHeight;
const boxes = editor.#boxes = [];
for (let i = 0; i < quadPoints.length; i += 8) {
x: (quadPoints[4] - trX) / pageWidth,
y: (trY - (1 - quadPoints[i + 5])) / pageHeight,
width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth,
height: (quadPoints[i + 5] - quadPoints[i + 1]) / pageHeight
editor.#createOutlines();
serialize(isForCopying = false) {
if (this.isEmpty() || isForCopying) {
const rect = this.getRect(0, 0);
const color = AnnotationEditor._colorManager.convert(this.color);
annotationType: AnnotationEditorType.HIGHLIGHT,
thickness: this.#thickness,
quadPoints: this.#serializeBoxes(),
outlines: this.#serializeOutlines(rect),
pageIndex: this.pageIndex,
rotation: this.#getRotation(),
structTreeParentId: this._structTreeParentId
static canCreateNewEmptyEditor() {
;// CONCATENATED MODULE: ./src/display/editor/ink.js
class InkEditor extends AnnotationEditor {
#boundCanvasPointermove = this.canvasPointermove.bind(this);
#boundCanvasPointerleave = this.canvasPointerleave.bind(this);
#boundCanvasPointerup = this.canvasPointerup.bind(this);
#boundCanvasPointerdown = this.canvasPointerdown.bind(this);
#canvasContextMenuTimeoutId = null;
#currentPath2D = new Path2D();
#hasSomethingToDraw = false;
#isCanvasInitialized = false;
#requestFrameCallback = null;
static _defaultColor = null;
static _defaultOpacity = 1;
static _defaultThickness = 1;
static _editorType = AnnotationEditorType.INK;
this.color = params.color || null;
this.thickness = params.thickness || null;
this.opacity = params.opacity || null;
this.translationX = this.translationY = 0;
this._willKeepAspectRatio = true;
static initialize(l10n, uiManager) {
AnnotationEditor.initialize(l10n, uiManager);
static updateDefaultParams(type, value) {
case AnnotationEditorParamsType.INK_THICKNESS:
InkEditor._defaultThickness = value;
case AnnotationEditorParamsType.INK_COLOR:
InkEditor._defaultColor = value;
case AnnotationEditorParamsType.INK_OPACITY:
InkEditor._defaultOpacity = value / 100;
updateParams(type, value) {
case AnnotationEditorParamsType.INK_THICKNESS:
this.#updateThickness(value);
case AnnotationEditorParamsType.INK_COLOR:
this.#updateColor(value);
case AnnotationEditorParamsType.INK_OPACITY:
this.#updateOpacity(value);
static get defaultPropertiesToUpdate() {
return [[AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(InkEditor._defaultOpacity * 100)]];
get propertiesToUpdate() {
return [[AnnotationEditorParamsType.INK_THICKNESS, this.thickness || InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, this.color || InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(100 * (this.opacity ?? InkEditor._defaultOpacity))]];
#updateThickness(thickness) {
const setThickness = th => {
const savedThickness = this.thickness;
cmd: setThickness.bind(this, thickness),
undo: setThickness.bind(this, savedThickness),
post: this._uiManager.updateUI.bind(this._uiManager, this),
type: AnnotationEditorParamsType.INK_THICKNESS,
overwriteIfSameType: true,
const setColor = col => {
const savedColor = this.color;
cmd: setColor.bind(this, color),
undo: setColor.bind(this, savedColor),
post: this._uiManager.updateUI.bind(this._uiManager, this),
type: AnnotationEditorParamsType.INK_COLOR,
overwriteIfSameType: true,
#updateOpacity(opacity) {
const setOpacity = op => {
const savedOpacity = this.opacity;
cmd: setOpacity.bind(this, opacity),
undo: setOpacity.bind(this, savedOpacity),
post: this._uiManager.updateUI.bind(this._uiManager, this),
type: AnnotationEditorParamsType.INK_OPACITY,
overwriteIfSameType: true,
if (!this.isAttachedToDOM) {
if (this.canvas === null) {
this.canvas.width = this.canvas.height = 0;
if (this.#canvasContextMenuTimeoutId) {
clearTimeout(this.#canvasContextMenuTimeoutId);
this.#canvasContextMenuTimeoutId = null;
this.#observer.disconnect();
if (!this.parent && parent) {
this._uiManager.removeShouldRescale(this);
} else if (this.parent && parent === null) {
this._uiManager.addShouldRescale(this);
const [parentWidth, parentHeight] = this.parentDimensions;
const width = this.width * parentWidth;
const height = this.height * parentHeight;
this.setDimensions(width, height);
if (this.#disableEditing || this.canvas === null) {
this._isDraggable = false;
this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown);
if (!this.isInEditMode() || this.canvas === null) {
this._isDraggable = !this.isEmpty();
this.div.classList.remove("editing");
this.canvas.removeEventListener("pointerdown", this.#boundCanvasPointerdown);
this._isDraggable = !this.isEmpty();
return this.paths.length === 0 || this.paths.length === 1 && this.paths[0].length === 0;
parentDimensions: [width, height]
switch (parentRotation) {
return [0, height, height, width];
return [width, height, width, height];
return [width, 0, height, width];
return [0, 0, width, height];
ctx.lineWidth = thickness * parentScale / scaleFactor;
ctx.strokeStyle = `${color}${opacityToHex(opacity)}`;
this.canvas.addEventListener("contextmenu", noContextMenu);
this.canvas.addEventListener("pointerleave", this.#boundCanvasPointerleave);
this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove);
this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup);
this.canvas.removeEventListener("pointerdown", this.#boundCanvasPointerdown);
if (!this.#isCanvasInitialized) {
this.#isCanvasInitialized = true;
this.thickness ||= InkEditor._defaultThickness;
this.color ||= InkEditor._defaultColor || AnnotationEditor._defaultLineColor;
this.opacity ??= InkEditor._defaultOpacity;
this.currentPath.push([x, y]);
this.#hasSomethingToDraw = false;
this.#requestFrameCallback = () => {
if (this.#requestFrameCallback) {
window.requestAnimationFrame(this.#requestFrameCallback);