ghostty/pkg/macos/text/font.zig
Mitchell Hashimoto 45da58188c fix up for new zig
2023-07-01 09:23:39 -07:00

223 lines
6.4 KiB
Zig

const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const foundation = @import("../foundation.zig");
const graphics = @import("../graphics.zig");
const text = @import("../text.zig");
const c = @import("c.zig");
pub const Font = opaque {
pub fn createWithFontDescriptor(desc: *text.FontDescriptor, size: f32) Allocator.Error!*Font {
return @as(
?*Font,
@ptrFromInt(@intFromPtr(c.CTFontCreateWithFontDescriptor(
@ptrCast(desc),
size,
null,
))),
) orelse Allocator.Error.OutOfMemory;
}
pub fn copyWithAttributes(self: *Font, size: f32, attrs: ?*text.FontDescriptor) Allocator.Error!*Font {
return @as(
?*Font,
@ptrFromInt(@intFromPtr(c.CTFontCreateCopyWithAttributes(
@ptrCast(self),
size,
null,
@ptrCast(attrs),
))),
) orelse Allocator.Error.OutOfMemory;
}
pub fn release(self: *Font) void {
c.CFRelease(self);
}
pub fn getGlyphsForCharacters(self: *Font, chars: []const u16, glyphs: []graphics.Glyph) bool {
assert(chars.len == glyphs.len);
return c.CTFontGetGlyphsForCharacters(
@ptrCast(self),
chars.ptr,
glyphs.ptr,
@intCast(chars.len),
);
}
pub fn drawGlyphs(
self: *Font,
glyphs: []const graphics.Glyph,
positions: []const graphics.Point,
context: anytype, // Must be some context type from graphics
) void {
assert(positions.len == glyphs.len);
c.CTFontDrawGlyphs(
@ptrCast(self),
glyphs.ptr,
@ptrCast(positions.ptr),
glyphs.len,
@ptrCast(context),
);
}
pub fn getBoundingRectForGlyphs(
self: *Font,
orientation: FontOrientation,
glyphs: []const graphics.Glyph,
rects: ?[]graphics.Rect,
) graphics.Rect {
if (rects) |s| assert(glyphs.len == s.len);
return @bitCast(c.CTFontGetBoundingRectsForGlyphs(
@ptrCast(self),
@intFromEnum(orientation),
glyphs.ptr,
@ptrCast(if (rects) |s| s.ptr else null),
@intCast(glyphs.len),
));
}
pub fn getAdvancesForGlyphs(
self: *Font,
orientation: FontOrientation,
glyphs: []const graphics.Glyph,
advances: ?[]graphics.Size,
) f64 {
if (advances) |s| assert(glyphs.len == s.len);
return c.CTFontGetAdvancesForGlyphs(
@ptrCast(self),
@intFromEnum(orientation),
glyphs.ptr,
@ptrCast(if (advances) |s| s.ptr else null),
@intCast(glyphs.len),
);
}
pub fn copyAttribute(self: *Font, comptime attr: text.FontAttribute) attr.Value() {
return @ptrFromInt(@intFromPtr(c.CTFontCopyAttribute(
@ptrCast(self),
@ptrCast(attr.key()),
)));
}
pub fn copyDisplayName(self: *Font) *foundation.String {
return @ptrFromInt(@intFromPtr(c.CTFontCopyDisplayName(@ptrCast(self))));
}
pub fn getSymbolicTraits(self: *Font) text.FontSymbolicTraits {
return @bitCast(c.CTFontGetSymbolicTraits(@ptrCast(self)));
}
pub fn getAscent(self: *Font) f64 {
return c.CTFontGetAscent(@ptrCast(self));
}
pub fn getDescent(self: *Font) f64 {
return c.CTFontGetDescent(@ptrCast(self));
}
pub fn getLeading(self: *Font) f64 {
return c.CTFontGetLeading(@ptrCast(self));
}
pub fn getBoundingBox(self: *Font) graphics.Rect {
return @bitCast(c.CTFontGetBoundingBox(@ptrCast(self)));
}
pub fn getUnderlinePosition(self: *Font) f64 {
return c.CTFontGetUnderlinePosition(@ptrCast(self));
}
pub fn getUnderlineThickness(self: *Font) f64 {
return c.CTFontGetUnderlineThickness(@ptrCast(self));
}
pub fn getUnitsPerEm(self: *Font) u32 {
return c.CTFontGetUnitsPerEm(@ptrCast(self));
}
pub fn getSize(self: *Font) f64 {
return c.CTFontGetSize(@ptrCast(self));
}
};
pub const FontOrientation = enum(c_uint) {
default = c.kCTFontOrientationDefault,
horizontal = c.kCTFontOrientationHorizontal,
vertical = c.kCTFontOrientationVertical,
};
test {
const testing = std.testing;
const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
defer name.release();
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
defer desc.release();
const font = try Font.createWithFontDescriptor(desc, 12);
defer font.release();
// Traits
{
const traits = font.getSymbolicTraits();
try testing.expect(!traits.color_glyphs);
}
var glyphs = [1]graphics.Glyph{0};
try testing.expect(font.getGlyphsForCharacters(
&[_]u16{'A'},
&glyphs,
));
try testing.expect(glyphs[0] > 0);
// Bounding rect
{
var rect = font.getBoundingRectForGlyphs(.horizontal, &glyphs, null);
try testing.expect(rect.size.width > 0);
var singles: [1]graphics.Rect = undefined;
rect = font.getBoundingRectForGlyphs(.horizontal, &glyphs, &singles);
try testing.expect(rect.size.width > 0);
try testing.expect(singles[0].size.width > 0);
}
// Advances
{
var advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, null);
try testing.expect(advance > 0);
var singles: [1]graphics.Size = undefined;
advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, &singles);
try testing.expect(advance > 0);
try testing.expect(singles[0].width > 0);
}
// Draw
{
const cs = try graphics.ColorSpace.createDeviceGray();
defer cs.release();
const ctx = try graphics.BitmapContext.create(null, 80, 80, 8, 80, cs, 0);
defer ctx.release();
var pos = [_]graphics.Point{.{ .x = 0, .y = 0 }};
font.drawGlyphs(
&glyphs,
&pos,
ctx,
);
}
}
test "copy" {
const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
defer name.release();
const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
defer desc.release();
const font = try Font.createWithFontDescriptor(desc, 12);
defer font.release();
const f2 = try font.copyWithAttributes(14, null);
defer f2.release();
}