-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
propagate_anchors should resolve component references in special layers (intermediate, alternate, etc.) #1017
Comments
I can add the |
@schriftgestalt thanks Georg, but since I have already written one, do you mind taking a look at #1018? |
Here is my version: 94cbdff |
thanks Georg, that's helpful, I hadn't noticed the dedup handling of multiple color layers with same palette index. I noticed that your code falls back to the layer's |
another thing I noticed is that Glyphs.app's built-in |
turns out this is actually more complicated than I had initially thought. I can't even make it work properly from within Glyphs.app. I made a test font where I combine intermediate and alternate layers and reference these from composite glyphs to see how they work together here: @schriftgestalt maybe we can schedule a meet to talk about this? I'm interested in this not just for a more accurate anchor propagation, but also because at some point in fontc we'll need to implement support for alternate layers (currently only intermediates are supported) and this is relevant for that as well. |
There is one more check I missed in my implementation. The code in my branch is from a method called:
So all layers that don’t have any attributes will return nil. |
thanks for the tip about glyph.layerGroups(), I did notice that the "Show master Compatibility" view was in fact getting the groups of compatible layers correctly. However sometimes the interpolation is failing (e.g. in the glyph "P" which I marked as non-export). We can take a look together when you're back from holidays, thanks! |
In both the old anchor_propagation.py and the new one ported from fontc (#1011), anchor propagation is only performed for 'master' layers. To get the anchors from a given component, we search the base glyph for a layer with the same
layerId
, however this only works for master layers. If we can't find a layer in the referenced base glyph that has the samelayerId
as the layer where a component is defined, we skip that component and move on.Therefore, composite glyphs inside non-master, special layers such as intermediate (brace) or alternate (bracket) layers, or the various color layers, do not currently inherit any anchors from their component glyphs.
Ideally we should match the built-in Glyphs.app implementation and propagate the anchors for special layers as well.
In order to do that, we need to fully implement the logic for resolving component references, which is encapsulated in the
GSComponent.componentLayer
property from Glyphs.app Python scripting API.As Georg explained in #853, a componentLayer is not the parent layer where a component is defined, but the layer which the component is pointing to in the base glyph, either one of the existing layers or a new one interpolated as needed.
The logic to resolve component references goes as follows (currently only point 1. is implemented):
First, search the component's base glyph for a layer with the same
layerId
; if we find one, this must be a 'master' layer for only the latter kind have the samelayerId
in all the glyphs - non-master layers have a uniquelayerId
, not useful for matching across glyphs.Else, look for a layer with the same
associatedMasterId
and a matchinglayerKey
. Wait - what's a layer key now?! It's what the privateGSLayer.layerKey()
method returns in the Glyphs.app python interface: a string that concatenates a layer's optional attributes such as the intermediate coordinates, alternate layer's axis range, whether it's a color layer or "color palette" or bitmap strike or SVG color layer. It may look like{600}
orRegular [550‹wght‹700]
orColor 1
or a combination of these. Also note, the layerKey is alwaysNone
for master layers (they don't need one) and for non-master/non-special (attribute-less) layers like the newly created ones used as draft or backup (they aren't compiled into the font anyway).So, when step 1 - direct match by
layerId
- is unsuccessful, step 2 is to search for a layer in the base glyph that has the samelayerKey
as the component's parent layer as well as the sameassociatedMasterId
; the latter is equally important, as the layerKey is not sufficient by itself for a match. There's a private obj-c method namedGSGlyph.layerForKey_masterId_
that one can call from the Glyphs.app's Python interface, which uses these two string parameters, layerKey and the master id associated with a layer, to find the matching layer in another glyph if any (orNone
if none is found); it's used internally by Glyphs.app to implement thecomponentLayer
property, Georg said.If the above steps failed and the component's parent layer is an intermediate layer, it means that the composite glyph where our component is defined has more (intermediate) layers than the base glyph it is pointing to - otherwise we would have found a match already (or it might also mean the base glyph has an intermediate layer with the same coordinates but it's associated with a different master...). No problem. To resolve this component reference, we need to create a new layer by interpolating the base glyph at the intermediate location of the component's parent layer. Easy peasy? I wish.
Else, if step 1 and 2 didn't find a match and step 3 doesn't apply to us (the component's parent layer is not an intermediate that we can interpolate), then fall back to the layer in the base glyph which has the same
associatedMasterId
as the component's parent layer. This is guaranteed to be there (unless the master ids are bogus) because all glyphs have >= len(font.masters) layers, and all layers (must) have a validassociatedMasterId
.So, the missing pieces for fully resolving component references, are:
implement the
layerKey
method, to peform step 2. This is relatively straightforward and can be tested by comparing with the output of Glyphs.app Macro panel.write code to build variation models for any glyph to interpolate new layers with it, in step 3. At some point we did have some of that code: @simoncozens made Build intermediate layers with non-intermediate components #971, and later removed in remove resolve_intermediate_components from preflight, no longer needed #992 because this was no longer needed. However even that is not complete because it doesn't take into account the fact that a given Glyphs.app's glyph may define multiple variation models: the presence of alternate ('bracket') layers means that there can be multiple versions of the same glyph (to be activated within some axis ranges) that are incompatible with one another, each with it's own set of source layers. Alternates can even contain additional intermediate layers, so it's not necessarily one model (same locations, same scalars) applied to distinct groups of source layers, but it could be different models altogether, defined over more or less source locations. Depending on which axis range the desired interpolation coordinates fall within, one needs to use one of these alternate models.
Once we have all of that, we should be able to take any component and find or create the layer it is referencing, even when the component is in a non-master, special layer; and we can complete the propagate_anchors code to properly match Glyphs.app.
Now for layerKey, I already have a branch, which I'll PR soon.
But for the glyph layer interpolator the work is more involved than I can commit to at this stage. I do have some WIP code but it's not in a reviewable state. I don't know if I'll have time to finish it myself right now, so I opened this issue and may also push it to a WIP branch for later.
The text was updated successfully, but these errors were encountered: