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

Line wrapping/text reflow does not respect buffer size #219

Open
ten3roberts opened this issue Jan 26, 2024 · 6 comments
Open

Line wrapping/text reflow does not respect buffer size #219

ten3roberts opened this issue Jan 26, 2024 · 6 comments

Comments

@ten3roberts
Copy link

I have noticed that the buffer size set by Buffer.set_size is not always respected by the line wrapping, but overruns slightly, usually by the half end of the word.

I can reproduce this with varying texts and fonts, and discovered this when writing my GUI library which needs to limit text sizes to fit.

I have managed to find the minimal repro.

The code has the same effect if you measure the distance between the first and last glyph, so line_w is correct. Rendering the text also shows that it overflows despite having vertical space to wrap.

use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping, Wrap};

fn main() {
    let mut font_system = FontSystem::new();
    let metrics = Metrics::new(14.0, 20.0);

    let mut buffer = Buffer::new(&mut font_system, metrics);

    let mut buffer = buffer.borrow_with(&mut font_system);

    // Add some text!
    buffer.set_wrap(Wrap::Word);
    buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);

    // Set a size for the text buffer, in pixels
    buffer.set_size(50.0, 1000.0);

    // Perform shaping as desired
    buffer.shape_until_scroll();

    let measured_size = measure(&buffer);

    assert!(
        measured_size <= buffer.size().0,
        "Measured width is larger than buffer width\n{} <= {}",
        measured_size,
        buffer.size().0
    );
}

fn measure(buffer: &Buffer) -> f32 {
    buffer
        .layout_runs()
        .fold(0.0f32, |width, run| width.max(run.line_w))
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/cosmic-test`
thread 'main' panicked at src/main.rs:23:5:
Measured width is larger than buffer width
79.24716 <= 50
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

[Process exited 101]
@ten3roberts
Copy link
Author

Example when rendering some text in a wgpu app:

image

@dtzxporter
Copy link
Contributor

dtzxporter commented Feb 1, 2024

Did a little investigation here, It looks like this example uncovers two possible issues with layout:

It looks like cosmic-text should treat wrapping mode as 'best case' like the web. When Word is selected, it should fall back to Glyph to make a best effort attempt.

Second, I have no idea why your example has an empty run.

(Latest master branch)

0 39.04004 ['L', 'o', 'r', 'e', 'm']
0 37.54297 ['i', 'p', 's', 'u', 'm']
0 32.908203 ['d', 'o', 'l', 'o', 'r']
0 14.075195 ['s', 'i', 't']
0 34.282227 ['a', 'm', 'e', 't', ',']
0 19.557617 ['q', 'u', 'i']
0 38.82129 ['m', 'i', 'n', 'i', 'm']
0 39.135742 ['l', 'a', 'b', 'o', 'r', 'e']
0 65.734375 ['a', 'd', 'i', 'p', 'i', 's', 'i', 'c', 'i', 'n', 'g']
0 38.82129 ['m', 'i', 'n', 'i', 'm']
0 21.998047 ['s', 'i', 'n', 't']
0 36.620117 ['c', 'i', 'l', 'l', 'u', 'm']
0 21.998047 ['s', 'i', 'n', 't']
0 71.9209 ['c', 'o', 'n', 's', 'e', 'c', 't', 'e', 't', 'u', 'r']
0 21.998047 []
0 61.024414 ['c', 'u', 'p', 'i', 'd', 'a', 't', 'a', 't', '.']

@ten3roberts
Copy link
Author

Awesome, I also discovered the empty run (due to unwrapping the first and last glyphs to measure width).

fontdue handles wrapping as best case too, and falls back to glyph breaking when there does not seem to be enough space.

However, the wrapping that I've seen overruns on are not due to insufficient space for a word, visibly, there is a solution where no overruns occur as there is plenty of horizontal space for multiple words, and large enough vertical space for it to wrap more lines.

@dtzxporter
Copy link
Contributor

Example when rendering some text in a wgpu app:

image

Any chance you can post the cosmic-text code that replicates this on your end? Would make it easier than fiddling with it until it breaks :D

@ten3roberts
Copy link
Author

Example when rendering some text in a wgpu app:

image

Any chance you can post the cosmic-text code that replicates this on your end? Would make it easier than fiddling with it until it breaks :D

Absolutely, I am developing a UI framework https://github.com/ten3roberts/violet, which is where I discovered it.

I can push out a branch with an isolating example of a text block being wrapped. The renderer that consumes the text is rather primitive at the moment, and contained in src/wgpu/text_renderer.rs, along with some layout in a TextBufferState to share between the renderer and layout systems.

@dtzxporter
Copy link
Contributor

dtzxporter commented Feb 2, 2024

Edit: Glyph fallback and empty run fix is merged into master now. I'll look into this overflow one as soon as I can repro it.

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

2 participants