Missing font match causes a mess to the whole `tspan` font resolving
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.