Spaces:
Runtime error
Runtime error
# 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. | |
""" | |
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)) | |