-
Notifications
You must be signed in to change notification settings - Fork 168
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
[spec] Variable FEA Syntax #1350
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,12 @@ layout: default | |
OpenType™ Feature File Specification | ||
--- | ||
|
||
Copyright 2015-2020 Adobe. All Rights Reserved. This software is licensed as | ||
Copyright 2015-2021 Adobe. All Rights Reserved. This software is licensed as | ||
OpenSource, under the Apache License, Version 2.0. This license is available at: | ||
http://opensource.org/licenses/Apache-2.0. | ||
|
||
Document version 1.25.1 | ||
Last updated 5 July 2020 | ||
Document version 1.26.0 | ||
Last updated XX XXXXXX 2021 **TODO: adjust date** | ||
|
||
**Caution: Portions of the syntax unimplemented by Adobe are subject to change.** | ||
|
||
|
@@ -48,6 +48,7 @@ Last updated 5 July 2020 | |
- [c. parameters](#4.c) | ||
- [d. lookupflag](#4.d) | ||
- [e. lookup](#4.e) | ||
- [i. Specifying FeatureVariations (variable fonts)](#4.e.i) | ||
- [f. markClass](#4.f) | ||
- [g. subtable](#4.g) | ||
- [h. Examples](#4.h) | ||
|
@@ -261,6 +262,9 @@ The following are keywords only in their corresponding table/feature blocks: | |
| [`location`](#9.i) | STAT table | ✅ | | ||
| [`ElidableAxisValueName`](#9.i) | STAT table | ✅ | | ||
| [`OlderSiblingFontAttribute`](#9.i) | STAT table | ✅ | | ||
| [`conditionset`](#X.x) TODO: link up | Variable sub and pos lookups | ❌ | | ||
| [`variation`](#4.e.i) | Variable sub and pos lookups | ❌ | | ||
|
||
|
||
The following are keywords only where a tag is expected: | ||
|
||
|
@@ -284,7 +288,7 @@ dflt # can be used only with the language keyword and as the language value wit | |
{ } braces Enclose a feature, lookup, table, or anonymous block | ||
[ ] square brackets Enclose components of a glyph class | ||
< > angle brackets Enclose a device, value record, contour point, anchor, or caret | ||
( ) parentheses Enclose the file name to be included | ||
( ) parentheses Enclose the file name to be included. Within a `<metric>`, enclose variable scalar values. | ||
|
||
|
||
<a name="2.e"></a> | ||
|
@@ -310,6 +314,28 @@ the values of various table fields [§[9](#9)]. | |
|
||
_[ Note: Multiple master support has been withdrawn as of OpenType specification 1.3. ]_ | ||
|
||
_[ Note: the following is unimplemented and is subject to change. ]_ | ||
|
||
For **variable fonts only**, a `<metric>` value can make use of the following syntax to specify how values should vary based on axis locations: | ||
``` | ||
(<location_spec1>:<value1> [...] <location_specN>:<valueN>) | ||
``` | ||
|
||
Notes: | ||
- A `<location_spec>` consists of one or more `<axis_tag>=<axis_location>` pairs, separated by commas | ||
- `<location_spec>:<value>` pairs are separated by spaces within the variable `<metric>` | ||
- multiple `<location_spec>:<value>` pairs may be specified within a variable `<metric>` | ||
|
||
Example showing a simple single-axis variable scalar value. The value is -100 when Weight is 200, and -150 when Weight is 900: | ||
``` | ||
<(wght=200:-100 wght=900:-150)> | ||
``` | ||
|
||
A more complex example combining single and multiple `<axis_location>`s extends the above example to specify that the value should be -120 when Weight is 900 and Width is 150: | ||
``` | ||
<(wght=200:-100 wght=900:-150 wght=900,wdth=150:-120)> | ||
``` | ||
|
||
<a name="2.e.iii"></a> | ||
#### 2.e.iii. Device table _[ Currently not implemented. ]_ | ||
|
||
|
@@ -1267,6 +1293,44 @@ rules, the implementation sorts the rules to avoid conflict; for example, the | |
ligature substitution rule for f_f_i will be written before the ligature | ||
substitution rule for f_i, no matter what their order is in the feature file. | ||
|
||
<a name="4.e.i"></a> | ||
#### 4.e.i Feature Variations (variable font feature replacement) | ||
|
||
For variable fonts *only*, it is possible to specify [FeatureVariations](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table), which allow the use of different lookups for different parts of the variation space. | ||
|
||
<a name="4.e.i.1"></a> | ||
##### 4.e.i.1 `conditionset` | ||
|
||
A named `conditionset` defines [conditions](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table) that can trigger a variable font lookup to swap in different lookups when the conditions are matched. A `conditionset` is simply a list of axis tags and min/max values. A conditionset is matched when all conditions are true. Syntax: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haven't followed this, but if .fea strictly follows the OTF FeatureVariations structure, it will end up fairly limited in its practical use. One of the reasons fontTools.varLib.featureVars became so complex* is that it allows both AND and OR. *) so complex that Behdad rewrote it almost from scratch, to make it behave and perform decently, after my initial sloppy implementation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are the situations when "OR" cannot be replaced by separate conditionsets, each having just AND? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. E.g.
I may be missing something but if you want 'OR' matching, you just define multiple conditionsets, each describing all 'AND' conditions, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In FontLab 7, when you assign a special tag to the target glyph, it replaces the source glyph under specified conditions:
if Each cond is a 2-letter (internal FL) axis tag with bounds, like If I need 'OR', I add a second special tag. I know there is some redundancy in this, but I’m genuinely curious if there are conditionsets that involve both OR and AND which cannot be expressed as a list of 'AND' conditionsets. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Just wasn't at the meeting where we all agreed that FEA should be regarded as assembly-equivalent, to be emitted by programs, and he's still trying to make it easy for humans to write. ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
TIL. Thanks, and agreed that's a good thing. Back to lurk mode! |
||
```fea | ||
conditionset <name of set> { | ||
<axis tag> <minValue> <maxValue>; | ||
# additional tag min max entries | ||
} <name of set>; | ||
``` | ||
For example, to define a "heavy" condition, for the 'wght' axis between 700-900: | ||
```fea | ||
conditionset heavy { | ||
wght 700 900; | ||
} heavy; | ||
``` | ||
|
||
<a name="4.e.i.2"></a> | ||
##### 4.e.i.2 `variation` (FeatureVariation) | ||
A `variation` is akin to a regular `feature` definition, with an added `conditionset` specifier to indicate that the feature should be active when the conditions of the conditionset are met. The syntax is: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OT gives us feature table substitutions - in the sense that one feature table is swapped out and another is swapped in. So if you have:
then if the heavy condition is met, the variation gets swapped in, the original feature gets swapped out, and only the If this is the intent, then this should be clearer in the text here. I suggest replacing "to indicate that the feature should be active" (which is kind of wooly anyway - "active"?) with "to indicate that the feature should be replaced by the rules contained in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I missed this important point when I first reviewed this. The way we implemented in feaLib, by using featureVars.addFeartureVariationsRaw, means that the new Feature table that replaces the default one when the conditions are met simply extends the existing feature's lookup list indices, but does not replaces it: Maybe we should try to support both use-cases somehow, making this explicit at the syntax level. |
||
```fea | ||
variation <feature tag> <conditionset name or NULL> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the meaning of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe it means this (quoting from ot spec):
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right,
Do you have a preference? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the difference between the two following?
and
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @moyogo |
||
# feature specifications | ||
} <feature tag>; | ||
``` | ||
For example, let's say we've defined a `lookup` called "heavy_symbols" that we want to swap in when the "wght" axis is between 700-900 (our "heavy" `conditionset` from above): | ||
``` | ||
variation rvrn heavy { | ||
lookup symbols_heavy; | ||
} rvrn; | ||
``` | ||
|
||
|
||
<a name="4.f"></a> | ||
### 4.f. markClass | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unclear what happens when Weight is neither 200 or 900.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simoncozens could you comment/clarify here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I've been actually using this, I can add more here:
<(-100 wght=900:-150)>
In answer to Fred's question, we could add something like "The value of the variable scalar at locations other than the provided master locations will be interpolated according to OpenType Font Variation interpolation rules" if necessary, but to me it feels tautological.