Merge pull request #20055 from calixteman/bug1975264
[Editor] When an annotation is added, announce it if the user is using a screen reader (bug 1975264)
This commit is contained in:
commit
ad31385792
@ -533,6 +533,14 @@ pdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor r
|
|||||||
pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.
|
pdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.
|
||||||
pdfjs-editor-alt-text-settings-close-button = Close
|
pdfjs-editor-alt-text-settings-close-button = Close
|
||||||
|
|
||||||
|
## Accessibility labels (announced by screen readers) for objects added to the editor.
|
||||||
|
|
||||||
|
pdfjs-editor-highlight-added-alert = Highlight added
|
||||||
|
pdfjs-editor-freetext-added-alert = Text added
|
||||||
|
pdfjs-editor-ink-added-alert = Drawing added
|
||||||
|
pdfjs-editor-stamp-added-alert = Image added
|
||||||
|
pdfjs-editor-signature-added-alert = Signature added
|
||||||
|
|
||||||
## "Annotations removed" bar
|
## "Annotations removed" bar
|
||||||
|
|
||||||
pdfjs-editor-undo-bar-message-highlight = Highlight removed
|
pdfjs-editor-undo-bar-message-highlight = Highlight removed
|
||||||
|
|||||||
@ -106,6 +106,9 @@ class DrawingEditor extends AnnotationEditor {
|
|||||||
#createDrawOutlines({ drawOutlines, drawId, drawingOptions }) {
|
#createDrawOutlines({ drawOutlines, drawId, drawingOptions }) {
|
||||||
this.#drawOutlines = drawOutlines;
|
this.#drawOutlines = drawOutlines;
|
||||||
this._drawingOptions ||= drawingOptions;
|
this._drawingOptions ||= drawingOptions;
|
||||||
|
if (!this.annotationElementId) {
|
||||||
|
this._uiManager.a11yAlert(`pdfjs-editor-${this.editorType}-added-alert`);
|
||||||
|
}
|
||||||
|
|
||||||
if (drawId >= 0) {
|
if (drawId >= 0) {
|
||||||
this._drawId = drawId;
|
this._drawId = drawId;
|
||||||
|
|||||||
@ -180,6 +180,7 @@ class AnnotationEditor {
|
|||||||
this._willKeepAspectRatio = false;
|
this._willKeepAspectRatio = false;
|
||||||
this._initialOptions.isCentered = parameters.isCentered;
|
this._initialOptions.isCentered = parameters.isCentered;
|
||||||
this._structTreeParentId = null;
|
this._structTreeParentId = null;
|
||||||
|
this.annotationElementId = parameters.annotationElementId || null;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
rotation,
|
rotation,
|
||||||
@ -427,6 +428,9 @@ class AnnotationEditor {
|
|||||||
* Commit the data contained in this editor.
|
* Commit the data contained in this editor.
|
||||||
*/
|
*/
|
||||||
commit() {
|
commit() {
|
||||||
|
if (!this.isInEditMode()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.addToAnnotationStorage();
|
this.addToAnnotationStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1641,6 +1645,7 @@ class AnnotationEditor {
|
|||||||
parent,
|
parent,
|
||||||
id: parent.getNextId(),
|
id: parent.getNextId(),
|
||||||
uiManager,
|
uiManager,
|
||||||
|
annotationElementId: data.annotationElementId,
|
||||||
});
|
});
|
||||||
editor.rotation = data.rotation;
|
editor.rotation = data.rotation;
|
||||||
editor.#accessibilityData = data.accessibilityData;
|
editor.#accessibilityData = data.accessibilityData;
|
||||||
|
|||||||
@ -131,6 +131,9 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
FreeTextEditor._defaultColor ||
|
FreeTextEditor._defaultColor ||
|
||||||
AnnotationEditor._defaultLineColor;
|
AnnotationEditor._defaultLineColor;
|
||||||
this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
|
this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
|
||||||
|
if (!this.annotationElementId) {
|
||||||
|
this._uiManager.a11yAlert("pdfjs-editor-freetext-added-alert");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -782,6 +785,7 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
pageIndex: pageNumber - 1,
|
pageIndex: pageNumber - 1,
|
||||||
rect: rect.slice(0),
|
rect: rect.slice(0),
|
||||||
rotation,
|
rotation,
|
||||||
|
annotationElementId: id,
|
||||||
id,
|
id,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
popupRef,
|
popupRef,
|
||||||
@ -791,7 +795,6 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
editor.#fontSize = data.fontSize;
|
editor.#fontSize = data.fontSize;
|
||||||
editor.#color = Util.makeHexColor(...data.color);
|
editor.#color = Util.makeHexColor(...data.color);
|
||||||
editor.#content = FreeTextEditor.#deserializeContent(data.value);
|
editor.#content = FreeTextEditor.#deserializeContent(data.value);
|
||||||
editor.annotationElementId = data.id || null;
|
|
||||||
editor._initialData = initialData;
|
editor._initialData = initialData;
|
||||||
|
|
||||||
return editor;
|
return editor;
|
||||||
|
|||||||
@ -126,6 +126,10 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
this.#addToDrawLayer();
|
this.#addToDrawLayer();
|
||||||
this.rotate(this.rotation);
|
this.rotate(this.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.annotationElementId) {
|
||||||
|
this._uiManager.a11yAlert("pdfjs-editor-highlight-added-alert");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@ -876,6 +880,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
pageIndex: pageNumber - 1,
|
pageIndex: pageNumber - 1,
|
||||||
rect: rect.slice(0),
|
rect: rect.slice(0),
|
||||||
rotation,
|
rotation,
|
||||||
|
annotationElementId: id,
|
||||||
id,
|
id,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
popupRef,
|
popupRef,
|
||||||
@ -904,6 +909,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
pageIndex: pageNumber - 1,
|
pageIndex: pageNumber - 1,
|
||||||
rect: rect.slice(0),
|
rect: rect.slice(0),
|
||||||
rotation,
|
rotation,
|
||||||
|
annotationElementId: id,
|
||||||
id,
|
id,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
popupRef,
|
popupRef,
|
||||||
@ -918,7 +924,6 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
if (inkLists) {
|
if (inkLists) {
|
||||||
editor.#thickness = data.thickness;
|
editor.#thickness = data.thickness;
|
||||||
}
|
}
|
||||||
editor.annotationElementId = data.id || null;
|
|
||||||
editor._initialData = initialData;
|
editor._initialData = initialData;
|
||||||
|
|
||||||
const [pageWidth, pageHeight] = editor.pageDimensions;
|
const [pageWidth, pageHeight] = editor.pageDimensions;
|
||||||
|
|||||||
@ -164,6 +164,7 @@ class InkEditor extends DrawingEditor {
|
|||||||
pageIndex: pageNumber - 1,
|
pageIndex: pageNumber - 1,
|
||||||
rect: rect.slice(0),
|
rect: rect.slice(0),
|
||||||
rotation,
|
rotation,
|
||||||
|
annotationElementId: id,
|
||||||
id,
|
id,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
popupRef,
|
popupRef,
|
||||||
@ -171,7 +172,6 @@ class InkEditor extends DrawingEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const editor = await super.deserialize(data, parent, uiManager);
|
const editor = await super.deserialize(data, parent, uiManager);
|
||||||
editor.annotationElementId = data.id || null;
|
|
||||||
editor._initialData = initialData;
|
editor._initialData = initialData;
|
||||||
|
|
||||||
return editor;
|
return editor;
|
||||||
|
|||||||
@ -484,6 +484,9 @@ class StampEditor extends AnnotationEditor {
|
|||||||
if (this.#bitmapFileName) {
|
if (this.#bitmapFileName) {
|
||||||
this.div.setAttribute("aria-description", this.#bitmapFileName);
|
this.div.setAttribute("aria-description", this.#bitmapFileName);
|
||||||
}
|
}
|
||||||
|
if (!this.annotationElementId) {
|
||||||
|
this._uiManager.a11yAlert("pdfjs-editor-stamp-added-alert");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) {
|
copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) {
|
||||||
@ -781,6 +784,7 @@ class StampEditor extends AnnotationEditor {
|
|||||||
pageIndex: pageNumber - 1,
|
pageIndex: pageNumber - 1,
|
||||||
rect: rect.slice(0),
|
rect: rect.slice(0),
|
||||||
rotation,
|
rotation,
|
||||||
|
annotationElementId: id,
|
||||||
id,
|
id,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
accessibilityData: {
|
accessibilityData: {
|
||||||
@ -812,7 +816,6 @@ class StampEditor extends AnnotationEditor {
|
|||||||
editor.width = (rect[2] - rect[0]) / parentWidth;
|
editor.width = (rect[2] - rect[0]) / parentWidth;
|
||||||
editor.height = (rect[3] - rect[1]) / parentHeight;
|
editor.height = (rect[3] - rect[1]) / parentHeight;
|
||||||
|
|
||||||
editor.annotationElementId = data.id || null;
|
|
||||||
if (accessibilityData) {
|
if (accessibilityData) {
|
||||||
editor.altTextData = accessibilityData;
|
editor.altTextData = accessibilityData;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -676,6 +676,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#viewer = null;
|
#viewer = null;
|
||||||
|
|
||||||
|
#viewerAlert = null;
|
||||||
|
|
||||||
#updateModeCapability = null;
|
#updateModeCapability = null;
|
||||||
|
|
||||||
static TRANSLATE_SMALL = 1; // page units.
|
static TRANSLATE_SMALL = 1; // page units.
|
||||||
@ -818,6 +820,7 @@ class AnnotationEditorUIManager {
|
|||||||
constructor(
|
constructor(
|
||||||
container,
|
container,
|
||||||
viewer,
|
viewer,
|
||||||
|
viewerAlert,
|
||||||
altTextManager,
|
altTextManager,
|
||||||
signatureManager,
|
signatureManager,
|
||||||
eventBus,
|
eventBus,
|
||||||
@ -834,6 +837,7 @@ class AnnotationEditorUIManager {
|
|||||||
const signal = (this._signal = this.#abortController.signal);
|
const signal = (this._signal = this.#abortController.signal);
|
||||||
this.#container = container;
|
this.#container = container;
|
||||||
this.#viewer = viewer;
|
this.#viewer = viewer;
|
||||||
|
this.#viewerAlert = viewerAlert;
|
||||||
this.#altTextManager = altTextManager;
|
this.#altTextManager = altTextManager;
|
||||||
this.#signatureManager = signatureManager;
|
this.#signatureManager = signatureManager;
|
||||||
this._eventBus = eventBus;
|
this._eventBus = eventBus;
|
||||||
@ -1175,6 +1179,19 @@ class AnnotationEditorUIManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a11yAlert(messageId, args = null) {
|
||||||
|
const viewerAlert = this.#viewerAlert;
|
||||||
|
if (!viewerAlert) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
viewerAlert.setAttribute("data-l10n-id", messageId);
|
||||||
|
if (args) {
|
||||||
|
viewerAlert.setAttribute("data-l10n-args", JSON.stringify(args));
|
||||||
|
} else {
|
||||||
|
viewerAlert.removeAttribute("data-l10n-args");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#selectionChange() {
|
#selectionChange() {
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
if (!selection || selection.isCollapsed) {
|
if (!selection || selection.isCollapsed) {
|
||||||
|
|||||||
@ -110,6 +110,9 @@ describe("FreeText Editor", () => {
|
|||||||
await waitForSelectedEditor(page, editorSelector);
|
await waitForSelectedEditor(page, editorSelector);
|
||||||
await waitForStorageEntries(page, 1);
|
await waitForStorageEntries(page, 1);
|
||||||
|
|
||||||
|
const alert = await page.$eval("#viewer-alert", el => el.textContent);
|
||||||
|
expect(alert).toEqual("Text added");
|
||||||
|
|
||||||
let content = await page.$eval(editorSelector, el =>
|
let content = await page.$eval(editorSelector, el =>
|
||||||
el.innerText.trimEnd()
|
el.innerText.trimEnd()
|
||||||
);
|
);
|
||||||
|
|||||||
@ -78,6 +78,9 @@ describe("Highlight Editor", () => {
|
|||||||
|
|
||||||
await page.waitForSelector(`${getEditorSelector(0)}`);
|
await page.waitForSelector(`${getEditorSelector(0)}`);
|
||||||
|
|
||||||
|
const alert = await page.$eval("#viewer-alert", el => el.textContent);
|
||||||
|
expect(alert).toEqual("Highlight added");
|
||||||
|
|
||||||
const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
|
const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
|
||||||
Array.from(new Array(13).keys(), n => 13 - n)
|
Array.from(new Array(13).keys(), n => 13 - n)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -84,6 +84,9 @@ describe("Ink Editor", () => {
|
|||||||
await commit(page);
|
await commit(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alert = await page.$eval("#viewer-alert", el => el.textContent);
|
||||||
|
expect(alert).toEqual("Drawing added");
|
||||||
|
|
||||||
await clearAll(page);
|
await clearAll(page);
|
||||||
|
|
||||||
await kbUndo(page);
|
await kbUndo(page);
|
||||||
|
|||||||
@ -181,6 +181,9 @@ describe("Signature Editor", () => {
|
|||||||
{ visible: true }
|
{ visible: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const alert = await page.$eval("#viewer-alert", el => el.textContent);
|
||||||
|
expect(alert).toEqual("Signature added");
|
||||||
|
|
||||||
// Check the tooltip.
|
// Check the tooltip.
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
`.altText.editDescription[title="Hello World"]`
|
`.altText.editDescription[title="Hello World"]`
|
||||||
|
|||||||
@ -125,6 +125,9 @@ describe("Stamp Editor", () => {
|
|||||||
const editorSelector = getEditorSelector(0);
|
const editorSelector = getEditorSelector(0);
|
||||||
await waitForImage(page, editorSelector);
|
await waitForImage(page, editorSelector);
|
||||||
|
|
||||||
|
const alert = await page.$eval("#viewer-alert", el => el.textContent);
|
||||||
|
expect(alert).toEqual("Image added");
|
||||||
|
|
||||||
const { width } = await getEditorDimensions(page, editorSelector);
|
const { width } = await getEditorDimensions(page, editorSelector);
|
||||||
|
|
||||||
// The image is bigger than the page, so it has been scaled down to
|
// The image is bigger than the page, so it has been scaled down to
|
||||||
|
|||||||
@ -49,22 +49,6 @@
|
|||||||
--new-alt-text-warning-image: url(images/altText_warning.svg);
|
--new-alt-text-warning-image: url(images/altText_warning.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The following class is used to hide an element but keep it available to
|
|
||||||
* for screen readers. */
|
|
||||||
.visuallyHidden {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
border: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer {
|
.textLayer {
|
||||||
&.highlighting {
|
&.highlighting {
|
||||||
cursor: var(--editorFreeHighlight-editing-cursor);
|
cursor: var(--editorFreeHighlight-editing-cursor);
|
||||||
|
|||||||
@ -492,6 +492,7 @@ const PDFViewerApplication = {
|
|||||||
const pdfViewer = (this.pdfViewer = new PDFViewer({
|
const pdfViewer = (this.pdfViewer = new PDFViewer({
|
||||||
container,
|
container,
|
||||||
viewer,
|
viewer,
|
||||||
|
viewerAlert: appConfig.viewerAlert,
|
||||||
eventBus,
|
eventBus,
|
||||||
renderingQueue,
|
renderingQueue,
|
||||||
linkService,
|
linkService,
|
||||||
|
|||||||
@ -278,6 +278,8 @@ class PDFViewer {
|
|||||||
|
|
||||||
#textLayerMode = TextLayerMode.ENABLE;
|
#textLayerMode = TextLayerMode.ENABLE;
|
||||||
|
|
||||||
|
#viewerAlert = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFViewerOptions} options
|
* @param {PDFViewerOptions} options
|
||||||
*/
|
*/
|
||||||
@ -291,6 +293,7 @@ class PDFViewer {
|
|||||||
}
|
}
|
||||||
this.container = options.container;
|
this.container = options.container;
|
||||||
this.viewer = options.viewer || options.container.firstElementChild;
|
this.viewer = options.viewer || options.container.firstElementChild;
|
||||||
|
this.#viewerAlert = options.viewerAlert || null;
|
||||||
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
|
if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
|
||||||
@ -927,6 +930,7 @@ class PDFViewer {
|
|||||||
this.#annotationEditorUIManager = new AnnotationEditorUIManager(
|
this.#annotationEditorUIManager = new AnnotationEditorUIManager(
|
||||||
this.container,
|
this.container,
|
||||||
viewer,
|
viewer,
|
||||||
|
this.#viewerAlert,
|
||||||
this.#altTextManager,
|
this.#altTextManager,
|
||||||
this.#signatureManager,
|
this.#signatureManager,
|
||||||
eventBus,
|
eventBus,
|
||||||
|
|||||||
@ -229,6 +229,22 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The following class is used to hide an element but keep it available to
|
||||||
|
* for screen readers. */
|
||||||
|
.visuallyHidden {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden,
|
.hidden,
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
|||||||
@ -98,6 +98,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body tabindex="0">
|
<body tabindex="0">
|
||||||
|
<span id="viewer-alert" class="visuallyHidden" role="alert"></span>
|
||||||
<div id="outerContainer">
|
<div id="outerContainer">
|
||||||
|
|
||||||
<div id="sidebarContainer">
|
<div id="sidebarContainer">
|
||||||
|
|||||||
@ -33,6 +33,7 @@ function getViewerConfiguration() {
|
|||||||
principalContainer: document.getElementById("mainContainer"),
|
principalContainer: document.getElementById("mainContainer"),
|
||||||
mainContainer: document.getElementById("viewerContainer"),
|
mainContainer: document.getElementById("viewerContainer"),
|
||||||
viewerContainer: document.getElementById("viewer"),
|
viewerContainer: document.getElementById("viewer"),
|
||||||
|
viewerAlert: document.getElementById("viewer-alert"),
|
||||||
toolbar: {
|
toolbar: {
|
||||||
container: document.getElementById("toolbarContainer"),
|
container: document.getElementById("toolbarContainer"),
|
||||||
numPages: document.getElementById("numPages"),
|
numPages: document.getElementById("numPages"),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user