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 32 def __init__(self, tag=None): 33 DefaultTable.DefaultTable.__init__(self, tag) 34 self.version = 1 35 self.flags = 1 36 self.numStrikes = 0 37 self.strikes = {} 38 self.strikeOffsets = [] 39 40 def decompile(self, data, ttFont): 41 # read table header 42 sstruct.unpack(sbixHeaderFormat, data[ : sbixHeaderFormatSize], self) 43 # collect offsets to individual strikes in self.strikeOffsets 44 for i in range(self.numStrikes): 45 current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize 46 offset_entry = sbixStrikeOffset() 47 sstruct.unpack(sbixStrikeOffsetFormat, \ 48 data[current_offset:current_offset+sbixStrikeOffsetFormatSize], \ 49 offset_entry) 50 self.strikeOffsets.append(offset_entry.strikeOffset) 51 52 # decompile Strikes 53 for i in range(self.numStrikes-1, -1, -1): 54 current_strike = Strike(rawdata=data[self.strikeOffsets[i]:]) 55 data = data[:self.strikeOffsets[i]] 56 current_strike.decompile(ttFont) 57 #print " Strike length: %xh" % len(bitmapSetData) 58 #print "Number of Glyph entries:", len(current_strike.glyphs) 59 if current_strike.ppem in self.strikes: 60 from fontTools import ttLib 61 raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike") 62 self.strikes[current_strike.ppem] = current_strike 63 64 # after the glyph data records have been extracted, we don't need the offsets anymore 65 del self.strikeOffsets 66 del self.numStrikes 67 68 def compile(self, ttFont): 69 sbixData = b"" 70 self.numStrikes = len(self.strikes) 71 sbixHeader = sstruct.pack(sbixHeaderFormat, self) 72 73 # calculate offset to start of first strike 74 setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes 75 76 for si in sorted(self.strikes.keys()): 77 current_strike = self.strikes[si] 78 current_strike.compile(ttFont) 79 # append offset to this strike to table header 80 current_strike.strikeOffset = setOffset 81 sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike) 82 setOffset += len(current_strike.data) 83 sbixData += current_strike.data 84 85 return sbixHeader + sbixData 86 87 def toXML(self, xmlWriter, ttFont): 88 xmlWriter.simpletag("version", value=self.version) 89 xmlWriter.newline() 90 xmlWriter.simpletag("flags", value=num2binary(self.flags, 16)) 91 xmlWriter.newline() 92 for i in sorted(self.strikes.keys()): 93 self.strikes[i].toXML(xmlWriter, ttFont) 94 95 def fromXML(self, name, attrs, content, ttFont): 96 if name =="version": 97 setattr(self, name, safeEval(attrs["value"])) 98 elif name == "flags": 99 setattr(self, name, binary2num(attrs["value"])) 100 elif name == "strike": 101 current_strike = Strike() 102 for element in content: 103 if isinstance(element, tuple): 104 name, attrs, content = element 105 current_strike.fromXML(name, attrs, content, ttFont) 106 self.strikes[current_strike.ppem] = current_strike 107 else: 108 from fontTools import ttLib 109 raise ttLib.TTLibError("can't handle '%s' element" % name) 110 111 112# Helper classes 113 114class sbixStrikeOffset(object): 115 pass 116