Skip to content

Commit 5e1d210

Browse files
authored
Merge pull request #31 from discord/i18n-fallback-testing
Alter font loading code to support fallback fonts in `fontFamily`
2 parents 06a915c + bfd630b commit 5e1d210

File tree

3 files changed

+118
-11
lines changed

3 files changed

+118
-11
lines changed

Libraries/Text/RCTTextAttributes.m

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,52 @@ - (NSParagraphStyle *)effectiveParagraphStyle
192192

193193
- (UIFont *)effectiveFont
194194
{
195-
// FIXME: RCTFont has thread-safety issues and must be rewritten.
196-
return [RCTFont updateFont:nil
197-
withFamily:_fontFamily
198-
size:@(isnan(_fontSize) ? 0 : _fontSize)
199-
weight:_fontWeight
200-
style:_fontStyle
201-
variant:_fontVariant
202-
scaleMultiplier:self.effectiveFontSizeMultiplier];
195+
NSArray *rawFontFamilies = [_fontFamily componentsSeparatedByString:@","];
196+
197+
// If _fontFamily is nil or has a single fontFamily, then use the original RN logic.
198+
if (rawFontFamilies.count <= 1) {
199+
// FIXME: RCTFont has thread-safety issues and must be rewritten.
200+
return [RCTFont updateFont:nil
201+
withFamily:_fontFamily
202+
size:@(isnan(_fontSize) ? 0 : _fontSize)
203+
weight:_fontWeight
204+
style:_fontStyle
205+
variant:_fontVariant
206+
scaleMultiplier:self.effectiveFontSizeMultiplier];
207+
}
208+
209+
NSMutableArray *fonts = [NSMutableArray new];
210+
for (NSString *rawFontFamily in rawFontFamilies) {
211+
NSString *fontFamily = [rawFontFamily stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
212+
if (fontFamily.length == 0) {
213+
continue;
214+
}
215+
216+
UIFont *font = [RCTFont updateFont:nil
217+
withFamily:fontFamily
218+
size:@(isnan(_fontSize) ? 0 : _fontSize)
219+
weight:_fontWeight
220+
style:_fontStyle
221+
variant:_fontVariant
222+
scaleMultiplier:self.effectiveFontSizeMultiplier];
223+
224+
if (font) {
225+
[fonts addObject:font];
226+
}
227+
}
228+
229+
UIFont *primaryFont = fonts[0];
230+
231+
NSMutableArray *fontDescriptors = [NSMutableArray new];
232+
for (NSUInteger i = 1; i < fonts.count; i++) {
233+
UIFont *font = fonts[i];
234+
[fontDescriptors addObject:font.fontDescriptor];
235+
}
236+
237+
UIFontDescriptor *fontDescriptor = [primaryFont.fontDescriptor fontDescriptorByAddingAttributes:
238+
@{UIFontDescriptorCascadeListAttribute: fontDescriptors}];
239+
240+
return [UIFont fontWithDescriptor:fontDescriptor size:primaryFont.pointSize];
203241
}
204242

205243
- (CGFloat)effectiveFontSizeMultiplier

ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ public String getName() {
108108

109109
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
110110
private boolean getIsReduceMotionEnabledValue() {
111-
float defaultAnimationScale = Float.parseFloat(Settings.Global.TRANSITION_ANIMATION_SCALE);
112-
float animationScale = Settings.Global.getFloat(mContentResolver, defaultAnimationScale);
113-
return animationScale == 0f;
111+
String value =
112+
Settings.Global.getString(mContentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE);
113+
114+
return value != null && value.equals("0.0");
114115
}
115116

116117
@Override

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@
1010
import android.content.Context;
1111
import android.content.res.AssetManager;
1212
import android.graphics.Typeface;
13+
import android.graphics.fonts.Font;
14+
import android.graphics.fonts.FontFamily;
15+
import android.os.Build;
1316
import android.util.SparseArray;
1417
import androidx.annotation.Nullable;
18+
import androidx.annotation.RequiresApi;
1519
import androidx.core.content.res.ResourcesCompat;
1620
import com.facebook.infer.annotation.Nullsafe;
21+
22+
import java.io.IOException;
23+
import java.util.ArrayList;
1724
import java.util.HashMap;
25+
import java.util.List;
1826
import java.util.Map;
1927

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

133141
private static Typeface createAssetTypeface(
134142
String fontFamilyName, int style, AssetManager assetManager) {
143+
// This logic attempts to safely check if the frontend code is attempting to use
144+
// fallback fonts, and if it is, to use the fallback typeface creation logic.
145+
String[] fontFamilyNames = fontFamilyName != null ? fontFamilyName.split(",") : null;
146+
if (fontFamilyNames != null) {
147+
for (int i = 0; i < fontFamilyNames.length; i++) {
148+
fontFamilyNames[i] = fontFamilyNames[i].trim();
149+
}
150+
}
151+
152+
// If there are multiple font family names:
153+
// For newer versions of Android, construct a Typeface with fallbacks
154+
// For older versions of Android, ignore all the fallbacks and just use the first font family
155+
if (fontFamilyNames != null && fontFamilyNames.length > 1) {
156+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
157+
return createAssetTypefaceWithFallbacks(fontFamilyNames, style, assetManager);
158+
} else {
159+
fontFamilyName = fontFamilyNames[0];
160+
}
161+
}
162+
163+
// Lastly, after all those checks above, this is the original RN logic for
164+
// getting the typeface.
135165
String extension = EXTENSIONS[style];
136166
for (String fileExtension : FILE_EXTENSIONS) {
137167
String fileName =
@@ -151,6 +181,44 @@ private static Typeface createAssetTypeface(
151181
return Typeface.create(fontFamilyName, style);
152182
}
153183

184+
@RequiresApi(api = Build.VERSION_CODES.Q)
185+
private static Typeface createAssetTypefaceWithFallbacks(
186+
String[] fontFamilyNames, int style, AssetManager assetManager) {
187+
List<FontFamily> fontFamilies = new ArrayList<>();
188+
189+
// Iterate over the list of fontFamilyNames, constructing new FontFamily objects
190+
// for use in the CustomFallbackBuilder below.
191+
for (String fontFamilyName : fontFamilyNames) {
192+
String extension = EXTENSIONS[style];
193+
for (String fileExtension : FILE_EXTENSIONS) {
194+
String fileName =
195+
new StringBuilder()
196+
.append(FONTS_ASSET_PATH)
197+
.append(fontFamilyName)
198+
.append(extension)
199+
.append(fileExtension)
200+
.toString();
201+
try {
202+
Font font = new Font.Builder(assetManager, fileName).build();
203+
FontFamily family = new FontFamily.Builder(font).build();
204+
fontFamilies.add(family);
205+
} catch (RuntimeException e) {
206+
// If the typeface asset does not exist, try another extension.
207+
continue;
208+
} catch (IOException e) {
209+
// If the font asset does not exist, try another extension.
210+
continue;
211+
}
212+
}
213+
}
214+
215+
Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(fontFamilies.get(0));
216+
for (int i = 1; i < fontFamilies.size(); i++) {
217+
fallbackBuilder.addCustomFallback(fontFamilies.get(i));
218+
}
219+
return fallbackBuilder.build();
220+
}
221+
154222
/** Responsible for caching typefaces for each custom font family. */
155223
private static class AssetFontFamily {
156224

0 commit comments

Comments
 (0)