font: set "backend" enum vs booleans

This commit is contained in:
Mitchell Hashimoto
2022-10-02 09:41:37 -07:00
parent 12c9482d48
commit f41cbf228b
4 changed files with 89 additions and 65 deletions

View File

@ -19,10 +19,12 @@ const Presentation = @import("main.zig").Presentation;
face: ?Face = null, face: ?Face = null,
/// Fontconfig /// Fontconfig
fc: if (options.fontconfig) ?Fontconfig else void = if (options.fontconfig) null else {}, fc: if (options.backend == .fontconfig_freetype) ?Fontconfig else void =
if (options.backend == .fontconfig_freetype) null else {},
/// CoreText /// CoreText
ct: if (options.coretext) ?CoreText else void = if (options.coretext) null else {}, ct: if (options.backend == .coretext) ?CoreText else void =
if (options.backend == .coretext) null else {},
/// Fontconfig specific data. This is only present if building with fontconfig. /// Fontconfig specific data. This is only present if building with fontconfig.
pub const Fontconfig = struct { pub const Fontconfig = struct {
@ -61,8 +63,11 @@ pub fn initLoaded(face: Face) DeferredFace {
pub fn deinit(self: *DeferredFace) void { pub fn deinit(self: *DeferredFace) void {
if (self.face) |*face| face.deinit(); if (self.face) |*face| face.deinit();
if (options.fontconfig) if (self.fc) |*fc| fc.deinit(); switch (options.backend) {
if (options.coretext) if (self.ct) |*ct| ct.deinit(); .fontconfig_freetype => if (self.fc) |*fc| fc.deinit(),
.coretext => if (self.ct) |*ct| ct.deinit(),
.freetype => {},
}
self.* = undefined; self.* = undefined;
} }
@ -74,16 +79,16 @@ pub inline fn loaded(self: DeferredFace) bool {
/// Returns the name of this face. The memory is always owned by the /// Returns the name of this face. The memory is always owned by the
/// face so it doesn't have to be freed. /// face so it doesn't have to be freed.
pub fn name(self: DeferredFace) ![:0]const u8 { pub fn name(self: DeferredFace) ![:0]const u8 {
if (options.fontconfig) { switch (options.backend) {
if (self.fc) |fc| .fontconfig_freetype => if (self.fc) |fc|
return (try fc.pattern.get(.fullname, 0)).string; return (try fc.pattern.get(.fullname, 0)).string,
}
if (options.coretext) { .coretext => if (self.ct) |ct| {
if (self.ct) |ct| {
const display_name = ct.font.copyDisplayName(); const display_name = ct.font.copyDisplayName();
return display_name.cstringPtr(.utf8) orelse "<unsupported internal encoding>"; return display_name.cstringPtr(.utf8) orelse "<unsupported internal encoding>";
} },
.freetype => {},
} }
return "TODO: built-in font names"; return "TODO: built-in font names";
@ -98,19 +103,21 @@ pub fn load(
// No-op if we already loaded // No-op if we already loaded
if (self.face != null) return; if (self.face != null) return;
if (options.fontconfig) { switch (options.backend) {
try self.loadFontconfig(lib, size); .fontconfig_freetype => {
return; try self.loadFontconfig(lib, size);
} return;
},
if (options.coretext) { .coretext => {
try self.loadCoreText(lib, size); try self.loadCoreText(lib, size);
return; return;
} },
// Unreachable because we must be already loaded or have the // Unreachable because we must be already loaded or have the
// proper configuration for one of the other deferred mechanisms. // proper configuration for one of the other deferred mechanisms.
unreachable; .freetype => unreachable,
}
} }
fn loadFontconfig( fn loadFontconfig(
@ -181,40 +188,44 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
return face.glyphIndex(cp) != null; return face.glyphIndex(cp) != null;
} }
// If we are using fontconfig, use the fontconfig metadata to switch (options.backend) {
// avoid loading the face. .fontconfig_freetype => {
if (options.fontconfig) { // If we are using fontconfig, use the fontconfig metadata to
if (self.fc) |fc| { // avoid loading the face.
// Check if char exists if (self.fc) |fc| {
if (!fc.charset.hasChar(cp)) return false; // Check if char exists
if (!fc.charset.hasChar(cp)) return false;
// If we have a presentation, check it matches // If we have a presentation, check it matches
if (p) |desired| { if (p) |desired| {
const emoji_lang = "und-zsye"; const emoji_lang = "und-zsye";
const actual: Presentation = if (fc.langset.hasLang(emoji_lang)) const actual: Presentation = if (fc.langset.hasLang(emoji_lang))
.emoji .emoji
else else
.text; .text;
return desired == actual; return desired == actual;
}
return true;
} }
},
return true; .coretext => {
} // If we are using coretext, we check the loaded CT font.
} if (self.ct) |ct| {
// Turn UTF-32 into UTF-16 for CT API
var unichars: [2]u16 = undefined;
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(cp, &unichars);
const len: usize = if (pair) 2 else 1;
// If we are using coretext, we check the loaded CT font. // Get our glyphs
if (options.coretext) { var glyphs = [2]macos.graphics.Glyph{ 0, 0 };
if (self.ct) |ct| { return ct.font.getGlyphsForCharacters(unichars[0..len], glyphs[0..len]);
// Turn UTF-32 into UTF-16 for CT API }
var unichars: [2]u16 = undefined; },
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(cp, &unichars);
const len: usize = if (pair) 2 else 1;
// Get our glyphs .freetype => {},
var glyphs = [2]macos.graphics.Glyph{ 0, 0 };
return ct.font.getGlyphsForCharacters(unichars[0..len], glyphs[0..len]);
}
} }
// This is unreachable because discovery mechanisms terminate, and // This is unreachable because discovery mechanisms terminate, and
@ -239,7 +250,7 @@ test "preloaded" {
} }
test "fontconfig" { test "fontconfig" {
if (!options.fontconfig) return error.SkipZigTest; if (options.backend != .fontconfig_freetype) return error.SkipZigTest;
const discovery = @import("main.zig").discovery; const discovery = @import("main.zig").discovery;
const testing = std.testing; const testing = std.testing;
@ -269,7 +280,7 @@ test "fontconfig" {
} }
test "coretext" { test "coretext" {
if (!options.coretext) return error.SkipZigTest; if (options.backend != .coretext) return error.SkipZigTest;
const discovery = @import("main.zig").discovery; const discovery = @import("main.zig").discovery;
const testing = std.testing; const testing = std.testing;

View File

@ -224,7 +224,7 @@ test {
} }
test { test {
if (!options.fontconfig) return error.SkipZigTest; if (options.backend != .fontconfig_freetype) return error.SkipZigTest;
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -9,12 +9,11 @@ const DeferredFace = @import("main.zig").DeferredFace;
const log = std.log.named(.discovery); const log = std.log.named(.discovery);
/// Discover implementation for the compile options. /// Discover implementation for the compile options.
pub const Discover = if (options.fontconfig) pub const Discover = switch (options.backend) {
Fontconfig .fontconfig_freetype => Fontconfig,
else if (options.coretext) .coretext => CoreText,
CoreText else => void,
else };
void;
/// Descriptor is used to search for fonts. The only required field /// Descriptor is used to search for fonts. The only required field
/// is "family". The rest are ignored unless they're set to a non-zero /// is "family". The rest are ignored unless they're set to a non-zero
@ -273,7 +272,7 @@ pub const CoreText = struct {
}; };
test "fontconfig" { test "fontconfig" {
if (!options.fontconfig) return error.SkipZigTest; if (options.backend != .fontconfig_freetype) return error.SkipZigTest;
const testing = std.testing; const testing = std.testing;
@ -286,7 +285,7 @@ test "fontconfig" {
} }
test "core text" { test "core text" {
if (!options.coretext) return error.SkipZigTest; if (options.backend != .coretext) return error.SkipZigTest;
const testing = std.testing; const testing = std.testing;

View File

@ -14,11 +14,25 @@ pub const Discover = discovery.Discover;
/// Build options /// Build options
pub const options: struct { pub const options: struct {
coretext: bool = false, backend: Backend,
fontconfig: bool = false,
} = .{ } = .{
.coretext = build_options.coretext, .backend = if (build_options.coretext)
.fontconfig = build_options.fontconfig, .coretext
else if (build_options.fontconfig)
.fontconfig_freetype
else
.freetype,
};
pub const Backend = enum {
/// FreeType for font rendering with no font discovery enabled.
freetype,
/// Fontconfig for font discovery and FreeType for font rendering.
fontconfig_freetype,
/// CoreText for both font discovery and rendering (macOS).
coretext,
}; };
/// The styles that a family can take. /// The styles that a family can take.