Skip to content

Commit

Permalink
Enable bitmap font usage under CoreText (#4115)
Browse files Browse the repository at this point in the history
macOS bitmap-only fonts are a poorly documented format, which are often
distributed as `.dfont` or `.dfon` files. They use a 'bhed' table in
place of the usual 'head', but the table format is byte-identical, so
enabling the use of bitmap-only fonts only requires us to properly fetch
this table while calculating metrics.

ref: https://fontforge.org/docs/techref/bitmaponlysfnt.html

Reverts #3550 for obvious reasons, and may close issue #2168 because
this should now mean that bitmap fonts are properly supported under both
font backends due to #3837 - unless `otb` fonts are still not properly
supported under FreeType (they are not supported under CoreText because
CoreText does not know how to handle them).

I tested this change with the `.dfont` distribution of
[Cozette](https://github.com/slavfox/Cozette) v1.25.2 and saw no visual
issues.
  • Loading branch information
mitchellh authored Dec 30, 2024
2 parents 41df2d9 + 7a4215a commit ef542c6
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 47 deletions.
49 changes: 4 additions & 45 deletions src/font/discovery.zig
Original file line number Diff line number Diff line change
Expand Up @@ -362,16 +362,9 @@ pub const CoreText = struct {
const list = set.createMatchingFontDescriptors();
defer list.release();

// Bring the list of descriptors in to zig land
var zig_list = try copyMatchingDescriptors(alloc, list);
errdefer alloc.free(zig_list);

// Filter them. We don't use `CTFontCollectionSetExclusionDescriptors`
// to do this because that requires a mutable collection. This way is
// much more straight forward.
zig_list = try alloc.realloc(zig_list, filterDescriptors(zig_list));

// Sort our descriptors
const zig_list = try copyMatchingDescriptors(alloc, list);
errdefer alloc.free(zig_list);
sortMatchingDescriptors(&desc, zig_list);

return DiscoverIterator{
Expand Down Expand Up @@ -558,47 +551,13 @@ pub const CoreText = struct {
for (0..result.len) |i| {
result[i] = list.getValueAtIndex(macos.text.FontDescriptor, i);

// We need to retain because once the list
// is freed it will release all its members.
// We need to retain becauseonce the list is freed it will
// release all its members.
result[i].retain();
}
return result;
}

/// Filter any descriptors out of the list that aren't acceptable for
/// some reason or another (e.g. the font isn't in a format we can handle).
///
/// Invalid descriptors are filled in from the end of
/// the list and the new length for the list is returned.
fn filterDescriptors(list: []*macos.text.FontDescriptor) usize {
var end = list.len;
var i: usize = 0;
while (i < end) {
if (validDescriptor(list[i])) {
i += 1;
} else {
list[i].release();
end -= 1;
list[i] = list[end];
}
}
return end;
}

/// Used by `filterDescriptors` to decide whether a descriptor is valid.
fn validDescriptor(desc: *macos.text.FontDescriptor) bool {
if (desc.copyAttribute(macos.text.FontAttribute.format)) |format| {
defer format.release();
var value: c_int = undefined;
assert(format.getValue(.int, &value));

// Bitmap fonts are not currently supported.
if (value == macos.text.c.kCTFontFormatBitmap) return false;
}

return true;
}

fn sortMatchingDescriptors(
desc: *const Descriptor,
list: []*macos.text.FontDescriptor,
Expand Down
13 changes: 11 additions & 2 deletions src/font/face/coretext.zig
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,17 @@ pub const Face = struct {
fn calcMetrics(ct_font: *macos.text.Font) CalcMetricsError!font.face.Metrics {
// Read the 'head' table out of the font data.
const head: opentype.Head = head: {
const tag = macos.text.FontTableTag.init("head");
const data = ct_font.copyTable(tag) orelse return error.CopyTableError;
// macOS bitmap-only fonts use a 'bhed' tag rather than 'head', but
// the table format is byte-identical to the 'head' table, so if we
// can't find 'head' we try 'bhed' instead before failing.
//
// ref: https://fontforge.org/docs/techref/bitmaponlysfnt.html
const head_tag = macos.text.FontTableTag.init("head");
const bhed_tag = macos.text.FontTableTag.init("bhed");
const data =
ct_font.copyTable(head_tag) orelse
ct_font.copyTable(bhed_tag) orelse
return error.CopyTableError;
defer data.release();
const ptr = data.getPointer();
const len = data.getLength();
Expand Down

0 comments on commit ef542c6

Please sign in to comment.