: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
this.container.classList.add("fileAttachmentAnnotation");
if (data.hasAppearance || data.fillAlpha === 0) {
trigger = document.createElement("div");
trigger = document.createElement("img");
trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`;
if (data.fillAlpha && data.fillAlpha < 1) {
trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`;
trigger.addEventListener("dblclick", this.#download.bind(this));
} = util_FeatureTest.platform;
container.addEventListener("keydown", evt => {
if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) {
if (!data.popupRef && this.hasPopupData) {
trigger.classList.add("popupTriggerArea");
container.append(trigger);
getElementsToTriggerPopup() {
this.container.classList.add("highlightArea");
this.downloadManager?.openOrDownloadData(this.content, this.filename);
#accessibilityManager = null;
#annotationCanvasMap = null;
#editableAnnotations = new Map();
annotationEditorUIManager,
this.#accessibilityManager = accessibilityManager;
this.#annotationCanvasMap = annotationCanvasMap;
this.viewport = viewport;
this._annotationEditorUIManager = annotationEditorUIManager;
#appendElement(element, id) {
const contentElement = element.firstChild || element;
contentElement.id = `${AnnotationPrefix}${id}`;
this.div.append(element);
this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false);
setLayerDimensions(layer, this.viewport);
const popupToElements = new Map();
linkService: params.linkService,
downloadManager: params.downloadManager,
imageResourcesPath: params.imageResourcesPath || "",
renderForms: params.renderForms !== false,
svgFactory: new DOMSVGFactory(),
annotationStorage: params.annotationStorage || new AnnotationStorage(),
enableScripting: params.enableScripting === true,
hasJSActions: params.hasJSActions,
fieldObjects: params.fieldObjects,
for (const data of annotations) {
const isPopupAnnotation = data.annotationType === AnnotationType.POPUP;
if (!isPopupAnnotation) {
} = getRectDims(data.rect);
if (width <= 0 || height <= 0) {
const elements = popupToElements.get(data.id);
elementParams.elements = elements;
elementParams.data = data;
const element = AnnotationElementFactory.create(elementParams);
if (!element.isRenderable) {
if (!isPopupAnnotation && data.popupRef) {
const elements = popupToElements.get(data.popupRef);
popupToElements.set(data.popupRef, [element]);
const rendered = element.render();
rendered.style.visibility = "hidden";
this.#appendElement(rendered, data.id);
if (element.annotationEditorType > 0) {
this.#editableAnnotations.set(element.data.id, element);
this._annotationEditorUIManager?.renderAnnotationElement(element);
this.#setAnnotationCanvasMap();
this.viewport = viewport;
setLayerDimensions(layer, {
rotation: viewport.rotation
this.#setAnnotationCanvasMap();
#setAnnotationCanvasMap() {
if (!this.#annotationCanvasMap) {
for (const [id, canvas] of this.#annotationCanvasMap) {
const element = layer.querySelector(`[data-annotation-id="${id}"]`);
canvas.className = "annotationContent";
} else if (firstChild.nodeName === "CANVAS") {
firstChild.replaceWith(canvas);
} else if (!firstChild.classList.contains("annotationContent")) {
firstChild.before(canvas);
firstChild.after(canvas);
this.#annotationCanvasMap.clear();
getEditableAnnotations() {
return Array.from(this.#editableAnnotations.values());
getEditableAnnotation(id) {
return this.#editableAnnotations.get(id);
;// CONCATENATED MODULE: ./src/display/editor/freetext.js
const EOL_PATTERN = /\r\n?|\n/g;
class FreeTextEditor extends AnnotationEditor {
#boundEditorDivBlur = this.editorDivBlur.bind(this);
#boundEditorDivFocus = this.editorDivFocus.bind(this);
#boundEditorDivInput = this.editorDivInput.bind(this);
#boundEditorDivKeydown = this.editorDivKeydown.bind(this);
#boundEditorDivPaste = this.editorDivPaste.bind(this);
#editorDivId = `${this.id}-editor`;
static _freeTextDefaultContent = "";
static _internalPadding = 0;
static _defaultColor = null;
static _defaultFontSize = 10;
static get _keyboardManager() {
const proto = FreeTextEditor.prototype;
const arrowChecker = self => self.isEmpty();
const small = AnnotationEditorUIManager.TRANSLATE_SMALL;
const big = AnnotationEditorUIManager.TRANSLATE_BIG;
return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, {
}], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, {
}], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, {
}], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, {
}], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, {
}], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, {
}], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, {
}], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, {
}], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, {
static _type = "freetext";
static _editorType = AnnotationEditorType.FREETEXT;
this.#color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor;
this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
static initialize(l10n, uiManager) {
AnnotationEditor.initialize(l10n, uiManager, {
strings: ["pdfjs-free-text-default-content"]
const style = getComputedStyle(document.documentElement);
this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding"));
static updateDefaultParams(type, value) {
case AnnotationEditorParamsType.FREETEXT_SIZE:
FreeTextEditor._defaultFontSize = value;
case AnnotationEditorParamsType.FREETEXT_COLOR:
FreeTextEditor._defaultColor = value;
updateParams(type, value) {
case AnnotationEditorParamsType.FREETEXT_SIZE:
this.#updateFontSize(value);
case AnnotationEditorParamsType.FREETEXT_COLOR:
this.#updateColor(value);
static get defaultPropertiesToUpdate() {
return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]];
get propertiesToUpdate() {
return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]];
#updateFontSize(fontSize) {
const setFontsize = size => {
this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`;
this.translate(0, -(size - this.#fontSize) * this.parentScale);
this.#setEditorDimensions();
const savedFontsize = this.#fontSize;
cmd: setFontsize.bind(this, fontSize),
undo: setFontsize.bind(this, savedFontsize),
post: this._uiManager.updateUI.bind(this._uiManager, this),
type: AnnotationEditorParamsType.FREETEXT_SIZE,
overwriteIfSameType: true,
const setColor = col => {
this.#color = this.editorDiv.style.color = 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.FREETEXT_COLOR,
overwriteIfSameType: true,
this._uiManager.translateSelectedEditors(x, y, true);
getInitialTranslation() {
const scale = this.parentScale;
return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale];
if (!this.isAttachedToDOM) {
if (this.isInEditMode()) {
this.parent.setEditingState(false);
this.parent.updateToolbar(AnnotationEditorType.FREETEXT);
this.overlayDiv.classList.remove("enabled");
this.editorDiv.contentEditable = true;
this._isDraggable = false;
this.div.removeAttribute("aria-activedescendant");
this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown);
this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus);
this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur);
this.editorDiv.addEventListener("input", this.#boundEditorDivInput);
this.editorDiv.addEventListener("paste", this.#boundEditorDivPaste);
if (!this.isInEditMode()) {
this.parent.setEditingState(true);
this.overlayDiv.classList.add("enabled");
this.editorDiv.contentEditable = false;
this.div.setAttribute("aria-activedescendant", this.#editorDivId);
this._isDraggable = true;
this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus);
this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur);
this.editorDiv.removeEventListener("input", this.#boundEditorDivInput);
this.editorDiv.removeEventListener("paste", this.#boundEditorDivPaste);
this.parent.div.classList.add("freetextEditing");
if (!this._focusEventsAllowed) {
if (event.target !== this.editorDiv) {
if (this._initialOptions?.isCentered) {
this._initialOptions = null;
return !this.editorDiv || this.editorDiv.innerText.trim() === "";
this.parent.setEditingState(true);
this.parent.div.classList.add("freetextEditing");
this.editorDiv.normalize();
for (const child of this.editorDiv.childNodes) {
buffer.push(FreeTextEditor.#getNodeContent(child));
return buffer.join("\n");
const [parentWidth, parentHeight] = this.parentDimensions;
if (this.isAttachedToDOM) {
rect = this.div.getBoundingClientRect();
const savedDisplay = div.style.display;
const savedVisibility = div.classList.contains("hidden");
div.classList.remove("hidden");
div.style.display = "hidden";
currentLayer.div.append(this.div);
rect = div.getBoundingClientRect();
div.style.display = savedDisplay;
div.classList.toggle("hidden", savedVisibility);
if (this.rotation % 180 === this.parentRotation % 180) {
this.width = rect.width / parentWidth;
this.height = rect.height / parentHeight;
this.width = rect.height / parentWidth;
this.height = rect.width / parentHeight;
this.fixAndSetPosition();
if (!this.isInEditMode()) {
const savedText = this.#content;
const newText = this.#content = this.#extractText().trimEnd();
if (savedText === newText) {
const setText = text => {
this._uiManager.rebuild(this);
this.#setEditorDimensions();
this.#setEditorDimensions();
shouldGetKeyboardEvents() {
return this.isInEditMode();
if (event.target === this.div && event.key === "Enter") {
editorDivKeydown(event) {
FreeTextEditor._keyboardManager.exec(this, event);
this.parent.div.classList.toggle("freetextEditing", this.isEmpty());