42
42
from threading import Timer
43
43
import warnings
44
44
45
+ import matplotlib as mpl
45
46
from matplotlib import afm , cbook , ft2font , rcParams , get_cachedir
46
47
from matplotlib .fontconfig_pattern import (
47
48
parse_fontconfig_pattern , generate_fontconfig_pattern )
@@ -831,15 +832,23 @@ def copy(self):
831
832
class JSONEncoder (json .JSONEncoder ):
832
833
def default (self , o ):
833
834
if isinstance (o , FontManager ):
834
- return dict (o .__dict__ , _class = 'FontManager' )
835
+ return dict (o .__dict__ , __class__ = 'FontManager' )
835
836
elif isinstance (o , FontEntry ):
836
- return dict (o .__dict__ , _class = 'FontEntry' )
837
+ d = dict (o .__dict__ , __class__ = 'FontEntry' )
838
+ try :
839
+ # Cache paths of fonts shipped with mpl relative to the mpl
840
+ # data path, which helps in the presence of venvs.
841
+ d ["fname" ] = str (
842
+ Path (d ["fname" ]).relative_to (mpl .get_data_path ()))
843
+ except ValueError :
844
+ pass
845
+ return d
837
846
else :
838
847
return super ().default (o )
839
848
840
849
841
850
def _json_decode (o ):
842
- cls = o .pop ('_class ' , None )
851
+ cls = o .pop ('__class__ ' , None )
843
852
if cls is None :
844
853
return o
845
854
elif cls == 'FontManager' :
@@ -849,15 +858,21 @@ def _json_decode(o):
849
858
elif cls == 'FontEntry' :
850
859
r = FontEntry .__new__ (FontEntry )
851
860
r .__dict__ .update (o )
861
+ if not os .path .isabs (r .fname ):
862
+ r .fname = os .path .join (mpl .get_data_path (), r .fname )
852
863
return r
853
864
else :
854
- raise ValueError ("don't know how to deserialize _class =%s" % cls )
865
+ raise ValueError ("don't know how to deserialize __class__ =%s" % cls )
855
866
856
867
857
868
def json_dump (data , filename ):
858
- """Dumps a data structure as JSON in the named file.
859
- Handles FontManager and its fields."""
869
+ """
870
+ Dumps a data structure as JSON in the named file.
860
871
872
+ Handles FontManager and its fields. File paths that are children of the
873
+ Matplotlib data path (typically, fonts shipped with Matplotlib) are stored
874
+ relative to that data path (to remain valid across virtualenvs).
875
+ """
861
876
with open (filename , 'w' ) as fh :
862
877
try :
863
878
json .dump (data , fh , cls = JSONEncoder , indent = 2 )
@@ -866,9 +881,13 @@ def json_dump(data, filename):
866
881
867
882
868
883
def json_load (filename ):
869
- """Loads a data structure as JSON from the named file.
870
- Handles FontManager and its fields."""
884
+ """
885
+ Loads a data structure as JSON from the named file.
871
886
887
+ Handles FontManager and its fields. Relative file paths are interpreted
888
+ as being relative to the Matplotlib data path, and transformed into
889
+ absolute paths.
890
+ """
872
891
with open (filename , 'r' ) as fh :
873
892
return json .load (fh , object_hook = _json_decode )
874
893
@@ -954,30 +973,32 @@ def __init__(self, size=None, weight='normal'):
954
973
_log .info ('font search path %s' , str (paths ))
955
974
# Load TrueType fonts and create font dictionary.
956
975
957
- self .ttffiles = findSystemFonts (paths ) + findSystemFonts ()
958
976
self .defaultFamily = {
959
977
'ttf' : 'DejaVu Sans' ,
960
978
'afm' : 'Helvetica' }
961
979
self .defaultFont = {}
962
980
963
- for fname in self .ttffiles :
964
- _log .debug ('trying fontname %s' , fname )
965
- if fname .lower ().find ('DejaVuSans.ttf' )>= 0 :
966
- self .defaultFont ['ttf' ] = fname
967
- break
968
- else :
969
- # use anything
970
- self .defaultFont ['ttf' ] = self .ttffiles [0 ]
971
-
972
- self .ttflist = createFontList (self .ttffiles )
973
-
974
- self .afmfiles = (findSystemFonts (paths , fontext = 'afm' )
975
- + findSystemFonts (fontext = 'afm' ))
976
- self .afmlist = createFontList (self .afmfiles , fontext = 'afm' )
977
- if len (self .afmfiles ):
978
- self .defaultFont ['afm' ] = self .afmfiles [0 ]
979
- else :
980
- self .defaultFont ['afm' ] = None
981
+ ttffiles = findSystemFonts (paths ) + findSystemFonts ()
982
+ self .defaultFont ['ttf' ] = next (
983
+ (fname for fname in ttffiles
984
+ if fname .lower ().endswith ("dejavusans.ttf" )),
985
+ ttffiles [0 ])
986
+ self .ttflist = createFontList (ttffiles )
987
+
988
+ afmfiles = (findSystemFonts (paths , fontext = 'afm' )
989
+ + findSystemFonts (fontext = 'afm' ))
990
+ self .afmlist = createFontList (afmfiles , fontext = 'afm' )
991
+ self .defaultFont ['afm' ] = afmfiles [0 ] if afmfiles else None
992
+
993
+ @property
994
+ @cbook .deprecated ("3.0" )
995
+ def ttffiles (self ):
996
+ return [font .fname for font in self .ttflist ]
997
+
998
+ @property
999
+ @cbook .deprecated ("3.0" )
1000
+ def afmfiles (self ):
1001
+ return [font .fname for font in self .afmlist ]
981
1002
982
1003
def get_default_weight (self ):
983
1004
"""
0 commit comments