[TextMeshPro 2.0.1] How to create FontAsset programmatically in editor?

Hello, I’m trying to generate TMP_FontAsset programmatically in editor (I need to generate different font assets for different locales to save some download size and to allow for chinese language). I can’t seem to find any editor API to do it, I figure Editor Window either uses internal methods or has generation logic built-in.
Tried to use runtime API using TMP_FontAsset.CreateFontAsset to create asset and adding characters with TryAddCharacters, but it crashes for me with Null Reference Exception with the following call stack

NullReferenceException: Object reference not set to an instance of an object
TMPro.TMP_FontAsset.TryAddCharacters (System.UInt32[] unicodes, System.UInt32[]& missingUnicodes) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs:1082)
TMPro.TMP_FontAsset.TryAddCharacters (System.UInt32[] unicodes) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs:981)

So how does one create FontAsset programmatically?

I’m using TextMeshPro 2.0.1

Bump. I find it hard to believe that no one had the need to create font assets automatically for example from translation files the game has.

See this thread: TryAddCharacters broken in 2018.4.6f1

Thanks for that, unfortunatelly the resulting asset cannot be saved as is - it produces all kinds of error if saved in AssetDatabase and used as a font in TextMeshPro object. The only solution I see is to copy and adapt the asset creation code from FontCreatorWindow.

Is there a way to create a feature request?

The TMP_FontAsset API should work.

See if the following code works on your end.

using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using TMPro;

public class RuntimeFontAssetCreation : MonoBehaviour
{
    public Font SourceFont;
    private TMP_Text m_TextComponent;
    public TMP_FontAsset m_FontAsset;

    private void Awake()
    {
        GameObject go = new GameObject();
        m_TextComponent = go.AddComponent<TextMeshPro>();
        m_TextComponent.text = string.Empty;
        m_FontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic);
        m_FontAsset.TryAddCharacters("Text");
        m_TextComponent.font = m_FontAsset;
        //GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(s_GlyphIndexArray);
    }
}

I have this script assigned to an empty game object. Assign some font file to this object. Enter play mode and you can inspector the public m_FontAsset to see if the characters / glyphs were added.

The above won’t produce a persistent asset but that is easy to fix using AssetDatabase API.

P.S. Although I do read the vast majority of TMP related posts, I don’t always have time to reply.

You already kind of did :wink:

So let me know how the script above works. From there we can figure out why you were / are getting these NRE.

P.S.S Make sure the font file you assign is set to Dynamic with Include Font Data enabled. It should be set that way on import but just in case.

Thank you for the reply,
I had to switch to 2.1.0-preview.2 to avoid nullref exception in TryAddCharacters for the code to work, but assets created that way cause following errors somewhere inside textmespro

Material doesn't have a float or range property '_CullMode'
UnityEngine.Material:GetFloat(String)
TMPro.TMP_SubMeshUI:UpdateMaterial() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:691)
TMPro.TMP_SubMeshUI:SetMaterialDirty() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:601)
TMPro.TMP_SubMeshUI:SetSharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:823)
TMPro.TMP_SubMeshUI:set_sharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:85)
TMPro.TextMeshProUGUI:SetArraySizes(UnicodeChar[]) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1421)
TMPro.TMP_Text:ParseInputText() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_Text.cs:1858)
TMPro.TextMeshProUGUI:OnPreRenderCanvas() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1647)
TMPro.TextMeshProUGUI:Rebuild(CanvasUpdate) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TextMeshProUGUI.cs:209)
UnityEngine.Canvas:SendWillRenderCanvases()
Material doesn't have a float or range property '_CullMode'
UnityEngine.Material:GetFloat(String)
TMPro.TMP_SubMeshUI:UpdateMaterial() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:691)
TMPro.TMP_SubMeshUI:SetMaterialDirty() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:601)
TMPro.TMP_SubMeshUI:SetSharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:823)
TMPro.TMP_SubMeshUI:set_fallbackMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:107)
TMPro.TextMeshProUGUI:SetArraySizes(UnicodeChar[]) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1430)
TMPro.TMP_Text:ParseInputText() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_Text.cs:1858)
TMPro.TextMeshProUGUI:OnPreRenderCanvas() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1647)
TMPro.TextMeshProUGUI:Rebuild(CanvasUpdate) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TextMeshProUGUI.cs:209)
UnityEngine.Canvas:SendWillRenderCanvases()

Whereas regularly created font assets do not.

It is also not exactly what I need, I need to create and save to assetdatabase customly created font assets with static AtlasPopulationMode in Editor

I tried the following code to save it, but the error is still present and the asset does not look valid (texture size is 0 for example)

fontAsset.atlasPopulationMode = AtlasPopulationMode.Static;
                fontAsset.material.name = localeGroup.name + " Material";
                fontAsset.atlasTexture.name = localeGroup.name + " Atlas";
             
                AssetDatabase.DeleteAsset(fontAssetPath);
                AssetDatabase.CreateAsset(fontAsset, fontAssetPath);
                AssetDatabase.AddObjectToAsset(fontAsset.material, fontAsset);
                AssetDatabase.AddObjectToAsset(fontAsset.atlasTexture, fontAsset);

About the NRE’s - they’re due to a bug reported in https://discussions.unity.com/t/754270 They’re still reproducible in 2.0.0, but fixed in 2.1.0-preview

The _CullMode errors are related to the need to update to TMP Essential Resources. See the Important Note in the following sticky thread / post .

There is another potential issues related to the culling mode feature addition for which a fix is available in the following post .

I’ve managed to save asset and after updating shaders it seems to work. It is not a competely identical to one created by fontassetcreatorwindow though, namely it does not have creationSettings filled out. Next problem is is to figure out how to replace old asset with the new one preserving external references.There seem to be no method to reinitialize FontAsset from another FontAsset and FontAsset has some internal fields that cannot be accessed from outside.

Edit: Actually nevermind that, copied asset with unitys “copy-through-serialization” approach