summaryrefslogtreecommitdiff
path: root/src/font
diff options
context:
space:
mode:
authorDaniel Wennberg <daniel.wennberg@gmail.com>2025-09-18 12:34:32 -0700
committerDaniel Wennberg <daniel.wennberg@gmail.com>2025-09-18 12:34:32 -0700
commit4af4e18725b7cdfd3632bcc7eabd5a82c465ea55 (patch)
tree0c81e5ea04fc80ccb2c77dde1351c4ffe97c530a /src/font
parentbb607e0999f35cf24b31d5c861fd16414130c94f (diff)
Use approximate equality for float comparisons
Diffstat (limited to 'src/font')
-rw-r--r--src/font/Collection.zig173
1 files changed, 102 insertions, 71 deletions
diff --git a/src/font/Collection.zig b/src/font/Collection.zig
index c06358cbf..5a66749d6 100644
--- a/src/font/Collection.zig
+++ b/src/font/Collection.zig
@@ -1371,6 +1371,9 @@ test "adjusted sizes" {
}
test "face metrics" {
+ // The web canvas backend doesn't calculate face metrics, only cell metrics
+ if (options.backend != .web_canvas) return error.SkipZigTest;
+
const testing = std.testing;
const alloc = testing.allocator;
const narrowFont = font.embedded.cozette;
@@ -1403,80 +1406,108 @@ test "face metrics" {
.size_adjustment = .none,
});
- const narrowMetrics = (try c.getFace(narrowIndex)).getMetrics();
- const wideMetrics = (try c.getFace(wideIndex)).getMetrics();
+ const narrowMetrics: font.Metrics.FaceMetrics = (try c.getFace(narrowIndex)).getMetrics();
+ const wideMetrics: font.Metrics.FaceMetrics = (try c.getFace(wideIndex)).getMetrics();
// Verify provided/measured metrics. Measured
// values are backend-dependent due to hinting.
- if (options.backend != .web_canvas) {
- try std.testing.expectEqual(font.Metrics.FaceMetrics{
- .px_per_em = 16.0,
- .cell_width = switch (options.backend) {
- .freetype,
- .fontconfig_freetype,
- .coretext_freetype,
- => 8.0,
- .coretext,
- .coretext_harfbuzz,
- .coretext_noshape,
- => 7.3828125,
- .web_canvas => unreachable,
- },
- .ascent = 12.3046875,
- .descent = -3.6953125,
- .line_gap = 0.0,
- .underline_position = -1.2265625,
- .underline_thickness = 1.2265625,
- .strikethrough_position = 6.15625,
- .strikethrough_thickness = 1.234375,
- .cap_height = 9.84375,
- .ex_height = 7.3828125,
- .ascii_height = switch (options.backend) {
- .freetype,
- .fontconfig_freetype,
- .coretext_freetype,
- => 18.0625,
- .coretext,
- .coretext_harfbuzz,
- .coretext_noshape,
- => 16.0,
- .web_canvas => unreachable,
- },
- }, narrowMetrics);
- try std.testing.expectEqual(font.Metrics.FaceMetrics{
- .px_per_em = 16.0,
- .cell_width = switch (options.backend) {
- .freetype,
- .fontconfig_freetype,
- .coretext_freetype,
- => 10.0,
- .coretext,
- .coretext_harfbuzz,
- .coretext_noshape,
- => 9.6,
- .web_canvas => unreachable,
- },
- .ascent = 14.72,
- .descent = -3.52,
- .line_gap = 1.6,
- .underline_position = -1.6,
- .underline_thickness = 0.8,
- .strikethrough_position = 4.24,
- .strikethrough_thickness = 0.8,
- .cap_height = 11.36,
- .ex_height = 8.48,
- .ascii_height = switch (options.backend) {
- .freetype,
- .fontconfig_freetype,
- .coretext_freetype,
- => 16.0,
- .coretext,
- .coretext_harfbuzz,
- .coretext_noshape,
- => 15.472000000000001,
- .web_canvas => unreachable,
- },
- }, wideMetrics);
+ const narrowMetricsExpected = font.Metrics.FaceMetrics{
+ .px_per_em = 16.0,
+ .cell_width = switch (options.backend) {
+ .freetype,
+ .fontconfig_freetype,
+ .coretext_freetype,
+ => 8.0,
+ .coretext,
+ .coretext_harfbuzz,
+ .coretext_noshape,
+ => 7.3828125,
+ .web_canvas => unreachable,
+ },
+ .ascent = 12.3046875,
+ .descent = -3.6953125,
+ .line_gap = 0.0,
+ .underline_position = -1.2265625,
+ .underline_thickness = 1.2265625,
+ .strikethrough_position = 6.15625,
+ .strikethrough_thickness = 1.234375,
+ .cap_height = 9.84375,
+ .ex_height = 7.3828125,
+ .ascii_height = switch (options.backend) {
+ .freetype,
+ .fontconfig_freetype,
+ .coretext_freetype,
+ => 18.0625,
+ .coretext,
+ .coretext_harfbuzz,
+ .coretext_noshape,
+ => 16.0,
+ .web_canvas => unreachable,
+ },
+ };
+ const wideMetricsExpected = font.Metrics.FaceMetrics{
+ .px_per_em = 16.0,
+ .cell_width = switch (options.backend) {
+ .freetype,
+ .fontconfig_freetype,
+ .coretext_freetype,
+ => 10.0,
+ .coretext,
+ .coretext_harfbuzz,
+ .coretext_noshape,
+ => 9.6,
+ .web_canvas => unreachable,
+ },
+ .ascent = 14.72,
+ .descent = -3.52,
+ .line_gap = 1.6,
+ .underline_position = -1.6,
+ .underline_thickness = 0.8,
+ .strikethrough_position = 4.24,
+ .strikethrough_thickness = 0.8,
+ .cap_height = 11.36,
+ .ex_height = 8.48,
+ .ascii_height = switch (options.backend) {
+ .freetype,
+ .fontconfig_freetype,
+ .coretext_freetype,
+ => 16.0,
+ .coretext,
+ .coretext_harfbuzz,
+ .coretext_noshape,
+ => 15.472000000000001,
+ .web_canvas => unreachable,
+ },
+ };
+
+ inline for (
+ .{ narrowMetricsExpected, wideMetricsExpected },
+ .{ narrowMetrics, wideMetrics },
+ ) |metricsExpected, metricsActual| {
+ inline for (@typeInfo(font.Metrics.FaceMetrics).@"struct".fields) |field| {
+ const expected = @field(metricsExpected, field.name);
+ const actual = @field(metricsActual, field.name);
+ // Unwrap optional fields
+ const expectedValue, const actualValue = unwrap: switch (@typeInfo(field.type)) {
+ .optional => |Tinfo| {
+ if (expected) |expectedValue| {
+ const actualValue = actual orelse std.math.nan(Tinfo.child);
+ break :unwrap .{ expectedValue, actualValue };
+ }
+ // Null values can be compared directly
+ try std.testing.expectEqual(expected, actual);
+ continue;
+ },
+ else => break :unwrap .{ expected, actual },
+ };
+ // All non-null values are floats
+ const eps = std.math.floatEps(@TypeOf(actualValue - expectedValue));
+ try std.testing.expectApproxEqRel(
+ expectedValue,
+ actualValue,
+ std.math.sqrt(eps),
+ );
+ }
}
// Verify estimated metrics. icWidth() should equal the smaller of