How do you dynamically create a UFont at Runtime in c++?

How do you dynamically create a UFont object at runtime from a .ttf font in c++? Assuming I can get the .ttf font into memory as bytes, TArray, how can I create UFontFace from that and plug it into a UFont?

I want my users to be able to upload custom .ttf fonts in addition to the ones that I provide for maximum customization.

It’s simpler if you can write the font to disk and avoid dealing with UFontFace.

FLegacySlateFontInfoCache::GetSystemFont provides an example of this, and you’d want to do something similar but populate the CompositeFont inside your UFont (you’d also need to make sure that FontCacheType is set to EFontCacheType::Runtime on your UFont).

If you do want to use a UFontFace then you’d need to populate its FontFaceData via a call to FFontFaceData::MakeFontFaceData passing in your raw TTF data (you’d also need to make sure that LoadingPolicy is set to EFontLoadingPolicy::Inline on your UFontFace). It’s then just a case of using that UFontFace as the asset on the FFontData for the CompositeFont inside your UFont.

Thanks for your reply!

I’m working with the HTML5 platform, so I don’t think writing to disk is an option here.

My understanding of what you suggested for the raw TTF data is -

  1. Create an FFontFaceData struct using FFontFaceData::MakeFontFaceData( .ttf file as bytes TArray ). Will I need to strip any headers or anything out of the .ttf file as bytes?
  2. Create an FFontFaceDataRef using the FFontFaceData from step 1.
  3. Create a new UFontFace object and set it’s public member variable FontFaceData to the FFontFaceDataRef from step 2.

From here I’m unsure on how you would make an FCompositeFont from the UFontFace. How do you do that?

Once I have the FCompositeFont, then I can just create a new UFont object and set the UFont’s CompositeFont to the newly created FCompositeFont? Then the UFont will be ready to use in UMG and such?

UFontFace* FontFace = NewObject(…);
FontFace->LoadingPolicy = EFontLoadingPolicy::Inline;
FontFace->FontFaceData = FFontFaceData::MakeFontFaceData(…);

UFont* Font = NewObject<UFont>(...);
Font->FontCacheType = EFontCacheType::Runtime;
FTypefaceEntry& TypefaceEntry = Font->CompositeFont.DefaultTypeface.Fonts[Font->CompositeFont.DefaultTypeface.Fonts.AddDefaulted()];
TypefaceEntry.Font = FFontData(FontFace);

Something like that. Note that at that point in time nothing is keeping your font from being GC’d, but if you use it with a UMG widget then the widget will keep it alive (there are also other ways to keep an object alive).

Also note that MakeFontFaceData takes an r-value reference, so you’ll either need to MoveTemp or CopyTemp your data into it (depending on whether you still need your source array afterwards).

Will I need to strip any headers or anything out of the .ttf file as bytes?

No, it’s just raw TTF/OTF payload data. FreeType is what actually makes use of the data.

This worked! Thank you so much!!

For anyone running into GC issues, try this:

// After making the font
FontFace->AddToRoot();
Font->AddToRoot();

...

// Later on, when exiting your game or releasing resources
FontFace->RemoveFromRoot();
FontFace->ConditionalBeginDestroy();
Font->RemoveFromRoot();
Font->ConditionalBeginDestroy();