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

79 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.fixedTools import floatToFixedToStr, strToFixedToFloat 

3from fontTools.misc.textTools import safeEval, num2binary, binary2num 

4from fontTools.misc.timeTools import ( 

5 timestampFromString, 

6 timestampToString, 

7 timestampNow, 

8) 

9from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff # For backward compat 

10from fontTools.misc.arrayTools import intRect, unionRect 

11from . import DefaultTable 

12import logging 

13 

14 

15log = logging.getLogger(__name__) 

16 

17headFormat = """ 

18 > # big endian 

19 tableVersion: 16.16F 

20 fontRevision: 16.16F 

21 checkSumAdjustment: I 

22 magicNumber: I 

23 flags: H 

24 unitsPerEm: H 

25 created: Q 

26 modified: Q 

27 xMin: h 

28 yMin: h 

29 xMax: h 

30 yMax: h 

31 macStyle: H 

32 lowestRecPPEM: H 

33 fontDirectionHint: h 

34 indexToLocFormat: h 

35 glyphDataFormat: h 

36""" 

37 

38 

39class table__h_e_a_d(DefaultTable.DefaultTable): 

40 dependencies = ["maxp", "loca", "CFF ", "CFF2"] 

41 

42 def decompile(self, data, ttFont): 

43 dummy, rest = sstruct.unpack2(headFormat, data, self) 

44 if rest: 

45 # this is quite illegal, but there seem to be fonts out there that do this 

46 log.warning("extra bytes at the end of 'head' table") 

47 assert rest == b"\0\0" 

48 

49 # For timestamp fields, ignore the top four bytes. Some fonts have 

50 # bogus values there. Since till 2038 those bytes only can be zero, 

51 # ignore them. 

52 # 

53 # https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810 

54 for stamp in "created", "modified": 

55 value = getattr(self, stamp) 

56 if value > 0xFFFFFFFF: 

57 log.warning("'%s' timestamp out of range; ignoring top bytes", stamp) 

58 value &= 0xFFFFFFFF 

59 setattr(self, stamp, value) 

60 if value < 0x7C259DC0: # January 1, 1970 00:00:00 

61 log.warning( 

62 "'%s' timestamp seems very low; regarding as unix timestamp", stamp 

63 ) 

64 value += 0x7C259DC0 

65 setattr(self, stamp, value) 

66 

67 def compile(self, ttFont): 

68 if ttFont.recalcBBoxes: 

69 # For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc(). 

70 if "CFF " in ttFont: 

71 topDict = ttFont["CFF "].cff.topDictIndex[0] 

72 self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox) 

73 elif "CFF2" in ttFont: 

74 topDict = ttFont["CFF2"].cff.topDictIndex[0] 

75 charStrings = topDict.CharStrings 

76 fontBBox = None 

77 for charString in charStrings.values(): 

78 bounds = charString.calcBounds(charStrings) 

79 if bounds is not None: 

80 if fontBBox is not None: 

81 fontBBox = unionRect(fontBBox, bounds) 

82 else: 

83 fontBBox = bounds 

84 if fontBBox is not None: 

85 self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox) 

86 if ttFont.recalcTimestamp: 

87 self.modified = timestampNow() 

88 data = sstruct.pack(headFormat, self) 

89 return data 

90 

91 def toXML(self, writer, ttFont): 

92 writer.comment("Most of this table will be recalculated by the compiler") 

93 writer.newline() 

94 _, names, fixes = sstruct.getformat(headFormat) 

95 for name in names: 

96 value = getattr(self, name) 

97 if name in fixes: 

98 value = floatToFixedToStr(value, precisionBits=fixes[name]) 

99 elif name in ("created", "modified"): 

100 value = timestampToString(value) 

101 elif name in ("magicNumber", "checkSumAdjustment"): 

102 if value < 0: 

103 value = value + 0x100000000 

104 value = hex(value) 

105 if value[-1:] == "L": 

106 value = value[:-1] 

107 elif name in ("macStyle", "flags"): 

108 value = num2binary(value, 16) 

109 writer.simpletag(name, value=value) 

110 writer.newline() 

111 

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

113 value = attrs["value"] 

114 fixes = sstruct.getformat(headFormat)[2] 

115 if name in fixes: 

116 value = strToFixedToFloat(value, precisionBits=fixes[name]) 

117 elif name in ("created", "modified"): 

118 value = timestampFromString(value) 

119 elif name in ("macStyle", "flags"): 

120 value = binary2num(value) 

121 else: 

122 value = safeEval(value) 

123 setattr(self, name, value)