imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Add `ImFontLoader::FontAddFallbackSrc` for loading fallback fonts dynamically

Open cfillion opened this issue 5 months ago • 3 comments

This adds a hook for backends to lookup fallback fonts from the user's system when encountering a missing glyph and merge them to the current ImFont on demand.

Demonstration

static const char *ResolveSystemFont(BackendDetails *, const ImWchar codepoint, unsigned int *index)
{
  return ...; // eg. using FontConfig or DirectWrite
}

static void LookupFallbackFont(ImFontAtlas *atlas, ImFont *font, const ImWchar codepoint)
{
  auto details {static_cast<BackendDetails *>(font->Sources.front()->UserData)};

  ImFontConfig cfg;
  cfg.UserData = details; // added as a convenience to quickly map ImFont -> backend impl details
  cfg.MergeTarget = font; // replaces `MergeMode = true` to merge with any font not just the last added one
  // zero or more sources may be added here (they'll be initialized across all baked instances as long as MergeTarget == font)
  if(const char *fallback_file {ResolveSystemFont(details, codepoint, &cfg.FontNo)})
    atlas->AddFontFromFileTTF(fallback_file, 0, &cfg);
}

void Setup()
{
  static auto loader {*ImGuiFreeType::GetFontLoader()};
  loader.FontAddFallbackSrc = &LookupFallbackFont;
  atlas->SetFontLoader(&loader);
}

cfillion avatar Jul 24 '25 19:07 cfillion

I will definitively want to merge such feature, but I believe it needs to wait until I refactor font adding/loading functions and memory ownership, so I am putting to likely put that on hold for a bit.

There's actually a sort of "regression" in 1.92.0 as we keep font data in RAM and they may be loaded multiple times, which is an incredible waste. We are going to switch to a model where the library keeps a dictionary of unique data blob for fonts.

ocornut avatar Jul 29 '25 10:07 ocornut

Also, since missing glyphs are cached per-baked font, it unnecessarily retries every time the font size changes (eg. auto-scaling). (Requiring the backend's fallback hook to do its own caching on top.)

cfillion avatar Jul 29 '25 10:07 cfillion

I will definitively want to merge such feature, but I believe it needs to wait until I refactor font adding/loading functions and memory ownership, so I am putting to likely put that on hold for a bit.

There's actually a sort of "regression" in 1.92.0 as we keep font data in RAM and they may be loaded multiple times, which is an incredible waste. We are going to switch to a model where the library keeps a dictionary of unique data blob for fonts.

Technically speaking this can likely be exposed with a simple flag e.g. ImFontFlags_DeferLoading but however the data source is loaded and referenced is the important backbone part of it. I imagine we might make a ImFontSrc be provided e.g a data pointer or a function callback.

I may get back to this sooner as I am presently trying to figure out which is the preferred order to tackle incoming font changes.

ocornut avatar Aug 26 '25 16:08 ocornut