Merge pull request #2086 from ghostty-org/split

font/shaper: split text runs on common bad ligature pairs
This commit is contained in:
Mitchell Hashimoto
2024-08-11 15:40:22 -07:00
committed by GitHub
3 changed files with 58 additions and 16 deletions

View File

@ -698,6 +698,27 @@ test "run iterator" {
while (try it.next(alloc)) |_| count += 1; while (try it.next(alloc)) |_| count += 1;
try testing.expectEqual(@as(usize, 3), count); 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" { test "run iterator: empty cells with background set" {

View File

@ -104,6 +104,30 @@ pub const RunIterator = struct {
if (j > self.i) style: { if (j > self.i) style: {
const prev_cell = cells[j - 1]; 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 the style is exactly the change then fast path out.
if (prev_cell.style_id == cell.style_id) break :style; if (prev_cell.style_id == cell.style_id) break :style;

View File

@ -12,21 +12,18 @@ const font = @import("font/main.zig");
/// If true, the default font features should be disabled for the given face. /// If true, the default font features should be disabled for the given face.
pub fn disableDefaultFontFeatures(face: *const font.Face) bool { pub fn disableDefaultFontFeatures(face: *const font.Face) bool {
var buf: [64]u8 = undefined; _ = face;
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,
};
// CodeNewRoman, Menlo and Monaco both have a default ligature of "fi" that // This function used to do something, but we integrated the logic
// looks really bad in terminal grids, so we want to disable ligatures // we checked for directly into our shaping algorithm. It's likely
// by default for these faces. // there are other broken fonts for other reasons so I'm keeping this
// // around so its easy to add more checks in the future.
// JuliaMono has a default ligature of "st" that looks bad. return false;
return std.mem.eql(u8, name, "CodeNewRoman") or
std.mem.eql(u8, name, "CodeNewRoman Nerd Font") or // var buf: [64]u8 = undefined;
std.mem.eql(u8, name, "JuliaMono") or // const name = face.name(&buf) catch |err| switch (err) {
std.mem.eql(u8, name, "Menlo") or // // If the name doesn't fit in buf we know this will be false
std.mem.eql(u8, name, "Monaco"); // // because we have no quirks fonts that are longer than buf!
// error.OutOfMemory => return false,
// };
} }