: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
this.editorDiv.setAttribute("role", "comment");
this.editorDiv.removeAttribute("aria-multiline");
this.editorDiv.setAttribute("role", "textbox");
this.editorDiv.setAttribute("aria-multiline", true);
this.editorDiv = document.createElement("div");
this.editorDiv.className = "internal";
this.editorDiv.setAttribute("id", this.#editorDivId);
this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text");
AnnotationEditor._l10nPromise.get("pdfjs-free-text-default-content").then(msg => this.editorDiv?.setAttribute("default-content", msg));
this.editorDiv.contentEditable = true;
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;
this.div.append(this.editorDiv);
this.overlayDiv = document.createElement("div");
this.overlayDiv.classList.add("overlay", "enabled");
this.div.append(this.overlayDiv);
bindEvents(this, this.div, ["dblclick", "keydown"]);
const [parentWidth, parentHeight] = this.parentDimensions;
if (this.annotationElementId) {
let [tx, ty] = this.getInitialTranslation();
[tx, ty] = this.pageTranslationToScreen(tx, ty);
const [pageWidth, pageHeight] = this.pageDimensions;
const [pageX, pageY] = this.pageTranslation;
posX = baseX + (position[0] - pageX) / pageWidth;
posY = baseY + this.height - (position[1] - pageY) / pageHeight;
posX = baseX + (position[0] - pageX) / pageWidth;
posY = baseY - (position[1] - pageY) / pageHeight;
posX = baseX - this.width + (position[0] - pageX) / pageWidth;
posY = baseY - (position[1] - pageY) / pageHeight;
posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth;
posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight;
this.setAt(posX * parentWidth, posY * parentHeight, tx, ty);
this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
this._isDraggable = true;
this.editorDiv.contentEditable = false;
this._isDraggable = false;
this.editorDiv.contentEditable = true;
static #getNodeContent(node) {
return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, "");
const clipboardData = event.clipboardData || window.clipboardData;
if (types.length === 1 && types[0] === "text/plain") {
const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n");
const selection = window.getSelection();
if (!selection.rangeCount) {
this.editorDiv.normalize();
selection.deleteFromDocument();
const range = selection.getRangeAt(0);
if (!paste.includes("\n")) {
range.insertNode(document.createTextNode(paste));
this.editorDiv.normalize();
selection.collapseToStart();
if (startContainer.nodeType === Node.TEXT_NODE) {
const parent = startContainer.parentElement;
bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, ""));
if (parent !== this.editorDiv) {
let buffer = bufferBefore;
for (const child of this.editorDiv.childNodes) {
buffer.push(FreeTextEditor.#getNodeContent(child));
bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, ""));
} else if (startContainer === this.editorDiv) {
let buffer = bufferBefore;
for (const child of this.editorDiv.childNodes) {
if (i++ === startOffset) {
buffer.push(FreeTextEditor.#getNodeContent(child));
this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`;
const newRange = new Range();
let beforeLength = bufferBefore.reduce((acc, line) => acc + line.length, 0);
} of this.editorDiv.childNodes) {
if (firstChild.nodeType === Node.TEXT_NODE) {
const length = firstChild.nodeValue.length;
if (beforeLength <= length) {
newRange.setStart(firstChild, beforeLength);
newRange.setEnd(firstChild, beforeLength);
selection.removeAllRanges();
selection.addRange(newRange);
this.editorDiv.replaceChildren();
for (const line of this.#content.split("\n")) {
const div = document.createElement("div");
div.append(line ? document.createTextNode(line) : document.createElement("br"));
this.editorDiv.append(div);
return this.#content.replaceAll("\xa0", " ");
static #deserializeContent(content) {
return content.replaceAll(" ", "\xa0");
static deserialize(data, parent, uiManager) {
if (data instanceof FreeTextAnnotationElement) {
if (!textContent || textContent.length === 0) {
annotationType: AnnotationEditorType.FREETEXT,
color: Array.from(fontColor),
value: textContent.join("\n"),
pageIndex: pageNumber - 1,
const editor = super.deserialize(data, parent, uiManager);
editor.#fontSize = data.fontSize;
editor.#color = Util.makeHexColor(...data.color);
editor.#content = FreeTextEditor.#deserializeContent(data.value);
editor.annotationElementId = data.id || null;
editor.#initialData = initialData;
serialize(isForCopying = false) {
pageIndex: this.pageIndex,
id: this.annotationElementId,
const padding = FreeTextEditor._internalPadding * this.parentScale;
const rect = this.getRect(padding, padding);
const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color);
annotationType: AnnotationEditorType.FREETEXT,
fontSize: this.#fontSize,
value: this.#serializeContent(),
pageIndex: this.pageIndex,
structTreeParentId: this._structTreeParentId
if (this.annotationElementId && !this.#hasElementChanged(serialized)) {
serialized.id = this.annotationElementId;
#hasElementChanged(serialized) {
return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;
renderAnnotationElement(annotation) {
const content = super.renderAnnotationElement(annotation);
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;
content.replaceChildren();
for (const line of this.#content.split("\n")) {
const div = document.createElement("div");
div.append(line ? document.createTextNode(line) : document.createElement("br"));
const padding = FreeTextEditor._internalPadding * this.parentScale;
annotation.updateEdited({
rect: this.getRect(padding, padding),
popupContent: this.#content
resetAnnotationElement(annotation) {
super.resetAnnotationElement(annotation);
annotation.resetEdited();
;// CONCATENATED MODULE: ./src/display/editor/outliner.js
constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) {
const NUMBER_OF_DIGITS = 4;
const EPSILON = 10 ** -NUMBER_OF_DIGITS;
const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON;
const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON;
const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON;
const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON;
const left = [x1, y1, y2, true];
const right = [x2, y1, y2, false];
this.#verticalEdges.push(left, right);
minX = Math.min(minX, x1);
maxX = Math.max(maxX, x2);
minY = Math.min(minY, y1);
maxY = Math.max(maxY, y2);
const bboxWidth = maxX - minX + 2 * innerMargin;
const bboxHeight = maxY - minY + 2 * innerMargin;
const shiftedMinX = minX - innerMargin;
const shiftedMinY = minY - innerMargin;
const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2);
const lastPoint = [lastEdge[0], lastEdge[2]];
for (const edge of this.#verticalEdges) {
const [x, y1, y2] = edge;
edge[0] = (x - shiftedMinX) / bboxWidth;
edge[1] = (y1 - shiftedMinY) / bboxHeight;
edge[2] = (y2 - shiftedMinY) / bboxHeight;
this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
const outlineVerticalEdges = [];
for (const edge of this.#verticalEdges) {
outlineVerticalEdges.push(...this.#breakEdge(edge));
outlineVerticalEdges.push(...this.#breakEdge(edge));
return this.#getOutlines(outlineVerticalEdges);
#getOutlines(outlineVerticalEdges) {
const allEdges = new Set();
for (const edge of outlineVerticalEdges) {
const [x, y1, y2] = edge;
edges.push([x, y1, edge], [x, y2, edge]);
edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
for (let i = 0, ii = edges.length; i < ii; i += 2) {
const edge1 = edges[i][2];
const edge2 = edges[i + 1][2];
while (allEdges.size > 0) {
const edge = allEdges.values().next().value;
let [x, y1, y2, edge1, edge2] = edge;
if (allEdges.has(edge1)) {
} else if (allEdges.has(edge2)) {
[x, y1, y2, edge1, edge2] = e;
outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2);
lastPointY = lastPointY === y1 ? y2 : y1;
outline.push(lastPointX, lastPointY);
return new HighlightOutline(outlines, this.#box);
const array = this.#intervals;
let end = array.length - 1;
const middle = start + end >> 1;
const y1 = array[middle][0];
const index = this.#binarySearch(y1);
this.#intervals.splice(index, 0, [y1, y2]);
const index = this.#binarySearch(y1);
for (let i = index; i < this.#intervals.length; i++) {
const [start, end] = this.#intervals[i];
if (start === y1 && end === y2) {
this.#intervals.splice(i, 1);
for (let i = index - 1; i >= 0; i--) {
const [start, end] = this.#intervals[i];
if (start === y1 && end === y2) {
this.#intervals.splice(i, 1);
const [x, y1, y2] = edge;
const results = [[x, y1, y2]];
const index = this.#binarySearch(y2);
for (let i = 0; i < index; i++) {
const [start, end] = this.#intervals[i];
for (let j = 0, jj = results.length; j < jj; j++) {
const [, y3, y4] = results[j];
if (end <= y3 || y4 <= start) {
results.push([x, end, y4]);