1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.text.font
18 
19 import android.content.Context
20 import android.graphics.Typeface
21 import androidx.annotation.RestrictTo
22 import androidx.compose.runtime.State
23 import androidx.compose.ui.text.InternalTextApi
24 import kotlin.coroutines.CoroutineContext
25 
26 /**
27  * Create a new fontFamilyResolver for use outside of composition context
28  *
29  * Example usages:
30  * - Application.onCreate to preload fonts
31  * - Creating Paragraph objects on background thread
32  *
33  * Usages inside of Composition should use LocalFontFamilyResolver.current
34  *
35  * All instances of FontFamily.Resolver created by [createFontFamilyResolver] share the same
36  * typeface caches.
37  */
createFontFamilyResolvernull38 fun createFontFamilyResolver(context: Context): FontFamily.Resolver {
39     return FontFamilyResolverImpl(
40         AndroidFontLoader(context),
41         AndroidFontResolveInterceptor(context)
42     )
43 }
44 
45 /**
46  * Create a new fontFamilyResolver for use outside of composition context with a coroutine context.
47  *
48  * Example usages:
49  * - Application.onCreate to preload fonts
50  * - Creating Paragraph objects on background thread
51  * - Configuring LocalFontFamilyResolver with a different CoroutineScope
52  *
53  * Usages inside of Composition should use LocalFontFamilyResolver.current
54  *
55  * Any [kotlinx.coroutines.CoroutineExceptionHandler] provided will be called with exceptions
56  * related to fallback font loading. These exceptions are not fatal, and indicate that font fallback
57  * continued to the next font load.
58  *
59  * If no [kotlinx.coroutines.CoroutineExceptionHandler] is provided, a default implementation will
60  * be added that ignores all exceptions.
61  *
62  * All instances of FontFamily.Resolver created by [createFontFamilyResolver] share the same
63  * typeface caches.
64  *
65  * @param context Android context for resolving fonts
66  * @param coroutineContext context to launch async requests in during resolution.
67  */
createFontFamilyResolvernull68 fun createFontFamilyResolver(
69     context: Context,
70     coroutineContext: CoroutineContext
71 ): FontFamily.Resolver {
72     return FontFamilyResolverImpl(
73         AndroidFontLoader(context),
74         AndroidFontResolveInterceptor(context),
75         GlobalTypefaceRequestCache,
76         FontListFontFamilyTypefaceAdapter(GlobalAsyncTypefaceCache, coroutineContext)
77     )
78 }
79 
80 /**
81  * Create a FontFamily.resolver that does not share a cache with other FontFamily.Resolvers.
82  *
83  * This is primarily useful for testing or benchmarking.
84  */
85 @InternalTextApi // exposed for benchmarking, not a stable API.
86 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
emptyCacheFontFamilyResolvernull87 fun emptyCacheFontFamilyResolver(context: Context): FontFamily.Resolver {
88     return FontFamilyResolverImpl(
89         AndroidFontLoader(context),
90         typefaceRequestCache = TypefaceRequestCache(),
91         fontListFontFamilyTypefaceAdapter = FontListFontFamilyTypefaceAdapter(AsyncTypefaceCache())
92     )
93 }
94 
95 /**
96  * Resolve a font to an Android Typeface
97  *
98  * On Android, font resolution always produces an [android.graphics.Typeface].
99  *
100  * This convenience method converts State<Any> to State<Typeface> to avoid casting the result.
101  *
102  * @param fontFamily fontFamily to resolve from
103  * @param fontWeight font weight to resolve in [fontFamily], will use closest match if not exact
104  * @param fontStyle italic or upright text, to resolve in [fontFamily]
105  * @param fontSynthesis allow font synthesis if [fontFamily] or [fontStyle] don't have an exact
106  *   match. This will allow "fake bold" (drawing with too wide a brush) and "fake italic" (drawing
107  *   then skewing) to be applied when no exact match is present for the weight and style.
108  */
FontFamilynull109 fun FontFamily.Resolver.resolveAsTypeface(
110     fontFamily: FontFamily? = null,
111     fontWeight: FontWeight = FontWeight.Normal,
112     fontStyle: FontStyle = FontStyle.Normal,
113     fontSynthesis: FontSynthesis = FontSynthesis.All
114 ): State<Typeface> {
115     // this unchecked cast is done here to avoid callers having to do it at every call site
116     @Suppress("UNCHECKED_CAST")
117     return resolve(fontFamily, fontWeight, fontStyle, fontSynthesis) as State<Typeface>
118 }
119