-
Notifications
You must be signed in to change notification settings - Fork 51
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
issues with composites' anchor propagation #368
Comments
A couple of thoughts:
|
Another thing might be that depending on whether the script is RTL or LTR, the numbering _1, _2... should change direction as well? I'm not sure about that one and I don't know what Glyphs.app does actually. |
I know. I opened the issue here because that ufo2ft filter was copied from glyphsLib originally, and in glyphsLib that feature is enabled by default whereas in ufo2ft is opt-in (and hasn't been released either I think).
hm, this is getting too complicated and error prone. |
I made another discovery in Glyphs.app, by the way. But also, if one manually places top_1, bottom_1, etc. anchors in a ligature glyph, and the latter glyph is used as component in another composite glyph, which also is classified as "Ligature", then the anchors are propagated. So for example, in Noto Sans Arabic, the "lam_alef-ar" glyph (a Ligature) contains the following anchors: top_1, top_2, bottom_1 and bottom_2. The composite glyph "lam_alef-ar.fina" (and other similar composite glyphs that include lam_alef-ar as component) will have the propagated anchors and thus will be included in the mark2liga_arabic lookup with its own So, it appears that at least for the ligature glyphs anchor propagation is limited to glyphs that belong to the same subCategory. It would be nice if @schriftgestalt could confirm this or add his comments to this thread. The problem with this approach is that it'd be trickier to extend it for the generic UFO workflow. In glyphsLib we can look up the GlyphData.xml for a glyph's subCategory. In the ufo2ft filter, how do we know whether a glyph is a "Ligature". Parsing the glyph name for underscores is not a good idea. We might check for the presence of the private, glyphsLib-specific subCategory key in the GLIF lib. But I don't like that very much either.. 🤔 |
Like I said:
|
the most important finding so far is that I could not reproduce anywhere in Glyphs.app the behaviour that is implemented here: that code adds a numbered prefix This never happens in Glyphs.app to my knowledge. The _1, _2, etc. anchors have to be placed manually by the font designer. If they are there, and the glyph is a Ligature, the mark2liga will be generated. If that glyph is in turn used as component in another composite glyph also marked as Ligature, the latter composite glyph will inherit the same _1, _2. etc. anchors of its component. And if there is a name clash in this situation, similarly to what happens in the case of non-ligature composite glyphs, the last component in the list wins, overwriting propagated anchors from earlier components. There is never a numbered suffix added. We need to revise this algorithm accordingly if we care about matching Glyphs.app behaviour. |
Sorry I don't understand what you're talking about. |
We should store the glyph’s subCategory in the GLIF lib. |
I’m working on a big chunk of code and will comment here when I’m done. |
It's not just that we don't match Glyphs.app's output -- which for me would already be a good enough argument for changing this. But our current anchor propagation algorithm has serious problems on its own. The mere fact that two components have anchors with the same name does not make the composite glyph a ligature, for which a mark2liga lookup is needed. It could well be that the composite glyph happens to be using two components that happens to have anchor with same name, but itself it is not used as a ligature. It may have its own unicode codepoint, or it may not have one but it is in turn used as a component is some other compound glyph, but never accessed as such nor via liga features. Moreover, the anchor's numbered suffix is not simply the number of repetitions of that mark class in the ligature. It defines the index of the ligature component which the anchor attaches to. So to do it right, one would need to know, not only that a glyph is a ligature to begin with, but also how many "components" (in the GSUB or GDEF sense) this ligature is made of: i.e. the base characters one needs to type in order to trigger a ligature substitution, or how many times the caret splits the ligature glyph (+1). And then, one would need to map the component outlines (in the glyf sense) to these ligature components to properly number the propagated anchors with repeated names. And one should also take into account those ligature component that may have no anchors at all. A stupid example (I couldn't come up with a better one, sorry): make a ligature glyph called "f_a_t", composed of three components, "f", "a", and "t"; the "f" and "t" glyphs have a "top" anchor; "a" has none (for whatever reason). The propagated anchors I expect to get in the "f_a_t" glyph are: top_1 (from "f"), and "top_3" (from "t"); and a "NULL_2" (or maybe just "_2"?) placed anywhere because its position is not important, only the name is, which indicates that the second component of the ligature, "a", does not have any anchor. The mark feature will then have:
|
That is right.
what about acute, there the a and the acutecomb have a There is one tricky thing with the ligature anchors. If you have a lam_alef with some mark attached as a precomposed glyph. The ligature pos feature should use the top anchor from the mark and not from the base lam_alef. That can be automated when the mark is directly aligned to the right anchor. That can set from the info box with the anchor symbol. |
ufo2ft (since v2.0) has the propagateAnchors filters (copied from glyphsLib's). I guess we should now remove this functionality in glyphsLib and fix the ufo2ft's implementation to match the output of Glyphs.app. I'm thinking we could use the |
I ran into this (I assume) while trying to port Cantarell over to using UFOs and a Designspace: https://gitlab.gnome.org/GNOME/cantarell-fonts/merge_requests/14 Basically, by default, non-letters like Should glyphsLib just write the filter with explicit includes to the UFO's lib key? |
Made a test case: Cantarell-Regular-anchor-propagation-test.ufo.zip
|
@schriftgestalt Georg, when you have some spare time, could you please describe in some details (i.e. in a way that we could reliably replicate it in our pipeline) how automatic anchor propagation for composite glyphs is supposed to work in Glyphs.app? |
I’ll try:
|
looks like @schriftgestalt will or has already added a new attribute for GSComponent that allows to disable anchor propagation for that specific glyph, see https://github.com/schriftgestalt/GlyphsDev/issues/91#issuecomment-1256887150 |
ran into an issue related to this just now in comparing the output of fontc and fontmake: the current anchor propagation implementation here is generating ligature-like anchors for glyphs like 'DZ', which are not ligatures. Concretely this means that fontmake thinks these glyphs are ligatures for the purposes of mark-to-base (because it sees that there are numbered anchors) but thinks they are not ligatures for the purposes of mark2lig (since they aren't in the ligature subcategory.) fontc has the advantage that the anchor propagation there is based directly on source provided by Georg, whereas iirc the implementation here was more guesswork. The solution in any case is to just do 'last writer wins' in the case of duplicate names unless the propagation target is an explicit ligature glyph. |
For none ligature glyphs, you should get the anchors from the last component they show up. I just tried the DZ in Glyphs. The D and Z have a "top" anchor so you get the one from the Z. The D has a center anchor (for the bar to make a "Eth"). As the Z doesn’t have a center have it, you get the one from the D. |
@schriftgestalt This is inconsistent with your logic:
Why is the anchor propagation copying anchors that neither of these functions add or preserve? For example, from Set/Reset Anchors:
After anchor propagation:
Besides the inconsistencies, one can wonder why dz should have those additional anchors at all or why where they end up. |
Set Anchors is not checking the components. It "just" locks at the glyphData. What I was speaking about is the function |
@schriftgestalt I am well aware. |
I’m sorry. I didn't read you post.
That the
SetAnchor and traverseAnchors are meant for different circumstances and can’t be applied interchangeably. In the DZ case, I would expect that the user would add a "top" anchor if needed at all. |
…r from last component see #368 (comment)
… on GSFont Fixes #368 Translate fontc' propagate_anchors.rs from Rust to Python and apply to the GSFont in the 'preflight' step before it's passed on to UFOBuilder The Rust code was based off the original Glyphs.app's Objective-C code (Georg shared a snippet with us privately) and is as such more 'correct' then the old, reverse-engineered implementation. The old glyphsLib.builder.anchor_propagation module is deprecated but kept around in case users may instantiate the UFOBuilder class directly with propagate_anchors=True. Also, the existing `propagate_anchors` parameters of `to_designspace`, `to_ufos` and other high-level methods continues to have an equivalent effect but now controls the **new** propagate anchors logic. Clients such as fontmake do not need to do anything besides upgrading glyphsLib to use this.
…onent wins #368 (comment) test borrowed from #1007 (which this PR will supersede)
… on GSFont Fixes #368 Translate fontc' propagate_anchors.rs from Rust to Python and apply to the GSFont in the 'preflight' step before it's passed on to UFOBuilder The Rust code was based off the original Glyphs.app's Objective-C code (Georg shared a snippet with us privately) and is as such more 'correct' then the old, reverse-engineered implementation. The old glyphsLib.builder.anchor_propagation module is deprecated but kept around in case users may instantiate the UFOBuilder class directly with propagate_anchors=True. Also, the existing `propagate_anchors` parameters of `to_designspace`, `to_ufos` and other high-level methods continues to have an equivalent effect but now controls the **new** propagate anchors logic. Clients such as fontmake do not need to do anything besides upgrading glyphsLib to use this.
…onent wins #368 (comment) test borrowed from #1007 (which this PR will supersede)
…onent wins #368 (comment) test borrowed from #1007 (which this PR will supersede)
… on GSFont Fixes #368 Translate fontc' propagate_anchors.rs from Rust to Python and apply to the GSFont in the 'preflight' step before it's passed on to UFOBuilder The Rust code was based off the original Glyphs.app's Objective-C code (Georg shared a snippet with us privately) and is as such more 'correct' then the old, reverse-engineered implementation. The old glyphsLib.builder.anchor_propagation module is deprecated but kept around in case users may instantiate the UFOBuilder class directly with propagate_anchors=True. Also, the existing `propagate_anchors` parameters of `to_designspace`, `to_ufos` and other high-level methods continues to have an equivalent effect but now controls the **new** propagate anchors logic. Clients such as fontmake do not need to do anything besides upgrading glyphsLib to use this.
…onent wins #368 (comment) test borrowed from #1007 (which this PR will supersede)
while working on the ufo2ft MarkFeatureWriter (I'm adding support for abvm and blwm), I noticed a few issues with glyphsLib's current implementation of the propagate anchor feature in Glyphs.app, whereby composite glyphs inherit anchors from their components.
I started the discussion here https://gitter.im/fonttools-dev/glyphsLib?at=5b0eef9793dc78791c9a5a64
I'll try to summarize my comments again here.
The way glyphsLib's anchor propagation currently works is more or less the following: anchors are automatically propagated from the component's base glyphs to the composite glyphs that reference them; if the target composite glyph already has an anchor with the same name, the anchor from the component is not propagated. In addition, if the composite references more than one components (either the same component more than once, or different components having anchors with same name), then the anchors with clashing names are duplexed with a
_1
,_2
, suffix.The latter is a convention used in Glyphs.app to denote anchors on ligature components (the index is the ligature component number, starting from 1). They are translated in
pos ligature ... ligComponent ...;
or mark2liga (LookupType 5) lookups.One problem I noticed is that, neither glyphsLib nor Glyphs.app (from what I can tell) handle the case when in a ligature glyph, one of the ligature components has no marks attached.
The Feature File spec defines a special
<anchor NULL>
for this purpose:https://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#6.d
It's not clear what should happen when there is a gap in the sequence of
_1
,_2
, etc. numbers, if one or more ligature components contains no anchors. How is one supposed to denote such a NULL anchor (i.e. absence of anchors in a ligature component) within this anchor naming scheme? FWIW, Glyphs.app completely ignores such anchors on ligatures unless they start from_1
, and stops as soon as there is a gap. 🤔I also noticed a crucial difference between our anchor propagation algorithm and Glyphs.app's. We do the propagation to any composite glyph, and do the numbered suffix trick whenever there is any name collision in the propagated anchors.
Glyphs.app, on the other hand, does not seem to do any automatic anchor propagation when the composite glyph is marked as
subCategory
"Ligature" (either in the built-in GlyphData.xml database, or in the CMD-ALT-I panel for overriding these properties on a glyph-by-glyph basis). Marking the glyph to something else like 'Letter" re-enables the anchor propagation.Another important difference, which is related to this point, is that: we (i.e. ufo2ft MarkFeatureWriter) always write
pos ligature
rules every time we see suchtop_1
,bottom_2
, etc. anchors; Glyphs.app, on the other hand, only generates mark2liga lookups for glyphs that are marked explicitly as subCategory "Ligature" (and, as I noted above, contains some numbered anchors starting from 1 and with no gaps).Finally, there's the thing that in our current implementation the numbered suffix only gets added if there is more than one anchor with the same name being propagated from components to composite glyph. Now say a ligature glyph is made of multiple components but only one of them has a given anchor; in this case, this propagated anchor will not get the _1, _2, etc. suffixes because it does not clash with other components' anchors since it is the only one. But then the ligature composite glyph may end up with a mixture of ligature-ish anchors (suffixed with _1, _2, etc.) and other normal base anchors without the suffix. How should the MarkFeatureWriter treat this hybrid glyphs? Are they "ligature" (because they contains _1, _2, etc.) and so we add them to the mark2liga lookup and the GDEF Ligature class? Or are they "base" glyphs and we add them to the mark2base lookup? For the current implementation in ufo2ft MarkFeatureWriter, they are a bit of both: they are included in both the mark2base and the mark2liga lookups, referencing different markClasses. This looks kind of weird to me, that for some classes of marks the same glyph can be treated as either a ligature in the mark2liga lookup, or a single base glyph in the mark2base lookup. What about the GDEF GlyphClassDefinitions for this? Is it 1 (Base) or 2 (Ligature)? It can't be both.
I'm still not sure how I'm gonna fix these problems in the MarkFeatureWriter. However, as far as glyphsLib anchor propagation is concerned, I'm thinking that maybe we should disable it for composite glyphs that are classified as subCategory "Ligature" to better match Glyphs.app output.
The text was updated successfully, but these errors were encountered: