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
« 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
15log = logging.getLogger(__name__)
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"""
39class table__h_e_a_d(DefaultTable.DefaultTable):
40 dependencies = ["maxp", "loca", "CFF ", "CFF2"]
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"
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)
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
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()
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)