1*e1fe3e4aSElliott Hughesfrom fontTools.misc import sstruct 2*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import safeEval 3*e1fe3e4aSElliott Hughesfrom .sbixGlyph import Glyph 4*e1fe3e4aSElliott Hughesimport struct 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott HughessbixStrikeHeaderFormat = """ 7*e1fe3e4aSElliott Hughes > 8*e1fe3e4aSElliott Hughes ppem: H # The PPEM for which this strike was designed (e.g., 9, 9*e1fe3e4aSElliott Hughes # 12, 24) 10*e1fe3e4aSElliott Hughes resolution: H # The screen resolution (in dpi) for which this strike 11*e1fe3e4aSElliott Hughes # was designed (e.g., 72) 12*e1fe3e4aSElliott Hughes""" 13*e1fe3e4aSElliott Hughes 14*e1fe3e4aSElliott HughessbixGlyphDataOffsetFormat = """ 15*e1fe3e4aSElliott Hughes > 16*e1fe3e4aSElliott Hughes glyphDataOffset: L # Offset from the beginning of the strike data record 17*e1fe3e4aSElliott Hughes # to data for the individual glyph 18*e1fe3e4aSElliott Hughes""" 19*e1fe3e4aSElliott Hughes 20*e1fe3e4aSElliott HughessbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat) 21*e1fe3e4aSElliott HughessbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat) 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott Hughesclass Strike(object): 25*e1fe3e4aSElliott Hughes def __init__(self, rawdata=None, ppem=0, resolution=72): 26*e1fe3e4aSElliott Hughes self.data = rawdata 27*e1fe3e4aSElliott Hughes self.ppem = ppem 28*e1fe3e4aSElliott Hughes self.resolution = resolution 29*e1fe3e4aSElliott Hughes self.glyphs = {} 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott Hughes def decompile(self, ttFont): 32*e1fe3e4aSElliott Hughes if self.data is None: 33*e1fe3e4aSElliott Hughes from fontTools import ttLib 34*e1fe3e4aSElliott Hughes 35*e1fe3e4aSElliott Hughes raise ttLib.TTLibError 36*e1fe3e4aSElliott Hughes if len(self.data) < sbixStrikeHeaderFormatSize: 37*e1fe3e4aSElliott Hughes from fontTools import ttLib 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes raise ( 40*e1fe3e4aSElliott Hughes ttLib.TTLibError, 41*e1fe3e4aSElliott Hughes "Strike header too short: Expected %x, got %x.", 42*e1fe3e4aSElliott Hughes ) % (sbixStrikeHeaderFormatSize, len(self.data)) 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes # read Strike header from raw data 45*e1fe3e4aSElliott Hughes sstruct.unpack( 46*e1fe3e4aSElliott Hughes sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self 47*e1fe3e4aSElliott Hughes ) 48*e1fe3e4aSElliott Hughes 49*e1fe3e4aSElliott Hughes # calculate number of glyphs 50*e1fe3e4aSElliott Hughes (firstGlyphDataOffset,) = struct.unpack( 51*e1fe3e4aSElliott Hughes ">L", 52*e1fe3e4aSElliott Hughes self.data[ 53*e1fe3e4aSElliott Hughes sbixStrikeHeaderFormatSize : sbixStrikeHeaderFormatSize 54*e1fe3e4aSElliott Hughes + sbixGlyphDataOffsetFormatSize 55*e1fe3e4aSElliott Hughes ], 56*e1fe3e4aSElliott Hughes ) 57*e1fe3e4aSElliott Hughes self.numGlyphs = ( 58*e1fe3e4aSElliott Hughes firstGlyphDataOffset - sbixStrikeHeaderFormatSize 59*e1fe3e4aSElliott Hughes ) // sbixGlyphDataOffsetFormatSize - 1 60*e1fe3e4aSElliott Hughes # ^ -1 because there's one more offset than glyphs 61*e1fe3e4aSElliott Hughes 62*e1fe3e4aSElliott Hughes # build offset list for single glyph data offsets 63*e1fe3e4aSElliott Hughes self.glyphDataOffsets = [] 64*e1fe3e4aSElliott Hughes for i in range( 65*e1fe3e4aSElliott Hughes self.numGlyphs + 1 66*e1fe3e4aSElliott Hughes ): # + 1 because there's one more offset than glyphs 67*e1fe3e4aSElliott Hughes start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize 68*e1fe3e4aSElliott Hughes (current_offset,) = struct.unpack( 69*e1fe3e4aSElliott Hughes ">L", self.data[start : start + sbixGlyphDataOffsetFormatSize] 70*e1fe3e4aSElliott Hughes ) 71*e1fe3e4aSElliott Hughes self.glyphDataOffsets.append(current_offset) 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott Hughes # iterate through offset list and slice raw data into glyph data records 74*e1fe3e4aSElliott Hughes for i in range(self.numGlyphs): 75*e1fe3e4aSElliott Hughes current_glyph = Glyph( 76*e1fe3e4aSElliott Hughes rawdata=self.data[ 77*e1fe3e4aSElliott Hughes self.glyphDataOffsets[i] : self.glyphDataOffsets[i + 1] 78*e1fe3e4aSElliott Hughes ], 79*e1fe3e4aSElliott Hughes gid=i, 80*e1fe3e4aSElliott Hughes ) 81*e1fe3e4aSElliott Hughes current_glyph.decompile(ttFont) 82*e1fe3e4aSElliott Hughes self.glyphs[current_glyph.glyphName] = current_glyph 83*e1fe3e4aSElliott Hughes del self.glyphDataOffsets 84*e1fe3e4aSElliott Hughes del self.numGlyphs 85*e1fe3e4aSElliott Hughes del self.data 86*e1fe3e4aSElliott Hughes 87*e1fe3e4aSElliott Hughes def compile(self, ttFont): 88*e1fe3e4aSElliott Hughes self.glyphDataOffsets = b"" 89*e1fe3e4aSElliott Hughes self.bitmapData = b"" 90*e1fe3e4aSElliott Hughes 91*e1fe3e4aSElliott Hughes glyphOrder = ttFont.getGlyphOrder() 92*e1fe3e4aSElliott Hughes 93*e1fe3e4aSElliott Hughes # first glyph starts right after the header 94*e1fe3e4aSElliott Hughes currentGlyphDataOffset = ( 95*e1fe3e4aSElliott Hughes sbixStrikeHeaderFormatSize 96*e1fe3e4aSElliott Hughes + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1) 97*e1fe3e4aSElliott Hughes ) 98*e1fe3e4aSElliott Hughes for glyphName in glyphOrder: 99*e1fe3e4aSElliott Hughes if glyphName in self.glyphs: 100*e1fe3e4aSElliott Hughes # we have glyph data for this glyph 101*e1fe3e4aSElliott Hughes current_glyph = self.glyphs[glyphName] 102*e1fe3e4aSElliott Hughes else: 103*e1fe3e4aSElliott Hughes # must add empty glyph data record for this glyph 104*e1fe3e4aSElliott Hughes current_glyph = Glyph(glyphName=glyphName) 105*e1fe3e4aSElliott Hughes current_glyph.compile(ttFont) 106*e1fe3e4aSElliott Hughes current_glyph.glyphDataOffset = currentGlyphDataOffset 107*e1fe3e4aSElliott Hughes self.bitmapData += current_glyph.rawdata 108*e1fe3e4aSElliott Hughes currentGlyphDataOffset += len(current_glyph.rawdata) 109*e1fe3e4aSElliott Hughes self.glyphDataOffsets += sstruct.pack( 110*e1fe3e4aSElliott Hughes sbixGlyphDataOffsetFormat, current_glyph 111*e1fe3e4aSElliott Hughes ) 112*e1fe3e4aSElliott Hughes 113*e1fe3e4aSElliott Hughes # add last "offset", really the end address of the last glyph data record 114*e1fe3e4aSElliott Hughes dummy = Glyph() 115*e1fe3e4aSElliott Hughes dummy.glyphDataOffset = currentGlyphDataOffset 116*e1fe3e4aSElliott Hughes self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy) 117*e1fe3e4aSElliott Hughes 118*e1fe3e4aSElliott Hughes # pack header 119*e1fe3e4aSElliott Hughes self.data = sstruct.pack(sbixStrikeHeaderFormat, self) 120*e1fe3e4aSElliott Hughes # add offsets and image data after header 121*e1fe3e4aSElliott Hughes self.data += self.glyphDataOffsets + self.bitmapData 122*e1fe3e4aSElliott Hughes 123*e1fe3e4aSElliott Hughes def toXML(self, xmlWriter, ttFont): 124*e1fe3e4aSElliott Hughes xmlWriter.begintag("strike") 125*e1fe3e4aSElliott Hughes xmlWriter.newline() 126*e1fe3e4aSElliott Hughes xmlWriter.simpletag("ppem", value=self.ppem) 127*e1fe3e4aSElliott Hughes xmlWriter.newline() 128*e1fe3e4aSElliott Hughes xmlWriter.simpletag("resolution", value=self.resolution) 129*e1fe3e4aSElliott Hughes xmlWriter.newline() 130*e1fe3e4aSElliott Hughes glyphOrder = ttFont.getGlyphOrder() 131*e1fe3e4aSElliott Hughes for i in range(len(glyphOrder)): 132*e1fe3e4aSElliott Hughes if glyphOrder[i] in self.glyphs: 133*e1fe3e4aSElliott Hughes self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont) 134*e1fe3e4aSElliott Hughes # TODO: what if there are more glyph data records than (glyf table) glyphs? 135*e1fe3e4aSElliott Hughes xmlWriter.endtag("strike") 136*e1fe3e4aSElliott Hughes xmlWriter.newline() 137*e1fe3e4aSElliott Hughes 138*e1fe3e4aSElliott Hughes def fromXML(self, name, attrs, content, ttFont): 139*e1fe3e4aSElliott Hughes if name in ["ppem", "resolution"]: 140*e1fe3e4aSElliott Hughes setattr(self, name, safeEval(attrs["value"])) 141*e1fe3e4aSElliott Hughes elif name == "glyph": 142*e1fe3e4aSElliott Hughes if "graphicType" in attrs: 143*e1fe3e4aSElliott Hughes myFormat = safeEval("'''" + attrs["graphicType"] + "'''") 144*e1fe3e4aSElliott Hughes else: 145*e1fe3e4aSElliott Hughes myFormat = None 146*e1fe3e4aSElliott Hughes if "glyphname" in attrs: 147*e1fe3e4aSElliott Hughes myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''") 148*e1fe3e4aSElliott Hughes elif "name" in attrs: 149*e1fe3e4aSElliott Hughes myGlyphName = safeEval("'''" + attrs["name"] + "'''") 150*e1fe3e4aSElliott Hughes else: 151*e1fe3e4aSElliott Hughes from fontTools import ttLib 152*e1fe3e4aSElliott Hughes 153*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("Glyph must have a glyph name.") 154*e1fe3e4aSElliott Hughes if "originOffsetX" in attrs: 155*e1fe3e4aSElliott Hughes myOffsetX = safeEval(attrs["originOffsetX"]) 156*e1fe3e4aSElliott Hughes else: 157*e1fe3e4aSElliott Hughes myOffsetX = 0 158*e1fe3e4aSElliott Hughes if "originOffsetY" in attrs: 159*e1fe3e4aSElliott Hughes myOffsetY = safeEval(attrs["originOffsetY"]) 160*e1fe3e4aSElliott Hughes else: 161*e1fe3e4aSElliott Hughes myOffsetY = 0 162*e1fe3e4aSElliott Hughes current_glyph = Glyph( 163*e1fe3e4aSElliott Hughes glyphName=myGlyphName, 164*e1fe3e4aSElliott Hughes graphicType=myFormat, 165*e1fe3e4aSElliott Hughes originOffsetX=myOffsetX, 166*e1fe3e4aSElliott Hughes originOffsetY=myOffsetY, 167*e1fe3e4aSElliott Hughes ) 168*e1fe3e4aSElliott Hughes for element in content: 169*e1fe3e4aSElliott Hughes if isinstance(element, tuple): 170*e1fe3e4aSElliott Hughes name, attrs, content = element 171*e1fe3e4aSElliott Hughes current_glyph.fromXML(name, attrs, content, ttFont) 172*e1fe3e4aSElliott Hughes current_glyph.compile(ttFont) 173*e1fe3e4aSElliott Hughes self.glyphs[current_glyph.glyphName] = current_glyph 174*e1fe3e4aSElliott Hughes else: 175*e1fe3e4aSElliott Hughes from fontTools import ttLib 176*e1fe3e4aSElliott Hughes 177*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("can't handle '%s' element" % name) 178