use freetype for font display
This commit is contained in:
+73
-42
@@ -426,44 +426,87 @@ test "map_to_output merges like any other setting" {
|
||||
|
||||
// ─── tab titles ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Stands in for a `Face`, so the eliding rules can be checked without a font
|
||||
/// on the machine running the tests. One cell wide per character, in the 26.6
|
||||
/// units `font.fit` counts in.
|
||||
const Mono = struct {
|
||||
pub fn advance(_: Mono, _: u21) i32 {
|
||||
return font.fixed(1);
|
||||
}
|
||||
pub fn ellipsisAdvance(self: Mono) i32 {
|
||||
return self.advance(font.ellipsis);
|
||||
}
|
||||
};
|
||||
|
||||
/// A proportional face: 'i' is half a cell, the ellipsis two. Real faces are
|
||||
/// like this, and it is where fixed-cell arithmetic goes wrong.
|
||||
const Prop = struct {
|
||||
pub fn advance(_: Prop, cp: u21) i32 {
|
||||
return switch (cp) {
|
||||
'i' => @divExact(font.fixed(1), 2),
|
||||
font.ellipsis => font.fixed(2),
|
||||
else => font.fixed(1),
|
||||
};
|
||||
}
|
||||
pub fn ellipsisAdvance(self: Prop) i32 {
|
||||
return self.advance(font.ellipsis);
|
||||
}
|
||||
};
|
||||
|
||||
test "a title that fits is drawn whole" {
|
||||
const f = font.fit("foot", 10);
|
||||
const f = font.fit("foot", font.fixed(10), Mono{});
|
||||
try testing.expectEqual(@as(usize, 4), f.end);
|
||||
try testing.expectEqual(@as(usize, 4), f.cells);
|
||||
try testing.expectEqual(font.fixed(4), f.width);
|
||||
try testing.expect(!f.elided);
|
||||
|
||||
// Exactly full is still whole: the ellipsis costs a cell, so only spend it
|
||||
// Exactly full is still whole: the ellipsis costs room, so only spend it
|
||||
// when something is actually cut.
|
||||
const exact = font.fit("foot", 4);
|
||||
try testing.expectEqual(@as(usize, 4), exact.cells);
|
||||
const exact = font.fit("foot", font.fixed(4), Mono{});
|
||||
try testing.expectEqual(@as(usize, 4), exact.end);
|
||||
try testing.expect(!exact.elided);
|
||||
}
|
||||
|
||||
test "a title too long for the tab loses its last cell to the ellipsis" {
|
||||
const f = font.fit("firefox", 4);
|
||||
test "a title too long for the tab is cut short for the ellipsis" {
|
||||
const f = font.fit("firefox", font.fixed(4), Mono{});
|
||||
try testing.expectEqualStrings("fir", "firefox"[0..f.end]);
|
||||
try testing.expectEqual(@as(usize, 3), f.cells);
|
||||
try testing.expectEqual(font.fixed(4), f.width);
|
||||
try testing.expect(f.elided);
|
||||
|
||||
// One cell of room leaves nothing but the ellipsis, and no room at all
|
||||
// draws nothing rather than indexing past the end.
|
||||
const one = font.fit("firefox", 1);
|
||||
// Room for the ellipsis alone leaves nothing but the ellipsis, and no room
|
||||
// at all draws nothing rather than indexing past the end.
|
||||
const one = font.fit("firefox", font.fixed(1), Mono{});
|
||||
try testing.expectEqual(@as(usize, 0), one.end);
|
||||
try testing.expect(one.elided);
|
||||
|
||||
const none = font.fit("firefox", 0);
|
||||
const none = font.fit("firefox", 0, Mono{});
|
||||
try testing.expectEqual(@as(usize, 0), none.end);
|
||||
try testing.expect(!none.elided);
|
||||
}
|
||||
|
||||
test "eliding gives up as many characters as the ellipsis costs" {
|
||||
// "wiki" alone would fit in three cells; the two-cell ellipsis means all
|
||||
// but the first character has to go, which counting cells would miss.
|
||||
const f = font.fit("wikiw", font.fixed(3), Prop{});
|
||||
try testing.expect(f.elided);
|
||||
try testing.expectEqualStrings("w", "wikiw"[0..f.end]);
|
||||
try testing.expect(f.width <= font.fixed(3));
|
||||
|
||||
// Narrow characters earn their place: three of them survive where two wide
|
||||
// ones would not.
|
||||
const narrow = font.fit("wiiwwww", font.fixed(4), Prop{});
|
||||
try testing.expect(narrow.elided);
|
||||
try testing.expectEqualStrings("wii", "wiiwwww"[0..narrow.end]);
|
||||
try testing.expect(narrow.width <= font.fixed(4));
|
||||
}
|
||||
|
||||
test "titles are measured in characters, not bytes" {
|
||||
// Three codepoints, six bytes: a tab four cells wide holds them all.
|
||||
const f = font.fit("ünïx", 4);
|
||||
try testing.expectEqual(@as(usize, 4), f.cells);
|
||||
// Four codepoints, six bytes: a tab four cells wide holds them all.
|
||||
const f = font.fit("ünïx", font.fixed(4), Mono{});
|
||||
try testing.expectEqual("ünïx".len, f.end);
|
||||
try testing.expect(!f.elided);
|
||||
|
||||
// And a cut lands on a codepoint boundary, never mid-sequence.
|
||||
const cut = font.fit("ünïx", 3);
|
||||
const cut = font.fit("ünïx", font.fixed(3), Mono{});
|
||||
try testing.expect(cut.elided);
|
||||
try testing.expectEqualStrings("ün", "ünïx"[0..cut.end]);
|
||||
}
|
||||
@@ -478,32 +521,20 @@ test "malformed utf-8 in a title yields one glyph per bad byte" {
|
||||
try testing.expectEqual(@as(?u21, null), it.next());
|
||||
}
|
||||
|
||||
test "text measuring and cell fitting agree" {
|
||||
const scale = 2;
|
||||
// Whatever cellsForWidth says fits must actually fit.
|
||||
var width: i32 = 0;
|
||||
while (width < 200) : (width += 1) {
|
||||
const cells = font.cellsForWidth(width, scale);
|
||||
try testing.expect(font.textWidth(cells, scale) <= width);
|
||||
try testing.expect(font.textWidth(cells + 1, scale) > width);
|
||||
}
|
||||
test "control characters in a title draw as spaces" {
|
||||
var it: font.Iterator = .{ .bytes = "a\nb\tc\x7f" };
|
||||
try testing.expectEqual(@as(?u21, 'a'), it.next());
|
||||
try testing.expectEqual(@as(?u21, ' '), it.next());
|
||||
try testing.expectEqual(@as(?u21, 'b'), it.next());
|
||||
try testing.expectEqual(@as(?u21, ' '), it.next());
|
||||
try testing.expectEqual(@as(?u21, 'c'), it.next());
|
||||
try testing.expectEqual(@as(?u21, ' '), it.next());
|
||||
try testing.expectEqual(@as(?u21, null), it.next());
|
||||
}
|
||||
|
||||
test "printable ascii all have glyphs, and unknown codepoints fall back" {
|
||||
// The space is blank, and nothing else printable is.
|
||||
var c: u8 = 0x21;
|
||||
while (c <= 0x7e) : (c += 1) {
|
||||
const rows = font.glyph(c);
|
||||
var lit = false;
|
||||
for (rows) |r| lit = lit or r != 0;
|
||||
try testing.expect(lit);
|
||||
// Nothing may spill outside the five column cell.
|
||||
for (rows) |r| try testing.expectEqual(@as(u8, 0), r & ~@as(u8, 0b11111));
|
||||
}
|
||||
for (font.glyph(' ')) |r| try testing.expectEqual(@as(u8, 0), r);
|
||||
|
||||
// Control characters stay blank; other codepoints get the fallback box.
|
||||
for (font.glyph('\n')) |r| try testing.expectEqual(@as(u8, 0), r);
|
||||
try testing.expect(!std.mem.eql(u8, font.glyph('?'), font.glyph('あ')));
|
||||
try testing.expectEqualSlices(u8, font.glyph(0xfffd), font.glyph('あ'));
|
||||
test "fixed point widths round up to whole pixels" {
|
||||
try testing.expectEqual(@as(i32, 0), font.pixels(0));
|
||||
try testing.expectEqual(@as(i32, 1), font.pixels(1));
|
||||
try testing.expectEqual(@as(i32, 1), font.pixels(font.fixed(1)));
|
||||
try testing.expectEqual(@as(i32, 2), font.pixels(font.fixed(1) + 1));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user