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:
parent
3c3c32c5a4
commit
ca4c45681e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user