unified-font-object / ufo-spec

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

Thinking about a variable-first version of UFO #175

Open belluzj opened 3 years ago

belluzj commented 3 years ago

I'm trying to think about how to make the Designspace + UFO format more suitable for variable fonts, so to speak "variable-first". That would mean several things potentially (throwing ideas here to start a discussion):

  1. adopting a new convention, instead of 1 designspace + several UFOs, have 1 designspace + 1 UFO with layers
  2. adding support for "variable" stuff in various places in UFOs (e.g. in kerning.plist, features.fea)
  3. adopting the proposal Designspace 5 to ensure that even complex setups can be fully described with a single designspace (e.g. when you want to build several VFs, or have "axes" that don't interpolate such as incompatible Roman vs Italics)
  4. merging the designspace and UFO formats? i.e. UFO top-level, includes a .designspace file next to features.fea? If merging is a bad word because people are opposed to the two formats working in lockstep, then UFO officially "adopting" designspaces like it has "adopted" .fea would be another way to put it (but personally I'd be up for this spec also specifying .designspace).

1. New convention: 1 designspace, 1 UFO with layers

I imagine that this could be a new "convention" on how to store font data using a Designspace + a UFO, which will be made workable by ongoing developments in the designspace format and the feature file syntax, and would solve a few issues with designspace + UFOs.

The idea would be that instead of having a designspace with 4 sources that point to 4 UFOs, you would still have the designspace but each source would point to a different layer within 1 UFO.

Benefits:

Drawbacks:

2. adding support for "variable" stuff in various places

kerning.plist, features.fea and probably other things should be able to express variations.

3. and 4: basically tying the knot between UFO and designspace

With Designspace 5 making sure that 1 designspace is always enough, we could also say that a UFO optionally has a designspace file like it can have a features.fea. And basically after this, one UFO without designspace = one static font, one UFO with a designspace = one font family, potentially variable in places, potentially not if you have discrete axes only...

Basically turn UFO into a top-level container for full font families.

5. the future

Once tools are used to the idea of opening up one UFO and finding all sorts of variable things inside, then UFO can evolve "internally" to become the format of the future that is perfect in every respect for the purpose of storing variable stuff.

belluzj commented 3 years ago

Also worth noting: in the plan above, the changes to the UFO format are opt-in, for users who don't care about them, they could not use the new .fea syntax extensions for variations, just put single numbers in kerning.plist as before (a single number is a small variation space that says: constant value over the whole space), and don't supply a .designspace file within the UFO, and not have more than one layer. They could even keep using a .designspace at the level above to link together several UFOs.

So, adopting this plan does not preclude users of the UFO format from benefiting from other improvements to the format that are orthogonal to this variation-related improvements, e.g. contents.plist becoming optional.

justvanrossum commented 3 years ago

All interesting ideas. (I'm currently imagining kerning as a spreadsheet (csv) with a column for left glyph/group, a column for right glyph/group, and N columns for N sources/designspace locations.)

I worry about layer organization. Layers have several purposes, I think these are the main ones:

How can we organize layers in a single UFO to (for example) make a variable color font? Can we do this with a naming convention on top of the current infrastructure?

belluzj commented 3 years ago

I think a naming convention would be good, but I also think that the layer names should not encode information about the contents or the purpose of the layer. I would say that all the "meaning" that is attached to a layer, should be stored as structured properties inside the layer (e.g. in the lib) or in the designspace if related to interpolation. I'm thinking of:

Then, from the structured data, the software can derive a safe and unique name by applying a naming convention, e.g.

f"{safe_name(layer.human_name}_{layer.color}{'#'+id if not unique else ''}{'.background' if layer.is_background else ''}"

or something like that (see later for other ideas).

Also, in terms of glyph has layers vs layer has glyphs, I think we could go two ways about this:

I don't know whether such a convention would be more "the UFO way" and make more sense/be more practical to handle for design tools or libraries, compared to pretending that glyphs have layers.

belluzj commented 3 years ago

I realize that I actually haven't addressed your question of variable color font in my example "ufo-specific" convention. Maybe we could have inside the various public.variations.wght100 glyphs that represent variations of the color layers from public.colors, such as A.color1.red

chrissimpkins commented 3 years ago

public.variations.wght100.wdth200

This seems to be a UFO master path naming convention that (some) designers use and the path to the master with axis values is duplicated in the designspace file, then the axis data are duplicated again in the designspace to define the axis values for the master that already specifies the axis definitions in the path.

I like this idea. You would get the master semantics in source paths and might be able to eliminate much of the duplicated axis data to streamline a tightly integrated designspace spec.

e.g.,

<source filename="GenericSans-opsz144-wght700-GRAD0.ufo">
      <location>
        <dimension name="Size" xvalue="144"/>
        <dimension name="Weight" xvalue="700"/>
        <dimension name="Grade" xvalue="0"/>
      </location>
</source>

becomes:

<source>
      <location>
        <dimension tag="opsz" xvalue="144"/>
        <dimension tag="wght" xvalue="700"/>
        <dimension tag="GRAD" xvalue="0"/>
      </location>
</source>

and defines the sources in public.variations.opsz144.wght700.GRAD0

LettError commented 3 years ago

How would this handle one ufo source appearing in multiple designspace locations?

chrissimpkins commented 3 years ago

Can you post an example design space? It wouldn't be typical to duplicate the UFO master directory paths with the same data would it? It would just require an approach to point to the same master sources from two or more locations in the designspace?

LettError commented 3 years ago

Yes, multiple locations should be able to point to the same source. For instance if you want to simulate a triangular space in 2D or pyramid like structures in 3. While maybe not typical, let's aim for method that does allows some freedom.

chrissimpkins commented 3 years ago

Where would you define that in the current designspace + UFO format? This would be two or three different filename source paths that include the same axis name/tag dimensions? Or they would be defined as instances? What is currently unique about the definition of those masters?

LettError commented 3 years ago

Not sure what you mean. You propose to include location data for a master in the UFO file name. As a ufo can appear multiple times in a single designspace, the scheme would not work. Also, not uncommon, a ufo can be part of multiple designspaces.

chrissimpkins commented 3 years ago

Let's take it out of the location data context. Can there be a unique "what it is" definition for the master data that moves from a unique UFO source directory path to a unique UFO source layer path? These "what it is" data are defined in the source layer path and, in some fashion, in the designspace file so that these are integrated and a user does not need to bother with "master" file paths in the designspace file. Use of axis location data seems like an obvious and semantically useful approach that is in use by developers, but you could allow users to call the master "Bob" = public.variation.Bob. :) They could also define the master with the string "opsz144.wght700.GRAD0" = public.variation.opsz144.wght700.GRAD0 in the designspace file to achieve the semantics that I suggested above but allow for the flexibility to support use cases that you point out. Unique UFO directory paths become unique master names with semantics that are used in the layer path.

My point is simply that threre is likely a way to make all of that pathing internal to the integrated designspace + UFO system so that a user does not need to care about paths when they define masters in a designspace file, but approach this in a way that allows humans (and software, but my focus here is on humans) to identify where the master sources are located because that will remain important. The "how it is used" issue belongs in a different area of the spec I think?

chrissimpkins commented 3 years ago

Re-reading Jany's comments above it sounds like he recommends against using the layer path for semantics:

I think a naming convention would be good, but I also think that the layer names should not encode information about the contents or the purpose of the layer.

I tend to disagree and think that from a user standpoint this would be helpful. I would much prefer to inspect a directory listing of layer paths to understand what they are than to dive into another XML file within the layer directory to gain this understanding.

belluzj commented 3 years ago

I also think that layer names should be readable by humans and carry meaning, but I don't think it's good to have names be the "source of truth" for that meaning. The meaning of the layer should be encoded in structured data in the layer's lib for example. From that data, the name can be generated by an algorithm, that will also take care of truncating if the name becomes too long for the filesystem (imagine a font with many axes, or a conditional layer with complicated conditions), or to add #{number} at the end to make sure it's unique, etc.

I'm thinking of the Glyphs.app "bracket layer" trick, which at first is nice and easy to use, but hits limitations when you want several conditions with "and" or "or" in between for example.

Also, about Erik's comments, I suggested putting the designspace location of the layer in its data, but he's right that a single layer can be used in several places, and where a layer fits in the designspace is already encoded by data in the <source> element that points to the layer, so there would be no point reproducing that data inside the layer's lib for example.

However I still think it would be nice to have in the layer's name. The library that writes UFOs to disk could take advantage of knowing where layers are used in the designspace file, and use that information to generate the layer names with the sort of scheme I was proposing. If the layer happens to be used in several locations, they could be listed all in the name, eg. public.variations.wght100.wdth{100,200} or public.variations.{wght100.wdth200,wght900.wdth100} or even the naming algorithm could give up and produce public.variations.variousLocations#1, or take advantage of a user-provided human name for that layer: public.variations.topOfPyramidInMyFancyDesignspace