mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-12 10:48:39 +03:00
font/shaper: split text runs on common bad ligature pairs
Fixes #2081 Many fonts have a bad ligature for "fl", "fi", or "st". We previously maintained a list of such fonts in quirks.zig. However, these are so common that it was suggested we do something more systematic and this commit is that. This commit changes our text run splitting algorithm to always split on `fl`, `fi`, and `st`. This will cause some more runs for well behaved fonts but the combination of those characters is rare enough and our caching algorithm is good enough that it should be minimal overhead. This commit renders our existing quirks fonts obsolete but I kept that logic around so we can add to it if/when we find other quirky font behaviors.
This commit is contained in:
@ -698,6 +698,27 @@ test "run iterator" {
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
try testing.expectEqual(@as(usize, 3), count);
|
||||
}
|
||||
|
||||
// Bad ligatures
|
||||
for (&[_][]const u8{ "fl", "fi", "st" }) |bad| {
|
||||
// Make a screen with some data
|
||||
var screen = try terminal.Screen.init(alloc, 5, 3, 0);
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString(bad);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(
|
||||
testdata.grid,
|
||||
&screen,
|
||||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
try testing.expectEqual(@as(usize, 2), count);
|
||||
}
|
||||
}
|
||||
|
||||
test "run iterator: empty cells with background set" {
|
||||
|
@ -104,6 +104,30 @@ pub const RunIterator = struct {
|
||||
if (j > self.i) style: {
|
||||
const prev_cell = cells[j - 1];
|
||||
|
||||
// If the prev cell and this cell are both plain
|
||||
// codepoints then we check if they are commonly "bad"
|
||||
// ligatures and spit the run if they are.
|
||||
if (prev_cell.content_tag == .codepoint and
|
||||
cell.content_tag == .codepoint)
|
||||
{
|
||||
const prev_cp = prev_cell.codepoint();
|
||||
switch (prev_cp) {
|
||||
// fl, fi
|
||||
'f' => {
|
||||
const cp = cell.codepoint();
|
||||
if (cp == 'l' or cp == 'i') break;
|
||||
},
|
||||
|
||||
// st
|
||||
's' => {
|
||||
const cp = cell.codepoint();
|
||||
if (cp == 't') break;
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// If the style is exactly the change then fast path out.
|
||||
if (prev_cell.style_id == cell.style_id) break :style;
|
||||
|
||||
|
@ -12,21 +12,18 @@ const font = @import("font/main.zig");
|
||||
|
||||
/// If true, the default font features should be disabled for the given face.
|
||||
pub fn disableDefaultFontFeatures(face: *const font.Face) bool {
|
||||
var buf: [64]u8 = undefined;
|
||||
const name = face.name(&buf) catch |err| switch (err) {
|
||||
// If the name doesn't fit in buf we know this will be false
|
||||
// because we have no quirks fonts that are longer than buf!
|
||||
error.OutOfMemory => return false,
|
||||
};
|
||||
_ = face;
|
||||
|
||||
// CodeNewRoman, Menlo and Monaco both have a default ligature of "fi" that
|
||||
// looks really bad in terminal grids, so we want to disable ligatures
|
||||
// by default for these faces.
|
||||
//
|
||||
// JuliaMono has a default ligature of "st" that looks bad.
|
||||
return std.mem.eql(u8, name, "CodeNewRoman") or
|
||||
std.mem.eql(u8, name, "CodeNewRoman Nerd Font") or
|
||||
std.mem.eql(u8, name, "JuliaMono") or
|
||||
std.mem.eql(u8, name, "Menlo") or
|
||||
std.mem.eql(u8, name, "Monaco");
|
||||
// This function used to do something, but we integrated the logic
|
||||
// we checked for directly into our shaping algorithm. It's likely
|
||||
// there are other broken fonts for other reasons so I'm keeping this
|
||||
// around so its easy to add more checks in the future.
|
||||
return false;
|
||||
|
||||
// var buf: [64]u8 = undefined;
|
||||
// const name = face.name(&buf) catch |err| switch (err) {
|
||||
// // If the name doesn't fit in buf we know this will be false
|
||||
// // because we have no quirks fonts that are longer than buf!
|
||||
// error.OutOfMemory => return false,
|
||||
// };
|
||||
}
|
||||
|
Reference in New Issue
Block a user