aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/_h_e_a_d.py
blob: fe29c8fc4fffc91e27bf52b5a0b440325d500426 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from fontTools.misc import sstruct
from fontTools.misc.fixedTools import floatToFixedToStr, strToFixedToFloat
from fontTools.misc.textTools import safeEval, num2binary, binary2num
from fontTools.misc.timeTools import (
    timestampFromString,
    timestampToString,
    timestampNow,
)
from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff  # For backward compat
from fontTools.misc.arrayTools import intRect, unionRect
from . import DefaultTable
import logging


log = logging.getLogger(__name__)

headFormat = """
		>	# big endian
		tableVersion:       16.16F
		fontRevision:       16.16F
		checkSumAdjustment: I
		magicNumber:        I
		flags:              H
		unitsPerEm:         H
		created:            Q
		modified:           Q
		xMin:               h
		yMin:               h
		xMax:               h
		yMax:               h
		macStyle:           H
		lowestRecPPEM:      H
		fontDirectionHint:  h
		indexToLocFormat:   h
		glyphDataFormat:    h
"""


class table__h_e_a_d(DefaultTable.DefaultTable):
    dependencies = ["maxp", "loca", "CFF ", "CFF2"]

    def decompile(self, data, ttFont):
        dummy, rest = sstruct.unpack2(headFormat, data, self)
        if rest:
            # this is quite illegal, but there seem to be fonts out there that do this
            log.warning("extra bytes at the end of 'head' table")
            assert rest == b"\0\0"

        # For timestamp fields, ignore the top four bytes.  Some fonts have
        # bogus values there.  Since till 2038 those bytes only can be zero,
        # ignore them.
        #
        # https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
        for stamp in "created", "modified":
            value = getattr(self, stamp)
            if value > 0xFFFFFFFF:
                log.warning("'%s' timestamp out of range; ignoring top bytes", stamp)
                value &= 0xFFFFFFFF
                setattr(self, stamp, value)
            if value < 0x7C259DC0:  # January 1, 1970 00:00:00
                log.warning(
                    "'%s' timestamp seems very low; regarding as unix timestamp", stamp
                )
                value += 0x7C259DC0
                setattr(self, stamp, value)

    def compile(self, ttFont):
        if ttFont.recalcBBoxes:
            # For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc().
            if "CFF " in ttFont:
                topDict = ttFont["CFF "].cff.topDictIndex[0]
                self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox)
            elif "CFF2" in ttFont:
                topDict = ttFont["CFF2"].cff.topDictIndex[0]
                charStrings = topDict.CharStrings
                fontBBox = None
                for charString in charStrings.values():
                    bounds = charString.calcBounds(charStrings)
                    if bounds is not None:
                        if fontBBox is not None:
                            fontBBox = unionRect(fontBBox, bounds)
                        else:
                            fontBBox = bounds
                if fontBBox is not None:
                    self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox)
        if ttFont.recalcTimestamp:
            self.modified = timestampNow()
        data = sstruct.pack(headFormat, self)
        return data

    def toXML(self, writer, ttFont):
        writer.comment("Most of this table will be recalculated by the compiler")
        writer.newline()
        _, names, fixes = sstruct.getformat(headFormat)
        for name in names:
            value = getattr(self, name)
            if name in fixes:
                value = floatToFixedToStr(value, precisionBits=fixes[name])
            elif name in ("created", "modified"):
                value = timestampToString(value)
            elif name in ("magicNumber", "checkSumAdjustment"):
                if value < 0:
                    value = value + 0x100000000
                value = hex(value)
                if value[-1:] == "L":
                    value = value[:-1]
            elif name in ("macStyle", "flags"):
                value = num2binary(value, 16)
            writer.simpletag(name, value=value)
            writer.newline()

    def fromXML(self, name, attrs, content, ttFont):
        value = attrs["value"]
        fixes = sstruct.getformat(headFormat)[2]
        if name in fixes:
            value = strToFixedToFloat(value, precisionBits=fixes[name])
        elif name in ("created", "modified"):
            value = timestampFromString(value)
        elif name in ("macStyle", "flags"):
            value = binary2num(value)
        else:
            value = safeEval(value)
        setattr(self, name, value)