From 46dfe644933db2735146e60e2d36d942efb5bdd9 Mon Sep 17 00:00:00 2001 From: Antoine Jaury Date: Sat, 5 Sep 2026 14:49:39 +0200 Subject: [PATCH] chore: add emoji fallback font support for web --- CHANGELOG.md | 1 + README.md | 4 + .../resources/licenses/NotoColorEmoji-OFL.txt | 92 ++++++++++++++++++ .../font/noto_color_emoji.ttf | Bin 0 -> 76000 bytes .../theme/EmojiFallbackFont.android.kt | 7 ++ .../kotlin/bzh/ajaury/chombev/App.kt | 3 + .../ajaury/chombev/theme/EmojiFallbackFont.kt | 15 +++ .../chombev/theme/EmojiFallbackFont.ios.kt | 7 ++ .../chombev/theme/EmojiFallbackFont.jvm.kt | 7 ++ .../chombev/theme/EmojiFallbackFont.web.kt | 34 +++++++ 10 files changed, 170 insertions(+) create mode 100644 data/resources/licenses/NotoColorEmoji-OFL.txt create mode 100644 data/resources/src/webMain/composeResources/font/noto_color_emoji.ttf create mode 100644 shared/src/androidMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.android.kt create mode 100644 shared/src/commonMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.kt create mode 100644 shared/src/iosMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.ios.kt create mode 100644 shared/src/jvmMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.jvm.kt create mode 100644 shared/src/webMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.web.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c807a7..8c2007b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Desktop: audio playback failed with *Failed to play audio* on every record, because the JDK cannot decode the `.m4a` (AAC) recordings on its own. +- add emoji fallback for web ## [1.0.0-alpha02] - 2026-08-04 diff --git a/README.md b/README.md index bb79e1f..0d6eab4 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,10 @@ runs with crash reporting disabled until you configure it. See ## License +The web build bundles a subset of [Noto Color Emoji](https://github.com/googlefonts/noto-emoji), +licensed under the SIL Open Font License 1.1 +([data/resources/licenses/NotoColorEmoji-OFL.txt](./data/resources/licenses/NotoColorEmoji-OFL.txt)). + This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details. ``` diff --git a/data/resources/licenses/NotoColorEmoji-OFL.txt b/data/resources/licenses/NotoColorEmoji-OFL.txt new file mode 100644 index 0000000..d952d62 --- /dev/null +++ b/data/resources/licenses/NotoColorEmoji-OFL.txt @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/data/resources/src/webMain/composeResources/font/noto_color_emoji.ttf b/data/resources/src/webMain/composeResources/font/noto_color_emoji.ttf new file mode 100644 index 0000000000000000000000000000000000000000..374200bc8b72362ba91fffecae1ae6c48701b74f GIT binary patch literal 76000 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 z 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Ð)#9)>$Ip7aAn_4;5(;Mn*nHES*)hBbr zR4`CTc4Vo@m_RCf)R3gCPi3_OD!amkVR5y_0tYA2@KYql0;u-C^^&breX>C{mHq>R$Io| zs>Zmk(YaD0Dq7(;GiI0-%B}%=JbV3fTdGSky#CMYx=T_TYS8|vu0o@owA|lyyiT>d zs!(S~RI+M-pkT^XjrCdS0R_tQa%2yF=4UCXZf{~8XB}s!V6C)-6dCa}DRPUB}}~OYpM}&sQ%+-*;uv3JUKmC)n?#a&ms+;6YZ2Qc|(u z=eW>SV;2JRtBlp1kFoJGmQ*D=XqibZB7`?meeV) zs3*ypjGk=`M&DiJb}`y#SnXx4&YMzdm{en>lm%7!;_B!U^=$M2*~J)&DW=Fw+TE@+ zUxGK;Y_um5dt@-K?~Jx_uHs11;&u})vGb&#NOijslO8iZCft0oios6U1$txjHe2#( zW$4_9tYN%e4;#+@jr7Ed4E6vx-VvFcOvn0qFfErQ9KIYB-BX_veJCDdgytp2%+=~1 zZ%#0nl2TY0a5Gz#l3XF(u5=f&);-aUdSUb~;hy%OO2kc!Ip)nzb>xwAd}k9^v=}jN zB>M2FstaA=KiTx8|G?e9yDGG)-um+7}CNrx>?$z5fr+;yBvTQ`c zOe#iV{TX@b=r9+T=k)frGRN#SRnx~b8dpnS58E(Z!-l4$qU3}`JwvvmbHcF?^X%f?RACY3MV(=LboRq|iwyf_T}e%X+h1Lp zQ<+46*VDjx4`>AjQ+(c3w@*s0zFdD{noOSUi?*RWMONZN!e fGp&6e!E#%orz- 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 X1%OG^AIs*#^Oe!7gLi9Kxvl>LC{7P4 literal 0 HcmV?d00001 diff --git a/shared/src/androidMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.android.kt b/shared/src/androidMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.android.kt new file mode 100644 index 0000000..08f1046 --- /dev/null +++ b/shared/src/androidMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.android.kt @@ -0,0 +1,7 @@ +package bzh.ajaury.chombev.theme + +import androidx.compose.runtime.Composable + +/** No-op: Android supplies the system emoji font. */ +@Composable +internal actual fun RegisterEmojiFallbackFont() = Unit diff --git a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt index e112594..76028dc 100644 --- a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt +++ b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt @@ -27,6 +27,7 @@ import bzh.ajaury.chombev.onboarding.ui.viewmodel.OnboardingViewModel import bzh.ajaury.chombev.player.ui.PlayerScreen import bzh.ajaury.chombev.records.ui.RecordsScreen import bzh.ajaury.chombev.theme.ChomBevTheme +import bzh.ajaury.chombev.theme.RegisterEmojiFallbackFont import bzh.ajaury.chombev.web.model.WebPage import bzh.ajaury.chombev.web.ui.WebViewScreen import org.koin.compose.KoinApplication @@ -53,6 +54,8 @@ fun App() { val darkTheme = isSystemInDarkTheme() ChomBevTheme(darkTheme = darkTheme) { + RegisterEmojiFallbackFont() + val onboardingViewModel: OnboardingViewModel = koinViewModel() val onboardingUiState by onboardingViewModel.uiState.collectAsStateWithLifecycle() diff --git a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.kt b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.kt new file mode 100644 index 0000000..309c6fe --- /dev/null +++ b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.kt @@ -0,0 +1,15 @@ +package bzh.ajaury.chombev.theme + +import androidx.compose.runtime.Composable + +/** + * Makes colour emoji renderable on platforms whose font manager has no emoji font of its own. + * + * Android, iOS and Desktop resolve emoji through the system fonts, so their implementations do + * nothing. Only the web needs a bundled font: Skia runs in the browser without any system font + * manager, and the default font baked into skiko has no emoji glyphs — they come out as tofu boxes. + * + * Emits nothing; call it once, high in the composition. + */ +@Composable +internal expect fun RegisterEmojiFallbackFont() diff --git a/shared/src/iosMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.ios.kt b/shared/src/iosMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.ios.kt new file mode 100644 index 0000000..c9e8524 --- /dev/null +++ b/shared/src/iosMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.ios.kt @@ -0,0 +1,7 @@ +package bzh.ajaury.chombev.theme + +import androidx.compose.runtime.Composable + +/** No-op: iOS supplies Apple Color Emoji. */ +@Composable +internal actual fun RegisterEmojiFallbackFont() = Unit diff --git a/shared/src/jvmMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.jvm.kt b/shared/src/jvmMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.jvm.kt new file mode 100644 index 0000000..443e576 --- /dev/null +++ b/shared/src/jvmMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.jvm.kt @@ -0,0 +1,7 @@ +package bzh.ajaury.chombev.theme + +import androidx.compose.runtime.Composable + +/** No-op: The desktop JVM resolves emoji through the system font manager. */ +@Composable +internal actual fun RegisterEmojiFallbackFont() = Unit diff --git a/shared/src/webMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.web.kt b/shared/src/webMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.web.kt new file mode 100644 index 0000000..78d3df2 --- /dev/null +++ b/shared/src/webMain/kotlin/bzh/ajaury/chombev/theme/EmojiFallbackFont.web.kt @@ -0,0 +1,34 @@ +package bzh.ajaury.chombev.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalFontFamilyResolver +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.platform.Font +import bzh.ajaury.chombev.resources.generated.resources.Res +import bzh.ajaury.chombev.resources.generated.resources.noto_color_emoji +import org.jetbrains.compose.resources.getFontResourceBytes +import org.jetbrains.compose.resources.rememberResourceEnvironment + +private const val EMOJI_FONT_IDENTITY = "NotoColorEmoji" + +/** + * Loads the bundled Noto Color Emoji subset and hands it to the font resolver. + * + * Resolving a font is what registers its typeface with Skia's fallback provider — Compose builds + * its [org.jetbrains.skia.paragraph.FontCollection] on a `FontMgrWithFallback` fed by every font + * the app has loaded — so a single [androidx.compose.ui.text.font.FontFamily.Resolver.preload] is + * enough for emoji to appear in any `Text`, without touching the screens that display them. + */ +@Composable +internal actual fun RegisterEmojiFallbackFont() { + val resourceEnvironment = rememberResourceEnvironment() + val fontFamilyResolver = LocalFontFamilyResolver.current + + LaunchedEffect(resourceEnvironment, fontFamilyResolver) { + val fontBytes = getFontResourceBytes(resourceEnvironment, Res.font.noto_color_emoji) + fontFamilyResolver.preload( + FontFamily(Font(identity = EMOJI_FONT_IDENTITY, data = fontBytes)), + ) + } +}