• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.misc import sstruct
2from fontTools.misc.fixedTools import floatToFixedToStr
3from fontTools.misc.textTools import safeEval
4# from itertools import *
5from functools import partial
6from . import DefaultTable
7from . import grUtils
8import struct
9
10
11Glat_format_0 = """
12    >        # big endian
13    version: 16.16F
14"""
15
16Glat_format_3 = """
17    >
18    version: 16.16F
19    compression:L    # compression scheme or reserved
20"""
21
22Glat_format_1_entry = """
23    >
24    attNum:     B    # Attribute number of first attribute
25    num:        B    # Number of attributes in this run
26"""
27Glat_format_23_entry = """
28    >
29    attNum:     H    # Attribute number of first attribute
30    num:        H    # Number of attributes in this run
31"""
32
33Glat_format_3_octabox_metrics = """
34    >
35    subboxBitmap:   H    # Which subboxes exist on 4x4 grid
36    diagNegMin:     B    # Defines minimum negatively-sloped diagonal (si)
37    diagNegMax:     B    # Defines maximum negatively-sloped diagonal (sa)
38    diagPosMin:     B    # Defines minimum positively-sloped diagonal (di)
39    diagPosMax:     B    # Defines maximum positively-sloped diagonal (da)
40"""
41
42Glat_format_3_subbox_entry = """
43    >
44    left:           B    # xi
45    right:          B    # xa
46    bottom:         B    # yi
47    top:            B    # ya
48    diagNegMin:     B    # Defines minimum negatively-sloped diagonal (si)
49    diagNegMax:     B    # Defines maximum negatively-sloped diagonal (sa)
50    diagPosMin:     B    # Defines minimum positively-sloped diagonal (di)
51    diagPosMax:     B    # Defines maximum positively-sloped diagonal (da)
52"""
53
54class _Object() :
55    pass
56
57class _Dict(dict) :
58    pass
59
60class table_G__l_a_t(DefaultTable.DefaultTable):
61    '''
62    Support Graphite Glat tables
63    '''
64
65    def __init__(self, tag=None):
66        DefaultTable.DefaultTable.__init__(self, tag)
67        self.scheme = 0
68
69    def decompile(self, data, ttFont):
70        sstruct.unpack2(Glat_format_0, data, self)
71        self.version = float(floatToFixedToStr(self.version, precisionBits=16))
72        if self.version <= 1.9:
73            decoder = partial(self.decompileAttributes12,fmt=Glat_format_1_entry)
74        elif self.version <= 2.9:
75            decoder = partial(self.decompileAttributes12,fmt=Glat_format_23_entry)
76        elif self.version >= 3.0:
77            (data, self.scheme) = grUtils.decompress(data)
78            sstruct.unpack2(Glat_format_3, data, self)
79            self.hasOctaboxes = (self.compression & 1) == 1
80            decoder = self.decompileAttributes3
81
82        gloc = ttFont['Gloc']
83        self.attributes = {}
84        count = 0
85        for s,e in zip(gloc,gloc[1:]):
86            self.attributes[ttFont.getGlyphName(count)] = decoder(data[s:e])
87            count += 1
88
89    def decompileAttributes12(self, data, fmt):
90        attributes = _Dict()
91        while len(data) > 3:
92            e, data = sstruct.unpack2(fmt, data, _Object())
93            keys = range(e.attNum, e.attNum+e.num)
94            if len(data) >= 2 * e.num :
95                vals = struct.unpack_from(('>%dh' % e.num), data)
96                attributes.update(zip(keys,vals))
97                data = data[2*e.num:]
98        return attributes
99
100    def decompileAttributes3(self, data):
101        if self.hasOctaboxes:
102            o, data = sstruct.unpack2(Glat_format_3_octabox_metrics, data, _Object())
103            numsub = bin(o.subboxBitmap).count("1")
104            o.subboxes = []
105            for b in range(numsub):
106                if len(data) >= 8 :
107                    subbox, data = sstruct.unpack2(Glat_format_3_subbox_entry,
108                                                    data, _Object())
109                    o.subboxes.append(subbox)
110        attrs = self.decompileAttributes12(data, Glat_format_23_entry)
111        if self.hasOctaboxes:
112            attrs.octabox = o
113        return attrs
114
115    def compile(self, ttFont):
116        data = sstruct.pack(Glat_format_0, self)
117        if self.version <= 1.9:
118            encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
119        elif self.version <= 2.9:
120            encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
121        elif self.version >= 3.0:
122            self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0)
123            data = sstruct.pack(Glat_format_3, self)
124            encoder = self.compileAttributes3
125
126        glocs = []
127        for n in range(len(self.attributes)):
128            glocs.append(len(data))
129            data += encoder(self.attributes[ttFont.getGlyphName(n)])
130        glocs.append(len(data))
131        ttFont['Gloc'].set(glocs)
132
133        if self.version >= 3.0:
134            data = grUtils.compress(self.scheme, data)
135        return data
136
137    def compileAttributes12(self, attrs, fmt):
138        data = b""
139        for e in grUtils.entries(attrs):
140            data += sstruct.pack(fmt, {'attNum' : e[0], 'num' : e[1]}) + \
141                    struct.pack(('>%dh' % len(e[2])), *e[2])
142        return data
143
144    def compileAttributes3(self, attrs):
145        if self.hasOctaboxes:
146            o = attrs.octabox
147            data = sstruct.pack(Glat_format_3_octabox_metrics, o)
148            numsub = bin(o.subboxBitmap).count("1")
149            for b in range(numsub) :
150                data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b])
151        else:
152            data = ""
153        return data + self.compileAttributes12(attrs, Glat_format_23_entry)
154
155    def toXML(self, writer, ttFont):
156        writer.simpletag('version', version=self.version, compressionScheme=self.scheme)
157        writer.newline()
158        for n, a in sorted(self.attributes.items(), key=lambda x:ttFont.getGlyphID(x[0])):
159            writer.begintag('glyph', name=n)
160            writer.newline()
161            if hasattr(a, 'octabox'):
162                o = a.octabox
163                formatstring, names, fixes = sstruct.getformat(Glat_format_3_octabox_metrics)
164                vals = {}
165                for k in names:
166                    if k == 'subboxBitmap': continue
167                    vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 255)
168                vals['bitmap'] = "{:0X}".format(o.subboxBitmap)
169                writer.begintag('octaboxes', **vals)
170                writer.newline()
171                formatstring, names, fixes = sstruct.getformat(Glat_format_3_subbox_entry)
172                for s in o.subboxes:
173                    vals = {}
174                    for k in names:
175                        vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 255)
176                    writer.simpletag('octabox', **vals)
177                    writer.newline()
178                writer.endtag('octaboxes')
179                writer.newline()
180            for k, v in sorted(a.items()):
181                writer.simpletag('attribute', index=k, value=v)
182                writer.newline()
183            writer.endtag('glyph')
184            writer.newline()
185
186    def fromXML(self, name, attrs, content, ttFont):
187        if name == 'version' :
188            self.version = float(safeEval(attrs['version']))
189            self.scheme = int(safeEval(attrs['compressionScheme']))
190        if name != 'glyph' : return
191        if not hasattr(self, 'attributes'):
192            self.attributes = {}
193        gname = attrs['name']
194        attributes = _Dict()
195        for element in content:
196            if not isinstance(element, tuple): continue
197            tag, attrs, subcontent = element
198            if tag == 'attribute' :
199                k = int(safeEval(attrs['index']))
200                v = int(safeEval(attrs['value']))
201                attributes[k]=v
202            elif tag == 'octaboxes':
203                self.hasOctaboxes = True
204                o = _Object()
205                o.subboxBitmap = int(attrs['bitmap'], 16)
206                o.subboxes = []
207                del attrs['bitmap']
208                for k, v in attrs.items():
209                    setattr(o, k, int(float(v[:-1]) * 255. / 100. + 0.5))
210                for element in subcontent:
211                    if not isinstance(element, tuple): continue
212                    (tag, attrs, subcontent) = element
213                    so = _Object()
214                    for k, v in attrs.items():
215                        setattr(so, k, int(float(v[:-1]) * 255. / 100. + 0.5))
216                    o.subboxes.append(so)
217                attributes.octabox = o
218        self.attributes[gname] = attributes
219