mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: specific codepoint lookup in internals
This commit is contained in:
@ -51,6 +51,14 @@ pub const MutableDictionary = opaque {
|
||||
))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createMutableCopy(cap: usize, src: *Dictionary) Allocator.Error!*MutableDictionary {
|
||||
return @intToPtr(?*MutableDictionary, @ptrToInt(c.CFDictionaryCreateMutableCopy(
|
||||
null,
|
||||
@intCast(c.CFIndex, cap),
|
||||
@ptrCast(c.CFDictionaryRef, src),
|
||||
))) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *MutableDictionary) void {
|
||||
foundation.CFRelease(self);
|
||||
}
|
||||
|
@ -18,6 +18,19 @@ pub const FontDescriptor = opaque {
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn createCopyWithAttributes(
|
||||
original: *FontDescriptor,
|
||||
dict: *foundation.Dictionary,
|
||||
) Allocator.Error!*FontDescriptor {
|
||||
return @intToPtr(
|
||||
?*FontDescriptor,
|
||||
@ptrToInt(c.CTFontDescriptorCreateCopyWithAttributes(
|
||||
@ptrCast(c.CTFontDescriptorRef, original),
|
||||
@ptrCast(c.CFDictionaryRef, dict),
|
||||
)),
|
||||
) orelse Allocator.Error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn release(self: *FontDescriptor) void {
|
||||
c.CFRelease(self);
|
||||
}
|
||||
@ -28,6 +41,12 @@ pub const FontDescriptor = opaque {
|
||||
@ptrCast(c.CFStringRef, attr.key()),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn copyAttributes(self: *FontDescriptor) *foundation.Dictionary {
|
||||
return @intToPtr(*foundation.Dictionary, @ptrToInt(c.CTFontDescriptorCopyAttributes(
|
||||
@ptrCast(c.CTFontDescriptorRef, self),
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const FontAttribute = enum {
|
||||
|
@ -189,7 +189,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
||||
});
|
||||
defer disco_it.deinit();
|
||||
if (try disco_it.next()) |face| {
|
||||
log.debug("font regular: {s}", .{try face.name()});
|
||||
log.info("font regular: {s}", .{try face.name()});
|
||||
try group.addFace(alloc, .regular, face);
|
||||
}
|
||||
}
|
||||
@ -201,7 +201,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
||||
});
|
||||
defer disco_it.deinit();
|
||||
if (try disco_it.next()) |face| {
|
||||
log.debug("font bold: {s}", .{try face.name()});
|
||||
log.info("font bold: {s}", .{try face.name()});
|
||||
try group.addFace(alloc, .bold, face);
|
||||
}
|
||||
}
|
||||
@ -213,7 +213,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
||||
});
|
||||
defer disco_it.deinit();
|
||||
if (try disco_it.next()) |face| {
|
||||
log.debug("font italic: {s}", .{try face.name()});
|
||||
log.info("font italic: {s}", .{try face.name()});
|
||||
try group.addFace(alloc, .italic, face);
|
||||
}
|
||||
}
|
||||
@ -226,7 +226,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
||||
});
|
||||
defer disco_it.deinit();
|
||||
if (try disco_it.next()) |face| {
|
||||
log.debug("font bold+italic: {s}", .{try face.name()});
|
||||
log.info("font bold+italic: {s}", .{try face.name()});
|
||||
try group.addFace(alloc, .bold_italic, face);
|
||||
}
|
||||
}
|
||||
|
@ -256,10 +256,10 @@ test "resize" {
|
||||
var lib = try Library.init();
|
||||
defer lib.deinit();
|
||||
|
||||
var group = try init(alloc, lib, .{ .points = 12 });
|
||||
var group = try init(alloc, lib, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer group.deinit(alloc);
|
||||
|
||||
try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })));
|
||||
try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 })));
|
||||
|
||||
// Load a letter
|
||||
{
|
||||
@ -278,7 +278,7 @@ test "resize" {
|
||||
}
|
||||
|
||||
// Resize
|
||||
try group.setSize(.{ .points = 24 });
|
||||
try group.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||
{
|
||||
const idx = group.indexForCodepoint('A', .regular, null).?;
|
||||
const face = try group.faceFromIndex(idx);
|
||||
|
@ -253,7 +253,7 @@ test "resize" {
|
||||
try cache.group.addFace(
|
||||
alloc,
|
||||
.regular,
|
||||
DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })),
|
||||
DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 })),
|
||||
);
|
||||
|
||||
// Load a letter
|
||||
@ -272,7 +272,7 @@ test "resize" {
|
||||
}
|
||||
|
||||
// Resize
|
||||
try cache.setSize(.{ .points = 24 });
|
||||
try cache.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||
{
|
||||
const idx = (try cache.indexForCodepoint(alloc, 'A', .regular, null)).?;
|
||||
const face = try cache.group.faceFromIndex(idx);
|
||||
|
@ -6,7 +6,7 @@ const macos = @import("macos");
|
||||
const options = @import("main.zig").options;
|
||||
const DeferredFace = @import("main.zig").DeferredFace;
|
||||
|
||||
const log = std.log.named(.discovery);
|
||||
const log = std.log.scoped(.discovery);
|
||||
|
||||
/// Discover implementation for the compile options.
|
||||
pub const Discover = switch (options.backend) {
|
||||
@ -27,12 +27,15 @@ pub const Descriptor = struct {
|
||||
///
|
||||
/// On systems that use fontconfig (Linux), this can be a full
|
||||
/// fontconfig pattern, such as "Fira Code-14:bold".
|
||||
family: [:0]const u8,
|
||||
family: ?[:0]const u8 = null,
|
||||
|
||||
/// A codepoint that this font must be able to render.
|
||||
codepoint: u32 = 0,
|
||||
|
||||
/// Font size in points that the font should support. For conversion
|
||||
/// to pixels, we will use 72 DPI for Mac and 96 DPI for everything else.
|
||||
/// (If pixel conversion is necessary, i.e. emoji fonts)
|
||||
size: u16,
|
||||
size: u16 = 0,
|
||||
|
||||
/// True if we want to search specifically for a font that supports
|
||||
/// bold, italic, or both.
|
||||
@ -44,7 +47,15 @@ pub const Descriptor = struct {
|
||||
/// must still do this.
|
||||
pub fn toFcPattern(self: Descriptor) *fontconfig.Pattern {
|
||||
const pat = fontconfig.Pattern.create();
|
||||
assert(pat.add(.family, .{ .string = self.family }, false));
|
||||
if (self.family) |family| {
|
||||
assert(pat.add(.family, .{ .string = family }, false));
|
||||
}
|
||||
if (self.codepoint > 0) {
|
||||
const cs = fontconfig.CharSet.create();
|
||||
defer cs.destroy();
|
||||
assert(cs.addChar(self.codepoint));
|
||||
assert(pat.add(.charset, .{ .char_set = cs }, false));
|
||||
}
|
||||
if (self.size > 0) assert(pat.add(
|
||||
.size,
|
||||
.{ .integer = self.size },
|
||||
@ -70,13 +81,28 @@ pub const Descriptor = struct {
|
||||
const attrs = try macos.foundation.MutableDictionary.create(0);
|
||||
defer attrs.release();
|
||||
|
||||
// Family is always set
|
||||
const family = try macos.foundation.String.createWithBytes(self.family, .utf8, false);
|
||||
// Family
|
||||
if (self.family) |family_bytes| {
|
||||
const family = try macos.foundation.String.createWithBytes(family_bytes, .utf8, false);
|
||||
defer family.release();
|
||||
attrs.setValue(
|
||||
macos.text.FontAttribute.family_name.key(),
|
||||
family,
|
||||
);
|
||||
}
|
||||
|
||||
// Codepoint support
|
||||
if (self.codepoint > 0) {
|
||||
const cs = try macos.foundation.CharacterSet.createWithCharactersInRange(.{
|
||||
.location = self.codepoint,
|
||||
.length = 1,
|
||||
});
|
||||
defer cs.release();
|
||||
attrs.setValue(
|
||||
macos.text.FontAttribute.character_set.key(),
|
||||
cs,
|
||||
);
|
||||
}
|
||||
|
||||
// Set our size attribute if set
|
||||
if (self.size > 0) {
|
||||
@ -254,9 +280,26 @@ pub const CoreText = struct {
|
||||
pub fn next(self: *DiscoverIterator) !?DeferredFace {
|
||||
if (self.i >= self.list.getCount()) return null;
|
||||
|
||||
// Get our descriptor. We need to remove the character set
|
||||
// limitation because we may have used that to filter but we
|
||||
// don't want it anymore because it'll restrict the characters
|
||||
// available.
|
||||
//const desc = self.list.getValueAtIndex(macos.text.FontDescriptor, self.i);
|
||||
const desc = desc: {
|
||||
const original = self.list.getValueAtIndex(macos.text.FontDescriptor, self.i);
|
||||
|
||||
// For some reason simply copying the attributes and recreating
|
||||
// the descriptor removes the charset restriction. This is tested.
|
||||
const attrs = original.copyAttributes();
|
||||
defer attrs.release();
|
||||
break :desc try macos.text.FontDescriptor.createWithAttributes(
|
||||
@ptrCast(*macos.foundation.Dictionary, attrs),
|
||||
);
|
||||
};
|
||||
defer desc.release();
|
||||
|
||||
// Create our font. We need a size to initialize it so we use size
|
||||
// 12 but we will alter the size later.
|
||||
const desc = self.list.getValueAtIndex(macos.text.FontDescriptor, self.i);
|
||||
const font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||
errdefer font.release();
|
||||
|
||||
@ -284,6 +327,25 @@ test "fontconfig" {
|
||||
}
|
||||
}
|
||||
|
||||
test "fontconfig codepoint" {
|
||||
if (options.backend != .fontconfig_freetype) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
var fc = Fontconfig.init();
|
||||
var it = try fc.discover(.{ .codepoint = 'A', .size = 12 });
|
||||
defer it.deinit();
|
||||
|
||||
// The first result should have the codepoint. Later ones may not
|
||||
// because fontconfig returns all fonts sorted.
|
||||
const face = (try it.next()).?;
|
||||
try testing.expect(!face.loaded());
|
||||
try testing.expect(face.hasCodepoint('A', null));
|
||||
|
||||
// Should have other codepoints too
|
||||
try testing.expect(face.hasCodepoint('B', null));
|
||||
}
|
||||
|
||||
test "coretext" {
|
||||
if (options.backend != .coretext) return error.SkipZigTest;
|
||||
|
||||
@ -300,3 +362,23 @@ test "core text" {
|
||||
}
|
||||
try testing.expect(count > 0);
|
||||
}
|
||||
|
||||
test "coretext codepoint" {
|
||||
if (options.backend != .coretext) return error.SkipZigTest;
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
var ct = CoreText.init();
|
||||
defer ct.deinit();
|
||||
var it = try ct.discover(.{ .codepoint = 'A', .size = 12 });
|
||||
defer it.deinit();
|
||||
|
||||
// The first result should have the codepoint. Later ones may not
|
||||
// because fontconfig returns all fonts sorted.
|
||||
const face = (try it.next()).?;
|
||||
try testing.expect(!face.loaded());
|
||||
try testing.expect(face.hasCodepoint('A', null));
|
||||
|
||||
// Should have other codepoints too
|
||||
try testing.expect(face.hasCodepoint('B', null));
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ test {
|
||||
var atlas = try Atlas.init(alloc, 512, .greyscale);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer ft_font.deinit();
|
||||
|
||||
try testing.expectEqual(Presentation.text, ft_font.presentation);
|
||||
@ -477,7 +477,7 @@ test {
|
||||
const g1 = try ft_font.renderGlyph(alloc, &atlas, ft_font.glyphIndex('A').?, null);
|
||||
try testing.expectEqual(@as(u32, 11), g1.height);
|
||||
|
||||
try ft_font.setSize(.{ .points = 24 });
|
||||
try ft_font.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||
const g2 = try ft_font.renderGlyph(alloc, &atlas, ft_font.glyphIndex('A').?, null);
|
||||
try testing.expectEqual(@as(u32, 21), g2.height);
|
||||
}
|
||||
@ -493,7 +493,7 @@ test "color emoji" {
|
||||
var atlas = try Atlas.init(alloc, 512, .rgba);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer ft_font.deinit();
|
||||
|
||||
try testing.expectEqual(Presentation.emoji, ft_font.presentation);
|
||||
@ -517,7 +517,7 @@ test "metrics" {
|
||||
var atlas = try Atlas.init(alloc, 512, .greyscale);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var ft_font = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer ft_font.deinit();
|
||||
|
||||
try testing.expectEqual(font.face.Metrics{
|
||||
@ -531,7 +531,7 @@ test "metrics" {
|
||||
}, ft_font.metrics);
|
||||
|
||||
// Resize should change metrics
|
||||
try ft_font.setSize(.{ .points = 24 });
|
||||
try ft_font.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||
try testing.expectEqual(font.face.Metrics{
|
||||
.cell_width = 16,
|
||||
.cell_height = 35,
|
||||
|
Reference in New Issue
Block a user