I'm trying to render a String
of dynamic length on an Image
using imageproc
.
I need a method or function to calculate the width of the text when rendered using a specific Font
and Scale
to center the text on the image.
rusttype
is the font library used by imageproc.
The rusttype repo contains an example (examples/image.rs), which measures the bounds of a line of text and renders it to an image. Basically what you're searching, besides the centering part.
// 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
};
I didn't like always needing to collect into a Vec
, while also needing to use non-integer sizes. So in the past I created my own version of what's essentially in the example.
One important note, is that the position passed to layout doesn't really matter, when you only need the size. However, if you actually need to render the glyphs, then the position is important as otherwise the result will contain aliasing artifacts.
// 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)
}
If the text has multiple lines, then you'd manually need to use .lines()
on the string, and then sum the line heights and line spacing.