22
22
"""
23
23
24
24
from contextlib import nullcontext
25
- from math import radians , cos , sin
25
+ import math
26
26
27
27
import numpy as np
28
28
from PIL import features
@@ -71,7 +71,7 @@ def __init__(self, width, height, dpi):
71
71
self ._filter_renderers = []
72
72
73
73
self ._update_methods ()
74
- self .mathtext_parser = MathTextParser ('agg ' )
74
+ self .mathtext_parser = MathTextParser ('path ' )
75
75
76
76
self .bbox = Bbox .from_bounds (0 , 0 , self .width , self .height )
77
77
@@ -173,47 +173,63 @@ def draw_path(self, gc, path, transform, rgbFace=None):
173
173
174
174
def draw_mathtext (self , gc , x , y , s , prop , angle ):
175
175
"""Draw mathtext using :mod:`matplotlib.mathtext`."""
176
- ox , oy , width , height , descent , font_image = \
177
- self .mathtext_parser .parse (s , self .dpi , prop ,
178
- antialiased = gc .get_antialiased ())
179
-
180
- xd = descent * sin (radians (angle ))
181
- yd = descent * cos (radians (angle ))
182
- x = round (x + ox + xd )
183
- y = round (y - oy + yd )
184
- self ._renderer .draw_text_image (font_image , x , y + 1 , angle , gc )
176
+ # y is downwards.
177
+ parse = self .mathtext_parser .parse (
178
+ s , self .dpi , prop , antialiased = gc .get_antialiased ())
179
+ cos = math .cos (math .radians (angle ))
180
+ sin = math .sin (math .radians (angle ))
181
+ for font , size , char , glyph_index , dx , dy in parse .glyphs : # dy is upwards.
182
+ font .set_size (size , self .dpi )
183
+ hf = font ._hinting_factor
184
+ font ._set_transform (
185
+ [[round (0x10000 * cos / hf ), round (0x10000 * - sin )],
186
+ [round (0x10000 * sin / hf ), round (0x10000 * cos )]],
187
+ [round (0x40 * (x + dx * cos - dy * sin )),
188
+ # FreeType's y is upwards.
189
+ round (0x40 * (self .height - y + dx * sin + dy * cos ))]
190
+ )
191
+ bitmap = font ._render_glyph (glyph_index , get_hinting_flag ())
192
+ # draw_text_image's y is downwards & the bitmap bottom side.
193
+ self ._renderer .draw_text_image (
194
+ bitmap ["buffer" ],
195
+ bitmap ["left" ],
196
+ int (self .height ) - bitmap ["top" ] + bitmap ["buffer" ].shape [0 ],
197
+ 0 , gc )
198
+ if not angle :
199
+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
200
+ self ._renderer .draw_text_image (
201
+ np .full ((round (h ), round (w )), np .uint8 (0xff )),
202
+ round (x + dx ), round (y - dy - h ),
203
+ 0 , gc )
204
+ else :
205
+ rgba = gc .get_rgb ()
206
+ if len (rgba ) == 3 or gc .get_forced_alpha ():
207
+ rgba = rgba [:3 ] + (gc .get_alpha (),)
208
+ gc1 = self .new_gc ()
209
+ gc1 .set_linewidth (0 )
210
+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
211
+ path = Path ._create_closed (
212
+ [(dx , dy ), (dx + w , dy ), (dx + w , dy + h ), (dx , dy + h )])
213
+ self ._renderer .draw_path (
214
+ gc1 , path ,
215
+ mpl .transforms .Affine2D ()
216
+ .rotate_deg (angle ).translate (x , self .height - y ),
217
+ rgba )
218
+ gc1 .restore ()
185
219
186
220
def draw_text (self , gc , x , y , s , prop , angle , ismath = False , mtext = None ):
187
221
# docstring inherited
188
222
if ismath :
189
223
return self .draw_mathtext (gc , x , y , s , prop , angle )
190
224
font = self ._prepare_font (prop )
191
- # We pass '0' for angle here, since it will be rotated (in raster
192
- # space) in the following call to draw_text_image).
193
- font .set_text (s , 0 , flags = get_hinting_flag (),
225
+ font .set_text (s , angle , flags = get_hinting_flag (),
194
226
language = mtext .get_language () if mtext is not None else None )
195
- font .draw_glyphs_to_bitmap (
196
- antialiased = gc .get_antialiased ())
197
- d = font .get_descent () / 64.0
198
- # The descent needs to be adjusted for the angle.
199
- xo , yo = font .get_bitmap_offset ()
200
- xo /= 64.0
201
- yo /= 64.0
202
-
203
- rad = radians (angle )
204
- xd = d * sin (rad )
205
- yd = d * cos (rad )
206
- # Rotating the offset vector ensures text rotates around the anchor point.
207
- # Without this, rotated text offsets incorrectly, causing a horizontal shift.
208
- # Applying the 2D rotation matrix.
209
- rotated_xo = xo * cos (rad ) - yo * sin (rad )
210
- rotated_yo = xo * sin (rad ) + yo * cos (rad )
211
- # Subtract rotated_yo to account for the inverted y-axis in computer graphics,
212
- # compared to the mathematical convention.
213
- x = round (x + rotated_xo + xd )
214
- y = round (y - rotated_yo + yd )
215
-
216
- self ._renderer .draw_text_image (font , x , y + 1 , angle , gc )
227
+ for bitmap in font ._render_glyphs (x , self .height - y ):
228
+ self ._renderer .draw_text_image (
229
+ bitmap ["buffer" ],
230
+ bitmap ["left" ],
231
+ int (self .height ) - bitmap ["top" ] + bitmap ["buffer" ].shape [0 ],
232
+ 0 , gc )
217
233
218
234
def get_text_width_height_descent (self , s , prop , ismath ):
219
235
# docstring inherited
@@ -223,9 +239,8 @@ def get_text_width_height_descent(self, s, prop, ismath):
223
239
return super ().get_text_width_height_descent (s , prop , ismath )
224
240
225
241
if ismath :
226
- ox , oy , width , height , descent , font_image = \
227
- self .mathtext_parser .parse (s , self .dpi , prop )
228
- return width , height , descent
242
+ parse = self .mathtext_parser .parse (s , self .dpi , prop )
243
+ return parse .width , parse .height , parse .depth
229
244
230
245
font = self ._prepare_font (prop )
231
246
font .set_text (s , 0.0 , flags = get_hinting_flag ())
@@ -247,8 +262,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None):
247
262
Z = np .array (Z * 255.0 , np .uint8 )
248
263
249
264
w , h , d = self .get_text_width_height_descent (s , prop , ismath = "TeX" )
250
- xd = d * sin (radians (angle ))
251
- yd = d * cos (radians (angle ))
265
+ xd = d * math . sin (math . radians (angle ))
266
+ yd = d * math . cos (math . radians (angle ))
252
267
x = round (x + xd )
253
268
y = round (y + yd )
254
269
self ._renderer .draw_text_image (Z , x , y , angle , gc )
0 commit comments