diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index 741763c8a..4742fe0c4 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -359,9 +359,7 @@ class AnnotationEditorLayer { for (const editable of editables) { const { id } = editable.data; if (this.#uiManager.isDeletedAnnotationElement(id)) { - editable.updateEdited({ - deleted: true, - }); + editable.updateEdited({ deleted: true }); continue; } let editor = resetAnnotations.get(id); diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 80c56af23..a032bcd17 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -2155,6 +2155,10 @@ class AnnotationEditor { * @returns {HTMLElement|null} */ renderAnnotationElement(annotation) { + if (this.deleted) { + annotation.hide(); + return null; + } let content = annotation.container.querySelector(".annotationContent"); if (!content) { content = document.createElement("div"); diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index d25bb2e58..da947d61f 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -885,6 +885,9 @@ class FreeTextEditor extends AnnotationEditor { /** @inheritdoc */ renderAnnotationElement(annotation) { const content = super.renderAnnotationElement(annotation); + if (!content) { + return null; + } const { style } = content; style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; style.color = this.#color; diff --git a/src/display/editor/highlight.js b/src/display/editor/highlight.js index b8e7edb58..59aec20d1 100644 --- a/src/display/editor/highlight.js +++ b/src/display/editor/highlight.js @@ -1059,6 +1059,10 @@ class HighlightEditor extends AnnotationEditor { /** @inheritdoc */ renderAnnotationElement(annotation) { + if (this.deleted) { + annotation.hide(); + return null; + } const params = { rect: this.getRect(0, 0), }; diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index bd122c6f6..58afba998 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -295,6 +295,10 @@ class InkEditor extends DrawingEditor { /** @inheritdoc */ renderAnnotationElement(annotation) { + if (this.deleted) { + annotation.hide(); + return null; + } const { points, rect } = this.serializeDraw(/* isForCopying = */ false); const params = { rect, diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index ae4571d4f..bc59ae615 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -930,6 +930,10 @@ class StampEditor extends AnnotationEditor { /** @inheritdoc */ renderAnnotationElement(annotation) { + if (this.deleted) { + annotation.hide(); + return null; + } const params = { rect: this.getRect(0, 0), }; diff --git a/test/integration/freetext_editor_spec.mjs b/test/integration/freetext_editor_spec.mjs index df7301df1..545d7a759 100644 --- a/test/integration/freetext_editor_spec.mjs +++ b/test/integration/freetext_editor_spec.mjs @@ -37,6 +37,7 @@ import { kbModifierDown, kbModifierUp, kbRedo, + kbSelectAll, kbUndo, loadAndWait, moveEditor, @@ -3547,4 +3548,62 @@ describe("FreeText Editor", () => { ); }); }); + + describe("Delete some annotations, scroll to the end and then scroll to the beginning", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "tracemonkey_with_annotations.pdf", + ".annotationEditorLayer" + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the annotations aren't displayed after scrolling", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToFreeText(page); + + await kbSelectAll(page); + await page.waitForFunction( + () => document.querySelectorAll(".selectedEditor").length === 4 + ); + + await page.keyboard.press("Backspace"); + await page.waitForFunction(() => { + const { map } = + window.PDFViewerApplication.pdfDocument.annotationStorage + .serializable; + return ( + map.size === 4 && [...map.values()].every(entry => entry.deleted) + ); + }); + + // Disable editing mode. + await switchToFreeText(page, /* disable = */ true); + + const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat( + Array.from(new Array(13).keys(), n => 13 - n) + ); + for (const pageNumber of oneToOne) { + await scrollIntoView( + page, + `.page[data-page-number = "${pageNumber}"]` + ); + } + + await page.waitForFunction( + () => + document.querySelectorAll( + `.annotationLayer > section:is(.stampAnnotation, .inkAnnotation, .highlightAnnotation, .freeTextAnnotation)[hidden = ""]` + ).length === 4 + ); + }) + ); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 233649a1f..58549f5c8 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -739,3 +739,4 @@ !dates.pdf !dates_save.pdf !print_protection.pdf +!tracemonkey_with_annotations.pdf diff --git a/test/pdfs/tracemonkey_with_annotations.pdf b/test/pdfs/tracemonkey_with_annotations.pdf new file mode 100755 index 000000000..0cd834270 Binary files /dev/null and b/test/pdfs/tracemonkey_with_annotations.pdf differ