Пример #1
0
    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 = {}
Пример #2
0
    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 = {}
Пример #3
0
    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 = {}
Пример #4
0
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')