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