xref: /aosp_15_r20/external/fonttools/Lib/fontTools/ttLib/tables/sbixStrike.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.misc import sstruct
2*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import safeEval
3*e1fe3e4aSElliott Hughesfrom .sbixGlyph import Glyph
4*e1fe3e4aSElliott Hughesimport struct
5*e1fe3e4aSElliott Hughes
6*e1fe3e4aSElliott HughessbixStrikeHeaderFormat = """
7*e1fe3e4aSElliott Hughes	>
8*e1fe3e4aSElliott Hughes	ppem:          H	# The PPEM for which this strike was designed (e.g., 9,
9*e1fe3e4aSElliott Hughes						# 12, 24)
10*e1fe3e4aSElliott Hughes	resolution:    H	# The screen resolution (in dpi) for which this strike
11*e1fe3e4aSElliott Hughes						# was designed (e.g., 72)
12*e1fe3e4aSElliott Hughes"""
13*e1fe3e4aSElliott Hughes
14*e1fe3e4aSElliott HughessbixGlyphDataOffsetFormat = """
15*e1fe3e4aSElliott Hughes	>
16*e1fe3e4aSElliott Hughes	glyphDataOffset:   L	# Offset from the beginning of the strike data record
17*e1fe3e4aSElliott Hughes							# to data for the individual glyph
18*e1fe3e4aSElliott Hughes"""
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott HughessbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
21*e1fe3e4aSElliott HughessbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)
22*e1fe3e4aSElliott Hughes
23*e1fe3e4aSElliott Hughes
24*e1fe3e4aSElliott Hughesclass Strike(object):
25*e1fe3e4aSElliott Hughes    def __init__(self, rawdata=None, ppem=0, resolution=72):
26*e1fe3e4aSElliott Hughes        self.data = rawdata
27*e1fe3e4aSElliott Hughes        self.ppem = ppem
28*e1fe3e4aSElliott Hughes        self.resolution = resolution
29*e1fe3e4aSElliott Hughes        self.glyphs = {}
30*e1fe3e4aSElliott Hughes
31*e1fe3e4aSElliott Hughes    def decompile(self, ttFont):
32*e1fe3e4aSElliott Hughes        if self.data is None:
33*e1fe3e4aSElliott Hughes            from fontTools import ttLib
34*e1fe3e4aSElliott Hughes
35*e1fe3e4aSElliott Hughes            raise ttLib.TTLibError
36*e1fe3e4aSElliott Hughes        if len(self.data) < sbixStrikeHeaderFormatSize:
37*e1fe3e4aSElliott Hughes            from fontTools import ttLib
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes            raise (
40*e1fe3e4aSElliott Hughes                ttLib.TTLibError,
41*e1fe3e4aSElliott Hughes                "Strike header too short: Expected %x, got %x.",
42*e1fe3e4aSElliott Hughes            ) % (sbixStrikeHeaderFormatSize, len(self.data))
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughes        # read Strike header from raw data
45*e1fe3e4aSElliott Hughes        sstruct.unpack(
46*e1fe3e4aSElliott Hughes            sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self
47*e1fe3e4aSElliott Hughes        )
48*e1fe3e4aSElliott Hughes
49*e1fe3e4aSElliott Hughes        # calculate number of glyphs
50*e1fe3e4aSElliott Hughes        (firstGlyphDataOffset,) = struct.unpack(
51*e1fe3e4aSElliott Hughes            ">L",
52*e1fe3e4aSElliott Hughes            self.data[
53*e1fe3e4aSElliott Hughes                sbixStrikeHeaderFormatSize : sbixStrikeHeaderFormatSize
54*e1fe3e4aSElliott Hughes                + sbixGlyphDataOffsetFormatSize
55*e1fe3e4aSElliott Hughes            ],
56*e1fe3e4aSElliott Hughes        )
57*e1fe3e4aSElliott Hughes        self.numGlyphs = (
58*e1fe3e4aSElliott Hughes            firstGlyphDataOffset - sbixStrikeHeaderFormatSize
59*e1fe3e4aSElliott Hughes        ) // sbixGlyphDataOffsetFormatSize - 1
60*e1fe3e4aSElliott Hughes        # ^ -1 because there's one more offset than glyphs
61*e1fe3e4aSElliott Hughes
62*e1fe3e4aSElliott Hughes        # build offset list for single glyph data offsets
63*e1fe3e4aSElliott Hughes        self.glyphDataOffsets = []
64*e1fe3e4aSElliott Hughes        for i in range(
65*e1fe3e4aSElliott Hughes            self.numGlyphs + 1
66*e1fe3e4aSElliott Hughes        ):  # + 1 because there's one more offset than glyphs
67*e1fe3e4aSElliott Hughes            start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
68*e1fe3e4aSElliott Hughes            (current_offset,) = struct.unpack(
69*e1fe3e4aSElliott Hughes                ">L", self.data[start : start + sbixGlyphDataOffsetFormatSize]
70*e1fe3e4aSElliott Hughes            )
71*e1fe3e4aSElliott Hughes            self.glyphDataOffsets.append(current_offset)
72*e1fe3e4aSElliott Hughes
73*e1fe3e4aSElliott Hughes        # iterate through offset list and slice raw data into glyph data records
74*e1fe3e4aSElliott Hughes        for i in range(self.numGlyphs):
75*e1fe3e4aSElliott Hughes            current_glyph = Glyph(
76*e1fe3e4aSElliott Hughes                rawdata=self.data[
77*e1fe3e4aSElliott Hughes                    self.glyphDataOffsets[i] : self.glyphDataOffsets[i + 1]
78*e1fe3e4aSElliott Hughes                ],
79*e1fe3e4aSElliott Hughes                gid=i,
80*e1fe3e4aSElliott Hughes            )
81*e1fe3e4aSElliott Hughes            current_glyph.decompile(ttFont)
82*e1fe3e4aSElliott Hughes            self.glyphs[current_glyph.glyphName] = current_glyph
83*e1fe3e4aSElliott Hughes        del self.glyphDataOffsets
84*e1fe3e4aSElliott Hughes        del self.numGlyphs
85*e1fe3e4aSElliott Hughes        del self.data
86*e1fe3e4aSElliott Hughes
87*e1fe3e4aSElliott Hughes    def compile(self, ttFont):
88*e1fe3e4aSElliott Hughes        self.glyphDataOffsets = b""
89*e1fe3e4aSElliott Hughes        self.bitmapData = b""
90*e1fe3e4aSElliott Hughes
91*e1fe3e4aSElliott Hughes        glyphOrder = ttFont.getGlyphOrder()
92*e1fe3e4aSElliott Hughes
93*e1fe3e4aSElliott Hughes        # first glyph starts right after the header
94*e1fe3e4aSElliott Hughes        currentGlyphDataOffset = (
95*e1fe3e4aSElliott Hughes            sbixStrikeHeaderFormatSize
96*e1fe3e4aSElliott Hughes            + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
97*e1fe3e4aSElliott Hughes        )
98*e1fe3e4aSElliott Hughes        for glyphName in glyphOrder:
99*e1fe3e4aSElliott Hughes            if glyphName in self.glyphs:
100*e1fe3e4aSElliott Hughes                # we have glyph data for this glyph
101*e1fe3e4aSElliott Hughes                current_glyph = self.glyphs[glyphName]
102*e1fe3e4aSElliott Hughes            else:
103*e1fe3e4aSElliott Hughes                # must add empty glyph data record for this glyph
104*e1fe3e4aSElliott Hughes                current_glyph = Glyph(glyphName=glyphName)
105*e1fe3e4aSElliott Hughes            current_glyph.compile(ttFont)
106*e1fe3e4aSElliott Hughes            current_glyph.glyphDataOffset = currentGlyphDataOffset
107*e1fe3e4aSElliott Hughes            self.bitmapData += current_glyph.rawdata
108*e1fe3e4aSElliott Hughes            currentGlyphDataOffset += len(current_glyph.rawdata)
109*e1fe3e4aSElliott Hughes            self.glyphDataOffsets += sstruct.pack(
110*e1fe3e4aSElliott Hughes                sbixGlyphDataOffsetFormat, current_glyph
111*e1fe3e4aSElliott Hughes            )
112*e1fe3e4aSElliott Hughes
113*e1fe3e4aSElliott Hughes        # add last "offset", really the end address of the last glyph data record
114*e1fe3e4aSElliott Hughes        dummy = Glyph()
115*e1fe3e4aSElliott Hughes        dummy.glyphDataOffset = currentGlyphDataOffset
116*e1fe3e4aSElliott Hughes        self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)
117*e1fe3e4aSElliott Hughes
118*e1fe3e4aSElliott Hughes        # pack header
119*e1fe3e4aSElliott Hughes        self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
120*e1fe3e4aSElliott Hughes        # add offsets and image data after header
121*e1fe3e4aSElliott Hughes        self.data += self.glyphDataOffsets + self.bitmapData
122*e1fe3e4aSElliott Hughes
123*e1fe3e4aSElliott Hughes    def toXML(self, xmlWriter, ttFont):
124*e1fe3e4aSElliott Hughes        xmlWriter.begintag("strike")
125*e1fe3e4aSElliott Hughes        xmlWriter.newline()
126*e1fe3e4aSElliott Hughes        xmlWriter.simpletag("ppem", value=self.ppem)
127*e1fe3e4aSElliott Hughes        xmlWriter.newline()
128*e1fe3e4aSElliott Hughes        xmlWriter.simpletag("resolution", value=self.resolution)
129*e1fe3e4aSElliott Hughes        xmlWriter.newline()
130*e1fe3e4aSElliott Hughes        glyphOrder = ttFont.getGlyphOrder()
131*e1fe3e4aSElliott Hughes        for i in range(len(glyphOrder)):
132*e1fe3e4aSElliott Hughes            if glyphOrder[i] in self.glyphs:
133*e1fe3e4aSElliott Hughes                self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont)
134*e1fe3e4aSElliott Hughes                # TODO: what if there are more glyph data records than (glyf table) glyphs?
135*e1fe3e4aSElliott Hughes        xmlWriter.endtag("strike")
136*e1fe3e4aSElliott Hughes        xmlWriter.newline()
137*e1fe3e4aSElliott Hughes
138*e1fe3e4aSElliott Hughes    def fromXML(self, name, attrs, content, ttFont):
139*e1fe3e4aSElliott Hughes        if name in ["ppem", "resolution"]:
140*e1fe3e4aSElliott Hughes            setattr(self, name, safeEval(attrs["value"]))
141*e1fe3e4aSElliott Hughes        elif name == "glyph":
142*e1fe3e4aSElliott Hughes            if "graphicType" in attrs:
143*e1fe3e4aSElliott Hughes                myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
144*e1fe3e4aSElliott Hughes            else:
145*e1fe3e4aSElliott Hughes                myFormat = None
146*e1fe3e4aSElliott Hughes            if "glyphname" in attrs:
147*e1fe3e4aSElliott Hughes                myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
148*e1fe3e4aSElliott Hughes            elif "name" in attrs:
149*e1fe3e4aSElliott Hughes                myGlyphName = safeEval("'''" + attrs["name"] + "'''")
150*e1fe3e4aSElliott Hughes            else:
151*e1fe3e4aSElliott Hughes                from fontTools import ttLib
152*e1fe3e4aSElliott Hughes
153*e1fe3e4aSElliott Hughes                raise ttLib.TTLibError("Glyph must have a glyph name.")
154*e1fe3e4aSElliott Hughes            if "originOffsetX" in attrs:
155*e1fe3e4aSElliott Hughes                myOffsetX = safeEval(attrs["originOffsetX"])
156*e1fe3e4aSElliott Hughes            else:
157*e1fe3e4aSElliott Hughes                myOffsetX = 0
158*e1fe3e4aSElliott Hughes            if "originOffsetY" in attrs:
159*e1fe3e4aSElliott Hughes                myOffsetY = safeEval(attrs["originOffsetY"])
160*e1fe3e4aSElliott Hughes            else:
161*e1fe3e4aSElliott Hughes                myOffsetY = 0
162*e1fe3e4aSElliott Hughes            current_glyph = Glyph(
163*e1fe3e4aSElliott Hughes                glyphName=myGlyphName,
164*e1fe3e4aSElliott Hughes                graphicType=myFormat,
165*e1fe3e4aSElliott Hughes                originOffsetX=myOffsetX,
166*e1fe3e4aSElliott Hughes                originOffsetY=myOffsetY,
167*e1fe3e4aSElliott Hughes            )
168*e1fe3e4aSElliott Hughes            for element in content:
169*e1fe3e4aSElliott Hughes                if isinstance(element, tuple):
170*e1fe3e4aSElliott Hughes                    name, attrs, content = element
171*e1fe3e4aSElliott Hughes                    current_glyph.fromXML(name, attrs, content, ttFont)
172*e1fe3e4aSElliott Hughes                    current_glyph.compile(ttFont)
173*e1fe3e4aSElliott Hughes            self.glyphs[current_glyph.glyphName] = current_glyph
174*e1fe3e4aSElliott Hughes        else:
175*e1fe3e4aSElliott Hughes            from fontTools import ttLib
176*e1fe3e4aSElliott Hughes
177*e1fe3e4aSElliott Hughes            raise ttLib.TTLibError("can't handle '%s' element" % name)
178