Scripting Hatch Outline

I’m trying to run Hatch Outline filter via scripting, but I cannot get the parameters right. I’m assuming I need to pass the number values in string as in other filters, and the 5th parameter (offset curve) is optional. That should be all I need, but I cannot get the results I want.

def filterForName(name):
	for filter in Glyphs.filters:
		if filter.__class__.__name__ == name:
			return filter

theFilter = filterForName('HatchOutlineFilter')
theFilter.processLayer_withArguments_(layer, ['', str(10), str(10), str(20), str(45), str(6)])

Also, the document page shows a sample with the filter name (‘GlyphsFilterOffsetCurve’), even though I seem to run it with empty string. What’s its purpose?

offsetCurveFilter.processFont_withArguments_(font, ['GlyphsFilterOffsetCurve', '10', '10', '1', '0.5', 'include:%s' % glyph.name])

Also, how can I apply the line cap style in Offset Curve? It’s not possible in custom parameters either.

Still no progress. Is the Hatch Outline not implemented correctly?

The labels need to part of the arguments, which can be in any order. Like so:

theFilter.processLayer_withArguments_(layer, [
	"OriginX:10",
	"OriginY:10",
	"StepWidth:20",
	"Angle45",
	"Offset:6",
	# "Background:1", # when sourcing outline from background
])

Which document page are you referring to?

What custom parameter syntax are you using? The format, including line caps, is described here:

Most filters implemented in Glyphs, have a GlyphsFilter prefix. When accessing them from the Filter custom parameter, you can you omit the prefix. When looking up the filter by class name, you need to keep the suffix.

Thanks. Hatch Outline now works.

Here, the example code refers to filter by name in the parameters (even though that’s supposedly done in filterForName(), and it can indeed be blank), and the rest is just a list of numbers in string which does work.
https://docu.glyphsapp.com/#GSApplication.filters

Which suffix do I need to keep? (I guess you mean prefix)

I am not sure what you mean since I can run Round Corner without filter name or parameter prefix like below, and it works.

theFilter = filterForName('GlyphsFilterRoundCorner')
theFilter.processLayer_withArguments_(layer,['', str(10), str(1)])

Thanks to Florian’s instruction, it at least gives me the correct result when applied to a real layer that exist in a font, but else. I’m trying to make a reporter plugin that previews the result of a filter. But Hatch Outline somehow results in empty bezierpath when run on a non-real layer like this:

copyLayer = layer.copy()
hatchOutlineFilter.processLayer_withArguments_(copyLayer, [parameters])
print(copyLayer.shapes)

This reports zero shapes. Other filters work fine though. What is wrong?

I still need help with this. Can anyone confirm Hatch Outline not working in layers not present in a font? I get no shapes in the layer after running the filter.

The layer needs to have a connection to a GSGlyph and GSFont to be processed correctly. You can do this like so:

copyLayer = layer.copy()
copyLayer.parent = layer.parent
hatchOutlineFilter.processLayer_withArguments_(copyLayer, [parameters])
print(copyLayer.shapes)

The .parent is the glyph of the layer. Set the same parent on the copy as the original layer. This does not add the layer copy to the glyph or font, but informs the layer about the glyph and font it would belong to.

You can also use this:

HatchOutlineFilterClass = NSClassFromString("HatchOutlineFilter")

copyLayer = layer.copy()
copyLayer.parent = layer.parent

origin = NSMakePoint(10, 10)
stepWidth = 10
angle = 10
offset = 10

HatchOutlineFilterClass.hatchLayer_origin_stepWidth_angle_offset_checkSelection_shadowLayer_(copyLayer, origin, stepWidth, angle, offset, False, None)
print(copyLayer.shapes)

The biggest difference is that this is a class method. So you don’t need an instance of the filter.

Thanks both. But it seems all other filters than HatchOutline don’t need to belong to a GSGlyph to return an expected result. Is that expected?

The filter computes the bounds of the shapes. And that needs to access the font (to get to brace/bracket and components). There could be situations where it would be save without getting to the font, but then it would fail in others and that would be terrible to debug.

I don’t quite understand why this filter in particular needs bounds value and glyph/font attached to do it while no other filter does, but thanks for the explanation.

In general, it’s best practice to set the font (“parent”) of a layer copy when processing it. For components to work, but also metrics and other metadata to come along. But I have not looked into what data in this specific case made the difference.