diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 3a69ef95b..ee2460572 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -594,7 +594,10 @@ pub const Face = struct { // All of these metrics are based on our layout above. const cell_height = @ceil(layout_metrics.height); const cell_baseline = @ceil(layout_metrics.height - layout_metrics.ascent); + const underline_thickness = @ceil(@as(f32, @floatCast(ct_font.getUnderlineThickness()))); + const strikethrough_thickness = underline_thickness; + const strikethrough_position = strikethrough_position: { // This is the height of lower case letters in our font. const ex_height = ct_font.getXHeight(); @@ -608,20 +611,21 @@ pub const Face = struct { // is the distance from the top down to the baseline, then // we subtract half of the ex height to go back up to the // correct height that should evenly split lowercase text. - const pos = layout_metrics.ascent - ex_height * 0.5 + 1; + const pos = layout_metrics.ascent - + ex_height * 0.5 + + strikethrough_thickness * 0.5 + + 1; break :strikethrough_position @ceil(pos); }; - const strikethrough_thickness = underline_thickness; // Underline position reported is usually something like "-1" to // represent the amount under the baseline. We add this to our real // baseline to get the actual value from the bottom (+y is up). // The final underline position is +y from the TOP (confusing) // so we have to subtract from the cell height. - const underline_position = cell_height - - (cell_baseline + @ceil(@as(f32, @floatCast(ct_font.getUnderlinePosition())))) + - 1; + const underline_position = @ceil(layout_metrics.ascent - + @as(f32, @floatCast(ct_font.getUnderlinePosition())) + 1); // Note: is this useful? // const units_per_em = ct_font.getUnitsPerEm(); diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index 04f037c85..32664a1fd 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -660,52 +660,50 @@ pub const Face = struct { // is reversed. const cell_baseline = -1 * f26dot6ToFloat(size_metrics.descender); + const underline_thickness = @max(@as(f32, 1), fontUnitsToPxY( + face, + face.handle.*.underline_thickness, + )); + // The underline position. This is a value from the top where the // underline should go. const underline_position: f32 = underline_pos: { - // The ascender is already scaled for scalable fonts, but the - // underline position is not. - const ascender_px = @as(i32, @intCast(size_metrics.ascender)) >> 6; - const declared_px = freetype.mulFix( + const declared_px = @as(f32, @floatFromInt(freetype.mulFix( face.handle.*.underline_position, @intCast(face.handle.*.size.*.metrics.y_scale), - ) >> 6; + ))) / 64; // We use the declared underline position if its available - const declared = ascender_px - declared_px; + const declared = cell_height - cell_baseline - declared_px; if (declared > 0) - break :underline_pos @floatFromInt(declared); + break :underline_pos declared; // If we have no declared underline position, we go slightly under the // cell height (mainly: non-scalable fonts, i.e. emoji) break :underline_pos cell_height - 1; }; - const underline_thickness = @max(@as(f32, 1), fontUnitsToPxY( - face, - face.handle.*.underline_thickness, - )); // The strikethrough position. We use the position provided by the // font if it exists otherwise we calculate a best guess. const strikethrough: struct { pos: f32, thickness: f32, - } = if (face.getSfntTable(.os2)) |os2| .{ - .pos = pos: { - // Ascender is scaled, strikeout pos is not - const ascender_px = @as(i32, @intCast(size_metrics.ascender)) >> 6; - const declared_px = freetype.mulFix( - os2.yStrikeoutPosition, - @as(i32, @intCast(face.handle.*.size.*.metrics.y_scale)), - ) >> 6; + } = if (face.getSfntTable(.os2)) |os2| st: { + const thickness = @max(@as(f32, 1), fontUnitsToPxY(face, os2.yStrikeoutSize)); - break :pos @floatFromInt(ascender_px - declared_px); - }, - .thickness = @max(@as(f32, 1), fontUnitsToPxY(face, os2.yStrikeoutSize)), + const pos = @as(f32, @floatFromInt(freetype.mulFix( + os2.yStrikeoutPosition, + @as(i32, @intCast(face.handle.*.size.*.metrics.y_scale)), + ))) / 64; + + break :st .{ + .pos = @ceil(cell_height - cell_baseline - pos + thickness + 1), + .thickness = thickness, + }; } else .{ // Exactly 50% of the ex height so that our strikethrough is // centered through lowercase text. This is a common choice. - .pos = cell_baseline - ex_height * 0.5 + 1, + .pos = @ceil(cell_height - cell_baseline - ex_height * 0.5 + underline_thickness), .thickness = underline_thickness, };