font/harfbuzz: force LTR font shaping

Fixes #2570
Related to #1740

See #1740 for details.
This commit is contained in:
Mitchell Hashimoto
2024-11-03 09:51:13 -08:00
parent d47df211b0
commit f04b6c8768
4 changed files with 51 additions and 0 deletions

View File

@ -14,6 +14,7 @@ pub const emoji = @embedFile("res/NotoColorEmoji.ttf");
pub const emoji_text = @embedFile("res/NotoEmoji-Regular.ttf"); pub const emoji_text = @embedFile("res/NotoEmoji-Regular.ttf");
/// Fonts with general properties /// Fonts with general properties
pub const arabic = @embedFile("res/KawkabMono-Regular.ttf");
pub const variable = @embedFile("res/Lilex-VF.ttf"); pub const variable = @embedFile("res/Lilex-VF.ttf");
/// Font with nerd fonts embedded. /// Font with nerd fonts embedded.

Binary file not shown.

View File

@ -190,6 +190,11 @@ pub const Shaper = struct {
// Reset the buffer for our current run // Reset the buffer for our current run
self.shaper.hb_buf.reset(); self.shaper.hb_buf.reset();
self.shaper.hb_buf.setContentType(.unicode); self.shaper.hb_buf.setContentType(.unicode);
// We don't support RTL text because RTL in terminals is messy.
// Its something we want to improve. For now, we force LTR because
// our renderers assume a strictly increasing X value.
self.shaper.hb_buf.setDirection(.ltr);
} }
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void { pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
@ -453,6 +458,46 @@ test "shape monaspace ligs" {
} }
} }
// Ghostty doesn't currently support RTL and our renderers assume
// that cells are in strict LTR order. This means that we need to
// force RTL text to be LTR for rendering. This test ensures that
// we are correctly forcing RTL text to be LTR.
test "shape arabic forced LTR" {
const testing = std.testing;
const alloc = testing.allocator;
var testdata = try testShaperWithFont(alloc, .arabic);
defer testdata.deinit();
var screen = try terminal.Screen.init(alloc, 120, 30, 0);
defer screen.deinit();
try screen.testWriteString(@embedFile("testdata/arabic.txt"));
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)) |run| {
count += 1;
try testing.expectEqual(@as(usize, 25), run.cells);
const cells = try shaper.shape(run);
try testing.expectEqual(@as(usize, 25), cells.len);
var x: u16 = cells[0].x;
for (cells[1..]) |cell| {
try testing.expectEqual(x + 1, cell.x);
x = cell.x;
}
}
try testing.expectEqual(@as(usize, 1), count);
}
test "shape emoji width" { test "shape emoji width" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -1146,6 +1191,7 @@ const TestShaper = struct {
const TestFont = enum { const TestFont = enum {
inconsolata, inconsolata,
monaspace_neon, monaspace_neon,
arabic,
}; };
/// Helper to return a fully initialized shaper. /// Helper to return a fully initialized shaper.
@ -1159,6 +1205,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
const testFont = switch (font_req) { const testFont = switch (font_req) {
.inconsolata => font.embedded.inconsolata, .inconsolata => font.embedded.inconsolata,
.monaspace_neon => font.embedded.monaspace_neon, .monaspace_neon => font.embedded.monaspace_neon,
.arabic => font.embedded.arabic,
}; };
var lib = try Library.init(); var lib = try Library.init();

3
src/font/shaper/testdata/arabic.txt vendored Normal file
View File

@ -0,0 +1,3 @@
غريبه لاني عربي أبا عن جد
واتكلم الانجليزية بطلاقة اكثر من ٢٥ سنه
ومع هذا اجد العربيه افضل لان فيها الكثير من المفردات الاكثر دقه بالوصف