612 lines
23 KiB
Python
Executable File
612 lines
23 KiB
Python
Executable File
"""
|
|
This module supports embedded TeX expressions in matplotlib via dvipng
|
|
and dvips for the raster and postscript backends. The tex and
|
|
dvipng/dvips information is cached in ~/.matplotlib/tex.cache for reuse between
|
|
sessions
|
|
|
|
Requirements:
|
|
|
|
* latex
|
|
* \*Agg backends: dvipng
|
|
* PS backend: latex w/ psfrag, dvips, and Ghostscript 8.51
|
|
(older versions do not work properly)
|
|
|
|
Backends:
|
|
|
|
* \*Agg
|
|
* PS
|
|
* PDF
|
|
|
|
For raster output, you can get RGBA numpy arrays from TeX expressions
|
|
as follows::
|
|
|
|
texmanager = TexManager()
|
|
s = '\\TeX\\ is Number $\\displaystyle\\sum_{n=1}^\\infty\\frac{-e^{i\pi}}{2^n}$!'
|
|
Z = self.texmanager.get_rgba(s, size=12, dpi=80, rgb=(1,0,0))
|
|
|
|
To enable tex rendering of all text in your matplotlib figure, set
|
|
text.usetex in your matplotlibrc file (http://matplotlib.sf.net/matplotlibrc)
|
|
or include these two lines in your script::
|
|
|
|
from matplotlib import rc
|
|
rc('text', usetex=True)
|
|
|
|
"""
|
|
|
|
import copy, glob, os, shutil, sys, warnings
|
|
from subprocess import Popen, PIPE, STDOUT
|
|
|
|
try:
|
|
from hashlib import md5
|
|
except ImportError:
|
|
from md5 import md5 #Deprecated in 2.5
|
|
|
|
import distutils.version
|
|
import numpy as np
|
|
import matplotlib as mpl
|
|
from matplotlib import rcParams
|
|
from matplotlib._png import read_png
|
|
import matplotlib.dviread as dviread
|
|
import re
|
|
|
|
DEBUG = False
|
|
|
|
if sys.platform.startswith('win'): cmd_split = '&'
|
|
else: cmd_split = ';'
|
|
|
|
def dvipng_hack_alpha():
|
|
p = Popen('dvipng -version', shell=True, stdin=PIPE, stdout=PIPE,
|
|
stderr=STDOUT, close_fds=(sys.platform!='win32'))
|
|
stdin, stdout = p.stdin, p.stdout
|
|
for line in stdout:
|
|
if line.startswith('dvipng '):
|
|
version = line.split()[-1]
|
|
mpl.verbose.report('Found dvipng version %s'% version,
|
|
'helpful')
|
|
version = distutils.version.LooseVersion(version)
|
|
return version < distutils.version.LooseVersion('1.6')
|
|
mpl.verbose.report('No dvipng was found', 'helpful')
|
|
return False
|
|
|
|
|
|
class TexManager:
|
|
|
|
"""
|
|
Convert strings to dvi files using TeX, caching the results to a
|
|
working dir
|
|
"""
|
|
|
|
oldpath = mpl.get_home()
|
|
if oldpath is None: oldpath = mpl.get_data_path()
|
|
oldcache = os.path.join(oldpath, '.tex.cache')
|
|
|
|
configdir = mpl.get_configdir()
|
|
texcache = os.path.join(configdir, 'tex.cache')
|
|
|
|
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)
|
|
if not os.path.exists(texcache):
|
|
os.mkdir(texcache)
|
|
|
|
_dvipng_hack_alpha = None
|
|
#_dvipng_hack_alpha = dvipng_hack_alpha()
|
|
# mappable cache of
|
|
rgba_arrayd = {}
|
|
grey_arrayd = {}
|
|
postscriptd = {}
|
|
pscnt = 0
|
|
|
|
serif = ('cmr', '')
|
|
sans_serif = ('cmss', '')
|
|
monospace = ('cmtt', '')
|
|
cursive = ('pzc', r'\usepackage{chancery}')
|
|
font_family = 'serif'
|
|
font_families = ('serif', 'sans-serif', 'cursive', 'monospace')
|
|
|
|
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}'),
|
|
'cursive': ('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', '')}
|
|
|
|
_rc_cache = None
|
|
_rc_cache_keys = ('text.latex.preamble', )\
|
|
+ tuple(['font.'+n for n in ('family', ) + font_families])
|
|
|
|
def __init__(self):
|
|
|
|
if not os.path.isdir(self.texcache):
|
|
os.mkdir(self.texcache)
|
|
ff = rcParams['font.family'].lower()
|
|
if ff in self.font_families:
|
|
self.font_family = ff
|
|
else:
|
|
mpl.verbose.report('The %s font family is not compatible with LaTeX. serif will be used by default.' % ff, 'helpful')
|
|
self.font_family = 'serif'
|
|
|
|
fontconfig = [self.font_family]
|
|
for font_family, font_family_attr in \
|
|
[(ff, ff.replace('-', '_')) for ff in self.font_families]:
|
|
for font in rcParams['font.'+font_family]:
|
|
if font.lower() in self.font_info:
|
|
found_font = self.font_info[font.lower()]
|
|
setattr(self, font_family_attr,
|
|
self.font_info[font.lower()])
|
|
if DEBUG:
|
|
print 'family: %s, font: %s, info: %s'%(font_family,
|
|
font, self.font_info[font.lower()])
|
|
break
|
|
else:
|
|
if DEBUG: print '$s font is not compatible with usetex'
|
|
else:
|
|
mpl.verbose.report('No LaTeX-compatible font found for the %s font family in rcParams. Using default.' % ff, 'helpful')
|
|
setattr(self, font_family_attr, self.font_info[font_family])
|
|
fontconfig.append(getattr(self, font_family_attr)[0])
|
|
self._fontconfig = ''.join(fontconfig)
|
|
|
|
# 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_basefile(self, tex, fontsize, dpi=None):
|
|
"""
|
|
returns a filename based on a hash of the string, fontsize, and dpi
|
|
"""
|
|
s = ''.join([tex, self.get_font_config(), '%f'%fontsize,
|
|
self.get_custom_preamble(), str(dpi or '')])
|
|
# make sure hash is consistent for all strings, regardless of encoding:
|
|
bytes = unicode(s).encode('utf-8')
|
|
return os.path.join(self.texcache, md5(bytes).hexdigest())
|
|
|
|
def get_font_config(self):
|
|
"""Reinitializes self if relevant rcParams on have changed."""
|
|
if self._rc_cache is None:
|
|
self._rc_cache = dict([(k,None) for k in self._rc_cache_keys])
|
|
changed = [par for par in self._rc_cache_keys if rcParams[par] != \
|
|
self._rc_cache[par]]
|
|
if changed:
|
|
if DEBUG: print 'DEBUG following keys changed:', changed
|
|
for k in changed:
|
|
if DEBUG:
|
|
print 'DEBUG %-20s: %-10s -> %-10s' % \
|
|
(k, self._rc_cache[k], rcParams[k])
|
|
# deepcopy may not be necessary, but feels more future-proof
|
|
self._rc_cache[k] = copy.deepcopy(rcParams[k])
|
|
if DEBUG: print 'DEBUG RE-INIT\nold fontconfig:', self._fontconfig
|
|
self.__init__()
|
|
if DEBUG: print 'DEBUG fontconfig:', self._fontconfig
|
|
return self._fontconfig
|
|
|
|
def get_font_preamble(self):
|
|
"""
|
|
returns a string containing font configuration for the tex preamble
|
|
"""
|
|
return self._font_preamble
|
|
|
|
def get_custom_preamble(self):
|
|
"""returns a string containing user additions to the tex preamble"""
|
|
return '\n'.join(rcParams['text.latex.preamble'])
|
|
|
|
def _get_shell_cmd(self, *args):
|
|
"""
|
|
On windows, changing directories can be complicated by the presence of
|
|
multiple drives. get_shell_cmd deals with this issue.
|
|
"""
|
|
if sys.platform == 'win32':
|
|
command = ['%s'% os.path.splitdrive(self.texcache)[0]]
|
|
else:
|
|
command = []
|
|
command.extend(args)
|
|
return ' && '.join(command)
|
|
|
|
def make_tex(self, tex, fontsize):
|
|
"""
|
|
Generate a tex file to render the tex string at a specific font size
|
|
|
|
returns the file name
|
|
"""
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
texfile = '%s.tex'%basefile
|
|
fh = file(texfile, 'w')
|
|
custom_preamble = self.get_custom_preamble()
|
|
fontcmd = {'sans-serif' : r'{\sffamily %s}',
|
|
'monospace' : r'{\ttfamily %s}'}.get(self.font_family,
|
|
r'{\rmfamily %s}')
|
|
tex = fontcmd % tex
|
|
|
|
if rcParams['text.latex.unicode']:
|
|
unicode_preamble = """\usepackage{ucs}
|
|
\usepackage[utf8x]{inputenc}"""
|
|
else:
|
|
unicode_preamble = ''
|
|
|
|
s = r"""\documentclass{article}
|
|
%s
|
|
%s
|
|
%s
|
|
\usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry}
|
|
\pagestyle{empty}
|
|
\begin{document}
|
|
\fontsize{%f}{%f}%s
|
|
\end{document}
|
|
""" % (self._font_preamble, unicode_preamble, custom_preamble,
|
|
fontsize, fontsize*1.25, tex)
|
|
if rcParams['text.latex.unicode']:
|
|
fh.write(s.encode('utf8'))
|
|
else:
|
|
try:
|
|
fh.write(s)
|
|
except UnicodeEncodeError, err:
|
|
mpl.verbose.report("You are using unicode and latex, but have "
|
|
"not enabled the matplotlib 'text.latex.unicode' "
|
|
"rcParam.", 'helpful')
|
|
raise
|
|
|
|
fh.close()
|
|
|
|
return texfile
|
|
|
|
|
|
_re_vbox = re.compile(r"MatplotlibBox:\(([\d.]+)pt\+([\d.]+)pt\)x([\d.]+)pt")
|
|
|
|
def make_tex_preview(self, tex, fontsize):
|
|
"""
|
|
Generate a tex file to render the tex string at a specific
|
|
font size. It uses the preview.sty to determin the dimension
|
|
(width, height, descent) of the output.
|
|
|
|
returns the file name
|
|
"""
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
texfile = '%s.tex'%basefile
|
|
fh = file(texfile, 'w')
|
|
custom_preamble = self.get_custom_preamble()
|
|
fontcmd = {'sans-serif' : r'{\sffamily %s}',
|
|
'monospace' : r'{\ttfamily %s}'}.get(self.font_family,
|
|
r'{\rmfamily %s}')
|
|
tex = fontcmd % tex
|
|
|
|
if rcParams['text.latex.unicode']:
|
|
unicode_preamble = """\usepackage{ucs}
|
|
\usepackage[utf8x]{inputenc}"""
|
|
else:
|
|
unicode_preamble = ''
|
|
|
|
|
|
|
|
# newbox, setbox, immediate, etc. are used to find the box
|
|
# extent of the rendered text.
|
|
|
|
|
|
s = r"""\documentclass{article}
|
|
%s
|
|
%s
|
|
%s
|
|
\usepackage[active,showbox,tightpage]{preview}
|
|
\usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry}
|
|
|
|
%% we override the default showbox as it is treated as an error and makes
|
|
%% the exit status not zero
|
|
\def\showbox#1{\immediate\write16{MatplotlibBox:(\the\ht#1+\the\dp#1)x\the\wd#1}}
|
|
|
|
\begin{document}
|
|
\begin{preview}
|
|
{\fontsize{%f}{%f}%s}
|
|
\end{preview}
|
|
\end{document}
|
|
""" % (self._font_preamble, unicode_preamble, custom_preamble,
|
|
fontsize, fontsize*1.25, tex)
|
|
if rcParams['text.latex.unicode']:
|
|
fh.write(s.encode('utf8'))
|
|
else:
|
|
try:
|
|
fh.write(s)
|
|
except UnicodeEncodeError, err:
|
|
mpl.verbose.report("You are using unicode and latex, but have "
|
|
"not enabled the matplotlib 'text.latex.unicode' "
|
|
"rcParam.", 'helpful')
|
|
raise
|
|
|
|
fh.close()
|
|
|
|
return texfile
|
|
|
|
|
|
def make_dvi(self, tex, fontsize):
|
|
"""
|
|
generates a dvi file containing latex's layout of tex string
|
|
|
|
returns the file name
|
|
"""
|
|
|
|
|
|
if rcParams['text.latex.preview']:
|
|
return self.make_dvi_preview(tex, fontsize)
|
|
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
dvifile = '%s.dvi'% basefile
|
|
|
|
if DEBUG or not os.path.exists(dvifile):
|
|
texfile = self.make_tex(tex, fontsize)
|
|
outfile = basefile+'.output'
|
|
command = self._get_shell_cmd('cd "%s"'% self.texcache,
|
|
'latex -interaction=nonstopmode %s > "%s"'\
|
|
%(os.path.split(texfile)[-1], outfile))
|
|
mpl.verbose.report(command, 'debug')
|
|
exit_status = os.system(command)
|
|
try:
|
|
fh = file(outfile)
|
|
report = fh.read()
|
|
fh.close()
|
|
except IOError:
|
|
report = 'No latex error report available.'
|
|
try:
|
|
os.stat(dvifile)
|
|
exists = True
|
|
except OSError:
|
|
exists = False
|
|
if exit_status or not exists:
|
|
raise RuntimeError(('LaTeX was not able to process the following \
|
|
string:\n%s\nHere is the full report generated by LaTeX: \n\n'% repr(tex)) + report)
|
|
else: mpl.verbose.report(report, 'debug')
|
|
for fname in glob.glob(basefile+'*'):
|
|
if fname.endswith('dvi'): pass
|
|
elif fname.endswith('tex'): pass
|
|
else:
|
|
try: os.remove(fname)
|
|
except OSError: pass
|
|
|
|
return dvifile
|
|
|
|
|
|
def make_dvi_preview(self, tex, fontsize):
|
|
"""
|
|
generates a dvi file containing latex's layout of tex
|
|
string. It calls make_tex_preview() method and store the size
|
|
information (width, height, descent) in a separte file.
|
|
|
|
returns the file name
|
|
"""
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
dvifile = '%s.dvi'% basefile
|
|
baselinefile = '%s.baseline'% basefile
|
|
|
|
if DEBUG or not os.path.exists(dvifile) or \
|
|
not os.path.exists(baselinefile):
|
|
texfile = self.make_tex_preview(tex, fontsize)
|
|
outfile = basefile+'.output'
|
|
command = self._get_shell_cmd('cd "%s"'% self.texcache,
|
|
'latex -interaction=nonstopmode %s > "%s"'\
|
|
%(os.path.split(texfile)[-1], outfile))
|
|
mpl.verbose.report(command, 'debug')
|
|
exit_status = os.system(command)
|
|
try:
|
|
fh = file(outfile)
|
|
report = fh.read()
|
|
fh.close()
|
|
|
|
except IOError:
|
|
report = 'No latex error report available.'
|
|
if exit_status:
|
|
raise RuntimeError(('LaTeX was not able to process the following \
|
|
string:\n%s\nHere is the full report generated by LaTeX: \n\n'% repr(tex)) + report)
|
|
else: mpl.verbose.report(report, 'debug')
|
|
|
|
# find the box extent information in the latex output
|
|
# file and store them in ".baseline" file
|
|
m = TexManager._re_vbox.search(report)
|
|
open(basefile+'.baseline',"w").write(" ".join(m.groups()))
|
|
|
|
for fname in glob.glob(basefile+'*'):
|
|
if fname.endswith('dvi'): pass
|
|
elif fname.endswith('tex'): pass
|
|
elif fname.endswith('baseline'): pass
|
|
else:
|
|
try: os.remove(fname)
|
|
except OSError: pass
|
|
|
|
return dvifile
|
|
|
|
def make_png(self, tex, fontsize, dpi):
|
|
"""
|
|
generates a png file containing latex's rendering of tex string
|
|
|
|
returns the filename
|
|
"""
|
|
basefile = self.get_basefile(tex, fontsize, dpi)
|
|
pngfile = '%s.png'% basefile
|
|
|
|
# see get_rgba for a discussion of the background
|
|
if DEBUG or not os.path.exists(pngfile):
|
|
dvifile = self.make_dvi(tex, fontsize)
|
|
outfile = basefile+'.output'
|
|
command = self._get_shell_cmd('cd "%s"' % self.texcache,
|
|
'dvipng -bg Transparent -D %s -T tight -o \
|
|
"%s" "%s" > "%s"'%(dpi, os.path.split(pngfile)[-1],
|
|
os.path.split(dvifile)[-1], outfile))
|
|
mpl.verbose.report(command, 'debug')
|
|
exit_status = os.system(command)
|
|
try:
|
|
fh = file(outfile)
|
|
report = fh.read()
|
|
fh.close()
|
|
except IOError:
|
|
report = 'No dvipng error report available.'
|
|
if exit_status:
|
|
raise RuntimeError('dvipng was not able to \
|
|
process the following file:\n%s\nHere is the full report generated by dvipng: \
|
|
\n\n'% dvifile + report)
|
|
else: mpl.verbose.report(report, 'debug')
|
|
try: os.remove(outfile)
|
|
except OSError: pass
|
|
|
|
return pngfile
|
|
|
|
def make_ps(self, tex, fontsize):
|
|
"""
|
|
generates a postscript file containing latex's rendering of tex string
|
|
|
|
returns the file name
|
|
"""
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
psfile = '%s.epsf'% basefile
|
|
|
|
if DEBUG or not os.path.exists(psfile):
|
|
dvifile = self.make_dvi(tex, fontsize)
|
|
outfile = basefile+'.output'
|
|
command = self._get_shell_cmd('cd "%s"'% self.texcache,
|
|
'dvips -q -E -o "%s" "%s" > "%s"'\
|
|
%(os.path.split(psfile)[-1],
|
|
os.path.split(dvifile)[-1], outfile))
|
|
mpl.verbose.report(command, 'debug')
|
|
exit_status = os.system(command)
|
|
fh = file(outfile)
|
|
if exit_status:
|
|
raise RuntimeError('dvipng was not able to \
|
|
process the flowing file:\n%s\nHere is the full report generated by dvipng: \
|
|
\n\n'% dvifile + fh.read())
|
|
else: mpl.verbose.report(fh.read(), 'debug')
|
|
fh.close()
|
|
os.remove(outfile)
|
|
|
|
return psfile
|
|
|
|
def get_ps_bbox(self, tex, fontsize):
|
|
"""
|
|
returns a list containing the postscript bounding box for latex's
|
|
rendering of the tex string
|
|
"""
|
|
psfile = self.make_ps(tex, fontsize)
|
|
ps = file(psfile)
|
|
for line in ps:
|
|
if line.startswith('%%BoundingBox:'):
|
|
return [int(val) for val in line.split()[1:]]
|
|
raise RuntimeError('Could not parse %s'%psfile)
|
|
|
|
def get_grey(self, tex, fontsize=None, dpi=None):
|
|
"""returns the alpha channel"""
|
|
key = tex, self.get_font_config(), fontsize, dpi
|
|
alpha = self.grey_arrayd.get(key)
|
|
|
|
if alpha is None:
|
|
pngfile = self.make_png(tex, fontsize, dpi)
|
|
X = read_png(os.path.join(self.texcache, pngfile))
|
|
|
|
if rcParams['text.dvipnghack'] is not None:
|
|
hack = rcParams['text.dvipnghack']
|
|
else:
|
|
if TexManager._dvipng_hack_alpha is None:
|
|
TexManager._dvipng_hack_alpha = dvipng_hack_alpha()
|
|
hack = TexManager._dvipng_hack_alpha
|
|
|
|
|
|
if hack:
|
|
# hack the alpha channel
|
|
# dvipng assumed 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
|
|
#alpha = npy.sqrt(1-X[:,:,0]) # should this be sqrt here?
|
|
alpha = 1-X[:,:,0]
|
|
else:
|
|
alpha = X[:,:,-1]
|
|
|
|
self.grey_arrayd[key] = alpha
|
|
return alpha
|
|
|
|
|
|
def get_rgba(self, tex, fontsize=None, dpi=None, rgb=(0,0,0)):
|
|
"""
|
|
Returns latex's rendering of the tex string as an rgba array
|
|
"""
|
|
if not fontsize: fontsize = rcParams['font.size']
|
|
if not dpi: dpi = rcParams['savefig.dpi']
|
|
r,g,b = rgb
|
|
key = tex, self.get_font_config(), fontsize, dpi, tuple(rgb)
|
|
Z = self.rgba_arrayd.get(key)
|
|
|
|
if Z is None:
|
|
alpha = self.get_grey(tex, fontsize, dpi)
|
|
|
|
Z = np.zeros((alpha.shape[0], alpha.shape[1], 4), np.float)
|
|
|
|
Z[:,:,0] = r
|
|
Z[:,:,1] = g
|
|
Z[:,:,2] = b
|
|
Z[:,:,3] = alpha
|
|
self.rgba_arrayd[key] = Z
|
|
|
|
return Z
|
|
|
|
|
|
def get_text_width_height_descent(self, tex, fontsize, renderer=None):
|
|
"""
|
|
return width, heigth and descent of the text.
|
|
"""
|
|
if tex.strip() == '':
|
|
return 0, 0, 0
|
|
|
|
if renderer:
|
|
dpi_fraction = renderer.points_to_pixels(1.)
|
|
else:
|
|
dpi_fraction = 1.
|
|
|
|
if rcParams['text.latex.preview']:
|
|
# use preview.sty
|
|
basefile = self.get_basefile(tex, fontsize)
|
|
baselinefile = '%s.baseline'% basefile
|
|
|
|
|
|
if DEBUG or not os.path.exists(baselinefile):
|
|
dvifile = self.make_dvi_preview(tex, fontsize)
|
|
|
|
l = open(baselinefile).read().split()
|
|
height, depth, width = [float(l1)*dpi_fraction for l1 in l]
|
|
return width, height+depth, depth
|
|
|
|
else:
|
|
# use dviread. It sometimes returns a wrong descent.
|
|
dvifile = self.make_dvi(tex, fontsize)
|
|
dvi = dviread.Dvi(dvifile, 72*dpi_fraction)
|
|
page = iter(dvi).next()
|
|
dvi.close()
|
|
# A total height (including the descent) needs to be returned.
|
|
return page.width, page.height+page.descent, page.descent
|