• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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