From 0382dd0e252ff4d8be5956114ea176a68fe10bba Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 26 Sep 2024 15:47:27 +0200 Subject: [PATCH] [Editor] When deleting an annotation with popup, then delete the popup too --- src/core/document.js | 6 +++ src/display/editor/editor.js | 21 +++++++--- src/display/editor/freetext.js | 16 +++----- src/display/editor/highlight.js | 17 ++++---- test/integration/freetext_editor_spec.mjs | 1 + test/integration/highlight_editor_spec.mjs | 44 +++++++++++++++++++++ test/pdfs/.gitignore | 1 + test/pdfs/highlight_popup.pdf | Bin 0 -> 12160 bytes test/unit/api_spec.js | 23 +++++++++++ 9 files changed, 104 insertions(+), 25 deletions(-) create mode 100755 test/pdfs/highlight_popup.pdf diff --git a/src/core/document.js b/src/core/document.js index edfc1cc629751..982033dcbc4b9 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -284,6 +284,12 @@ class Page { } if (annotation.deleted) { deletedAnnotations.put(ref, ref); + if (annotation.popupRef) { + const popupRef = Ref.fromString(annotation.popupRef); + if (popupRef) { + deletedAnnotations.put(popupRef, popupRef); + } + } continue; } existingAnnotations?.put(ref); diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index e6cd333f96107..54036b1d68e13 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -80,6 +80,8 @@ class AnnotationEditor { _initialOptions = Object.create(null); + _initialData = null; + _isVisible = true; _uiManager = null; @@ -1335,6 +1337,19 @@ class AnnotationEditor { */ rotate(_angle) {} + /** + * Serialize the editor when it has been deleted. + * @returns {Object} + */ + serializeDeleted() { + return { + id: this.annotationElementId, + deleted: true, + pageIndex: this.pageIndex, + popupRef: this._initialData?.popupRef || "", + }; + } + /** * Serialize the editor. * The result of the serialization will be used to construct a @@ -1809,11 +1824,7 @@ class FakeEditor extends AnnotationEditor { } serialize() { - return { - id: this.annotationElementId, - deleted: true, - pageIndex: this.pageIndex, - }; + return this.serializeDeleted(); } } diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 19f63375858b2..c3d4c958e7619 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -48,8 +48,6 @@ class FreeTextEditor extends AnnotationEditor { #fontSize; - #initialData = null; - static _freeTextDefaultContent = ""; static _internalPadding = 0; @@ -598,7 +596,7 @@ class FreeTextEditor extends AnnotationEditor { // position is the position of the first glyph in the annotation // and it's relative to its container. - const { position } = this.#initialData; + const { position } = this._initialData; let [tx, ty] = this.getInitialTranslation(); [tx, ty] = this.pageTranslationToScreen(tx, ty); const [pageWidth, pageHeight] = this.pageDimensions; @@ -781,6 +779,7 @@ class FreeTextEditor extends AnnotationEditor { rect, rotation, id, + popupRef, }, textContent, textPosition, @@ -805,6 +804,7 @@ class FreeTextEditor extends AnnotationEditor { rotation, id, deleted: false, + popupRef, }; } const editor = super.deserialize(data, parent, uiManager); @@ -812,7 +812,7 @@ class FreeTextEditor extends AnnotationEditor { editor.#color = Util.makeHexColor(...data.color); editor.#content = FreeTextEditor.#deserializeContent(data.value); editor.annotationElementId = data.id || null; - editor.#initialData = initialData; + editor._initialData = initialData; return editor; } @@ -824,11 +824,7 @@ class FreeTextEditor extends AnnotationEditor { } if (this.deleted) { - return { - pageIndex: this.pageIndex, - id: this.annotationElementId, - deleted: true, - }; + return this.serializeDeleted(); } const padding = FreeTextEditor._internalPadding * this.parentScale; @@ -866,7 +862,7 @@ class FreeTextEditor extends AnnotationEditor { } #hasElementChanged(serialized) { - const { value, fontSize, color, pageIndex } = this.#initialData; + const { value, fontSize, color, pageIndex } = this._initialData; return ( this._hasBeenMoved || diff --git a/src/display/editor/highlight.js b/src/display/editor/highlight.js index 65fc7445622d6..2e2d9cbf7f423 100644 --- a/src/display/editor/highlight.js +++ b/src/display/editor/highlight.js @@ -55,8 +55,6 @@ class HighlightEditor extends AnnotationEditor { #id = null; - #initialData = null; - #isFreeHighlight = false; #lastPoint = null; @@ -785,7 +783,7 @@ class HighlightEditor extends AnnotationEditor { let initialData = null; if (data instanceof HighlightAnnotationElement) { const { - data: { quadPoints, rect, rotation, id, color, opacity }, + data: { quadPoints, rect, rotation, id, color, opacity, popupRef }, parent: { page: { pageNumber }, }, @@ -801,6 +799,7 @@ class HighlightEditor extends AnnotationEditor { rotation, id, deleted: false, + popupRef, }; } else if (data instanceof InkAnnotationElement) { const { @@ -811,6 +810,7 @@ class HighlightEditor extends AnnotationEditor { id, color, borderStyle: { rawWidth: thickness }, + popupRef, }, parent: { page: { pageNumber }, @@ -827,6 +827,7 @@ class HighlightEditor extends AnnotationEditor { rotation, id, deleted: false, + popupRef, }; } @@ -839,7 +840,7 @@ class HighlightEditor extends AnnotationEditor { editor.#thickness = data.thickness; } editor.annotationElementId = data.id || null; - editor.#initialData = initialData; + editor._initialData = initialData; const [pageWidth, pageHeight] = editor.pageDimensions; const [pageX, pageY] = editor.pageTranslation; @@ -902,11 +903,7 @@ class HighlightEditor extends AnnotationEditor { } if (this.deleted) { - return { - pageIndex: this.pageIndex, - id: this.annotationElementId, - deleted: true, - }; + return this.serializeDeleted(); } const rect = this.getRect(0, 0); @@ -934,7 +931,7 @@ class HighlightEditor extends AnnotationEditor { } #hasElementChanged(serialized) { - const { color } = this.#initialData; + const { color } = this._initialData; return serialized.color.some((c, i) => c !== color[i]); } diff --git a/test/integration/freetext_editor_spec.mjs b/test/integration/freetext_editor_spec.mjs index fcf7839e6a368..29e89f28ae32e 100644 --- a/test/integration/freetext_editor_spec.mjs +++ b/test/integration/freetext_editor_spec.mjs @@ -1237,6 +1237,7 @@ describe("FreeText Editor", () => { pageIndex: 0, id: "51R", deleted: true, + popupRef: "", }, ]); diff --git a/test/integration/highlight_editor_spec.mjs b/test/integration/highlight_editor_spec.mjs index 6a7c820e2aed7..931ff440a4439 100644 --- a/test/integration/highlight_editor_spec.mjs +++ b/test/integration/highlight_editor_spec.mjs @@ -1972,6 +1972,50 @@ describe("Highlight Editor", () => { }); }); + describe("Highlight (delete an existing annotation)", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "highlight_popup.pdf", + ".annotationEditorLayer" + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must delete an existing annotation and its popup", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const modeChangedHandle = await waitForAnnotationModeChanged(page); + await waitAndClick(page, "[data-annotation-id='24R']", { count: 2 }); + await awaitPromise(modeChangedHandle); + await page.waitForSelector("#highlightParamsToolbarContainer"); + + const editorSelector = getEditorSelector(0); + await page.waitForSelector(editorSelector); + await page.waitForSelector(`${editorSelector} button.delete`); + await page.click(`${editorSelector} button.delete`); + await waitForSerialized(page, 1); + + const serialized = await getSerialized(page); + expect(serialized) + .withContext(`In ${browserName}`) + .toEqual([ + { + pageIndex: 0, + id: "24R", + deleted: true, + popupRef: "25R", + }, + ]); + }) + ); + }); + }); + describe("Free Highlight (edit existing in double clicking on it)", () => { let pages; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 1ef97d165a638..187a90b761482 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -670,3 +670,4 @@ !bug1918115.pdf !bug1919513.pdf !issue16038.pdf +!highlight_popup.pdf diff --git a/test/pdfs/highlight_popup.pdf b/test/pdfs/highlight_popup.pdf new file mode 100755 index 0000000000000000000000000000000000000000..c3b1005b9f877e7e4f21b4f927cb2fd76624098b GIT binary patch literal 12160 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 w^wZs