How to Load and Use Custom Fonts with ImFontAtlas in Dear ImGui

Dear ImGui aggregates fonts into a single GPU texture using the ImFontAtlas class exposed via ImGuiIO::Fonts, which you populate with AddFontFromFileTTF() or AddFontFromMemoryTTF() and rasterize by calling Build() before rendering.

Dear ImGui's font system centers around the ImFontAtlas class, which packs glyph bitmaps and custom graphics into a unified texture atlas for efficient rendering. Whether you are loading system fonts or embedding TTF data directly, understanding how to configure the atlas through the ocornut/imgui source code ensures crisp text rendering without unnecessary texture bloat.

Understanding the ImFontAtlas Architecture

The ImFontAtlas serves as a texture packer that rasterizes font glyphs and optional custom graphics into a single bitmap. According to the source code in imgui.h (lines 3600–3717), the atlas maintains a TexRef identifier and manages the lifetime of font data until you explicitly clear it.

Access the global atlas instance through the ImGuiIO structure:

ImGuiIO& io = ImGui::GetIO();
ImFontAtlas* atlas = io.Fonts;  // Always available after ImGui::CreateContext()

The atlas holds ImFont objects, each referencing specific glyph ranges within the shared texture.

Loading Fonts from Files and Memory

Loading TTF Files from Disk

The most common entry point is AddFontFromFileTTF(), declared in imgui.h at line 3713. This method rasterizes a TrueType or OpenType file at a specified pixel size:

ImGuiIO& io = ImGui::GetIO();
ImFont* font = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/arial.ttf", 16.0f);

The function returns an ImFont* pointer that you can push onto the font stack with ImGui::PushFont() when you need to switch typefaces mid-frame.

Loading from Memory Buffers

For embedded assets or encrypted font data, use AddFontFromMemoryTTF() (also declared at line 3713 in imgui.h). This variant accepts a raw byte buffer and takes ownership of the memory by default:

unsigned char* fontData = /* loaded from .ttf file */;
int fontDataSize = /* size in bytes */;
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontData, fontDataSize, 18.0f);

Critical: By default, the atlas frees this buffer after building. If you need to retain the data (for example, to rebuild the atlas later), you must configure ownership flags before adding the font.

Configuring Fonts with ImFontConfig

The ImFontConfig struct (defined around line 3600 in imgui.h) provides granular control over rasterization and memory management. Always zero-initialize the struct to ensure default values:

ImFontConfig cfg;
cfg.SizePixels = 20.0f;
cfg.OversampleH = 3;           // Horizontal oversampling for subpixel precision
cfg.OversampleV = 3;           // Vertical oversampling
cfg.GlyphRanges = io.Fonts->GetGlyphRangesCyrillic();  // Limit glyph set
cfg.FontDataOwnedByAtlas = false;  // Keep fontData alive after Build()

ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontData, size, 20.0f, &cfg);

Managing Glyph Range Lifetime

As warned in imgui.h at line 3699, the GlyphRanges pointer is stored internally without copying the array. The underlying data must remain valid until you call Build() or GetTexDataAsRGBA32(). Use the built-in helpers like GetGlyphRangesDefault(), GetGlyphRangesChineseFull(), or define a custom static ImWchar array.

Controlling Font Data Ownership

When using AddFontFromMemoryTTF(), set cfg.FontDataOwnedByAtlas = false (as shown at line 3714 in imgui.h) if your application manages the buffer lifetime. If left as true (the default), the atlas calls IM_FREE() on the pointer during destruction.

Building the Atlas and GPU Integration

Texture Generation

The atlas remains in a dirty state until you request the texture data. You can force rasterization and packing by calling:

io.Fonts->Build();  // Returns bool indicating success

Alternatively, retrieving the raw pixels with GetTexDataAsRGBA32() or GetTexDataAsAlpha8() implicitly triggers a build. After building, upload the resulting texture to your GPU and store the identifier:

unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// ... upload to GPU ...
io.Fonts->SetTexID((ImTextureID)myTextureId);

Adding Custom Rectangles for Icons

To embed colored icons or graphics alongside fonts, reserve space using the API at lines 3889–3892 in imgui.h:

ImFontAtlasRect rect;
ImFontAtlasRectId id = io.Fonts->AddCustomRect(64, 64, &rect);

// After building:
io.Fonts->Build();
io.Fonts->GetCustomRect(id, &rect);
// rect.uv0 and rect.uv1 now contain normalized coordinates for ImDrawList

Any modification to the atlas—adding fonts or custom rectangles—invalidates the texture and requires rebuilding, which regenerates the UV coordinates.

Optional FreeType Rasterization Backend

For superior hinting and subpixel rendering, enable the FreeType backend by defining IMGUI_ENABLE_FREETYPE before including headers. The implementation resides in misc/freetype/imgui_freetype.cpp.

Activate the loader before adding fonts:

io.Fonts->SetFontLoader(ImGui::GetFreeTypeFontLoader());
ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", 16.0f);

This replaces the default stb_truetype rasterizer with FreeType, supporting advanced features like LCD subpixel rendering and glyph emboldening.

Summary

  • Access the atlas through ImGuiIO::Fonts after creating the ImGui context.
  • Load fonts using AddFontFromFileTTF() for files or AddFontFromMemoryTTF() for embedded data, with signatures defined in imgui.h at line 3713.
  • Configure rasterization via ImFontConfig (line 3600) to control oversampling, glyph ranges, and memory ownership.
  • Preserve memory safety by ensuring glyph range arrays outlive the build process and setting FontDataOwnedByAtlas = false when retaining buffers.
  • Build the texture explicitly with Build() or implicitly via GetTexDataAsRGBA32(), then upload to your GPU backend.
  • Embed custom graphics using AddCustomRect() (lines 3889–3892) for icons that share the font texture.
  • Improve quality by enabling the FreeType loader in misc/freetype/imgui_freetype.cpp for advanced hinting.

Frequently Asked Questions

Why does my application crash when specifying custom glyph ranges?

The ImFontConfig::GlyphRanges member stores a raw pointer without copying the array data. As documented in imgui.h at line 3699, the glyph range array must remain valid in memory until the atlas is built. Store your ImWchar ranges as static constants or ensure they outlive the Build() call to prevent dangling pointer access.

How do I prevent ImFontAtlas from deleting my font data buffer?

When using AddFontFromMemoryTTF(), set cfg.FontDataOwnedByAtlas = false in your ImFontConfig struct before adding the font. This flag, referenced at line 3714 in imgui.h, tells the atlas not to call IM_FREE() on the buffer during destruction, allowing your application to manage the memory lifecycle independently.

When should I call Build() on the font atlas?

Call Build() after adding all fonts and custom rectangles but before your first draw call, typically during application initialization or after loading a new font at runtime. If you modify the atlas later—adding fonts or custom rects—you must rebuild and re-upload the texture to the GPU, as the pixel buffer and UV coordinates change.

Can I use colored icons with ImFontAtlas alongside my fonts?

Yes. Reserve space using AddCustomRect(width, height) (lines 3889–3892 in imgui.h) to allocate a region in the atlas texture. After calling Build(), retrieve the UV coordinates with GetCustomRect() and use them with ImDrawList::AddImage() to render colored graphics that share the same texture as your glyphs.

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 →