mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
face: add more RLS types and explicit error sets
This commit is contained in:
@ -530,7 +530,15 @@ pub const Face = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn calcMetrics(ct_font: *macos.text.Font) !font.face.Metrics {
|
||||
const CalcMetricsError = error{
|
||||
CopyTableError,
|
||||
InvalidHeadTable,
|
||||
InvalidPostTable,
|
||||
InvalidOS2Table,
|
||||
OS2VersionNotSupported,
|
||||
};
|
||||
|
||||
fn calcMetrics(ct_font: *macos.text.Font) CalcMetricsError!font.face.Metrics {
|
||||
// Read the 'head' table out of the font data.
|
||||
const head: opentype.Head = head: {
|
||||
const tag = macos.text.FontTableTag.init("head");
|
||||
@ -538,7 +546,12 @@ pub const Face = struct {
|
||||
defer data.release();
|
||||
const ptr = data.getPointer();
|
||||
const len = data.getLength();
|
||||
break :head try opentype.Head.init(ptr[0..len]);
|
||||
break :head opentype.Head.init(ptr[0..len]) catch |err| {
|
||||
return switch (err) {
|
||||
error.EndOfStream,
|
||||
=> error.InvalidHeadTable,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Read the 'post' table out of the font data.
|
||||
@ -548,7 +561,11 @@ pub const Face = struct {
|
||||
defer data.release();
|
||||
const ptr = data.getPointer();
|
||||
const len = data.getLength();
|
||||
break :post try opentype.Post.init(ptr[0..len]);
|
||||
break :post opentype.Post.init(ptr[0..len]) catch |err| {
|
||||
return switch (err) {
|
||||
error.EndOfStream => error.InvalidOS2Table,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Read the 'OS/2' table out of the font data.
|
||||
@ -558,12 +575,17 @@ pub const Face = struct {
|
||||
defer data.release();
|
||||
const ptr = data.getPointer();
|
||||
const len = data.getLength();
|
||||
break :os2 try opentype.OS2.init(ptr[0..len]);
|
||||
break :os2 opentype.OS2.init(ptr[0..len]) catch |err| {
|
||||
return switch (err) {
|
||||
error.EndOfStream => error.InvalidOS2Table,
|
||||
error.OS2VersionNotSupported => error.OS2VersionNotSupported,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const units_per_em = head.unitsPerEm;
|
||||
const px_per_em = ct_font.getSize();
|
||||
const px_per_unit = px_per_em / @as(f64, @floatFromInt(units_per_em));
|
||||
const units_per_em: f64 = @floatFromInt(head.unitsPerEm);
|
||||
const px_per_em: f64 = ct_font.getSize();
|
||||
const px_per_unit: f64 = px_per_em / units_per_em;
|
||||
|
||||
const ascent = @as(f64, @floatFromInt(os2.sTypoAscender)) * px_per_unit;
|
||||
const descent = @as(f64, @floatFromInt(os2.sTypoDescender)) * px_per_unit;
|
||||
@ -576,7 +598,7 @@ pub const Face = struct {
|
||||
|
||||
// If the underline position isn't 0 then we do use it,
|
||||
// even if the thickness is't properly specified.
|
||||
const underline_position = if (has_broken_underline and post.underlinePosition == 0)
|
||||
const underline_position: ?f64 = if (has_broken_underline and post.underlinePosition == 0)
|
||||
null
|
||||
else
|
||||
@as(f64, @floatFromInt(post.underlinePosition)) * px_per_unit;
|
||||
@ -589,25 +611,25 @@ pub const Face = struct {
|
||||
// Similar logic to the underline above.
|
||||
const has_broken_strikethrough = os2.yStrikeoutSize == 0;
|
||||
|
||||
const strikethrough_position = if (has_broken_strikethrough and os2.yStrikeoutPosition == 0)
|
||||
const strikethrough_position: ?f64 = if (has_broken_strikethrough and os2.yStrikeoutPosition == 0)
|
||||
null
|
||||
else
|
||||
@as(f64, @floatFromInt(os2.yStrikeoutPosition)) * px_per_unit;
|
||||
|
||||
const strikethrough_thickness = if (has_broken_strikethrough)
|
||||
const strikethrough_thickness: ?f64 = if (has_broken_strikethrough)
|
||||
null
|
||||
else
|
||||
@as(f64, @floatFromInt(os2.yStrikeoutSize)) * px_per_unit;
|
||||
|
||||
// We fall back to whatever CoreText does if
|
||||
// the OS/2 table doesn't specify a cap height.
|
||||
const cap_height = if (os2.sCapHeight) |sCapHeight|
|
||||
const cap_height: f64 = if (os2.sCapHeight) |sCapHeight|
|
||||
@as(f64, @floatFromInt(sCapHeight)) * px_per_unit
|
||||
else
|
||||
ct_font.getCapHeight();
|
||||
|
||||
// Ditto for ex height.
|
||||
const ex_height = if (os2.sxHeight) |sxHeight|
|
||||
const ex_height: f64 = if (os2.sxHeight) |sxHeight|
|
||||
@as(f64, @floatFromInt(sxHeight)) * px_per_unit
|
||||
else
|
||||
ct_font.getXHeight();
|
||||
@ -648,24 +670,24 @@ pub const Face = struct {
|
||||
|
||||
return font.face.Metrics.calc(.{
|
||||
.cell_width = cell_width,
|
||||
|
||||
.ascent = ascent,
|
||||
.descent = descent,
|
||||
.line_gap = line_gap,
|
||||
|
||||
.underline_position = underline_position,
|
||||
.underline_thickness = underline_thickness,
|
||||
|
||||
.strikethrough_position = strikethrough_position,
|
||||
.strikethrough_thickness = strikethrough_thickness,
|
||||
|
||||
.cap_height = cap_height,
|
||||
.ex_height = ex_height,
|
||||
});
|
||||
}
|
||||
|
||||
/// Copy the font table data for the given tag.
|
||||
pub fn copyTable(self: Face, alloc: Allocator, tag: *const [4]u8) !?[]u8 {
|
||||
pub fn copyTable(
|
||||
self: Face,
|
||||
alloc: Allocator,
|
||||
tag: *const [4]u8,
|
||||
) Allocator.Error!?[]u8 {
|
||||
const data = self.font.copyTable(macos.text.FontTableTag.init(tag)) orelse
|
||||
return null;
|
||||
defer data.release();
|
||||
@ -693,7 +715,9 @@ const ColorState = struct {
|
||||
svg: ?opentype.SVG,
|
||||
svg_data: ?*macos.foundation.Data,
|
||||
|
||||
pub fn init(f: *macos.text.Font) !ColorState {
|
||||
pub const Error = error{InvalidSVGTable};
|
||||
|
||||
pub fn init(f: *macos.text.Font) Error!ColorState {
|
||||
// sbix is true if the table exists in the font data at all.
|
||||
// In the future we probably want to actually parse it and
|
||||
// check for glyphs.
|
||||
@ -714,8 +738,16 @@ const ColorState = struct {
|
||||
errdefer data.release();
|
||||
const ptr = data.getPointer();
|
||||
const len = data.getLength();
|
||||
const svg = opentype.SVG.init(ptr[0..len]) catch |err| {
|
||||
return switch (err) {
|
||||
error.EndOfStream,
|
||||
error.SVGVersionNotSupported,
|
||||
=> error.InvalidSVGTable,
|
||||
};
|
||||
};
|
||||
|
||||
break :svg .{
|
||||
.svg = try opentype.SVG.init(ptr[0..len]),
|
||||
.svg = svg,
|
||||
.data = data,
|
||||
};
|
||||
};
|
||||
|
@ -598,6 +598,11 @@ pub const Face = struct {
|
||||
return @as(opentype.sfnt.F26Dot6, @bitCast(@as(u32, @intCast(v)))).to(f64);
|
||||
}
|
||||
|
||||
const CalcMetricsError = error{
|
||||
CopyTableError,
|
||||
MissingOS2Table,
|
||||
};
|
||||
|
||||
/// Calculate the metrics associated with a face. This is not public because
|
||||
/// the metrics are calculated for every face and cached since they're
|
||||
/// frequently required for renderers and take up next to little memory space
|
||||
@ -610,7 +615,7 @@ pub const Face = struct {
|
||||
fn calcMetrics(
|
||||
face: freetype.Face,
|
||||
modifiers: ?*const font.face.Metrics.ModifierSet,
|
||||
) !font.face.Metrics {
|
||||
) CalcMetricsError!font.face.Metrics {
|
||||
const size_metrics = face.handle.*.size.*.metrics;
|
||||
|
||||
// This code relies on this assumption, and it should always be
|
||||
@ -618,18 +623,18 @@ pub const Face = struct {
|
||||
assert(size_metrics.x_ppem == size_metrics.y_ppem);
|
||||
|
||||
// Read the 'head' table out of the font data.
|
||||
const head = face.getSfntTable(.head) orelse return error.CannotGetTable;
|
||||
const head = face.getSfntTable(.head) orelse return error.CopyTableError;
|
||||
|
||||
// Read the 'post' table out of the font data.
|
||||
const post = face.getSfntTable(.post) orelse return error.CannotGetTable;
|
||||
const post = face.getSfntTable(.post) orelse return error.CopyTableError;
|
||||
|
||||
// Read the 'OS/2' table out of the font data.
|
||||
const os2 = face.getSfntTable(.os2) orelse return error.CannotGetTable;
|
||||
const os2 = face.getSfntTable(.os2) orelse return error.CopyTableError;
|
||||
|
||||
// Some fonts don't actually have an OS/2 table, which
|
||||
// we need in order to do the metrics calculations, in
|
||||
// such cases FreeType sets the version to 0xFFFF
|
||||
if (os2.version == 0xFFFF) return error.MissingTable;
|
||||
if (os2.version == 0xFFFF) return error.MissingOS2Table;
|
||||
|
||||
const units_per_em = head.Units_Per_EM;
|
||||
const px_per_em: f64 = @floatFromInt(size_metrics.y_ppem);
|
||||
|
@ -135,10 +135,9 @@ pub const Head = extern struct {
|
||||
glyphDataFormat: sfnt.int16 align(1),
|
||||
|
||||
/// Parse the table from raw data.
|
||||
pub fn init(data: []const u8) !Head {
|
||||
pub fn init(data: []const u8) error{EndOfStream}!Head {
|
||||
var fbs = std.io.fixedBufferStream(data);
|
||||
const reader = fbs.reader();
|
||||
|
||||
return try reader.readStructEndian(Head, .big);
|
||||
}
|
||||
};
|
||||
|
@ -349,7 +349,10 @@ pub const OS2 = struct {
|
||||
usUpperOpticalPointSize: ?u16 = null,
|
||||
|
||||
/// Parse the table from raw data.
|
||||
pub fn init(data: []const u8) !OS2 {
|
||||
pub fn init(data: []const u8) error{
|
||||
EndOfStream,
|
||||
OS2VersionNotSupported,
|
||||
}!OS2 {
|
||||
var fbs = std.io.fixedBufferStream(data);
|
||||
const reader = fbs.reader();
|
||||
|
||||
|
@ -47,10 +47,9 @@ pub const Post = extern struct {
|
||||
maxMemType1: sfnt.uint32 align(1),
|
||||
|
||||
/// Parse the table from raw data.
|
||||
pub fn init(data: []const u8) !Post {
|
||||
pub fn init(data: []const u8) error{EndOfStream}!Post {
|
||||
var fbs = std.io.fixedBufferStream(data);
|
||||
const reader = fbs.reader();
|
||||
|
||||
return try reader.readStructEndian(Post, .big);
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,10 @@ pub const SVG = struct {
|
||||
/// All records in the table.
|
||||
records: []const [12]u8,
|
||||
|
||||
pub fn init(data: []const u8) !SVG {
|
||||
pub fn init(data: []const u8) error{
|
||||
EndOfStream,
|
||||
SVGVersionNotSupported,
|
||||
}!SVG {
|
||||
var fbs = std.io.fixedBufferStream(data);
|
||||
const reader = fbs.reader();
|
||||
|
||||
|
Reference in New Issue
Block a user