Search Unity

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

Discussion in 'UGUI & TextMesh Pro' started by Dmitry_Kozlovtsev, Nov 8, 2019.

  1. Dmitry_Kozlovtsev

    Dmitry_Kozlovtsev

    Joined:
    Mar 2, 2018
    Posts:
    13
    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
    Code (csharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. 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)
    3. 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
     
  2. Dmitry_Kozlovtsev

    Dmitry_Kozlovtsev

    Joined:
    Mar 2, 2018
    Posts:
    13
    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.
     
  3. llauer

    llauer

    Joined:
    Aug 9, 2016
    Posts:
    1
  4. Dmitry_Kozlovtsev

    Dmitry_Kozlovtsev

    Joined:
    Mar 2, 2018
    Posts:
    13
    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?
     
  5. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The TMP_FontAsset API should work.

    See if the following code works on your end.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.TextCore;
    4. using UnityEngine.TextCore.LowLevel;
    5. using TMPro;
    6.  
    7. public class RuntimeFontAssetCreation : MonoBehaviour
    8. {
    9.     public Font SourceFont;
    10.     private TMP_Text m_TextComponent;
    11.     public TMP_FontAsset m_FontAsset;
    12.  
    13.     private void Awake()
    14.     {
    15.         GameObject go = new GameObject();
    16.         m_TextComponent = go.AddComponent<TextMeshPro>();
    17.         m_TextComponent.text = string.Empty;
    18.         m_FontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic);
    19.         m_FontAsset.TryAddCharacters("Text");
    20.         m_TextComponent.font = m_FontAsset;
    21.         //GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(s_GlyphIndexArray);
    22.     }
    23. }
    24.  
    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 ;)

    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.
     
  6. Dmitry_Kozlovtsev

    Dmitry_Kozlovtsev

    Joined:
    Mar 2, 2018
    Posts:
    13
    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

    Code (CSharp):
    1. Material doesn't have a float or range property '_CullMode'
    2. UnityEngine.Material:GetFloat(String)
    3. TMPro.TMP_SubMeshUI:UpdateMaterial() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:691)
    4. TMPro.TMP_SubMeshUI:SetMaterialDirty() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:601)
    5. TMPro.TMP_SubMeshUI:SetSharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:823)
    6. TMPro.TMP_SubMeshUI:set_sharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:85)
    7. TMPro.TextMeshProUGUI:SetArraySizes(UnicodeChar[]) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1421)
    8. TMPro.TMP_Text:ParseInputText() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_Text.cs:1858)
    9. TMPro.TextMeshProUGUI:OnPreRenderCanvas() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1647)
    10. TMPro.TextMeshProUGUI:Rebuild(CanvasUpdate) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TextMeshProUGUI.cs:209)
    11. UnityEngine.Canvas:SendWillRenderCanvases()

    Code (CSharp):
    1. Material doesn't have a float or range property '_CullMode'
    2. UnityEngine.Material:GetFloat(String)
    3. TMPro.TMP_SubMeshUI:UpdateMaterial() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:691)
    4. TMPro.TMP_SubMeshUI:SetMaterialDirty() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:601)
    5. TMPro.TMP_SubMeshUI:SetSharedMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:823)
    6. TMPro.TMP_SubMeshUI:set_fallbackMaterial(Material) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_SubMeshUI.cs:107)
    7. TMPro.TextMeshProUGUI:SetArraySizes(UnicodeChar[]) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1430)
    8. TMPro.TMP_Text:ParseInputText() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMP_Text.cs:1858)
    9. TMPro.TextMeshProUGUI:OnPreRenderCanvas() (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TMPro_UGUI_Private.cs:1647)
    10. TMPro.TextMeshProUGUI:Rebuild(CanvasUpdate) (at Packages/com.unity.textmeshpro@2.1.0-preview.2/Scripts/Runtime/TextMeshProUGUI.cs:209)
    11. UnityEngine.Canvas:SendWillRenderCanvases()
    12.  
    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)

    Code (CSharp):
    1. fontAsset.atlasPopulationMode = AtlasPopulationMode.Static;
    2.                 fontAsset.material.name = localeGroup.name + " Material";
    3.                 fontAsset.atlasTexture.name = localeGroup.name + " Atlas";
    4.              
    5.                 AssetDatabase.DeleteAsset(fontAssetPath);
    6.                 AssetDatabase.CreateAsset(fontAsset, fontAssetPath);
    7.                 AssetDatabase.AddObjectToAsset(fontAsset.material, fontAsset);
    8.                 AssetDatabase.AddObjectToAsset(fontAsset.atlasTexture, fontAsset);

    About the NRE's - they're due to a bug reported in https://forum.unity.com/threads/tryaddcharacters-broken-in-2018-4-6f1.726881/ They're still reproducible in 2.0.0, but fixed in 2.1.0-preview
     
    Last edited: Nov 12, 2019
  7. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    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.
     
  8. Dmitry_Kozlovtsev

    Dmitry_Kozlovtsev

    Joined:
    Mar 2, 2018
    Posts:
    13
    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
     
    Last edited: Nov 13, 2019