When you set bbox_inches = 'tight' in Matplotlib's savefig() function, it tries to find the tightest bounding box that encapsulates all the content in your figure window. Unfortunately, the tightest bounding box appears to include invisible axes.
For example, here is a snippet where setting bbox_inches = 'tight' works as desired:
import matplotlib.pylab as plt fig = plt.figure(figsize = (5,5)) data_ax = fig.add_axes([0.2, 0.2, 0.6, 0.6]) data_ax.plot([1,2], [1,2]) plt.savefig('Test1.pdf', bbox_inches = 'tight', pad_inches = 0)
which produces:
The bounds of the saved pdf correspond to the bounds of the content. This is great, except that I like to use a set of invisible figure axes to place annotations in. If the invisible axes extend beyond the bounds of the visible content, then the pdf bounds are larger than the visible content. For example:
import matplotlib.pylab as plt fig = plt.figure(figsize = (5,5)) fig_ax = fig.add_axes([0, 0, 1, 1], frame_on = False) fig_ax.xaxis.set_visible(False) fig_ax.yaxis.set_visible(False) data_ax = fig.add_axes([0.2, 0.2, 0.6, 0.6]) data_ax.plot([1,2], [1,2]) plt.savefig('Test2.pdf', bbox_inches = 'tight', pad_inches = 0)
producing
How can I force savefig() to ignore invisible items in the figure window? The only solution I have come up with is to calculate the bounding box myself and explicitly specify the bbox to savefig().
In case it matters, I am running Matplotlib 1.2.1 under Python 2.7.3 on Mac OS X 10.8.5.
The relevant function (called by canvas.print_figure
which is called by figure.savefig
to generate the bounding box) in backend_bases.py
:
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. """ bb = [] for ax in self.axes: if ax.get_visible(): bb.append(ax.get_tightbbox(renderer)) _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
The only consideration that goes into deciding if an axes is 'visible' is if ax.get_visible()
returns true, even if you have no visible (either artist.get_visible() == False
or simple transparent) artists in the axes.
The bounding box behavior you observe is the correct behavior.
tcaswell, thanks for your help. My original question was, "How can I force savefig() to ignore invisible items in the figure window?" When I put fig_ax.set_visible(False)
then savefig()
ignores the invisible axes. Unfortunately, when I set fig_ax.set_visible(False)
then any artist placed in fig_ax is also invisible. I am back to the original plot I posted, where fig_ax
does not exist.
As you intimated in your comment, tcaswell, I think the proper solution is avoid creating fig_ax
. I am currently working on placing my annotations and data axis labels in the default figure object fig
. It's a bit annoying since fig
uses normalized figure units instead of mm units, but I can deal with it.