xref: /aosp_15_r20/external/fonttools/Lib/fontTools/ttLib/tables/sbixGlyph.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
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