From c6f7e722c95615bd0871a47cb6dd9e844f183421 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 7 Sep 2023 18:02:55 +0200 Subject: [PATCH] [Editor] Add the parent tag id (if any) to the serialized editors (bug 1845087) The tag id will be useful in order to update the StructTree when saving the pdf. --- src/display/editor/annotation_editor_layer.js | 2 +- src/display/editor/editor.js | 1 + src/display/editor/freetext.js | 1 + src/display/editor/ink.js | 1 + src/display/editor/stamp.js | 1 + test/integration/freetext_editor_spec.js | 49 +++++++++++++++++++ web/text_accessibility.js | 22 ++++++--- 7 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index 88516c38f4152..fc724cdc51214 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -409,7 +409,7 @@ class AnnotationEditorLayer { }, 0); } - this.#accessibilityManager?.moveElementInDOM( + editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM( this.div, editor.div, editor.contentDiv, diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 1fe4bc56c78fa..6b9732a2b7493 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -80,6 +80,7 @@ class AnnotationEditor { this.annotationElementId = null; this._willKeepAspectRatio = false; this._initialOptions.isCentered = parameters.isCentered; + this._structTreeParentId = null; const { rotation, diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 7e96d518ced10..3c973ff8ec4f2 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -735,6 +735,7 @@ class FreeTextEditor extends AnnotationEditor { pageIndex: this.pageIndex, rect, rotation: this.rotation, + structTreeParentId: this._structTreeParentId, }; if (isForCopying) { diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index f4858eabbc10c..811db67430092 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -1199,6 +1199,7 @@ class InkEditor extends AnnotationEditor { pageIndex: this.pageIndex, rect, rotation: this.rotation, + structTreeParentId: this._structTreeParentId, }; } } diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index 1d6cd7ea432fe..61cb48fece04c 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -506,6 +506,7 @@ class StampEditor extends AnnotationEditor { rect: this.getRect(0, 0), rotation: this.rotation, isSvg: this.#isSvg, + structTreeParentId: this._structTreeParentId, }; if (isForCopying) { diff --git a/test/integration/freetext_editor_spec.js b/test/integration/freetext_editor_spec.js index bfa05e7882324..bfb615fff7832 100644 --- a/test/integration/freetext_editor_spec.js +++ b/test/integration/freetext_editor_spec.js @@ -2429,4 +2429,53 @@ describe("FreeText Editor", () => { ); }); }); + + describe("FreeText accessibility", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("bug1823296.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that the parent structTree id is correct", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await page.click("#editorFreeText"); + + const parentId = "p3R_mc8"; + const rect = await page.evaluate(id => { + const parent = document.getElementById(id); + let span = null; + for (const child of parent.childNodes) { + if (child.innerText === "000.[5]") { + span = child; + break; + } + } + const { x, y, width, height } = span.getBoundingClientRect(); + return { x, y, width, height }; + }, parentId); + await page.mouse.click( + rect.x + rect.width + 5, + rect.y + rect.height / 2 + ); + await page.waitForTimeout(10); + await page.type(`${getEditorSelector(0)} .internal`, "Hello Wolrd"); + + // Commit. + await page.keyboard.press("Escape"); + await page.waitForTimeout(10); + + await waitForStorageEntries(page, 1); + + const id = await getFirstSerialized(page, x => x.structTreeParentId); + expect(id).withContext(`In ${browserName}`).toEqual(parentId); + }) + ); + }); + }); }); diff --git a/web/text_accessibility.js b/web/text_accessibility.js index 19741c4bf37cb..e9178baad9579 100644 --- a/web/text_accessibility.js +++ b/web/text_accessibility.js @@ -179,17 +179,18 @@ class TextAccessibilityManager { * in order to correctly position this editor in the text flow. * @param {HTMLElement} element * @param {boolean} isRemovable + * @returns {string|null} The id in the struct tree if any. */ addPointerInTextLayer(element, isRemovable) { const { id } = element; if (!id) { - return; + return null; } if (!this.#enabled) { // The text layer needs to be there, so we postpone the association. this.#waitingElements.set(element, isRemovable); - return; + return null; } if (isRemovable) { @@ -198,7 +199,7 @@ class TextAccessibilityManager { const children = this.#textChildren; if (!children || children.length === 0) { - return; + return null; } const index = binarySearchFirstItem( @@ -208,20 +209,25 @@ class TextAccessibilityManager { ); const nodeIndex = Math.max(0, index - 1); - this.#addIdToAriaOwns(id, children[nodeIndex]); + const child = children[nodeIndex]; + this.#addIdToAriaOwns(id, child); this.#textNodes.set(id, nodeIndex); + + const parent = child.parentNode; + return parent?.classList.contains("markedContent") ? parent.id : null; } /** * Move a div in the DOM in order to respect the visual order. * @param {HTMLDivElement} element + * @returns {string|null} The id in the struct tree if any. */ moveElementInDOM(container, element, contentElement, isRemovable) { - this.addPointerInTextLayer(contentElement, isRemovable); + const id = this.addPointerInTextLayer(contentElement, isRemovable); if (!container.hasChildNodes()) { container.append(element); - return; + return id; } const children = Array.from(container.childNodes).filter( @@ -229,7 +235,7 @@ class TextAccessibilityManager { ); if (children.length === 0) { - return; + return id; } const elementToCompare = contentElement || element; @@ -247,6 +253,8 @@ class TextAccessibilityManager { } else { children[index - 1].after(element); } + + return id; } }