Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding an example for calculating the bounding box #124

Open
incetarik opened this issue Sep 21, 2022 · 8 comments
Open

Adding an example for calculating the bounding box #124

incetarik opened this issue Sep 21, 2022 · 8 comments

Comments

@incetarik
Copy link

Hi, first of all thank you for your work.

I tried this library to measure a string in a given font.
To test, I also had my SVG editor open and I saw that the characters bounds are 100% correct.
Yet, I could not find a way to calculate the overall text bounding box. I mean the box I have in the SVG editor for the text itself, not the characters.

Could you please add an example for calculating the expected string bounding box, if there is a way to calculate that properly?

@incetarik
Copy link
Author

Any update?

@leecbaker
Copy link

You can probably get what you need with this:

  • Assuming you already have a font
  • Create one or more TextStyle for your text
  • Create a Layout with Layout::new, and append the TextStyles to it
  • Iterate through the glyphs in the layout with layout.glyphs(). You can use the x, y, width, and height to figure out individual bounds for each letter; the min and max of each of those values should give you the 4 bounds of your bounding box.
    Something like this:
let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
layout.append(
    &[&self.font],
    &TextStyle::new(string, self.font_size, 0),
);

for glyph in layout.glyphs() {
    let left = glyph.x;
    let top = glyph.y;
    let right = glyph.x + glyph.width;
    let bottom = glyph.y + glyph.height; // may need to flip the sign, or flip top and bottom depending on CoordinateSystem

    // find the max of each of these, and that will be your bounding box.
}

@toyboot4e
Copy link

toyboot4e commented Oct 2, 2022

@leecbaker Hi 👋 You're calculating each character's bounds, but I think the author wants the whole text bounds. I'm wrong, I'm just showing a hopefully more efficient version.

@incetarik I have the following code for calculating the whole text bounds:

// after making your text `Layout`:
let glyphs = layout.glyphs();

let text_width = match layout.lines() {
    Some(lines) => lines
        .iter()
        .map(|ln| {
            let glyph = &glyphs[ln.glyph_end];
            glyph.x + glyph.width as f32
        })
        // same as `.max()`, but for the non-Ord `f32`
        .fold(0.0 / 0.0, |m, v| v.max(m)),
    None => 0.0,
};

let text_height = layout.height();

Window rendering with the calculated with/height (with no padding):

text-bounds

The width is measured correctly. The height might have to be adjusted if I make mistake. @leecbaker 's answer would be more accurate about the height. Combining the two might be the best answer.

@incetarik
Copy link
Author

You can probably get what you need with this:

  • Assuming you already have a font
  • Create one or more TextStyle for your text
  • Create a Layout with Layout::new, and append the TextStyles to it
  • Iterate through the glyphs in the layout with layout.glyphs(). You can use the x, y, width, and height to figure out individual bounds for each letter; the min and max of each of those values should give you the 4 bounds of your bounding box.
    Something like this:
let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
layout.append(
    &[&self.font],
    &TextStyle::new(string, self.font_size, 0),
);

for glyph in layout.glyphs() {
    let left = glyph.x;
    let top = glyph.y;
    let right = glyph.x + glyph.width;
    let bottom = glyph.y + glyph.height; // may need to flip the sign, or flip top and bottom depending on CoordinateSystem

    // find the max of each of these, and that will be your bounding box.
}

@leecbaker Hello, I have done the following, yet the results are not matching:

let mut layout = fontdue::layout::Layout::new(fontdue::layout::CoordinateSystem::PositiveYUp);
let style = fontdue::layout::TextStyle::new(input, opts.font_size, 0);

 layout.append(&[&font], &style);

let (width, height) = layout.glyphs()
  .iter()
  .map(|g| {
    let left = g.x;
    let top = g.y;
    let right = left + g.width as f32;
    let bottom = top + g.height as f32;

    let height = bottom - top;
    (right, height)
  })
  .fold((0.0f32, layout.height()), |l, r| (l.0.max(r.0), l.1.max(r.1)));

Here are the tests:

Test 1

Screen Shot 2022-10-02 at 13 11 19

Caption Value
Rust Result W: 284, H: 38
Font Size 32px

Test 2

Screen Shot 2022-10-02 at 13 13 55

Caption Value
Rust Result W: 67, H: 38
Font Size 32px

Screen Shot 2022-10-02 at 13 15 56

Caption Value
Rust Result W: 281, H: 32
Font Size 27px

The font being tested is: Raleway-Medium
The font file:

Raleway-Medium.zip

@mooman219
Copy link
Owner

Sorry about that! Fontdue may add more padding than is typically required between some glyphs, so the results will not match output as seen in the browser, or another tool.

@incetarik
Copy link
Author

Sorry about that! Fontdue may add more padding than is typically required between some glyphs, so the results will not match output as seen in the browser, or another tool.

@mooman219 will there be any action for this then? Or maybe providing another function that does not add extra padding? At least, which method would be more accurate? I may also iterate through the letters and use font.metrics and then utilizeadvance_width for example.

@mooman219
Copy link
Owner

mooman219 commented Oct 4, 2022

The extra padding is because I don't expose fractional offsets, so fontdue is making some quality judgments for you at font creation time. This was an opinion I baked in because people using fractional offsets had the following issues:

  • It was easy to accidental have blurry glyphs (especially for pixel art style glyphs)
  • It required the user handle glyph overlap well

The goal was to give you decent glyphs out of the box. Right now you'll get whole pixel glyph alignment without overlap,

Fractional offsets are actually already supported internally, but will need some work to expose in a friendly way. Coincidentally someone else just asked for this so it's probably about time for me to figure out what's ergonomic for both the average user of this library and for someone that's willing to dig into the spicy parts of layout.

@incetarik
Copy link
Author

Any improvements on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants