[feaLib] Support lookups with mixed glyph/class-based kerning
Resolves https://github.com/behdad/fonttools/issues/456
This commit is contained in:
parent
14e1389d84
commit
faeba0c0c0
@ -617,12 +617,12 @@ class Builder(object):
|
|||||||
|
|
||||||
def add_class_pair_pos(self, location, glyphclass1, value1,
|
def add_class_pair_pos(self, location, glyphclass1, value1,
|
||||||
glyphclass2, value2):
|
glyphclass2, value2):
|
||||||
lookup = self.get_lookup_(location, ClassPairPosBuilder)
|
lookup = self.get_lookup_(location, PairPosBuilder)
|
||||||
lookup.add_pair(location, glyphclass1, value1, glyphclass2, value2)
|
lookup.addClassPair(location, glyphclass1, value1, glyphclass2, value2)
|
||||||
|
|
||||||
def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
|
def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
|
||||||
lookup = self.get_lookup_(location, SpecificPairPosBuilder)
|
lookup = self.get_lookup_(location, PairPosBuilder)
|
||||||
lookup.add_pair(location, glyph1, value1, glyph2, value2)
|
lookup.addGlyphPair(location, glyph1, value1, glyph2, value2)
|
||||||
|
|
||||||
def add_single_pos(self, location, prefix, suffix, pos):
|
def add_single_pos(self, location, prefix, suffix, pos):
|
||||||
if prefix or suffix:
|
if prefix or suffix:
|
||||||
@ -885,34 +885,6 @@ class MultipleSubstBuilder(LookupBuilder):
|
|||||||
return self.buildLookup_([subtable])
|
return self.buildLookup_([subtable])
|
||||||
|
|
||||||
|
|
||||||
class SpecificPairPosBuilder(LookupBuilder):
|
|
||||||
def __init__(self, font, location):
|
|
||||||
LookupBuilder.__init__(self, font, location, 'GPOS', 2)
|
|
||||||
self.pairs = {} # (glyph1, glyph2) -> (value1, value2)
|
|
||||||
self.locations = {} # (glyph1, glyph2) -> (filepath, line, column)
|
|
||||||
|
|
||||||
def add_pair(self, location, glyph1, value1, glyph2, value2):
|
|
||||||
key = (glyph1, glyph2)
|
|
||||||
oldValue = self.pairs.get(key, None)
|
|
||||||
if oldValue is not None:
|
|
||||||
otherLoc = self.locations[key]
|
|
||||||
raise FeatureLibError(
|
|
||||||
'Already defined position for pair %s %s at %s:%d:%d'
|
|
||||||
% (glyph1, glyph2, otherLoc[0], otherLoc[1], otherLoc[2]),
|
|
||||||
location)
|
|
||||||
val1, _ = makeOpenTypeValueRecord(value1)
|
|
||||||
val2, _ = makeOpenTypeValueRecord(value2)
|
|
||||||
self.pairs[key] = (val1, val2)
|
|
||||||
self.locations[key] = location
|
|
||||||
|
|
||||||
def equals(self, other):
|
|
||||||
return (LookupBuilder.equals(self, other) and self.pairs == other.pairs)
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
subtables = otl.buildPairPosGlyphs(self.pairs, self.glyphMap)
|
|
||||||
return self.buildLookup_(subtables)
|
|
||||||
|
|
||||||
|
|
||||||
class CursivePosBuilder(LookupBuilder):
|
class CursivePosBuilder(LookupBuilder):
|
||||||
def __init__(self, font, location):
|
def __init__(self, font, location):
|
||||||
LookupBuilder.__init__(self, font, location, 'GPOS', 3)
|
LookupBuilder.__init__(self, font, location, 'GPOS', 3)
|
||||||
@ -1107,29 +1079,45 @@ class ClassPairPosSubtableBuilder(object):
|
|||||||
self.subtables_.append(st)
|
self.subtables_.append(st)
|
||||||
|
|
||||||
|
|
||||||
class ClassPairPosBuilder(LookupBuilder):
|
class PairPosBuilder(LookupBuilder):
|
||||||
SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
|
SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
|
||||||
|
|
||||||
def __init__(self, font, location):
|
def __init__(self, font, location):
|
||||||
LookupBuilder.__init__(self, font, location, 'GPOS', 2)
|
LookupBuilder.__init__(self, font, location, 'GPOS', 2)
|
||||||
self.pairs = [] # [(location, gc1, value1, gc2, value2)*]
|
self.pairs = [] # [(gc1, value1, gc2, value2)*]
|
||||||
|
self.glyphPairs = {} # (glyph1, glyph2) --> (value1, value2)
|
||||||
|
self.locations = {} # (gc1, gc2) --> (filepath, line, column)
|
||||||
|
|
||||||
def add_pair(self, location, glyphclass1, value1, glyphclass2, value2):
|
def addClassPair(self, location, glyphclass1, value1, glyphclass2, value2):
|
||||||
self.pairs.append((location, glyphclass1, value1, glyphclass2, value2))
|
self.pairs.append((glyphclass1, value1, glyphclass2, value2))
|
||||||
|
|
||||||
|
def addGlyphPair(self, location, glyph1, value1, glyph2, value2):
|
||||||
|
key = (glyph1, glyph2)
|
||||||
|
oldValue = self.glyphPairs.get(key, None)
|
||||||
|
if oldValue is not None:
|
||||||
|
otherLoc = self.locations[key]
|
||||||
|
raise FeatureLibError(
|
||||||
|
'Already defined position for pair %s %s at %s:%d:%d'
|
||||||
|
% (glyph1, glyph2, otherLoc[0], otherLoc[1], otherLoc[2]),
|
||||||
|
location)
|
||||||
|
val1, _ = makeOpenTypeValueRecord(value1)
|
||||||
|
val2, _ = makeOpenTypeValueRecord(value2)
|
||||||
|
self.glyphPairs[key] = (val1, val2)
|
||||||
|
self.locations[key] = location
|
||||||
|
|
||||||
def add_subtable_break(self, location):
|
def add_subtable_break(self, location):
|
||||||
self.pairs.append((location,
|
self.pairs.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
|
||||||
self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
|
|
||||||
self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_))
|
self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_))
|
||||||
|
|
||||||
def equals(self, other):
|
def equals(self, other):
|
||||||
return (LookupBuilder.equals(self, other) and
|
return (LookupBuilder.equals(self, other) and
|
||||||
|
self.glyphPairs == other.glyphPairs and
|
||||||
self.pairs == other.pairs)
|
self.pairs == other.pairs)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
builders = {}
|
builders = {}
|
||||||
builder = None
|
builder = None
|
||||||
for location, glyphclass1, value1, glyphclass2, value2 in self.pairs:
|
for glyphclass1, value1, glyphclass2, value2 in self.pairs:
|
||||||
if glyphclass1 is self.SUBTABLE_BREAK_:
|
if glyphclass1 is self.SUBTABLE_BREAK_:
|
||||||
if builder is not None:
|
if builder is not None:
|
||||||
builder.addSubtableBreak()
|
builder.addSubtableBreak()
|
||||||
@ -1143,6 +1131,9 @@ class ClassPairPosBuilder(LookupBuilder):
|
|||||||
builders[(valFormat1, valFormat2)] = builder
|
builders[(valFormat1, valFormat2)] = builder
|
||||||
builder.addPair(glyphclass1, val1, glyphclass2, val2)
|
builder.addPair(glyphclass1, val1, glyphclass2, val2)
|
||||||
subtables = []
|
subtables = []
|
||||||
|
if self.glyphPairs:
|
||||||
|
subtables.extend(
|
||||||
|
otl.buildPairPosGlyphs(self.glyphPairs, self.glyphMap))
|
||||||
for key in sorted(builders.keys()):
|
for key in sorted(builders.keys()):
|
||||||
subtables.extend(builders[key].subtables())
|
subtables.extend(builders[key].subtables())
|
||||||
return self.buildLookup_(subtables)
|
return self.buildLookup_(subtables)
|
||||||
|
16
Lib/fontTools/feaLib/testdata/GPOS_2.fea
vendored
16
Lib/fontTools/feaLib/testdata/GPOS_2.fea
vendored
@ -1,18 +1,28 @@
|
|||||||
languagesystem DFLT dflt;
|
languagesystem DFLT dflt;
|
||||||
|
|
||||||
feature kern {
|
# Mixes kerning between single glyphs, and class-based kerning.
|
||||||
|
# https://github.com/behdad/fonttools/issues/456
|
||||||
|
lookup MixedKerning {
|
||||||
|
pos v v 14;
|
||||||
|
pos [D O Q] [T V W] -26;
|
||||||
|
} MixedKerning;
|
||||||
|
|
||||||
|
lookup GlyphKerning {
|
||||||
pos T one 100;
|
pos T one 100;
|
||||||
pos T two 200;
|
pos T two 200;
|
||||||
pos T two.oldstyle 200;
|
pos T two.oldstyle 200;
|
||||||
pos T three 300;
|
pos T three 300;
|
||||||
pos T four 400;
|
pos T four 400;
|
||||||
|
|
||||||
pos X a 100;
|
pos X a 100;
|
||||||
pos X b 200;
|
pos X b 200;
|
||||||
|
|
||||||
pos Y a 100;
|
pos Y a 100;
|
||||||
pos Y b 200;
|
pos Y b 200;
|
||||||
pos Y c <3 3 3 3>;
|
pos Y c <3 3 3 3>;
|
||||||
|
} GlyphKerning;
|
||||||
|
|
||||||
|
feature kern {
|
||||||
|
lookup GlyphKerning;
|
||||||
|
lookup MixedKerning;
|
||||||
} kern;
|
} kern;
|
||||||
|
|
||||||
feature vkrn {
|
feature vkrn {
|
||||||
|
56
Lib/fontTools/feaLib/testdata/GPOS_2.ttx
vendored
56
Lib/fontTools/feaLib/testdata/GPOS_2.ttx
vendored
@ -23,21 +23,67 @@
|
|||||||
<FeatureRecord index="0">
|
<FeatureRecord index="0">
|
||||||
<FeatureTag value="kern"/>
|
<FeatureTag value="kern"/>
|
||||||
<Feature>
|
<Feature>
|
||||||
<!-- LookupCount=1 -->
|
<!-- LookupCount=2 -->
|
||||||
<LookupListIndex index="0" value="0"/>
|
<LookupListIndex index="0" value="1"/>
|
||||||
|
<LookupListIndex index="1" value="0"/>
|
||||||
</Feature>
|
</Feature>
|
||||||
</FeatureRecord>
|
</FeatureRecord>
|
||||||
<FeatureRecord index="1">
|
<FeatureRecord index="1">
|
||||||
<FeatureTag value="vkrn"/>
|
<FeatureTag value="vkrn"/>
|
||||||
<Feature>
|
<Feature>
|
||||||
<!-- LookupCount=1 -->
|
<!-- LookupCount=1 -->
|
||||||
<LookupListIndex index="0" value="1"/>
|
<LookupListIndex index="0" value="2"/>
|
||||||
</Feature>
|
</Feature>
|
||||||
</FeatureRecord>
|
</FeatureRecord>
|
||||||
</FeatureList>
|
</FeatureList>
|
||||||
<LookupList>
|
<LookupList>
|
||||||
<!-- LookupCount=2 -->
|
<!-- LookupCount=3 -->
|
||||||
<Lookup index="0">
|
<Lookup index="0">
|
||||||
|
<!-- LookupType=2 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=2 -->
|
||||||
|
<PairPos index="0" Format="1">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="v"/>
|
||||||
|
</Coverage>
|
||||||
|
<ValueFormat1 value="4"/>
|
||||||
|
<ValueFormat2 value="0"/>
|
||||||
|
<!-- PairSetCount=1 -->
|
||||||
|
<PairSet index="0">
|
||||||
|
<!-- PairValueCount=1 -->
|
||||||
|
<PairValueRecord index="0">
|
||||||
|
<SecondGlyph value="v"/>
|
||||||
|
<Value1 XAdvance="14"/>
|
||||||
|
</PairValueRecord>
|
||||||
|
</PairSet>
|
||||||
|
</PairPos>
|
||||||
|
<PairPos index="1" Format="2">
|
||||||
|
<Coverage>
|
||||||
|
<Glyph value="D"/>
|
||||||
|
<Glyph value="O"/>
|
||||||
|
<Glyph value="Q"/>
|
||||||
|
</Coverage>
|
||||||
|
<ValueFormat1 value="4"/>
|
||||||
|
<ValueFormat2 value="0"/>
|
||||||
|
<ClassDef1>
|
||||||
|
</ClassDef1>
|
||||||
|
<ClassDef2>
|
||||||
|
<ClassDef glyph="T" class="1"/>
|
||||||
|
<ClassDef glyph="V" class="1"/>
|
||||||
|
<ClassDef glyph="W" class="1"/>
|
||||||
|
</ClassDef2>
|
||||||
|
<!-- Class1Count=1 -->
|
||||||
|
<!-- Class2Count=2 -->
|
||||||
|
<Class1Record index="0">
|
||||||
|
<Class2Record index="0">
|
||||||
|
</Class2Record>
|
||||||
|
<Class2Record index="1">
|
||||||
|
<Value1 XAdvance="-26"/>
|
||||||
|
</Class2Record>
|
||||||
|
</Class1Record>
|
||||||
|
</PairPos>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="1">
|
||||||
<!-- LookupType=2 -->
|
<!-- LookupType=2 -->
|
||||||
<LookupFlag value="0"/>
|
<LookupFlag value="0"/>
|
||||||
<!-- SubTableCount=2 -->
|
<!-- SubTableCount=2 -->
|
||||||
@ -112,7 +158,7 @@
|
|||||||
</PairSet>
|
</PairSet>
|
||||||
</PairPos>
|
</PairPos>
|
||||||
</Lookup>
|
</Lookup>
|
||||||
<Lookup index="1">
|
<Lookup index="2">
|
||||||
<!-- LookupType=2 -->
|
<!-- LookupType=2 -->
|
||||||
<LookupFlag value="0"/>
|
<LookupFlag value="0"/>
|
||||||
<!-- SubTableCount=1 -->
|
<!-- SubTableCount=1 -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user