font: calculate cell height

This commit is contained in:
Mitchell Hashimoto
2022-10-09 08:51:30 -07:00
parent 8b3959dceb
commit 150b0a4d51

View File

@ -44,7 +44,7 @@ pub const Face = struct {
const ct_font = try base.copyWithAttributes(@intToFloat(f32, size.points), null); const ct_font = try base.copyWithAttributes(@intToFloat(f32, size.points), null);
errdefer ct_font.release(); errdefer ct_font.release();
const hb_font = try harfbuzz.coretext.createFont(ct_font); var hb_font = try harfbuzz.coretext.createFont(ct_font);
errdefer hb_font.destroy(); errdefer hb_font.destroy();
const traits = ct_font.getSymbolicTraits(); const traits = ct_font.getSymbolicTraits();
@ -53,7 +53,7 @@ pub const Face = struct {
.font = ct_font, .font = ct_font,
.hb_font = hb_font, .hb_font = hb_font,
.presentation = if (traits.color_glyphs) .emoji else .text, .presentation = if (traits.color_glyphs) .emoji else .text,
.metrics = calcMetrics(ct_font), .metrics = try calcMetrics(ct_font),
}; };
} }
@ -83,7 +83,7 @@ pub const Face = struct {
return @intCast(u32, glyphs[0]); return @intCast(u32, glyphs[0]);
} }
fn calcMetrics(ct_font: *macos.text.Font) font.face.Metrics { fn calcMetrics(ct_font: *macos.text.Font) !font.face.Metrics {
// Cell width is calculated by calculating the widest width of the // Cell width is calculated by calculating the widest width of the
// visible ASCII characters. Usually 'M' is widest but we just take // visible ASCII characters. Usually 'M' is widest but we just take
// whatever is widest. // whatever is widest.
@ -118,13 +118,63 @@ pub const Face = struct {
break :cell_width @floatCast(f32, max); break :cell_width @floatCast(f32, max);
}; };
// Calculate the cell height by using CoreText's layout engine
// to tell us after laying out some text. This is inspired by Kitty's
// approach. Previously we were using descent/ascent math and it wasn't
// quite the same with CoreText and I never figured out why.
const cell_height: f32 = cell_height: { const cell_height: f32 = cell_height: {
const diff = ct_font.getAscent() + ct_font.getDescent() + ct_font.getLeading(); const unit = "AQWMH_gyl " ** 100;
break :cell_height @floatCast(f32, diff);
// Setup our string we'll layout. We just stylize a string of
// ASCII characters to setup the letters.
const string = try macos.foundation.MutableAttributedString.create(unit.len);
defer string.release();
const rep = try macos.foundation.String.createWithBytes(unit, .utf8, false);
defer rep.release();
string.replaceString(macos.foundation.Range.init(0, 0), rep);
string.setAttribute(
macos.foundation.Range.init(0, unit.len),
macos.text.StringAttribute.font,
ct_font,
);
// Create our framesetter with our string. This is used to
// emit "frames" for the layout.
const fs = try macos.text.Framesetter.createWithAttributedString(
@ptrCast(*macos.foundation.AttributedString, string),
);
defer fs.release();
// Create a rectangle to fit all of this and create a frame of it.
const path = try macos.graphics.MutablePath.create();
path.addRect(null, macos.graphics.Rect.init(10, 10, 200, 200));
defer path.release();
const frame = try fs.createFrame(
macos.foundation.Range.init(0, 0),
@ptrCast(*macos.graphics.Path, path),
null,
);
defer frame.release();
// Get the two points where the lines start in order to determine
// the line height.
var points: [2]macos.graphics.Point = undefined;
frame.getLineOrigins(macos.foundation.Range.init(0, 1), points[0..]);
frame.getLineOrigins(macos.foundation.Range.init(1, 1), points[1..]);
break :cell_height @floatCast(f32, points[0].y - points[1].y);
}; };
std.log.warn("width={}, height={}", .{ cell_width, cell_height }); std.log.warn("width={}, height={}", .{ cell_width, cell_height });
return undefined; return font.face.Metrics{
.cell_width = cell_width,
.cell_height = cell_height,
.cell_baseline = 0,
.underline_position = 0,
.underline_thickness = 0,
.strikethrough_position = 0,
.strikethrough_thickness = 0,
};
} }
}; };
@ -176,10 +226,10 @@ test "in-memory" {
const testFont = @import("../test.zig").fontRegular; const testFont = @import("../test.zig").fontRegular;
var lib = try font.Library.init(); var lib = try font.Library.init();
defer lib.deinit(); //defer lib.deinit();
var face = try Face.init(lib, testFont, .{ .points = 12 }); var face = try Face.init(lib, testFont, .{ .points = 12 });
defer face.deinit(); //defer face.deinit();
try testing.expectEqual(font.Presentation.text, face.presentation); try testing.expectEqual(font.Presentation.text, face.presentation);