Measure ascii height and use to upper bound ic_width
parent
0f0a61c38d
commit
6781fbda93
|
|
@ -117,6 +117,16 @@ pub const FaceMetrics = struct {
|
||||||
/// lowercase x glyph.
|
/// lowercase x glyph.
|
||||||
ex_height: ?f64 = null,
|
ex_height: ?f64 = null,
|
||||||
|
|
||||||
|
/// The measured height of the bounding box containing all printable
|
||||||
|
/// ASCII characters. This can be different from ascent - descent for
|
||||||
|
/// two reasons: non-letter symbols like @ and $ often exceed the
|
||||||
|
/// the ascender and descender lines; and fonts often bake the line
|
||||||
|
/// gap into the ascent and descent metrics (as per, e.g., the Google
|
||||||
|
/// Fonts guidelines: https://simoncozens.github.io/gf-docs/metrics.html).
|
||||||
|
///
|
||||||
|
/// Positive value in px
|
||||||
|
ascii_height: ?f64 = null,
|
||||||
|
|
||||||
/// The width of the character "水" (CJK water ideograph, U+6C34),
|
/// The width of the character "水" (CJK water ideograph, U+6C34),
|
||||||
/// if present. This is used for font size adjustment, to normalize
|
/// if present. This is used for font size adjustment, to normalize
|
||||||
/// the width of CJK fonts mixed with latin fonts.
|
/// the width of CJK fonts mixed with latin fonts.
|
||||||
|
|
@ -144,11 +154,20 @@ pub const FaceMetrics = struct {
|
||||||
return 0.75 * self.capHeight();
|
return 0.75 * self.capHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function for getting the ASCII height. If we
|
||||||
|
/// couldn't measure this, we use 1.5 * cap_height as our
|
||||||
|
/// estimator, based on measurements across programming fonts.
|
||||||
|
pub inline fn asciiHeight(self: FaceMetrics) f64 {
|
||||||
|
if (self.ascii_height) |value| if (value > 0) return value;
|
||||||
|
return 1.5 * self.capHeight();
|
||||||
|
}
|
||||||
|
|
||||||
/// Convenience function for getting the ideograph width. If this is
|
/// Convenience function for getting the ideograph width. If this is
|
||||||
/// not defined in the font, we estimate it as two cell widths.
|
/// not defined in the font, we estimate it as the minimum of the
|
||||||
|
/// ascii height and two cell widths.
|
||||||
pub inline fn icWidth(self: FaceMetrics) f64 {
|
pub inline fn icWidth(self: FaceMetrics) f64 {
|
||||||
if (self.ic_width) |value| if (value > 0) return value;
|
if (self.ic_width) |value| if (value > 0) return value;
|
||||||
return 2 * self.cell_width;
|
return @min(self.asciiHeight(), 2 * self.cell_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function for getting the underline thickness. If
|
/// Convenience function for getting the underline thickness. If
|
||||||
|
|
|
||||||
|
|
@ -775,7 +775,10 @@ pub const Face = struct {
|
||||||
// Cell width is calculated by calculating the widest width of the
|
// Cell width is calculated by calculating the widest width of the
|
||||||
// visible ASCII characters. Usually 'M' is widest but we just take
|
// visible ASCII characters. Usually 'M' is widest but we just take
|
||||||
// whatever is widest.
|
// whatever is widest.
|
||||||
const cell_width: f64 = cell_width: {
|
//
|
||||||
|
// ASCII height is calculated as the height of the overall bounding
|
||||||
|
// box of the same characters.
|
||||||
|
const cell_width: f64, const ascii_height: f64 = measurements: {
|
||||||
// Build a comptime array of all the ASCII chars
|
// Build a comptime array of all the ASCII chars
|
||||||
const unichars = comptime unichars: {
|
const unichars = comptime unichars: {
|
||||||
const len = 127 - 32;
|
const len = 127 - 32;
|
||||||
|
|
@ -803,7 +806,10 @@ pub const Face = struct {
|
||||||
max = @max(advances[i].width, max);
|
max = @max(advances[i].width, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
break :cell_width max;
|
// Get the overall bounding rect for the glyphs
|
||||||
|
const rect = ct_font.getBoundingRectsForGlyphs(.horizontal, &glyphs, null);
|
||||||
|
|
||||||
|
break :measurements .{ max, rect.size.height };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Measure "水" (CJK water ideograph, U+6C34) for our ic width.
|
// Measure "水" (CJK water ideograph, U+6C34) for our ic width.
|
||||||
|
|
@ -864,6 +870,7 @@ pub const Face = struct {
|
||||||
|
|
||||||
.cap_height = cap_height,
|
.cap_height = cap_height,
|
||||||
.ex_height = ex_height,
|
.ex_height = ex_height,
|
||||||
|
.ascii_height = ascii_height,
|
||||||
.ic_width = ic_width,
|
.ic_width = ic_width,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -960,13 +960,19 @@ pub const Face = struct {
|
||||||
// visible ASCII characters. Usually 'M' is widest but we just take
|
// visible ASCII characters. Usually 'M' is widest but we just take
|
||||||
// whatever is widest.
|
// whatever is widest.
|
||||||
//
|
//
|
||||||
|
// ASCII height is calculated as the height of the overall bounding
|
||||||
|
// box of the same characters.
|
||||||
|
//
|
||||||
// If we fail to load any visible ASCII we just use max_advance from
|
// If we fail to load any visible ASCII we just use max_advance from
|
||||||
// the metrics provided by FreeType.
|
// the metrics provided by FreeType, and set ascii_height to null as
|
||||||
const cell_width: f64 = cell_width: {
|
// it's optional.
|
||||||
|
const cell_width: f64, const ascii_height: ?f64 = measurements: {
|
||||||
self.ft_mutex.lock();
|
self.ft_mutex.lock();
|
||||||
defer self.ft_mutex.unlock();
|
defer self.ft_mutex.unlock();
|
||||||
|
|
||||||
var max: f64 = 0.0;
|
var max: f64 = 0.0;
|
||||||
|
var top: f64 = 0.0;
|
||||||
|
var bottom: f64 = 0.0;
|
||||||
var c: u8 = ' ';
|
var c: u8 = ' ';
|
||||||
while (c < 127) : (c += 1) {
|
while (c < 127) : (c += 1) {
|
||||||
if (face.getCharIndex(c)) |glyph_index| {
|
if (face.getCharIndex(c)) |glyph_index| {
|
||||||
|
|
@ -974,20 +980,37 @@ pub const Face = struct {
|
||||||
.render = false,
|
.render = false,
|
||||||
.no_svg = true,
|
.no_svg = true,
|
||||||
})) {
|
})) {
|
||||||
|
const glyph = face.handle.*.glyph;
|
||||||
max = @max(
|
max = @max(
|
||||||
f26dot6ToF64(face.handle.*.glyph.*.advance.x),
|
f26dot6ToF64(glyph.*.advance.x),
|
||||||
max,
|
max,
|
||||||
);
|
);
|
||||||
|
top = @max(
|
||||||
|
f26dot6ToF64(glyph.*.metrics.horiBearingY),
|
||||||
|
top,
|
||||||
|
);
|
||||||
|
bottom = @min(
|
||||||
|
f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height),
|
||||||
|
bottom,
|
||||||
|
);
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we couldn't get any widths, just use FreeType's max_advance.
|
// If we couldn't get valid measurements, just use
|
||||||
|
// FreeType's max_advance and null, respectively.
|
||||||
if (max == 0.0) {
|
if (max == 0.0) {
|
||||||
break :cell_width f26dot6ToF64(size_metrics.max_advance);
|
max = f26dot6ToF64(size_metrics.max_advance);
|
||||||
}
|
}
|
||||||
|
const rect_height: ?f64 = rect_height: {
|
||||||
|
const estimate = top - bottom;
|
||||||
|
if (estimate <= 0.0) {
|
||||||
|
break :rect_height null;
|
||||||
|
}
|
||||||
|
break :rect_height estimate;
|
||||||
|
};
|
||||||
|
|
||||||
break :cell_width max;
|
break :measurements .{ max, rect_height };
|
||||||
};
|
};
|
||||||
|
|
||||||
// We use the cap and ex heights specified by the font if they're
|
// We use the cap and ex heights specified by the font if they're
|
||||||
|
|
@ -1089,6 +1112,7 @@ pub const Face = struct {
|
||||||
|
|
||||||
.cap_height = cap_height,
|
.cap_height = cap_height,
|
||||||
.ex_height = ex_height,
|
.ex_height = ex_height,
|
||||||
|
.ascii_height = ascii_height,
|
||||||
.ic_width = ic_width,
|
.ic_width = ic_width,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue