13
13
from PIL import Image
14
14
15
15
import matplotlib as mpl
16
- from matplotlib import _api , cbook
16
+ from matplotlib import _api , cbook , font_manager as fm
17
17
from matplotlib .backend_bases import (
18
18
_Backend , _check_savefig_extra_args , FigureCanvasBase , FigureManagerBase ,
19
19
RendererBase )
20
20
from matplotlib .backends .backend_mixed import MixedModeRenderer
21
21
from matplotlib .colors import rgb2hex
22
22
from matplotlib .dates import UTC
23
- from matplotlib .font_manager import findfont , get_font
24
- from matplotlib .ft2font import LOAD_NO_HINTING
25
23
from matplotlib .mathtext import MathTextParser
26
24
from matplotlib .path import Path
27
25
from matplotlib import _path
@@ -93,6 +91,12 @@ def escape_attrib(s):
93
91
return s
94
92
95
93
94
+ def _quote_escape_attrib (s ):
95
+ return ('"' + escape_cdata (s ) + '"' if '"' not in s else
96
+ "'" + escape_cdata (s ) + "'" if "'" not in s else
97
+ '"' + escape_attrib (s ) + '"' )
98
+
99
+
96
100
def short_float_fmt (x ):
97
101
"""
98
102
Create a short string representation of a float, which is %f
@@ -158,8 +162,8 @@ def start(self, tag, attrib={}, **extra):
158
162
for k , v in {** attrib , ** extra }.items ():
159
163
if v :
160
164
k = escape_cdata (k )
161
- v = escape_attrib (v )
162
- self .__write (' %s="%s" ' % (k , v ))
165
+ v = _quote_escape_attrib (v )
166
+ self .__write (' %s=%s ' % (k , v ))
163
167
self .__open = 1
164
168
return len (self .__tags ) - 1
165
169
@@ -261,15 +265,7 @@ def generate_transform(transform_list=[]):
261
265
262
266
263
267
def generate_css (attrib = {}):
264
- if attrib :
265
- output = StringIO ()
266
- attrib = attrib .items ()
267
- for k , v in attrib :
268
- k = escape_attrib (k )
269
- v = escape_attrib (v )
270
- output .write ("%s:%s;" % (k , v ))
271
- return output .getvalue ()
272
- return ''
268
+ return "; " .join (f"{ k } : { v } " for k , v in attrib .items ())
273
269
274
270
275
271
_capstyle_d = {'projecting' : 'square' , 'butt' : 'butt' , 'round' : 'round' }
@@ -462,8 +458,8 @@ def _make_flip_transform(self, transform):
462
458
.translate (0.0 , self .height ))
463
459
464
460
def _get_font (self , prop ):
465
- fname = findfont (prop )
466
- font = get_font (fname )
461
+ fname = fm . findfont (prop )
462
+ font = fm . get_font (fname )
467
463
font .clear ()
468
464
size = prop .get_size_in_points ()
469
465
font .set_size (size , 72.0 )
@@ -1106,16 +1102,23 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None):
1106
1102
style ['opacity' ] = short_float_fmt (alpha )
1107
1103
1108
1104
if not ismath :
1109
- font = self ._get_font (prop )
1110
- font .set_text (s , 0.0 , flags = LOAD_NO_HINTING )
1111
-
1112
1105
attrib = {}
1113
- style ['font-family' ] = str (font .family_name )
1114
- style ['font-weight' ] = str (prop .get_weight ()).lower ()
1115
- style ['font-stretch' ] = str (prop .get_stretch ()).lower ()
1116
- style ['font-style' ] = prop .get_style ().lower ()
1117
- # Must add "px" to workaround a Firefox bug
1118
- style ['font-size' ] = short_float_fmt (prop .get_size ()) + 'px'
1106
+
1107
+ font_parts = []
1108
+ if prop .get_style () != 'normal' :
1109
+ font_parts .append (prop .get_style ())
1110
+ if prop .get_variant () != 'normal' :
1111
+ font_parts .append (prop .get_variant ())
1112
+ weight = fm .weight_dict [prop .get_weight ()]
1113
+ if weight != 400 :
1114
+ font_parts .append (f'{ weight } ' )
1115
+ font_parts .extend ([
1116
+ f'{ short_float_fmt (prop .get_size ())} px' ,
1117
+ f'{ prop .get_family ()[0 ]!r} ' , # ensure quoting
1118
+ ])
1119
+ style ['font' ] = ' ' .join (font_parts )
1120
+ if prop .get_stretch () != 'normal' :
1121
+ style ['font-stretch' ] = prop .get_stretch ()
1119
1122
attrib ['style' ] = generate_css (style )
1120
1123
1121
1124
if mtext and (angle == 0 or mtext .get_rotation_mode () == "anchor" ):
@@ -1175,11 +1178,22 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None):
1175
1178
# Sort the characters by font, and output one tspan for each.
1176
1179
spans = {}
1177
1180
for font , fontsize , thetext , new_x , new_y in glyphs :
1178
- style = generate_css ({
1179
- 'font-size' : short_float_fmt (fontsize ) + 'px' ,
1180
- 'font-family' : font .family_name ,
1181
- 'font-style' : font .style_name .lower (),
1182
- 'font-weight' : font .style_name .lower ()})
1181
+ entry = fm .ttfFontProperty (font )
1182
+ font_parts = []
1183
+ if entry .style != 'normal' :
1184
+ font_parts .append (entry .style )
1185
+ if entry .variant != 'normal' :
1186
+ font_parts .append (entry .variant )
1187
+ if entry .weight != 400 :
1188
+ font_parts .append (f'{ entry .weight } ' )
1189
+ font_parts .extend ([
1190
+ f'{ short_float_fmt (fontsize )} px' ,
1191
+ f'{ entry .name !r} ' , # ensure quoting
1192
+ ])
1193
+ style = {'font' : ' ' .join (font_parts )}
1194
+ if entry .stretch != 'normal' :
1195
+ style ['font-stretch' ] = entry .stretch
1196
+ style = generate_css (style )
1183
1197
if thetext == 32 :
1184
1198
thetext = 0xa0 # non-breaking space
1185
1199
spans .setdefault (style , []).append ((new_x , - new_y , thetext ))
0 commit comments