resvg icon indicating copy to clipboard operation
resvg copied to clipboard

Missing font match causes a mess to the whole `tspan` font resolving

Open bczhc opened this issue 1 month ago β€’ 0 comments

For example.. I have this text Font ⬀ set its font as DejaVu Serif. Character ⬀ is not in DejaVu Serif, and it's expected to fallback to another font, but the preceding string Font should be left as is. However usvg will fallback the whole text somehow to a random font, causing https://github.com/typst/svg2pdf/issues/94.

I wrote this to play around with this:

use fontdb::Database;
use std::sync::Arc;
use usvg::{Node, Options};

fn main() {
    let mut fontdb = Database::new();
    fontdb.load_system_fonts();

    test("Font 123");
    test("Font ⬀");
    test("Font π‘–‡");
    test("Font πŸ˜€");
    test("Font ι‚£");
    test("Font πŸ–");
    test("Font πͺˆΏπ³‘Έ");
    test("Font 𳑸");
}

fn test(svg_text: &str) {
    let src = format!(
        r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 170">
  <text
          style="font-size:10px"
          x="0"
          y="0"
          id="text1"><tspan
          id="tspan1"
          x="0"
          y="100"
          style="font-family:'DejaVu Serif'">{}</tspan></text>
</svg>
"#,
        svg_text
    );

    let mut db = Database::new();
    db.load_system_fonts();
    let db = Arc::new(db);
    let opts = Options {
        fontdb: Arc::clone(&db),
        dpi: 96.0,
        ..Default::default()
    };
    let tree = usvg::Tree::from_str(&src, &opts).unwrap();
    let Node::Text(text) = tree.root().children().first().unwrap() else {
        return;
    };
    let span = text.layouted().first().unwrap();
    let glyphs = &span.positioned_glyphs;
    for x in glyphs {
        let font_name = &db.face(x.font).unwrap().post_script_name;
        println!("Font face for glyph '{}': {}", x.text, font_name);
    }
    println!();
}

On my end it outputs:

Font face for glyph 'F': DejaVuSerif
Font face for glyph 'o': DejaVuSerif
Font face for glyph 'n': DejaVuSerif
Font face for glyph 't': DejaVuSerif
Font face for glyph ' ': DejaVuSerif
Font face for glyph '1': DejaVuSerif
Font face for glyph '2': DejaVuSerif
Font face for glyph '3': DejaVuSerif

Font face for glyph 'F': BabelStoneHan
Font face for glyph 'o': BabelStoneHan
Font face for glyph 'n': BabelStoneHan
Font face for glyph 't': BabelStoneHan
Font face for glyph ' ': BabelStoneHan
Font face for glyph '⬀': BabelStoneHan

Font face for glyph 'F': DejaVuSerif
Font face for glyph 'o': DejaVuSerif
Font face for glyph 'n': DejaVuSerif
Font face for glyph 't': DejaVuSerif
Font face for glyph ' ': DejaVuSerif
Font face for glyph 'π‘–‡': NotoSansSiddham-Regular

Font face for glyph 'F': BabelStoneHan
Font face for glyph 'o': BabelStoneHan
Font face for glyph 'n': BabelStoneHan
Font face for glyph 't': BabelStoneHan
Font face for glyph ' ': BabelStoneHan
Font face for glyph 'πŸ˜€': BabelStoneHan

Font face for glyph 'F': FZKTK--GBK1-0
Font face for glyph 'o': FZKTK--GBK1-0
Font face for glyph 'n': FZKTK--GBK1-0
Font face for glyph 't': FZKTK--GBK1-0
Font face for glyph ' ': FZKTK--GBK1-0
Font face for glyph 'ι‚£': FZKTK--GBK1-0

Font face for glyph 'F': DejaVuSans-Bold
Font face for glyph 'o': DejaVuSans-Bold
Font face for glyph 'n': DejaVuSans-Bold
Font face for glyph 't': DejaVuSans-Bold
Font face for glyph ' ': DejaVuSans-Bold
Font face for glyph 'πŸ–': DejaVuSans-Bold

Font face for glyph 'F': DejaVuSerif
Font face for glyph 'o': DejaVuSerif
Font face for glyph 'n': DejaVuSerif
Font face for glyph 't': DejaVuSerif
Font face for glyph ' ': DejaVuSerif
Font face for glyph 'πͺˆΏ': FZSONGS_SIP--GB1-5
Font face for glyph '𳑸': PlangothicP2

Font face for glyph 'F': PlangothicP2
Font face for glyph 'o': PlangothicP2
Font face for glyph 'n': PlangothicP2
Font face for glyph 't': PlangothicP2
Font face for glyph ' ': PlangothicP2
Font face for glyph '𳑸': PlangothicP2

You may observe mostly they got a wrong fallback overall. However the third and the seventh one seem to act as expected - don't know why and i haven't found the rules yet. So why I said they fell back to some random fonts, just look at the test case Font ι‚£; i'm pretty sure the character ι‚£ should use Noto Serif CJK SC here, not a random FZKTK--GBK1-0.

bczhc avatar Nov 10 '25 17:11 bczhc