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()
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.