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