#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Function to export a matplotlib figure object as multiple files simply while minimizing
whitespace surrounding the final image"""
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import os
import fileinput
import copy
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from PIL import Image
from PIL import PngImagePlugin
[docs]
def export_image(output_file=None, fltype=None, pad=0.03, dpi=300, custom=None, pdfpages=None, delim=None,
Metadata=True, **kwargs):
"""
This function exports a figure with the specified file name and type(s) to the specified
location. Multiple file types can be exported at once. Avoids the use of plt.tight_layout()
which can behave odd and minimizes whitespace on the edge of figures.
Note:
This function is typically called within plotting functions but can be used on its own.
Extensions are not required in the ``output_file`` argument. They will be added according to what
``fltype`` is set to. The default output file types are png and eps. However, if extensions
are provided, they will be used, provided that the argument ``fltype`` is not passed. The
``custom`` argument provides extra flexibility if the default settings of this function are
not desired. If the ``custom`` functionality is the only desired output, ``fltype`` can be set
to ``False`` to prevent additional exports.
PS and EPS files need to have their font definitions fixed so that they will be called properly
which is done automatically if they are used.
Figures can also be appended to an existing ``mpl.backends.backend_pdf.PdfPages`` object passed
with the ``pdfpages`` argument. These objects are used to created multi-page PDF documents.
Parameters:
output_file (str or list): Details the file location and file name in one parameter or a list of
files to export with or without file extensions. If not file extensions are provided,
the parameter ``fltype`` will need to be specified if its defaults are not desired
fltype (str, list, bool): The file extension or list of extensions. See plt.savefig() docs
for which file types are supported. Can set to ``False`` to prevent normal
functionality when ``custom`` settings are the only desired output.
pad (float): The amount of padding around the figure
dpi (int): The output file resolution
custom (dict): Indicates a custom dpi and file extension if an odd assortment of files are
needed
pdfpages (mpl.object): A multi-page PDF file object created by
``mpl.backends.backend_pdf.PdfPages``. If a PdfPages object is passed, the figure is
exported to it in addition to the other files if specified. Use this method to generate
PDF files with multiple pages.
delim (str): delimiter in the output_file str passes that indicates different types of files. set
to `None` to ensure filenames with spaces can be used.
kwargs: Any other permissible keyword arguments to send to plt.savefig() (e.g., )
Examples:
A simple call using the default settings exporting two images at 300 dpi:
.. code-block:: python
gs.export_image(output_file='../Figures/histplt')
# 'histplt.png' and 'histplt.eps' are exported
A call specifying only a png file and altering the dpi setting and setting the background
to transparent (via \**kwargs):
.. code-block:: python
gs.export_image(output_file='../Figures/histplt.png', dpi=250, transparent=True)
#'histplt.png' is exported in '../Figures/'
A call using only the custom argument:
.. code-block:: python
gs.export_image(output_file='../Figures/histplt', fltype=False, custom={600:'png', 200:'png'})
# 'histplt_600.png' and 'histplt_200.png' are exported in '../Figures/'
A call using a combination of arguments:
.. code-block:: python
gs.export_image(output_file='../Figures/histplt', custom={600:'jpg'})
'histplt.png' and 'histplt.eps' at 300 dip in addition to histplt_600.jpg' are exported in '../Figures/'
A call using a more complicated combination of arguments:
.. code-block:: python
gs.export_image(output_file=['../Figures/png/histplt', '../Figures/eps/histplt'], custom={600:'png'})
#'histplt.png' @ 300 dpi and 'histplt_600.png' @ 600 dpi are placed in '../Figures/png/' while 'histplt.eps' is placed in '../Figures/eps/'
Create a PDFPages matplotlib object and save the figure to it:
.. code-block:: python
:linenos:
from matplotlib.backends.backend_pdf import PdfPages
pdfpages = PdfPages('output_file.pdf')
gs.location_plot(data_file)
gs.export_image(pdfpages=pdfpages)
pdfpages.close()
"""
def exporttompl(flname, dpi, Metadata, **kwargs):
"""
Small function to pass information to matplotlib for exporting images for easier
iteration
"""
_, flext = os.path.splitext(flname)
# Set up a pdfpage automatically so we can insert metadata
if flext.lower() == '.pdf':
pdffig = PdfPages(flname)
plt.savefig(pdffig, format='pdf', bbox_inches='tight', pad_inches=pad,
dpi=dpi, **kwargs)
if Metadata:
meta_dictionary = pdffig.infodict()
for meta in Metadata:
meta_dictionary[meta] = Metadata[meta]
pdffig.close()
else:
plt.savefig(flname, bbox_inches='tight', pad_inches=pad, dpi=dpi, **kwargs)
# Correct EPS or PS font definition if needed
if flext in ['.eps', '.ps']:
# Grab a font for now
font = mpl.rcParams['font.family']
for line in fileinput.FileInput(flname, inplace=1):
# Get the font code used in the EPS or PS file
if "/FontName" in line:
dump = copy.deepcopy(line)
dump = dump.split('/')
dump = dump[2].split(' ')
font = dump[0]
line = line.replace("/b'%s'" % font, "/%s" % font)
print(line, end='')
if flext == '.png' and Metadata is not None:
im = Image.open(flname)
meta = PngImagePlugin.PngInfo()
for x in Metadata:
meta.add_text(x, Metadata[x])
im.save(flname, "png", pnginfo=meta)
# if flext.lower() is in ['.jpeg', '.jpeg']:
# --------------------------------------------------------------------------------------
# START OF MAIN FUNCTION
# --------------------------------------------------------------------------------------
# Sanity check
if not output_file and not pdfpages:
raise ValueError("One or both of `output_file` or `pdfpages` needs to be specified.")
# set up basic metadata info
if Metadata is True:
Metadata = {'Author': os.getcwd()}
if output_file:
# If input oufl is a string, convert it to a list
if delim is not None:
if isinstance(output_file, str):
if delim in output_file:
return ValueError("Output files need to be delimited by a single space not a comma")
output_file = output_file.split(delim)
# If input fltype is a string, convert it to a list
if isinstance(fltype, str):
if ',' in fltype:
return ValueError("File types need to be delimited by a single space not a comma")
fltype = fltype.split(' ')
# Get the extensions from output_file if they exist
exts = []
output_files = []
if not isinstance(output_file, list):
output_file = [output_file]
for fl in output_file:
outf, ext = os.path.splitext(fl)
if ext != '':
exts.append(ext.replace(".", ""))
output_files.append(outf)
if len(exts) > 0 and fltype is None:
fltype = exts
output_file = output_files
# Set fltype to defaults if True or None is passed and they aren't contained in output_file
if fltype is None or (isinstance(fltype, bool) and fltype):
fltype = ['png', 'eps']
# Extend the output_file if needed
if len(output_file) == 1 and fltype:
output_file = output_file * len(fltype)
# Export figures required through regular usage
if not isinstance(fltype, bool) or fltype:
for i, ext in enumerate(fltype):
flname = '%s.%s' % (output_file[i], ext)
exporttompl(flname, dpi, Metadata, **kwargs)
# Export the custom files
if custom is not None:
for cus_dpi in custom:
flname = '%s_%s.%s' % (output_file[0], cus_dpi, custom[cus_dpi])
exporttompl(flname, cus_dpi, Metadata, **kwargs)
# Export the the PDFPages object if required
if pdfpages:
# Try and use the variable fig if it exists (works for gs plotting functions as fig is set)
try:
pdfpages.savefig(fig, bbox_inches='tight', pad_inches=pad, dpi=dpi, **kwargs)
except:
# Grab the mpl figure if it isn't already set
fig = plt.gcf()
pdfpages.savefig(fig, bbox_inches='tight', pad_inches=pad, dpi=dpi, **kwargs)