| # Copyright 2013 Google, Inc. All Rights Reserved. |
| # |
| # Google Author(s): Behdad Esfahbod |
| |
| from fontTools.misc.py23 import * |
| 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, basestring): |
| continue |
| layers = [] |
| for element in content: |
| if isinstance(element, basestring): |
| 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)) |