3333
3434"""
3535
36- import glob , md5 , os , shutil , sys , warnings
37- from tempfile import gettempdir
38- from matplotlib import get_configdir , get_home , get_data_path , \
39- rcParams , verbose
36+ import copy , glob , md5 , os , shutil , sys , warnings
37+ import numpy as npy
38+ import matplotlib as mpl
39+ from matplotlib import rcParams
4040from matplotlib ._image import readpng
41- from matplotlib .numerix import ravel , where , array , \
42- zeros , Float , absolute , nonzero , sqrt
4341
44- debug = False
42+ DEBUG = False
4543
4644if sys .platform .startswith ('win' ): cmd_split = '&'
4745else : cmd_split = ';'
4846
49-
5047def get_dvipng_version ():
5148 stdin , stdout = os .popen4 ('dvipng -version' )
5249 for line in stdout :
5350 if line .startswith ('dvipng ' ):
5451 version = line .split ()[- 1 ]
55- verbose .report ('Found dvipng version %s' % version ,
52+ mpl . verbose .report ('Found dvipng version %s' % version ,
5653 'helpful' )
5754 return version
5855 raise RuntimeError ('Could not obtain dvipng version' )
@@ -64,14 +61,13 @@ class TexManager:
6461 working dir
6562 """
6663
67- oldpath = get_home ()
68- if oldpath is None : oldpath = get_data_path ()
64+ oldpath = mpl . get_home ()
65+ if oldpath is None : oldpath = mpl . get_data_path ()
6966 oldcache = os .path .join (oldpath , '.tex.cache' )
7067
71- configdir = get_configdir ()
68+ configdir = mpl . get_configdir ()
7269 texcache = os .path .join (configdir , 'tex.cache' )
7370
74-
7571 if os .path .exists (oldcache ):
7672 print >> sys .stderr , """\
7773 WARNING: found a TeX cache dir in the deprecated location "%s".
@@ -82,6 +78,7 @@ class TexManager:
8278
8379 dvipngVersion = get_dvipng_version ()
8480
81+ # mappable cache of
8582 arrayd = {}
8683 postscriptd = {}
8784 pscnt = 0
@@ -91,12 +88,15 @@ class TexManager:
9188 monospace = ('cmtt' , '' )
9289 cursive = ('pzc' , r'\usepackage{chancery}' )
9390 font_family = 'serif'
91+ font_families = ('serif' , 'sans-serif' , 'cursive' , 'monospace' )
9492
95- font_info = {'new century schoolbook' : ('pnc' , r'\renewcommand{\rmdefault}{pnc}' ),
93+ font_info = {'new century schoolbook' : ('pnc' ,
94+ r'\renewcommand{\rmdefault}{pnc}' ),
9695 'bookman' : ('pbk' , r'\renewcommand{\rmdefault}{pbk}' ),
9796 'times' : ('ptm' , r'\usepackage{mathptmx}' ),
9897 'palatino' : ('ppl' , r'\usepackage{mathpazo}' ),
9998 'zapf chancery' : ('pzc' , r'\usepackage{chancery}' ),
99+ 'cursive' : ('pzc' , r'\usepackage{chancery}' ),
100100 'charter' : ('pch' , r'\usepackage{charter}' ),
101101 'serif' : ('cmr' , '' ),
102102 'sans-serif' : ('cmss' , '' ),
@@ -107,49 +107,37 @@ class TexManager:
107107 'computer modern roman' : ('cmr' , '' ),
108108 'computer modern sans serif' : ('cmss' , '' ),
109109 'computer modern typewriter' : ('cmtt' , '' )}
110+
111+ _rc_cache = None
112+ _rc_cache_keys = ('text.latex.preamble' , )\
113+ + tuple ('font.' + n for n in ('family' , ) + font_families )
110114
111115 def __init__ (self ):
112116
113117 if not os .path .isdir (self .texcache ):
114118 os .mkdir (self .texcache )
115- if rcParams ['font.family' ].lower () in ('serif' , 'sans-serif' , 'cursive' , 'monospace' ):
116- self .font_family = rcParams ['font.family' ].lower ()
119+ ff = rcParams ['font.family' ].lower ()
120+ if ff in self .font_families :
121+ self .font_family = ff
117122 else :
118123 warnings .warn ('The %s font family is not compatible with LaTeX. serif will be used by default.' % ff )
119124 self .font_family = 'serif'
120- self ._fontconfig = self .font_family
121- for font in rcParams ['font.serif' ]:
122- try :
123- self .serif = self .font_info [font .lower ()]
124- except KeyError :
125- continue
126- else :
127- break
128- self ._fontconfig += self .serif [0 ]
129- for font in rcParams ['font.sans-serif' ]:
130- try :
131- self .sans_serif = self .font_info [font .lower ()]
132- except KeyError :
133- continue
134- else :
135- break
136- self ._fontconfig += self .sans_serif [0 ]
137- for font in rcParams ['font.monospace' ]:
138- try :
139- self .monospace = self .font_info [font .lower ()]
140- except KeyError :
141- continue
142- else :
143- break
144- self ._fontconfig += self .monospace [0 ]
145- for font in rcParams ['font.cursive' ]:
146- try :
147- self .cursive = self .font_info [font .lower ()]
148- except KeyError :
149- continue
150- else :
151- break
152- self ._fontconfig += self .cursive [0 ]
125+
126+ fontconfig = [self .font_family ]
127+ for font_family , font_family_attr in \
128+ ((ff , ff .replace ('-' , '_' )) for ff in self .font_families ):
129+ for font in rcParams ['font.' + font_family ]:
130+ if DEBUG : print 'family: %s, font: %s, info: %s' % (font_family ,
131+ font , self .font_info [font .lower ()])
132+ if font .lower () in self .font_info :
133+ setattr (self , font_family_attr ,
134+ self .font_info [font .lower ()])
135+ break
136+ else :
137+ warnings .warn ('No LaTeX-compatible font found for the %s font family in rcParams. Using default.' % ff )
138+ setattr (self , font_family_attr , font_family )
139+ fontconfig .append (getattr (self , font_family_attr )[0 ])
140+ self ._fontconfig = '' .join (fontconfig )
153141
154142 # The following packages and commands need to be included in the latex
155143 # file's preamble:
@@ -158,17 +146,33 @@ def __init__(self):
158146 while r'\usepackage{type1cm}' in cmd :
159147 cmd .remove (r'\usepackage{type1cm}' )
160148 cmd = '\n ' .join (cmd )
161- self ._font_preamble = '\n ' .join ([r'\usepackage{type1cm}' ,
162- cmd ,
163- r'\usepackage{textcomp}' ])
149+ self ._font_preamble = '\n ' .join ([r'\usepackage{type1cm}' , cmd ,
150+ r'\usepackage{textcomp}' ])
164151
165152 def get_basefile (self , tex , fontsize , dpi = None ):
166- s = tex + self ._fontconfig + ('%f' % fontsize ) + self .get_custom_preamble ()
167- if dpi : s += ('%s' % dpi )
168- bytes = unicode (s ).encode ('utf-8' ) # make sure hash is consistent for all strings, regardless of encoding
153+ s = '' .join ([tex , self .get_font_config (), '%f' % fontsize ,
154+ self .get_custom_preamble (), str (dpi or '' )])
155+ # make sure hash is consistent for all strings, regardless of encoding:
156+ bytes = unicode (s ).encode ('utf-8' )
169157 return os .path .join (self .texcache , md5 .md5 (bytes ).hexdigest ())
170158
171159 def get_font_config (self ):
160+ "Reinitializes self if rcParams self depends on have changed."
161+ if self ._rc_cache is None :
162+ self ._rc_cache = dict ((k ,None ) for k in self ._rc_cache_keys )
163+ changed = [par for par in self ._rc_cache_keys if rcParams [par ] != \
164+ self ._rc_cache [par ]]
165+ if changed :
166+ if DEBUG : print 'DEBUG following keys changed:' , changed
167+ for k in changed :
168+ if DEBUG :
169+ print 'DEBUG %-20s: %-10s -> %-10s' % \
170+ (k , self ._rc_cache [k ], rcParams [k ])
171+ # deepcopy may not be necessary, but feels more future-proof
172+ self ._rc_cache [k ] = copy .deepcopy (rcParams [k ])
173+ if DEBUG : print 'DEBUG RE-INIT\n old fontconfig:' , self ._fontconfig
174+ self .__init__ ()
175+ if DEBUG : print 'DEBUG fontconfig:' , self ._fontconfig
172176 return self ._fontconfig
173177
174178 def get_font_preamble (self ):
@@ -222,34 +226,33 @@ def make_tex(self, tex, fontsize):
222226 try :
223227 fh .write (s )
224228 except UnicodeEncodeError , err :
225- verbose .report ("You are using unicode and latex, but have "
226- "not enabled the matplotlib 'text.latex.unicode' "
227- "rcParam." , 'helpful' )
229+ mpl . verbose .report ("You are using unicode and latex, but have "
230+ "not enabled the matplotlib 'text.latex.unicode' "
231+ "rcParam." , 'helpful' )
228232 raise
229233
230234 fh .close ()
231235
232236 return texfile
233237
234- def make_dvi (self , tex , fontsize , force = 0 ):
235- if debug : force = True
238+ def make_dvi (self , tex , fontsize ):
236239
237240 basefile = self .get_basefile (tex , fontsize )
238241 dvifile = '%s.dvi' % basefile
239242
240- if force or not os .path .exists (dvifile ):
243+ if DEBUG or not os .path .exists (dvifile ):
241244 texfile = self .make_tex (tex , fontsize )
242245 outfile = basefile + '.output'
243246 command = self .get_shell_cmd ('cd "%s"' % self .texcache ,
244247 'latex -interaction=nonstopmode %s > "%s"' \
245248 % (os .path .split (texfile )[- 1 ], outfile ))
246- verbose .report (command , 'debug' )
249+ mpl . verbose .report (command , 'debug' )
247250 exit_status = os .system (command )
248251 fh = file (outfile )
249252 if exit_status :
250253 raise RuntimeError (('LaTeX was not able to process the following \
251254 string:\n %s\n Here is the full report generated by LaTeX: \n \n '% repr (tex )) + fh .read ())
252- else : verbose .report (fh .read (), 'debug' )
255+ else : mpl . verbose .report (fh .read (), 'debug' )
253256 fh .close ()
254257 for fname in glob .glob (basefile + '*' ):
255258 if fname .endswith ('dvi' ): pass
@@ -258,54 +261,51 @@ def make_dvi(self, tex, fontsize, force=0):
258261
259262 return dvifile
260263
261- def make_png (self , tex , fontsize , dpi , force = 0 ):
262- if debug : force = True
263-
264+ def make_png (self , tex , fontsize , dpi ):
264265 basefile = self .get_basefile (tex , fontsize , dpi )
265266 pngfile = '%s.png' % basefile
266267
267268 # see get_rgba for a discussion of the background
268- if force or not os .path .exists (pngfile ):
269+ if DEBUG or not os .path .exists (pngfile ):
269270 dvifile = self .make_dvi (tex , fontsize )
270271 outfile = basefile + '.output'
271272 command = self .get_shell_cmd ('cd "%s"' % self .texcache ,
272273 'dvipng -bg Transparent -D %s -T tight -o \
273274 "%s" "%s" > "%s"'% (dpi , os .path .split (pngfile )[- 1 ],
274275 os .path .split (dvifile )[- 1 ], outfile ))
275- verbose .report (command , 'debug' )
276+ mpl . verbose .report (command , 'debug' )
276277 exit_status = os .system (command )
277278 fh = file (outfile )
278279 if exit_status :
279280 raise RuntimeError ('dvipng was not able to \
280281 process the flowing file:\n %s\n Here is the full report generated by dvipng: \
281282\n \n '% dvifile + fh .read ())
282- else : verbose .report (fh .read (), 'debug' )
283+ else : mpl . verbose .report (fh .read (), 'debug' )
283284 fh .close ()
284285 os .remove (outfile )
285286
286287 return pngfile
287288
288- def make_ps (self , tex , fontsize , force = 0 ):
289- if debug : force = True
289+ def make_ps (self , tex , fontsize ):
290290
291291 basefile = self .get_basefile (tex , fontsize )
292292 psfile = '%s.epsf' % basefile
293293
294- if force or not os .path .exists (psfile ):
294+ if DEBUG or not os .path .exists (psfile ):
295295 dvifile = self .make_dvi (tex , fontsize )
296296 outfile = basefile + '.output'
297297 command = self .get_shell_cmd ('cd "%s"' % self .texcache ,
298298 'dvips -q -E -o "%s" "%s" > "%s"' \
299299 % (os .path .split (psfile )[- 1 ],
300300 os .path .split (dvifile )[- 1 ], outfile ))
301- verbose .report (command , 'debug' )
301+ mpl . verbose .report (command , 'debug' )
302302 exit_status = os .system (command )
303303 fh = file (outfile )
304304 if exit_status :
305305 raise RuntimeError ('dvipng was not able to \
306306 process the flowing file:\n %s\n Here is the full report generated by dvipng: \
307307\n \n '% dvifile + fh .read ())
308- else : verbose .report (fh .read (), 'debug' )
308+ else : mpl . verbose .report (fh .read (), 'debug' )
309309 fh .close ()
310310 os .remove (outfile )
311311
@@ -346,21 +346,20 @@ def get_rgba(self, tex, fontsize=None, dpi=None, rgb=(0,0,0)):
346346 if not fontsize : fontsize = rcParams ['font.size' ]
347347 if not dpi : dpi = rcParams ['savefig.dpi' ]
348348 r ,g ,b = rgb
349- key = tex , fontsize , dpi , tuple (rgb )
349+ key = tex , self . get_font_config (), fontsize , dpi , tuple (rgb )
350350 Z = self .arrayd .get (key )
351351
352352 if Z is None :
353- # force=True to skip cacheing while debugging
354- pngfile = self .make_png (tex , fontsize , dpi , force = False )
353+ pngfile = self .make_png (tex , fontsize , dpi )
355354 X = readpng (os .path .join (self .texcache , pngfile ))
356355
357356 if (self .dvipngVersion < '1.6' ) or rcParams ['text.dvipnghack' ]:
358357 # hack the alpha channel as described in comment above
359- alpha = sqrt (1 - X [:,:,0 ])
358+ alpha = npy . sqrt (1 - X [:,:,0 ])
360359 else :
361360 alpha = X [:,:,- 1 ]
362361
363- Z = zeros (X .shape , Float )
362+ Z = npy . zeros (X .shape , npy . float )
364363 Z [:,:,0 ] = r
365364 Z [:,:,1 ] = g
366365 Z [:,:,2 ] = b
0 commit comments