font: add bitmap font tests for BDF, PCF, and OTB formats (#10193)

Adds test coverage for bitmap font rendering in the FreeType backend
using the Spleen 8x16 font in three formats:

- BDF (Bitmap Distribution Format)
- PCF (Portable Compiled Format)
- OTB (OpenType Bitmap)

Tests validate glyph dimensions and pixel-perfect rendering against
expected patterns, following the model established by the existing
TerminusTTF test.

Spleen was chosen because it ships in all three required formats from a
single source, providing consistent test data across formats. Its BSD
2-Clause license is compatible with the existing font licenses in the
repo (OFL, MIT). No existing embedded fonts could be used since they are
all TTF/OTF files that use the TrueType loader rather than the
BDF/PCF/OTB loaders being tested.

Addresses #8524.

---

This PR was written with assistance from Claude Code.
This commit is contained in:
Mitchell Hashimoto
2026-01-06 09:26:27 -08:00
committed by GitHub
9 changed files with 22515 additions and 1 deletions

1
.gitattributes vendored
View File

@@ -10,4 +10,5 @@ pkg/libintl/libintl.h linguist-generated=true
pkg/simdutf/vendor/** linguist-vendored
src/font/nerd_font_attributes.zig linguist-generated=true
src/font/nerd_font_codepoint_tables.py linguist-generated=true
src/font/res/** linguist-vendored
src/terminal/res/** linguist-vendored

View File

@@ -47,3 +47,9 @@ pub const monaspace_neon = @embedFile("res/MonaspaceNeon-Regular.otf");
/// Terminus TTF is a scalable font with bitmap glyphs at various sizes.
pub const terminus_ttf = @embedFile("res/TerminusTTF-Regular.ttf");
/// Spleen is a monospaced bitmap font available in multiple formats.
/// Used for testing bitmap font support across different file formats.
pub const spleen_bdf = @embedFile("res/spleen-8x16.bdf");
pub const spleen_pcf = @embedFile("res/spleen-8x16.pcf");
pub const spleen_otb = @embedFile("res/spleen-8x16.otb");

View File

@@ -1284,3 +1284,153 @@ test "bitmap glyph" {
}
}
}
// Expected pixel pattern for Spleen 8x16 'A' (glyph index from char 'A')
// Derived from BDF BITMAP data: 00,00,7C,C6,C6,C6,FE,C6,C6,C6,C6,C6,00,00,00,00
const spleen_A =
\\........
\\........
\\.#####..
\\##...##.
\\##...##.
\\##...##.
\\#######.
\\##...##.
\\##...##.
\\##...##.
\\##...##.
\\##...##.
\\........
\\........
\\........
\\........
;
// Including the newline
const spleen_A_pitch = 9;
// Test parameters for bitmap font tests
const spleen_test_point_size = 12;
const spleen_test_dpi = 96;
test "bitmap glyph BDF" {
const alloc = testing.allocator;
const testFont = font.embedded.spleen_bdf;
var lib = try Library.init(alloc);
defer lib.deinit();
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
defer atlas.deinit(alloc);
// Spleen 8x16 is a pure bitmap font at 16px height
var ft_font = try Face.init(lib, testFont, .{ .size = .{
.points = spleen_test_point_size,
.xdpi = spleen_test_dpi,
.ydpi = spleen_test_dpi,
} });
defer ft_font.deinit();
// Get glyph index for 'A'
const glyph_index = ft_font.glyphIndex('A') orelse return error.GlyphNotFound;
const glyph = try ft_font.renderGlyph(
alloc,
&atlas,
glyph_index,
.{ .grid_metrics = font.Metrics.calc(ft_font.getMetrics()) },
);
// Verify dimensions match Spleen 8x16
try testing.expectEqual(8, glyph.width);
try testing.expectEqual(16, glyph.height);
// Verify pixel-perfect rendering
for (0..glyph.height) |y| {
for (0..glyph.width) |x| {
const pixel = spleen_A[y * spleen_A_pitch + x];
try testing.expectEqual(
@as(u8, if (pixel == '#') 255 else 0),
atlas.data[(glyph.atlas_y + y) * atlas.size + (glyph.atlas_x + x)],
);
}
}
}
test "bitmap glyph PCF" {
const alloc = testing.allocator;
const testFont = font.embedded.spleen_pcf;
var lib = try Library.init(alloc);
defer lib.deinit();
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
defer atlas.deinit(alloc);
var ft_font = try Face.init(lib, testFont, .{ .size = .{
.points = spleen_test_point_size,
.xdpi = spleen_test_dpi,
.ydpi = spleen_test_dpi,
} });
defer ft_font.deinit();
const glyph_index = ft_font.glyphIndex('A') orelse return error.GlyphNotFound;
const glyph = try ft_font.renderGlyph(
alloc,
&atlas,
glyph_index,
.{ .grid_metrics = font.Metrics.calc(ft_font.getMetrics()) },
);
try testing.expectEqual(8, glyph.width);
try testing.expectEqual(16, glyph.height);
for (0..glyph.height) |y| {
for (0..glyph.width) |x| {
const pixel = spleen_A[y * spleen_A_pitch + x];
try testing.expectEqual(
@as(u8, if (pixel == '#') 255 else 0),
atlas.data[(glyph.atlas_y + y) * atlas.size + (glyph.atlas_x + x)],
);
}
}
}
test "bitmap glyph OTB" {
const alloc = testing.allocator;
const testFont = font.embedded.spleen_otb;
var lib = try Library.init(alloc);
defer lib.deinit();
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
defer atlas.deinit(alloc);
var ft_font = try Face.init(lib, testFont, .{ .size = .{
.points = spleen_test_point_size,
.xdpi = spleen_test_dpi,
.ydpi = spleen_test_dpi,
} });
defer ft_font.deinit();
const glyph_index = ft_font.glyphIndex('A') orelse return error.GlyphNotFound;
const glyph = try ft_font.renderGlyph(
alloc,
&atlas,
glyph_index,
.{ .grid_metrics = font.Metrics.calc(ft_font.getMetrics()) },
);
try testing.expectEqual(8, glyph.width);
try testing.expectEqual(16, glyph.height);
for (0..glyph.height) |y| {
for (0..glyph.width) |x| {
const pixel = spleen_A[y * spleen_A_pitch + x];
try testing.expectEqual(
@as(u8, if (pixel == '#') 255 else 0),
atlas.data[(glyph.atlas_y + y) * atlas.size + (glyph.atlas_x + x)],
);
}
}
}

24
src/font/res/BSD-2-Clause.txt vendored Normal file
View File

@@ -0,0 +1,24 @@
Copyright (c) 2018-2024, Frederic Cambus
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,6 +1,6 @@
# Fonts and Licenses
This project uses several fonts which fall under the SIL Open Font License (OFL-1.1) and MIT License:
This project uses several fonts which fall under the SIL Open Font License (OFL-1.1), MIT License, and BSD 2-Clause License:
- Code New Roman (OFL-1.1)
- [© 2014 Sam Radian. All Rights Reserved.](https://github.com/chrissimpkins/codeface/blob/master/fonts/code-new-roman/license.txt)
@@ -28,8 +28,12 @@ This project uses several fonts which fall under the SIL Open Font License (OFL-
- Terminus TTF (OFL-1.1)
- [Copyright (c) 2010-2020 Dimitar Toshkov Zhekov with Reserved Font Name "Terminus Font"](https://sourceforge.net/projects/terminus-font/)
- [Copyright (c) 2011-2023 Tilman Blumenbach with Reserved Font Name "Terminus (TTF)"](https://files.ax86.net/terminus-ttf/)
- Spleen (BSD 2-Clause)
- [Copyright (c) 2018-2024, Frederic Cambus](https://github.com/fcambus/spleen)
A full copy of the OFL license can be found at [OFL.txt](./OFL.txt).
An accompanying FAQ is also available at <https://openfontlicense.org/>.
A full copy of the MIT license can be found at [MIT.txt](./MIT.txt).
A full copy of the BSD 2-Clause license can be found at [BSD-2-Clause.txt](./BSD-2-Clause.txt).

22328
src/font/res/spleen-8x16.bdf vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
src/font/res/spleen-8x16.otb vendored Normal file

Binary file not shown.

BIN
src/font/res/spleen-8x16.pcf vendored Normal file

Binary file not shown.

View File

@@ -19,6 +19,7 @@ extend-exclude = [
# Fonts
"*.ttf",
"*.otf",
"*.bdf",
# Images
"*.png",
"*.ico",