Coverage for /usr/lib/python3/dist-packages/fontTools/ttLib/tables/sbixStrike.py: 15%

96 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from fontTools.misc import sstruct 

2from fontTools.misc.textTools import safeEval 

3from .sbixGlyph import Glyph 

4import struct 

5 

6sbixStrikeHeaderFormat = """ 

7 > 

8 ppem: H # The PPEM for which this strike was designed (e.g., 9, 

9 # 12, 24) 

10 resolution: H # The screen resolution (in dpi) for which this strike 

11 # was designed (e.g., 72) 

12""" 

13 

14sbixGlyphDataOffsetFormat = """ 

15 > 

16 glyphDataOffset: L # Offset from the beginning of the strike data record 

17 # to data for the individual glyph 

18""" 

19 

20sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat) 

21sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat) 

22 

23 

24class Strike(object): 

25 def __init__(self, rawdata=None, ppem=0, resolution=72): 

26 self.data = rawdata 

27 self.ppem = ppem 

28 self.resolution = resolution 

29 self.glyphs = {} 

30 

31 def decompile(self, ttFont): 

32 if self.data is None: 

33 from fontTools import ttLib 

34 

35 raise ttLib.TTLibError 

36 if len(self.data) < sbixStrikeHeaderFormatSize: 

37 from fontTools import ttLib 

38 

39 raise ( 

40 ttLib.TTLibError, 

41 "Strike header too short: Expected %x, got %x.", 

42 ) % (sbixStrikeHeaderFormatSize, len(self.data)) 

43 

44 # read Strike header from raw data 

45 sstruct.unpack( 

46 sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self 

47 ) 

48 

49 # calculate number of glyphs 

50 (firstGlyphDataOffset,) = struct.unpack( 

51 ">L", 

52 self.data[ 

53 sbixStrikeHeaderFormatSize : sbixStrikeHeaderFormatSize 

54 + sbixGlyphDataOffsetFormatSize 

55 ], 

56 ) 

57 self.numGlyphs = ( 

58 firstGlyphDataOffset - sbixStrikeHeaderFormatSize 

59 ) // sbixGlyphDataOffsetFormatSize - 1 

60 # ^ -1 because there's one more offset than glyphs 

61 

62 # build offset list for single glyph data offsets 

63 self.glyphDataOffsets = [] 

64 for i in range( 

65 self.numGlyphs + 1 

66 ): # + 1 because there's one more offset than glyphs 

67 start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize 

68 (current_offset,) = struct.unpack( 

69 ">L", self.data[start : start + sbixGlyphDataOffsetFormatSize] 

70 ) 

71 self.glyphDataOffsets.append(current_offset) 

72 

73 # iterate through offset list and slice raw data into glyph data records 

74 for i in range(self.numGlyphs): 

75 current_glyph = Glyph( 

76 rawdata=self.data[ 

77 self.glyphDataOffsets[i] : self.glyphDataOffsets[i + 1] 

78 ], 

79 gid=i, 

80 ) 

81 current_glyph.decompile(ttFont) 

82 self.glyphs[current_glyph.glyphName] = current_glyph 

83 del self.glyphDataOffsets 

84 del self.numGlyphs 

85 del self.data 

86 

87 def compile(self, ttFont): 

88 self.glyphDataOffsets = b"" 

89 self.bitmapData = b"" 

90 

91 glyphOrder = ttFont.getGlyphOrder() 

92 

93 # first glyph starts right after the header 

94 currentGlyphDataOffset = ( 

95 sbixStrikeHeaderFormatSize 

96 + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1) 

97 ) 

98 for glyphName in glyphOrder: 

99 if glyphName in self.glyphs: 

100 # we have glyph data for this glyph 

101 current_glyph = self.glyphs[glyphName] 

102 else: 

103 # must add empty glyph data record for this glyph 

104 current_glyph = Glyph(glyphName=glyphName) 

105 current_glyph.compile(ttFont) 

106 current_glyph.glyphDataOffset = currentGlyphDataOffset 

107 self.bitmapData += current_glyph.rawdata 

108 currentGlyphDataOffset += len(current_glyph.rawdata) 

109 self.glyphDataOffsets += sstruct.pack( 

110 sbixGlyphDataOffsetFormat, current_glyph 

111 ) 

112 

113 # add last "offset", really the end address of the last glyph data record 

114 dummy = Glyph() 

115 dummy.glyphDataOffset = currentGlyphDataOffset 

116 self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy) 

117 

118 # pack header 

119 self.data = sstruct.pack(sbixStrikeHeaderFormat, self) 

120 # add offsets and image data after header 

121 self.data += self.glyphDataOffsets + self.bitmapData 

122 

123 def toXML(self, xmlWriter, ttFont): 

124 xmlWriter.begintag("strike") 

125 xmlWriter.newline() 

126 xmlWriter.simpletag("ppem", value=self.ppem) 

127 xmlWriter.newline() 

128 xmlWriter.simpletag("resolution", value=self.resolution) 

129 xmlWriter.newline() 

130 glyphOrder = ttFont.getGlyphOrder() 

131 for i in range(len(glyphOrder)): 

132 if glyphOrder[i] in self.glyphs: 

133 self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont) 

134 # TODO: what if there are more glyph data records than (glyf table) glyphs? 

135 xmlWriter.endtag("strike") 

136 xmlWriter.newline() 

137 

138 def fromXML(self, name, attrs, content, ttFont): 

139 if name in ["ppem", "resolution"]: 

140 setattr(self, name, safeEval(attrs["value"])) 

141 elif name == "glyph": 

142 if "graphicType" in attrs: 

143 myFormat = safeEval("'''" + attrs["graphicType"] + "'''") 

144 else: 

145 myFormat = None 

146 if "glyphname" in attrs: 

147 myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''") 

148 elif "name" in attrs: 

149 myGlyphName = safeEval("'''" + attrs["name"] + "'''") 

150 else: 

151 from fontTools import ttLib 

152 

153 raise ttLib.TTLibError("Glyph must have a glyph name.") 

154 if "originOffsetX" in attrs: 

155 myOffsetX = safeEval(attrs["originOffsetX"]) 

156 else: 

157 myOffsetX = 0 

158 if "originOffsetY" in attrs: 

159 myOffsetY = safeEval(attrs["originOffsetY"]) 

160 else: 

161 myOffsetY = 0 

162 current_glyph = Glyph( 

163 glyphName=myGlyphName, 

164 graphicType=myFormat, 

165 originOffsetX=myOffsetX, 

166 originOffsetY=myOffsetY, 

167 ) 

168 for element in content: 

169 if isinstance(element, tuple): 

170 name, attrs, content = element 

171 current_glyph.fromXML(name, attrs, content, ttFont) 

172 current_glyph.compile(ttFont) 

173 self.glyphs[current_glyph.glyphName] = current_glyph 

174 else: 

175 from fontTools import ttLib 

176 

177 raise ttLib.TTLibError("can't handle '%s' element" % name)