diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 0fe0fc40c00b..47892aacd425 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -441,7 +441,9 @@ def buffer_rgba(self): """ return self.renderer.buffer_rgba() - def print_raw(self, filename_or_obj): + def print_raw(self, filename_or_obj, *, metadata=None): + if metadata is not None: + raise ValueError("metadata not supported for raw/rgba") FigureCanvasAgg.draw(self) renderer = self.get_renderer() with cbook.open_file_cm(filename_or_obj, "wb") as fh: @@ -518,22 +520,22 @@ def print_to_buffer(self): # print_figure(), and the latter ensures that `self.figure.dpi` already # matches the dpi kwarg (if any). - def print_jpg(self, filename_or_obj, *, pil_kwargs=None): + def print_jpg(self, filename_or_obj, *, metadata=None, pil_kwargs=None): # savefig() has already applied savefig.facecolor; we now set it to # white to make imsave() blend semi-transparent figures against an # assumed white background. with mpl.rc_context({"savefig.facecolor": "white"}): - self._print_pil(filename_or_obj, "jpeg", pil_kwargs) + self._print_pil(filename_or_obj, "jpeg", pil_kwargs, metadata) print_jpeg = print_jpg - def print_tif(self, filename_or_obj, *, pil_kwargs=None): - self._print_pil(filename_or_obj, "tiff", pil_kwargs) + def print_tif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): + self._print_pil(filename_or_obj, "tiff", pil_kwargs, metadata) print_tiff = print_tif - def print_webp(self, filename_or_obj, *, pil_kwargs=None): - self._print_pil(filename_or_obj, "webp", pil_kwargs) + def print_webp(self, filename_or_obj, *, metadata=None, pil_kwargs=None): + self._print_pil(filename_or_obj, "webp", pil_kwargs, metadata) print_jpg.__doc__, print_tif.__doc__, print_webp.__doc__ = map( """ diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index d2568fddbf06..00df76d344a2 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -3259,6 +3259,11 @@ def savefig(self, fname, *, transparent=None, **kwargs): `~.FigureCanvasSVG.print_svg`. - 'eps' and 'ps' with PS backend: Only 'Creator' is supported. + Not supported for 'pgf', 'raw', and 'rgba' as those formats do not support + embedding metadata. + Does not currently support 'jpg', 'tiff', or 'webp', but may include + embedding EXIF metadata in the future. + bbox_inches : str or `.Bbox`, default: :rc:`savefig.bbox` Bounding box in inches: only the given portion of the figure is saved. If 'tight', try to figure out the tight bbox of the figure. diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 495f131a1b24..373197aa0aea 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1610,6 +1610,7 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, Metadata in the image file. The supported keys depend on the output format, see the documentation of the respective backends for more information. + Currently only supported for "png", "pdf", "ps", "eps", and "svg". pil_kwargs : dict, optional Keyword arguments passed to `PIL.Image.Image.save`. If the 'pnginfo' key is present, it completely overrides *metadata*, including the @@ -1674,6 +1675,8 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, for k, v in metadata.items(): if v is not None: pnginfo.add_text(k, v) + elif metadata is not None: + raise ValueError(f"metadata not supported for format {format!r}") if format in ["jpg", "jpeg"]: format = "jpeg" # Pillow doesn't recognize "jpg". facecolor = mpl.rcParams["savefig.facecolor"] diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 80d80f969163..f51ce72937a2 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -1548,3 +1548,14 @@ def test_gridspec_no_mutate_input(): plt.subplots(1, 2, width_ratios=[1, 2], gridspec_kw=gs) assert gs == gs_orig plt.subplot_mosaic('AB', width_ratios=[1, 2], gridspec_kw=gs) + + +@pytest.mark.parametrize('fmt', ['eps', 'pdf', 'png', 'ps', 'svg', 'svgz']) +def test_savefig_metadata(fmt): + Figure().savefig(io.BytesIO(), format=fmt, metadata={}) + + +@pytest.mark.parametrize('fmt', ['jpeg', 'jpg', 'tif', 'tiff', 'webp', "raw", "rgba"]) +def test_savefig_metadata_error(fmt): + with pytest.raises(ValueError, match="metadata not supported"): + Figure().savefig(io.BytesIO(), format=fmt, metadata={})