Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No system fonts fallback on Android 12 #2303

Closed
rwrx opened this issue Nov 12, 2021 · 11 comments · Fixed by #2306
Closed

No system fonts fallback on Android 12 #2303

rwrx opened this issue Nov 12, 2021 · 11 comments · Fixed by #2306

Comments

@rwrx
Copy link
Contributor

rwrx commented Nov 12, 2021

A user of our app reported that on Android 12 labels with Chinese characters are not shown on the map. I have checked it and unfortunately, it is the case that on Android 12 (Pixel 3a device) labels with Chinese characters are not shown. On Android 11 and older it is working properly. I have also tried to target Android 12 in our app and it did not helped. I have not digged into Tangram-ES what could be the case yet, I am just reporting it now. I am also attaching screenshots from our app (Walkabout style) and I have also noticed that it is not working in StreetComplete app too (@westnordost). Screenshots are showing Taiwan.

gpxviewerpro
streetcomplete

@tallytalwar
Copy link
Member

If I remember correctly we access system font files for part of our label rendering pipeline. Since it's working on android 11 but not on android 12, it would be good to verify if font file (fallback) exists and at the correct location on android 12. Maybe comparing font.xml on 11 and 12 should provide some hint.

@rwrx
Copy link
Contributor Author

rwrx commented Nov 16, 2021

@tallytalwar thank you a lot. I have checked fonts.xml on Android 11 (Samsung Galaxy Tab A) and on Android 12 (Google Pixel 3a) and there is a difference. It seems that parser cannot parse fonts.xml on Android 12 because it contains text and also inner tags:

<familyset version="23">
    <!-- first font is default -->
    <family name="sans-serif">
    <font weight="100" style="normal">Roboto-Regular.ttf
      <axis tag="ital" stylevalue="0" />
      <axis tag="wdth" stylevalue="100" />
      <axis tag="wght" stylevalue="100" />
    </font>
    ...

this is from Android 11:

<familyset version="23">
    <!-- first font is default -->
    <family name="sans-serif">
      <font weight="100" style="normal">Roboto-Thin.ttf</font>
      ...

Here are complete fonts.xml files:

Google Pixel 3a (Android 12) fonts.xml:
https://gist.github.com/rwrx/a1e16c0ce834b27d3855df40eab32f7d

Samsung Galaxy Tab A (Android 11) fonts.xml:
https://gist.github.com/rwrx/c6000e5187641fbb5d5fbf529e97190c

I am not sure what do to with this, as I have discovered that FontConfig.java file which is parsing this fonts.xml file is using XmlPullParser and it throws exception due to this (incorrect?) fonts.xml file.

@rwrx
Copy link
Contributor Author

rwrx commented Nov 16, 2021

Method nextText() of XmlPullParser throws exception on line:

with message:
END_TAG expected (position:START_TAG (empty) <axis tag='ital' stylevalue='0'>@28:45 in java.io.InputStreamReader@47f8a2)

In Android documentation for nextText() - https://developer.android.com/reference/org/xmlpull/v1/XmlPullParser#nextText() method there is written This function together with nextTag make it very easy to parse XML that has no mixed content.. So basically it fails to parse fonts.xml because of mixed content inside <font> tag.

@matteblair
Copy link
Member

Our approach for locating system fonts has been unfortunately limited by the fact that the font XML files are both un-documented and non-standardized. App developers are not supposed to parse them. Our code is able to handle many font XML files, but it often breaks when new Android versions are released.

Fortunately, in API level 29 Android added an interface for accessing system fonts programmatically: https://developer.android.com/reference/android/graphics/fonts/SystemFonts We still want to support older versions of Android, but when Tangram ES is running on a new enough version we could use this API to locate the available system fonts (including CJK fonts) without parsing XML at all.

This change will require some time to implement and test. In the meantime, you can work around these font errors by bundling your own font with support for Chinese scripts and referencing it in your Tangram scene: https://tangrams.readthedocs.io/en/main/Syntax-Reference/fonts/

@rwrx
Copy link
Contributor Author

rwrx commented Nov 16, 2021

Thank you. Now I can see that this is not that easy to implement. I have tried to make use of SystemFonts, but unfortunately I cannot find a way to get importance of font fallbacks. It is only returning system fonts.

At least I have tried to modify FontConfig.getFontFallback() method:

public static synchronized String getFontFallback(final int importance, final int weightHint) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        String fallback = "";

        if (importance == 0) {
            Set<Font> fonts = SystemFonts.getAvailableFonts();

            for (Font font : fonts) {
                String filePath = font.getFile().getAbsolutePath();

                if (filePath.toLowerCase().contains("cjk") && filePath.toLowerCase().contains("sans") && font.getStyle().getWeight() == weightHint) {
                    fallback = filePath;
                    break;
                }
            }
        }

        return fallback;
    }
    else {
        if (!initialized) {
            init();
        }

        int diffWeight = Integer.MAX_VALUE;
        String fallback = "";

        for (int i = 0; i < fallbackFontDict.size(); ++i) {
            int diff = Math.abs(fallbackFontDict.keyAt(i) - weightHint);

            if (diff < diffWeight) {
                final List<String> fallbacks = fallbackFontDict.valueAt(i);

                if (importance < fallbacks.size()) {
                    fallback = fallbacks.get(importance);
                    diffWeight = diff;
                }
            }
        }

        return fallback;
    }
}

But it is really a "hack" and I assume that it will work only on my device model Google Pixel 3a or in better case on all models from Google or Android One. @matteblair do you have some hints on how to improve this "hack"?

@westnordost
Copy link
Contributor

Users of StreetComplete report that no text is displayed at all on Android 12. I use a style which sets the font family of every label to a custom font ("Roboto", Android's default font). If this is indeed the same bug, I'd propose to change the title.

@rwrx
Copy link
Contributor Author

rwrx commented Nov 17, 2021

Yes, to me it seems that it is the same bug. Maybe changing title to something like "No system fonts fallback on Android 12"?

@rwrx rwrx changed the title Labels with Chinese characters are not shown on Android 12 No system fonts fallback on Android 12 Nov 19, 2021
@matteblair
Copy link
Member

Sorry I've been slow to follow up on this - my job has been keeping me busy!

Since the current approach to locating fonts is clearly causing problems on Android 12, I think the Android font loading logic should add a completely new code path for API level 29 or newer that builds both fontDict and fallbackFontDict using the new Font APIs instead of parsing XML.

I don't expect this to be terribly complicated but it will require a bit of time to implement and test.

@rwrx
Copy link
Contributor Author

rwrx commented Nov 28, 2021

@matteblair thank you for your answer. Is there some way I can help with this to be implemented sooner?

@matteblair
Copy link
Member

I've started working on this. Unfortunately there is more to the problem than I initially understood. I've run into two issues:

  1. The SystemFonts API doesn't have an obvious way to determine which fonts should be treated as "fallbacks" and what order they should be checked. This information was previously determined by the fonts.xml structure. For some fonts it's possible to look for strings like "CJK" in the file name, but this is incomplete and not robust.
  2. In Android 12 the system fonts no longer use separate files for weight variations like "light" and "bold". Instead, these variations are encoded in a single font file using "variation axes". Tangram's font code doesn't currently handle font variation axes, and until it does we won't be able to load "light" or "bold" versions of any system fonts. This is less important than getting basic text rendering working for all languages, but still needs to be addressed.

I'm still researching answers to these problems - just wanted to give a status update.

@matteblair
Copy link
Member

@rwrx @westnordost This issue was auto-closed because I believe #2306 resolves it, but please give this latest change a try and let me know what you see!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants