Access filter from Python

I want to use Offset Path filter from my script, but is there a way to do it? Do I need to reinvent the filter?

Take a peek in my Noodler or BroadNibber plugins.

I take reinvention as the answer. And thanks for the sample maths.

EDIT: I found how it’s using the filter, not math. Thank god.

It appears, that not all Filters are accessible via NSClassFromString.

"GlyphsFilterRemoveOverlap" for example is, while "GlyphsFilterRoundedFont" is not. The latter is what I need. Any suggestions please?

Edit
I found thisFilter = NSClassFromString("RoundedFont") which I can get a help on, but i still cannot figure out how to apply it to a layer. I am not even sure if it is actually something.

thisFilter.roundedLayer_error_(thisLayer, None) gives me this Error: TypeError: Expecting instance of RoundedFont as self, got one of NSKVONotifying_GSLayer

Any Class that is currently accessible in the namespace can be accessed that way. Are you sure you have the right name and that the class is really loaded? Perhaps you need to allocate and initialize first (depends on the kind of class method you want use).

No, I am not sure, but opposed to some phantasy class name, I don’t get a nonetype on this:

thisFilter = NSClassFromString("RoundedFont")
print thisFilter

Basically my question is: How can I call the Rounded Font Filter on a layer. It’s impossible to Trial and Error find any NSClassFromString that might be possible.

The documentation for the filters is on the ‘Core’ part of the documentation under the ‘GlyphsFilter’ protocol.

Of course I checked that (I usually do this and run a lot of dir() and help() on everything before asking here.

The core docu provides the same information as running help() on the mentioned (also attempted to be inititalized with alloc().init()) filter class instance.

So, It has some methods like runFilterWithLayer_error_ which doesn’t work as I expect.
I still cannot find this particular thing. Please help me out with some tangible info.

I tried (among many other things):

f = Glyphs.font
thisFilter = NSClassFromString("RoundedFont")
for layer in f.selectedLayers:
	thisFilter.runFilterWithLayer_error_(layer, None)

it returns:
TypeError: Expecting instance of RoundedFont as self, got one of NSKVONotifying_GSLayer
even though the documentation asks for the GSLayer as first attribute. So what is going wrong here?

BTW: If I’m super funny and pass the filter instance again into the first argument:
thisFilter.runFilterWithLayer_error_(thisFilter, None)
I get this:
TypeError: Expecting instance of RoundedFont as self, got one of RoundedFont

Ooooookay …

Do you want something like this?

roundCornerFilter = NSClassFromString("GlyphsFilterRoundCorner")
roundCornerFilter.roundLayer_radius_checkSelection_visualCorrect_grid_( Layer, noodleRadius, False, True, False )

Feel free to poke around in the Noodler source code.

The runFilter methods are geared for different API approaches, e.g. as custom parameters etc., and may not lend itself for calling it from outside. What you want is probably NSClassFromString("RoundedFont").roundedLayer_error_(layer,None).

You can find stuff like that quickly with my Method Reporter script: PutNSClassFromString("RoundedFont") into the class field, and filter out the methods which contain layer.

Thanks, somthing like this but not this. I am looking for the Rounded Font Filter.

I did, and I found your Round Corner version and the roundLayer_radius_checkSelection_visualCorrect_grid_ method, that’s exactly why and how I tried to get the Rounded Font working.


I also tried this before. The help() and the Method Reporter returns a few methods, which I all tried, and as I’ve written before: They don’t work. Here is exactly the same code with your proposal:

f = Glyphs.font
for layer in f.selectedLayers:
	NSClassFromString("RoundedFont").roundedLayer_error_(layer,None)

Error:

TypeError: Expecting instance of RoundedFont as self, got one of NSKVONotifying_GSLayer

And when doing this:

f = Glyphs.font
thisFilter = NSClassFromString("RoundedFont").alloc().init()
for layer in f.selectedLayers:
	thisFilter.roundedLayer_error_(layer,None)

Nothing happens at all.

This is almost correct. You only need to make an instance from the class before you can use it:

f = Glyphs.font
thisFilterClass = NSClassFromString("RoundedFont")
thisFilter = thisFilterClass.alloc().init()
thisFilter.setController_(f.currentTab)
for layer in f.selectedLayers:
	print thisFilter.runFilterWithLayer_error_(layer, None)

The method retunes an error message if there is a problem. One thing it needs is a stem value. It will try to get one from the font. And the current version needs to have access to the current tab. Thats why the setController_().

Oh Georg, thank you soooo much! This works indeed. I am super happy!

I am a little confused though, since I thought I initialized an instance before. I wasn’t aware that

thisFilter = NSClassFromString("RoundedFont").alloc().init()

and

thisFilterClass = NSClassFromString("RoundedFont")
thisFilter = thisFilterClass.alloc().init()

actually make a difference.

Anyway, are there plans to get rid of the setContoller_()? I wanted to make a filter that can be run on all selected Glyphs in the Font Overview.

They are the same. But in the last version of your code, you omitted the alloc().init().

You can stick in the windowController, too. But I removed the reliance on the controller.

Usually I implement the filter that they have one descriptive main method. In this case it is:

- (BOOL)roundedLayer:(GSLayer *)Layer error:(NSError *__autoreleasing*)error;

That means you can use it like this:

thisFilter = NSClassFromString("RoundedFont").alloc().init()
thisFilter.setVerticalStem_(verticalStem)
thisFilter.roundedLayer_error_(layer, None)
But that will not round single corners. You need to do that yourself with the `GlyphsFilterRoundCorner` filter:
  • (void)roundLayer:(GSLayer *)layer radius:(CGFloat)maxRadius checkSelection:(BOOL)checkSelection visualCorrect:(BOOL)visualCorrect grid:(BOOL)doRound;
thisFilterClass = NSClassFromString("GlyphsFilterRoundCorner")
thisFilterClass.roundLayer_radius_checkSelection_visualCorrect_grid_(layer, maxRadius, checkSelection, visualCorrect, doRound)

Note that you don’t need to run alloc().init() for this to work. This is a class method. You can get it from the + or - before the ObjectivC method description.

Thanks so much. It seems to be way harder than I expected. Still couldn’t manage to make it work other than with the active layer in Edit Window (which is not where I need it).

1a) with

thisFilter = NSClassFromString("RoundedFont").alloc().init()
thisFilter.setVerticalStem_(verticalStem)
thisFilter.roundedLayer_error_(layer, None)

I get: AttributeError: 'RoundedFont' object has no attribute 'setVerticalStem_'

1a) with

thisFilter = NSClassFromString("RoundedFont").alloc().init()
# thisFilter.setVerticalStem_(verticalStem)
thisFilter.roundedLayer_error_(layer, None)

Nothing hapens.


There is a difference between the result of both Rounding Filters (Round Corners & Round Font). What we need is the reuslt of Round Font. But also there is a difference in the result if you select all paths or if you don’t (the inner corners are vs. are not affected).

My plan was to run a script that:

  • 1. remove ovelaps (no problem)
  • 2. select all paths in the layer (no problem)
  • 3. run Round Corner filter
    for all layers in all selected glyphs (or as an export custom parameter)

Now I wonder what you mean by »But that will not round single corners. You need to do that yourself with the GlyphsFilterRoundCorner filter:« Because this one is not the filter I need (different results).

Could one consider it as a bug, that the Rounded Font filter gives different results if either the paths are selected or if the are not?

This is the main issue, why I need to wrap it into another filter to add the path selection automatically before this. We need this Round Font filter on export via a custom parameter.

Any ideas?