From 7e91e776c9d10d3b295de06ee7f665d8106306d8 Mon Sep 17 00:00:00 2001 From: pabs3 Date: Sun, 22 Feb 2009 08:55:00 +0000 Subject: [PATCH] Apply remainder of #1675210: add support for TrueType Collection (TTC) files. git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@564 4cde692c-a291-49d1-8350-778aa11640f8 --- Lib/fontTools/ttLib/__init__.py | 7 ++++--- Lib/fontTools/ttLib/sfnt.py | 28 +++++++++++++++++++++++++++- Lib/fontTools/ttx.py | 16 ++++++++++++---- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py index 45abfbbca..39c1e5517 100644 --- a/Lib/fontTools/ttLib/__init__.py +++ b/Lib/fontTools/ttLib/__init__.py @@ -42,7 +42,7 @@ Dumping 'prep' table... """ # -# $Id: __init__.py,v 1.50 2008-03-01 11:43:00 jvr Exp $ +# $Id: __init__.py,v 1.51 2009-02-22 08:55:00 pabs3 Exp $ # import sys @@ -70,7 +70,8 @@ class TTFont: def __init__(self, file=None, res_name_or_index=None, sfntVersion="\000\001\000\000", checkChecksums=0, - verbose=0, recalcBBoxes=1, allowVID=0, ignoreDecompileErrors=False): + verbose=0, recalcBBoxes=1, allowVID=0, ignoreDecompileErrors=False, + fontNumber=-1): """The constructor can be called with a few different arguments. When reading a font from disk, 'file' should be either a pathname @@ -152,7 +153,7 @@ class TTFont: file = open(file, "rb") else: pass # assume "file" is a readable file object - self.reader = sfnt.SFNTReader(file, checkChecksums) + self.reader = sfnt.SFNTReader(file, checkChecksums, fontNumber=fontNumber) self.sfntVersion = self.reader.sfntVersion def close(self): diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py index db90c7a4c..c0d8ff1af 100644 --- a/Lib/fontTools/ttLib/sfnt.py +++ b/Lib/fontTools/ttLib/sfnt.py @@ -20,7 +20,7 @@ import os class SFNTReader: - def __init__(self, file, checkChecksums=1): + def __init__(self, file, checkChecksums=1, fontNumber=-1): self.file = file self.checkChecksums = checkChecksums data = self.file.read(sfntDirectorySize) @@ -28,6 +28,19 @@ class SFNTReader: from fontTools import ttLib raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)" sstruct.unpack(sfntDirectoryFormat, data, self) + if self.sfntVersion == "ttcf": + assert ttcHeaderSize == sfntDirectorySize + sstruct.unpack(ttcHeaderFormat, data, self) + assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version + if not 0 <= fontNumber < self.numFonts: + from fontTools import ttLib + raise ttLib.TTLibError, "specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1) + offsetTable = struct.unpack(">%dL" % self.numFonts, self.file.read(self.numFonts * 4)) + if self.Version == 0x00020000: + pass # ignoring version 2.0 signatures + self.file.seek(offsetTable[fontNumber]) + data = self.file.read(sfntDirectorySize) + sstruct.unpack(sfntDirectoryFormat, data, self) if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"): from fontTools import ttLib raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)" @@ -165,6 +178,19 @@ class SFNTWriter: # -- sfnt directory helpers and cruft +ttcHeaderFormat = """ + > # big endian + TTCTag: 4s # "ttcf" + Version: L # 0x00010000 or 0x00020000 + numFonts: L # number of fonts + # OffsetTable[numFonts]: L # array with offsets from beginning of file + # ulDsigTag: L # version 2.0 only + # ulDsigLength: L # version 2.0 only + # ulDsigOffset: L # version 2.0 only +""" + +ttcHeaderSize = sstruct.calcsize(ttcHeaderFormat) + sfntDirectoryFormat = """ > # big endian sfntVersion: 4s diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py index 2a4eb4ae8..914c2e1c9 100644 --- a/Lib/fontTools/ttx.py +++ b/Lib/fontTools/ttx.py @@ -41,6 +41,8 @@ usage: ttx [options] inputfile1 [... inputfileN] file smaller. -e Don't ignore decompilation errors, but show a full traceback and abort. + -y Select font number for TrueTrue Collection, + starting from 0. Compile options: -m Merge with TrueType-input-file: specify a TrueType or OpenType @@ -99,6 +101,7 @@ class Options: def __init__(self, rawOptions, numFiles): self.onlyTables = [] self.skipTables = [] + self.fontNumber = -1 for option, value in rawOptions: # general options if option == "-h": @@ -122,6 +125,8 @@ class Options: self.splitTables = 1 elif option == "-i": self.disassembleInstructions = 0 + elif option == "-y": + self.fontNumber = int(value) # compile options elif option == "-m": self.mergeFile = value @@ -141,7 +146,7 @@ class Options: def ttList(input, output, options): import string - ttf = TTFont(input) + ttf = TTFont(input, fontNumber=options.fontNumber) reader = ttf.reader tags = reader.keys() tags.sort() @@ -163,7 +168,8 @@ def ttList(input, output, options): def ttDump(input, output, options): print 'Dumping "%s" to "%s"...' % (input, output) ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID, - ignoreDecompileErrors=options.ignoreDecompileErrors) + ignoreDecompileErrors=options.ignoreDecompileErrors, + fontNumber=options.fontNumber) ttf.saveXML(output, tables=options.onlyTables, skipTables=options.skipTables, @@ -224,6 +230,8 @@ def guessFileType(fileName): head = header[:4] if head == "OTTO": return "OTF" + elif head == "ttcf": + return "TTC" elif head in ("\0\1\0\0", "true"): return "TTF" elif head.lower() == "