2
2
Helper module for the *bbox_inches* parameter in `.Figure.savefig`.
3
3
"""
4
4
5
- import contextlib
6
-
7
- from matplotlib .cbook import _setattr_cm
8
5
from matplotlib .transforms import Bbox , TransformedBbox , Affine2D
9
6
10
7
@@ -18,55 +15,63 @@ def adjust_bbox(fig, bbox_inches, fixed_dpi=None):
18
15
changes, the scale of the original figure is conserved. A
19
16
function which restores the original values are returned.
20
17
"""
21
- def no_op_apply_aspect ( position = None ):
22
- return
23
-
24
- stack = contextlib . ExitStack ()
18
+ origBbox = fig . bbox
19
+ origBboxInches = fig . bbox_inches
20
+ orig_tight_layout = fig . get_tight_layout ()
21
+ _boxout = fig . transFigure . _boxout
25
22
26
- stack .callback (fig .set_tight_layout , fig .get_tight_layout ())
27
23
fig .set_tight_layout (False )
28
24
25
+ old_aspect = []
26
+ locator_list = []
27
+ sentinel = object ()
29
28
for ax in fig .axes :
30
- pos = ax .get_position (original = False ).frozen ()
31
-
32
- def _l (a , r , pos = pos ):
33
- return pos
34
-
35
- stack .callback (ax .set_axes_locator , ax .get_axes_locator ())
36
- ax .set_axes_locator (_l )
37
-
38
- # override the method that enforces the aspect ratio
39
- # on the Axes
40
- stack .enter_context (_setattr_cm (ax , apply_aspect = no_op_apply_aspect ))
41
-
42
- if fixed_dpi is not None :
43
- tr = Affine2D ().scale (fixed_dpi )
44
- dpi_scale = fixed_dpi / fig .dpi
45
- else :
46
- tr = Affine2D ().scale (fig .dpi )
47
- dpi_scale = 1.
29
+ locator_list .append (ax .get_axes_locator ())
30
+ current_pos = ax .get_position (original = False ).frozen ()
31
+ ax .set_axes_locator (lambda a , r , _pos = current_pos : _pos )
32
+ # override the method that enforces the aspect ratio on the Axes
33
+ if 'apply_aspect' in ax .__dict__ :
34
+ old_aspect .append (ax .apply_aspect )
35
+ else :
36
+ old_aspect .append (sentinel )
37
+ ax .apply_aspect = lambda pos = None : None
38
+
39
+ def restore_bbox ():
40
+ for ax , loc , aspect in zip (fig .axes , locator_list , old_aspect ):
41
+ ax .set_axes_locator (loc )
42
+ if aspect is sentinel :
43
+ # delete our no-op function which un-hides the original method
44
+ del ax .apply_aspect
45
+ else :
46
+ ax .apply_aspect = aspect
47
+
48
+ fig .bbox = origBbox
49
+ fig .bbox_inches = origBboxInches
50
+ fig .set_tight_layout (orig_tight_layout )
51
+ fig .transFigure ._boxout = _boxout
52
+ fig .transFigure .invalidate ()
53
+ fig .patch .set_bounds (0 , 0 , 1 , 1 )
54
+
55
+ if fixed_dpi is None :
56
+ fixed_dpi = fig .dpi
57
+ tr = Affine2D ().scale (fixed_dpi )
58
+ dpi_scale = fixed_dpi / fig .dpi
48
59
49
60
_bbox = TransformedBbox (bbox_inches , tr )
50
61
51
- stack .enter_context (
52
- _setattr_cm (fig , bbox_inches = Bbox .from_bounds (
53
- 0 , 0 , bbox_inches .width , bbox_inches .height )))
62
+ fig .bbox_inches = Bbox .from_bounds (0 , 0 ,
63
+ bbox_inches .width , bbox_inches .height )
54
64
x0 , y0 = _bbox .x0 , _bbox .y0
55
65
w1 , h1 = fig .bbox .width * dpi_scale , fig .bbox .height * dpi_scale
56
- stack .enter_context (
57
- _setattr_cm (fig .transFigure ,
58
- _boxout = Bbox .from_bounds (- x0 , - y0 , w1 , h1 )))
66
+ fig .transFigure ._boxout = Bbox .from_bounds (- x0 , - y0 , w1 , h1 )
59
67
fig .transFigure .invalidate ()
60
- stack .callback (fig .transFigure .invalidate )
61
68
62
- stack .enter_context (
63
- _setattr_cm (fig , bbox = TransformedBbox (fig .bbox_inches , tr )))
69
+ fig .bbox = TransformedBbox (fig .bbox_inches , tr )
64
70
65
- stack .callback (fig .patch .set_bounds , 0 , 0 , 1 , 1 )
66
71
fig .patch .set_bounds (x0 / w1 , y0 / h1 ,
67
72
fig .bbox .width / w1 , fig .bbox .height / h1 )
68
73
69
- return stack . close
74
+ return restore_bbox
70
75
71
76
72
77
def process_figure_for_rasterizing (fig , bbox_inches_restore , fixed_dpi = None ):
0 commit comments