From 56a84ae905192a3ede80cb21d80ae7a56935a24e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 18 Dec 2017 11:57:42 +0000 Subject: [PATCH] Temporarily revert PR #1035 bamidei/split_g_l_y_f_to_one_per_file The split-glyf-to-one-glyph-per-file feature is only partially implemented, as it was discussed here: https://github.com/fonttools/fonttools/issues/153#issuecomment-346677171 I need to cut a bugfix release today, but I don't want to ship this as is. I prefer to temporarily rever, tag a release from master branch, then revert it again to its present state so that bamidei can complete his work. --- Revert "[glyf] make splitGlyphs output more compact" This reverts commit d08d635a93289982b028aacd3b9039fbe742f3e8. Revert "more whitespace" This reverts commit bd030f61c6b360fd360632bbc6c19abb057c9a24. Revert "minor whitespace" This reverts commit f2a8c787b13b92ae0e0f61477e584316144747c7. Revert "Merge pull request #1035 from bamidei/split_g_l_y_f_to_one_per_file" This reverts commit 17b89d9dde7691dbbb6815efba92eff446af62ac, reversing changes made to b8482d9666f08d3603ee93d3ca52931550f715fc. --- Lib/fontTools/misc/xmlReader.py | 46 ++++++----------- Lib/fontTools/ttLib/__init__.py | 42 ++++++--------- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 71 +++++++------------------- Lib/fontTools/ttx.py | 9 +--- Tests/misc/xmlReader_test.py | 41 --------------- 5 files changed, 49 insertions(+), 160 deletions(-) diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py index 438549d7d..a8931bb10 100644 --- a/Lib/fontTools/misc/xmlReader.py +++ b/Lib/fontTools/misc/xmlReader.py @@ -17,8 +17,7 @@ BUFSIZE = 0x4000 class XMLReader(object): - def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False): - + def __init__(self, fileOrPath, ttFont, progress=None, quiet=None): if fileOrPath == '-': fileOrPath = sys.stdin if not hasattr(fileOrPath, "read"): @@ -36,7 +35,6 @@ class XMLReader(object): self.quiet = quiet self.root = None self.contentStack = [] - self.contentOnly = contentOnly self.stackSize = 0 def read(self, rootless=False): @@ -75,24 +73,8 @@ class XMLReader(object): 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) @@ -103,7 +85,15 @@ class XMLReader(object): self.ttFont.sfntVersion = sfntVersion self.contentStack.append([]) elif 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) subReader = XMLReader(subFile, self.ttFont, self.progress) subReader.read() self.contentStack.append([]) @@ -129,11 +119,6 @@ class XMLReader(object): 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]) @@ -149,13 +134,12 @@ class XMLReader(object): 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 + 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): diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py index 100903f9a..a9db4bba0 100644 --- a/Lib/fontTools/ttLib/__init__.py +++ b/Lib/fontTools/ttLib/__init__.py @@ -247,8 +247,7 @@ class TTFont(object): def saveXML(self, fileOrPath, progress=None, quiet=None, tables=None, skipTables=None, splitTables=False, disassembleInstructions=True, - splitGlyphs=False, bitmapGlyphDataFormat='raw', newlinestr=None): - + bitmapGlyphDataFormat='raw', newlinestr=None): """Export the font as TTX (an XML-based text file), or as a series of text files when splitTables is true. In the latter case, the 'fileOrPath' argument should be a path to a directory. @@ -291,7 +290,7 @@ class TTFont(object): if not splitTables: writer.newline() - if splitTables or splitGlyphs: + else: # 'fileOrPath' must now be a path path, ext = os.path.splitext(fileOrPath) fileNameTemplate = path + ".%s" + ext @@ -300,11 +299,8 @@ class TTFont(object): if progress: progress.set(i) tag = tables[i] - if splitTables or (splitGlyphs and tag == 'glyf'): - tablePath = fileNameTemplate % tagToIdentifier(tag) - else: - tablePath = None if splitTables: + tablePath = fileNameTemplate % tagToIdentifier(tag) tableWriter = xmlWriter.XMLWriter(tablePath, idlefunc=idlefunc, newlinestr=newlinestr) tableWriter.begintag("ttFont", ttLibVersion=version) @@ -314,7 +310,7 @@ class TTFont(object): writer.newline() else: tableWriter = writer - self._tableToXML(tableWriter, tag, progress, splitGlyphs=splitGlyphs) + self._tableToXML(tableWriter, tag, progress) if splitTables: tableWriter.endtag("ttFont") tableWriter.newline() @@ -328,7 +324,7 @@ class TTFont(object): if not hasattr(fileOrPath, "write") and fileOrPath != "-": writer.close() - def _tableToXML(self, writer, tag, progress, splitGlyphs=False, quiet=None): + def _tableToXML(self, writer, tag, progress, quiet=None): if quiet is not None: deprecateArgument("quiet", "configure logging instead") if tag in self: @@ -350,9 +346,7 @@ class TTFont(object): attrs['raw'] = True writer.begintag(xmlTag, **attrs) writer.newline() - if tag == "glyf": - table.toXML(writer, self, progress, splitGlyphs) - elif tag == "CFF ": + if tag in ("glyf", "CFF "): table.toXML(writer, self, progress) else: table.toXML(writer, self) @@ -879,8 +873,8 @@ def _escapechar(c): return hex(byteord(c))[2:] -def nameToIdentifier(name): - """Convert a name to a valid (but UGLY) python identifier, +def tagToIdentifier(tag): + """Convert a table tag to a valid (but UGLY) python identifier, as well as a filename that's guaranteed to be unique even on a caseless file system. Each character is mapped to two characters. Lowercase letters get an underscore before the letter, uppercase @@ -893,25 +887,19 @@ def nameToIdentifier(name): 'OS/2' -> 'O_S_2f_2' """ import re - while len(name) > 1 and name[-1] == ' ': - name = name[:-1] + tag = Tag(tag) + if tag == "GlyphOrder": + return tag + assert len(tag) == 4, "tag should be 4 characters long" + while len(tag) > 1 and tag[-1] == ' ': + tag = tag[:-1] ident = "" - for c in name: + for c in tag: ident = ident + _escapechar(c) if re.match("[0-9]", ident): ident = "_" + ident return ident -def tagToIdentifier(tag): - """This performs the same conversion which nameToIdentifiier does - with the additional assertion that the source tag is 4 characters - long which is criteria for a valid tag name. - """ - if tag == "GlyphOrder": - return tag - ret = nameToIdentifier(tag) - assert len(tag) == 4, "tag should be 4 characters long" - return ret def identifierToTag(ident): """the opposite of tagToIdentifier()""" diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 7acf677c1..52117129f 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -5,7 +5,6 @@ from collections import namedtuple from fontTools.misc.py23 import * from fontTools.misc import sstruct from fontTools import ttLib -from fontTools import version from fontTools.misc.textTools import safeEval, pad from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect from fontTools.misc.bezierTools import calcQuadraticBounds @@ -17,17 +16,10 @@ import sys import struct import array import logging -import os -from fontTools.misc import xmlWriter -from fontTools.ttLib import nameToIdentifier + log = logging.getLogger(__name__) -# We compute the version the same as is computed in ttlib/__init__ -# so that we can write 'ttLibVersion' attribute of the glyf TTX files -# when glyf is written to separate files. -version = ".".join(version.split('.')[:2]) - # # The Apple and MS rasterizers behave differently for # scaled composite components: one does scale first and then translate @@ -118,16 +110,12 @@ class table__g_l_y_f(DefaultTable.DefaultTable): ttFont['maxp'].numGlyphs = len(self.glyphs) return data - def toXML(self, writer, ttFont, progress=None, splitGlyphs=False): - notice = ( - "The xMin, yMin, xMax and yMax values\n" - "will be recalculated by the compiler.") + def toXML(self, writer, ttFont, progress=None): + writer.newline() glyphNames = ttFont.getGlyphNames() - if not splitGlyphs: - writer.newline() - writer.comment(notice) - writer.newline() - writer.newline() + writer.comment("The xMin, yMin, xMax and yMax values\nwill be recalculated by the compiler.") + writer.newline() + writer.newline() counter = 0 progressStep = 10 numGlyphs = len(glyphNames) @@ -138,44 +126,21 @@ class table__g_l_y_f(DefaultTable.DefaultTable): counter = counter + 1 glyph = self[glyphName] if glyph.numberOfContours: - if splitGlyphs: - path, ext = os.path.splitext(writer.file.name) - fileNameTemplate = path + ".%s" + ext - glyphPath = fileNameTemplate % nameToIdentifier(glyphName) - glyphWriter = xmlWriter.XMLWriter( - glyphPath, idlefunc=writer.idlefunc, - newlinestr=writer.newlinestr) - glyphWriter.begintag("ttFont", ttLibVersion=version) - glyphWriter.newline() - glyphWriter.begintag("glyf") - glyphWriter.newline() - glyphWriter.comment(notice) - glyphWriter.newline() - writer.simpletag("TTGlyph", src=os.path.basename(glyphPath)) - else: - glyphWriter = writer - glyphWriter.begintag('TTGlyph', [ - ("name", glyphName), - ("xMin", glyph.xMin), - ("yMin", glyph.yMin), - ("xMax", glyph.xMax), - ("yMax", glyph.yMax), - ]) - glyphWriter.newline() - glyph.toXML(glyphWriter, ttFont) - glyphWriter.endtag('TTGlyph') - glyphWriter.newline() - if splitGlyphs: - glyphWriter.endtag("glyf") - glyphWriter.newline() - glyphWriter.endtag("ttFont") - glyphWriter.newline() - glyphWriter.close() + writer.begintag('TTGlyph', [ + ("name", glyphName), + ("xMin", glyph.xMin), + ("yMin", glyph.yMin), + ("xMax", glyph.xMax), + ("yMax", glyph.yMax), + ]) + writer.newline() + glyph.toXML(writer, ttFont) + writer.endtag('TTGlyph') + writer.newline() else: writer.simpletag('TTGlyph', name=glyphName) writer.comment("contains no outline data") - if not splitGlyphs: - writer.newline() + writer.newline() writer.newline() def fromXML(self, name, attrs, content, ttFont): diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py index f69543782..002215ce5 100644 --- a/Lib/fontTools/ttx.py +++ b/Lib/fontTools/ttx.py @@ -38,9 +38,6 @@ usage: ttx [options] inputfile1 [... inputfileN] to the individual table dumps. This file can be used as input to ttx, as long as the table files are in the same directory. - -g Split glyf table: Save the glyf data into separate TTX files - per glyph and write a small TTX for the glyf table which - contains references to the individual TTGlyph elements. -i Do NOT disassemble TT instructions: when this option is given, all TrueType programs (glyph programs, the font program and the pre-program) will be written to the TTX file as hex data @@ -113,7 +110,6 @@ class Options(object): verbose = False quiet = False splitTables = False - splitGlyphs = False disassembleInstructions = True mergeFile = None recalcBBoxes = True @@ -164,8 +160,6 @@ class Options(object): self.skipTables.append(value) elif option == "-s": self.splitTables = True - elif option == "-g": - self.splitGlyphs = True elif option == "-i": self.disassembleInstructions = False elif option == "-z": @@ -261,7 +255,6 @@ def ttDump(input, output, options): tables=options.onlyTables, skipTables=options.skipTables, splitTables=options.splitTables, - splitGlyphs=options.splitGlyphs, disassembleInstructions=options.disassembleInstructions, bitmapGlyphDataFormat=options.bitmapGlyphDataFormat, newlinestr=options.newlinestr) @@ -325,7 +318,7 @@ def guessFileType(fileName): def parseOptions(args): - rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sgim:z:baey:", + rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sim:z:baey:", ['unicodedata=', "recalc-timestamp", 'flavor=', 'version', 'with-zopfli', 'newline=']) diff --git a/Tests/misc/xmlReader_test.py b/Tests/misc/xmlReader_test.py index 14b69787d..622abf5cd 100644 --- a/Tests/misc/xmlReader_test.py +++ b/Tests/misc/xmlReader_test.py @@ -142,48 +142,7 @@ class TestXMLReader(unittest.TestCase): self.assertTrue(reader.file.closed) os.remove(tmp.name) - def test_read_sub_file(self): - # Verifies that sub-file content is able to be read to a table. - expectedContent = u'testContent' - expectedNameID = '1' - expectedPlatform = '3' - expectedLangId = '0x409' - with tempfile.NamedTemporaryFile(delete=False) as tmp: - subFileData = ( - '' - '' - '' - '%s' - '' - '' - '' - ) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent) - tmp.write(subFileData.encode("utf-8")) - - with tempfile.NamedTemporaryFile(delete=False) as tmp2: - fileData = ( - '' - '' - '' - '' - '' - ) % tmp.name - tmp2.write(fileData.encode('utf-8')) - - ttf = TTFont() - with open(tmp2.name, "rb") as f: - reader = XMLReader(f, ttf) - reader.read() - reader.close() - nameTable = ttf['name'] - self.assertTrue(int(expectedNameID) == nameTable.names[0].nameID) - self.assertTrue(int(expectedLangId, 16) == nameTable.names[0].langID) - self.assertTrue(int(expectedPlatform) == nameTable.names[0].platformID) - self.assertEqual(expectedContent, nameTable.names[0].string.decode(nameTable.names[0].getEncoding())) - - os.remove(tmp.name) - os.remove(tmp2.name) if __name__ == '__main__': import sys