-
Notifications
You must be signed in to change notification settings - Fork 6
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
Variable substitution: Too many regions with mixed features #53
Comments
The feature-level might be too coarse granularity. There's only so many features one can use... For example, if one wants to put all in |
Fair enough - this idea is at the level of damage control. If we don't do something better soon, I think it's preferable to add this, if there is something better we may not need it. |
Here's one idea: A new The problem with this is that it doesn't allow disabling lookups already in the feature. |
Interesting. You need the "original" feature in order to have a featureIndex to substitute for (and to match the script and language system, but that could just contain any lookups that apply at every position, or be empty if there are no such lookups. Then you add to that list by going through the new Ordering doesn't matter (if I'm remembering right) because the lookups are You could also add some sort of termination condition to this. Say that when you encounter a zero as the Seems a lot better for normal uses and at least right off the bat I'm not seeing any obvious holes. |
The main issue would be that most implementations prefer not to have to process feature-variations at the default location. If we lift that, then yes, what you describe should work IMO.
Correct.
Sgtm.
|
OK, one hole: This design doesn't follow the Microsoft/Apple convention of preserving the behavior of the default instance in a context that doesn't understand variable fonts. But maybe the group would be more flexible on that now. [Oops, scooped.] |
OK, suppose we do want to preserve that behavior for the sake of consistency. Then:
Done. |
Thinking about this a bit more, the change from terminating at the first matching element (either "globally" or per-feature-index) to continuing means that what I described as the "logical" analysis of conditions doesn't apply anymore. That's fine for many scenarios but we should think through the cases a bit more. I'm wondering things like whether it would be valuable to be able to negate a condition included in a set, or is that just an annoyance. (This seems more important with condition values, but we could also just modify that spec (assuming it goes through) to allow the negation of the calculated value.) |
If I understand you correctly, negation should be easy if we just say if |
Ah, that's clever. So if the So let's assume we have a convenient negation for every condition (I'll add a note to the other issue). Let's also explicitly note that the output of the search is a set of lookups, so if you add the same one multiple times it's the same as adding it once. Thinking about this more, I think this system would be formally complete, in that it would allow one to include a lookup according to any (standard) logical formula of conditions. That just follows from disjunctive normal form. And while I don't think it would be necessary (or desirable) to support arbitrary boolean formulas in, say, feature files, it does mean that any tricky situation can just be internally "phrased" as an arbitrary expression and then mechanically reduced to DNF, which is a good fallback. ( I think the remaining question would be whether we would want any extensions to the system to make it more practical to use and understand. Having thought about this a bit I think I know what the candidate would be. The practical problem with this new system is that it's harder (or, given what I've just said, maybe "wordier") to handle fallbacks or alternatives. Consider the other stereotypical VF case: you want a sudden change in the kerning between "T" and "o". And that you want to choose that point independently on three axes, and rather than being clever with a single value you want to use substitution (for whatever reasons). So you'll have one variable kerning value for one case and another for the other case. If you have conditions to express where you want one value you can just use them. The thing we've made difficult (or "wordy") is how to positively express where to put the other one. There are three equivalent ways of looking at the expression: it's a disjunction of negations, it's the negation of a condition set, or it's an "else" on a condition set. Of these the negation of a condition set seems most salient to this system we're imagining. Given the lack of intermediate versioning, that would probably mean bumping the major version on the FeatureVariations table and then adding flags to either the FeatureVariationRecord or the ConditionSet, one of which can be "negate the condition set". So according to my current, hours-old view, this would leave us here:
|
"else" sounds good to me. |
In that case I suppose we would rev the major version on the FeatureVariations table and specify that that version has a FeatureVariationRecord something like (ignoring my awkward choice of terminology):
And specify that either of the |
@behdad Do you think people will care if you can change the feature parameters by position with this mechanism or would that be so esoteric a need that we could just require that the parameters of the default feature table always apply? (Or, I suppose we could consider allowing mixing of the two mechanisms so that if there are entries of the existing kind you use the parameters from them. |
That's a good question. In HarfBuzz we currently only look at the default feature (in fact we have it as a face function, not a font with variation settings. We can spec it either way I think.
Yeah I think that's fine. |
I've been playing around with breaking this sketch down into subtables and records. One (tentative) decision I made is that with the move away from terminating at first match, I think it makes more sense to have the conditions sets below the feature index rather than above it. It may involve a bit of duplication in the font but this way you only have to go through the lists for those features that are active. Anyway, here's a very rough doc: new_substitution.pdf I suppose we could give the new FeatureVariations table version 1.1 by putting the |
Thanks Skef. Looks good to me. |
@behdad: "most implementations prefer not to have to process feature-variations at the default location" What is your basis for this statement? Harfbuzz and Apple, at least, do process feature-variations at the default location. In the attached test font feavartest.ttf.zip, /A (square) substitutes for /A.alt (circle) between wght = 1 (-1.0) and wght = 700 (0.5). It renders as a circle at default (wght = 400) in macOS and Harfbuzz (FontGoggles). Note also this ConditionTable in the TTX of Bahnschrift (a Microsoft system font), which, because it straddles 0, implies that Microsoft processes feature-variations at default. <ConditionTable index="0" Format="1">
<AxisIndex value="0"/>
<FilterRangeMinValue value="-0.8"/>
<FilterRangeMaxValue value="1.0"/>
</ConditionTable> |
You are indeed correct. I was surprised but checked the code and indeed it works. Anyway; that's even better for our new design. Thanks for pointing it out. |
@Lorp Even if most implementations that have variable font support are processing the variable-font-specific tables at the default location, there's still the question of allowing the default location to render correctly on systems that don't have any variable font support. Quite a bit of the existing design of variable fonts revolves around that issue, and it's hard to say if and when it's proponents will ease up on that. |
In any case, moving the feature indices above the condition sets also created a place for feature-specific flags. In the PDF write-up I added one to indicate whether the lookup indices from the "current" feature table (either the one in GSUB or the one selected by the existing mechanism -- usually the former) should be copied into the initial set. Having that control should make the issue moot -- copy them when it makes sense, don't copy them when it doesn't. |
Sgtm. Make them sorted by feature tag so it's easier to lookup and we're good I think. |
With luck this issue will be superseded by #57 |
More complete document with background: conditions.pdf
Suppose that you have a feature with three substitutions on one axis, as
well as a different feature with three entirely unrelated substitutions on a
different axis. For example, dollar changes at
wght
-.5, cent atwght
0,and euro at
wght
.5, while one changes atfoo
-.5, two atfoo
0, andthree at
foo
.5.Although these substitutions do not seem to be related in the abstract, and
will probably not appear to be related when encoded in a feature file, the
feature compiler must treat them as related when building the GSUB feature
variation subtable in its present form. This is because there is only one
unified list of feature variation records per table (GSUB or GPOS). So the
above pattern of substitution will not result in 6 regions plus the default (3
for
wght
, 3 forfoo
), but 15.With a "logical" encoding those would be (with redundant conditions omitted):
More generally, this means that whatever features use this table, the compiler
must carve up the geometry across all of them. Therefore the scaling problem is
just not within a feature but across all features.
This seems like a flaw in the current specification, especially because it is
not an inherent part of the mechanism.
Sketch of solution
Conceptually, all that is needed to solve the problem is advance knowledge of
which features are encoded among the feature variation records. This list
could be encoded by sorted tag in some new subtable.
Then, as the Feature Variation records are examined in order, instead of
stopping at the first match the search stops when a record corresponding to
each feature in the initial list is found. That way the entries for different
layout features can be interspersed without interfering with one another. If
the feature list is present you use the new search convention, if not you use
the old one.
The text was updated successfully, but these errors were encountered: