diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index f330514a4..6f39ddd7c 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -17,8 +17,6 @@ // eslint-disable-next-line max-len /** @typedef {import("./tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */ // eslint-disable-next-line max-len -/** @typedef {import("../annotation_storage.js").AnnotationStorage} AnnotationStorage */ -// eslint-disable-next-line max-len /** @typedef {import("../../web/text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ /** @typedef {import("../../web/interfaces").IL10n} IL10n */ @@ -33,7 +31,6 @@ import { InkEditor } from "./ink.js"; * @property {HTMLDivElement} div * @property {AnnotationEditorUIManager} uiManager * @property {boolean} enabled - * @property {AnnotationStorage} annotationStorage * @property {TextAccessibilityManager} [accessibilityManager] * @property {number} pageIndex * @property {IL10n} l10n @@ -73,7 +70,6 @@ class AnnotationEditorLayer { options.uiManager.registerEditorTypes([FreeTextEditor, InkEditor]); this.#uiManager = options.uiManager; - this.annotationStorage = options.annotationStorage; this.pageIndex = options.pageIndex; this.div = options.div; this.#accessibilityManager = options.accessibilityManager; @@ -213,7 +209,6 @@ class AnnotationEditorLayer { this.#uiManager.removeEditor(editor); this.detach(editor); - this.annotationStorage.remove(editor.id); editor.div.style.display = "none"; setTimeout(() => { // When the div is removed from DOM the focus can move on the @@ -244,7 +239,6 @@ class AnnotationEditorLayer { } this.attach(editor); - editor.pageIndex = this.pageIndex; editor.parent?.detach(editor); editor.setParent(this); if (editor.div && editor.isAttachedToDOM) { @@ -270,7 +264,7 @@ class AnnotationEditorLayer { this.moveEditorInDOM(editor); editor.onceAdded(); - this.addToAnnotationStorage(editor); + this.#uiManager.addToAnnotationStorage(editor); } moveEditorInDOM(editor) { @@ -282,16 +276,6 @@ class AnnotationEditorLayer { ); } - /** - * Add an editor in the annotation storage. - * @param {AnnotationEditor} editor - */ - addToAnnotationStorage(editor) { - if (!editor.isEmpty() && !this.annotationStorage.has(editor.id)) { - this.annotationStorage.setValue(editor.id, editor); - } - } - /** * Add or rebuild depending if it has been removed or not. * @param {AnnotationEditor} editor @@ -365,9 +349,9 @@ class AnnotationEditorLayer { deserialize(data) { switch (data.annotationType) { case AnnotationEditorType.FREETEXT: - return FreeTextEditor.deserialize(data, this); + return FreeTextEditor.deserialize(data, this, this.#uiManager); case AnnotationEditorType.INK: - return InkEditor.deserialize(data, this); + return InkEditor.deserialize(data, this, this.#uiManager); } return null; } @@ -384,6 +368,7 @@ class AnnotationEditorLayer { id, x: event.offsetX, y: event.offsetY, + uiManager: this.#uiManager, }); if (editor) { this.add(editor); @@ -520,8 +505,8 @@ class AnnotationEditorLayer { for (const editor of this.#editors.values()) { this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); - editor.isAttachedToDOM = false; editor.setParent(null); + editor.isAttachedToDOM = false; editor.div.remove(); } this.div = null; @@ -571,14 +556,6 @@ class AnnotationEditorLayer { this.updateMode(); } - /** - * Get the scale factor from the viewport. - * @returns {number} - */ - get scaleFactor() { - return this.viewport.scale; - } - /** * Get page dimensions. * @returns {Object} dimensions. @@ -591,11 +568,6 @@ class AnnotationEditorLayer { return [width, height]; } - get viewportBaseDimensions() { - const { width, height, rotation } = this.viewport; - return rotation % 180 === 0 ? [width, height] : [height, width]; - } - /** * Set the dimensions of the main div. */ diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 4900ec989..19e43579b 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -15,12 +15,15 @@ // eslint-disable-next-line max-len /** @typedef {import("./annotation_editor_layer.js").AnnotationEditorLayer} AnnotationEditorLayer */ +// eslint-disable-next-line max-len +/** @typedef {import("./tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */ import { bindEvents, ColorManager } from "./tools.js"; import { FeatureTest, shadow, unreachable } from "../../shared/util.js"; /** * @typedef {Object} AnnotationEditorParameters + * @property {AnnotationEditorUIManager} uiManager - the global manager * @property {AnnotationEditorLayer} parent - the layer containing this editor * @property {string} id - editor id * @property {number} x - x-coordinate @@ -41,6 +44,8 @@ class AnnotationEditor { #isInEditMode = false; + _uiManager = null; + #zIndex = AnnotationEditor._zIndex++; static _colorManager = new ColorManager(); @@ -61,15 +66,15 @@ class AnnotationEditor { this.pageIndex = parameters.parent.pageIndex; this.name = parameters.name; this.div = null; + this._uiManager = parameters.uiManager; - const [width, height] = this.parent.viewportBaseDimensions; + this.rotation = this.parent.viewport.rotation; + this.pageDimensions = this.parent.pageDimensions; + const [width, height] = this.parentDimensions; this.x = parameters.x / width; this.y = parameters.y / height; - this.rotation = this.parent.viewport.rotation; this.isAttachedToDOM = false; - - this._serialized = undefined; } static get _defaultLineColor() { @@ -80,9 +85,16 @@ class AnnotationEditor { ); } - setParent(parent) { - this._serialized = !parent ? this.serialize() : undefined; - this.parent = parent; + /** + * Add some commands into the CommandManager (undo/redo stuff). + * @param {Object} params + */ + addCommands(params) { + this._uiManager.addCommands(params); + } + + get currentLayer() { + return this._uiManager.currentLayer; } /** @@ -99,6 +111,14 @@ class AnnotationEditor { this.div.style.zIndex = this.#zIndex; } + setParent(parent) { + if (parent !== null) { + this.pageIndex = parent.pageIndex; + this.pageDimensions = parent.pageDimensions; + } + this.parent = parent; + } + /** * onfocus callback. */ @@ -130,7 +150,7 @@ class AnnotationEditor { event.preventDefault(); - if (!this.parent.isMultipleSelection) { + if (!this.parent?.isMultipleSelection) { this.commitOrRemove(); } } @@ -147,7 +167,11 @@ class AnnotationEditor { * Commit the data contained in this editor. */ commit() { - this.parent.addToAnnotationStorage(this); + this.addToAnnotationStorage(); + } + + addToAnnotationStorage() { + this._uiManager.addToAnnotationStorage(this); } /** @@ -170,7 +194,7 @@ class AnnotationEditor { * @param {number} ty - y-translation in screen coordinates. */ setAt(x, y, tx, ty) { - const [width, height] = this.parent.viewportBaseDimensions; + const [width, height] = this.parentDimensions; [tx, ty] = this.screenToPageTranslation(tx, ty); this.x = (x + tx) / width; @@ -186,7 +210,7 @@ class AnnotationEditor { * @param {number} y - y-translation in screen coordinates. */ translate(x, y) { - const [width, height] = this.parent.viewportBaseDimensions; + const [width, height] = this.parentDimensions; [x, y] = this.screenToPageTranslation(x, y); this.x += x / width; @@ -202,8 +226,7 @@ class AnnotationEditor { * @param {number} y */ screenToPageTranslation(x, y) { - const { rotation } = this.parent.viewport; - switch (rotation) { + switch (this.parentRotation) { case 90: return [y, -x]; case 180: @@ -215,13 +238,27 @@ class AnnotationEditor { } } + get parentScale() { + return this._uiManager.viewParameters.realScale; + } + + get parentRotation() { + return this._uiManager.viewParameters.rotation; + } + + get parentDimensions() { + const { realScale } = this._uiManager.viewParameters; + const [pageWidth, pageHeight] = this.pageDimensions; + return [pageWidth * realScale, pageHeight * realScale]; + } + /** * Set the dimensions of this editor. * @param {number} width * @param {number} height */ setDims(width, height) { - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.div.style.width = `${(100 * width) / parentWidth}%`; this.div.style.height = `${(100 * height) / parentHeight}%`; } @@ -235,7 +272,7 @@ class AnnotationEditor { return; } - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; if (!widthPercent) { style.width = `${(100 * parseFloat(width)) / parentWidth}%`; } @@ -302,10 +339,10 @@ class AnnotationEditor { } getRect(tx, ty) { - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; - const [pageWidth, pageHeight] = this.parent.pageDimensions; - const shiftX = (pageWidth * tx) / parentWidth; - const shiftY = (pageHeight * ty) / parentHeight; + const scale = this.parentScale; + const [pageWidth, pageHeight] = this.pageDimensions; + const shiftX = tx / scale; + const shiftY = ty / scale; const x = this.x * pageWidth; const y = this.y * pageHeight; const width = this.width * pageWidth; @@ -443,16 +480,18 @@ class AnnotationEditor { * * @param {Object} data * @param {AnnotationEditorLayer} parent + * @param {AnnotationEditorUIManager} uiManager * @returns {AnnotationEditor} */ - static deserialize(data, parent) { + static deserialize(data, parent, uiManager) { const editor = new this.prototype.constructor({ parent, id: parent.getNextId(), + uiManager, }); editor.rotation = data.rotation; - const [pageWidth, pageHeight] = parent.pageDimensions; + const [pageWidth, pageHeight] = editor.pageDimensions; const [x, y, width, height] = editor.getRectInCurrentCoords( data.rect, pageHeight diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 6042b9028..71b6be66c 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -153,12 +153,12 @@ class FreeTextEditor extends AnnotationEditor { #updateFontSize(fontSize) { const setFontsize = size => { this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`; - this.translate(0, -(size - this.#fontSize) * this.parent.scaleFactor); + this.translate(0, -(size - this.#fontSize) * this.parentScale); this.#fontSize = size; this.#setEditorDimensions(); }; const savedFontsize = this.#fontSize; - this.parent.addCommands({ + this.addCommands({ cmd: () => { setFontsize(fontSize); }, @@ -178,14 +178,12 @@ class FreeTextEditor extends AnnotationEditor { */ #updateColor(color) { const savedColor = this.#color; - this.parent.addCommands({ + this.addCommands({ cmd: () => { - this.#color = color; - this.editorDiv.style.color = color; + this.#color = this.editorDiv.style.color = color; }, undo: () => { - this.#color = savedColor; - this.editorDiv.style.color = savedColor; + this.#color = this.editorDiv.style.color = savedColor; }, mustExec: true, type: AnnotationEditorParamsType.FREETEXT_COLOR, @@ -197,10 +195,10 @@ class FreeTextEditor extends AnnotationEditor { /** @inheritdoc */ getInitialTranslation() { // The start of the base line is where the user clicked. + const scale = this.parentScale; return [ - -FreeTextEditor._internalPadding * this.parent.scaleFactor, - -(FreeTextEditor._internalPadding + this.#fontSize) * - this.parent.scaleFactor, + -FreeTextEditor._internalPadding * scale, + -(FreeTextEditor._internalPadding + this.#fontSize) * scale, ]; } @@ -254,9 +252,11 @@ class FreeTextEditor extends AnnotationEditor { this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur); this.editorDiv.removeEventListener("input", this.#boundEditorDivInput); - // On Chrome, the focus is given to when contentEditable is set to - // false, hence we focus the div. - this.div.focus(); + if (this.pageIndex === this._uiManager.currentPageIndex) { + // On Chrome, the focus is given to when contentEditable is set to + // false, hence we focus the div. + this.div.focus(); + } // In case the blur callback hasn't been called. this.isEditing = false; @@ -311,8 +311,22 @@ class FreeTextEditor extends AnnotationEditor { } #setEditorDimensions() { - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; - const rect = this.div.getBoundingClientRect(); + const [parentWidth, parentHeight] = this.parentDimensions; + + let rect; + if (this.isAttachedToDOM) { + rect = this.div.getBoundingClientRect(); + } else { + // This editor isn't on screen but we need to get its dimensions, so + // we just insert it in the DOM, get its bounding box and then remove it. + const { currentLayer, div } = this; + const savedDisplay = div.style.display; + div.style.display = "hidden"; + currentLayer.div.append(this.div); + rect = div.getBoundingClientRect(); + div.remove(); + div.style.display = savedDisplay; + } this.width = rect.width / parentWidth; this.height = rect.height / parentHeight; @@ -323,6 +337,10 @@ class FreeTextEditor extends AnnotationEditor { * @returns {undefined} */ commit() { + if (!this.isInEditMode()) { + return; + } + super.commit(); if (!this.#hasAlreadyBeenCommitted) { // This editor has something and it's the first time @@ -435,7 +453,7 @@ class FreeTextEditor extends AnnotationEditor { if (this.width) { // This editor was created in using copy (ctrl+c). - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.setAt( baseX * parentWidth, baseY * parentHeight, @@ -466,8 +484,8 @@ class FreeTextEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent) { - const editor = super.deserialize(data, parent); + static deserialize(data, parent, uiManager) { + const editor = super.deserialize(data, parent, uiManager); editor.#fontSize = data.fontSize; editor.#color = Util.makeHexColor(...data.color); @@ -478,19 +496,17 @@ class FreeTextEditor extends AnnotationEditor { /** @inheritdoc */ serialize() { - if (this._serialized !== undefined) { - return this._serialized; - } - if (this.isEmpty()) { return null; } - const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor; + const padding = FreeTextEditor._internalPadding * this.parentScale; const rect = this.getRect(padding, padding); const color = AnnotationEditor._colorManager.convert( - getComputedStyle(this.editorDiv).color + this.isAttachedToDOM + ? getComputedStyle(this.editorDiv).color + : this.#color ); return { @@ -498,7 +514,7 @@ class FreeTextEditor extends AnnotationEditor { color, fontSize: this.#fontSize, value: this.#content, - pageIndex: this.parent.pageIndex, + pageIndex: this.pageIndex, rect, rotation: this.rotation, }; diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index 434d49e13..429c44362 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -165,7 +165,7 @@ class InkEditor extends AnnotationEditor { */ #updateThickness(thickness) { const savedThickness = this.thickness; - this.parent.addCommands({ + this.addCommands({ cmd: () => { this.thickness = thickness; this.#fitToContent(); @@ -187,7 +187,7 @@ class InkEditor extends AnnotationEditor { */ #updateColor(color) { const savedColor = this.color; - this.parent.addCommands({ + this.addCommands({ cmd: () => { this.color = color; this.#redraw(); @@ -210,7 +210,7 @@ class InkEditor extends AnnotationEditor { #updateOpacity(opacity) { opacity /= 100; const savedOpacity = this.opacity; - this.parent.addCommands({ + this.addCommands({ cmd: () => { this.opacity = opacity; this.#redraw(); @@ -268,6 +268,27 @@ class InkEditor extends AnnotationEditor { super.remove(); } + setParent(parent) { + if (!this.parent && parent) { + // We've a parent hence the rescale will be handled thanks to the + // ResizeObserver. + this._uiManager.removeShouldRescale(this); + } else if (this.parent && parent === null) { + // The editor is removed from the DOM, hence we handle the rescale thanks + // to the onScaleChanging callback. + // This way, it'll be saved/printed correctly. + this._uiManager.addShouldRescale(this); + } + super.setParent(parent); + } + + onScaleChanging() { + const [parentWidth, parentHeight] = this.parentDimensions; + const width = this.width * parentWidth; + const height = this.height * parentHeight; + this.setDimensions(width, height); + } + /** @inheritdoc */ enableEditMode() { if (this.#disableEditing || this.canvas === null) { @@ -311,14 +332,17 @@ class InkEditor extends AnnotationEditor { } #getInitialBBox() { - const { width, height, rotation } = this.parent.viewport; - switch (rotation) { + const { + parentRotation, + parentDimensions: [width, height], + } = this; + switch (parentRotation) { case 90: - return [0, width, width, height]; + return [0, height, height, width]; case 180: return [width, height, width, height]; case 270: - return [height, 0, width, height]; + return [width, 0, height, width]; default: return [0, 0, width, height]; } @@ -328,12 +352,12 @@ class InkEditor extends AnnotationEditor { * Set line styles. */ #setStroke() { - this.ctx.lineWidth = - (this.thickness * this.parent.scaleFactor) / this.scaleFactor; - this.ctx.lineCap = "round"; - this.ctx.lineJoin = "round"; - this.ctx.miterLimit = 10; - this.ctx.strokeStyle = `${this.color}${opacityToHex(this.opacity)}`; + const { ctx, color, opacity, thickness, parentScale, scaleFactor } = this; + ctx.lineWidth = (thickness * parentScale) / scaleFactor; + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + ctx.miterLimit = 10; + ctx.strokeStyle = `${color}${opacityToHex(opacity)}`; } /** @@ -445,7 +469,7 @@ class InkEditor extends AnnotationEditor { } }; - this.parent.addCommands({ cmd, undo, mustExec: true }); + this.addCommands({ cmd, undo, mustExec: true }); } /** @@ -493,9 +517,11 @@ class InkEditor extends AnnotationEditor { // When commiting, the position of this editor is changed, hence we must // move it to the right position in the DOM. this.parent.moveEditorInDOM(this); - // After the div has been moved in the DOM, the focus may have been stolen - // by document.body, hence we just keep it here. - this.div.focus(); + if (this.pageIndex === this._uiManager.currentPageIndex) { + // After the div has been moved in the DOM, the focus may have been stolen + // by document.body, hence we just keep it here. + this.div.focus(); + } } /** @inheritdoc */ @@ -581,7 +607,7 @@ class InkEditor extends AnnotationEditor { this.#boundCanvasPointermove ); - this.parent.addToAnnotationStorage(this); + this.addToAnnotationStorage(); } /** @@ -649,7 +675,7 @@ class InkEditor extends AnnotationEditor { if (this.width) { // This editor was created in using copy (ctrl+c). - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.setAt( baseX * parentWidth, baseY * parentHeight, @@ -676,7 +702,7 @@ class InkEditor extends AnnotationEditor { if (!this.#isCanvasInitialized) { return; } - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.canvas.width = Math.ceil(this.width * parentWidth); this.canvas.height = Math.ceil(this.height * parentHeight); this.#updateTransform(); @@ -712,7 +738,7 @@ class InkEditor extends AnnotationEditor { this.setDims(width, height); } - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.width = width / parentWidth; this.height = height / parentHeight; @@ -940,7 +966,7 @@ class InkEditor extends AnnotationEditor { */ #getPadding() { return this.#disableEditing - ? Math.ceil(this.thickness * this.parent.scaleFactor) + ? Math.ceil(this.thickness * this.parentScale) : 0; } @@ -967,7 +993,7 @@ class InkEditor extends AnnotationEditor { const width = Math.ceil(padding + this.#baseWidth * this.scaleFactor); const height = Math.ceil(padding + this.#baseHeight * this.scaleFactor); - const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions; + const [parentWidth, parentHeight] = this.parentDimensions; this.width = width / parentWidth; this.height = height / parentHeight; @@ -1005,17 +1031,17 @@ class InkEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent) { - const editor = super.deserialize(data, parent); + static deserialize(data, parent, uiManager) { + const editor = super.deserialize(data, parent, uiManager); editor.thickness = data.thickness; editor.color = Util.makeHexColor(...data.color); editor.opacity = data.opacity; - const [pageWidth, pageHeight] = parent.pageDimensions; + const [pageWidth, pageHeight] = editor.pageDimensions; const width = editor.width * pageWidth; const height = editor.height * pageHeight; - const scaleFactor = parent.scaleFactor; + const scaleFactor = editor.parentScale; const padding = data.thickness / 2; editor.#aspectRatio = width / height; @@ -1058,10 +1084,6 @@ class InkEditor extends AnnotationEditor { /** @inheritdoc */ serialize() { - if (this._serialized !== undefined) { - return this._serialized; - } - if (this.isEmpty()) { return null; } @@ -1078,12 +1100,12 @@ class InkEditor extends AnnotationEditor { thickness: this.thickness, opacity: this.opacity, paths: this.#serializePaths( - this.scaleFactor / this.parent.scaleFactor, + this.scaleFactor / this.parentScale, this.translationX, this.translationY, height ), - pageIndex: this.parent.pageIndex, + pageIndex: this.pageIndex, rect, rotation: this.rotation, }; diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index e502934b7..80e1c4f9d 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -25,7 +25,7 @@ import { Util, warn, } from "../../shared/util.js"; -import { getColorValues, getRGB } from "../display_utils.js"; +import { getColorValues, getRGB, PixelsPerInch } from "../display_utils.js"; function bindEvents(obj, element, names) { for (const name of names) { @@ -350,12 +350,16 @@ class AnnotationEditorUIManager { #allLayers = new Map(); + #annotationStorage = null; + #commandManager = new CommandManager(); #currentPageIndex = 0; #editorTypes = null; + #editorsToRescale = new Set(); + #eventBus = null; #idManager = new IdManager(); @@ -378,6 +382,10 @@ class AnnotationEditorUIManager { #boundOnPageChanging = this.onPageChanging.bind(this); + #boundOnScaleChanging = this.onScaleChanging.bind(this); + + #boundOnRotationChanging = this.onRotationChanging.bind(this); + #previousStates = { isEditing: false, isEmpty: true, @@ -413,22 +421,32 @@ class AnnotationEditorUIManager { [["Escape", "mac+Escape"], AnnotationEditorUIManager.prototype.unselectAll], ]); - constructor(container, eventBus) { + constructor(container, eventBus, annotationStorage) { this.#container = container; this.#eventBus = eventBus; this.#eventBus._on("editingaction", this.#boundOnEditingAction); this.#eventBus._on("pagechanging", this.#boundOnPageChanging); + this.#eventBus._on("scalechanging", this.#boundOnScaleChanging); + this.#eventBus._on("rotationchanging", this.#boundOnRotationChanging); + this.#annotationStorage = annotationStorage; + this.viewParameters = { + realScale: PixelsPerInch.PDF_TO_CSS_UNITS, + rotation: 0, + }; } destroy() { this.#removeKeyboardManager(); this.#eventBus._off("editingaction", this.#boundOnEditingAction); this.#eventBus._off("pagechanging", this.#boundOnPageChanging); + this.#eventBus._off("scalechanging", this.#boundOnScaleChanging); + this.#eventBus._off("rotationchanging", this.#boundOnRotationChanging); for (const layer of this.#allLayers.values()) { layer.destroy(); } this.#allLayers.clear(); this.#allEditors.clear(); + this.#editorsToRescale.clear(); this.#activeEditor = null; this.#selectedEditors.clear(); this.#commandManager.destroy(); @@ -442,6 +460,41 @@ class AnnotationEditorUIManager { this.#container.focus(); } + addShouldRescale(editor) { + this.#editorsToRescale.add(editor); + } + + removeShouldRescale(editor) { + this.#editorsToRescale.delete(editor); + } + + onScaleChanging({ scale }) { + this.commitOrRemove(); + this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS; + for (const editor of this.#editorsToRescale) { + editor.onScaleChanging(); + } + } + + onRotationChanging({ pagesRotation }) { + this.commitOrRemove(); + this.viewParameters.rotation = pagesRotation; + } + + /** + * Add an editor in the annotation storage. + * @param {AnnotationEditor} editor + */ + addToAnnotationStorage(editor) { + if ( + !editor.isEmpty() && + this.#annotationStorage && + !this.#annotationStorage.has(editor.id) + ) { + this.#annotationStorage.setValue(editor.id, editor); + } + } + #addKeyboardManager() { // The keyboard events are caught at the container level in order to be able // to execute some callbacks even if the current page doesn't have focus. @@ -646,6 +699,14 @@ class AnnotationEditorUIManager { return this.#idManager.getId(); } + get currentLayer() { + return this.#allLayers.get(this.#currentPageIndex); + } + + get currentPageIndex() { + return this.#currentPageIndex; + } + /** * Add a new layer for a page which will contains the editors. * @param {AnnotationEditorLayer} layer @@ -783,6 +844,7 @@ class AnnotationEditorUIManager { removeEditor(editor) { this.#allEditors.delete(editor.id); this.unselect(editor); + this.#annotationStorage?.remove(editor.id); } /** diff --git a/test/integration/freetext_editor_spec.js b/test/integration/freetext_editor_spec.js index 37f904bd5..50443cfbb 100644 --- a/test/integration/freetext_editor_spec.js +++ b/test/integration/freetext_editor_spec.js @@ -599,6 +599,49 @@ describe("Editor", () => { [0, 0, 0], [0, 0, 0], ]); + + // Increase the font size for all the annotations. + // Select all. + await page.keyboard.down("Control"); + await page.keyboard.press("a"); + await page.keyboard.up("Control"); + await page.waitForTimeout(10); + + page.evaluate(() => { + window.PDFViewerApplication.eventBus.dispatch( + "switchannotationeditorparams", + { + source: null, + type: /* AnnotationEditorParamsType.FREETEXT_SIZE */ 1, + value: 13, + } + ); + }); + + await page.waitForTimeout(10); + expect(await serialize("fontSize")) + .withContext(`In ${browserName}`) + .toEqual([13, 13]); + + // Change the colors for all the annotations. + page.evaluate(() => { + window.PDFViewerApplication.eventBus.dispatch( + "switchannotationeditorparams", + { + source: null, + type: /* AnnotationEditorParamsType.FREETEXT_COLOR */ 2, + value: "#FF0000", + } + ); + }); + + await page.waitForTimeout(10); + expect(await serialize("color")) + .withContext(`In ${browserName}`) + .toEqual([ + [255, 0, 0], + [255, 0, 0], + ]); }) ); }); diff --git a/web/default_factory.js b/web/default_factory.js index 0688eee58..3d93b5474 100644 --- a/web/default_factory.js +++ b/web/default_factory.js @@ -111,9 +111,7 @@ class DefaultAnnotationEditorLayerFactory { * @property {HTMLDivElement} pageDiv * @property {PDFPageProxy} pdfPage * @property {IL10n} l10n - * @property {AnnotationStorage} [annotationStorage] - Storage for annotation * @property {TextAccessibilityManager} [accessibilityManager] - * data in forms. */ /** @@ -126,7 +124,6 @@ class DefaultAnnotationEditorLayerFactory { pdfPage, accessibilityManager = null, l10n, - annotationStorage = null, }) { return new AnnotationEditorLayerBuilder({ uiManager, @@ -134,7 +131,6 @@ class DefaultAnnotationEditorLayerFactory { pdfPage, accessibilityManager, l10n, - annotationStorage, }); } } diff --git a/web/interfaces.js b/web/interfaces.js index e558cb39b..b96c7fcdc 100644 --- a/web/interfaces.js +++ b/web/interfaces.js @@ -237,9 +237,7 @@ class IPDFAnnotationEditorLayerFactory { * @property {HTMLDivElement} pageDiv * @property {PDFPageProxy} pdfPage * @property {IL10n} l10n - * @property {AnnotationStorage} [annotationStorage] - Storage for annotation * @property {TextAccessibilityManager} [accessibilityManager] - * data in forms. */ /** @@ -251,7 +249,6 @@ class IPDFAnnotationEditorLayerFactory { pageDiv, pdfPage, l10n, - annotationStorage = null, accessibilityManager, }) {} } diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index d6ab1ae3f..142b5e0fe 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -730,7 +730,8 @@ class PDFViewer { } else if (isValidAnnotationEditorMode(mode)) { this.#annotationEditorUIManager = new AnnotationEditorUIManager( this.container, - this.eventBus + this.eventBus, + this.pdfDocument?.annotationStorage ); if (mode !== AnnotationEditorType.NONE) { this.#annotationEditorUIManager.updateMode(mode); @@ -1741,9 +1742,7 @@ class PDFViewer { * @property {HTMLDivElement} pageDiv * @property {PDFPageProxy} pdfPage * @property {IL10n} l10n - * @property {AnnotationStorage} [annotationStorage] - Storage for annotation * @property {TextAccessibilityManager} [accessibilityManager] - * data in forms. */ /** @@ -1756,13 +1755,11 @@ class PDFViewer { pdfPage, accessibilityManager = null, l10n, - annotationStorage = this.pdfDocument?.annotationStorage, }) { return new AnnotationEditorLayerBuilder({ uiManager, pageDiv, pdfPage, - annotationStorage, accessibilityManager, l10n, });