Search Unity

Load font from mobile device and assign as fallback TMP_FontAsset

Discussion in 'UGUI & TextMesh Pro' started by Alexfedel, Apr 6, 2019.

  1. Alexfedel

    Alexfedel

    Joined:
    Sep 25, 2015
    Posts:
    15
    I'm working on a mobile app with multi-language support, including Japanese. I do not want to include the Japanese font in the app, but I also want to use TextMesh Pro.
    The font should be loaded from the device and assigned as a fallback TMP_FontAsset to my standard TMP_FontAsset.
    I know unitys ui text-component is using this kind of system to make all characters of all languages available.

    I´m using Unity 2018.3.11f1 and TextMesh Pro 1.4.0 with Dynamic SDF.

    This is my code, but it is not working as expected. Does anyone know what I'm making wrong or is it not possible how I try?

    Code (CSharp):
    1.     public TMP_FontAsset defaultFontAsset;
    2.     public TextMeshProUGUI testLabel;
    3.    
    4.     private void AssignFallbackFont(){
    5.  
    6.         string[] fonts = Font.GetOSInstalledFontNames();
    7.  
    8.         for(int i = 0; i < fonts.Length; i++){
    9.            
    10.             /*
    11.             I know my android device has this font
    12.             and it should have all japanese characters
    13.             */
    14.             if(  fonts[i] == "Noto Sans CJK JP" ){
    15.                
    16.                 Font font = Font.CreateDynamicFontFromOSFont(fonts[i], 12);
    17.                
    18.                 // some japanese characters to test
    19.                 font.RequestCharactersInTexture("テスト");
    20.                
    21.                 TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(font, 44, 5,  GlyphRenderMode.SDFAA, 512, 512, AtlasPopulationMode.Dynamic);
    22.                
    23.                 // this is not working it returns false
    24.                 bool success = fontAsset.TryAddCharacters("テスト");
    25.                 print(success);
    26.                
    27.                 defaultFontAsset.fallbackFontAssetTable.Add(fontAsset);
    28.                
    29.                 // I just see squares
    30.                 testLabel.text = "テスト";
    31.             }
    32.         }
    33.     }
     
    carlossaoud likes this.
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    This is not working because the font created by using CreateDynamicFontFromOSFont does not have a reference to the font file / font data unlike font files imported in the project with the Include Font Data property set to true.

    The FontEngine used by TMP, can create font assets from the file path of any given font. So if you have the path of this OS font files, then adding a new overload for the CreateFontAsset that would take this path could be added in TMP.

    Alternatively, I would need to make changes to Unity to either force the fontData to be referenced by Fonts created using CreateDynamicFontFromOSFont or modify the Font class to expose a reference to the file path for the font. Both of these options, are problematic only because they would require back porting the changes.
     
  3. Alexfedel

    Alexfedel

    Joined:
    Sep 25, 2015
    Posts:
    15
    I understand. I think it would be nice if TMP could create a FontAsset from a given font file path.
    However, I have to find a way to get the OS font file paths. It´s a bit unfortunate that Unity has a function "GetOSInstalledFontNames", but you only get the font names and not the absolute file path.

    It think TMP Pro is almost perfect with the new Dynamic SDF support. If sometime there would be a more or less simple solution to automatically get the OS fonts as fallback like unitys Text-Component does, so we wouldn't get any problems with different languages, I would be very grateful.

    Thank you for the great work!
     
    1g0rrr, habitoti and MingweiWang like this.
  4. habitoti

    habitoti

    Joined:
    Feb 28, 2013
    Posts:
    141
    Absolutely agree! Mobile devices have at least one almost complete or very comprehensive Unicode font on board that would allow you to dynamically create whatever you haven't pre-rendered as font asset. Plus you'd instantly get a full set of Emojiis, not just a very limited subset.
     
  5. mdsitton

    mdsitton

    Joined:
    Jan 27, 2018
    Posts:
    66
    100% agree that this should be fixed. We're already having to ship cjk fonts with our game to be able to use TMP.
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I am adding a new function called GetPathsToOSFonts() which will retrieve the paths to fonts installed on local devices.

    I am also modifying the Font constructor to allow users to pass a path to a local on device font retrieved with the previous new function. This newly created font will include the Font Data needed for TMP to create TMP_FontAssets at runtime from these fonts.

    This new functionality will be included in a future release of Unity 2018.4 and 2019.x. This will also require an updated version of the TMP package.

    See the following post.
     
    Last edited: Jun 16, 2019
    1g0rrr likes this.
  7. habitoti

    habitoti

    Joined:
    Feb 28, 2013
    Posts:
    141
    This sounds like an awesome and incredibly useful feature that could solve the font issues once and for all! Do you have a some more specific ETA?
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Hopefully within the next 2 - 3 weeks.
     
  9. Alexfedel

    Alexfedel

    Joined:
    Sep 25, 2015
    Posts:
    15
    Thank you, it is working on Android. The android phones I tested all have "/system/fonts/NotoSansCJK-Regular.ttc", which I can use to create the TMP_FontAsset for japanese characters. I also search for some fallback font names if "NotoSansCJK-Regular" not exists.
    Unfortunately it is not working on iOS; GetPathsToOSFonts doesn't return any font paths. Probably you can't access iOS fonts? Although unitys Text component is able to load japanese fonts and GetOSInstalledFontNames() also returns valid japanese font names.
     
  10. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Prior to the inclusion of this feature, I did test GetPathsToOSFonts() on iOS so that should be working. I'll try taking another look over the next few days.
     
  11. Alexfedel

    Alexfedel

    Joined:
    Sep 25, 2015
    Posts:
    15
    I tested it on 3 different iOS devices and did not get any font paths. All devices have iOS 12. I use Unity 2019.2.4.
     
  12. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    There is indeed an issue on iOS with getting the paths to OS fonts.

    I'll try looking into this over the weekend / early next week. I will provide an update as soon as I have more information.
     
  13. michael_looply

    michael_looply

    Joined:
    Mar 26, 2018
    Posts:
    5
    Is there any update on retrieving iOS system font paths @Stephan_B? It looks like no system fonts are returned on iOS with TMP 2.1.0.
     
  14. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    No updates. Trying to get the next preview released. Should be able to provide an update early next week,

    In the meantime and although the GetPathsToOSFonts() doesn't return anything, assuming you know the path of an OS font on the device, we should be able to create a font asset. I haven't tested it but I believe it should work.
     
  15. michael_looply

    michael_looply

    Joined:
    Mar 26, 2018
    Posts:
    5
    Alright, thank you for the suggestion - will give that a go in the meantime.
     
  16. jonahyakuto

    jonahyakuto

    Joined:
    Mar 9, 2017
    Posts:
    11
    Any update on this? It seems Apple restricts anyone from accessing iOS system fonts :(. I wonder if there's a workaround to using iOS system fonts as fallback fonts.
     
  17. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,945
    @Stephan_B
    Any update? I'm building a text editor so can TMPro be used to load system fonts with a dynamic atlas?
    For example, user will type a "word" like "Stephan"
    the we show a list of all system fonts via Font.GetOSInstalledFontNames();

    then user select a font and we create TMpro based dynamic Font atlas and apply to that word? It this part doable with your recent updates?

    Thanks
     
  18. RamonEsteveCuevas

    RamonEsteveCuevas

    Joined:
    Jan 29, 2016
    Posts:
    3
    Is there any update on this? We would really like to use this functionality as well and are also released on iOS or is there some sort of fallback to use it on iOS?
     
  19. habitoti

    habitoti

    Joined:
    Feb 28, 2013
    Posts:
    141
    Had anyone luck in explicitly giving the iOS fontpath (whatever that is)?
     
  20. habitoti

    habitoti

    Joined:
    Feb 28, 2013
    Posts:
    141
    I was a bit euphoric when I stumbled over this in the https://docs.unity3d.com/Manual/class-Font.html documentation:
    But then I noticed it was for the "old" legacy Textmesh, not TextMesh Pro. Wouldn't it be great if TextMesh Pro also would just have a built-in fallback to a list of font names (maybe with a list or range of glyph codes each should cover) to then automagically do fallback rendering based on available system fonts?
     
  21. NikitaLikhoded

    NikitaLikhoded

    Joined:
    Mar 6, 2019
    Posts:
    3
    @Stephan_B any updates here? Habitoti's idea with built-in fallback system seems to be very usefull
     
    Last edited: Jul 7, 2020
  22. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I did add a new Constructor to the Font class where if you pass the fully qualified path, it will create a Font object which can be used to create a Font Asset. This should work on iOS provided you have the valid path to such font file on the device.
     
  23. NikitaLikhoded

    NikitaLikhoded

    Joined:
    Mar 6, 2019
    Posts:
    3
    @Stephan_B thanks for your answer. But habitoti wrote about built-in fallback for system fonts. For example: I need to support as much as possible languages for user input. On android (in most cases) it can be performed by using Noto* fonts. For now I need to create font assets for each unique characters subset (for example, NotoNaskhArabic for arabic, NotoSansCJK for chinese, japanese and korean, NotoSansArmenian for armenain etc). It's not really a good idea because of memory limits on low-end devices. Any ideas how it can be performed without native fallbacks for system fonts?

    If I understood everything right, i have two options (for now):
    1. Add built-in Dictionary<char, string>, where key is character and value is filename of font which contains that character. On user input, if existing font assets can't handle current character -> create new font asset using path from dictionary. But this option will only work if fonts, that I decided to include in dictionary, exists on user device (for example, Noto* fonts included in dictionary, but some old devices or devices with custom roms doesn't have that font family)
    2. Core changes in TMPro_FontAsset class to do something like this:
    a. if TryAddCharacterInternal returns false -> try to render character with UnityEngine.UI.Text instance (UnityEngine.UI.Text have built-in fallback for system fonts, as habitoti mentioned above)
    b. if (a) succeed -> add uiText.font.mainTexture to atlasTextures (and mark this entry somehow like "don't use this atlas for TryAddGlyphToTexture")
    c. Create Glyph and TMP_Character using info from uiText and add them to m_GlyphTable, m_GlyphLookupDictionary, m_CharacterTable, m_CharacterLookupDictionary, m_GlyphIndexList
    d. keep glyph info up-to-date on uiText.FontTextureChanged (doesn't matter how to catch call of this method)​
    But to do all that things (and I'm not sure if this idea should work by design) I need to include source of TMPro to project (not as package) and manually update for new versions.​

    What do you think about all of this?
     
    Last edited: Jul 9, 2020
  24. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The main platform where GetPathsToOSFonts() doesn't return a list of all system fonts is iOS where in this case, you need to know what system fonts are available and their path.

    In terms of using system fonts, whether you use TMP or the legacy text system, the font file still has to be open to check if it contains certain glyphs. Both system use Freetype internally to load these font files so there is no difference in terms of memory usage.

    This legacy text system fallback to system fonts for most platforms is a predefined / hardcoded list of fonts to search. Again nothing magical there. If that fails, it then checks other system font in the order they are found.

    Here is an example of what it checked on Android and the order in which it searches for characters.

    "Roboto",
    "Noto Sans CJK SC",
    "Noto Sans Mono CJK SC",
    "Noto Sans SC",
    "Noto Sans CJK TC",
    "Noto Sans Mono CJK TC",
    "Noto Sans TC",
    "Noto Sans CJK KR",
    "Noto Sans Mono CJK KR",
    "Noto Sans KR",
    "Droid Sans Hangul",
    "Noto Sans CJK JP",
    "Noto Sans Mono CJK JP",
    "Noto Sans JP",
    "Droid Sans Japanese",
    "MotoyaLMaru",
    "NanumGothic",
    "Droid Sans",
    "Droid Sans Fallback",
    "DroidSansFallback"

    I most certainly want to improve support for system fonts but once again there is nothing magical here as it requires knowledge of what fonts are on the system and searching these fonts to find the requested characters. In all cases, we have to load those font files to search them.

    From an efficiency point of view, it is most efficient if you have a list of all characters contained in your project broken out by language or language groups where you can either prepare your font assets ahead of time (static or dynamic or mixture of both) or with knowledge of the characters used in the project, know what fonts are available on the platforms you are targeting so you can create font assets at runtime from those system fonts as needed.

    Knowing what is available on the platforms you are targeting allows you to carefully select fonts that you will work with your design as opposed to characters coming from some random font file.

    One of the things, I am consider is enabling users to define an ordered list of system font files per platform (potentially the path to these files). This list could be pre-populated by default but still allow you to modify it for your own needs.

    I am still thinking / evolving this so please provide your thoughts.
     
    Last edited: Jul 9, 2020
  25. NikitaLikhoded

    NikitaLikhoded

    Joined:
    Mar 6, 2019
    Posts:
    3
    @Stephan_B thanks for detailed answer. Can you please provide list of fonts that Unity use for fallback on iOS?
     
  26. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Let me look for that tomorrow but most likely you can find this in the apple developer documentation.
     
    saunity and NikitaLikhoded like this.
  27. abcjjy

    abcjjy

    Joined:
    Mar 6, 2015
    Posts:
    35
    @Stephan_B That's a good idea. It is better to use system font names instead of font file paths. For now, the only thing that prevents me from migrating to TMP is system font support. It is crucial for apps supporting CJK languages and user generated contents. It is cumbersome to include all the font atlas or ttfs in app. This is the last missing piece before fully deprecating the legacy Text component.

    Dynamic font atlas is neither efficient nor elegant. But it is convenient and easy to use. In fact, I prefer to use dynamic font by default. As long as its efficiency is not worse than the legacy Text, it is OK in most cases.

    TMP is superior over legacy Text in almost every aspect. Why do we still have to use the legacy Text?
     
    NikitaLikhoded likes this.
  28. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I have fixed the issue with GetPathsToOSFonts() where it will now work as expected on iOS and other platforms.

    This fix will require a new version of Unity. I will backport this fix all the way to 2018.4.

    Here is an example implementation to create dynamic font assets from OS Fonts.

    Code (csharp):
    1.  
    2. // Get the file path of all OS fonts.
    3. string[] fontPaths = Font.GetPathsToOSFonts();// Create new Font using one of those paths.
    4.  
    5. Font osFont = new Font(fontPaths[124]);// Create new font asset using this OS font.
    6.  
    7. TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(osFont);
    8.  
    The above currently works on all platforms with the exception of iOS which will be fixed when the change lands in an updated version of Unity.

    I will explore making another change to allow dynamic font asset to be created directly from the font file path skipping having to create a font object. I will provide an update on this once I have more information.
     
    oharinth, carlossaoud, Thaina and 2 others like this.
  29. GustavNinja

    GustavNinja

    Joined:
    Jun 13, 2016
    Posts:
    96
    In which future release of 2018.4 will this be released in? Currently exploring this option for our game on ios, android and webgl.
     
  30. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Mostly likely in 2018.4.26 since I believe 25 is close to being released.
     
    GustavNinja and NikitaLikhoded like this.
  31. liuzhiqiang_unity

    liuzhiqiang_unity

    Joined:
    Feb 11, 2020
    Posts:
    1
    In which future release of 2019.4 will this be released in?
     
  32. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Unity 2019.4.7f1
     
    celikomerdev likes this.
  33. thiagolrosa

    thiagolrosa

    Joined:
    Feb 22, 2017
    Posts:
    60
    What about 2019.2?
     
  34. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    These changes are contained in Unity itself and since there are no more release of 2019.2 or 2019.3, these changes are only present in 2018.4 (LTS), 2019.4 (LTS) and 2020.x.
     
    NikitaLikhoded likes this.
  35. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Thanks for the help on this thread. Can you please mention what is the fallback search order for iOS (is it something used by Unity or something that is enforced by the OS ?)

    Also - what is the expected performance "degradation" in case i define all system OS fonts used internally by Unity as "fallback fonts" ?
     
    srebelo-glu and NikitaLikhoded like this.
  36. felipe-shida-playkids

    felipe-shida-playkids

    Joined:
    Sep 16, 2020
    Posts:
    13
    We are trying to use this strategy to load a system font in case the player is unable to download our asset bundle containing the font assets for Asian languages. We managed to get all OS fonts using
    the
    Code (CSharp):
    1. Font.GetPathsToOSFonts()
    method, but we wanted to find a font that could render characters from the target language.

    We tried using the
    Code (CSharp):
    1. Font.HasCharacter('SOME_KANJI')
    method to try to find if the Font represented those unicode characters, but every call to HasCharacter returned false. If we load the font as a DynamicFont hasCharacter always return true, even for western fonts.

    Is there a smart way for knowing if a ttf/ttc font file can render a specific set of characters? So we can chose it as our fallback asian font?

    Currently we are just checking if the font filename contains the string "CJK" and it works most of the time, since android almost always have a NotoSansCJK installed.
     
  37. chy_qiujunqi

    chy_qiujunqi

    Joined:
    Jun 10, 2021
    Posts:
    1
    Hi,Can you please provide list of fonts that Unity use for fallback on Both iOS and Android
     
  38. saunity

    saunity

    Joined:
    Jul 3, 2014
    Posts:
    2
    @Stephan_B Thank you for your help on this thread, It's really helpful. : )
    I am also looking for ordered list of system fallback fonts Unity uses for legacy Unity Text (Unity 2019.4). It would be really helpful if you can share this ordered list for iOS and Android(If it has changed from what you have posted above).
     
  39. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    233
    Hi! Please update script reference to let users know that they can pass PATH to Font constructor.
    https://docs.unity3d.com/ScriptReference/Font-ctor.html