bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Vertically-centered text is too low

Open viridia opened this issue 1 year ago • 21 comments

Bevy version

0.15.0-dev, although this bug has been around a long time.

What you did

In a flexbox or grid, place a text element with vertical centering.

What went wrong

The text appears 1 or 2 pixels lower than it should, as seen in the following example screenshot:

Image

Additional information

I had hoped that the migration to cosmic-text would fix this, but apparently not.

@UkoeHB

viridia avatar Oct 17 '24 17:10 viridia

Is the text itself not centered, or the text node not centered in the parent? I assume you're seeing the problem with JustifyContent::Center?

UkoeHB avatar Oct 17 '24 19:10 UkoeHB

This particular example is using align, not justify (it's a flex row, not a column) - but I think the type of alignment doesn't matter. I've seen it in a bunch of different situations. I suspect it's a problem in the way the text is being measured, or perhaps a rounding issue.

viridia avatar Oct 17 '24 19:10 viridia

This isn't a layout problem but the text not centered I think, though when I've seen it was too high, not too low. You can tell for certain by setting a background color on the text itself.

Which font are you using? Try a different font, see if you get a different result?

ickshonpe avatar Oct 17 '24 19:10 ickshonpe

I'm using OpenSans.

viridia avatar Oct 17 '24 19:10 viridia

I also had problems with OpenSans and switched to FiraSans. Maybe the font extraction is bugged.

UkoeHB avatar Oct 17 '24 19:10 UkoeHB

Here's what it looks like with Ubuntu: Image

viridia avatar Oct 17 '24 19:10 viridia

This makes it clear, definitely malpositioned: Image

The gray rect is the text node, the red is the parent it's vertically centered within.

ickshonpe avatar Oct 17 '24 19:10 ickshonpe

It does look better with FiraSans:

Image

viridia avatar Oct 17 '24 19:10 viridia

Does anyone know if there is an API in Bevy or its dependencies that helps visualize how text is laid out that would be helpful in investigating this type of problem? (Something like Paint.FontMetrics or Paint.getTextBounds() in Android).

mamekoro avatar Oct 18 '24 12:10 mamekoro

I thought it might be some problem with my layout rounding hacks but there's no difference after removing them. And nothing changes with different font sizes or UiScales.

ickshonpe avatar Oct 18 '24 14:10 ickshonpe

Should have checked this first, you can see the same problem with Text2d:

Image

The y glyph is overflowing in the same way as with the UI examples. (The maroon rectangle is a sprite set to the same size as the computed text bounds from TextLayoutInfo,)

In main with ab_glyph the vertical positions are correct and I can't see anything in bevy_text that could be responsible, so it's probably an issue with comic text. Tried updating bevy to their main branch but didn't help.

use bevy::color::palettes::css::MAROON;
use bevy::prelude::*;
use bevy::text::TextLayoutInfo;
use bevy::window::WindowResolution;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                resolution: WindowResolution::default().with_scale_factor_override(1.),
                ..Default::default()
            }),
            ..Default::default()
        }))
        .add_systems(Startup, setup)
        .add_systems(Update, update_sprite)
        .run();
}

fn setup(mut commands: Commands<'_, '_>, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2d);

    commands
        .spawn((
            Text2d::new("Primary"),
            TextFont {
                font: asset_server.load("OpenSans-Regular.ttf"),
                font_size: 100.,
                font_smoothing: bevy::text::FontSmoothing::None,
            },
        ))
        .with_child((
            Sprite {
                color: MAROON.into(),
                ..Default::default()
            },
            Transform::from_translation(-Vec3::Z),
        ));
}

fn update_sprite(text_query: Query<&TextLayoutInfo>, mut sprite_query: Query<&mut Sprite>) {
    let size = text_query.single().size;
    sprite_query.single_mut().custom_size = Some(size);
}

ickshonpe avatar Oct 18 '24 15:10 ickshonpe

I'm not sure if it is really malpositioned. I opened OpenSans-Regular.ttf on FontForge and it seems that glyph y originally reached below the descent line.

Image

mamekoro avatar Oct 22 '24 14:10 mamekoro

I don't think we should be looking at the descender height, but the ascender height. In your example, the distance between the top of the P and the top line is less than the width of the strokes. In the earlier screenshot, however, the distance is more like 3 stroke widths.

viridia avatar Oct 23 '24 01:10 viridia

This is what I see in Firefox:

  • Open Sans Regular
  • First pink box: font-size: 60px, line-height: 0.5 (30px)
  • Second pink box: font-size: 60px, line-height: 1 (60px)
  • Rectangle: 60x60px

Image

~~I think it defines the text box based on the ascenders height (not the uppercase) and the baseline (red lines), then centers vertically the text based on the line-height (pink boxes). It can overflow in both directions.~~ (Edit: "em squre" is used?)

Image

I just realized I don't understand what font-size: 60px means. I knew different fonts have different cap-height/x-height… but what's 60px here? 😅


Edit: this comment is probably noise.

doup avatar Nov 10 '24 19:11 doup

the number of pixels in height

BenjaminBrienen avatar Nov 10 '24 19:11 BenjaminBrienen

(the following might not be true, as I'm reading contradictory (?) definitions of "em square")


@BenjaminBrienen yes, but height of what? Because it's not of the caps, or from descenders to ascenders…

I forgot to answer myself, font-size refers to the height of the "em square" as it's noted in the web specs:

The font size corresponds to the em square, a concept used in typography. Note that certain glyphs may bleed outside their em squares.

What's interesting is the note, "glyphs may bleed outside"… and I suppose that's what I see in my screenshot. The em-square is 60px, but some diacritics (Ñ) or descenders (y) bleed out. (I tried to check this in FontForge but I have no idea how I can visualize it).

Image

~~Which leads me to think that either Bevy or cosmic-text it's not considering the "em-square"? Why? It's just a hunch after seeing the Firefox/Chrome screenshot, which clearly bleeds from both top and bottom which suggest that "Open Sans Regular" em square bleeds? ickshonpe's screenshots have way too much space both at the top and bottom, in one of the screenshots even the y doesn't overflow. (btw… why the inconsistency? maybe the screenshots have different line-height?)~~ Nope, Bevy uses 1.2 line-height by default, I was using 1

Image

doup avatar Nov 10 '24 22:11 doup

Ignore my previous comments (🙈), I just realized that Bevy uses 1.2 hard-coded line-height, which gives the same result as in Firefox. The good part is that I learned few new things xD

Image

Also, probably there isn't any problem with the overflowing y, it's OK if a glyph bleeds outside of the "em square". If you change Bevy's line-height to 1 you'll probably see even more overflowing.

doup avatar Nov 10 '24 23:11 doup

For what is worth… bevy-reactor vs Firefox: 1px offset.

Image

HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"
      rel="stylesheet"
    />
    <style>
      .button-wrapper {
        display: inline-flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        align-content: center;
        padding: 0 10.5px;
        border: 0;
        color: #ececec;
        cursor: pointer;
        min-height: 24px;
        min-width: 24px;
        position: relative;
      }

      .button-bg {
        display: grid;
        position: absolute;
        inset: 0;
        border-radius: var(--border-radius);
        background-color: #39393e;
      }

      .open-sans {
        font-family: "Open Sans", sans-serif;
        font-optical-sizing: auto;
        font-weight: normal;
        font-style: normal;
        font-variation-settings: "wdth" 100;
        font-size: var(--size);
        line-height: var(--line-height);
      }

      .text {
        position: relative;
      }
    </style>
  </head>
  <body class="flex-center">
    <div style="background-color: #666; padding: 16px">
      <div
        class="button-wrapper open-sans"
        style="--line-height: 1.2; --size: 14px; --border-radius: 3px"
      >
        <div class="button-bg"></div>
        <div class="text">Default</div>
      </div>
      <div
        class="button-wrapper open-sans"
        style="--line-height: 1; --size: 14px; --border-radius: 3px"
      >
        <div class="button-bg"></div>
        <div class="text">Default</div>
      </div>
    </div>
  </body>
</html>

doup avatar Nov 11 '24 16:11 doup

But which is correct? 🤔

BenjaminBrienen avatar Nov 11 '24 16:11 BenjaminBrienen

IMO, OP is correct, text looks too low in Bevy. Md/Xs/Xxs look particularly bad in the first screenshot, or the first two rows (which are Md size).

Also note that between the two screenshots there is some anti-aliasing difference. But no idea if that's relevant. It could be because FF uses another anti-aliasing? Or the FF text is not pixel grid aligned and it's 0.5px higher or whatever? 🤷‍♂

doup avatar Nov 11 '24 16:11 doup

I had the same issue, but the opposite (the text is too high): https://github.com/bevyengine/bevy/issues/16627

My solution was to edit the font using FontForge, go to Element -> Font Info... -> OS/2 -> Metrics and increase the Win Ascent, Typo Ascent and HHead Ascent. Not sure if there's any nasty hidden side-effects, but it works as a quick hack.

musjj avatar Dec 03 '24 16:12 musjj