From a86c83d24da26b7ae9c380bfb10a7b2a0e8a7281 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 5 Nov 2020 19:00:40 +0100 Subject: [PATCH] Better positioning of mathtext glyphs. --- lib/matplotlib/mathtext.py | 107 ++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 37589e267726..7497c4178426 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -101,70 +101,69 @@ class MathtextBackendAgg(MathtextBackend): Render glyphs and rectangles to an FTImage buffer, which is later transferred to the Agg image by the Agg backend. """ - def __init__(self): - self.ox = 0 - self.oy = 0 - self.image = None - self.mode = 'bbox' - self.bbox = [0, 0, 0, 0] - super().__init__() - - def _update_bbox(self, x1, y1, x2, y2): - self.bbox = [min(self.bbox[0], x1), - min(self.bbox[1], y1), - max(self.bbox[2], x2), - max(self.bbox[3], y2)] - def set_canvas_size(self, w, h, d): - super().set_canvas_size(w, h, d) - if self.mode != 'bbox': - self.image = FT2Image(np.ceil(w), np.ceil(h + max(d, 0))) + def __init__(self): + MathtextBackend.__init__(self) + self._xmin = self._ymin = np.inf + self._xmax = self._ymax = -np.inf + self._glyphs = [] + self._rects = [] + + ox = cbook.deprecated("3.4")(property(lambda self: 0)) + oy = cbook.deprecated("3.4")(property(lambda self: 0)) + mode = cbook.deprecated("3.4")(property(lambda self: "bbox")) + image = cbook.deprecated("3.4")(property(lambda self: None)) + bbox = cbook.deprecated("3.4")(property( + lambda self: [self._xmin, self._ymin, self._xmax, self._ymax])) def render_glyph(self, ox, oy, info): - if self.mode == 'bbox': - self._update_bbox(ox + info.metrics.xmin, - oy - info.metrics.ymax, - ox + info.metrics.xmax, - oy - info.metrics.ymin) - else: - info.font.draw_glyph_to_bitmap( - self.image, ox, oy - info.metrics.iceberg, info.glyph, - antialiased=rcParams['text.antialiased']) + self._glyphs.append((ox, oy, info)) + metrics = info.metrics + self._xmin = min(self._xmin, ox + metrics.xmin) + self._ymin = min(self._ymin, self.height - oy + metrics.ymin) + self._xmax = max(self._xmax, ox + metrics.xmax) + self._ymax = max(self._ymax, self.height - oy + metrics.ymax) def render_rect_filled(self, x1, y1, x2, y2): - if self.mode == 'bbox': - self._update_bbox(x1, y1, x2, y2) - else: + self._rects.append((x1, y1, x2, y2)) + self._xmin = min(self._xmin, x1) + self._xmax = max(self._xmax, x2) + self._ymin = min(self._ymin, y1) + self._ymax = max(self._ymax, y2) + + def get_results(self, box, used_characters): + orig_height = box.height + orig_depth = box.depth + _mathtext.ship(0, 0, box) + xmin = np.floor(self._xmin) + xmax = np.ceil(self._xmax) + ymin = np.floor(self._ymin) + ymax = np.ceil(self._ymax) + dxmin = self._xmin - xmin + dymin = self._ymin - ymin + image = FT2Image(np.ceil(xmax - xmin) + 1, np.ceil(ymax - ymin) + 1) + + for ox, oy, info in self._glyphs: + info.font.draw_glyph_to_bitmap( + image, ox + dxmin, oy - info.metrics.iceberg + dymin, + info.glyph, antialiased=rcParams['text.antialiased']) + for x1, y1, x2, y2 in self._rects: + x1 += dxmin + x2 += dymin + y1 += dxmin + y2 += dymin height = max(int(y2 - y1) - 1, 0) if height == 0: - center = (y2 + y1) / 2.0 - y = int(center - (height + 1) / 2.0) + center = (y2 + y1) / 2 + y = int(center - (height + 1) / 2) else: y = int(y1) - self.image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height) + image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height) - def get_results(self, box, used_characters): - self.mode = 'bbox' - orig_height = box.height - orig_depth = box.depth - _mathtext.ship(0, 0, box) - bbox = self.bbox - bbox = [bbox[0] - 1, bbox[1] - 1, bbox[2] + 1, bbox[3] + 1] - self.mode = 'render' - self.set_canvas_size( - bbox[2] - bbox[0], - (bbox[3] - bbox[1]) - orig_depth, - (bbox[3] - bbox[1]) - orig_height) - _mathtext.ship(-bbox[0], -bbox[1], box) - result = (self.ox, - self.oy, - self.width, - self.height + self.depth, - self.depth, - self.image, - used_characters) - self.image = None - return result + return (0, 0, + np.ceil(xmax - xmin), np.ceil(ymax - ymin), -ymin, + image, + used_characters) def get_hinting_type(self): from matplotlib.backends import backend_agg