2013-09-19 20:02:17 -04:00
|
|
|
# Copyright 2013 Google, Inc. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Google Author(s): Behdad Esfahbod
|
|
|
|
|
2013-11-27 17:27:45 -05:00
|
|
|
from fontTools.misc.py23 import *
|
|
|
|
from fontTools.misc.textTools import safeEval
|
2013-11-27 02:34:11 -05:00
|
|
|
from . import DefaultTable
|
2013-07-24 12:31:50 -04:00
|
|
|
import struct
|
|
|
|
|
|
|
|
|
|
|
|
class table_C_O_L_R_(DefaultTable.DefaultTable):
|
|
|
|
|
|
|
|
""" This table is structured so that you can treat it like a dictionary keyed by glyph name.
|
|
|
|
ttFont['COLR'][<glyphName>] will return the color layers for any glyph
|
|
|
|
ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def decompile(self, data, ttFont):
|
|
|
|
self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
|
|
|
|
self.version, numBaseGlyphRecords, offsetBaseGlyphRecord, offsetLayerRecord, numLayerRecords = struct.unpack(">HHLLH", data[:14])
|
|
|
|
assert (self.version == 0), "Version of COLR table is higher than I know how to handle"
|
|
|
|
glyphOrder = ttFont.getGlyphOrder()
|
|
|
|
gids = []
|
|
|
|
layerLists = []
|
|
|
|
glyphPos = offsetBaseGlyphRecord
|
|
|
|
for i in range(numBaseGlyphRecords):
|
|
|
|
gid, firstLayerIndex, numLayers = struct.unpack(">HHH", data[glyphPos:glyphPos+6])
|
|
|
|
glyphPos += 6
|
|
|
|
gids.append(gid)
|
|
|
|
assert (firstLayerIndex + numLayers <= numLayerRecords)
|
|
|
|
layerPos = offsetLayerRecord + firstLayerIndex * 4
|
|
|
|
layers = []
|
|
|
|
for j in range(numLayers):
|
|
|
|
layerGid, colorID = struct.unpack(">HH", data[layerPos:layerPos+4])
|
|
|
|
try:
|
|
|
|
layerName = glyphOrder[layerGid]
|
|
|
|
except IndexError:
|
|
|
|
layerName = self.getGlyphName(layerGid)
|
|
|
|
layerPos += 4
|
|
|
|
layers.append(LayerRecord(layerName, colorID))
|
|
|
|
layerLists.append(layers)
|
|
|
|
|
|
|
|
self.ColorLayers = colorLayerLists = {}
|
|
|
|
try:
|
2019-03-03 13:12:50 +01:00
|
|
|
names = [glyphOrder[gid] for gid in gids]
|
2013-07-24 12:31:50 -04:00
|
|
|
except IndexError:
|
|
|
|
getGlyphName = self.getGlyphName
|
2019-03-03 13:12:50 +01:00
|
|
|
names = map(getGlyphName, gids)
|
2013-07-24 12:31:50 -04:00
|
|
|
|
2019-03-03 13:12:50 +01:00
|
|
|
for name, layerList in zip(names, layerLists):
|
|
|
|
colorLayerLists[name] = layerList
|
2013-07-24 12:31:50 -04:00
|
|
|
|
|
|
|
def compile(self, ttFont):
|
|
|
|
ordered = []
|
2013-12-04 21:28:50 -05:00
|
|
|
ttFont.getReverseGlyphMap(rebuild=True)
|
2013-07-24 12:31:50 -04:00
|
|
|
glyphNames = self.ColorLayers.keys()
|
|
|
|
for glyphName in glyphNames:
|
|
|
|
try:
|
|
|
|
gid = ttFont.getGlyphID(glyphName)
|
|
|
|
except:
|
|
|
|
assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName)
|
|
|
|
ordered.append([gid, glyphName, self.ColorLayers[glyphName]])
|
|
|
|
ordered.sort()
|
|
|
|
|
|
|
|
glyphMap = []
|
|
|
|
layerMap = []
|
|
|
|
for (gid, glyphName, layers) in ordered:
|
|
|
|
glyphMap.append(struct.pack(">HHH", gid, len(layerMap), len(layers)))
|
|
|
|
for layer in layers:
|
|
|
|
layerMap.append(struct.pack(">HH", ttFont.getGlyphID(layer.name), layer.colorID))
|
|
|
|
|
|
|
|
dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))]
|
|
|
|
dataList.extend(glyphMap)
|
|
|
|
dataList.extend(layerMap)
|
2013-11-27 21:17:35 -05:00
|
|
|
data = bytesjoin(dataList)
|
2013-07-24 12:31:50 -04:00
|
|
|
return data
|
|
|
|
|
|
|
|
def toXML(self, writer, ttFont):
|
|
|
|
writer.simpletag("version", value=self.version)
|
|
|
|
writer.newline()
|
|
|
|
ordered = []
|
|
|
|
glyphNames = self.ColorLayers.keys()
|
|
|
|
for glyphName in glyphNames:
|
|
|
|
try:
|
|
|
|
gid = ttFont.getGlyphID(glyphName)
|
|
|
|
except:
|
|
|
|
assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName)
|
|
|
|
ordered.append([gid, glyphName, self.ColorLayers[glyphName]])
|
|
|
|
ordered.sort()
|
|
|
|
for entry in ordered:
|
|
|
|
writer.begintag("ColorGlyph", name=entry[1])
|
|
|
|
writer.newline()
|
|
|
|
for layer in entry[2]:
|
|
|
|
layer.toXML(writer, ttFont)
|
|
|
|
writer.endtag("ColorGlyph")
|
|
|
|
writer.newline()
|
|
|
|
|
2013-11-27 03:19:32 -05:00
|
|
|
def fromXML(self, name, attrs, content, ttFont):
|
2013-07-24 12:31:50 -04:00
|
|
|
if not hasattr(self, "ColorLayers"):
|
|
|
|
self.ColorLayers = {}
|
|
|
|
self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
|
|
|
|
if name == "ColorGlyph":
|
|
|
|
glyphName = attrs["name"]
|
|
|
|
for element in content:
|
2013-11-27 23:37:57 -05:00
|
|
|
if isinstance(element, basestring):
|
2013-07-24 12:31:50 -04:00
|
|
|
continue
|
|
|
|
layers = []
|
|
|
|
for element in content:
|
2013-11-27 23:37:57 -05:00
|
|
|
if isinstance(element, basestring):
|
2013-07-24 12:31:50 -04:00
|
|
|
continue
|
|
|
|
layer = LayerRecord()
|
2013-11-27 03:19:32 -05:00
|
|
|
layer.fromXML(element[0], element[1], element[2], ttFont)
|
2013-07-24 12:31:50 -04:00
|
|
|
layers.append (layer)
|
2019-03-03 13:12:50 +01:00
|
|
|
self[glyphName] = layers
|
2013-11-27 02:33:03 -05:00
|
|
|
elif "value" in attrs:
|
2013-11-27 04:00:15 -05:00
|
|
|
setattr(self, name, safeEval(attrs["value"]))
|
2013-07-24 12:31:50 -04:00
|
|
|
|
|
|
|
def __getitem__(self, glyphSelector):
|
2013-11-27 05:17:37 -05:00
|
|
|
if isinstance(glyphSelector, int):
|
2013-07-24 12:31:50 -04:00
|
|
|
# its a gid, convert to glyph name
|
|
|
|
glyphSelector = self.getGlyphName(glyphSelector)
|
|
|
|
|
2013-11-27 02:33:03 -05:00
|
|
|
if glyphSelector not in self.ColorLayers:
|
2013-07-24 12:31:50 -04:00
|
|
|
return None
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-07-24 12:31:50 -04:00
|
|
|
return self.ColorLayers[glyphSelector]
|
|
|
|
|
|
|
|
def __setitem__(self, glyphSelector, value):
|
2013-11-27 05:17:37 -05:00
|
|
|
if isinstance(glyphSelector, int):
|
2013-07-24 12:31:50 -04:00
|
|
|
# its a gid, convert to glyph name
|
|
|
|
glyphSelector = self.getGlyphName(glyphSelector)
|
|
|
|
|
|
|
|
if value:
|
|
|
|
self.ColorLayers[glyphSelector] = value
|
2013-11-27 02:33:03 -05:00
|
|
|
elif glyphSelector in self.ColorLayers:
|
2013-07-24 12:31:50 -04:00
|
|
|
del self.ColorLayers[glyphSelector]
|
|
|
|
|
2014-05-14 13:51:10 -06:00
|
|
|
def __delitem__(self, glyphSelector):
|
|
|
|
del self.ColorLayers[glyphSelector]
|
|
|
|
|
2013-11-28 14:26:58 -05:00
|
|
|
class LayerRecord(object):
|
2013-07-24 12:31:50 -04:00
|
|
|
|
2015-04-26 00:54:30 -04:00
|
|
|
def __init__(self, name=None, colorID=None):
|
2013-07-24 12:31:50 -04:00
|
|
|
self.name = name
|
|
|
|
self.colorID = colorID
|
|
|
|
|
|
|
|
def toXML(self, writer, ttFont):
|
|
|
|
writer.simpletag("layer", name=self.name, colorID=self.colorID)
|
|
|
|
writer.newline()
|
|
|
|
|
2013-11-27 03:19:32 -05:00
|
|
|
def fromXML(self, eltname, attrs, content, ttFont):
|
2013-07-24 12:31:50 -04:00
|
|
|
for (name, value) in attrs.items():
|
|
|
|
if name == "name":
|
2013-11-27 05:17:37 -05:00
|
|
|
if isinstance(value, int):
|
2013-07-24 12:31:50 -04:00
|
|
|
value = ttFont.getGlyphName(value)
|
|
|
|
setattr(self, name, value)
|
|
|
|
else:
|
2013-11-27 04:00:15 -05:00
|
|
|
setattr(self, name, safeEval(value))
|