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,48 +173,68 @@ 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 , 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 (
192+ glyph_index , get_hinting_flag (),
193+ RenderMode .NORMAL if gc .get_antialiased () else RenderMode .MONO )
194+ buffer = np .asarray (bitmap .buffer )
195+ if not gc .get_antialiased ():
196+ buffer *= 0xff
197+ # draw_text_image's y is downwards & the bitmap bottom side.
198+ self ._renderer .draw_text_image (
199+ buffer ,
200+ bitmap .left ,
201+ int (self .height ) - bitmap .top + bitmap .buffer .shape [0 ],
202+ 0 , gc )
203+ rgba = gc .get_rgb ()
204+ if len (rgba ) == 3 or gc .get_forced_alpha ():
205+ rgba = rgba [:3 ] + (gc .get_alpha (),)
206+ gc1 = self .new_gc ()
207+ gc1 .set_linewidth (0 )
208+ gc1 .set_snap (gc .get_snap ())
209+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
210+ path = Path ._create_closed (
211+ [(dx , dy ), (dx + w , dy ), (dx + w , dy + h ), (dx , dy + h )])
212+ self ._renderer .draw_path (
213+ gc1 , path ,
214+ mpl .transforms .Affine2D ()
215+ .rotate_deg (angle ).translate (x , self .height - y ),
216+ rgba )
217+ gc1 .restore ()
185218
186219 def draw_text (self , gc , x , y , s , prop , angle , ismath = False , mtext = None ):
187220 # docstring inherited
188221 if ismath :
189222 return self .draw_mathtext (gc , x , y , s , prop , angle )
190223 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 (),
224+ font .set_text (s , angle , flags = get_hinting_flag (),
194225 features = mtext .get_fontfeatures () if mtext is not None else None ,
195226 language = mtext .get_language () if mtext is not None else None )
196- font .draw_glyphs_to_bitmap (
197- antialiased = gc .get_antialiased ())
198- d = font .get_descent () / 64.0
199- # The descent needs to be adjusted for the angle.
200- xo , yo = font .get_bitmap_offset ()
201- xo /= 64.0
202- yo /= 64.0
203-
204- rad = radians (angle )
205- xd = d * sin (rad )
206- yd = d * cos (rad )
207- # Rotating the offset vector ensures text rotates around the anchor point.
208- # Without this, rotated text offsets incorrectly, causing a horizontal shift.
209- # Applying the 2D rotation matrix.
210- rotated_xo = xo * cos (rad ) - yo * sin (rad )
211- rotated_yo = xo * sin (rad ) + yo * cos (rad )
212- # Subtract rotated_yo to account for the inverted y-axis in computer graphics,
213- # compared to the mathematical convention.
214- x = round (x + rotated_xo + xd )
215- y = round (y - rotated_yo + yd )
216-
217- self ._renderer .draw_text_image (font , x , y + 1 , angle , gc )
227+ for bitmap in font ._render_glyphs (
228+ x , self .height - y ,
229+ RenderMode .NORMAL if gc .get_antialiased () else RenderMode .MONO ,
230+ ):
231+ buffer = bitmap .buffer
232+ if not gc .get_antialiased ():
233+ buffer *= 0xff
234+ self ._renderer .draw_text_image (
235+ buffer ,
236+ bitmap .left , int (self .height ) - bitmap .top + buffer .shape [0 ],
237+ 0 , gc )
218238
219239 def get_text_width_height_descent (self , s , prop , ismath ):
220240 # docstring inherited
@@ -224,9 +244,8 @@ def get_text_width_height_descent(self, s, prop, ismath):
224244 return super ().get_text_width_height_descent (s , prop , ismath )
225245
226246 if ismath :
227- ox , oy , width , height , descent , font_image = \
228- self .mathtext_parser .parse (s , self .dpi , prop )
229- return width , height , descent
247+ parse = self .mathtext_parser .parse (s , self .dpi , prop )
248+ return parse .width , parse .height , parse .depth
230249
231250 font = self ._prepare_font (prop )
232251 font .set_text (s , 0.0 , flags = get_hinting_flag ())
@@ -248,8 +267,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None):
248267 Z = np .array (Z * 255.0 , np .uint8 )
249268
250269 w , h , d = self .get_text_width_height_descent (s , prop , ismath = "TeX" )
251- xd = d * sin (radians (angle ))
252- yd = d * cos (radians (angle ))
270+ xd = d * math . sin (math . radians (angle ))
271+ yd = d * math . cos (math . radians (angle ))
253272 x = round (x + xd )
254273 y = round (y + yd )
255274 self ._renderer .draw_text_image (Z , x , y , angle , gc )
0 commit comments