1# Copyright 2013 Google, Inc. All Rights Reserved. 2# 3# Google Author(s): Behdad Esfahbod 4 5from __future__ import print_function, division, absolute_import 6from fontTools.misc.py23 import * 7from fontTools.misc.textTools import safeEval 8from . import DefaultTable 9import struct 10 11 12class table_C_O_L_R_(DefaultTable.DefaultTable): 13 14 """ This table is structured so that you can treat it like a dictionary keyed by glyph name. 15 ttFont['COLR'][<glyphName>] will return the color layers for any glyph 16 ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph. 17 """ 18 19 def decompile(self, data, ttFont): 20 self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID 21 self.version, numBaseGlyphRecords, offsetBaseGlyphRecord, offsetLayerRecord, numLayerRecords = struct.unpack(">HHLLH", data[:14]) 22 assert (self.version == 0), "Version of COLR table is higher than I know how to handle" 23 glyphOrder = ttFont.getGlyphOrder() 24 gids = [] 25 layerLists = [] 26 glyphPos = offsetBaseGlyphRecord 27 for i in range(numBaseGlyphRecords): 28 gid, firstLayerIndex, numLayers = struct.unpack(">HHH", data[glyphPos:glyphPos+6]) 29 glyphPos += 6 30 gids.append(gid) 31 assert (firstLayerIndex + numLayers <= numLayerRecords) 32 layerPos = offsetLayerRecord + firstLayerIndex * 4 33 layers = [] 34 for j in range(numLayers): 35 layerGid, colorID = struct.unpack(">HH", data[layerPos:layerPos+4]) 36 try: 37 layerName = glyphOrder[layerGid] 38 except IndexError: 39 layerName = self.getGlyphName(layerGid) 40 layerPos += 4 41 layers.append(LayerRecord(layerName, colorID)) 42 layerLists.append(layers) 43 44 self.ColorLayers = colorLayerLists = {} 45 try: 46 names = [glyphOrder[gid] for gid in gids] 47 except IndexError: 48 getGlyphName = self.getGlyphName 49 names = map(getGlyphName, gids) 50 51 for name, layerList in zip(names, layerLists): 52 colorLayerLists[name] = layerList 53 54 def compile(self, ttFont): 55 ordered = [] 56 ttFont.getReverseGlyphMap(rebuild=True) 57 glyphNames = self.ColorLayers.keys() 58 for glyphName in glyphNames: 59 try: 60 gid = ttFont.getGlyphID(glyphName) 61 except: 62 assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName) 63 ordered.append([gid, glyphName, self.ColorLayers[glyphName]]) 64 ordered.sort() 65 66 glyphMap = [] 67 layerMap = [] 68 for (gid, glyphName, layers) in ordered: 69 glyphMap.append(struct.pack(">HHH", gid, len(layerMap), len(layers))) 70 for layer in layers: 71 layerMap.append(struct.pack(">HH", ttFont.getGlyphID(layer.name), layer.colorID)) 72 73 dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))] 74 dataList.extend(glyphMap) 75 dataList.extend(layerMap) 76 data = bytesjoin(dataList) 77 return data 78 79 def toXML(self, writer, ttFont): 80 writer.simpletag("version", value=self.version) 81 writer.newline() 82 ordered = [] 83 glyphNames = self.ColorLayers.keys() 84 for glyphName in glyphNames: 85 try: 86 gid = ttFont.getGlyphID(glyphName) 87 except: 88 assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName) 89 ordered.append([gid, glyphName, self.ColorLayers[glyphName]]) 90 ordered.sort() 91 for entry in ordered: 92 writer.begintag("ColorGlyph", name=entry[1]) 93 writer.newline() 94 for layer in entry[2]: 95 layer.toXML(writer, ttFont) 96 writer.endtag("ColorGlyph") 97 writer.newline() 98 99 def fromXML(self, name, attrs, content, ttFont): 100 if not hasattr(self, "ColorLayers"): 101 self.ColorLayers = {} 102 self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID 103 if name == "ColorGlyph": 104 glyphName = attrs["name"] 105 for element in content: 106 if isinstance(element, basestring): 107 continue 108 layers = [] 109 for element in content: 110 if isinstance(element, basestring): 111 continue 112 layer = LayerRecord() 113 layer.fromXML(element[0], element[1], element[2], ttFont) 114 layers.append (layer) 115 self[glyphName] = layers 116 elif "value" in attrs: 117 setattr(self, name, safeEval(attrs["value"])) 118 119 def __getitem__(self, glyphSelector): 120 if isinstance(glyphSelector, int): 121 # its a gid, convert to glyph name 122 glyphSelector = self.getGlyphName(glyphSelector) 123 124 if glyphSelector not in self.ColorLayers: 125 return None 126 127 return self.ColorLayers[glyphSelector] 128 129 def __setitem__(self, glyphSelector, value): 130 if isinstance(glyphSelector, int): 131 # its a gid, convert to glyph name 132 glyphSelector = self.getGlyphName(glyphSelector) 133 134 if value: 135 self.ColorLayers[glyphSelector] = value 136 elif glyphSelector in self.ColorLayers: 137 del self.ColorLayers[glyphSelector] 138 139 def __delitem__(self, glyphSelector): 140 del self.ColorLayers[glyphSelector] 141 142class LayerRecord(object): 143 144 def __init__(self, name=None, colorID=None): 145 self.name = name 146 self.colorID = colorID 147 148 def toXML(self, writer, ttFont): 149 writer.simpletag("layer", name=self.name, colorID=self.colorID) 150 writer.newline() 151 152 def fromXML(self, eltname, attrs, content, ttFont): 153 for (name, value) in attrs.items(): 154 if name == "name": 155 if isinstance(value, int): 156 value = ttFont.getGlyphName(value) 157 setattr(self, name, value) 158 else: 159 setattr(self, name, safeEval(value)) 160