Skip to content

Commit 20f131b

Browse files
Use path-based MathTextParser
1 parent 5bc35a7 commit 20f131b

File tree

1 file changed

+68
-30
lines changed

1 file changed

+68
-30
lines changed

matplotlib_pyodide/html5_canvas_backend.py

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
import math
44
from functools import lru_cache
55

6+
import matplotlib.pyplot as plt
67
import numpy as np
7-
from matplotlib import __version__, interactive
8+
from matplotlib import __version__, figure, interactive
89
from matplotlib.backend_bases import (
910
FigureManagerBase,
1011
GraphicsContextBase,
1112
RendererBase,
1213
_Backend,
1314
)
15+
from matplotlib.backends import backend_agg
1416
from matplotlib.colors import colorConverter, rgb2hex
1517
from matplotlib.font_manager import findfont
1618
from matplotlib.ft2font import LOAD_NO_HINTING, FT2Font
@@ -204,7 +206,11 @@ def __init__(self, ctx, width, height, dpi, fig):
204206
self.ctx.width = self.width
205207
self.ctx.height = self.height
206208
self.dpi = dpi
207-
self.mathtext_parser = MathTextParser("bitmap")
209+
210+
# Create path-based math text parser; as the bitmap parser
211+
# was deprecated in 3.4 and removed after 3.5
212+
self.mathtext_parser = MathTextParser("path")
213+
208214
self._get_font_helper = lru_cache(maxsize=50)(self._get_font_helper)
209215

210216
# Keep the state of fontfaces that are loading
@@ -240,6 +246,51 @@ def _matplotlib_color_to_CSS(self, color, alpha, alpha_overrides, is_RGB=True):
240246

241247
return CSS_color
242248

249+
def _math_to_rgba(self, s, prop, rgb):
250+
"""Convert math text to an RGBA array using path parser and figure"""
251+
from io import BytesIO
252+
253+
# Get the text dimensions
254+
width, height, depth, _, _ = self.mathtext_parser.parse(s, dpi=72, prop=prop)
255+
256+
# Create a figure of the right size
257+
fig = figure.Figure(figsize=(width / 72, height / 72))
258+
259+
# Add text to the figure
260+
# Note: depth/height gives us the baseline position
261+
fig.text(0, depth / height, s, fontproperties=prop, color=rgb)
262+
263+
# Set up the Agg backend
264+
backend_agg.FigureCanvasAgg(fig)
265+
266+
# Render to PNG
267+
buf = BytesIO()
268+
fig.savefig(buf, dpi=self.dpi, format="png", transparent=True)
269+
buf.seek(0)
270+
271+
# Read back the image
272+
rgba = plt.imread(buf)
273+
return rgba, depth
274+
275+
def _draw_math_text(self, gc, x, y, s, prop, angle):
276+
# Get color from graphics context
277+
rgb = gc.get_rgb()
278+
279+
# Get RGBA array using the new method
280+
rgba, depth = self._math_to_rgba(s, prop, rgb)
281+
282+
angle = math.radians(angle)
283+
if angle != 0:
284+
self.ctx.save()
285+
self.ctx.translate(x, y)
286+
self.ctx.rotate(-angle)
287+
self.ctx.translate(-x, -y)
288+
289+
self.draw_image(gc, x, -y - depth, np.flipud(rgba))
290+
291+
if angle != 0:
292+
self.ctx.restore()
293+
243294
def _set_style(self, gc, rgbFace=None):
244295
if rgbFace is not None:
245296
self.ctx.fillStyle = self._matplotlib_color_to_CSS(
@@ -330,41 +381,19 @@ def get_text_width_height_descent(self, s, prop, ismath):
330381
w: float
331382
h: float
332383
if ismath:
333-
image, d = self.mathtext_parser.parse(s, self.dpi, prop)
334-
image_arr = np.asarray(image)
335-
h, w = image_arr.shape
384+
# Use the path parser to get exact metrics
385+
width, height, depth, _, _ = self.mathtext_parser.parse(
386+
s, dpi=72, prop=prop
387+
)
388+
return width, height, depth
336389
else:
337390
font, _ = self._get_font(prop)
338391
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
339392
w, h = font.get_width_height()
340393
w /= 64.0
341394
h /= 64.0
342395
d = font.get_descent() / 64.0
343-
return w, h, d
344-
345-
def _draw_math_text(self, gc, x, y, s, prop, angle):
346-
rgba, descent = self.mathtext_parser.to_rgba(
347-
s, gc.get_rgb(), self.dpi, prop.get_size_in_points()
348-
)
349-
height, width, _ = rgba.shape
350-
angle = math.radians(angle)
351-
if angle != 0:
352-
self.ctx.save()
353-
self.ctx.translate(x, y)
354-
self.ctx.rotate(-angle)
355-
self.ctx.translate(-x, -y)
356-
self.draw_image(gc, x, -y - descent, np.flipud(rgba))
357-
if angle != 0:
358-
self.ctx.restore()
359-
360-
def load_font_into_web(self, loaded_face, font_url):
361-
fontface = loaded_face.result()
362-
document.fonts.add(fontface)
363-
self.fonts_loading.pop(font_url, None)
364-
365-
# Redraw figure after font has loaded
366-
self.fig.draw()
367-
return fontface
396+
return w, h, d
368397

369398
def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
370399
if ismath:
@@ -421,6 +450,15 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
421450
if angle != 0:
422451
self.ctx.restore()
423452

453+
def load_font_into_web(self, loaded_face, font_url):
454+
fontface = loaded_face.result()
455+
document.fonts.add(fontface)
456+
self.fonts_loading.pop(font_url, None)
457+
458+
# Redraw figure after font has loaded
459+
self.fig.draw()
460+
return fontface
461+
424462

425463
class FigureManagerHTMLCanvas(FigureManagerBase):
426464
def __init__(self, canvas, num):

0 commit comments

Comments
 (0)