1 /*
2  * Copyright 2021 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 @file:JvmName("AndroidFontKt")
18 
19 package androidx.compose.ui.text.font
20 
21 import android.content.Context
22 import android.content.res.AssetManager
23 import android.graphics.Typeface
24 import android.os.ParcelFileDescriptor
25 import androidx.annotation.RequiresApi
26 import androidx.compose.runtime.Stable
27 import java.io.File
28 
29 /**
30  * Create a Font declaration from a file in the assets directory. The content of the [File] is read
31  * during construction.
32  *
33  * @param path full path starting from the assets directory (i.e. dir/myfont.ttf for
34  *   assets/dir/myfont.ttf).
35  * @param assetManager Android AssetManager
36  * @param weight The weight of the font. The system uses this to match a font to a font request that
37  *   is given in a [androidx.compose.ui.text.SpanStyle].
38  * @param style The style of the font, normal or italic. The system uses this to match a font to a
39  *   font request that is given in a [androidx.compose.ui.text.SpanStyle].
40  * @param variationSettings on API 26 and above these settings are applied to a variable font when
41  *   the font is loaded
42  */
43 @Stable
Fontnull44 fun Font(
45     path: String,
46     assetManager: AssetManager,
47     weight: FontWeight = FontWeight.Normal,
48     style: FontStyle = FontStyle.Normal,
49     variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
50 ): Font = AndroidAssetFont(assetManager, path, weight, style, variationSettings)
51 
52 /**
53  * Create a Font declaration from a file. The content of the [File] is read during construction.
54  *
55  * @param file the font file.
56  * @param weight The weight of the font. The system uses this to match a font to a font request that
57  *   is given in a [androidx.compose.ui.text.SpanStyle].
58  * @param style The style of the font, normal or italic. The system uses this to match a font to a
59  *   font request that is given in a [androidx.compose.ui.text.SpanStyle].
60  * @param variationSettings on API 26 and above these settings are applied to a variable font when
61  *   the font is loaded
62  */
63 @Stable
64 @Suppress("StreamFiles")
65 fun Font(
66     file: File,
67     weight: FontWeight = FontWeight.Normal,
68     style: FontStyle = FontStyle.Normal,
69     variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
70 ): Font = AndroidFileFont(file, weight, style, variationSettings)
71 
72 /**
73  * Create a Font declaration from a [ParcelFileDescriptor]. The content of the
74  * [ParcelFileDescriptor] is read during construction.
75  *
76  * @param fileDescriptor the file descriptor for the font file.
77  * @param weight The weight of the font. The system uses this to match a font to a font request that
78  *   is given in a [androidx.compose.ui.text.SpanStyle].
79  * @param style The style of the font, normal or italic. The system uses this to match a font to a
80  *   font request that is given in a [androidx.compose.ui.text.SpanStyle].
81  * @param variationSettings these settings are applied to a variable font when the font is loaded
82  */
83 @RequiresApi(26)
84 @Stable
85 fun Font(
86     fileDescriptor: ParcelFileDescriptor,
87     weight: FontWeight = FontWeight.Normal,
88     style: FontStyle = FontStyle.Normal,
89     variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
90 ): Font = AndroidFileDescriptorFont(fileDescriptor, weight, style, variationSettings)
91 
92 /**
93  * Font for use on Android.
94  *
95  * All [AndroidFont] produce an [android.graphics.Typeface] which may be used to draw text on
96  * Android. This is the main low-level API for introducing a new Font description to Compose on
97  * Android for both blocking and async load.
98  *
99  * You may subclass this to add new types of font descriptions that may be used in
100  * [FontListFontFamily]. For example, you can add a [FontLoadingStrategy.Blocking] font that returns
101  * a Typeface from a local resource not supported by an existing [Font]. Or, you can create an
102  * [FontLoadingStrategy.Async] font that loads a font file from your server.
103  *
104  * When introducing new font descriptors, it is recommended to follow the patterns of providing a
105  * public Font constructor and a private implementation class:
106  * 1. Declare an internal or private subclass of AndroidFont
107  * 2. Expose a public Font(...) constructor that returns your new type.
108  *
109  * Font constructors are
110  * 1. Regular functions named `Font` that return type `Font`
111  * 2. The first argument is the font name, or similar object that describes the font uniquely
112  * 3. If the font has a provider, loader, or similar argument, put it after the font name.
113  * 4. The last two arguments are FontWeight and FontStyle.
114  *
115  * Examples of Font constructors:
116  * ```
117  * fun Font("myIdentifier", MyFontLoader, FontWeight, FontStyle): Font
118  * fun Font(CustomFontDescription(...), MyFontLoader, FontWeight, FontStyle): Font
119  * fun Font(CustomFontDescription(...), FontWeight, FontStyle): Font
120  * ```
121  *
122  * @param loadingStrategy loadingStrategy this font will provide in fallback chains
123  * @param typefaceLoader a loader that knows how to load this [AndroidFont], may be shared between
124  *   several fonts
125  */
126 abstract class AndroidFont
127 constructor(
128     final override val loadingStrategy: FontLoadingStrategy,
129     val typefaceLoader: TypefaceLoader,
130     variationSettings: FontVariation.Settings,
131 ) : Font {
132 
133     @Deprecated(
134         "Replaced with fontVariation constructor",
135         ReplaceWith("AndroidFont(loadingStrategy, typefaceLoader, FontVariation.Settings())")
136     )
137     constructor(
138         loadingStrategy: FontLoadingStrategy,
139         typefaceLoader: TypefaceLoader,
140     ) : this(loadingStrategy, typefaceLoader, FontVariation.Settings())
141 
142     /**
143      * The settings that will be applied to this font, if supported by the font.
144      *
145      * If the font does not support a [FontVariation.Setting], it has no effect.
146      *
147      * Subclasses are required to apply these variation settings during font loading path on
148      * appropriate API levels, for example by using [Typeface.Builder.setFontVariationSettings].
149      *
150      * Subclasses may safely apply all variation settings without querying the font file. Android
151      * will ignore any unsupported axis.
152      */
153     val variationSettings: FontVariation.Settings = variationSettings
154 
155     /**
156      * Loader for loading an [AndroidFont] and producing an [android.graphics.Typeface].
157      *
158      * This interface is not intended to be used by application developers for text display. To load
159      * a typeface for display use [FontFamily.Resolver].
160      *
161      * [TypefaceLoader] allows the introduction of new types of font descriptors for use in
162      * [FontListFontFamily]. A [TypefaceLoader] allows a new subclass of [AndroidFont] to be used by
163      * normal compose text rendering methods.
164      *
165      * Examples of new types of fonts that [TypefaceLoader] can add:
166      * - [FontLoadingStrategy.Blocking] [Font] that loads Typeface from a local resource not
167      *   supported by an existing font
168      * - [FontLoadingStrategy.OptionalLocal] [Font] that "loads" a platform Typeface only available
169      *   on some devices.
170      * - [FontLoadingStrategy.Async] [Font] that loads a font from a backend via a network request.
171      *
172      * During resolution from [FontFamily.Resolver], an [AndroidFont] subclass will be queried for
173      * an appropriate loader.
174      *
175      * The loader attached to an instance of an [AndroidFont] is only required to be able to load
176      * that instance, though it is advised to create one loader for all instances of the same
177      * subclass and share them between [AndroidFont] instances to avoid allocations or allow
178      * caching.
179      */
180     interface TypefaceLoader {
181         /**
182          * Immediately load the font in a blocking manner such that it will be available this frame.
183          *
184          * This method will be called on a UI-critical thread, however it has been determined that
185          * this font is required for the current frame. This method is allowed to perform small
186          * amounts of I/O to load a font file from disk.
187          *
188          * This method should never perform expensive I/O operations, such as loading from a remote
189          * source. If expensive operations are required to complete the font, this method may choose
190          * to throw. Note that this method will never be called for fonts with
191          * [FontLoadingStrategy.Async].
192          *
193          * It is possible for [loadBlocking] to be called for the same instance of [AndroidFont] in
194          * parallel. Implementations should support parallel concurrent loads, or de-dup.
195          *
196          * @param context current Android context for loading the font
197          * @param font the font to load which contains this loader as [AndroidFont.typefaceLoader]
198          * @return [android.graphics.Typeface] for loaded font, or null if the font fails to load
199          */
200         fun loadBlocking(context: Context, font: AndroidFont): Typeface?
201 
202         /**
203          * Asynchronously load the font, from either local or remote sources such that it will cause
204          * text reflow when loading completes.
205          *
206          * This method will be called on a UI-critical thread, and should not block the thread for
207          * font loading from sources slower than the local filesystem. More expensive loads should
208          * dispatch to an appropriate thread.
209          *
210          * This method is always called in a timeout context and must return it's final value within
211          * 15 seconds. If the Typeface is not resolved within 15 seconds, the async load is
212          * cancelled and considered a permanent failure. Implementations should use structured
213          * concurrency to cooperatively cancel work.
214          *
215          * Compose does not know what resources are required to satisfy a font load. Subclasses
216          * implementing [FontLoadingStrategy.Async] behavior should ensure requests are de-duped for
217          * the same resource.
218          *
219          * It is possible for [awaitLoad] to be called for the same instance of [AndroidFont] in
220          * parallel. Implementations should support parallel concurrent loads, or de-dup.
221          *
222          * @param context current Android context for loading the font
223          * @param font the font to load which contains this loader as [AndroidFont.typefaceLoader]
224          * @return [android.graphics.Typeface] for loaded font, or null if not available
225          */
226         suspend fun awaitLoad(context: Context, font: AndroidFont): Typeface?
227     }
228 }
229 
230 // keep generating AndroidFontKt to avoid API change
generateAndroidFontKtForApiCompatibilitynull231 private fun generateAndroidFontKtForApiCompatibility() {}
232