| from fontTools import ttLib |
| from fontTools.misc.textTools import safeEval |
| from fontTools.ttLib.tables.DefaultTable import DefaultTable |
| import sys |
| import os |
| import logging |
| |
| |
| log = logging.getLogger(__name__) |
| |
| class TTXParseError(Exception): pass |
| |
| BUFSIZE = 0x4000 |
| |
| |
| class XMLReader(object): |
| |
| def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False): |
| if fileOrPath == '-': |
| fileOrPath = sys.stdin |
| if not hasattr(fileOrPath, "read"): |
| self.file = open(fileOrPath, "rb") |
| self._closeStream = True |
| else: |
| # assume readable file object |
| self.file = fileOrPath |
| self._closeStream = False |
| self.ttFont = ttFont |
| self.progress = progress |
| if quiet is not None: |
| from fontTools.misc.loggingTools import deprecateArgument |
| deprecateArgument("quiet", "configure logging instead") |
| self.quiet = quiet |
| self.root = None |
| self.contentStack = [] |
| self.contentOnly = contentOnly |
| self.stackSize = 0 |
| |
| def read(self, rootless=False): |
| if rootless: |
| self.stackSize += 1 |
| if self.progress: |
| self.file.seek(0, 2) |
| fileSize = self.file.tell() |
| self.progress.set(0, fileSize // 100 or 1) |
| self.file.seek(0) |
| self._parseFile(self.file) |
| if self._closeStream: |
| self.close() |
| if rootless: |
| self.stackSize -= 1 |
| |
| def close(self): |
| self.file.close() |
| |
| def _parseFile(self, file): |
| from xml.parsers.expat import ParserCreate |
| parser = ParserCreate() |
| parser.StartElementHandler = self._startElementHandler |
| parser.EndElementHandler = self._endElementHandler |
| parser.CharacterDataHandler = self._characterDataHandler |
| |
| pos = 0 |
| while True: |
| chunk = file.read(BUFSIZE) |
| if not chunk: |
| parser.Parse(chunk, 1) |
| break |
| pos = pos + len(chunk) |
| if self.progress: |
| self.progress.set(pos // 100) |
| parser.Parse(chunk, 0) |
| |
| def _startElementHandler(self, name, attrs): |
| if self.stackSize == 1 and self.contentOnly: |
| # We already know the table we're parsing, skip |
| # parsing the table tag and continue to |
| # stack '2' which begins parsing content |
| self.contentStack.append([]) |
| self.stackSize = 2 |
| return |
| stackSize = self.stackSize |
| self.stackSize = stackSize + 1 |
| subFile = attrs.get("src") |
| if subFile is not None: |
| if hasattr(self.file, 'name'): |
| # if file has a name, get its parent directory |
| dirname = os.path.dirname(self.file.name) |
| else: |
| # else fall back to using the current working directory |
| dirname = os.getcwd() |
| subFile = os.path.join(dirname, subFile) |
| if not stackSize: |
| if name != "ttFont": |
| raise TTXParseError("illegal root tag: %s" % name) |
| sfntVersion = attrs.get("sfntVersion") |
| if sfntVersion is not None: |
| if len(sfntVersion) != 4: |
| sfntVersion = safeEval('"' + sfntVersion + '"') |
| self.ttFont.sfntVersion = sfntVersion |
| self.contentStack.append([]) |
| elif stackSize == 1: |
| if subFile is not None: |
| subReader = XMLReader(subFile, self.ttFont, self.progress) |
| subReader.read() |
| self.contentStack.append([]) |
| return |
| tag = ttLib.xmlToTag(name) |
| msg = "Parsing '%s' table..." % tag |
| if self.progress: |
| self.progress.setLabel(msg) |
| log.info(msg) |
| if tag == "GlyphOrder": |
| tableClass = ttLib.GlyphOrder |
| elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])): |
| tableClass = DefaultTable |
| else: |
| tableClass = ttLib.getTableClass(tag) |
| if tableClass is None: |
| tableClass = DefaultTable |
| if tag == 'loca' and tag in self.ttFont: |
| # Special-case the 'loca' table as we need the |
| # original if the 'glyf' table isn't recompiled. |
| self.currentTable = self.ttFont[tag] |
| else: |
| self.currentTable = tableClass(tag) |
| self.ttFont[tag] = self.currentTable |
| self.contentStack.append([]) |
| elif stackSize == 2 and subFile is not None: |
| subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True) |
| subReader.read() |
| self.contentStack.append([]) |
| self.root = subReader.root |
| elif stackSize == 2: |
| self.contentStack.append([]) |
| self.root = (name, attrs, self.contentStack[-1]) |
| else: |
| l = [] |
| self.contentStack[-1].append((name, attrs, l)) |
| self.contentStack.append(l) |
| |
| def _characterDataHandler(self, data): |
| if self.stackSize > 1: |
| self.contentStack[-1].append(data) |
| |
| def _endElementHandler(self, name): |
| self.stackSize = self.stackSize - 1 |
| del self.contentStack[-1] |
| if not self.contentOnly: |
| if self.stackSize == 1: |
| self.root = None |
| elif self.stackSize == 2: |
| name, attrs, content = self.root |
| self.currentTable.fromXML(name, attrs, content, self.ttFont) |
| self.root = None |
| |
| |
| class ProgressPrinter(object): |
| |
| 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) |