From 16397274f9d66acc38bbf65662f2d701748e49a0 Mon Sep 17 00:00:00 2001 From: tonyfast Date: Sat, 9 Oct 2021 18:17:59 -0700 Subject: [PATCH 1/5] Add wai-aria property to the artist accessibility annotations. --- lib/matplotlib/artist.py | 30 +++++++++++++++++++++++++++++ lib/matplotlib/tests/test_artist.py | 7 +++++++ 2 files changed, 37 insertions(+) diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 7967f597d483..916774aa60f4 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -192,6 +192,7 @@ def __init__(self): self._clippath = None self._clipon = True self._label = '' + self._aria = None self._picker = None self._rasterized = False self._agg_filter = None @@ -1085,6 +1086,35 @@ def set_in_layout(self, in_layout): """ self._in_layout = in_layout + def get_aria(self): + """Return any ARIA properties assigned to the artist""" + return dict(self._aria) + + def set_aria(self, aria): + """ + Set ARIA properties to the artist. + + A primary use of this method is to attach aria-label to the artist to + provide alt text to figures. + + Parameters + ---------- + aria : dict + + """ + # TODO validation + if not isinstance(aria, dict): + if aria is not None: + raise TypeError( + f'aria must be dict or None, not {type(aria)}') + self._aria = aria + + def update_aria(self, **aria): + """Update WAI-ARIA properties on the artist.""" + # TODO validation + for k, v in aria.items(): + self._aria[k] = v + def get_label(self): """Return the label used for this artist in the legend.""" return self._label diff --git a/lib/matplotlib/tests/test_artist.py b/lib/matplotlib/tests/test_artist.py index 9bfb4ebce1bd..10bd7752f68e 100644 --- a/lib/matplotlib/tests/test_artist.py +++ b/lib/matplotlib/tests/test_artist.py @@ -562,3 +562,10 @@ def draw(self, renderer, extra): assert 'aardvark' == art.draw(renderer, 'aardvark') assert 'aardvark' == art.draw(renderer, extra='aardvark') + + +def test_set_aria(): + art = martist.Artist() + with pytest.raises(TypeError, match='^aria must be dict'): + art.set_aria([]) + art.set_aria(dict(label="artist alt text")) From 3ee25a03a92e1d57306fb115f7770b079f14075d Mon Sep 17 00:00:00 2001 From: Tony Fast Date: Mon, 11 Oct 2021 17:17:22 -0700 Subject: [PATCH 2/5] Update lib/matplotlib/artist.py Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/artist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 916774aa60f4..84372547f542 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -192,7 +192,7 @@ def __init__(self): self._clippath = None self._clipon = True self._label = '' - self._aria = None + self._aria = {} self._picker = None self._rasterized = False self._agg_filter = None From 3d570ba4799c5c11999fd9f0d39dd7a1541a69ed Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 28 Oct 2022 16:02:21 -0400 Subject: [PATCH 3/5] ENH: add aria-label to svg output if available --- lib/matplotlib/backends/backend_svg.py | 12 +++++++++--- lib/matplotlib/tests/test_backend_svg.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 03b376a69894..1a5190083e2a 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -317,7 +317,7 @@ def _check_is_iterable_of_str(infos, key): class RendererSVG(RendererBase): def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, - *, metadata=None): + *, metadata=None, aria=None): self.width = width self.height = height self.writer = XMLWriter(svgwriter) @@ -337,6 +337,7 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, self._hatchd = {} self._has_gouraud = False self._n_gradients = 0 + self._aria = dict(aria or {}) super().__init__() self._glyph_map = dict() @@ -350,7 +351,9 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, viewBox='0 0 %s %s' % (str_width, str_height), xmlns="http://www.w3.org/2000/svg", version="1.1", - attrib={'xmlns:xlink': "http://www.w3.org/1999/xlink"}) + attrib={'xmlns:xlink': "http://www.w3.org/1999/xlink"}, + **{k: self._aria[k] for k in ['aria-label'] if k in self._aria} + ) self._write_metadata(metadata) self._write_default_style() @@ -1373,9 +1376,12 @@ def print_svg(self, filename, *, bbox_inches_restore=None, metadata=None): self.figure.dpi = 72 width, height = self.figure.get_size_inches() w, h = width * 72, height * 72 + aria = self.figure.get_aria() renderer = MixedModeRenderer( self.figure, width, height, dpi, - RendererSVG(w, h, fh, image_dpi=dpi, metadata=metadata), + RendererSVG( + w, h, fh, image_dpi=dpi, metadata=metadata, aria=aria + ), bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) renderer.finalize() diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py index 78383904c4fd..7c9dd162518e 100644 --- a/lib/matplotlib/tests/test_backend_svg.py +++ b/lib/matplotlib/tests/test_backend_svg.py @@ -609,3 +609,20 @@ def test_svg_font_string(font_str, include_generic): assert font_info == f"{size}px {font_str}" assert text_count == len(ax.texts) + + +def test_aria(): + fig, ax = plt.subplots() + + with BytesIO() as fd: + fig.savefig(fd, format="svg") + buf = fd.getvalue() + + assert b'aria-label' not in buf + + fig.set_aria({'aria-label': 'A test of inserting the label'}) + with BytesIO() as fd: + fig.savefig(fd, format="svg") + buf = fd.getvalue() + + assert b'aria-label' in buf From 2c57f4b0e2c5c4627ac9b87c6e108932f570588f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 28 Oct 2022 16:17:36 -0400 Subject: [PATCH 4/5] DOC: add whats new for aria roles + svg usage --- doc/users/next_whats_new/aria.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 doc/users/next_whats_new/aria.rst diff --git a/doc/users/next_whats_new/aria.rst b/doc/users/next_whats_new/aria.rst new file mode 100644 index 000000000000..dbd3234d05bc --- /dev/null +++ b/doc/users/next_whats_new/aria.rst @@ -0,0 +1,23 @@ +All ``Arist`` now carry wai-aria data +------------------------------------- + +It is now possible to attach `wai-aria +`__ role +information to any `~matplotlib.artist.Aritist`. These roles are industry the +industry standard for providing accessibility mark up on the web. This +information can be used by downstream applications for providing accessible +descriptions of visualizations. Best practices in the space are still +developing, but by providing a mechanism to store and access this information +we will enable this development. + +There are three methods provided: + +- `~matplotlib.artist.Aritist.set_aria` which will completely replace any existing roles. +- `~matplotlib.artist.Aritist.update_aria` which will update the current roles in-place. +- `~matplotlib.artist.Aritist.get_aria` which will return a copy of the current roles. + +We currently do no validation on either the keys or the values. + + +Matplotlib will use the `'aria-label'` role when saving svg output if it is +provided. From 87032e3a63dc6d2eeb2eaadfd65ec1e7beae9dbe Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 28 Oct 2022 16:27:52 -0400 Subject: [PATCH 5/5] DOC: fix wording, spelling, and docs build Co-authored-by: Kevin Anderson --- doc/api/artist_api.rst | 15 +++++++++++++++ doc/users/next_whats_new/aria.rst | 10 +++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index b99842372bbe..6bba98028147 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -186,6 +186,21 @@ Miscellaneous Artist.get_in_layout Artist.stale + +Accessibility +------------- + + +.. autosummary:: + :template: autosummary.rst + :toctree: _as_gen + :nosignatures: + + Artist.get_aria + Artist.set_aria + Artist.update_aria + + Functions ========= diff --git a/doc/users/next_whats_new/aria.rst b/doc/users/next_whats_new/aria.rst index dbd3234d05bc..7dde632d00dd 100644 --- a/doc/users/next_whats_new/aria.rst +++ b/doc/users/next_whats_new/aria.rst @@ -3,7 +3,7 @@ All ``Arist`` now carry wai-aria data It is now possible to attach `wai-aria `__ role -information to any `~matplotlib.artist.Aritist`. These roles are industry the +information to any `~matplotlib.artist.Artist`. These roles are the industry standard for providing accessibility mark up on the web. This information can be used by downstream applications for providing accessible descriptions of visualizations. Best practices in the space are still @@ -12,12 +12,12 @@ we will enable this development. There are three methods provided: -- `~matplotlib.artist.Aritist.set_aria` which will completely replace any existing roles. -- `~matplotlib.artist.Aritist.update_aria` which will update the current roles in-place. -- `~matplotlib.artist.Aritist.get_aria` which will return a copy of the current roles. +- `~matplotlib.artist.Artist.set_aria` which will completely replace any existing roles. +- `~matplotlib.artist.Artist.update_aria` which will update the current roles in-place. +- `~matplotlib.artist.Artist.get_aria` which will return a copy of the current roles. We currently do no validation on either the keys or the values. -Matplotlib will use the `'aria-label'` role when saving svg output if it is +Matplotlib will use the ``'aria-label'`` role when saving svg output if it is provided.