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.pull/6824/head
parent
6f84a5d682
commit
f0080529c4
|
|
@ -1015,25 +1015,35 @@ test "shape emoji width long" {
|
||||||
var testdata = try testShaper(alloc);
|
var testdata = try testShaper(alloc);
|
||||||
defer testdata.deinit();
|
defer testdata.deinit();
|
||||||
|
|
||||||
var buf: [32]u8 = undefined;
|
// Make a screen and add a long emoji sequence to it.
|
||||||
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
|
|
||||||
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
||||||
defer screen.deinit();
|
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
|
// Get our run iterator
|
||||||
var shaper = &testdata.shaper;
|
var shaper = &testdata.shaper;
|
||||||
var it = shaper.runIterator(
|
var it = shaper.runIterator(
|
||||||
testdata.grid,
|
testdata.grid,
|
||||||
&screen,
|
&screen,
|
||||||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -540,25 +540,35 @@ test "shape emoji width long" {
|
||||||
var testdata = try testShaper(alloc);
|
var testdata = try testShaper(alloc);
|
||||||
defer testdata.deinit();
|
defer testdata.deinit();
|
||||||
|
|
||||||
var buf: [32]u8 = undefined;
|
// Make a screen and add a long emoji sequence to it.
|
||||||
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
|
|
||||||
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
var screen = try terminal.Screen.init(alloc, 30, 3, 0);
|
||||||
defer screen.deinit();
|
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
|
// Get our run iterator
|
||||||
var shaper = &testdata.shaper;
|
var shaper = &testdata.shaper;
|
||||||
var it = shaper.runIterator(
|
var it = shaper.runIterator(
|
||||||
testdata.grid,
|
testdata.grid,
|
||||||
&screen,
|
&screen,
|
||||||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -360,11 +360,16 @@ pub const RunIterator = struct {
|
||||||
|
|
||||||
// Find a font that supports this codepoint. If none support this
|
// Find a font that supports this codepoint. If none support this
|
||||||
// then the whole grapheme can't be rendered so we return null.
|
// 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(
|
const idx = try self.grid.getIndex(
|
||||||
alloc,
|
alloc,
|
||||||
cp,
|
cp,
|
||||||
style,
|
style,
|
||||||
presentation,
|
null,
|
||||||
) orelse return null;
|
) orelse return null;
|
||||||
candidates.appendAssumeCapacity(idx);
|
candidates.appendAssumeCapacity(idx);
|
||||||
}
|
}
|
||||||
|
|
@ -375,7 +380,7 @@ pub const RunIterator = struct {
|
||||||
for (cps) |cp| {
|
for (cps) |cp| {
|
||||||
// Ignore Emoji ZWJs
|
// Ignore Emoji ZWJs
|
||||||
if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue;
|
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 {
|
} else {
|
||||||
// If the while completed, then we have a candidate that
|
// If the while completed, then we have a candidate that
|
||||||
// supports all of our codepoints.
|
// supports all of our codepoints.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue