aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/sbixGlyph.py
blob: fd687a18808b6b2655951f9a6934916d7bafbc71 (plain)
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
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 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.graphicType == "dupe":
                # 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.graphicType == "dupe":
                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.graphicType == "dupe":
            # graphicType == "dupe" 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":
            # glyph is a "dupe", 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)