1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-23 08:38:06 +02:00

Merge pull request #16977 from calixteman/add_alt_text

[Editor] Allow the user to add and save an alt-text for images (bug 1844952)
This commit is contained in:
calixteman 2023-09-19 21:59:59 +02:00 committed by GitHub
commit 54f6264d5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 316 additions and 8 deletions

View file

@ -34,8 +34,14 @@ import { FeatureTest, shadow, unreachable } from "../../shared/util.js";
* Base class for editors.
*/
class AnnotationEditor {
#altText = "";
#altTextDecorative = false;
#altTextButton = null;
#altTextAriaDescription = null;
#keepAspectRatio = false;
#resizersDiv = null;
@ -810,7 +816,7 @@ class AnnotationEditor {
if (this.#altTextButton) {
return;
}
const altText = (this.#altTextButton = document.createElement("span"));
const altText = (this.#altTextButton = document.createElement("button"));
altText.className = "altText";
AnnotationEditor._l10nPromise.get("alt_text_button_label").then(msg => {
altText.textContent = msg;
@ -820,14 +826,17 @@ class AnnotationEditor {
"click",
event => {
event.preventDefault();
this._uiManager.editAltText(this);
},
{ capture: true }
);
altText.addEventListener("keydown", event => {
if (event.target === altText && event.key === "Enter") {
event.preventDefault();
this._uiManager.editAltText(this);
}
});
this.#setAltTextButtonState();
this.div.append(altText);
if (!AnnotationEditor.SMALL_EDITOR_SIZE) {
// We take the width of the alt text button and we add 40% to it to be
@ -840,6 +849,55 @@ class AnnotationEditor {
}
}
#setAltTextButtonState() {
const button = this.#altTextButton;
if (!button) {
return;
}
// TODO: remove the aria-describedby once the tooltip stuff is implemented:
// the tooltip willl contain a span with the description, hence we could use
// it.
if (this.#altTextDecorative) {
button.classList.add("done");
button.title = "";
if (this.#altTextAriaDescription) {
button.removeAttribute("aria-describedby");
this.#altTextAriaDescription.remove();
this.#altTextAriaDescription = null;
}
} else if (this.#altText) {
button.classList.add("done");
button.title = this.#altText;
let description = this.#altTextAriaDescription;
if (!description) {
this.#altTextAriaDescription = description =
document.createElement("span");
description.className = "description";
const id = (description.id = `${this.id}-alt-text`);
button.append(description);
button.setAttribute("aria-describedby", id);
}
description.innerText = this.#altText;
}
}
getClientDimensions() {
return this.div.getBoundingClientRect();
}
get altTextData() {
return {
altText: this.#altText,
decorative: this.#altTextDecorative,
};
}
set altTextData({ altText, decorative }) {
this.#altText = altText;
this.#altTextDecorative = decorative;
this.#setAltTextButtonState();
}
/**
* Render this editor in a div.
* @returns {HTMLDivElement}

View file

@ -484,7 +484,7 @@ class StampEditor extends AnnotationEditor {
return null;
}
const editor = super.deserialize(data, parent, uiManager);
const { rect, bitmapUrl, bitmapId, isSvg } = data;
const { rect, bitmapUrl, bitmapId, isSvg, accessibilityData } = data;
if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) {
editor.#bitmapId = bitmapId;
} else {
@ -496,6 +496,10 @@ class StampEditor extends AnnotationEditor {
editor.width = (rect[2] - rect[0]) / parentWidth;
editor.height = (rect[3] - rect[1]) / parentHeight;
if (accessibilityData) {
editor.altTextData = accessibilityData;
}
return editor;
}
@ -520,9 +524,15 @@ class StampEditor extends AnnotationEditor {
// of this annotation and the clipboard doesn't support ImageBitmaps,
// hence we serialize the bitmap to a data url.
serialized.bitmapUrl = this.#serializeBitmap(/* toUrl = */ true);
serialized.accessibilityData = this.altTextData;
return serialized;
}
const { decorative, altText } = this.altTextData;
if (!decorative && altText) {
serialized.accessibilityData = { type: "Figure", alt: altText };
}
if (context === null) {
return serialized;
}

View file

@ -525,6 +525,8 @@ class AnnotationEditorUIManager {
#allLayers = new Map();
#altTextManager = null;
#annotationStorage = null;
#commandManager = new CommandManager();
@ -693,9 +695,17 @@ class AnnotationEditorUIManager {
);
}
constructor(container, viewer, eventBus, pdfDocument, pageColors) {
constructor(
container,
viewer,
altTextManager,
eventBus,
pdfDocument,
pageColors
) {
this.#container = container;
this.#viewer = viewer;
this.#altTextManager = altTextManager;
this.#eventBus = eventBus;
this.#eventBus._on("editingaction", this.#boundOnEditingAction);
this.#eventBus._on("pagechanging", this.#boundOnPageChanging);
@ -711,7 +721,7 @@ class AnnotationEditorUIManager {
}
destroy() {
this.#removeKeyboardManager();
this.removeKeyboardManager();
this.#removeFocusManager();
this.#eventBus._off("editingaction", this.#boundOnEditingAction);
this.#eventBus._off("pagechanging", this.#boundOnPageChanging);
@ -726,6 +736,7 @@ class AnnotationEditorUIManager {
this.#activeEditor = null;
this.#selectedEditors.clear();
this.#commandManager.destroy();
this.#altTextManager.destroy();
}
get hcmFilter() {
@ -749,6 +760,10 @@ class AnnotationEditorUIManager {
);
}
editAltText(editor) {
this.#altTextManager?.editAltText(this, editor);
}
onPageChanging({ pageNumber }) {
this.#currentPageIndex = pageNumber - 1;
}
@ -860,13 +875,13 @@ class AnnotationEditorUIManager {
lastActiveElement.focus();
}
#addKeyboardManager() {
addKeyboardManager() {
// The keyboard events are caught at the container level in order to be able
// to execute some callbacks even if the current page doesn't have focus.
window.addEventListener("keydown", this.#boundKeydown, { capture: true });
}
#removeKeyboardManager() {
removeKeyboardManager() {
window.removeEventListener("keydown", this.#boundKeydown, {
capture: true,
});
@ -1039,7 +1054,7 @@ class AnnotationEditorUIManager {
setEditingState(isEditing) {
if (isEditing) {
this.#addFocusManager();
this.#addKeyboardManager();
this.addKeyboardManager();
this.#addCopyPasteListeners();
this.#dispatchUpdateStates({
isEditing: this.#mode !== AnnotationEditorType.NONE,
@ -1050,7 +1065,7 @@ class AnnotationEditorUIManager {
});
} else {
this.#removeFocusManager();
this.#removeKeyboardManager();
this.removeKeyboardManager();
this.#removeCopyPasteListeners();
this.#dispatchUpdateStates({
isEditing: false,