from fontTools import ttLib from fontTools.misc.textTools import safeEval import types import string import Numeric, array from xml.parsers.xmlproc import xmlproc xmlerror = "xmlerror" xml_parse_error = "XML parse error" 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): def __init__(self, ttFont, progress=None): self.ttFont = ttFont self.progress = progress self.root = None self.content_stack = [] self.lastpos = 0 def handle_start_tag(self, name, attrs): if self.progress: 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) if not stacksize: if name <> "ttFont": raise xml_parse_error, "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([]) elif stacksize == 1: msg = "Parsing '%s' table..." % ttLib.xmltag2tag(name) if self.progress: self.progress.setlabel(msg) elif self.ttFont.verbose: ttLib.debugmsg(msg) else: print msg tag = ttLib.xmltag2tag(name) tableclass = ttLib.getTableClass(tag) if tableclass is None: from fontTools.ttLib.tables.DefaultTable import DefaultTable tableclass = DefaultTable self.current_table = tableclass(tag) self.ttFont[tag] = self.current_table self.content_stack.append([]) elif stacksize == 2: self.content_stack.append([]) self.root = (name, attrs, self.content_stack[-1]) else: list = [] self.content_stack[-1].append(name, attrs, list) self.content_stack.append(list) def handle_data(self, data, start, end): if len(self.locator.stack) > 1: self.content_stack[-1].append(data[start:end]) def handle_end_tag(self, name): del self.content_stack[-1] stack = self.locator.stack stacksize = len(stack) if stacksize == 1: self.root = None elif stacksize == 2: self.current_table.fromXML(self.root, self.ttFont) self.root = None class ProgressPrinter: def __init__(self, title, maxval=100): print title def set(self, val, maxval=None): pass def increment(self, val=1): pass def setlabel(self, text): print text