fonttools/Lib/robofab/tools/accentBuilder.py
Just van Rossum feb0046176 Moved Data, Lib and install.py one level down the tree
git-svn-id: http://svn.robofab.com/trunk@67 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c
2008-03-11 09:18:32 +00:00

349 lines
12 KiB
Python
Executable File

"""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