19d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod# Copyright 2013 Google, Inc. All Rights Reserved.
29d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod#
39d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod# Google Author(s): Behdad Esfahbod
49d7dc222990efdab0681ce7c519ef3a73d3fd718Behdad Esfahbod
51ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
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 = []
57dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod		ttFont.getReverseGlyphMap(rebuild=True)
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)
7718316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		data = bytesjoin(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:
107faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod				if isinstance(element, basestring):
10850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod					continue
10950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod			layers = []
11050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod			for element in content:
111faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod				if isinstance(element, basestring):
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
141487b15fd9492fdb3e9d578866e539f726f12b4afBehdad Esfahbod	def __delitem__(self, glyphSelector):
142487b15fd9492fdb3e9d578866e539f726f12b4afBehdad Esfahbod		del self.ColorLayers[glyphSelector]
143487b15fd9492fdb3e9d578866e539f726f12b4afBehdad Esfahbod
144e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass LayerRecord(object):
14550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod
14650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod	def __init__(self, name = None, colorID = None):
14750d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod		self.name = name
14850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod		self.colorID = colorID
14950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod
15050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod	def toXML(self, writer, ttFont):
15150d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod		writer.simpletag("layer", name=self.name, colorID=self.colorID)
15250d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod		writer.newline()
15350d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod
1543a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, eltname, attrs, content, ttFont):
15550d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod		for (name, value) in attrs.items():
15650d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod			if name == "name":
157b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if isinstance(value, int):
15850d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod					value = ttFont.getGlyphName(value)
15950d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod				setattr(self, name, value)
16050d9a44e5887e303480af571cca8b69cd82c3173Behdad Esfahbod			else:
1617cc6d271ac955782d730161b27e728001fb5f347Behdad Esfahbod				setattr(self, name, safeEval(value))
162