Spaces:
Sleeping
Sleeping
| 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) | |
| if self.ttFont.reader is None and not self.ttFont.tables: | |
| 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: | |
| # parser parses in chunks, so we may get multiple calls | |
| # for the same text node; thus we need to append the data | |
| # to the last item in the content stack: | |
| # https://github.com/fonttools/fonttools/issues/2614 | |
| if ( | |
| data != "\n" | |
| and self.contentStack[-1] | |
| and isinstance(self.contentStack[-1][-1], str) | |
| and self.contentStack[-1][-1] != "\n" | |
| ): | |
| self.contentStack[-1][-1] += data | |
| else: | |
| 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) | |