Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/opentype/OTProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ export default class OTProcessor {
this.features = {};
this.lookups = {};

// Setup variation substitutions
this.variationsIndex = font._variationProcessor
? this.findVariationsIndex(font._variationProcessor.normalizedCoords)
// Setup variation substitutions. FeatureVariations apply at the font's
// current location, defaulting to the normalized origin (all-zero coords)
// when no variation is set — matching HarfBuzz, which always evaluates them.
// fontkit only builds a _variationProcessor once coords are applied (or for
// CFF2), so a plain variable font would otherwise skip FeatureVariations
// (e.g. default-location bracket-layer substitutions) entirely.
let variationCoords = font._variationProcessor
? font._variationProcessor.normalizedCoords
: font.fvar
? new Array(font.fvar.axis.length).fill(0)
: null;
this.variationsIndex = variationCoords
? this.findVariationsIndex(variationCoords)
: -1;

// initialize to default script + language
Expand Down
33 changes: 33 additions & 0 deletions test/data/fonttest/TestFeatureVariations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# Generates TestFeatureVariations.ttf: a minimal TTF variable font whose `rvrn`
# FeatureVariations substitutes A -> A.alt under a condition set that covers the
# default location (normalized wght in [0, 1], which includes the default 0).
# Used to test that fontkit evaluates FeatureVariations at the default location.
# Build: python3 TestFeatureVariations.py TestFeatureVariations.ttf
import sys
from fontTools.fontBuilder import FontBuilder
from fontTools.varLib.featureVars import addFeatureVariations
from fontTools.pens.ttGlyphPen import TTGlyphPen


def box(x0, y0, x1, y1):
pen = TTGlyphPen(None)
pen.moveTo((x0, y0)); pen.lineTo((x0, y1))
pen.lineTo((x1, y1)); pen.lineTo((x1, y0)); pen.closePath()
return pen.glyph()


glyphs = ['.notdef', 'A', 'A.alt']
fb = FontBuilder(1000, isTTF=True)
fb.setupGlyphOrder(glyphs)
fb.setupCharacterMap({0x41: 'A'})
fb.setupGlyf({'.notdef': box(0, 0, 0, 0), 'A': box(100, 0, 500, 700),
'A.alt': box(100, 0, 400, 700)})
fb.setupHorizontalMetrics({g: (600, 100) for g in glyphs})
fb.setupHorizontalHeader(ascent=800, descent=-200)
fb.setupNameTable({'familyName': 'TestFeatureVariations', 'styleName': 'Regular'})
fb.setupOS2(); fb.setupPost()
fb.setupFvar(axes=[('wght', 0, 400, 1000, 'Weight')], instances=[])
fb.setupGvar({})
addFeatureVariations(fb.font, [([{'wght': (0, 1)}], {'A': 'A.alt'})], featureTag='rvrn')
fb.save(sys.argv[1])
Binary file added test/data/fonttest/TestFeatureVariations.ttf
Binary file not shown.
11 changes: 11 additions & 0 deletions test/shaping.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,4 +582,15 @@ describe('shaping', function () {
test('SHBALI-2/12', 'NotoSans/NotoSansBalinese-Regular.ttf', "ᬓ᭄ᭅᬸ", '23+2275|162+0|60@0,-1000+0');
});
});

describe('FeatureVariations', function () {
it('applies FeatureVariations at the default variation location', function () {
// TestFeatureVariations.ttf's rvrn feature substitutes A -> A.alt under a
// condition set covering the default location. HarfBuzz applies it; so
// must fontkit, even with no explicit variation instance selected.
let font = fontkit.openSync(new URL('data/fonttest/TestFeatureVariations.ttf', import.meta.url));
let { glyphs } = font.layout('A');
assert.deepEqual(glyphs.map(g => g.name), ['A.alt']);
});
});
});