123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- 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)
|