New to Typophile? Accounts are free, and easy to set up.
I've completed a three-weight font with class kerning and everything. The font has many glyphs with alternates, but now after looking the font for awhile, I'm having second thoughts. I'd like to switch the alternate as the main character, for example, /four with /four.alt and /S with /S.alt and so on. Is there any easy way to change all instances of a glyph and its name in a font (including kerning, classes, code, etc)? Any macros or scripts that could handle this? I'm working with Fontlab 5.04.
EDIT: Anyone? Please help, it's a two days of manual work there, all very prone to mistakes.
26 Sep 2011 — 2:37pm
Here's a small script I wrote for renaming glyphs. It assumes Robofab is installed. You type in two list of glyph names--the current ones and the ones you'd like to replace them with. With your particular task, you'd probably have to run it three times--once to change your standard names to a temporary name (e.g., a.temp), once to change your alt glyphs to standard names, and once to change the temporarily named glyphs to the alt names. (Describing this sequence, it's apparent that it wouldn't be too hard to write a script that does it all in one run, but I have other things I'm supposed to be working on. Even so, it should still be faster than doing it manually.)
As for the class kerning, I'll assume you've created the kerning classes in FontLab's class panel. You could export your classes as a text file, do a round of search and replaces in a text editor (similar to the steps I described with the script), and then import the classes, replacing the existing ones. Note: the kerning will stay with the original glyphs, even when you rename them. Once you've done all this, rebuild the kern feature. There's probably an easier way to do this, but I've done it this way and it works.
Here's the script:
from robofab.world import CurrentFont, CurrentGlyph f = CurrentFont() tString = "a b c" # put your 'before' glyph list here listOne = tString.split(" ") tString = "a.1 b.1 c.1" # put your 'after' glyph list here listTwo = tString.split(" ") i = 0 while i < len(listOne): g = f[listOne[i]] g.name = listTwo[i] g.update() g.autoUnicodes() g.update() i = i+1 fl.UpdateFont() print "All done."27 Sep 2011 — 3:05pm
Scratch that script (unless it's useful for some reason). Here's a version that will swap any two lists of glyphs. Note: The lists (obviously) must be equal in length.
# Swap Lists of Glyphs from robofab.world import CurrentFont, CurrentGlyph f = CurrentFont() tString = "a b c" # <- put your first glyph list here listOne = tString.split(" ") tString = "A B C" # <- put your second glyph list here listTwo = tString.split(" ") listTemp = [] for i in listOne: listTemp.append(i + ".temp") l = len(listOne) def setGlyphNames(listA, listB): i = 0 while i < l: g = f[listA[i]] g.name = listB[i] g.update() g.autoUnicodes() g.update() i = i+1 if len(listOne) == len(listTwo): setGlyphNames(listOne, listTemp) # a -> a.temp setGlyphNames(listTwo, listOne) # A -> a setGlyphNames(listTemp, listTwo) # a.temp -> A fl.UpdateFont() print 'Done! %d pairs of glyphs swapped.' % l else: print "Can't run. Lists are not equal."Edit: I deleted the version I posted here earlier. This version is shorter and more efficient.
The second part about class kerning stands.
27 Sep 2011 — 2:11pm
Wonderful Mark! Many thanks. I've installed Robofab earlier, but haven't got the time to learn how to use it. This certainly gives good motivation to it.
Just one thing: what should be defined to listThree? Should there be a line that counts the number of items in the two other lists and puts that into listThree? Or did I misunderstood something? Also, I tried to make a lists of 19 glyphs, but FL macro window crops the end of the lines. That's no problem really, I can run the script in several passes.
It did cross my mind to TextWrangle the kerning (it's all in the OT kern feature), but it is still a lot of work. Can't python access that data in the same way it changes the names and unicodes?
27 Sep 2011 — 3:07pm
listThree? I don't see that. In an earlier version I posted it may have been in there (that was the name I previously used instead of "listTemp"). "listTemp" is a copy of "listOne" with ".temp" appended to the glyph names. It's necessary to assign these temporary names to the first set of glyphs before assigning the first list names to the glyphs in the second list. Once that's done, then it's safe to assign the names from the second list to the glyphs in the first list. You can't just sort of say a=A and A=a or you end up with both having the same names (wich won't work anyway).
It's up to you to make sure the two lists have the same number of items. (Presumably, you are swapping pairs of glyphs; one list has the first member of the pair, the other list has the other. So they should number the same.) Edit: I rewrote it to include code to check that the lists are the same length. It will tell you if they are not.
Also, it is possible to put way more than 19 glyphs in the lists. You can make the macro window wider, and you can scroll to the right. (I have run into a limit before, but it was way higher than 19.)
It's probably possible to access the OT features and class data with a script, but I haven't investigated that.
27 Sep 2011 — 2:46pm
Thanks, Mark!
27 Sep 2011 — 3:30pm
By the way, if you still have trouble with the length of the strings, you can build it in stages like this:
The rest continues on as before. You just have to make sure that all but the last one have a space at the end.
28 Sep 2011 — 7:34am
Great! The script works now and it switches the glyphs like it should. I did also manage to build up longer lists. I'm not sure if the code swaps the unicodes like it should. The .alt glyphs in my font don't have unicode values at all. After running the script, the both switched glyphs have the same unicode value. If I understand your script correctly, it actually switches the names of the glyphs, and then based on that name, it adds a unicode value. Could these unnecessary unicodes result from the autoUnicode function?
Maybe after I study Python a bit more, I can find out about accessing the OT features and class data. Considering also about starting a new thread for that :)
28 Sep 2011 — 8:09am
Hmm. The "autoUnicodes" method should remove the unicode value if there isn't one that corresponds to the glyph name. You can fix it by selecting the affected glyphs in the Font window and using the Generate Unicode... command from the Glyph menu. If you try to generate a font without fixing it, FontLab may hang.
28 Sep 2011 — 8:51am
Maybe clearing the unicode values before running autoUnicodes would ensure that existing values don't persist.
Here's a short script that swaps names and unicodes for any two selected glyphs in the font window:
And yes, it is possible to access classes and features through python via
fl.font.classesandfl.font.features, but those require further parsing to manipulate the data. It'd be nice to have a python method similar to the "Rename Glyph" tool in the Glyph menu, but that functionality seems to be absent from the python API.28 Sep 2011 — 2:15pm
Awesome script Mark! Thank you :)
Been looking for something like this for a long time. And it works like a charm on FL 5.04.
However on the new 5.1 I get an error saying can't find robofub.world or something.
Have any idea why that is?
28 Sep 2011 — 4:09pm
Yeah, I have the same problem. I'm not sure if RoboFab is supported in 5.1 yet. Haven't had time to investigate.
28 Sep 2011 — 5:16pm
> I'm not sure if RoboFab is supported in 5.1 yet.
Apparently is possible (not easy) to install it, but it doesn’t work quite well yet. I’d say stick to 5.0.4 for now.
28 Sep 2011 — 5:28pm
BTW: I didn’t prove the script yet, but sounds awesome, so I’ll do it very soon. Thanks for sharing, Mark!
28 Sep 2011 — 6:07pm
I asked about it on the RoboFab group ( http://groups.google.com/group/robofab ) and Tal Leming responded:
This worked for me using MacOS 10.6 and FL 5.1. Scripts run noticeably faster, too.
28 Sep 2011 — 6:55pm
OK, so it seems that the main problem is only with 10.7. Thanks again Mark!
28 Sep 2011 — 11:01pm
Thanks, Mark and especially Tal!
I am running Lion and will soon try your script with fingers crossed
Film at 11:00
[1:58 AM after trying script]
Got through fine and tried to run "export ufo" and got the following error message:
Traceback (most recent call last):
File "", line 29, in
ImportError: No module named robofab.world
29 Sep 2011 — 7:05am
That probably means you weren't able to install RoboFab successfully.
29 Sep 2011 — 9:02am
Argh :-/
FAIL
29 Sep 2011 — 9:03am
I would suggest joining the RoboFab group ( http://groups.google.com/group/robofab ) and asking for help there.
29 Sep 2011 — 10:06am
Thanks,Mark, I just did.
29 Sep 2011 — 10:24am
Mark's script works pretty well in Robofont as well. If I wasn't busy I'd figure out how to update the character set sorting. This would be great if I could pair it with groups and a gui.
# Swap Lists of Glyphs # Original version by Mark Simonson f = CurrentFont() tString = "A" # first glyph list listOne = tString.split(" ") tString = "B" # second glyph list listTwo = tString.split(" ") listTemp = [] for i in listOne: listTemp.append(i + ".temp") l = len(listOne) def setGlyphNames(listA, listB): i = 0 while i < l: g = f[listA[i]] g.name = listB[i] g.update() g.autoUnicodes() g.update() i = i+1 setGlyphNames(listOne, listTemp) # a -> a.temp setGlyphNames(listTwo, listOne) # A -> a setGlyphNames(listTemp, listTwo) # a.temp -> A print 'Done! %d pairs of glyphs swapped.' % l29 Sep 2011 — 10:33am
I tried to get it to work in RoboFont, but couldn't quite do it. It looks like the main difference is leaving out the import line?
29 Sep 2011 — 10:59am
Many thanks to you too, gargoyle! But wait a second, doesn't the "Rename Glyph" -tool actually rename _ALL_ references to the glyphs including classes, kerning, opentype features? I could go one by one renaming my glyphs like this: S -> S.temp, S.alt -> S, S.temp -> S.alt. Shouldn't this do the job right? Ok, I have something like 40 glyphs per weight to switch but I could totally skip the TextWrangler. Now how come I never thought of the Rename Glyph -tool before. Duh!
29 Sep 2011 — 11:05am
Actually, that's what I use if there are not too many to change.
29 Sep 2011 — 12:23pm
Regarding the duplicate Unicode values when swapping with .alt or other non-coded glyphs— apparently Robofab’s
autoUnicodesmethod doesn’t remove the value unless it finds a new one to substitute. Addingg.unicodes = []aboveg.autoUnicodes()will prevent coded glyphs from keeping their old values after swapping names with non-coded glyphs.As an exercise of my limited scripting abilities, I tried to extend my “Swap Two Selected Glyphs” macro to mimic Mark’s “Swap Lists of Glyphs”. This version is FontLab-only and does not require Robofab.
#FLM: Swap Two Glyph Lists """Swaps two sets of glyphs.""" # (based on Mark Simonson's "Swap List of Glyphs") f = fl.font listOne = "a b c" # <- put your first glyph list here listTwo = "A B C" # <- put your second glyph list here listOne, listTwo = listOne.split(" "), listTwo.split(" ") if len(listOne) == len(listTwo): for i in range( len(listOne) ): g1 = f.glyphs[ f.FindGlyph( listOne[i] ) ] g2 = f.glyphs[ f.FindGlyph( listTwo[i] ) ] fl.SetUndo(g1.index) fl.SetUndo(g2.index) g1.name, g2.name = g2.name, g1.name g1.unicodes, g2.unicodes = g2.unicodes, g1.unicodes fl.UpdateFont() print 'Done! %d pairs of glyphs swapped.' % len(listOne) else: print "Can't run. Lists are not equal."30 Sep 2011 — 10:44am
Excellent, thank you all!
30 Sep 2011 — 11:12am
That's clever--I didn't realize that kind of statement worked in that way. Could have saved all that nonsense with "listTemp" in my version.