mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
calculate font metrics via canvas
This commit is contained in:
@ -41,8 +41,8 @@ fetch(url.href).then(response =>
|
||||
new Uint8Array(memory.buffer, font_ptr).set(font);
|
||||
|
||||
// Call whatever example you want:
|
||||
const face = face_new(font_ptr, font.byteLength);
|
||||
face_free(face);
|
||||
const face = face_new(font_ptr, font.byteLength, 14);
|
||||
//face_free(face);
|
||||
} finally {
|
||||
free(font_ptr);
|
||||
}
|
||||
|
@ -49,21 +49,22 @@ pub const Face = struct {
|
||||
const canvas = try doc.call(js.Object, "createElement", .{js.string("canvas")});
|
||||
errdefer canvas.deinit();
|
||||
|
||||
log.debug("face initialized: {s}", .{raw});
|
||||
|
||||
return Face{
|
||||
var result = Face{
|
||||
.alloc = alloc,
|
||||
.font_str = font_str,
|
||||
.size = size,
|
||||
// TODO: figure out how we're going to do emoji with web canvas
|
||||
.presentation = .text,
|
||||
|
||||
.canvas = canvas,
|
||||
|
||||
// TODO: real metrics
|
||||
// We're going to calculate these right after initialization.
|
||||
.metrics = undefined,
|
||||
|
||||
// TODO: figure out how we're going to do emoji with web canvas
|
||||
.presentation = .text,
|
||||
};
|
||||
try result.calcMetrics();
|
||||
|
||||
log.debug("face initialized: {s}", .{raw});
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Face) void {
|
||||
@ -71,6 +72,72 @@ pub const Face = struct {
|
||||
self.canvas.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Calculate the metrics associated with a given face.
|
||||
fn calcMetrics(self: *Face) !void {
|
||||
// This will return the same context on subsequent calls so it
|
||||
// is important to reset it.
|
||||
const ctx = try self.canvas.call(js.Object, "getContext", .{js.string("2d")});
|
||||
defer ctx.deinit();
|
||||
|
||||
// Set our context font
|
||||
var font_val = try std.fmt.allocPrint(
|
||||
self.alloc,
|
||||
"{d}px {s}",
|
||||
.{ self.size.points, self.font_str },
|
||||
);
|
||||
defer self.alloc.free(font_val);
|
||||
try ctx.set("font", js.string(font_val));
|
||||
|
||||
// If the font property didn't change, then the font set didn't work.
|
||||
// We do this check because it is very easy to put an invalid font
|
||||
// in and this at least makes it show up in the logs.
|
||||
{
|
||||
const check = try ctx.getAlloc(js.String, self.alloc, "font");
|
||||
defer self.alloc.free(check);
|
||||
if (!std.mem.eql(u8, font_val, check)) {
|
||||
log.warn("canvas font didn't set, fonts may be broken, expected={s} got={s}", .{
|
||||
font_val,
|
||||
check,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cell width is the width of our M text
|
||||
const cell_width: f32 = cell_width: {
|
||||
const metrics = try ctx.call(js.Object, "measureText", .{js.string("M")});
|
||||
defer metrics.deinit();
|
||||
break :cell_width try metrics.get(f32, "actualBoundingBoxRight");
|
||||
};
|
||||
|
||||
// To get the cell height we render a high and low character and get
|
||||
// the total of the ascent and descent. This should equal our
|
||||
// pixel height but this is a more surefire way to get it.
|
||||
const height_metrics = try ctx.call(js.Object, "measureText", .{js.string("M_")});
|
||||
defer height_metrics.deinit();
|
||||
const asc = try height_metrics.get(f32, "actualBoundingBoxAscent");
|
||||
const desc = try height_metrics.get(f32, "actualBoundingBoxDescent");
|
||||
const cell_height = asc + desc;
|
||||
const cell_baseline = desc;
|
||||
|
||||
// There isn't a declared underline position for canvas measurements
|
||||
// so we just go 1 under the cell height to match freetype logic
|
||||
// at this time (our freetype logic).
|
||||
const underline_position = cell_height - 1;
|
||||
const underline_thickness: f32 = 1;
|
||||
|
||||
self.metrics = .{
|
||||
.cell_width = cell_width,
|
||||
.cell_height = cell_height,
|
||||
.cell_baseline = cell_baseline,
|
||||
.underline_position = underline_position,
|
||||
.underline_thickness = underline_thickness,
|
||||
.strikethrough_position = underline_position,
|
||||
.strikethrough_thickness = underline_thickness,
|
||||
};
|
||||
|
||||
log.debug("metrics font={s} value={}", .{ font_val, self.metrics });
|
||||
}
|
||||
};
|
||||
|
||||
/// The wasm-compatible API.
|
||||
|
2
vendor/zig-js
vendored
2
vendor/zig-js
vendored
@ -1 +1 @@
|
||||
Subproject commit faa353da8aca78269bc7d2afaf481b755007e849
|
||||
Subproject commit b6352b62a61e306df09e8bec7c7a99153829ec61
|
Reference in New Issue
Block a user