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,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 , 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 , int (self .height ) - bitmap .top + buffer .shape [0 ],
201+ 0 , gc )
202+ rgba = gc .get_rgb ()
203+ if len (rgba ) == 3 or gc .get_forced_alpha ():
204+ rgba = rgba [:3 ] + (gc .get_alpha (),)
205+ gc1 = self .new_gc ()
206+ gc1 .set_linewidth (0 )
207+ gc1 .set_snap (gc .get_snap ())
208+ for dx , dy , w , h in parse .rects : # dy is upwards & the rect top side.
209+ path = Path ._create_closed (
210+ [(dx , dy ), (dx + w , dy ), (dx + w , dy + h ), (dx , dy + h )])
211+ self ._renderer .draw_path (
212+ gc1 , path ,
213+ mpl .transforms .Affine2D ()
214+ .rotate_deg (angle ).translate (x , self .height - y ),
215+ rgba )
216+ gc1 .restore ()
185217
186218 def draw_text (self , gc , x , y , s , prop , angle , ismath = False , mtext = None ):
187219 # docstring inherited
188220 if ismath :
189221 return self .draw_mathtext (gc , x , y , s , prop , angle )
190222 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 (),
223+ font .set_text (s , angle , flags = get_hinting_flag (),
194224 features = mtext .get_fontfeatures () if mtext is not None else None ,
195225 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 )
226+ for bitmap in font ._render_glyphs (
227+ x , self .height - y ,
228+ RenderMode .NORMAL if gc .get_antialiased () else RenderMode .MONO ,
229+ ):
230+ buffer = bitmap .buffer
231+ if not gc .get_antialiased ():
232+ buffer *= 0xff
233+ self ._renderer .draw_text_image (
234+ buffer ,
235+ bitmap .left , int (self .height ) - bitmap .top + buffer .shape [0 ],
236+ 0 , gc )
218237
219238 def get_text_width_height_descent (self , s , prop , ismath ):
220239 # docstring inherited
@@ -224,9 +243,8 @@ def get_text_width_height_descent(self, s, prop, ismath):
224243 return super ().get_text_width_height_descent (s , prop , ismath )
225244
226245 if ismath :
227- ox , oy , width , height , descent , font_image = \
228- self .mathtext_parser .parse (s , self .dpi , prop )
229- return width , height , descent
246+ parse = self .mathtext_parser .parse (s , self .dpi , prop )
247+ return parse .width , parse .height , parse .depth
230248
231249 font = self ._prepare_font (prop )
232250 font .set_text (s , 0.0 , flags = get_hinting_flag ())
@@ -248,8 +266,8 @@ def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None):
248266 Z = np .array (Z * 255.0 , np .uint8 )
249267
250268 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 ))
269+ xd = d * math . sin (math . radians (angle ))
270+ yd = d * math . cos (math . radians (angle ))
253271 x = round (x + xd )
254272 y = round (y + yd )
255273 self ._renderer .draw_text_image (Z , x , y , angle , gc )
0 commit comments