Merge pull request #18800 from calixteman/popup_deletion

[Editor] When deleting an annotation with popup, then delete the popup too
This commit is contained in:
calixteman 2024-09-26 18:04:24 +02:00 committed by GitHub
commit c46ac3f73f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 104 additions and 25 deletions

View File

@ -284,6 +284,12 @@ class Page {
} }
if (annotation.deleted) { if (annotation.deleted) {
deletedAnnotations.put(ref, ref); deletedAnnotations.put(ref, ref);
if (annotation.popupRef) {
const popupRef = Ref.fromString(annotation.popupRef);
if (popupRef) {
deletedAnnotations.put(popupRef, popupRef);
}
}
continue; continue;
} }
existingAnnotations?.put(ref); existingAnnotations?.put(ref);

View File

@ -80,6 +80,8 @@ class AnnotationEditor {
_initialOptions = Object.create(null); _initialOptions = Object.create(null);
_initialData = null;
_isVisible = true; _isVisible = true;
_uiManager = null; _uiManager = null;
@ -1335,6 +1337,19 @@ class AnnotationEditor {
*/ */
rotate(_angle) {} 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. * Serialize the editor.
* The result of the serialization will be used to construct a * The result of the serialization will be used to construct a
@ -1809,11 +1824,7 @@ class FakeEditor extends AnnotationEditor {
} }
serialize() { serialize() {
return { return this.serializeDeleted();
id: this.annotationElementId,
deleted: true,
pageIndex: this.pageIndex,
};
} }
} }

View File

@ -48,8 +48,6 @@ class FreeTextEditor extends AnnotationEditor {
#fontSize; #fontSize;
#initialData = null;
static _freeTextDefaultContent = ""; static _freeTextDefaultContent = "";
static _internalPadding = 0; static _internalPadding = 0;
@ -598,7 +596,7 @@ class FreeTextEditor extends AnnotationEditor {
// position is the position of the first glyph in the annotation // position is the position of the first glyph in the annotation
// and it's relative to its container. // and it's relative to its container.
const { position } = this.#initialData; const { position } = this._initialData;
let [tx, ty] = this.getInitialTranslation(); let [tx, ty] = this.getInitialTranslation();
[tx, ty] = this.pageTranslationToScreen(tx, ty); [tx, ty] = this.pageTranslationToScreen(tx, ty);
const [pageWidth, pageHeight] = this.pageDimensions; const [pageWidth, pageHeight] = this.pageDimensions;
@ -781,6 +779,7 @@ class FreeTextEditor extends AnnotationEditor {
rect, rect,
rotation, rotation,
id, id,
popupRef,
}, },
textContent, textContent,
textPosition, textPosition,
@ -805,6 +804,7 @@ class FreeTextEditor extends AnnotationEditor {
rotation, rotation,
id, id,
deleted: false, deleted: false,
popupRef,
}; };
} }
const editor = super.deserialize(data, parent, uiManager); const editor = super.deserialize(data, parent, uiManager);
@ -812,7 +812,7 @@ class FreeTextEditor extends AnnotationEditor {
editor.#color = Util.makeHexColor(...data.color); editor.#color = Util.makeHexColor(...data.color);
editor.#content = FreeTextEditor.#deserializeContent(data.value); editor.#content = FreeTextEditor.#deserializeContent(data.value);
editor.annotationElementId = data.id || null; editor.annotationElementId = data.id || null;
editor.#initialData = initialData; editor._initialData = initialData;
return editor; return editor;
} }
@ -824,11 +824,7 @@ class FreeTextEditor extends AnnotationEditor {
} }
if (this.deleted) { if (this.deleted) {
return { return this.serializeDeleted();
pageIndex: this.pageIndex,
id: this.annotationElementId,
deleted: true,
};
} }
const padding = FreeTextEditor._internalPadding * this.parentScale; const padding = FreeTextEditor._internalPadding * this.parentScale;
@ -866,7 +862,7 @@ class FreeTextEditor extends AnnotationEditor {
} }
#hasElementChanged(serialized) { #hasElementChanged(serialized) {
const { value, fontSize, color, pageIndex } = this.#initialData; const { value, fontSize, color, pageIndex } = this._initialData;
return ( return (
this._hasBeenMoved || this._hasBeenMoved ||

View File

@ -55,8 +55,6 @@ class HighlightEditor extends AnnotationEditor {
#id = null; #id = null;
#initialData = null;
#isFreeHighlight = false; #isFreeHighlight = false;
#lastPoint = null; #lastPoint = null;
@ -785,7 +783,7 @@ class HighlightEditor extends AnnotationEditor {
let initialData = null; let initialData = null;
if (data instanceof HighlightAnnotationElement) { if (data instanceof HighlightAnnotationElement) {
const { const {
data: { quadPoints, rect, rotation, id, color, opacity }, data: { quadPoints, rect, rotation, id, color, opacity, popupRef },
parent: { parent: {
page: { pageNumber }, page: { pageNumber },
}, },
@ -801,6 +799,7 @@ class HighlightEditor extends AnnotationEditor {
rotation, rotation,
id, id,
deleted: false, deleted: false,
popupRef,
}; };
} else if (data instanceof InkAnnotationElement) { } else if (data instanceof InkAnnotationElement) {
const { const {
@ -811,6 +810,7 @@ class HighlightEditor extends AnnotationEditor {
id, id,
color, color,
borderStyle: { rawWidth: thickness }, borderStyle: { rawWidth: thickness },
popupRef,
}, },
parent: { parent: {
page: { pageNumber }, page: { pageNumber },
@ -827,6 +827,7 @@ class HighlightEditor extends AnnotationEditor {
rotation, rotation,
id, id,
deleted: false, deleted: false,
popupRef,
}; };
} }
@ -839,7 +840,7 @@ class HighlightEditor extends AnnotationEditor {
editor.#thickness = data.thickness; editor.#thickness = data.thickness;
} }
editor.annotationElementId = data.id || null; editor.annotationElementId = data.id || null;
editor.#initialData = initialData; editor._initialData = initialData;
const [pageWidth, pageHeight] = editor.pageDimensions; const [pageWidth, pageHeight] = editor.pageDimensions;
const [pageX, pageY] = editor.pageTranslation; const [pageX, pageY] = editor.pageTranslation;
@ -902,11 +903,7 @@ class HighlightEditor extends AnnotationEditor {
} }
if (this.deleted) { if (this.deleted) {
return { return this.serializeDeleted();
pageIndex: this.pageIndex,
id: this.annotationElementId,
deleted: true,
};
} }
const rect = this.getRect(0, 0); const rect = this.getRect(0, 0);
@ -934,7 +931,7 @@ class HighlightEditor extends AnnotationEditor {
} }
#hasElementChanged(serialized) { #hasElementChanged(serialized) {
const { color } = this.#initialData; const { color } = this._initialData;
return serialized.color.some((c, i) => c !== color[i]); return serialized.color.some((c, i) => c !== color[i]);
} }

View File

@ -1237,6 +1237,7 @@ describe("FreeText Editor", () => {
pageIndex: 0, pageIndex: 0,
id: "51R", id: "51R",
deleted: true, deleted: true,
popupRef: "",
}, },
]); ]);

View File

@ -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)", () => { describe("Free Highlight (edit existing in double clicking on it)", () => {
let pages; let pages;

View File

@ -670,3 +670,4 @@
!bug1918115.pdf !bug1918115.pdf
!bug1919513.pdf !bug1919513.pdf
!issue16038.pdf !issue16038.pdf
!highlight_popup.pdf

BIN
test/pdfs/highlight_popup.pdf Executable file

Binary file not shown.

View File

@ -2926,6 +2926,29 @@ describe("api", function () {
await loadingTask.destroy(); await loadingTask.destroy();
}); });
it("write an highlight annotation and delete its popup", async function () {
let loadingTask = getDocument(
buildGetDocumentParams("highlight_popup.pdf")
);
let pdfDoc = await loadingTask.promise;
pdfDoc.annotationStorage.setValue("pdfjs_internal_editor_0", {
deleted: true,
id: "24R",
pageIndex: 0,
popupRef: "25R",
});
const data = await pdfDoc.saveDocument();
await loadingTask.destroy();
loadingTask = getDocument(data);
pdfDoc = await loadingTask.promise;
const page = await pdfDoc.getPage(1);
const annotations = await page.getAnnotations();
expect(annotations).toEqual([]);
await loadingTask.destroy();
});
it("read content from multiline textfield containing an empty line", async function () { it("read content from multiline textfield containing an empty line", async function () {
const loadingTask = getDocument(buildGetDocumentParams("issue17492.pdf")); const loadingTask = getDocument(buildGetDocumentParams("issue17492.pdf"));
const pdfDoc = await loadingTask.promise; const pdfDoc = await loadingTask.promise;