Home | History | Annotate | Download | only in tables
      1 """_g_l_y_f.py -- Converter classes for the 'glyf' table."""
      2 
      3 from __future__ import print_function, division, absolute_import
      4 from collections import namedtuple
      5 from fontTools.misc.py23 import *
      6 from fontTools.misc import sstruct
      7 from fontTools import ttLib
      8 from fontTools import version
      9 from fontTools.misc.textTools import safeEval, pad
     10 from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect
     11 from fontTools.misc.bezierTools import calcQuadraticBounds
     12 from fontTools.misc.fixedTools import (
     13 	fixedToFloat as fi2fl,
     14 	floatToFixed as fl2fi,
     15 	otRound,
     16 )
     17 from numbers import Number
     18 from . import DefaultTable
     19 from . import ttProgram
     20 import sys
     21 import struct
     22 import array
     23 import logging
     24 import os
     25 from fontTools.misc import xmlWriter
     26 from fontTools.misc.filenames import userNameToFileName
     27 
     28 log = logging.getLogger(__name__)
     29 
     30 # We compute the version the same as is computed in ttlib/__init__
     31 # so that we can write 'ttLibVersion' attribute of the glyf TTX files
     32 # when glyf is written to separate files.
     33 version = ".".join(version.split('.')[:2])
     34 
     35 #
     36 # The Apple and MS rasterizers behave differently for
     37 # scaled composite components: one does scale first and then translate
     38 # and the other does it vice versa. MS defined some flags to indicate
     39 # the difference, but it seems nobody actually _sets_ those flags.
     40 #
     41 # Funny thing: Apple seems to _only_ do their thing in the
     42 # WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE
     43 # (eg. Charcoal)...
     44 #
     45 SCALE_COMPONENT_OFFSET_DEFAULT = 0   # 0 == MS, 1 == Apple
     46 
     47 
     48 class table__g_l_y_f(DefaultTable.DefaultTable):
     49 
     50 	# this attribute controls the amount of padding applied to glyph data upon compile.
     51 	# Glyph lenghts are aligned to multiples of the specified value. 
     52 	# Allowed values are (0, 1, 2, 4). '0' means no padding; '1' (default) also means
     53 	# no padding, except for when padding would allow to use short loca offsets.
     54 	padding = 1
     55 
     56 	def decompile(self, data, ttFont):
     57 		loca = ttFont['loca']
     58 		last = int(loca[0])
     59 		noname = 0
     60 		self.glyphs = {}
     61 		self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
     62 		for i in range(0, len(loca)-1):
     63 			try:
     64 				glyphName = glyphOrder[i]
     65 			except IndexError:
     66 				noname = noname + 1
     67 				glyphName = 'ttxautoglyph%s' % i
     68 			next = int(loca[i+1])
     69 			glyphdata = data[last:next]
     70 			if len(glyphdata) != (next - last):
     71 				raise ttLib.TTLibError("not enough 'glyf' table data")
     72 			glyph = Glyph(glyphdata)
     73 			self.glyphs[glyphName] = glyph
     74 			last = next
     75 		if len(data) - next >= 4:
     76 			log.warning(
     77 				"too much 'glyf' table data: expected %d, received %d bytes",
     78 				next, len(data))
     79 		if noname:
     80 			log.warning('%s glyphs have no name', noname)
     81 		if ttFont.lazy is False: # Be lazy for None and True
     82 			for glyph in self.glyphs.values():
     83 				glyph.expand(self)
     84 
     85 	def compile(self, ttFont):
     86 		if not hasattr(self, "glyphOrder"):
     87 			self.glyphOrder = ttFont.getGlyphOrder()
     88 		padding = self.padding
     89 		assert padding in (0, 1, 2, 4)
     90 		locations = []
     91 		currentLocation = 0
     92 		dataList = []
     93 		recalcBBoxes = ttFont.recalcBBoxes
     94 		for glyphName in self.glyphOrder:
     95 			glyph = self.glyphs[glyphName]
     96 			glyphData = glyph.compile(self, recalcBBoxes)
     97 			if padding > 1:
     98 				glyphData = pad(glyphData, size=padding)
     99 			locations.append(currentLocation)
    100 			currentLocation = currentLocation + len(glyphData)
    101 			dataList.append(glyphData)
    102 		locations.append(currentLocation)
    103 
    104 		if padding == 1 and currentLocation < 0x20000:
    105 			# See if we can pad any odd-lengthed glyphs to allow loca
    106 			# table to use the short offsets.
    107 			indices = [i for i,glyphData in enumerate(dataList) if len(glyphData) % 2 == 1]
    108 			if indices and currentLocation + len(indices) < 0x20000:
    109 				# It fits.  Do it.
    110 				for i in indices:
    111 					dataList[i] += b'\0'
    112 				currentLocation = 0
    113 				for i,glyphData in enumerate(dataList):
    114 					locations[i] = currentLocation
    115 					currentLocation += len(glyphData)
    116 				locations[len(dataList)] = currentLocation
    117 
    118 		data = bytesjoin(dataList)
    119 		if 'loca' in ttFont:
    120 			ttFont['loca'].set(locations)
    121 		if 'maxp' in ttFont:
    122 			ttFont['maxp'].numGlyphs = len(self.glyphs)
    123 		return data
    124 
    125 	def toXML(self, writer, ttFont, splitGlyphs=False):
    126 		notice = (
    127 			"The xMin, yMin, xMax and yMax values\n"
    128 			"will be recalculated by the compiler.")
    129 		glyphNames = ttFont.getGlyphNames()
    130 		if not splitGlyphs:
    131 			writer.newline()
    132 			writer.comment(notice)
    133 			writer.newline()
    134 			writer.newline()
    135 		numGlyphs = len(glyphNames)
    136 		if splitGlyphs:
    137 			path, ext = os.path.splitext(writer.file.name)
    138 			existingGlyphFiles = set()
    139 		for glyphName in glyphNames:
    140 			glyph = self[glyphName]
    141 			if glyph.numberOfContours:
    142 				if splitGlyphs:
    143 					glyphPath = userNameToFileName(
    144 						tounicode(glyphName, 'utf-8'),
    145 						existingGlyphFiles,
    146 						prefix=path + ".",
    147 						suffix=ext)
    148 					existingGlyphFiles.add(glyphPath.lower())
    149 					glyphWriter = xmlWriter.XMLWriter(
    150 						glyphPath, idlefunc=writer.idlefunc,
    151 						newlinestr=writer.newlinestr)
    152 					glyphWriter.begintag("ttFont", ttLibVersion=version)
    153 					glyphWriter.newline()
    154 					glyphWriter.begintag("glyf")
    155 					glyphWriter.newline()
    156 					glyphWriter.comment(notice)
    157 					glyphWriter.newline()
    158 					writer.simpletag("TTGlyph", src=os.path.basename(glyphPath))
    159 				else:
    160 					glyphWriter = writer
    161 				glyphWriter.begintag('TTGlyph', [
    162 							("name", glyphName),
    163 							("xMin", glyph.xMin),
    164 							("yMin", glyph.yMin),
    165 							("xMax", glyph.xMax),
    166 							("yMax", glyph.yMax),
    167 							])
    168 				glyphWriter.newline()
    169 				glyph.toXML(glyphWriter, ttFont)
    170 				glyphWriter.endtag('TTGlyph')
    171 				glyphWriter.newline()
    172 				if splitGlyphs:
    173 					glyphWriter.endtag("glyf")
    174 					glyphWriter.newline()
    175 					glyphWriter.endtag("ttFont")
    176 					glyphWriter.newline()
    177 					glyphWriter.close()
    178 			else:
    179 				writer.simpletag('TTGlyph', name=glyphName)
    180 				writer.comment("contains no outline data")
    181 				if not splitGlyphs:
    182 					writer.newline()
    183 			writer.newline()
    184 
    185 	def fromXML(self, name, attrs, content, ttFont):
    186 		if name != "TTGlyph":
    187 			return
    188 		if not hasattr(self, "glyphs"):
    189 			self.glyphs = {}
    190 		if not hasattr(self, "glyphOrder"):
    191 			self.glyphOrder = ttFont.getGlyphOrder()
    192 		glyphName = attrs["name"]
    193 		log.debug("unpacking glyph '%s'", glyphName)
    194 		glyph = Glyph()
    195 		for attr in ['xMin', 'yMin', 'xMax', 'yMax']:
    196 			setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
    197 		self.glyphs[glyphName] = glyph
    198 		for element in content:
    199 			if not isinstance(element, tuple):
    200 				continue
    201 			name, attrs, content = element
    202 			glyph.fromXML(name, attrs, content, ttFont)
    203 		if not ttFont.recalcBBoxes:
    204 			glyph.compact(self, 0)
    205 
    206 	def setGlyphOrder(self, glyphOrder):
    207 		self.glyphOrder = glyphOrder
    208 
    209 	def getGlyphName(self, glyphID):
    210 		return self.glyphOrder[glyphID]
    211 
    212 	def getGlyphID(self, glyphName):
    213 		# XXX optimize with reverse dict!!!
    214 		return self.glyphOrder.index(glyphName)
    215 
    216 	def removeHinting(self):
    217 		for glyph in self.glyphs.values():
    218 			glyph.removeHinting()
    219 
    220 	def keys(self):
    221 		return self.glyphs.keys()
    222 
    223 	def has_key(self, glyphName):
    224 		return glyphName in self.glyphs
    225 
    226 	__contains__ = has_key
    227 
    228 	def __getitem__(self, glyphName):
    229 		glyph = self.glyphs[glyphName]
    230 		glyph.expand(self)
    231 		return glyph
    232 
    233 	def __setitem__(self, glyphName, glyph):
    234 		self.glyphs[glyphName] = glyph
    235 		if glyphName not in self.glyphOrder:
    236 			self.glyphOrder.append(glyphName)
    237 
    238 	def __delitem__(self, glyphName):
    239 		del self.glyphs[glyphName]
    240 		self.glyphOrder.remove(glyphName)
    241 
    242 	def __len__(self):
    243 		assert len(self.glyphOrder) == len(self.glyphs)
    244 		return len(self.glyphs)
    245 
    246 
    247 glyphHeaderFormat = """
    248 		>	# big endian
    249 		numberOfContours:	h
    250 		xMin:				h
    251 		yMin:				h
    252 		xMax:				h
    253 		yMax:				h
    254 """
    255 
    256 # flags
    257 flagOnCurve = 0x01
    258 flagXShort = 0x02
    259 flagYShort = 0x04
    260 flagRepeat = 0x08
    261 flagXsame =  0x10
    262 flagYsame = 0x20
    263 flagOverlapSimple = 0x40
    264 flagReserved = 0x80
    265 
    266 # These flags are kept for XML output after decompiling the coordinates
    267 keepFlags = flagOnCurve + flagOverlapSimple
    268 
    269 _flagSignBytes = {
    270 	0: 2,
    271 	flagXsame: 0,
    272 	flagXShort|flagXsame: +1,
    273 	flagXShort: -1,
    274 	flagYsame: 0,
    275 	flagYShort|flagYsame: +1,
    276 	flagYShort: -1,
    277 }
    278 
    279 def flagBest(x, y, onCurve):
    280 	"""For a given x,y delta pair, returns the flag that packs this pair
    281 	most efficiently, as well as the number of byte cost of such flag."""
    282 
    283 	flag = flagOnCurve if onCurve else 0
    284 	cost = 0
    285 	# do x
    286 	if x == 0:
    287 		flag = flag | flagXsame
    288 	elif -255 <= x <= 255:
    289 		flag = flag | flagXShort
    290 		if x > 0:
    291 			flag = flag | flagXsame
    292 		cost += 1
    293 	else:
    294 		cost += 2
    295 	# do y
    296 	if y == 0:
    297 		flag = flag | flagYsame
    298 	elif -255 <= y <= 255:
    299 		flag = flag | flagYShort
    300 		if y > 0:
    301 			flag = flag | flagYsame
    302 		cost += 1
    303 	else:
    304 		cost += 2
    305 	return flag, cost
    306 
    307 def flagFits(newFlag, oldFlag, mask):
    308 	newBytes = _flagSignBytes[newFlag & mask]
    309 	oldBytes = _flagSignBytes[oldFlag & mask]
    310 	return newBytes == oldBytes or abs(newBytes) > abs(oldBytes)
    311 
    312 def flagSupports(newFlag, oldFlag):
    313 	return ((oldFlag & flagOnCurve) == (newFlag & flagOnCurve) and
    314 		flagFits(newFlag, oldFlag, flagXsame|flagXShort) and
    315 		flagFits(newFlag, oldFlag, flagYsame|flagYShort))
    316 
    317 def flagEncodeCoord(flag, mask, coord, coordBytes):
    318 	byteCount = _flagSignBytes[flag & mask]
    319 	if byteCount == 1:
    320 		coordBytes.append(coord)
    321 	elif byteCount == -1:
    322 		coordBytes.append(-coord)
    323 	elif byteCount == 2:
    324 		coordBytes.append((coord >> 8) & 0xFF)
    325 		coordBytes.append(coord & 0xFF)
    326 
    327 def flagEncodeCoords(flag, x, y, xBytes, yBytes):
    328 	flagEncodeCoord(flag, flagXsame|flagXShort, x, xBytes)
    329 	flagEncodeCoord(flag, flagYsame|flagYShort, y, yBytes)
    330 
    331 
    332 ARG_1_AND_2_ARE_WORDS		= 0x0001  # if set args are words otherwise they are bytes
    333 ARGS_ARE_XY_VALUES		= 0x0002  # if set args are xy values, otherwise they are points
    334 ROUND_XY_TO_GRID		= 0x0004  # for the xy values if above is true
    335 WE_HAVE_A_SCALE			= 0x0008  # Sx = Sy, otherwise scale == 1.0
    336 NON_OVERLAPPING			= 0x0010  # set to same value for all components (obsolete!)
    337 MORE_COMPONENTS			= 0x0020  # indicates at least one more glyph after this one
    338 WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040  # Sx, Sy
    339 WE_HAVE_A_TWO_BY_TWO		= 0x0080  # t00, t01, t10, t11
    340 WE_HAVE_INSTRUCTIONS		= 0x0100  # instructions follow
    341 USE_MY_METRICS			= 0x0200  # apply these metrics to parent glyph
    342 OVERLAP_COMPOUND		= 0x0400  # used by Apple in GX fonts
    343 SCALED_COMPONENT_OFFSET		= 0x0800  # composite designed to have the component offset scaled (designed for Apple)
    344 UNSCALED_COMPONENT_OFFSET	= 0x1000  # composite designed not to have the component offset scaled (designed for MS)
    345 
    346 
    347 CompositeMaxpValues = namedtuple('CompositeMaxpValues', ['nPoints', 'nContours', 'maxComponentDepth'])
    348 
    349 
    350 class Glyph(object):
    351 
    352 	def __init__(self, data=""):
    353 		if not data:
    354 			# empty char
    355 			self.numberOfContours = 0
    356 			return
    357 		self.data = data
    358 
    359 	def compact(self, glyfTable, recalcBBoxes=True):
    360 		data = self.compile(glyfTable, recalcBBoxes)
    361 		self.__dict__.clear()
    362 		self.data = data
    363 
    364 	def expand(self, glyfTable):
    365 		if not hasattr(self, "data"):
    366 			# already unpacked
    367 			return
    368 		if not self.data:
    369 			# empty char
    370 			del self.data
    371 			self.numberOfContours = 0
    372 			return
    373 		dummy, data = sstruct.unpack2(glyphHeaderFormat, self.data, self)
    374 		del self.data
    375 		# Some fonts (eg. Neirizi.ttf) have a 0 for numberOfContours in
    376 		# some glyphs; decompileCoordinates assumes that there's at least
    377 		# one, so short-circuit here.
    378 		if self.numberOfContours == 0:
    379 			return
    380 		if self.isComposite():
    381 			self.decompileComponents(data, glyfTable)
    382 		else:
    383 			self.decompileCoordinates(data)
    384 
    385 	def compile(self, glyfTable, recalcBBoxes=True):
    386 		if hasattr(self, "data"):
    387 			if recalcBBoxes:
    388 				# must unpack glyph in order to recalculate bounding box
    389 				self.expand(glyfTable)
    390 			else:
    391 				return self.data
    392 		if self.numberOfContours == 0:
    393 			return ""
    394 		if recalcBBoxes:
    395 			self.recalcBounds(glyfTable)
    396 		data = sstruct.pack(glyphHeaderFormat, self)
    397 		if self.isComposite():
    398 			data = data + self.compileComponents(glyfTable)
    399 		else:
    400 			data = data + self.compileCoordinates()
    401 		return data
    402 
    403 	def toXML(self, writer, ttFont):
    404 		if self.isComposite():
    405 			for compo in self.components:
    406 				compo.toXML(writer, ttFont)
    407 			haveInstructions = hasattr(self, "program")
    408 		else:
    409 			last = 0
    410 			for i in range(self.numberOfContours):
    411 				writer.begintag("contour")
    412 				writer.newline()
    413 				for j in range(last, self.endPtsOfContours[i] + 1):
    414 					attrs = [
    415 							("x", self.coordinates[j][0]),
    416 							("y", self.coordinates[j][1]),
    417 							("on", self.flags[j] & flagOnCurve),
    418 						]
    419 					if self.flags[j] & flagOverlapSimple:
    420 						# Apple's rasterizer uses flagOverlapSimple in the first contour/first pt to flag glyphs that contain overlapping contours
    421 						attrs.append(("overlap", 1))
    422 					writer.simpletag("pt", attrs)
    423 					writer.newline()
    424 				last = self.endPtsOfContours[i] + 1
    425 				writer.endtag("contour")
    426 				writer.newline()
    427 			haveInstructions = self.numberOfContours > 0
    428 		if haveInstructions:
    429 			if self.program:
    430 				writer.begintag("instructions")
    431 				writer.newline()
    432 				self.program.toXML(writer, ttFont)
    433 				writer.endtag("instructions")
    434 			else:
    435 				writer.simpletag("instructions")
    436 			writer.newline()
    437 
    438 	def fromXML(self, name, attrs, content, ttFont):
    439 		if name == "contour":
    440 			if self.numberOfContours < 0:
    441 				raise ttLib.TTLibError("can't mix composites and contours in glyph")
    442 			self.numberOfContours = self.numberOfContours + 1
    443 			coordinates = GlyphCoordinates()
    444 			flags = []
    445 			for element in content:
    446 				if not isinstance(element, tuple):
    447 					continue
    448 				name, attrs, content = element
    449 				if name != "pt":
    450 					continue  # ignore anything but "pt"
    451 				coordinates.append((safeEval(attrs["x"]), safeEval(attrs["y"])))
    452 				flag = not not safeEval(attrs["on"])
    453 				if "overlap" in attrs and bool(safeEval(attrs["overlap"])):
    454 					flag |= flagOverlapSimple
    455 				flags.append(flag)
    456 			flags = array.array("B", flags)
    457 			if not hasattr(self, "coordinates"):
    458 				self.coordinates = coordinates
    459 				self.flags = flags
    460 				self.endPtsOfContours = [len(coordinates)-1]
    461 			else:
    462 				self.coordinates.extend (coordinates)
    463 				self.flags.extend(flags)
    464 				self.endPtsOfContours.append(len(self.coordinates)-1)
    465 		elif name == "component":
    466 			if self.numberOfContours > 0:
    467 				raise ttLib.TTLibError("can't mix composites and contours in glyph")
    468 			self.numberOfContours = -1
    469 			if not hasattr(self, "components"):
    470 				self.components = []
    471 			component = GlyphComponent()
    472 			self.components.append(component)
    473 			component.fromXML(name, attrs, content, ttFont)
    474 		elif name == "instructions":
    475 			self.program = ttProgram.Program()
    476 			for element in content:
    477 				if not isinstance(element, tuple):
    478 					continue
    479 				name, attrs, content = element
    480 				self.program.fromXML(name, attrs, content, ttFont)
    481 
    482 	def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
    483 		assert self.isComposite()
    484 		nContours = 0
    485 		nPoints = 0
    486 		for compo in self.components:
    487 			baseGlyph = glyfTable[compo.glyphName]
    488 			if baseGlyph.numberOfContours == 0:
    489 				continue
    490 			elif baseGlyph.numberOfContours > 0:
    491 				nP, nC = baseGlyph.getMaxpValues()
    492 			else:
    493 				nP, nC, maxComponentDepth = baseGlyph.getCompositeMaxpValues(
    494 						glyfTable, maxComponentDepth + 1)
    495 			nPoints = nPoints + nP
    496 			nContours = nContours + nC
    497 		return CompositeMaxpValues(nPoints, nContours, maxComponentDepth)
    498 
    499 	def getMaxpValues(self):
    500 		assert self.numberOfContours > 0
    501 		return len(self.coordinates), len(self.endPtsOfContours)
    502 
    503 	def decompileComponents(self, data, glyfTable):
    504 		self.components = []
    505 		more = 1
    506 		haveInstructions = 0
    507 		while more:
    508 			component = GlyphComponent()
    509 			more, haveInstr, data = component.decompile(data, glyfTable)
    510 			haveInstructions = haveInstructions | haveInstr
    511 			self.components.append(component)
    512 		if haveInstructions:
    513 			numInstructions, = struct.unpack(">h", data[:2])
    514 			data = data[2:]
    515 			self.program = ttProgram.Program()
    516 			self.program.fromBytecode(data[:numInstructions])
    517 			data = data[numInstructions:]
    518 			if len(data) >= 4:
    519 				log.warning(
    520 					"too much glyph data at the end of composite glyph: %d excess bytes",
    521 					len(data))
    522 
    523 	def decompileCoordinates(self, data):
    524 		endPtsOfContours = array.array("h")
    525 		endPtsOfContours.fromstring(data[:2*self.numberOfContours])
    526 		if sys.byteorder != "big": endPtsOfContours.byteswap()
    527 		self.endPtsOfContours = endPtsOfContours.tolist()
    528 
    529 		data = data[2*self.numberOfContours:]
    530 
    531 		instructionLength, = struct.unpack(">h", data[:2])
    532 		data = data[2:]
    533 		self.program = ttProgram.Program()
    534 		self.program.fromBytecode(data[:instructionLength])
    535 		data = data[instructionLength:]
    536 		nCoordinates = self.endPtsOfContours[-1] + 1
    537 		flags, xCoordinates, yCoordinates = \
    538 				self.decompileCoordinatesRaw(nCoordinates, data)
    539 
    540 		# fill in repetitions and apply signs
    541 		self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates)
    542 		xIndex = 0
    543 		yIndex = 0
    544 		for i in range(nCoordinates):
    545 			flag = flags[i]
    546 			# x coordinate
    547 			if flag & flagXShort:
    548 				if flag & flagXsame:
    549 					x = xCoordinates[xIndex]
    550 				else:
    551 					x = -xCoordinates[xIndex]
    552 				xIndex = xIndex + 1
    553 			elif flag & flagXsame:
    554 				x = 0
    555 			else:
    556 				x = xCoordinates[xIndex]
    557 				xIndex = xIndex + 1
    558 			# y coordinate
    559 			if flag & flagYShort:
    560 				if flag & flagYsame:
    561 					y = yCoordinates[yIndex]
    562 				else:
    563 					y = -yCoordinates[yIndex]
    564 				yIndex = yIndex + 1
    565 			elif flag & flagYsame:
    566 				y = 0
    567 			else:
    568 				y = yCoordinates[yIndex]
    569 				yIndex = yIndex + 1
    570 			coordinates[i] = (x, y)
    571 		assert xIndex == len(xCoordinates)
    572 		assert yIndex == len(yCoordinates)
    573 		coordinates.relativeToAbsolute()
    574 		# discard all flags except "keepFlags"
    575 		self.flags = array.array("B", (f & keepFlags for f in flags))
    576 
    577 	def decompileCoordinatesRaw(self, nCoordinates, data):
    578 		# unpack flags and prepare unpacking of coordinates
    579 		flags = array.array("B", [0] * nCoordinates)
    580 		# Warning: deep Python trickery going on. We use the struct module to unpack
    581 		# the coordinates. We build a format string based on the flags, so we can
    582 		# unpack the coordinates in one struct.unpack() call.
    583 		xFormat = ">" # big endian
    584 		yFormat = ">" # big endian
    585 		i = j = 0
    586 		while True:
    587 			flag = byteord(data[i])
    588 			i = i + 1
    589 			repeat = 1
    590 			if flag & flagRepeat:
    591 				repeat = byteord(data[i]) + 1
    592 				i = i + 1
    593 			for k in range(repeat):
    594 				if flag & flagXShort:
    595 					xFormat = xFormat + 'B'
    596 				elif not (flag & flagXsame):
    597 					xFormat = xFormat + 'h'
    598 				if flag & flagYShort:
    599 					yFormat = yFormat + 'B'
    600 				elif not (flag & flagYsame):
    601 					yFormat = yFormat + 'h'
    602 				flags[j] = flag
    603 				j = j + 1
    604 			if j >= nCoordinates:
    605 				break
    606 		assert j == nCoordinates, "bad glyph flags"
    607 		data = data[i:]
    608 		# unpack raw coordinates, krrrrrr-tching!
    609 		xDataLen = struct.calcsize(xFormat)
    610 		yDataLen = struct.calcsize(yFormat)
    611 		if len(data) - (xDataLen + yDataLen) >= 4:
    612 			log.warning(
    613 				"too much glyph data: %d excess bytes", len(data) - (xDataLen + yDataLen))
    614 		xCoordinates = struct.unpack(xFormat, data[:xDataLen])
    615 		yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen])
    616 		return flags, xCoordinates, yCoordinates
    617 
    618 	def compileComponents(self, glyfTable):
    619 		data = b""
    620 		lastcomponent = len(self.components) - 1
    621 		more = 1
    622 		haveInstructions = 0
    623 		for i in range(len(self.components)):
    624 			if i == lastcomponent:
    625 				haveInstructions = hasattr(self, "program")
    626 				more = 0
    627 			compo = self.components[i]
    628 			data = data + compo.compile(more, haveInstructions, glyfTable)
    629 		if haveInstructions:
    630 			instructions = self.program.getBytecode()
    631 			data = data + struct.pack(">h", len(instructions)) + instructions
    632 		return data
    633 
    634 	def compileCoordinates(self):
    635 		assert len(self.coordinates) == len(self.flags)
    636 		data = []
    637 		endPtsOfContours = array.array("h", self.endPtsOfContours)
    638 		if sys.byteorder != "big": endPtsOfContours.byteswap()
    639 		data.append(endPtsOfContours.tostring())
    640 		instructions = self.program.getBytecode()
    641 		data.append(struct.pack(">h", len(instructions)))
    642 		data.append(instructions)
    643 
    644 		deltas = self.coordinates.copy()
    645 		if deltas.isFloat():
    646 			# Warn?
    647 			deltas.toInt()
    648 		deltas.absoluteToRelative()
    649 
    650 		# TODO(behdad): Add a configuration option for this?
    651 		deltas = self.compileDeltasGreedy(self.flags, deltas)
    652 		#deltas = self.compileDeltasOptimal(self.flags, deltas)
    653 
    654 		data.extend(deltas)
    655 		return bytesjoin(data)
    656 
    657 	def compileDeltasGreedy(self, flags, deltas):
    658 		# Implements greedy algorithm for packing coordinate deltas:
    659 		# uses shortest representation one coordinate at a time.
    660 		compressedflags = []
    661 		xPoints = []
    662 		yPoints = []
    663 		lastflag = None
    664 		repeat = 0
    665 		for flag,(x,y) in zip(flags, deltas):
    666 			# Oh, the horrors of TrueType
    667 			# do x
    668 			if x == 0:
    669 				flag = flag | flagXsame
    670 			elif -255 <= x <= 255:
    671 				flag = flag | flagXShort
    672 				if x > 0:
    673 					flag = flag | flagXsame
    674 				else:
    675 					x = -x
    676 				xPoints.append(bytechr(x))
    677 			else:
    678 				xPoints.append(struct.pack(">h", x))
    679 			# do y
    680 			if y == 0:
    681 				flag = flag | flagYsame
    682 			elif -255 <= y <= 255:
    683 				flag = flag | flagYShort
    684 				if y > 0:
    685 					flag = flag | flagYsame
    686 				else:
    687 					y = -y
    688 				yPoints.append(bytechr(y))
    689 			else:
    690 				yPoints.append(struct.pack(">h", y))
    691 			# handle repeating flags
    692 			if flag == lastflag and repeat != 255:
    693 				repeat = repeat + 1
    694 				if repeat == 1:
    695 					compressedflags.append(flag)
    696 				else:
    697 					compressedflags[-2] = flag | flagRepeat
    698 					compressedflags[-1] = repeat
    699 			else:
    700 				repeat = 0
    701 				compressedflags.append(flag)
    702 			lastflag = flag
    703 		compressedFlags = array.array("B", compressedflags).tostring()
    704 		compressedXs = bytesjoin(xPoints)
    705 		compressedYs = bytesjoin(yPoints)
    706 		return (compressedFlags, compressedXs, compressedYs)
    707 
    708 	def compileDeltasOptimal(self, flags, deltas):
    709 		# Implements optimal, dynaic-programming, algorithm for packing coordinate
    710 		# deltas.  The savings are negligible :(.
    711 		candidates = []
    712 		bestTuple = None
    713 		bestCost = 0
    714 		repeat = 0
    715 		for flag,(x,y) in zip(flags, deltas):
    716 			# Oh, the horrors of TrueType
    717 			flag, coordBytes = flagBest(x, y, flag)
    718 			bestCost += 1 + coordBytes
    719 			newCandidates = [(bestCost, bestTuple, flag, coordBytes),
    720 							(bestCost+1, bestTuple, (flag|flagRepeat), coordBytes)]
    721 			for lastCost,lastTuple,lastFlag,coordBytes in candidates:
    722 				if lastCost + coordBytes <= bestCost + 1 and (lastFlag & flagRepeat) and (lastFlag < 0xff00) and flagSupports(lastFlag, flag):
    723 					if (lastFlag & 0xFF) == (flag|flagRepeat) and lastCost == bestCost + 1:
    724 						continue
    725 					newCandidates.append((lastCost + coordBytes, lastTuple, lastFlag+256, coordBytes))
    726 			candidates = newCandidates
    727 			bestTuple = min(candidates, key=lambda t:t[0])
    728 			bestCost = bestTuple[0]
    729 
    730 		flags = []
    731 		while bestTuple:
    732 			cost, bestTuple, flag, coordBytes = bestTuple
    733 			flags.append(flag)
    734 		flags.reverse()
    735 
    736 		compressedFlags = array.array("B")
    737 		compressedXs = array.array("B")
    738 		compressedYs = array.array("B")
    739 		coords = iter(deltas)
    740 		ff = []
    741 		for flag in flags:
    742 			repeatCount, flag = flag >> 8, flag & 0xFF
    743 			compressedFlags.append(flag)
    744 			if flag & flagRepeat:
    745 				assert(repeatCount > 0)
    746 				compressedFlags.append(repeatCount)
    747 			else:
    748 				assert(repeatCount == 0)
    749 			for i in range(1 + repeatCount):
    750 				x,y = next(coords)
    751 				flagEncodeCoords(flag, x, y, compressedXs, compressedYs)
    752 				ff.append(flag)
    753 		try:
    754 			next(coords)
    755 			raise Exception("internal error")
    756 		except StopIteration:
    757 			pass
    758 		compressedFlags = compressedFlags.tostring()
    759 		compressedXs = compressedXs.tostring()
    760 		compressedYs = compressedYs.tostring()
    761 
    762 		return (compressedFlags, compressedXs, compressedYs)
    763 
    764 	def recalcBounds(self, glyfTable):
    765 		coords, endPts, flags = self.getCoordinates(glyfTable)
    766 		if len(coords) > 0:
    767 			if 0:
    768 				# This branch calculates exact glyph outline bounds
    769 				# analytically, handling cases without on-curve
    770 				# extremas, etc.  However, the glyf table header
    771 				# simply says that the bounds should be min/max x/y
    772 				# "for coordinate data", so I suppose that means no
    773 				# fancy thing here, just get extremas of all coord
    774 				# points (on and off).  As such, this branch is
    775 				# disabled.
    776 
    777 				# Collect on-curve points
    778 				onCurveCoords = [coords[j] for j in range(len(coords))
    779 								if flags[j] & flagOnCurve]
    780 				# Add implicit on-curve points
    781 				start = 0
    782 				for end in endPts:
    783 					last = end
    784 					for j in range(start, end + 1):
    785 						if not ((flags[j] | flags[last]) & flagOnCurve):
    786 							x = (coords[last][0] + coords[j][0]) / 2
    787 							y = (coords[last][1] + coords[j][1]) / 2
    788 							onCurveCoords.append((x,y))
    789 						last = j
    790 					start = end + 1
    791 				# Add bounds for curves without an explicit extrema
    792 				start = 0
    793 				for end in endPts:
    794 					last = end
    795 					for j in range(start, end + 1):
    796 						if not (flags[j] & flagOnCurve):
    797 							next = j + 1 if j < end else start
    798 							bbox = calcBounds([coords[last], coords[next]])
    799 							if not pointInRect(coords[j], bbox):
    800 								# Ouch!
    801 								log.warning("Outline has curve with implicit extrema.")
    802 								# Ouch!  Find analytical curve bounds.
    803 								pthis = coords[j]
    804 								plast = coords[last]
    805 								if not (flags[last] & flagOnCurve):
    806 									plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
    807 								pnext = coords[next]
    808 								if not (flags[next] & flagOnCurve):
    809 									pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
    810 								bbox = calcQuadraticBounds(plast, pthis, pnext)
    811 								onCurveCoords.append((bbox[0],bbox[1]))
    812 								onCurveCoords.append((bbox[2],bbox[3]))
    813 						last = j
    814 					start = end + 1
    815 
    816 				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
    817 			else:
    818 				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
    819 		else:
    820 			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
    821 
    822 	def isComposite(self):
    823 		"""Can be called on compact or expanded glyph."""
    824 		if hasattr(self, "data") and self.data:
    825 			return struct.unpack(">h", self.data[:2])[0] == -1
    826 		else:
    827 			return self.numberOfContours == -1
    828 
    829 	def __getitem__(self, componentIndex):
    830 		if not self.isComposite():
    831 			raise ttLib.TTLibError("can't use glyph as sequence")
    832 		return self.components[componentIndex]
    833 
    834 	def getCoordinates(self, glyfTable):
    835 		if self.numberOfContours > 0:
    836 			return self.coordinates, self.endPtsOfContours, self.flags
    837 		elif self.isComposite():
    838 			# it's a composite
    839 			allCoords = GlyphCoordinates()
    840 			allFlags = array.array("B")
    841 			allEndPts = []
    842 			for compo in self.components:
    843 				g = glyfTable[compo.glyphName]
    844 				try:
    845 					coordinates, endPts, flags = g.getCoordinates(glyfTable)
    846 				except RecursionError:
    847 					raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName)
    848 				if hasattr(compo, "firstPt"):
    849 					# move according to two reference points
    850 					x1,y1 = allCoords[compo.firstPt]
    851 					x2,y2 = coordinates[compo.secondPt]
    852 					move = x1-x2, y1-y2
    853 				else:
    854 					move = compo.x, compo.y
    855 
    856 				coordinates = GlyphCoordinates(coordinates)
    857 				if not hasattr(compo, "transform"):
    858 					coordinates.translate(move)
    859 				else:
    860 					apple_way = compo.flags & SCALED_COMPONENT_OFFSET
    861 					ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
    862 					assert not (apple_way and ms_way)
    863 					if not (apple_way or ms_way):
    864 						scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
    865 					else:
    866 						scale_component_offset = apple_way
    867 					if scale_component_offset:
    868 						# the Apple way: first move, then scale (ie. scale the component offset)
    869 						coordinates.translate(move)
    870 						coordinates.transform(compo.transform)
    871 					else:
    872 						# the MS way: first scale, then move
    873 						coordinates.transform(compo.transform)
    874 						coordinates.translate(move)
    875 				offset = len(allCoords)
    876 				allEndPts.extend(e + offset for e in endPts)
    877 				allCoords.extend(coordinates)
    878 				allFlags.extend(flags)
    879 			return allCoords, allEndPts, allFlags
    880 		else:
    881 			return GlyphCoordinates(), [], array.array("B")
    882 
    883 	def getComponentNames(self, glyfTable):
    884 		if not hasattr(self, "data"):
    885 			if self.isComposite():
    886 				return [c.glyphName for c in self.components]
    887 			else:
    888 				return []
    889 
    890 		# Extract components without expanding glyph
    891 
    892 		if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
    893 			return []  # Not composite
    894 
    895 		data = self.data
    896 		i = 10
    897 		components = []
    898 		more = 1
    899 		while more:
    900 			flags, glyphID = struct.unpack(">HH", data[i:i+4])
    901 			i += 4
    902 			flags = int(flags)
    903 			components.append(glyfTable.getGlyphName(int(glyphID)))
    904 
    905 			if flags & ARG_1_AND_2_ARE_WORDS: i += 4
    906 			else: i += 2
    907 			if flags & WE_HAVE_A_SCALE: i += 2
    908 			elif flags & WE_HAVE_AN_X_AND_Y_SCALE: i += 4
    909 			elif flags & WE_HAVE_A_TWO_BY_TWO: i += 8
    910 			more = flags & MORE_COMPONENTS
    911 
    912 		return components
    913 
    914 	def trim(self, remove_hinting=False):
    915 		""" Remove padding and, if requested, hinting, from a glyph.
    916 			This works on both expanded and compacted glyphs, without
    917 			expanding it."""
    918 		if not hasattr(self, "data"):
    919 			if remove_hinting:
    920 				self.program = ttProgram.Program()
    921 				self.program.fromBytecode([])
    922 			# No padding to trim.
    923 			return
    924 		if not self.data:
    925 			return
    926 		numContours = struct.unpack(">h", self.data[:2])[0]
    927 		data = array.array("B", self.data)
    928 		i = 10
    929 		if numContours >= 0:
    930 			i += 2 * numContours # endPtsOfContours
    931 			nCoordinates = ((data[i-2] << 8) | data[i-1]) + 1
    932 			instructionLen = (data[i] << 8) | data[i+1]
    933 			if remove_hinting:
    934 				# Zero instruction length
    935 				data[i] = data [i+1] = 0
    936 				i += 2
    937 				if instructionLen:
    938 					# Splice it out
    939 					data = data[:i] + data[i+instructionLen:]
    940 				instructionLen = 0
    941 			else:
    942 				i += 2 + instructionLen
    943 
    944 			coordBytes = 0
    945 			j = 0
    946 			while True:
    947 				flag = data[i]
    948 				i = i + 1
    949 				repeat = 1
    950 				if flag & flagRepeat:
    951 					repeat = data[i] + 1
    952 					i = i + 1
    953 				xBytes = yBytes = 0
    954 				if flag & flagXShort:
    955 					xBytes = 1
    956 				elif not (flag & flagXsame):
    957 					xBytes = 2
    958 				if flag & flagYShort:
    959 					yBytes = 1
    960 				elif not (flag & flagYsame):
    961 					yBytes = 2
    962 				coordBytes += (xBytes + yBytes) * repeat
    963 				j += repeat
    964 				if j >= nCoordinates:
    965 					break
    966 			assert j == nCoordinates, "bad glyph flags"
    967 			i += coordBytes
    968 			# Remove padding
    969 			data = data[:i]
    970 		else:
    971 			more = 1
    972 			we_have_instructions = False
    973 			while more:
    974 				flags =(data[i] << 8) | data[i+1]
    975 				if remove_hinting:
    976 					flags &= ~WE_HAVE_INSTRUCTIONS
    977 				if flags & WE_HAVE_INSTRUCTIONS:
    978 					we_have_instructions = True
    979 				data[i+0] = flags >> 8
    980 				data[i+1] = flags & 0xFF
    981 				i += 4
    982 				flags = int(flags)
    983 
    984 				if flags & ARG_1_AND_2_ARE_WORDS: i += 4
    985 				else: i += 2
    986 				if flags & WE_HAVE_A_SCALE: i += 2
    987 				elif flags & WE_HAVE_AN_X_AND_Y_SCALE: i += 4
    988 				elif flags & WE_HAVE_A_TWO_BY_TWO: i += 8
    989 				more = flags & MORE_COMPONENTS
    990 			if we_have_instructions:
    991 				instructionLen = (data[i] << 8) | data[i+1]
    992 				i += 2 + instructionLen
    993 			# Remove padding
    994 			data = data[:i]
    995 
    996 		self.data = data.tostring()
    997 
    998 	def removeHinting(self):
    999 		self.trim (remove_hinting=True)
   1000 
   1001 	def draw(self, pen, glyfTable, offset=0):
   1002 
   1003 		if self.isComposite():
   1004 			for component in self.components:
   1005 				glyphName, transform = component.getComponentInfo()
   1006 				pen.addComponent(glyphName, transform)
   1007 			return
   1008 
   1009 		coordinates, endPts, flags = self.getCoordinates(glyfTable)
   1010 		if offset:
   1011 			coordinates = coordinates.copy()
   1012 			coordinates.translate((offset, 0))
   1013 		start = 0
   1014 		for end in endPts:
   1015 			end = end + 1
   1016 			contour = coordinates[start:end]
   1017 			cFlags = flags[start:end]
   1018 			start = end
   1019 			if 1 not in cFlags:
   1020 				# There is not a single on-curve point on the curve,
   1021 				# use pen.qCurveTo's special case by specifying None
   1022 				# as the on-curve point.
   1023 				contour.append(None)
   1024 				pen.qCurveTo(*contour)
   1025 			else:
   1026 				# Shuffle the points so that contour the is guaranteed
   1027 				# to *end* in an on-curve point, which we'll use for
   1028 				# the moveTo.
   1029 				firstOnCurve = cFlags.index(1) + 1
   1030 				contour = contour[firstOnCurve:] + contour[:firstOnCurve]
   1031 				cFlags = cFlags[firstOnCurve:] + cFlags[:firstOnCurve]
   1032 				pen.moveTo(contour[-1])
   1033 				while contour:
   1034 					nextOnCurve = cFlags.index(1) + 1
   1035 					if nextOnCurve == 1:
   1036 						pen.lineTo(contour[0])
   1037 					else:
   1038 						pen.qCurveTo(*contour[:nextOnCurve])
   1039 					contour = contour[nextOnCurve:]
   1040 					cFlags = cFlags[nextOnCurve:]
   1041 			pen.closePath()
   1042 
   1043 	def drawPoints(self, pen, glyfTable, offset=0):
   1044 		"""Draw the glyph using the supplied pointPen. Opposed to Glyph.draw(),
   1045 		this will not change the point indices.
   1046 		"""
   1047 
   1048 		if self.isComposite():
   1049 			for component in self.components:
   1050 				glyphName, transform = component.getComponentInfo()
   1051 				pen.addComponent(glyphName, transform)
   1052 			return
   1053 
   1054 		coordinates, endPts, flags = self.getCoordinates(glyfTable)
   1055 		if offset:
   1056 			coordinates = coordinates.copy()
   1057 			coordinates.translate((offset, 0))
   1058 		start = 0
   1059 		for end in endPts:
   1060 			end = end + 1
   1061 			contour = coordinates[start:end]
   1062 			cFlags = flags[start:end]
   1063 			start = end
   1064 			pen.beginPath()
   1065 			# Start with the appropriate segment type based on the final segment
   1066 			segmentType = "line" if cFlags[-1] == 1 else "qcurve"
   1067 			for i, pt in enumerate(contour):
   1068 				if cFlags[i] == 1:
   1069 					pen.addPoint(pt, segmentType=segmentType)
   1070 					segmentType = "line"
   1071 				else:
   1072 					pen.addPoint(pt)
   1073 					segmentType = "qcurve"
   1074 			pen.endPath()
   1075 
   1076 	def __eq__(self, other):
   1077 		if type(self) != type(other):
   1078 			return NotImplemented
   1079 		return self.__dict__ == other.__dict__
   1080 
   1081 	def __ne__(self, other):
   1082 		result = self.__eq__(other)
   1083 		return result if result is NotImplemented else not result
   1084 
   1085 class GlyphComponent(object):
   1086 
   1087 	def __init__(self):
   1088 		pass
   1089 
   1090 	def getComponentInfo(self):
   1091 		"""Return the base glyph name and a transform."""
   1092 		# XXX Ignoring self.firstPt & self.lastpt for now: I need to implement
   1093 		# something equivalent in fontTools.objects.glyph (I'd rather not
   1094 		# convert it to an absolute offset, since it is valuable information).
   1095 		# This method will now raise "AttributeError: x" on glyphs that use
   1096 		# this TT feature.
   1097 		if hasattr(self, "transform"):
   1098 			[[xx, xy], [yx, yy]] = self.transform
   1099 			trans = (xx, xy, yx, yy, self.x, self.y)
   1100 		else:
   1101 			trans = (1, 0, 0, 1, self.x, self.y)
   1102 		return self.glyphName, trans
   1103 
   1104 	def decompile(self, data, glyfTable):
   1105 		flags, glyphID = struct.unpack(">HH", data[:4])
   1106 		self.flags = int(flags)
   1107 		glyphID = int(glyphID)
   1108 		self.glyphName = glyfTable.getGlyphName(int(glyphID))
   1109 		data = data[4:]
   1110 
   1111 		if self.flags & ARG_1_AND_2_ARE_WORDS:
   1112 			if self.flags & ARGS_ARE_XY_VALUES:
   1113 				self.x, self.y = struct.unpack(">hh", data[:4])
   1114 			else:
   1115 				x, y = struct.unpack(">HH", data[:4])
   1116 				self.firstPt, self.secondPt = int(x), int(y)
   1117 			data = data[4:]
   1118 		else:
   1119 			if self.flags & ARGS_ARE_XY_VALUES:
   1120 				self.x, self.y = struct.unpack(">bb", data[:2])
   1121 			else:
   1122 				x, y = struct.unpack(">BB", data[:2])
   1123 				self.firstPt, self.secondPt = int(x), int(y)
   1124 			data = data[2:]
   1125 
   1126 		if self.flags & WE_HAVE_A_SCALE:
   1127 			scale, = struct.unpack(">h", data[:2])
   1128 			self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]]  # fixed 2.14
   1129 			data = data[2:]
   1130 		elif self.flags & WE_HAVE_AN_X_AND_Y_SCALE:
   1131 			xscale, yscale = struct.unpack(">hh", data[:4])
   1132 			self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]]  # fixed 2.14
   1133 			data = data[4:]
   1134 		elif self.flags & WE_HAVE_A_TWO_BY_TWO:
   1135 			(xscale, scale01,
   1136 					scale10, yscale) = struct.unpack(">hhhh", data[:8])
   1137 			self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)],
   1138 							[fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
   1139 			data = data[8:]
   1140 		more = self.flags & MORE_COMPONENTS
   1141 		haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
   1142 		self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
   1143 				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
   1144 				NON_OVERLAPPING | OVERLAP_COMPOUND)
   1145 		return more, haveInstructions, data
   1146 
   1147 	def compile(self, more, haveInstructions, glyfTable):
   1148 		data = b""
   1149 
   1150 		# reset all flags we will calculate ourselves
   1151 		flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
   1152 				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
   1153 				NON_OVERLAPPING | OVERLAP_COMPOUND)
   1154 		if more:
   1155 			flags = flags | MORE_COMPONENTS
   1156 		if haveInstructions:
   1157 			flags = flags | WE_HAVE_INSTRUCTIONS
   1158 
   1159 		if hasattr(self, "firstPt"):
   1160 			if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255):
   1161 				data = data + struct.pack(">BB", self.firstPt, self.secondPt)
   1162 			else:
   1163 				data = data + struct.pack(">HH", self.firstPt, self.secondPt)
   1164 				flags = flags | ARG_1_AND_2_ARE_WORDS
   1165 		else:
   1166 			x = otRound(self.x)
   1167 			y = otRound(self.y)
   1168 			flags = flags | ARGS_ARE_XY_VALUES
   1169 			if (-128 <= x <= 127) and (-128 <= y <= 127):
   1170 				data = data + struct.pack(">bb", x, y)
   1171 			else:
   1172 				data = data + struct.pack(">hh", x, y)
   1173 				flags = flags | ARG_1_AND_2_ARE_WORDS
   1174 
   1175 		if hasattr(self, "transform"):
   1176 			transform = [[fl2fi(x,14) for x in row] for row in self.transform]
   1177 			if transform[0][1] or transform[1][0]:
   1178 				flags = flags | WE_HAVE_A_TWO_BY_TWO
   1179 				data = data + struct.pack(">hhhh",
   1180 						transform[0][0], transform[0][1],
   1181 						transform[1][0], transform[1][1])
   1182 			elif transform[0][0] != transform[1][1]:
   1183 				flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
   1184 				data = data + struct.pack(">hh",
   1185 						transform[0][0], transform[1][1])
   1186 			else:
   1187 				flags = flags | WE_HAVE_A_SCALE
   1188 				data = data + struct.pack(">h",
   1189 						transform[0][0])
   1190 
   1191 		glyphID = glyfTable.getGlyphID(self.glyphName)
   1192 		return struct.pack(">HH", flags, glyphID) + data
   1193 
   1194 	def toXML(self, writer, ttFont):
   1195 		attrs = [("glyphName", self.glyphName)]
   1196 		if not hasattr(self, "firstPt"):
   1197 			attrs = attrs + [("x", self.x), ("y", self.y)]
   1198 		else:
   1199 			attrs = attrs + [("firstPt", self.firstPt), ("secondPt", self.secondPt)]
   1200 
   1201 		if hasattr(self, "transform"):
   1202 			transform = self.transform
   1203 			if transform[0][1] or transform[1][0]:
   1204 				attrs = attrs + [
   1205 						("scalex", transform[0][0]), ("scale01", transform[0][1]),
   1206 						("scale10", transform[1][0]), ("scaley", transform[1][1]),
   1207 						]
   1208 			elif transform[0][0] != transform[1][1]:
   1209 				attrs = attrs + [
   1210 						("scalex", transform[0][0]), ("scaley", transform[1][1]),
   1211 						]
   1212 			else:
   1213 				attrs = attrs + [("scale", transform[0][0])]
   1214 		attrs = attrs + [("flags", hex(self.flags))]
   1215 		writer.simpletag("component", attrs)
   1216 		writer.newline()
   1217 
   1218 	def fromXML(self, name, attrs, content, ttFont):
   1219 		self.glyphName = attrs["glyphName"]
   1220 		if "firstPt" in attrs:
   1221 			self.firstPt = safeEval(attrs["firstPt"])
   1222 			self.secondPt = safeEval(attrs["secondPt"])
   1223 		else:
   1224 			self.x = safeEval(attrs["x"])
   1225 			self.y = safeEval(attrs["y"])
   1226 		if "scale01" in attrs:
   1227 			scalex = safeEval(attrs["scalex"])
   1228 			scale01 = safeEval(attrs["scale01"])
   1229 			scale10 = safeEval(attrs["scale10"])
   1230 			scaley = safeEval(attrs["scaley"])
   1231 			self.transform = [[scalex, scale01], [scale10, scaley]]
   1232 		elif "scalex" in attrs:
   1233 			scalex = safeEval(attrs["scalex"])
   1234 			scaley = safeEval(attrs["scaley"])
   1235 			self.transform = [[scalex, 0], [0, scaley]]
   1236 		elif "scale" in attrs:
   1237 			scale = safeEval(attrs["scale"])
   1238 			self.transform = [[scale, 0], [0, scale]]
   1239 		self.flags = safeEval(attrs["flags"])
   1240 
   1241 	def __eq__(self, other):
   1242 		if type(self) != type(other):
   1243 			return NotImplemented
   1244 		return self.__dict__ == other.__dict__
   1245 
   1246 	def __ne__(self, other):
   1247 		result = self.__eq__(other)
   1248 		return result if result is NotImplemented else not result
   1249 
   1250 class GlyphCoordinates(object):
   1251 
   1252 	def __init__(self, iterable=[], typecode="h"):
   1253 		self._a = array.array(typecode)
   1254 		self.extend(iterable)
   1255 
   1256 	@property
   1257 	def array(self):
   1258 		return self._a
   1259 
   1260 	def isFloat(self):
   1261 		return self._a.typecode == 'd'
   1262 
   1263 	def _ensureFloat(self):
   1264 		if self.isFloat():
   1265 			return
   1266 		# The conversion to list() is to work around Jython bug
   1267 		self._a = array.array("d", list(self._a))
   1268 
   1269 	def _checkFloat(self, p):
   1270 		if self.isFloat():
   1271 			return p
   1272 		if any(v > 0x7FFF or v < -0x8000 for v in p):
   1273 			self._ensureFloat()
   1274 			return p
   1275 		if any(isinstance(v, float) for v in p):
   1276 			p = [int(v) if int(v) == v else v for v in p]
   1277 			if any(isinstance(v, float) for v in p):
   1278 				self._ensureFloat()
   1279 		return p
   1280 
   1281 	@staticmethod
   1282 	def zeros(count):
   1283 		return GlyphCoordinates([(0,0)] * count)
   1284 
   1285 	def copy(self):
   1286 		c = GlyphCoordinates(typecode=self._a.typecode)
   1287 		c._a.extend(self._a)
   1288 		return c
   1289 
   1290 	def __len__(self):
   1291 		return len(self._a) // 2
   1292 
   1293 	def __getitem__(self, k):
   1294 		if isinstance(k, slice):
   1295 			indices = range(*k.indices(len(self)))
   1296 			return [self[i] for i in indices]
   1297 		return self._a[2*k],self._a[2*k+1]
   1298 
   1299 	def __setitem__(self, k, v):
   1300 		if isinstance(k, slice):
   1301 			indices = range(*k.indices(len(self)))
   1302 			# XXX This only works if len(v) == len(indices)
   1303 			for j,i in enumerate(indices):
   1304 				self[i] = v[j]
   1305 			return
   1306 		v = self._checkFloat(v)
   1307 		self._a[2*k],self._a[2*k+1] = v
   1308 
   1309 	def __delitem__(self, i):
   1310 		i = (2*i) % len(self._a)
   1311 		del self._a[i]
   1312 		del self._a[i]
   1313 
   1314 	def __repr__(self):
   1315 		return 'GlyphCoordinates(['+','.join(str(c) for c in self)+'])'
   1316 
   1317 	def append(self, p):
   1318 		p = self._checkFloat(p)
   1319 		self._a.extend(tuple(p))
   1320 
   1321 	def extend(self, iterable):
   1322 		for p in iterable:
   1323 			p = self._checkFloat(p)
   1324 			self._a.extend(p)
   1325 
   1326 	def toInt(self):
   1327 		if not self.isFloat():
   1328 			return
   1329 		a = array.array("h")
   1330 		for n in self._a:
   1331 			a.append(otRound(n))
   1332 		self._a = a
   1333 
   1334 	def relativeToAbsolute(self):
   1335 		a = self._a
   1336 		x,y = 0,0
   1337 		for i in range(len(a) // 2):
   1338 			x = a[2*i  ] + x
   1339 			y = a[2*i+1] + y
   1340 			self[i] = (x, y)
   1341 
   1342 	def absoluteToRelative(self):
   1343 		a = self._a
   1344 		x,y = 0,0
   1345 		for i in range(len(a) // 2):
   1346 			dx = a[2*i  ] - x
   1347 			dy = a[2*i+1] - y
   1348 			x = a[2*i  ]
   1349 			y = a[2*i+1]
   1350 			self[i] = (dx, dy)
   1351 
   1352 	def translate(self, p):
   1353 		"""
   1354 		>>> GlyphCoordinates([(1,2)]).translate((.5,0))
   1355 		"""
   1356 		(x,y) = self._checkFloat(p)
   1357 		a = self._a
   1358 		for i in range(len(a) // 2):
   1359 			self[i] = (a[2*i] + x, a[2*i+1] + y)
   1360 
   1361 	def scale(self, p):
   1362 		"""
   1363 		>>> GlyphCoordinates([(1,2)]).scale((.5,0))
   1364 		"""
   1365 		(x,y) = self._checkFloat(p)
   1366 		a = self._a
   1367 		for i in range(len(a) // 2):
   1368 			self[i] = (a[2*i] * x, a[2*i+1] * y)
   1369 
   1370 	def transform(self, t):
   1371 		"""
   1372 		>>> GlyphCoordinates([(1,2)]).transform(((.5,0),(.2,.5)))
   1373 		"""
   1374 		a = self._a
   1375 		for i in range(len(a) // 2):
   1376 			x = a[2*i  ]
   1377 			y = a[2*i+1]
   1378 			px = x * t[0][0] + y * t[1][0]
   1379 			py = x * t[0][1] + y * t[1][1]
   1380 			self[i] = (px, py)
   1381 
   1382 	def __eq__(self, other):
   1383 		"""
   1384 		>>> g = GlyphCoordinates([(1,2)])
   1385 		>>> g2 = GlyphCoordinates([(1.0,2)])
   1386 		>>> g3 = GlyphCoordinates([(1.5,2)])
   1387 		>>> g == g2
   1388 		True
   1389 		>>> g == g3
   1390 		False
   1391 		>>> g2 == g3
   1392 		False
   1393 		"""
   1394 		if type(self) != type(other):
   1395 			return NotImplemented
   1396 		return self._a == other._a
   1397 
   1398 	def __ne__(self, other):
   1399 		"""
   1400 		>>> g = GlyphCoordinates([(1,2)])
   1401 		>>> g2 = GlyphCoordinates([(1.0,2)])
   1402 		>>> g3 = GlyphCoordinates([(1.5,2)])
   1403 		>>> g != g2
   1404 		False
   1405 		>>> g != g3
   1406 		True
   1407 		>>> g2 != g3
   1408 		True
   1409 		"""
   1410 		result = self.__eq__(other)
   1411 		return result if result is NotImplemented else not result
   1412 
   1413 	# Math operations
   1414 
   1415 	def __pos__(self):
   1416 		"""
   1417 		>>> g = GlyphCoordinates([(1,2)])
   1418 		>>> g
   1419 		GlyphCoordinates([(1, 2)])
   1420 		>>> g2 = +g
   1421 		>>> g2
   1422 		GlyphCoordinates([(1, 2)])
   1423 		>>> g2.translate((1,0))
   1424 		>>> g2
   1425 		GlyphCoordinates([(2, 2)])
   1426 		>>> g
   1427 		GlyphCoordinates([(1, 2)])
   1428 		"""
   1429 		return self.copy()
   1430 	def __neg__(self):
   1431 		"""
   1432 		>>> g = GlyphCoordinates([(1,2)])
   1433 		>>> g
   1434 		GlyphCoordinates([(1, 2)])
   1435 		>>> g2 = -g
   1436 		>>> g2
   1437 		GlyphCoordinates([(-1, -2)])
   1438 		>>> g
   1439 		GlyphCoordinates([(1, 2)])
   1440 		"""
   1441 		r = self.copy()
   1442 		a = r._a
   1443 		for i in range(len(a)):
   1444 			a[i] = -a[i]
   1445 		return r
   1446 	def __round__(self):
   1447 		"""
   1448 		Note: This is Python 3 only.  Python 2 does not call __round__.
   1449 		As such, we cannot test this method either. :(
   1450 		"""
   1451 		r = self.copy()
   1452 		r.toInt()
   1453 		return r
   1454 
   1455 	def __add__(self, other): return self.copy().__iadd__(other)
   1456 	def __sub__(self, other): return self.copy().__isub__(other)
   1457 	def __mul__(self, other): return self.copy().__imul__(other)
   1458 	def __truediv__(self, other): return self.copy().__itruediv__(other)
   1459 
   1460 	__radd__ = __add__
   1461 	__rmul__ = __mul__
   1462 	def __rsub__(self, other): return other + (-self)
   1463 
   1464 	def __iadd__(self, other):
   1465 		"""
   1466 		>>> g = GlyphCoordinates([(1,2)])
   1467 		>>> g += (.5,0)
   1468 		>>> g
   1469 		GlyphCoordinates([(1.5, 2.0)])
   1470 		>>> g2 = GlyphCoordinates([(3,4)])
   1471 		>>> g += g2
   1472 		>>> g
   1473 		GlyphCoordinates([(4.5, 6.0)])
   1474 		"""
   1475 		if isinstance(other, tuple):
   1476 			assert len(other) ==  2
   1477 			self.translate(other)
   1478 			return self
   1479 		if isinstance(other, GlyphCoordinates):
   1480 			if other.isFloat(): self._ensureFloat()
   1481 			other = other._a
   1482 			a = self._a
   1483 			assert len(a) == len(other)
   1484 			for i in range(len(a) // 2):
   1485 				self[i] = (a[2*i] + other[2*i], a[2*i+1] + other[2*i+1])
   1486 			return self
   1487 		return NotImplemented
   1488 
   1489 	def __isub__(self, other):
   1490 		"""
   1491 		>>> g = GlyphCoordinates([(1,2)])
   1492 		>>> g -= (.5,0)
   1493 		>>> g
   1494 		GlyphCoordinates([(0.5, 2.0)])
   1495 		>>> g2 = GlyphCoordinates([(3,4)])
   1496 		>>> g -= g2
   1497 		>>> g
   1498 		GlyphCoordinates([(-2.5, -2.0)])
   1499 		"""
   1500 		if isinstance(other, tuple):
   1501 			assert len(other) ==  2
   1502 			self.translate((-other[0],-other[1]))
   1503 			return self
   1504 		if isinstance(other, GlyphCoordinates):
   1505 			if other.isFloat(): self._ensureFloat()
   1506 			other = other._a
   1507 			a = self._a
   1508 			assert len(a) == len(other)
   1509 			for i in range(len(a) // 2):
   1510 				self[i] = (a[2*i] - other[2*i], a[2*i+1] - other[2*i+1])
   1511 			return self
   1512 		return NotImplemented
   1513 
   1514 	def __imul__(self, other):
   1515 		"""
   1516 		>>> g = GlyphCoordinates([(1,2)])
   1517 		>>> g *= (2,.5)
   1518 		>>> g *= 2
   1519 		>>> g
   1520 		GlyphCoordinates([(4.0, 2.0)])
   1521 		>>> g = GlyphCoordinates([(1,2)])
   1522 		>>> g *= 2
   1523 		>>> g
   1524 		GlyphCoordinates([(2, 4)])
   1525 		"""
   1526 		if isinstance(other, Number):
   1527 			other = (other, other)
   1528 		if isinstance(other, tuple):
   1529 			if other == (1,1):
   1530 				return self
   1531 			assert len(other) ==  2
   1532 			self.scale(other)
   1533 			return self
   1534 		return NotImplemented
   1535 
   1536 	def __itruediv__(self, other):
   1537 		"""
   1538 		>>> g = GlyphCoordinates([(1,3)])
   1539 		>>> g /= (.5,1.5)
   1540 		>>> g /= 2
   1541 		>>> g
   1542 		GlyphCoordinates([(1.0, 1.0)])
   1543 		"""
   1544 		if isinstance(other, Number):
   1545 			other = (other, other)
   1546 		if isinstance(other, tuple):
   1547 			if other == (1,1):
   1548 				return self
   1549 			assert len(other) ==  2
   1550 			self.scale((1./other[0],1./other[1]))
   1551 			return self
   1552 		return NotImplemented
   1553 
   1554 	def __bool__(self):
   1555 		"""
   1556 		>>> g = GlyphCoordinates([])
   1557 		>>> bool(g)
   1558 		False
   1559 		>>> g = GlyphCoordinates([(0,0), (0.,0)])
   1560 		>>> bool(g)
   1561 		True
   1562 		>>> g = GlyphCoordinates([(0,0), (1,0)])
   1563 		>>> bool(g)
   1564 		True
   1565 		>>> g = GlyphCoordinates([(0,.5), (0,0)])
   1566 		>>> bool(g)
   1567 		True
   1568 		"""
   1569 		return bool(self._a)
   1570 
   1571 	__nonzero__ = __bool__
   1572 
   1573 
   1574 def reprflag(flag):
   1575 	bin = ""
   1576 	if isinstance(flag, str):
   1577 		flag = byteord(flag)
   1578 	while flag:
   1579 		if flag & 0x01:
   1580 			bin = "1" + bin
   1581 		else:
   1582 			bin = "0" + bin
   1583 		flag = flag >> 1
   1584 	bin = (14 - len(bin)) * "0" + bin
   1585 	return bin
   1586 
   1587 
   1588 if __name__ == "__main__":
   1589 	import doctest, sys
   1590 	sys.exit(doctest.testmod().failed)
   1591