Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Variable font weight support for variable OTF fonts #7477

Open
Stehsaer opened this issue Apr 9, 2024 · 3 comments
Open

Comments

@Stehsaer
Copy link

Stehsaer commented Apr 9, 2024

Version/Branch of Dear ImGui:

Version 1.90.2, Branch: master

Back-ends:

imgui_impl_GLFW.cpp + imgui_impl_Vulkan.cpp

Compiler, OS:

Windows 11 + (MinGW's GCC / Clang(MSVC) / Clang(MInGW) / MSVC)

Full config/build information:

Dear ImGui 1.90.2 (19020)
--------------------------------

sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1938
define: _MSVC_LANG=201402
--------------------------------

io.BackendPlatformName: imgui_impl_glfw
io.BackendRendererName: imgui_impl_vulkan
io.ConfigFlags: 0x00000000
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x0000000E
 HasMouseCursors
 HasSetMousePos
 RendererHasVtxOffset
--------------------------------

io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,128
io.DisplaySize: 2560.00,1494.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------

style.WindowPadding: 12.00,12.00
style.WindowBorderSize: 1.00
style.FramePadding: 6.00,4.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 12.00,6.00
style.ItemInnerSpacing: 6.00,6.00

Details:

Feature Request

Hi! I'm loading Catarell font from GNOME FOUNDATION (Link), using imgui_freetype. It was loaded perfectly but I've noticed that the font weight is too thin and it didn't match my expectation. I've tried specifying FontBuilderFlags in ImFontConfig, where ImGuiFreeTypeBuilderFlags_Bold made the font thicker than expectation, and ImGuiFreeTypeBuilderFlags_ForceAutoHint simply didn't affect the font weight.

Since the Catarell font is a VARIABLE Opentype font, as described in the release page, I'm requesting for a feature that adds more control over font weight for the freetype rasterizer.

  1. One option may be to add a separate option of specifying font weight in ImFontConfig, eg. using enum ImFontWeight
ImFontConfig cfg;
cfg.FontWeight = ImFontWeight_Bold500;

(Explanation: it's often the case where regular stands for font weight 400, and bold stands for font weight 500)
or

ImFontConfig cfg;
cfg.FontWeight = 500;
  1. Another option may be to add separate enum options in ImGuiFreeTypeBuilderFlags in header imgui_freetype.h and add support in the implementation
enum ImGuiFreeTypeBuilderFlags
{
    ImGuiFreeTypeBuilderFlags_NoHinting  = 1 << 0,   // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
    ...
    ImGuiFreeTypeBuilderFlags_Bitmap   = 1 << 9,    // Enable FreeType bitmap glyphs

    /* Extra Font Weight Options Added After Here*/
	ImGuiFreeTypeBuilderFlags_FontWeight100 = 1 << 10,
    ImGuiFreeTypeBuilderFlags_FontWeight200 = 2 << 10,
    ...
    ImGuiFreeTypeBuilderFlags_FontWeight900 = 9 << 10,
};

Additional Info

Resolution and size of my monitor: 2560*1600, 16 inches
Windows DPI: 150%

Screenshots/Video:

Regular
image
Thinner than I expected

Bold
With ImGuiFreeTypeBuilderFlags_Bold specified in ImFontConfig:
image
This bolds too much, more than I expected.

The whole idea about this feature request is to have more control over the font weight, if the font supports, so that I can tweak to have effects I expected.

Minimal, Complete and Verifiable Example code:

Regular

io.Fonts->AddFontFromFileTTF("fonts/cantarell.otf", 16 * scale, &cfg);
ImGui::GetStyle().ScaleAllSizes(scale);

Bold

ImFontConfig cfg;
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold;

io.Fonts->AddFontFromFileTTF("fonts/cantarell.otf", 16 * scale, &cfg);
ImGui::GetStyle().ScaleAllSizes(scale);

(Explanation: scale stands for content scale acquired from GLFW api)

@ocornut
Copy link
Owner

ocornut commented Apr 9, 2024

I am not sure how this would translate into Freetype directives or if Freetype can handle it. Have you looked into it?

@alien-brother
Copy link

alien-brother commented Apr 9, 2024

As a quick workaround, there are some ready to use fixed versions of Cantarell in the Debian package, e.g., the .deb file here https://ftp.debian.org/debian/pool/main/f/fonts-cantarell/.

That said, variable font support would be nice.

@PathogenDavid
Copy link
Contributor

I am not sure how this would translate into Freetype directives or if Freetype can handle it.

I briefly looked into it and it looks like variable font parameters are controlled via FreeType's Multiple Masters interface. It looks non-trivial to work with due to its flexible nature. (Although maybe a lot of it can be ignored if we narrow the scope to OpenType variable fonts?)


Additional flags would not be a great solution. Variable fonts expose much more than just weight, and a lot of the point of their existence is that the values are continuous rather than discrete. (See the examples at the top of this page.)

Exposing ImFontConfig::FontWeight or something similar also seems not ideal because it'd definitely cause confusion when it doesn't work with non-variant fonts or the stb_truetype implementation. (Ideally we want people to naturally gravitate towards explicitly loading their bold font variant in those scenarios.)

The easiest route might be to just offer a callback for users to further customize the FT_Face at the end of FreeTypeFont::InitFont and offload all the variable font complexity to them. (This should probably be done regardless as a way to allow customizing custom font axes.)

Or if we want to go a step further we could offer in-box customization of the registered OpenType axis tags -- see also MDN. (My assumption is that most variable fonts are OpenType fonts because that's what the web supports.)


The main issue would be how to get this information to the imgui_freetype implementation since the current interface doesn't offer much. My gut instinct is to add a field to ImFontConfig for loader-specific extended config, IE:

struct ImFontConfig
{
//...
    unsigned int    FontBuilderFlags;       // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
    void*           FontBuilderSettings;    // NULL     // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. THE POINTER NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Leave as NULL if unsure.
//...
};
struct ImGuiFreeTypeBuilderSettings
{
    // Variable font axis values (Only used for variable OpenType fonts!)
    float Weight;      // 400 // wght
    float Width;       // 100 // wdth
    float Slant;       // 0   // slnt
    float Italic;      // 0   // ital
    // opsz is intentionally omitted, my understanding is it's meant to be controlled programatically based on DPI outside of unusual circumstances that advanced users could handle in the callback.

    void (*AdvancedCustomizationCallback)(FT_Face face, const ImFontConfig& cfg, ImGuiFreeTypeBuilderFlags atlas_flags);
};

This has the minor downside of needing to keep the FontBuilderSettings pointer alive as long as the font is alive, but we already require that for ImFontConfig::GlyphRanges so it's maybe not that big of a burden to place on API consumers.

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

No branches or pull requests

4 participants