我正在尝试使用
String
在
Image
上渲染动态长度的
imageproc
。
我需要一种方法或函数来计算使用特定
Font
和 Scale
渲染时文本的宽度,以使文本在图像上居中。
rusttype
是imageproc使用的字体库。
rusttype 存储库包含一个示例 (examples/image.rs),它测量一行文本的边界并将其呈现为图像。 除了居中部分之外,基本上就是您要搜索的内容。
// rusttype = "0.9.2"
use rusttype::{point, Font, Scale};
let v_metrics = font.v_metrics(scale);
let glyphs: Vec<_> = font.layout(text, scale, point(0.0, 0.0)).collect();
let glyphs_height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
let glyphs_width = {
let min_x = glyphs
.first()
.map(|g| g.pixel_bounding_box().unwrap().min.x)
.unwrap();
let max_x = glyphs
.last()
.map(|g| g.pixel_bounding_box().unwrap().max.x)
.unwrap();
(max_x - min_x) as u32
};
我不喜欢总是需要收集到
Vec
,同时还需要使用非整数大小。因此,在过去,我创建了自己的示例中的“本质上”版本。
一个重要的注意事项是,当您只需要大小时,传递给布局的位置并不“真正”重要。但是,如果您确实需要渲染字形,那么位置很重要,否则结果将包含锯齿伪影。
// rusttype = "0.9.2"
use rusttype::{point, Font, Scale};
fn measure_line(font: &Font, text: &str, scale: Scale) -> (f32, f32) {
let width = font
.layout(text, scale, point(0.0, 0.0))
.map(|g| g.position().x + g.unpositioned().h_metrics().advance_width)
.last()
.unwrap_or(0.0);
let v_metrics = font.v_metrics(scale);
let height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
(width, height)
}
如果文本有多行,那么您需要手动在字符串上使用
.lines()
直接使用 ttf_parser crate(版本 0.20.0),我得到了更好的结果,到目前为止似乎是准确的。
use ttf_parser::Face;
pub fn face_text_size(
face: &Face,
text: &str,
font_size: f32,
) -> (f32, f32) {
let units_per_em = face.units_per_em() as f32;
let scale_factor = font_size / units_per_em;
let width = text
.chars()
.filter_map(|ch| {
face.glyph_index(ch)
.and_then(|glyph_id| face.glyph_hor_advance(glyph_id))
})
.map(|advance| advance as f32 * scale_factor)
.sum::<f32>();
let ascender = face.ascender() as f32 * scale_factor;
let descender = face.descender() as f32 * scale_factor;
let height = (ascender - descender).abs();
(width, height)
}