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
This commit is contained in:
pabs3 2009-02-22 08:55:00 +00:00
parent d67cf25974
commit 7e91e776c9
3 changed files with 43 additions and 8 deletions

View File

@ -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 import sys
@ -70,7 +70,8 @@ class TTFont:
def __init__(self, file=None, res_name_or_index=None, def __init__(self, file=None, res_name_or_index=None,
sfntVersion="\000\001\000\000", checkChecksums=0, 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. """The constructor can be called with a few different arguments.
When reading a font from disk, 'file' should be either a pathname When reading a font from disk, 'file' should be either a pathname
@ -152,7 +153,7 @@ class TTFont:
file = open(file, "rb") file = open(file, "rb")
else: else:
pass # assume "file" is a readable file object 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 self.sfntVersion = self.reader.sfntVersion
def close(self): def close(self):

View File

@ -20,7 +20,7 @@ import os
class SFNTReader: class SFNTReader:
def __init__(self, file, checkChecksums=1): def __init__(self, file, checkChecksums=1, fontNumber=-1):
self.file = file self.file = file
self.checkChecksums = checkChecksums self.checkChecksums = checkChecksums
data = self.file.read(sfntDirectorySize) data = self.file.read(sfntDirectorySize)
@ -28,6 +28,19 @@ class SFNTReader:
from fontTools import ttLib from fontTools import ttLib
raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)" raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)"
sstruct.unpack(sfntDirectoryFormat, data, self) 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"): if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
from fontTools import ttLib from fontTools import ttLib
raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)" raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
@ -165,6 +178,19 @@ class SFNTWriter:
# -- sfnt directory helpers and cruft # -- 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 = """ sfntDirectoryFormat = """
> # big endian > # big endian
sfntVersion: 4s sfntVersion: 4s

View File

@ -41,6 +41,8 @@ usage: ttx [options] inputfile1 [... inputfileN]
file smaller. file smaller.
-e Don't ignore decompilation errors, but show a full traceback -e Don't ignore decompilation errors, but show a full traceback
and abort. and abort.
-y <number> Select font number for TrueTrue Collection,
starting from 0.
Compile options: Compile options:
-m Merge with TrueType-input-file: specify a TrueType or OpenType -m Merge with TrueType-input-file: specify a TrueType or OpenType
@ -99,6 +101,7 @@ class Options:
def __init__(self, rawOptions, numFiles): def __init__(self, rawOptions, numFiles):
self.onlyTables = [] self.onlyTables = []
self.skipTables = [] self.skipTables = []
self.fontNumber = -1
for option, value in rawOptions: for option, value in rawOptions:
# general options # general options
if option == "-h": if option == "-h":
@ -122,6 +125,8 @@ class Options:
self.splitTables = 1 self.splitTables = 1
elif option == "-i": elif option == "-i":
self.disassembleInstructions = 0 self.disassembleInstructions = 0
elif option == "-y":
self.fontNumber = int(value)
# compile options # compile options
elif option == "-m": elif option == "-m":
self.mergeFile = value self.mergeFile = value
@ -141,7 +146,7 @@ class Options:
def ttList(input, output, options): def ttList(input, output, options):
import string import string
ttf = TTFont(input) ttf = TTFont(input, fontNumber=options.fontNumber)
reader = ttf.reader reader = ttf.reader
tags = reader.keys() tags = reader.keys()
tags.sort() tags.sort()
@ -163,7 +168,8 @@ def ttList(input, output, options):
def ttDump(input, output, options): def ttDump(input, output, options):
print 'Dumping "%s" to "%s"...' % (input, output) print 'Dumping "%s" to "%s"...' % (input, output)
ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID, ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID,
ignoreDecompileErrors=options.ignoreDecompileErrors) ignoreDecompileErrors=options.ignoreDecompileErrors,
fontNumber=options.fontNumber)
ttf.saveXML(output, ttf.saveXML(output,
tables=options.onlyTables, tables=options.onlyTables,
skipTables=options.skipTables, skipTables=options.skipTables,
@ -224,6 +230,8 @@ def guessFileType(fileName):
head = header[:4] head = header[:4]
if head == "OTTO": if head == "OTTO":
return "OTF" return "OTF"
elif head == "ttcf":
return "TTC"
elif head in ("\0\1\0\0", "true"): elif head in ("\0\1\0\0", "true"):
return "TTF" return "TTF"
elif head.lower() == "<?xm": elif head.lower() == "<?xm":
@ -236,7 +244,7 @@ def guessFileType(fileName):
def parseOptions(args): def parseOptions(args):
try: try:
rawOptions, files = getopt.getopt(args, "ld:vht:x:sim:bae") rawOptions, files = getopt.getopt(args, "ld:vht:x:sim:baey:")
except getopt.GetoptError: except getopt.GetoptError:
usage() usage()
@ -248,7 +256,7 @@ def parseOptions(args):
for input in files: for input in files:
tp = guessFileType(input) tp = guessFileType(input)
if tp in ("OTF", "TTF"): if tp in ("OTF", "TTF", "TTC"):
extension = ".ttx" extension = ".ttx"
if options.listTables: if options.listTables:
action = ttList action = ttList