r"""
Supported commands:
-------------------
* _, ^, to any depth
* commands for typesetting functions (\sin, \cos etc.),
* commands for changing the current font (\rm, \cal etc.),
* Space/kern commands "\ ", \thinspace
* \frac
Small TO-DO's:
--------------
* Display braces etc. \} not working (displaying wierd characters) etc.
* better placing of sub/superscripts. F_1^1y_{1_{2_{3_{4}^3}}3}1_23
* implement crampedness (or is it smth. else?). y_1 vs. y_1^1
* add better italic correction. F^1
* implement other space/kern commands
TO-DO's:
--------
* \over, \above, \choose etc.
* Add support for other backends
"""
import os
from math import fabs, floor, ceil
from matplotlib import get_data_path, rcParams
from matplotlib._mathtext_data import tex2uni
from matplotlib.ft2font import FT2Font, KERNING_DEFAULT
from matplotlib.font_manager import findSystemFonts
from copy import deepcopy
from matplotlib.cbook import Bunch
_path = get_data_path()
faces = ('mit', 'rm', 'tt', 'cal', 'nonascii')
filenamesd = {}
fonts = {}
# Filling the above dicts
for face in faces:
# The filename without the path
barefname = rcParams['mathtext.' + face]
base, ext = os.path.splitext(barefname)
if not ext:
ext = '.ttf'
barefname = base + ext
# First, we search for the font in the system font dir
for fname in findSystemFonts(fontext=ext[1:]):
if fname.endswith(barefname):
filenamesd[face] = fname
break
# We check if the for loop above had success. If it failed, we try to
# find the font in the mpl-data dir
if not face in filenamesd:
filenamesd[face] = os.path.join(_path, barefname)
fonts[face] = FT2Font(filenamesd[face])
svg_elements = Bunch(svg_glyphs=[], svg_lines=[])
esc_char = '\\'
# Grouping delimiters
begin_group_char = '{'
end_group_char = '}'
dec_delim = '.'
word_delim = ' '
mathstyles = ["display", "text", "script", "scriptscript"]
modes = ["mathmode", "displaymathmode"]
# Commands
scripts = ("_", "^")
functions = ("sin", "tan", "cos", "exp", "arctan", "arccos", "arcsin", "cot",
"lim", "log")
reserved = ("{", "}", "%", "$", "#", "~")
# Commands that change the environment (in the current scope)
setters = faces
# Maximum number of nestings (groups within groups)
max_depth = 10
#~ environment = {
#~ "mode": "mathmode",
#~ "mathstyle" : "display",
#~ "cramped" : False,
#~ # We start with zero scriptdepth (should be incremented by a Scripted
#~ # instance)
#~ "scriptdepth" : 0,
#~ "face" : None,
#~ "fontsize" : 12,
#~ "dpi" : 100,
#~ }
# _textclass can be unicode or str.
_textclass = unicode
# Exception classes
class TexParseError(Exception):
pass
# Helper classes
class Scriptfactors(dict):
"""Used for returning the factor with wich you should multiply the
fontsize to get the font size of the script
"""
_scriptfactors = {
0 : 1, # Normal text
1: 0.8, # Script
2: 0.6, # Scriptscript
# For keys > 3 returns 0.6
}
def __getitem__(self, key):
if not isinstance(key, int):
raise KeyError("Integer value needed for scriptdepth")
if key < 0:
raise KeyError("scriptdepth must be positive")
if key in self._scriptfactors:
return self._scriptfactors[key]
else:
# Maximum depth of scripts is 2 (scriptscript)
return self._scriptfactors[2]
scriptfactors = Scriptfactors()
class Environment:
"""Class used for representing the TeX environment variables"""
def __init__(self):
self.mode = "mathmode"
self.mathstyle = "display"
self.cramped = False
# We start with zero scriptdepth (should be incremented by a Scripted
# instance)
self.scriptdepth = 0
self.face = None
self.fontsize = 12
self.dpi = 100
self.output = "AGG"
def copy(self):
return deepcopy(self)
# The topmost environment
environment = Environment()
# Helper functions used by the parser
def parse_tex(texstring):
texstring = normalize_tex(texstring)
_parsed = to_list(texstring)
#_parsed = Hbox(_parsed)
return _parsed
def remove_comments(texstring):
# TO-DO
return texstring
def group_split(texstring):
"""Splits the string into three parts based on the grouping delimiters,
and returns them as a list.
"""
if texstring == begin_group_char + end_group_char:
return '', [], ''
length = len(texstring)
i = texstring.find(begin_group_char)
if i == -1:
return texstring, '', ''
pos_begin = i
count = 1
num_groups = 0
while count != 0:
i = i + 1
# First we check some things
if num_groups > max_depth:
message = "Maximum number of nestings reached. Too many groups"
raise TexParseError(message)
if i == length:
message = "Group not closed properly"
raise TexParseError(message)
if texstring[i] == end_group_char:
count -= 1
elif texstring[i] == begin_group_char:
num_groups += 1
count += 1
before = texstring[:pos_begin]
if pos_begin + 1 == i:
grouping = []
else:
grouping = texstring[pos_begin + 1:i]
after = texstring[i + 1:]
return before, grouping, after
def break_up_commands(texstring):
"""Breaks up a string (mustn't contain any groupings) into a list
of commands and pure text.
"""
result = []
if not texstring:
return result
_texstrings = texstring.split(esc_char)
for i, _texstring in enumerate(_texstrings):
_command, _puretext = split_command(_texstring)
if i == 0 and _texstrings[0]:
# Case when the first command is a not a command but text
result.extend([c for c in _command])
result.extend(_puretext)
continue
if _command:
result.append(esc_char + _command)
if _puretext:
if _puretext[0] == word_delim:
_puretext = _puretext[1:]
result.extend(_puretext)
return result
def split_command(texstring):
"""Splits a texstring into a command part and a pure text (as a list) part
"""
if not texstring:
return "", []
_puretext = []
_command, _rest = get_first_word(texstring)
if not _command:
_command = texstring[0]
_rest = texstring[1:]
_puretext = [c for c in _rest]
#~ while True:
#~ _word, _rest = get_first_word(_rest)
#~ if _word:
#~ _puretext.append(_word)
#~ if _rest:
#~ _puretext.extend(_rest[0])
#~ if len(_rest) == 1:
#~ break
#~ _rest = _rest[1:]
#~ else:
#~ break
return _command, _puretext
def get_first_word(texstring):
_word = ""
i = 0
_length = len(texstring)
if _length == 0:
return "", ""
if texstring[0].isalpha():
while _length > i and texstring[i].isalpha():
_word += texstring[i]
i = i + 1
elif texstring[0].isdigit():
while _length > i and (texstring[i].isdigit()):
_word += texstring[i]
i = i + 1
return _word, texstring[i:]
def to_list(texstring):
"""Parses the normalized tex string and returns a list. Used recursively.
"""
result = []
if not texstring:
return result
# Checking for groupings: begin_group_char...end_group_char
before, grouping, after = group_split(texstring)
#print "Before: ", before, '\n', grouping, '\n', after
if before:
result.extend(break_up_commands(before))
if grouping or grouping == []:
result.append(to_list(grouping))
if after:
result.extend(to_list(after))
return result
def normalize_tex(texstring):
"""Normalizes the whole TeX expression (that is: prepares it for
parsing)"""
texstring = remove_comments(texstring)
# Removing the escaped escape character (replacing it)
texstring = texstring.replace(esc_char + esc_char, esc_char + 'backslash ')
# Removing the escaped scope/grouping characters
texstring = texstring.replace(esc_char + begin_group_char, esc_char + 'lbrace ')
texstring = texstring.replace(esc_char + end_group_char, esc_char + 'rbrace ')
# Now we should have a clean expression, so we check if all the groupings
# are OK (every begin_group_char should have a matching end_group_char)
# TO-DO
# Replacing all space-like characters with a single space word_delim
texstring = word_delim.join(texstring.split())
# Removing unnecessary white space
texstring = word_delim.join(texstring.split())
return texstring
def is_command(item):
try:
return item.startswith(esc_char)
except AttributeError:
return False
# Helper functions used by the renderer
def get_frac_bar_height(env):
# TO-DO: Find a better way to calculate the height of the rule
c = TexCharClass(env, ".")
return (c.ymax - c.ymin)/2
def get_font(env):
env = env.copy()
# TO-DO: Perhaps this should be done somewhere else
fontsize = env.fontsize * scriptfactors[env.scriptdepth]
dpi = env.dpi
if not env.face:
env.face = "rm"
font = fonts[env.face]
font.set_size(fontsize, dpi)
return font
#~ font = FT2Font(filenamesd[face])
#~ if fonts:
#~ fonts[max(fonts.keys()) + 1] = font
#~ else:
#~ fonts[1] = font
def infer_face(env, item):
if item.isalpha():
if env.mode == "mathmode" and item < "z":
face = "mit"
else:
# TO-DO: Perhaps change to 'rm'
face = "nonascii"
elif item.isdigit():
face = "rm"
elif ord(item) < 256:
face = "rm"
else:
face = "nonascii"
return face
def get_space(env):
_env = env.copy()
if not _env.face:
_env.face = "rm"
space = TexCharClass(_env, " ")
return space
def get_kern(first, second):
# TO-DO: Something's wrong
if isinstance(first,TexCharClass) and isinstance(second, TexCharClass):
if first.env.__dict__ == second.env.__dict__:
font = get_font(first.env)
advance = -font.get_kerning(first.uniindex, second.uniindex,
KERNING_DEFAULT)/64.0
#print first.char, second.char, advance
return Kern(first.env, advance)
else:
return Kern(first.env, 0)
else:
return Kern(first.env, 0)
# Classes used for renderering
class Renderer:
"""Abstract class that implements the rendering methods"""
def __init__(self, env):
# We initialize all the values to 0.0
self.xmin, self.ymin, self.xmax, self.ymax = (0.0,)*4
self.width, self.height = (0.0,)*2
(self.hadvance, self.hbearingx, self.hbearingy,
self.hdescent)= (0.0,)*4
(self.vadvance, self.vbearingx, self.vbearingy)= (0.0,)*3
self.env = env
def __render__(self):
raise NotImplementedError("Derived must override")
class Hbox(Renderer):
"""A class that corresponds to a TeX hbox."""
def __init__(self, env, texlist=[]):
Renderer.__init__(self, env)
self.items = texlist
if not self.items:
# empty group
return
previous = None
for item in self.items:
# Checking for kerning
if previous:
kern = get_kern(previous, item)
item.hadvance += kern.hadvance
self.hbearingy = max((item.hbearingy, self.hbearingy))
self.ymax = max((item.ymax, self.ymax))
self.ymin = min((item.ymin, self.ymin))
self.hadvance += item.hadvance
previous = item
first = self.items[0]
self.hbearingx = 0#first.hbearingx
self.xmin = 0#first.xmin
last = self.items[-1]
self.xmax = self.hadvance# + fabs(last.hadvance - last.xmax)
self.xmax -= first.hbearingx
self.width = self.xmax - self.xmin
self.height = self.ymax - self.ymin
def render(self, x, y):
for item in self.items:
item.render(x, y)
x += item.hadvance
class Scripted(Renderer):
"""Used for creating elements that have sub/superscripts"""
def __init__(self, env, nuc=None, type="ord", sub=None,
sup=None):
Renderer.__init__(self, env)
if not nuc:
nuc = Hbox([])
if not sub:
sub = Hbox([])
if not sup:
sup = Hbox([])
self.nuc = nuc
self.sub = sub
self.sup = sup
self.type = type
# Heuristics for figuring out how much the subscripts origin has to be
# below the origin of the nucleus (the descent of the letter "j").
# TO-DO: Change with a better alternative. Not working: F_1^1y_1
c = TexCharClass(env, "j")
C = TexCharClass(env, "M")
self.subpad = c.height - c.hbearingy
# If subscript is complex (i.e. a large Hbox - fraction etc.)
# we have to aditionaly lower the subscript
if sub.ymax > (C.height/2.1 + self.subpad):
self.subpad = sub.ymax - C.height/2.1
#self.subpad = max(self.subpad)
#self.subpad = 0.5*sub.height
# Similar for the superscript
self.suppad = max(nuc.height/1.9, C.ymax/1.9) - sup.ymin# - C.hbearingy
#self.hadvance = nuc.hadvance + max((sub.hadvance, sup.hadvance))
self.xmin = nuc.xmin
self.xmax = max(nuc.hadvance, nuc.hbearingx + nuc.width) +\
max((sub.hadvance, sub.hbearingx + sub.width,
sup.hadvance, sup.hbearingx + sup.width))# - corr
self.ymin = min(nuc.ymin, -self.subpad + sub.ymin)
self.ymax = max((nuc.ymax, self.suppad + sup.hbearingy))
# The bearing of the whole element is the bearing of the nucleus
self.hbearingx = nuc.hbearingx
self.hadvance = self.xmax
# Heruistics. Feel free to change
self.hbearingy = self.ymax
self.width = self.xmax - self.xmin
self.height = self.ymax - self.ymin
def render(self, x, y):
nuc, sub, sup = self.nuc, self.sub, self.sup
nx = x
ny = y
subx = x + max(nuc.hadvance, nuc.hbearingx + nuc.width)# + sub.hbearingx
suby = y + self.subpad# - subfactor*self.env.fontsize
supx = x + max(nuc.hadvance, nuc.hbearingx + nuc.width)# + sup.hbearingx
supy = y - self.suppad# + 10#subfactor*self.env.fontsize
self.nuc.render(nx, ny)
self.sub.render(subx, suby)
self.sup.render(supx, supy)
def __repr__(self):
tmp = [repr(i) for i in [self.env, self.nuc, self.type,
self.sub, self.sup]]
tmp = tuple(tmp)
return "Scripted(env=%s,nuc=%s, type=%s, \
sub=%s, sup=%s)"%tmp
class Fraction(Renderer):
"""A class for rendering a fraction."""
def __init__(self, env, num, den):
Renderer.__init__(self, env)
self.numer = num
self.denom = den
# TO-DO: Find a better way to implement the fraction bar
self.pad = get_frac_bar_height(self.env)
pad = self.pad
self.bar = Line(env.copy(), max(num.width, den.width) + 2*pad, pad)
#~ self.bar.hbearingx = pad
#~ self.bar.hadvance = self.bar.width + 2*pad
#~ self.bar.hbearingy = pad + pad
self.xmin = 0
#self.xmax = self.bar.hadvance
self.xmax = self.bar.width# + 2*pad
self.ymin = -(2*pad + den.height)
self.ymax = 2*pad + num.height
# The amount by which we raise the bar (the whole fraction)
# of the bottom (origin)
# TO-DO: Find a better way to implement it
_env = env.copy()
_env.face = "rm"
c = TexCharClass(_env, "+")
self.barpad = 1./2.*(c.ymax-c.ymin) + c.ymin
self.ymin += self.barpad
self.ymax += self.barpad
self.width = self.xmax - self.xmin
self.height = self.ymax - self.ymin
#print self.width, self.height
#self.hbearingx = pad
self.hbearingx = 0
self.hbearingy = self.ymax
#self.hadvance = self.bar.hadvance
self.hadvance = self.xmax
def render(self, x, y):
y -= self.barpad
pad = self.pad
#print self.bar.xmax, self.bar.xmin, self.bar.ymin, self.bar.ymax
self.bar.render(x, y)
nx = x - self.numer.hbearingx + (self.width - self.numer.width)/2.
ny = y - 2*pad - (self.numer.height - self.numer.ymax)
self.numer.render(nx, ny)
dx = x - self.denom.hbearingx+ (self.width - self.denom.width)/2.
dy = y + 2*pad + self.denom.hbearingy
self.denom.render(dx, dy)
# Primitives
class TexCharClass(Renderer):
"""A class that implements rendering of a single character."""
def __init__(self, env, char, uniindex=None):
#Renderer.__init__(self, env)
self.env = env
# uniindex is used to override ord(char) (needed on platforms where
# there is only BMP support for unicode, i.e. windows)
msg = "A char (string with length == 1) is needed"
if isinstance(_textclass(char), _textclass) and len(char) == 1:
self.char = char
else:
raise ValueError(msg)
if not uniindex:
self.uniindex = ord(char)
else:
if isinstance(uniindex, int):
self.uniindex = uniindex
else:
raise ValueError("uniindex must be an int")
#print self.env.face, filenamesd
# TO-DO: This code is needed for BaKoMa fonts. To be removed when
# mathtext migrates to completely unicode fonts
if self.env.face == "rm" and filenamesd["rm"].endswith("cmr10.ttf"):
_env = self.env.copy()
if self.char in ("{", "}"):
_env.face = "cal"
font = get_font(_env)
if self.char == "{":
index = 118
elif self.char == "}":
index = 119
glyph = font.load_char(index)
else:
font = get_font(self.env)
glyph = font.load_char(self.uniindex)
else:
font = get_font(self.env)
glyph = font.load_char(self.uniindex)
self.glyph = glyph
self.xmin, self.ymin, self.xmax, self.ymax = [
val/64.0 for val in self.glyph.bbox]
self.width = self.xmax - self.xmin#glyph.width/64.0
self.height = self.ymax - self.ymin#glyph.height/64.0
self.hadvance = glyph.horiAdvance/64.0
self.hbearingx = glyph.horiBearingX/64.0
self.hbearingy = glyph.horiBearingY/64.0
def render(self, x, y):
#y -= self.ymax
#y -= (self.height - self.hbearingy)
#print x, y
font = get_font(self.env)
output = self.env.output
if output == "AGG":
x += self.hbearingx
y -= self.hbearingy
font.draw_glyph_to_bitmap(x, y, self.glyph)
elif output == "SVG":
familyname = font.get_sfnt()[(1,0,0,1)]
thetext = unichr(self.uniindex)
thetext.encode('utf-8')
fontsize = self.env.fontsize * scriptfactors[self.env.scriptdepth]
svg_elements.svg_glyphs.append((familyname, fontsize,thetext, x,
y, None)) # None was originaly metrics (in old mathtext)
class Kern(Renderer):
"""Class that implements the rendering of a Kern."""
def __init__(self, env, advance):
Renderer.__init__(self, env)
self.width = advance
self.hadvance = advance
def render(self, x, y):
pass
def __repr__(self):
return "Kern(%s, %s)"%(self.env, self.hadvance)
class Line(Renderer):
"""Class that implements the rendering of a line."""
def __init__(self, env, width, height):
Renderer.__init__(self, env)
self.ymin = -height/2.
self.xmax = width
self.ymax = height/2.
self.width = width
self.height = height
self.hadvance = width
self.hbearingy = self.ymax
def render(self, x, y):
font = get_font(self.env)
coords = (x + self.xmin, y + self.ymin, x + self.xmax,
y + self.ymax)
#print coords
#print "\n".join(repr(self.__dict__).split(","))
if self.env.output == "AGG":
coords = (coords[0]+2, coords[1]-1, coords[2]-2,
coords[3]-1)
#print coords
font.draw_rect_filled(*coords)
else:
svg_elements.svg_lines.append(coords)
#~ familyname = font.get_sfnt()[(1,0,0,1)]
#~ svg_elements.svg_glyphs.append((familyname, self.env.fontsize,
#~ "---", x,y, None))
# Main parser functions
def handle_tokens(texgroup, env):
"""Scans the entire (tex)group to handle tokens. Tokens are other groups,
commands, characters, kerns etc. Used recursively.
"""
result = []
# So we're sure that nothing changes the outer environment
env = env.copy()
while texgroup:
item = texgroup.pop(0)
#print texgroup, type(texgroup)
#print env.face, type(item), repr(item)
if isinstance(item, list):
appendix = handle_tokens(item, env.copy())
elif item in scripts:
sub, sup, texgroup = handle_scripts(item, texgroup, env.copy())
try:
nuc = result.pop()
except IndexError:
nuc = Hbox([])
appendix = Scripted(env.copy(), nuc=nuc, sub=sub, sup=sup)
elif is_command(item):
command = item.strip(esc_char)
texgroup, env = handle_command(command, texgroup, env.copy(),
allowsetters=True)
continue
elif isinstance(item, _textclass):
if item == word_delim and env.mode == "mathmode":
# Disregard space in mathmode
continue
uniindex = ord(item)
appendix = handle_char(uniindex, env.copy())
else:
appendix = item
result.append(appendix)
return Hbox(env.copy(),result)
def handle_command(command, texgroup, env, allowsetters=False):
"""Handles TeX commands that don't have backward propagation, and
aren't setting anything in the environment.
"""
# First we deal with setters - commands that change the
# environment of the current group (scope)
if command in setters:
if not allowsetters:
raise TexParseError("Seter not allowed here")
if command in faces:
env.face = command
else:
raise TexParseError("Unknown setter: %s%s"%(esc_char, command))
return texgroup, env
elif command == "frac":
texgroup, args = get_args(command, texgroup, env.copy(), 2)
num, den = args
frac = Fraction(env=env, num=num, den=den)
appendix = frac
elif command in functions:
_tex = "%srm %sthinspace %s"%(esc_char, esc_char, command)
appendix = handle_tokens(parse_tex(_tex), env.copy())
elif command in (" "):
space = get_space(env)
appendix = Kern(env, space.hadvance)
elif command == "thinspace":
#print command
space = get_space(env)
appendix = Kern(env, 1/2. * space.hadvance)
elif command in reserved:
uniindex = ord(command)
appendix = handle_char(uniindex, env.copy())
elif command in tex2uni:
uniindex = tex2uni[command]
appendix = handle_char(uniindex, env.copy())
else:
#appendix = handle_tokens([r"\backslash"] + [
#~ c for c in command], env.copy())
#appendix.env = env.copy()
#print appendix
raise TexParseError("Unknown command: " + esc_char + command)
appendix = [appendix]
appendix.extend(texgroup)
#print "App",appendix
return appendix, env
def handle_scripts(firsttype, texgroup, env):
sub = None
sup = None
env = env.copy()
# The environment for the script elements
_env = env.copy()
_env.scriptdepth += 1
firstscript = texgroup.pop(0)
if firstscript in scripts:
# An "_" or "^", immediately folowed by another "_" or "^"
raise TexParseError("Missing { inserted. " + firsttype + firstscript)
elif is_command(firstscript):
command = firstscript.strip(esc_char)
texgroup, _env = handle_command(command, texgroup, _env)
firstscript = texgroup.pop(0)
else:
_tmp = handle_tokens([firstscript], _env)
firstscript = _tmp.items.pop(0)
if firsttype == "_":
sub = firstscript
else:
sup = firstscript
# Check if the next item is also a command for scripting
try:
second = texgroup[0]
except IndexError:
second = None
if second in scripts:
secondtype = texgroup.pop(0)
if secondtype == firsttype:
raise TexParseError("Double script: " + secondtype)
try:
secondscript = texgroup.pop(0)
except IndexError:
raise TexParseError("Empty script: " + secondtype)
if secondscript in scripts:
# An "_" or "^", immediately folowed by another "_" or "^"
raise TexParseError("Missing { inserted. "\
+ secondtype + secondscript)
elif is_command(secondscript):
command = secondscript.strip(esc_char)
texgroup, _env = handle_command(command, texgroup, _env)
secondscript = texgroup.pop(0)
else:
_tmp = handle_tokens([secondscript], _env)
secondscript = _tmp.items.pop(0)
if secondtype == "_":
sub = secondscript
else:
sup = secondscript
# Check if the next item is also a command for scripting
try:
next = texgroup[0]
except IndexError:
next = None
if next in scripts:
raise TexParseError("Double script: " + next)
return sub, sup, texgroup
def handle_char(uniindex, env):
env = env.copy()
char = unichr(uniindex)
if not env.face:
env.face = infer_face(env, char)
return TexCharClass(env, char, uniindex=uniindex)
def get_args(command, texgroup, env, num_args):
"""Returns the arguments needed by a TeX command"""
args = []
i = 0
while i < num_args:
try:
arg = texgroup.pop(0)
except IndexError:
msg = "%s is missing it's %d argument"%(command, i+1)
raise TexParseError(msg)
# We skip space
if arg == " ":
continue
tmp = handle_tokens([arg], env.copy())
arg = tmp.items.pop()
args.append(arg)
i += 1
return texgroup, args
# Functions exported to backends
def math_parse_s_ft2font(s, dpi, fontsize, angle=0, output="AGG"):
"""This function is called by the backends"""
# Reseting the variables used for rendering
for font in fonts.values():
font.clear()
svg_elements.svg_glyphs = []
svg_elements.svg_lines = []
s = s[1:-1]
parsed = parse_tex(_textclass(s))
env = environment.copy()
env.dpi = dpi
env.fontsize = fontsize
env.output = output
parsed = handle_tokens(parsed, env)
#print "\n".join(str(parsed.__dict__).split(","))
width, height = parsed.width + 2, parsed.height + 2
#print width, height
if output == "AGG":
for key in fonts:
fonts[key].set_bitmap_size(width, height)
parsed.render(-parsed.items[0].hbearingx, height + parsed.ymin - 1)
#~ parsed.render(-parsed.hbearingx, height - 1 - (
#~ parsed.height - parsed.hbearingy))
if output == "AGG":
return width, height, fonts.values()
elif output == "SVG":
return width, height, svg_elements
def math_parse_s_ft2font_svg(s, dpi, fontsize, angle=0):
return math_parse_s_ft2font(s, dpi, fontsize, angle, "SVG")
def math_parse_s_ft2font1(s, dpi, fontsize, angle=0):
"Used only for testing"
s = s[1:-1]
parsed = parse_tex(_textclass(s))
env = environment.copy()
env.dpi = dpi
env.fontsize = fontsize
parsed = handle_tokens(parsed, env)
#print "\n".join(str(parsed.__dict__).split(","))
width, height = parsed.width + 10, parsed.height + 10
width, height = 300, 300
#print width, height
for key in fonts:
fonts[key].set_bitmap_size(width, height)
parsed.render(width/2., height/2.)
#fonts["mit"].draw_rect(0, 0, 40, 0)
#fonts["mit"].draw_rect(0, 1, 40, 0)
#parsed.render(20, 20)
#~ parsed.render(-parsed.hbearingx, height - 1 - (
#~ parsed.height - parsed.hbearingy))
_fonts = fonts.values()
return width, height, _fonts
if __name__ == '__main__':
#texstring = r"\\{ \horse\ Hello\^ ^ a^b_c}"
#texstring = r" asdf { \horse{}tralala1234\ \zztop{} \ Hello\^^a^{b_c}}"
#texstring = r"{}{} { }"
#texstring = r"{{{_ }}}"
#texstring = r"\horse{}"
#texstring = r"\horse;,.?)_)(*(*^*%&$$%{} Haha! Kako je frajeru?"
#texstring = r"a_2\trav 32"
#texstring = r"a_24{\sum_4^5} _3"
texstring = _textclass(r"1_2^{4^5}32 5")
parsed = parse_tex(texstring)
#~ print bool(a)
#print is_scriptcommand('\\subscript')
syntax highlighted by Code2HTML, v. 0.9.1