unified-font-object / ufo-spec

The official Unified Font Object specification source files.
http://unifiedfontobject.org
171 stars 30 forks source link

Better anchor definition #32

Open moyogo opened 8 years ago

moyogo commented 8 years ago

For ligature anchors, many UFOs designed with one authoring tool don’t work with other authoring tools as they have different ways of storing this information. Some authoring tools expect specific suffixes (like _1, _2,... or #1, #2,...) while others expect specific prefixes. It would be better to standardize this, either in the name or preferably with an attribute (for example ligatureIndex).

/cc @graphicore @khaledhosny @jamesgk

moyogo commented 5 years ago

@benkiel Indeed, Glyphs.app and ufo2ft follow specific rules that get translated to AFDKO feature syntax, which then gets translated into GPOS lookups. Adobe’s markFeatureWriter.py follows similar rules but isn’t as complete. In short, an anchor will translate to a mark-to-mark lookup only if it is both a base anchor and a mark anchor in glyphs that also have mark anchors, and an anchor will translate to a mark-to-liga lookup only if there are matching numbered anchors in ligature glyphs. FontForge follows rules much closer to the model of GPOS lookups.

@typoman The AFDKO feature syntax is higher level than GPOS lookups so you won’t see exactly the same structure. In short the _top is only defined once in the AFDKO example you give, but it will be duplicated, one in the mark lookup and another one in the mkmk lookup in GPOS. So you could very well have "_top", "_topmkmk" (at the same coordinates), and "topmkmk" in circumflexcomb that will produce the same GPOS structure as having "_top" and "top".

Glyphs.app and ufo2ft make the anchor type implicit as long as you follow their rules. FontForge makes the anchor type explicit. The UFO spec doesn’t currently mention that there is a relation between "_top" and "top".

Sorry, I’m just babbling about how different authoring tools work without providing a way forward. But we may need to either specify rules in the spec that follow or a close to what Glyphs.app and ufo2ft do or a way for the user to be more specific.

@gferreira’s skipExportAnchors as a list of anchor not to export would already help the user.

justvanrossum commented 5 years ago

I think we should reconsider adding a lib to anchors after all, given all the possible uses for anchors listed in this thread alone.

benkiel commented 5 years ago

One other thought, if we allowed a mark to have more than one type, it would accommodate what Glyphs/UFO does and also what FontForge does and allows for anchor re-use but also more specificity.

typoman commented 4 years ago

Here I've gathered some information about how tools deal with anchors in UFO. I hope this could be useful for people who are trying to figure out how things already work in UFO tools. Some of this is just copy-paste from different places in GitHub.

Glyphs app

Tibetan script (tibt) mark features do not go to abvm and blwm features but just one mkmk. Its lookup also doesn’t get lookupflag MarkAttachmentType as that prohibits the attachment of marks to different anchors than the previous mark.

FDK mark feature writer

I dropped some minor details. For more details read the source of markFeatureWriter in ufo2ft or WriteFeaturesMarkFDK in python module repo of the FDK.

What’s the issue

To interpret what’s the purpose of anchor there is lots of guesswork on the anchor name and glyph data during the binary compile. Some features are generated only on the compile without getting exposed to the user (composites anchor propagation). Writing OpenType features is a user’s job, not the compiler. An authoring tool could automate some of it (like propagating composite anchors) but there shouldn't be anything left for compiler's guessing. Also for the sake of transparency adding some attributes to anchor can help to remove the guesswork and give more control to the user. My suggestion is either to have an anchor lib or the following attributes:

Anchor Definition

Explicit anchor attributes to define its definition:

Lookup definition

Still, the compiler needs to guess how to write the flags, lookups and which feature the anchor definitions belong to. Again this can be automated by an authoring tool and saved in the UFO but it shouldn't be a compiler's guesswork. This could be achieved with data structures on the font level and anchor could reference that data. This could be one way of doing it:

In the end, maybe anchor.lib could be an easier solution instead of adding all these attributes. Since there is no anchor lib in UFO, personally I'm thinking that I will write the mark feature inside the data folder. I might have my own syntax for the feature file which is easier to read and diagnose but I haven't finished it yet.

schriftgestalt commented 4 years ago

I think Glyphs.app uses a naming scheme … For ligature anchors it would use _name_1, _name_2 etc, afaik, may be not fully correct though.

The ligature anchos are without underscore prefix.

Would defining glyph.lib["public.*"] keys for glyph type and ligature component count be helpful?

Glyphs.app defines several attributes for each glyph: script, category (letter, mark), subCategory (ligature, nonspacing), decomposition (list of glyphInfo objects (that each have all the above info)). ligature component count can be computed from the decomposition info (iterate it and count the number of none mark glyphs).

As explained in the issue you reference, adding lib to anchor is problematic, so please focus on solutions that don't require that.

In the issue it stats that is complicated and .ufo doesn't support it. Both problems can be solved.

Regarding anchor.type: what are the needed values for such a field? In the above comments I read: base, mark, ligature, entry, exit

sometimes anchos are only used to position components. That can be true for all five types. So a flag that says: Don't consider when generation GPOS.

For entry and exit a prefix is needed to differentiate it from the mark anchor. The prefix for mark anchor is _, the prefix for cursive anchor is # so it becomes #entry or #exit.

entry/exit anchor don’t need differentiations. They have a unique name. If you add any suffix (a '#' or an emoji) the anchors are used to (cursively) position components but are ignored when generating GPOS (see above). I see how it could be possible to add options to have multiple cursive lookups. But maybe I’ll wait what is decided on this topic here.

About the "Anchor Definition" and "Lookup definition": this is a very detailed and good description what is needed. But it might be too complex for most people. We need to find a good balance.

schriftgestalt commented 4 years ago

the anchor.type should allow custom types. e.g. In Glyphs.app, you can add a 'LSB' anchor to define alternate metrics for the palt feature (used in CJK fonts)

benkiel commented 4 years ago

My understanding of the current state is that we're going to add a .lib to the anchor, with that any type can be stored.

schriftgestalt commented 4 years ago

Good. The comment was just for the .type case.

benkiel commented 4 years ago

@schriftgestalt I think @justvanrossum has folded on this, .lib seems to be current consensus. We'll want to register some standard keys for anchor for common uses, of course.

schriftgestalt commented 4 years ago

I understand. I had wrote that just before he meeting and posted it because I had written it.

dy commented 3 years ago

Sorry if I miss or repeat something, just 2 cents to @khaledhosny comment:

One thing that would still be unsolved is contextual mark positioning. I have no idea how that would be supported for anchors without full OpenType machinery in the format. So I guess people will have to keep writing that manually or have font-specific scripts to handle them.

Would be useful to have anchors accessible by name in features.fea file via anchor format E, so that glif anchors automatically create anchorDefs - that would simplify contextual rules.

position cursive meem.medial <anchor entry_default> <anchor exit_default>;
position cursive @BACK_COND meem.medial' <anchor entry_special> <anchor exit_special> @AHEAD_COND;

Update. anchorDef is global, not per-character, so this solution is unsustainable.