"""A simple set of tools for building accented glyphs. # Hey look! A demonstration: from robofab.accentBuilder import AccentTools, buildRelatedAccentList font = CurrentFont # a list of accented glyphs that you want to build myList=['Aacute', 'aacute'] # search for glyphs related to glyphs in myList and add them to myList myList=buildRelatedAccentList(font, myList)+myList # start the class at=AccentTools(font, myList) # clear away any anchors that exist (this is optional) at.clearAnchors() # add necessary anchors if you want to at.buildAnchors(ucXOffset=20, ucYOffset=40, lcXOffset=15, lcYOffset=30) # print a report of any errors that occured at.printAnchorErrors() # build the accented glyphs if you want to at.buildAccents() # print a report of any errors that occured at.printAccentErrors() """ #XXX! This is *very* experimental! I think it works, but you never know. from robofab.gString import lowercase_plain, accents, uppercase_plain, splitAccent, findAccentBase from robofab.tools.toolsAll import readGlyphConstructions import robofab from robofab.interface.all.dialogs import ProgressBar from robofab.world import RFWorld inFontLab = RFWorld().inFontLab anchorColor=125 accentColor=75 def stripSuffix(glyphName): """strip away unnecessary suffixes from a glyph name""" if glyphName.find('.') != -1: baseName = glyphName.split('.')[0] if glyphName.find('.sc') != -1: baseName = '.'.join([baseName, 'sc']) return baseName else: return glyphName def buildRelatedAccentList(font, list): """build a list of related glyphs suitable for use with AccentTools""" searchList = [] baseGlyphs = {} foundList = [] for glyphName in list: splitNames = splitAccent(glyphName) baseName = splitNames[0] accentNames = splitNames[1] if baseName not in searchList: searchList.append(baseName) if baseName not in baseGlyphs.keys(): baseGlyphs[baseName] = [accentNames] else: baseGlyphs[baseName].append(accentNames) foundGlyphs = findRelatedGlyphs(font, searchList, doAccents=0) for baseGlyph in foundGlyphs.keys(): for foundGlyph in foundGlyphs[baseGlyph]: for accentNames in baseGlyphs[baseGlyph]: foundList.append(makeAccentName(foundGlyph, accentNames)) return foundList def findRelatedGlyphs(font, searchItem, doAccents=True): """Gather up a bunch of related glyph names. Send it either a single glyph name 'a', or a list of glyph names ['a', 'x'] and it returns a dict like: {'a': ['atilde', 'a.alt', 'a.swash']}. if doAccents is False it will skip accented glyph names. This is a relatively slow operation!""" relatedGlyphs = {} for name in font.keys(): base = name.split('.')[0] if name not in relatedGlyphs.keys(): relatedGlyphs[name] = [] if base not in relatedGlyphs.keys(): relatedGlyphs[base] = [] if doAccents: accentBase = findAccentBase(name) if accentBase not in relatedGlyphs.keys(): relatedGlyphs[accentBase] = [] baseAccentBase = findAccentBase(base) if baseAccentBase not in relatedGlyphs.keys(): relatedGlyphs[baseAccentBase] = [] if base != name and name not in relatedGlyphs[base]: relatedGlyphs[base].append(name) if doAccents: if accentBase != name and name not in relatedGlyphs[accentBase]: relatedGlyphs[accentBase].append(name) if baseAccentBase != name and name not in relatedGlyphs[baseAccentBase]: relatedGlyphs[baseAccentBase].append(name) foundGlyphs = {} if isinstance(searchItem, str): searchList = [searchItem] else: searchList = searchItem for glyph in searchList: foundGlyphs[glyph] = relatedGlyphs[glyph] return foundGlyphs def makeAccentName(baseName, accentNames): """make an accented glyph name""" if isinstance(accentNames, str): accentNames = [accentNames] build = [] if baseName.find('.') != -1: base = baseName.split('.')[0] suffix = baseName.split('.')[1] else: base = baseName suffix = '' build.append(base) for accent in accentNames: build.append(accent) buildJoin = ''.join(build) name = '.'.join([buildJoin, suffix]) return name def nameBuster(glyphName, glyphConstruct): stripedSuffixName = stripSuffix(glyphName) suffix = None errors = [] accentNames = [] baseName = glyphName if glyphName.find('.') != -1: suffix = glyphName.split('.')[1] if glyphName.find('.sc') != -1: suffix = glyphName.split('.sc')[1] if stripedSuffixName not in glyphConstruct.keys(): errors.append('%s: %s not in glyph construction database'%(glyphName, stripedSuffixName)) else: if suffix is None: baseName = glyphConstruct[stripedSuffixName][0] else: if glyphName.find('.sc') != -1: baseName = ''.join([glyphConstruct[stripedSuffixName][0], suffix]) else: baseName = '.'.join([glyphConstruct[stripedSuffixName][0], suffix]) accentNames = glyphConstruct[stripedSuffixName][1:] return (baseName, stripedSuffixName, accentNames, errors) class AccentTools: def __init__(self, font, accentList): """several tools for working with anchors and building accents""" self.glyphConstructions = readGlyphConstructions() self.accentList = accentList self.anchorErrors = ['ANCHOR ERRORS:'] self.accentErrors = ['ACCENT ERRORS:'] self.font = font def clearAnchors(self, doProgress=True): """clear all anchors in the font""" tickCount = len(self.font) if doProgress: bar = ProgressBar("Cleaning all anchors...", tickCount) tick = 0 for glyphName in self.accentList: if doProgress: bar.label(glyphName) baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions) existError = False if len(errors) > 0: existError = True if not existError: toClear = [baseName] for accent, position in accentNames: toClear.append(accent) for glyphName in toClear: try: self.font[glyphName].clearAnchors() except IndexError: pass if doProgress: bar.tick(tick) tick = tick+1 if doProgress: bar.close() def buildAnchors(self, ucXOffset=0, ucYOffset=0, lcXOffset=0, lcYOffset=0, markGlyph=True, doProgress=True): """add the necessary anchors to the glyphs if they don't exist some flag definitions: uc/lc/X/YOffset=20 offset values for the anchors markGlyph=1 mark the glyph that is created doProgress=1 show a progress bar""" accentOffset = 10 tickCount = len(self.accentList) if doProgress: bar = ProgressBar('Adding anchors...', tickCount) tick = 0 for glyphName in self.accentList: if doProgress: bar.label(glyphName) previousPositions = {} baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions) existError = False if len(errors) > 0: existError = True for anchorError in errors: self.anchorErrors.append(anchorError) if not existError: existError = False try: self.font[baseName] except IndexError: self.anchorErrors.append(' '.join([glyphName, ':', baseName, 'does not exist.'])) existError = True for accentName, accentPosition in accentNames: try: self.font[accentName] except IndexError: self.anchorErrors.append(' '.join([glyphName, ':', accentName, 'does not exist.'])) existError = True if not existError: #glyph = self.font.newGlyph(glyphName, clear=True) for accentName, accentPosition in accentNames: if baseName.split('.')[0] in lowercase_plain: xOffset = lcXOffset-accentOffset yOffset = lcYOffset-accentOffset else: xOffset = ucXOffset-accentOffset yOffset = ucYOffset-accentOffset # should I add a cedilla and ogonek yoffset override here? if accentPosition not in previousPositions.keys(): self._dropAnchor(self.font[baseName], accentPosition, xOffset, yOffset) if markGlyph: self.font[baseName].mark = anchorColor if inFontLab: self.font[baseName].update() else: self._dropAnchor(self.font[previousPositions[accentPosition]], accentPosition, xOffset, yOffset) self._dropAnchor(self.font[accentName], accentPosition, accentOffset, accentOffset, doAccentPosition=1) previousPositions[accentPosition] = accentName if markGlyph: self.font[accentName].mark = anchorColor if inFontLab: self.font[accentName].update() if inFontLab: self.font.update() if doProgress: bar.tick(tick) tick = tick+1 if doProgress: bar.close() def printAnchorErrors(self): """print errors encounted during buildAnchors""" if len(self.anchorErrors) == 1: print 'No anchor errors encountered' else: for i in self.anchorErrors: print i def _dropAnchor(self, glyph, positionName, xOffset=0, yOffset=0, doAccentPosition=False): """anchor adding method. for internal use only.""" existingAnchorNames = [] for anchor in glyph.getAnchors(): existingAnchorNames.append(anchor.name) if doAccentPosition: positionName = ''.join(['_', positionName]) if positionName not in existingAnchorNames: glyphLeft, glyphBottom, glyphRight, glyphTop = glyph.box glyphXCenter = glyph.width/2 if positionName == 'top': glyph.appendAnchor(positionName, (glyphXCenter, glyphTop+yOffset)) elif positionName == 'bottom': glyph.appendAnchor(positionName, (glyphXCenter, glyphBottom-yOffset)) elif positionName == 'left': glyph.appendAnchor(positionName, (glyphLeft-xOffset, glyphTop)) elif positionName == 'right': glyph.appendAnchor(positionName, (glyphRight+xOffset, glyphTop)) elif positionName == '_top': glyph.appendAnchor(positionName, (glyphXCenter, glyphBottom-yOffset)) elif positionName == '_bottom': glyph.appendAnchor(positionName, (glyphXCenter, glyphTop+yOffset)) elif positionName == '_left': glyph.appendAnchor(positionName, (glyphRight+xOffset, glyphTop)) elif positionName == '_right': glyph.appendAnchor(positionName, (glyphLeft-xOffset, glyphTop)) if inFontLab: glyph.update() def buildAccents(self, clear=True, adjustWidths=True, markGlyph=True, doProgress=True): """build accented glyphs. some flag definitions: clear=1 clear the glyphs if they already exist markGlyph=1 mark the glyph that is created doProgress=1 show a progress bar adjustWidths=1 will fix right and left margins when left or right accents are added""" tickCount = len(self.accentList) if doProgress: bar = ProgressBar('Building accented glyphs...', tickCount) tick = 0 for glyphName in self.accentList: if doProgress: bar.label(glyphName) existError = False anchorError = False baseName, stripedSuffixName, accentNames, errors = nameBuster(glyphName, self.glyphConstructions) if len(errors) > 0: existError = True for accentError in errors: self.accentErrors.append(accentError) if not existError: baseAnchors = [] try: self.font[baseName] except IndexError: self.accentErrors.append('%s: %s does not exist.'%(glyphName, baseName)) existError = True else: for anchor in self.font[baseName].anchors: baseAnchors.append(anchor.name) for accentName, accentPosition in accentNames: accentAnchors = [] try: self.font[accentName] except IndexError: self.accentErrors.append('%s: %s does not exist.'%(glyphName, accentName)) existError = True else: for anchor in self.font[accentName].getAnchors(): accentAnchors.append(anchor.name) if accentPosition not in baseAnchors: self.accentErrors.append('%s: %s not in %s anchors.'%(glyphName, accentPosition, baseName)) anchorError = True if ''.join(['_', accentPosition]) not in accentAnchors: self.accentErrors.append('%s: %s not in %s anchors.'%(glyphName, ''.join(['_', accentPosition]), accentName)) anchorError = True if not existError and not anchorError: destination = self.font.compileGlyph(glyphName, baseName, self.glyphConstructions[stripedSuffixName][1:], adjustWidths) if markGlyph: destination.mark = accentColor if doProgress: bar.tick(tick) tick = tick+1 if doProgress: bar.close() def printAccentErrors(self): """print errors encounted during buildAccents""" if len(self.accentErrors) == 1: print 'No accent errors encountered' else: for i in self.accentErrors: print i