Rusttype how to position glyphs vertically

381 views Asked by At

I'm trying to use the rusttype crate to render text. So far it's been fantastic, but I am running into a wall when trying to correctly position individual glyphs.

I am rendering text by rendering each glyph to an RgbaImage (from the image crate, like in the rusttype image example) and rendering it to a quad mesh.

pub struct Glyph {
    pub image: image::RgbaImage,
    pub glyph: rusttype::PositionedGlyph<'static>,
    pub vertical_offset: f32,
}

// &self.font is a rusttype::Font type
pub fn draw_glyph(&self, ch: char, font_size: f32) -> Option<Glyph> {
    // Set size
    let scale = rusttype::Scale::uniform(font_size);
    let v_metrics = self.font.v_metrics(scale);

    // Position and scale glyph
    let offset = rusttype::point(0.0, 0.0 + v_metrics.ascent);
    let glyph = self.font.glyph(ch).scaled(scale).positioned(offset);

    // Get glyph dimensions
    let bounds = glyph.pixel_bounding_box()?;
    let glyph_height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
    let glyph_width = (bounds.max.x - bounds.min.x) as u32;

    // Try to align glyphs on a baseline
    let offset_y = bounds.height() as f32 - v_metrics.ascent;

    // Generate image
    let mut image =
        image::ImageBuffer::from_pixel(glyph_width, glyph_height, image::Rgba([200; 4]));
    glyph.draw(|x, y, v| {
        image.put_pixel(x, y, image::Rgba([0 + (v * 255.0) as u8; 4]));
    });

    Some(Glyph {
        glyph: glyph,
        image: image,
        vertical_offset: offset_y,
    })
}

However, without the vertical_offset calculation, I receive the following text:

The word "sandwich" with incorrectly vertical aligned characters

As you can see, the characters are all over the place.

I need to take into account the ascent and descent size of the font. However, this is where I run into issues.

I can compensate for the ascent using the following calculation:

let bounds = glyph.pixel_bounding_box()?;
let offset_y = bounds.height() as f32 - v_metrics.ascent;

And then shifting the quad meshes downwards by offset_y.

This improves the output when there are no descenders like "g" in the text:

enter image description here

But does not help when there are descending characters involved:

enter image description here

I cannot figure out how the rusttype examples handle this. They use the Font::layout method, but this doesn't have any special vertical align code.

I'm definitely doing something wrong. Please help!

1

There are 1 answers

0
orlp On

Try compensating for the descent as well:

let offset_y = bounds.height() as f32 - v_metrics.ascent + v_metrics.descent;