Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to use fonts installed on the system #898

Merged
merged 4 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions compose/ui/ui-text/api/desktop/ui-text.api
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,16 @@ public final class androidx/compose/ui/text/platform/SkiaParagraph_skikoKt {
public static final fun toSkPlaceholderAlignment-do9X-Gg (I)Lorg/jetbrains/skia/paragraph/PlaceholderAlignment;
}

public final class androidx/compose/ui/text/platform/SystemFont : androidx/compose/ui/text/platform/PlatformFont {
public static final field $stable I
public synthetic fun <init> (Ljava/lang/String;Landroidx/compose/ui/text/font/FontWeight;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;Landroidx/compose/ui/text/font/FontWeight;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getIdentity ()Ljava/lang/String;
public fun getStyle-_-LCdwA ()I
public fun getWeight ()Landroidx/compose/ui/text/font/FontWeight;
public fun toString ()Ljava/lang/String;
}

public final class androidx/compose/ui/text/style/BaselineShift {
public static final field Companion Landroidx/compose/ui/text/style/BaselineShift$Companion;
public static final synthetic fun box-impl (F)Landroidx/compose/ui/text/style/BaselineShift;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
package androidx.compose.ui.text.platform

import org.jetbrains.skia.Typeface as SkTypeface
import org.jetbrains.skia.FontStyle as SkFontStyle
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.font.*
import androidx.compose.ui.util.fastForEach
import java.io.File
import java.security.MessageDigest
import org.jetbrains.skia.Data
import org.jetbrains.skia.FontSlant
import org.jetbrains.skia.FontWidth
import org.jetbrains.skia.makeFromFile

actual sealed class PlatformFont : Font {
Expand Down Expand Up @@ -163,6 +164,7 @@ internal actual fun loadTypeface(font: Font): SkTypeface {
is ResourceFont -> typefaceResource(font.name)
is FileFont -> SkTypeface.makeFromFile(font.file.toString())
is LoadedFont -> SkTypeface.makeFromData(Data.makeFromBytes(font.data))
is SystemFont -> SkTypeface.makeFromName(font.identity, font.skFontStyle)
}
}

Expand All @@ -176,6 +178,12 @@ private fun typefaceResource(resourceName: String): SkTypeface {
return SkTypeface.makeFromData(Data.makeFromBytes(bytes))
}

private val Font.skFontStyle: SkFontStyle get() = SkFontStyle(
weight = weight.weight,
width = FontWidth.NORMAL,
slant = if (style == FontStyle.Italic) FontSlant.ITALIC else FontSlant.UPRIGHT
)

internal actual fun currentPlatform(): Platform {
val name = System.getProperty("os.name")
return when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.SkiaFontLoader
import androidx.compose.ui.text.platform.Font
import androidx.compose.ui.text.platform.GenericFontFamiliesMapping
import androidx.compose.ui.text.platform.Typeface
import androidx.compose.ui.text.platform.aliases
import com.google.common.truth.Truth
import org.jetbrains.skia.Data
import org.jetbrains.skia.Typeface
Expand Down Expand Up @@ -66,10 +66,10 @@ class DesktopFontTest {
@Test
fun ensureRegistered() {
Truth.assertThat(fontLoader.loadPlatformTypes(FontFamily.Cursive).aliases)
.isEqualTo(GenericFontFamiliesMapping[FontFamily.Cursive.name])
.isEqualTo(FontFamily.Cursive.aliases)

Truth.assertThat(fontLoader.loadPlatformTypes(FontFamily.Default).aliases)
.isEqualTo(GenericFontFamiliesMapping[FontFamily.SansSerif.name])
.isEqualTo(FontFamily.SansSerif.aliases)

Truth.assertThat(fontLoader.loadPlatformTypes(loadedFontFamily).aliases)
.isEqualTo(listOf("Sample Font"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ import org.jetbrains.skia.Typeface as SkTypeface
import androidx.compose.ui.text.Cache
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.ExpireAfterAccessCache
import androidx.compose.ui.text.WeakKeysCache
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.font.DefaultFontFamily
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontListFontFamily
import androidx.compose.ui.text.font.FontLoadingStrategy
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.font.GenericFontFamily
import androidx.compose.ui.text.font.LoadedFontFamily
import androidx.compose.ui.text.font.Typeface
import androidx.compose.ui.text.font.createFontFamilyResolver
import org.jetbrains.skia.FontMgr
import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.TypefaceFontProvider
Expand All @@ -31,6 +39,20 @@ expect sealed class PlatformFont() : Font {
internal val cacheKey: String
}

/**
* A Font that already installed in system.
*/
@ExperimentalTextApi
class SystemFont(
override val identity: String,
override val weight: FontWeight = FontWeight.Normal,
override val style: FontStyle = FontStyle.Normal
) : PlatformFont() {
override fun toString(): String {
return "SystemInstalledFont(identity='$identity', weight=$weight, style=$style)"
}
}

/**
* Defines a Font using byte array with loaded font data.
*
Expand Down Expand Up @@ -91,10 +113,11 @@ fun Font(
style: FontStyle = FontStyle.Normal
): Font = LoadedFont(identity, data, weight, style)

internal class SkiaBackedTypeface(
val alias: String?,
private class SkiaBackedTypeface(
alias: String?,
val nativeTypeface: SkTypeface
) : Typeface {
val alias = alias ?: nativeTypeface.familyName
override val fontFamily: FontFamily? = null
}

Expand Down Expand Up @@ -150,11 +173,6 @@ internal class FontCache {
fonts.setAssetFontManager(fontProvider)
}

private fun mapGenericFontFamily(generic: GenericFontFamily): List<String> {
return GenericFontFamiliesMapping[generic.name]
?: error("Unknown generic font family ${generic.name}")
}

internal fun load(font: PlatformFont): FontLoadResult {
val typeface = typefacesCache.get(font.cacheKey) {
loadTypeface(font)
Expand Down Expand Up @@ -183,19 +201,23 @@ internal class FontCache {
private fun ensureRegistered(fontFamily: FontFamily): List<String> =
when (fontFamily) {
is FontListFontFamily -> {
// not supported
throw IllegalArgumentException(
"Don't load FontListFontFamily through ensureRegistered: $fontFamily"
)
val fonts = fontFamily.fonts.filterIsInstance<SystemFont>()
if (fonts.size == fontFamily.fonts.size) {
fonts.map { it.identity }
} else {
// not supported
throw IllegalArgumentException(
"Don't load FontListFontFamily through ensureRegistered: $fontFamily"
)
}
}
is LoadedFontFamily -> {
val typeface = fontFamily.typeface as SkiaBackedTypeface
val alias = typeface.alias ?: typeface.nativeTypeface.familyName
ensureRegistered(typeface.nativeTypeface, alias)
listOf(alias)
ensureRegistered(typeface.nativeTypeface, typeface.alias)
listOf(typeface.alias)
}
is GenericFontFamily -> mapGenericFontFamily(fontFamily)
FontFamily.Default -> mapGenericFontFamily(FontFamily.SansSerif)
is GenericFontFamily -> fontFamily.aliases
is DefaultFontFamily -> FontFamily.SansSerif.aliases
else -> throw IllegalArgumentException("Unknown font family type: $fontFamily")
}
}
Expand All @@ -214,7 +236,11 @@ internal enum class Platform {
internal expect fun currentPlatform(): Platform
internal expect fun loadTypeface(font: Font): SkTypeface

internal val GenericFontFamiliesMapping: Map<String, List<String>> by lazy {
internal val GenericFontFamily.aliases
get() = GenericFontFamiliesMapping[name]
?: error("Unknown generic font family $name")

private val GenericFontFamiliesMapping: Map<String, List<String>> by lazy {
when (currentPlatform()) {
Platform.Linux ->
mapOf(
Expand Down