C_O_L_R_.py revision 30e691edd056ba22fa8970280e986747817bec3d
19d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod# Copyright 2013 Google, Inc. All Rights Reserved. 29d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod# 39d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod# Google Author(s): Behdad Esfahbod 49d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod 530e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom __future__ import print_function 630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 730e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.textTools import safeEval 82b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom . import DefaultTable 930e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport operator 1050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbodimport struct 1150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 1250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 1350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbodclass table_C_O_L_R_(DefaultTable.DefaultTable): 1450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 1550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod """ This table is structured so that you can treat it like a dictionary keyed by glyph name. 1650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ttFont['COLR'][<glyphName>] will return the color layers for any glyph 1750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph. 1850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod """ 1950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 2050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def decompile(self, data, ttFont): 2150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID 2250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.version, numBaseGlyphRecords, offsetBaseGlyphRecord, offsetLayerRecord, numLayerRecords = struct.unpack(">HHLLH", data[:14]) 2350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod assert (self.version == 0), "Version of COLR table is higher than I know how to handle" 2450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphOrder = ttFont.getGlyphOrder() 2550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod gids = [] 2650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerLists = [] 2750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphPos = offsetBaseGlyphRecord 2850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for i in range(numBaseGlyphRecords): 2950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod gid, firstLayerIndex, numLayers = struct.unpack(">HHH", data[glyphPos:glyphPos+6]) 3050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphPos += 6 3150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod gids.append(gid) 3250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod assert (firstLayerIndex + numLayers <= numLayerRecords) 3350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerPos = offsetLayerRecord + firstLayerIndex * 4 3450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layers = [] 3550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for j in range(numLayers): 3650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerGid, colorID = struct.unpack(">HH", data[layerPos:layerPos+4]) 3750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod try: 3850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerName = glyphOrder[layerGid] 3950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod except IndexError: 4050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerName = self.getGlyphName(layerGid) 4150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerPos += 4 4250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layers.append(LayerRecord(layerName, colorID)) 4350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerLists.append(layers) 4450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 4550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.ColorLayers = colorLayerLists = {} 4650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod try: 47e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids)) 4850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod except IndexError: 4950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod getGlyphName = self.getGlyphName 50e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod names = list(map(getGlyphName, gids )) 5150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 52e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists)) 5350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 5450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 5550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def compile(self, ttFont): 5650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered = [] 5750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ttFont.getReverseGlyphMap(rebuild=1) 5850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphNames = self.ColorLayers.keys() 5950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for glyphName in glyphNames: 6050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod try: 6150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod gid = ttFont.getGlyphID(glyphName) 6250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod except: 6350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName) 6450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered.append([gid, glyphName, self.ColorLayers[glyphName]]) 6550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered.sort() 6650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 6750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphMap = [] 6850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerMap = [] 6950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for (gid, glyphName, layers) in ordered: 7050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphMap.append(struct.pack(">HHH", gid, len(layerMap), len(layers))) 7150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for layer in layers: 7250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layerMap.append(struct.pack(">HH", ttFont.getGlyphID(layer.name), layer.colorID)) 7350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 7450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))] 7550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod dataList.extend(glyphMap) 7650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod dataList.extend(layerMap) 7750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod data = "".join(dataList) 7850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod return data 7950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 8050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def toXML(self, writer, ttFont): 8150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.simpletag("version", value=self.version) 8250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.newline() 8350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered = [] 8450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphNames = self.ColorLayers.keys() 8550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for glyphName in glyphNames: 8650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod try: 8750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod gid = ttFont.getGlyphID(glyphName) 8850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod except: 8950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName) 9050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered.append([gid, glyphName, self.ColorLayers[glyphName]]) 9150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod ordered.sort() 9250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for entry in ordered: 9350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.begintag("ColorGlyph", name=entry[1]) 9450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.newline() 9550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for layer in entry[2]: 9650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layer.toXML(writer, ttFont) 9750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.endtag("ColorGlyph") 9850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.newline() 9950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 1003a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, ttFont): 10150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod if not hasattr(self, "ColorLayers"): 10250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.ColorLayers = {} 10350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID 10450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod if name == "ColorGlyph": 10550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphName = attrs["name"] 10650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for element in content: 107b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if isinstance(element, str): 10850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod continue 10950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layers = [] 11050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for element in content: 111b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if isinstance(element, str): 11250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod continue 11350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layer = LayerRecord() 1143a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod layer.fromXML(element[0], element[1], element[2], ttFont) 11550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod layers.append (layer) 11650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod operator.setitem(self, glyphName, layers) 117bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif "value" in attrs: 1187cc6d271ac955782d730161b27e728001fb5f347Behdad Esfahbod setattr(self, name, safeEval(attrs["value"])) 11950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 12050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 12150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def __getitem__(self, glyphSelector): 122b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if isinstance(glyphSelector, int): 12350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod # its a gid, convert to glyph name 12450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphSelector = self.getGlyphName(glyphSelector) 12550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 126bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if glyphSelector not in self.ColorLayers: 12750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod return None 12850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 12950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod return self.ColorLayers[glyphSelector] 13050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 13150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def __setitem__(self, glyphSelector, value): 132b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if isinstance(glyphSelector, int): 13350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod # its a gid, convert to glyph name 13450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod glyphSelector = self.getGlyphName(glyphSelector) 13550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 13650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod if value: 13750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.ColorLayers[glyphSelector] = value 138bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod elif glyphSelector in self.ColorLayers: 13950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod del self.ColorLayers[glyphSelector] 14050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 14150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbodclass LayerRecord: 14250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 14350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def __init__(self, name = None, colorID = None): 14450d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.name = name 14550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod self.colorID = colorID 14650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 14750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod def toXML(self, writer, ttFont): 14850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.simpletag("layer", name=self.name, colorID=self.colorID) 14950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod writer.newline() 15050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod 1513a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, eltname, attrs, content, ttFont): 15250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod for (name, value) in attrs.items(): 15350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod if name == "name": 154b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if isinstance(value, int): 15550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod value = ttFont.getGlyphName(value) 15650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod setattr(self, name, value) 15750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod else: 1587cc6d271ac955782d730161b27e728001fb5f347Behdad Esfahbod setattr(self, name, safeEval(value)) 159