2222"""
2323
2424from contextlib import nullcontext
25- from math import radians , cos , sin
25+ import math
2626
2727import numpy as np
2828from PIL import features
3232from matplotlib .backend_bases import (
3333 _Backend , FigureCanvasBase , FigureManagerBase , RendererBase )
3434from matplotlib .font_manager import fontManager as _fontManager , get_font
35- from matplotlib .ft2font import LoadFlags
35+ from matplotlib .ft2font import LoadFlags , RenderMode
3636from matplotlib .mathtext import MathTextParser
3737from matplotlib .path import Path
3838from matplotlib .transforms import Bbox , BboxBase
@@ -71,7 +71,7 @@ def __init__(self, width, height, dpi):
7171 self ._filter_renderers = []
7272
7373 self ._update_methods ()
74- self .mathtext_parser = MathTextParser ('agg ' )
74+ self .mathtext_parser = MathTextParser ('path ' )
7575
7676 self .bbox = Bbox .from_bounds (0 , 0 , self .width , self .height )
7777
@@ -173,46 +173,67 @@ def draw_path(self, gc, path, transform, rgbFace=None):
173173
174174 def draw_mathtext (self , gc , x , y , s , prop , angle ):
175175 """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 , 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 (
192+ font .get_char_index (char ), get_hinting_flag (),
193+ RenderMode .NORMAL if gc .get_antialiased () else RenderMode .MONO )
194+ # draw_text_image's y is downwards & the bitmap bottom side.
195+ self ._renderer .draw_text_image (
196+ bitmap ["buffer" ],
197+ bitmap ["left" ],
198+ int (self .height ) - bitmap ["top" ] + bitmap ["buffer" ].shape [0 ],
199+ 0 , gc )
200+ if not angle :
201+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
202+ self ._renderer .draw_text_image (
203+ np .full ((round (h ), round (w )), np .uint8 (0xff )),
204+ round (x + dx ), round (y - dy - h ),
205+ 0 , gc )
206+ else :
207+ rgba = gc .get_rgb ()
208+ if len (rgba ) == 3 or gc .get_forced_alpha ():
209+ rgba = rgba [:3 ] + (gc .get_alpha (),)
210+ gc1 = self .new_gc ()
211+ gc1 .set_linewidth (0 )
212+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
213+ path = Path ._create_closed (
214+ [(dx , dy ), (dx + w , dy ), (dx + w , dy + h ), (dx , dy + h )])
215+ self ._renderer .draw_path (
216+ gc1 , path ,
217+ mpl .transforms .Affine2D ()
218+ .rotate_deg (angle ).translate (x , self .height - y ),
219+ rgba )
220+ gc1 .restore ()
185221
186222 def draw_text (self , gc , x , y , s , prop , angle , ismath = False , mtext = None ):
187223 # docstring inherited
188224 if ismath :
189225 return self .draw_mathtext (gc , x , y , s , prop , angle )
190226 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 ())
194- font .draw_glyphs_to_bitmap (
195- antialiased = gc .get_antialiased ())
196- d = font .get_descent () / 64.0
197- # The descent needs to be adjusted for the angle.
198- xo , yo = font .get_bitmap_offset ()
199- xo /= 64.0
200- yo /= 64.0
201-
202- rad = radians (angle )
203- xd = d * sin (rad )
204- yd = d * cos (rad )
205- # Rotating the offset vector ensures text rotates around the anchor point.
206- # Without this, rotated text offsets incorrectly, causing a horizontal shift.
207- # Applying the 2D rotation matrix.
208- rotated_xo = xo * cos (rad ) - yo * sin (rad )
209- rotated_yo = xo * sin (rad ) + yo * cos (rad )
210- # Subtract rotated_yo to account for the inverted y-axis in computer graphics,
211- # compared to the mathematical convention.
212- x = round (x + rotated_xo + xd )
213- y = round (y - rotated_yo + yd )
214-
215- self ._renderer .draw_text_image (font , x , y + 1 , angle , gc )
227+ font .set_text (s , angle , flags = get_hinting_flag ())
228+ for bitmap in font ._render_glyphs (
229+ x , self .height - y ,
230+ RenderMode .NORMAL if gc .get_antialiased () else RenderMode .MONO ,
231+ ):
232+ self ._renderer .draw_text_image (
233+ bitmap ["buffer" ],
234+ bitmap ["left" ],
235+ int (self .height ) - bitmap ["top" ] + bitmap ["buffer" ].shape [0 ],
236+ 0 , gc )
216237
217238 def get_text_width_height_descent (self , s , prop , ismath ):
218239 # docstring inherited
@@ -222,9 +243,8 @@ def get_text_width_height_descent(self, s, prop, ismath):
222243 return super ().get_text_width_height_descent (s , prop , ismath )
223244
224245 if ismath :
225- ox , oy , width , height , descent , font_image = \
226- self .mathtext_parser .parse (s , self .dpi , prop )
227- return width , height , descent
246+ parse = self .mathtext_parser .parse (s , self .dpi , prop )
247+ return parse .width , parse .height , parse .depth
228248
229249 font = self ._prepare_font (prop )
230250 font .set_text (s , 0.0 , flags = get_hinting_flag ())
@@ -246,8 +266,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None):
246266 Z = np .array (Z * 255.0 , np .uint8 )
247267
248268 w , h , d = self .get_text_width_height_descent (s , prop , ismath = "TeX" )
249- xd = d * sin (radians (angle ))
250- yd = d * cos (radians (angle ))
269+ xd = d * math . sin (math . radians (angle ))
270+ yd = d * math . cos (math . radians (angle ))
251271 x = round (x + xd )
252272 y = round (y + yd )
253273 self ._renderer .draw_text_image (Z , x , y , angle , gc )
0 commit comments