IIRC FontLab 5 does not paste kerning reliably. I think someone—John Hudson and Karsten Luecke?—wrote scripts to copy/paste kerning correctly. There were great Typophile threads about this…
I think the main issue is when you copy a selection of glyphs and an early glyph has a pair with a glyph that is later in the selection. When the first glyph is pasted, if a second glyph is not yet present as a key in the font, the second glyph/value is not assigned to the first. Something like that.
There are probably better ways around this using a Python script and RoboFab f.kerning objects.
Depending upon the scope and structure of the kerning to be transferred, I’ve also gone at this before via AFMs with some intermediate GREP work before appending/replacing.
I need to export / import only some blocks of a kerning table.
I wouldn't try to use the paste kerning option for that. What you want is a script that allows export of kerning for selected glyphs to a text file; the corresponding import script should have options to add to or replace existing kerning for the same glyphs in the target font source. I have scripts like this, but they're not mine to share. I suggest contacting Karsten.
Perhaps this would do - I have reworked one of my scripts, so it could suit your needs. It works by either glyph names or glyph references that form a kerning pair. You could use a dictionary or multiple lists, and it will copy kerning information from one pair to another for all MMM layers.
#FLM: Copy Kerning Pair to Pair v.1.0
# Vassil Kateliev (2016)
# - Dependancies ----------------
from FL import *
# - Functions ----------------
def copyKerningPair2Pair(leftSRC, rightSRC, leftDST, rightDST):
# - Source Glyph
if isinstance(leftSRC, basestring) and isinstance(rightSRC, basestring):
leftSRC = fl.font[fl.font.FindGlyph(leftSRC)]
rightSRC = fl.font[fl.font.FindGlyph(rightSRC)] #fl.font.FindGlyph(rightSRC)
# - Destination Glyph
if isinstance(leftDST, basestring) and isinstance(rightDST, basestring):
leftDST = fl.font[fl.font.FindGlyph(leftDST)]
rightDST = fl.font[fl.font.FindGlyph(rightDST)] #fl.font.FindGlyph(rightDST)
if len(leftSRC.kerning):
keySRC = [kpair for kpair in range(len(leftSRC.kerning)) if leftSRC.kerning[kpair].key == rightSRC.index]
if keySRC:
if len(leftDST.kerning):
keyDST = [kpair for kpair in range(len(leftDST.kerning)) if leftDST.kerning[kpair].key == rightDST.index]
if keyDST:
for layer in range(leftDST.layers_number):
leftDST.kerning[keyDST[0]].values[layer] = leftSRC.kerning[keySRC[0]].values[layer]
print 'DONE:\tCopied kerning data from %s to %s.' %(leftSRC.name + ':' + rightSRC.name, leftDST.name + ':' + rightDST.name)
else:
newKernPair = KerningPair(leftSRC.kerning[keySRC[0]])
leftDST.kerning.append(newKernPair)
print 'WARN:\tDestination pair %s not found! Created NEW!' %(leftDST.name + ':' + rightDST.name)
else:
print 'ERROR:\tSource pair %s not found!' %(leftSRC.name + ':' + rightSRC.name)
else:
print 'ERROR:\tSource pair %s not found!' %(leftSRC.name + ':' + rightSRC.name)
# - Run ---
# Example using Dictinary:
copyKerning = {('a','b'):('c','d')}
for key,value in copyKerning.iteritems():
copyKerningPair2Pair(key[0],key[1],value[0],value[1])
fl.UpdateFont()
Thanks a lot but I need something that works differently (and I don't work with MMs). I need something that point to specific pairs like in this piece of code:
left = ['exclamdown', 'questiondown', 'quotesingle', 'quoteleft','quoteright','quotesinglbase','hyphen', 'period', 'parenleft', 'bracketleft', 'braceleft', 'slash']
middle= ['A', 'AE', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Thorn', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
right = ['exclam', 'question', 'quotesingle', 'quoteleft', 'quoteright','hyphen', 'period', 'colon','parenright', 'bracketright', 'braceright', 'slash']
for A in left:
for B in middle:
print A,B
for A in middle:
for B in right:
print A,B
Then the kerning values should be stored in a dictionary (I think) and written in a text file.
Other script should be use to import these values in the receiving font.
I think I can write it but at the moment I am too busy and my coding skills are more than modest and takes me quite a lot of time.
So just to be clear, and I hope, that I understand you correctly: (1) You wish to copy kerning info from [left]:[middle] to [middle]:[right], or (2) you wish to store information for [left]:middle] and [middle]:[right] to a file and import it to another typeface... i presume you need the second option?
No, iterations between 'left' and 'middle' and 'middle' and 'right' is my system to distinguish left and right kerning. Basically I want to copy / store the kerning values of the pairs generated by:
for A in left:
for B in middle:
print A,B
for A in middle:
for B in right:
print A,B
...and then to be able to import these values to the same pairs of another font. It shouldn't be difficult.
One more remark: The script is now not MMM capable and will not create new pairs, just update the existing ones. I have left place for this "future functionality", but in my experience, so far I had little (and inconsistent) success making Fontlab create new pairs and assigning values to them - most of the time it ends in a mess with duplicate pairs and etc. Perhaps Adam can clarify us the process of adding new pairs (and hopefully in MMM space)
I need something able to create new pairs in the receiving fonts if necessary. What about creating the same pairs first by assigning them any value (for example: kerning[A,B]=1) and then importing the values stored from the inputing font? Why don't you use Robofab's objects?
OK, I figured out a (very basic) way to write a txt with my targeted kerning stored. Now I have to see how I import this information into another font.
import sys
sys.dont_write_bytecode = True
from robofab.world import CurrentFont
font = CurrentFont()
kerning = font.kerning
target = []
left = ['exclamdown', 'questiondown', 'quotesingle', 'quoteleft','quoteright','quotesinglbase','hyphen', 'period', 'parenleft', 'bracketleft', 'braceleft', 'slash']
middle= ['A', 'AE', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Thorn', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
right = ['exclam', 'question', 'quotesingle', 'quoteleft', 'quoteright','hyphen', 'period', 'colon','parenright', 'bracketright', 'braceright', 'slash']
for x in left:
for y in middle:
target.append( x+' ' + y +' '+ str(kerning[x,y]) )
for x in middle:
for y in right:
target.append( x+' ' + y +' '+ str(kerning[x,y]) )
import os.path
path = os.path.join( os.path.split(fl.font.file_name)[0] , "Kerning.txt")
f = open(path, 'w')
f.write("\r".join(target))
f.close()
I need something able to create new pairs in the receiving fonts if necessary. What about creating the same pairs first by assigning them any value (for example: kerning[A,B]=1) and then importing the values stored from the inputing font? Why don't you use Robofab's objects?
In reverse order I am not particularly found of Robofab once I established its limited-to-nonexistent MM capability, so I usually work with Fontlab directly.
I have revisited my work and the following code will create new pairs, if necessary - it was more simple than I imagined
Note, that this is in Python 2.7 syntax, so it needs one of the last version(s) of Fontlab (5.2). For Python 2.4 the dictionary comprehension must be converted to nested loops the way you do it.
Why do you need the kerning information saved in .txt file? Do you mean to edit it between import/export sessions? Otherwise i will suggest using pickle.dump() - it is reliable, faster and easier to use.
#FLM: Export Kerning Block v.1.1
# Vassil Kateliev (2016)
# - Dependancies ----------------
from FL import *
import itertools, pickle
# - Functions ----------------
def getKerningValue(fontSRC, leftSRC, rightSRC):
# - Source Glyph
leftSRC = fontSRC[fontSRC.FindGlyph(leftSRC)]
rightSRC = fontSRC[fontSRC.FindGlyph(rightSRC)]
if len(leftSRC.kerning):
keySRC = [kpair for kpair in range(len(leftSRC.kerning)) if leftSRC.kerning[kpair].key == rightSRC.index]
if len(keySRC):
return int(leftSRC.kerning[keySRC[0]].value)
else:
return None
else:
return None
def putKerningValue(fontDST, leftDST, rightDST, value):
# - Source Glyph
leftDST = fontDST[fontDST.FindGlyph(leftDST)]
rightDST = fontDST[fontDST.FindGlyph(rightDST)]
if len(leftDST.kerning):
keyDST = [kpair for kpair in range(len(leftDST.kerning)) if leftDST.kerning[kpair].key == rightDST.index]
if len(keyDST):
leftDST.kerning[keyDST[0]].value = value
print 'DONE:\tCopied kerning data to %s.' %(leftDST.name + ':' + leftDST.name)
else:
newKernPair = KerningPair(rightDST.index, value)
leftDST.kerning.append(newKernPair)
print 'WARN:\tDestination Kerning pair %s not found! Created NEW!' %(leftDST.name + ':' + leftDST.name)
else:
newKernPair = KerningPair(rightDST.index, value)
leftDST.kerning.append(newKernPair)
print 'WARN:\tDestination Kerning pair %s not found! Created NEW!' %(leftDST.name + ':' + leftDST.name)
# - Run ---
# -- Init
fontSRC = fl.font # Source font = CurrentFont! Change to desired font!
fontDST = fl.font # Destination font = CurrentFont! Change to desired font!
fileName = str(fl.font.file_name.replace('.vfb', '')) + '-KerningBlock.p' # Database file
left = ['exclamdown', 'questiondown', 'quotesingle', 'quoteleft','quoteright','quotesinglbase','hyphen', 'period', 'parenleft', 'bracketleft', 'braceleft', 'slash']
middle= ['A', 'AE', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Thorn', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
right = ['exclam', 'question', 'quotesingle', 'quoteleft', 'quoteright','hyphen', 'period', 'colon','parenright', 'bracketright', 'braceright', 'slash']
# -- Build
left2middle = {pair : getKerningValue(fontSRC, pair[0],pair[1]) for pair in itertools.product(left, middle) if getKerningValue(fontSRC, pair[0],pair[1]) != None}
middle2right = {pair : getKerningValue(fontSRC, pair[0],pair[1]) for pair in itertools.product(middle, right) if getKerningValue(fontSRC, pair[0],pair[1]) != None}
# -- Export
exportKerning = left2middle.copy()
exportKerning.update(middle2right)
pickle.dump(exportKerning, open(fileName, "wb"))
# -- Import
importKerning = pickle.load(open(fileName, "rb"))
print importKerning
for key,value in importKerning.iteritems():
putKerningValue(fontDST, key[0],key[1],value)
fl.UpdateFont()
Comments
There are probably better ways around this using a Python script and RoboFab f.kerning objects.
Depending upon the scope and structure of the kerning to be transferred, I’ve also gone at this before via AFMs with some intermediate GREP work before appending/replacing.
I wouldn't try to use the paste kerning option for that. What you want is a script that allows export of kerning for selected glyphs to a text file; the corresponding import script should have options to add to or replace existing kerning for the same glyphs in the target font source. I have scripts like this, but they're not mine to share. I suggest contacting Karsten.
What about creating the same pairs first by assigning them any value (for example: kerning[A,B]=1) and then importing the values stored from the inputing font?
Why don't you use Robofab's objects?
Now I have to see how I import this information into another font.
I have revisited my work and the following code will create new pairs, if necessary - it was more simple than I imagined
Note, that this is in Python 2.7 syntax, so it needs one of the last version(s) of Fontlab (5.2). For Python 2.4 the dictionary comprehension must be converted to nested loops the way you do it.
Why do you need the kerning information saved in .txt file? Do you mean to edit it between import/export sessions? Otherwise i will suggest using pickle.dump() - it is reliable, faster and easier to use.