From: <lee...@us...> - 2009-01-17 10:28:52
|
Revision: 6804 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6804&view=rev Author: leejjoon Date: 2009-01-17 10:28:48 +0000 (Sat, 17 Jan 2009) Log Message: ----------- bbox_inches option for savefig Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/figure.py trunk/matplotlib/lib/matplotlib/transforms.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-17 05:47:42 UTC (rev 6803) +++ trunk/matplotlib/CHANGELOG 2009-01-17 10:28:48 UTC (rev 6804) @@ -1,3 +1,6 @@ +2009-01-16 Implement bbox_inches option for savefig. If bbox_inches is + "tight", try to determine the tight bounding box. - JJL + 2009-01-16 Fix bug in is_string_like so it doesn't raise an unnecessary exception. - EF Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-01-17 05:47:42 UTC (rev 6803) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-01-17 10:28:48 UTC (rev 6804) @@ -33,6 +33,9 @@ import matplotlib.path as path from matplotlib import rcParams +from matplotlib.transforms import Bbox, TransformedBbox, Affine2D +import cStringIO + class RendererBase: """An abstract base class to handle drawing/rendering operations. @@ -1419,6 +1422,28 @@ self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) + bbox_inches = kwargs.pop("bbox_inches", None) + + if bbox_inches: + # call adjust_bbox to save only the given area + if bbox_inches == "tight": + # save the figure to estimate the bounding box + result = getattr(self, method_name)( + cStringIO.StringIO(), + dpi=dpi, + facecolor=facecolor, + edgecolor=edgecolor, + orientation=orientation, + **kwargs) + renderer = self.figure._cachedRenderer + bbox_inches = self.figure.get_tightbbox(renderer) + pad = kwargs.pop("pad_inches", 0.1) + bbox_inches = bbox_inches.padded(pad) + + restore_bbox = self._adjust_bbox(self.figure, format, + bbox_inches) + + try: result = getattr(self, method_name)( filename, @@ -1428,6 +1453,9 @@ orientation=orientation, **kwargs) finally: + if bbox_inches and restore_bbox: + restore_bbox() + self.figure.dpi = origDPI self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) @@ -1435,6 +1463,91 @@ #self.figure.canvas.draw() ## seems superfluous return result + + def _adjust_bbox(self, fig, format, bbox_inches): + """ + Temporarily adjust the figure so that only the specified area + (bbox_inches) is saved. + + It modifies fig.bbox, fig.bbox_inches, + fig.transFigure._boxout, and fig.patch. While the figure size + changes, the scale of the original figure is conserved. A + function whitch restores the original values are returned. + """ + + origBbox = fig.bbox + origBboxInches = fig.bbox_inches + _boxout = fig.transFigure._boxout + + def restore_bbox(): + fig.bbox = origBbox + fig.bbox_inches = origBboxInches + fig.transFigure._boxout = _boxout + fig.transFigure.invalidate() + fig.patch.set_bounds(0, 0, 1, 1) + + if format in ["png", "raw", "rgba"]: + self._adjust_bbox_png(fig, bbox_inches) + return restore_bbox + elif format in ["pdf", "eps"]: + self._adjust_bbox_pdf(fig, bbox_inches) + return restore_bbox + else: + warnings.warn("bbox_inches option for %s backend is not implemented yet." % (format)) + return None + + + def _adjust_bbox_png(self, fig, bbox_inches): + """ + _adjust_bbox for png (Agg) format + """ + + tr = fig.dpi_scale_trans + + _bbox = TransformedBbox(bbox_inches, + tr) + x0, y0 = _bbox.x0, _bbox.y0 + fig.bbox_inches = Bbox.from_bounds(0, 0, + bbox_inches.width, + bbox_inches.height) + + x0, y0 = _bbox.x0, _bbox.y0 + w1, h1 = fig.bbox.width, fig.bbox.height + self.figure.transFigure._boxout = Bbox.from_bounds(-x0, -y0, + w1, h1) + self.figure.transFigure.invalidate() + + fig.bbox = TransformedBbox(fig.bbox_inches, tr) + + fig.patch.set_bounds(x0/w1, y0/h1, + fig.bbox.width/w1, fig.bbox.height/h1) + + + def _adjust_bbox_pdf(self, fig, bbox_inches): + """ + _adjust_bbox for pdf & eps format + """ + + tr = Affine2D().scale(72) + + _bbox = TransformedBbox(bbox_inches, tr) + + fig.bbox_inches = Bbox.from_bounds(0, 0, + bbox_inches.width, + bbox_inches.height) + x0, y0 = _bbox.x0, _bbox.y0 + f = 72. / fig.dpi + w1, h1 = fig.bbox.width*f, fig.bbox.height*f + self.figure.transFigure._boxout = Bbox.from_bounds(-x0, -y0, + w1, h1) + self.figure.transFigure.invalidate() + + fig.bbox = TransformedBbox(fig.bbox_inches, tr) + + fig.patch.set_bounds(x0/w1, y0/h1, + fig.bbox.width/w1, fig.bbox.height/h1) + + def get_default_filetype(self): raise NotImplementedError Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2009-01-17 05:47:42 UTC (rev 6803) +++ trunk/matplotlib/lib/matplotlib/figure.py 2009-01-17 10:28:48 UTC (rev 6804) @@ -33,6 +33,7 @@ import matplotlib.cbook as cbook + class SubplotParams: """ A class to hold the parameters for a subplot @@ -971,6 +972,16 @@ a plot on top of a colored background on a web page. The transparency of these patches will be restored to their original values upon exit of this function. + + *bbox_inches*: + Bbox in inches. Only the given portion of the figure is + saved. If 'tight', try to figure out the tight bbox of + the figure. + + *pad_inches*: + Amount of padding around the figure when bbox_inches is + 'tight'. + """ for key in ('dpi', 'facecolor', 'edgecolor'): @@ -1091,6 +1102,41 @@ return blocking_input(timeout=timeout) + + def get_tightbbox(self, renderer): + """ + Return a (tight) bounding box of the figure in inches. + + It only accounts axes title, axis labels, and axis + ticklabels. Needs improvement. + """ + + artists = [] + bb = [] + for ax in self.axes: + + artists.append(ax.xaxis.label) + artists.append(ax.yaxis.label) + artists.append(ax.title) + artists.append(ax) + + bbx1, bbx2 = ax.xaxis.get_ticklabel_extents(renderer) + bby1, bby2 = ax.yaxis.get_ticklabel_extents(renderer) + bb.extend([bbx1, bbx2, bby1, bby2]) + + + bb.extend([c.get_window_extent(renderer) for c in artists \ + if c.get_visible()]) + + _bbox = Bbox.union([b for b in bb if b.width!=0 or b.height!=0]) + + bbox_inches = TransformedBbox(_bbox, + Affine2D().scale(1./self.dpi)) + + return bbox_inches + + + def figaspect(arg): """ Create a figure with specified aspect ratio. If *arg* is a number, Modified: trunk/matplotlib/lib/matplotlib/transforms.py =================================================================== --- trunk/matplotlib/lib/matplotlib/transforms.py 2009-01-17 05:47:42 UTC (rev 6803) +++ trunk/matplotlib/lib/matplotlib/transforms.py 2009-01-17 10:28:48 UTC (rev 6804) @@ -635,7 +635,7 @@ Return a new :class:`Bbox` that is padded on all four sides by the given value. """ - points = self._points + points = self.get_points() return Bbox(points + [[-p, -p], [p, p]]) def translated(self, tx, ty): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |