From 37962ea11a46376dd5eb20aec9fd3bc6814f0856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 20 Feb 2026 14:14:56 +0100 Subject: [PATCH 01/20] feat: unified async RiveFonts.addFallbackFont(source) API Replace separate addFallbackFont/addFallbackFontFromResource/addFallbackFontFromURL with a single addFallbackFont(source: FontSource) that accepts require() IDs, URI objects, resource names, URLs, or raw ArrayBuffer bytes. All methods are now async. Renamed native HybridObject from Rive to RiveFontConfig to better reflect its purpose. --- .../nitro/rive/HybridRiveFontConfig.kt | 126 +++++ .../main/java/com/margelo/nitro/rive/Rive.kt | 12 - .../app/src/main/res/raw/kanit_regular.ttf | Bin 0 -> 173148 bytes example/assets/rive/fallback_fonts.riv | Bin 0 -> 26042 bytes example/assets/rive/style_fallback_fonts.riv | Bin 0 -> 8336 bytes example/ios/kanit_regular.ttf | Bin 0 -> 173148 bytes .../src/exercisers/FontFallbackExample.tsx | 524 ++++++++++++++++++ ios/HybridRiveFontConfig.swift | 101 ++++ ios/Rive.swift | 5 - nitro.json | 6 +- .../android/c++/JHybridRiveFontConfigSpec.cpp | 134 +++++ ...Spec.hpp => JHybridRiveFontConfigSpec.hpp} | 26 +- .../generated/android/c++/JHybridRiveSpec.cpp | 59 -- ...iveSpec.kt => HybridRiveFontConfigSpec.kt} | 32 +- .../generated/android/rive+autolinking.cmake | 4 +- nitrogen/generated/android/riveOnLoad.cpp | 8 +- nitrogen/generated/ios/RNRiveAutolinking.mm | 6 +- .../generated/ios/RNRiveAutolinking.swift | 10 +- ....cpp => HybridRiveFontConfigSpecSwift.cpp} | 4 +- .../ios/c++/HybridRiveFontConfigSpecSwift.hpp | 118 ++++ .../generated/ios/c++/HybridRiveSpecSwift.hpp | 82 --- .../ios/swift/HybridRiveFontConfigSpec.swift | 60 ++ .../swift/HybridRiveFontConfigSpec_cxx.swift | 222 ++++++++ .../generated/ios/swift/HybridRiveSpec.swift | 56 -- .../ios/swift/HybridRiveSpec_cxx.swift | 139 ----- .../shared/c++/HybridRiveFontConfigSpec.cpp | 25 + .../shared/c++/HybridRiveFontConfigSpec.hpp | 68 +++ .../generated/shared/c++/HybridRiveSpec.cpp | 21 - .../generated/shared/c++/HybridRiveSpec.hpp | 62 --- src/core/RiveFonts.ts | 61 ++ src/index.tsx | 1 + src/specs/Rive.nitro.ts | 6 - src/specs/RiveFontConfig.nitro.ts | 34 ++ 33 files changed, 1533 insertions(+), 479 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt delete mode 100644 android/src/main/java/com/margelo/nitro/rive/Rive.kt create mode 100644 example/android/app/src/main/res/raw/kanit_regular.ttf create mode 100644 example/assets/rive/fallback_fonts.riv create mode 100644 example/assets/rive/style_fallback_fonts.riv create mode 100644 example/ios/kanit_regular.ttf create mode 100644 example/src/exercisers/FontFallbackExample.tsx create mode 100644 ios/HybridRiveFontConfig.swift delete mode 100644 ios/Rive.swift create mode 100644 nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp rename nitrogen/generated/android/c++/{JHybridRiveSpec.hpp => JHybridRiveFontConfigSpec.hpp} (53%) delete mode 100644 nitrogen/generated/android/c++/JHybridRiveSpec.cpp rename nitrogen/generated/android/kotlin/com/margelo/nitro/rive/{HybridRiveSpec.kt => HybridRiveFontConfigSpec.kt} (58%) rename nitrogen/generated/ios/c++/{HybridRiveSpecSwift.cpp => HybridRiveFontConfigSpecSwift.cpp} (72%) create mode 100644 nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp delete mode 100644 nitrogen/generated/ios/c++/HybridRiveSpecSwift.hpp create mode 100644 nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift create mode 100644 nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift delete mode 100644 nitrogen/generated/ios/swift/HybridRiveSpec.swift delete mode 100644 nitrogen/generated/ios/swift/HybridRiveSpec_cxx.swift create mode 100644 nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp create mode 100644 nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp delete mode 100644 nitrogen/generated/shared/c++/HybridRiveSpec.cpp delete mode 100644 nitrogen/generated/shared/c++/HybridRiveSpec.hpp create mode 100644 src/core/RiveFonts.ts delete mode 100644 src/specs/Rive.nitro.ts create mode 100644 src/specs/RiveFontConfig.nitro.ts diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt new file mode 100644 index 00000000..ebd74ec2 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -0,0 +1,126 @@ +package com.margelo.nitro.rive + +import android.util.Log +import androidx.annotation.Keep +import app.rive.runtime.kotlin.fonts.FontFallbackStrategy +import app.rive.runtime.kotlin.fonts.FontHelper +import app.rive.runtime.kotlin.fonts.Fonts +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.NitroModules +import com.margelo.nitro.core.ArrayBuffer +import com.margelo.nitro.core.Promise +import java.net.URL +import java.util.Collections +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Keep +@DoNotStrip +class HybridRiveFontConfig : HybridRiveFontConfigSpec() { + companion object { + private const val TAG = "RiveFonts" + private var systemFallbackEnabled = false + private val customFonts: MutableList = Collections.synchronizedList(mutableListOf()) + + private val fontFallbackStrategy = object : FontFallbackStrategy { + override fun getFont(weight: Fonts.Weight): List { + val result = mutableListOf() + synchronized(customFonts) { + result.addAll(customFonts) + } + val systemFonts = FontHelper.getFallbackFonts(Fonts.FontOpts(weight = weight)) + for (font in systemFonts) { + FontHelper.getFontBytes(font)?.let { result.add(it) } + } + return result + } + } + + private fun ensureSystemFallback() { + if (systemFallbackEnabled) return + FontFallbackStrategy.stylePicker = fontFallbackStrategy + systemFallbackEnabled = true + } + + private fun resetFontCache() { + try { + FontFallbackStrategy.cppResetFontCache() + } catch (e: Exception) { + Log.e(TAG, "Failed to reset font cache: ${e.message}") + } + } + } + + override fun enableSystemFontFallback(): Promise { + return Promise.async { + ensureSystemFallback() + } + } + + override fun addFallbackFont(bytes: ArrayBuffer): Promise { + return Promise.async { + ensureSystemFallback() + val buffer = bytes.getBuffer(false) + val byteArray = ByteArray(buffer.remaining()) + buffer.get(byteArray) + customFonts.add(byteArray) + resetFontCache() + } + } + + override fun addFallbackFontFromResource(resource: String): Promise { + return Promise.async { + ensureSystemFallback() + val context = NitroModules.applicationContext + ?: throw Error("Application context not available") + + val resName = resource.substringBeforeLast(".") + + val rawId = context.resources.getIdentifier(resName, "raw", context.packageName) + if (rawId != 0) { + val fontBytes = context.resources.openRawResource(rawId).use { it.readBytes() } + customFonts.add(fontBytes) + resetFontCache() + return@async + } + + val fontId = context.resources.getIdentifier(resName, "font", context.packageName) + if (fontId != 0) { + val fontBytes = context.resources.openRawResource(fontId).use { it.readBytes() } + customFonts.add(fontBytes) + resetFontCache() + return@async + } + + val assetPaths = listOf("fonts/$resource", resource) + for (assetPath in assetPaths) { + try { + val fontBytes = context.assets.open(assetPath).use { it.readBytes() } + customFonts.add(fontBytes) + resetFontCache() + return@async + } catch (_: Exception) {} + } + + throw Error("Font resource not found: $resource (checked res/raw, res/font, assets/fonts)") + } + } + + override fun addFallbackFontFromURL(url: String): Promise { + return Promise.async { + ensureSystemFallback() + val fontBytes = withContext(Dispatchers.IO) { + URL(url).readBytes() + } + customFonts.add(fontBytes) + resetFontCache() + } + } + + override fun clearCustomFallbackFonts(): Promise { + return Promise.async { + customFonts.clear() + resetFontCache() + } + } +} diff --git a/android/src/main/java/com/margelo/nitro/rive/Rive.kt b/android/src/main/java/com/margelo/nitro/rive/Rive.kt deleted file mode 100644 index 84ae2e99..00000000 --- a/android/src/main/java/com/margelo/nitro/rive/Rive.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.margelo.nitro.rive - -import androidx.annotation.Keep -import com.facebook.proguard.annotations.DoNotStrip - -@Keep -@DoNotStrip -class Rive : HybridRiveSpec() { - override fun multiply(a: Double, b: Double): Double { - return a * b - } -} diff --git a/example/android/app/src/main/res/raw/kanit_regular.ttf b/example/android/app/src/main/res/raw/kanit_regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e9bc0a2f5d0d1ad0df1fa20c44e381834c128c58 GIT binary patch literal 173148 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

b3 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 WUxU|wBsmXJwm*L^`NIR0kpBhA#Pr|* literal 0 HcmV?d00001 diff --git a/example/assets/rive/style_fallback_fonts.riv b/example/assets/rive/style_fallback_fonts.riv new file mode 100644 index 0000000000000000000000000000000000000000..9748dc26d5fe9cf184280b45e4a9c14a8d8172f8 GIT binary patch literal 8336 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 L-ASK&juHJ8%R<@7 literal 0 HcmV?d00001 diff --git a/example/ios/kanit_regular.ttf b/example/ios/kanit_regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e9bc0a2f5d0d1ad0df1fa20c44e381834c128c58 GIT binary patch literal 173148 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

b3 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 = { + kanitBundled: { + label: 'Kanit (Bundled)', + sublabel: 'From native resources — 173KB', + source: 'kanit_regular.ttf', + }, + kanit: { + label: 'Kanit (URL)', + sublabel: 'Geometric Thai+Latin — 173KB', + source: { + uri: 'https://raw.githubusercontent.com/google/fonts/main/ofl/kanit/Kanit-Regular.ttf', + }, + }, + notoSerifThai: { + label: 'Noto Serif Thai', + sublabel: 'Serif Thai — 292KB', + source: { + uri: 'https://raw.githubusercontent.com/google/fonts/main/ofl/notoserifthai/NotoSerifThai%5Bwdth%2Cwght%5D.ttf', + }, + }, +}; + +type FontKey = keyof typeof FONTS; + +type LogEntry = { + message: string; + type: 'success' | 'error' | 'info'; + timestamp: number; +}; + +export default function FontFallbackExample() { + const [mounted, setMounted] = useState(false); + const [selectedFonts, setSelectedFonts] = useState>(new Set()); + const [loadingFont, setLoadingFont] = useState(null); + const [log, setLog] = useState([]); + const [inputText, setInputText] = useState('ABC 你好 สวัสดี'); + + const addLog = (message: string, type: LogEntry['type'] = 'info') => { + setLog((prev) => [...prev, { message, type, timestamp: Date.now() }]); + }; + + const toggleFont = async (key: FontKey) => { + if (mounted) return; + const font = FONTS[key]!; + const isSelected = selectedFonts.has(key); + + if (isSelected) { + setSelectedFonts((prev) => { + const next = new Set(prev); + next.delete(key); + return next; + }); + addLog(`Removed ${font.label} from selection`, 'info'); + return; + } + + setLoadingFont(key); + try { + await RiveFonts.addFallbackFont(font.source); + addLog(`Added ${font.label} as fallback font`, 'success'); + setSelectedFonts((prev) => new Set(prev).add(key)); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + addLog(`${font.label}: ${msg}`, 'error'); + console.error(`FontFallback ${font.label}:`, err); + } finally { + setLoadingFont(null); + } + }; + + const handleMount = async () => { + await RiveFonts.enableSystemFontFallback(); + setMounted(true); + addLog( + `Mounted with: ${selectedFonts.size > 0 ? [...selectedFonts].map((k) => FONTS[k]!.label).join(', ') : 'system defaults only'}`, + 'info' + ); + }; + + const handleReset = async () => { + await RiveFonts.clearCustomFallbackFonts(); + setSelectedFonts(new Set()); + setMounted(false); + addLog('Cleared fonts & unmounted view', 'info'); + }; + + return ( + + {mounted ? ( + + ) : ( + + Configure Fallback Fonts + + Select custom fonts below, then mount the view. Font lookups are + cached at startup — to change fonts, unmount and remount. + + + )} + + + {mounted && ( + <> + + + + + + + + + + )} + + + {mounted ? 'Active Fonts' : 'Select Fonts'} + + + {(Object.keys(FONTS) as FontKey[]).map((key) => ( + toggleFont(key)} + /> + ))} + + {mounted ? ( + + ) : ( + 0 + ? `Mount View with ${selectedFonts.size} Custom Font${selectedFonts.size > 1 ? 's' : ''}` + : 'Mount View (System Defaults Only)' + } + onPress={handleMount} + /> + )} + + {log.length > 0 && ( + + + Log + setLog([])}> + Clear + + + {log.map((entry) => ( + + {entry.message} + + ))} + + )} + + + ); +} + +function MountedView({ text }: { text: string }) { + const { riveViewRef, setHybridRef } = useRive(); + const { riveFile, isLoading, error } = useRiveFile( + require('../../assets/rive/font_fallback.riv') + ); + const viewModel = useMemo( + () => riveFile?.defaultArtboardViewModel(), + [riveFile] + ); + const instance = useMemo( + () => viewModel?.createDefaultInstance(), + [viewModel] + ); + + const { setValue: setRiveText, error: textError } = useRiveString( + TEXT_PROPERTY, + instance + ); + + if (textError) { + console.error('Failed to bind text property:', textError); + } + + useEffect(() => { + setRiveText(text); + riveViewRef?.playIfNeeded(); + }, [text, setRiveText, riveViewRef]); + + if (isLoading) { + return ( + + + + ); + } + + if (error || !riveFile || !instance) { + return ( + + + {error || 'Failed to set up view model'} + + + ); + } + + return ( + + + + ); +} + +function FontToggle({ + label, + sublabel, + selected, + disabled, + loading, + onPress, +}: { + label: string; + sublabel: string; + selected: boolean; + disabled: boolean; + loading: boolean; + onPress: () => void; +}) { + return ( + + {loading ? ( + + ) : ( + <> + + + {label} + + {sublabel} + + {selected ? '✓' : ''} + + )} + + ); +} + +function Preset({ + label, + text, + onPress, +}: { + label: string; + text: string; + onPress: (text: string) => void; +}) { + return ( + onPress(text)}> + {label} + + ); +} + +function ActionButton({ + label, + onPress, + destructive, +}: { + label: string; + onPress: () => void; + destructive?: boolean; +}) { + return ( + + {label} + + ); +} + +FontFallbackExample.metadata = { + name: 'Font Fallback', + description: + 'Tests RiveFonts API — configure custom fallback fonts before mounting the Rive view', +} satisfies Metadata; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + }, + setupContainer: { + height: 220, + backgroundColor: '#f0f0f0', + justifyContent: 'center', + alignItems: 'center', + padding: 20, + }, + setupTitle: { + fontSize: 18, + fontWeight: '700', + color: '#333', + marginBottom: 8, + }, + setupHint: { + fontSize: 13, + color: '#666', + textAlign: 'center', + }, + riveContainer: { + height: 220, + backgroundColor: '#f0f0f0', + justifyContent: 'center', + alignItems: 'center', + }, + rive: { + width: '100%', + height: '100%', + }, + errorText: { + color: 'red', + textAlign: 'center', + padding: 20, + }, + controls: { + flex: 1, + }, + controlsContent: { + padding: 16, + gap: 10, + paddingBottom: 40, + }, + input: { + borderWidth: 1, + borderColor: '#ccc', + borderRadius: 10, + padding: 12, + fontSize: 16, + backgroundColor: '#fafafa', + }, + presets: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + preset: { + backgroundColor: '#e8e8e8', + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 6, + }, + presetText: { + fontSize: 13, + color: '#333', + }, + sectionTitle: { + fontSize: 14, + fontWeight: '600', + color: '#333', + marginTop: 6, + }, + fontToggle: { + flexDirection: 'row', + alignItems: 'center', + borderWidth: 1, + borderColor: '#ddd', + borderRadius: 10, + padding: 14, + backgroundColor: '#fafafa', + minHeight: 56, + }, + fontToggleSelected: { + borderColor: '#007AFF', + backgroundColor: '#F0F7FF', + }, + fontToggleDisabled: { + opacity: 0.6, + }, + fontToggleInfo: { + flex: 1, + }, + fontToggleLabel: { + fontSize: 16, + fontWeight: '600', + color: '#333', + }, + fontToggleLabelSelected: { + color: '#007AFF', + }, + fontToggleSublabel: { + fontSize: 12, + color: '#888', + marginTop: 2, + }, + fontToggleCheck: { + fontSize: 18, + color: '#007AFF', + fontWeight: '700', + width: 24, + textAlign: 'center', + }, + button: { + backgroundColor: '#007AFF', + borderRadius: 10, + padding: 14, + alignItems: 'center', + minHeight: 50, + justifyContent: 'center', + }, + buttonDestructive: { + backgroundColor: '#FF3B30', + }, + buttonText: { + color: '#fff', + fontSize: 16, + fontWeight: '600', + textAlign: 'center', + }, + logContainer: { + marginTop: 6, + backgroundColor: '#1a1a2e', + borderRadius: 10, + padding: 12, + }, + logHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 8, + }, + logTitle: { + color: '#888', + fontSize: 12, + fontWeight: '600', + textTransform: 'uppercase', + }, + clearLog: { + color: '#007AFF', + fontSize: 12, + }, + logEntry: { + color: '#ccc', + fontSize: 13, + fontFamily: 'monospace', + paddingVertical: 2, + }, + logSuccess: { + color: '#4CD964', + }, + logError: { + color: '#FF3B30', + }, +}); diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift new file mode 100644 index 00000000..064bf686 --- /dev/null +++ b/ios/HybridRiveFontConfig.swift @@ -0,0 +1,101 @@ +import CoreText +import NitroModules +import RiveRuntime + +class HybridRiveFontConfig: HybridRiveFontConfigSpec { + private static var systemFallbackEnabled = false + private static var customFonts: [UIFont] = [] + + func enableSystemFontFallback() throws -> Promise { + return Promise.async { + Self.ensureSystemFallback() + } + } + + func addFallbackFont(bytes: ArrayBuffer) throws -> Promise { + return Promise.async { + Self.ensureSystemFallback() + let data = bytes.toData(copyIfNeeded: true) + let font = try Self.createUIFont(from: data) + Self.customFonts.append(font) + Self.updateFallbackFonts() + } + } + + func addFallbackFontFromResource(resource: String) throws -> Promise { + return Promise.async { + Self.ensureSystemFallback() + let nsResource = resource as NSString + let name = nsResource.deletingPathExtension + let ext = nsResource.pathExtension.isEmpty ? nil : nsResource.pathExtension + + guard let path = Bundle.main.path(forResource: name, ofType: ext) else { + throw RuntimeError.error(withMessage: "Font resource not found: \(resource)") + } + let data = try Data(contentsOf: URL(fileURLWithPath: path)) + let font = try Self.createUIFont(from: data) + Self.customFonts.append(font) + Self.updateFallbackFonts() + } + } + + func addFallbackFontFromURL(url: String) throws -> Promise { + return Promise.async { + Self.ensureSystemFallback() + guard let parsedURL = URL(string: url) else { + throw RuntimeError.error(withMessage: "Invalid font URL: \(url)") + } + let (data, _) = try await URLSession.shared.data(from: parsedURL) + let font = try Self.createUIFont(from: data) + Self.customFonts.append(font) + Self.updateFallbackFonts() + } + } + + func clearCustomFallbackFonts() throws -> Promise { + return Promise.async { + Self.customFonts.removeAll() + Self.updateFallbackFonts() + } + } + + private static func ensureSystemFallback() { + guard !systemFallbackEnabled else { return } + _ = RiveFont.self + systemFallbackEnabled = true + } + + private static func createUIFont(from data: Data) throws -> UIFont { + guard let provider = CGDataProvider(data: data as CFData), + let cgFont = CGFont(provider) + else { + throw RuntimeError.error(withMessage: "Failed to decode font data") + } + + var error: Unmanaged? + if !CTFontManagerRegisterGraphicsFont(cgFont, &error) { + let cfError = error?.takeRetainedValue() + let domain = cfError.map { CFErrorGetDomain($0) as String } ?? "" + // Ignore "already registered" errors + if domain != kCTFontManagerErrorDomain as String { + throw RuntimeError.error( + withMessage: "Failed to register font: \(cfError?.localizedDescription ?? "unknown error")" + ) + } + } + + guard let fontName = cgFont.postScriptName as String? else { + throw RuntimeError.error(withMessage: "Failed to get font name from data") + } + guard let font = UIFont(name: fontName, size: UIFont.systemFontSize) else { + throw RuntimeError.error(withMessage: "Failed to create UIFont for: \(fontName)") + } + return font + } + + private static func updateFallbackFonts() { + var providers: [RiveFallbackFontProvider] = customFonts + providers.append(RiveFallbackFontDescriptor()) + RiveFont.fallbackFonts = providers + } +} diff --git a/ios/Rive.swift b/ios/Rive.swift deleted file mode 100644 index 2901590f..00000000 --- a/ios/Rive.swift +++ /dev/null @@ -1,5 +0,0 @@ -class Rive: HybridRiveSpec { - public func multiply(a: Double, b: Double) throws -> Double { - return a * b - } -} diff --git a/nitro.json b/nitro.json index f60b2005..e1e89ae8 100644 --- a/nitro.json +++ b/nitro.json @@ -9,9 +9,9 @@ "androidCxxLibName": "rive" }, "autolinking": { - "Rive": { - "swift": "Rive", - "kotlin": "Rive" + "RiveFontConfig": { + "swift": "HybridRiveFontConfig", + "kotlin": "HybridRiveFontConfig" }, "RiveFileFactory": { "swift": "HybridRiveFileFactory", diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp new file mode 100644 index 00000000..f8eca05b --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp @@ -0,0 +1,134 @@ +/// +/// JHybridRiveFontConfigSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridRiveFontConfigSpec.hpp" + + + +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::rive { + + jni::local_ref JHybridRiveFontConfigSpec::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + void JHybridRiveFontConfigSpec::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridRiveFontConfigSpec::initHybrid), + }); + } + + size_t JHybridRiveFontConfigSpec::getExternalMemorySize() noexcept { + static const auto method = javaClassStatic()->getMethod("getMemorySize"); + return method(_javaPart); + } + + bool JHybridRiveFontConfigSpec::equals(const std::shared_ptr& other) { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _javaPart == otherCast->_javaPart; + } + return false; + } + + void JHybridRiveFontConfigSpec::dispose() noexcept { + static const auto method = javaClassStatic()->getMethod("dispose"); + method(_javaPart); + } + + std::string JHybridRiveFontConfigSpec::toString() { + static const auto method = javaClassStatic()->getMethod("toString"); + auto javaString = method(_javaPart); + return javaString->toStdString(); + } + + // Properties + + + // Methods + std::shared_ptr> JHybridRiveFontConfigSpec::enableSystemFontFallback() { + static const auto method = javaClassStatic()->getMethod()>("enableSystemFontFallback"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFont(const std::shared_ptr& bytes) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* bytes */)>("addFallbackFont"); + auto __result = method(_javaPart, JArrayBuffer::wrap(bytes)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontFromResource(const std::string& resource) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* resource */)>("addFallbackFontFromResource"); + auto __result = method(_javaPart, jni::make_jstring(resource)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontFromURL(const std::string& url) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* url */)>("addFallbackFontFromURL"); + auto __result = method(_javaPart, jni::make_jstring(url)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + std::shared_ptr> JHybridRiveFontConfigSpec::clearCustomFallbackFonts() { + static const auto method = javaClassStatic()->getMethod()>("clearCustomFallbackFonts"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JHybridRiveSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp similarity index 53% rename from nitrogen/generated/android/c++/JHybridRiveSpec.hpp rename to nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp index 633677a6..b7877b9a 100644 --- a/nitrogen/generated/android/c++/JHybridRiveSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp @@ -1,5 +1,5 @@ /// -/// HybridRiveSpec.hpp +/// HybridRiveFontConfigSpec.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro /// Copyright © Marc Rousavy @ Margelo @@ -9,7 +9,7 @@ #include #include -#include "HybridRiveSpec.hpp" +#include "HybridRiveFontConfigSpec.hpp" @@ -18,22 +18,22 @@ namespace margelo::nitro::rive { using namespace facebook; - class JHybridRiveSpec: public jni::HybridClass, - public virtual HybridRiveSpec { + class JHybridRiveFontConfigSpec: public jni::HybridClass, + public virtual HybridRiveFontConfigSpec { public: - static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/HybridRiveSpec;"; + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/HybridRiveFontConfigSpec;"; static jni::local_ref initHybrid(jni::alias_ref jThis); static void registerNatives(); protected: // C++ constructor (called from Java via `initHybrid()`) - explicit JHybridRiveSpec(jni::alias_ref jThis) : - HybridObject(HybridRiveSpec::TAG), + explicit JHybridRiveFontConfigSpec(jni::alias_ref jThis) : + HybridObject(HybridRiveFontConfigSpec::TAG), HybridBase(jThis), _javaPart(jni::make_global(jThis)) {} public: - ~JHybridRiveSpec() override { + ~JHybridRiveFontConfigSpec() override { // Hermes GC can destroy JS objects on a non-JNI Thread. jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); } @@ -45,7 +45,7 @@ namespace margelo::nitro::rive { std::string toString() override; public: - inline const jni::global_ref& getJavaPart() const noexcept { + inline const jni::global_ref& getJavaPart() const noexcept { return _javaPart; } @@ -55,12 +55,16 @@ namespace margelo::nitro::rive { public: // Methods - double multiply(double a, double b) override; + std::shared_ptr> enableSystemFontFallback() override; + std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) override; + std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override; + std::shared_ptr> addFallbackFontFromURL(const std::string& url) override; + std::shared_ptr> clearCustomFallbackFonts() override; private: friend HybridBase; using HybridBase::HybridBase; - jni::global_ref _javaPart; + jni::global_ref _javaPart; }; } // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JHybridRiveSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveSpec.cpp deleted file mode 100644 index f1fcd7a4..00000000 --- a/nitrogen/generated/android/c++/JHybridRiveSpec.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/// -/// JHybridRiveSpec.cpp -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -#include "JHybridRiveSpec.hpp" - - - - - -namespace margelo::nitro::rive { - - jni::local_ref JHybridRiveSpec::initHybrid(jni::alias_ref jThis) { - return makeCxxInstance(jThis); - } - - void JHybridRiveSpec::registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", JHybridRiveSpec::initHybrid), - }); - } - - size_t JHybridRiveSpec::getExternalMemorySize() noexcept { - static const auto method = javaClassStatic()->getMethod("getMemorySize"); - return method(_javaPart); - } - - bool JHybridRiveSpec::equals(const std::shared_ptr& other) { - if (auto otherCast = std::dynamic_pointer_cast(other)) { - return _javaPart == otherCast->_javaPart; - } - return false; - } - - void JHybridRiveSpec::dispose() noexcept { - static const auto method = javaClassStatic()->getMethod("dispose"); - method(_javaPart); - } - - std::string JHybridRiveSpec::toString() { - static const auto method = javaClassStatic()->getMethod("toString"); - auto javaString = method(_javaPart); - return javaString->toStdString(); - } - - // Properties - - - // Methods - double JHybridRiveSpec::multiply(double a, double b) { - static const auto method = javaClassStatic()->getMethod("multiply"); - auto __result = method(_javaPart, a, b); - return __result; - } - -} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt similarity index 58% rename from nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveSpec.kt rename to nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt index 826a3b32..8d6e9b89 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt @@ -1,5 +1,5 @@ /// -/// HybridRiveSpec.kt +/// HybridRiveFontConfigSpec.kt /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro /// Copyright © Marc Rousavy @ Margelo @@ -10,11 +10,13 @@ package com.margelo.nitro.rive import androidx.annotation.Keep import com.facebook.jni.HybridData import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.Promise +import com.margelo.nitro.core.ArrayBuffer import com.margelo.nitro.core.HybridObject /** - * A Kotlin class representing the Rive HybridObject. - * Implement this abstract class to create Kotlin-based instances of Rive. + * A Kotlin class representing the RiveFontConfig HybridObject. + * Implement this abstract class to create Kotlin-based instances of RiveFontConfig. */ @DoNotStrip @Keep @@ -23,7 +25,7 @@ import com.margelo.nitro.core.HybridObject "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" ) -abstract class HybridRiveSpec: HybridObject() { +abstract class HybridRiveFontConfigSpec: HybridObject() { @DoNotStrip private var mHybridData: HybridData = initHybrid() @@ -38,7 +40,7 @@ abstract class HybridRiveSpec: HybridObject() { // Default implementation of `HybridObject.toString()` override fun toString(): String { - return "[HybridObject Rive]" + return "[HybridObject RiveFontConfig]" } // Properties @@ -47,11 +49,27 @@ abstract class HybridRiveSpec: HybridObject() { // Methods @DoNotStrip @Keep - abstract fun multiply(a: Double, b: Double): Double + abstract fun enableSystemFontFallback(): Promise + + @DoNotStrip + @Keep + abstract fun addFallbackFont(bytes: ArrayBuffer): Promise + + @DoNotStrip + @Keep + abstract fun addFallbackFontFromResource(resource: String): Promise + + @DoNotStrip + @Keep + abstract fun addFallbackFontFromURL(url: String): Promise + + @DoNotStrip + @Keep + abstract fun clearCustomFallbackFonts(): Promise private external fun initHybrid(): HybridData companion object { - protected const val TAG = "HybridRiveSpec" + protected const val TAG = "HybridRiveFontConfigSpec" } } diff --git a/nitrogen/generated/android/rive+autolinking.cmake b/nitrogen/generated/android/rive+autolinking.cmake index 6b89147c..bc2203b2 100644 --- a/nitrogen/generated/android/rive+autolinking.cmake +++ b/nitrogen/generated/android/rive+autolinking.cmake @@ -34,9 +34,9 @@ target_sources( ../nitrogen/generated/android/riveOnLoad.cpp # Shared Nitrogen C++ sources ../nitrogen/generated/shared/c++/HybridBindableArtboardSpec.cpp - ../nitrogen/generated/shared/c++/HybridRiveSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveFileSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveFileFactorySpec.cpp + ../nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveImageSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveImageFactorySpec.cpp ../nitrogen/generated/shared/c++/HybridRiveRuntimeSpec.cpp @@ -56,9 +56,9 @@ target_sources( ../nitrogen/generated/shared/c++/HybridViewModelArtboardPropertySpec.cpp # Android-specific Nitrogen C++ sources ../nitrogen/generated/android/c++/JHybridBindableArtboardSpec.cpp - ../nitrogen/generated/android/c++/JHybridRiveSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFileSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFileFactorySpec.cpp + ../nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveImageSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveImageFactorySpec.cpp ../nitrogen/generated/android/c++/JHybridRiveRuntimeSpec.cpp diff --git a/nitrogen/generated/android/riveOnLoad.cpp b/nitrogen/generated/android/riveOnLoad.cpp index 25e1473d..8a4d3db9 100644 --- a/nitrogen/generated/android/riveOnLoad.cpp +++ b/nitrogen/generated/android/riveOnLoad.cpp @@ -16,9 +16,9 @@ #include #include "JHybridBindableArtboardSpec.hpp" -#include "JHybridRiveSpec.hpp" #include "JHybridRiveFileSpec.hpp" #include "JHybridRiveFileFactorySpec.hpp" +#include "JHybridRiveFontConfigSpec.hpp" #include "JHybridRiveImageSpec.hpp" #include "JHybridRiveImageFactorySpec.hpp" #include "JHybridRiveRuntimeSpec.hpp" @@ -54,9 +54,9 @@ int initialize(JavaVM* vm) { return facebook::jni::initialize(vm, [] { // Register native JNI methods margelo::nitro::rive::JHybridBindableArtboardSpec::registerNatives(); - margelo::nitro::rive::JHybridRiveSpec::registerNatives(); margelo::nitro::rive::JHybridRiveFileSpec::registerNatives(); margelo::nitro::rive::JHybridRiveFileFactorySpec::registerNatives(); + margelo::nitro::rive::JHybridRiveFontConfigSpec::registerNatives(); margelo::nitro::rive::JHybridRiveImageSpec::registerNatives(); margelo::nitro::rive::JHybridRiveImageFactorySpec::registerNatives(); margelo::nitro::rive::JHybridRiveRuntimeSpec::registerNatives(); @@ -83,9 +83,9 @@ int initialize(JavaVM* vm) { // Register Nitro Hybrid Objects HybridObjectRegistry::registerHybridObjectConstructor( - "Rive", + "RiveFontConfig", []() -> std::shared_ptr { - static DefaultConstructableObject object("com/margelo/nitro/rive/Rive"); + static DefaultConstructableObject object("com/margelo/nitro/rive/HybridRiveFontConfig"); auto instance = object.create(); return instance->cthis()->shared(); } diff --git a/nitrogen/generated/ios/RNRiveAutolinking.mm b/nitrogen/generated/ios/RNRiveAutolinking.mm index ed0e32a6..263fbb58 100644 --- a/nitrogen/generated/ios/RNRiveAutolinking.mm +++ b/nitrogen/generated/ios/RNRiveAutolinking.mm @@ -10,7 +10,7 @@ #import "RNRive-Swift-Cxx-Umbrella.hpp" #import -#include "HybridRiveSpecSwift.hpp" +#include "HybridRiveFontConfigSpecSwift.hpp" #include "HybridRiveFileFactorySpecSwift.hpp" #include "HybridRiveFileSpecSwift.hpp" #include "HybridRiveViewSpecSwift.hpp" @@ -27,9 +27,9 @@ + (void) load { using namespace margelo::nitro::rive; HybridObjectRegistry::registerHybridObjectConstructor( - "Rive", + "RiveFontConfig", []() -> std::shared_ptr { - std::shared_ptr hybridObject = RNRive::RNRiveAutolinking::createRive(); + std::shared_ptr hybridObject = RNRive::RNRiveAutolinking::createRiveFontConfig(); return hybridObject; } ); diff --git a/nitrogen/generated/ios/RNRiveAutolinking.swift b/nitrogen/generated/ios/RNRiveAutolinking.swift index 35432ce8..658b9bb4 100644 --- a/nitrogen/generated/ios/RNRiveAutolinking.swift +++ b/nitrogen/generated/ios/RNRiveAutolinking.swift @@ -12,16 +12,16 @@ import NitroModules public final class RNRiveAutolinking { public typealias bridge = margelo.nitro.rive.bridge.swift - public static func createRive() -> bridge.std__shared_ptr_HybridRiveSpec_ { - let hybridObject = Rive() - return { () -> bridge.std__shared_ptr_HybridRiveSpec_ in + public static func createRiveFontConfig() -> bridge.std__shared_ptr_HybridRiveFontConfigSpec_ { + let hybridObject = HybridRiveFontConfig() + return { () -> bridge.std__shared_ptr_HybridRiveFontConfigSpec_ in let __cxxWrapped = hybridObject.getCxxWrapper() return __cxxWrapped.getCxxPart() }() } - public static func isRiveRecyclable() -> Bool { - return Rive.self is any RecyclableView.Type + public static func isRiveFontConfigRecyclable() -> Bool { + return HybridRiveFontConfig.self is any RecyclableView.Type } public static func createRiveFileFactory() -> bridge.std__shared_ptr_HybridRiveFileFactorySpec_ { diff --git a/nitrogen/generated/ios/c++/HybridRiveSpecSwift.cpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.cpp similarity index 72% rename from nitrogen/generated/ios/c++/HybridRiveSpecSwift.cpp rename to nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.cpp index 06217812..404ef2b7 100644 --- a/nitrogen/generated/ios/c++/HybridRiveSpecSwift.cpp +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.cpp @@ -1,11 +1,11 @@ /// -/// HybridRiveSpecSwift.cpp +/// HybridRiveFontConfigSpecSwift.cpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro /// Copyright © Marc Rousavy @ Margelo /// -#include "HybridRiveSpecSwift.hpp" +#include "HybridRiveFontConfigSpecSwift.hpp" namespace margelo::nitro::rive { } // namespace margelo::nitro::rive diff --git a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp new file mode 100644 index 00000000..95f957d2 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp @@ -0,0 +1,118 @@ +/// +/// HybridRiveFontConfigSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridRiveFontConfigSpec.hpp" + +// Forward declaration of `HybridRiveFontConfigSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridRiveFontConfigSpec_cxx; } + +// Forward declaration of `ArrayBufferHolder` to properly resolve imports. +namespace NitroModules { class ArrayBufferHolder; } + +#include +#include +#include +#include + +#include "RNRive-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::rive { + + /** + * The C++ part of HybridRiveFontConfigSpec_cxx.swift. + * + * HybridRiveFontConfigSpecSwift (C++) accesses HybridRiveFontConfigSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridRiveFontConfigSpec_cxx can directly inherit from the C++ class HybridRiveFontConfigSpec + * to simplify the whole structure and memory management. + */ + class HybridRiveFontConfigSpecSwift: public virtual HybridRiveFontConfigSpec { + public: + // Constructor from a Swift instance + explicit HybridRiveFontConfigSpecSwift(const RNRive::HybridRiveFontConfigSpec_cxx& swiftPart): + HybridObject(HybridRiveFontConfigSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline RNRive::HybridRiveFontConfigSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + inline std::shared_ptr> enableSystemFontFallback() override { + auto __result = _swiftPart.enableSystemFontFallback(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) override { + auto __result = _swiftPart.addFallbackFont(ArrayBufferHolder(bytes)); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override { + auto __result = _swiftPart.addFallbackFontFromResource(resource); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> addFallbackFontFromURL(const std::string& url) override { + auto __result = _swiftPart.addFallbackFontFromURL(url); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + inline std::shared_ptr> clearCustomFallbackFonts() override { + auto __result = _swiftPart.clearCustomFallbackFonts(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } + + private: + RNRive::HybridRiveFontConfigSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/ios/c++/HybridRiveSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveSpecSwift.hpp deleted file mode 100644 index 705422d2..00000000 --- a/nitrogen/generated/ios/c++/HybridRiveSpecSwift.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/// -/// HybridRiveSpecSwift.hpp -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -#pragma once - -#include "HybridRiveSpec.hpp" - -// Forward declaration of `HybridRiveSpec_cxx` to properly resolve imports. -namespace RNRive { class HybridRiveSpec_cxx; } - - - - - -#include "RNRive-Swift-Cxx-Umbrella.hpp" - -namespace margelo::nitro::rive { - - /** - * The C++ part of HybridRiveSpec_cxx.swift. - * - * HybridRiveSpecSwift (C++) accesses HybridRiveSpec_cxx (Swift), and might - * contain some additional bridging code for C++ <> Swift interop. - * - * Since this obviously introduces an overhead, I hope at some point in - * the future, HybridRiveSpec_cxx can directly inherit from the C++ class HybridRiveSpec - * to simplify the whole structure and memory management. - */ - class HybridRiveSpecSwift: public virtual HybridRiveSpec { - public: - // Constructor from a Swift instance - explicit HybridRiveSpecSwift(const RNRive::HybridRiveSpec_cxx& swiftPart): - HybridObject(HybridRiveSpec::TAG), - _swiftPart(swiftPart) { } - - public: - // Get the Swift part - inline RNRive::HybridRiveSpec_cxx& getSwiftPart() noexcept { - return _swiftPart; - } - - public: - inline size_t getExternalMemorySize() noexcept override { - return _swiftPart.getMemorySize(); - } - bool equals(const std::shared_ptr& other) override { - if (auto otherCast = std::dynamic_pointer_cast(other)) { - return _swiftPart.equals(otherCast->_swiftPart); - } - return false; - } - void dispose() noexcept override { - _swiftPart.dispose(); - } - std::string toString() override { - return _swiftPart.toString(); - } - - public: - // Properties - - - public: - // Methods - inline double multiply(double a, double b) override { - auto __result = _swiftPart.multiply(std::forward(a), std::forward(b)); - if (__result.hasError()) [[unlikely]] { - std::rethrow_exception(__result.error()); - } - auto __value = std::move(__result.value()); - return __value; - } - - private: - RNRive::HybridRiveSpec_cxx _swiftPart; - }; - -} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift new file mode 100644 index 00000000..e6fe55ec --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift @@ -0,0 +1,60 @@ +/// +/// HybridRiveFontConfigSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/// See ``HybridRiveFontConfigSpec`` +public protocol HybridRiveFontConfigSpec_protocol: HybridObject { + // Properties + + + // Methods + func enableSystemFontFallback() throws -> Promise + func addFallbackFont(bytes: ArrayBuffer) throws -> Promise + func addFallbackFontFromResource(resource: String) throws -> Promise + func addFallbackFontFromURL(url: String) throws -> Promise + func clearCustomFallbackFonts() throws -> Promise +} + +public extension HybridRiveFontConfigSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject RiveFontConfig]" + } +} + +/// See ``HybridRiveFontConfigSpec`` +open class HybridRiveFontConfigSpec_base { + private weak var cxxWrapper: HybridRiveFontConfigSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridRiveFontConfigSpec_cxx { + #if DEBUG + guard self is any HybridRiveFontConfigSpec else { + fatalError("`self` is not a `HybridRiveFontConfigSpec`! Did you accidentally inherit from `HybridRiveFontConfigSpec_base` instead of `HybridRiveFontConfigSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridRiveFontConfigSpec_cxx(self as! any HybridRiveFontConfigSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the RiveFontConfig HybridObject. + * Implement this protocol to create Swift-based instances of RiveFontConfig. + * ```swift + * class HybridRiveFontConfig : HybridRiveFontConfigSpec { + * // ... + * } + * ``` + */ +public typealias HybridRiveFontConfigSpec = HybridRiveFontConfigSpec_protocol & HybridRiveFontConfigSpec_base diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift new file mode 100644 index 00000000..bd09f63a --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift @@ -0,0 +1,222 @@ +/// +/// HybridRiveFontConfigSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/** + * A class implementation that bridges HybridRiveFontConfigSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridRiveFontConfigSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::rive::bridge::swift`) + * from `RNRive-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.rive.bridge.swift + + /** + * Holds an instance of the `HybridRiveFontConfigSpec` Swift protocol. + */ + private var __implementation: any HybridRiveFontConfigSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridRiveFontConfigSpec_ + + /** + * Create a new `HybridRiveFontConfigSpec_cxx` that wraps the given `HybridRiveFontConfigSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridRiveFontConfigSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridRiveFontConfigSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridRiveFontConfigSpec() -> any HybridRiveFontConfigSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridRiveFontConfigSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridRiveFontConfigSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridRiveFontConfigSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridRiveFontConfigSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridRiveFontConfigSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridRiveFontConfigSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + @inline(__always) + public final func enableSystemFontFallback() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.enableSystemFontFallback() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func addFallbackFont(bytes: ArrayBuffer) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.addFallbackFont(bytes: bytes) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func addFallbackFontFromResource(resource: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.addFallbackFontFromResource(resource: String(resource)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func addFallbackFontFromURL(url: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.addFallbackFontFromURL(url: String(url)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + + @inline(__always) + public final func clearCustomFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.clearCustomFallbackFonts() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } +} diff --git a/nitrogen/generated/ios/swift/HybridRiveSpec.swift b/nitrogen/generated/ios/swift/HybridRiveSpec.swift deleted file mode 100644 index 8ca7f1e2..00000000 --- a/nitrogen/generated/ios/swift/HybridRiveSpec.swift +++ /dev/null @@ -1,56 +0,0 @@ -/// -/// HybridRiveSpec.swift -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -import Foundation -import NitroModules - -/// See ``HybridRiveSpec`` -public protocol HybridRiveSpec_protocol: HybridObject { - // Properties - - - // Methods - func multiply(a: Double, b: Double) throws -> Double -} - -public extension HybridRiveSpec_protocol { - /// Default implementation of ``HybridObject.toString`` - func toString() -> String { - return "[HybridObject Rive]" - } -} - -/// See ``HybridRiveSpec`` -open class HybridRiveSpec_base { - private weak var cxxWrapper: HybridRiveSpec_cxx? = nil - public init() { } - public func getCxxWrapper() -> HybridRiveSpec_cxx { - #if DEBUG - guard self is any HybridRiveSpec else { - fatalError("`self` is not a `HybridRiveSpec`! Did you accidentally inherit from `HybridRiveSpec_base` instead of `HybridRiveSpec`?") - } - #endif - if let cxxWrapper = self.cxxWrapper { - return cxxWrapper - } else { - let cxxWrapper = HybridRiveSpec_cxx(self as! any HybridRiveSpec) - self.cxxWrapper = cxxWrapper - return cxxWrapper - } - } -} - -/** - * A Swift base-protocol representing the Rive HybridObject. - * Implement this protocol to create Swift-based instances of Rive. - * ```swift - * class HybridRive : HybridRiveSpec { - * // ... - * } - * ``` - */ -public typealias HybridRiveSpec = HybridRiveSpec_protocol & HybridRiveSpec_base diff --git a/nitrogen/generated/ios/swift/HybridRiveSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveSpec_cxx.swift deleted file mode 100644 index 84ef93e3..00000000 --- a/nitrogen/generated/ios/swift/HybridRiveSpec_cxx.swift +++ /dev/null @@ -1,139 +0,0 @@ -/// -/// HybridRiveSpec_cxx.swift -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -import Foundation -import NitroModules - -/** - * A class implementation that bridges HybridRiveSpec over to C++. - * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. - * - * Also, some Swift types need to be bridged with special handling: - * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) - * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper - * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ - */ -open class HybridRiveSpec_cxx { - /** - * The Swift <> C++ bridge's namespace (`margelo::nitro::rive::bridge::swift`) - * from `RNRive-Swift-Cxx-Bridge.hpp`. - * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. - */ - public typealias bridge = margelo.nitro.rive.bridge.swift - - /** - * Holds an instance of the `HybridRiveSpec` Swift protocol. - */ - private var __implementation: any HybridRiveSpec - - /** - * Holds a weak pointer to the C++ class that wraps the Swift class. - */ - private var __cxxPart: bridge.std__weak_ptr_HybridRiveSpec_ - - /** - * Create a new `HybridRiveSpec_cxx` that wraps the given `HybridRiveSpec`. - * All properties and methods bridge to C++ types. - */ - public init(_ implementation: any HybridRiveSpec) { - self.__implementation = implementation - self.__cxxPart = .init() - /* no base class */ - } - - /** - * Get the actual `HybridRiveSpec` instance this class wraps. - */ - @inline(__always) - public func getHybridRiveSpec() -> any HybridRiveSpec { - return __implementation - } - - /** - * Casts this instance to a retained unsafe raw pointer. - * This acquires one additional strong reference on the object! - */ - public func toUnsafe() -> UnsafeMutableRawPointer { - return Unmanaged.passRetained(self).toOpaque() - } - - /** - * Casts an unsafe pointer to a `HybridRiveSpec_cxx`. - * The pointer has to be a retained opaque `Unmanaged`. - * This removes one strong reference from the object! - */ - public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridRiveSpec_cxx { - return Unmanaged.fromOpaque(pointer).takeRetainedValue() - } - - /** - * Gets (or creates) the C++ part of this Hybrid Object. - * The C++ part is a `std::shared_ptr`. - */ - public func getCxxPart() -> bridge.std__shared_ptr_HybridRiveSpec_ { - let cachedCxxPart = self.__cxxPart.lock() - if Bool(fromCxx: cachedCxxPart) { - return cachedCxxPart - } else { - let newCxxPart = bridge.create_std__shared_ptr_HybridRiveSpec_(self.toUnsafe()) - __cxxPart = bridge.weakify_std__shared_ptr_HybridRiveSpec_(newCxxPart) - return newCxxPart - } - } - - - - /** - * Get the memory size of the Swift class (plus size of any other allocations) - * so the JS VM can properly track it and garbage-collect the JS object if needed. - */ - @inline(__always) - public var memorySize: Int { - return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize - } - - /** - * Compares this object with the given [other] object for reference equality. - */ - @inline(__always) - public func equals(other: HybridRiveSpec_cxx) -> Bool { - return self.__implementation === other.__implementation - } - - /** - * Call dispose() on the Swift class. - * This _may_ be called manually from JS. - */ - @inline(__always) - public func dispose() { - self.__implementation.dispose() - } - - /** - * Call toString() on the Swift class. - */ - @inline(__always) - public func toString() -> String { - return self.__implementation.toString() - } - - // Properties - - - // Methods - @inline(__always) - public final func multiply(a: Double, b: Double) -> bridge.Result_double_ { - do { - let __result = try self.__implementation.multiply(a: a, b: b) - let __resultCpp = __result - return bridge.create_Result_double_(__resultCpp) - } catch (let __error) { - let __exceptionPtr = __error.toCpp() - return bridge.create_Result_double_(__exceptionPtr) - } - } -} diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp new file mode 100644 index 00000000..87685e34 --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp @@ -0,0 +1,25 @@ +/// +/// HybridRiveFontConfigSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridRiveFontConfigSpec.hpp" + +namespace margelo::nitro::rive { + + void HybridRiveFontConfigSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridMethod("enableSystemFontFallback", &HybridRiveFontConfigSpec::enableSystemFontFallback); + prototype.registerHybridMethod("addFallbackFont", &HybridRiveFontConfigSpec::addFallbackFont); + prototype.registerHybridMethod("addFallbackFontFromResource", &HybridRiveFontConfigSpec::addFallbackFontFromResource); + prototype.registerHybridMethod("addFallbackFontFromURL", &HybridRiveFontConfigSpec::addFallbackFontFromURL); + prototype.registerHybridMethod("clearCustomFallbackFonts", &HybridRiveFontConfigSpec::clearCustomFallbackFonts); + }); + } + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp new file mode 100644 index 00000000..eee8b0ab --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp @@ -0,0 +1,68 @@ +/// +/// HybridRiveFontConfigSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include +#include +#include + +namespace margelo::nitro::rive { + + using namespace margelo::nitro; + + /** + * An abstract base class for `RiveFontConfig` + * Inherit this class to create instances of `HybridRiveFontConfigSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridRiveFontConfig: public HybridRiveFontConfigSpec { + * public: + * HybridRiveFontConfig(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridRiveFontConfigSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridRiveFontConfigSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridRiveFontConfigSpec() override = default; + + public: + // Properties + + + public: + // Methods + virtual std::shared_ptr> enableSystemFontFallback() = 0; + virtual std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) = 0; + virtual std::shared_ptr> addFallbackFontFromResource(const std::string& resource) = 0; + virtual std::shared_ptr> addFallbackFontFromURL(const std::string& url) = 0; + virtual std::shared_ptr> clearCustomFallbackFonts() = 0; + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "RiveFontConfig"; + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/shared/c++/HybridRiveSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveSpec.cpp deleted file mode 100644 index 9309c469..00000000 --- a/nitrogen/generated/shared/c++/HybridRiveSpec.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/// -/// HybridRiveSpec.cpp -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -#include "HybridRiveSpec.hpp" - -namespace margelo::nitro::rive { - - void HybridRiveSpec::loadHybridMethods() { - // load base methods/properties - HybridObject::loadHybridMethods(); - // load custom methods/properties - registerHybrids(this, [](Prototype& prototype) { - prototype.registerHybridMethod("multiply", &HybridRiveSpec::multiply); - }); - } - -} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/shared/c++/HybridRiveSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveSpec.hpp deleted file mode 100644 index 8874aed1..00000000 --- a/nitrogen/generated/shared/c++/HybridRiveSpec.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/// -/// HybridRiveSpec.hpp -/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. -/// https://github.com/mrousavy/nitro -/// Copyright © Marc Rousavy @ Margelo -/// - -#pragma once - -#if __has_include() -#include -#else -#error NitroModules cannot be found! Are you sure you installed NitroModules properly? -#endif - - - - - -namespace margelo::nitro::rive { - - using namespace margelo::nitro; - - /** - * An abstract base class for `Rive` - * Inherit this class to create instances of `HybridRiveSpec` in C++. - * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. - * @example - * ```cpp - * class HybridRive: public HybridRiveSpec { - * public: - * HybridRive(...): HybridObject(TAG) { ... } - * // ... - * }; - * ``` - */ - class HybridRiveSpec: public virtual HybridObject { - public: - // Constructor - explicit HybridRiveSpec(): HybridObject(TAG) { } - - // Destructor - ~HybridRiveSpec() override = default; - - public: - // Properties - - - public: - // Methods - virtual double multiply(double a, double b) = 0; - - protected: - // Hybrid Setup - void loadHybridMethods() override; - - protected: - // Tag for logging - static constexpr auto TAG = "Rive"; - }; - -} // namespace margelo::nitro::rive diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts new file mode 100644 index 00000000..a040c89b --- /dev/null +++ b/src/core/RiveFonts.ts @@ -0,0 +1,61 @@ +import { NitroModules } from 'react-native-nitro-modules'; +import { Image } from 'react-native'; +import type { RiveFontConfig } from '../specs/RiveFontConfig.nitro'; + +const RiveFontConfigInternal = + NitroModules.createHybridObject('RiveFontConfig'); + +/** + * A font source that can be: + * - `number` — a Metro `require('./Font.ttf')` asset ID + * - `{ uri: string }` — a URI object (http/https URL, file:// path, or resource name) + * - `string` — a native resource name (e.g. "kanit_regular.ttf") or URL + * - `ArrayBuffer` — raw font bytes (TTF/OTF) + */ +export type FontSource = number | { uri: string } | string | ArrayBuffer; + +export namespace RiveFonts { + export async function enableSystemFontFallback(): Promise { + return RiveFontConfigInternal.enableSystemFontFallback(); + } + + export async function addFallbackFont(source: FontSource): Promise { + if (source instanceof ArrayBuffer) { + return RiveFontConfigInternal.addFallbackFont(source); + } + + if (typeof source === 'number') { + const resolved = Image.resolveAssetSource(source); + if (!resolved?.uri) { + throw new Error( + `Invalid font asset: could not resolve require() ID ${source}. Ensure 'ttf' is in metro.config.js assetExts.` + ); + } + return dispatchByURI(resolved.uri); + } + + if (typeof source === 'object' && 'uri' in source) { + return dispatchByURI(source.uri); + } + + if (typeof source === 'string') { + if (/^https?:\/\//.test(source) || /^file:\/\//.test(source)) { + return RiveFontConfigInternal.addFallbackFontFromURL(source); + } + return RiveFontConfigInternal.addFallbackFontFromResource(source); + } + + throw new Error(`Invalid font source: ${String(source)}`); + } + + export async function clearCustomFallbackFonts(): Promise { + return RiveFontConfigInternal.clearCustomFallbackFonts(); + } +} + +function dispatchByURI(uri: string): Promise { + if (/^https?:\/\//.test(uri) || /^file:\/\//.test(uri)) { + return RiveFontConfigInternal.addFallbackFontFromURL(uri); + } + return RiveFontConfigInternal.addFallbackFontFromResource(uri); +} diff --git a/src/index.tsx b/src/index.tsx index b4bad0b6..1b36a5e5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -39,6 +39,7 @@ export { Alignment } from './core/Alignment'; export { RiveFileFactory } from './core/RiveFile'; export { RiveImages } from './core/RiveImages'; export type { RiveImage } from './specs/RiveImage.nitro'; +export { RiveFonts, type FontSource } from './core/RiveFonts'; export { RiveColor } from './core/RiveColor'; export { type RiveEvent, RiveEventType } from './core/Events'; export { type RiveError, RiveErrorType } from './core/Errors'; diff --git a/src/specs/Rive.nitro.ts b/src/specs/Rive.nitro.ts deleted file mode 100644 index c126a78d..00000000 --- a/src/specs/Rive.nitro.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { HybridObject } from 'react-native-nitro-modules'; - -export interface Rive - extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { - multiply(a: number, b: number): number; -} diff --git a/src/specs/RiveFontConfig.nitro.ts b/src/specs/RiveFontConfig.nitro.ts new file mode 100644 index 00000000..266d8e72 --- /dev/null +++ b/src/specs/RiveFontConfig.nitro.ts @@ -0,0 +1,34 @@ +import type { HybridObject } from 'react-native-nitro-modules'; + +export interface RiveFontConfig + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Enable system font fallback for missing glyphs (CJK, Arabic, Hebrew, etc.). + * On iOS, this activates Core Text's automatic font matching. + * On Android, this sets up a FontFallbackStrategy using system fonts. + */ + enableSystemFontFallback(): Promise; + + /** + * Add a custom fallback font from raw bytes (TTF/OTF). + * Custom fonts are tried before system fonts when resolving missing glyphs. + */ + addFallbackFont(bytes: ArrayBuffer): Promise; + + /** + * Add a custom fallback font from a bundled resource. + * @param resource - The resource name (e.g., "NotoSansCJK.ttf") + */ + addFallbackFontFromResource(resource: string): Promise; + + /** + * Add a custom fallback font from a URL (http/https/file://). + */ + addFallbackFontFromURL(url: string): Promise; + + /** + * Clear all custom fallback fonts. + * System font fallback remains active if previously enabled. + */ + clearCustomFallbackFonts(): Promise; +} From 6814e516001d81ac9f632b4840292f8e07513e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 23 Feb 2026 12:53:24 +0100 Subject: [PATCH 02/20] fix: add kanit_regular.ttf to Xcode bundle resources The font file existed in example/ios/ but wasn't referenced in the Xcode project, so addFallbackFontFromResource couldn't find it at runtime. --- example/ios/RiveExample.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/ios/RiveExample.xcodeproj/project.pbxproj b/example/ios/RiveExample.xcodeproj/project.pbxproj index ade84b2a..86c5844b 100644 --- a/example/ios/RiveExample.xcodeproj/project.pbxproj +++ b/example/ios/RiveExample.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; F8EBA76E2DD7CE540010BBD0 /* rewards.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8EBA76D2DD7CE540010BBD0 /* rewards.riv */; }; + F8EBA7712DD7CF000010BBD0 /* kanit_regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F8EBA7702DD7CF000010BBD0 /* kanit_regular.ttf */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -27,6 +28,7 @@ E0AD05C932EEB95D292EBDBA /* Pods-RiveExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiveExample.release.xcconfig"; path = "Target Support Files/Pods-RiveExample/Pods-RiveExample.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F8EBA76D2DD7CE540010BBD0 /* rewards.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = rewards.riv; sourceTree = ""; }; + F8EBA7702DD7CF000010BBD0 /* kanit_regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = kanit_regular.ttf; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -60,6 +62,7 @@ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, F8EBA76D2DD7CE540010BBD0 /* rewards.riv */, + F8EBA7702DD7CF000010BBD0 /* kanit_regular.ttf */, ); name = RiveExample; sourceTree = ""; @@ -175,6 +178,7 @@ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, F8EBA76E2DD7CE540010BBD0 /* rewards.riv in Resources */, + F8EBA7712DD7CF000010BBD0 /* kanit_regular.ttf in Resources */, 35B325D75078933C519F2ACC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; From a18865727c91bcb42259076978cf4e0cfde3ce03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 23 Feb 2026 13:25:01 +0100 Subject: [PATCH 03/20] fix: guard setRiveText call until instance is ready Prevents "Cannot set value for property" error on initial render when the riv file hasn't loaded yet. --- example/src/exercisers/FontFallbackExample.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 4f286545..7f45bee1 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -19,6 +19,7 @@ import { } from '@rive-app/react-native'; import { type Metadata } from '../shared/metadata'; +// Rive file: https://rive.app/community/files/26480-49641-simple-test-text-property/ const TEXT_PROPERTY = 'text'; const FONTS: Record = { @@ -231,9 +232,10 @@ function MountedView({ text }: { text: string }) { } useEffect(() => { + if (!instance) return; setRiveText(text); riveViewRef?.playIfNeeded(); - }, [text, setRiveText, riveViewRef]); + }, [text, setRiveText, riveViewRef, instance]); if (isLoading) { return ( From 85403548a0bb69be8f16b807632c804ed8e04fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 23 Feb 2026 14:02:39 +0100 Subject: [PATCH 04/20] Revert "fix: guard setRiveText call until instance is ready" This reverts commit 16688d677ecf86156a43097b033fd2aa391691f4. --- example/src/exercisers/FontFallbackExample.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 7f45bee1..4f286545 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -19,7 +19,6 @@ import { } from '@rive-app/react-native'; import { type Metadata } from '../shared/metadata'; -// Rive file: https://rive.app/community/files/26480-49641-simple-test-text-property/ const TEXT_PROPERTY = 'text'; const FONTS: Record = { @@ -232,10 +231,9 @@ function MountedView({ text }: { text: string }) { } useEffect(() => { - if (!instance) return; setRiveText(text); riveViewRef?.playIfNeeded(); - }, [text, setRiveText, riveViewRef, instance]); + }, [text, setRiveText, riveViewRef]); if (isLoading) { return ( From 48c76e5ee3d610b4f6b1cd18767af04b61dcc4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 23 Feb 2026 16:09:23 +0100 Subject: [PATCH 05/20] style: fix prettier formatting in FontFallbackExample --- example/src/exercisers/FontFallbackExample.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 4f286545..ac00028f 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -21,7 +21,10 @@ import { type Metadata } from '../shared/metadata'; const TEXT_PROPERTY = 'text'; -const FONTS: Record = { +const FONTS: Record< + string, + { label: string; sublabel: string; source: FontSource } +> = { kanitBundled: { label: 'Kanit (Bundled)', sublabel: 'From native resources — 173KB', @@ -141,8 +144,16 @@ export default function FontFallbackExample() { - - + + )} From 7c3a85064d386a8cb02c000f4cb0b2566388a7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 23 Feb 2026 16:26:41 +0100 Subject: [PATCH 06/20] test: track transient errors in harness test for reliable regression detection --- example/__tests__/hooks.harness.tsx | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/example/__tests__/hooks.harness.tsx b/example/__tests__/hooks.harness.tsx index b4bb234b..7f460769 100644 --- a/example/__tests__/hooks.harness.tsx +++ b/example/__tests__/hooks.harness.tsx @@ -170,12 +170,22 @@ describe('useViewModelInstance hook', () => { }); }); -class UseRiveStringContext { - value: string | undefined = undefined; - error: Error | null = null; - errorEverSet = false; - setValue: ((v: string) => void) | null = null; - setValueCalledBeforeReady = false; +type UseRiveStringContext = { + value: string | undefined; + error: Error | null; + errorEverSet: boolean; + setValue: ((v: string) => void) | null; + setValueCalledBeforeReady: boolean; +}; + +function createUseRiveStringContext(): UseRiveStringContext { + return { + value: undefined, + error: null, + errorEverSet: false, + setValue: null, + setValueCalledBeforeReady: false, + }; } /** @@ -223,7 +233,7 @@ function UseRiveStringBeforeReadyComponent({ describe('useRiveString setValue before ready (#141)', () => { it('does not error when setValue is called before instance loads', async () => { - const context = new UseRiveStringContext(); + const context = createUseRiveStringContext(); await render( Date: Mon, 23 Feb 2026 20:48:37 +0100 Subject: [PATCH 07/20] refactor: convert UseRiveStringContext to class --- example/__tests__/hooks.harness.tsx | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/example/__tests__/hooks.harness.tsx b/example/__tests__/hooks.harness.tsx index 7f460769..b4bb234b 100644 --- a/example/__tests__/hooks.harness.tsx +++ b/example/__tests__/hooks.harness.tsx @@ -170,22 +170,12 @@ describe('useViewModelInstance hook', () => { }); }); -type UseRiveStringContext = { - value: string | undefined; - error: Error | null; - errorEverSet: boolean; - setValue: ((v: string) => void) | null; - setValueCalledBeforeReady: boolean; -}; - -function createUseRiveStringContext(): UseRiveStringContext { - return { - value: undefined, - error: null, - errorEverSet: false, - setValue: null, - setValueCalledBeforeReady: false, - }; +class UseRiveStringContext { + value: string | undefined = undefined; + error: Error | null = null; + errorEverSet = false; + setValue: ((v: string) => void) | null = null; + setValueCalledBeforeReady = false; } /** @@ -233,7 +223,7 @@ function UseRiveStringBeforeReadyComponent({ describe('useRiveString setValue before ready (#141)', () => { it('does not error when setValue is called before instance loads', async () => { - const context = createUseRiveStringContext(); + const context = new UseRiveStringContext(); await render( Date: Tue, 24 Feb 2026 15:39:48 +0100 Subject: [PATCH 08/20] feat: add system font by name support and array-based addFallbackFonts API Adds `{ name: string }` to FontSource for system fonts (e.g. `{ name: 'Thonburi' }` on iOS, `{ name: 'serif' }` on Android). Renames `addFallbackFont` to `addFallbackFonts` accepting an array to make font ordering explicit and avoid sequential awaits. --- .../nitro/rive/HybridRiveFontConfig.kt | 21 +++++- .../src/exercisers/FontFallbackExample.tsx | 27 ++++++-- ios/HybridRiveFontConfig.swift | 11 ++++ .../android/c++/JHybridRiveFontConfigSpec.cpp | 15 +++++ .../android/c++/JHybridRiveFontConfigSpec.hpp | 1 + .../nitro/rive/HybridRiveFontConfigSpec.kt | 4 ++ .../ios/c++/HybridRiveFontConfigSpecSwift.hpp | 8 +++ .../ios/swift/HybridRiveFontConfigSpec.swift | 1 + .../swift/HybridRiveFontConfigSpec_cxx.swift | 19 ++++++ .../shared/c++/HybridRiveFontConfigSpec.cpp | 1 + .../shared/c++/HybridRiveFontConfigSpec.hpp | 1 + src/core/RiveFonts.ts | 64 ++++++++++++------- src/specs/RiveFontConfig.nitro.ts | 6 ++ 13 files changed, 150 insertions(+), 29 deletions(-) diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index ebd74ec2..03271c32 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -30,7 +30,12 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { } val systemFonts = FontHelper.getFallbackFonts(Fonts.FontOpts(weight = weight)) for (font in systemFonts) { - FontHelper.getFontBytes(font)?.let { result.add(it) } + try { + FontHelper.getFontBytes(font)?.let { result.add(it) } + } catch (e: OutOfMemoryError) { + Log.w(TAG, "Skipped system font due to memory pressure: ${e.message}") + break + } } return result } @@ -117,6 +122,20 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { } } + override fun addFallbackFontByName(name: String): Promise { + return Promise.async { + ensureSystemFallback() + val fonts = FontHelper.getFallbackFonts(Fonts.FontOpts(familyName = name)) + if (fonts.isEmpty()) { + throw Error("System font not found: $name") + } + val fontBytes = FontHelper.getFontBytes(fonts.first()) + ?: throw Error("Could not read font bytes for: $name") + customFonts.add(fontBytes) + resetFontCache() + } + } + override fun clearCustomFallbackFonts(): Promise { return Promise.async { customFonts.clear() diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index ac00028f..21db533b 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -44,6 +44,16 @@ const FONTS: Record< uri: 'https://raw.githubusercontent.com/google/fonts/main/ofl/notoserifthai/NotoSerifThai%5Bwdth%2Cwght%5D.ttf', }, }, + thonburiSystem: { + label: 'Thonburi (System)', + sublabel: 'Built-in Thai font — iOS only', + source: { name: 'Thonburi' }, + }, + serifSystem: { + label: 'serif (System)', + sublabel: 'Noto Serif — Android only', + source: { name: 'serif' }, + }, }; type FontKey = keyof typeof FONTS; @@ -82,7 +92,7 @@ export default function FontFallbackExample() { setLoadingFont(key); try { - await RiveFonts.addFallbackFont(font.source); + await RiveFonts.addFallbackFonts([font.source]); addLog(`Added ${font.label} as fallback font`, 'success'); setSelectedFonts((prev) => new Set(prev).add(key)); } catch (err) { @@ -95,7 +105,15 @@ export default function FontFallbackExample() { }; const handleMount = async () => { - await RiveFonts.enableSystemFontFallback(); + try { + // addFallbackFonts already enables system fallback internally, + // so this is only needed for the "no custom fonts" path + if (selectedFonts.size === 0) { + await RiveFonts.enableSystemFontFallback(); + } + } catch (err) { + console.error('enableSystemFontFallback:', err); + } setMounted(true); addLog( `Mounted with: ${selectedFonts.size > 0 ? [...selectedFonts].map((k) => FONTS[k]!.label).join(', ') : 'system defaults only'}`, @@ -221,6 +239,7 @@ export default function FontFallbackExample() { function MountedView({ text }: { text: string }) { const { riveViewRef, setHybridRef } = useRive(); const { riveFile, isLoading, error } = useRiveFile( + // https://rive.app/marketplace/26480-49641-simple-test-text-property/ require('../../assets/rive/font_fallback.riv') ); const viewModel = useMemo( @@ -372,7 +391,7 @@ const styles = StyleSheet.create({ backgroundColor: '#fff', }, setupContainer: { - height: 220, + height: 120, backgroundColor: '#f0f0f0', justifyContent: 'center', alignItems: 'center', @@ -390,7 +409,7 @@ const styles = StyleSheet.create({ textAlign: 'center', }, riveContainer: { - height: 220, + height: 120, backgroundColor: '#f0f0f0', justifyContent: 'center', alignItems: 'center', diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift index 064bf686..e7713e21 100644 --- a/ios/HybridRiveFontConfig.swift +++ b/ios/HybridRiveFontConfig.swift @@ -52,6 +52,17 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { } } + func addFallbackFontByName(name: String) throws -> Promise { + return Promise.async { + Self.ensureSystemFallback() + guard let font = UIFont(name: name, size: UIFont.systemFontSize) else { + throw RuntimeError.error(withMessage: "System font not found: \(name)") + } + Self.customFonts.append(font) + Self.updateFallbackFonts() + } + } + func clearCustomFallbackFonts() throws -> Promise { return Promise.async { Self.customFonts.removeAll() diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp index f8eca05b..0e1ea099 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp @@ -115,6 +115,21 @@ namespace margelo::nitro::rive { return __promise; }(); } + std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontByName(const std::string& name) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* name */)>("addFallbackFontByName"); + auto __result = method(_javaPart, jni::make_jstring(name)); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } std::shared_ptr> JHybridRiveFontConfigSpec::clearCustomFallbackFonts() { static const auto method = javaClassStatic()->getMethod()>("clearCustomFallbackFonts"); auto __result = method(_javaPart); diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp index b7877b9a..32cba42a 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp @@ -59,6 +59,7 @@ namespace margelo::nitro::rive { std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) override; std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override; std::shared_ptr> addFallbackFontFromURL(const std::string& url) override; + std::shared_ptr> addFallbackFontByName(const std::string& name) override; std::shared_ptr> clearCustomFallbackFonts() override; private: diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt index 8d6e9b89..a4047cef 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt @@ -63,6 +63,10 @@ abstract class HybridRiveFontConfigSpec: HybridObject() { @Keep abstract fun addFallbackFontFromURL(url: String): Promise + @DoNotStrip + @Keep + abstract fun addFallbackFontByName(name: String): Promise + @DoNotStrip @Keep abstract fun clearCustomFallbackFonts(): Promise diff --git a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp index 95f957d2..1cd3cd33 100644 --- a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp @@ -102,6 +102,14 @@ namespace margelo::nitro::rive { auto __value = std::move(__result.value()); return __value; } + inline std::shared_ptr> addFallbackFontByName(const std::string& name) override { + auto __result = _swiftPart.addFallbackFontByName(name); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } inline std::shared_ptr> clearCustomFallbackFonts() override { auto __result = _swiftPart.clearCustomFallbackFonts(); if (__result.hasError()) [[unlikely]] { diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift index e6fe55ec..b0eead30 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift @@ -18,6 +18,7 @@ public protocol HybridRiveFontConfigSpec_protocol: HybridObject { func addFallbackFont(bytes: ArrayBuffer) throws -> Promise func addFallbackFontFromResource(resource: String) throws -> Promise func addFallbackFontFromURL(url: String) throws -> Promise + func addFallbackFontByName(name: String) throws -> Promise func clearCustomFallbackFonts() throws -> Promise } diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift index bd09f63a..30a06fa0 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift @@ -201,6 +201,25 @@ open class HybridRiveFontConfigSpec_cxx { } } + @inline(__always) + public final func addFallbackFontByName(name: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.addFallbackFontByName(name: String(name)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + @inline(__always) public final func clearCustomFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { do { diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp index 87685e34..baac405f 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp @@ -18,6 +18,7 @@ namespace margelo::nitro::rive { prototype.registerHybridMethod("addFallbackFont", &HybridRiveFontConfigSpec::addFallbackFont); prototype.registerHybridMethod("addFallbackFontFromResource", &HybridRiveFontConfigSpec::addFallbackFontFromResource); prototype.registerHybridMethod("addFallbackFontFromURL", &HybridRiveFontConfigSpec::addFallbackFontFromURL); + prototype.registerHybridMethod("addFallbackFontByName", &HybridRiveFontConfigSpec::addFallbackFontByName); prototype.registerHybridMethod("clearCustomFallbackFonts", &HybridRiveFontConfigSpec::clearCustomFallbackFonts); }); } diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp index eee8b0ab..b113d648 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp @@ -54,6 +54,7 @@ namespace margelo::nitro::rive { virtual std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) = 0; virtual std::shared_ptr> addFallbackFontFromResource(const std::string& resource) = 0; virtual std::shared_ptr> addFallbackFontFromURL(const std::string& url) = 0; + virtual std::shared_ptr> addFallbackFontByName(const std::string& name) = 0; virtual std::shared_ptr> clearCustomFallbackFonts() = 0; protected: diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index a040c89b..524d7eb1 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -10,47 +10,63 @@ const RiveFontConfigInternal = * - `number` — a Metro `require('./Font.ttf')` asset ID * - `{ uri: string }` — a URI object (http/https URL, file:// path, or resource name) * - `string` — a native resource name (e.g. "kanit_regular.ttf") or URL + * - `{ name: string }` — a system font name (e.g. "Arial", "sans-serif") * - `ArrayBuffer` — raw font bytes (TTF/OTF) */ -export type FontSource = number | { uri: string } | string | ArrayBuffer; +export type FontSource = + | number + | { uri: string } + | { name: string } + | string + | ArrayBuffer; export namespace RiveFonts { export async function enableSystemFontFallback(): Promise { return RiveFontConfigInternal.enableSystemFontFallback(); } - export async function addFallbackFont(source: FontSource): Promise { - if (source instanceof ArrayBuffer) { - return RiveFontConfigInternal.addFallbackFont(source); + export async function addFallbackFonts(sources: FontSource[]): Promise { + for (const source of sources) { + await addSingleFont(source); } + } - if (typeof source === 'number') { - const resolved = Image.resolveAssetSource(source); - if (!resolved?.uri) { - throw new Error( - `Invalid font asset: could not resolve require() ID ${source}. Ensure 'ttf' is in metro.config.js assetExts.` - ); - } - return dispatchByURI(resolved.uri); - } + export async function clearCustomFallbackFonts(): Promise { + return RiveFontConfigInternal.clearCustomFallbackFonts(); + } +} - if (typeof source === 'object' && 'uri' in source) { - return dispatchByURI(source.uri); - } +async function addSingleFont(source: FontSource): Promise { + if (source instanceof ArrayBuffer) { + return RiveFontConfigInternal.addFallbackFont(source); + } - if (typeof source === 'string') { - if (/^https?:\/\//.test(source) || /^file:\/\//.test(source)) { - return RiveFontConfigInternal.addFallbackFontFromURL(source); - } - return RiveFontConfigInternal.addFallbackFontFromResource(source); + if (typeof source === 'number') { + const resolved = Image.resolveAssetSource(source); + if (!resolved?.uri) { + throw new Error( + `Invalid font asset: could not resolve require() ID ${source}. Ensure 'ttf' is in metro.config.js assetExts.` + ); } + return dispatchByURI(resolved.uri); + } - throw new Error(`Invalid font source: ${String(source)}`); + if (typeof source === 'object' && 'name' in source) { + return RiveFontConfigInternal.addFallbackFontByName(source.name); } - export async function clearCustomFallbackFonts(): Promise { - return RiveFontConfigInternal.clearCustomFallbackFonts(); + if (typeof source === 'object' && 'uri' in source) { + return dispatchByURI(source.uri); } + + if (typeof source === 'string') { + if (/^https?:\/\//.test(source) || /^file:\/\//.test(source)) { + return RiveFontConfigInternal.addFallbackFontFromURL(source); + } + return RiveFontConfigInternal.addFallbackFontFromResource(source); + } + + throw new Error(`Invalid font source: ${String(source)}`); } function dispatchByURI(uri: string): Promise { diff --git a/src/specs/RiveFontConfig.nitro.ts b/src/specs/RiveFontConfig.nitro.ts index 266d8e72..0763b67a 100644 --- a/src/specs/RiveFontConfig.nitro.ts +++ b/src/specs/RiveFontConfig.nitro.ts @@ -26,6 +26,12 @@ export interface RiveFontConfig */ addFallbackFontFromURL(url: string): Promise; + /** + * Add a system font as a fallback by its font family name. + * On iOS uses UIFont(name:), on Android uses FontHelper to resolve system fonts. + */ + addFallbackFontByName(name: string): Promise; + /** * Clear all custom fallback fonts. * System font fallback remains active if previously enabled. From 07a23e9f97098fcb3c41a4a6bc126348b2868b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 24 Feb 2026 15:54:13 +0100 Subject: [PATCH 09/20] docs: add JSDoc to addFallbackFonts --- src/core/RiveFonts.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index 524d7eb1..e9ccb4ca 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -25,6 +25,11 @@ export namespace RiveFonts { return RiveFontConfigInternal.enableSystemFontFallback(); } + /** + * Add fallback fonts for missing glyphs in Rive graphics. + * Fonts are tried in array order — first font that contains the glyph wins. + * These fonts are tried before the default system font fallback. + */ export async function addFallbackFonts(sources: FontSource[]): Promise { for (const source of sources) { await addSingleFont(source); From 03b5d4b6fad92cb3cfc20d4bb3149e4b6422c156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 24 Feb 2026 16:04:20 +0100 Subject: [PATCH 10/20] refactor: batch font cache reset via applyFallbackFonts --- .../nitro/rive/HybridRiveFontConfig.kt | 10 +++++----- ios/HybridRiveFontConfig.swift | 8 +++++--- .../android/c++/JHybridRiveFontConfigSpec.cpp | 15 +++++++++++++++ .../android/c++/JHybridRiveFontConfigSpec.hpp | 1 + .../nitro/rive/HybridRiveFontConfigSpec.kt | 4 ++++ .../ios/c++/HybridRiveFontConfigSpecSwift.hpp | 8 ++++++++ .../ios/swift/HybridRiveFontConfigSpec.swift | 1 + .../swift/HybridRiveFontConfigSpec_cxx.swift | 19 +++++++++++++++++++ .../shared/c++/HybridRiveFontConfigSpec.cpp | 1 + .../shared/c++/HybridRiveFontConfigSpec.hpp | 1 + src/core/RiveFonts.ts | 1 + src/specs/RiveFontConfig.nitro.ts | 6 ++++++ 12 files changed, 67 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index 03271c32..606a8bea 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -69,7 +69,6 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { val byteArray = ByteArray(buffer.remaining()) buffer.get(byteArray) customFonts.add(byteArray) - resetFontCache() } } @@ -85,7 +84,6 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { if (rawId != 0) { val fontBytes = context.resources.openRawResource(rawId).use { it.readBytes() } customFonts.add(fontBytes) - resetFontCache() return@async } @@ -93,7 +91,6 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { if (fontId != 0) { val fontBytes = context.resources.openRawResource(fontId).use { it.readBytes() } customFonts.add(fontBytes) - resetFontCache() return@async } @@ -102,7 +99,6 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { try { val fontBytes = context.assets.open(assetPath).use { it.readBytes() } customFonts.add(fontBytes) - resetFontCache() return@async } catch (_: Exception) {} } @@ -118,7 +114,6 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { URL(url).readBytes() } customFonts.add(fontBytes) - resetFontCache() } } @@ -132,6 +127,11 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { val fontBytes = FontHelper.getFontBytes(fonts.first()) ?: throw Error("Could not read font bytes for: $name") customFonts.add(fontBytes) + } + } + + override fun applyFallbackFonts(): Promise { + return Promise.async { resetFontCache() } } diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift index e7713e21..95a27111 100644 --- a/ios/HybridRiveFontConfig.swift +++ b/ios/HybridRiveFontConfig.swift @@ -18,7 +18,6 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { let data = bytes.toData(copyIfNeeded: true) let font = try Self.createUIFont(from: data) Self.customFonts.append(font) - Self.updateFallbackFonts() } } @@ -35,7 +34,6 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { let data = try Data(contentsOf: URL(fileURLWithPath: path)) let font = try Self.createUIFont(from: data) Self.customFonts.append(font) - Self.updateFallbackFonts() } } @@ -48,7 +46,6 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { let (data, _) = try await URLSession.shared.data(from: parsedURL) let font = try Self.createUIFont(from: data) Self.customFonts.append(font) - Self.updateFallbackFonts() } } @@ -59,6 +56,11 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { throw RuntimeError.error(withMessage: "System font not found: \(name)") } Self.customFonts.append(font) + } + } + + func applyFallbackFonts() throws -> Promise { + return Promise.async { Self.updateFallbackFonts() } } diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp index 0e1ea099..734dd0a1 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp @@ -130,6 +130,21 @@ namespace margelo::nitro::rive { return __promise; }(); } + std::shared_ptr> JHybridRiveFontConfigSpec::applyFallbackFonts() { + static const auto method = javaClassStatic()->getMethod()>("applyFallbackFonts"); + auto __result = method(_javaPart); + return [&]() { + auto __promise = Promise::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { + __promise->resolve(); + }); + __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { + jni::JniException __jniError(__throwable); + __promise->reject(std::make_exception_ptr(__jniError)); + }); + return __promise; + }(); + } std::shared_ptr> JHybridRiveFontConfigSpec::clearCustomFallbackFonts() { static const auto method = javaClassStatic()->getMethod()>("clearCustomFallbackFonts"); auto __result = method(_javaPart); diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp index 32cba42a..98fa1917 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp @@ -60,6 +60,7 @@ namespace margelo::nitro::rive { std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override; std::shared_ptr> addFallbackFontFromURL(const std::string& url) override; std::shared_ptr> addFallbackFontByName(const std::string& name) override; + std::shared_ptr> applyFallbackFonts() override; std::shared_ptr> clearCustomFallbackFonts() override; private: diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt index a4047cef..9ce3e4cf 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt @@ -67,6 +67,10 @@ abstract class HybridRiveFontConfigSpec: HybridObject() { @Keep abstract fun addFallbackFontByName(name: String): Promise + @DoNotStrip + @Keep + abstract fun applyFallbackFonts(): Promise + @DoNotStrip @Keep abstract fun clearCustomFallbackFonts(): Promise diff --git a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp index 1cd3cd33..29d55b52 100644 --- a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp @@ -110,6 +110,14 @@ namespace margelo::nitro::rive { auto __value = std::move(__result.value()); return __value; } + inline std::shared_ptr> applyFallbackFonts() override { + auto __result = _swiftPart.applyFallbackFonts(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } inline std::shared_ptr> clearCustomFallbackFonts() override { auto __result = _swiftPart.clearCustomFallbackFonts(); if (__result.hasError()) [[unlikely]] { diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift index b0eead30..b52e9f2d 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift @@ -19,6 +19,7 @@ public protocol HybridRiveFontConfigSpec_protocol: HybridObject { func addFallbackFontFromResource(resource: String) throws -> Promise func addFallbackFontFromURL(url: String) throws -> Promise func addFallbackFontByName(name: String) throws -> Promise + func applyFallbackFonts() throws -> Promise func clearCustomFallbackFonts() throws -> Promise } diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift index 30a06fa0..031a7f00 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift @@ -220,6 +220,25 @@ open class HybridRiveFontConfigSpec_cxx { } } + @inline(__always) + public final func applyFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { + do { + let __result = try self.__implementation.applyFallbackFonts() + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in + let __promise = bridge.create_std__shared_ptr_Promise_void__() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + __result + .then({ __result in __promiseHolder.resolve() }) + .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) + return __promise + }() + return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + } + } + @inline(__always) public final func clearCustomFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { do { diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp index baac405f..4f554a6d 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp @@ -19,6 +19,7 @@ namespace margelo::nitro::rive { prototype.registerHybridMethod("addFallbackFontFromResource", &HybridRiveFontConfigSpec::addFallbackFontFromResource); prototype.registerHybridMethod("addFallbackFontFromURL", &HybridRiveFontConfigSpec::addFallbackFontFromURL); prototype.registerHybridMethod("addFallbackFontByName", &HybridRiveFontConfigSpec::addFallbackFontByName); + prototype.registerHybridMethod("applyFallbackFonts", &HybridRiveFontConfigSpec::applyFallbackFonts); prototype.registerHybridMethod("clearCustomFallbackFonts", &HybridRiveFontConfigSpec::clearCustomFallbackFonts); }); } diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp index b113d648..8b22a5c6 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp @@ -55,6 +55,7 @@ namespace margelo::nitro::rive { virtual std::shared_ptr> addFallbackFontFromResource(const std::string& resource) = 0; virtual std::shared_ptr> addFallbackFontFromURL(const std::string& url) = 0; virtual std::shared_ptr> addFallbackFontByName(const std::string& name) = 0; + virtual std::shared_ptr> applyFallbackFonts() = 0; virtual std::shared_ptr> clearCustomFallbackFonts() = 0; protected: diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index e9ccb4ca..8147dd8b 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -34,6 +34,7 @@ export namespace RiveFonts { for (const source of sources) { await addSingleFont(source); } + await RiveFontConfigInternal.applyFallbackFonts(); } export async function clearCustomFallbackFonts(): Promise { diff --git a/src/specs/RiveFontConfig.nitro.ts b/src/specs/RiveFontConfig.nitro.ts index 0763b67a..255393a9 100644 --- a/src/specs/RiveFontConfig.nitro.ts +++ b/src/specs/RiveFontConfig.nitro.ts @@ -32,6 +32,12 @@ export interface RiveFontConfig */ addFallbackFontByName(name: string): Promise; + /** + * Apply pending font changes — updates the native fallback font list and resets caches. + * Called once after adding fonts, rather than after each individual add. + */ + applyFallbackFonts(): Promise; + /** * Clear all custom fallback fonts. * System font fallback remains active if previously enabled. From d372290762ed5895a28c3dcf3e476b26f454fb79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 24 Feb 2026 16:11:29 +0100 Subject: [PATCH 11/20] fix: ktlint try-catch-finally-spacing --- .../main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index 606a8bea..adbb6e01 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -100,7 +100,8 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { val fontBytes = context.assets.open(assetPath).use { it.readBytes() } customFonts.add(fontBytes) return@async - } catch (_: Exception) {} + } catch (_: Exception) { + } } throw Error("Font resource not found: $resource (checked res/raw, res/font, assets/fonts)") From e1bc43bc794e34d29f152ab3a654531e02c8a984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 27 Feb 2026 10:25:52 +0100 Subject: [PATCH 12/20] refactor: two-phase font loading with weight-based fallback map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate font loading from configuration — loadFont() returns opaque FallbackFont handles, setFallbackFonts() accepts a weight-keyed map. System fonts always appended as last resort. --- .../margelo/nitro/rive/HybridFallbackFont.kt | 10 + .../nitro/rive/HybridRiveFontConfig.kt | 157 ++++++------- example/ios/Podfile.lock | 10 +- .../src/exercisers/FontFallbackExample.tsx | 26 ++- ios/HybridFallbackFont.swift | 10 + ios/HybridRiveFontConfig.swift | 100 ++++---- .../android/c++/JHybridFallbackFontSpec.cpp | 55 +++++ .../android/c++/JHybridFallbackFontSpec.hpp | 66 ++++++ .../android/c++/JHybridRiveFontConfigSpec.cpp | 104 ++++----- .../android/c++/JHybridRiveFontConfigSpec.hpp | 12 +- .../nitro/rive/HybridFallbackFontSpec.kt | 55 +++++ .../nitro/rive/HybridRiveFontConfigSpec.kt | 12 +- .../generated/android/rive+autolinking.cmake | 2 + nitrogen/generated/android/riveOnLoad.cpp | 2 + .../generated/ios/RNRive-Swift-Cxx-Bridge.cpp | 75 ++++-- .../generated/ios/RNRive-Swift-Cxx-Bridge.hpp | 218 ++++++++++++------ .../ios/RNRive-Swift-Cxx-Umbrella.hpp | 15 +- .../ios/c++/HybridFallbackFontSpecSwift.cpp | 11 + .../ios/c++/HybridFallbackFontSpecSwift.hpp | 75 ++++++ .../ios/c++/HybridRiveFontConfigSpecSwift.hpp | 33 +-- ...__shared_ptr_HybridFallbackFontSpec_.swift | 51 ++++ .../ios/swift/HybridFallbackFontSpec.swift | 56 +++++ .../swift/HybridFallbackFontSpec_cxx.swift | 129 +++++++++++ .../ios/swift/HybridRiveFontConfigSpec.swift | 12 +- .../swift/HybridRiveFontConfigSpec_cxx.swift | 97 ++++---- .../shared/c++/HybridFallbackFontSpec.cpp | 21 ++ .../shared/c++/HybridFallbackFontSpec.hpp | 62 +++++ .../shared/c++/HybridRiveFontConfigSpec.cpp | 12 +- .../shared/c++/HybridRiveFontConfigSpec.hpp | 20 +- src/core/RiveFonts.ts | 86 ++++--- src/index.tsx | 7 +- src/specs/RiveFontConfig.nitro.ts | 49 +--- 32 files changed, 1141 insertions(+), 509 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/rive/HybridFallbackFont.kt create mode 100644 ios/HybridFallbackFont.swift create mode 100644 nitrogen/generated/android/c++/JHybridFallbackFontSpec.cpp create mode 100644 nitrogen/generated/android/c++/JHybridFallbackFontSpec.hpp create mode 100644 nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridFallbackFontSpec.kt create mode 100644 nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.cpp create mode 100644 nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.hpp create mode 100644 nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridFallbackFontSpec_.swift create mode 100644 nitrogen/generated/ios/swift/HybridFallbackFontSpec.swift create mode 100644 nitrogen/generated/ios/swift/HybridFallbackFontSpec_cxx.swift create mode 100644 nitrogen/generated/shared/c++/HybridFallbackFontSpec.cpp create mode 100644 nitrogen/generated/shared/c++/HybridFallbackFontSpec.hpp diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridFallbackFont.kt b/android/src/main/java/com/margelo/nitro/rive/HybridFallbackFont.kt new file mode 100644 index 00000000..9b3cc18e --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/rive/HybridFallbackFont.kt @@ -0,0 +1,10 @@ +package com.margelo.nitro.rive + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +@Keep +@DoNotStrip +class HybridFallbackFont( + val fontBytes: ByteArray +) : HybridFallbackFontSpec() diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index adbb6e01..9a5d7f4c 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -10,7 +10,6 @@ import com.margelo.nitro.NitroModules import com.margelo.nitro.core.ArrayBuffer import com.margelo.nitro.core.Promise import java.net.URL -import java.util.Collections import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -19,33 +18,8 @@ import kotlinx.coroutines.withContext class HybridRiveFontConfig : HybridRiveFontConfigSpec() { companion object { private const val TAG = "RiveFonts" - private var systemFallbackEnabled = false - private val customFonts: MutableList = Collections.synchronizedList(mutableListOf()) - - private val fontFallbackStrategy = object : FontFallbackStrategy { - override fun getFont(weight: Fonts.Weight): List { - val result = mutableListOf() - synchronized(customFonts) { - result.addAll(customFonts) - } - val systemFonts = FontHelper.getFallbackFonts(Fonts.FontOpts(weight = weight)) - for (font in systemFonts) { - try { - FontHelper.getFontBytes(font)?.let { result.add(it) } - } catch (e: OutOfMemoryError) { - Log.w(TAG, "Skipped system font due to memory pressure: ${e.message}") - break - } - } - return result - } - } - - private fun ensureSystemFallback() { - if (systemFallbackEnabled) return - FontFallbackStrategy.stylePicker = fontFallbackStrategy - systemFallbackEnabled = true - } + private val fontsByWeight: MutableMap> = + java.util.Collections.synchronizedMap(mutableMapOf()) private fun resetFontCache() { try { @@ -56,90 +30,101 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { } } - override fun enableSystemFontFallback(): Promise { + override fun loadFontFromURL(url: String): Promise { return Promise.async { - ensureSystemFallback() + val fontBytes = withContext(Dispatchers.IO) { + URL(url).readBytes() + } + HybridFallbackFont(fontBytes) } } - override fun addFallbackFont(bytes: ArrayBuffer): Promise { - return Promise.async { - ensureSystemFallback() - val buffer = bytes.getBuffer(false) - val byteArray = ByteArray(buffer.remaining()) - buffer.get(byteArray) - customFonts.add(byteArray) - } - } + override fun loadFontFromResource(resource: String): HybridFallbackFontSpec { + val context = NitroModules.applicationContext + ?: throw Error("Application context not available") - override fun addFallbackFontFromResource(resource: String): Promise { - return Promise.async { - ensureSystemFallback() - val context = NitroModules.applicationContext - ?: throw Error("Application context not available") + val resName = resource.substringBeforeLast(".") - val resName = resource.substringBeforeLast(".") + val rawId = context.resources.getIdentifier(resName, "raw", context.packageName) + if (rawId != 0) { + val fontBytes = context.resources.openRawResource(rawId).use { it.readBytes() } + return HybridFallbackFont(fontBytes) + } - val rawId = context.resources.getIdentifier(resName, "raw", context.packageName) - if (rawId != 0) { - val fontBytes = context.resources.openRawResource(rawId).use { it.readBytes() } - customFonts.add(fontBytes) - return@async - } + val fontId = context.resources.getIdentifier(resName, "font", context.packageName) + if (fontId != 0) { + val fontBytes = context.resources.openRawResource(fontId).use { it.readBytes() } + return HybridFallbackFont(fontBytes) + } - val fontId = context.resources.getIdentifier(resName, "font", context.packageName) - if (fontId != 0) { - val fontBytes = context.resources.openRawResource(fontId).use { it.readBytes() } - customFonts.add(fontBytes) - return@async + val assetPaths = listOf("fonts/$resource", resource) + for (assetPath in assetPaths) { + try { + val fontBytes = context.assets.open(assetPath).use { it.readBytes() } + return HybridFallbackFont(fontBytes) + } catch (_: Exception) { } + } - val assetPaths = listOf("fonts/$resource", resource) - for (assetPath in assetPaths) { - try { - val fontBytes = context.assets.open(assetPath).use { it.readBytes() } - customFonts.add(fontBytes) - return@async - } catch (_: Exception) { - } - } + throw Error("Font resource not found: $resource (checked res/raw, res/font, assets/fonts)") + } - throw Error("Font resource not found: $resource (checked res/raw, res/font, assets/fonts)") - } + override fun loadFontFromBytes(bytes: ArrayBuffer): HybridFallbackFontSpec { + val buffer = bytes.getBuffer(false) + val byteArray = ByteArray(buffer.remaining()) + buffer.get(byteArray) + return HybridFallbackFont(byteArray) } - override fun addFallbackFontFromURL(url: String): Promise { - return Promise.async { - ensureSystemFallback() - val fontBytes = withContext(Dispatchers.IO) { - URL(url).readBytes() - } - customFonts.add(fontBytes) + override fun loadFontByName(name: String): HybridFallbackFontSpec { + val fonts = FontHelper.getFallbackFonts(Fonts.FontOpts(familyName = name)) + if (fonts.isEmpty()) { + throw Error("System font not found: $name") } + val fontBytes = FontHelper.getFontBytes(fonts.first()) + ?: throw Error("Could not read font bytes for: $name") + return HybridFallbackFont(fontBytes) } - override fun addFallbackFontByName(name: String): Promise { - return Promise.async { - ensureSystemFallback() - val fonts = FontHelper.getFallbackFonts(Fonts.FontOpts(familyName = name)) - if (fonts.isEmpty()) { - throw Error("System font not found: $name") - } - val fontBytes = FontHelper.getFontBytes(fonts.first()) - ?: throw Error("Could not read font bytes for: $name") - customFonts.add(fontBytes) + override fun setFontsForWeight(weight: Double, fonts: Array) { + val key = weight.toInt() + val byteArrays = fonts.mapNotNull { (it as? HybridFallbackFont)?.fontBytes } + synchronized(fontsByWeight) { + fontsByWeight[key] = byteArrays } } override fun applyFallbackFonts(): Promise { return Promise.async { + FontFallbackStrategy.stylePicker = object : FontFallbackStrategy { + override fun getFont(weight: Fonts.Weight): List { + val requestedWeight = weight.weight + val fonts = synchronized(fontsByWeight) { + fontsByWeight[requestedWeight] ?: fontsByWeight[0] ?: emptyList() + } + val result = fonts.toMutableList() + val systemFonts = FontHelper.getFallbackFonts(Fonts.FontOpts(weight = weight)) + for (font in systemFonts) { + try { + FontHelper.getFontBytes(font)?.let { result.add(it) } + } catch (e: OutOfMemoryError) { + Log.w(TAG, "Skipped system font due to memory pressure: ${e.message}") + break + } + } + return result + } + } resetFontCache() } } - override fun clearCustomFallbackFonts(): Promise { + override fun clearFallbackFonts(): Promise { return Promise.async { - customFonts.clear() + synchronized(fontsByWeight) { + fontsByWeight.clear() + } + FontFallbackStrategy.stylePicker = null resetFontCache() } } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index a87eb944..6b1bb731 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1754,7 +1754,7 @@ PODS: - React-logger (= 0.79.2) - React-perflogger (= 0.79.2) - React-utils (= 0.79.2) - - RiveRuntime (6.15.2) + - RiveRuntime (6.13.0) - RNCAsyncStorage (2.2.0): - DoubleConversion - glog @@ -1904,7 +1904,7 @@ PODS: - ReactCommon/turbomodule/core - RNWorklets - Yoga - - RNRive (0.2.4): + - RNRive (0.2.3): - DoubleConversion - glog - hermes-engine @@ -1928,7 +1928,7 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RiveRuntime (= 6.15.2) + - RiveRuntime (= 6.13.0) - Yoga - RNWorklets (0.6.1): - DoubleConversion @@ -2325,12 +2325,12 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 04d5eb15eb46be6720e17a4a7fa92940a776e584 ReactCodegen: c63eda03ba1d94353fb97b031fc84f75a0d125ba ReactCommon: 76d2dc87136d0a667678668b86f0fca0c16fdeb0 - RiveRuntime: b2c29a7e1fbf2aabd1fffcb7b9cf43c4d515a841 + RiveRuntime: 903690a5ba698b2a7e8d462e8aa7ceeba862614c RNCAsyncStorage: a1c8cc8a99c32de1244a9cf707bf9d83d0de0f71 RNCPicker: 28c076ae12a1056269ec0305fe35fac3086c477d RNGestureHandler: 6b39f4e43e4b3a0fb86de9531d090ff205a011d5 RNReanimated: 66b68ebe3baf7ec9e716bd059d700726f250d344 - RNRive: 5abb7e84bce9e6a0fe3ede1b366c442ac67dda75 + RNRive: bcdbc29a0e02ea0ed1b2588395f68fa415a259df RNWorklets: b1faafefb82d9f29c4018404a0fb33974b494a7b SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 9f110fc4b7aa538663cba3c14cbb1c335f43c13f diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 21db533b..046aac42 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -16,6 +16,7 @@ import { useRiveFile, useRiveString, type FontSource, + type FallbackFont, } from '@rive-app/react-native'; import { type Metadata } from '../shared/metadata'; @@ -67,6 +68,9 @@ type LogEntry = { export default function FontFallbackExample() { const [mounted, setMounted] = useState(false); const [selectedFonts, setSelectedFonts] = useState>(new Set()); + const [loadedFonts, setLoadedFonts] = useState>( + new Map() + ); const [loadingFont, setLoadingFont] = useState(null); const [log, setLog] = useState([]); const [inputText, setInputText] = useState('ABC 你好 สวัสดี'); @@ -86,15 +90,21 @@ export default function FontFallbackExample() { next.delete(key); return next; }); + setLoadedFonts((prev) => { + const next = new Map(prev); + next.delete(key); + return next; + }); addLog(`Removed ${font.label} from selection`, 'info'); return; } setLoadingFont(key); try { - await RiveFonts.addFallbackFonts([font.source]); - addLog(`Added ${font.label} as fallback font`, 'success'); + const handle = await RiveFonts.loadFont(font.source); + addLog(`Loaded ${font.label}`, 'success'); setSelectedFonts((prev) => new Set(prev).add(key)); + setLoadedFonts((prev) => new Map(prev).set(key, handle)); } catch (err) { const msg = err instanceof Error ? err.message : String(err); addLog(`${font.label}: ${msg}`, 'error'); @@ -106,13 +116,10 @@ export default function FontFallbackExample() { const handleMount = async () => { try { - // addFallbackFonts already enables system fallback internally, - // so this is only needed for the "no custom fonts" path - if (selectedFonts.size === 0) { - await RiveFonts.enableSystemFontFallback(); - } + const handles = [...loadedFonts.values()]; + await RiveFonts.setFallbackFonts({ 0: handles }); } catch (err) { - console.error('enableSystemFontFallback:', err); + console.error('setFallbackFonts:', err); } setMounted(true); addLog( @@ -122,8 +129,9 @@ export default function FontFallbackExample() { }; const handleReset = async () => { - await RiveFonts.clearCustomFallbackFonts(); + await RiveFonts.clearFallbackFonts(); setSelectedFonts(new Set()); + setLoadedFonts(new Map()); setMounted(false); addLog('Cleared fonts & unmounted view', 'info'); }; diff --git a/ios/HybridFallbackFont.swift b/ios/HybridFallbackFont.swift new file mode 100644 index 00000000..f8c5f03c --- /dev/null +++ b/ios/HybridFallbackFont.swift @@ -0,0 +1,10 @@ +import NitroModules + +class HybridFallbackFont: HybridFallbackFontSpec { + let font: UIFont + + init(font: UIFont) { + self.font = font + super.init() + } +} diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift index 95a27111..cfa2191e 100644 --- a/ios/HybridRiveFontConfig.swift +++ b/ios/HybridRiveFontConfig.swift @@ -3,81 +3,72 @@ import NitroModules import RiveRuntime class HybridRiveFontConfig: HybridRiveFontConfigSpec { - private static var systemFallbackEnabled = false - private static var customFonts: [UIFont] = [] + private static var fontsByWeight: [Int: [UIFont]] = [:] - func enableSystemFontFallback() throws -> Promise { + func loadFontFromURL(url: String) throws -> Promise<(any HybridFallbackFontSpec)> { return Promise.async { - Self.ensureSystemFallback() - } - } - - func addFallbackFont(bytes: ArrayBuffer) throws -> Promise { - return Promise.async { - Self.ensureSystemFallback() - let data = bytes.toData(copyIfNeeded: true) + guard let parsedURL = URL(string: url) else { + throw RuntimeError.error(withMessage: "Invalid font URL: \(url)") + } + let (data, _) = try await URLSession.shared.data(from: parsedURL) let font = try Self.createUIFont(from: data) - Self.customFonts.append(font) + return HybridFallbackFont(font: font) } } - func addFallbackFontFromResource(resource: String) throws -> Promise { - return Promise.async { - Self.ensureSystemFallback() - let nsResource = resource as NSString - let name = nsResource.deletingPathExtension - let ext = nsResource.pathExtension.isEmpty ? nil : nsResource.pathExtension + func loadFontFromResource(resource: String) throws -> (any HybridFallbackFontSpec) { + let nsResource = resource as NSString + let name = nsResource.deletingPathExtension + let ext = nsResource.pathExtension.isEmpty ? nil : nsResource.pathExtension - guard let path = Bundle.main.path(forResource: name, ofType: ext) else { - throw RuntimeError.error(withMessage: "Font resource not found: \(resource)") - } - let data = try Data(contentsOf: URL(fileURLWithPath: path)) - let font = try Self.createUIFont(from: data) - Self.customFonts.append(font) + guard let path = Bundle.main.path(forResource: name, ofType: ext) else { + throw RuntimeError.error(withMessage: "Font resource not found: \(resource)") } + let data = try Data(contentsOf: URL(fileURLWithPath: path)) + let font = try Self.createUIFont(from: data) + return HybridFallbackFont(font: font) } - func addFallbackFontFromURL(url: String) throws -> Promise { - return Promise.async { - Self.ensureSystemFallback() - guard let parsedURL = URL(string: url) else { - throw RuntimeError.error(withMessage: "Invalid font URL: \(url)") - } - let (data, _) = try await URLSession.shared.data(from: parsedURL) - let font = try Self.createUIFont(from: data) - Self.customFonts.append(font) - } + func loadFontFromBytes(bytes: ArrayBuffer) throws -> (any HybridFallbackFontSpec) { + let data = bytes.toData(copyIfNeeded: true) + let font = try Self.createUIFont(from: data) + return HybridFallbackFont(font: font) } - func addFallbackFontByName(name: String) throws -> Promise { - return Promise.async { - Self.ensureSystemFallback() - guard let font = UIFont(name: name, size: UIFont.systemFontSize) else { - throw RuntimeError.error(withMessage: "System font not found: \(name)") - } - Self.customFonts.append(font) + func loadFontByName(name: String) throws -> (any HybridFallbackFontSpec) { + guard let font = UIFont(name: name, size: UIFont.systemFontSize) else { + throw RuntimeError.error(withMessage: "System font not found: \(name)") } + return HybridFallbackFont(font: font) + } + + func setFontsForWeight(weight: Double, fonts: [(any HybridFallbackFontSpec)]) throws { + let key = Int(weight) + let uiFonts = fonts.compactMap { ($0 as? HybridFallbackFont)?.font } + Self.fontsByWeight[key] = uiFonts } func applyFallbackFonts() throws -> Promise { return Promise.async { - Self.updateFallbackFonts() + _ = RiveFont.self + RiveFont.fallbackFontsCallback = { weight in + let requestedWeight = Int(weight.rawWeight) + let fonts = Self.fontsByWeight[requestedWeight] ?? Self.fontsByWeight[0] ?? [] + var providers: [RiveFallbackFontProvider] = fonts + providers.append(RiveFallbackFontDescriptor()) + return providers + } } } - func clearCustomFallbackFonts() throws -> Promise { + func clearFallbackFonts() throws -> Promise { return Promise.async { - Self.customFonts.removeAll() - Self.updateFallbackFonts() + Self.fontsByWeight.removeAll() + RiveFont.fallbackFontsCallback = { _ in [RiveFallbackFontDescriptor()] } + RiveFont.fallbackFonts = [RiveFallbackFontDescriptor()] } } - private static func ensureSystemFallback() { - guard !systemFallbackEnabled else { return } - _ = RiveFont.self - systemFallbackEnabled = true - } - private static func createUIFont(from data: Data) throws -> UIFont { guard let provider = CGDataProvider(data: data as CFData), let cgFont = CGFont(provider) @@ -89,7 +80,6 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { if !CTFontManagerRegisterGraphicsFont(cgFont, &error) { let cfError = error?.takeRetainedValue() let domain = cfError.map { CFErrorGetDomain($0) as String } ?? "" - // Ignore "already registered" errors if domain != kCTFontManagerErrorDomain as String { throw RuntimeError.error( withMessage: "Failed to register font: \(cfError?.localizedDescription ?? "unknown error")" @@ -105,10 +95,4 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { } return font } - - private static func updateFallbackFonts() { - var providers: [RiveFallbackFontProvider] = customFonts - providers.append(RiveFallbackFontDescriptor()) - RiveFont.fallbackFonts = providers - } } diff --git a/nitrogen/generated/android/c++/JHybridFallbackFontSpec.cpp b/nitrogen/generated/android/c++/JHybridFallbackFontSpec.cpp new file mode 100644 index 00000000..d3e62d0d --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridFallbackFontSpec.cpp @@ -0,0 +1,55 @@ +/// +/// JHybridFallbackFontSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "JHybridFallbackFontSpec.hpp" + + + + + +namespace margelo::nitro::rive { + + jni::local_ref JHybridFallbackFontSpec::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + void JHybridFallbackFontSpec::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridFallbackFontSpec::initHybrid), + }); + } + + size_t JHybridFallbackFontSpec::getExternalMemorySize() noexcept { + static const auto method = javaClassStatic()->getMethod("getMemorySize"); + return method(_javaPart); + } + + bool JHybridFallbackFontSpec::equals(const std::shared_ptr& other) { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _javaPart == otherCast->_javaPart; + } + return false; + } + + void JHybridFallbackFontSpec::dispose() noexcept { + static const auto method = javaClassStatic()->getMethod("dispose"); + method(_javaPart); + } + + std::string JHybridFallbackFontSpec::toString() { + static const auto method = javaClassStatic()->getMethod("toString"); + auto javaString = method(_javaPart); + return javaString->toStdString(); + } + + // Properties + + + // Methods + + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JHybridFallbackFontSpec.hpp b/nitrogen/generated/android/c++/JHybridFallbackFontSpec.hpp new file mode 100644 index 00000000..52a871dc --- /dev/null +++ b/nitrogen/generated/android/c++/JHybridFallbackFontSpec.hpp @@ -0,0 +1,66 @@ +/// +/// HybridFallbackFontSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridFallbackFontSpec.hpp" + + + + +namespace margelo::nitro::rive { + + using namespace facebook; + + class JHybridFallbackFontSpec: public jni::HybridClass, + public virtual HybridFallbackFontSpec { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/HybridFallbackFontSpec;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + + protected: + // C++ constructor (called from Java via `initHybrid()`) + explicit JHybridFallbackFontSpec(jni::alias_ref jThis) : + HybridObject(HybridFallbackFontSpec::TAG), + HybridBase(jThis), + _javaPart(jni::make_global(jThis)) {} + + public: + ~JHybridFallbackFontSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + size_t getExternalMemorySize() noexcept override; + bool equals(const std::shared_ptr& other) override; + void dispose() noexcept override; + std::string toString() override; + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + + + public: + // Methods + + + private: + friend HybridBase; + using HybridBase::HybridBase; + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp index 734dd0a1..94ae9635 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp @@ -7,14 +7,19 @@ #include "JHybridRiveFontConfigSpec.hpp" +// Forward declaration of `HybridFallbackFontSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridFallbackFontSpec; } - +#include +#include "HybridFallbackFontSpec.hpp" #include #include +#include "JHybridFallbackFontSpec.hpp" #include +#include #include #include -#include +#include namespace margelo::nitro::rive { @@ -55,28 +60,14 @@ namespace margelo::nitro::rive { // Methods - std::shared_ptr> JHybridRiveFontConfigSpec::enableSystemFontFallback() { - static const auto method = javaClassStatic()->getMethod()>("enableSystemFontFallback"); - auto __result = method(_javaPart); - return [&]() { - auto __promise = Promise::create(); - __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { - __promise->resolve(); - }); - __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { - jni::JniException __jniError(__throwable); - __promise->reject(std::make_exception_ptr(__jniError)); - }); - return __promise; - }(); - } - std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFont(const std::shared_ptr& bytes) { - static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* bytes */)>("addFallbackFont"); - auto __result = method(_javaPart, JArrayBuffer::wrap(bytes)); + std::shared_ptr>> JHybridRiveFontConfigSpec::loadFontFromURL(const std::string& url) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* url */)>("loadFontFromURL"); + auto __result = method(_javaPart, jni::make_jstring(url)); return [&]() { - auto __promise = Promise::create(); - __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { - __promise->resolve(); + auto __promise = Promise>::create(); + __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) { + auto __result = jni::static_ref_cast(__boxedResult); + __promise->resolve(__result->cthis()->shared_cast()); }); __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { jni::JniException __jniError(__throwable); @@ -85,50 +76,33 @@ namespace margelo::nitro::rive { return __promise; }(); } - std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontFromResource(const std::string& resource) { - static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* resource */)>("addFallbackFontFromResource"); + std::shared_ptr JHybridRiveFontConfigSpec::loadFontFromResource(const std::string& resource) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* resource */)>("loadFontFromResource"); auto __result = method(_javaPart, jni::make_jstring(resource)); - return [&]() { - auto __promise = Promise::create(); - __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { - __promise->resolve(); - }); - __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { - jni::JniException __jniError(__throwable); - __promise->reject(std::make_exception_ptr(__jniError)); - }); - return __promise; - }(); + return __result->cthis()->shared_cast(); } - std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontFromURL(const std::string& url) { - static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* url */)>("addFallbackFontFromURL"); - auto __result = method(_javaPart, jni::make_jstring(url)); - return [&]() { - auto __promise = Promise::create(); - __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { - __promise->resolve(); - }); - __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { - jni::JniException __jniError(__throwable); - __promise->reject(std::make_exception_ptr(__jniError)); - }); - return __promise; - }(); + std::shared_ptr JHybridRiveFontConfigSpec::loadFontFromBytes(const std::shared_ptr& bytes) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* bytes */)>("loadFontFromBytes"); + auto __result = method(_javaPart, JArrayBuffer::wrap(bytes)); + return __result->cthis()->shared_cast(); } - std::shared_ptr> JHybridRiveFontConfigSpec::addFallbackFontByName(const std::string& name) { - static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* name */)>("addFallbackFontByName"); + std::shared_ptr JHybridRiveFontConfigSpec::loadFontByName(const std::string& name) { + static const auto method = javaClassStatic()->getMethod(jni::alias_ref /* name */)>("loadFontByName"); auto __result = method(_javaPart, jni::make_jstring(name)); - return [&]() { - auto __promise = Promise::create(); - __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& /* unit */) { - __promise->resolve(); - }); - __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) { - jni::JniException __jniError(__throwable); - __promise->reject(std::make_exception_ptr(__jniError)); - }); - return __promise; - }(); + return __result->cthis()->shared_cast(); + } + void JHybridRiveFontConfigSpec::setFontsForWeight(double weight, const std::vector>& fonts) { + static const auto method = javaClassStatic()->getMethod> /* fonts */)>("setFontsForWeight"); + method(_javaPart, weight, [&]() { + size_t __size = fonts.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = fonts[__i]; + auto __elementJni = std::dynamic_pointer_cast(__element)->getJavaPart(); + __array->setElement(__i, __elementJni.get()); + } + return __array; + }()); } std::shared_ptr> JHybridRiveFontConfigSpec::applyFallbackFonts() { static const auto method = javaClassStatic()->getMethod()>("applyFallbackFonts"); @@ -145,8 +119,8 @@ namespace margelo::nitro::rive { return __promise; }(); } - std::shared_ptr> JHybridRiveFontConfigSpec::clearCustomFallbackFonts() { - static const auto method = javaClassStatic()->getMethod()>("clearCustomFallbackFonts"); + std::shared_ptr> JHybridRiveFontConfigSpec::clearFallbackFonts() { + static const auto method = javaClassStatic()->getMethod()>("clearFallbackFonts"); auto __result = method(_javaPart); return [&]() { auto __promise = Promise::create(); diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp index 98fa1917..8737e7f3 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp @@ -55,13 +55,13 @@ namespace margelo::nitro::rive { public: // Methods - std::shared_ptr> enableSystemFontFallback() override; - std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) override; - std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override; - std::shared_ptr> addFallbackFontFromURL(const std::string& url) override; - std::shared_ptr> addFallbackFontByName(const std::string& name) override; + std::shared_ptr>> loadFontFromURL(const std::string& url) override; + std::shared_ptr loadFontFromResource(const std::string& resource) override; + std::shared_ptr loadFontFromBytes(const std::shared_ptr& bytes) override; + std::shared_ptr loadFontByName(const std::string& name) override; + void setFontsForWeight(double weight, const std::vector>& fonts) override; std::shared_ptr> applyFallbackFonts() override; - std::shared_ptr> clearCustomFallbackFonts() override; + std::shared_ptr> clearFallbackFonts() override; private: friend HybridBase; diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridFallbackFontSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridFallbackFontSpec.kt new file mode 100644 index 00000000..615dc0c1 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridFallbackFontSpec.kt @@ -0,0 +1,55 @@ +/// +/// HybridFallbackFontSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.rive + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.HybridObject + +/** + * A Kotlin class representing the FallbackFont HybridObject. + * Implement this abstract class to create Kotlin-based instances of FallbackFont. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridFallbackFontSpec: HybridObject() { + @DoNotStrip + private var mHybridData: HybridData = initHybrid() + + init { + super.updateNative(mHybridData) + } + + override fun updateNative(hybridData: HybridData) { + mHybridData = hybridData + super.updateNative(hybridData) + } + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject FallbackFont]" + } + + // Properties + + + // Methods + + + private external fun initHybrid(): HybridData + + companion object { + protected const val TAG = "HybridFallbackFontSpec" + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt index 9ce3e4cf..f82caf1e 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt @@ -49,23 +49,23 @@ abstract class HybridRiveFontConfigSpec: HybridObject() { // Methods @DoNotStrip @Keep - abstract fun enableSystemFontFallback(): Promise + abstract fun loadFontFromURL(url: String): Promise @DoNotStrip @Keep - abstract fun addFallbackFont(bytes: ArrayBuffer): Promise + abstract fun loadFontFromResource(resource: String): HybridFallbackFontSpec @DoNotStrip @Keep - abstract fun addFallbackFontFromResource(resource: String): Promise + abstract fun loadFontFromBytes(bytes: ArrayBuffer): HybridFallbackFontSpec @DoNotStrip @Keep - abstract fun addFallbackFontFromURL(url: String): Promise + abstract fun loadFontByName(name: String): HybridFallbackFontSpec @DoNotStrip @Keep - abstract fun addFallbackFontByName(name: String): Promise + abstract fun setFontsForWeight(weight: Double, fonts: Array): Unit @DoNotStrip @Keep @@ -73,7 +73,7 @@ abstract class HybridRiveFontConfigSpec: HybridObject() { @DoNotStrip @Keep - abstract fun clearCustomFallbackFonts(): Promise + abstract fun clearFallbackFonts(): Promise private external fun initHybrid(): HybridData diff --git a/nitrogen/generated/android/rive+autolinking.cmake b/nitrogen/generated/android/rive+autolinking.cmake index bc2203b2..020c674e 100644 --- a/nitrogen/generated/android/rive+autolinking.cmake +++ b/nitrogen/generated/android/rive+autolinking.cmake @@ -36,6 +36,7 @@ target_sources( ../nitrogen/generated/shared/c++/HybridBindableArtboardSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveFileSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveFileFactorySpec.cpp + ../nitrogen/generated/shared/c++/HybridFallbackFontSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveImageSpec.cpp ../nitrogen/generated/shared/c++/HybridRiveImageFactorySpec.cpp @@ -58,6 +59,7 @@ target_sources( ../nitrogen/generated/android/c++/JHybridBindableArtboardSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFileSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFileFactorySpec.cpp + ../nitrogen/generated/android/c++/JHybridFallbackFontSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveImageSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveImageFactorySpec.cpp diff --git a/nitrogen/generated/android/riveOnLoad.cpp b/nitrogen/generated/android/riveOnLoad.cpp index 8a4d3db9..5bd9eb58 100644 --- a/nitrogen/generated/android/riveOnLoad.cpp +++ b/nitrogen/generated/android/riveOnLoad.cpp @@ -18,6 +18,7 @@ #include "JHybridBindableArtboardSpec.hpp" #include "JHybridRiveFileSpec.hpp" #include "JHybridRiveFileFactorySpec.hpp" +#include "JHybridFallbackFontSpec.hpp" #include "JHybridRiveFontConfigSpec.hpp" #include "JHybridRiveImageSpec.hpp" #include "JHybridRiveImageFactorySpec.hpp" @@ -56,6 +57,7 @@ int initialize(JavaVM* vm) { margelo::nitro::rive::JHybridBindableArtboardSpec::registerNatives(); margelo::nitro::rive::JHybridRiveFileSpec::registerNatives(); margelo::nitro::rive::JHybridRiveFileFactorySpec::registerNatives(); + margelo::nitro::rive::JHybridFallbackFontSpec::registerNatives(); margelo::nitro::rive::JHybridRiveFontConfigSpec::registerNatives(); margelo::nitro::rive::JHybridRiveImageSpec::registerNatives(); margelo::nitro::rive::JHybridRiveImageFactorySpec::registerNatives(); diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp index ee02c6de..89ae6cb9 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp @@ -9,12 +9,13 @@ // Include C++ implementation defined types #include "HybridBindableArtboardSpecSwift.hpp" +#include "HybridFallbackFontSpecSwift.hpp" #include "HybridRiveFileFactorySpecSwift.hpp" #include "HybridRiveFileSpecSwift.hpp" +#include "HybridRiveFontConfigSpecSwift.hpp" #include "HybridRiveImageFactorySpecSwift.hpp" #include "HybridRiveImageSpecSwift.hpp" #include "HybridRiveRuntimeSpecSwift.hpp" -#include "HybridRiveSpecSwift.hpp" #include "HybridRiveViewSpecSwift.hpp" #include "HybridViewModelArtboardPropertySpecSwift.hpp" #include "HybridViewModelBooleanPropertySpecSwift.hpp" @@ -49,22 +50,6 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } - // pragma MARK: std::shared_ptr - std::shared_ptr create_std__shared_ptr_HybridRiveSpec_(void* NON_NULL swiftUnsafePointer) noexcept { - RNRive::HybridRiveSpec_cxx swiftPart = RNRive::HybridRiveSpec_cxx::fromUnsafe(swiftUnsafePointer); - return std::make_shared(swiftPart); - } - void* NON_NULL get_std__shared_ptr_HybridRiveSpec_(std__shared_ptr_HybridRiveSpec_ cppType) { - std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); - #ifdef NITRO_DEBUG - if (swiftWrapper == nullptr) [[unlikely]] { - throw std::runtime_error("Class \"HybridRiveSpec\" is not implemented in Swift!"); - } - #endif - RNRive::HybridRiveSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); - return swiftPart.toUnsafe(); - } - // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridViewModelSpec_(void* NON_NULL swiftUnsafePointer) noexcept { RNRive::HybridViewModelSpec_cxx swiftPart = RNRive::HybridViewModelSpec_cxx::fromUnsafe(swiftUnsafePointer); @@ -145,6 +130,54 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridFallbackFontSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + RNRive::HybridFallbackFontSpec_cxx swiftPart = RNRive::HybridFallbackFontSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridFallbackFontSpec_(std__shared_ptr_HybridFallbackFontSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridFallbackFontSpec\" is not implemented in Swift!"); + } + #endif + RNRive::HybridFallbackFontSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + + // pragma MARK: std::function& /* result */)> + Func_void_std__shared_ptr_HybridFallbackFontSpec_ create_Func_void_std__shared_ptr_HybridFallbackFontSpec_(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = RNRive::Func_void_std__shared_ptr_HybridFallbackFontSpec_::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](const std::shared_ptr& result) mutable -> void { + swiftClosure.call(result); + }; + } + + // pragma MARK: std::function + Func_void create_Func_void(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = RNRive::Func_void::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)]() mutable -> void { + swiftClosure.call(); + }; + } + + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridRiveFontConfigSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + RNRive::HybridRiveFontConfigSpec_cxx swiftPart = RNRive::HybridRiveFontConfigSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridRiveFontConfigSpec_(std__shared_ptr_HybridRiveFontConfigSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridRiveFontConfigSpec\" is not implemented in Swift!"); + } + #endif + RNRive::HybridRiveFontConfigSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + // pragma MARK: std::function& /* result */)> Func_void_std__shared_ptr_HybridRiveImageSpec_ create_Func_void_std__shared_ptr_HybridRiveImageSpec_(void* NON_NULL swiftClosureWrapper) noexcept { auto swiftClosure = RNRive::Func_void_std__shared_ptr_HybridRiveImageSpec_::fromUnsafe(swiftClosureWrapper); @@ -169,14 +202,6 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } - // pragma MARK: std::function - Func_void create_Func_void(void* NON_NULL swiftClosureWrapper) noexcept { - auto swiftClosure = RNRive::Func_void::fromUnsafe(swiftClosureWrapper); - return [swiftClosure = std::move(swiftClosure)]() mutable -> void { - swiftClosure.call(); - }; - } - // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridRiveRuntimeSpec_(void* NON_NULL swiftUnsafePointer) noexcept { RNRive::HybridRiveRuntimeSpec_cxx swiftPart = RNRive::HybridRiveRuntimeSpec_cxx::fromUnsafe(swiftUnsafePointer); diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp index 425cbd31..6ccaab64 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp @@ -22,18 +22,20 @@ namespace margelo::nitro::rive { enum class DataBindMode; } namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridBindableArtboardSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridBindableArtboardSpec; } +// Forward declaration of `HybridFallbackFontSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridFallbackFontSpec; } // Forward declaration of `HybridRiveFileFactorySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveFileFactorySpec; } // Forward declaration of `HybridRiveFileSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveFileSpec; } +// Forward declaration of `HybridRiveFontConfigSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridRiveFontConfigSpec; } // Forward declaration of `HybridRiveImageFactorySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageFactorySpec; } // Forward declaration of `HybridRiveImageSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageSpec; } // Forward declaration of `HybridRiveRuntimeSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveRuntimeSpec; } -// Forward declaration of `HybridRiveSpec` to properly resolve imports. -namespace margelo::nitro::rive { class HybridRiveSpec; } // Forward declaration of `HybridRiveViewSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveViewSpec; } // Forward declaration of `HybridViewModelArtboardPropertySpec` to properly resolve imports. @@ -76,18 +78,20 @@ namespace margelo::nitro::rive { struct UnifiedRiveEvent; } // Forward declarations of Swift defined types // Forward declaration of `HybridBindableArtboardSpec_cxx` to properly resolve imports. namespace RNRive { class HybridBindableArtboardSpec_cxx; } +// Forward declaration of `HybridFallbackFontSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridFallbackFontSpec_cxx; } // Forward declaration of `HybridRiveFileFactorySpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveFileFactorySpec_cxx; } // Forward declaration of `HybridRiveFileSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveFileSpec_cxx; } +// Forward declaration of `HybridRiveFontConfigSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridRiveFontConfigSpec_cxx; } // Forward declaration of `HybridRiveImageFactorySpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageFactorySpec_cxx; } // Forward declaration of `HybridRiveImageSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageSpec_cxx; } // Forward declaration of `HybridRiveRuntimeSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveRuntimeSpec_cxx; } -// Forward declaration of `HybridRiveSpec_cxx` to properly resolve imports. -namespace RNRive { class HybridRiveSpec_cxx; } // Forward declaration of `HybridRiveViewSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveViewSpec_cxx; } // Forward declaration of `HybridViewModelArtboardPropertySpec_cxx` to properly resolve imports. @@ -123,12 +127,13 @@ namespace RNRive { class HybridViewModelTriggerPropertySpec_cxx; } #include "DataBindMode.hpp" #include "Fit.hpp" #include "HybridBindableArtboardSpec.hpp" +#include "HybridFallbackFontSpec.hpp" #include "HybridRiveFileFactorySpec.hpp" #include "HybridRiveFileSpec.hpp" +#include "HybridRiveFontConfigSpec.hpp" #include "HybridRiveImageFactorySpec.hpp" #include "HybridRiveImageSpec.hpp" #include "HybridRiveRuntimeSpec.hpp" -#include "HybridRiveSpec.hpp" #include "HybridRiveViewSpec.hpp" #include "HybridViewModelArtboardPropertySpec.hpp" #include "HybridViewModelBooleanPropertySpec.hpp" @@ -178,27 +183,6 @@ namespace margelo::nitro::rive::bridge::swift { using std__weak_ptr_HybridBindableArtboardSpec_ = std::weak_ptr; inline std__weak_ptr_HybridBindableArtboardSpec_ weakify_std__shared_ptr_HybridBindableArtboardSpec_(const std::shared_ptr& strong) noexcept { return strong; } - // pragma MARK: std::shared_ptr - /** - * Specialized version of `std::shared_ptr`. - */ - using std__shared_ptr_HybridRiveSpec_ = std::shared_ptr; - std::shared_ptr create_std__shared_ptr_HybridRiveSpec_(void* NON_NULL swiftUnsafePointer) noexcept; - void* NON_NULL get_std__shared_ptr_HybridRiveSpec_(std__shared_ptr_HybridRiveSpec_ cppType); - - // pragma MARK: std::weak_ptr - using std__weak_ptr_HybridRiveSpec_ = std::weak_ptr; - inline std__weak_ptr_HybridRiveSpec_ weakify_std__shared_ptr_HybridRiveSpec_(const std::shared_ptr& strong) noexcept { return strong; } - - // pragma MARK: Result - using Result_double_ = Result; - inline Result_double_ create_Result_double_(double value) noexcept { - return Result::withValue(std::move(value)); - } - inline Result_double_ create_Result_double_(const std::exception_ptr& error) noexcept { - return Result::withError(error); - } - // pragma MARK: std::optional /** * Specialized version of `std::optional`. @@ -480,59 +464,61 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>>::withError(error); } - // pragma MARK: std::shared_ptr>> + // pragma MARK: std::shared_ptr /** - * Specialized version of `std::shared_ptr>>`. + * Specialized version of `std::shared_ptr`. */ - using std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___ = std::shared_ptr>>; - inline std::shared_ptr>> create_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___() noexcept { - return Promise>::create(); + using std__shared_ptr_HybridFallbackFontSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridFallbackFontSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridFallbackFontSpec_(std__shared_ptr_HybridFallbackFontSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridFallbackFontSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridFallbackFontSpec_ weakify_std__shared_ptr_HybridFallbackFontSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: std::shared_ptr>> + /** + * Specialized version of `std::shared_ptr>>`. + */ + using std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___ = std::shared_ptr>>; + inline std::shared_ptr>> create_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___() noexcept { + return Promise>::create(); } - inline PromiseHolder> wrap_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___(std::shared_ptr>> promise) noexcept { - return PromiseHolder>(std::move(promise)); + inline PromiseHolder> wrap_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___(std::shared_ptr>> promise) noexcept { + return PromiseHolder>(std::move(promise)); } - // pragma MARK: std::function& /* result */)> + // pragma MARK: std::function& /* result */)> /** - * Specialized version of `std::function&)>`. + * Specialized version of `std::function&)>`. */ - using Func_void_std__shared_ptr_HybridRiveImageSpec_ = std::function& /* result */)>; + using Func_void_std__shared_ptr_HybridFallbackFontSpec_ = std::function& /* result */)>; /** - * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. + * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. */ - class Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper final { + class Func_void_std__shared_ptr_HybridFallbackFontSpec__Wrapper final { public: - explicit Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} - inline void call(std::shared_ptr result) const noexcept { + explicit Func_void_std__shared_ptr_HybridFallbackFontSpec__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} + inline void call(std::shared_ptr result) const noexcept { _function->operator()(result); } private: - std::unique_ptr& /* result */)>> _function; + std::unique_ptr& /* result */)>> _function; } SWIFT_NONCOPYABLE; - Func_void_std__shared_ptr_HybridRiveImageSpec_ create_Func_void_std__shared_ptr_HybridRiveImageSpec_(void* NON_NULL swiftClosureWrapper) noexcept; - inline Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper wrap_Func_void_std__shared_ptr_HybridRiveImageSpec_(Func_void_std__shared_ptr_HybridRiveImageSpec_ value) noexcept { - return Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper(std::move(value)); + Func_void_std__shared_ptr_HybridFallbackFontSpec_ create_Func_void_std__shared_ptr_HybridFallbackFontSpec_(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__shared_ptr_HybridFallbackFontSpec__Wrapper wrap_Func_void_std__shared_ptr_HybridFallbackFontSpec_(Func_void_std__shared_ptr_HybridFallbackFontSpec_ value) noexcept { + return Func_void_std__shared_ptr_HybridFallbackFontSpec__Wrapper(std::move(value)); } - // pragma MARK: std::shared_ptr + // pragma MARK: std::vector> /** - * Specialized version of `std::shared_ptr`. + * Specialized version of `std::vector>`. */ - using std__shared_ptr_HybridRiveImageFactorySpec_ = std::shared_ptr; - std::shared_ptr create_std__shared_ptr_HybridRiveImageFactorySpec_(void* NON_NULL swiftUnsafePointer) noexcept; - void* NON_NULL get_std__shared_ptr_HybridRiveImageFactorySpec_(std__shared_ptr_HybridRiveImageFactorySpec_ cppType); - - // pragma MARK: std::weak_ptr - using std__weak_ptr_HybridRiveImageFactorySpec_ = std::weak_ptr; - inline std__weak_ptr_HybridRiveImageFactorySpec_ weakify_std__shared_ptr_HybridRiveImageFactorySpec_(const std::shared_ptr& strong) noexcept { return strong; } - - // pragma MARK: Result>>> - using Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ = Result>>>; - inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____(const std::shared_ptr>>& value) noexcept { - return Result>>>::withValue(value); - } - inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____(const std::exception_ptr& error) noexcept { - return Result>>>::withError(error); + using std__vector_std__shared_ptr_HybridFallbackFontSpec__ = std::vector>; + inline std::vector> create_std__vector_std__shared_ptr_HybridFallbackFontSpec__(size_t size) noexcept { + std::vector> vector; + vector.reserve(size); + return vector; } // pragma MARK: std::shared_ptr> @@ -569,17 +555,35 @@ namespace margelo::nitro::rive::bridge::swift { return Func_void_Wrapper(std::move(value)); } - // pragma MARK: std::shared_ptr + // pragma MARK: std::shared_ptr /** - * Specialized version of `std::shared_ptr`. + * Specialized version of `std::shared_ptr`. */ - using std__shared_ptr_HybridRiveRuntimeSpec_ = std::shared_ptr; - std::shared_ptr create_std__shared_ptr_HybridRiveRuntimeSpec_(void* NON_NULL swiftUnsafePointer) noexcept; - void* NON_NULL get_std__shared_ptr_HybridRiveRuntimeSpec_(std__shared_ptr_HybridRiveRuntimeSpec_ cppType); + using std__shared_ptr_HybridRiveFontConfigSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridRiveFontConfigSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridRiveFontConfigSpec_(std__shared_ptr_HybridRiveFontConfigSpec_ cppType); - // pragma MARK: std::weak_ptr - using std__weak_ptr_HybridRiveRuntimeSpec_ = std::weak_ptr; - inline std__weak_ptr_HybridRiveRuntimeSpec_ weakify_std__shared_ptr_HybridRiveRuntimeSpec_(const std::shared_ptr& strong) noexcept { return strong; } + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridRiveFontConfigSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridRiveFontConfigSpec_ weakify_std__shared_ptr_HybridRiveFontConfigSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result>>> + using Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____ = Result>>>; + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____(const std::shared_ptr>>& value) noexcept { + return Result>>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____(const std::exception_ptr& error) noexcept { + return Result>>>::withError(error); + } + + // pragma MARK: Result> + using Result_std__shared_ptr_HybridFallbackFontSpec__ = Result>; + inline Result_std__shared_ptr_HybridFallbackFontSpec__ create_Result_std__shared_ptr_HybridFallbackFontSpec__(const std::shared_ptr& value) noexcept { + return Result>::withValue(value); + } + inline Result_std__shared_ptr_HybridFallbackFontSpec__ create_Result_std__shared_ptr_HybridFallbackFontSpec__(const std::exception_ptr& error) noexcept { + return Result>::withError(error); + } // pragma MARK: Result>> using Result_std__shared_ptr_Promise_void___ = Result>>; @@ -590,6 +594,73 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>::withError(error); } + // pragma MARK: std::shared_ptr>> + /** + * Specialized version of `std::shared_ptr>>`. + */ + using std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___ = std::shared_ptr>>; + inline std::shared_ptr>> create_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___() noexcept { + return Promise>::create(); + } + inline PromiseHolder> wrap_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec___(std::shared_ptr>> promise) noexcept { + return PromiseHolder>(std::move(promise)); + } + + // pragma MARK: std::function& /* result */)> + /** + * Specialized version of `std::function&)>`. + */ + using Func_void_std__shared_ptr_HybridRiveImageSpec_ = std::function& /* result */)>; + /** + * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift. + */ + class Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper final { + public: + explicit Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper(std::function& /* result */)>&& func): _function(std::make_unique& /* result */)>>(std::move(func))) {} + inline void call(std::shared_ptr result) const noexcept { + _function->operator()(result); + } + private: + std::unique_ptr& /* result */)>> _function; + } SWIFT_NONCOPYABLE; + Func_void_std__shared_ptr_HybridRiveImageSpec_ create_Func_void_std__shared_ptr_HybridRiveImageSpec_(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper wrap_Func_void_std__shared_ptr_HybridRiveImageSpec_(Func_void_std__shared_ptr_HybridRiveImageSpec_ value) noexcept { + return Func_void_std__shared_ptr_HybridRiveImageSpec__Wrapper(std::move(value)); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridRiveImageFactorySpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridRiveImageFactorySpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridRiveImageFactorySpec_(std__shared_ptr_HybridRiveImageFactorySpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridRiveImageFactorySpec_ = std::weak_ptr; + inline std__weak_ptr_HybridRiveImageFactorySpec_ weakify_std__shared_ptr_HybridRiveImageFactorySpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result>>> + using Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ = Result>>>; + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____(const std::shared_ptr>>& value) noexcept { + return Result>>>::withValue(value); + } + inline Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____ create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridRiveImageSpec____(const std::exception_ptr& error) noexcept { + return Result>>>::withError(error); + } + + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridRiveRuntimeSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridRiveRuntimeSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridRiveRuntimeSpec_(std__shared_ptr_HybridRiveRuntimeSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridRiveRuntimeSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridRiveRuntimeSpec_ weakify_std__shared_ptr_HybridRiveRuntimeSpec_(const std::shared_ptr& strong) noexcept { return strong; } + // pragma MARK: std::optional /** * Specialized version of `std::optional`. @@ -895,6 +966,15 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>::withError(error); } + // pragma MARK: Result + using Result_double_ = Result; + inline Result_double_ create_Result_double_(double value) noexcept { + return Result::withValue(std::move(value)); + } + inline Result_double_ create_Result_double_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + // pragma MARK: Result using Result_bool_ = Result; inline Result_bool_ create_Result_bool_(bool value) noexcept { diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp index ab1751e6..b7531b3d 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp @@ -22,18 +22,20 @@ namespace margelo::nitro::rive { enum class DataBindMode; } namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridBindableArtboardSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridBindableArtboardSpec; } +// Forward declaration of `HybridFallbackFontSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridFallbackFontSpec; } // Forward declaration of `HybridRiveFileFactorySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveFileFactorySpec; } // Forward declaration of `HybridRiveFileSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveFileSpec; } +// Forward declaration of `HybridRiveFontConfigSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridRiveFontConfigSpec; } // Forward declaration of `HybridRiveImageFactorySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageFactorySpec; } // Forward declaration of `HybridRiveImageSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageSpec; } // Forward declaration of `HybridRiveRuntimeSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveRuntimeSpec; } -// Forward declaration of `HybridRiveSpec` to properly resolve imports. -namespace margelo::nitro::rive { class HybridRiveSpec; } // Forward declaration of `HybridRiveViewSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveViewSpec; } // Forward declaration of `HybridViewModelArtboardPropertySpec` to properly resolve imports. @@ -81,12 +83,13 @@ namespace margelo::nitro::rive { struct UnifiedRiveEvent; } #include "DataBindMode.hpp" #include "Fit.hpp" #include "HybridBindableArtboardSpec.hpp" +#include "HybridFallbackFontSpec.hpp" #include "HybridRiveFileFactorySpec.hpp" #include "HybridRiveFileSpec.hpp" +#include "HybridRiveFontConfigSpec.hpp" #include "HybridRiveImageFactorySpec.hpp" #include "HybridRiveImageSpec.hpp" #include "HybridRiveRuntimeSpec.hpp" -#include "HybridRiveSpec.hpp" #include "HybridRiveViewSpec.hpp" #include "HybridViewModelArtboardPropertySpec.hpp" #include "HybridViewModelBooleanPropertySpec.hpp" @@ -130,18 +133,20 @@ namespace margelo::nitro::rive { struct UnifiedRiveEvent; } // Forward declarations of Swift defined types // Forward declaration of `HybridBindableArtboardSpec_cxx` to properly resolve imports. namespace RNRive { class HybridBindableArtboardSpec_cxx; } +// Forward declaration of `HybridFallbackFontSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridFallbackFontSpec_cxx; } // Forward declaration of `HybridRiveFileFactorySpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveFileFactorySpec_cxx; } // Forward declaration of `HybridRiveFileSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveFileSpec_cxx; } +// Forward declaration of `HybridRiveFontConfigSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridRiveFontConfigSpec_cxx; } // Forward declaration of `HybridRiveImageFactorySpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageFactorySpec_cxx; } // Forward declaration of `HybridRiveImageSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageSpec_cxx; } // Forward declaration of `HybridRiveRuntimeSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveRuntimeSpec_cxx; } -// Forward declaration of `HybridRiveSpec_cxx` to properly resolve imports. -namespace RNRive { class HybridRiveSpec_cxx; } // Forward declaration of `HybridRiveViewSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveViewSpec_cxx; } // Forward declaration of `HybridViewModelArtboardPropertySpec_cxx` to properly resolve imports. diff --git a/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.cpp b/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.cpp new file mode 100644 index 00000000..2a81a925 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridFallbackFontSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridFallbackFontSpecSwift.hpp" + +namespace margelo::nitro::rive { +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.hpp new file mode 100644 index 00000000..c75ab171 --- /dev/null +++ b/nitrogen/generated/ios/c++/HybridFallbackFontSpecSwift.hpp @@ -0,0 +1,75 @@ +/// +/// HybridFallbackFontSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridFallbackFontSpec.hpp" + +// Forward declaration of `HybridFallbackFontSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridFallbackFontSpec_cxx; } + + + + + +#include "RNRive-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::rive { + + /** + * The C++ part of HybridFallbackFontSpec_cxx.swift. + * + * HybridFallbackFontSpecSwift (C++) accesses HybridFallbackFontSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridFallbackFontSpec_cxx can directly inherit from the C++ class HybridFallbackFontSpec + * to simplify the whole structure and memory management. + */ + class HybridFallbackFontSpecSwift: public virtual HybridFallbackFontSpec { + public: + // Constructor from a Swift instance + explicit HybridFallbackFontSpecSwift(const RNRive::HybridFallbackFontSpec_cxx& swiftPart): + HybridObject(HybridFallbackFontSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline RNRive::HybridFallbackFontSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + bool equals(const std::shared_ptr& other) override { + if (auto otherCast = std::dynamic_pointer_cast(other)) { + return _swiftPart.equals(otherCast->_swiftPart); + } + return false; + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + + + public: + // Methods + + + private: + RNRive::HybridFallbackFontSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp index 29d55b52..427d2b8d 100644 --- a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp @@ -12,13 +12,18 @@ // Forward declaration of `HybridRiveFontConfigSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveFontConfigSpec_cxx; } +// Forward declaration of `HybridFallbackFontSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridFallbackFontSpec; } // Forward declaration of `ArrayBufferHolder` to properly resolve imports. namespace NitroModules { class ArrayBufferHolder; } +#include +#include "HybridFallbackFontSpec.hpp" #include +#include #include #include -#include +#include #include "RNRive-Swift-Cxx-Umbrella.hpp" @@ -70,45 +75,43 @@ namespace margelo::nitro::rive { public: // Methods - inline std::shared_ptr> enableSystemFontFallback() override { - auto __result = _swiftPart.enableSystemFontFallback(); + inline std::shared_ptr>> loadFontFromURL(const std::string& url) override { + auto __result = _swiftPart.loadFontFromURL(url); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } auto __value = std::move(__result.value()); return __value; } - inline std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) override { - auto __result = _swiftPart.addFallbackFont(ArrayBufferHolder(bytes)); + inline std::shared_ptr loadFontFromResource(const std::string& resource) override { + auto __result = _swiftPart.loadFontFromResource(resource); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } auto __value = std::move(__result.value()); return __value; } - inline std::shared_ptr> addFallbackFontFromResource(const std::string& resource) override { - auto __result = _swiftPart.addFallbackFontFromResource(resource); + inline std::shared_ptr loadFontFromBytes(const std::shared_ptr& bytes) override { + auto __result = _swiftPart.loadFontFromBytes(ArrayBufferHolder(bytes)); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } auto __value = std::move(__result.value()); return __value; } - inline std::shared_ptr> addFallbackFontFromURL(const std::string& url) override { - auto __result = _swiftPart.addFallbackFontFromURL(url); + inline std::shared_ptr loadFontByName(const std::string& name) override { + auto __result = _swiftPart.loadFontByName(name); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } auto __value = std::move(__result.value()); return __value; } - inline std::shared_ptr> addFallbackFontByName(const std::string& name) override { - auto __result = _swiftPart.addFallbackFontByName(name); + inline void setFontsForWeight(double weight, const std::vector>& fonts) override { + auto __result = _swiftPart.setFontsForWeight(std::forward(weight), fonts); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } - auto __value = std::move(__result.value()); - return __value; } inline std::shared_ptr> applyFallbackFonts() override { auto __result = _swiftPart.applyFallbackFonts(); @@ -118,8 +121,8 @@ namespace margelo::nitro::rive { auto __value = std::move(__result.value()); return __value; } - inline std::shared_ptr> clearCustomFallbackFonts() override { - auto __result = _swiftPart.clearCustomFallbackFonts(); + inline std::shared_ptr> clearFallbackFonts() override { + auto __result = _swiftPart.clearFallbackFonts(); if (__result.hasError()) [[unlikely]] { std::rethrow_exception(__result.error()); } diff --git a/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridFallbackFontSpec_.swift b/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridFallbackFontSpec_.swift new file mode 100644 index 00000000..b7dc1138 --- /dev/null +++ b/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridFallbackFontSpec_.swift @@ -0,0 +1,51 @@ +/// +/// Func_void_std__shared_ptr_HybridFallbackFontSpec_.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/** + * Wraps a Swift `(_ value: (any HybridFallbackFontSpec)) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_std__shared_ptr_HybridFallbackFontSpec_ { + public typealias bridge = margelo.nitro.rive.bridge.swift + + private let closure: (_ value: (any HybridFallbackFontSpec)) -> Void + + public init(_ closure: @escaping (_ value: (any HybridFallbackFontSpec)) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(value: bridge.std__shared_ptr_HybridFallbackFontSpec_) -> Void { + self.closure({ () -> any HybridFallbackFontSpec in + let __unsafePointer = bridge.get_std__shared_ptr_HybridFallbackFontSpec_(value) + let __instance = HybridFallbackFontSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridFallbackFontSpec() + }()) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_std__shared_ptr_HybridFallbackFontSpec_`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__shared_ptr_HybridFallbackFontSpec_ { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/nitrogen/generated/ios/swift/HybridFallbackFontSpec.swift b/nitrogen/generated/ios/swift/HybridFallbackFontSpec.swift new file mode 100644 index 00000000..85663357 --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridFallbackFontSpec.swift @@ -0,0 +1,56 @@ +/// +/// HybridFallbackFontSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/// See ``HybridFallbackFontSpec`` +public protocol HybridFallbackFontSpec_protocol: HybridObject { + // Properties + + + // Methods + +} + +public extension HybridFallbackFontSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject FallbackFont]" + } +} + +/// See ``HybridFallbackFontSpec`` +open class HybridFallbackFontSpec_base { + private weak var cxxWrapper: HybridFallbackFontSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridFallbackFontSpec_cxx { + #if DEBUG + guard self is any HybridFallbackFontSpec else { + fatalError("`self` is not a `HybridFallbackFontSpec`! Did you accidentally inherit from `HybridFallbackFontSpec_base` instead of `HybridFallbackFontSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridFallbackFontSpec_cxx(self as! any HybridFallbackFontSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the FallbackFont HybridObject. + * Implement this protocol to create Swift-based instances of FallbackFont. + * ```swift + * class HybridFallbackFont : HybridFallbackFontSpec { + * // ... + * } + * ``` + */ +public typealias HybridFallbackFontSpec = HybridFallbackFontSpec_protocol & HybridFallbackFontSpec_base diff --git a/nitrogen/generated/ios/swift/HybridFallbackFontSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridFallbackFontSpec_cxx.swift new file mode 100644 index 00000000..9376705e --- /dev/null +++ b/nitrogen/generated/ios/swift/HybridFallbackFontSpec_cxx.swift @@ -0,0 +1,129 @@ +/// +/// HybridFallbackFontSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/** + * A class implementation that bridges HybridFallbackFontSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridFallbackFontSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::rive::bridge::swift`) + * from `RNRive-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.rive.bridge.swift + + /** + * Holds an instance of the `HybridFallbackFontSpec` Swift protocol. + */ + private var __implementation: any HybridFallbackFontSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridFallbackFontSpec_ + + /** + * Create a new `HybridFallbackFontSpec_cxx` that wraps the given `HybridFallbackFontSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridFallbackFontSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridFallbackFontSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridFallbackFontSpec() -> any HybridFallbackFontSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridFallbackFontSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridFallbackFontSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridFallbackFontSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridFallbackFontSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridFallbackFontSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Compares this object with the given [other] object for reference equality. + */ + @inline(__always) + public func equals(other: HybridFallbackFontSpec_cxx) -> Bool { + return self.__implementation === other.__implementation + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + + + // Methods + +} diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift index b52e9f2d..85cf4f6b 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift @@ -14,13 +14,13 @@ public protocol HybridRiveFontConfigSpec_protocol: HybridObject { // Methods - func enableSystemFontFallback() throws -> Promise - func addFallbackFont(bytes: ArrayBuffer) throws -> Promise - func addFallbackFontFromResource(resource: String) throws -> Promise - func addFallbackFontFromURL(url: String) throws -> Promise - func addFallbackFontByName(name: String) throws -> Promise + func loadFontFromURL(url: String) throws -> Promise<(any HybridFallbackFontSpec)> + func loadFontFromResource(resource: String) throws -> (any HybridFallbackFontSpec) + func loadFontFromBytes(bytes: ArrayBuffer) throws -> (any HybridFallbackFontSpec) + func loadFontByName(name: String) throws -> (any HybridFallbackFontSpec) + func setFontsForWeight(weight: Double, fonts: [(any HybridFallbackFontSpec)]) throws -> Void func applyFallbackFonts() throws -> Promise - func clearCustomFallbackFonts() throws -> Promise + func clearFallbackFonts() throws -> Promise } public extension HybridRiveFontConfigSpec_protocol { diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift index 031a7f00..c3da6478 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift @@ -126,97 +126,84 @@ open class HybridRiveFontConfigSpec_cxx { // Methods @inline(__always) - public final func enableSystemFontFallback() -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func loadFontFromURL(url: std.string) -> bridge.Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____ { do { - let __result = try self.__implementation.enableSystemFontFallback() - let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in - let __promise = bridge.create_std__shared_ptr_Promise_void__() - let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) + let __result = try self.__implementation.loadFontFromURL(url: String(url)) + let __resultCpp = { () -> bridge.std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___ in + let __promise = bridge.create_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___() + let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec___(__promise) __result - .then({ __result in __promiseHolder.resolve() }) + .then({ __result in __promiseHolder.resolve({ () -> bridge.std__shared_ptr_HybridFallbackFontSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }()) }) .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) return __promise }() - return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + return bridge.create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____(__resultCpp) } catch (let __error) { let __exceptionPtr = __error.toCpp() - return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + return bridge.create_Result_std__shared_ptr_Promise_std__shared_ptr_HybridFallbackFontSpec____(__exceptionPtr) } } @inline(__always) - public final func addFallbackFont(bytes: ArrayBuffer) -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func loadFontFromResource(resource: std.string) -> bridge.Result_std__shared_ptr_HybridFallbackFontSpec__ { do { - let __result = try self.__implementation.addFallbackFont(bytes: bytes) - let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in - let __promise = bridge.create_std__shared_ptr_Promise_void__() - let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) - __result - .then({ __result in __promiseHolder.resolve() }) - .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) - return __promise + let __result = try self.__implementation.loadFontFromResource(resource: String(resource)) + let __resultCpp = { () -> bridge.std__shared_ptr_HybridFallbackFontSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() }() - return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__resultCpp) } catch (let __error) { let __exceptionPtr = __error.toCpp() - return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__exceptionPtr) } } @inline(__always) - public final func addFallbackFontFromResource(resource: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func loadFontFromBytes(bytes: ArrayBuffer) -> bridge.Result_std__shared_ptr_HybridFallbackFontSpec__ { do { - let __result = try self.__implementation.addFallbackFontFromResource(resource: String(resource)) - let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in - let __promise = bridge.create_std__shared_ptr_Promise_void__() - let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) - __result - .then({ __result in __promiseHolder.resolve() }) - .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) - return __promise + let __result = try self.__implementation.loadFontFromBytes(bytes: bytes) + let __resultCpp = { () -> bridge.std__shared_ptr_HybridFallbackFontSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() }() - return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__resultCpp) } catch (let __error) { let __exceptionPtr = __error.toCpp() - return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__exceptionPtr) } } @inline(__always) - public final func addFallbackFontFromURL(url: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func loadFontByName(name: std.string) -> bridge.Result_std__shared_ptr_HybridFallbackFontSpec__ { do { - let __result = try self.__implementation.addFallbackFontFromURL(url: String(url)) - let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in - let __promise = bridge.create_std__shared_ptr_Promise_void__() - let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) - __result - .then({ __result in __promiseHolder.resolve() }) - .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) - return __promise + let __result = try self.__implementation.loadFontByName(name: String(name)) + let __resultCpp = { () -> bridge.std__shared_ptr_HybridFallbackFontSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() }() - return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__resultCpp) } catch (let __error) { let __exceptionPtr = __error.toCpp() - return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__exceptionPtr) } } @inline(__always) - public final func addFallbackFontByName(name: std.string) -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func setFontsForWeight(weight: Double, fonts: bridge.std__vector_std__shared_ptr_HybridFallbackFontSpec__) -> bridge.Result_void_ { do { - let __result = try self.__implementation.addFallbackFontByName(name: String(name)) - let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in - let __promise = bridge.create_std__shared_ptr_Promise_void__() - let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) - __result - .then({ __result in __promiseHolder.resolve() }) - .catch({ __error in __promiseHolder.reject(__error.toCpp()) }) - return __promise - }() - return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp) + try self.__implementation.setFontsForWeight(weight: weight, fonts: fonts.map({ __item in { () -> any HybridFallbackFontSpec in + let __unsafePointer = bridge.get_std__shared_ptr_HybridFallbackFontSpec_(__item) + let __instance = HybridFallbackFontSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridFallbackFontSpec() + }() })) + return bridge.create_Result_void_() } catch (let __error) { let __exceptionPtr = __error.toCpp() - return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr) + return bridge.create_Result_void_(__exceptionPtr) } } @@ -240,9 +227,9 @@ open class HybridRiveFontConfigSpec_cxx { } @inline(__always) - public final func clearCustomFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { + public final func clearFallbackFonts() -> bridge.Result_std__shared_ptr_Promise_void___ { do { - let __result = try self.__implementation.clearCustomFallbackFonts() + let __result = try self.__implementation.clearFallbackFonts() let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in let __promise = bridge.create_std__shared_ptr_Promise_void__() let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise) diff --git a/nitrogen/generated/shared/c++/HybridFallbackFontSpec.cpp b/nitrogen/generated/shared/c++/HybridFallbackFontSpec.cpp new file mode 100644 index 00000000..4ed3104b --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridFallbackFontSpec.cpp @@ -0,0 +1,21 @@ +/// +/// HybridFallbackFontSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#include "HybridFallbackFontSpec.hpp" + +namespace margelo::nitro::rive { + + void HybridFallbackFontSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + + }); + } + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/shared/c++/HybridFallbackFontSpec.hpp b/nitrogen/generated/shared/c++/HybridFallbackFontSpec.hpp new file mode 100644 index 00000000..33172c5e --- /dev/null +++ b/nitrogen/generated/shared/c++/HybridFallbackFontSpec.hpp @@ -0,0 +1,62 @@ +/// +/// HybridFallbackFontSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + + + +namespace margelo::nitro::rive { + + using namespace margelo::nitro; + + /** + * An abstract base class for `FallbackFont` + * Inherit this class to create instances of `HybridFallbackFontSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridFallbackFont: public HybridFallbackFontSpec { + * public: + * HybridFallbackFont(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridFallbackFontSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridFallbackFontSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridFallbackFontSpec() override = default; + + public: + // Properties + + + public: + // Methods + + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "FallbackFont"; + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp index 4f554a6d..cc2ab2b2 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp @@ -14,13 +14,13 @@ namespace margelo::nitro::rive { HybridObject::loadHybridMethods(); // load custom methods/properties registerHybrids(this, [](Prototype& prototype) { - prototype.registerHybridMethod("enableSystemFontFallback", &HybridRiveFontConfigSpec::enableSystemFontFallback); - prototype.registerHybridMethod("addFallbackFont", &HybridRiveFontConfigSpec::addFallbackFont); - prototype.registerHybridMethod("addFallbackFontFromResource", &HybridRiveFontConfigSpec::addFallbackFontFromResource); - prototype.registerHybridMethod("addFallbackFontFromURL", &HybridRiveFontConfigSpec::addFallbackFontFromURL); - prototype.registerHybridMethod("addFallbackFontByName", &HybridRiveFontConfigSpec::addFallbackFontByName); + prototype.registerHybridMethod("loadFontFromURL", &HybridRiveFontConfigSpec::loadFontFromURL); + prototype.registerHybridMethod("loadFontFromResource", &HybridRiveFontConfigSpec::loadFontFromResource); + prototype.registerHybridMethod("loadFontFromBytes", &HybridRiveFontConfigSpec::loadFontFromBytes); + prototype.registerHybridMethod("loadFontByName", &HybridRiveFontConfigSpec::loadFontByName); + prototype.registerHybridMethod("setFontsForWeight", &HybridRiveFontConfigSpec::setFontsForWeight); prototype.registerHybridMethod("applyFallbackFonts", &HybridRiveFontConfigSpec::applyFallbackFonts); - prototype.registerHybridMethod("clearCustomFallbackFonts", &HybridRiveFontConfigSpec::clearCustomFallbackFonts); + prototype.registerHybridMethod("clearFallbackFonts", &HybridRiveFontConfigSpec::clearFallbackFonts); }); } diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp index 8b22a5c6..4780eb7c 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp @@ -13,11 +13,15 @@ #error NitroModules cannot be found! Are you sure you installed NitroModules properly? #endif +// Forward declaration of `HybridFallbackFontSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridFallbackFontSpec; } - +#include +#include "HybridFallbackFontSpec.hpp" #include -#include #include +#include +#include namespace margelo::nitro::rive { @@ -50,13 +54,13 @@ namespace margelo::nitro::rive { public: // Methods - virtual std::shared_ptr> enableSystemFontFallback() = 0; - virtual std::shared_ptr> addFallbackFont(const std::shared_ptr& bytes) = 0; - virtual std::shared_ptr> addFallbackFontFromResource(const std::string& resource) = 0; - virtual std::shared_ptr> addFallbackFontFromURL(const std::string& url) = 0; - virtual std::shared_ptr> addFallbackFontByName(const std::string& name) = 0; + virtual std::shared_ptr>> loadFontFromURL(const std::string& url) = 0; + virtual std::shared_ptr loadFontFromResource(const std::string& resource) = 0; + virtual std::shared_ptr loadFontFromBytes(const std::shared_ptr& bytes) = 0; + virtual std::shared_ptr loadFontByName(const std::string& name) = 0; + virtual void setFontsForWeight(double weight, const std::vector>& fonts) = 0; virtual std::shared_ptr> applyFallbackFonts() = 0; - virtual std::shared_ptr> clearCustomFallbackFonts() = 0; + virtual std::shared_ptr> clearFallbackFonts() = 0; protected: // Hybrid Setup diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index 8147dd8b..eb1626ee 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -1,6 +1,9 @@ import { NitroModules } from 'react-native-nitro-modules'; import { Image } from 'react-native'; -import type { RiveFontConfig } from '../specs/RiveFontConfig.nitro'; +import type { + RiveFontConfig, + FallbackFont, +} from '../specs/RiveFontConfig.nitro'; const RiveFontConfigInternal = NitroModules.createHybridObject('RiveFontConfig'); @@ -20,64 +23,59 @@ export type FontSource = | string | ArrayBuffer; -export namespace RiveFonts { - export async function enableSystemFontFallback(): Promise { - return RiveFontConfigInternal.enableSystemFontFallback(); - } +export type FallbackFontMap = Record; - /** - * Add fallback fonts for missing glyphs in Rive graphics. - * Fonts are tried in array order — first font that contains the glyph wins. - * These fonts are tried before the default system font fallback. - */ - export async function addFallbackFonts(sources: FontSource[]): Promise { - for (const source of sources) { - await addSingleFont(source); +export namespace RiveFonts { + export async function loadFont(source: FontSource): Promise { + if (source instanceof ArrayBuffer) { + return RiveFontConfigInternal.loadFontFromBytes(source); } - await RiveFontConfigInternal.applyFallbackFonts(); - } - export async function clearCustomFallbackFonts(): Promise { - return RiveFontConfigInternal.clearCustomFallbackFonts(); - } -} + if (typeof source === 'number') { + const resolved = Image.resolveAssetSource(source); + if (!resolved?.uri) { + throw new Error( + `Invalid font asset: could not resolve require() ID ${source}. Ensure 'ttf' is in metro.config.js assetExts.` + ); + } + return loadFontByURI(resolved.uri); + } -async function addSingleFont(source: FontSource): Promise { - if (source instanceof ArrayBuffer) { - return RiveFontConfigInternal.addFallbackFont(source); - } + if (typeof source === 'object' && 'name' in source) { + return RiveFontConfigInternal.loadFontByName(source.name); + } - if (typeof source === 'number') { - const resolved = Image.resolveAssetSource(source); - if (!resolved?.uri) { - throw new Error( - `Invalid font asset: could not resolve require() ID ${source}. Ensure 'ttf' is in metro.config.js assetExts.` - ); + if (typeof source === 'object' && 'uri' in source) { + return loadFontByURI(source.uri); } - return dispatchByURI(resolved.uri); - } - if (typeof source === 'object' && 'name' in source) { - return RiveFontConfigInternal.addFallbackFontByName(source.name); - } + if (typeof source === 'string') { + if (/^https?:\/\//.test(source) || /^file:\/\//.test(source)) { + return RiveFontConfigInternal.loadFontFromURL(source); + } + return RiveFontConfigInternal.loadFontFromResource(source); + } - if (typeof source === 'object' && 'uri' in source) { - return dispatchByURI(source.uri); + throw new Error(`Invalid font source: ${String(source)}`); } - if (typeof source === 'string') { - if (/^https?:\/\//.test(source) || /^file:\/\//.test(source)) { - return RiveFontConfigInternal.addFallbackFontFromURL(source); + export async function setFallbackFonts( + fontsByWeight: FallbackFontMap + ): Promise { + for (const [weight, fonts] of Object.entries(fontsByWeight)) { + RiveFontConfigInternal.setFontsForWeight(Number(weight), fonts); } - return RiveFontConfigInternal.addFallbackFontFromResource(source); + await RiveFontConfigInternal.applyFallbackFonts(); } - throw new Error(`Invalid font source: ${String(source)}`); + export async function clearFallbackFonts(): Promise { + return RiveFontConfigInternal.clearFallbackFonts(); + } } -function dispatchByURI(uri: string): Promise { +function loadFontByURI(uri: string): Promise | FallbackFont { if (/^https?:\/\//.test(uri) || /^file:\/\//.test(uri)) { - return RiveFontConfigInternal.addFallbackFontFromURL(uri); + return RiveFontConfigInternal.loadFontFromURL(uri); } - return RiveFontConfigInternal.addFallbackFontFromResource(uri); + return RiveFontConfigInternal.loadFontFromResource(uri); } diff --git a/src/index.tsx b/src/index.tsx index 1b36a5e5..88a81d7a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -39,7 +39,12 @@ export { Alignment } from './core/Alignment'; export { RiveFileFactory } from './core/RiveFile'; export { RiveImages } from './core/RiveImages'; export type { RiveImage } from './specs/RiveImage.nitro'; -export { RiveFonts, type FontSource } from './core/RiveFonts'; +export { + RiveFonts, + type FontSource, + type FallbackFontMap, +} from './core/RiveFonts'; +export type { FallbackFont } from './specs/RiveFontConfig.nitro'; export { RiveColor } from './core/RiveColor'; export { type RiveEvent, RiveEventType } from './core/Events'; export { type RiveError, RiveErrorType } from './core/Errors'; diff --git a/src/specs/RiveFontConfig.nitro.ts b/src/specs/RiveFontConfig.nitro.ts index 255393a9..3bd3fd22 100644 --- a/src/specs/RiveFontConfig.nitro.ts +++ b/src/specs/RiveFontConfig.nitro.ts @@ -1,46 +1,15 @@ import type { HybridObject } from 'react-native-nitro-modules'; +export interface FallbackFont + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {} + export interface RiveFontConfig extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { - /** - * Enable system font fallback for missing glyphs (CJK, Arabic, Hebrew, etc.). - * On iOS, this activates Core Text's automatic font matching. - * On Android, this sets up a FontFallbackStrategy using system fonts. - */ - enableSystemFontFallback(): Promise; - - /** - * Add a custom fallback font from raw bytes (TTF/OTF). - * Custom fonts are tried before system fonts when resolving missing glyphs. - */ - addFallbackFont(bytes: ArrayBuffer): Promise; - - /** - * Add a custom fallback font from a bundled resource. - * @param resource - The resource name (e.g., "NotoSansCJK.ttf") - */ - addFallbackFontFromResource(resource: string): Promise; - - /** - * Add a custom fallback font from a URL (http/https/file://). - */ - addFallbackFontFromURL(url: string): Promise; - - /** - * Add a system font as a fallback by its font family name. - * On iOS uses UIFont(name:), on Android uses FontHelper to resolve system fonts. - */ - addFallbackFontByName(name: string): Promise; - - /** - * Apply pending font changes — updates the native fallback font list and resets caches. - * Called once after adding fonts, rather than after each individual add. - */ + loadFontFromURL(url: string): Promise; + loadFontFromResource(resource: string): FallbackFont; + loadFontFromBytes(bytes: ArrayBuffer): FallbackFont; + loadFontByName(name: string): FallbackFont; + setFontsForWeight(weight: number, fonts: FallbackFont[]): void; applyFallbackFonts(): Promise; - - /** - * Clear all custom fallback fonts. - * System font fallback remains active if previously enabled. - */ - clearCustomFallbackFonts(): Promise; + clearFallbackFonts(): Promise; } From 8c5a01f1c0230cca3d628430231f23791e1462f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 27 Feb 2026 12:40:30 +0100 Subject: [PATCH 13/20] refactor: use 'default' key instead of 0 for fallback font weight map --- example/src/exercisers/FontFallbackExample.tsx | 2 +- src/core/RiveFonts.ts | 14 +++++++++++--- src/index.tsx | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 046aac42..6e0b14de 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -117,7 +117,7 @@ export default function FontFallbackExample() { const handleMount = async () => { try { const handles = [...loadedFonts.values()]; - await RiveFonts.setFallbackFonts({ 0: handles }); + await RiveFonts.setFallbackFonts({ default: handles }); } catch (err) { console.error('setFallbackFonts:', err); } diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index eb1626ee..0933f595 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -23,7 +23,13 @@ export type FontSource = | string | ArrayBuffer; -export type FallbackFontMap = Record; +export type FontWeight = number | 'default'; + +export type FallbackFontMap = Partial>; + +function resolveWeight(key: string): number { + return key === 'default' ? 0 : Number(key); +} export namespace RiveFonts { export async function loadFont(source: FontSource): Promise { @@ -62,8 +68,10 @@ export namespace RiveFonts { export async function setFallbackFonts( fontsByWeight: FallbackFontMap ): Promise { - for (const [weight, fonts] of Object.entries(fontsByWeight)) { - RiveFontConfigInternal.setFontsForWeight(Number(weight), fonts); + for (const [key, fonts] of Object.entries(fontsByWeight)) { + if (fonts) { + RiveFontConfigInternal.setFontsForWeight(resolveWeight(key), fonts); + } } await RiveFontConfigInternal.applyFallbackFonts(); } diff --git a/src/index.tsx b/src/index.tsx index 88a81d7a..28756ac0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -42,6 +42,7 @@ export type { RiveImage } from './specs/RiveImage.nitro'; export { RiveFonts, type FontSource, + type FontWeight, type FallbackFontMap, } from './core/RiveFonts'; export type { FallbackFont } from './specs/RiveFontConfig.nitro'; From 36aadefc3b9f385c22524133ae6d5ff6c7b1fd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 2 Mar 2026 14:50:58 +0100 Subject: [PATCH 14/20] fix: load only first matching system font in fallback strategy --- .../com/margelo/nitro/rive/HybridRiveFontConfig.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index 9a5d7f4c..7a4e0b49 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -103,15 +103,9 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { fontsByWeight[requestedWeight] ?: fontsByWeight[0] ?: emptyList() } val result = fonts.toMutableList() - val systemFonts = FontHelper.getFallbackFonts(Fonts.FontOpts(weight = weight)) - for (font in systemFonts) { - try { - FontHelper.getFontBytes(font)?.let { result.add(it) } - } catch (e: OutOfMemoryError) { - Log.w(TAG, "Skipped system font due to memory pressure: ${e.message}") - break - } - } + // Append first matching system font as last resort, matching rive-android default behavior: + // https://github.com/rive-app/rive-android/blob/602343c/kotlin/src/main/java/app/rive/runtime/kotlin/fonts/FontHelpers.kt#L484-L486 + FontHelper.getFallbackFontBytes(Fonts.FontOpts(weight = weight))?.let { result.add(it) } return result } } From 313d01a0c89d97547d7c365d090bddef4e7ba6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 2 Mar 2026 20:07:30 +0100 Subject: [PATCH 15/20] refactor: extract DEFAULT_WEIGHT constant for fallback font weight map --- .../main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt | 3 ++- ios/HybridRiveFontConfig.swift | 3 ++- src/core/RiveFonts.ts | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index 7a4e0b49..ad81febc 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.withContext class HybridRiveFontConfig : HybridRiveFontConfigSpec() { companion object { private const val TAG = "RiveFonts" + private const val DEFAULT_WEIGHT = 0 private val fontsByWeight: MutableMap> = java.util.Collections.synchronizedMap(mutableMapOf()) @@ -100,7 +101,7 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { override fun getFont(weight: Fonts.Weight): List { val requestedWeight = weight.weight val fonts = synchronized(fontsByWeight) { - fontsByWeight[requestedWeight] ?: fontsByWeight[0] ?: emptyList() + fontsByWeight[requestedWeight] ?: fontsByWeight[DEFAULT_WEIGHT] ?: emptyList() } val result = fonts.toMutableList() // Append first matching system font as last resort, matching rive-android default behavior: diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift index cfa2191e..2698b4a9 100644 --- a/ios/HybridRiveFontConfig.swift +++ b/ios/HybridRiveFontConfig.swift @@ -3,6 +3,7 @@ import NitroModules import RiveRuntime class HybridRiveFontConfig: HybridRiveFontConfigSpec { + private static let defaultWeight = 0 private static var fontsByWeight: [Int: [UIFont]] = [:] func loadFontFromURL(url: String) throws -> Promise<(any HybridFallbackFontSpec)> { @@ -53,7 +54,7 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { _ = RiveFont.self RiveFont.fallbackFontsCallback = { weight in let requestedWeight = Int(weight.rawWeight) - let fonts = Self.fontsByWeight[requestedWeight] ?? Self.fontsByWeight[0] ?? [] + let fonts = Self.fontsByWeight[requestedWeight] ?? Self.fontsByWeight[Self.defaultWeight] ?? [] var providers: [RiveFallbackFontProvider] = fonts providers.append(RiveFallbackFontDescriptor()) return providers diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index 0933f595..6a84a455 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -27,8 +27,10 @@ export type FontWeight = number | 'default'; export type FallbackFontMap = Partial>; +const DEFAULT_WEIGHT = 0; + function resolveWeight(key: string): number { - return key === 'default' ? 0 : Number(key); + return key === 'default' ? DEFAULT_WEIGHT : Number(key); } export namespace RiveFonts { From f58fd11a31d26ac4dc24a1404acb7dfb926c076e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 4 Mar 2026 14:09:24 +0100 Subject: [PATCH 16/20] refactor: make system font fallback opt-in via 'default' sentinel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System fallback is no longer silently appended — users explicitly include 'default' in the font array to opt in. --- .../nitro/rive/HybridDefaultFallbackFont.kt | 8 +++ .../nitro/rive/HybridRiveFontConfig.kt | 28 ++++++--- .../src/exercisers/FontFallbackExample.tsx | 58 +++++++++++-------- ios/HybridDefaultFallbackFont.swift | 3 + ios/HybridRiveFontConfig.swift | 24 +++++--- .../android/c++/JHybridRiveFontConfigSpec.cpp | 5 ++ .../android/c++/JHybridRiveFontConfigSpec.hpp | 1 + .../nitro/rive/HybridRiveFontConfigSpec.kt | 4 ++ .../generated/ios/RNRive-Swift-Cxx-Bridge.cpp | 17 ++++++ .../generated/ios/RNRive-Swift-Cxx-Bridge.hpp | 29 ++++++++++ .../ios/c++/HybridRiveFontConfigSpecSwift.hpp | 8 +++ .../ios/swift/HybridRiveFontConfigSpec.swift | 1 + .../swift/HybridRiveFontConfigSpec_cxx.swift | 15 +++++ .../shared/c++/HybridRiveFontConfigSpec.cpp | 1 + .../shared/c++/HybridRiveFontConfigSpec.hpp | 1 + src/core/RiveFonts.ts | 13 ++++- src/index.tsx | 3 + src/specs/RiveFontConfig.nitro.ts | 1 + 18 files changed, 177 insertions(+), 43 deletions(-) create mode 100644 android/src/main/java/com/margelo/nitro/rive/HybridDefaultFallbackFont.kt create mode 100644 ios/HybridDefaultFallbackFont.swift diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridDefaultFallbackFont.kt b/android/src/main/java/com/margelo/nitro/rive/HybridDefaultFallbackFont.kt new file mode 100644 index 00000000..f7f5152b --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/rive/HybridDefaultFallbackFont.kt @@ -0,0 +1,8 @@ +package com.margelo.nitro.rive + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +@Keep +@DoNotStrip +class HybridDefaultFallbackFont : HybridFallbackFontSpec() diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt index ad81febc..8245a31d 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt @@ -19,7 +19,7 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { companion object { private const val TAG = "RiveFonts" private const val DEFAULT_WEIGHT = 0 - private val fontsByWeight: MutableMap> = + private val fontsByWeight: MutableMap> = java.util.Collections.synchronizedMap(mutableMapOf()) private fun resetFontCache() { @@ -87,11 +87,14 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { return HybridFallbackFont(fontBytes) } + override fun getSystemDefaultFont(): HybridFallbackFontSpec { + return HybridDefaultFallbackFont() + } + override fun setFontsForWeight(weight: Double, fonts: Array) { val key = weight.toInt() - val byteArrays = fonts.mapNotNull { (it as? HybridFallbackFont)?.fontBytes } synchronized(fontsByWeight) { - fontsByWeight[key] = byteArrays + fontsByWeight[key] = fonts.toList() } } @@ -100,14 +103,21 @@ class HybridRiveFontConfig : HybridRiveFontConfigSpec() { FontFallbackStrategy.stylePicker = object : FontFallbackStrategy { override fun getFont(weight: Fonts.Weight): List { val requestedWeight = weight.weight - val fonts = synchronized(fontsByWeight) { + val specs = synchronized(fontsByWeight) { fontsByWeight[requestedWeight] ?: fontsByWeight[DEFAULT_WEIGHT] ?: emptyList() } - val result = fonts.toMutableList() - // Append first matching system font as last resort, matching rive-android default behavior: - // https://github.com/rive-app/rive-android/blob/602343c/kotlin/src/main/java/app/rive/runtime/kotlin/fonts/FontHelpers.kt#L484-L486 - FontHelper.getFallbackFontBytes(Fonts.FontOpts(weight = weight))?.let { result.add(it) } - return result + return specs.mapNotNull { spec -> + when (spec) { + is HybridDefaultFallbackFont -> + FontHelper.getFallbackFontBytes(Fonts.FontOpts(weight = weight)) + is HybridFallbackFont -> + spec.fontBytes + else -> { + Log.e(TAG, "Unknown fallback font spec type: ${spec::class.simpleName}") + null + } + } + } } } resetFontCache() diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 6e0b14de..3d573e17 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -67,10 +67,11 @@ type LogEntry = { export default function FontFallbackExample() { const [mounted, setMounted] = useState(false); - const [selectedFonts, setSelectedFonts] = useState>(new Set()); + const [selectedFonts, setSelectedFonts] = useState([]); const [loadedFonts, setLoadedFonts] = useState>( new Map() ); + const [systemFallback, setSystemFallback] = useState(false); const [loadingFont, setLoadingFont] = useState(null); const [log, setLog] = useState([]); const [inputText, setInputText] = useState('ABC 你好 สวัสดี'); @@ -82,14 +83,10 @@ export default function FontFallbackExample() { const toggleFont = async (key: FontKey) => { if (mounted) return; const font = FONTS[key]!; - const isSelected = selectedFonts.has(key); + const isSelected = selectedFonts.includes(key); if (isSelected) { - setSelectedFonts((prev) => { - const next = new Set(prev); - next.delete(key); - return next; - }); + setSelectedFonts((prev) => prev.filter((k) => k !== key)); setLoadedFonts((prev) => { const next = new Map(prev); next.delete(key); @@ -103,7 +100,7 @@ export default function FontFallbackExample() { try { const handle = await RiveFonts.loadFont(font.source); addLog(`Loaded ${font.label}`, 'success'); - setSelectedFonts((prev) => new Set(prev).add(key)); + setSelectedFonts((prev) => [...prev, key]); setLoadedFonts((prev) => new Map(prev).set(key, handle)); } catch (err) { const msg = err instanceof Error ? err.message : String(err); @@ -116,22 +113,30 @@ export default function FontFallbackExample() { const handleMount = async () => { try { - const handles = [...loadedFonts.values()]; - await RiveFonts.setFallbackFonts({ default: handles }); + const entries: (FallbackFont | 'default')[] = selectedFonts + .map((key) => loadedFonts.get(key)) + .filter((f): f is FallbackFont => f != null); + if (systemFallback) { + entries.push('default'); + } + await RiveFonts.setFallbackFonts({ default: entries }); } catch (err) { console.error('setFallbackFonts:', err); } setMounted(true); + const parts = selectedFonts.map((k) => FONTS[k]!.label); + if (systemFallback) parts.push('System Fallback'); addLog( - `Mounted with: ${selectedFonts.size > 0 ? [...selectedFonts].map((k) => FONTS[k]!.label).join(', ') : 'system defaults only'}`, + `Mounted with: ${parts.length > 0 ? parts.join(', ') : 'no fonts configured'}`, 'info' ); }; const handleReset = async () => { await RiveFonts.clearFallbackFonts(); - setSelectedFonts(new Set()); + setSelectedFonts([]); setLoadedFonts(new Map()); + setSystemFallback(false); setMounted(false); addLog('Cleared fonts & unmounted view', 'info'); }; @@ -193,13 +198,22 @@ export default function FontFallbackExample() { key={key} label={FONTS[key]!.label} sublabel={FONTS[key]!.sublabel} - selected={selectedFonts.has(key)} + order={selectedFonts.indexOf(key) + 1 || undefined} disabled={mounted} loading={loadingFont === key} onPress={() => toggleFont(key)} /> ))} + setSystemFallback((prev) => !prev)} + /> + {mounted ? ( ) : ( - 0 - ? `Mount View with ${selectedFonts.size} Custom Font${selectedFonts.size > 1 ? 's' : ''}` - : 'Mount View (System Defaults Only)' - } - onPress={handleMount} - /> + )} {log.length > 0 && ( @@ -308,18 +315,19 @@ function MountedView({ text }: { text: string }) { function FontToggle({ label, sublabel, - selected, + order, disabled, loading, onPress, }: { label: string; sublabel: string; - selected: boolean; + order?: number; disabled: boolean; loading: boolean; onPress: () => void; }) { + const selected = order != null; return ( {sublabel} - {selected ? '✓' : ''} + + {selected ? `${order}` : ''} + )} diff --git a/ios/HybridDefaultFallbackFont.swift b/ios/HybridDefaultFallbackFont.swift new file mode 100644 index 00000000..035356e4 --- /dev/null +++ b/ios/HybridDefaultFallbackFont.swift @@ -0,0 +1,3 @@ +import NitroModules + +class HybridDefaultFallbackFont: HybridFallbackFontSpec {} diff --git a/ios/HybridRiveFontConfig.swift b/ios/HybridRiveFontConfig.swift index 2698b4a9..3425a5a3 100644 --- a/ios/HybridRiveFontConfig.swift +++ b/ios/HybridRiveFontConfig.swift @@ -4,7 +4,7 @@ import RiveRuntime class HybridRiveFontConfig: HybridRiveFontConfigSpec { private static let defaultWeight = 0 - private static var fontsByWeight: [Int: [UIFont]] = [:] + private static var fontsByWeight: [Int: [any HybridFallbackFontSpec]] = [:] func loadFontFromURL(url: String) throws -> Promise<(any HybridFallbackFontSpec)> { return Promise.async { @@ -43,10 +43,13 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { return HybridFallbackFont(font: font) } + func getSystemDefaultFont() throws -> (any HybridFallbackFontSpec) { + return HybridDefaultFallbackFont() + } + func setFontsForWeight(weight: Double, fonts: [(any HybridFallbackFontSpec)]) throws { let key = Int(weight) - let uiFonts = fonts.compactMap { ($0 as? HybridFallbackFont)?.font } - Self.fontsByWeight[key] = uiFonts + Self.fontsByWeight[key] = fonts } func applyFallbackFonts() throws -> Promise { @@ -54,10 +57,17 @@ class HybridRiveFontConfig: HybridRiveFontConfigSpec { _ = RiveFont.self RiveFont.fallbackFontsCallback = { weight in let requestedWeight = Int(weight.rawWeight) - let fonts = Self.fontsByWeight[requestedWeight] ?? Self.fontsByWeight[Self.defaultWeight] ?? [] - var providers: [RiveFallbackFontProvider] = fonts - providers.append(RiveFallbackFontDescriptor()) - return providers + let specs = Self.fontsByWeight[requestedWeight] ?? Self.fontsByWeight[Self.defaultWeight] ?? [] + return specs.compactMap { spec in + if spec is HybridDefaultFallbackFont { + return RiveFallbackFontDescriptor() + } else if let fallbackFont = spec as? HybridFallbackFont { + return fallbackFont.font + } else { + print("[RiveFonts] Unknown fallback font spec type: \(type(of: spec))") + return nil + } + } } } } diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp index 94ae9635..62759763 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.cpp @@ -91,6 +91,11 @@ namespace margelo::nitro::rive { auto __result = method(_javaPart, jni::make_jstring(name)); return __result->cthis()->shared_cast(); } + std::shared_ptr JHybridRiveFontConfigSpec::getSystemDefaultFont() { + static const auto method = javaClassStatic()->getMethod()>("getSystemDefaultFont"); + auto __result = method(_javaPart); + return __result->cthis()->shared_cast(); + } void JHybridRiveFontConfigSpec::setFontsForWeight(double weight, const std::vector>& fonts) { static const auto method = javaClassStatic()->getMethod> /* fonts */)>("setFontsForWeight"); method(_javaPart, weight, [&]() { diff --git a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp index 8737e7f3..ff10b319 100644 --- a/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveFontConfigSpec.hpp @@ -59,6 +59,7 @@ namespace margelo::nitro::rive { std::shared_ptr loadFontFromResource(const std::string& resource) override; std::shared_ptr loadFontFromBytes(const std::shared_ptr& bytes) override; std::shared_ptr loadFontByName(const std::string& name) override; + std::shared_ptr getSystemDefaultFont() override; void setFontsForWeight(double weight, const std::vector>& fonts) override; std::shared_ptr> applyFallbackFonts() override; std::shared_ptr> clearFallbackFonts() override; diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt index f82caf1e..401502ee 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveFontConfigSpec.kt @@ -63,6 +63,10 @@ abstract class HybridRiveFontConfigSpec: HybridObject() { @Keep abstract fun loadFontByName(name: String): HybridFallbackFontSpec + @DoNotStrip + @Keep + abstract fun getSystemDefaultFont(): HybridFallbackFontSpec + @DoNotStrip @Keep abstract fun setFontsForWeight(weight: Double, fonts: Array): Unit diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp index 89ae6cb9..244490db 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp @@ -15,6 +15,7 @@ #include "HybridRiveFontConfigSpecSwift.hpp" #include "HybridRiveImageFactorySpecSwift.hpp" #include "HybridRiveImageSpecSwift.hpp" +#include "HybridRiveInternalSpecSwift.hpp" #include "HybridRiveRuntimeSpecSwift.hpp" #include "HybridRiveViewSpecSwift.hpp" #include "HybridViewModelArtboardPropertySpecSwift.hpp" @@ -202,6 +203,22 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridRiveInternalSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + RNRive::HybridRiveInternalSpec_cxx swiftPart = RNRive::HybridRiveInternalSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridRiveInternalSpec_(std__shared_ptr_HybridRiveInternalSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridRiveInternalSpec\" is not implemented in Swift!"); + } + #endif + RNRive::HybridRiveInternalSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridRiveRuntimeSpec_(void* NON_NULL swiftUnsafePointer) noexcept { RNRive::HybridRiveRuntimeSpec_cxx swiftPart = RNRive::HybridRiveRuntimeSpec_cxx::fromUnsafe(swiftUnsafePointer); diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp index 6ccaab64..cf50d283 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp @@ -34,6 +34,8 @@ namespace margelo::nitro::rive { class HybridRiveFontConfigSpec; } namespace margelo::nitro::rive { class HybridRiveImageFactorySpec; } // Forward declaration of `HybridRiveImageSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageSpec; } +// Forward declaration of `HybridRiveInternalSpec` to properly resolve imports. +namespace margelo::nitro::rive { class HybridRiveInternalSpec; } // Forward declaration of `HybridRiveRuntimeSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveRuntimeSpec; } // Forward declaration of `HybridRiveViewSpec` to properly resolve imports. @@ -62,6 +64,8 @@ namespace margelo::nitro::rive { class HybridViewModelSpec; } namespace margelo::nitro::rive { class HybridViewModelStringPropertySpec; } // Forward declaration of `HybridViewModelTriggerPropertySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridViewModelTriggerPropertySpec; } +// Forward declaration of `MemoryUsage` to properly resolve imports. +namespace margelo::nitro::rive { struct MemoryUsage; } // Forward declaration of `ReferencedAssetsType` to properly resolve imports. namespace margelo::nitro::rive { struct ReferencedAssetsType; } // Forward declaration of `ResolvedReferencedAsset` to properly resolve imports. @@ -90,6 +94,8 @@ namespace RNRive { class HybridRiveFontConfigSpec_cxx; } namespace RNRive { class HybridRiveImageFactorySpec_cxx; } // Forward declaration of `HybridRiveImageSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageSpec_cxx; } +// Forward declaration of `HybridRiveInternalSpec_cxx` to properly resolve imports. +namespace RNRive { class HybridRiveInternalSpec_cxx; } // Forward declaration of `HybridRiveRuntimeSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveRuntimeSpec_cxx; } // Forward declaration of `HybridRiveViewSpec_cxx` to properly resolve imports. @@ -133,6 +139,7 @@ namespace RNRive { class HybridViewModelTriggerPropertySpec_cxx; } #include "HybridRiveFontConfigSpec.hpp" #include "HybridRiveImageFactorySpec.hpp" #include "HybridRiveImageSpec.hpp" +#include "HybridRiveInternalSpec.hpp" #include "HybridRiveRuntimeSpec.hpp" #include "HybridRiveViewSpec.hpp" #include "HybridViewModelArtboardPropertySpec.hpp" @@ -147,6 +154,7 @@ namespace RNRive { class HybridViewModelTriggerPropertySpec_cxx; } #include "HybridViewModelSpec.hpp" #include "HybridViewModelStringPropertySpec.hpp" #include "HybridViewModelTriggerPropertySpec.hpp" +#include "MemoryUsage.hpp" #include "ReferencedAssetsType.hpp" #include "ResolvedReferencedAsset.hpp" #include "RiveError.hpp" @@ -649,6 +657,27 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>>::withError(error); } + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridRiveInternalSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridRiveInternalSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridRiveInternalSpec_(std__shared_ptr_HybridRiveInternalSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridRiveInternalSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridRiveInternalSpec_ weakify_std__shared_ptr_HybridRiveInternalSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: Result + using Result_MemoryUsage_ = Result; + inline Result_MemoryUsage_ create_Result_MemoryUsage_(const MemoryUsage& value) noexcept { + return Result::withValue(value); + } + inline Result_MemoryUsage_ create_Result_MemoryUsage_(const std::exception_ptr& error) noexcept { + return Result::withError(error); + } + // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp index 427d2b8d..d056e08a 100644 --- a/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridRiveFontConfigSpecSwift.hpp @@ -107,6 +107,14 @@ namespace margelo::nitro::rive { auto __value = std::move(__result.value()); return __value; } + inline std::shared_ptr getSystemDefaultFont() override { + auto __result = _swiftPart.getSystemDefaultFont(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } inline void setFontsForWeight(double weight, const std::vector>& fonts) override { auto __result = _swiftPart.setFontsForWeight(std::forward(weight), fonts); if (__result.hasError()) [[unlikely]] { diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift index 85cf4f6b..e6af995c 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec.swift @@ -18,6 +18,7 @@ public protocol HybridRiveFontConfigSpec_protocol: HybridObject { func loadFontFromResource(resource: String) throws -> (any HybridFallbackFontSpec) func loadFontFromBytes(bytes: ArrayBuffer) throws -> (any HybridFallbackFontSpec) func loadFontByName(name: String) throws -> (any HybridFallbackFontSpec) + func getSystemDefaultFont() throws -> (any HybridFallbackFontSpec) func setFontsForWeight(weight: Double, fonts: [(any HybridFallbackFontSpec)]) throws -> Void func applyFallbackFonts() throws -> Promise func clearFallbackFonts() throws -> Promise diff --git a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift index c3da6478..c4d6b44a 100644 --- a/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift +++ b/nitrogen/generated/ios/swift/HybridRiveFontConfigSpec_cxx.swift @@ -192,6 +192,21 @@ open class HybridRiveFontConfigSpec_cxx { } } + @inline(__always) + public final func getSystemDefaultFont() -> bridge.Result_std__shared_ptr_HybridFallbackFontSpec__ { + do { + let __result = try self.__implementation.getSystemDefaultFont() + let __resultCpp = { () -> bridge.std__shared_ptr_HybridFallbackFontSpec_ in + let __cxxWrapped = __result.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__shared_ptr_HybridFallbackFontSpec__(__exceptionPtr) + } + } + @inline(__always) public final func setFontsForWeight(weight: Double, fonts: bridge.std__vector_std__shared_ptr_HybridFallbackFontSpec__) -> bridge.Result_void_ { do { diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp index cc2ab2b2..111806a2 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.cpp @@ -18,6 +18,7 @@ namespace margelo::nitro::rive { prototype.registerHybridMethod("loadFontFromResource", &HybridRiveFontConfigSpec::loadFontFromResource); prototype.registerHybridMethod("loadFontFromBytes", &HybridRiveFontConfigSpec::loadFontFromBytes); prototype.registerHybridMethod("loadFontByName", &HybridRiveFontConfigSpec::loadFontByName); + prototype.registerHybridMethod("getSystemDefaultFont", &HybridRiveFontConfigSpec::getSystemDefaultFont); prototype.registerHybridMethod("setFontsForWeight", &HybridRiveFontConfigSpec::setFontsForWeight); prototype.registerHybridMethod("applyFallbackFonts", &HybridRiveFontConfigSpec::applyFallbackFonts); prototype.registerHybridMethod("clearFallbackFonts", &HybridRiveFontConfigSpec::clearFallbackFonts); diff --git a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp index 4780eb7c..a6df0aaa 100644 --- a/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridRiveFontConfigSpec.hpp @@ -58,6 +58,7 @@ namespace margelo::nitro::rive { virtual std::shared_ptr loadFontFromResource(const std::string& resource) = 0; virtual std::shared_ptr loadFontFromBytes(const std::shared_ptr& bytes) = 0; virtual std::shared_ptr loadFontByName(const std::string& name) = 0; + virtual std::shared_ptr getSystemDefaultFont() = 0; virtual void setFontsForWeight(double weight, const std::vector>& fonts) = 0; virtual std::shared_ptr> applyFallbackFonts() = 0; virtual std::shared_ptr> clearFallbackFonts() = 0; diff --git a/src/core/RiveFonts.ts b/src/core/RiveFonts.ts index 6a84a455..ea9d2df5 100644 --- a/src/core/RiveFonts.ts +++ b/src/core/RiveFonts.ts @@ -25,7 +25,9 @@ export type FontSource = export type FontWeight = number | 'default'; -export type FallbackFontMap = Partial>; +export type FallbackFontEntry = FallbackFont | 'default'; + +export type FallbackFontMap = Partial>; const DEFAULT_WEIGHT = 0; @@ -70,8 +72,13 @@ export namespace RiveFonts { export async function setFallbackFonts( fontsByWeight: FallbackFontMap ): Promise { - for (const [key, fonts] of Object.entries(fontsByWeight)) { - if (fonts) { + for (const [key, entries] of Object.entries(fontsByWeight)) { + if (entries) { + const fonts = entries.map((entry) => + entry === 'default' + ? RiveFontConfigInternal.getSystemDefaultFont() + : entry + ); RiveFontConfigInternal.setFontsForWeight(resolveWeight(key), fonts); } } diff --git a/src/index.tsx b/src/index.tsx index 28756ac0..78895524 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -43,6 +43,7 @@ export { RiveFonts, type FontSource, type FontWeight, + type FallbackFontEntry, type FallbackFontMap, } from './core/RiveFonts'; export type { FallbackFont } from './specs/RiveFontConfig.nitro'; @@ -63,4 +64,6 @@ export { useRiveFile } from './hooks/useRiveFile'; export { type RiveFileInput } from './hooks/useRiveFile'; export { type SetValueAction } from './types'; export { RiveRuntime } from './core/RiveRuntime'; +export { RiveInternal } from './core/RiveInternal'; +export type { MemoryUsage } from './specs/RiveInternal.nitro'; export { DataBindMode }; diff --git a/src/specs/RiveFontConfig.nitro.ts b/src/specs/RiveFontConfig.nitro.ts index 3bd3fd22..0e5fe549 100644 --- a/src/specs/RiveFontConfig.nitro.ts +++ b/src/specs/RiveFontConfig.nitro.ts @@ -9,6 +9,7 @@ export interface RiveFontConfig loadFontFromResource(resource: string): FallbackFont; loadFontFromBytes(bytes: ArrayBuffer): FallbackFont; loadFontByName(name: string): FallbackFont; + getSystemDefaultFont(): FallbackFont; setFontsForWeight(weight: number, fonts: FallbackFont[]): void; applyFallbackFonts(): Promise; clearFallbackFonts(): Promise; From ad1e74f92145a30f44053875b6723973303c9b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 4 Mar 2026 14:20:11 +0100 Subject: [PATCH 17/20] chore: remove uncommitted RiveInternal exports to fix CI --- src/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 78895524..b8ad3344 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -64,6 +64,4 @@ export { useRiveFile } from './hooks/useRiveFile'; export { type RiveFileInput } from './hooks/useRiveFile'; export { type SetValueAction } from './types'; export { RiveRuntime } from './core/RiveRuntime'; -export { RiveInternal } from './core/RiveInternal'; -export type { MemoryUsage } from './specs/RiveInternal.nitro'; export { DataBindMode }; From 4efeb5b9b2a2e14c72748d1c8f0a7fbba12f554b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 4 Mar 2026 14:23:47 +0100 Subject: [PATCH 18/20] chore: regenerate nitrogen bindings without RiveInternal --- .../generated/ios/RNRive-Swift-Cxx-Bridge.cpp | 17 ----------- .../generated/ios/RNRive-Swift-Cxx-Bridge.hpp | 29 ------------------- 2 files changed, 46 deletions(-) diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp index 244490db..89ae6cb9 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp @@ -15,7 +15,6 @@ #include "HybridRiveFontConfigSpecSwift.hpp" #include "HybridRiveImageFactorySpecSwift.hpp" #include "HybridRiveImageSpecSwift.hpp" -#include "HybridRiveInternalSpecSwift.hpp" #include "HybridRiveRuntimeSpecSwift.hpp" #include "HybridRiveViewSpecSwift.hpp" #include "HybridViewModelArtboardPropertySpecSwift.hpp" @@ -203,22 +202,6 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } - // pragma MARK: std::shared_ptr - std::shared_ptr create_std__shared_ptr_HybridRiveInternalSpec_(void* NON_NULL swiftUnsafePointer) noexcept { - RNRive::HybridRiveInternalSpec_cxx swiftPart = RNRive::HybridRiveInternalSpec_cxx::fromUnsafe(swiftUnsafePointer); - return std::make_shared(swiftPart); - } - void* NON_NULL get_std__shared_ptr_HybridRiveInternalSpec_(std__shared_ptr_HybridRiveInternalSpec_ cppType) { - std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); - #ifdef NITRO_DEBUG - if (swiftWrapper == nullptr) [[unlikely]] { - throw std::runtime_error("Class \"HybridRiveInternalSpec\" is not implemented in Swift!"); - } - #endif - RNRive::HybridRiveInternalSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); - return swiftPart.toUnsafe(); - } - // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridRiveRuntimeSpec_(void* NON_NULL swiftUnsafePointer) noexcept { RNRive::HybridRiveRuntimeSpec_cxx swiftPart = RNRive::HybridRiveRuntimeSpec_cxx::fromUnsafe(swiftUnsafePointer); diff --git a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp index cf50d283..6ccaab64 100644 --- a/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp @@ -34,8 +34,6 @@ namespace margelo::nitro::rive { class HybridRiveFontConfigSpec; } namespace margelo::nitro::rive { class HybridRiveImageFactorySpec; } // Forward declaration of `HybridRiveImageSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveImageSpec; } -// Forward declaration of `HybridRiveInternalSpec` to properly resolve imports. -namespace margelo::nitro::rive { class HybridRiveInternalSpec; } // Forward declaration of `HybridRiveRuntimeSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridRiveRuntimeSpec; } // Forward declaration of `HybridRiveViewSpec` to properly resolve imports. @@ -64,8 +62,6 @@ namespace margelo::nitro::rive { class HybridViewModelSpec; } namespace margelo::nitro::rive { class HybridViewModelStringPropertySpec; } // Forward declaration of `HybridViewModelTriggerPropertySpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridViewModelTriggerPropertySpec; } -// Forward declaration of `MemoryUsage` to properly resolve imports. -namespace margelo::nitro::rive { struct MemoryUsage; } // Forward declaration of `ReferencedAssetsType` to properly resolve imports. namespace margelo::nitro::rive { struct ReferencedAssetsType; } // Forward declaration of `ResolvedReferencedAsset` to properly resolve imports. @@ -94,8 +90,6 @@ namespace RNRive { class HybridRiveFontConfigSpec_cxx; } namespace RNRive { class HybridRiveImageFactorySpec_cxx; } // Forward declaration of `HybridRiveImageSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveImageSpec_cxx; } -// Forward declaration of `HybridRiveInternalSpec_cxx` to properly resolve imports. -namespace RNRive { class HybridRiveInternalSpec_cxx; } // Forward declaration of `HybridRiveRuntimeSpec_cxx` to properly resolve imports. namespace RNRive { class HybridRiveRuntimeSpec_cxx; } // Forward declaration of `HybridRiveViewSpec_cxx` to properly resolve imports. @@ -139,7 +133,6 @@ namespace RNRive { class HybridViewModelTriggerPropertySpec_cxx; } #include "HybridRiveFontConfigSpec.hpp" #include "HybridRiveImageFactorySpec.hpp" #include "HybridRiveImageSpec.hpp" -#include "HybridRiveInternalSpec.hpp" #include "HybridRiveRuntimeSpec.hpp" #include "HybridRiveViewSpec.hpp" #include "HybridViewModelArtboardPropertySpec.hpp" @@ -154,7 +147,6 @@ namespace RNRive { class HybridViewModelTriggerPropertySpec_cxx; } #include "HybridViewModelSpec.hpp" #include "HybridViewModelStringPropertySpec.hpp" #include "HybridViewModelTriggerPropertySpec.hpp" -#include "MemoryUsage.hpp" #include "ReferencedAssetsType.hpp" #include "ResolvedReferencedAsset.hpp" #include "RiveError.hpp" @@ -657,27 +649,6 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>>::withError(error); } - // pragma MARK: std::shared_ptr - /** - * Specialized version of `std::shared_ptr`. - */ - using std__shared_ptr_HybridRiveInternalSpec_ = std::shared_ptr; - std::shared_ptr create_std__shared_ptr_HybridRiveInternalSpec_(void* NON_NULL swiftUnsafePointer) noexcept; - void* NON_NULL get_std__shared_ptr_HybridRiveInternalSpec_(std__shared_ptr_HybridRiveInternalSpec_ cppType); - - // pragma MARK: std::weak_ptr - using std__weak_ptr_HybridRiveInternalSpec_ = std::weak_ptr; - inline std__weak_ptr_HybridRiveInternalSpec_ weakify_std__shared_ptr_HybridRiveInternalSpec_(const std::shared_ptr& strong) noexcept { return strong; } - - // pragma MARK: Result - using Result_MemoryUsage_ = Result; - inline Result_MemoryUsage_ create_Result_MemoryUsage_(const MemoryUsage& value) noexcept { - return Result::withValue(value); - } - inline Result_MemoryUsage_ create_Result_MemoryUsage_(const std::exception_ptr& error) noexcept { - return Result::withError(error); - } - // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. From 208eb41b6f20498e4d6a93553553332dac1160d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 4 Mar 2026 15:19:20 +0100 Subject: [PATCH 19/20] chore: update Podfile.lock for RiveRuntime 6.15.2 --- example/ios/Podfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 6b1bb731..0aa1d96f 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1754,7 +1754,7 @@ PODS: - React-logger (= 0.79.2) - React-perflogger (= 0.79.2) - React-utils (= 0.79.2) - - RiveRuntime (6.13.0) + - RiveRuntime (6.15.2) - RNCAsyncStorage (2.2.0): - DoubleConversion - glog @@ -1904,7 +1904,7 @@ PODS: - ReactCommon/turbomodule/core - RNWorklets - Yoga - - RNRive (0.2.3): + - RNRive (0.2.5): - DoubleConversion - glog - hermes-engine @@ -1928,7 +1928,7 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RiveRuntime (= 6.13.0) + - RiveRuntime (= 6.15.2) - Yoga - RNWorklets (0.6.1): - DoubleConversion @@ -2325,12 +2325,12 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 04d5eb15eb46be6720e17a4a7fa92940a776e584 ReactCodegen: c63eda03ba1d94353fb97b031fc84f75a0d125ba ReactCommon: 76d2dc87136d0a667678668b86f0fca0c16fdeb0 - RiveRuntime: 903690a5ba698b2a7e8d462e8aa7ceeba862614c + RiveRuntime: b2c29a7e1fbf2aabd1fffcb7b9cf43c4d515a841 RNCAsyncStorage: a1c8cc8a99c32de1244a9cf707bf9d83d0de0f71 RNCPicker: 28c076ae12a1056269ec0305fe35fac3086c477d RNGestureHandler: 6b39f4e43e4b3a0fb86de9531d090ff205a011d5 RNReanimated: 66b68ebe3baf7ec9e716bd059d700726f250d344 - RNRive: bcdbc29a0e02ea0ed1b2588395f68fa415a259df + RNRive: acc0d2356fbb2f65dce95b25cc154bdd0c8ceecc RNWorklets: b1faafefb82d9f29c4018404a0fb33974b494a7b SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 9f110fc4b7aa538663cba3c14cbb1c335f43c13f From 6e3de0b1b923fd38f411556d5e0006d21a498342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 6 Mar 2026 08:18:02 +0100 Subject: [PATCH 20/20] refactor: replace 'default' sentinel with RiveFonts.systemFallback() --- example/src/exercisers/FontFallbackExample.tsx | 6 +++--- src/core/RiveFonts.ts | 17 +++++++---------- src/index.tsx | 1 - 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/example/src/exercisers/FontFallbackExample.tsx b/example/src/exercisers/FontFallbackExample.tsx index 3d573e17..84ecf81d 100644 --- a/example/src/exercisers/FontFallbackExample.tsx +++ b/example/src/exercisers/FontFallbackExample.tsx @@ -113,11 +113,11 @@ export default function FontFallbackExample() { const handleMount = async () => { try { - const entries: (FallbackFont | 'default')[] = selectedFonts + const entries: FallbackFont[] = selectedFonts .map((key) => loadedFonts.get(key)) .filter((f): f is FallbackFont => f != null); if (systemFallback) { - entries.push('default'); + entries.push(RiveFonts.systemFallback()); } await RiveFonts.setFallbackFonts({ default: entries }); } catch (err) { @@ -207,7 +207,7 @@ export default function FontFallbackExample() { >; +export type FallbackFontMap = Partial>; const DEFAULT_WEIGHT = 0; @@ -69,16 +67,15 @@ export namespace RiveFonts { throw new Error(`Invalid font source: ${String(source)}`); } + export function systemFallback(): FallbackFont { + return RiveFontConfigInternal.getSystemDefaultFont(); + } + export async function setFallbackFonts( fontsByWeight: FallbackFontMap ): Promise { - for (const [key, entries] of Object.entries(fontsByWeight)) { - if (entries) { - const fonts = entries.map((entry) => - entry === 'default' - ? RiveFontConfigInternal.getSystemDefaultFont() - : entry - ); + for (const [key, fonts] of Object.entries(fontsByWeight)) { + if (fonts) { RiveFontConfigInternal.setFontsForWeight(resolveWeight(key), fonts); } } diff --git a/src/index.tsx b/src/index.tsx index b8ad3344..28756ac0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -43,7 +43,6 @@ export { RiveFonts, type FontSource, type FontWeight, - type FallbackFontEntry, type FallbackFontMap, } from './core/RiveFonts'; export type { FallbackFont } from './specs/RiveFontConfig.nitro';