• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.misc import sstruct
2from fontTools.misc.textTools import safeEval, num2binary, binary2num
3from . import DefaultTable
4from .sbixStrike import Strike
5
6
7sbixHeaderFormat = """
8	>
9	version:       H	# Version number (set to 1)
10	flags:         H	# The only two bits used in the flags field are bits 0
11						# and 1. For historical reasons, bit 0 must always be 1.
12						# Bit 1 is a sbixDrawOutlines flag and is interpreted as
13						# follows:
14						#     0: Draw only 'sbix' bitmaps
15						#     1: Draw both 'sbix' bitmaps and outlines, in that
16						#        order
17	numStrikes:    L	# Number of bitmap strikes to follow
18"""
19sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat)
20
21
22sbixStrikeOffsetFormat = """
23	>
24	strikeOffset:  L	# Offset from begining of table to data for the
25						# individual strike
26"""
27sbixStrikeOffsetFormatSize = sstruct.calcsize(sbixStrikeOffsetFormat)
28
29
30class table__s_b_i_x(DefaultTable.DefaultTable):
31    def __init__(self, tag=None):
32        DefaultTable.DefaultTable.__init__(self, tag)
33        self.version = 1
34        self.flags = 1
35        self.numStrikes = 0
36        self.strikes = {}
37        self.strikeOffsets = []
38
39    def decompile(self, data, ttFont):
40        # read table header
41        sstruct.unpack(sbixHeaderFormat, data[:sbixHeaderFormatSize], self)
42        # collect offsets to individual strikes in self.strikeOffsets
43        for i in range(self.numStrikes):
44            current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize
45            offset_entry = sbixStrikeOffset()
46            sstruct.unpack(
47                sbixStrikeOffsetFormat,
48                data[current_offset : current_offset + sbixStrikeOffsetFormatSize],
49                offset_entry,
50            )
51            self.strikeOffsets.append(offset_entry.strikeOffset)
52
53        # decompile Strikes
54        for i in range(self.numStrikes - 1, -1, -1):
55            current_strike = Strike(rawdata=data[self.strikeOffsets[i] :])
56            data = data[: self.strikeOffsets[i]]
57            current_strike.decompile(ttFont)
58            # print "  Strike length: %xh" % len(bitmapSetData)
59            # print "Number of Glyph entries:", len(current_strike.glyphs)
60            if current_strike.ppem in self.strikes:
61                from fontTools import ttLib
62
63                raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike")
64            self.strikes[current_strike.ppem] = current_strike
65
66        # after the glyph data records have been extracted, we don't need the offsets anymore
67        del self.strikeOffsets
68        del self.numStrikes
69
70    def compile(self, ttFont):
71        sbixData = b""
72        self.numStrikes = len(self.strikes)
73        sbixHeader = sstruct.pack(sbixHeaderFormat, self)
74
75        # calculate offset to start of first strike
76        setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes
77
78        for si in sorted(self.strikes.keys()):
79            current_strike = self.strikes[si]
80            current_strike.compile(ttFont)
81            # append offset to this strike to table header
82            current_strike.strikeOffset = setOffset
83            sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike)
84            setOffset += len(current_strike.data)
85            sbixData += current_strike.data
86
87        return sbixHeader + sbixData
88
89    def toXML(self, xmlWriter, ttFont):
90        xmlWriter.simpletag("version", value=self.version)
91        xmlWriter.newline()
92        xmlWriter.simpletag("flags", value=num2binary(self.flags, 16))
93        xmlWriter.newline()
94        for i in sorted(self.strikes.keys()):
95            self.strikes[i].toXML(xmlWriter, ttFont)
96
97    def fromXML(self, name, attrs, content, ttFont):
98        if name == "version":
99            setattr(self, name, safeEval(attrs["value"]))
100        elif name == "flags":
101            setattr(self, name, binary2num(attrs["value"]))
102        elif name == "strike":
103            current_strike = Strike()
104            for element in content:
105                if isinstance(element, tuple):
106                    name, attrs, content = element
107                    current_strike.fromXML(name, attrs, content, ttFont)
108            self.strikes[current_strike.ppem] = current_strike
109        else:
110            from fontTools import ttLib
111
112            raise ttLib.TTLibError("can't handle '%s' element" % name)
113
114
115# Helper classes
116
117
118class sbixStrikeOffset(object):
119    pass
120