[MPEG-OTSPEC] Questions about variable composites

Skef Iterum skef at skef.org
Tue Aug 22 09:07:26 CEST 2023


Since the TypeCon meeting last week I've been preoccupied with a number 
of questions about the variable composite proposal.  I hope these are 
the good, potentially productive kind of question rather than the 
crabby, lets-just-not-do-this type, but they aren't small. I'm wondering 
if things might be significantly better with some significant changes.

I've boiled these thoughts into two interrelated multi-part questions, 
which I have added as issues in the boring-expansion-spec GitHub 
repository and will reproduce here. The linked issues seem like good 
contexts for subsequent discussion.


        Should variable composites be in the glyf table, and why? (#103
        <https://github.com/harfbuzz/boring-expansion-spec/issues/103>)

I think I understand how we got to the current proposal. Roughly:

 1. The variable composites specification extends the current glyf
    composites mechanism.
 2. Leaving variable composites in the glyf table saves some bytes, in
    that the offsets can remain in loca and you share the Tuple
    Variation Store offsets with gvar.

However:

 1. Maybe the overall variable composites system shouldn't be so
    directly derived from the glyf mechanism (see the other question).
 2. Everything proposed would seem to apply just as well to pulling
    outlines out of a CFF2 table.
 3. We already have a model for how to do this in an external table,
    that being COLR.

Right now, a system that understands COLR starts by looking in that 
table for an entry. If it finds one, it pulls path data from either glyf 
or CFF(2). If it doesn't, it falls back to glyf or CFF(2).  All of this 
happens "below"/subsequent to shaping:

(shaping) -> COLR -> (glyf | CFF(2))

It seems like what "variable compositing" amounts to is an additional, 
simplified shaping step. Call it "intra-glyph shaping", which occurs here:

(inter-glyph shaping) -> COLR -> (intra-glyph shaping) -> (glyf | CFF2)

The only reason the system doesn't already look like this is that the 
compositing data is stored in the glyf table.

Set aside the question of other potential changes and just consider the 
current proposal: If one wanted to have this mechanism for CFF2 also, 
would it be substantially different? If it had to live inside the CFF2 
table it would be formatted differently (with blends instead of a 
separate tuple variation store, perhaps using floats instead of 
fixed-point values of different scales, etc.) But would the meaning of 
the parameters be any different? Would other parameters be needed, or 
redundant, in the CFF2 case?  I don't see how, or why.

So suppose the system worked this way instead:

 1. Variable composite data is in its own table, call it "vcmp". It has
    some top-level mechanism for mapping data to GIDs analogous to that
    of COLR. The per-glyph tuple variation stores could be at an offset
    within the data.
 2. For the sake of argument, leave the per-glyph format exactly like it
    is now, except for an additional `hint flags` field in the component
    record (and minus the stuff needed to play nice in the glyf table,
    like `numberOfContours`).
 3. Prohibit the use of the existing `glyf` composite mechanism when
    using this separate table.
 4. Specify that when there is path data for a GID in the (glyf |
    CFF(2)) table, and that GID also has a composite entry, the path
    data is added with no transformation to the composite data. (This
    was asked for toward the end of the TypeCon meeting.)
 5. Specify that when there is hinting data for a GID in the (glyf |
    CFF(2)) table, (TrueType instructions or CFF stems) and that GID
    also has a composite entry, the relationship of the additional
    hinting data to the component hinting data is determined by the hint
    flags.

The main thing to work out with this system would be the details of the 
hint flags, but those problems are analogous for the two path data 
sources.  Maybe you need different flags for glyf and for CFF2 — which 
could overlap, because one assumes mixing sources is off the table — but 
in each case the only thing to be worked out is how to reconcile the 
hinting data. (We know this because we already have COLR, so we already 
have implementations that grab data from the bottom-level tables, alter 
the points according to affine transformations, and render the results.)

This change would have these cons:

 1. A modest increase in size, due redundant loca/gvar/vcmp offset
    entries and duplication across the tuple variation stores (header,
    regions).
 2. ?

And these pros:

 1. Assuming someone does the work of specifying the hinting behavior
    for CFF2, the system would work just as well with CFF2 and glyf.
    This reduces pressure on glyf format changes. CFF2 already goes
    above 64k glyphs, already supports cubics, and can already
    losslessly represent quadratics as cubics (at the cost of using
    floating point values in the conversion, when that precision is needed).
 2. If the composite system needs to do other things, its internal
    structure doesn't need to be so closely tied to the older glyf
    composite mechanism.

Note: Although I can't make any promises, I've thought through some of 
what one would need to say about CFF2 hinting and variable components. 
It does seem like there could be a viable model here where overall 
hinting quality could approach that of the current system. 
("Decompositing" to CFF (or CFF2) would involve some hinting 
compromises, but that's already true for CFF2 to CFF because of overlap.)


        Variable Compositing is analogous to shaping. So what about
        substitution? (#104
        <https://github.com/harfbuzz/boring-expansion-spec/issues/104>)

I noted in the other question that "variable compositing" seems to 
amount to an additional, simplified shaping step. However, as specified 
the system only includes an analog of positioning, and lacks an analog 
of substitution.

Let's consider a specific case.

Suppose that you are working in a model that has three conceptual 
layers: atoms, molecules, and glyphs. Perhaps these are exposed by a 
font editor.

For a given molecule, the designer decides she wants the outline of one 
atom to change within one sub-region of design space, and a different 
atom to change within a slightly different sub-region of design space. 
The molecule is used in 25 different glyphs. With the existing proposal 
it seems like there are two options:

 1. Force the designer to play tricks with the masters so that all
    versions of the atom interpolate, and then position the masters in
    design space right next to each other for quick interpolations. This
    increases the burden on the designer.
 2. Allow the designer to specify different, non-interpolable versions
    of an atom in different subspaces of design space, and sort things
    out in the compiled font.

In our example, it seems like the only option for 2 with the current 
proposal would be to use GSUB's `rvrn` or something similar. Given that 
the molecule has four versions (for each permutation of default and 
altered atom), you would need 100 GIDs to handle the 25 glyphs. You 
would also need to either duplicate the composite data for the other, 
always-present atoms across the four molecules, or add another "base 
molecule" layer into the hierarchy to collect that data together to 
avoid duplication.

Now, of course, in *some* cases you'll need to do something like this 
anyway: mainly when swapping an atom affects the metrics of the ultimate 
glyph. But such cases seem like the exception rather than the rule.

So:

 1. Should there be some more targeted way of supporting this sort of
    case in a variable composite model?
 2. Does this suggest that the model should draw a little bit more from
    GSUB/GPOS and perhaps be less closely tied to the older glyf model?
    (For example, you might need distinct positioning data for the
    different atoms that can be substituted into a molecule, perhaps
    loosely analogous to distinct contextual positioning GPOS rules that
    could apply after a substitution.)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.aau.at/pipermail/mpeg-otspec/attachments/20230822/0539a980/attachment-0001.html>


More information about the mpeg-otspec mailing list