Skip to content

Inline FontFeatures changes in Buffer::set_rich_text are ignored within a word #530

Description

@Byunk

When adjacent rich-text spans differ only in FontFeatures, the given feature is ignore if it occurs within a word.

For example, 'HNO3' where '3' has 'subs' feature produce the normal '3' glyph, while shaping '3' separately with the same attributes correctly produces subscript.

Environment

Reproduction

Cargo.toml:

[package]
name = "example"
version = "0.1.0"
edition = "2024"

[dependencies]
cosmic-text = "0.19"

main.rs:

use cosmic_text::{
    Attrs, Buffer, Family, FeatureTag, FontFeatures, FontSystem, Metrics, Shaping, fontdb,
};

fn main() {
    let mut database = fontdb::Database::new();
    database.load_font_data(include_bytes!("../Carlito/Carlito-Regular.ttf").to_vec());
    let mut font_system = FontSystem::new_with_locale_and_db("en-US".into(), database);

    let normal = Attrs::new().family(Family::Name("Carlito"));
    let mut features = FontFeatures::new();
    features.enable(FeatureTag::new(b"subs"));
    let subscript = normal.clone().font_features(features);

    // glyph id for "3" without features
    let normal_id = shape_text(&mut font_system, "3", &normal)[0];
    // glyph id for "3" with "subs" feature enabled
    let subscript_id = shape_text(&mut font_system, "3", &subscript)[0];

    let mut buffer = Buffer::new(&mut font_system, Metrics::new(32.0, 40.0));
    buffer.set_rich_text(
        [("HNO", normal.clone()), ("3", subscript)],
        &normal,
        Shaping::Advanced,
        None,
    );
    // glyph id for "3" within shaped text
    let rich_text_id = glyph_ids(&mut font_system, &mut buffer)[3];

    println!("normal: {normal_id}");
    println!("subscript: {subscript_id}");
    println!("rich text: {rich_text_id}");

    assert_ne!(normal_id, subscript_id); // must be different when font supports subscript

    // FAIL: currently panic here
    assert_eq!(rich_text_id, subscript_id);
}

fn shape_text(font_system: &mut FontSystem, text: &str, attrs: &Attrs<'_>) -> Vec<u16> {
    let mut buffer = Buffer::new(font_system, Metrics::new(32.0, 40.0));
    buffer.set_text(text, attrs, Shaping::Advanced, None);
    glyph_ids(font_system, &mut buffer)
}

fn glyph_ids(font_system: &mut FontSystem, buffer: &mut Buffer) -> Vec<u16> {
    buffer.shape_until_scroll(font_system, false);
    buffer
        .layout_runs()
        .flat_map(|run| run.glyphs.iter().map(|glyph| glyph.glyph_id))
        .collect()
}

Output with Carlito Regular:

normal '3' glyph:     397
standalone subs '3':  405
rich-text subs '3':   397

which leads panic on the last assert_eq

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions