Merge pull request #20567 from calixteman/bug2009627

Hide the text in the text layer associated with MathML elements (bug 2009627)
This commit is contained in:
calixteman 2026-01-14 14:19:37 +01:00 committed by GitHub
commit 6612d7afaf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 98 additions and 24 deletions

View File

@ -498,4 +498,50 @@ describe("accessibility", () => {
);
});
});
describe("Text elements must be aria-hidden when there's MathML and annotations", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("bug2009627.pdf", ".textLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the text in text layer is aria-hidden", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const isSanitizerSupported = await page.evaluate(() => {
try {
// eslint-disable-next-line no-undef
return typeof Sanitizer !== "undefined";
} catch {
return false;
}
});
const ariaHidden = await page.evaluate(() =>
Array.from(
document.querySelectorAll(".structTree :has(> math)")
).map(el =>
document
.getElementById(el.getAttribute("aria-owns"))
.getAttribute("aria-hidden")
)
);
if (isSanitizerSupported) {
expect(ariaHidden)
.withContext(`In ${browserName}`)
.toEqual(["true", "true", "true"]);
} else {
// eslint-disable-next-line no-console
console.log(
`Pending in Chrome: Sanitizer API (in ${browserName}) is not supported`
);
}
})
);
});
});
});

View File

@ -867,3 +867,4 @@
!bitmap-trailing-7fff-stripped.pdf
!bitmap.pdf
!bomb_giant.pdf
!bug2009627.pdf

BIN
test/pdfs/bug2009627.pdf Executable file

Binary file not shown.

View File

@ -493,7 +493,7 @@ class PDFPageView extends BasePDFPageView {
const treeDom = await this.structTreeLayer?.render();
if (treeDom) {
this.l10n.pause();
this.structTreeLayer?.addElementsToTextLayer();
this.structTreeLayer?.updateTextLayer();
if (this.canvas && treeDom.parentNode !== this.canvas) {
// Pause translation when inserting the structTree in the DOM.
this.canvas.append(treeDom);

View File

@ -184,6 +184,10 @@ class StructTreeLayerBuilder {
#elementsToAddToTextLayer = null;
#elementsToHideInTextLayer = null;
#elementsToStealFromTextLayer = null;
/**
* @param {StructTreeLayerBuilderOptions} options
*/
@ -304,15 +308,49 @@ class StructTreeLayerBuilder {
return true;
}
addElementsToTextLayer() {
if (!this.#elementsToAddToTextLayer) {
return;
updateTextLayer() {
if (this.#elementsToAddToTextLayer) {
for (const [id, img] of this.#elementsToAddToTextLayer) {
document.getElementById(id)?.append(img);
}
this.#elementsToAddToTextLayer.clear();
this.#elementsToAddToTextLayer = null;
}
for (const [id, img] of this.#elementsToAddToTextLayer) {
document.getElementById(id)?.append(img);
if (this.#elementsToHideInTextLayer) {
for (const id of this.#elementsToHideInTextLayer) {
const elem = document.getElementById(id);
if (elem) {
elem.ariaHidden = true;
}
}
this.#elementsToHideInTextLayer.length = 0;
this.#elementsToHideInTextLayer = null;
}
if (this.#elementsToStealFromTextLayer) {
for (
let i = 0, ii = this.#elementsToStealFromTextLayer.length;
i < ii;
i += 2
) {
const element = this.#elementsToStealFromTextLayer[i];
const ids = this.#elementsToStealFromTextLayer[i + 1];
let textContent = "";
for (const id of ids) {
const elem = document.getElementById(id);
if (elem) {
textContent += elem.textContent.trim() || "";
// Aria-hide the element in order to avoid duplicate reading of the
// math content by screen readers.
elem.ariaHidden = "true";
}
}
if (textContent) {
element.textContent = textContent;
}
}
this.#elementsToStealFromTextLayer.length = 0;
this.#elementsToStealFromTextLayer = null;
}
this.#elementsToAddToTextLayer.clear();
this.#elementsToAddToTextLayer = null;
}
#walk(node) {
@ -325,21 +363,13 @@ class StructTreeLayerBuilder {
const { role } = node;
if (MathMLElements.has(role)) {
element = document.createElementNS(MathMLNamespace, role);
let text = "";
const ids = [];
(this.#elementsToStealFromTextLayer ||= []).push(element, ids);
for (const { type, id } of node.children || []) {
if (type !== "content" || !id) {
continue;
if (type === "content" && id) {
ids.push(id);
}
const elem = document.getElementById(id);
if (!elem) {
continue;
}
text += elem.textContent.trim() || "";
// Aria-hide the element in order to avoid duplicate reading of the
// math content by screen readers.
elem.ariaHidden = "true";
}
element.textContent = text;
} else {
element = document.createElement("span");
}
@ -365,10 +395,7 @@ class StructTreeLayerBuilder {
if (!id) {
continue;
}
const elem = document.getElementById(id);
if (elem) {
elem.ariaHidden = true;
}
(this.#elementsToHideInTextLayer ||= []).push(id);
}
// For now, we don't want to keep the alt text if there's valid
// MathML (see https://github.com/w3c/mathml-aam/issues/37).