#Text render bug in 0.11 when text starts with a newline

112 messages Β· Page 1 of 1 (latest)

daring timber
#
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());

    let text = "\nfirst line\nsecond line\nthird line\n";

    commands.spawn(TextBundle {
        text: Text::from_section(
            text.to_string(),
            TextStyle {
                font: asset_server.load("fonts/FiraSans-Bold.ttf"),
                font_size: 60.0,
                color: Color::YELLOW,
            },
        ),
        style: Style {
            position_type: PositionType::Absolute,
            ..Default::default()
        },
        background_color: BackgroundColor(Color::RED),
        ..Default::default()
    });
}

For this example bevy 0.10.1 showed 3 lines:

first line
second line
third line

bevy 0.11 shows:

first line
second line
<empty line>

It seems like the newline character at the starts is being a wrench in the whole text calculation and there are even more, even weirder bugs that I can follow up with that also have the newline char at the start. I did a git bisect and I got this commit/PR as the breaking change: https://github.com/bevyengine/bevy/pull/7779/files

Any text experts here that might know what this might be about?

#

Also, with the given input I'm not really sure what I would expect as the output honestly. Maybe this:

<empty line>
first line
second line
third line
#

Adding a ss of what I'm getting in 0.11:

grave hinge
#

Whatever it is I think it needs a github issue and should be fixed in 0.11.1 ^^

daring timber
#

Definitely, tho at the moment it's still unclear to me what is the scope of the bug, since I noticed even weirder things where no text was showing, or it was showing just the third line depending on whether they had spaces in them etc etc.

#

I'll make an issue once I do a bit more research and maybe till then someone who knows the text system a bit better might drop by

placid jetty
#

@grave garden

grave garden
#

yep there are still a load of bugs like this I've been grinding through

#

the problem is its quite hard to work out even if its a problem in bevy or taffy or ab_glyph a lot of the time

#

its some sort of rounding problem again I guess, if you change the font size to 59.9 or 60.1 it works fine

#

oh but it still misses the first empty line, thats odd

peak fossil
#

Yeah πŸ™ˆ

        let measure = TextPipeline::default().create_text_measure(
            &self.fonts,
            &text.sections,
            // Seems like this requires an epsilon, otherwise text wraps poorly.
            1.01,
            text.alignment,
            text.linebreak_behavior,
        );
        measure.map_or(Vec2::ZERO, |m| m.compute_size(bounds))
grave garden
#

hmmm

#

just noticed we're still on an ab_glyph version from over 2 years ago

#

and glyph_layout

#

I'll try updating them

grave garden
#

oh yeah

#

that worked

#

oh dear, I've been working around these bugs in ab_glyph for months and never thought to check if they'd fixed them πŸ˜…

grave hinge
#

well x)

grave garden
#

oh started writing up the PR and realised it didn't work after all, I just ran the wrong example πŸ˜₯

#

I understand where the problem is though, it shapes the text correctly kind of

#

the correct output would be five lines starting and ending with an empty line

#

but it misses out the empty lines

#

so you just have three lines of output

#

but

#

it also doesn't miss out the empty lines

#

if the original message was fit into a three line output you would have the empty line and then the first line and the second line, and it wouldn't display the third line

#

so it displays the text for that three line output but then takes out the empty line again

#

so it only shows two lines

grave garden
#

there are three issues, solved two

#

bevy has three different functions that calculate the size of a text shape

#

and one of them floors the size values while the others don't

#

so the values don't agree and that messes up the layout I think for some font sizes

#

and also when calculating the size of the text shape it just chooses the smallest rect that fits all the glyphs

#

and ignores empty lines

#

so a leading a or trailing empty line is just ignored.

#

it's easy to fix the leading line as we don't use ab_glyph to vertically align text, can just set the minimum x value to 0

#

but trailing newlines I'm not sure about

grave hinge
#

Thank you for all your work on this πŸ’œ

grave garden
#

well I was worried it was caused by one of my PRs but it`s older issue in parts I didn't touch πŸ˜…

grave hinge
#

Well you made the UI way more interesting to use this version so it's not a surprise more bugs are found :D

grave garden
#

yeah every bug fix just reveals two more bugs πŸ˜‚

daring timber
#

Do you know why this example worked before the linked PR? I noticed this in my game because it's a port of an ASCII console version and had some parts of the UI done via characters. When I updated to 0.11 some parts of the UI were just cut at the bottom (the line of text was missing)

#

And is there an issue present for these bugs now? Or should I now go ahead and make one for the missing last line?

#

I was trying to see if a newline is treated as a glyph that is taken into consideration when calculating the bounding boxes, but I'm still not familiar with how this all works to find the answer

grave garden
#

I'm just doing the PR that fixes most of the problems and should be included in a 0.11.1 release

#

before #7779 bevy's text shaping just ignored the UI layout mostly so even though there were a lot of bugs in Bevy UI and Taffy most of them didn't do anything

#

you could never get this situation before #7779 because the layout didn't respond to the size of sections of text

#

not properly anyway

daring timber
#

this might be a bad example then, since it "worked fine" (not really if you expect an empty first line) before the change

#

if the layout is now responding to the text size, what is causing the text in the third line to not appear altogether? I would expect it to show but he cut off at some point or something

grave garden
#

when it calculates the size of the text block it ignores whitespace, so it only counts 3 lines

#

and it aligns the first glyph so its at the top left of the text node

#

but then

#

when it comes to drawing the text

#

it sees there is only space for 3 lines

#

so it draws the first 3 lines, the empty line (not drawn but abstractly it overflows and is above the top of the node), the first line of text and the second line of text

#

and the third line of text isn't drawn

daring timber
#

okay great, that sounds like a logic error at least, and not some weird floating error schenenigens

#

right?

#

also I'm not sure why my app doesn't send me notifs from this post

grave garden
grave garden
daring timber
#

I will definitely check it when I snag the time, at least if it improves the issues I found

daring timber
#

I did some tests and this PR solves the problem of the missing text in the scenarios I had before. It correctly shows the leading empty lines. However I'm not sure what should happen with the trailing ones? The PR changes don't seem to do anything about them, as no matter how much of them I put at the end of the text, the last line that gets showed is the last line containing text.

grave garden
#

Yes the trailing ones are more are annoying as what happens

#

is that you give ab_glyph the text sections that make up the text that you want to draw

#

and ab_glyph returns a vec of glyphs with position and size and an index to look up the character in the font

#

for each glyph

#

and then the text pipeline finds the smallest rect that contains all the glyphs

#

but the problem with the trailing lines is that they are empty and don't contain any glyphs at all, so the bounding rect ignores those lines

#

so there is no way to get the correct size information

#

I can think of some hacks to fix it like I could just add a character to the end of any text that ends in a newline

#

then when the glyphs are returned use the geometry for the ending character's glyph to calculate the bottom of the text block

#

and then afterwards delete the glyph from the end

#

or calculate the size of the empty lines from the font information, that could be more annoying though

#

as you have to account for multiple text sections that are just newlines each using a different font

#

it's annoying to have to do either of those things though

#

maybe there is a way to get the correct size from ab_glyph after all, you could have a look into it and do the PR if you like

#

Trailing newlines at least aren't nearly so much of a problem for users as the text will be drawn in the right position and there won't be any lines disappearing at least

#

that's why I split it up and only tackled leading newlines in #9133, the leading newlines definitely had to be fixed for the 0.11.1 release, the trailing ones are annoying but can wait till 0.12

#

but should be fixed for 0.11.1 if we can

daring timber
#

yea it's definitely an improvement, since it was buggy before and now it's just slightly incomplete behavior that is probably considered completely by most users

#

I think having extra logic in bevy to account for the newlines is a better approach than adding extra glyphs to work around ab_glyph

#

If I understood you right you are saying that for a text "\n1\n2\n" we will get an array from ab_glyph that contains positions and sizes etc. for characters 1 and 2 and nothing for \n?

#

Is there a way to get the line height based on font?

#

So that we can check in bevy how many newlines are leading and trailing and add that to the height calculations?

#

I very likely might be oversimplifying this as I don't know a lot about text printout yet; I didn't understand what you meant about the multiple text sections using different fonts, can you elaborate on that?

grave garden
#

well the Text component has a field sections: Vec<TextSection>

#

and each TextSection is made up of a String and a TextStyle

#

and TextStyle's declaration is

pub struct TextStyle {
    pub font: Handle<Font>,
    pub font_size: f32,
    pub color: Color,
}
#

so each TextSection can have a different font

#

which allows you to have a single block of Text block using lots of different colours and fonts

#

so if someone wanted to be really annoying they can do something like:

#
    commands.spawn(TextBundle::from_sections(
        (0..10).iter()
        .map(|n| TextSection::new("\n".into(), TextStyle { font_size: 10. * (n + 1) as f32, ..default()})
    )));
#

which creates 10 empty lines of text, each a different height than the last

daring timber
#

but then the line height would just be the maximum of those font sizes right?

grave garden
#

no the sum

daring timber
#

ah I meant, for each line

grave garden
#

yes

daring timber
#

if a line has two different fonts its height would be the max of those?

grave garden
#

yes

daring timber
#

and for each line it is calculated separately

#

ok

#

that doesn't sound that bad?

grave garden
#

its not that hard, but it annoying and whenever you fix something like this, something else always breaks hehe

daring timber
#

how so? what do you think would break here?

grave garden
#

I 've no idea, you find out a month later when someone posts a new issue

#

there are so many interactions, like #7779 didn't create the bug caused the disappearing lines, it was already there but somehow didn't cause a problem until after text wrapping was fixed

daring timber
#

I'll try looking into this

grave garden
#

I think you are probably right, it shouldn't be that hard to find a fix