Merge pull request #15782 from calixteman/15780
[api-minor][Editor] Don't use the editor parent which can be null.
This commit is contained in:
commit
fe3df4dcb4
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
if (this.pageIndex === this._uiManager.currentPageIndex) {
|
||||
// On Chrome, the focus is given to <body> 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,
|
||||
};
|
||||
|
@ -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,10 +517,12 @@ 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);
|
||||
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 */
|
||||
focusin(event) {
|
||||
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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],
|
||||
]);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}) {}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user