From 08bca077b262abe4b804653fbe8605ae44b0808d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 17 Nov 2022 16:07:20 -0800 Subject: [PATCH] search for unknown codepoints in any available font face If an unknown codepoint is rendered, we now will query the OS for ANY font that can satisfy the codepoint (rather than rendering `?`). --- src/Window.zig | 4 ++-- src/font/Group.zig | 47 +++++++++++++++++++++++++++++++++++------ src/font/GroupCache.zig | 2 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index c4ec2a012..748d75148 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -175,12 +175,12 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { errdefer alloc.destroy(font_group); font_group.* = try font.GroupCache.init(alloc, group: { var group = try font.Group.init(alloc, font_lib, font_size); - errdefer group.deinit(alloc); + errdefer group.deinit(); // Search for fonts if (font.Discover != void) { var disco = font.Discover.init(); - defer disco.deinit(); + group.discover = disco; if (config.@"font-family") |family| { var disco_it = try disco.discover(.{ diff --git a/src/font/Group.zig b/src/font/Group.zig index b7173fc61..1d1f168fc 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -29,6 +29,9 @@ const log = std.log.scoped(.font_group); // to the user so we can change this later. const StyleArray = std.EnumArray(Style, std.ArrayListUnmanaged(DeferredFace)); +/// The allocator for this group +alloc: Allocator, + /// The library being used for all the faces. lib: Library, @@ -39,12 +42,16 @@ size: font.face.DesiredSize, /// Instead, use the functions available on Group. faces: StyleArray, +/// If discovery is available, we'll look up fonts where we can't find +/// the codepoint. +discover: ?font.Discover = null, + pub fn init( alloc: Allocator, lib: Library, size: font.face.DesiredSize, ) !Group { - var result = Group{ .lib = lib, .size = size, .faces = undefined }; + var result = Group{ .alloc = alloc, .lib = lib, .size = size, .faces = undefined }; // Initialize all our styles to initially sized lists. var i: usize = 0; @@ -56,11 +63,15 @@ pub fn init( return result; } -pub fn deinit(self: *Group, alloc: Allocator) void { +pub fn deinit(self: *Group) void { var it = self.faces.iterator(); while (it.next()) |entry| { for (entry.value.items) |*item| item.deinit(); - entry.value.deinit(alloc); + entry.value.deinit(self.alloc); + } + + if (font.Discover != void) { + if (self.discover) |*discover| discover.deinit(); } } @@ -126,7 +137,7 @@ pub const FontIndex = packed struct { /// is allowed. This func will NOT determine the default presentation for /// a code point. pub fn indexForCodepoint( - self: Group, + self: *Group, cp: u32, style: Style, p: ?Presentation, @@ -134,6 +145,28 @@ pub fn indexForCodepoint( // If we can find the exact value, then return that. if (self.indexForCodepointExact(cp, style, p)) |value| return value; + // Try looking for another font that will satisfy this request. + if (font.Discover != void) { + if (self.discover) |*disco| discover: { + var disco_it = disco.discover(.{ + .codepoint = cp, + .size = self.size.points, + .bold = style == .bold or style == .bold_italic, + .italic = style == .italic or style == .bold_italic, + }) catch break :discover; + defer disco_it.deinit(); + + if (disco_it.next() catch break :discover) |face| { + log.info("found codepoint 0x{x} in fallback face={s}", .{ + cp, + face.name() catch "", + }); + self.addFace(self.alloc, style, face) catch break :discover; + if (self.indexForCodepointExact(cp, style, p)) |value| return value; + } + } + } + // If this is already regular, we're done falling back. if (style == .regular and p == null) return null; @@ -200,7 +233,7 @@ test { defer lib.deinit(); var group = try init(alloc, lib, .{ .points = 12 }); - defer group.deinit(alloc); + defer group.deinit(); 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, testEmoji, .{ .points = 12 }))); @@ -257,7 +290,7 @@ test "resize" { defer lib.deinit(); var group = try init(alloc, lib, .{ .points = 12, .xdpi = 96, .ydpi = 96 }); - defer group.deinit(alloc); + defer group.deinit(); try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 }))); @@ -311,7 +344,7 @@ test "discover monospace with fontconfig and freetype" { var lib = try Library.init(); defer lib.deinit(); var group = try init(alloc, lib, .{ .points = 12 }); - defer group.deinit(alloc); + defer group.deinit(); try group.addFace(alloc, .regular, (try it.next()).?); // Should find all visible ASCII diff --git a/src/font/GroupCache.zig b/src/font/GroupCache.zig index 44e7d8337..4ed68f6dc 100644 --- a/src/font/GroupCache.zig +++ b/src/font/GroupCache.zig @@ -70,7 +70,7 @@ pub fn deinit(self: *GroupCache, alloc: Allocator) void { self.glyphs.deinit(alloc); self.atlas_greyscale.deinit(alloc); self.atlas_color.deinit(alloc); - self.group.deinit(alloc); + self.group.deinit(); } /// Reset the cache. This should be called: