133133from cStringIO import StringIO
134134from math import floor , ceil
135135from sets import Set
136- from unicodedata import category
136+ import unicodedata
137137from warnings import warn
138138
139139from numpy import inf , isinf
@@ -462,7 +462,7 @@ def _get_font(self, font):
462462
463463 cached_font = self .fonts .get (basename )
464464 if cached_font is None :
465- font = FT2Font (os . path . join ( self . basepath , basename + ".ttf" ) )
465+ font = FT2Font (basename )
466466 cached_font = self .CachedFont (font )
467467 self .fonts [basename ] = cached_font
468468 self .fonts [font .postscript_name ] = cached_font
@@ -545,15 +545,24 @@ class BakomaFonts(TruetypeFonts):
545545 """
546546 Use the Bakoma true type fonts for rendering
547547 """
548- fontmap = { 'cal' : 'cmsy10' ,
549- 'rm' : 'cmr10' ,
550- 'tt' : 'cmtt10' ,
551- 'it' : 'cmmi10' ,
552- 'bf' : 'cmb10' ,
553- 'sf' : 'cmss10' ,
554- 'ex' : 'cmex10'
555- }
556-
548+ _fontmap = { 'cal' : 'cmsy10' ,
549+ 'rm' : 'cmr10' ,
550+ 'tt' : 'cmtt10' ,
551+ 'it' : 'cmmi10' ,
552+ 'bf' : 'cmb10' ,
553+ 'sf' : 'cmss10' ,
554+ 'ex' : 'cmex10'
555+ }
556+ fontmap = {}
557+
558+ def __init__ (self , * args , ** kwargs ):
559+ TruetypeFonts .__init__ (self , * args , ** kwargs )
560+ if not len (self .fontmap ):
561+ for key , val in self ._fontmap .items ():
562+ fullpath = os .path .join (self .basepath , val + ".ttf" )
563+ self .fontmap [key ] = fullpath
564+ self .fontmap [val ] = fullpath
565+
557566 def _get_offset (self , cached_font , glyph , fontsize , dpi ):
558567 if cached_font .font .postscript_name == 'Cmex10' :
559568 return glyph .height / 64.0 / 2.0 + 256.0 / 64.0 * dpi / 72.0
@@ -564,7 +573,7 @@ def _get_offset(self, cached_font, glyph, fontsize, dpi):
564573 def _get_glyph (self , fontname , sym , fontsize ):
565574 if fontname in self .fontmap and latex_to_bakoma .has_key (sym ):
566575 basename , num = latex_to_bakoma [sym ]
567- slanted = basename == "cmmi10" or sym in self ._slanted_symbols
576+ slanted = ( basename == "cmmi10" ) or sym in self ._slanted_symbols
568577 cached_font = self ._get_font (basename )
569578 symbol_name = cached_font .font .get_glyph_name (num )
570579 num = cached_font .glyphmap [num ]
@@ -638,15 +647,24 @@ def get_sized_alternatives_for_symbol(self, fontname, sym):
638647class UnicodeFonts (TruetypeFonts ):
639648 """An abstract base class for handling Unicode fonts.
640649 """
641- fontmap = { 'cal' : 'cmsy10' ,
642- 'rm' : 'DejaVuSerif' ,
643- 'tt' : 'DejaVuSansMono' ,
644- 'it' : 'DejaVuSerif-Italic' ,
645- 'bf' : 'DejaVuSerif-Bold' ,
646- 'sf' : 'DejaVuSans' ,
647- None : 'DejaVuSerif-Italic'
648- }
649650
651+ fontmap = {}
652+
653+ def __init__ (self , * args , ** kwargs ):
654+ # This must come first so the backend's owner is set correctly
655+ if rcParams ['mathtext.fallback_to_cm' ]:
656+ self .cm_fallback = BakomaFonts (* args , ** kwargs )
657+ else :
658+ self .cm_fallback = None
659+ TruetypeFonts .__init__ (self , * args , ** kwargs )
660+ if not len (self .fontmap ):
661+ for texfont in "cal rm tt it bf sf" .split ():
662+ setting = rcParams ['mathtext.' + texfont ]
663+ family , weight , style = setting
664+ prop = FontProperties (family = family , weight = weight , style = style )
665+ font = fontManager .findfont (prop )
666+ self .fontmap [texfont ] = font
667+
650668 def _get_offset (self , cached_font , glyph , fontsize , dpi ):
651669 return 0.
652670
@@ -662,33 +680,65 @@ def _get_glyph(self, fontname, sym, fontsize):
662680 uniindex = get_unicode_index (sym [4 :])
663681 fontsize *= GROW_FACTOR
664682 else :
665- warn ("No TeX to unicode mapping for '%s'" % sym ,
683+ uniindex = ord ('?' )
684+ warn ("No TeX to unicode mapping for '%s'" % sym .encode ('ascii' , 'replace' ),
666685 MathTextWarning )
667686
668687 # Only characters in the "Letter" class should be italicized in 'it'
669- # mode. This class includes greek letters, of course.
670- if (fontname == 'it'
671- and not category (unichr (uniindex )).startswith ("L" )):
672- fontname = 'rm'
673-
674- slanted = (fontname == 'it' )
675-
676- cached_font = self ._get_font (fontname )
688+ # mode. Greek capital letters should be Roman.
677689 if found_symbol :
690+ new_fontname = fontname
691+
692+ if fontname == 'it' :
693+ unistring = unichr (uniindex )
694+ if (not unicodedata .category (unistring ).startswith ("L" )
695+ or unicodedata .name (unistring ).startswith ("GREEK CAPITAL" )):
696+ new_fontname = 'rm'
697+
698+ slanted = (new_fontname == 'it' )
699+ cached_font = self ._get_font (new_fontname )
678700 try :
679701 glyphindex = cached_font .charmap [uniindex ]
680702 except KeyError :
681703 warn ("Font '%s' does not have a glyph for '%s'" %
682- (cached_font .font .postscript_name , sym ),
704+ (cached_font .font .postscript_name , sym . encode ( 'ascii' , 'replace' ) ),
683705 MathTextWarning )
684706 found_symbol = False
685707
686708 if not found_symbol :
687- uniindex = 0xA4 # currency character, for lack of anything better
688- glyphindex = cached_font .charmap [uniindex ]
709+ if self .cm_fallback :
710+ warn ("Substituting with a symbol from the Computer Modern family." ,
711+ MathTextWarning )
712+ return self .cm_fallback ._get_glyph (fontname , sym , fontsize )
713+ else :
714+ new_fontname = fontname
715+ cached_font = self ._get_font (fontname )
716+ uniindex = 0xA4 # currency character, for lack of anything better
717+ glyphindex = cached_font .charmap [uniindex ]
718+ slanted = False
689719
690720 symbol_name = cached_font .font .get_glyph_name (glyphindex )
691721 return cached_font , uniindex , symbol_name , fontsize , slanted
722+
723+ def set_canvas_size (self , w , h ):
724+ 'Dimension the drawing canvas; may be a noop'
725+ TruetypeFonts .set_canvas_size (self , w , h )
726+ if self .cm_fallback :
727+ self .cm_fallback .set_canvas_size (w , h )
728+
729+ def get_used_characters (self ):
730+ used_characters = dict (self .used_characters )
731+ if self .cm_fallback :
732+ fallback_characters = self .cm_fallback .get_used_characters ()
733+ for key , val in fallback_characters :
734+ used_characters .setdefault (key , Set ()).update (val )
735+ return used_characters
736+
737+ def get_fonts (self ):
738+ fonts = [x .font for x in self .fonts .values ()]
739+ if self .cm_fallback :
740+ fonts .extend (self .cm_fallback .get_fonts ())
741+ return list (set (fonts ))
692742
693743class StandardPsFonts (Fonts ):
694744 """
@@ -750,7 +800,7 @@ def _get_info (self, fontname, sym, fontsize, dpi):
750800 # This class includes greek letters, so we're ok
751801 if (fontname == 'it' and
752802 (len (sym ) > 1 or
753- not category (unicode (sym )).startswith ("L" ))):
803+ not unicodedata . category (unicode (sym )).startswith ("L" ))):
754804 fontname = 'rm'
755805
756806 found_symbol = False
@@ -2302,10 +2352,10 @@ def __call__(self, s, dpi, prop, angle=0):
23022352 font_output = StandardPsFonts (prop )
23032353 else :
23042354 backend = self ._backend_mapping [self .output ]()
2305- font_output = BakomaFonts ( prop , backend )
2306- # When we have a decent Unicode font, we should test and
2307- # then make this available as an option
2308- #~ font_output = UnicodeFonts(prop, backend)
2355+ if rcParams [ 'mathtext.use_cm' ]:
2356+ font_output = BakomaFonts ( prop , backend )
2357+ else :
2358+ font_output = UnicodeFonts (prop , backend )
23092359
23102360 fontsize = prop .get_size_in_points ()
23112361 if self ._parser is None :
0 commit comments