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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# Copyright 2013 Google, Inc. All Rights Reserved.
#
# Google Author(s): Behdad Esfahbod
from fontTools.misc.textTools import safeEval
from . import DefaultTable
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.
"""
@staticmethod
def _decompileColorLayersV0(table):
if not table.LayerRecordArray:
return {}
colorLayerLists = {}
layerRecords = table.LayerRecordArray.LayerRecord
numLayerRecords = len(layerRecords)
for baseRec in table.BaseGlyphRecordArray.BaseGlyphRecord:
baseGlyph = baseRec.BaseGlyph
firstLayerIndex = baseRec.FirstLayerIndex
numLayers = baseRec.NumLayers
assert (firstLayerIndex + numLayers <= numLayerRecords)
layers = []
for i in range(firstLayerIndex, firstLayerIndex+numLayers):
layerRec = layerRecords[i]
layers.append(
LayerRecord(layerRec.LayerGlyph, layerRec.PaletteIndex)
)
colorLayerLists[baseGlyph] = layers
return colorLayerLists
def _toOTTable(self, ttFont):
from . import otTables
from fontTools.colorLib.builder import populateCOLRv0
tableClass = getattr(otTables, self.tableTag)
table = tableClass()
table.Version = self.version
populateCOLRv0(
table,
{
baseGlyph: [(layer.name, layer.colorID) for layer in layers]
for baseGlyph, layers in self.ColorLayers.items()
},
glyphMap=ttFont.getReverseGlyphMap(rebuild=True),
)
return table
def decompile(self, data, ttFont):
from .otBase import OTTableReader
from . import otTables
# We use otData to decompile, but we adapt the decompiled otTables to the
# existing COLR v0 API for backward compatibility.
reader = OTTableReader(data, tableTag=self.tableTag)
tableClass = getattr(otTables, self.tableTag)
table = tableClass()
table.decompile(reader, ttFont)
self.version = table.Version
if self.version == 0:
self.ColorLayers = self._decompileColorLayersV0(table)
else:
# for new versions, keep the raw otTables around
self.table = table
def compile(self, ttFont):
from .otBase import OTTableWriter
if hasattr(self, "table"):
table = self.table
else:
table = self._toOTTable(ttFont)
writer = OTTableWriter(tableTag=self.tableTag)
table.compile(writer, ttFont)
return writer.getAllData()
def toXML(self, writer, ttFont):
if hasattr(self, "table"):
self.table.toXML2(writer, ttFont)
else:
writer.simpletag("version", value=self.version)
writer.newline()
for baseGlyph in sorted(self.ColorLayers.keys(), key=ttFont.getGlyphID):
writer.begintag("ColorGlyph", name=baseGlyph)
writer.newline()
for layer in self.ColorLayers[baseGlyph]:
layer.toXML(writer, ttFont)
writer.endtag("ColorGlyph")
writer.newline()
def fromXML(self, name, attrs, content, ttFont):
if name == "version": # old COLR v0 API
setattr(self, name, safeEval(attrs["value"]))
elif name == "ColorGlyph":
if not hasattr(self, "ColorLayers"):
self.ColorLayers = {}
glyphName = attrs["name"]
for element in content:
if isinstance(element, str):
continue
layers = []
for element in content:
if isinstance(element, str):
continue
layer = LayerRecord()
layer.fromXML(element[0], element[1], element[2], ttFont)
layers.append (layer)
self.ColorLayers[glyphName] = layers
else: # new COLR v1 API
from . import otTables
if not hasattr(self, "table"):
tableClass = getattr(otTables, self.tableTag)
self.table = tableClass()
self.table.fromXML(name, attrs, content, ttFont)
self.table.populateDefaults()
self.version = self.table.Version
def __getitem__(self, glyphName):
if not isinstance(glyphName, str):
raise TypeError(f"expected str, found {type(glyphName).__name__}")
return self.ColorLayers[glyphName]
def __setitem__(self, glyphName, value):
if not isinstance(glyphName, str):
raise TypeError(f"expected str, found {type(glyphName).__name__}")
if value is not None:
self.ColorLayers[glyphName] = value
elif glyphName in self.ColorLayers:
del self.ColorLayers[glyphName]
def __delitem__(self, glyphName):
del self.ColorLayers[glyphName]
class LayerRecord(object):
def __init__(self, name=None, colorID=None):
self.name = name
self.colorID = colorID
def toXML(self, writer, ttFont):
writer.simpletag("layer", name=self.name, colorID=self.colorID)
writer.newline()
def fromXML(self, eltname, attrs, content, ttFont):
for (name, value) in attrs.items():
if name == "name":
setattr(self, name, value)
else:
setattr(self, name, safeEval(value))
|