In the old glyphs there was a script called diacritic ligature maker (by @mekkablue ), but in the latest version, I can’t find it.
Is this now gone totally from your scripts or am I just missing something?
Cheers
Si
In the old glyphs there was a script called diacritic ligature maker (by @mekkablue ), but in the latest version, I can’t find it.
Is this now gone totally from your scripts or am I just missing something?
Cheers
Si
There should be an updated version in Filipe Negrão’s Scripts collection (under kerning → create_kerning_groups_for_ligatures), although I did not try it myself.
See also: Glyphs Scripts Index
It probably survives as a GitHub gist somewhere.
But better create contextual alternates rather than actual ligatures. My experience was that the ligatures were always too many and most of them made no sense (e.g. Gaelic lenited f with an Esperanto circumflex).
Thanks, I have managed to get my ligatures down to just a handful, well, around 15 now. I just wanted to see how many ligatures your script generated to see if it was worth adding them.
Hi all,
I’m trying to use Filipe Negrão’s script (Diacritic Ligature Maker), but I can’t get it to work. I am making a display typeface, and I have looked into contextual alternates and I don’t think they will do what I am trying to achieve. When running the script with the entire repository as a folder inside my Scripts folder, I get this error:
Creating variations for A_B:
Traceback (most recent call last):
File "Diacritic Ligature Maker.py", line 101
listOfLigatures = process( thisGlyphName )
^^^^^^^^^^^^^^^^^^^^^^^^
File "Diacritic Ligature Maker.py", line 76, in process
diacriticLetterLists = [ [n]+namesOfGlyphsContainingThisComponent(n) for n in namesOfLigatureLetters ]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Diacritic Ligature Maker.py", line 76, in <listcomp>
diacriticLetterLists = [ [n]+namesOfGlyphsContainingThisComponent(n) for n in namesOfLigatureLetters ]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Diacritic Ligature Maker.py", line 33, in namesOfGlyphsContainingThisComponent
listOfGlyphs = thisFont.glyphsContainingComponentWithName_masterID_( componentName, thisFontMasterID )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NSKVONotifying_GSFont' object has no attribute 'glyphsContainingComponentWithName_masterID_'
Please note that this is my first typeface made using Glyphs (and I am relatively new with font design softwares), so I am not 100% sure that I set up my anchors correctly. I do think I did however, as shown in the screenshot. I am also a noob at Python, and ChatGPT couldn’t help me on this one, despite hours of trying.
I also contacted Filipe Negrão within this forum’s messaging system (about 5 days ago), and he hasn’t answered yet. I’ll ping him here just in case, sorry for the spam @filipenegrao.
Last thing, if the problem does come from the code, I suggest the ability to manually input which letters can get which accents, (for example that it doesn’t generate A_Bacute, since, to my knowledge, no latin-based language uses that comb on that letter. If the script automatically just uses the available combs to generate diacritics used by any language though, that would be even better (I suppose), but that might be too much to ask.
Thanks in advance for your help!
You need to change glyphsContainingComponentWithName_masterID_
to glyphsContainingComponentWithName_masterId_
.
Thank you for such a fast answer! That did not work out of the box (I got other errors regarding syntax), but it was enough to get ChatGPT to make it work. I no longer get errors, with this updated function:
def createLigatureWithBaseLigature(newLigatureName, baseGlyphName):
individualGlyphNames = newLigatureName.split("_")
# Ensure each glyph has at least two components:
diacriticComponents = []
for n in individualGlyphNames:
components = thisFont.glyphs[n].layers[thisFontMasterID].components
if len(components) > 1:
diacriticComponents.append(components[1])
else:
print(f"Warning: {n} has fewer than 2 components. Skipping.")
diacriticComponents.append(None)
baseGlyphAnchorNames = [a.name for a in thisFont.glyphs[baseGlyphName].layers[thisFontMasterID].anchors]
baseGlyphAnchorNameLists = [ [ "_%s" % a[:-2] for a in baseGlyphAnchorNames if a.endswith("_%i" % (i+1)) ] for i in range(len(individualGlyphNames)) ]
newGlyph = GSGlyph()
newGlyph.name = newLigatureName
thisFont.glyphs.append(newGlyph)
for thisLayerID in [m.id for m in thisFont.masters]:
# add base ligature:
thisLayer = newGlyph.layers[thisLayerID]
thisLayer.components.append(GSComponent(baseGlyphName)) # Updated line
# add marks:
for i in range(len(diacriticComponents)):
thisComponent = diacriticComponents[i]
if thisComponent:
newComponent = GSComponent(thisComponent.componentName)
thisLayer.components.append(newComponent)
oldAnchor = thisComponent.anchor
if oldAnchor:
newAnchor = "%s_%i" % (oldAnchor, i+1)
else:
anchorBaseName = [a.name[1:] for a in thisComponent.component.layers[thisFontMasterID].anchors if a.name in baseGlyphAnchorNameLists[i]][0]
newAnchor = "%s_%i" % (anchorBaseName, i+1)
print(" %s -> %s" % (thisComponent.componentName, newAnchor))
newComponent.setAnchor_(newAnchor)
However:
These are just examples, but I suppose you understand what I mean. I could get those sorted manually, but I’m working with 300+ ligatures. So I’d really appreciate if you could help me automate it.
Thanks in advance!
Nevermind! I got it. I didn’t understand how the script worked.
If anyone else ever stumbles upon this and is looking for answers:
The script creates diacritic of the selected ligature (combinations included) for each glyph that 1. starts with the letter of one of the letters included in the selected ligature and 2. whose base diacritic it is referenced to.
Example 1: My copyright glyph was made out of a circle as a path with a C component inside it. The script recognised it as a C with a comb, and tried to generate A_C as A_copyright. The solution here is to decompose the C inside the copyright glyph, so that the script doesn’t recognise it as a C with a comb.
Example 2: The script wasn’t generating Aogonek because I had decomposed my ogonekcomb. The script, looking for an “A…”, found Aogonek but didn’t find the ogonek comb, so it supposed it was a separate glyph, just like how the copyright glyph isn’t a C with a comb.
If ever useful, here’s the updated script for anyone needing it! Big thanks for your help.
#MenuTitle: Diacritic Ligature Maker edited
# -*- coding: utf-8 -*-
from __future__ import division, print_function, unicode_literals
__doc__="""
For selected ligatures with appropriate anchors (top_1, top_2, etc.), all possible diacritic variations are created. E.g., A_h -> Adieresis_h, Aacute_h, A_hcircumflex, Adieresis_hcircumflex, etc.
"""
import itertools, traceback
thisFont = Glyphs.font # frontmost font
thisFontMaster = thisFont.selectedFontMaster # active master
thisFontMasterID = thisFontMaster.id
listOfSelectedLayers = thisFont.selectedLayers # active layers of selected glyphs
# brings macro window to front and clears its log:
Glyphs.clearLog()
Glyphs.showMacroWindow()
def ligatures( l1, l2 ):
returnList = []
for i in itertools.product( l1, l2 ):
returnList.append("_".join( list(i) ))
return returnList
def allLigaturesFromNameLists( l ):
if len( l ) > 1:
return ligatures( l[0], allLigaturesFromNameLists( l[1:] ) )
else:
return l[0]
def namesOfGlyphsContainingThisComponent( componentName ):
listOfGlyphs = thisFont.glyphsContainingComponentWithName_masterId_( componentName, thisFontMasterID )
nameList = [ g.name for g in listOfGlyphs if "_" not in g.name ]
return nameList
def createLigatureWithBaseLigature(newLigatureName, baseGlyphName):
individualGlyphNames = newLigatureName.split("_")
# Ensure each glyph has at least two components:
diacriticComponents = []
for n in individualGlyphNames:
components = thisFont.glyphs[n].layers[thisFontMasterID].components
if len(components) > 1:
diacriticComponents.append(components[1])
else:
print(f"Warning: {n} has fewer than 2 components. Skipping.")
diacriticComponents.append(None)
baseGlyphAnchorNames = [a.name for a in thisFont.glyphs[baseGlyphName].layers[thisFontMasterID].anchors]
baseGlyphAnchorNameLists = [ [ "_%s" % a[:-2] for a in baseGlyphAnchorNames if a.endswith("_%i" % (i+1)) ] for i in range(len(individualGlyphNames)) ]
newGlyph = GSGlyph()
newGlyph.name = newLigatureName
thisFont.glyphs.append(newGlyph)
for thisLayerID in [m.id for m in thisFont.masters]:
# add base ligature:
thisLayer = newGlyph.layers[thisLayerID]
thisLayer.components.append(GSComponent(baseGlyphName)) # Updated line
# add marks:
for i in range(len(diacriticComponents)):
thisComponent = diacriticComponents[i]
if thisComponent:
newComponent = GSComponent(thisComponent.componentName)
thisLayer.components.append(newComponent)
oldAnchor = thisComponent.anchor
if oldAnchor:
newAnchor = "%s_%i" % (oldAnchor, i+1)
else:
anchorBaseName = [a.name[1:] for a in thisComponent.component.layers[thisFontMasterID].anchors if a.name in baseGlyphAnchorNameLists[i]][0]
newAnchor = "%s_%i" % (anchorBaseName, i+1)
print(" %s -> %s" % (thisComponent.componentName, newAnchor))
newComponent.setAnchor_(newAnchor)
def process( thisLigatureName ):
if not "_" in thisLigatureName:
print(" %s is not a ligature." % thisLigatureName)
return None
thisLigatureNameWithoutExtension = thisLigatureName.split(".")[0]
namesOfLigatureLetters = thisLigatureNameWithoutExtension.split("_")
diacriticLetterLists = [ [n]+namesOfGlyphsContainingThisComponent(n) for n in namesOfLigatureLetters ]
listOfLigatures = [ n for n in allLigaturesFromNameLists( diacriticLetterLists ) if n != thisLigatureNameWithoutExtension ]
for diacriticLigatureName in listOfLigatures:
if not thisFont.glyphs[diacriticLigatureName]:
print(" Creating %s" % diacriticLigatureName)
try:
createLigatureWithBaseLigature( diacriticLigatureName, thisLigatureName )
except Exception as e:
print(traceback.format_exc())
print(" Error: Could not create '%s' in all masters. Does '%s' have all necessary anchors?" % ( diacriticLigatureName, thisLigatureName ))
# raise e
else:
print(" Skipping %s: already exists in font" % diacriticLigatureName)
return listOfLigatures
thisFont.disableUpdateInterface() # suppresses UI updates in Font View
allNewLigatures = []
for thisLayer in listOfSelectedLayers:
thisGlyph = thisLayer.parent
thisGlyphName = thisGlyph.name
print("Creating variations for %s:" % thisGlyphName)
thisGlyph.beginUndo() # begin undo grouping
listOfLigatures = process( thisGlyphName )
if listOfLigatures:
allNewLigatures += listOfLigatures
thisGlyph.endUndo() # end undo grouping
thisFont.enableUpdateInterface() # re-enables UI updates in Font View
print("\nCopy and paste this paragraph in an Edit Tab to see all ligatures mentioned above:\n\n/%s\n" % ( "/".join(allNewLigatures) ))