Skip to content

Commit

Permalink
Merge pull request #31 from discord/i18n-fallback-testing
Browse files Browse the repository at this point in the history
Alter font loading code to support fallback fonts in `fontFamily`
  • Loading branch information
stevenpetryk authored Nov 15, 2022
2 parents 06a915c + bfd630b commit 5e1d210
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 11 deletions.
54 changes: 46 additions & 8 deletions Libraries/Text/RCTTextAttributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,52 @@ - (NSParagraphStyle *)effectiveParagraphStyle

- (UIFont *)effectiveFont
{
// FIXME: RCTFont has thread-safety issues and must be rewritten.
return [RCTFont updateFont:nil
withFamily:_fontFamily
size:@(isnan(_fontSize) ? 0 : _fontSize)
weight:_fontWeight
style:_fontStyle
variant:_fontVariant
scaleMultiplier:self.effectiveFontSizeMultiplier];
NSArray *rawFontFamilies = [_fontFamily componentsSeparatedByString:@","];

// If _fontFamily is nil or has a single fontFamily, then use the original RN logic.
if (rawFontFamilies.count <= 1) {
// FIXME: RCTFont has thread-safety issues and must be rewritten.
return [RCTFont updateFont:nil
withFamily:_fontFamily
size:@(isnan(_fontSize) ? 0 : _fontSize)
weight:_fontWeight
style:_fontStyle
variant:_fontVariant
scaleMultiplier:self.effectiveFontSizeMultiplier];
}

NSMutableArray *fonts = [NSMutableArray new];
for (NSString *rawFontFamily in rawFontFamilies) {
NSString *fontFamily = [rawFontFamily stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (fontFamily.length == 0) {
continue;
}

UIFont *font = [RCTFont updateFont:nil
withFamily:fontFamily
size:@(isnan(_fontSize) ? 0 : _fontSize)
weight:_fontWeight
style:_fontStyle
variant:_fontVariant
scaleMultiplier:self.effectiveFontSizeMultiplier];

if (font) {
[fonts addObject:font];
}
}

UIFont *primaryFont = fonts[0];

NSMutableArray *fontDescriptors = [NSMutableArray new];
for (NSUInteger i = 1; i < fonts.count; i++) {
UIFont *font = fonts[i];
[fontDescriptors addObject:font.fontDescriptor];
}

UIFontDescriptor *fontDescriptor = [primaryFont.fontDescriptor fontDescriptorByAddingAttributes:
@{UIFontDescriptorCascadeListAttribute: fontDescriptors}];

return [UIFont fontWithDescriptor:fontDescriptor size:primaryFont.pointSize];
}

- (CGFloat)effectiveFontSizeMultiplier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ public String getName() {

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private boolean getIsReduceMotionEnabledValue() {
float defaultAnimationScale = Float.parseFloat(Settings.Global.TRANSITION_ANIMATION_SCALE);
float animationScale = Settings.Global.getFloat(mContentResolver, defaultAnimationScale);
return animationScale == 0f;
String value =
Settings.Global.getString(mContentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE);

return value != null && value.equals("0.0");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.os.Build;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.res.ResourcesCompat;
import com.facebook.infer.annotation.Nullsafe;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -132,6 +140,28 @@ public void setTypeface(String fontFamilyName, int style, Typeface typeface) {

private static Typeface createAssetTypeface(
String fontFamilyName, int style, AssetManager assetManager) {
// This logic attempts to safely check if the frontend code is attempting to use
// fallback fonts, and if it is, to use the fallback typeface creation logic.
String[] fontFamilyNames = fontFamilyName != null ? fontFamilyName.split(",") : null;
if (fontFamilyNames != null) {
for (int i = 0; i < fontFamilyNames.length; i++) {
fontFamilyNames[i] = fontFamilyNames[i].trim();
}
}

// If there are multiple font family names:
// For newer versions of Android, construct a Typeface with fallbacks
// For older versions of Android, ignore all the fallbacks and just use the first font family
if (fontFamilyNames != null && fontFamilyNames.length > 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return createAssetTypefaceWithFallbacks(fontFamilyNames, style, assetManager);
} else {
fontFamilyName = fontFamilyNames[0];
}
}

// Lastly, after all those checks above, this is the original RN logic for
// getting the typeface.
String extension = EXTENSIONS[style];
for (String fileExtension : FILE_EXTENSIONS) {
String fileName =
Expand All @@ -151,6 +181,44 @@ private static Typeface createAssetTypeface(
return Typeface.create(fontFamilyName, style);
}

@RequiresApi(api = Build.VERSION_CODES.Q)
private static Typeface createAssetTypefaceWithFallbacks(
String[] fontFamilyNames, int style, AssetManager assetManager) {
List<FontFamily> fontFamilies = new ArrayList<>();

// Iterate over the list of fontFamilyNames, constructing new FontFamily objects
// for use in the CustomFallbackBuilder below.
for (String fontFamilyName : fontFamilyNames) {
String extension = EXTENSIONS[style];
for (String fileExtension : FILE_EXTENSIONS) {
String fileName =
new StringBuilder()
.append(FONTS_ASSET_PATH)
.append(fontFamilyName)
.append(extension)
.append(fileExtension)
.toString();
try {
Font font = new Font.Builder(assetManager, fileName).build();
FontFamily family = new FontFamily.Builder(font).build();
fontFamilies.add(family);
} catch (RuntimeException e) {
// If the typeface asset does not exist, try another extension.
continue;
} catch (IOException e) {
// If the font asset does not exist, try another extension.
continue;
}
}
}

Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(fontFamilies.get(0));
for (int i = 1; i < fontFamilies.size(); i++) {
fallbackBuilder.addCustomFallback(fontFamilies.get(i));
}
return fallbackBuilder.build();
}

/** Responsible for caching typefaces for each custom font family. */
private static class AssetFontFamily {

Expand Down

0 comments on commit 5e1d210

Please sign in to comment.