[Editor] Provide an element to render in the annotation layer after a freetext has been edited (bug 1890535)

This commit is contained in:
Calixte Denizet 2024-04-09 15:25:49 +02:00
parent 7290faf840
commit 71ea8499f0
11 changed files with 379 additions and 42 deletions

View file

@ -20,6 +20,8 @@
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("../../web/interfaces").IDownloadManager} IDownloadManager */ /** @typedef {import("../../web/interfaces").IDownloadManager} IDownloadManager */
/** @typedef {import("../../web/interfaces").IPDFLinkService} IPDFLinkService */ /** @typedef {import("../../web/interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
import { import {
AnnotationBorderStyleType, AnnotationBorderStyleType,
@ -157,6 +159,8 @@ class AnnotationElementFactory {
} }
class AnnotationElement { class AnnotationElement {
#updates = null;
#hasBorder = false; #hasBorder = false;
constructor( constructor(
@ -197,6 +201,52 @@ class AnnotationElement {
return AnnotationElement._hasPopupData(this.data); return AnnotationElement._hasPopupData(this.data);
} }
updateEdited(params) {
if (!this.container) {
return;
}
this.#updates ||= {
rect: this.data.rect.slice(0),
};
const { rect } = params;
if (rect) {
this.#setRectEdited(rect);
}
}
resetEdited() {
if (!this.#updates) {
return;
}
this.#setRectEdited(this.#updates.rect);
this.#updates = null;
}
#setRectEdited(rect) {
const {
container: { style },
data: { rect: currentRect, rotation },
parent: {
viewport: {
rawDims: { pageWidth, pageHeight, pageX, pageY },
},
},
} = this;
currentRect?.splice(0, 4, ...rect);
const { width, height } = getRectDims(rect);
style.left = `${(100 * (rect[0] - pageX)) / pageWidth}%`;
style.top = `${(100 * (pageHeight - rect[3] + pageY)) / pageHeight}%`;
if (rotation === 0) {
style.width = `${(100 * width) / pageWidth}%`;
style.height = `${(100 * height) / pageHeight}%`;
} else {
this.setRotation(rotation);
}
}
/** /**
* Create an empty container for the annotation's HTML element. * Create an empty container for the annotation's HTML element.
* *
@ -216,13 +266,14 @@ class AnnotationElement {
if (!(this instanceof WidgetAnnotationElement)) { if (!(this instanceof WidgetAnnotationElement)) {
container.tabIndex = DEFAULT_TAB_INDEX; container.tabIndex = DEFAULT_TAB_INDEX;
} }
const { style } = container;
// The accessibility manager will move the annotation in the DOM in // The accessibility manager will move the annotation in the DOM in
// order to match the visual ordering. // order to match the visual ordering.
// But if an annotation is above an other one, then we must draw it // But if an annotation is above an other one, then we must draw it
// after the other one whatever the order is in the DOM, hence the // after the other one whatever the order is in the DOM, hence the
// use of the z-index. // use of the z-index.
container.style.zIndex = this.parent.zIndex++; style.zIndex = this.parent.zIndex++;
if (data.popupRef) { if (data.popupRef) {
container.setAttribute("aria-haspopup", "dialog"); container.setAttribute("aria-haspopup", "dialog");
@ -236,8 +287,6 @@ class AnnotationElement {
container.classList.add("norotate"); container.classList.add("norotate");
} }
const { pageWidth, pageHeight, pageX, pageY } = viewport.rawDims;
if (!data.rect || this instanceof PopupAnnotationElement) { if (!data.rect || this instanceof PopupAnnotationElement) {
const { rotation } = data; const { rotation } = data;
if (!data.hasOwnCanvas && rotation !== 0) { if (!data.hasOwnCanvas && rotation !== 0) {
@ -248,35 +297,26 @@ class AnnotationElement {
const { width, height } = getRectDims(data.rect); const { width, height } = getRectDims(data.rect);
// Do *not* modify `data.rect`, since that will corrupt the annotation
// position on subsequent calls to `_createContainer` (see issue 6804).
const rect = Util.normalizeRect([
data.rect[0],
page.view[3] - data.rect[1] + page.view[1],
data.rect[2],
page.view[3] - data.rect[3] + page.view[1],
]);
if (!ignoreBorder && data.borderStyle.width > 0) { if (!ignoreBorder && data.borderStyle.width > 0) {
container.style.borderWidth = `${data.borderStyle.width}px`; style.borderWidth = `${data.borderStyle.width}px`;
const horizontalRadius = data.borderStyle.horizontalCornerRadius; const horizontalRadius = data.borderStyle.horizontalCornerRadius;
const verticalRadius = data.borderStyle.verticalCornerRadius; const verticalRadius = data.borderStyle.verticalCornerRadius;
if (horizontalRadius > 0 || verticalRadius > 0) { if (horizontalRadius > 0 || verticalRadius > 0) {
const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`;
container.style.borderRadius = radius; style.borderRadius = radius;
} else if (this instanceof RadioButtonWidgetAnnotationElement) { } else if (this instanceof RadioButtonWidgetAnnotationElement) {
const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`; const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`;
container.style.borderRadius = radius; style.borderRadius = radius;
} }
switch (data.borderStyle.style) { switch (data.borderStyle.style) {
case AnnotationBorderStyleType.SOLID: case AnnotationBorderStyleType.SOLID:
container.style.borderStyle = "solid"; style.borderStyle = "solid";
break; break;
case AnnotationBorderStyleType.DASHED: case AnnotationBorderStyleType.DASHED:
container.style.borderStyle = "dashed"; style.borderStyle = "dashed";
break; break;
case AnnotationBorderStyleType.BEVELED: case AnnotationBorderStyleType.BEVELED:
@ -288,7 +328,7 @@ class AnnotationElement {
break; break;
case AnnotationBorderStyleType.UNDERLINE: case AnnotationBorderStyleType.UNDERLINE:
container.style.borderBottomStyle = "solid"; style.borderBottomStyle = "solid";
break; break;
default: default:
@ -298,24 +338,34 @@ class AnnotationElement {
const borderColor = data.borderColor || null; const borderColor = data.borderColor || null;
if (borderColor) { if (borderColor) {
this.#hasBorder = true; this.#hasBorder = true;
container.style.borderColor = Util.makeHexColor( style.borderColor = Util.makeHexColor(
borderColor[0] | 0, borderColor[0] | 0,
borderColor[1] | 0, borderColor[1] | 0,
borderColor[2] | 0 borderColor[2] | 0
); );
} else { } else {
// Transparent (invisible) border, so do not draw it at all. // Transparent (invisible) border, so do not draw it at all.
container.style.borderWidth = 0; style.borderWidth = 0;
} }
} }
container.style.left = `${(100 * (rect[0] - pageX)) / pageWidth}%`; // Do *not* modify `data.rect`, since that will corrupt the annotation
container.style.top = `${(100 * (rect[1] - pageY)) / pageHeight}%`; // position on subsequent calls to `_createContainer` (see issue 6804).
const rect = Util.normalizeRect([
data.rect[0],
page.view[3] - data.rect[1] + page.view[1],
data.rect[2],
page.view[3] - data.rect[3] + page.view[1],
]);
const { pageWidth, pageHeight, pageX, pageY } = viewport.rawDims;
style.left = `${(100 * (rect[0] - pageX)) / pageWidth}%`;
style.top = `${(100 * (rect[1] - pageY)) / pageHeight}%`;
const { rotation } = data; const { rotation } = data;
if (data.hasOwnCanvas || rotation === 0) { if (data.hasOwnCanvas || rotation === 0) {
container.style.width = `${(100 * width) / pageWidth}%`; style.width = `${(100 * width) / pageWidth}%`;
container.style.height = `${(100 * height) / pageHeight}%`; style.height = `${(100 * height) / pageHeight}%`;
} else { } else {
this.setRotation(rotation, container); this.setRotation(rotation, container);
} }
@ -2897,6 +2947,7 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
* @property {Object<string, Array<Object>> | null} [fieldObjects] * @property {Object<string, Array<Object>> | null} [fieldObjects]
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap]
* @property {TextAccessibilityManager} [accessibilityManager] * @property {TextAccessibilityManager} [accessibilityManager]
* @property {AnnotationEditorUIManager} [annotationEditorUIManager]
*/ */
/** /**
@ -2913,6 +2964,7 @@ class AnnotationLayer {
div, div,
accessibilityManager, accessibilityManager,
annotationCanvasMap, annotationCanvasMap,
annotationEditorUIManager,
page, page,
viewport, viewport,
}) { }) {
@ -2922,6 +2974,7 @@ class AnnotationLayer {
this.page = page; this.page = page;
this.viewport = viewport; this.viewport = viewport;
this.zIndex = 0; this.zIndex = 0;
this._annotationEditorUIManager = annotationEditorUIManager;
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
// For testing purposes. // For testing purposes.
@ -3011,15 +3064,16 @@ class AnnotationLayer {
} }
} }
if (element.annotationEditorType > 0) {
this.#editableAnnotations.set(element.data.id, element);
}
const rendered = element.render(); const rendered = element.render();
if (data.hidden) { if (data.hidden) {
rendered.style.visibility = "hidden"; rendered.style.visibility = "hidden";
} }
this.#appendElement(rendered, data.id); this.#appendElement(rendered, data.id);
if (element.annotationEditorType > 0) {
this.#editableAnnotations.set(element.data.id, element);
this._annotationEditorUIManager?.renderAnnotationElement(element);
}
} }
this.#setAnnotationCanvasMap(); this.#setAnnotationCanvasMap();
@ -3051,13 +3105,16 @@ class AnnotationLayer {
continue; continue;
} }
canvas.className = "annotationContent";
const { firstChild } = element; const { firstChild } = element;
if (!firstChild) { if (!firstChild) {
element.append(canvas); element.append(canvas);
} else if (firstChild.nodeName === "CANVAS") { } else if (firstChild.nodeName === "CANVAS") {
firstChild.replaceWith(canvas); firstChild.replaceWith(canvas);
} else { } else if (!firstChild.classList.contains("annotationContent")) {
firstChild.before(canvas); firstChild.before(canvas);
} else {
firstChild.after(canvas);
} }
} }
this.#annotationCanvasMap.clear(); this.#annotationCanvasMap.clear();

View file

@ -248,7 +248,9 @@ class AnnotationEditorLayer {
const annotationElementIds = new Set(); const annotationElementIds = new Set();
for (const editor of this.#editors.values()) { for (const editor of this.#editors.values()) {
editor.enableEditing(); editor.enableEditing();
editor.show(true);
if (editor.annotationElementId) { if (editor.annotationElementId) {
this.#uiManager.removeChangedExistingAnnotation(editor);
annotationElementIds.add(editor.annotationElementId); annotationElementIds.add(editor.annotationElementId);
} }
} }
@ -283,13 +285,19 @@ class AnnotationEditorLayer {
this.#isDisabling = true; this.#isDisabling = true;
this.div.tabIndex = -1; this.div.tabIndex = -1;
this.togglePointerEvents(false); this.togglePointerEvents(false);
const hiddenAnnotationIds = new Set(); const changedAnnotations = new Map();
const resetAnnotations = new Map();
for (const editor of this.#editors.values()) { for (const editor of this.#editors.values()) {
editor.disableEditing(); editor.disableEditing();
if (!editor.annotationElementId || editor.serialize() !== null) { if (!editor.annotationElementId) {
hiddenAnnotationIds.add(editor.annotationElementId);
continue; continue;
} }
if (editor.serialize() !== null) {
changedAnnotations.set(editor.annotationElementId, editor);
continue;
} else {
resetAnnotations.set(editor.annotationElementId, editor);
}
this.getEditableAnnotation(editor.annotationElementId)?.show(); this.getEditableAnnotation(editor.annotationElementId)?.show();
editor.remove(); editor.remove();
} }
@ -299,12 +307,23 @@ class AnnotationEditorLayer {
const editables = this.#annotationLayer.getEditableAnnotations(); const editables = this.#annotationLayer.getEditableAnnotations();
for (const editable of editables) { for (const editable of editables) {
const { id } = editable.data; const { id } = editable.data;
if ( if (this.#uiManager.isDeletedAnnotationElement(id)) {
hiddenAnnotationIds.has(id) ||
this.#uiManager.isDeletedAnnotationElement(id)
) {
continue; continue;
} }
let editor = resetAnnotations.get(id);
if (editor) {
editor.resetAnnotationElement(editable);
editor.show(false);
editable.show();
continue;
}
editor = changedAnnotations.get(id);
if (editor) {
this.#uiManager.addChangedExistingAnnotation(editor);
editor.renderAnnotationElement(editable);
editor.show(false);
}
editable.show(); editable.show();
} }
} }
@ -461,7 +480,7 @@ class AnnotationEditorLayer {
return; return;
} }
if (editor.annotationElementId) { if (editor.parent && editor.annotationElementId) {
this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId); this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId);
AnnotationEditor.deleteAnnotationElement(editor); AnnotationEditor.deleteAnnotationElement(editor);
editor.annotationElementId = null; editor.annotationElementId = null;

View file

@ -1336,6 +1336,17 @@ class AnnotationEditor {
return editor; return editor;
} }
/**
* Check if an existing annotation associated with this editor has been
* modified.
* @returns {boolean}
*/
get hasBeenModified() {
return (
!!this.annotationElementId && (this.deleted || this.serialize() !== null)
);
}
/** /**
* Remove this editor. * Remove this editor.
* It's used on ctrl+backspace action. * It's used on ctrl+backspace action.
@ -1710,6 +1721,37 @@ class AnnotationEditor {
} }
this.#disabled = true; this.#disabled = true;
} }
/**
* Render an annotation in the annotation layer.
* @param {Object} annotation
* @returns {HTMLElement}
*/
renderAnnotationElement(annotation) {
let content = annotation.container.querySelector(".annotationContent");
if (!content) {
content = document.createElement("div");
content.classList.add("annotationContent", this.editorType);
annotation.container.prepend(content);
} else if (content.nodeName === "CANVAS") {
const canvas = content;
content = document.createElement("div");
content.classList.add("annotationContent", this.editorType);
canvas.before(content);
}
return content;
}
resetAnnotationElement(annotation) {
const { firstChild } = annotation.container;
if (
firstChild.nodeName === "DIV" &&
firstChild.classList.contains("annotationContent")
) {
firstChild.remove();
}
}
} }
// This class is used to fake an editor which has been deleted. // This class is used to fake an editor which has been deleted.

View file

@ -408,11 +408,14 @@ class FreeTextEditor extends AnnotationEditor {
// we just insert it in the DOM, get its bounding box and then remove it. // we just insert it in the DOM, get its bounding box and then remove it.
const { currentLayer, div } = this; const { currentLayer, div } = this;
const savedDisplay = div.style.display; const savedDisplay = div.style.display;
const savedVisibility = div.classList.contains("hidden");
div.classList.remove("hidden");
div.style.display = "hidden"; div.style.display = "hidden";
currentLayer.div.append(this.div); currentLayer.div.append(this.div);
rect = div.getBoundingClientRect(); rect = div.getBoundingClientRect();
div.remove(); div.remove();
div.style.display = savedDisplay; div.style.display = savedDisplay;
div.classList.toggle("hidden", savedVisibility);
} }
// The dimensions are relative to the rotation of the page, hence we need to // The dimensions are relative to the rotation of the page, hence we need to
@ -778,7 +781,7 @@ class FreeTextEditor extends AnnotationEditor {
value: textContent.join("\n"), value: textContent.join("\n"),
position: textPosition, position: textPosition,
pageIndex: pageNumber - 1, pageIndex: pageNumber - 1,
rect, rect: rect.slice(0),
rotation, rotation,
id, id,
deleted: false, deleted: false,
@ -853,6 +856,38 @@ class FreeTextEditor extends AnnotationEditor {
serialized.pageIndex !== pageIndex serialized.pageIndex !== pageIndex
); );
} }
/** @inheritdoc */
renderAnnotationElement(annotation) {
const content = super.renderAnnotationElement(annotation);
if (this.deleted) {
return content;
}
const { style } = content;
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;
content.replaceChildren();
for (const line of this.#content.split("\n")) {
const div = document.createElement("div");
div.append(
line ? document.createTextNode(line) : document.createElement("br")
);
content.append(div);
}
const padding = FreeTextEditor._internalPadding * this.parentScale;
annotation.updateEdited({
rect: this.getRect(padding, padding),
});
return content;
}
resetAnnotationElement(annotation) {
super.resetAnnotationElement(annotation);
annotation.resetEdited();
}
} }
export { FreeTextEditor }; export { FreeTextEditor };

View file

@ -544,6 +544,8 @@ class AnnotationEditorUIManager {
#annotationStorage = null; #annotationStorage = null;
#changedExistingAnnotations = null;
#commandManager = new CommandManager(); #commandManager = new CommandManager();
#currentPageIndex = 0; #currentPageIndex = 0;
@ -1682,6 +1684,7 @@ class AnnotationEditorUIManager {
*/ */
addDeletedAnnotationElement(editor) { addDeletedAnnotationElement(editor) {
this.#deletedAnnotationsElementIds.add(editor.annotationElementId); this.#deletedAnnotationsElementIds.add(editor.annotationElementId);
this.addChangedExistingAnnotation(editor);
editor.deleted = true; editor.deleted = true;
} }
@ -1700,6 +1703,7 @@ class AnnotationEditorUIManager {
*/ */
removeDeletedAnnotationElement(editor) { removeDeletedAnnotationElement(editor) {
this.#deletedAnnotationsElementIds.delete(editor.annotationElementId); this.#deletedAnnotationsElementIds.delete(editor.annotationElementId);
this.removeChangedExistingAnnotation(editor);
editor.deleted = false; editor.deleted = false;
} }
@ -2243,6 +2247,32 @@ class AnnotationEditorUIManager {
} }
return boxes.length === 0 ? null : boxes; return boxes.length === 0 ? null : boxes;
} }
addChangedExistingAnnotation({ annotationElementId, id }) {
(this.#changedExistingAnnotations ||= new Map()).set(
annotationElementId,
id
);
}
removeChangedExistingAnnotation({ annotationElementId }) {
this.#changedExistingAnnotations?.delete(annotationElementId);
}
renderAnnotationElement(annotation) {
const editorId = this.#changedExistingAnnotations?.get(annotation.data.id);
if (!editorId) {
return;
}
const editor = this.#annotationStorage.getRawValue(editorId);
if (!editor) {
return;
}
if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) {
return;
}
editor.renderAnnotationElement(annotation);
}
} }
export { export {

View file

@ -1125,15 +1125,24 @@ describe("FreeText Editor", () => {
); );
// We want to check that the editor is displayed but not the original // We want to check that the editor is displayed but not the original
// annotation. // canvas.
editorIds = await getEditors(page, "freeText"); editorIds = await getEditors(page, "freeText");
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1); expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1);
const hidden = await page.$eval( const hidden = await page.$eval(
"[data-annotation-id='26R']", "[data-annotation-id='26R'] canvas",
el => el.hidden el => getComputedStyle(el).display === "none"
); );
expect(hidden).withContext(`In ${browserName}`).toBeTrue(); expect(hidden).withContext(`In ${browserName}`).toBeTrue();
// Check we've now a div containing the text.
const newDivText = await page.$eval(
"[data-annotation-id='26R'] div.annotationContent",
el => el.innerText.replaceAll("\xa0", " ")
);
expect(newDivText)
.withContext(`In ${browserName}`)
.toEqual("Hello World from Acrobat and edited in Firefox");
// Re-enable editing mode. // Re-enable editing mode.
await switchToFreeText(page); await switchToFreeText(page);
await page.focus(".annotationEditorLayer"); await page.focus(".annotationEditorLayer");
@ -3715,4 +3724,123 @@ describe("FreeText Editor", () => {
); );
}); });
}); });
describe("Update a freetext and scroll", () => {
let pages;
beforeAll(async () => {
pages = await loadAndWait(
"tracemonkey_freetext.pdf",
".annotationEditorLayer"
);
});
afterAll(async () => {
await closePages(pages);
});
it("must check that a freetext is still there after having updated it and scroll the doc", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToFreeText(page);
const editorSelector = getEditorSelector(0);
const editorRect = await page.$eval(editorSelector, el => {
const { x, y, width, height } = el.getBoundingClientRect();
return { x, y, width, height };
});
await page.mouse.click(
editorRect.x + editorRect.width / 2,
editorRect.y + editorRect.height / 2,
{ count: 2 }
);
await page.waitForSelector(
`${editorSelector} .overlay:not(.enabled)`
);
await kbGoToEnd(page);
await page.waitForFunction(
sel =>
document.getSelection().anchorOffset ===
document.querySelector(sel).innerText.length,
{},
`${editorSelector} .internal`
);
await page.type(
`${editorSelector} .internal`,
" and edited in Firefox"
);
// Disable editing mode.
await page.click("#editorFreeText");
await page.waitForSelector(
`.annotationEditorLayer:not(.freetextEditing)`
);
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.waitForSelector("[data-annotation-id='998R'] canvas");
let hidden = await page.$eval(
"[data-annotation-id='998R'] canvas",
el => getComputedStyle(el).display === "none"
);
expect(hidden).withContext(`In ${browserName}`).toBeTrue();
// Check we've now a div containing the text.
await page.waitForSelector(
"[data-annotation-id='998R'] div.annotationContent"
);
const newDivText = await page.$eval(
"[data-annotation-id='998R'] div.annotationContent",
el => el.innerText.replaceAll("\xa0", " ")
);
expect(newDivText)
.withContext(`In ${browserName}`)
.toEqual("Hello World and edited in Firefox");
const oneToThirteen = Array.from(new Array(13).keys(), n => n + 2);
for (const pageNumber of oneToThirteen) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await switchToFreeText(page);
await kbUndo(page);
await waitForSerialized(page, 0);
// Disable editing mode.
await page.click("#editorFreeText");
await page.waitForSelector(
`.annotationEditorLayer:not(.freetextEditing)`
);
const thirteenToOne = Array.from(new Array(13).keys(), n => 13 - n);
for (const pageNumber of thirteenToOne) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await page.waitForSelector("[data-annotation-id='998R'] canvas");
hidden = await page.$eval(
"[data-annotation-id='998R'] canvas",
el => getComputedStyle(el).display === "none"
);
expect(hidden).withContext(`In ${browserName}`).toBeFalse();
})
);
});
});
}); });

View file

@ -643,3 +643,4 @@
!bug1889122.pdf !bug1889122.pdf
!issue17929.pdf !issue17929.pdf
!issue12213.pdf !issue12213.pdf
!tracemonkey_freetext.pdf

Binary file not shown.

View file

@ -94,11 +94,22 @@
} }
} }
canvas { .annotationContent {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
&.freetext {
background: transparent;
border: none;
inset: 0;
overflow: visible;
white-space: nowrap;
font: 10px sans-serif;
line-height: 1.35;
user-select: none;
}
} }
section { section {
@ -107,6 +118,12 @@
pointer-events: auto; pointer-events: auto;
box-sizing: border-box; box-sizing: border-box;
transform-origin: 0 0; transform-origin: 0 0;
&:has(div.annotationContent) {
canvas.annotationContent {
display: none;
}
}
} }
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a { :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a {

View file

@ -22,6 +22,8 @@
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ /** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
// eslint-disable-next-line max-len
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
import { AnnotationLayer } from "pdfjs-lib"; import { AnnotationLayer } from "pdfjs-lib";
import { PresentationModeState } from "./ui_utils.js"; import { PresentationModeState } from "./ui_utils.js";
@ -41,6 +43,7 @@ import { PresentationModeState } from "./ui_utils.js";
* [fieldObjectsPromise] * [fieldObjectsPromise]
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] * @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap]
* @property {TextAccessibilityManager} [accessibilityManager] * @property {TextAccessibilityManager} [accessibilityManager]
* @property {AnnotationEditorUIManager} [annotationEditorUIManager]
* @property {function} [onAppend] * @property {function} [onAppend]
*/ */
@ -64,6 +67,7 @@ class AnnotationLayerBuilder {
fieldObjectsPromise = null, fieldObjectsPromise = null,
annotationCanvasMap = null, annotationCanvasMap = null,
accessibilityManager = null, accessibilityManager = null,
annotationEditorUIManager = null,
onAppend = null, onAppend = null,
}) { }) {
this.pdfPage = pdfPage; this.pdfPage = pdfPage;
@ -77,6 +81,7 @@ class AnnotationLayerBuilder {
this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null); this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null);
this._annotationCanvasMap = annotationCanvasMap; this._annotationCanvasMap = annotationCanvasMap;
this._accessibilityManager = accessibilityManager; this._accessibilityManager = accessibilityManager;
this._annotationEditorUIManager = annotationEditorUIManager;
this.#onAppend = onAppend; this.#onAppend = onAppend;
this.annotationLayer = null; this.annotationLayer = null;
@ -128,6 +133,7 @@ class AnnotationLayerBuilder {
div, div,
accessibilityManager: this._accessibilityManager, accessibilityManager: this._accessibilityManager,
annotationCanvasMap: this._annotationCanvasMap, annotationCanvasMap: this._annotationCanvasMap,
annotationEditorUIManager: this._annotationEditorUIManager,
page: this.pdfPage, page: this.pdfPage,
viewport: viewport.clone({ dontFlip: true }), viewport: viewport.clone({ dontFlip: true }),
}); });

View file

@ -938,6 +938,7 @@ class PDFPageView {
) { ) {
const { const {
annotationStorage, annotationStorage,
annotationEditorUIManager,
downloadManager, downloadManager,
enableScripting, enableScripting,
fieldObjectsPromise, fieldObjectsPromise,
@ -958,6 +959,7 @@ class PDFPageView {
fieldObjectsPromise, fieldObjectsPromise,
annotationCanvasMap: this._annotationCanvasMap, annotationCanvasMap: this._annotationCanvasMap,
accessibilityManager: this._accessibilityManager, accessibilityManager: this._accessibilityManager,
annotationEditorUIManager,
onAppend: annotationLayerDiv => { onAppend: annotationLayerDiv => {
this.#addLayer(annotationLayerDiv, "annotationLayer"); this.#addLayer(annotationLayerDiv, "annotationLayer");
}, },