def __init__(self, size=12.0, weight='normal'): self.__default_size = size self.__default_weight = weight paths = [rcParams['datapath']] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: if os.environ.has_key(pathname): ttfpath = os.environ[pathname] if ttfpath.find(';') >= 0: #win32 style paths.extend(ttfpath.split(';')) elif ttfpath.find(':') >= 0: # unix style paths.extend(ttfpath.split(':')) else: paths.append(ttfpath) # Load TrueType fonts and create font dictionary. self.ttffiles = findSystemFonts(paths) + findSystemFonts() for fname in self.ttffiles: if fname.lower().find('vera.ttf')>=0: self.defaultFont = fname break else: # use anything self.defaultFont = self.ttffiles[0] cache_message = \ """Saving TTF font cache for non-PS backends to %s. Delete this file to have matplotlib rebuild the cache.""" ttfpath = get_home() if ttfpath is None: ttfpath = get_data_path() ttfcache = os.path.join(ttfpath, '.ttffont.cache') try: import cPickle as pickle except ImportError: import pickle def rebuild(): self.ttfdict = createFontDict(self.ttffiles) pickle.dump(self.ttfdict, file(ttfcache, 'w')) print cache_message % ttfcache try: self.ttfdict = pickle.load(file(ttfcache)) except: rebuild() else: # verify all the cached fnames still exist; if not rebuild for fname in ttfdict_to_fnames(self.ttfdict): if not os.path.exists(fname): rebuild() break #self.ttfdict = createFontDict(self.ttffiles) # Load AFM fonts for PostScript # Only load file names at this stage, the font dictionary will be # created when needed. self.afmfiles = findSystemFonts(paths, fontext='afm') + \ findSystemFonts(fontext='afm') self.afmdict = {}
def __init__(self, size=12.0, weight="normal"): self.__default_size = size self.__default_weight = weight paths = [rcParams["datapath"]] # Create list of font paths for pathname in ["TTFPATH", "AFMPATH"]: if os.environ.has_key(pathname): ttfpath = os.environ[pathname] if ttfpath.find(";") >= 0: # win32 style paths.extend(ttfpath.split(";")) elif ttfpath.find(":") >= 0: # unix style paths.extend(ttfpath.split(":")) else: paths.append(ttfpath) verbose.report("font search path %s" % (str(paths))) # Load TrueType fonts and create font dictionary. self.ttffiles = findSystemFonts(paths) + findSystemFonts() for fname in self.ttffiles: if fname.lower().find("vera.ttf") >= 0: self.defaultFont = fname break else: # use anything self.defaultFont = self.ttffiles[0] cache_message = """Saving TTF font cache for non-PS backends to %s. Delete this file to have matplotlib rebuild the cache.""" ttfpath = get_home() if ttfpath is None: ttfpath = get_data_path() ttfcache = os.path.join(ttfpath, ".ttffont.cache") try: import cPickle as pickle except ImportError: import pickle def rebuild(): self.ttfdict = createFontDict(self.ttffiles) pickle.dump(self.ttfdict, file(ttfcache, "w")) verbose.report(cache_message % ttfcache) try: self.ttfdict = pickle.load(file(ttfcache)) except: rebuild() else: # verify all the cached fnames still exist; if not rebuild for fname in ttfdict_to_fnames(self.ttfdict): if not os.path.exists(fname): rebuild() break verbose.report("loaded ttfcache file %s" % ttfcache) # self.ttfdict = createFontDict(self.ttffiles) # Load AFM fonts for PostScript # Only load file names at this stage, the font dictionary will be # created when needed. self.afmfiles = findSystemFonts(paths, fontext="afm") + findSystemFonts(fontext="afm") self.afmdict = {}
def __init__(self, size=12.0, weight='normal'): self.__default_size = size self.__default_weight = weight paths = [rcParams['datapath']] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: if os.environ.has_key(pathname): ttfpath = os.environ[pathname] if ttfpath.find(';') >= 0: #win32 style paths.extend(ttfpath.split(';')) elif ttfpath.find(':') >= 0: # unix style paths.extend(ttfpath.split(':')) else: paths.append(ttfpath) verbose.report('font search path %s'%(str(paths))) # Load TrueType fonts and create font dictionary. self.ttffiles = findSystemFonts(paths) + findSystemFonts() for fname in self.ttffiles: verbose.report('trying fontname %s' % fname, 'debug') if fname.lower().find('vera.ttf')>=0: self.defaultFont = fname break else: # use anything self.defaultFont = self.ttffiles[0] cache_message = \ """Saving TTF font cache for non-PS backends to %s. Delete this file to have matplotlib rebuild the cache.""" oldcache = os.path.join(get_home(), 'ttffont.cache') ttfcache = os.path.join(get_configdir(), 'ttffont.cache') if os.path.exists(oldcache): print >> sys.stderr, 'Moving old ttfcache location "%s" to new location "%s"'%(oldcache, ttfcache) shutil.move(oldcache, ttfcache) try: import cPickle as pickle except ImportError: import pickle def rebuild(): self.ttfdict = createFontDict(self.ttffiles) pickle.dump(self.ttfdict, file(ttfcache, 'w')) verbose.report(cache_message % ttfcache) try: self.ttfdict = pickle.load(file(ttfcache)) except: rebuild() else: # verify all the cached fnames still exist; if not rebuild for fname in ttfdict_to_fnames(self.ttfdict): if not os.path.exists(fname): rebuild() break verbose.report('loaded ttfcache file %s'%ttfcache) #self.ttfdict = createFontDict(self.ttffiles) # Load AFM fonts for PostScript # Only load file names at this stage, the font dictionary will be # created when needed. self.afmfiles = findSystemFonts(paths, fontext='afm') + \ findSystemFonts(fontext='afm') self.afmdict = {}
class TexManager: """ Convert strings to dvi files using TeX, caching the results to a working dir """ oldpath = get_home() if oldpath is None: oldpath = get_data_path() oldcache = os.path.join(oldpath, '.tex.cache') configdir = get_configdir() texcache = os.path.join(configdir, 'tex.cache') os.environ['TEXMFOUTPUT'] = texcache if os.path.exists(oldcache): print >> sys.stderr, """\ WARNING: found a TeX cache dir in the deprecated location "%s". Moving it to the new default location "%s".""" % (oldcache, texcache) shutil.move(oldcache, texcache) dvipngVersion = None arrayd = {} postscriptd = {} pscnt = 0 serif = ('cmr', '') sans_serif = ('cmss', '') monospace = ('cmtt', '') cursive = ('pzc', r'\usepackage{chancery}') font_family = 'serif' font_info = { 'new century schoolbook': ('pnc', r'\renewcommand{\rmdefault}{pnc}'), 'bookman': ('pbk', r'\renewcommand{\rmdefault}{pbk}'), 'times': ('ptm', r'\usepackage{mathptmx}'), 'palatino': ('ppl', r'\usepackage{mathpazo}'), 'zapf chancery': ('pzc', r'\usepackage{chancery}'), 'charter': ('pch', r'\usepackage{charter}'), 'serif': ('cmr', ''), 'sans-serif': ('cmss', ''), 'helvetica': ('phv', r'\usepackage{helvet}'), 'avant garde': ('pag', r'\usepackage{avant}'), 'courier': ('pcr', r'\usepackage{courier}'), 'monospace': ('cmtt', ''), 'computer modern roman': ('cmr', ''), 'computer modern sans serif': ('cmss', ''), 'computer modern typewriter': ('cmtt', '') } def __init__(self): if not os.path.isdir(self.texcache): os.mkdir(self.texcache) if rcParams['font.family'].lower() in ('serif', 'sans-serif', 'cursive', 'monospace'): self.font_family = rcParams['font.family'].lower() else: warnings.warn( 'The %s font family is not compatible with LaTeX. serif will be used by default.' % ff) self.font_family = 'serif' self._fontconfig = self.font_family for font in rcParams['font.serif']: try: self.serif = self.font_info[font.lower()] except KeyError: continue else: break self._fontconfig += self.serif[0] for font in rcParams['font.sans-serif']: try: self.sans_serif = self.font_info[font.lower()] except KeyError: continue else: break self._fontconfig += self.sans_serif[0] for font in rcParams['font.monospace']: try: self.monospace = self.font_info[font.lower()] except KeyError: continue else: break self._fontconfig += self.monospace[0] for font in rcParams['font.cursive']: try: self.cursive = self.font_info[font.lower()] except KeyError: continue else: break self._fontconfig += self.cursive[0] # The following packages and commands need to be included in the latex # file's preamble: cmd = [self.serif[1], self.sans_serif[1], self.monospace[1]] if self.font_family == 'cursive': cmd.append(self.cursive[1]) while r'\usepackage{type1cm}' in cmd: cmd.remove(r'\usepackage{type1cm}') cmd = '\n'.join(cmd) self._font_preamble = '\n'.join( [r'\usepackage{type1cm}', cmd, r'\usepackage{textcomp}']) def get_prefix(self, tex, fontsize): s = tex + self._fontconfig + '%f' % fontsize return md5.md5(s).hexdigest() def get_font_config(self): return self._fontconfig def get_font_preamble(self): return self._font_preamble def get_tex_command(self, tex, fname, fontsize): fh = file(fname, 'w') fontcmd = { 'sans-serif': r'{\sffamily %s}', 'monospace': r'{\ttfamily %s}' }.get(self.font_family, r'{\rmfamily %s}') tex = fontcmd % tex s = r"""\documentclass[10pt]{article} %s \setlength{\paperwidth}{72in} \setlength{\paperheight}{72in} \pagestyle{empty} \begin{document} \fontsize{%f}{%f}%s \end{document} """ % (self._font_preamble, fontsize, fontsize * 1.25, tex) fh.write(s) fh.close() command = 'latex -interaction=nonstopmode "%s"' % fname return command def make_dvi(self, tex, fontsize, force=0): if debug: force = True prefix = self.get_prefix(tex, fontsize) fname = os.path.join(self.texcache, prefix + '.tex') dvibase = prefix + '.dvi' dvifile = os.path.join(self.texcache, dvibase) if force or not os.path.exists(dvifile): command = self.get_tex_command(tex, fname, fontsize) verbose.report(command, 'debug-annoying') stdin, stdout, stderr = os.popen3(command) verbose.report(stdout.read(), 'debug-annoying') err = stderr.read() if err: verbose.report(err, 'helpful') # tex will put it's output in the current dir if possible, and # if not in TEXMFOUTPUT. So check for existence in current # dir and move it if necessary and then cleanup if os.path.exists(dvibase): shutil.move(dvibase, dvifile) for fname in glob.glob(prefix + '*'): os.remove(fname) return dvifile def make_png(self, tex, fontsize, force=0): if debug: force = True dvifile = self.make_dvi(tex, fontsize) prefix = self.get_prefix(tex, fontsize) pngfile = os.path.join(self.texcache, '%s.png' % prefix) self.get_dvipng_version() # raises if dvipng is not up-to-date #print 'makepng', prefix, dvifile, pngfile #command = 'dvipng -bg Transparent -fg "rgb 0.0 0.0 0.0" -D %d -T tight -o "%s" "%s"'% (dpi, pngfile, dvifile) command = 'dvipng -bg Transparent -D 80 -T tight -o "%s" "%s"' % ( pngfile, dvifile) #assume white bg #command = "dvipng -bg 'rgb 1.0 1.0 1.0' -fg 'rgb 0.0 0.0 0.0' -D %d -T tight -o %s %s"% (dpi, pngfile, dvifile) # assume gray bg #command = "dvipng -bg 'rgb 0.75 0.75 .75' -fg 'rgb 0.0 0.0 0.0' -D %d -T tight -o %s %s"% (dpi, pngfile, dvifile) # see get_rgba for a discussion of the background if force or not os.path.exists(pngfile): verbose.report(command, 'debug-annoying') stdin, stdout, stderr = os.popen3(command) verbose.report(stdout.read(), 'debug-annoying') err = stderr.read() if err: verbose.report(err, 'helpful') return pngfile def make_ps(self, tex, fontsize, force=0): if debug: force = True dvifile = self.make_dvi(tex, fontsize) prefix = self.get_prefix(tex, fontsize) psfile = os.path.join(self.texcache, '%s.epsf' % prefix) if not os.path.exists(psfile): command = 'dvips -q -E -o "%s" "%s"' % (psfile, dvifile) verbose.report(command, 'debug-annoying') stdin, stdout, stderr = os.popen3(command) verbose.report(stdout.read(), 'debug-annoying') err = stderr.read() if err: verbose.report(err, 'helpful') return psfile def get_ps_bbox(self, tex, fontsize): key = tex val = self.postscriptd.get(key) if val is not None: return val psfile = self.make_ps(tex, fontsize) ps = file(psfile).read() for line in ps.split('\n'): if line.startswith('%%BoundingBox:'): return [int(val) for val in line.split()[1:]] raise RuntimeError('Could not parse %s' % psfile) def __get_ps(self, tex, fontsize=10, rgb=(0, 0, 0)): """ Return bbox, header, texps for tex string via make_ps """ # this is badly broken and safe to ignore. key = tex, fontsize, dpi, rgb val = self.postscriptd.get(key) if val is not None: return val psfile = self.make_ps(tex, fontsize) ps = file(psfile).read() # parse the ps bbox = None header = [] tex = [] inheader = False texon = False fonts = [] infont = False replaced = {} for line in ps.split('\n'): if line.startswith('%%EndProlog'): inheader = False if line.startswith('%%Trailer'): break if line.startswith('%%BoundingBox:'): bbox = [int(val) for val in line.split()[1:]] continue if line.startswith('%%BeginFont:'): fontname = line.split()[-1].strip() newfontname = fontname + str(self.pscnt) replaced[fontname] = newfontname thisfont = [line] infont = True continue if line.startswith('%%EndFont'): thisfont.append('%%EndFont\n') fonts.append('\n'.join(thisfont).replace( fontname, newfontname)) thisfont = [] infont = False continue if infont: thisfont.append(line) continue if line.startswith('%%BeginProcSet:'): inheader = True if inheader: header.append(line) if line.startswith('%%EndSetup'): assert (not inheader) texon = True continue if texon: line = line.replace('eop end', 'end') tex.append(line) def clean(s): for k, v in replaced.items(): s = s.replace(k, v) return s header.append('\n') tex.append('\n') if bbox is None: raise RuntimeError('Failed to parse dvips file: %s' % psfile) replaced['TeXDict'] = 'TeXDict%d' % self.pscnt header = clean('\n'.join(header)) tex = clean('\n'.join(tex)) fonts = '\n'.join(fonts) val = bbox, header, fonts, tex self.postscriptd[key] = val self.pscnt += 1 return val def get_rgba(self, tex, fontsize=None, rgb=(0, 0, 0)): """ Return tex string as an rgba array """ # dvipng assumes a constant background, whereas we want to # overlay these rasters with antialiasing over arbitrary # backgrounds that may have other figure elements under them. # When you set dvipng -bg Transparent, it actually makes the # alpha channel 1 and does the background compositing and # antialiasing itself and puts the blended data in the rgb # channels. So what we do is extract the alpha information # from the red channel, which is a blend of the default dvipng # background (white) and foreground (black). So the amount of # red (or green or blue for that matter since white and black # blend to a grayscale) is the alpha intensity. Once we # extract the correct alpha information, we assign it to the # alpha channel properly and let the users pick their rgb. In # this way, we can overlay tex strings on arbitrary # backgrounds with antialiasing # # red = alpha*red_foreground + (1-alpha)*red_background # Since the foreground is black (0) and the background is # white (1) this reduces to red = 1-alpha or alpha = 1-red if not fontsize: fontsize = rcParams['font.size'] r, g, b = rgb key = tex, fontsize, tuple(rgb) Z = self.arrayd.get(key) if Z is None: # force=True to skip cacheing while debugging pngfile = self.make_png(tex, fontsize, force=False) X = readpng(pngfile) vers = self.get_dvipng_version() #print 'dvipng version', vers if vers < '1.6' or rcParams['text.dvipnghack']: # hack the alpha channel as described in comment above alpha = sqrt(1 - X[:, :, 0]) else: alpha = X[:, :, -1] Z = zeros(X.shape, Float) Z[:, :, 0] = r Z[:, :, 1] = g Z[:, :, 2] = b Z[:, :, 3] = alpha self.arrayd[key] = Z return Z def get_dvipng_version(self): if self.dvipngVersion is not None: return self.dvipngVersion sin, sout = os.popen2('dvipng --version') for line in sout.readlines(): if line.startswith('dvipng '): self.dvipngVersion = line.split()[-1] return self.dvipngVersion raise RuntimeError('Could not obtain dvipng version')