mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-09 01:35:43 +02:00
[Editor] Make the text layer focusable before the editors (bug 1881746)
Keep the different layers in a constant order to avoid the use of a z-index and a tab-index.
This commit is contained in:
parent
0022310b9c
commit
1b00511301
14 changed files with 187 additions and 36 deletions
|
@ -237,6 +237,7 @@ class AnnotationEditorLayer {
|
||||||
* editor creation.
|
* editor creation.
|
||||||
*/
|
*/
|
||||||
enable() {
|
enable() {
|
||||||
|
this.div.tabIndex = 0;
|
||||||
this.togglePointerEvents(true);
|
this.togglePointerEvents(true);
|
||||||
const annotationElementIds = new Set();
|
const annotationElementIds = new Set();
|
||||||
for (const editor of this.#editors.values()) {
|
for (const editor of this.#editors.values()) {
|
||||||
|
@ -274,6 +275,7 @@ class AnnotationEditorLayer {
|
||||||
*/
|
*/
|
||||||
disable() {
|
disable() {
|
||||||
this.#isDisabling = true;
|
this.#isDisabling = true;
|
||||||
|
this.div.tabIndex = -1;
|
||||||
this.togglePointerEvents(false);
|
this.togglePointerEvents(false);
|
||||||
const hiddenAnnotationIds = new Set();
|
const hiddenAnnotationIds = new Set();
|
||||||
for (const editor of this.#editors.values()) {
|
for (const editor of this.#editors.values()) {
|
||||||
|
@ -333,6 +335,7 @@ class AnnotationEditorLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
enableTextSelection() {
|
enableTextSelection() {
|
||||||
|
this.div.tabIndex = -1;
|
||||||
if (this.#textLayer?.div && !this.#boundTextLayerPointerDown) {
|
if (this.#textLayer?.div && !this.#boundTextLayerPointerDown) {
|
||||||
this.#boundTextLayerPointerDown = this.#textLayerPointerDown.bind(this);
|
this.#boundTextLayerPointerDown = this.#textLayerPointerDown.bind(this);
|
||||||
this.#textLayer.div.addEventListener(
|
this.#textLayer.div.addEventListener(
|
||||||
|
@ -344,6 +347,7 @@ class AnnotationEditorLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
disableTextSelection() {
|
disableTextSelection() {
|
||||||
|
this.div.tabIndex = 0;
|
||||||
if (this.#textLayer?.div && this.#boundTextLayerPointerDown) {
|
if (this.#textLayer?.div && this.#boundTextLayerPointerDown) {
|
||||||
this.#textLayer.div.removeEventListener(
|
this.#textLayer.div.removeEventListener(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
|
|
|
@ -44,6 +44,8 @@ class AnnotationEditor {
|
||||||
|
|
||||||
#altText = null;
|
#altText = null;
|
||||||
|
|
||||||
|
#disabled = false;
|
||||||
|
|
||||||
#keepAspectRatio = false;
|
#keepAspectRatio = false;
|
||||||
|
|
||||||
#resizersDiv = null;
|
#resizersDiv = null;
|
||||||
|
@ -1002,7 +1004,7 @@ class AnnotationEditor {
|
||||||
this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
|
this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
|
||||||
this.div.className = this.name;
|
this.div.className = this.name;
|
||||||
this.div.setAttribute("id", this.id);
|
this.div.setAttribute("id", this.id);
|
||||||
this.div.setAttribute("tabIndex", 0);
|
this.div.tabIndex = this.#disabled ? -1 : 0;
|
||||||
if (!this._isVisible) {
|
if (!this._isVisible) {
|
||||||
this.div.classList.add("hidden");
|
this.div.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
@ -1680,6 +1682,20 @@ class AnnotationEditor {
|
||||||
this.div.classList.toggle("hidden", !visible);
|
this.div.classList.toggle("hidden", !visible);
|
||||||
this._isVisible = visible;
|
this._isVisible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enable() {
|
||||||
|
if (this.div) {
|
||||||
|
this.div.tabIndex = 0;
|
||||||
|
}
|
||||||
|
this.#disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable() {
|
||||||
|
if (this.div) {
|
||||||
|
this.div.tabIndex = -1;
|
||||||
|
}
|
||||||
|
this.#disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class is used to fake an editor which has been deleted.
|
// This class is used to fake an editor which has been deleted.
|
||||||
|
|
|
@ -1596,6 +1596,9 @@ class AnnotationEditorUIManager {
|
||||||
for (const layer of this.#allLayers.values()) {
|
for (const layer of this.#allLayers.values()) {
|
||||||
layer.enable();
|
layer.enable();
|
||||||
}
|
}
|
||||||
|
for (const editor of this.#allEditors.values()) {
|
||||||
|
editor.enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1609,6 +1612,9 @@ class AnnotationEditorUIManager {
|
||||||
for (const layer of this.#allLayers.values()) {
|
for (const layer of this.#allLayers.values()) {
|
||||||
layer.disable();
|
layer.disable();
|
||||||
}
|
}
|
||||||
|
for (const editor of this.#allEditors.values()) {
|
||||||
|
editor.disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
closePages,
|
closePages,
|
||||||
createPromise,
|
createPromise,
|
||||||
dragAndDropAnnotation,
|
dragAndDropAnnotation,
|
||||||
|
firstPageOnTop,
|
||||||
getEditors,
|
getEditors,
|
||||||
getEditorSelector,
|
getEditorSelector,
|
||||||
getFirstSerialized,
|
getFirstSerialized,
|
||||||
|
@ -39,6 +40,7 @@ import {
|
||||||
kbUndo,
|
kbUndo,
|
||||||
loadAndWait,
|
loadAndWait,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
|
waitForAnnotationEditorLayer,
|
||||||
waitForEvent,
|
waitForEvent,
|
||||||
waitForSelectedEditor,
|
waitForSelectedEditor,
|
||||||
waitForSerialized,
|
waitForSerialized,
|
||||||
|
@ -923,6 +925,7 @@ describe("FreeText Editor", () => {
|
||||||
let currentId = 0;
|
let currentId = 0;
|
||||||
|
|
||||||
for (let step = 0; step < 3; step++) {
|
for (let step = 0; step < 3; step++) {
|
||||||
|
await firstPageOnTop(page);
|
||||||
const rect = await page.$eval(".annotationEditorLayer", el => {
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
// With Chrome something is wrong when serializing a DomRect,
|
// With Chrome something is wrong when serializing a DomRect,
|
||||||
// hence we extract the values and just return them.
|
// hence we extract the values and just return them.
|
||||||
|
@ -931,8 +934,8 @@ describe("FreeText Editor", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = `Hello ${step}`;
|
const data = `Hello ${step}`;
|
||||||
const x = rect.x + 0.1 * rect.width;
|
const x = Math.max(rect.x + 0.1 * rect.width, 10);
|
||||||
const y = rect.y + 0.1 * rect.height;
|
const y = Math.max(rect.y + 0.1 * rect.height, 10);
|
||||||
await page.mouse.click(x, y);
|
await page.mouse.click(x, y);
|
||||||
await page.waitForSelector(getEditorSelector(currentId), {
|
await page.waitForSelector(getEditorSelector(currentId), {
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -945,9 +948,12 @@ describe("FreeText Editor", () => {
|
||||||
`${getEditorSelector(currentId)} .overlay.enabled`
|
`${getEditorSelector(currentId)} .overlay.enabled`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const promise = await waitForAnnotationEditorLayer(page);
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
document.getElementById("pageRotateCw").click();
|
document.getElementById("pageRotateCw").click();
|
||||||
});
|
});
|
||||||
|
await awaitPromise(promise);
|
||||||
|
|
||||||
currentId += 1;
|
currentId += 1;
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
".page[data-page-number='1'] .canvasWrapper",
|
".page[data-page-number='1'] .canvasWrapper",
|
||||||
|
|
|
@ -22,6 +22,8 @@ import {
|
||||||
getSerialized,
|
getSerialized,
|
||||||
kbBigMoveLeft,
|
kbBigMoveLeft,
|
||||||
kbBigMoveUp,
|
kbBigMoveUp,
|
||||||
|
kbFocusNext,
|
||||||
|
kbFocusPrevious,
|
||||||
kbSelectAll,
|
kbSelectAll,
|
||||||
loadAndWait,
|
loadAndWait,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
|
@ -1556,4 +1558,52 @@ describe("Highlight Editor", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Text layer must have the focus before highlights", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check the focus order", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#editorHighlight");
|
||||||
|
await page.waitForSelector(".annotationEditorLayer.highlightEditing");
|
||||||
|
|
||||||
|
let rect = await getSpanRectFromText(page, 1, "Abstract");
|
||||||
|
let x = rect.x + rect.width / 2;
|
||||||
|
let y = rect.y + rect.height / 2;
|
||||||
|
await page.mouse.click(x, y, { count: 2, delay: 100 });
|
||||||
|
await page.waitForSelector(getEditorSelector(0));
|
||||||
|
|
||||||
|
rect = await getSpanRectFromText(page, 1, "Languages");
|
||||||
|
x = rect.x + rect.width / 2;
|
||||||
|
y = rect.y + rect.height / 2;
|
||||||
|
await page.mouse.click(x, y, { count: 2, delay: 100 });
|
||||||
|
await page.waitForSelector(getEditorSelector(1));
|
||||||
|
await page.focus(getEditorSelector(1));
|
||||||
|
|
||||||
|
await kbFocusPrevious(page);
|
||||||
|
await page.waitForFunction(
|
||||||
|
sel => document.querySelector(sel) === document.activeElement,
|
||||||
|
{},
|
||||||
|
`.page[data-page-number="1"] > .textLayer`
|
||||||
|
);
|
||||||
|
|
||||||
|
await kbFocusNext(page);
|
||||||
|
await page.waitForFunction(
|
||||||
|
sel => document.querySelector(sel) === document.activeElement,
|
||||||
|
{},
|
||||||
|
getEditorSelector(1)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -318,9 +318,12 @@ async function scrollIntoView(page, selector) {
|
||||||
const handle = await page.evaluateHandle(
|
const handle = await page.evaluateHandle(
|
||||||
sel => [
|
sel => [
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
document
|
const container = document.getElementById("viewerContainer");
|
||||||
.getElementById("viewerContainer")
|
if (container.scrollHeight <= container.clientHeight) {
|
||||||
.addEventListener("scrollend", resolve, { once: true });
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.addEventListener("scrollend", resolve, { once: true });
|
||||||
const element = document.querySelector(sel);
|
const element = document.querySelector(sel);
|
||||||
element.scrollIntoView({ behavior: "instant", block: "start" });
|
element.scrollIntoView({ behavior: "instant", block: "start" });
|
||||||
}),
|
}),
|
||||||
|
@ -330,6 +333,21 @@ async function scrollIntoView(page, selector) {
|
||||||
return awaitPromise(handle);
|
return awaitPromise(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function firstPageOnTop(page) {
|
||||||
|
const handle = await page.evaluateHandle(() => [
|
||||||
|
new Promise(resolve => {
|
||||||
|
const container = document.getElementById("viewerContainer");
|
||||||
|
if (container.scrollTop === 0 && container.scrollLeft === 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.addEventListener("scrollend", resolve, { once: true });
|
||||||
|
container.scrollTo(0, 0);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
return awaitPromise(handle);
|
||||||
|
}
|
||||||
|
|
||||||
async function hover(page, selector) {
|
async function hover(page, selector) {
|
||||||
const rect = await page.$eval(selector, el => {
|
const rect = await page.$eval(selector, el => {
|
||||||
const { x, y, width, height } = el.getBoundingClientRect();
|
const { x, y, width, height } = el.getBoundingClientRect();
|
||||||
|
@ -461,12 +479,31 @@ async function kbDeleteLastWord(page) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function kbFocusNext(page) {
|
||||||
|
const handle = await createPromise(page, resolve => {
|
||||||
|
window.addEventListener("focusin", resolve, { once: true });
|
||||||
|
});
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await awaitPromise(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function kbFocusPrevious(page) {
|
||||||
|
const handle = await createPromise(page, resolve => {
|
||||||
|
window.addEventListener("focusin", resolve, { once: true });
|
||||||
|
});
|
||||||
|
await page.keyboard.down("Shift");
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await page.keyboard.up("Shift");
|
||||||
|
await awaitPromise(handle);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
awaitPromise,
|
awaitPromise,
|
||||||
clearInput,
|
clearInput,
|
||||||
closePages,
|
closePages,
|
||||||
createPromise,
|
createPromise,
|
||||||
dragAndDropAnnotation,
|
dragAndDropAnnotation,
|
||||||
|
firstPageOnTop,
|
||||||
getAnnotationStorage,
|
getAnnotationStorage,
|
||||||
getComputedStyleSelector,
|
getComputedStyleSelector,
|
||||||
getEditorDimensions,
|
getEditorDimensions,
|
||||||
|
@ -484,6 +521,8 @@ export {
|
||||||
kbBigMoveUp,
|
kbBigMoveUp,
|
||||||
kbCopy,
|
kbCopy,
|
||||||
kbDeleteLastWord,
|
kbDeleteLastWord,
|
||||||
|
kbFocusNext,
|
||||||
|
kbFocusPrevious,
|
||||||
kbGoToBegin,
|
kbGoToBegin,
|
||||||
kbGoToEnd,
|
kbGoToEnd,
|
||||||
kbModifierDown,
|
kbModifierDown,
|
||||||
|
|
|
@ -109,7 +109,6 @@
|
||||||
font-size: calc(100px * var(--scale-factor));
|
font-size: calc(100px * var(--scale-factor));
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
z-index: 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer.waiting {
|
.annotationEditorLayer.waiting {
|
||||||
|
|
|
@ -30,13 +30,13 @@ import { GenericL10n } from "web-null_l10n";
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AnnotationEditorLayerBuilderOptions
|
* @typedef {Object} AnnotationEditorLayerBuilderOptions
|
||||||
* @property {AnnotationEditorUIManager} [uiManager]
|
* @property {AnnotationEditorUIManager} [uiManager]
|
||||||
* @property {HTMLDivElement} pageDiv
|
|
||||||
* @property {PDFPageProxy} pdfPage
|
* @property {PDFPageProxy} pdfPage
|
||||||
* @property {IL10n} [l10n]
|
* @property {IL10n} [l10n]
|
||||||
* @property {TextAccessibilityManager} [accessibilityManager]
|
* @property {TextAccessibilityManager} [accessibilityManager]
|
||||||
* @property {AnnotationLayer} [annotationLayer]
|
* @property {AnnotationLayer} [annotationLayer]
|
||||||
* @property {TextLayer} [textLayer]
|
* @property {TextLayer} [textLayer]
|
||||||
* @property {DrawLayer} [drawLayer]
|
* @property {DrawLayer} [drawLayer]
|
||||||
|
* @property {function} [onAppend]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AnnotationEditorLayerBuilder {
|
class AnnotationEditorLayerBuilder {
|
||||||
|
@ -44,6 +44,8 @@ class AnnotationEditorLayerBuilder {
|
||||||
|
|
||||||
#drawLayer = null;
|
#drawLayer = null;
|
||||||
|
|
||||||
|
#onAppend = null;
|
||||||
|
|
||||||
#textLayer = null;
|
#textLayer = null;
|
||||||
|
|
||||||
#uiManager;
|
#uiManager;
|
||||||
|
@ -52,7 +54,6 @@ class AnnotationEditorLayerBuilder {
|
||||||
* @param {AnnotationEditorLayerBuilderOptions} options
|
* @param {AnnotationEditorLayerBuilderOptions} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.pageDiv = options.pageDiv;
|
|
||||||
this.pdfPage = options.pdfPage;
|
this.pdfPage = options.pdfPage;
|
||||||
this.accessibilityManager = options.accessibilityManager;
|
this.accessibilityManager = options.accessibilityManager;
|
||||||
this.l10n = options.l10n;
|
this.l10n = options.l10n;
|
||||||
|
@ -66,6 +67,7 @@ class AnnotationEditorLayerBuilder {
|
||||||
this.#annotationLayer = options.annotationLayer || null;
|
this.#annotationLayer = options.annotationLayer || null;
|
||||||
this.#textLayer = options.textLayer || null;
|
this.#textLayer = options.textLayer || null;
|
||||||
this.#drawLayer = options.drawLayer || null;
|
this.#drawLayer = options.drawLayer || null;
|
||||||
|
this.#onAppend = options.onAppend || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,10 +93,9 @@ class AnnotationEditorLayerBuilder {
|
||||||
// Create an AnnotationEditor layer div
|
// Create an AnnotationEditor layer div
|
||||||
const div = (this.div = document.createElement("div"));
|
const div = (this.div = document.createElement("div"));
|
||||||
div.className = "annotationEditorLayer";
|
div.className = "annotationEditorLayer";
|
||||||
div.tabIndex = 0;
|
|
||||||
div.hidden = true;
|
div.hidden = true;
|
||||||
div.dir = this.#uiManager.direction;
|
div.dir = this.#uiManager.direction;
|
||||||
this.pageDiv.append(div);
|
this.#onAppend?.(div);
|
||||||
|
|
||||||
this.annotationEditorLayer = new AnnotationEditorLayer({
|
this.annotationEditorLayer = new AnnotationEditorLayer({
|
||||||
uiManager: this.#uiManager,
|
uiManager: this.#uiManager,
|
||||||
|
@ -125,7 +126,6 @@ class AnnotationEditorLayerBuilder {
|
||||||
if (!this.div) {
|
if (!this.div) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.pageDiv = null;
|
|
||||||
this.annotationEditorLayer.destroy();
|
this.annotationEditorLayer.destroy();
|
||||||
this.div.remove();
|
this.div.remove();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
z-index: 3;
|
|
||||||
|
|
||||||
&[data-main-rotation="90"] .norotate {
|
&[data-main-rotation="90"] .norotate {
|
||||||
transform: rotate(270deg) translateX(-100%);
|
transform: rotate(270deg) translateX(-100%);
|
||||||
|
|
|
@ -28,7 +28,6 @@ import { PresentationModeState } from "./ui_utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AnnotationLayerBuilderOptions
|
* @typedef {Object} AnnotationLayerBuilderOptions
|
||||||
* @property {HTMLDivElement} pageDiv
|
|
||||||
* @property {PDFPageProxy} pdfPage
|
* @property {PDFPageProxy} pdfPage
|
||||||
* @property {AnnotationStorage} [annotationStorage]
|
* @property {AnnotationStorage} [annotationStorage]
|
||||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||||
|
@ -42,16 +41,18 @@ 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 {function} [onAppend]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AnnotationLayerBuilder {
|
class AnnotationLayerBuilder {
|
||||||
|
#onAppend = null;
|
||||||
|
|
||||||
#onPresentationModeChanged = null;
|
#onPresentationModeChanged = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AnnotationLayerBuilderOptions} options
|
* @param {AnnotationLayerBuilderOptions} options
|
||||||
*/
|
*/
|
||||||
constructor({
|
constructor({
|
||||||
pageDiv,
|
|
||||||
pdfPage,
|
pdfPage,
|
||||||
linkService,
|
linkService,
|
||||||
downloadManager,
|
downloadManager,
|
||||||
|
@ -63,8 +64,8 @@ class AnnotationLayerBuilder {
|
||||||
fieldObjectsPromise = null,
|
fieldObjectsPromise = null,
|
||||||
annotationCanvasMap = null,
|
annotationCanvasMap = null,
|
||||||
accessibilityManager = null,
|
accessibilityManager = null,
|
||||||
|
onAppend = null,
|
||||||
}) {
|
}) {
|
||||||
this.pageDiv = pageDiv;
|
|
||||||
this.pdfPage = pdfPage;
|
this.pdfPage = pdfPage;
|
||||||
this.linkService = linkService;
|
this.linkService = linkService;
|
||||||
this.downloadManager = downloadManager;
|
this.downloadManager = downloadManager;
|
||||||
|
@ -76,6 +77,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.#onAppend = onAppend;
|
||||||
|
|
||||||
this.annotationLayer = null;
|
this.annotationLayer = null;
|
||||||
this.div = null;
|
this.div = null;
|
||||||
|
@ -115,7 +117,7 @@ class AnnotationLayerBuilder {
|
||||||
// if there is at least one annotation.
|
// if there is at least one annotation.
|
||||||
const div = (this.div = document.createElement("div"));
|
const div = (this.div = document.createElement("div"));
|
||||||
div.className = "annotationLayer";
|
div.className = "annotationLayer";
|
||||||
this.pageDiv.append(div);
|
this.#onAppend?.(div);
|
||||||
|
|
||||||
if (annotations.length === 0) {
|
if (annotations.length === 0) {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
|
@ -99,6 +99,14 @@ const DEFAULT_LAYER_PROPERTIES =
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LAYERS_ORDER = new Map([
|
||||||
|
["canvasWrapper", 0],
|
||||||
|
["textLayer", 1],
|
||||||
|
["annotationLayer", 2],
|
||||||
|
["annotationEditorLayer", 3],
|
||||||
|
["xfaLayer", 2],
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @implements {IRenderableView}
|
* @implements {IRenderableView}
|
||||||
*/
|
*/
|
||||||
|
@ -127,6 +135,8 @@ class PDFPageView {
|
||||||
|
|
||||||
#viewportMap = new WeakMap();
|
#viewportMap = new WeakMap();
|
||||||
|
|
||||||
|
#layers = [null, null, null, null];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFPageViewOptions} options
|
* @param {PDFPageViewOptions} options
|
||||||
*/
|
*/
|
||||||
|
@ -223,6 +233,19 @@ class PDFPageView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#addLayer(div, name) {
|
||||||
|
const pos = LAYERS_ORDER.get(name);
|
||||||
|
this.#layers[pos] = div;
|
||||||
|
for (let i = pos - 1; i >= 0; i--) {
|
||||||
|
const layer = this.#layers[i];
|
||||||
|
if (layer) {
|
||||||
|
layer.after(div);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.div.prepend(div);
|
||||||
|
}
|
||||||
|
|
||||||
get renderingState() {
|
get renderingState() {
|
||||||
return this.#renderingState;
|
return this.#renderingState;
|
||||||
}
|
}
|
||||||
|
@ -392,7 +415,7 @@ class PDFPageView {
|
||||||
if (this.xfaLayer?.div) {
|
if (this.xfaLayer?.div) {
|
||||||
// Pause translation when inserting the xfaLayer in the DOM.
|
// Pause translation when inserting the xfaLayer in the DOM.
|
||||||
this.l10n.pause();
|
this.l10n.pause();
|
||||||
this.div.append(this.xfaLayer.div);
|
this.#addLayer(this.xfaLayer.div, "xfaLayer");
|
||||||
this.l10n.resume();
|
this.l10n.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,6 +554,10 @@ class PDFPageView {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
node.remove();
|
node.remove();
|
||||||
|
const layerIndex = this.#layers.indexOf(node);
|
||||||
|
if (layerIndex >= 0) {
|
||||||
|
this.#layers[layerIndex] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
div.removeAttribute("data-loaded");
|
div.removeAttribute("data-loaded");
|
||||||
|
|
||||||
|
@ -877,7 +904,8 @@ class PDFPageView {
|
||||||
// overflow will be hidden in Firefox.
|
// overflow will be hidden in Firefox.
|
||||||
const canvasWrapper = document.createElement("div");
|
const canvasWrapper = document.createElement("div");
|
||||||
canvasWrapper.classList.add("canvasWrapper");
|
canvasWrapper.classList.add("canvasWrapper");
|
||||||
div.append(canvasWrapper);
|
canvasWrapper.setAttribute("aria-hidden", true);
|
||||||
|
this.#addLayer(canvasWrapper, "canvasWrapper");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.textLayer &&
|
!this.textLayer &&
|
||||||
|
@ -891,13 +919,13 @@ class PDFPageView {
|
||||||
accessibilityManager: this._accessibilityManager,
|
accessibilityManager: this._accessibilityManager,
|
||||||
enablePermissions:
|
enablePermissions:
|
||||||
this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
|
this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
|
||||||
});
|
onAppend: textLayerDiv => {
|
||||||
this.textLayer.onAppend = textLayerDiv => {
|
|
||||||
// Pause translation when inserting the textLayer in the DOM.
|
// Pause translation when inserting the textLayer in the DOM.
|
||||||
this.l10n.pause();
|
this.l10n.pause();
|
||||||
this.div.append(textLayerDiv);
|
this.#addLayer(textLayerDiv, "textLayer");
|
||||||
this.l10n.resume();
|
this.l10n.resume();
|
||||||
};
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -915,7 +943,6 @@ class PDFPageView {
|
||||||
|
|
||||||
this._annotationCanvasMap ||= new Map();
|
this._annotationCanvasMap ||= new Map();
|
||||||
this.annotationLayer = new AnnotationLayerBuilder({
|
this.annotationLayer = new AnnotationLayerBuilder({
|
||||||
pageDiv: div,
|
|
||||||
pdfPage,
|
pdfPage,
|
||||||
annotationStorage,
|
annotationStorage,
|
||||||
imageResourcesPath: this.imageResourcesPath,
|
imageResourcesPath: this.imageResourcesPath,
|
||||||
|
@ -927,6 +954,9 @@ class PDFPageView {
|
||||||
fieldObjectsPromise,
|
fieldObjectsPromise,
|
||||||
annotationCanvasMap: this._annotationCanvasMap,
|
annotationCanvasMap: this._annotationCanvasMap,
|
||||||
accessibilityManager: this._accessibilityManager,
|
accessibilityManager: this._accessibilityManager,
|
||||||
|
onAppend: annotationLayerDiv => {
|
||||||
|
this.#addLayer(annotationLayerDiv, "annotationLayer");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1042,13 +1072,15 @@ class PDFPageView {
|
||||||
if (!this.annotationEditorLayer) {
|
if (!this.annotationEditorLayer) {
|
||||||
this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
|
this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
|
||||||
uiManager: annotationEditorUIManager,
|
uiManager: annotationEditorUIManager,
|
||||||
pageDiv: div,
|
|
||||||
pdfPage,
|
pdfPage,
|
||||||
l10n,
|
l10n,
|
||||||
accessibilityManager: this._accessibilityManager,
|
accessibilityManager: this._accessibilityManager,
|
||||||
annotationLayer: this.annotationLayer?.annotationLayer,
|
annotationLayer: this.annotationLayer?.annotationLayer,
|
||||||
textLayer: this.textLayer,
|
textLayer: this.textLayer,
|
||||||
drawLayer: this.drawLayer.getDrawLayer(),
|
drawLayer: this.drawLayer.getDrawLayer(),
|
||||||
|
onAppend: annotationEditorLayerDiv => {
|
||||||
|
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.#renderAnnotationEditorLayer();
|
this.#renderAnnotationEditorLayer();
|
||||||
|
|
|
@ -75,7 +75,6 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdfViewer .page {
|
.pdfViewer .page {
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
text-size-adjust: none;
|
text-size-adjust: none;
|
||||||
forced-color-adjust: none;
|
forced-color-adjust: none;
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
z-index: 2;
|
|
||||||
caret-color: CanvasText;
|
caret-color: CanvasText;
|
||||||
|
|
||||||
&.highlighting {
|
&.highlighting {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { removeNullCharacters } from "./ui_utils.js";
|
||||||
* @property {TextHighlighter} highlighter - Optional object that will handle
|
* @property {TextHighlighter} highlighter - Optional object that will handle
|
||||||
* highlighting text from the find controller.
|
* highlighting text from the find controller.
|
||||||
* @property {TextAccessibilityManager} [accessibilityManager]
|
* @property {TextAccessibilityManager} [accessibilityManager]
|
||||||
|
* @property {function} [onAppend]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +39,8 @@ import { removeNullCharacters } from "./ui_utils.js";
|
||||||
class TextLayerBuilder {
|
class TextLayerBuilder {
|
||||||
#enablePermissions = false;
|
#enablePermissions = false;
|
||||||
|
|
||||||
|
#onAppend = null;
|
||||||
|
|
||||||
#rotation = 0;
|
#rotation = 0;
|
||||||
|
|
||||||
#scale = 0;
|
#scale = 0;
|
||||||
|
@ -48,6 +51,7 @@ class TextLayerBuilder {
|
||||||
highlighter = null,
|
highlighter = null,
|
||||||
accessibilityManager = null,
|
accessibilityManager = null,
|
||||||
enablePermissions = false,
|
enablePermissions = false,
|
||||||
|
onAppend = null,
|
||||||
}) {
|
}) {
|
||||||
this.textContentItemsStr = [];
|
this.textContentItemsStr = [];
|
||||||
this.renderingDone = false;
|
this.renderingDone = false;
|
||||||
|
@ -57,14 +61,10 @@ class TextLayerBuilder {
|
||||||
this.highlighter = highlighter;
|
this.highlighter = highlighter;
|
||||||
this.accessibilityManager = accessibilityManager;
|
this.accessibilityManager = accessibilityManager;
|
||||||
this.#enablePermissions = enablePermissions === true;
|
this.#enablePermissions = enablePermissions === true;
|
||||||
|
this.#onAppend = onAppend;
|
||||||
/**
|
|
||||||
* Callback used to attach the textLayer to the DOM.
|
|
||||||
* @type {function}
|
|
||||||
*/
|
|
||||||
this.onAppend = null;
|
|
||||||
|
|
||||||
this.div = document.createElement("div");
|
this.div = document.createElement("div");
|
||||||
|
this.div.tabIndex = 0;
|
||||||
this.div.className = "textLayer";
|
this.div.className = "textLayer";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class TextLayerBuilder {
|
||||||
this.#rotation = rotation;
|
this.#rotation = rotation;
|
||||||
// Ensure that the textLayer is appended to the DOM *before* handling
|
// Ensure that the textLayer is appended to the DOM *before* handling
|
||||||
// e.g. a pending search operation.
|
// e.g. a pending search operation.
|
||||||
this.onAppend(this.div);
|
this.#onAppend?.(this.div);
|
||||||
this.highlighter?.enable();
|
this.highlighter?.enable();
|
||||||
this.accessibilityManager?.enable();
|
this.accessibilityManager?.enable();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue