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