mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
fix(font/shape): don't require emoji presentation for grapheme parts
Also update shaper test that fails because the run iterator can't apply that logic since `testWriteString` doesn't do proper grpaheme clustering so the parts are actually split across multiple cells. Several other tests are technically incorrect for the same reason but still pass, so I've decided not to fix them here.
This commit is contained in:
@@ -1015,25 +1015,35 @@ test "shape emoji width long" {
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
|
||||
var buf: [32]u8 = undefined;
|
||||
var buf_idx: usize = 0;
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F9D4, buf[buf_idx..]); // man: beard
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F3FB, buf[buf_idx..]); // light skin tone (Fitz 1-2)
|
||||
buf_idx += try std.unicode.utf8Encode(0x200D, buf[buf_idx..]); // ZWJ
|
||||
buf_idx += try std.unicode.utf8Encode(0x2642, buf[buf_idx..]); // male sign
|
||||
buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // emoji representation
|
||||
|
||||
// Make a screen with some data
|
||||
// Make a screen and add a long emoji sequence to it.
|
||||
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
var page = screen.pages.pages.first.?.data;
|
||||
var row = page.getRow(1);
|
||||
const cell = &row.cells.ptr(page.memory)[0];
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 0x1F9D4 }, // Person with beard
|
||||
};
|
||||
var graphemes = [_]u21{
|
||||
0x1F3FB, // Light skin tone (Fitz 1-2)
|
||||
0x200D, // ZWJ
|
||||
0x2642, // Male sign
|
||||
0xFE0F, // Emoji presentation selector
|
||||
};
|
||||
try page.setGraphemes(
|
||||
row,
|
||||
cell,
|
||||
graphemes[0..],
|
||||
);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(
|
||||
testdata.grid,
|
||||
&screen,
|
||||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
@@ -540,25 +540,35 @@ test "shape emoji width long" {
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
|
||||
var buf: [32]u8 = undefined;
|
||||
var buf_idx: usize = 0;
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F9D4, buf[buf_idx..]); // man: beard
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F3FB, buf[buf_idx..]); // light skin tone (Fitz 1-2)
|
||||
buf_idx += try std.unicode.utf8Encode(0x200D, buf[buf_idx..]); // ZWJ
|
||||
buf_idx += try std.unicode.utf8Encode(0x2642, buf[buf_idx..]); // male sign
|
||||
buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // emoji representation
|
||||
|
||||
// Make a screen with some data
|
||||
// Make a screen and add a long emoji sequence to it.
|
||||
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
var page = screen.pages.pages.first.?.data;
|
||||
var row = page.getRow(1);
|
||||
const cell = &row.cells.ptr(page.memory)[0];
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 0x1F9D4 }, // Person with beard
|
||||
};
|
||||
var graphemes = [_]u21{
|
||||
0x1F3FB, // Light skin tone (Fitz 1-2)
|
||||
0x200D, // ZWJ
|
||||
0x2642, // Male sign
|
||||
0xFE0F, // Emoji presentation selector
|
||||
};
|
||||
try page.setGraphemes(
|
||||
row,
|
||||
cell,
|
||||
graphemes[0..],
|
||||
);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(
|
||||
testdata.grid,
|
||||
&screen,
|
||||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
@@ -360,11 +360,16 @@ pub const RunIterator = struct {
|
||||
|
||||
// Find a font that supports this codepoint. If none support this
|
||||
// then the whole grapheme can't be rendered so we return null.
|
||||
//
|
||||
// We explicitly do not require the additional grapheme components
|
||||
// to support the base presentation, since it is common for emoji
|
||||
// fonts to support the base emoji with emoji presentation but not
|
||||
// certain ZWJ-combined characters like the male and female signs.
|
||||
const idx = try self.grid.getIndex(
|
||||
alloc,
|
||||
cp,
|
||||
style,
|
||||
presentation,
|
||||
null,
|
||||
) orelse return null;
|
||||
candidates.appendAssumeCapacity(idx);
|
||||
}
|
||||
@@ -375,7 +380,7 @@ pub const RunIterator = struct {
|
||||
for (cps) |cp| {
|
||||
// Ignore Emoji ZWJs
|
||||
if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue;
|
||||
if (!self.grid.hasCodepoint(idx, cp, presentation)) break;
|
||||
if (!self.grid.hasCodepoint(idx, cp, null)) break;
|
||||
} else {
|
||||
// If the while completed, then we have a candidate that
|
||||
// supports all of our codepoints.
|
||||
|
Reference in New Issue
Block a user