sammycage / lunasvg

SVG rendering and manipulation library in C++
MIT License
913 stars 128 forks source link

Possible issue with non-standard font #195

Open Randalphwa opened 1 month ago

Randalphwa commented 1 month ago

The SVG file below includes a text element with the font:

 style="font-family:'AmadeoStd', 'Amadeo Std';font-size:11px;"

The screen capture is of a text control label using the same font with the lunasvg rendering underneath. So even though the font is installed on my system (Windows 11) and works for the text control, it is not being rendered by lunasvg using that font. Not sure if that's by design or lunasvg doesn't support all fonts available on the OS/User's machine?

hello

GitHub doesn't have this font, and at least on my firefox it doesn't render at all, so here is the XML code:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
    <g transform="matrix(4.88894,5.28256,-4.24298,3.92683,78.4066,-42.6755)">
        <g transform="matrix(11,0,0,11,18.5165,17.5)">
        </g>
        <text x="0.894px" y="17.5px" style="font-family:'AmadeoStd', 'Amadeo Std';font-size:11px;">Hel<tspan x="12.422px 15.238px " y="17.5px 17.5px ">lo</tspan></text>
    </g>
    <g id="outer" transform="matrix(4.26667,0,0,4.26667,0,0)">
        <rect x="0" y="0" width="30" height="30" style="fill:none;"/>
    </g>
</svg>

Hello

KeyWorksRW commented 1 month ago

Just checked, and the SVG renders correctly in this issue in my Edge browser. I'm assuming it will not render correctly on any system without the AmadeoStd installed -- I just assumed it would be rendered correctly by lunasvg if the font was available.

sammycage commented 1 month ago

Thanks for sharing the details! Currently, lunasvg doesn’t support using fonts installed on the user’s system directly, except for a few default fonts set during setup. This may be why the text isn’t rendering as expected compared to your text control.

However, you can add custom fonts to lunasvg by using the lunasvg_add_font_face_from_file function. For example:

lunasvg_add_font_face_from_file("AmadeoStd", false, false, "/path/to/AmadeoStd.ttf");

This instructs lunasvg to use the specified font file for rendering text with the font family in your SVG, ensuring the correct font is applied.

Randalphwa commented 1 month ago

If I'm reading the code correctly, for Windows only Arial and Times are available and only then if Windows is installed on the C: drive. Text written in Arial would appear correctly on Windows, and differently on Unix (which uses DejaVuSans). So if the user wants to be certain the text is rendered the same on all platforms, they should include the font and load it into any Document that uses it before rendering the SVG. Is that essentially correct?

If I'm understanding correctly, for an SVG file rendering of a UI element that contains text, such as a drop down menu, then the dev should ideally read the UI font and supply that to the document. E.g., for Windows, that would mean locating the fonts directory, and calling lunasvg_add_font_face_from_file to load the Segoeui.tff file. That way the SVG rendering of the menu would match the UI of the OS.

sammycage commented 1 month ago

Great points! Yes, you’re absolutely correct.

Randalphwa commented 1 month ago

Any thoughts/plans about supporting font-face? That would allow embedding the font into the svg file:

    <style>
        @font-face {
                font-family: 'MyFontFamily';
                font-style: normal;
                font-weight: 400;
                src: local('MyFont'), local('MyFont'),
                url(data:font/myfont;base64,BASE_64_STRING_GOES_HERE) format('myfont');
        }
    </style>

I haven't actually tried doing this in an actual SVG file rendered with browser and/or Affinity Designer, but in theory it would prevent using the wrong font.

Just to be clear, I'm only trying to predict potential questions/issues that might come up both for direct lunasvg clients, and indirect clients when I finish hooking up lunasvg to wxWidgets. Though if you supported this, I'd probably add support for converting a font to base64 and embedding it in wxUiEditor, which in turn compresses the entire svg file and embeds it into the program using it.

sammycage commented 4 weeks ago

Thank you for the suggestion! Currently, there aren't any plans to support @font-face within the SVG framework, but I might explore this possibility in the future. For now, lunasvg_add_font_face_from_file or lunasvg_add_font_face_from_data could be used to embed fonts.

Randalphwa commented 4 weeks ago

Looking through the ttf files on my system, embedding the font in the SVG file might not be all that practical. Most of my fonts would increase the svg file by 1 meg when embedded, with one that would increase it by 42 megs. It's not just the size that's the issue, but lunasvg has to parse over all of that data to find the end tag and that's going to affect performance. I've already got the code in place for wxUiEditor to embed binary files, so I'll just look into a way to hook that up to the SVG files that need specific fonts.

sammycage commented 4 weeks ago

Thank you for sharing your insights! If the font data is static or already resides in memory, you can embed it without any additional memory overhead by using lunasvg_add_font_face_from_data and setting destroy_func to nullptr, or by defining a destroy function to free the data if needed. For example:

lunasvg_add_font_face_from_data("MyFamily" /*family*/, false /*bold*/, false /*italic*/, data, data_length, nullptr /*destroy_func*/, nullptr /*closure*/);
sammycage commented 3 weeks ago

@Randalphwa How is it going?

Randalphwa commented 3 weeks ago

I'll be using the information from this issue to update the documentation in the wxlunasvg port that I'm working on, basically stating what lunasvg 3.0 currently can and cannot do in terms of text rendering. The daily builds of wxUiEditor now use lunasvg 3 for rendering, but I have not started work on adding the code that would enable using text versus curves in the SVG files that are used for UI. It's a much higher priority for me to get lunasvg available as a replacement for nanosvg in wxWidgets 3.3.

At the risk of derailing this issue, in reviewing some of the C++17 changes in 3.0, they make a lot of sense and presumably should improve performance. However, wxWidgets 3.3 only requires C++11 to build. I'm still working out the best way to handle that. I want the default builds including all the binary releases to use your 3.0 version. I really don't want to use nanosvg as a fullback -- it has too many rendering issues that don't occur even in lunasvg 2.4.1. I'm in the process of setting up a change to the wxlunasvg fork of your code to have a static copy of the 2.4.1 code with a dynamic copy of the current code base. However, I still haven't worked out how to fall back to the older code using the quite complicated build system that wxWidgets has to use to cover so many toolchains out there including older compilers.