2014-01-14 15:07:50 +08:00
from __future__ import print_function , division , absolute_import
2013-11-27 17:27:45 -05:00
from fontTools . misc . py23 import *
from fontTools . misc . textTools import safeEval , readHex
2015-04-19 03:50:55 -07:00
from fontTools . misc . encodingTools import getEncoding
2014-05-27 16:01:47 -04:00
from fontTools . ttLib import getSearchRange
2014-03-12 12:17:33 -07:00
from fontTools . unicode import Unicode
2013-11-27 02:34:11 -05:00
from . import DefaultTable
2013-11-27 17:27:45 -05:00
import sys
1999-12-16 21:34:53 +00:00
import struct
import array
2016-01-24 14:39:20 +00:00
import logging
log = logging . getLogger ( __name__ )
1999-12-16 21:34:53 +00:00
2016-12-27 18:29:10 -05:00
def _make_map ( font , chars , gids ) :
assert len ( chars ) == len ( gids )
cmap = { }
glyphOrder = font . getGlyphOrder ( )
2016-12-27 18:38:42 -05:00
for char , gid in zip ( chars , gids ) :
2016-12-27 18:39:17 -05:00
if gid is 0 :
continue
2016-12-27 18:38:42 -05:00
try :
name = glyphOrder [ gid ]
except IndexError :
name = font . getGlyphName ( gid )
2016-12-27 18:39:17 -05:00
cmap [ char ] = name
2016-12-27 18:29:10 -05:00
return cmap
1999-12-16 21:34:53 +00:00
class table__c_m_a_p ( DefaultTable . DefaultTable ) :
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def getcmap ( self , platformID , platEncID ) :
for subtable in self . tables :
2015-04-26 02:01:01 -04:00
if ( subtable . platformID == platformID and
1999-12-16 21:34:53 +00:00
subtable . platEncID == platEncID ) :
return subtable
return None # not found
2015-04-26 02:01:01 -04:00
2017-11-04 07:45:11 +01:00
def getBestCmap ( self , cmapPreferences = ( ( 3 , 10 ) , ( 0 , 6 ) , ( 0 , 4 ) , ( 3 , 1 ) , ( 0 , 3 ) , ( 0 , 2 ) , ( 0 , 1 ) , ( 0 , 0 ) ) ) :
2017-11-03 16:19:48 +01:00
""" Return the ' best ' unicode cmap dictionary available in the font,
or None , if no unicode cmap subtable is available .
2017-11-03 15:16:32 +01:00
By default it will search for the following ( platformID , platEncID )
pairs :
2017-11-04 07:45:11 +01:00
( 3 , 10 ) , ( 0 , 6 ) , ( 0 , 4 ) , ( 3 , 1 ) , ( 0 , 3 ) , ( 0 , 2 ) , ( 0 , 1 ) , ( 0 , 0 )
This can be customized via the cmapPreferences argument .
2017-11-03 11:36:01 +01:00
"""
for platformID , platEncID in cmapPreferences :
cmapSubtable = self . getcmap ( platformID , platEncID )
if cmapSubtable is not None :
return cmapSubtable . cmap
2017-11-03 16:19:48 +01:00
return None # None of the requested cmap subtables were found
2017-11-03 11:36:01 +01:00
2015-09-04 10:21:55 +02:00
def buildReversed ( self ) :
""" Returns a reverse cmap such as { ' one ' : {0x31} , ' A ' : { 0x41,0x391}}.
The values are sets of Unicode codepoints because
some fonts map different codepoints to the same glyph .
For example , U + 0041 LATIN CAPITAL LETTER A and U + 0391
GREEK CAPITAL LETTER ALPHA are sometimes the same glyph .
"""
result = { }
for subtable in self . tables :
if subtable . isUnicode ( ) :
for codepoint , name in subtable . cmap . items ( ) :
result . setdefault ( name , set ( ) ) . add ( codepoint )
return result
1999-12-16 21:34:53 +00:00
def decompile ( self , data , ttFont ) :
tableVersion , numSubTables = struct . unpack ( " >HH " , data [ : 4 ] )
self . tableVersion = int ( tableVersion )
self . tables = tables = [ ]
2006-10-21 13:54:30 +00:00
seenOffsets = { }
1999-12-16 21:34:53 +00:00
for i in range ( numSubTables ) :
platformID , platEncID , offset = struct . unpack (
" >HHl " , data [ 4 + i * 8 : 4 + ( i + 1 ) * 8 ] )
platformID , platEncID = int ( platformID ) , int ( platEncID )
format , length = struct . unpack ( " >HH " , data [ offset : offset + 4 ] )
2013-10-09 15:55:07 -07:00
if format in [ 8 , 10 , 12 , 13 ] :
2003-02-08 10:45:23 +00:00
format , reserved , length = struct . unpack ( " >HHL " , data [ offset : offset + 8 ] )
2008-05-16 15:07:09 +00:00
elif format in [ 14 ] :
format , length = struct . unpack ( " >HL " , data [ offset : offset + 6 ] )
2015-04-26 02:01:01 -04:00
2008-02-29 14:43:49 +00:00
if not length :
2016-01-24 14:39:20 +00:00
log . error (
" cmap subtable is reported as having zero length: platformID %s , "
" platEncID %s , format %s offset %s . Skipping table. " ,
platformID , platEncID , format , offset )
2008-02-29 14:43:49 +00:00
continue
2015-04-26 02:40:53 -04:00
table = CmapSubtable . newSubtable ( format )
1999-12-16 21:34:53 +00:00
table . platformID = platformID
table . platEncID = platEncID
2006-10-21 13:54:30 +00:00
# Note that by default we decompile only the subtable header info;
# any other data gets decompiled only when an attribute of the
# subtable is referenced.
table . decompileHeader ( data [ offset : offset + int ( length ) ] , ttFont )
2013-11-27 02:33:03 -05:00
if offset in seenOffsets :
2015-06-22 19:11:50 -07:00
table . data = None # Mark as decompiled
2006-10-21 13:54:30 +00:00
table . cmap = tables [ seenOffsets [ offset ] ] . cmap
else :
seenOffsets [ offset ] = i
1999-12-16 21:34:53 +00:00
tables . append ( table )
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def compile ( self , ttFont ) :
2019-01-10 19:35:34 +01:00
self . tables . sort ( ) # sort according to the spec; see CmapSubtable.__lt__()
1999-12-16 21:34:53 +00:00
numSubTables = len ( self . tables )
totalOffset = 4 + 8 * numSubTables
data = struct . pack ( " >HH " , self . tableVersion , numSubTables )
2013-11-27 21:09:03 -05:00
tableData = b " "
2006-10-21 13:54:30 +00:00
seen = { } # Some tables are the same object reference. Don't compile them twice.
done = { } # Some tables are different objects, but compile to the same data chunk
1999-12-16 21:34:53 +00:00
for table in self . tables :
2006-10-21 13:54:30 +00:00
try :
offset = seen [ id ( table . cmap ) ]
except KeyError :
chunk = table . compile ( ttFont )
2013-11-27 02:33:03 -05:00
if chunk in done :
2006-10-21 13:54:30 +00:00
offset = done [ chunk ]
else :
offset = seen [ id ( table . cmap ) ] = done [ chunk ] = totalOffset + len ( tableData )
tableData = tableData + chunk
1999-12-16 21:34:53 +00:00
data = data + struct . pack ( " >HHl " , table . platformID , table . platEncID , offset )
return data + tableData
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def toXML ( self , writer , ttFont ) :
writer . simpletag ( " tableVersion " , version = self . tableVersion )
writer . newline ( )
for table in self . tables :
table . toXML ( writer , ttFont )
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
1999-12-16 21:34:53 +00:00
if name == " tableVersion " :
self . tableVersion = safeEval ( attrs [ " version " ] )
return
2013-11-27 02:40:30 -05:00
if name [ : 12 ] != " cmap_format_ " :
1999-12-16 21:34:53 +00:00
return
if not hasattr ( self , " tables " ) :
self . tables = [ ]
2004-09-25 07:30:47 +00:00
format = safeEval ( name [ 12 : ] )
2015-04-26 02:40:53 -04:00
table = CmapSubtable . newSubtable ( format )
1999-12-16 21:34:53 +00:00
table . platformID = safeEval ( attrs [ " platformID " ] )
table . platEncID = safeEval ( attrs [ " platEncID " ] )
2013-11-27 03:19:32 -05:00
table . fromXML ( name , attrs , content , ttFont )
1999-12-16 21:34:53 +00:00
self . tables . append ( table )
2013-11-28 14:26:58 -05:00
class CmapSubtable ( object ) :
2015-04-26 02:01:01 -04:00
2015-04-26 02:40:53 -04:00
@staticmethod
def getSubtableClass ( format ) :
""" Return the subtable class for a format. """
return cmap_classes . get ( format , cmap_format_unknown )
@staticmethod
def newSubtable ( format ) :
""" Return a new instance of a subtable for format. """
subtableClass = CmapSubtable . getSubtableClass ( format )
return subtableClass ( format )
1999-12-16 21:34:53 +00:00
def __init__ ( self , format ) :
self . format = format
2006-10-21 13:54:30 +00:00
self . data = None
self . ttFont = None
def __getattr__ ( self , attr ) :
# allow lazy decompilation of subtables.
if attr [ : 2 ] == ' __ ' : # don't handle requests for member functions like '__lt__'
2013-11-27 02:42:28 -05:00
raise AttributeError ( attr )
2013-12-04 16:31:44 -05:00
if self . data is None :
2013-11-27 02:42:28 -05:00
raise AttributeError ( attr )
2006-10-21 13:54:30 +00:00
self . decompile ( None , None ) # use saved data.
2015-08-09 00:33:50 -07:00
self . data = None # Once this table has been decompiled, make sure we don't
# just return the original data. Also avoids recursion when
# called with an attribute that the cmap subtable doesn't have.
2006-10-21 13:54:30 +00:00
return getattr ( self , attr )
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
def decompileHeader ( self , data , ttFont ) :
format , length , language = struct . unpack ( " >HHH " , data [ : 6 ] )
assert len ( data ) == length , " corrupt cmap table format %d (data length: %d , header length: %d ) " % ( format , len ( data ) , length )
self . format = int ( format )
self . length = int ( length )
self . language = int ( language )
self . data = data [ 6 : ]
self . ttFont = ttFont
1999-12-16 21:34:53 +00:00
def toXML ( self , writer , ttFont ) :
writer . begintag ( self . __class__ . __name__ , [
( " platformID " , self . platformID ) ,
( " platEncID " , self . platEncID ) ,
2004-09-25 09:06:58 +00:00
( " language " , self . language ) ,
1999-12-16 21:34:53 +00:00
] )
writer . newline ( )
2013-11-27 04:15:34 -05:00
codes = sorted ( self . cmap . items ( ) )
2004-09-25 09:06:58 +00:00
self . _writeCodes ( codes , writer )
1999-12-16 21:34:53 +00:00
writer . endtag ( self . __class__ . __name__ )
writer . newline ( )
2004-09-25 09:06:58 +00:00
2015-04-19 03:50:55 -07:00
def getEncoding ( self , default = None ) :
""" Returns the Python encoding name for this cmap subtable based on its platformID,
platEncID , and language . If encoding for these values is not known , by default
None is returned . That can be overriden by passing a value to the default
argument .
Note that if you want to choose a " preferred " cmap subtable , most of the time
self . isUnicode ( ) is what you want as that one only returns true for the modern ,
commonly used , Unicode - compatible triplets , not the legacy ones .
"""
return getEncoding ( self . platformID , self . platEncID , self . language , default )
2014-03-12 12:17:33 -07:00
def isUnicode ( self ) :
return ( self . platformID == 0 or
2015-03-26 02:44:48 -04:00
( self . platformID == 3 and self . platEncID in [ 0 , 1 , 10 ] ) )
2014-03-12 12:17:33 -07:00
def isSymbol ( self ) :
return self . platformID == 3 and self . platEncID == 0
2004-09-25 09:06:58 +00:00
def _writeCodes ( self , codes , writer ) :
2014-03-12 12:17:33 -07:00
isUnicode = self . isUnicode ( )
2004-09-25 09:06:58 +00:00
for code , name in codes :
writer . simpletag ( " map " , code = hex ( code ) , name = name )
if isUnicode :
writer . comment ( Unicode [ code ] )
writer . newline ( )
2015-04-26 02:01:01 -04:00
2013-11-27 18:58:45 -05:00
def __lt__ ( self , other ) :
if not isinstance ( other , CmapSubtable ) :
2013-12-07 03:40:44 -05:00
return NotImplemented
2013-08-17 11:11:22 -04:00
2013-11-27 18:58:45 -05:00
# implemented so that list.sort() sorts according to the spec.
1999-12-16 21:34:53 +00:00
selfTuple = (
2013-10-28 12:16:41 +01:00
getattr ( self , " platformID " , None ) ,
getattr ( self , " platEncID " , None ) ,
getattr ( self , " language " , None ) ,
self . __dict__ )
1999-12-16 21:34:53 +00:00
otherTuple = (
2013-10-28 12:16:41 +01:00
getattr ( other , " platformID " , None ) ,
getattr ( other , " platEncID " , None ) ,
getattr ( other , " language " , None ) ,
other . __dict__ )
2013-11-27 18:58:45 -05:00
return selfTuple < otherTuple
1999-12-16 21:34:53 +00:00
class cmap_format_0 ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def decompile ( self , data , ttFont ) :
2006-10-21 13:54:30 +00:00
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2006-10-21 13:54:30 +00:00
data = self . data # decompileHeader assigns the data after the header to self.data
assert 262 == self . length , " Format 0 cmap subtable not 262 bytes "
2016-12-27 18:29:10 -05:00
gids = array . array ( " B " )
gids . fromstring ( self . data )
charCodes = list ( range ( len ( gids ) ) )
self . cmap = _make_map ( self . ttFont , charCodes , gids )
2006-10-21 13:54:30 +00:00
1999-12-16 21:34:53 +00:00
def compile ( self , ttFont ) :
2006-10-21 13:54:30 +00:00
if self . data :
return struct . pack ( " >HHH " , 0 , 262 , self . language ) + self . data
2017-01-10 10:46:44 -08:00
cmap = self . cmap
assert set ( cmap . keys ( ) ) . issubset ( range ( 256 ) )
getGlyphID = ttFont . getGlyphID
valueList = [ getGlyphID ( cmap [ i ] ) if i in cmap else 0 for i in range ( 256 ) ]
2006-10-21 13:54:30 +00:00
2016-12-27 18:29:10 -05:00
gids = array . array ( " B " , valueList )
data = struct . pack ( " >HHH " , 0 , 262 , self . language ) + gids . tostring ( )
1999-12-16 21:34:53 +00:00
assert len ( data ) == 262
return data
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2004-09-25 07:30:47 +00:00
self . language = safeEval ( attrs [ " language " ] )
2006-10-21 13:54:30 +00:00
if not hasattr ( self , " cmap " ) :
self . cmap = { }
cmap = self . cmap
1999-12-16 21:34:53 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
1999-12-16 21:34:53 +00:00
continue
name , attrs , content = element
2013-11-27 02:40:30 -05:00
if name != " map " :
1999-12-16 21:34:53 +00:00
continue
2006-10-21 13:54:30 +00:00
cmap [ safeEval ( attrs [ " code " ] ) ] = attrs [ " name " ]
1999-12-16 21:34:53 +00:00
2003-08-28 18:04:23 +00:00
subHeaderFormat = " >HHhH "
2013-11-28 14:26:58 -05:00
class SubHeader ( object ) :
2003-08-28 18:04:23 +00:00
def __init__ ( self ) :
self . firstCode = None
self . entryCount = None
self . idDelta = None
self . idRangeOffset = None
self . glyphIndexArray = [ ]
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
class cmap_format_2 ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
def setIDDelta ( self , subHeader ) :
subHeader . idDelta = 0
# find the minGI which is not zero.
minGI = subHeader . glyphIndexArray [ 0 ]
for gid in subHeader . glyphIndexArray :
if ( gid != 0 ) and ( gid < minGI ) :
minGI = gid
# The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
# idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K.
2015-04-26 02:01:01 -04:00
# We would like to pick an idDelta such that the first glyphArray GID is 1,
2006-10-21 13:54:30 +00:00
# so that we are more likely to be able to combine glypharray GID subranges.
# This means that we have a problem when minGI is > 32K
# Since the final gi is reconstructed from the glyphArray GID by:
2019-01-10 19:35:34 +01:00
# (short)finalGID = (gid + idDelta) % 0x10000),
2006-10-21 13:54:30 +00:00
# we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
2015-04-26 02:01:01 -04:00
# negative number to an unsigned short.
2006-10-21 13:54:30 +00:00
2019-01-10 19:35:34 +01:00
if ( minGI > 1 ) :
if minGI > 0x7FFF :
2006-10-21 13:54:30 +00:00
subHeader . idDelta = - ( 0x10000 - minGI ) - 1
else :
2019-01-10 19:35:34 +01:00
subHeader . idDelta = minGI - 1
2006-10-21 13:54:30 +00:00
idDelta = subHeader . idDelta
for i in range ( subHeader . entryCount ) :
gid = subHeader . glyphIndexArray [ i ]
2015-04-26 02:01:01 -04:00
if gid > 0 :
subHeader . glyphIndexArray [ i ] = gid - idDelta
2006-10-21 13:54:30 +00:00
1999-12-16 21:34:53 +00:00
def decompile ( self , data , ttFont ) :
2006-10-21 13:54:30 +00:00
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2006-10-21 13:54:30 +00:00
data = self . data # decompileHeader assigns the data after the header to self.data
2003-08-28 18:04:23 +00:00
subHeaderKeys = [ ]
maxSubHeaderindex = 0
# get the key array, and determine the number of subHeaders.
2006-10-21 13:54:30 +00:00
allKeys = array . array ( " H " )
allKeys . fromstring ( data [ : 512 ] )
data = data [ 512 : ]
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : allKeys . byteswap ( )
2013-11-27 17:46:17 -05:00
subHeaderKeys = [ key / / 8 for key in allKeys ]
2006-10-21 13:54:30 +00:00
maxSubHeaderindex = max ( subHeaderKeys )
2015-04-26 02:01:01 -04:00
2003-08-28 18:04:23 +00:00
#Load subHeaders
subHeaderList = [ ]
2006-10-21 13:54:30 +00:00
pos = 0
2003-08-28 18:04:23 +00:00
for i in range ( maxSubHeaderindex + 1 ) :
subHeader = SubHeader ( )
( subHeader . firstCode , subHeader . entryCount , subHeader . idDelta , \
2006-10-21 13:54:30 +00:00
subHeader . idRangeOffset ) = struct . unpack ( subHeaderFormat , data [ pos : pos + 8 ] )
pos + = 8
giDataPos = pos + subHeader . idRangeOffset - 2
giList = array . array ( " H " )
giList . fromstring ( data [ giDataPos : giDataPos + subHeader . entryCount * 2 ] )
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : giList . byteswap ( )
2006-10-21 13:54:30 +00:00
subHeader . glyphIndexArray = giList
2003-08-28 18:04:23 +00:00
subHeaderList . append ( subHeader )
2015-04-26 02:01:01 -04:00
# How this gets processed.
2003-08-28 18:04:23 +00:00
# Charcodes may be one or two bytes.
2019-01-10 19:35:34 +01:00
# The first byte of a charcode is mapped through the subHeaderKeys, to select
2003-08-28 18:04:23 +00:00
# a subHeader. For any subheader but 0, the next byte is then mapped through the
2015-04-26 02:01:01 -04:00
# selected subheader. If subheader Index 0 is selected, then the byte itself is
2003-08-28 18:04:23 +00:00
# mapped through the subheader, and there is no second byte.
# Then assume that the subsequent byte is the first byte of the next charcode,and repeat.
2015-04-26 02:01:01 -04:00
#
2003-08-28 18:04:23 +00:00
# Each subheader references a range in the glyphIndexArray whose length is entryCount.
# The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray
# referenced by another subheader.
# The only subheader that will be referenced by more than one first-byte value is the subheader
# that maps the entire range of glyphID values to glyphIndex 0, e.g notdef:
# {firstChar 0, EntryCount 0,idDelta 0,idRangeOffset xx}
# A byte being mapped though a subheader is treated as in index into a mapping of array index to font glyphIndex.
# A subheader specifies a subrange within (0...256) by the
# firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero
# (e.g. glyph not in font).
# If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar).
2015-04-26 02:01:01 -04:00
# The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by
2003-08-28 18:04:23 +00:00
# counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the
# glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex.
# Example for Logocut-Medium
# first byte of charcode = 129; selects subheader 1.
# subheader 1 = {firstChar 64, EntryCount 108,idDelta 42,idRangeOffset 0252}
# second byte of charCode = 66
# the index offset = 66-64 = 2.
# The subrange of the glyphIndexArray starting at 0x0252 bytes from the idRangeOffset word is:
# [glyphIndexArray index], [subrange array index] = glyphIndex
# [256], [0]=1 from charcode [129, 64]
# [257], [1]=2 from charcode [129, 65]
# [258], [2]=3 from charcode [129, 66]
# [259], [3]=4 from charcode [129, 67]
2015-04-26 02:01:01 -04:00
# So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero,
2006-10-21 13:54:30 +00:00
# add it to the glyphID to get the final glyphIndex
2003-08-28 18:04:23 +00:00
# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
2015-04-26 02:01:01 -04:00
2013-11-27 22:00:49 -05:00
self . data = b " "
2016-12-27 18:29:10 -05:00
cmap = { }
2006-10-21 13:54:30 +00:00
notdefGI = 0
2003-08-28 18:04:23 +00:00
for firstByte in range ( 256 ) :
subHeadindex = subHeaderKeys [ firstByte ]
subHeader = subHeaderList [ subHeadindex ]
if subHeadindex == 0 :
if ( firstByte < subHeader . firstCode ) or ( firstByte > = subHeader . firstCode + subHeader . entryCount ) :
2006-10-21 13:54:30 +00:00
continue # gi is notdef.
2003-08-28 18:04:23 +00:00
else :
charCode = firstByte
offsetIndex = firstByte - subHeader . firstCode
gi = subHeader . glyphIndexArray [ offsetIndex ]
if gi != 0 :
2006-10-21 13:54:30 +00:00
gi = ( gi + subHeader . idDelta ) % 0x10000
else :
continue # gi is notdef.
cmap [ charCode ] = gi
2003-08-28 18:04:23 +00:00
else :
if subHeader . entryCount :
2006-10-21 13:54:30 +00:00
charCodeOffset = firstByte * 256 + subHeader . firstCode
2003-08-28 18:04:23 +00:00
for offsetIndex in range ( subHeader . entryCount ) :
2006-10-21 13:54:30 +00:00
charCode = charCodeOffset + offsetIndex
2003-08-28 18:04:23 +00:00
gi = subHeader . glyphIndexArray [ offsetIndex ]
if gi != 0 :
2006-10-21 13:54:30 +00:00
gi = ( gi + subHeader . idDelta ) % 0x10000
else :
continue
cmap [ charCode ] = gi
# If not subHeader.entryCount, then all char codes with this first byte are
2015-04-26 02:01:01 -04:00
# mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the
2006-10-21 13:54:30 +00:00
# same as mapping it to .notdef.
2016-12-27 18:29:10 -05:00
2013-11-27 06:26:55 -05:00
gids = list ( cmap . values ( ) )
charCodes = list ( cmap . keys ( ) )
2016-12-27 18:29:10 -05:00
self . cmap = _make_map ( self . ttFont , charCodes , gids )
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def compile ( self , ttFont ) :
2006-10-21 13:54:30 +00:00
if self . data :
return struct . pack ( " >HHH " , self . format , self . length , self . language ) + self . data
2003-08-28 18:04:23 +00:00
kEmptyTwoCharCodeRange = - 1
2006-10-21 13:54:30 +00:00
notdefGI = 0
2013-11-27 04:15:34 -05:00
items = sorted ( self . cmap . items ( ) )
2006-10-21 13:54:30 +00:00
charCodes = [ item [ 0 ] for item in items ]
names = [ item [ 1 ] for item in items ]
nameMap = ttFont . getReverseGlyphMap ( )
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
2013-12-04 21:28:50 -05:00
nameMap = ttFont . getReverseGlyphMap ( rebuild = True )
2006-10-21 13:54:30 +00:00
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
# allow virtual GIDs in format 2 tables
gids = [ ]
for name in names :
try :
gid = nameMap [ name ]
except KeyError :
try :
if ( name [ : 3 ] == ' gid ' ) :
2019-03-03 10:30:05 +01:00
gid = int ( name [ 3 : ] )
2006-10-21 13:54:30 +00:00
else :
gid = ttFont . getGlyphID ( name )
except :
raise KeyError ( name )
gids . append ( gid )
2019-01-10 19:35:34 +01:00
# Process the (char code to gid) item list in char code order.
2015-04-26 02:01:01 -04:00
# By definition, all one byte char codes map to subheader 0.
# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
2006-10-21 13:54:30 +00:00
# which defines all char codes in its range to map to notdef) unless proven otherwise.
# Note that since the char code items are processed in char code order, all the char codes with the
# same first byte are in sequential order.
2003-08-28 18:04:23 +00:00
2019-01-10 19:35:34 +01:00
subHeaderKeys = [ kEmptyTwoCharCodeRange for x in range ( 256 ) ] # list of indices into subHeaderList.
2003-08-28 18:04:23 +00:00
subHeaderList = [ ]
2006-10-21 13:54:30 +00:00
2019-01-10 19:35:34 +01:00
# We force this subheader entry 0 to exist in the subHeaderList in the case where some one comes up
2006-10-21 13:54:30 +00:00
# with a cmap where all the one byte char codes map to notdef,
# with the result that the subhead 0 would not get created just by processing the item list.
charCode = charCodes [ 0 ]
if charCode > 255 :
subHeader = SubHeader ( )
subHeader . firstCode = 0
subHeader . entryCount = 0
subHeader . idDelta = 0
subHeader . idRangeOffset = 0
subHeaderList . append ( subHeader )
2015-04-26 02:01:01 -04:00
2003-08-28 18:04:23 +00:00
lastFirstByte = - 1
2006-10-21 13:54:30 +00:00
items = zip ( charCodes , gids )
for charCode , gid in items :
if gid == 0 :
continue
2003-08-28 18:04:23 +00:00
firstbyte = charCode >> 8
secondByte = charCode & 0x00FF
2006-10-21 13:54:30 +00:00
if firstbyte != lastFirstByte : # Need to update the current subhead, and start a new one.
2003-08-28 18:04:23 +00:00
if lastFirstByte > - 1 :
2006-10-21 13:54:30 +00:00
# fix GI's and iDelta of current subheader.
self . setIDDelta ( subHeader )
# If it was sunheader 0 for one-byte charCodes, then we need to set the subHeaderKeys value to zero
# for the indices matching the char codes.
if lastFirstByte == 0 :
for index in range ( subHeader . entryCount ) :
charCode = subHeader . firstCode + index
subHeaderKeys [ charCode ] = 0
2003-08-28 18:04:23 +00:00
assert ( subHeader . entryCount == len ( subHeader . glyphIndexArray ) ) , " Error - subhead entry count does not match len of glyphID subrange. "
# init new subheader
subHeader = SubHeader ( )
subHeader . firstCode = secondByte
2006-10-21 13:54:30 +00:00
subHeader . entryCount = 1
subHeader . glyphIndexArray . append ( gid )
subHeaderList . append ( subHeader )
subHeaderKeys [ firstbyte ] = len ( subHeaderList ) - 1
2003-08-28 18:04:23 +00:00
lastFirstByte = firstbyte
else :
2006-10-21 13:54:30 +00:00
# need to fill in with notdefs all the code points between the last charCode and the current charCode.
2003-08-28 18:04:23 +00:00
codeDiff = secondByte - ( subHeader . firstCode + subHeader . entryCount )
for i in range ( codeDiff ) :
2006-10-21 13:54:30 +00:00
subHeader . glyphIndexArray . append ( notdefGI )
subHeader . glyphIndexArray . append ( gid )
2003-08-28 18:04:23 +00:00
subHeader . entryCount = subHeader . entryCount + codeDiff + 1
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
# fix GI's and iDelta of last subheader that we we added to the subheader array.
self . setIDDelta ( subHeader )
# Now we add a final subheader for the subHeaderKeys which maps to empty two byte charcode ranges.
2003-08-28 18:04:23 +00:00
subHeader = SubHeader ( )
subHeader . firstCode = 0
subHeader . entryCount = 0
subHeader . idDelta = 0
subHeader . idRangeOffset = 2
subHeaderList . append ( subHeader )
emptySubheadIndex = len ( subHeaderList ) - 1
for index in range ( 256 ) :
2006-10-21 13:54:30 +00:00
if subHeaderKeys [ index ] == kEmptyTwoCharCodeRange :
2003-08-28 18:04:23 +00:00
subHeaderKeys [ index ] = emptySubheadIndex
# Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the
2006-10-21 13:54:30 +00:00
# idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray,
2015-04-26 02:01:01 -04:00
# since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with
2003-08-28 18:04:23 +00:00
# charcode 0 and GID 0.
2015-04-26 02:01:01 -04:00
2015-04-26 00:15:26 -04:00
idRangeOffset = ( len ( subHeaderList ) - 1 ) * 8 + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
2006-10-21 13:54:30 +00:00
subheadRangeLen = len ( subHeaderList ) - 1 # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2.
2015-04-26 02:01:01 -04:00
for index in range ( subheadRangeLen ) :
2006-10-21 13:54:30 +00:00
subHeader = subHeaderList [ index ]
subHeader . idRangeOffset = 0
2019-01-10 19:35:34 +01:00
for j in range ( index ) :
2006-10-21 13:54:30 +00:00
prevSubhead = subHeaderList [ j ]
if prevSubhead . glyphIndexArray == subHeader . glyphIndexArray : # use the glyphIndexArray subarray
subHeader . idRangeOffset = prevSubhead . idRangeOffset - ( index - j ) * 8
subHeader . glyphIndexArray = [ ]
break
2015-04-26 02:01:01 -04:00
if subHeader . idRangeOffset == 0 : # didn't find one.
2006-10-21 13:54:30 +00:00
subHeader . idRangeOffset = idRangeOffset
idRangeOffset = ( idRangeOffset - 8 ) + subHeader . entryCount * 2 # one less subheader, one more subArray.
else :
idRangeOffset = idRangeOffset - 8 # one less subheader
2003-08-28 18:04:23 +00:00
# Now we can write out the data!
length = 6 + 512 + 8 * len ( subHeaderList ) # header, 256 subHeaderKeys, and subheader array.
for subhead in subHeaderList [ : - 1 ] :
2006-10-21 13:54:30 +00:00
length = length + len ( subhead . glyphIndexArray ) * 2 # We can't use subhead.entryCount, as some of the subhead may share subArrays.
dataList = [ struct . pack ( " >HHH " , 2 , length , self . language ) ]
2003-08-28 18:04:23 +00:00
for index in subHeaderKeys :
2006-10-21 13:54:30 +00:00
dataList . append ( struct . pack ( " >H " , index * 8 ) )
2003-08-28 18:04:23 +00:00
for subhead in subHeaderList :
2006-10-21 13:54:30 +00:00
dataList . append ( struct . pack ( subHeaderFormat , subhead . firstCode , subhead . entryCount , subhead . idDelta , subhead . idRangeOffset ) )
2003-08-28 18:04:23 +00:00
for subhead in subHeaderList [ : - 1 ] :
for gi in subhead . glyphIndexArray :
2006-10-21 13:54:30 +00:00
dataList . append ( struct . pack ( " >H " , gi ) )
2013-11-27 21:17:35 -05:00
data = bytesjoin ( dataList )
2003-08-28 18:04:23 +00:00
assert ( len ( data ) == length ) , " Error: cmap format 2 is not same length as calculated! actual: " + str ( len ( data ) ) + " calc : " + str ( length )
return data
2006-10-21 13:54:30 +00:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2004-09-25 07:30:47 +00:00
self . language = safeEval ( attrs [ " language " ] )
2006-10-21 13:54:30 +00:00
if not hasattr ( self , " cmap " ) :
self . cmap = { }
cmap = self . cmap
2003-08-28 18:04:23 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
2003-08-28 18:04:23 +00:00
continue
name , attrs , content = element
2013-11-27 02:40:30 -05:00
if name != " map " :
2003-08-28 18:04:23 +00:00
continue
2006-10-21 13:54:30 +00:00
cmap [ safeEval ( attrs [ " code " ] ) ] = attrs [ " name " ]
1999-12-16 21:34:53 +00:00
cmap_format_4_format = " >7H "
2002-07-23 07:51:23 +00:00
#uint16 endCode[segCount] # Ending character code for each segment, last = 0xFFFF.
#uint16 reservedPad # This value should be zero
#uint16 startCode[segCount] # Starting character code for each segment
#uint16 idDelta[segCount] # Delta for all character codes in segment
#uint16 idRangeOffset[segCount] # Offset in bytes to glyph indexArray, or 0
#uint16 glyphIndexArray[variable] # Glyph index array
1999-12-16 21:34:53 +00:00
2002-07-20 21:57:26 +00:00
def splitRange ( startCode , endCode , cmap ) :
2002-07-23 07:51:23 +00:00
# Try to split a range of character codes into subranges with consecutive
# glyph IDs in such a way that the cmap4 subtable can be stored "most"
# efficiently. I can't prove I've got the optimal solution, but it seems
# to do well with the fonts I tested: none became bigger, many became smaller.
2002-07-20 21:57:26 +00:00
if startCode == endCode :
return [ ] , [ endCode ]
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
lastID = cmap [ startCode ]
lastCode = startCode
inOrder = None
orderedBegin = None
2002-07-23 07:51:23 +00:00
subRanges = [ ]
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
# Gather subranges in which the glyph IDs are consecutive.
2002-07-20 21:57:26 +00:00
for code in range ( startCode + 1 , endCode + 1 ) :
glyphID = cmap [ code ]
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
if glyphID - 1 == lastID :
if inOrder is None or not inOrder :
inOrder = 1
orderedBegin = lastCode
else :
if inOrder :
inOrder = 0
2002-07-23 07:51:23 +00:00
subRanges . append ( ( orderedBegin , lastCode ) )
2002-07-20 21:57:26 +00:00
orderedBegin = None
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
lastID = glyphID
lastCode = code
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
if inOrder :
2002-07-23 07:51:23 +00:00
subRanges . append ( ( orderedBegin , lastCode ) )
2002-07-20 21:57:26 +00:00
assert lastCode == endCode
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
# Now filter out those new subranges that would only make the data bigger.
# A new segment cost 8 bytes, not using a new segment costs 2 bytes per
# character.
newRanges = [ ]
for b , e in subRanges :
2002-07-20 21:57:26 +00:00
if b == startCode and e == endCode :
break # the whole range, we're fine
if b == startCode or e == endCode :
threshold = 4 # split costs one more segment
else :
threshold = 8 # split costs two more segments
if ( e - b + 1 ) > threshold :
2002-07-23 07:51:23 +00:00
newRanges . append ( ( b , e ) )
subRanges = newRanges
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
if not subRanges :
2002-07-20 21:57:26 +00:00
return [ ] , [ endCode ]
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
if subRanges [ 0 ] [ 0 ] != startCode :
subRanges . insert ( 0 , ( startCode , subRanges [ 0 ] [ 0 ] - 1 ) )
if subRanges [ - 1 ] [ 1 ] != endCode :
subRanges . append ( ( subRanges [ - 1 ] [ 1 ] + 1 , endCode ) )
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
# Fill the "holes" in the segments list -- those are the segments in which
# the glyph IDs are _not_ consecutive.
2002-07-20 21:57:26 +00:00
i = 1
2002-07-23 07:51:23 +00:00
while i < len ( subRanges ) :
if subRanges [ i - 1 ] [ 1 ] + 1 != subRanges [ i ] [ 0 ] :
subRanges . insert ( i , ( subRanges [ i - 1 ] [ 1 ] + 1 , subRanges [ i ] [ 0 ] - 1 ) )
2002-07-20 21:57:26 +00:00
i = i + 1
i = i + 1
2015-04-26 02:01:01 -04:00
2002-07-23 07:51:23 +00:00
# Transform the ranges into startCode/endCode lists.
2002-07-20 21:57:26 +00:00
start = [ ]
end = [ ]
2002-07-23 07:51:23 +00:00
for b , e in subRanges :
2002-07-20 21:57:26 +00:00
start . append ( b )
end . append ( e )
start . pop ( 0 )
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
assert len ( start ) + 1 == len ( end )
return start , end
1999-12-16 21:34:53 +00:00
class cmap_format_4 ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def decompile ( self , data , ttFont ) :
2006-10-21 13:54:30 +00:00
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2006-10-21 13:54:30 +00:00
data = self . data # decompileHeader assigns the data after the header to self.data
( segCountX2 , searchRange , entrySelector , rangeShift ) = \
struct . unpack ( " >4H " , data [ : 8 ] )
data = data [ 8 : ]
2013-11-27 17:46:17 -05:00
segCount = segCountX2 / / 2
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
allCodes = array . array ( " H " )
2006-10-21 13:54:30 +00:00
allCodes . fromstring ( data )
self . data = data = None
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : allCodes . byteswap ( )
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
# divide the data
2002-07-20 21:57:26 +00:00
endCode = allCodes [ : segCount ]
allCodes = allCodes [ segCount + 1 : ] # the +1 is skipping the reservedPad field
startCode = allCodes [ : segCount ]
allCodes = allCodes [ segCount : ]
idDelta = allCodes [ : segCount ]
allCodes = allCodes [ segCount : ]
idRangeOffset = allCodes [ : segCount ]
glyphIndexArray = allCodes [ segCount : ]
2006-10-21 13:54:30 +00:00
lenGIArray = len ( glyphIndexArray )
1999-12-16 21:34:53 +00:00
# build 2-byte character mapping
2006-10-21 13:54:30 +00:00
charCodes = [ ]
gids = [ ]
1999-12-16 21:34:53 +00:00
for i in range ( len ( startCode ) - 1 ) : # don't do 0xffff!
2014-05-14 00:08:15 -04:00
start = startCode [ i ]
delta = idDelta [ i ]
rangeOffset = idRangeOffset [ i ]
# *someone* needs to get killed.
partial = rangeOffset / / 2 - start + i - len ( idRangeOffset )
2013-11-27 03:34:48 -05:00
rangeCharCodes = list ( range ( startCode [ i ] , endCode [ i ] + 1 ) )
2014-05-14 00:00:47 -04:00
charCodes . extend ( rangeCharCodes )
2014-05-14 00:12:03 -04:00
if rangeOffset == 0 :
gids . extend ( [ ( charCode + delta ) & 0xFFFF for charCode in rangeCharCodes ] )
else :
for charCode in rangeCharCodes :
2014-05-14 00:08:15 -04:00
index = charCode + partial
2019-01-10 19:35:34 +01:00
assert ( index < lenGIArray ) , " In format 4 cmap, range ( %d ), the calculated index ( %d ) into the glyph index array is not less than the length of the array ( %d ) ! " % ( i , index , lenGIArray )
2013-11-27 02:40:30 -05:00
if glyphIndexArray [ index ] != 0 : # if not missing glyph
2014-05-14 00:08:15 -04:00
glyphID = glyphIndexArray [ index ] + delta
1999-12-16 21:34:53 +00:00
else :
glyphID = 0 # missing glyph
2014-05-14 00:12:03 -04:00
gids . append ( glyphID & 0xFFFF )
2006-10-21 13:54:30 +00:00
2016-12-27 18:29:10 -05:00
self . cmap = _make_map ( self . ttFont , charCodes , gids )
2006-10-21 13:54:30 +00:00
1999-12-16 21:34:53 +00:00
def compile ( self , ttFont ) :
2006-10-21 13:54:30 +00:00
if self . data :
return struct . pack ( " >HHH " , self . format , self . length , self . language ) + self . data
2015-04-26 02:01:01 -04:00
2013-11-27 06:26:55 -05:00
charCodes = list ( self . cmap . keys ( ) )
2019-03-03 11:59:59 +01:00
if not charCodes :
2006-10-21 13:54:30 +00:00
startCode = [ 0xffff ]
endCode = [ 0xffff ]
else :
2008-02-29 14:43:49 +00:00
charCodes . sort ( )
2019-03-03 10:30:05 +01:00
names = [ self . cmap [ code ] for code in charCodes ]
2006-10-21 13:54:30 +00:00
nameMap = ttFont . getReverseGlyphMap ( )
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
2013-12-04 21:28:50 -05:00
nameMap = ttFont . getReverseGlyphMap ( rebuild = True )
2006-10-21 13:54:30 +00:00
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
# allow virtual GIDs in format 4 tables
gids = [ ]
for name in names :
try :
gid = nameMap [ name ]
except KeyError :
try :
if ( name [ : 3 ] == ' gid ' ) :
2019-03-03 10:30:05 +01:00
gid = int ( name [ 3 : ] )
2006-10-21 13:54:30 +00:00
else :
gid = ttFont . getGlyphID ( name )
except :
raise KeyError ( name )
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
gids . append ( gid )
cmap = { } # code:glyphID mapping
2019-03-03 10:30:05 +01:00
for code , gid in zip ( charCodes , gids ) :
cmap [ code ] = gid
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
# Build startCode and endCode lists.
# Split the char codes in ranges of consecutive char codes, then split
# each range in more ranges of consecutive/not consecutive glyph IDs.
# See splitRange().
lastCode = charCodes [ 0 ]
endCode = [ ]
startCode = [ lastCode ]
for charCode in charCodes [ 1 : ] : # skip the first code, it's the first start code
if charCode == lastCode + 1 :
lastCode = charCode
continue
start , end = splitRange ( startCode [ - 1 ] , lastCode , cmap )
startCode . extend ( start )
endCode . extend ( end )
startCode . append ( charCode )
2002-07-20 21:57:26 +00:00
lastCode = charCode
2015-01-22 00:10:01 -08:00
start , end = splitRange ( startCode [ - 1 ] , lastCode , cmap )
startCode . extend ( start )
endCode . extend ( end )
2006-10-21 13:54:30 +00:00
startCode . append ( 0xffff )
endCode . append ( 0xffff )
2015-04-26 02:01:01 -04:00
2002-07-20 21:57:26 +00:00
# build up rest of cruft
1999-12-16 21:34:53 +00:00
idDelta = [ ]
idRangeOffset = [ ]
glyphIndexArray = [ ]
for i in range ( len ( endCode ) - 1 ) : # skip the closing codes (0xffff)
indices = [ ]
2002-07-20 21:57:26 +00:00
for charCode in range ( startCode [ i ] , endCode [ i ] + 1 ) :
indices . append ( cmap [ charCode ] )
2019-01-10 19:35:34 +01:00
if ( indices == list ( range ( indices [ 0 ] , indices [ 0 ] + len ( indices ) ) ) ) :
2014-05-21 19:13:34 -04:00
idDelta . append ( ( indices [ 0 ] - startCode [ i ] ) % 0x10000 )
1999-12-16 21:34:53 +00:00
idRangeOffset . append ( 0 )
else :
# someone *definitely* needs to get killed.
idDelta . append ( 0 )
idRangeOffset . append ( 2 * ( len ( endCode ) + len ( glyphIndexArray ) - i ) )
2002-07-20 21:57:26 +00:00
glyphIndexArray . extend ( indices )
1999-12-16 21:34:53 +00:00
idDelta . append ( 1 ) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
idRangeOffset . append ( 0 )
2015-04-26 02:01:01 -04:00
2014-05-27 16:01:47 -04:00
# Insane.
1999-12-16 21:34:53 +00:00
segCount = len ( endCode )
segCountX2 = segCount * 2
2014-05-27 16:01:47 -04:00
searchRange , entrySelector , rangeShift = getSearchRange ( segCount , 2 )
2015-04-26 02:01:01 -04:00
2013-08-16 12:56:08 -04:00
charCodeArray = array . array ( " H " , endCode + [ 0 ] + startCode )
2014-05-21 19:13:34 -04:00
idDeltaArray = array . array ( " H " , idDelta )
2013-08-16 12:56:08 -04:00
restArray = array . array ( " H " , idRangeOffset + glyphIndexArray )
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : charCodeArray . byteswap ( )
if sys . byteorder != " big " : idDeltaArray . byteswap ( )
if sys . byteorder != " big " : restArray . byteswap ( )
2014-05-21 17:57:14 -04:00
data = charCodeArray . tostring ( ) + idDeltaArray . tostring ( ) + restArray . tostring ( )
2006-10-21 13:54:30 +00:00
1999-12-16 21:34:53 +00:00
length = struct . calcsize ( cmap_format_4_format ) + len ( data )
2015-04-26 02:01:01 -04:00
header = struct . pack ( cmap_format_4_format , self . format , length , self . language ,
1999-12-16 21:34:53 +00:00
segCountX2 , searchRange , entrySelector , rangeShift )
2006-10-21 13:54:30 +00:00
return header + data
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2004-09-25 07:30:47 +00:00
self . language = safeEval ( attrs [ " language " ] )
2006-10-21 13:54:30 +00:00
if not hasattr ( self , " cmap " ) :
self . cmap = { }
cmap = self . cmap
1999-12-16 21:34:53 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
1999-12-16 21:34:53 +00:00
continue
2006-10-21 13:54:30 +00:00
nameMap , attrsMap , dummyContent = element
2013-11-27 02:40:30 -05:00
if nameMap != " map " :
2006-10-21 13:54:30 +00:00
assert 0 , " Unrecognized keyword in cmap subtable "
cmap [ safeEval ( attrsMap [ " code " ] ) ] = attrsMap [ " name " ]
1999-12-16 21:34:53 +00:00
class cmap_format_6 ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def decompile ( self , data , ttFont ) :
2006-10-21 13:54:30 +00:00
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2006-10-21 13:54:30 +00:00
data = self . data # decompileHeader assigns the data after the header to self.data
firstCode , entryCount = struct . unpack ( " >HH " , data [ : 4 ] )
1999-12-16 21:34:53 +00:00
firstCode = int ( firstCode )
2006-10-21 13:54:30 +00:00
data = data [ 4 : ]
2000-08-23 12:33:14 +00:00
#assert len(data) == 2 * entryCount # XXX not true in Apple's Helvetica!!!
2016-12-27 18:29:10 -05:00
gids = array . array ( " H " )
gids . fromstring ( data [ : 2 * int ( entryCount ) ] )
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : gids . byteswap ( )
2006-10-21 13:54:30 +00:00
self . data = data = None
2016-12-27 18:29:10 -05:00
charCodes = list ( range ( firstCode , firstCode + len ( gids ) ) )
self . cmap = _make_map ( self . ttFont , charCodes , gids )
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
def compile ( self , ttFont ) :
2006-10-21 13:54:30 +00:00
if self . data :
return struct . pack ( " >HHH " , self . format , self . length , self . language ) + self . data
cmap = self . cmap
2015-06-27 17:01:51 +01:00
codes = sorted ( cmap . keys ( ) )
2006-10-21 13:54:30 +00:00
if codes : # yes, there are empty cmap tables.
2013-11-27 03:34:48 -05:00
codes = list ( range ( codes [ 0 ] , codes [ - 1 ] + 1 ) )
2006-10-21 13:54:30 +00:00
firstCode = codes [ 0 ]
2017-12-15 13:56:48 +00:00
valueList = [
ttFont . getGlyphID ( cmap [ code ] ) if code in cmap else 0
for code in codes
]
2016-12-27 18:29:10 -05:00
gids = array . array ( " H " , valueList )
2018-09-05 21:11:36 -07:00
if sys . byteorder != " big " : gids . byteswap ( )
2016-12-27 18:29:10 -05:00
data = gids . tostring ( )
2006-10-21 13:54:30 +00:00
else :
2013-11-27 22:00:49 -05:00
data = b " "
2006-10-21 13:54:30 +00:00
firstCode = 0
2015-04-26 02:01:01 -04:00
header = struct . pack ( " >HHHHH " ,
2006-10-21 13:54:30 +00:00
6 , len ( data ) + 10 , self . language , firstCode , len ( codes ) )
1999-12-16 21:34:53 +00:00
return header + data
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2004-09-25 07:30:47 +00:00
self . language = safeEval ( attrs [ " language " ] )
2006-10-21 13:54:30 +00:00
if not hasattr ( self , " cmap " ) :
self . cmap = { }
cmap = self . cmap
1999-12-16 21:34:53 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
1999-12-16 21:34:53 +00:00
continue
name , attrs , content = element
2013-11-27 02:40:30 -05:00
if name != " map " :
1999-12-16 21:34:53 +00:00
continue
2006-10-21 13:54:30 +00:00
cmap [ safeEval ( attrs [ " code " ] ) ] = attrs [ " name " ]
1999-12-16 21:34:53 +00:00
2013-10-09 15:55:07 -07:00
class cmap_format_12_or_13 ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
def __init__ ( self , format ) :
self . format = format
self . reserved = 0
self . data = None
self . ttFont = None
def decompileHeader ( self , data , ttFont ) :
2003-02-08 10:45:23 +00:00
format , reserved , length , language , nGroups = struct . unpack ( " >HHLLL " , data [ : 16 ] )
2015-04-26 02:40:53 -04:00
assert len ( data ) == ( 16 + nGroups * 12 ) == ( length ) , " corrupt cmap table format %d (data length: %d , header length: %d ) " % ( self . format , len ( data ) , length )
2003-02-08 10:45:23 +00:00
self . format = format
self . reserved = reserved
self . length = length
self . language = language
self . nGroups = nGroups
2006-10-21 13:54:30 +00:00
self . data = data [ 16 : ]
self . ttFont = ttFont
def decompile ( self , data , ttFont ) :
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2006-10-21 13:54:30 +00:00
data = self . data # decompileHeader assigns the data after the header to self.data
charCodes = [ ]
gids = [ ]
pos = 0
for i in range ( self . nGroups ) :
startCharCode , endCharCode , glyphID = struct . unpack ( " >LLL " , data [ pos : pos + 12 ] )
pos + = 12
lenGroup = 1 + endCharCode - startCharCode
2014-05-14 00:22:14 -04:00
charCodes . extend ( list ( range ( startCharCode , endCharCode + 1 ) ) )
gids . extend ( self . _computeGIDs ( glyphID , lenGroup ) )
2006-10-21 13:54:30 +00:00
self . data = data = None
2016-12-27 18:29:10 -05:00
self . cmap = _make_map ( self . ttFont , charCodes , gids )
2015-04-26 02:01:01 -04:00
2003-02-08 10:45:23 +00:00
def compile ( self , ttFont ) :
2006-10-21 13:54:30 +00:00
if self . data :
2013-10-09 15:55:07 -07:00
return struct . pack ( " >HHLLL " , self . format , self . reserved , self . length , self . language , self . nGroups ) + self . data
2013-11-27 06:26:55 -05:00
charCodes = list ( self . cmap . keys ( ) )
names = list ( self . cmap . values ( ) )
2006-10-21 13:54:30 +00:00
nameMap = ttFont . getReverseGlyphMap ( )
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
2013-12-04 21:28:50 -05:00
nameMap = ttFont . getReverseGlyphMap ( rebuild = True )
2006-10-21 13:54:30 +00:00
try :
2019-03-03 10:30:05 +01:00
gids = [ nameMap [ name ] for name in names ]
2006-10-21 13:54:30 +00:00
except KeyError :
# allow virtual GIDs in format 12 tables
gids = [ ]
for name in names :
try :
gid = nameMap [ name ]
except KeyError :
try :
if ( name [ : 3 ] == ' gid ' ) :
2019-03-03 10:30:05 +01:00
gid = int ( name [ 3 : ] )
2006-10-21 13:54:30 +00:00
else :
gid = ttFont . getGlyphID ( name )
except :
raise KeyError ( name )
gids . append ( gid )
2015-04-26 02:01:01 -04:00
2003-02-08 10:45:23 +00:00
cmap = { } # code:glyphID mapping
2019-03-03 10:30:05 +01:00
for code , gid in zip ( charCodes , gids ) :
cmap [ code ] = gid
2003-02-08 10:45:23 +00:00
charCodes . sort ( )
2006-10-21 13:54:30 +00:00
index = 0
2003-02-08 10:45:23 +00:00
startCharCode = charCodes [ 0 ]
startGlyphID = cmap [ startCharCode ]
2013-10-09 15:55:07 -07:00
lastGlyphID = startGlyphID - self . _format_step
2006-10-21 13:54:30 +00:00
lastCharCode = startCharCode - 1
2004-09-25 07:30:47 +00:00
nGroups = 0
2019-01-10 19:35:34 +01:00
dataList = [ ]
2006-10-21 13:54:30 +00:00
maxIndex = len ( charCodes )
for index in range ( maxIndex ) :
charCode = charCodes [ index ]
2003-02-08 10:45:23 +00:00
glyphID = cmap [ charCode ]
2013-10-09 15:55:07 -07:00
if not self . _IsInSameRun ( glyphID , lastGlyphID , charCode , lastCharCode ) :
2006-10-21 13:54:30 +00:00
dataList . append ( struct . pack ( " >LLL " , startCharCode , lastCharCode , startGlyphID ) )
2003-02-08 10:45:23 +00:00
startCharCode = charCode
2006-10-21 13:54:30 +00:00
startGlyphID = glyphID
2003-02-08 10:45:23 +00:00
nGroups = nGroups + 1
2006-10-21 13:54:30 +00:00
lastGlyphID = glyphID
lastCharCode = charCode
dataList . append ( struct . pack ( " >LLL " , startCharCode , lastCharCode , startGlyphID ) )
2004-09-25 07:30:47 +00:00
nGroups = nGroups + 1
2013-11-27 21:17:35 -05:00
data = bytesjoin ( dataList )
2006-10-21 13:54:30 +00:00
lengthSubtable = len ( data ) + 16
2015-04-26 00:15:26 -04:00
assert len ( data ) == ( nGroups * 12 ) == ( lengthSubtable - 16 )
return struct . pack ( " >HHLLL " , self . format , self . reserved , lengthSubtable , self . language , nGroups ) + data
2015-04-26 02:01:01 -04:00
2003-02-08 10:45:23 +00:00
def toXML ( self , writer , ttFont ) :
writer . begintag ( self . __class__ . __name__ , [
( " platformID " , self . platformID ) ,
( " platEncID " , self . platEncID ) ,
( " format " , self . format ) ,
( " reserved " , self . reserved ) ,
( " length " , self . length ) ,
( " language " , self . language ) ,
( " nGroups " , self . nGroups ) ,
] )
writer . newline ( )
2013-11-27 04:15:34 -05:00
codes = sorted ( self . cmap . items ( ) )
2004-09-25 09:06:58 +00:00
self . _writeCodes ( codes , writer )
2003-02-08 10:45:23 +00:00
writer . endtag ( self . __class__ . __name__ )
writer . newline ( )
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2006-10-21 13:54:30 +00:00
self . format = safeEval ( attrs [ " format " ] )
self . reserved = safeEval ( attrs [ " reserved " ] )
self . length = safeEval ( attrs [ " length " ] )
2003-02-08 10:45:23 +00:00
self . language = safeEval ( attrs [ " language " ] )
2006-10-21 13:54:30 +00:00
self . nGroups = safeEval ( attrs [ " nGroups " ] )
if not hasattr ( self , " cmap " ) :
self . cmap = { }
cmap = self . cmap
2003-02-08 10:45:23 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
2003-02-08 10:45:23 +00:00
continue
name , attrs , content = element
2013-11-27 02:40:30 -05:00
if name != " map " :
2003-02-08 10:45:23 +00:00
continue
2006-10-21 13:54:30 +00:00
cmap [ safeEval ( attrs [ " code " ] ) ] = attrs [ " name " ]
2003-02-08 10:45:23 +00:00
2013-10-09 15:55:07 -07:00
class cmap_format_12 ( cmap_format_12_or_13 ) :
2015-04-26 02:40:53 -04:00
_format_step = 1
def __init__ ( self , format = 12 ) :
2013-10-09 15:55:07 -07:00
cmap_format_12_or_13 . __init__ ( self , format )
def _computeGIDs ( self , startingGlyph , numberOfGlyphs ) :
2013-11-27 03:34:48 -05:00
return list ( range ( startingGlyph , startingGlyph + numberOfGlyphs ) )
2013-10-09 15:55:07 -07:00
def _IsInSameRun ( self , glyphID , lastGlyphID , charCode , lastCharCode ) :
return ( glyphID == 1 + lastGlyphID ) and ( charCode == 1 + lastCharCode )
class cmap_format_13 ( cmap_format_12_or_13 ) :
2015-04-26 02:40:53 -04:00
_format_step = 0
def __init__ ( self , format = 13 ) :
2013-10-09 15:55:07 -07:00
cmap_format_12_or_13 . __init__ ( self , format )
def _computeGIDs ( self , startingGlyph , numberOfGlyphs ) :
return [ startingGlyph ] * numberOfGlyphs
def _IsInSameRun ( self , glyphID , lastGlyphID , charCode , lastCharCode ) :
return ( glyphID == lastGlyphID ) and ( charCode == 1 + lastCharCode )
2019-01-10 19:35:34 +01:00
def cvtToUVS ( threeByteString ) :
2013-11-28 06:35:12 -05:00
data = b " \0 " + threeByteString
2008-05-16 15:07:09 +00:00
val , = struct . unpack ( " >L " , data )
return val
2019-01-10 19:35:34 +01:00
def cvtFromUVS ( val ) :
2013-11-28 06:35:12 -05:00
assert 0 < = val < 0x1000000
fourByteString = struct . pack ( " >L " , val )
return fourByteString [ 1 : ]
2008-05-16 15:07:09 +00:00
2013-11-27 18:58:45 -05:00
2008-05-16 15:07:09 +00:00
class cmap_format_14 ( CmapSubtable ) :
def decompileHeader ( self , data , ttFont ) :
format , length , numVarSelectorRecords = struct . unpack ( " >HLL " , data [ : 10 ] )
self . data = data [ 10 : ]
self . length = length
self . numVarSelectorRecords = numVarSelectorRecords
self . ttFont = ttFont
self . language = 0xFF # has no language.
def decompile ( self , data , ttFont ) :
2014-07-14 20:01:04 -04:00
if data is not None and ttFont is not None :
2008-05-16 15:07:09 +00:00
self . decompileHeader ( data , ttFont )
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
2008-05-16 15:07:09 +00:00
data = self . data
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
self . cmap = { } # so that clients that expect this to exist in a cmap table won't fail.
uvsDict = { }
recOffset = 0
for n in range ( self . numVarSelectorRecords ) :
2019-01-10 19:35:34 +01:00
uvs , defOVSOffset , nonDefUVSOffset = struct . unpack ( " >3sLL " , data [ recOffset : recOffset + 11 ] )
2008-05-16 15:07:09 +00:00
recOffset + = 11
varUVS = cvtToUVS ( uvs )
if defOVSOffset :
2015-04-26 00:15:26 -04:00
startOffset = defOVSOffset - 10
2008-05-16 15:07:09 +00:00
numValues , = struct . unpack ( " >L " , data [ startOffset : startOffset + 4 ] )
startOffset + = 4
for r in range ( numValues ) :
uv , addtlCnt = struct . unpack ( " >3sB " , data [ startOffset : startOffset + 4 ] )
startOffset + = 4
firstBaseUV = cvtToUVS ( uv )
cnt = addtlCnt + 1
2013-11-27 03:34:48 -05:00
baseUVList = list ( range ( firstBaseUV , firstBaseUV + cnt ) )
2008-05-16 15:07:09 +00:00
glyphList = [ None ] * cnt
localUVList = zip ( baseUVList , glyphList )
try :
uvsDict [ varUVS ] . extend ( localUVList )
except KeyError :
2013-11-27 04:13:15 -05:00
uvsDict [ varUVS ] = list ( localUVList )
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
if nonDefUVSOffset :
2015-04-26 00:15:26 -04:00
startOffset = nonDefUVSOffset - 10
2008-05-16 15:07:09 +00:00
numRecs , = struct . unpack ( " >L " , data [ startOffset : startOffset + 4 ] )
startOffset + = 4
localUVList = [ ]
for r in range ( numRecs ) :
uv , gid = struct . unpack ( " >3sH " , data [ startOffset : startOffset + 5 ] )
startOffset + = 5
uv = cvtToUVS ( uv )
glyphName = self . ttFont . getGlyphName ( gid )
2019-01-10 19:19:35 +01:00
localUVList . append ( ( uv , glyphName ) )
2008-05-16 15:07:09 +00:00
try :
uvsDict [ varUVS ] . extend ( localUVList )
except KeyError :
uvsDict [ varUVS ] = localUVList
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
self . uvsDict = uvsDict
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
def toXML ( self , writer , ttFont ) :
writer . begintag ( self . __class__ . __name__ , [
( " platformID " , self . platformID ) ,
( " platEncID " , self . platEncID ) ,
] )
writer . newline ( )
uvsDict = self . uvsDict
2013-11-27 04:15:34 -05:00
uvsList = sorted ( uvsDict . keys ( ) )
2008-05-16 15:07:09 +00:00
for uvs in uvsList :
uvList = uvsDict [ uvs ]
2013-12-04 16:31:44 -05:00
uvList . sort ( key = lambda item : ( item [ 1 ] is not None , item [ 0 ] , item [ 1 ] ) )
2008-05-16 15:07:09 +00:00
for uv , gname in uvList :
2019-01-10 19:19:35 +01:00
attrs = [ ( " uv " , hex ( uv ) ) , ( " uvs " , hex ( uvs ) ) ]
if gname is not None :
attrs . append ( ( " name " , gname ) )
writer . simpletag ( " map " , attrs )
2008-05-16 15:07:09 +00:00
writer . newline ( )
writer . endtag ( self . __class__ . __name__ )
writer . newline ( )
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2019-01-10 19:35:34 +01:00
self . language = 0xFF # provide a value so that CmapSubtable.__lt__() won't fail
2008-05-16 15:07:09 +00:00
if not hasattr ( self , " cmap " ) :
self . cmap = { } # so that clients that expect this to exist in a cmap table won't fail.
if not hasattr ( self , " uvsDict " ) :
2015-04-26 00:54:30 -04:00
self . uvsDict = { }
2015-04-26 02:01:01 -04:00
uvsDict = self . uvsDict
2008-05-16 15:07:09 +00:00
2019-01-10 19:19:35 +01:00
# For backwards compatibility reasons we accept "None" as an indicator
# for "default mapping", unless the font actually has a glyph named
# "None".
_hasGlyphNamedNone = None
2008-05-16 15:07:09 +00:00
for element in content :
2013-11-27 05:17:37 -05:00
if not isinstance ( element , tuple ) :
2008-05-16 15:07:09 +00:00
continue
name , attrs , content = element
2013-11-27 02:40:30 -05:00
if name != " map " :
2008-05-16 15:07:09 +00:00
continue
uvs = safeEval ( attrs [ " uvs " ] )
uv = safeEval ( attrs [ " uv " ] )
2019-01-10 19:19:35 +01:00
gname = attrs . get ( " name " )
2008-05-16 15:07:09 +00:00
if gname == " None " :
2019-01-10 19:19:35 +01:00
if _hasGlyphNamedNone is None :
_hasGlyphNamedNone = " None " in ttFont . getGlyphOrder ( )
if not _hasGlyphNamedNone :
gname = None
2008-05-16 15:07:09 +00:00
try :
2019-01-10 19:19:35 +01:00
uvsDict [ uvs ] . append ( ( uv , gname ) )
2008-05-16 15:07:09 +00:00
except KeyError :
2019-01-10 19:19:35 +01:00
uvsDict [ uvs ] = [ ( uv , gname ) ]
2008-05-16 15:07:09 +00:00
def compile ( self , ttFont ) :
if self . data :
2015-04-26 00:15:26 -04:00
return struct . pack ( " >HLL " , self . format , self . length , self . numVarSelectorRecords ) + self . data
2008-05-16 15:07:09 +00:00
uvsDict = self . uvsDict
2013-11-27 04:15:34 -05:00
uvsList = sorted ( uvsDict . keys ( ) )
2008-05-16 15:07:09 +00:00
self . numVarSelectorRecords = len ( uvsList )
offset = 10 + self . numVarSelectorRecords * 11 # current value is end of VarSelectorRecords block.
data = [ ]
varSelectorRecords = [ ]
for uvs in uvsList :
entryList = uvsDict [ uvs ]
2013-12-04 16:31:44 -05:00
defList = [ entry for entry in entryList if entry [ 1 ] is None ]
2008-05-16 15:07:09 +00:00
if defList :
2013-11-27 04:38:16 -05:00
defList = [ entry [ 0 ] for entry in defList ]
2008-05-16 15:07:09 +00:00
defOVSOffset = offset
defList . sort ( )
lastUV = defList [ 0 ]
cnt = - 1
defRecs = [ ]
for defEntry in defList :
cnt + = 1
if ( lastUV + cnt ) != defEntry :
rec = struct . pack ( " >3sB " , cvtFromUVS ( lastUV ) , cnt - 1 )
lastUV = defEntry
defRecs . append ( rec )
cnt = 0
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
rec = struct . pack ( " >3sB " , cvtFromUVS ( lastUV ) , cnt )
defRecs . append ( rec )
numDefRecs = len ( defRecs )
data . append ( struct . pack ( " >L " , numDefRecs ) )
data . extend ( defRecs )
offset + = 4 + numDefRecs * 4
else :
defOVSOffset = 0
2013-12-04 16:31:44 -05:00
ndefList = [ entry for entry in entryList if entry [ 1 ] is not None ]
2008-05-16 15:07:09 +00:00
if ndefList :
nonDefUVSOffset = offset
ndefList . sort ( )
numNonDefRecs = len ( ndefList )
data . append ( struct . pack ( " >L " , numNonDefRecs ) )
offset + = 4 + numNonDefRecs * 5
for uv , gname in ndefList :
gid = ttFont . getGlyphID ( gname )
ndrec = struct . pack ( " >3sH " , cvtFromUVS ( uv ) , gid )
data . append ( ndrec )
else :
nonDefUVSOffset = 0
2015-04-26 02:01:01 -04:00
2008-05-16 15:07:09 +00:00
vrec = struct . pack ( " >3sLL " , cvtFromUVS ( uvs ) , defOVSOffset , nonDefUVSOffset )
varSelectorRecords . append ( vrec )
2015-04-26 02:01:01 -04:00
2013-11-27 21:17:35 -05:00
data = bytesjoin ( varSelectorRecords ) + bytesjoin ( data )
2008-05-16 15:07:09 +00:00
self . length = 10 + len ( data )
2015-04-26 00:15:26 -04:00
headerdata = struct . pack ( " >HLL " , self . format , self . length , self . numVarSelectorRecords )
2015-04-26 02:01:01 -04:00
2016-01-31 21:00:11 +00:00
return headerdata + data
2015-04-26 02:01:01 -04:00
1999-12-16 21:34:53 +00:00
class cmap_format_unknown ( CmapSubtable ) :
2015-04-26 02:01:01 -04:00
2004-09-25 09:06:58 +00:00
def toXML ( self , writer , ttFont ) :
2006-10-21 13:54:30 +00:00
cmapName = self . __class__ . __name__ [ : 12 ] + str ( self . format )
writer . begintag ( cmapName , [
2004-09-25 09:06:58 +00:00
( " platformID " , self . platformID ) ,
( " platEncID " , self . platEncID ) ,
] )
writer . newline ( )
2006-10-21 13:54:30 +00:00
writer . dumphex ( self . data )
writer . endtag ( cmapName )
2004-09-25 09:06:58 +00:00
writer . newline ( )
2015-04-26 02:01:01 -04:00
2013-11-27 03:19:32 -05:00
def fromXML ( self , name , attrs , content , ttFont ) :
2006-10-21 13:54:30 +00:00
self . data = readHex ( content )
self . cmap = { }
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
def decompileHeader ( self , data , ttFont ) :
2004-09-26 18:32:50 +00:00
self . language = 0 # dummy value
1999-12-16 21:34:53 +00:00
self . data = data
2015-04-26 02:01:01 -04:00
2006-10-21 13:54:30 +00:00
def decompile ( self , data , ttFont ) :
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
2019-01-10 19:35:34 +01:00
# If not, someone is calling the subtable decompile() directly, and must provide both args.
2013-12-04 16:31:44 -05:00
if data is not None and ttFont is not None :
2015-04-26 02:40:53 -04:00
self . decompileHeader ( data , ttFont )
2006-10-21 13:54:30 +00:00
else :
2013-12-04 16:31:44 -05:00
assert ( data is None and ttFont is None ) , " Need both data and ttFont arguments "
1999-12-16 21:34:53 +00:00
2006-10-21 13:54:30 +00:00
def compile ( self , ttFont ) :
if self . data :
return self . data
else :
return None
1999-12-16 21:34:53 +00:00
cmap_classes = {
0 : cmap_format_0 ,
2 : cmap_format_2 ,
4 : cmap_format_4 ,
6 : cmap_format_6 ,
2003-02-08 10:45:23 +00:00
12 : cmap_format_12 ,
2013-10-09 15:55:07 -07:00
13 : cmap_format_13 ,
2008-05-16 15:07:09 +00:00
14 : cmap_format_14 ,
2015-04-26 02:40:53 -04:00
}