This patch makes a clear separation between the way to draw and the editing stuff. It adds a class DrawEditor which should be extended in order to create new drawing tools. As an example, the ink tool has been rewritten in order to use it.
853 lines
21 KiB
JavaScript
853 lines
21 KiB
JavaScript
/* Copyright 2022 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { AnnotationEditorParamsType, unreachable } from "../../shared/util.js";
|
|
import { noContextMenu, stopEvent } from "../display_utils.js";
|
|
import { AnnotationEditor } from "./editor.js";
|
|
|
|
class DrawingOptions {
|
|
#svgProperties = Object.create(null);
|
|
|
|
updateProperty(name, value) {
|
|
this[name] = value;
|
|
this.updateSVGProperty(name, value);
|
|
}
|
|
|
|
updateProperties(properties) {
|
|
if (!properties) {
|
|
return;
|
|
}
|
|
for (const [name, value] of Object.entries(properties)) {
|
|
this.updateProperty(name, value);
|
|
}
|
|
}
|
|
|
|
updateSVGProperty(name, value) {
|
|
this.#svgProperties[name] = value;
|
|
}
|
|
|
|
toSVGProperties() {
|
|
const root = this.#svgProperties;
|
|
this.#svgProperties = Object.create(null);
|
|
return { root };
|
|
}
|
|
|
|
reset() {
|
|
this.#svgProperties = Object.create(null);
|
|
}
|
|
|
|
updateAll(options = this) {
|
|
this.updateProperties(options);
|
|
}
|
|
|
|
clone() {
|
|
unreachable("Not implemented");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Basic draw editor.
|
|
*/
|
|
class DrawingEditor extends AnnotationEditor {
|
|
#drawOutlines = null;
|
|
|
|
#mustBeCommitted;
|
|
|
|
_drawId = null;
|
|
|
|
static _currentDrawId = -1;
|
|
|
|
static _currentDraw = null;
|
|
|
|
static _currentDrawingOptions = null;
|
|
|
|
static _currentParent = null;
|
|
|
|
static _INNER_MARGIN = 3;
|
|
|
|
constructor(params) {
|
|
super(params);
|
|
this.#mustBeCommitted = params.mustBeCommitted || false;
|
|
|
|
if (params.drawOutlines) {
|
|
this.#createDrawOutlines(params);
|
|
this.#addToDrawLayer();
|
|
}
|
|
}
|
|
|
|
#createDrawOutlines({ drawOutlines, drawId, drawingOptions }) {
|
|
this.#drawOutlines = drawOutlines;
|
|
this._drawingOptions ||= drawingOptions;
|
|
|
|
if (drawId >= 0) {
|
|
this._drawId = drawId;
|
|
// We need to redraw the drawing because we changed the coordinates to be
|
|
// in the box coordinate system.
|
|
this.parent.drawLayer.finalizeDraw(
|
|
drawId,
|
|
drawOutlines.defaultProperties
|
|
);
|
|
} else {
|
|
// We create a new drawing.
|
|
this._drawId = this.#createDrawing(drawOutlines, this.parent);
|
|
}
|
|
|
|
this.#updateBbox(drawOutlines.box);
|
|
}
|
|
|
|
#createDrawing(drawOutlines, parent) {
|
|
const { id } = parent.drawLayer.draw(
|
|
DrawingEditor._mergeSVGProperties(
|
|
this._drawingOptions.toSVGProperties(),
|
|
drawOutlines.defaultSVGProperties
|
|
),
|
|
/* isPathUpdatable = */ false,
|
|
/* hasClip = */ false
|
|
);
|
|
return id;
|
|
}
|
|
|
|
static _mergeSVGProperties(p1, p2) {
|
|
const p1Keys = new Set(Object.keys(p1));
|
|
|
|
for (const [key, value] of Object.entries(p2)) {
|
|
if (p1Keys.has(key)) {
|
|
Object.assign(p1[key], value);
|
|
} else {
|
|
p1[key] = value;
|
|
}
|
|
}
|
|
return p1;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} options
|
|
* @return {DrawingOptions} the default options to use for a new editor.
|
|
*/
|
|
static getDefaultDrawingOptions(_options) {
|
|
unreachable("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* @return {Map<AnnotationEditorParamsType, string>} a map between the
|
|
* parameter types and the name of the options.
|
|
*/
|
|
// eslint-disable-next-line getter-return
|
|
static get typesMap() {
|
|
unreachable("Not implemented");
|
|
}
|
|
|
|
static get isDrawer() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @returns {boolean} `true` if several drawings can be added to the
|
|
* annotation.
|
|
*/
|
|
static get supportMultipleDrawings() {
|
|
return false;
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
static updateDefaultParams(type, value) {
|
|
const propertyName = this.typesMap.get(type);
|
|
if (propertyName) {
|
|
this._defaultDrawingOptions.updateProperty(propertyName, value);
|
|
}
|
|
if (this._currentParent) {
|
|
this._currentDraw.updateProperty(propertyName, value);
|
|
this._currentParent.drawLayer.updateProperties(
|
|
this._currentDrawId,
|
|
this._defaultDrawingOptions.toSVGProperties()
|
|
);
|
|
}
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
updateParams(type, value) {
|
|
const propertyName = this.constructor.typesMap.get(type);
|
|
if (propertyName) {
|
|
this._updateProperty(type, propertyName, value);
|
|
}
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
static get defaultPropertiesToUpdate() {
|
|
const properties = [];
|
|
const options = this._defaultDrawingOptions;
|
|
for (const [type, name] of this.typesMap) {
|
|
properties.push([type, options[name]]);
|
|
}
|
|
return properties;
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
get propertiesToUpdate() {
|
|
const properties = [];
|
|
const { _drawingOptions } = this;
|
|
for (const [type, name] of this.constructor.typesMap) {
|
|
properties.push([type, _drawingOptions[name]]);
|
|
}
|
|
return properties;
|
|
}
|
|
|
|
/**
|
|
* Update a property and make this action undoable.
|
|
* @param {string} color
|
|
*/
|
|
_updateProperty(type, name, value) {
|
|
const options = this._drawingOptions;
|
|
const savedValue = options[name];
|
|
const setter = val => {
|
|
options.updateProperty(name, val);
|
|
const bbox = this.#drawOutlines.updateProperty(name, val);
|
|
if (bbox) {
|
|
this.#updateBbox(bbox);
|
|
}
|
|
this.parent?.drawLayer.updateProperties(
|
|
this._drawId,
|
|
options.toSVGProperties()
|
|
);
|
|
};
|
|
this.addCommands({
|
|
cmd: setter.bind(this, value),
|
|
undo: setter.bind(this, savedValue),
|
|
post: this._uiManager.updateUI.bind(this._uiManager, this),
|
|
mustExec: true,
|
|
type,
|
|
overwriteIfSameType: true,
|
|
keepUndo: true,
|
|
});
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
_onResizing() {
|
|
this.parent?.drawLayer.updateProperties(
|
|
this._drawId,
|
|
DrawingEditor._mergeSVGProperties(
|
|
this.#drawOutlines.getPathResizingSVGProperties(
|
|
this.#convertToDrawSpace()
|
|
),
|
|
{
|
|
bbox: this.#rotateBox(),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
_onResized() {
|
|
this.parent?.drawLayer.updateProperties(
|
|
this._drawId,
|
|
DrawingEditor._mergeSVGProperties(
|
|
this.#drawOutlines.getPathResizedSVGProperties(
|
|
this.#convertToDrawSpace()
|
|
),
|
|
{
|
|
bbox: this.#rotateBox(),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
_onTranslating(x, y) {
|
|
this.parent?.drawLayer.updateProperties(this._drawId, {
|
|
bbox: this.#rotateBox(x, y),
|
|
});
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
_onTranslated() {
|
|
this.parent?.drawLayer.updateProperties(
|
|
this._drawId,
|
|
DrawingEditor._mergeSVGProperties(
|
|
this.#drawOutlines.getPathTranslatedSVGProperties(
|
|
this.#convertToDrawSpace(),
|
|
this.parentDimensions
|
|
),
|
|
{
|
|
bbox: this.#rotateBox(),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
_onStartDragging() {
|
|
this.parent?.drawLayer.updateProperties(this._drawId, {
|
|
rootClass: {
|
|
moving: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
_onStopDragging() {
|
|
this.parent?.drawLayer.updateProperties(this._drawId, {
|
|
rootClass: {
|
|
moving: false,
|
|
},
|
|
});
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
commit() {
|
|
super.commit();
|
|
|
|
this.disableEditMode();
|
|
this.disableEditing();
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
disableEditing() {
|
|
super.disableEditing();
|
|
this.div.classList.toggle("disabled", true);
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
enableEditing() {
|
|
super.enableEditing();
|
|
this.div.classList.toggle("disabled", false);
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
getBaseTranslation() {
|
|
// The editor itself doesn't have any CSS border (we're drawing one
|
|
// ourselves in using SVG).
|
|
return [0, 0];
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
get isResizable() {
|
|
return true;
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
onceAdded() {
|
|
if (!this.annotationElementId) {
|
|
this.parent.addUndoableEditor(this);
|
|
}
|
|
this._isDraggable = true;
|
|
if (this.#mustBeCommitted) {
|
|
this.#mustBeCommitted = false;
|
|
this.commit();
|
|
this.parent.setSelected(this);
|
|
this.div.focus();
|
|
}
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
remove() {
|
|
this.#cleanDrawLayer();
|
|
super.remove();
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
rebuild() {
|
|
if (!this.parent) {
|
|
return;
|
|
}
|
|
super.rebuild();
|
|
if (this.div === null) {
|
|
return;
|
|
}
|
|
|
|
this.#addToDrawLayer();
|
|
this.#updateBbox(this.#drawOutlines.box);
|
|
|
|
if (!this.isAttachedToDOM) {
|
|
// At some point this editor was removed and we're rebuilding it,
|
|
// hence we must add it to its parent.
|
|
this.parent.add(this);
|
|
}
|
|
}
|
|
|
|
setParent(parent) {
|
|
let mustBeSelected = false;
|
|
if (this.parent && !parent) {
|
|
this._uiManager.removeShouldRescale(this);
|
|
this.#cleanDrawLayer();
|
|
} else if (parent) {
|
|
this._uiManager.addShouldRescale(this);
|
|
this.#addToDrawLayer(parent);
|
|
// If mustBeSelected is true it means that this editor was selected
|
|
// when its parent has been destroyed, hence we must select it again.
|
|
mustBeSelected =
|
|
!this.parent && this.div?.classList.contains("selectedEditor");
|
|
}
|
|
super.setParent(parent);
|
|
if (mustBeSelected) {
|
|
// We select it after the parent has been set.
|
|
this.select();
|
|
}
|
|
}
|
|
|
|
#cleanDrawLayer() {
|
|
if (this._drawId === null || !this.parent) {
|
|
return;
|
|
}
|
|
this.parent.drawLayer.remove(this._drawId);
|
|
this._drawId = null;
|
|
|
|
// All the SVG properties must be reset in order to make it possible to
|
|
// undo.
|
|
this._drawingOptions.reset();
|
|
}
|
|
|
|
#addToDrawLayer(parent = this.parent) {
|
|
if (this._drawId !== null && this.parent === parent) {
|
|
return;
|
|
}
|
|
if (this._drawId !== null) {
|
|
// The parent has changed, we need to move the drawing to the new parent.
|
|
this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer);
|
|
return;
|
|
}
|
|
this._drawingOptions.updateAll();
|
|
this._drawId = this.#createDrawing(this.#drawOutlines, parent);
|
|
}
|
|
|
|
#convertToParentSpace([x, y, width, height]) {
|
|
const {
|
|
parentDimensions: [pW, pH],
|
|
rotation,
|
|
} = this;
|
|
switch (rotation) {
|
|
case 90:
|
|
return [y, 1 - x, width * (pH / pW), height * (pW / pH)];
|
|
case 180:
|
|
return [1 - x, 1 - y, width, height];
|
|
case 270:
|
|
return [1 - y, x, width * (pH / pW), height * (pW / pH)];
|
|
default:
|
|
return [x, y, width, height];
|
|
}
|
|
}
|
|
|
|
#convertToDrawSpace() {
|
|
const {
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
parentDimensions: [pW, pH],
|
|
rotation,
|
|
} = this;
|
|
switch (rotation) {
|
|
case 90:
|
|
return [1 - y, x, width * (pW / pH), height * (pH / pW)];
|
|
case 180:
|
|
return [1 - x, 1 - y, width, height];
|
|
case 270:
|
|
return [y, 1 - x, width * (pW / pH), height * (pH / pW)];
|
|
default:
|
|
return [x, y, width, height];
|
|
}
|
|
}
|
|
|
|
#updateBbox(bbox) {
|
|
[this.x, this.y, this.width, this.height] =
|
|
this.#convertToParentSpace(bbox);
|
|
if (this.div) {
|
|
this.fixAndSetPosition();
|
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
|
this.setDims(this.width * parentWidth, this.height * parentHeight);
|
|
}
|
|
this._onResized();
|
|
}
|
|
|
|
#rotateBox() {
|
|
// We've to deal with two rotations: the rotation of the annotation and the
|
|
// rotation of the parent page.
|
|
// When the page is rotated, all the layers are just rotated thanks to CSS
|
|
// but there is a notable exception: the canvas wrapper.
|
|
// The canvas wrapper is not rotated but the dimensions are (or not) swapped
|
|
// and the page is redrawn with the rotation applied to the canvas.
|
|
// The drawn layer is under the canvas wrapper and is not rotated so we have
|
|
// to "manually" rotate the coordinates.
|
|
//
|
|
// The coordinates (this.x, this.y) correspond to the top-left corner of
|
|
// the editor after it has been rotated in the page coordinate system.
|
|
|
|
const {
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
rotation,
|
|
parentRotation,
|
|
parentDimensions: [pW, pH],
|
|
} = this;
|
|
switch ((rotation * 4 + parentRotation) / 90) {
|
|
case 1:
|
|
// 0 -> 90
|
|
return [1 - y - height, x, height, width];
|
|
case 2:
|
|
// 0 -> 180
|
|
return [1 - x - width, 1 - y - height, width, height];
|
|
case 3:
|
|
// 0 -> 270
|
|
return [y, 1 - x - width, height, width];
|
|
case 4:
|
|
// 90 -> 0
|
|
return [
|
|
x,
|
|
y - width * (pW / pH),
|
|
height * (pH / pW),
|
|
width * (pW / pH),
|
|
];
|
|
case 5:
|
|
// 90 -> 90
|
|
return [1 - y, x, width * (pW / pH), height * (pH / pW)];
|
|
case 6:
|
|
// 90 -> 180
|
|
return [
|
|
1 - x - height * (pH / pW),
|
|
1 - y,
|
|
height * (pH / pW),
|
|
width * (pW / pH),
|
|
];
|
|
case 7:
|
|
// 90 -> 270
|
|
return [
|
|
y - width * (pW / pH),
|
|
1 - x - height * (pH / pW),
|
|
width * (pW / pH),
|
|
height * (pH / pW),
|
|
];
|
|
case 8:
|
|
// 180 -> 0
|
|
return [x - width, y - height, width, height];
|
|
case 9:
|
|
// 180 -> 90
|
|
return [1 - y, x - width, height, width];
|
|
case 10:
|
|
// 180 -> 180
|
|
return [1 - x, 1 - y, width, height];
|
|
case 11:
|
|
// 180 -> 270
|
|
return [y - height, 1 - x, height, width];
|
|
case 12:
|
|
// 270 -> 0
|
|
return [
|
|
x - height * (pH / pW),
|
|
y,
|
|
height * (pH / pW),
|
|
width * (pW / pH),
|
|
];
|
|
case 13:
|
|
// 270 -> 90
|
|
return [
|
|
1 - y - width * (pW / pH),
|
|
x - height * (pH / pW),
|
|
width * (pW / pH),
|
|
height * (pH / pW),
|
|
];
|
|
case 14:
|
|
// 270 -> 180
|
|
return [
|
|
1 - x,
|
|
1 - y - width * (pW / pH),
|
|
height * (pH / pW),
|
|
width * (pW / pH),
|
|
];
|
|
case 15:
|
|
// 270 -> 270
|
|
return [y, 1 - x, width * (pW / pH), height * (pH / pW)];
|
|
default:
|
|
// 0 -> 0
|
|
return [x, y, width, height];
|
|
}
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
rotate() {
|
|
if (!this.parent) {
|
|
return;
|
|
}
|
|
this.parent.drawLayer.updateProperties(
|
|
this._drawId,
|
|
DrawingEditor._mergeSVGProperties(
|
|
{
|
|
bbox: this.#rotateBox(),
|
|
},
|
|
this.#drawOutlines.updateRotation(
|
|
(this.parentRotation - this.rotation + 360) % 360
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
onScaleChanging() {
|
|
if (!this.parent) {
|
|
return;
|
|
}
|
|
this.#updateBbox(
|
|
this.#drawOutlines.updateParentDimensions(
|
|
this.parentDimensions,
|
|
this.parent.scale
|
|
)
|
|
);
|
|
}
|
|
|
|
static onScaleChangingWhenDrawing() {}
|
|
|
|
/** @inheritdoc */
|
|
render() {
|
|
if (this.div) {
|
|
return this.div;
|
|
}
|
|
|
|
const div = super.render();
|
|
div.classList.add("draw");
|
|
|
|
const drawDiv = document.createElement("div");
|
|
div.append(drawDiv);
|
|
drawDiv.setAttribute("aria-hidden", "true");
|
|
drawDiv.className = "internal";
|
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
|
this.setDims(this.width * parentWidth, this.height * parentHeight);
|
|
this._uiManager.addShouldRescale(this);
|
|
this.disableEditing();
|
|
|
|
return div;
|
|
}
|
|
|
|
/**
|
|
* Create a new drawer instance.
|
|
* @param {number} x - The x coordinate of the event.
|
|
* @param {number} y - The y coordinate of the event.
|
|
* @param {number} parentWidth - The parent width.
|
|
* @param {number} parentHeight - The parent height.
|
|
* @param {number} rotation - The parent rotation.
|
|
*/
|
|
static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) {
|
|
unreachable("Not implemented");
|
|
}
|
|
|
|
static startDrawing(
|
|
parent,
|
|
uiManager,
|
|
_isLTR,
|
|
{ target, offsetX: x, offsetY: y }
|
|
) {
|
|
const {
|
|
viewport: { rotation },
|
|
} = parent;
|
|
const { width: parentWidth, height: parentHeight } =
|
|
target.getBoundingClientRect();
|
|
const ac = new AbortController();
|
|
const signal = parent.combinedSignal(ac);
|
|
|
|
window.addEventListener(
|
|
"pointerup",
|
|
e => {
|
|
ac.abort();
|
|
parent.toggleDrawing(true);
|
|
this._endDraw(e);
|
|
},
|
|
{ signal }
|
|
);
|
|
window.addEventListener(
|
|
"pointerdown",
|
|
stopEvent /* Avoid to have undesired clicks during drawing. */,
|
|
{
|
|
capture: true,
|
|
passive: false,
|
|
signal,
|
|
}
|
|
);
|
|
window.addEventListener("contextmenu", noContextMenu, { signal });
|
|
target.addEventListener("pointermove", this._drawMove.bind(this), {
|
|
signal,
|
|
});
|
|
parent.toggleDrawing();
|
|
|
|
if (this._currentDraw) {
|
|
parent.drawLayer.updateProperties(
|
|
this._currentDrawId,
|
|
this._currentDraw.startNew(x, y, parentWidth, parentHeight, rotation)
|
|
);
|
|
return;
|
|
}
|
|
|
|
uiManager.updateUIForDefaultProperties(this);
|
|
|
|
this._currentDraw = this.createDrawerInstance(
|
|
x,
|
|
y,
|
|
parentWidth,
|
|
parentHeight,
|
|
rotation
|
|
);
|
|
this._currentDrawingOptions = this.getDefaultDrawingOptions();
|
|
this._currentParent = parent;
|
|
|
|
({ id: this._currentDrawId } = parent.drawLayer.draw(
|
|
this._mergeSVGProperties(
|
|
this._currentDrawingOptions.toSVGProperties(),
|
|
this._currentDraw.defaultSVGProperties
|
|
),
|
|
/* isPathUpdatable = */ true,
|
|
/* hasClip = */ false
|
|
));
|
|
}
|
|
|
|
static _drawMove({ offsetX, offsetY }) {
|
|
this._currentParent.drawLayer.updateProperties(
|
|
this._currentDrawId,
|
|
this._currentDraw.add(offsetX, offsetY)
|
|
);
|
|
}
|
|
|
|
static _endDraw({ offsetX, offsetY }) {
|
|
const parent = this._currentParent;
|
|
parent.drawLayer.updateProperties(
|
|
this._currentDrawId,
|
|
this._currentDraw.end(offsetX, offsetY)
|
|
);
|
|
if (this.supportMultipleDrawings) {
|
|
const draw = this._currentDraw;
|
|
const drawId = this._currentDrawId;
|
|
const lastElement = draw.getLastElement();
|
|
parent.addCommands({
|
|
cmd: () => {
|
|
parent.drawLayer.updateProperties(
|
|
drawId,
|
|
draw.setLastElement(lastElement)
|
|
);
|
|
},
|
|
undo: () => {
|
|
parent.drawLayer.updateProperties(drawId, draw.removeLastElement());
|
|
},
|
|
mustExec: false,
|
|
type: AnnotationEditorParamsType.DRAW_STEP,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
this.endDrawing();
|
|
}
|
|
|
|
static endDrawing() {
|
|
const parent = this._currentParent;
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
parent.toggleDrawing(true);
|
|
parent.cleanUndoStack(AnnotationEditorParamsType.DRAW_STEP);
|
|
|
|
if (!this._currentDraw.isEmpty()) {
|
|
const {
|
|
pageDimensions: [pageWidth, pageHeight],
|
|
scale,
|
|
} = parent;
|
|
|
|
parent.createAndAddNewEditor({ offsetX: 0, offsetY: 0 }, false, {
|
|
drawId: this._currentDrawId,
|
|
drawOutlines: this._currentDraw.getOutlines(
|
|
pageWidth * scale,
|
|
pageHeight * scale,
|
|
scale,
|
|
this._INNER_MARGIN
|
|
),
|
|
drawingOptions: this._currentDrawingOptions,
|
|
mustBeCommitted: true,
|
|
});
|
|
} else {
|
|
parent.drawLayer.remove(this._currentDrawId);
|
|
}
|
|
this._currentDrawId = -1;
|
|
this._currentDraw = null;
|
|
this._currentDrawingOptions = null;
|
|
this._currentParent = null;
|
|
}
|
|
|
|
/**
|
|
* Create the drawing options.
|
|
* @param {Object} _data
|
|
*/
|
|
createDrawingOptions(_data) {}
|
|
|
|
/**
|
|
* Deserialize the drawing outlines.
|
|
* @param {number} pageX - The x coordinate of the page.
|
|
* @param {number} pageY - The y coordinate of the page.
|
|
* @param {number} pageWidth - The width of the page.
|
|
* @param {number} pageHeight - The height of the page.
|
|
* @param {number} innerWidth - The inner width.
|
|
* @param {Object} data - The data to deserialize.
|
|
* @returns {Object} The deserialized outlines.
|
|
*/
|
|
static deserializeDraw(
|
|
_pageX,
|
|
_pageY,
|
|
_pageWidth,
|
|
_pageHeight,
|
|
_innerWidth,
|
|
_data
|
|
) {
|
|
unreachable("Not implemented");
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
static async deserialize(data, parent, uiManager) {
|
|
const {
|
|
rawDims: { pageWidth, pageHeight, pageX, pageY },
|
|
} = parent.viewport;
|
|
const drawOutlines = this.deserializeDraw(
|
|
pageX,
|
|
pageY,
|
|
pageWidth,
|
|
pageHeight,
|
|
this._INNER_MARGIN,
|
|
data
|
|
);
|
|
const editor = await super.deserialize(data, parent, uiManager);
|
|
editor.createDrawingOptions(data);
|
|
editor.#createDrawOutlines({ drawOutlines });
|
|
editor.#addToDrawLayer();
|
|
editor.onScaleChanging();
|
|
editor.rotate();
|
|
|
|
return editor;
|
|
}
|
|
|
|
serializeDraw(isForCopying) {
|
|
const [pageX, pageY] = this.pageTranslation;
|
|
const [pageWidth, pageHeight] = this.pageDimensions;
|
|
return this.#drawOutlines.serialize(
|
|
[pageX, pageY, pageWidth, pageHeight],
|
|
isForCopying
|
|
);
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
renderAnnotationElement(annotation) {
|
|
annotation.updateEdited({
|
|
rect: this.getRect(0, 0),
|
|
});
|
|
|
|
return null;
|
|
}
|
|
|
|
static canCreateNewEmptyEditor() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export { DrawingEditor, DrawingOptions };
|