Skip to content

Commit

Permalink
Merge pull request #21557 from anntzer/pdfpngtransparency
Browse files Browse the repository at this point in the history
Fix transparency when exporting to png via pgf backend.
  • Loading branch information
jklymak committed Dec 16, 2021
2 parents 951f752 + b47867c commit 98115e9
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 22 deletions.
14 changes: 11 additions & 3 deletions lib/matplotlib/__init__.py
Expand Up @@ -302,8 +302,8 @@ def _get_executable_info(name):
----------
name : str
The executable to query. The following values are currently supported:
"dvipng", "gs", "inkscape", "magick", "pdftops". This list is subject
to change without notice.
"dvipng", "gs", "inkscape", "magick", "pdftocairo", "pdftops". This
list is subject to change without notice.
Returns
-------
Expand All @@ -315,7 +315,10 @@ def _get_executable_info(name):
------
ExecutableNotFoundError
If the executable is not found or older than the oldest version
supported by Matplotlib.
supported by Matplotlib. For debugging purposes, it is also
possible to "hide" an executable from Matplotlib by adding it to the
:envvar:`_MPLHIDEEXECUTABLES` environment variable (a comma-separated
list), which must be set prior to any calls to this function.
ValueError
If the executable is not one that we know how to query.
"""
Expand Down Expand Up @@ -351,6 +354,9 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False):
f"Failed to determine the version of {args[0]} from "
f"{' '.join(args)}, which output {output}")

if name in os.environ.get("_MPLHIDEEXECUTABLES", "").split(","):
raise ExecutableNotFoundError(f"{name} was hidden")

if name == "dvipng":
return impl(["dvipng", "-version"], "(?m)^dvipng(?: .*)? (.+)", "1.6")
elif name == "gs":
Expand Down Expand Up @@ -407,6 +413,8 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False):
raise ExecutableNotFoundError(
f"You have ImageMagick {info.version}, which is unsupported")
return info
elif name == "pdftocairo":
return impl(["pdftocairo", "-v"], "pdftocairo version (.*)")
elif name == "pdftops":
info = impl(["pdftops", "-v"], "^pdftops version (.*)",
ignore_exit_code=True)
Expand Down
32 changes: 17 additions & 15 deletions lib/matplotlib/backends/backend_pgf.py
Expand Up @@ -168,26 +168,28 @@ def _metadata_to_str(key, value):

def make_pdf_to_png_converter():
"""Return a function that converts a pdf file to a png file."""
if shutil.which("pdftocairo"):
def cairo_convert(pdffile, pngfile, dpi):
cmd = ["pdftocairo", "-singlefile", "-png", "-r", "%d" % dpi,
pdffile, os.path.splitext(pngfile)[0]]
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
return cairo_convert
try:
mpl._get_executable_info("pdftocairo")
except mpl.ExecutableNotFoundError:
pass
else:
return lambda pdffile, pngfile, dpi: subprocess.check_output(
["pdftocairo", "-singlefile", "-transp", "-png", "-r", "%d" % dpi,
pdffile, os.path.splitext(pngfile)[0]],
stderr=subprocess.STDOUT)
try:
gs_info = mpl._get_executable_info("gs")
except mpl.ExecutableNotFoundError:
pass
else:
def gs_convert(pdffile, pngfile, dpi):
cmd = [gs_info.executable,
'-dQUIET', '-dSAFER', '-dBATCH', '-dNOPAUSE', '-dNOPROMPT',
'-dUseCIEColor', '-dTextAlphaBits=4',
'-dGraphicsAlphaBits=4', '-dDOINTERPOLATE',
'-sDEVICE=png16m', '-sOutputFile=%s' % pngfile,
'-r%d' % dpi, pdffile]
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
return gs_convert
return lambda pdffile, pngfile, dpi: subprocess.check_output(
[gs_info.executable,
'-dQUIET', '-dSAFER', '-dBATCH', '-dNOPAUSE', '-dNOPROMPT',
'-dUseCIEColor', '-dTextAlphaBits=4',
'-dGraphicsAlphaBits=4', '-dDOINTERPOLATE',
'-sDEVICE=pngalpha', '-sOutputFile=%s' % pngfile,
'-r%d' % dpi, pdffile],
stderr=subprocess.STDOUT)
raise RuntimeError("No suitable pdf to png renderer found.")


Expand Down
10 changes: 6 additions & 4 deletions lib/matplotlib/tests/test_backend_pgf.py
Expand Up @@ -312,10 +312,12 @@ def test_bbox_inches_tight():

@needs_xelatex
@needs_ghostscript
def test_png():
# Just a smoketest.
fig, ax = plt.subplots()
fig.savefig(BytesIO(), format="png", backend="pgf")
def test_png_transparency(): # Actually, also just testing that png works.
buf = BytesIO()
plt.figure().savefig(buf, format="png", backend="pgf", transparent=True)
buf.seek(0)
t = plt.imread(buf)
assert (t[..., 3] == 0).all() # fully transparent.


@needs_xelatex
Expand Down

0 comments on commit 98115e9

Please sign in to comment.