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