Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to embed font and other image files when using graphic libraries like SFML

Many of you have probably wondered- how can I get rid of a font or any other single file from the drive when using a library like SFML, which needs to load a font from a file path.

So, how do I embed that data into the executable, so the resulting executable does not depend on those resource files anymore?

like image 410
Karol Szustakowski Avatar asked Mar 09 '23 06:03

Karol Szustakowski


1 Answers

First of all, we have to get our resource. I have downloaded "BalooBhaijaan-Regular.ttf" font from google fonts.
Then, one should get the binary data of the given font. The easiest way to achieve this in my opinion is to use the linux "xxd" command with -i parameter which outputs in a C-style array.
Let's redirect the output to a file because It is usually going to be long if we are talking about true type fonts or larger images:

xxd -i BalooBhaijaan-Regular.ttf > font_data.txt

Create an empty C/C++ header or put the font data into an already existing file. I prefer using new header files as the output is going to be really long.
Of course, after pasting into your IDE you can change the array type to const as the content of a font usually doesn't change.
This is how it looks in my IDE: enter image description here You might of course wonder why is this a char array - simply because in a char array each "field" represents one byte.

As you might have noticed, xxd also creates another variable for us - the last variable in the font_data.txt is an unsigned int which informs us about the length of the array. We will need this later. Name of the "length-informing" integer is same as name of the array with "_len" suffix

Now, there are two ways to proceed:
1. load font from the memory using a builtin method (some libraries support it, SFML does)
2. create a "fake" file and load it
Lets talk about both cases


1.
This one is fairly simple, sfml supports loading file from memory given it's address and size, so we can just do this:

#include "BalooBhaijaanFont.hpp"
#include <SFML/Graphics.hpp>

int main(int argc, char** argv) {
    sf::RenderWindow mainWindow(sf::VideoMode(200,100), L"TEST");
    sf::Font fromMem;
    fromMem.loadFromMemory(&BalooBhaijaan_Regular_ttf, BalooBhaijaan_Regular_ttf_len);
    sf::Text text("WORKS!", fromMem);
    while(mainWindow.isOpen()){
        mainWindow.draw(text);
        mainWindow.display();
    }
    return 0;
}

As you can see, loading it with a builtin function is really easy.



2.
Now it's time for a temporary file approach which i really do NOT recommend - most libraries support loading from memory and if you are making your own library you are going to end with having a memory load function anyway.
Whilst it is still possible to create a file just to read it to a font class and then remove it, I do not see any sense of using this method unless you are extremely annoyed by additional files in your folders.
Just for reference:

#include "BalooBhaijaanFont.hpp"
#include <SFML/Graphics.hpp>

int main(int argc, char** argv) {
    sf::RenderWindow mainWindow(sf::VideoMode(200,100), L"TEST");
    sf::Font fromFile;
    {
        FILE * tempFile = fopen("tmpfont.ttf", "wb");
        fwrite( BalooBhaijaan_Regular_ttf, sizeof(char), BalooBhaijaan_Regular_ttf_len, tempFile );
        fclose(tempFile);
        fromFile.loadFromFile("tmpfont.ttf");
        std::remove("tmpfont.ttf");
    }
    sf::Text text("WORKS!", fromFile);
    while(mainWindow.isOpen()){
        mainWindow.draw(text);
        mainWindow.display();
    }
    return 0;
}
like image 176
Karol Szustakowski Avatar answered Mar 23 '23 00:03

Karol Szustakowski