From c54e2ba6895da1e2c0c1b677df32ad95b17244c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20W=C3=BCrtz?= Date: Sat, 9 Nov 2013 11:12:13 +0100 Subject: [PATCH 1/2] Fix dpi detection in tight_bbox (fixes #2586). --- doc/api/api_changes.rst | 6 ++ lib/matplotlib/backend_bases.py | 5 +- lib/matplotlib/backends/backend_mixed.py | 7 +-- lib/matplotlib/backends/backend_pdf.py | 2 + lib/matplotlib/backends/backend_ps.py | 2 + lib/matplotlib/backends/backend_svg.py | 2 + lib/matplotlib/tight_bbox.py | 73 ++++-------------------- 7 files changed, 28 insertions(+), 69 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 88b1e8e7c17f..a6d851b3d138 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -119,6 +119,12 @@ original location: * Removed the class `FigureManagerQTAgg` and deprecated `NavigationToolbar2QTAgg` which will be removed in 1.5. +* The function signatures of `tight_bbox.adjust_bbox` and + `tight_bbox.process_figure_for_rasterizing` have been changed. A new + `fixed_dpi` parameter allows for overriding the `figure.dpi` setting + instead of trying to deduce the intended behaviour from the file format. + + .. _changes_in_1_3: diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index c9212939b1a7..c7a10904f37e 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1625,6 +1625,7 @@ class FigureCanvasBase(object): ] supports_blit = True + fixed_dpi = None filetypes = _default_filetypes if _has_pil: @@ -2171,8 +2172,8 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w', bbox_inches = bbox_inches.padded(pad) - restore_bbox = tight_bbox.adjust_bbox(self.figure, format, - bbox_inches) + restore_bbox = tight_bbox.adjust_bbox(self.figure, bbox_inches, + canvas.fixed_dpi) _bbox_inches_restore = (bbox_inches, restore_bbox) else: diff --git a/lib/matplotlib/backends/backend_mixed.py b/lib/matplotlib/backends/backend_mixed.py index 3a439c330f66..85b3c42c5368 100644 --- a/lib/matplotlib/backends/backend_mixed.py +++ b/lib/matplotlib/backends/backend_mixed.py @@ -93,12 +93,9 @@ def start_rasterizing(self): if self._bbox_inches_restore: # when tight bbox is used r = process_figure_for_rasterizing(self.figure, - self._bbox_inches_restore, - mode="png") - + self._bbox_inches_restore) self._bbox_inches_restore = r - if self._rasterizing == 0: self._raster_renderer = self._raster_renderer_class( self._width*self.dpi, self._height*self.dpi, self.dpi) @@ -143,5 +140,5 @@ def stop_rasterizing(self): if self._bbox_inches_restore: # when tight bbox is used r = process_figure_for_rasterizing(self.figure, self._bbox_inches_restore, - mode="pdf") + self._figdpi) self._bbox_inches_restore = r diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 6d76cd58fd24..2e0e6086836c 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2434,6 +2434,8 @@ class FigureCanvasPdf(FigureCanvasBase): figure - A Figure instance """ + fixed_dpi = 72 + def draw(self): pass diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index a4f4ff6df0fb..0dffb4a921fc 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -966,6 +966,8 @@ def new_figure_manager_given_figure(num, figure): class FigureCanvasPS(FigureCanvasBase): _renderer_class = RendererPS + fixed_dpi = 72 + def draw(self): pass diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 585f51420e8b..de05ed0232f0 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -1150,6 +1150,8 @@ class FigureCanvasSVG(FigureCanvasBase): filetypes = {'svg': 'Scalable Vector Graphics', 'svgz': 'Scalable Vector Graphics'} + fixed_dpi = 72 + def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = svgwriter = io.open(filename, 'w', encoding='utf-8') diff --git a/lib/matplotlib/tight_bbox.py b/lib/matplotlib/tight_bbox.py index e89b5f8de9c9..f98a6bc10369 100644 --- a/lib/matplotlib/tight_bbox.py +++ b/lib/matplotlib/tight_bbox.py @@ -11,7 +11,7 @@ from matplotlib.transforms import Bbox, TransformedBbox, Affine2D -def adjust_bbox(fig, format, bbox_inches): +def adjust_bbox(fig, bbox_inches, fixed_dpi=None): """ Temporarily adjust the figure so that only the specified area (bbox_inches) is saved. @@ -50,63 +50,20 @@ def restore_bbox(): fig.transFigure.invalidate() fig.patch.set_bounds(0, 0, 1, 1) - adjust_bbox_handler = _adjust_bbox_handler_d.get(format) - if adjust_bbox_handler is not None: - adjust_bbox_handler(fig, bbox_inches) - return restore_bbox + if fixed_dpi is not None: + tr = Affine2D().scale(fixed_dpi) + dpi_scale = fixed_dpi / fig.dpi else: - warnings.warn("bbox_inches option for %s backend is not " - "implemented yet." % (format)) - return None - - -def adjust_bbox_png(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 - fig.transFigure._boxout = Bbox.from_bounds(-x0, -y0, - w1, h1) - fig.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(fig, bbox_inches): - """ - adjust_bbox for pdf & eps format - """ - - if fig._cachedRenderer.__class__.__name__ == "RendererPgf": tr = Affine2D().scale(fig.dpi) - f = 1. - else: - tr = Affine2D().scale(72) - f = 72. / fig.dpi + dpi_scale = 1. _bbox = TransformedBbox(bbox_inches, tr) fig.bbox_inches = Bbox.from_bounds(0, 0, - bbox_inches.width, - bbox_inches.height) + bbox_inches.width, bbox_inches.height) x0, y0 = _bbox.x0, _bbox.y0 - w1, h1 = fig.bbox.width * f, fig.bbox.height * f - fig.transFigure._boxout = Bbox.from_bounds(-x0, -y0, - w1, h1) + w1, h1 = fig.bbox.width * dpi_scale, fig.bbox.height * dpi_scale + fig.transFigure._boxout = Bbox.from_bounds(-x0, -y0, w1, h1) fig.transFigure.invalidate() fig.bbox = TransformedBbox(fig.bbox_inches, tr) @@ -114,10 +71,10 @@ def adjust_bbox_pdf(fig, bbox_inches): fig.patch.set_bounds(x0 / w1, y0 / h1, fig.bbox.width / w1, fig.bbox.height / h1) + return restore_bbox -def process_figure_for_rasterizing(figure, - bbox_inches_restore, mode): +def process_figure_for_rasterizing(fig, bbox_inches_restore, fixed_dpi=None): """ This need to be called when figure dpi changes during the drawing (e.g., rasterizing). It recovers the bbox and re-adjust it with @@ -126,14 +83,6 @@ def process_figure_for_rasterizing(figure, bbox_inches, restore_bbox = bbox_inches_restore restore_bbox() - r = adjust_bbox(figure, mode, - bbox_inches) + r = adjust_bbox(figure, bbox_inches, fixed_dpi) return bbox_inches, r - - -_adjust_bbox_handler_d = {} -for format in ["png", "raw", "rgba", "jpg", "jpeg", "tiff"]: - _adjust_bbox_handler_d[format] = adjust_bbox_png -for format in ["pgf", "pdf", "eps", "svg", "svgz"]: - _adjust_bbox_handler_d[format] = adjust_bbox_pdf From 2c84d107aa14052532ed1a17d2e637de171b3087 Mon Sep 17 00:00:00 2001 From: pwuertz Date: Sat, 9 Nov 2013 11:17:01 +0100 Subject: [PATCH 2/2] backend_pgf: use the bounding box as clip rectangle (fixes #2586) --- lib/matplotlib/backends/backend_pgf.py | 2 +- .../test_backend_pgf/pgf_bbox_inches.pdf | Bin 0 -> 15514 bytes lib/matplotlib/tests/test_backend_pgf.py | 22 ++++++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index b7b5a6a8852d..598ba8a0382d 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -799,7 +799,7 @@ def _print_pgf_to_fh(self, fh, *args, **kwargs): writeln(fh, r"\makeatletter") writeln(fh, r"\begin{pgfpicture}") writeln(fh, r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" % (w, h)) - writeln(fh, r"\pgfusepath{use as bounding box}") + writeln(fh, r"\pgfusepath{use as bounding box, clip}") _bbox_inches_restore = kwargs.pop("bbox_inches_restore", None) renderer = MixedModeRenderer(self.figure, w, h, dpi, RendererPgf(self.figure, fh), diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_bbox_inches.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dcc0f56c998be68f113503c582b3015def751878 GIT binary patch literal 15514 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