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