Spaces:
Running
Running
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) | |