mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-26 17:07:21 +02:00
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:
commit
6612d7afaf
@ -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`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -867,3 +867,4 @@
|
|||||||
!bitmap-trailing-7fff-stripped.pdf
|
!bitmap-trailing-7fff-stripped.pdf
|
||||||
!bitmap.pdf
|
!bitmap.pdf
|
||||||
!bomb_giant.pdf
|
!bomb_giant.pdf
|
||||||
|
!bug2009627.pdf
|
||||||
|
|||||||
BIN
test/pdfs/bug2009627.pdf
Executable file
BIN
test/pdfs/bug2009627.pdf
Executable file
Binary file not shown.
@ -493,7 +493,7 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
const treeDom = await this.structTreeLayer?.render();
|
const treeDom = await this.structTreeLayer?.render();
|
||||||
if (treeDom) {
|
if (treeDom) {
|
||||||
this.l10n.pause();
|
this.l10n.pause();
|
||||||
this.structTreeLayer?.addElementsToTextLayer();
|
this.structTreeLayer?.updateTextLayer();
|
||||||
if (this.canvas && treeDom.parentNode !== this.canvas) {
|
if (this.canvas && treeDom.parentNode !== this.canvas) {
|
||||||
// Pause translation when inserting the structTree in the DOM.
|
// Pause translation when inserting the structTree in the DOM.
|
||||||
this.canvas.append(treeDom);
|
this.canvas.append(treeDom);
|
||||||
|
|||||||
@ -184,6 +184,10 @@ class StructTreeLayerBuilder {
|
|||||||
|
|
||||||
#elementsToAddToTextLayer = null;
|
#elementsToAddToTextLayer = null;
|
||||||
|
|
||||||
|
#elementsToHideInTextLayer = null;
|
||||||
|
|
||||||
|
#elementsToStealFromTextLayer = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {StructTreeLayerBuilderOptions} options
|
* @param {StructTreeLayerBuilderOptions} options
|
||||||
*/
|
*/
|
||||||
@ -304,15 +308,49 @@ class StructTreeLayerBuilder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
addElementsToTextLayer() {
|
updateTextLayer() {
|
||||||
if (!this.#elementsToAddToTextLayer) {
|
if (this.#elementsToAddToTextLayer) {
|
||||||
return;
|
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) {
|
if (this.#elementsToHideInTextLayer) {
|
||||||
document.getElementById(id)?.append(img);
|
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) {
|
#walk(node) {
|
||||||
@ -325,21 +363,13 @@ class StructTreeLayerBuilder {
|
|||||||
const { role } = node;
|
const { role } = node;
|
||||||
if (MathMLElements.has(role)) {
|
if (MathMLElements.has(role)) {
|
||||||
element = document.createElementNS(MathMLNamespace, role);
|
element = document.createElementNS(MathMLNamespace, role);
|
||||||
let text = "";
|
const ids = [];
|
||||||
|
(this.#elementsToStealFromTextLayer ||= []).push(element, ids);
|
||||||
for (const { type, id } of node.children || []) {
|
for (const { type, id } of node.children || []) {
|
||||||
if (type !== "content" || !id) {
|
if (type === "content" && id) {
|
||||||
continue;
|
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 {
|
} else {
|
||||||
element = document.createElement("span");
|
element = document.createElement("span");
|
||||||
}
|
}
|
||||||
@ -365,10 +395,7 @@ class StructTreeLayerBuilder {
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const elem = document.getElementById(id);
|
(this.#elementsToHideInTextLayer ||= []).push(id);
|
||||||
if (elem) {
|
|
||||||
elem.ariaHidden = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// For now, we don't want to keep the alt text if there's valid
|
// 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).
|
// MathML (see https://github.com/w3c/mathml-aam/issues/37).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user