8000 Merge pull request #13207 from anntzer/png_pil_kwargs · matplotlib/matplotlib@b1e6ac6 · GitHub
[go: up one dir, main page]

Skip to content

Commit b1e6ac6

Browse files
authored
Merge pull request #13207 from anntzer/png_pil_kwargs
Allow saving PNGs through Pillow instead of the builtin _png module.
2 parents 7f4b044 + 9c7f926 commit b1e6ac6

File tree

4 files changed

+57
-15
lines changed

4 files changed

+57
-15
lines changed

doc/users/next_whats_new/2018-01-03-AL.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@
44
Matplotlib uses Pillow to handle saving to the JPEG and TIFF formats. The
55
`~Figure.savefig()` function gained a *pil_kwargs* keyword argument, which can
66
be used to forward arguments to Pillow's `PIL.Image.save()`.
7+
8+
The *pil_kwargs* argument can also be used when saving to PNG. In that case,
9+
Matplotlib also uses Pillow's `PIL.Image.save()` instead of going through its
10+
own builtin PNG support.

lib/matplotlib/backends/backend_agg.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,9 @@ def print_raw(self, filename_or_obj, *args, **kwargs):
453453

454454
print_rgba = print_raw
455455

456-
def print_png(self, filename_or_obj, *args, **kwargs):
456+
def print_png(self, filename_or_obj, *args,
457+
metadata=None, pil_kwargs=None,
458+
**kwargs):
457459
"""
458460
Write the figure to a PNG file.
459461
@@ -494,21 +496,45 @@ def print_png(self, filename_or_obj, *args, **kwargs):
494496
.. _PNG specification: \
495497
https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords
496498
499+
pil_kwargs : dict, optional
500+
If set to a non-None value, use Pillow to save the figure instead
501+
of Matplotlib's builtin PNG support, and pass these keyword
502+
arguments to `PIL.Image.save`.
503+
504+
If the 'pnginfo' key is present, it completely overrides
505+
*metadata*, including the default 'Software' key.
497506
"""
498-
FigureCanvasAgg.draw(self)
499-
renderer = self.get_renderer()
500507

501-
version_str = (
502-
'matplotlib version ' + __version__ + ', http://matplotlib.org/')
503-
metadata = OrderedDict({'Software': version_str})
504-
user_metadata = kwargs.pop("metadata", None)
505-
if user_metadata is not None:
506-
metadata.update(user_metadata)
508+
if metadata is None:
509+
metadata = {}
510+
metadata = {
511+
"Software":
512+
f"matplotlib version{__version__}, http://matplotlib.org/",
513+
**metadata,
514+
}
507515

508-
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
509-
cbook.open_file_cm(filename_or_obj, "wb") as fh:
510-
_png.write_png(renderer._renderer, fh,
511-
self.figure.dpi, metadata=metadata)
516+
if pil_kwargs is not None:
517+
from PIL import Image
518+
from PIL.PngImagePlugin import PngInfo
519+
buf, size = self.print_to_buffer()
520+
# Only use the metadata kwarg if pnginfo is not set, because the
521+
# semantics of duplicate keys in pnginfo is unclear.
522+
if "pnginfo" not in pil_kwargs:
523+
pnginfo = PngInfo()
524+
for k, v in metadata.items():
525+
pnginfo.add_text(k, v)
526+
pil_kwargs["pnginfo"] = pnginfo
527+
pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
528+
(Image.frombuffer("RGBA", size, buf, "raw", "RGBA", 0, 1)
529+
.save(filename_or_obj, format="png", **pil_kwargs))
530+
531+
else:
532+
FigureCanvasAgg.draw(self)
533+
renderer = self.get_renderer()
534+
with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
535+
cbook.open_file_cm(filename_or_obj, "wb") as fh:
536+
_png.write_png(renderer._renderer, fh,
537+
self.figure.dpi, metadata=metadata)
512538

513539
def print_to_buffer(self):
514540
FigureCanvasAgg.draw(self)

lib/matplotlib/figure.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2106,7 +2106,8 @@ def savefig(self, fname, *, frameon=None, transparent=None, **kwargs):
21062106
pil_kwargs : dict, optional
21072107
Additional keyword arguments that are passed to `PIL.Image.save`
21082108
when saving the figure. Only applicable for formats that are saved
2109-
using Pillow, i.e. JPEG and TIFF.
2109+
using Pillow, i.e. JPEG, TIFF, and (if the keyword is set to a
2110+
non-None value) PNG.
21102111
"""
21112112

21122113
kwargs.setdefault('dpi', rcParams['savefig.dpi'])

lib/matplotlib/tests/test_agg.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,18 @@ def test_jpeg_dpi():
241241
assert im.info['dpi'] == (200, 200)
242242

243243

244-
def test_pil_kwargs():
244+
def test_pil_kwargs_png():
245+
Image = pytest.importorskip("PIL.Image")
246+
from PIL.PngImagePlugin import PngInfo
247+
buf = io.BytesIO()
248+
pnginfo = PngInfo()
249+
pnginfo.add_text("Software", "test")
250+
plt.figure().savefig(buf, format="png", pil_kwargs={"pnginfo": pnginfo})
251+
im = Image.open(buf)
252+
assert im.info["Software"] == "test"
253+
254+
255+
def test_pil_kwargs_tiff():
245256
Image = pytest.importorskip("PIL.Image")
246257
from PIL.TiffTags import TAGS_V2 as TAGS
247258
buf = io.BytesIO()

0 commit comments

Comments
 (0)
0