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