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;
|
))) 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 {
|
pub fn release(self: *MutableDictionary) void {
|
||||||
foundation.CFRelease(self);
|
foundation.CFRelease(self);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,19 @@ pub const FontDescriptor = opaque {
|
|||||||
) orelse Allocator.Error.OutOfMemory;
|
) 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 {
|
pub fn release(self: *FontDescriptor) void {
|
||||||
c.CFRelease(self);
|
c.CFRelease(self);
|
||||||
}
|
}
|
||||||
@ -28,6 +41,12 @@ pub const FontDescriptor = opaque {
|
|||||||
@ptrCast(c.CFStringRef, attr.key()),
|
@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 {
|
pub const FontAttribute = enum {
|
||||||
|
@ -189,7 +189,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
});
|
});
|
||||||
defer disco_it.deinit();
|
defer disco_it.deinit();
|
||||||
if (try disco_it.next()) |face| {
|
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);
|
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();
|
defer disco_it.deinit();
|
||||||
if (try disco_it.next()) |face| {
|
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);
|
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();
|
defer disco_it.deinit();
|
||||||
if (try disco_it.next()) |face| {
|
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);
|
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();
|
defer disco_it.deinit();
|
||||||
if (try disco_it.next()) |face| {
|
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);
|
try group.addFace(alloc, .bold_italic, face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,10 +256,10 @@ test "resize" {
|
|||||||
var lib = try Library.init();
|
var lib = try Library.init();
|
||||||
defer lib.deinit();
|
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);
|
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
|
// Load a letter
|
||||||
{
|
{
|
||||||
@ -278,7 +278,7 @@ test "resize" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resize
|
// Resize
|
||||||
try group.setSize(.{ .points = 24 });
|
try group.setSize(.{ .points = 24, .xdpi = 96, .ydpi = 96 });
|
||||||
{
|
{
|
||||||
const idx = group.indexForCodepoint('A', .regular, null).?;
|
const idx = group.indexForCodepoint('A', .regular, null).?;
|
||||||
const face = try group.faceFromIndex(idx);
|
const face = try group.faceFromIndex(idx);
|
||||||
|
@ -253,7 +253,7 @@ test "resize" {
|
|||||||
try cache.group.addFace(
|
try cache.group.addFace(
|
||||||
alloc,
|
alloc,
|
||||||
.regular,
|
.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
|
// Load a letter
|
||||||
@ -272,7 +272,7 @@ test "resize" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 idx = (try cache.indexForCodepoint(alloc, 'A', .regular, null)).?;
|
||||||
const face = try cache.group.faceFromIndex(idx);
|
const face = try cache.group.faceFromIndex(idx);
|
||||||
|
@ -6,7 +6,7 @@ const macos = @import("macos");
|
|||||||
const options = @import("main.zig").options;
|
const options = @import("main.zig").options;
|
||||||
const DeferredFace = @import("main.zig").DeferredFace;
|
const DeferredFace = @import("main.zig").DeferredFace;
|
||||||
|
|
||||||
const log = std.log.named(.discovery);
|
const log = std.log.scoped(.discovery);
|
||||||
|
|
||||||
/// Discover implementation for the compile options.
|
/// Discover implementation for the compile options.
|
||||||
pub const Discover = switch (options.backend) {
|
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
|
/// On systems that use fontconfig (Linux), this can be a full
|
||||||
/// fontconfig pattern, such as "Fira Code-14:bold".
|
/// 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
|
/// 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.
|
/// to pixels, we will use 72 DPI for Mac and 96 DPI for everything else.
|
||||||
/// (If pixel conversion is necessary, i.e. emoji fonts)
|
/// (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
|
/// True if we want to search specifically for a font that supports
|
||||||
/// bold, italic, or both.
|
/// bold, italic, or both.
|
||||||
@ -44,7 +47,15 @@ pub const Descriptor = struct {
|
|||||||
/// must still do this.
|
/// must still do this.
|
||||||
pub fn toFcPattern(self: Descriptor) *fontconfig.Pattern {
|
pub fn toFcPattern(self: Descriptor) *fontconfig.Pattern {
|
||||||
const pat = fontconfig.Pattern.create();
|
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(
|
if (self.size > 0) assert(pat.add(
|
||||||
.size,
|
.size,
|
||||||
.{ .integer = self.size },
|
.{ .integer = self.size },
|
||||||
@ -70,13 +81,28 @@ pub const Descriptor = struct {
|
|||||||
const attrs = try macos.foundation.MutableDictionary.create(0);
|
const attrs = try macos.foundation.MutableDictionary.create(0);
|
||||||
defer attrs.release();
|
defer attrs.release();
|
||||||
|
|
||||||
// Family is always set
|
// Family
|
||||||
const family = try macos.foundation.String.createWithBytes(self.family, .utf8, false);
|
if (self.family) |family_bytes| {
|
||||||
|
const family = try macos.foundation.String.createWithBytes(family_bytes, .utf8, false);
|
||||||
defer family.release();
|
defer family.release();
|
||||||
attrs.setValue(
|
attrs.setValue(
|
||||||
macos.text.FontAttribute.family_name.key(),
|
macos.text.FontAttribute.family_name.key(),
|
||||||
family,
|
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
|
// Set our size attribute if set
|
||||||
if (self.size > 0) {
|
if (self.size > 0) {
|
||||||
@ -254,9 +280,26 @@ pub const CoreText = struct {
|
|||||||
pub fn next(self: *DiscoverIterator) !?DeferredFace {
|
pub fn next(self: *DiscoverIterator) !?DeferredFace {
|
||||||
if (self.i >= self.list.getCount()) return null;
|
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
|
// Create our font. We need a size to initialize it so we use size
|
||||||
// 12 but we will alter the size later.
|
// 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);
|
const font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||||
errdefer font.release();
|
errdefer font.release();
|
||||||
|
|
||||||
@ -284,7 +327,26 @@ test "fontconfig" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "core text" {
|
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;
|
if (options.backend != .coretext) return error.SkipZigTest;
|
||||||
|
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
@ -300,3 +362,23 @@ test "core text" {
|
|||||||
}
|
}
|
||||||
try testing.expect(count > 0);
|
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);
|
var atlas = try Atlas.init(alloc, 512, .greyscale);
|
||||||
defer atlas.deinit(alloc);
|
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();
|
defer ft_font.deinit();
|
||||||
|
|
||||||
try testing.expectEqual(Presentation.text, ft_font.presentation);
|
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);
|
const g1 = try ft_font.renderGlyph(alloc, &atlas, ft_font.glyphIndex('A').?, null);
|
||||||
try testing.expectEqual(@as(u32, 11), g1.height);
|
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);
|
const g2 = try ft_font.renderGlyph(alloc, &atlas, ft_font.glyphIndex('A').?, null);
|
||||||
try testing.expectEqual(@as(u32, 21), g2.height);
|
try testing.expectEqual(@as(u32, 21), g2.height);
|
||||||
}
|
}
|
||||||
@ -493,7 +493,7 @@ test "color emoji" {
|
|||||||
var atlas = try Atlas.init(alloc, 512, .rgba);
|
var atlas = try Atlas.init(alloc, 512, .rgba);
|
||||||
defer atlas.deinit(alloc);
|
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();
|
defer ft_font.deinit();
|
||||||
|
|
||||||
try testing.expectEqual(Presentation.emoji, ft_font.presentation);
|
try testing.expectEqual(Presentation.emoji, ft_font.presentation);
|
||||||
@ -517,7 +517,7 @@ test "metrics" {
|
|||||||
var atlas = try Atlas.init(alloc, 512, .greyscale);
|
var atlas = try Atlas.init(alloc, 512, .greyscale);
|
||||||
defer atlas.deinit(alloc);
|
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();
|
defer ft_font.deinit();
|
||||||
|
|
||||||
try testing.expectEqual(font.face.Metrics{
|
try testing.expectEqual(font.face.Metrics{
|
||||||
@ -531,7 +531,7 @@ test "metrics" {
|
|||||||
}, ft_font.metrics);
|
}, ft_font.metrics);
|
||||||
|
|
||||||
// Resize should change 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{
|
try testing.expectEqual(font.face.Metrics{
|
||||||
.cell_width = 16,
|
.cell_width = 16,
|
||||||
.cell_height = 35,
|
.cell_height = 35,
|
||||||
|
Reference in New Issue
Block a user