Skip to content

Commit 48a095f

Browse files
committed
Extract the Skiko text backend into the ui-skiko module
Move the Skia-backed implementation of ui-text into ui-skiko, leaving ui-text's nonAndroidMain Skia-free, mirroring the graphics extraction. ui-text keeps expect/actual delegators resolving a registered ComposeUiTextImplementation; the org.jetbrains.skia paragraph, font and text-paint code lives in ui-skiko. - Paragraph building, intrinsics, char helpers and the font subsystem (FontFamilyResolver, font loading, PlatformFont) relocate to ui-skiko. - The font subsystem is ui-text's own public API with per-platform loadTypeface actuals, so the unavoidable public Skia-typed surface stays in ui-text's skikoMain compat layer (Skia-free nonAndroidMain -> Skia skikoMain -> platform leaves); only the implementation moves. - ComposeUiSkikoRuntime.registerSkikoComposeImplementation() now wires both the graphics and text implementations (plus the temporary SkikoGraphicsCompat bridge). API baselines are regenerated in a later commit.
1 parent 7c8c91a commit 48a095f

86 files changed

Lines changed: 1617 additions & 866 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compose/ui/ui-skiko/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ androidXMultiplatform {
7676
dependsOn(nonAndroidTest)
7777
dependencies {
7878
implementation(libs.skikoCurrentOs)
79+
implementation(libs.junit)
80+
implementation(libs.truth)
81+
implementation(project(":internal-testutils-fonts"))
82+
implementation(project(":compose:foundation:foundation"))
83+
implementation(project(":compose:ui:ui-test-junit4"))
7984
}
8085
}
8186

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/Cache.jvm.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/Cache.jvm.kt

File renamed without changes.

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/CharHelpers.jvm.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/CharHelpers.jvm.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
*/
1616
package androidx.compose.ui.text
1717

18-
19-
/**
20-
* Get strong (R, L or AL) direction type.
21-
* See https://www.unicode.org/reports/tr9/
22-
*/
2318
internal actual fun CodePoint.strongDirectionType(): StrongDirectionType =
2419
when (getDirectionality()) {
2520
CharDirectionality.LEFT_TO_RIGHT -> StrongDirectionType.Ltr
@@ -29,6 +24,7 @@ internal actual fun CodePoint.strongDirectionType(): StrongDirectionType =
2924

3025
else -> StrongDirectionType.None
3126
}
27+
3228
internal actual fun CodePoint.isNeutralDirection(): Boolean =
3329
when (getDirectionality()) {
3430
CharDirectionality.OTHER_NEUTRALS,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2023 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 androidx.compose.runtime.Stable
20+
import androidx.compose.ui.text.ExperimentalTextApi
21+
import androidx.compose.ui.text.platform.JetBrainsRuntimeFontFamilies
22+
23+
/**
24+
* Load a [FontFamily] embedded in the JetBrains Runtime. It will return
25+
* `null` if the font family isn't embedded in the JetBrains Runtime,
26+
* or if using any Java runtime other than the JetBrains Runtime.
27+
*
28+
* Using this requires the current module to have access to the `sun.font`
29+
* APIs, which are closed by default. To open the access to those APIs, you
30+
* need to pass this argument to the JVM:
31+
* ```
32+
* --add-opens java.desktop/sun.font=ALL-UNNAMED
33+
* ```
34+
*
35+
* If the `sun.font` API is not accessible, this will always return `null`.
36+
*
37+
* @param familyName The case-insensitive font family name to load.
38+
* @return the requested font family, if running on the JetBrains Runtime,
39+
* and the `sun.font` API is accessible, and the font family exists in
40+
* the runtime. Otherwise, `null`.
41+
*/
42+
@ExperimentalTextApi
43+
@Stable
44+
fun EmbeddedFontFamily(familyName: String): FontFamily? =
45+
JetBrainsRuntimeFontFamilies.embeddedFamilies[familyName.lowercase()]

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontInterop.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontInterop.kt

File renamed without changes.

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontUtils.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontUtils.kt

File renamed without changes.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2020 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.platform
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.text.ExperimentalTextApi
21+
import androidx.compose.ui.text.font.Font
22+
import androidx.compose.ui.text.font.FontStyle
23+
import androidx.compose.ui.text.font.FontVariation
24+
import androidx.compose.ui.text.font.FontWeight
25+
import java.io.File
26+
import org.jetbrains.skia.Data
27+
import org.jetbrains.skia.FontMgr
28+
import org.jetbrains.skia.FontSlant
29+
import org.jetbrains.skia.FontStyle as SkFontStyle
30+
import org.jetbrains.skia.FontWidth
31+
import org.jetbrains.skia.Typeface as SkTypeface
32+
33+
@OptIn(ExperimentalTextApi::class)
34+
internal actual fun loadTypeface(font: Font): SkTypeface {
35+
if (font !is PlatformFont) {
36+
throw IllegalArgumentException("Unsupported font type: $font")
37+
}
38+
val typeface = when (font) {
39+
is ResourceFont -> typefaceResource(font.name)
40+
is FileFont -> FontMgr.default.makeFromFile(font.file.toString())
41+
is LoadedFont -> FontMgr.default.makeFromData(Data.makeFromBytes(font.data))
42+
is SystemFont -> FontMgr.default.matchFamilyStyle(font.identity, font.skFontStyle)
43+
} ?: (FontMgr.default.legacyMakeTypeface(font.identity, font.skFontStyle)
44+
?: error("loadTypeface legacyMakeTypeface failed"))
45+
return typeface.cloneWithVariationSettings(font.variationSettings)
46+
}
47+
48+
private fun typefaceResource(resourceName: String): SkTypeface {
49+
val contextClassLoader = Thread.currentThread().contextClassLoader!!
50+
val resource = contextClassLoader.getResourceAsStream(resourceName)
51+
?: (::typefaceResource.javaClass).getResourceAsStream(resourceName)
52+
?: error("Can't load font from $resourceName")
53+
54+
val bytes = resource.use { it.readAllBytes() }
55+
return FontMgr.default.makeFromData(Data.makeFromBytes(bytes))!!
56+
}
57+
58+
private val Font.skFontStyle: SkFontStyle
59+
get() = SkFontStyle(
60+
weight = weight.weight,
61+
width = FontWidth.NORMAL,
62+
slant = if (style == FontStyle.Italic) FontSlant.ITALIC else FontSlant.UPRIGHT
63+
)
64+
65+
internal actual fun currentPlatform(): Platform {
66+
val name = System.getProperty("os.name")
67+
return when {
68+
name.startsWith("Linux") -> Platform.Linux
69+
name.startsWith("Win") -> Platform.Windows
70+
name == "Mac OS X" -> Platform.MacOS
71+
else -> Platform.Unknown
72+
}
73+
}
74+
75+
/**
76+
* Creates a Font using a resource name.
77+
*
78+
* @param resource The resource name in classpath.
79+
* @param weight The weight of the font. The system uses this to match a font to a font request that
80+
* is given in a [androidx.compose.ui.text.SpanStyle].
81+
* @param style The style of the font, normal or italic. The system uses this to match a font to a
82+
* font request that is given in a [androidx.compose.ui.text.SpanStyle].
83+
* @see FontFamily
84+
*/
85+
@OptIn(InternalComposeUiApi::class)
86+
fun Font(
87+
resource: String,
88+
weight: FontWeight = FontWeight.Normal,
89+
style: FontStyle = FontStyle.Normal
90+
): Font = ResourceFont(resource, weight, style, FontVariation.Settings())
91+
92+
@OptIn(InternalComposeUiApi::class)
93+
fun Font(
94+
resource: String,
95+
weight: FontWeight = FontWeight.Normal,
96+
style: FontStyle = FontStyle.Normal,
97+
variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
98+
): Font = ResourceFont(resource, weight, style, variationSettings)
99+
100+
/**
101+
* Creates a Font using a file path.
102+
*
103+
* @param file File path to font.
104+
* @param weight The weight of the font. The system uses this to match a font to a font request that
105+
* is given in a [androidx.compose.ui.text.SpanStyle].
106+
* @param style The style of the font, normal or italic. The system uses this to match a font to a
107+
* font request that is given in a [androidx.compose.ui.text.SpanStyle].
108+
* @see FontFamily
109+
*/
110+
@OptIn(InternalComposeUiApi::class)
111+
fun Font(
112+
file: File,
113+
weight: FontWeight = FontWeight.Normal,
114+
style: FontStyle = FontStyle.Normal
115+
): Font = FileFont(file, weight, style, FontVariation.Settings())
116+
117+
@OptIn(InternalComposeUiApi::class)
118+
fun Font(
119+
file: File,
120+
weight: FontWeight = FontWeight.Normal,
121+
style: FontStyle = FontStyle.Normal,
122+
variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
123+
): Font = FileFont(file, weight, style, variationSettings)

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/InternalFontApiChecker.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/platform/InternalFontApiChecker.kt

File renamed without changes.

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/JetBrainsRuntimeFontFamilies.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/platform/JetBrainsRuntimeFontFamilies.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package androidx.compose.ui.text.platform
1818

1919
import androidx.compose.ui.text.font.FontFamily
20-
import androidx.compose.ui.text.font.FontListFontFamily
2120
import androidx.compose.ui.text.font.FontStyle
2221
import androidx.compose.ui.text.font.FontWeight
2322
import androidx.compose.ui.util.fastDistinctBy
@@ -112,7 +111,7 @@ internal object JetBrainsRuntimeFontFamilies {
112111
.distinctBy { (_, font) -> font.file.absolutePath }
113112
.groupBy { (familyName, _) -> familyName }
114113
.forEach { (identity, fileFonts) ->
115-
val fontFamily = FontListFontFamily(fileFonts.fastMap { it.second })
114+
val fontFamily = FontFamily(fileFonts.fastMap { it.second })
116115
embeddedFamilies += identity.lowercase() to fontFamily
117116
}
118117
} finally {

compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.desktop.kt renamed to compose/ui/ui-skiko/src/desktopMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.desktop.kt

File renamed without changes.

0 commit comments

Comments
 (0)