blob: b7e8281bceb256b493dbdd82b795c7f8d37ba37a [file] [log] [blame]
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
from itertools import *
from functools import partial
from . import DefaultTable
from . import grUtils
import struct, operator, warnings
Glat_format_0 = """
> # big endian
version: 16.16F
"""
Glat_format_3 = """
>
version: 16.16F
compression:L # compression scheme or reserved
"""
Glat_format_1_entry = """
>
attNum: B # Attribute number of first attribute
num: B # Number of attributes in this run
"""
Glat_format_23_entry = """
>
attNum: H # Attribute number of first attribute
num: H # Number of attributes in this run
"""
Glat_format_3_octabox_metrics = """
>
subboxBitmap: H # Which subboxes exist on 4x4 grid
diagNegMin: B # Defines minimum negatively-sloped diagonal (si)
diagNegMax: B # Defines maximum negatively-sloped diagonal (sa)
diagPosMin: B # Defines minimum positively-sloped diagonal (di)
diagPosMax: B # Defines maximum positively-sloped diagonal (da)
"""
Glat_format_3_subbox_entry = """
>
left: B # xi
right: B # xa
bottom: B # yi
top: B # ya
diagNegMin: B # Defines minimum negatively-sloped diagonal (si)
diagNegMax: B # Defines maximum negatively-sloped diagonal (sa)
diagPosMin: B # Defines minimum positively-sloped diagonal (di)
diagPosMax: B # Defines maximum positively-sloped diagonal (da)
"""
class _Object() :
pass
class _Dict(dict) :
pass
class table_G__l_a_t(DefaultTable.DefaultTable):
'''
Support Graphite Glat tables
'''
def __init__(self, tag=None):
DefaultTable.DefaultTable.__init__(self, tag)
self.scheme = 0
def decompile(self, data, ttFont):
sstruct.unpack2(Glat_format_0, data, self)
if self.version <= 1.9:
decoder = partial(self.decompileAttributes12,fmt=Glat_format_1_entry)
elif self.version <= 2.9:
decoder = partial(self.decompileAttributes12,fmt=Glat_format_23_entry)
elif self.version >= 3.0:
(data, self.scheme) = grUtils.decompress(data)
sstruct.unpack2(Glat_format_3, data, self)
self.hasOctaboxes = (self.compression & 1) == 1
decoder = self.decompileAttributes3
gloc = ttFont['Gloc']
self.attributes = {}
count = 0
for s,e in zip(gloc,gloc[1:]):
self.attributes[ttFont.getGlyphName(count)] = decoder(data[s:e])
count += 1
def decompileAttributes12(self, data, fmt):
attributes = _Dict()
while len(data) > 3:
e, data = sstruct.unpack2(fmt, data, _Object())
keys = range(e.attNum, e.attNum+e.num)
if len(data) >= 2 * e.num :
vals = struct.unpack_from(('>%dh' % e.num), data)
attributes.update(zip(keys,vals))
data = data[2*e.num:]
return attributes
def decompileAttributes3(self, data):
if self.hasOctaboxes:
o, data = sstruct.unpack2(Glat_format_3_octabox_metrics, data, _Object())
numsub = bin(o.subboxBitmap).count("1")
o.subboxes = []
for b in range(numsub):
if len(data) >= 8 :
subbox, data = sstruct.unpack2(Glat_format_3_subbox_entry,
data, _Object())
o.subboxes.append(subbox)
attrs = self.decompileAttributes12(data, Glat_format_23_entry)
if self.hasOctaboxes:
attrs.octabox = o
return attrs
def compile(self, ttFont):
data = sstruct.pack(Glat_format_0, self)
if self.version <= 1.9:
encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
elif self.version <= 2.9:
encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
elif self.version >= 3.0:
self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0)
data = sstruct.pack(Glat_format_3, self)
encoder = self.compileAttributes3
glocs = []
for n in range(len(self.attributes)):
glocs.append(len(data))
data += encoder(self.attributes[ttFont.getGlyphName(n)])
glocs.append(len(data))
ttFont['Gloc'].set(glocs)
if self.version >= 3.0:
data = grUtils.compress(self.scheme, data)
return data
def compileAttributes12(self, attrs, fmt):
data = b""
for e in grUtils.entries(attrs):
data += sstruct.pack(fmt, {'attNum' : e[0], 'num' : e[1]}) + \
struct.pack(('>%dh' % len(e[2])), *e[2])
return data
def compileAttributes3(self, attrs):
if self.hasOctaboxes:
o = attrs.octabox
data = sstruct.pack(Glat_format_3_octabox_metrics, o)
numsub = bin(o.subboxBitmap).count("1")
for b in range(numsub) :
data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b])
else:
data = ""
return data + self.compileAttributes12(attrs, Glat_format_23_entry)
def toXML(self, writer, ttFont):
writer.simpletag('version', version=self.version, compressionScheme=self.scheme)
writer.newline()
for n, a in sorted(self.attributes.items(), key=lambda x:ttFont.getGlyphID(x[0])):
writer.begintag('glyph', name=n)
writer.newline()
if hasattr(a, 'octabox'):
o = a.octabox
formatstring, names, fixes = sstruct.getformat(Glat_format_3_octabox_metrics)
vals = {}
for k in names:
if k == 'subboxBitmap': continue
vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 255)
vals['bitmap'] = "{:0X}".format(o.subboxBitmap)
writer.begintag('octaboxes', **vals)
writer.newline()
formatstring, names, fixes = sstruct.getformat(Glat_format_3_subbox_entry)
for s in o.subboxes:
vals = {}
for k in names:
vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 255)
writer.simpletag('octabox', **vals)
writer.newline()
writer.endtag('octaboxes')
writer.newline()
for k, v in sorted(a.items()):
writer.simpletag('attribute', index=k, value=v)
writer.newline()
writer.endtag('glyph')
writer.newline()
def fromXML(self, name, attrs, content, ttFont):
if name == 'version' :
self.version = float(safeEval(attrs['version']))
self.scheme = int(safeEval(attrs['compressionScheme']))
if name != 'glyph' : return
if not hasattr(self, 'attributes'):
self.attributes = {}
gname = attrs['name']
attributes = _Dict()
for element in content:
if not isinstance(element, tuple): continue
tag, attrs, subcontent = element
if tag == 'attribute' :
k = int(safeEval(attrs['index']))
v = int(safeEval(attrs['value']))
attributes[k]=v
elif tag == 'octaboxes':
self.hasOctaboxes = True
o = _Object()
o.subboxBitmap = int(attrs['bitmap'], 16)
o.subboxes = []
del attrs['bitmap']
for k, v in attrs.items():
setattr(o, k, int(float(v[:-1]) * 255. / 100. + 0.5))
for element in subcontent:
if not isinstance(element, tuple): continue
(tag, attrs, subcontent) = element
so = _Object()
for k, v in attrs.items():
setattr(so, k, int(float(v[:-1]) * 255. / 100. + 0.5))
o.subboxes.append(so)
attributes.octabox = o
self.attributes[gname] = attributes