Using fonts

ImGui is able to load and display fonts. It is capable of handling both OTF and TTF fonts.

To load and configure fonts you should use the _FontAtlas object available through _IO.fonts attribute. Neither _FontAtlas nor _IO.fonts are meant to be instantiated by user. You access them through the imgui.get_io() function:

import imgui

io = imgui.get_io()

Once you have access to the font atlas you can load custom fonts by providing path of the OTF or TTF file to the _FontAtlas.add_font_from_file_ttf() method:

io = imgui.get_io()
io.fonts.add_font_from_file_ttf(
    font_file_path, font_pixel_size, glyph_ranges
)

The _FontAtlas.add_font_from_file_ttf() acccepts three important arguments:

  • filename (int): path to OTF or TTF file

  • size_pixels (int): font size in pixels

  • glyph_ranges (_StaticGlyphRanges): ranges of glyphs to load from the font file

Once all your fonts are loaded you can rasterize font into texture that will be later used by your renderer on all widgets containing text. Remember that core of the ImGui library is completely decoupled from the rendering pipeline. It means that your renderer implementation is responsible for rendering fonts from the texture and ImGui only provides the abstract draw calls for rendering part of the textures and doesn’t even know what is actual implementation behind them.

For OpenGL renderers you can generate texture for all your loaded fonts using following code:

import imgui
from OpenGL import GL as gl

io = imgui.get_io()

last_texture = gl.glGetIntegerv(gl.GL_TEXTURE_BINDING_2D)

width, height, pixels = io.fonts.get_tex_data_as_rgba32()

font_texture = gl.glGenTextures(1)

gl.glBindTexture(gl.GL_TEXTURE_2D, font_texture)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixels)

io.fonts.texture_id = font_texture
gl.glBindTexture(gl.GL_TEXTURE_2D, last_texture)

Fortunately you don’t have to do this all by yourself. Every built-in integration class from imgui.integrations sub-package provides refresh_font_texture() that does this for you in a way that is native for given rendering pipeline.

Following example shows how this usually looks in practice:

io = imgui.get_io()
new_font = io.fonts.add_font_from_file_ttf(
    "DroidSans.ttf", 20,
)
impl.refresh_font_texture()

# later in frame code

imgui.begin("Default Window")

imgui.text("Text displayed using default font")

with imgui.font(new_font):
    imgui.text("Text displayed using custom font")

imgui.end()

Above code should render following result:

../_images/guideusingfonts_0.png

High-density screens

On some high-density displays like Retina Display fonts may appear blurred. It’s because font sizes are specified in screen size pixels but on high-density screens actual framebuffer will be much bigger so font texture needs to be slightly stretched.

In order to curcumvent this effect you can tell ImGui to generate bigger font

textures and set proper global font scaling factor as on following example:

import imgui

io = imgui.get_io()

# Font scaling factor is display dependent (different displays
# may have different densities), so you will have to find
# actual factor on your own
font_scaling_factor = 2
font_size_in_pixels = 30

io.fonts.add_font_from_file_ttf(
    "DroidSans.ttf", font_size_in_pixels * font_scaling_factor
)
io.font_global_scale /= font_scaling_factor

So how to relaibly guess required font scaling factor? One of the ways is to compare window size with size of the actual framebuffer assigned to that window. Implementation will be different for each rendering library. Here is one of possible examples for glfw:

win_w, win_h = glfw.get_window_size(window)
fb_w, fb_h = glfw.get_framebuffer_size(window)
font_scaling_factor = max(float(fb_w) / win_w, float(fb_h) / win_h)

If you set global scaling factor then you should also probably clear whole font atlas before adding any font atlas using _FontAtlas.clear(). Otherwise default built in font that is loaded on imgui startpup will be too small to read. Its place will be taken by first font you add to the atlas.