Place anchors in all layers at custom positions

Does a plugin or script exist that allows me to place one or more anchors at specified locations in all layers of the selected glyphs?

Do you mean a custom position as in a specified point, such as (60, 100)?

Otherwise, the mekkablue script Batch Insert Anchor is your friend.

For the first case, a simply three-liner will do:

for s in Font.selectedLayers:
	for l in s.parent.layers:
		l.anchors.append(GSAnchor("your_anchor_name", (60, 100)))

If you want to insert multiple anchors, try this:

anchors_to_insert = {
	"one": (60, 100),
	"two": (120, 200)
}
	
for s in Font.selectedLayers:
	for l in s.parent.layers:
		for anchor_name in anchors_to_insert:
			l.anchors.append(GSAnchor(anchor_name, anchors_to_insert[anchor_name]))

I recommend reading the documentation for scripts as basic as this: Glyphs.app Python Scripting API Documentation — Glyphs.app Python Scripting API 3.2 documentation

Rainers script can only insert the identically positioned anchor in all layers. I’m after a way to insert the identically named anchor, but with different y-coordinate on each layer.

I’ll try whipping up a script.

Then I don’t understand. You want to insert an anchor in all layers, but with a different position each time? How do you want this position to be calculated, is it relative to something else, or do you want to define concrete number values?

I just want to hardcode the values.

Then try this. Adapt it to your needs:

anchors_to_insert = {
	"one": [(60, 100), (70, 110)],
	"two": [(120, 200), (140, 220)]
}
	
for s in Font.selectedLayers:
	for index, l in enumerate(s.parent.layers):
		for anchor_name in anchors_to_insert:
			l.anchors.append(GSAnchor(anchor_name, anchors_to_insert[anchor_name][index]))

Feel free to ask if anything isn’t clear about how this works.

This is the working script that I ended up with, in case someone should ever need this.

#MenuTitle: Set the upper case anchors for Playfair
#Created by Claus
# -*- coding: utf-8 -*-
__doc__="""
Sets the upper case anchors for Playfair.
"""

from GlyphsApp import *
from Foundation import NSPoint  # Import NSPoint from Foundation

font = Glyphs.font

font.disableUpdateInterface()

# Define anchor names
anchor_names = ["test_1", "test_2", "test_3"]

# Define a dictionary to store anchor coordinates per anchor name
anchor_coordinates = {
    "test_1": [
        (10, 10), (10, 20), (10, 30), (10, 40),
        (10, 50), (10, 60), (10, 70), (10, 80)
    ],
    "test_2": [
        (20, 10), (20, 20), (20, 30), (20, 40),
        (20, 50), (20, 60), (20, 70), (20, 80)
    ],
    "test_3": [
        (30, 10), (30, 20), (30, 30), (30, 40),
        (30, 50), (30, 60), (30, 70), (30, 80)
    ]
}

selected_glyphs = font.selectedLayers

for selected_layer in selected_glyphs:
    glyph = selected_layer.parent
    for anchor_name in anchor_names:
        for layer_id, coordinate in enumerate(anchor_coordinates[anchor_name], start=1):
            if layer_id <= len(glyph.layers):
                layer = glyph.layers[layer_id - 1]
                layer.anchors.append(GSAnchor(anchor_name, NSPoint(coordinate[0], coordinate[1])))

font.enableUpdateInterface()

# EOF

Some issues:

  • Why are you iterating over anchor_names in order to get the items from anchor_coordinates, which already contains the anchor names as keys?
    You can simply iterate over anchor_coordinates directly, or over anchor_coordinates.keys().
  • You are iterating over indices, not layer IDs. The variable layer_id is misleading, it’s a simple integer you get from the enumerate function. Use layer_index, if you must.
  • Why are you starting the enumeration at 1? You are then subtracting 1 again when your list index is out of range because… you started enumerating at 1. Remember that the first item in a list has the index 0.
  • Why are you making an NSPoint from the coordinates, which are already stored as a tuple?
    Simply use NSPoint(*coordinate), or, much easier, coordinate. GSAnchor takes a simple tuple like defined in the anchor_coordinates dictionary, no need to make an NSPoint out of it beforehand.

I don’t see how my original script doesn’t work for you, apart from the number of coordinates not necessarily matching your number of layers, but that can easily be fixed.

Would you mind explaining what wasn’t working?