@@ -177,6 +186,7 @@ function Toolbar(props) {
title={intl.formatMessage({ id: 'pdfReader.highlightText' })}
disabled={props.readOnly}
onClick={() => handleToolClick('highlight')}
+ aria-description={_constructAriaDecription(1)}
>
{ (platform !== 'web' || ['epub', 'snapshot'].includes(props.type)) && (
)}
{props.type === 'pdf' && platform !== 'web' && (
)}
{props.type === 'pdf' && (
@@ -212,6 +225,7 @@ function Toolbar(props) {
title={intl.formatMessage({ id: 'pdfReader.selectArea' })}
disabled={props.readOnly}
onClick={() => handleToolClick('image')}
+ aria-description={_constructAriaDecription(5)}
>
)}
{props.type === 'pdf' && (
@@ -221,6 +235,7 @@ function Toolbar(props) {
title={intl.formatMessage({ id: 'pdfReader.draw' })}
disabled={props.readOnly}
onClick={() => handleToolClick('ink')}
+ aria-description={intl.formatMessage({ id: 'pdfReader.a11yAnnotationNotSupported' })}
>
)}
diff --git a/src/common/components/view-popup/find-popup.js b/src/common/components/view-popup/find-popup.js
index 22368bd3..ff969581 100644
--- a/src/common/components/view-popup/find-popup.js
+++ b/src/common/components/view-popup/find-popup.js
@@ -7,9 +7,9 @@ import { DEBOUNCE_FIND_POPUP_INPUT } from '../../defines';
import IconChevronUp from '../../../../res/icons/20/chevron-up.svg';
import IconChevronDown from '../../../../res/icons/20/chevron-down.svg';
import IconClose from '../../../../res/icons/20/x.svg';
-import { getCodeCombination, getKeyCombination } from '../../lib/utilities';
+import { getCodeCombination, getKeyCombination, isMac } from '../../lib/utilities';
-function FindPopup({ params, onChange, onFindNext, onFindPrevious, onAddAnnotation }) {
+function FindPopup({ params, onChange, onFindNext, onFindPrevious, onAddAnnotation, tools }) {
const intl = useIntl();
const inputRef = useRef();
const preventInputRef = useRef(false);
@@ -75,13 +75,17 @@ function FindPopup({ params, onChange, onFindNext, onFindPrevious, onAddAnnotati
else if (code === 'Ctrl-Alt-Digit1') {
preventInputRef.current = true;
if (params.result?.annotation) {
- onAddAnnotation({ ...params.result.annotation, type: 'highlight' }, true);
+ onAddAnnotation({ ...params.result.annotation, type: 'highlight', color: tools['highlight'].color }, true);
+ // Close popup after adding annotation
+ onChange({ ...params, popupOpen: false, active: false, result: null });
}
}
else if (code === 'Ctrl-Alt-Digit2') {
preventInputRef.current = true;
if (params.result?.annotation) {
- onAddAnnotation({ ...params.result.annotation, type: 'underline' }, true);
+ onAddAnnotation({ ...params.result.annotation, type: 'underline', color: tools['underline'].color }, true);
+ // Close popup after adding annotation
+ onChange({ ...params, popupOpen: false, active: false, result: null });
}
}
}
@@ -112,6 +116,11 @@ function FindPopup({ params, onChange, onFindNext, onFindPrevious, onAddAnnotati
title={intl.formatMessage({ id: 'pdfReader.find' })}
className="toolbar-text-input"
placeholder="Find in document…"
+ aria-description={
+ intl.formatMessage({ id: 'pdfReader.a11yTextualAnnotationFindInDocumentInstruction' })
+ + ` ${intl.formatMessage({ id: 'pdfReader.a11yAnnotationModifierControl' })} - ${intl.formatMessage({ id: `pdfReader.a11yAnnotationModifier${isMac() ? 'Mac' : ''}` })} - ${1}`
+ + `, ${intl.formatMessage({ id: 'pdfReader.a11yAnnotationModifierControl' })} - ${intl.formatMessage({ id: `pdfReader.a11yAnnotationModifier${isMac() ? 'Mac' : ''}` })} - ${2}`
+ }
value={query !== null ? query : params.query}
tabIndex="-1"
data-tabstop={1}
diff --git a/src/common/reader.js b/src/common/reader.js
index cfc3bdfd..0a0eda0b 100644
--- a/src/common/reader.js
+++ b/src/common/reader.js
@@ -271,6 +271,10 @@ class Reader {
onResizeSplitView={this.setSplitViewSize.bind(this)}
onAddAnnotation={(annotation, select) => {
annotation = this._annotationManager.addAnnotation(annotation);
+ // Tell screen readers the annotation was added after focus is settled
+ setTimeout(() => {
+ this.setA11yMessage(this._getString(`pdfReader.a11yAnnotationCreated.${annotation.type}`));
+ }, 100);
if (select) {
this.setSelectedAnnotations([annotation.id]);
} else {
@@ -307,6 +311,7 @@ class Reader {
onToggleContextPane={this._onToggleContextPane}
onChangeTextSelectionAnnotationMode={this.setTextSelectionAnnotationMode.bind(this)}
ref={this._readerRef}
+ tools={this._tools}
/>
@@ -781,6 +786,10 @@ class Reader {
let onAddAnnotation = (annotation, select) => {
annotation = this._annotationManager.addAnnotation(annotation);
+ // Tell screen readers the annotation was added after focus is settled
+ setTimeout(() => {
+ this.setA11yMessage(this._getString(`pdfReader.a11yAnnotationCreated.${annotation.type}`));
+ }, 100);
if (select) {
this.setSelectedAnnotations([annotation.id], true);
}
@@ -866,6 +875,10 @@ class Reader {
let onFocusAnnotation = (annotation) => {
if (!annotation) return;
// Announce the current annotation to screen readers
+ if (annotation.type == 'external-link') {
+ this.setA11yMessage(annotation.url);
+ return;
+ }
let annotationType = this._getString(`pdfReader.${annotation.type}Annotation`);
let annotationContent = `${annotationType}. ${annotation.text || annotation.comment}`;
this.setA11yMessage(annotationContent);
@@ -1178,10 +1191,11 @@ class Reader {
this._updateState({ selectedAnnotationIDs: ids });
// Don't navigate to annotation or focus comment if opening a context menu
+ // unless it is a note (so that one can type after creating it via shortcut, same as with text annotation)
if (!triggeringEvent || triggeringEvent.button !== 2) {
if (triggeredFromView) {
if (['note', 'highlight', 'underline'].includes(annotation.type)
- && !annotation.comment && (!triggeringEvent || !('key' in triggeringEvent))) {
+ && !annotation.comment && (!triggeringEvent || annotation.type === 'note')) {
this._enableAnnotationDeletionFromComment = true;
setTimeout(() => {
let content;
@@ -1199,6 +1213,32 @@ class Reader {
this._lastView.navigate({ annotationID: annotation.id });
}
}
+ // After a small delay for focus to settle, announce to screen readers that annotation
+ // is selected and how one can manipulate it
+ setTimeout(() => {
+ let a11yAnnouncement = this._getString(`pdfReader.a11yAnnotationSelected.${annotation.type}`);
+ if (document.querySelector('.annotation-popup')) {
+ // add note that popup is opened
+ a11yAnnouncement += ' ' + this._getString('pdfReader.a11yAnnotationPopupAppeared');
+ }
+ if (['highlight', 'underline'].includes(annotation.type)) {
+ // tell how to edit highlight/underline annotations
+ a11yAnnouncement += ' ' + this._getString('pdfReader.a11yEditTextAnnotation') + ' ' + this._getString(`pdfReader.a11yAnnotationModifier${isMac() ? 'Mac' : ''}`);
+ }
+ else if (['note', 'text', 'image'].includes(annotation.type)) {
+ // tell how to move and resize remaining types
+ a11yAnnouncement += ' ' + this._getString('pdfReader.a11yMoveAnnotation');
+ if (['text', 'image'].includes(annotation.type)) {
+ a11yAnnouncement += ' ' + this._getString('pdfReader.a11yResizeAnnotation');
+ }
+ }
+
+ // only announce if the content view is focused. E.g. if comment in
+ // sidebar has focus, say nothing as it will not be relevant
+ if (document.activeElement.nodeName === 'IFRAME') {
+ this.setA11yMessage(a11yAnnouncement);
+ }
+ }, 100);
}
}
// Smoothly scroll to the annotation, if only one was selected
diff --git a/src/en-us.strings.js b/src/en-us.strings.js
index 507f3dc7..b0ef361a 100644
--- a/src/en-us.strings.js
+++ b/src/en-us.strings.js
@@ -190,5 +190,28 @@ export default {
'pdfReader.convertToHighlight': 'Convert to Highlight',
'pdfReader.convertToUnderline': 'Convert to Underline',
'pdfReader.size': 'Size',
- 'pdfReader.merge': 'Merge'
+ 'pdfReader.merge': 'Merge',
+ 'pdfReader.a11yAnnotationModifierMac': 'Option',
+ 'pdfReader.a11yAnnotationModifier': 'Alt',
+ 'pdfReader.a11yAnnotationModifierControl': 'Control',
+ 'pdfReader.a11yTextualAnnotationFindInDocumentInstruction': 'To turn a search result into a highlight or underline annotation, press',
+ 'pdfReader.a11yTextualAnnotationInstruction': 'To annotate text via the keyboard, first use “Find in Document” to locate the phrase. Then, to turn the search result into an annotation, press Control -',
+ 'pdfReader.a11yAnnotationInstruction': 'To add this annotation to the document, focus the document and press Control -',
+ 'pdfReader.a11yAnnotationNotSupported': 'This annotation type cannot be created via the keyboard.',
+ 'pdfReader.a11yMoveAnnotation': 'Use the arrow keys to move the annotation.',
+ 'pdfReader.a11yEditTextAnnotation': 'To move the end of the text annotation, use the left/right arrow keys while holding Shift. To move the start of the annotation, use the arrow keys while holding Shift -',
+ 'pdfReader.a11yResizeAnnotation': 'To resize the annotation, use the arrow keys while holding Shift.',
+ 'pdfReader.a11yAnnotationPopupAppeared': 'Use Tab to navigate the annotation popup.',
+
+ "pdfReader.a11yAnnotationCreated.highlight": "Highlight annotation created",
+ "pdfReader.a11yAnnotationCreated.underline": "Underline annotation created",
+ "pdfReader.a11yAnnotationCreated.note": "Note annotation created",
+ "pdfReader.a11yAnnotationCreated.text": "Text annotation created",
+ "pdfReader.a11yAnnotationCreated.image": "Image annotation created",
+
+ "pdfReader.a11yAnnotationSelected.highlight": "Highlight annotation selected",
+ "pdfReader.a11yAnnotationSelected.underline": "Underline annotation selected",
+ "pdfReader.a11yAnnotationSelected.note": "Note annotation selected",
+ "pdfReader.a11yAnnotationSelected.text": "Text annotation selected",
+ "pdfReader.a11yAnnotationSelected.image": "Image annotation selected"
};