Complety revised the XML import code:

- use expat instead of xmlproc
- minor fixes here and there

Fixed bug in hmtx/vmtx code that only occured if all advances were equal.

FontTools now officially requires Python 2.0 or up, due to exapt and unicode.


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@179 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
jvr 2002-05-01 21:06:11 +00:00
parent 3c3c32c5a4
commit ca4c45681e
6 changed files with 64 additions and 164 deletions

View File

@ -42,7 +42,7 @@ Dumping 'prep' table...
"""
#
# $Id: __init__.py,v 1.17 2001-02-23 21:58:57 Just Exp $
# $Id: __init__.py,v 1.18 2002-05-01 21:06:11 jvr Exp $
#
__version__ = "1.0a6"
@ -244,30 +244,11 @@ class TTFont:
debugmsg("Done dumping TTX")
def importXML(self, file, progress=None):
"""Import an TTX file (an XML-based text format), so as to recreate
"""Import a TTX file (an XML-based text format), so as to recreate
a font object.
"""
import xmlImport, stat
from xml.parsers.xmlproc import xmlproc
builder = xmlImport.XMLApplication(self, progress)
if progress:
progress.set(0, os.stat(file)[stat.ST_SIZE] / 100 or 1)
proc = xmlImport.UnicodeProcessor()
proc.set_application(builder)
proc.set_error_handler(xmlImport.XMLErrorHandler(proc))
dir, filename = os.path.split(file)
if dir:
olddir = os.getcwd()
os.chdir(dir)
try:
proc.parse_resource(filename)
root = builder.root
finally:
if dir:
os.chdir(olddir)
# remove circular references
proc.deref()
del builder.progress
import xmlImport
xmlImport.importXML(self, file, progress)
def isLoaded(self, tag):
"""Return true if the table identified by 'tag' has been

View File

@ -40,8 +40,9 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
glyph = Glyph(glyphdata)
self.glyphs[glyphName] = glyph
last = next
if len(data) > next:
raise ttLib.TTLibError, "too much 'glyf' table data"
# this should become a warning:
#if len(data) > next:
# raise ttLib.TTLibError, "too much 'glyf' table data"
def compile(self, ttFont):
import string

View File

@ -75,7 +75,7 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
def fromXML(self, (name, attrs, content), ttFont):
if name <> "hdmxData":
return
content = string.join(content, " ")
content = string.join(content, "")
lines = string.split(content, ";")
topRow = string.split(lines[0])
assert topRow[0] == "ppem:", "illegal hdmx format"

View File

@ -52,6 +52,9 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
lastIndex = len(metrics)
while metrics[lastIndex-2][0] == lastAdvance:
lastIndex = lastIndex - 1
if lastIndex == 0:
# all advances are equal
break
additionalMetrics = metrics[lastIndex:]
additionalMetrics = map(lambda (advance, sb): sb, additionalMetrics)
metrics = metrics[:lastIndex]

View File

@ -107,13 +107,15 @@ class NameRecord:
self.platEncID = safeEval(attrs["platEncID"])
self.langID = safeEval(attrs["langID"])
if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
from fontTools.ttLib.xmlImport import UnicodeString
str = UnicodeString("")
s = ""
for element in content:
str = str + element
self.string = str.stripped().tostring()
s = s + element
s = unicode(s, "utf8")
s = s.strip()
self.string = s.encode("utf_16_be")
else:
self.string = string.strip(string.join(content, ""))
s = string.strip(string.join(content, ""))
self.string = unicode(s, "utf8").encode("latin1")
def __cmp__(self, other):
"""Compare method, so a list of NameRecords can be sorted

View File

@ -7,140 +7,45 @@ import Numeric, array
from xml.parsers.xmlproc import xmlproc
xmlerror = "xmlerror"
xml_parse_error = "XML parse error"
class TTXParseError(Exception): pass
class UnicodeString:
def __init__(self, value):
if isinstance(value, UnicodeString):
self.value = value.value
else:
if type(value) == types.StringType:
# Since Numeric interprets char codes as *signed*,
# we feed it through the array module.
value = array.array("B", value)
self.value = Numeric.array(value, Numeric.Int16)
def __len__(self):
return len(self.value)
#def __hash__(self):
# return hash(self.value.tostring())
#
#def __cmp__(self, other):
# if not isinstance(other, UnicodeString):
# return 1
# else:
# return not Numeric.alltrue(
# Numeric.equal(self.value, other.value))
def __add__(self, other):
if not isinstance(other, UnicodeString):
other = self.__class__(other)
return self.__class__(Numeric.concatenate((self.value, other.value)))
def __radd__(self, other):
if not isinstance(other, UnicodeString):
other = self.__class__(other)
return self.__class__(Numeric.concatenate((other.value, self.value)))
def __getslice__(self, i, j):
return self.__class__(self.value[i:j])
def __getitem__(self, i):
return self.__class__(self.value[i:i+1])
def tostring(self):
value = self.value
if ttLib.endian <> "big":
value = value.byteswapped()
return value.tostring()
def stripped(self):
value = self.value
i = 0
for i in range(len(value)):
if value[i] not in (0xa, 0xd, 0x9, 0x20):
break
value = value[i:]
i = 0
for i in range(len(value)-1, -1, -1):
if value[i] not in (0xa, 0xd, 0x9, 0x20):
break
value = value[:i+1]
return self.__class__(value)
def __repr__(self):
return "<%s %s at %x>" % (self.__class__.__name__, `self.value.tostring()`, id(self))
class UnicodeProcessor(xmlproc.XMLProcessor):
def parse_charref(self):
"Parses a character reference."
if self.now_at("x"):
digs=unhex(self.get_match(xmlproc.reg_hex_digits))
else:
try:
digs=string.atoi(self.get_match(xmlproc.reg_digits))
except ValueError,e:
self.report_error(3027)
digs=None
if digs == 169:
pass
if not self.now_at(";"): self.report_error(3005,";")
if digs==None: return
if not (digs==9 or digs==10 or digs==13 or \
(digs>=32 and digs<=255)):
if digs>255:
self.app.handle_data(UnicodeString([digs]),0,1)
else:
# hrm, I need to let some null bytes go through...
self.app.handle_data(chr(digs),0,1)
#self.report_error(3018,digs)
else:
if self.stack==[]:
self.report_error(3028)
self.app.handle_data(chr(digs),0,1)
class XMLErrorHandler(xmlproc.ErrorHandler):
def fatal(self, msg):
"Handles a fatal error message."
# we don't want no stinkin' sys.exit(1)
raise xml_parse_error, msg
class XMLApplication(xmlproc.Application):
class ExpatParser:
def __init__(self, ttFont, progress=None):
from xml.parsers.expat import ParserCreate
self.ttFont = ttFont
self.progress = progress
self.root = None
self.content_stack = []
self.contentStack = []
self.lastpos = 0
self.stackSize = 0
self.parser = ParserCreate()
self.parser.returns_unicode = 0
self.parser.StartElementHandler = self.StartElementHandler
self.parser.EndElementHandler = self.EndElementHandler
self.parser.CharacterDataHandler = self.CharacterDataHandler
def handle_start_tag(self, name, attrs):
if self.progress:
def ParseFile(self, file):
self.parser.ParseFile(file)
def StartElementHandler(self, name, attrs):
if 0 and self.progress:
# XXX
pos = self.locator.pos + self.locator.block_offset
if (pos - self.lastpos) > 4000:
self.progress.set(pos / 100)
self.lastpos = pos
stack = self.locator.stack
stackSize = len(stack)
stackSize = self.stackSize
self.stackSize = self.stackSize + 1
if not stackSize:
if name <> "ttFont":
raise xml_parse_error, "illegal root tag: %s" % name
raise TTXParseError, "illegal root tag: %s" % name
sfntVersion = attrs.get("sfntVersion", "\000\001\000\000")
if len(sfntVersion) <> 4:
sfntVersion = safeEval('"' + sfntVersion + '"')
self.ttFont.sfntVersion = sfntVersion
self.content_stack.append([])
self.contentStack.append([])
elif stackSize == 1:
msg = "Parsing '%s' table..." % ttLib.xmltag2tag(name)
if self.progress:
@ -157,31 +62,30 @@ class XMLApplication(xmlproc.Application):
if tableClass is None:
tableClass = DefaultTable
if self.ttFont.has_key(tag):
self.current_table = self.ttFont[tag]
self.currentTable = self.ttFont[tag]
else:
self.current_table = tableClass(tag)
self.ttFont[tag] = self.current_table
self.content_stack.append([])
self.currentTable = tableClass(tag)
self.ttFont[tag] = self.currentTable
self.contentStack.append([])
elif stackSize == 2:
self.content_stack.append([])
self.root = (name, attrs, self.content_stack[-1])
self.contentStack.append([])
self.root = (name, attrs, self.contentStack[-1])
else:
list = []
self.content_stack[-1].append((name, attrs, list))
self.content_stack.append(list)
self.contentStack[-1].append((name, attrs, list))
self.contentStack.append(list)
def handle_data(self, data, start, end):
if len(self.locator.stack) > 1:
self.content_stack[-1].append(data[start:end])
def CharacterDataHandler(self, data):
if self.stackSize > 1:
self.contentStack[-1].append(data)
def handle_end_tag(self, name):
del self.content_stack[-1]
stack = self.locator.stack
stackSize = len(stack)
if stackSize == 1:
def EndElementHandler(self, name):
self.stackSize = self.stackSize - 1
del self.contentStack[-1]
if self.stackSize == 1:
self.root = None
elif stackSize == 2:
self.current_table.fromXML(self.root, self.ttFont)
elif self.stackSize == 2:
self.currentTable.fromXML(self.root, self.ttFont)
self.root = None
@ -200,3 +104,12 @@ class ProgressPrinter:
print text
def importXML(ttFont, fileName, progress=None):
"""Import a TTX file (an XML-based text format), so as to recreate
a font object.
"""
p = ExpatParser(ttFont, progress)
file = open(fileName)
p.ParseFile(file)
file.close()