font: disable discretionary ligatures by default (#8164)

Closes #5372

Discretionary ligatures (denoted by the OpenType feature tag `dlig`) are
sometimes used by programming fonts (e.g. Iosevka) to provide more
"complex" and uncommon ligatures that may be useful in a programming
context. Unfortunately, this has some nasty side effects with certain
Japanese fallback fonts (#5372) due to perhaps a misaligned
understanding of the OpenType spec[^spec].

The spec details that `dlig` ligatures should only be used to contract
sequences of glyphs together into one glyph, and that it should be used
only for "special effect", **at the user's preference** (emphasis mine).
Indeed, it also suggests that:

> UI suggestion: This feature should be off by default.

All of this, combined with the fact that historical, nowadays unused and
even unintelligible Kanji ligatures are explicitly included as examples
of discretionary ligatures, shows that in the Japanese context at least
that the "level of discretion" is significantly higher than what is
found in programming fonts, where it is more understood to be
"opinionated and uncommon", rather than "obsolete and unreadable".

Furthermore, it appears that a lot of common programming fonts don't
even make use of the `dlig` feature — JetBrains Mono, FiraCode and
MonoLisa lack a `dlig` feature altogether, while Inconsolata seems to
only use it for ligatures that are more commonly found in `liga` or
`calt`, such as the `->` ligature. To a lot of people, then, this change
would literally alter nothing.

Therefore, it's my opinion that we should disable `dlig` by default.
It's arguably not being used correctly in the programming font space (or
at least not in a way that's coherent with other fonts), and it only
provides a marginal benefit while potentially rendering entire sentences
in Japanese (and possibly other languages) unreadable out of the box.

If someone upgrades to tip or 1.2 and then asks "why aren't the
ligatures working anymore", then at least they can always just turn on
`dlig` by themselves.

[^spec]:
https://learn.microsoft.com/en-us/typography/opentype/spec/features_ae#tag-dlig
pull/8173/head
Mitchell Hashimoto 2025-08-07 07:14:39 -07:00 committed by GitHub
commit 6238103f21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 8 additions and 3 deletions

View File

@ -1833,7 +1833,10 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
grid_ptr.* = try .init(alloc, .{ .collection = c });
errdefer grid_ptr.*.deinit(alloc);
var shaper = try Shaper.init(alloc, .{});
var shaper = try Shaper.init(alloc, .{
// Some of our tests rely on dlig being enabled by default
.features = &.{"dlig"},
});
errdefer shaper.deinit();
return TestShaper{

View File

@ -287,7 +287,6 @@ pub const FeatureList = struct {
/// These features are hardcoded to always be on by default. Users
/// can turn them off by setting the features to "-liga" for example.
pub const default_features = [_]Feature{
.{ .tag = "dlig".*, .value = 1 },
.{ .tag = "liga".*, .value = 1 },
};

View File

@ -1296,7 +1296,10 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
grid_ptr.* = try .init(alloc, .{ .collection = c });
errdefer grid_ptr.*.deinit(alloc);
var shaper = try Shaper.init(alloc, .{});
var shaper = try Shaper.init(alloc, .{
// Some of our tests rely on dlig being enabled by default
.features = &.{"dlig"},
});
errdefer shaper.deinit();
return TestShaper{