mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-23 08:38:06 +02:00
[Editor] Allow the user to add and save an alt-text for images (bug 1844952)
This commit is contained in:
parent
daae6589b6
commit
c12049db07
8 changed files with 316 additions and 8 deletions
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue