|
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: |
|
|
|
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: |
|
|
|
|
|
|
|
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"): |
|
|
|
dirname = os.path.dirname(self.file.name) |
|
else: |
|
|
|
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: |
|
|
|
|
|
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: |
|
|
|
|
|
|
|
|
|
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) |
|
|