1from __future__ import print_function, division, absolute_import 2from fontTools.misc.py23 import * 3from fontTools import ttLib 4from fontTools.misc.textTools import safeEval 5from fontTools.ttLib.tables.DefaultTable import DefaultTable 6import sys 7import os 8import logging 9 10 11log = logging.getLogger(__name__) 12 13class TTXParseError(Exception): pass 14 15BUFSIZE = 0x4000 16 17 18class XMLReader(object): 19 20 def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False): 21 if fileOrPath == '-': 22 fileOrPath = sys.stdin 23 if not hasattr(fileOrPath, "read"): 24 self.file = open(fileOrPath, "rb") 25 self._closeStream = True 26 else: 27 # assume readable file object 28 self.file = fileOrPath 29 self._closeStream = False 30 self.ttFont = ttFont 31 self.progress = progress 32 if quiet is not None: 33 from fontTools.misc.loggingTools import deprecateArgument 34 deprecateArgument("quiet", "configure logging instead") 35 self.quiet = quiet 36 self.root = None 37 self.contentStack = [] 38 self.contentOnly = contentOnly 39 self.stackSize = 0 40 41 def read(self, rootless=False): 42 if rootless: 43 self.stackSize += 1 44 if self.progress: 45 self.file.seek(0, 2) 46 fileSize = self.file.tell() 47 self.progress.set(0, fileSize // 100 or 1) 48 self.file.seek(0) 49 self._parseFile(self.file) 50 if self._closeStream: 51 self.close() 52 if rootless: 53 self.stackSize -= 1 54 55 def close(self): 56 self.file.close() 57 58 def _parseFile(self, file): 59 from xml.parsers.expat import ParserCreate 60 parser = ParserCreate() 61 parser.StartElementHandler = self._startElementHandler 62 parser.EndElementHandler = self._endElementHandler 63 parser.CharacterDataHandler = self._characterDataHandler 64 65 pos = 0 66 while True: 67 chunk = file.read(BUFSIZE) 68 if not chunk: 69 parser.Parse(chunk, 1) 70 break 71 pos = pos + len(chunk) 72 if self.progress: 73 self.progress.set(pos // 100) 74 parser.Parse(chunk, 0) 75 76 def _startElementHandler(self, name, attrs): 77 if self.stackSize == 1 and self.contentOnly: 78 # We already know the table we're parsing, skip 79 # parsing the table tag and continue to 80 # stack '2' which begins parsing content 81 self.contentStack.append([]) 82 self.stackSize = 2 83 return 84 stackSize = self.stackSize 85 self.stackSize = stackSize + 1 86 subFile = attrs.get("src") 87 if subFile is not None: 88 if hasattr(self.file, 'name'): 89 # if file has a name, get its parent directory 90 dirname = os.path.dirname(self.file.name) 91 else: 92 # else fall back to using the current working directory 93 dirname = os.getcwd() 94 subFile = os.path.join(dirname, subFile) 95 if not stackSize: 96 if name != "ttFont": 97 raise TTXParseError("illegal root tag: %s" % name) 98 sfntVersion = attrs.get("sfntVersion") 99 if sfntVersion is not None: 100 if len(sfntVersion) != 4: 101 sfntVersion = safeEval('"' + sfntVersion + '"') 102 self.ttFont.sfntVersion = sfntVersion 103 self.contentStack.append([]) 104 elif stackSize == 1: 105 if subFile is not None: 106 subReader = XMLReader(subFile, self.ttFont, self.progress) 107 subReader.read() 108 self.contentStack.append([]) 109 return 110 tag = ttLib.xmlToTag(name) 111 msg = "Parsing '%s' table..." % tag 112 if self.progress: 113 self.progress.setLabel(msg) 114 log.info(msg) 115 if tag == "GlyphOrder": 116 tableClass = ttLib.GlyphOrder 117 elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])): 118 tableClass = DefaultTable 119 else: 120 tableClass = ttLib.getTableClass(tag) 121 if tableClass is None: 122 tableClass = DefaultTable 123 if tag == 'loca' and tag in self.ttFont: 124 # Special-case the 'loca' table as we need the 125 # original if the 'glyf' table isn't recompiled. 126 self.currentTable = self.ttFont[tag] 127 else: 128 self.currentTable = tableClass(tag) 129 self.ttFont[tag] = self.currentTable 130 self.contentStack.append([]) 131 elif stackSize == 2 and subFile is not None: 132 subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True) 133 subReader.read() 134 self.contentStack.append([]) 135 self.root = subReader.root 136 elif stackSize == 2: 137 self.contentStack.append([]) 138 self.root = (name, attrs, self.contentStack[-1]) 139 else: 140 l = [] 141 self.contentStack[-1].append((name, attrs, l)) 142 self.contentStack.append(l) 143 144 def _characterDataHandler(self, data): 145 if self.stackSize > 1: 146 self.contentStack[-1].append(data) 147 148 def _endElementHandler(self, name): 149 self.stackSize = self.stackSize - 1 150 del self.contentStack[-1] 151 if not self.contentOnly: 152 if self.stackSize == 1: 153 self.root = None 154 elif self.stackSize == 2: 155 name, attrs, content = self.root 156 self.currentTable.fromXML(name, attrs, content, self.ttFont) 157 self.root = None 158 159 160class ProgressPrinter(object): 161 162 def __init__(self, title, maxval=100): 163 print(title) 164 165 def set(self, val, maxval=None): 166 pass 167 168 def increment(self, val=1): 169 pass 170 171 def setLabel(self, text): 172 print(text) 173