linebender / resvg

An SVG rendering library.
Apache License 2.0
2.84k stars 229 forks source link

Font family with number inside falls back to default font #804

Open antmelnyk opened 3 months ago

antmelnyk commented 3 months ago

Hey @RazrFalcon how's it going?

There is weird bug that is happening from as long as I remember when handling texts. When providing font-family with number in its name for text elements (i.e Nunito Sans 12pt or Sans Serif 4) ReSVG fallbacks to default family (Times New Roman for Nunito Sans 12pt) even though the family is present in the fontdb.

It fails inside: crates/usvg/src/parser/text.rs on

let mut families = parse_font_families(font_families) // fails here
        .ok()
        .log_none(|| {
            log::warn!(
                "Failed to parse {} value: '{}'. Falling back to {}.",
                AId::FontFamily,
                font_families,
                state.opt.font_family
            )
        })
        .unwrap_or_default(); // falls back to default empty vector

Inside svgtypes/src/stream.rs on (I assume) parse_ident function. I don't fully understand what is going on there, but the error is InvalidIdent.

I wouldn't bother with this, however some of the Google Fonts files (those I named) are parsed into fontdb with numbers inside family names. And basically it's not possible to use them because of the bug (or maybe numbers are not allowed by SVG specs?).

Example SVG (really any text element with specified font-family):

<svg id='root' width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
  <text id='text' xml:space='preserve' x='0' y='0' font-size='200' font-family='Nunito Sans 12pt' fill='#000000'>test</text>
</svg>

Example font: https://fonts.google.com/specimen/Nunito+Sans (Yes it's called Nunito Sans but family name inside the file is with {number}pt)

RazrFalcon commented 3 months ago

Have you tried putting it in quotes? Aka font-family='"Nunito Sans 12pt"'?

By the CSS spec, a font-family with 12pt is not a valid family "name", because each unquoted string is treated as an identifier, which cannot start with a number.

It might work in browsers, because they largely ignore the spec anyway for compatibility, but resvg does follow it pretty closely.

antmelnyk commented 3 months ago

That makes sense and it works, though I'd say a bit counterintuitive 😓 Thanks!

LaurenzV commented 3 months ago

Maybe we should relax the rules a bit if it works in other browsers (and document it).

RazrFalcon commented 3 months ago

That makes sense and it works, though I'd say a bit counterintuitive

I know, but that's the correct way.

Maybe we should relax the rules a bit if it works in other browsers

Who knows how browsers parse font-family. They probably have tones of hacks to bypass the CSS spec weird restrictions. I guess the easiest thing we can do is to simply check if the value has no commas and quotes and then treat it as a single name.

daviddeutsch commented 3 months ago

Since I've also stumbled over this before #358 - perhaps the uncommonly (to most) strict font handling might be something that should be documented?

Doesn't have to be that complex, just something along the lines of "resvg is very strict with fonts, so do make sure that you use exact matches in your font names and here is how you can figure out what the font names would be".

RazrFalcon commented 3 months ago

Then I would have to write a whole document about all the possible edge cases. SVG spec is enormous. This issue will probably be fixed in the next release, so simply ignore it for now.