text: # Module used by InstanceGenerator and KernFeatureGenerator
  
  ###################################################
  ### THE VALUES BELOW CAN BE EDITED AS NEEDED ######
  ###################################################
  
  kMarkFeatureFileName = "mark.fea"
  kMkmkFeatureFileName = "mkmk.fea"
  kMarkClassesFileName = "markclasses.fea"
  kAbvmFeatureFileName = "abvm.fea"
  kBlwmFeatureFileName = "blwm.fea"
  kCombMarksClassName = "COMBINING_MARKS"
  kLigaturesClassName = "LIGATURES_WITH_%d_COMPONENTS" # The '%d' part is required
  kDefaultGenMkmkFeature = False
  kDefaultTrimCasingTags = False
  kDefaultWriteMarkClassesFile = False
  kDefaultIndianScriptsFormat = False
  kCasingTagsList = ['LC','UC','SC','AC'] # All the tags must have the same number of characters, and that number must be equal to kCasingTagSize
  kCasingTagSize = 2
  kRTLtagsList = ['_AR','_HE'] # Arabic, Hebrew
  kIgnoreAnchorTag = "CXT"
  kLigatureComponentOrderTags = ['1ST','2ND','3RD','4TH'] # Add more as necessary to a maximum of 9 (nine)
  kIndianAboveMarks = "abvm"
  kIndianBelowMarks = "blwm"
  
  ###################################################
  
  __copyright__ = __license__ =  """
  Copyright (c) 2010-2013 Adobe Systems Incorporated. All rights reserved.
   
  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the "Software"), 
  to deal in the Software without restriction, including without limitation 
  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
  and/or sell copies of the Software, and to permit persons to whom the 
  Software is furnished to do so, subject to the following conditions:
   
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
   
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
  DEALINGS IN THE SOFTWARE.
  """
  
  __doc__ = """
  WriteMarkFeaturesFDK.py v2.2 - May 01 2013
  
  Contains a Python class (markDataClass) which, when provided with a FontLab font and a path to a folder, 
  will output a file named "mark.fea", containing a features-file syntax definition of the font's 
  mark attachment and achor data. The script can also generate "mkmk.fea" file. If the Indian scripts
  option is selected, all or part of the lookups will be written to files named "abvm/blwm.fea".
  
  VERY IMPORTANT: For this script to work, all the combining mark glyphs 
  must be added to an OpenType class named 'COMBINING_MARKS'.
  
  When designing the combining marks, often there's the need to make specific versions for uppercase
  and small cap glyphs, which are slightly different from the lowercase. For this reason, one might
  want to use a casing tag in the anchors' names (e.g. "_aboveLC", "_aboveUC" and "_aboveSC" instead
  of just "_above" for all the three cases) so that the position of the anchors can be tested in
  FontLab. But this distinction is not necessary for the 'mark' feature, and that is why this script 
  allows for those casing tags to be trimmed, by setting the value of 'kDefaultTrimCasingTags'.
  
  If the font has ligatures and the ligatures have anchors, the ligature glyphs have to be put in
  OpenType classes named "LIGATURES_WITH_X_COMPONENTS", where 'X' should be replaced by a number
  between 2 and 9 (inclusive). Additionally, the names of the anchors used on the ligature glyphs 
  need to have a tag (e.g. 1ST, 2ND) which is used for corresponding the anchors with the correct
  ligature component. Keep in mind that in Right-To-Left scripts, the first component of the ligature
  is the one on the right side of the glyph.
  
  The lookups will have a RightToLeft lookupflag if the name of the anchors contains one of the tags
  listed in the kRTLtagsList list.
  
  The anchors that are specific to Indian scripts much be named "abvm" (for Above Marks) or "blwm"
  (for Below Marks), and the Indian scripts option needs to be checked in the UI.
  
  ==================================================
  Versions:
  v1.0   - Feb 15 2010 - Initial release
  v1.1   - Feb 19 2010 - Added the option to generate 'mkmk' feature
  v1.2   - Aug 10 2010 - Added support for ligatures
  v1.3   - Apr 21 2011 - Added the ability to ignore certain base anchors
                         Added the option of writing the mark classes in a separate file
  v1.4   - Jul 11 2011 - Overhauled the way mark-to-base and mark-to-ligature positions are output; they're now written in separate lookup blocks.
                         Lookup flag "RightToLeft" is now written for Arabic and Hebrew lookups.
  v1.4.1 - Aug 10 2011 - The contents of the combining marks class and the ligatures classes are now verified for the presence of duplicates.
  v1.4.2 - Mar 03 2012 - Fixed bug in output of mkmk feature.
  v1.4.3 - Mar 04 2012 - Added checks for nameless anchors.
  v1.4.4 - Jun 14 2012 - Added checks to avoid anchors with the same name.
  v1.5   - Jun 15 2012 - Added the option to output the lookups in the format required for Indian scripts.
  v1.6   - Jul 19 2012 - The MarkAttachmentType lookupflag is no longer added to mark-to-mark lookups meant for the 'abvm' and 'blwm' features.
  v2.0   - Mar 08 2013 - Now compatible with UFOs -- this means the module can be used from Fontlab, from the commandline or from RoboFont.
  v2.1   - Apr 26 2013 - RTL tags were changed to minimize conflicts with anchor names written in caps. Improvements to the output of warning messages.
                         Contextual anchors no longer produce mark classes.
  v2.2   - May 01 2013 - Fixed bug in processing FontLab classes.
  v2.2.1 - Aug 16 2013 - Changed names of output files.
  """
  
  import os, time, re
  
  
  class WhichApp(object):
  	'Testing the environment'
  	
  	def __init__(self):
  		self.inRF = False
  		self.inFL = False
  		self.inDC = False
  		self.appName = "noApp"
  		
  		if not any((self.inRF, self.inFL, self.inDC)):
  			try:
  				import RoboFont
  				# print 'in Robofont'
  				self.inRF = True
  				self.appName = 'Robofont'
  			except ImportError:
  				pass
  
  		if not any((self.inRF, self.inFL, self.inDC)):
  			try:
  				import flsys
  				# print 'In FontLab, dork!'
  				self.inFL = True
  				self.appName = 'FontLab'
  			except ImportError:
  				pass
  
  		if not any((self.inRF, self.inFL, self.inDC)):
  			try:
  				import defcon
  				# print 'defcon'
  				self.inDC = True
  				self.appName = 'Defcon'
  			except ImportError:
  				pass
  
  
  class MarkDataClass(object):
  	
  	def __init__(self, font, folderPath, trimCasingTags=kDefaultTrimCasingTags, genMkmkFeature=kDefaultGenMkmkFeature, writeClassesFile=kDefaultWriteMarkClassesFile, indianScriptsFormat=kDefaultIndianScriptsFormat):
  		self.header = ['# Created: %s' % time.ctime()]
  
  		appTest = WhichApp()
  		self.inRF = appTest.inRF
  		self.inFL = appTest.inFL
  		self.inDC = appTest.inDC
  		self.appName = appTest.appName
  
  		self.f = font
  		self.MM = False
  		self.folder = folderPath
  		self.trimCasingTags = trimCasingTags
  		self.genMkmkFeature = genMkmkFeature
  		self.writeClassesFile = writeClassesFile
  		self.indianScriptsFormat = indianScriptsFormat
  		
  		self.lineBreak = '\n'
  		self.marksClassList = []
  		self.ligatureComponentsList = range(2,len(kLigatureComponentOrderTags)+1) # ligatures have a minimum of 2 components and a maximum determined by the size of kLigatureComponentOrderTags
  		self.ligatureClassesDict = {}
  		self.allLigaturesList = []
  		self.invalidGlyphNamesList = []
  		self.repeatedGlyphNamesList = []
  		self.markTypesList = []
  		self.anchorNamesNotTrimmed = []
  		
  		self.anchorsDataInCombMarksDict = {}
  		self.anchorsDataInBaseGlyphsDict = {}
  		self.anchorsDataInLigaturesDict = {}
  		self.markRelatedGlyphClassLinesList = []
  		self.markRelatedMarkClassLinesList = []
  		self.baseRelatedBasePosLinesList = []
  		self.ligatureRelatedBasePosLinesList = []
  
  		self.anchorsDataInCombMkmksDict = {} # {'below': ['gravebelowcmb,0,-246', 'acutebelowcmb,0,-246'], 'above': ['gravecmb,0,713', 'acutecmb,0,713']}
  		self.mkmkRelatedBasePosLinesList = []
  		self.classesNote = ''
  
  		if self.inFL:
  			self.header.append('# PS Name: %s' % self.f.font_name)
  			self.isMMfont(self.f) # sets self.MM to True or False
  			
  			flC = ReadFontLabClasses(self.f)
  			self.marksClassList = flC.marksClassList
  			self.ligatureClassesDict = flC.ligatureClassesDict
  			
  			if not self.MM:
  				self.header.append('# MM Inst: %s' % self.f.menu_name)
  
  		else:
  			self.header.append('# PS Name: %s' % self.f.info.postscriptFontName)
  			self.header.append('# MM Inst: %s' % self.f.info.styleMapFamilyName)
  
  			if self.f.groups.has_key(kCombMarksClassName):
  				self.marksClassList = self.f.groups[kCombMarksClassName]
  			for compNum in self.ligatureComponentsList:
  				if self.f.groups.has_key(kLigaturesClassName % compNum):
  		 			self.ligatureClassesDict[compNum] = self.f.groups[kLigaturesClassName % compNum]
  
  		self.header.append('# exported from %s\n\n' % self.appName)
  		
  		if not len(self.marksClassList):
  			print "\tERROR: %s class could not be found or is empty!" % kCombMarksClassName
  			return
  
  		self.validateCombMarksClassContents()
  		self.validateLigatureClassesContents()
  		if len(self.invalidGlyphNamesList):
  			print "\tERROR: The glyph(s) listed below could not be found! Please check the combining marks (and ligatures) classes.\n\t%s\n" % ("\n\t".join(self.invalidGlyphNamesList))
  			return
  		
  		if len(self.repeatedGlyphNamesList):
  			# the error messages have already been printed
  			return
  		
  		if len(self.ligatureClassesDict):
  			self.addAllLigaturesToList()
  		
  		self.collectAnchorDataFromCombMarks()
  		if not len(self.anchorsDataInCombMarksDict):
  			print "\tERROR: No valid anchors were found in combining mark glyphs!"
  			return
  		
  		self.buildMarkRelatedLines() # builds the markClass lines and the glyph classes lines
  		
  		self.markRelatedGlyphClassLinesList.sort()
  		self.markRelatedMarkClassLinesList.sort()
  		
  		self.collectMarkTypesList()
  		
  		self.collectAnchorDataFromBaseGlyphs()
  		if not len(self.anchorsDataInBaseGlyphsDict):
  			print "\tWARNING: No valid anchors were found in base glyphs!"
  			print
  		
  		if len(self.ligatureClassesDict):
  			self.collectAnchorDataFromLigatureGlyphs()
  
  		self.buildMarkLookups()
  		
  		if len(self.anchorNamesNotTrimmed):
  			print "\tNOTE: These anchor names were not trimmed:\n\t\t%s" % "\n\t\t".join(self.anchorNamesNotTrimmed)
  			
  		if self.writeClassesFile:
  			self.writeMarkClassesFile()
  		
  		self.buildClassesNote()
  		self.writeMarkFeatureFile()
  		
  		if self.genMkmkFeature:
  			self.collectAnchorDataFromCombMkmk()
  			self.buildMkmkRelatedLines()
  			self.writeMkmkFeatureFile()
  
  
  	def isMMfont(self, font):
  		'Checks if the FontLab font is a Multiple Master font.'
  		if font[0].layers_number > 1:
  			self.MM = True
  
  
  	def glyphFoundInFont(self, gName):
  		if self.inFL:
  			if self.f.FindGlyph(gName) != -1:
  				return True
  		else:
  			if gName in self.f.keys():
  				return True
  		return False
  	
  	
  	def validateCombMarksClassContents(self):
  		tempGlist = []
  		for gName in self.marksClassList:
  			if not self.glyphFoundInFont(gName):
  				self.invalidGlyphNamesList.append(gName)
  			if gName not in tempGlist:
  				tempGlist.append(gName)
  			else:	
  				self.repeatedGlyphNamesList.append(gName)
  				print "\tERROR: The glyph named %s is repeated in the class named %s" % (gName, kCombMarksClassName)
  
  
  	def validateLigatureClassesContents(self):
  		tempAllGlist = []
  		for element in self.ligatureClassesDict:
  			tempGlist = []
  			for gName in self.ligatureClassesDict[element]:
  				if not self.glyphFoundInFont(gName):
  					self.invalidGlyphNamesList.append(gName)
  				if gName not in tempGlist:
  					tempGlist.append(gName)
  					if gName not in tempAllGlist:
  						tempAllGlist.append(gName)
  					else:	
  						self.repeatedGlyphNamesList.append(gName)
  						print "\tERROR: The glyph named %s is used in more than one ligature class." % (gName)
  				else:	
  					self.repeatedGlyphNamesList.append(gName)
  					print "\tERROR: The glyph named %s is repeated in the class named %s" % (gName, kLigaturesClassName % element)
  
  
  	def addAllLigaturesToList(self):
  		for element in self.ligatureClassesDict:
  			self.allLigaturesList.extend(self.ligatureClassesDict[element])
  
  
  	def addAnchorDataToDict(self, anchor, gName, dict):
  		if anchor in dict:
  			gNameList = dict[anchor]
  			gNameList.append(gName)
  			dict[anchor] = gNameList
  		else:
  			dict[anchor] = [gName]
  
  
  	def trimCaseTags(self, anchorName):
  		if len(anchorName) >= kCasingTagSize + 3: # +3 to guarantee that the name is long enough and will remain long enough after being trimmed
  			if anchorName[-kCasingTagSize:] in kCasingTagsList:
  				return anchorName[:-kCasingTagSize]
  		if anchorName not in self.anchorNamesNotTrimmed:
  			self.anchorNamesNotTrimmed.append(anchorName)
  		return anchorName
  
  
  	def collectMarkTypesList(self):
  		uniqueMarksList = self.anchorsDataInCombMarksDict.keys()
  		for mark in uniqueMarksList:
  			markName = mark.split(',')[0][1:]  # ['_above,0,694', '_centerAR,0,0', '_above,0,519'] Removes the initial underscore as well
  			if markName not in self.markTypesList:
  				self.markTypesList.append(markName)
  		self.markTypesList.sort()
  		
  	
  	def collectAnchorDataFromCombMarks(self):
  		for gName in self.marksClassList:
  			g = self.f[gName]
  			markAnchorsNames = []
  			for anchorIndex in range(len(g.anchors)):
  				anchorName = g.anchors[anchorIndex].name
  				if len(anchorName) == 0:
  					print "\tERROR: Glyph %s has a nameless anchor." % gName
  					continue
  				if anchorName[0] == '_': # Consider only mark-related anchors (Anchors without undercore are for use in 'mkmk' feature)
  					if anchorName in markAnchorsNames:
  						print "\tERROR: Glyph %s has more than one anchor named %s." % (gName, anchorName)
  					else:
  						markAnchorsNames.append(anchorName)
  						point = g.anchors[anchorIndex]
  						if self.trimCasingTags:
  							anchorName = self.trimCaseTags(anchorName)
  						anchor = "%s,%d,%d" % (anchorName, point.x, point.y)
  						self.addAnchorDataToDict(anchor, gName, self.anchorsDataInCombMarksDict)
  
  
  	def collectAnchorDataFromBaseGlyphs(self):
  		if self.inFL:
  			glyphs = self.f.glyphs
  		else:
  			glyphs = []
  			glyphOrder = self.f.lib["public.glyphOrder"] # self.f.keys() can't be used because its order is not stable
  			for name in glyphOrder:
  				glyphs.append(self.f[name])
  		for g in glyphs:
  			gName = g.name
  			if gName not in (self.marksClassList + self.allLigaturesList):
  				markNamesLog = []
  				for anchorIndex in range(len(g.anchors)):
  					anchorName = g.anchors[anchorIndex].name
  					if len(anchorName) == 0:
  						print "\tERROR: Glyph %s has a nameless anchor." % gName
  						continue
  					if (anchorName[0] != '_') and (kIgnoreAnchorTag not in anchorName): # Consider only base-related anchors (the ones NOT prefixed with the underscore)
  						if anchorName in markNamesLog:
  							print "\tERROR: Glyph %s has more than one anchor named %s." % (gName, anchorName)
  						else:
  							markNamesLog.append(anchorName)
  							point = g.anchors[anchorIndex]
  							if self.trimCasingTags:
  								anchorName = self.trimCaseTags(anchorName)
  							anchor = "%s,%d,%d" % (anchorName, point.x, point.y)
  							self.addAnchorDataToDict(anchor, gName, self.anchorsDataInBaseGlyphsDict)
  
  
  	def collectAnchorDataFromLigatureGlyphs(self):
  		for elementNum in self.ligatureClassesDict:
  			for gName in self.ligatureClassesDict[elementNum]:
  				g = self.f[gName]
  				markNamesLog = []
  				for anchorIndex in range(len(g.anchors)):
  					anchorName = g.anchors[anchorIndex].name
  					if len(anchorName) == 0:
  						print "\tERROR: Glyph %s has a nameless anchor." % gName
  						continue
  					if (anchorName[0] != '_') and (kIgnoreAnchorTag not in anchorName): # Consider only base-related anchors (the ones NOT prefixed with the underscore)
  						if anchorName in markNamesLog:
  							print "\tERROR: Glyph %s has more than one anchor named %s." % (gName, anchorName)
  						else:
  							markNamesLog.append(anchorName)
  							point = g.anchors[anchorIndex]
  							if self.trimCasingTags:
  								anchorName = self.trimCaseTags(anchorName)
  							
  							# Arrange the data before adding it to the dictionary
  							for i in range(elementNum):
  								elementOrder = kLigatureComponentOrderTags[i]
  								if elementOrder in anchorName:
  									strippedAnchorName = re.sub(elementOrder, '', anchorName) # remove the component order tag using regexp
  									if strippedAnchorName[-1] == "_":
  										strippedAnchorName = strippedAnchorName[:-1] # remove the last character of the anchor's name, if it happens to be an underscore
  	
  									dictKey = "%s,%s" % (strippedAnchorName, gName)
  									anchor = "%s,%d,%d" % (elementOrder, point.x, point.y)
  									
  									if dictKey not in self.anchorsDataInLigaturesDict:
  										self.anchorsDataInLigaturesDict[dictKey] = [elementNum, anchor]
  									else:
  										self.anchorsDataInLigaturesDict[dictKey].append(anchor)
  									
  									break  # no need to finish the loop since a match was found
  						
  
  	def buildMarkRelatedLines(self):
  		anchorGroupList = self.anchorsDataInCombMarksDict.keys()
  		anchorGroupList.sort()
  		for anchor in anchorGroupList:
  			anchorName, anchorX, anchorY = anchor.split(',')
  			markClassName = "@MC%s" % anchorName
  			if kIgnoreAnchorTag in markClassName: # Don't make mark classes from contextual anchors
  				continue
  			if len(self.anchorsDataInCombMarksDict[anchor]) > 1: # make a glyph class only when there's more than one glyph
  				glyphClassName = "@mGC%s_%s_%s" % (anchorName, anchorX.replace('-','n'), anchorY.replace('-','n')) # Coordinates may be negative, and hyphens are not allowed in class names
  				glyphClassContents = ' '.join(self.anchorsDataInCombMarksDict[anchor])
  				glyphClassLine = "%s = [%s];\n" % (glyphClassName, glyphClassContents)
  				self.markRelatedGlyphClassLinesList.append(glyphClassLine)
  				#
  				markClassLine = "markClass %s <anchor %s %s> %s;\n" % (glyphClassName, anchorX, anchorY, markClassName)
  				self.markRelatedMarkClassLinesList.append(markClassLine)
  			else:
  				markClassLine = "markClass %s <anchor %s %s> %s;\n" % (self.anchorsDataInCombMarksDict[anchor][0], anchorX, anchorY, markClassName)
  				self.markRelatedMarkClassLinesList.append(markClassLine)
  
  
  	def buildMarkLookups(self):
  		for markType in self.markTypesList:
  			baseLinesList = self.buildBaseRelatedLines(markType)
  			
  			# Do base lookups
  			if len(baseLinesList):
  				self.baseRelatedBasePosLinesList.append('lookup MARK_BASE_%s {\n' % markType)
  				if markType[-3:] in kRTLtagsList:
  					self.baseRelatedBasePosLinesList.append('\tlookupflag RightToLeft;\n\n')
  				self.baseRelatedBasePosLinesList.extend(baseLinesList)
  				self.baseRelatedBasePosLinesList.append('} MARK_BASE_%s;\n\n\n' % markType)
  			else:
  				print "\tWARNING: The anchor %s is not used in any of the base glyphs." % markType
  	
  			# Do ligature lookups
  			if len(self.anchorsDataInLigaturesDict):
  				ligatureLinesList = self.buildLigatureRelatedLines(markType)
  				
  				if len(ligatureLinesList):
  					self.ligatureRelatedBasePosLinesList.append('lookup MARK_LIGATURE_%s {\n' % markType)
  					if markType[-3:] in kRTLtagsList:
  						self.ligatureRelatedBasePosLinesList.append('\tlookupflag RightToLeft;\n\n')
  					self.ligatureRelatedBasePosLinesList.extend(ligatureLinesList)
  					self.ligatureRelatedBasePosLinesList.append('} MARK_LIGATURE_%s;\n\n\n' % markType)
  				else:
  					print "\tWARNING: The anchor %s is not used in any of the ligature glyphs." % markType
  
  	
  	def buildBaseRelatedLines(self, markTypeName):
  		anchorGroupList = self.anchorsDataInBaseGlyphsDict.keys() # ['aboveAR,1675,697', 'below,367,-30', ... ]
  		anchorGroupList.sort()
  		glyphClassLinesList = []
  		basePosLinesList = []
  		
  		for anchor in anchorGroupList:
  			anchorName, anchorX, anchorY = anchor.split(',')
  			
  			if anchorName != markTypeName: # skip anchors that are not the same kind of the one requested
  				continue
  			
  			anchorAndMarkClass = "<anchor %s %s> mark @MC_%s" % (anchorX, anchorY, anchorName)
  			
  			if len(self.anchorsDataInBaseGlyphsDict[anchor]) > 1: # make a glyph class only when there's more than one glyph
  				glyphClassName = "@bGC_%s_%s" % (self.anchorsDataInBaseGlyphsDict[anchor][0], anchorName)
  				glyphClassContents = ' '.join(self.anchorsDataInBaseGlyphsDict[anchor])
  				glyphClassLine = "\t%s = [%s];\n" % (glyphClassName, glyphClassContents)
  				glyphClassLinesList.append(glyphClassLine)
  				#
  				basePosLine = "\tpos base %s %s;\n" % (glyphClassName, anchorAndMarkClass)
  				basePosLinesList.append(basePosLine)
  			else:
  				basePosLine = "\tpos base %s %s;\n" % (self.anchorsDataInBaseGlyphsDict[anchor][0], anchorAndMarkClass)
  				basePosLinesList.append(basePosLine)
  
  		glyphClassLinesList.sort()
  		basePosLinesList.sort()
  		
  		return glyphClassLinesList + basePosLinesList
  
  
  	def buildLigatureRelatedLines(self, markTypeName):
  		anchorGroupList = self.anchorsDataInLigaturesDict.keys() # ['aboveAR,arAlefMaksura_AlefMaksura', 'aboveAR,arAlefMaksura_AlefMaksura.f', 'aboveAR,arAlefMaksura_AlefMaksura.fj',
  		anchorGroupList.sort()
  		ligaturePosLinesList = []
  		
  		for anchor in anchorGroupList:
  			anchorName, gName = anchor.split(',')
  			
  			if anchorName != markTypeName: # skip anchors that are not the same kind of the one requested
  				continue
  			
  			elementCount = int(self.anchorsDataInLigaturesDict[anchor][0])
  			anchorPositionsList = self.anchorsDataInLigaturesDict[anchor][1:]  # ['1ST,1735,358', '2ND,680,234']
  			anchorPositionsList.sort()
  			
  			if elementCount != len(anchorPositionsList):
  				print "\tNOT IMPLEMENTED WARNING: Number of elements in ligature %s does not match the number of anchors of the type %s." % (gName, anchorName)
  				continue
  			
  			elementsAnchorsAndMarkClassesList = []
  			
  			for position in anchorPositionsList:
  				ord, anchorX, anchorY = position.split(',')
  				anchorAndMarkClass = "<anchor %s %s> mark @MC_%s" % (anchorX, anchorY, anchorName)
  				elementsAnchorsAndMarkClassesList.append(anchorAndMarkClass)
  			
  			ligaturePosLine = "\tpos ligature %s %s;\n" % (gName, ' ligComponent '.join(elementsAnchorsAndMarkClassesList))
  			ligaturePosLinesList.append(ligaturePosLine)
  		
  		return ligaturePosLinesList
  
  
  	def buildClassesNote(self):
  		if self.writeClassesFile:
  			classesInfoFile = kMarkClassesFileName
  		else:
  			classesInfoFile = kMarkFeatureFileName
  		self.classesNote = "# NOTE: The markClass declarations can be found in the file '%s'.\n\n" % classesInfoFile
  
  
  	def assembleClassesLines(self):
  		classesLinesList = []
  		if len(self.markRelatedGlyphClassLinesList):
  			classesLinesList.extend(self.markRelatedGlyphClassLinesList)
  			classesLinesList.append(self.lineBreak)
  		if len(self.markRelatedMarkClassLinesList):
  			classesLinesList.extend(self.markRelatedMarkClassLinesList)
  			classesLinesList.append(self.lineBreak)
  		return classesLinesList
  
  	
  	def writeMarkClassesFile(self):
  		print '\tSaving %s file...' % kMarkClassesFileName
  		filePath = os.path.join(self.folder, kMarkClassesFileName)
  		outfile = open(filePath, 'w')
  		outfile.write("\n".join(self.header))
  		outfile.writelines(self.assembleClassesLines())
  		outfile.close()
  
  
  	def writeMarkFeatureFile(self):
  		print '\tSaving %s file...' % kMarkFeatureFileName
  		filePath = os.path.join(self.folder, kMarkFeatureFileName)
  		outfile = open(filePath, 'w')
  		outfile.write("\n".join(self.header))
  		if self.writeClassesFile:
  			outfile.write(self.classesNote)
  		else:
  			outfile.writelines(self.assembleClassesLines())
  		
  		fileHeader = "\n".join(self.header) + self.classesNote
  
  		if self.indianScriptsFormat:
  			print '\tSaving %s and %s files...' % (kAbvmFeatureFileName, kBlwmFeatureFileName)
  			abvmFilePath = os.path.join(self.folder, kAbvmFeatureFileName)
  			blwmFilePath = os.path.join(self.folder, kBlwmFeatureFileName)
  			abvmfile = open(abvmFilePath, 'w')
  			blwmfile = open(blwmFilePath, 'w')
  			abvmfile.write(fileHeader)
  			blwmfile.write(fileHeader)
  			
  			# pos base lookups
  			for line in self.baseRelatedBasePosLinesList: # the lines will have to be analized and written to separate files
  				if kIndianAboveMarks in line:
  					abvmfile.write(line)
  				elif kIndianBelowMarks in line:
  					blwmfile.write(line)
  				else:
  					outfile.write(line)
  						
  			outfile.write(self.lineBreak)
  
  			# pos ligature lookups
  			for line in self.ligatureRelatedBasePosLinesList: # the lines will have to be analized and written to separate files
  				if kIndianAboveMarks in line:
  					abvmfile.write(line)
  				elif kIndianBelowMarks in line:
  					blwmfile.write(line)
  				else:
  					outfile.write(line)
  						
  			abvmfile.close()
  			blwmfile.close()
  
  			if not self.genMkmkFeature: # don't try to delete the files if mark-to-mark lookups are still to be processed
  				self.checkFileContentsAndDeleteIfEmpty(abvmFilePath, fileHeader)
  				self.checkFileContentsAndDeleteIfEmpty(blwmFilePath, fileHeader)
  
  		else: # the Indian scripts option is not checked; all the lines can be written to the file at once
  			if len(self.baseRelatedBasePosLinesList):
  				outfile.writelines(self.baseRelatedBasePosLinesList)
  				outfile.write(self.lineBreak)
  			if len(self.ligatureRelatedBasePosLinesList):
  				outfile.writelines(self.ligatureRelatedBasePosLinesList)
  
  		outfile.close()
  
  		self.checkFileContentsAndDeleteIfEmpty(filePath, fileHeader)
  
  
  	def collectAnchorDataFromCombMkmk(self):
  		for gName in self.marksClassList:
  			g = self.f[gName]
  			markAnchorsNames = []
  			for anchorIndex in range(len(g.anchors)):
  				anchorName = g.anchors[anchorIndex].name
  				if len(anchorName) == 0:
  					print "\tERROR: Glyph %s has a nameless anchor." % gName
  					continue
  				if anchorName[0] != '_': # Anchors without undercore are for use in 'mkmk' feature
  					if anchorName in markAnchorsNames:
  						print "\tERROR: Glyph %s has more than one anchor named %s." % (gName, anchorName)
  					else:
  						markAnchorsNames.append(anchorName)
  						point = g.anchors[anchorIndex]
  						if self.trimCasingTags:
  							anchorName = self.trimCaseTags(anchorName)
  						gAnchor = "%s,%d,%d" % (gName, point.x, point.y)
  						self.addAnchorDataToDict(anchorName, gAnchor, self.anchorsDataInCombMkmksDict)
  
  
  	def buildMkmkRelatedLines(self):
  		anchorGroupList = self.anchorsDataInCombMkmksDict.keys()
  		anchorGroupList.sort()
  		for anchorName in anchorGroupList:
  			if anchorName[-3:] in kRTLtagsList: rtlFlag = 'RightToLeft ' # Check the last two characters of the anchor's name
  			else: rtlFlag = ''
  			lookupName = "MKMK_MARK_%s" % anchorName
  			markClassName = "@MC_%s" % anchorName
  			self.mkmkRelatedBasePosLinesList.append("lookup %s {\n" % lookupName)
  			if (not (kIndianAboveMarks in markClassName or kIndianBelowMarks in markClassName)) or (not self.indianScriptsFormat): # skip the lookupflag for Indian script lookups but only when the output format is for Indian scripts as well
  				self.mkmkRelatedBasePosLinesList.append("\tlookupflag %sMarkAttachmentType %s;\n\n" % (rtlFlag, markClassName))
  			mkmkAnchorsList = self.anchorsDataInCombMkmksDict[anchorName]
  			mkmkAnchorsList.sort()
  			for mkmkAnchor in mkmkAnchorsList:
  				glyphName, anchorX, anchorY = mkmkAnchor.split(',')
  				mkmkLine = "\tpos mark %s <anchor %s %s> mark %s;\n" % (glyphName, anchorX, anchorY, markClassName)
  				self.mkmkRelatedBasePosLinesList.append(mkmkLine)
  			self.mkmkRelatedBasePosLinesList.append("} %s;\n\n\n" % lookupName)
  
  
  	def writeMkmkFeatureFile(self):
  		print '\tSaving %s file...' % kMkmkFeatureFileName
  		filePath = os.path.join(self.folder, kMkmkFeatureFileName)
  		outfile = open(filePath, 'w')
  		outfile.write("\n".join(self.header))
  		outfile.write(self.classesNote)
  		fileHeader = "\n".join(self.header) + self.classesNote
  		
  		if self.indianScriptsFormat:
  			print '\tAdding mark-to-mark lookups to %s and %s files...' % (kAbvmFeatureFileName, kBlwmFeatureFileName)
  			abvmFilePath = os.path.join(self.folder, kAbvmFeatureFileName)
  			blwmFilePath = os.path.join(self.folder, kBlwmFeatureFileName)
  			abvmfile = open(abvmFilePath, 'a') # append the data instead of writting a new file; the file is expected to contain lookups for 'pos base' already
  			blwmfile = open(blwmFilePath, 'a')
  			
  			for line in self.mkmkRelatedBasePosLinesList: # the lines will have to be analized and written to separate files
  				if kIndianAboveMarks in line:
  					abvmfile.write(line)
  				elif kIndianBelowMarks in line:
  					blwmfile.write(line)
  				else:
  					outfile.write(line)
  						
  			abvmfile.close()
  			blwmfile.close()
  
  			self.checkFileContentsAndDeleteIfEmpty(abvmFilePath, fileHeader)
  			self.checkFileContentsAndDeleteIfEmpty(blwmFilePath, fileHeader)
  
  		else: # the Indian scripts option is not checked; all the lines can be written to the file at once
  			if len(self.mkmkRelatedBasePosLinesList):
  				outfile.writelines(self.mkmkRelatedBasePosLinesList)
  		
  		outfile.close()
  		
  		self.checkFileContentsAndDeleteIfEmpty(filePath, fileHeader)
  
  
  	def checkFileContentsAndDeleteIfEmpty(self, filePath, fileHeader):
  		file = open(filePath, 'r')
  		fileContents = file.read()
  		file.close()
  		fileContents = fileContents.strip()
  		fileHeader = fileHeader.strip()
  		
  		if fileContents == fileHeader:
  			print "\tFile %s is empty. Deleted." % os.path.basename(filePath)
  			if os.path.exists(filePath):
  				os.remove(filePath)
  	
  
  
  class ReadFontLabClasses(object):
  	def __init__(self, font):
  		self.marksClassList = []
  		self.ligatureComponentsList = range(2,len(kLigatureComponentOrderTags)+1) # ligatures have a minimum of 2 components and a maximum determined by the size of kLigatureComponentOrderTags
  		self.ligatureClassesDict = {}
  		
  		for c in font.classes:
  			if c[0] not in ['_','.']: # Ignore kerning and metrics classes
  				classNameAndContentsList = c.split(':') # Split class name and class contents
  				if len(classNameAndContentsList) == 2: # Make sure there are only two elements, the class name and the class contents
  		
  					if classNameAndContentsList[0] == kCombMarksClassName: # Find class by matching the name
  						self.marksClassList = classNameAndContentsList[1].split() # Return the class contents as a list
  						continue
  
  					for compNum in self.ligatureComponentsList:
  						if classNameAndContentsList[0] == (kLigaturesClassName % compNum): # Find class by matching the name
  							self.ligatureClassesDict[compNum] = classNameAndContentsList[1].split() # Return the class contents as a list
  
  
  