123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- # 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))
|