Skip to content

Commit

Permalink
[Editor] Add a new base class to allow to add a drawing in the SVG la…
Browse files Browse the repository at this point in the history
…yer.

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.
  • Loading branch information
calixteman committed Nov 22, 2024
1 parent 1f6cc85 commit 5ccc04e
Show file tree
Hide file tree
Showing 16 changed files with 2,272 additions and 1,404 deletions.
54 changes: 29 additions & 25 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4466,7 +4466,7 @@ class InkAnnotation extends MarkupAnnotation {
ink.set("Subtype", Name.get("Ink"));
ink.set("CreationDate", `D:${getModificationDate()}`);
ink.set("Rect", rect);
ink.set("InkList", outlines?.points || paths.map(p => p.points));
ink.set("InkList", outlines?.points || paths.points);
ink.set("F", 4);
ink.set("Rotate", rotation);

Expand Down Expand Up @@ -4523,28 +4523,31 @@ class InkAnnotation extends MarkupAnnotation {
appearanceBuffer.push("/R0 gs");
}

const buffer = [];
for (const { bezier } of paths) {
buffer.length = 0;
buffer.push(
`${numberToString(bezier[0])} ${numberToString(bezier[1])} m`
);
if (bezier.length === 2) {
buffer.push(
`${numberToString(bezier[0])} ${numberToString(bezier[1])} l S`
);
} else {
for (let i = 2, ii = bezier.length; i < ii; i += 6) {
const curve = bezier
.slice(i, i + 6)
.map(numberToString)
.join(" ");
buffer.push(`${curve} c`);
for (const outline of paths.lines) {
for (let i = 0, ii = outline.length; i < ii; i += 6) {
if (isNaN(outline[i]) || outline[i] === null) {
appearanceBuffer.push(
`${numberToString(outline[i + 4])} ${numberToString(
outline[i + 5]
)} m`
);
} else {
const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6);
appearanceBuffer.push(
`${numberToString(c1x)} ${numberToString(c1y)} ` +
`${numberToString(c2x)} ${numberToString(c2y)} ` +
`${numberToString(x)} ${numberToString(y)} c`
);
}
buffer.push("S");
}
appearanceBuffer.push(buffer.join("\n"));
if (outline.length === 6) {
appearanceBuffer.push(
`${numberToString(outline[4])} ${numberToString(outline[5])} l`
);
}
}
appearanceBuffer.push("S");

const appearance = appearanceBuffer.join("\n");

const appearanceStreamDict = new Dict(xref);
Expand Down Expand Up @@ -4594,11 +4597,12 @@ class InkAnnotation extends MarkupAnnotation {
)} l`
);
} else {
const curve = outline
.slice(i, i + 6)
.map(numberToString)
.join(" ");
appearanceBuffer.push(`${curve} c`);
const [c1x, c1y, c2x, c2y, x, y] = outline.slice(i, i + 6);
appearanceBuffer.push(
`${numberToString(c1x)} ${numberToString(c1y)} ` +
`${numberToString(c2x)} ${numberToString(c2y)} ` +
`${numberToString(x)} ${numberToString(y)} c`
);
}
}
appearanceBuffer.push("h f");
Expand Down
16 changes: 15 additions & 1 deletion src/display/draw_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ class DrawLayer {
this.updateProperties(id, properties);
}

updateProperties(elementOrId, { root, bbox, rootClass, path }) {
updateProperties(elementOrId, properties) {
if (!properties) {
return;
}
const { root, bbox, rootClass, path } = properties;
const element =
typeof elementOrId === "number"
? this.#mapping.get(elementOrId)
Expand All @@ -207,6 +211,16 @@ class DrawLayer {
}
}

updateParent(id, layer) {
if (layer === this) {
return;
}
const root = this.#mapping.get(id);
layer.#parent.append(root);
this.#mapping.delete(id);
layer.#mapping.set(id, root);
}

remove(id) {
this.#toUpdate.delete(id);
if (this.#parent === null) {
Expand Down
103 changes: 66 additions & 37 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ class AnnotationEditorLayer {

#hadPointerDown = false;

#isCleaningUp = false;

#isDisabling = false;

#isDrawing = false;

#drawingAC = null;

#textLayer = null;

#textSelectionAC = null;
Expand Down Expand Up @@ -160,12 +162,9 @@ class AnnotationEditorLayer {
this.disableClick();
return;
case AnnotationEditorType.INK:
// We always want to have an ink editor ready to draw in.
this.addInkEditorIfNeeded(false);

this.disableTextSelection();
this.togglePointerEvents(true);
this.disableClick();
this.enableClick();
break;
case AnnotationEditorType.HIGHLIGHT:
this.enableTextSelection();
Expand Down Expand Up @@ -193,30 +192,6 @@ class AnnotationEditorLayer {
return textLayer === this.#textLayer?.div;
}

addInkEditorIfNeeded(isCommitting) {
if (this.#uiManager.getMode() !== AnnotationEditorType.INK) {
// We don't want to add an ink editor if we're not in ink mode!
return;
}

if (!isCommitting) {
// We're removing an editor but an empty one can already exist so in this
// case we don't need to create a new one.
for (const editor of this.#editors.values()) {
if (editor.isEmpty()) {
editor.setInBackground();
return;
}
}
}

const editor = this.createAndAddNewEditor(
{ offsetX: 0, offsetY: 0 },
/* isCentered = */ false
);
editor.setInBackground();
}

/**
* Set the editing state.
* @param {boolean} isEditing
Expand All @@ -233,6 +208,10 @@ class AnnotationEditorLayer {
this.#uiManager.addCommands(params);
}

cleanUndoStack(type) {
this.#uiManager.cleanUndoStack(type);
}

toggleDrawing(enabled = false) {
this.div.classList.toggle("drawing", !enabled);
}
Expand Down Expand Up @@ -482,10 +461,6 @@ class AnnotationEditorLayer {
this.#uiManager.removeEditor(editor);
editor.div.remove();
editor.isAttachedToDOM = false;

if (!this.#isCleaningUp) {
this.addInkEditorIfNeeded(/* isCommitting = */ false);
}
}

/**
Expand Down Expand Up @@ -766,6 +741,13 @@ class AnnotationEditorLayer {
}
this.#hadPointerDown = false;

if (
this.#currentEditorType?.isDrawer &&
this.#currentEditorType.supportMultipleDrawings
) {
return;
}

if (!this.#allowClick) {
this.#allowClick = true;
return;
Expand Down Expand Up @@ -808,10 +790,51 @@ class AnnotationEditorLayer {

this.#hadPointerDown = true;

if (this.#currentEditorType?.isDrawer) {
this.setDrawingSession(event);
return;
}

const editor = this.#uiManager.getActive();
this.#allowClick = !editor || editor.isEmpty();
}

setDrawingSession(event) {
if (this.#isDrawing) {
this.#currentEditorType.startDrawing(this, false, event);
return;
}

this.#isDrawing = true;
this.#drawingAC = new AbortController();
const signal = this.#uiManager.combinedSignal(this.#drawingAC);
this.div.focus();
this.div.addEventListener(
"blur",
({ relatedTarget }) => {
if (relatedTarget && !this.div.contains(relatedTarget)) {
this.commitOrRemove();
}
},
{
signal,
}
);
this.#uiManager.disableUserSelect(true);
this.#currentEditorType.startDrawing(this, false, event);
}

endDrawingSession() {
if (!this.#isDrawing) {
return;
}
this.#drawingAC.abort();
this.#drawingAC = null;
this.#isDrawing = false;
this.#uiManager.disableUserSelect(false);
this.#currentEditorType.endDrawing(this);
}

/**
*
* @param {AnnotationEditor} editor
Expand All @@ -828,6 +851,14 @@ class AnnotationEditorLayer {
return true;
}

commitOrRemove() {
if (this.#isDrawing) {
this.endDrawingSession();
return true;
}
return false;
}

/**
* Destroy the main editor.
*/
Expand Down Expand Up @@ -858,13 +889,11 @@ class AnnotationEditorLayer {
// When we're cleaning up, some editors are removed but we don't want
// to add a new one which will induce an addition in this.#editors, hence
// an infinite loop.
this.#isCleaningUp = true;
for (const editor of this.#editors.values()) {
if (editor.isEmpty()) {
editor.remove();
}
}
this.#isCleaningUp = false;
}

/**
Expand Down Expand Up @@ -896,14 +925,14 @@ class AnnotationEditorLayer {

const oldRotation = this.viewport.rotation;
const rotation = viewport.rotation;

this.viewport = viewport;
setLayerDimensions(this.div, { rotation });
if (oldRotation !== rotation) {
for (const editor of this.#editors.values()) {
editor.rotate(rotation);
}
}
this.addInkEditorIfNeeded(/* isCommitting = */ false);
}

/**
Expand Down
Loading

0 comments on commit 5ccc04e

Please sign in to comment.