| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- #!/usr/bin/env python
- # ***** BEGIN LICENSE BLOCK *****
- # Version: MPL 1.1/GPL 2.0/LGPL 2.1
- #
- # The contents of this file are subject to the Mozilla Public License Version
- # 1.1 (the "License"); you may not use this file except in compliance with
- # the License. You may obtain a copy of the License at
- # http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS IS" basis,
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- # for the specific language governing rights and limitations under the
- # License.
- #
- # The Original Code is font utility code.
- #
- # The Initial Developer of the Original Code is Mozilla Corporation.
- # Portions created by the Initial Developer are Copyright (C) 2009
- # the Initial Developer. All Rights Reserved.
- #
- # Contributor(s):
- # John Daggett <jdaggett@mozilla.com>
- #
- # Alternatively, the contents of this file may be used under the terms of
- # either the GNU General Public License Version 2 or later (the "GPL"), or
- # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- # in which case the provisions of the GPL or the LGPL are applicable instead
- # of those above. If you wish to allow use of your version of this file only
- # under the terms of either the GPL or the LGPL, and not to allow others to
- # use your version of this file under the terms of the MPL, indicate your
- # decision by deleting the provisions above and replace them with the notice
- # and other provisions required by the GPL or the LGPL. If you do not delete
- # the provisions above, a recipient may use your version of this file under
- # the terms of any one of the MPL, the GPL or the LGPL.
- #
- # ***** END LICENSE BLOCK ***** */
- # eotlitetool.py - create EOT version of OpenType font for use with IE
- #
- # Usage: eotlitetool.py [-o output-filename] font1 [font2 ...]
- #
- # OpenType file structure
- # http://www.microsoft.com/typography/otspec/otff.htm
- #
- # Types:
- #
- # BYTE 8-bit unsigned integer.
- # CHAR 8-bit signed integer.
- # USHORT 16-bit unsigned integer.
- # SHORT 16-bit signed integer.
- # ULONG 32-bit unsigned integer.
- # Fixed 32-bit signed fixed-point number (16.16)
- # LONGDATETIME Date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a signed 64-bit integer.
- #
- # SFNT Header
- #
- # Fixed sfnt version // 0x00010000 for version 1.0.
- # USHORT numTables // Number of tables.
- # USHORT searchRange // (Maximum power of 2 <= numTables) x 16.
- # USHORT entrySelector // Log2(maximum power of 2 <= numTables).
- # USHORT rangeShift // NumTables x 16-searchRange.
- #
- # Table Directory
- #
- # ULONG tag // 4-byte identifier.
- # ULONG checkSum // CheckSum for this table.
- # ULONG offset // Offset from beginning of TrueType font file.
- # ULONG length // Length of this table.
- #
- # OS/2 Table (Version 4)
- #
- # USHORT version // 0x0004
- # SHORT xAvgCharWidth
- # USHORT usWeightClass
- # USHORT usWidthClass
- # USHORT fsType
- # SHORT ySubscriptXSize
- # SHORT ySubscriptYSize
- # SHORT ySubscriptXOffset
- # SHORT ySubscriptYOffset
- # SHORT ySuperscriptXSize
- # SHORT ySuperscriptYSize
- # SHORT ySuperscriptXOffset
- # SHORT ySuperscriptYOffset
- # SHORT yStrikeoutSize
- # SHORT yStrikeoutPosition
- # SHORT sFamilyClass
- # BYTE panose[10]
- # ULONG ulUnicodeRange1 // Bits 0-31
- # ULONG ulUnicodeRange2 // Bits 32-63
- # ULONG ulUnicodeRange3 // Bits 64-95
- # ULONG ulUnicodeRange4 // Bits 96-127
- # CHAR achVendID[4]
- # USHORT fsSelection
- # USHORT usFirstCharIndex
- # USHORT usLastCharIndex
- # SHORT sTypoAscender
- # SHORT sTypoDescender
- # SHORT sTypoLineGap
- # USHORT usWinAscent
- # USHORT usWinDescent
- # ULONG ulCodePageRange1 // Bits 0-31
- # ULONG ulCodePageRange2 // Bits 32-63
- # SHORT sxHeight
- # SHORT sCapHeight
- # USHORT usDefaultChar
- # USHORT usBreakChar
- # USHORT usMaxContext
- #
- #
- # The Naming Table is organized as follows:
- #
- # [name table header]
- # [name records]
- # [string data]
- #
- # Name Table Header
- #
- # USHORT format // Format selector (=0).
- # USHORT count // Number of name records.
- # USHORT stringOffset // Offset to start of string storage (from start of table).
- #
- # Name Record
- #
- # USHORT platformID // Platform ID.
- # USHORT encodingID // Platform-specific encoding ID.
- # USHORT languageID // Language ID.
- # USHORT nameID // Name ID.
- # USHORT length // String length (in bytes).
- # USHORT offset // String offset from start of storage area (in bytes).
- #
- # head Table
- #
- # Fixed tableVersion // Table version number 0x00010000 for version 1.0.
- # Fixed fontRevision // Set by font manufacturer.
- # ULONG checkSumAdjustment // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum.
- # ULONG magicNumber // Set to 0x5F0F3CF5.
- # USHORT flags
- # USHORT unitsPerEm // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
- # LONGDATETIME created // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
- # LONGDATETIME modified // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
- # SHORT xMin // For all glyph bounding boxes.
- # SHORT yMin
- # SHORT xMax
- # SHORT yMax
- # USHORT macStyle
- # USHORT lowestRecPPEM // Smallest readable size in pixels.
- # SHORT fontDirectionHint
- # SHORT indexToLocFormat // 0 for short offsets, 1 for long.
- # SHORT glyphDataFormat // 0 for current format.
- #
- #
- #
- # Embedded OpenType (EOT) file format
- # http://www.w3.org/Submission/EOT/
- #
- # EOT version 0x00020001
- #
- # An EOT font consists of a header with the original OpenType font
- # appended at the end. Most of the data in the EOT header is simply a
- # copy of data from specific tables within the font data. The exceptions
- # are the 'Flags' field and the root string name field. The root string
- # is a set of names indicating domains for which the font data can be
- # used. A null root string implies the font data can be used anywhere.
- # The EOT header is in little-endian byte order but the font data remains
- # in big-endian order as specified by the OpenType spec.
- #
- # Overall structure:
- #
- # [EOT header]
- # [EOT name records]
- # [font data]
- #
- # EOT header
- #
- # ULONG eotSize // Total structure length in bytes (including string and font data)
- # ULONG fontDataSize // Length of the OpenType font (FontData) in bytes
- # ULONG version // Version number of this format - 0x00020001
- # ULONG flags // Processing Flags (0 == no special processing)
- # BYTE fontPANOSE[10] // OS/2 Table panose
- # BYTE charset // DEFAULT_CHARSET (0x01)
- # BYTE italic // 0x01 if ITALIC in OS/2 Table fsSelection is set, 0 otherwise
- # ULONG weight // OS/2 Table usWeightClass
- # USHORT fsType // OS/2 Table fsType (specifies embedding permission flags)
- # USHORT magicNumber // Magic number for EOT file - 0x504C.
- # ULONG unicodeRange1 // OS/2 Table ulUnicodeRange1
- # ULONG unicodeRange2 // OS/2 Table ulUnicodeRange2
- # ULONG unicodeRange3 // OS/2 Table ulUnicodeRange3
- # ULONG unicodeRange4 // OS/2 Table ulUnicodeRange4
- # ULONG codePageRange1 // OS/2 Table ulCodePageRange1
- # ULONG codePageRange2 // OS/2 Table ulCodePageRange2
- # ULONG checkSumAdjustment // head Table CheckSumAdjustment
- # ULONG reserved[4] // Reserved - must be 0
- # USHORT padding1 // Padding - must be 0
- #
- # EOT name records
- #
- # USHORT FamilyNameSize // Font family name size in bytes
- # BYTE FamilyName[FamilyNameSize] // Font family name (name ID = 1), little-endian UTF-16
- # USHORT Padding2 // Padding - must be 0
- #
- # USHORT StyleNameSize // Style name size in bytes
- # BYTE StyleName[StyleNameSize] // Style name (name ID = 2), little-endian UTF-16
- # USHORT Padding3 // Padding - must be 0
- #
- # USHORT VersionNameSize // Version name size in bytes
- # bytes VersionName[VersionNameSize] // Version name (name ID = 5), little-endian UTF-16
- # USHORT Padding4 // Padding - must be 0
- #
- # USHORT FullNameSize // Full name size in bytes
- # BYTE FullName[FullNameSize] // Full name (name ID = 4), little-endian UTF-16
- # USHORT Padding5 // Padding - must be 0
- #
- # USHORT RootStringSize // Root string size in bytes
- # BYTE RootString[RootStringSize] // Root string, little-endian UTF-16
- import optparse
- import struct
- class FontError(Exception):
- """Error related to font handling"""
- pass
- def multichar(str):
- vals = struct.unpack('4B', str[:4])
- return (vals[0] << 24) + (vals[1] << 16) + (vals[2] << 8) + vals[3]
- def multicharval(v):
- return struct.pack('4B', (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
- class EOT:
- EOT_VERSION = 0x00020001
- EOT_MAGIC_NUMBER = 0x504c
- EOT_DEFAULT_CHARSET = 0x01
- EOT_FAMILY_NAME_INDEX = 0 # order of names in variable portion of EOT header
- EOT_STYLE_NAME_INDEX = 1
- EOT_VERSION_NAME_INDEX = 2
- EOT_FULL_NAME_INDEX = 3
- EOT_NUM_NAMES = 4
-
- EOT_HEADER_PACK = '<4L10B2BL2H7L18x'
- class OpenType:
- SFNT_CFF = multichar('OTTO') # Postscript CFF SFNT version
- SFNT_TRUE = 0x10000 # Standard TrueType version
- SFNT_APPLE = multichar('true') # Apple TrueType version
-
- SFNT_UNPACK = '>I4H'
- TABLE_DIR_UNPACK = '>4I'
-
- TABLE_HEAD = multichar('head') # TrueType table tags
- TABLE_NAME = multichar('name')
- TABLE_OS2 = multichar('OS/2')
- TABLE_GLYF = multichar('glyf')
- TABLE_CFF = multichar('CFF ')
-
- OS2_FSSELECTION_ITALIC = 0x1
- OS2_UNPACK = '>4xH2xH22x10B4L4xH14x2L'
-
- HEAD_UNPACK = '>8xL'
-
- NAME_RECORD_UNPACK = '>6H'
- NAME_ID_FAMILY = 1
- NAME_ID_STYLE = 2
- NAME_ID_UNIQUE = 3
- NAME_ID_FULL = 4
- NAME_ID_VERSION = 5
- NAME_ID_POSTSCRIPT = 6
- PLATFORM_ID_UNICODE = 0 # Mac OS uses this typically
- PLATFORM_ID_MICROSOFT = 3
- ENCODING_ID_MICROSOFT_UNICODEBMP = 1 # with Microsoft platformID BMP-only Unicode encoding
- LANG_ID_MICROSOFT_EN_US = 0x0409 # with Microsoft platformID EN US lang code
- def eotname(ttf):
- i = ttf.rfind('.')
- if i != -1:
- ttf = ttf[:i]
- return ttf + '.eotlite'
- def readfont(f):
- data = open(f, 'rb').read()
- return data
- def get_table_directory(data):
- """read the SFNT header and table directory"""
- datalen = len(data)
- sfntsize = struct.calcsize(OpenType.SFNT_UNPACK)
- if sfntsize > datalen:
- raise FontError, 'truncated font data'
- sfntvers, numTables = struct.unpack(OpenType.SFNT_UNPACK, data[:sfntsize])[:2]
- if sfntvers != OpenType.SFNT_CFF and sfntvers != OpenType.SFNT_TRUE:
- raise FontError, 'invalid font type';
-
- font = {}
- font['version'] = sfntvers
- font['numTables'] = numTables
-
- # create set of offsets, lengths for tables
- table_dir_size = struct.calcsize(OpenType.TABLE_DIR_UNPACK)
- if sfntsize + table_dir_size * numTables > datalen:
- raise FontError, 'truncated font data, table directory extends past end of data'
- table_dir = {}
- for i in range(0, numTables):
- start = sfntsize + i * table_dir_size
- end = start + table_dir_size
- tag, check, bongo, dirlen = struct.unpack(OpenType.TABLE_DIR_UNPACK, data[start:end])
- table_dir[tag] = {'offset': bongo, 'length': dirlen, 'checksum': check}
-
- font['tableDir'] = table_dir
-
- return font
- def get_name_records(nametable):
- """reads through the name records within name table"""
- name = {}
- # read the header
- headersize = 6
- count, strOffset = struct.unpack('>2H', nametable[2:6])
- namerecsize = struct.calcsize(OpenType.NAME_RECORD_UNPACK)
- if count * namerecsize + headersize > len(nametable):
- raise FontError, 'names exceed size of name table'
- name['count'] = count
- name['strOffset'] = strOffset
-
- # read through the name records
- namerecs = {}
- for i in range(0, count):
- start = headersize + i * namerecsize
- end = start + namerecsize
- platformID, encodingID, languageID, nameID, namelen, offset = struct.unpack(OpenType.NAME_RECORD_UNPACK, nametable[start:end])
- if platformID != OpenType.PLATFORM_ID_MICROSOFT or \
- encodingID != OpenType.ENCODING_ID_MICROSOFT_UNICODEBMP or \
- languageID != OpenType.LANG_ID_MICROSOFT_EN_US:
- continue
- namerecs[nameID] = {'offset': offset, 'length': namelen}
-
- name['namerecords'] = namerecs
- return name
- def make_eot_name_headers(fontdata, nameTableDir):
- """extracts names from the name table and generates the names header portion of the EOT header"""
- nameoffset = nameTableDir['offset']
- namelen = nameTableDir['length']
- name = get_name_records(fontdata[nameoffset : nameoffset + namelen])
- namestroffset = name['strOffset']
- namerecs = name['namerecords']
-
- eotnames = (OpenType.NAME_ID_FAMILY, OpenType.NAME_ID_STYLE, OpenType.NAME_ID_VERSION, OpenType.NAME_ID_FULL)
- nameheaders = []
- for nameid in eotnames:
- if nameid in namerecs:
- namerecord = namerecs[nameid]
- noffset = namerecord['offset']
- nlen = namerecord['length']
- nformat = '%dH' % (nlen / 2) # length is in number of bytes
- start = nameoffset + namestroffset + noffset
- end = start + nlen
- nstr = struct.unpack('>' + nformat, fontdata[start:end])
- nameheaders.append(struct.pack('<H' + nformat + '2x', nlen, *nstr))
- else:
- nameheaders.append(struct.pack('4x')) # len = 0, padding = 0
-
- return ''.join(nameheaders)
- # just return a null-string (len = 0)
- def make_root_string():
- return struct.pack('2x')
- def make_eot_header(fontdata):
- """given ttf font data produce an EOT header"""
- fontDataSize = len(fontdata)
- font = get_table_directory(fontdata)
-
- # toss out .otf fonts, t2embed library doesn't support these
- tableDir = font['tableDir']
-
- # check for required tables
- required = (OpenType.TABLE_HEAD, OpenType.TABLE_NAME, OpenType.TABLE_OS2)
- for table in required:
- if not (table in tableDir):
- raise FontError, 'missing required table ' + multicharval(table)
-
- # read name strings
-
- # pull out data from individual tables to construct fixed header portion
- # need to calculate eotSize before packing
- version = EOT.EOT_VERSION
- flags = 0
- charset = EOT.EOT_DEFAULT_CHARSET
- magicNumber = EOT.EOT_MAGIC_NUMBER
-
- # read values from OS/2 table
- os2Dir = tableDir[OpenType.TABLE_OS2]
- os2offset = os2Dir['offset']
- os2size = struct.calcsize(OpenType.OS2_UNPACK)
-
- if os2size > os2Dir['length']:
- raise FontError, 'OS/2 table invalid length'
-
- os2fields = struct.unpack(OpenType.OS2_UNPACK, fontdata[os2offset : os2offset + os2size])
-
- panose = []
- urange = []
- codepage = []
-
- weight, fsType = os2fields[:2]
- panose[:10] = os2fields[2:12]
- urange[:4] = os2fields[12:16]
- fsSelection = os2fields[16]
- codepage[:2] = os2fields[17:19]
-
- italic = fsSelection & OpenType.OS2_FSSELECTION_ITALIC
-
- # read in values from head table
- headDir = tableDir[OpenType.TABLE_HEAD]
- headoffset = headDir['offset']
- headsize = struct.calcsize(OpenType.HEAD_UNPACK)
-
- if headsize > headDir['length']:
- raise FontError, 'head table invalid length'
-
- headfields = struct.unpack(OpenType.HEAD_UNPACK, fontdata[headoffset : headoffset + headsize])
- checkSumAdjustment = headfields[0]
-
- # make name headers
- nameheaders = make_eot_name_headers(fontdata, tableDir[OpenType.TABLE_NAME])
- rootstring = make_root_string()
-
- # calculate the total eot size
- eotSize = struct.calcsize(EOT.EOT_HEADER_PACK) + len(nameheaders) + len(rootstring) + fontDataSize
- fixed = struct.pack(EOT.EOT_HEADER_PACK,
- *([eotSize, fontDataSize, version, flags] + panose + [charset, italic] +
- [weight, fsType, magicNumber] + urange + codepage + [checkSumAdjustment]))
-
- return ''.join((fixed, nameheaders, rootstring))
-
-
- def write_eot_font(eot, header, data):
- open(eot,'wb').write(''.join((header, data)))
- return
- def main():
- # deal with options
- p = optparse.OptionParser()
- p.add_option('--output', '-o', default="world")
- options, args = p.parse_args()
-
- # iterate over font files
- for f in args:
- data = readfont(f)
- if len(data) == 0:
- print 'Error reading %s' % f
- else:
- eot = eotname(f)
- header = make_eot_header(data)
- write_eot_font(eot, header, data)
-
- if __name__ == '__main__':
- main()
-
-
|