Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Refactor mechanism for saving files. #2588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 27, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 97 additions & 136 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from six.moves import xrange

import os
import sys
import warnings
import time
import io
Expand All @@ -56,17 +57,76 @@
from matplotlib.path import Path
from matplotlib.cbook import mplDeprecation

try:
from importlib import import_module
except:
# simple python 2.6 implementation (no relative imports)
def import_module(name):
__import__(name)
return sys.modules[name]

try:
from PIL import Image
_has_pil = True
except ImportError:
_has_pil = False

_backend_d = {}

_default_filetypes = {
'ps': 'Postscript',
'eps': 'Encapsulated Postscript',
'pdf': 'Portable Document Format',
'pgf': 'PGF code for LaTeX',
'png': 'Portable Network Graphics',
'raw': 'Raw RGBA bitmap',
'rgba': 'Raw RGBA bitmap',
'svg': 'Scalable Vector Graphics',
'svgz': 'Scalable Vector Graphics'
}


_default_backends = {
'ps': 'matplotlib.backends.backend_ps',
'eps': 'matplotlib.backends.backend_ps',
'pdf': 'matplotlib.backends.backend_pdf',
'pgf': 'matplotlib.backends.backend_pgf',
'png': 'matplotlib.backends.backend_agg',
'raw': 'matplotlib.backends.backend_agg',
'rgba': 'matplotlib.backends.backend_agg',
'svg': 'matplotlib.backends.backend_svg',
'svgz': 'matplotlib.backends.backend_svg',
}


def register_backend(format, backend, description):
"""
Register a backend for saving to a given file format.

*format*
File extention

*backend*
Backend for handling file output (module string or canvas class)

*description*
Description of the file type
"""
_default_backends[format] = backend
_default_filetypes[format] = description


def register_backend(format, backend_class):
_backend_d[format] = backend_class
def get_registered_canvas_class(format):
"""
Return the registered default canvas for given file format.
Handles deferred import of required backend.
"""
if format not in _default_backends:
return None
backend_class = _default_backends[format]
if cbook.is_string_like(backend_class):
backend_class = import_module(backend_class).FigureCanvas
_default_backends[format] = backend_class
return backend_class


class ShowBase(object):
Expand Down Expand Up @@ -1566,6 +1626,20 @@ class FigureCanvasBase(object):

supports_blit = True

filetypes = _default_filetypes
if _has_pil:
# JPEG support
register_backend('jpg', 'matplotlib.backends.backend_agg',
'Joint Photographic Experts Group')
register_backend('jpeg', 'matplotlib.backends.backend_agg',
'Joint Photographic Experts Group')

# TIFF support
register_backend('tif', 'matplotlib.backends.backend_agg',
'Tagged Image File Format')
register_backend('tiff', 'matplotlib.backends.backend_agg',
'Tagged Image File Format')

def __init__(self, figure):
figure.set_canvas(self)
self.figure = figure
Expand Down Expand Up @@ -1927,119 +2001,6 @@ def get_width_height(self):
"""
return int(self.figure.bbox.width), int(self.figure.bbox.height)

filetypes = {
'eps': 'Encapsulated Postscript',
'pdf': 'Portable Document Format',
'pgf': 'LaTeX PGF Figure',
'png': 'Portable Network Graphics',
'ps': 'Postscript',
'raw': 'Raw RGBA bitmap',
'rgba': 'Raw RGBA bitmap',
'svg': 'Scalable Vector Graphics',
'svgz': 'Scalable Vector Graphics'}

# All of these print_* functions do a lazy import because
# a) otherwise we'd have cyclical imports, since all of these
# classes inherit from FigureCanvasBase
# b) so we don't import a bunch of stuff the user may never use

# TODO: these print_* throw ImportErrror when called from
# compare_images_decorator (decorators.py line 112)
# if the backend has not already been loaded earlier on. Simple trigger:
# >>> import matplotlib.tests.test_spines
# >>> list(matplotlib.tests.test_spines.test_spines_axes_positions())[0][0]()

def print_eps(self, *args, **kwargs):
from .backends.backend_ps import FigureCanvasPS # lazy import
ps = self.switch_backends(FigureCanvasPS)
return ps.print_eps(*args, **kwargs)

def print_pdf(self, *args, **kwargs):
from .backends.backend_pdf import FigureCanvasPdf # lazy import
pdf = self.switch_backends(FigureCanvasPdf)
return pdf.print_pdf(*args, **kwargs)

def print_pgf(self, *args, **kwargs):
from .backends.backend_pgf import FigureCanvasPgf # lazy import
pgf = self.switch_backends(FigureCanvasPgf)
return pgf.print_pgf(*args, **kwargs)

def print_png(self, *args, **kwargs):
from .backends.backend_agg import FigureCanvasAgg # lazy import
agg = self.switch_backends(FigureCanvasAgg)
return agg.print_png(*args, **kwargs)

def print_ps(self, *args, **kwargs):
from .backends.backend_ps import FigureCanvasPS # lazy import
ps = self.switch_backends(FigureCanvasPS)
return ps.print_ps(*args, **kwargs)

def print_raw(self, *args, **kwargs):
from .backends.backend_agg import FigureCanvasAgg # lazy import
agg = self.switch_backends(FigureCanvasAgg)
return agg.print_raw(*args, **kwargs)
print_bmp = print_rgba = print_raw

def print_svg(self, *args, **kwargs):
from .backends.backend_svg import FigureCanvasSVG # lazy import
svg = self.switch_backends(FigureCanvasSVG)
return svg.print_svg(*args, **kwargs)

def print_svgz(self, *args, **kwargs):
from .backends.backend_svg import FigureCanvasSVG # lazy import
svg = self.switch_backends(FigureCanvasSVG)
return svg.print_svgz(*args, **kwargs)

if _has_pil:
filetypes['jpg'] = 'Joint Photographic Experts Group'
filetypes['jpeg'] = filetypes['jpg']

def print_jpg(self, filename_or_obj, *args, **kwargs):
"""
Supported kwargs:

*quality*: The image quality, on a scale from 1 (worst) to
95 (best). The default is 95, if not given in the
matplotlibrc file in the savefig.jpeg_quality parameter.
Values above 95 should be avoided; 100 completely
disables the JPEG quantization stage.

*optimize*: If present, indicates that the encoder should
make an extra pass over the image in order to select
optimal encoder settings.

*progressive*: If present, indicates that this image
should be stored as a progressive JPEG file.
"""
from .backends.backend_agg import FigureCanvasAgg # lazy import
agg = self.switch_backends(FigureCanvasAgg)
buf, size = agg.print_to_buffer()
if kwargs.pop("dryrun", False):
return
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
options = cbook.restrict_dict(kwargs, ['quality', 'optimize',
'progressive'])

if 'quality' not in options:
options['quality'] = rcParams['savefig.jpeg_quality']

return image.save(filename_or_obj, format='jpeg', **options)
print_jpeg = print_jpg

filetypes['tif'] = filetypes['tiff'] = 'Tagged Image File Format'

def print_tif(self, filename_or_obj, *args, **kwargs):
from .backends.backend_agg import FigureCanvasAgg # lazy import
agg = self.switch_backends(FigureCanvasAgg)
buf, size = agg.print_to_buffer()
if kwargs.pop("dryrun", False):
return
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
dpi = (self.figure.dpi, self.figure.dpi)
return image.save(filename_or_obj, format='tiff',
dpi=dpi)
print_tiff = print_tif

@classmethod
def get_supported_filetypes(cls):
"""Return dict of savefig file formats supported by this backend"""
Expand All @@ -2057,29 +2018,27 @@ def get_supported_filetypes_grouped(cls):
groupings[name].sort()
return groupings

def _get_print_method(self, format):
def _get_output_canvas(self, format):
"""Return a canvas that is suitable for saving figures to a specified
file format. If necessary, this function will switch to a registered
backend that supports the format.
"""
method_name = 'print_%s' % format

# check for registered backends
if format in _backend_d:
backend_class = _backend_d[format]

def _print_method(*args, **kwargs):
backend = self.switch_backends(backend_class)
print_method = getattr(backend, method_name)
return print_method(*args, **kwargs)

return _print_method
# check if this canvas supports the requested format
if hasattr(self, method_name):
return self

formats = self.get_supported_filetypes()
if (format not in formats or not hasattr(self, method_name)):
formats = sorted(formats)
raise ValueError(
'Format "%s" is not supported.\n'
'Supported formats: '
'%s.' % (format, ', '.join(formats)))
# check if there is a default canvas for the requested format
canvas_class = get_registered_canvas_class(format)
if canvas_class:
return self.switch_backends(canvas_class)

return getattr(self, method_name)
# else report error for unsupported format
formats = sorted(self.get_supported_filetypes())
raise ValueError('Format "%s" is not supported.\n'
'Supported formats: '
'%s.' % (format, ', '.join(formats)))

def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
orientation='portrait', format=None, **kwargs):
Expand Down Expand Up @@ -2136,7 +2095,9 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
filename = filename.rstrip('.') + '.' + format
format = format.lower()

print_method = self._get_print_method(format)
# get canvas object and print method for format
canvas = self._get_output_canvas(format)
print_method = getattr(canvas, 'print_%s' % format)

if dpi is None:
dpi = rcParams['savefig.dpi']
Expand Down
59 changes: 55 additions & 4 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* linewidth
* lines, rectangles, ellipses
* clipping to a rectangle
* output to RGBA and PNG
* output to RGBA and PNG, optionally JPEG and TIFF
* alpha blending
* DPI scaling properly - everything scales properly (dashes, linewidths, etc)
* draw polygon
Expand All @@ -30,7 +30,7 @@
from matplotlib import verbose, rcParams
from matplotlib.backend_bases import RendererBase,\
FigureManagerBase, FigureCanvasBase
from matplotlib.cbook import is_string_like, maxdict
from matplotlib.cbook import is_string_like, maxdict, restrict_dict
from matplotlib.figure import Figure
from matplotlib.font_manager import findfont
from matplotlib.ft2font import FT2Font, LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING, \
Expand All @@ -42,6 +42,12 @@
from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg
from matplotlib import _png

try:
from PIL import Image
_has_pil = True
except ImportError:
_has_pil = False

backend_version = 'v2.2'

def get_hinting_flag():
Expand Down Expand Up @@ -456,8 +462,6 @@ def draw(self):
finally:
RendererAgg.lock.release()



def get_renderer(self, cleared=False):
l, b, w, h = self.figure.bbox.bounds
key = w, h, self.figure.dpi
Expand Down Expand Up @@ -533,3 +537,50 @@ def print_to_buffer(self):
(int(renderer.width), int(renderer.height)))
renderer.dpi = original_dpi
return result

if _has_pil:

# add JPEG support
def print_jpg(self, filename_or_obj, *args, **kwargs):
"""
Supported kwargs:

*quality*: The image quality, on a scale from 1 (worst) to
95 (best). The default is 95, if not given in the
matplotlibrc file in the savefig.jpeg_quality parameter.
Values above 95 should be avoided; 100 completely
disables the JPEG quantization stage.

*optimize*: If present, indicates that the encoder should
make an extra pass over the image in order to select
optimal encoder settings.

*progressive*: If present, indicates that this image
should be stored as a progressive JPEG file.
"""
buf, size = self.print_to_buffer()
if kwargs.pop("dryrun", False):
return
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
options = restrict_dict(kwargs, ['quality', 'optimize',
'progressive'])

if 'quality' not in options:
options['quality'] = rcParams['savefig.jpeg_quality']

return image.save(filename_or_obj, format='jpeg', **options)
print_jpeg = print_jpg

# add TIFF support
def print_tif(self, filename_or_obj, *args, **kwargs):
buf, size = self.print_to_buffer()
if kwargs.pop("dryrun", False):
return
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
dpi = (self.figure.dpi, self.figure.dpi)
return image.save(filename_or_obj, format='tiff',
dpi=dpi)
print_tiff = print_tif


FigureCanvas = FigureCanvasAgg
3 changes: 3 additions & 0 deletions lib/matplotlib/backends/backend_cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,3 +514,6 @@ def _save (self, fo, format, **kwargs):

ctx.show_page()
surface.finish()


FigureCanvas = FigureCanvasCairo
Loading