Home | History | Annotate | Download | only in tables
      1 # Copyright 2013 Google, Inc. All Rights Reserved.
      2 #
      3 # Google Author(s): Behdad Esfahbod
      4 
      5 from __future__ import print_function, division, absolute_import
      6 from fontTools.misc.py23 import *
      7 from fontTools.misc.textTools import safeEval
      8 from . import DefaultTable
      9 import operator
     10 import struct
     11 
     12 
     13 class 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 
    144 class 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