Skip to content

How to Center Text

There are many cases where you want to center some text in a box or on a button, for example when building a game menu. Simple math should allow you to align the text correctly, but there are a few gotchas to consider when positioning a sf::Text object.

Load the Font

You need to have a font with a specific character size loaded and set on the text object, otherwise the required text size can’t be determined. There is no default font in SFML 2.

Set a String

Unless you’re using a monospaced font, the characters of a text will have different width and heights. For example an “m” will be quite a bit wider than an “i”. So in order to perfectly position your sf::Text object, you need to pass a string to it, so with and height are correctly calculated.

This also means, that whenever you change the string of an sf::Text you have to recalculate the position.

Consider Local Bounds

Unlike an sf::Sprite or sf::RectangleShape the local bounds (i.e. getLocalBounds()) of a sf::Text object are not fixed to the position (0, 0), but can positive or negative in order to maintain a stable text baseline. If the local bounds would not adjust for that, you’d see your text jump around vertically when you change your text and the added “S” suddenly takes more space at the top.

A diagram showing the line terms used in typography, with the baseline highlighted.

More specifically, this means that you have to add the local bounds position to the origin or position of the sf::Text object, depending on how you align it.

Rounding Position and Origin

You have align text on integer positions, otherwise the rasterizer might render part of your text blurry, as it has to decide how to fake half-/semi-pixel rendering on your window or render texture. As such it’s already recommended to round your position or origin setting to integer values.

No Scale or Zoom

Another general tip for working with text, never scale the text or zoom a view with a text on it. The result will be washed out and hard to read text, since you’re essentially just enlarge the font texture. What you instead want to do, is to change the font size of your text and render on a separate view. That way you will always retain sharp texts.

Example Code

#include <SFML/Graphics.hpp>
#include <cmath>

sf::Vector2f round(const sf::Vector2f vector)
{
    return sf::Vector2f{ std::round(vector.x), std::round(vector.y) };
}

int main()
{
    auto window = sf::RenderWindow{ {800, 600, 32}, "SFML Window" };
    window.setFramerateLimit(60);
    auto font = sf::Font{};

    if (!font.loadFromFile("OpenSans.ttf"))
    {
        sf::err() << "Couldn't load font\n";
        return -1;
    }

    auto text = sf::Text{ "Centered Text", font };
    auto center = text.getGlobalBounds().getSize() / 2.f;
    auto localBounds = center + text.getLocalBounds().getPosition();
    auto rounded = round(localBounds);
    text.setOrigin(rounded);
    text.setPosition(sf::Vector2f{ window.getSize() / 2u });
    text.setFillColor(sf::Color{ 0x655A7CFF });

    auto rectangle = sf::RectangleShape{ {300.f, 100.f} };
    rectangle.setOutlineThickness(2.f);
    rectangle.setOutlineColor(sf::Color{ 0xAB92BFFF });
    rectangle.setOrigin(round(rectangle.getGlobalBounds().getSize() / 2.f));
    rectangle.setPosition(text.getPosition());
    rectangle.setFillColor(sf::Color::Transparent);

    while (window.isOpen())
    {
        for (auto event = sf::Event{}; window.pollEvent(event);)
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        window.clear(sf::Color{ 0xAFC1D6FF });
        window.draw(rectangle);
        window.draw(text);
        window.display();
    }
}

Example screen with the text centered