1*e1fe3e4aSElliott Hughesfrom fontTools.misc import sstruct 2*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import readHex, safeEval 3*e1fe3e4aSElliott Hughesimport struct 4*e1fe3e4aSElliott Hughes 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott HughessbixGlyphHeaderFormat = """ 7*e1fe3e4aSElliott Hughes > 8*e1fe3e4aSElliott Hughes originOffsetX: h # The x-value of the point in the glyph relative to its 9*e1fe3e4aSElliott Hughes # lower-left corner which corresponds to the origin of 10*e1fe3e4aSElliott Hughes # the glyph on the screen, that is the point on the 11*e1fe3e4aSElliott Hughes # baseline at the left edge of the glyph. 12*e1fe3e4aSElliott Hughes originOffsetY: h # The y-value of the point in the glyph relative to its 13*e1fe3e4aSElliott Hughes # lower-left corner which corresponds to the origin of 14*e1fe3e4aSElliott Hughes # the glyph on the screen, that is the point on the 15*e1fe3e4aSElliott Hughes # baseline at the left edge of the glyph. 16*e1fe3e4aSElliott Hughes graphicType: 4s # e.g. "png " 17*e1fe3e4aSElliott Hughes""" 18*e1fe3e4aSElliott Hughes 19*e1fe3e4aSElliott HughessbixGlyphHeaderFormatSize = sstruct.calcsize(sbixGlyphHeaderFormat) 20*e1fe3e4aSElliott Hughes 21*e1fe3e4aSElliott Hughes 22*e1fe3e4aSElliott Hughesclass Glyph(object): 23*e1fe3e4aSElliott Hughes def __init__( 24*e1fe3e4aSElliott Hughes self, 25*e1fe3e4aSElliott Hughes glyphName=None, 26*e1fe3e4aSElliott Hughes referenceGlyphName=None, 27*e1fe3e4aSElliott Hughes originOffsetX=0, 28*e1fe3e4aSElliott Hughes originOffsetY=0, 29*e1fe3e4aSElliott Hughes graphicType=None, 30*e1fe3e4aSElliott Hughes imageData=None, 31*e1fe3e4aSElliott Hughes rawdata=None, 32*e1fe3e4aSElliott Hughes gid=0, 33*e1fe3e4aSElliott Hughes ): 34*e1fe3e4aSElliott Hughes self.gid = gid 35*e1fe3e4aSElliott Hughes self.glyphName = glyphName 36*e1fe3e4aSElliott Hughes self.referenceGlyphName = referenceGlyphName 37*e1fe3e4aSElliott Hughes self.originOffsetX = originOffsetX 38*e1fe3e4aSElliott Hughes self.originOffsetY = originOffsetY 39*e1fe3e4aSElliott Hughes self.rawdata = rawdata 40*e1fe3e4aSElliott Hughes self.graphicType = graphicType 41*e1fe3e4aSElliott Hughes self.imageData = imageData 42*e1fe3e4aSElliott Hughes 43*e1fe3e4aSElliott Hughes # fix self.graphicType if it is null terminated or too short 44*e1fe3e4aSElliott Hughes if self.graphicType is not None: 45*e1fe3e4aSElliott Hughes if self.graphicType[-1] == "\0": 46*e1fe3e4aSElliott Hughes self.graphicType = self.graphicType[:-1] 47*e1fe3e4aSElliott Hughes if len(self.graphicType) > 4: 48*e1fe3e4aSElliott Hughes from fontTools import ttLib 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes raise ttLib.TTLibError( 51*e1fe3e4aSElliott Hughes "Glyph.graphicType must not be longer than 4 characters." 52*e1fe3e4aSElliott Hughes ) 53*e1fe3e4aSElliott Hughes elif len(self.graphicType) < 4: 54*e1fe3e4aSElliott Hughes # pad with spaces 55*e1fe3e4aSElliott Hughes self.graphicType += " "[: (4 - len(self.graphicType))] 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes def is_reference_type(self): 58*e1fe3e4aSElliott Hughes """Returns True if this glyph is a reference to another glyph's image data.""" 59*e1fe3e4aSElliott Hughes return self.graphicType == "dupe" or self.graphicType == "flip" 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughes def decompile(self, ttFont): 62*e1fe3e4aSElliott Hughes self.glyphName = ttFont.getGlyphName(self.gid) 63*e1fe3e4aSElliott Hughes if self.rawdata is None: 64*e1fe3e4aSElliott Hughes from fontTools import ttLib 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("No table data to decompile") 67*e1fe3e4aSElliott Hughes if len(self.rawdata) > 0: 68*e1fe3e4aSElliott Hughes if len(self.rawdata) < sbixGlyphHeaderFormatSize: 69*e1fe3e4aSElliott Hughes from fontTools import ttLib 70*e1fe3e4aSElliott Hughes 71*e1fe3e4aSElliott Hughes # print "Glyph %i header too short: Expected %x, got %x." % (self.gid, sbixGlyphHeaderFormatSize, len(self.rawdata)) 72*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("Glyph header too short.") 73*e1fe3e4aSElliott Hughes 74*e1fe3e4aSElliott Hughes sstruct.unpack( 75*e1fe3e4aSElliott Hughes sbixGlyphHeaderFormat, self.rawdata[:sbixGlyphHeaderFormatSize], self 76*e1fe3e4aSElliott Hughes ) 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott Hughes if self.is_reference_type(): 79*e1fe3e4aSElliott Hughes # this glyph is a reference to another glyph's image data 80*e1fe3e4aSElliott Hughes (gid,) = struct.unpack(">H", self.rawdata[sbixGlyphHeaderFormatSize:]) 81*e1fe3e4aSElliott Hughes self.referenceGlyphName = ttFont.getGlyphName(gid) 82*e1fe3e4aSElliott Hughes else: 83*e1fe3e4aSElliott Hughes self.imageData = self.rawdata[sbixGlyphHeaderFormatSize:] 84*e1fe3e4aSElliott Hughes self.referenceGlyphName = None 85*e1fe3e4aSElliott Hughes # clean up 86*e1fe3e4aSElliott Hughes del self.rawdata 87*e1fe3e4aSElliott Hughes del self.gid 88*e1fe3e4aSElliott Hughes 89*e1fe3e4aSElliott Hughes def compile(self, ttFont): 90*e1fe3e4aSElliott Hughes if self.glyphName is None: 91*e1fe3e4aSElliott Hughes from fontTools import ttLib 92*e1fe3e4aSElliott Hughes 93*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("Can't compile Glyph without glyph name") 94*e1fe3e4aSElliott Hughes # TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index? 95*e1fe3e4aSElliott Hughes # (needed if you just want to compile the sbix table on its own) 96*e1fe3e4aSElliott Hughes self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName)) 97*e1fe3e4aSElliott Hughes if self.graphicType is None: 98*e1fe3e4aSElliott Hughes rawdata = b"" 99*e1fe3e4aSElliott Hughes else: 100*e1fe3e4aSElliott Hughes rawdata = sstruct.pack(sbixGlyphHeaderFormat, self) 101*e1fe3e4aSElliott Hughes if self.is_reference_type(): 102*e1fe3e4aSElliott Hughes rawdata += struct.pack(">H", ttFont.getGlyphID(self.referenceGlyphName)) 103*e1fe3e4aSElliott Hughes else: 104*e1fe3e4aSElliott Hughes assert self.imageData is not None 105*e1fe3e4aSElliott Hughes rawdata += self.imageData 106*e1fe3e4aSElliott Hughes self.rawdata = rawdata 107*e1fe3e4aSElliott Hughes 108*e1fe3e4aSElliott Hughes def toXML(self, xmlWriter, ttFont): 109*e1fe3e4aSElliott Hughes if self.graphicType is None: 110*e1fe3e4aSElliott Hughes # TODO: ignore empty glyphs? 111*e1fe3e4aSElliott Hughes # a glyph data entry is required for each glyph, 112*e1fe3e4aSElliott Hughes # but empty ones can be calculated at compile time 113*e1fe3e4aSElliott Hughes xmlWriter.simpletag("glyph", name=self.glyphName) 114*e1fe3e4aSElliott Hughes xmlWriter.newline() 115*e1fe3e4aSElliott Hughes return 116*e1fe3e4aSElliott Hughes xmlWriter.begintag( 117*e1fe3e4aSElliott Hughes "glyph", 118*e1fe3e4aSElliott Hughes graphicType=self.graphicType, 119*e1fe3e4aSElliott Hughes name=self.glyphName, 120*e1fe3e4aSElliott Hughes originOffsetX=self.originOffsetX, 121*e1fe3e4aSElliott Hughes originOffsetY=self.originOffsetY, 122*e1fe3e4aSElliott Hughes ) 123*e1fe3e4aSElliott Hughes xmlWriter.newline() 124*e1fe3e4aSElliott Hughes if self.is_reference_type(): 125*e1fe3e4aSElliott Hughes # this glyph is a reference to another glyph id. 126*e1fe3e4aSElliott Hughes xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName) 127*e1fe3e4aSElliott Hughes else: 128*e1fe3e4aSElliott Hughes xmlWriter.begintag("hexdata") 129*e1fe3e4aSElliott Hughes xmlWriter.newline() 130*e1fe3e4aSElliott Hughes xmlWriter.dumphex(self.imageData) 131*e1fe3e4aSElliott Hughes xmlWriter.endtag("hexdata") 132*e1fe3e4aSElliott Hughes xmlWriter.newline() 133*e1fe3e4aSElliott Hughes xmlWriter.endtag("glyph") 134*e1fe3e4aSElliott Hughes xmlWriter.newline() 135*e1fe3e4aSElliott Hughes 136*e1fe3e4aSElliott Hughes def fromXML(self, name, attrs, content, ttFont): 137*e1fe3e4aSElliott Hughes if name == "ref": 138*e1fe3e4aSElliott Hughes # this glyph i.e. a reference to another glyph's image data. 139*e1fe3e4aSElliott Hughes # in this case imageData contains the glyph id of the reference glyph 140*e1fe3e4aSElliott Hughes # get glyph id from glyphname 141*e1fe3e4aSElliott Hughes glyphname = safeEval("'''" + attrs["glyphname"] + "'''") 142*e1fe3e4aSElliott Hughes self.imageData = struct.pack(">H", ttFont.getGlyphID(glyphname)) 143*e1fe3e4aSElliott Hughes self.referenceGlyphName = glyphname 144*e1fe3e4aSElliott Hughes elif name == "hexdata": 145*e1fe3e4aSElliott Hughes self.imageData = readHex(content) 146*e1fe3e4aSElliott Hughes else: 147*e1fe3e4aSElliott Hughes from fontTools import ttLib 148*e1fe3e4aSElliott Hughes 149*e1fe3e4aSElliott Hughes raise ttLib.TTLibError("can't handle '%s' element" % name) 150