Loading and Configuring Custom Fonts with ImFontAtlas in Dear ImGui

Dear ImGui uses the ImFontAtlas class to aggregate font glyphs and custom graphics into a single texture that your rendering backend uploads to the GPU. You access the global atlas through ImGuiIO::Fonts, load TrueType or OpenType files via AddFontFromFileTTF(), and configure rasterization settings using the ImFontConfig struct.

Dear ImGui (ocornut/imgui) does not render text using system font APIs; instead, it rasterizes glyphs into a unified texture atlas that the draw list samples during rendering. The ImFontAtlas class manages this aggregation, handling glyph packing, texture generation, and UV coordinate mapping. Mastering this workflow is essential for applications requiring custom typefaces, icon fonts, or multilingual text support.

Accessing the Global Font Atlas

The active Dear ImGui context exposes the font atlas through the ImGuiIO structure. Before the first frame, obtain the atlas pointer to begin adding fonts:

ImGuiIO& io = ImGui::GetIO();
ImFontAtlas* atlas = io.Fonts;

The io.Fonts pointer provides access to all font-loading methods and texture data. You must configure your fonts before the first call to ImGui::NewFrame(), as the atlas builds its texture lazily upon first use.

Loading Fonts from Files and Memory

Dear ImGui supports loading from both disk files and memory buffers. The AddFontFromFileTTF() method, declared in imgui.h at line 3713, is the most common entry point for loading TrueType or OpenType fonts.

Loading from Disk

To load a font with default settings, specify the file path and pixel size:

ImFont* myFont = io.Fonts->AddFontFromFileTTF("myfont.ttf", 18.0f);

If the file cannot be loaded, the function returns NULL and the default embedded font remains active.

Loading from Memory

For embedded resources or encrypted font data, use AddFontFromMemoryTTF() as declared in imgui.h at line 3714. This function takes ownership of the buffer unless configured otherwise:

unsigned char* ttfData = /* ... load file into memory ... */;
int ttfSize = /* size of the buffer */;
ImFont* font = io.Fonts->AddFontFromMemoryTTF(ttfData, ttfSize, 20.0f);

Critical: By default, the atlas assumes ownership of the memory buffer and frees it after building. Set FontDataOwnedByAtlas = false in the configuration struct to retain ownership.

Configuring Font Properties with ImFontConfig

The ImFontConfig struct, defined in imgui.h around line 3600, controls rasterization quality, glyph subsets, and memory ownership. Zero-initialize the struct to ensure default values:

ImFontConfig cfg;
cfg.GlyphRanges = io.Fonts->GetGlyphRangesDefault();
cfg.FontDataOwnedByAtlas = false;
ImFont* font = io.Fonts->AddFontFromFileTTF("arial.ttf", 16.0f, &cfg);

Restricting Glyph Ranges

To reduce texture memory, limit the font to specific Unicode blocks. The GlyphRanges member expects a null-terminated array of inclusive ranges (min, max pairs). Warning: The atlas stores this pointer, not a copy, so the array must remain valid until the atlas is built, as documented in imgui.h at line 3699.

// Load only Basic Latin and Cyrillic
ImFontConfig cfg;
cfg.GlyphRanges = io.Fonts->GetGlyphRangesCyrillic();
ImFont* font = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/arial.ttf", 16.0f, &cfg);

Oversampling for Quality

Increase oversampling to improve text quality at small sizes or when scaling:

ImFontConfig cfg;
cfg.OversampleH = 3;
cfg.OversampleV = 3;
ImFont* font = io.Fonts->AddFontFromMemoryTTF(ttfData, ttfSize, 20.0f, &cfg);

Higher values consume more texture memory but reduce aliasing artifacts.

Managing Font Data Ownership and Lifetime

When using AddFontFromMemoryTTF(), the atlas takes ownership of the font data by default. This is controlled by the FontDataOwnedByAtlas flag in ImFontConfig. If your application manages the memory lifecycle or uses static data, disable ownership transfer:

ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;  // Keep the buffer valid
ImFont* font = io.Fonts->AddFontFromMemoryTTF(ttfData, ttfSize, 20.0f, &cfg);

Failing to set this flag when necessary leads to use-after-free errors when the atlas builds.

Adding Custom Rectangles for Icons and Graphics

The atlas supports reserving space for user-defined graphics, such as colored icons or application-specific symbols. Use AddCustomRect() to reserve pixels, defined in imgui.h at lines 3889-3892:

ImFontAtlasRect iconRect;
ImFontAtlasRectId iconId = io.Fonts->AddCustomRect(64, 64, &iconRect);

After building the atlas, retrieve UV coordinates using GetCustomRect():

io.Fonts->Build();
io.Fonts->GetCustomRect(iconId, &iconRect);
// Use iconRect.uv0 and iconRect.uv1 for ImDrawList texture coordinates

Any modification to the atlas—adding fonts or custom rects—invalidates previous texture handles and UV coordinates. Always fetch custom rectangle data after each build.

Building the Atlas and Uploading to GPU

The atlas generates its texture lazily when you request the pixel data. Trigger a build explicitly or let it happen on first access:

unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);

For backends requiring single-channel textures, use GetTexDataAsAlpha8() instead. The TexDesiredFormat field controls the output format; default is ImTextureFormat_RGBA32.

After uploading to GPU, store the texture identifier:

io.Fonts->SetTexID(myTextureId);

Enabling FreeType for High-Quality Rasterization

Dear ImGui includes an optional FreeType backend for superior hinting and subpixel rendering. Enable it by defining IMGUI_ENABLE_FREETYPE and linking against the FreeType library. The implementation resides in misc/freetype/imgui_freetype.cpp.

Register the loader before adding fonts:

#define IMGUI_ENABLE_FREETYPE
#include "imgui.h"

// Before any AddFont calls
io.Fonts->SetFontLoader(ImGui::GetFreeTypeFontLoader());
ImFont* font = io.Fonts->AddFontFromFileTTF("myfont.ttf", 16.0f);

This replaces the default stb_truetype rasterizer with FreeType, providing better glyph shapes at small sizes and support for LCD-optimized rendering.

Summary

  • Access the atlas through ImGuiIO::Fonts before the first frame.
  • Load fonts using AddFontFromFileTTF() (imgui.h line 3713) or AddFontFromMemoryTTF() (imgui.h line 3714).
  • Configure rasterization with ImFontConfig (imgui.h line 3600), setting GlyphRanges for Unicode subsets and OversampleH/V for quality.
  • Manage memory by setting FontDataOwnedByAtlas = false when the buffer must outlive the atlas.
  • Add custom graphics via AddCustomRect() (imgui.h lines 3889-3892) and retrieve UVs after building.
  • Build and upload using GetTexDataAsRGBA32() or GetTexDataAsAlpha8(), then call SetTexID() with your GPU texture handle.
  • Use FreeType by enabling IMGUI_ENABLE_FREETYPE and calling SetFontLoader() with the FreeType loader from misc/freetype/imgui_freetype.cpp.

Frequently Asked Questions

How do I load multiple fonts at different sizes?

Call AddFontFromFileTTF() multiple times before building the atlas. Each call returns a distinct ImFont* pointer that you can push to ImGui::PushFont() when needed:

ImFont* font18 = io.Fonts->AddFontFromFileTTF("font.ttf", 18.0f);
ImFont* font24 = io.Fonts->AddFontFromFileTTF("font.ttf", 24.0f);
io.Fonts->Build();

Why does my custom font display as blocks or question marks?

This occurs when the texture atlas lacks the requested glyphs. Ensure your ImFontConfig::GlyphRanges includes the Unicode codepoints you are rendering. The default range only covers Basic Latin. For CJK or Cyrillic, use the built-in helpers like GetGlyphRangesChineseSimplifiedCommon() or GetGlyphRangesCyrillic().

Can I update the font atlas after the application has started?

Yes, but you must rebuild the texture and re-upload it to the GPU. Clearing and repopulating the atlas invalidates previous ImFont* pointers and UV coordinates. Call Clear() on the atlas, re-add your fonts, call Build() or GetTexDataAsRGBA32(), then update your backend texture. Be sure to refresh any cached custom rectangle UVs.

What is the difference between stb_truetype and FreeType?

Dear ImGui uses stb_truetype by default for lightweight, single-header rasterization. The optional FreeType backend (misc/freetype/imgui_freetype.cpp) provides advanced hinting, subpixel rendering, and better support for complex scripts. Enable FreeType by defining IMGUI_ENABLE_FREETYPE and setting the font loader via SetFontLoader(ImGui::GetFreeTypeFontLoader()) before adding fonts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →