Skip to content
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

Thinking about a variable-first version of UFO #175

Open
belluzj opened this issue Mar 26, 2021 · 13 comments
Open

Thinking about a variable-first version of UFO #175

belluzj opened this issue Mar 26, 2021 · 13 comments

Comments

@belluzj
Copy link

belluzj commented Mar 26, 2021

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:

  • reduces duplication of fontinfo.plist
  • moves towards the Glyphs.app data model that designers prefer (opens the door for other structural changes to UFO that would make it possible to reduce duplication of inter-glyph data such as unicodes etc = we could say these need only to be in the "foreground" layer).

Drawbacks:

  • only 1 kerning.plist and 1 features.fea = no variations? See next point

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

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

  • as seen from work by various people on extensions to the feature file syntax, there is a need anyway to introduce ways to variate features across a designspace, and it would be interesting to allow these variations using a model that is "each number in features.fea can specify its own variations" as opposed to the current "duplicate the whole features.fea at each location where you want the features to be different". See the meeting notes at the bottom of this thread
  • about kerning.plist, why not do the same? Have 1 kerning.plist for the whole space, with entries such as: { (left side, right side): variation space for 1 number }. The concept of 1 number having its own variation space will probably be introduced anyway in other places so it could be adopted here too.
    • that would also mean that you can have kerning variations decorrelated from source locations, and you could represent in the sources Just's tweet about Tu by making that particular pair have a jump in its variations at the right place.

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
Copy link
Author

belluzj commented Mar 26, 2021

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
Copy link
Contributor

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:

  • source outline layer
  • background/sketch layer
  • color layer

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
Copy link
Author

belluzj commented Mar 29, 2021

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:

  • the human name of the layer (because human designers can't be expected to find unique and meaningful names for all the layers when a single UFO starts having many, so there should be an option to assign a "name" that is different from the actual name that gets used as an identifier. Same as what Glyphs.app does with IDs vs names)
  • whether the layer represents a colored layer
  • whether the layer is the background of another layer
  • etc

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:

  • way 1: pretend glyphs have layers, but store it as layers having glyphs (what glyphsLib does when converting from Glyphs.app)
    • that means: go through all glyphs, gather their layers and properties into a set, then create all the layers from that set, and place glyphs into the layers that they had originally.
    • what is a bit stupid about this is that it creates many top-level layers with only 1 glyph inside, e.g. when Glyphs.app users create backup layers with a date & time, there's little chance that the time matches for all glyphs so that creates a bunch of layers with few glyphs in each.
    • what might be good is that it keeps the door open to refactor UFO internally later to be glyph-has-layers and adopt similar conventions to Glyphs.app
  • way 2: invent a convention that is UFO-specific. Example that I'm making up:
    • There's always public.default which has all the glyphs from the default master with unicodes and all other properties, but doesn't contain any other "fancy" glyphs. Eg. A, A.smcp, dollar
    • There's public.colors that contains several "glyphs" for each color layer of the main glyphs, with structured data inside each glyph's lib and a naming convention, eg. A.color1.red, A.color2.blue, A.color3.red, A.color4.black, A.smcp.color1.red etc.
      • None of these should have unicodes or other data that is redundant from public.default
    • public.variations with glyph masters for various locations, same with structured data + naming convention, eg. A.var.wght100.wdth50, A.var.wght100.wdth200 etc.
      • same, no unicodes.
      • Also, I'm toying here with the idea of putting all variations in a bag to allow from the get-go the idea that not all glyphs will have masters in the same locations. (= brace layers)
        • Probably a bad idea from a filesystem point of view as this folder would have too many files compared to others.
        • Also maybe a bad idea because it wouldn't be obvious from looking at the file whether each master covers all glyphs (which is mostly the case for corner masters)
    • Alternative to public.variations: have a bunch of public.variations.wght100.wdth200 etc. for each master, with structured data that leads to a naming convention. Glyphs in the layer have the same name as glyphs in public.default
      • actually that's the solution that fits the current model and abilities of Designspace files the best = <source> element points to a layer in the UFO that has a location and the same/a subset of the glyphs from the default source.
    • public.conditionals contains conditional glyphs ("bracket layers"), 1 glyph per condition for a main glyph, with structured data (in designspace rules) and naming convention, e.g. dollar.BRACKET.300
    • etc. for more use-cases
    • Then each of the previously defined layers have a background layer:
      • public.background that can have backgrounds for any main glyph
      • public.colors.background that has backgrounds for color glyphs
      • public.variations.background
      • etc

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.

  • What I'm doing here with having several "variant" glyphs attached to each "main" glyph makes it sound like you could define kerning across the variable space by kerning the "variant" glyphs, e.g. kerning A.var.wght100.wdth50 against T.var.wght100.wdth50. I don't know whether that's desirable or not.

@belluzj
Copy link
Author

belluzj commented Mar 30, 2021

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
Copy link
Contributor

chrissimpkins commented Apr 3, 2021

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
Copy link
Contributor

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

@chrissimpkins
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

chrissimpkins commented Apr 3, 2021

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
Copy link
Contributor

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
Copy link
Contributor

chrissimpkins commented Apr 3, 2021

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
Copy link
Contributor

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
Copy link
Author

belluzj commented Apr 9, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants