harfbuzz / boring-expansion-spec

Better-Engineered Font Formats; Part 1. Boring Expansion
79 stars 8 forks source link

[`GSUB`] Move Lookup #28

Open behdad opened 3 years ago

behdad commented 3 years ago

There was a Move Lookup proposal from Martin Hosken in 2016: https://github.com/OpenType/opentype-layout/blob/63d169c92160832cf5b2333f28b559c80c9a4389/proposals/20151104-movelookup.md

There are several aspects of that proposal that I didn't fully support back then. Biggest one being that I thought the glyph-skipping part should be left to the Glyph Filtering https://github.com/be-fonts/boring-expansion-spec/issues/25 proposal.

I now have a proposal in mind that I'm satisfied with and I think satisfies Martin's requirements as well, and I try to write that down in a subsequent comment.

What mine does not encompass however, and I like to see if we can make it do, is to be equivalent-in-power to the AAT mort/morx RearrangementSubtable. That subtable can reorder up to two glyphs at each side. Quoting https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html:

Verb Results
0 no change
1 Ax => xA
2 xD => Dx
3 AxD => DxA
4 ABx => xAB
5 ABx => xBA
6 xCD => CDx
7 xCD => DCx
8 AxCD => CDxA
9 AxCD => DCxA
10 ABxD => DxAB
11 ABxD => DxBA
12 ABxCD => CDxAB
13 ABxCD => CDxBA
14 ABxCD => DCxAB
15 ABxCD => DCxBA

Not slating this lookup for first milestone.

behdad commented 3 years ago

cc @mhosken @jfkthame @nedley

behdad commented 3 years ago

Or maybe the full 2x2 move is overkill. I'll go ahead and post my simplified proposal.

behdad commented 3 years ago

Okay here's a simple yet most expressive Move Lookup:

struct SubstMove {
  uint16 format; // == 1
  Offset16To<Coverage> coverage;
  Offset16To<Coverage> target;
  int8 distance;
  uint8 verb;
};

The first coverage match current glyph like every other lookup. Then we seek forward or backward depending on sign of distance (which cannot be zero), skipping according to lookup-flags. If target is non-null and found glyph is not in target coverage, no move happens. Otherwise, move happens according to verb, similar to AAT semantics.

behdad commented 3 years ago

This move lookup is mostly designed to be invoked from (Chain)Context lookups, but is quite useful on itself as well. And is simple and expressive enough that I'm slating it for first milestone.