1from fontTools.misc import sstruct 2from fontTools.misc.textTools import safeEval, num2binary, binary2num 3from . import DefaultTable 4from .sbixStrike import Strike 5 6 7sbixHeaderFormat = """ 8 > 9 version: H # Version number (set to 1) 10 flags: H # The only two bits used in the flags field are bits 0 11 # and 1. For historical reasons, bit 0 must always be 1. 12 # Bit 1 is a sbixDrawOutlines flag and is interpreted as 13 # follows: 14 # 0: Draw only 'sbix' bitmaps 15 # 1: Draw both 'sbix' bitmaps and outlines, in that 16 # order 17 numStrikes: L # Number of bitmap strikes to follow 18""" 19sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat) 20 21 22sbixStrikeOffsetFormat = """ 23 > 24 strikeOffset: L # Offset from begining of table to data for the 25 # individual strike 26""" 27sbixStrikeOffsetFormatSize = sstruct.calcsize(sbixStrikeOffsetFormat) 28 29 30class table__s_b_i_x(DefaultTable.DefaultTable): 31 def __init__(self, tag=None): 32 DefaultTable.DefaultTable.__init__(self, tag) 33 self.version = 1 34 self.flags = 1 35 self.numStrikes = 0 36 self.strikes = {} 37 self.strikeOffsets = [] 38 39 def decompile(self, data, ttFont): 40 # read table header 41 sstruct.unpack(sbixHeaderFormat, data[:sbixHeaderFormatSize], self) 42 # collect offsets to individual strikes in self.strikeOffsets 43 for i in range(self.numStrikes): 44 current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize 45 offset_entry = sbixStrikeOffset() 46 sstruct.unpack( 47 sbixStrikeOffsetFormat, 48 data[current_offset : current_offset + sbixStrikeOffsetFormatSize], 49 offset_entry, 50 ) 51 self.strikeOffsets.append(offset_entry.strikeOffset) 52 53 # decompile Strikes 54 for i in range(self.numStrikes - 1, -1, -1): 55 current_strike = Strike(rawdata=data[self.strikeOffsets[i] :]) 56 data = data[: self.strikeOffsets[i]] 57 current_strike.decompile(ttFont) 58 # print " Strike length: %xh" % len(bitmapSetData) 59 # print "Number of Glyph entries:", len(current_strike.glyphs) 60 if current_strike.ppem in self.strikes: 61 from fontTools import ttLib 62 63 raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike") 64 self.strikes[current_strike.ppem] = current_strike 65 66 # after the glyph data records have been extracted, we don't need the offsets anymore 67 del self.strikeOffsets 68 del self.numStrikes 69 70 def compile(self, ttFont): 71 sbixData = b"" 72 self.numStrikes = len(self.strikes) 73 sbixHeader = sstruct.pack(sbixHeaderFormat, self) 74 75 # calculate offset to start of first strike 76 setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes 77 78 for si in sorted(self.strikes.keys()): 79 current_strike = self.strikes[si] 80 current_strike.compile(ttFont) 81 # append offset to this strike to table header 82 current_strike.strikeOffset = setOffset 83 sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike) 84 setOffset += len(current_strike.data) 85 sbixData += current_strike.data 86 87 return sbixHeader + sbixData 88 89 def toXML(self, xmlWriter, ttFont): 90 xmlWriter.simpletag("version", value=self.version) 91 xmlWriter.newline() 92 xmlWriter.simpletag("flags", value=num2binary(self.flags, 16)) 93 xmlWriter.newline() 94 for i in sorted(self.strikes.keys()): 95 self.strikes[i].toXML(xmlWriter, ttFont) 96 97 def fromXML(self, name, attrs, content, ttFont): 98 if name == "version": 99 setattr(self, name, safeEval(attrs["value"])) 100 elif name == "flags": 101 setattr(self, name, binary2num(attrs["value"])) 102 elif name == "strike": 103 current_strike = Strike() 104 for element in content: 105 if isinstance(element, tuple): 106 name, attrs, content = element 107 current_strike.fromXML(name, attrs, content, ttFont) 108 self.strikes[current_strike.ppem] = current_strike 109 else: 110 from fontTools import ttLib 111 112 raise ttLib.TTLibError("can't handle '%s' element" % name) 113 114 115# Helper classes 116 117 118class sbixStrikeOffset(object): 119 pass 120