From b5c5ce7a8e37d7790d54927a1badc3980c5a42ef Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 11 Dec 2025 18:42:43 +0100 Subject: [PATCH 1/3] Map iOS fonts more closely to React Native RCTFont --- apps/example/src/Examples/API/FontMgr.tsx | 4 ++-- .../skia/apple/RNSkApplePlatformContext.h | 2 ++ .../skia/apple/RNSkApplePlatformContext.mm | 23 +++++++++++++++++++ packages/skia/cpp/api/JsiSkFontMgr.h | 16 ++++++++----- .../skia/cpp/rnskia/RNSkPlatformContext.h | 9 ++++++++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/apps/example/src/Examples/API/FontMgr.tsx b/apps/example/src/Examples/API/FontMgr.tsx index 0ff0c9d9bf..31c9a5d76a 100644 --- a/apps/example/src/Examples/API/FontMgr.tsx +++ b/apps/example/src/Examples/API/FontMgr.tsx @@ -35,9 +35,9 @@ export const FontMgr = () => { .fill(0) .map((_, i) => customFontMgr.getFamilyName(i)); const titleFont = matchFont({ - fontFamily: "Helvetica", + fontFamily: "System", fontSize: titleFontSize, - fontWeight: "bold", + fontWeight: "normal", }); const subtitleFont = matchFont(); return ( diff --git a/packages/skia/apple/RNSkApplePlatformContext.h b/packages/skia/apple/RNSkApplePlatformContext.h index 0d5d5b8f92..f7163cc036 100644 --- a/packages/skia/apple/RNSkApplePlatformContext.h +++ b/packages/skia/apple/RNSkApplePlatformContext.h @@ -75,6 +75,8 @@ class RNSkApplePlatformContext : public RNSkPlatformContext { std::vector getSystemFontFamilies() override; + std::string resolveFontFamily(const std::string &familyName) override; + private: ViewScreenshotService *_screenshotService; diff --git a/packages/skia/apple/RNSkApplePlatformContext.mm b/packages/skia/apple/RNSkApplePlatformContext.mm index 8effaafc1a..075f4b05cc 100644 --- a/packages/skia/apple/RNSkApplePlatformContext.mm +++ b/packages/skia/apple/RNSkApplePlatformContext.mm @@ -346,6 +346,29 @@ return families; } +std::string +RNSkApplePlatformContext::resolveFontFamily(const std::string &familyName) { + // Handle special font family names like React Native does + // See: RCTFont.mm in React Native + if (familyName == "System" || familyName == "system" || + familyName == "sans-serif") { + return ".AppleSystemUIFont"; + } + if (familyName == "SystemCondensed" || familyName == "system-condensed") { + // Return system font - condensed trait is handled via font style + return ".AppleSystemUIFont"; + } + // CSS generic font families + if (familyName == "serif") { + return "Times New Roman"; + } + if (familyName == "monospace") { + return "Courier New"; + } + // Return as-is if no mapping exists + return familyName; +} + void RNSkApplePlatformContext::runOnMainThread(std::function func) { dispatch_async(dispatch_get_main_queue(), ^{ func(); diff --git a/packages/skia/cpp/api/JsiSkFontMgr.h b/packages/skia/cpp/api/JsiSkFontMgr.h index 6d910f4948..aec7f266a5 100644 --- a/packages/skia/cpp/api/JsiSkFontMgr.h +++ b/packages/skia/cpp/api/JsiSkFontMgr.h @@ -48,17 +48,21 @@ class JsiSkFontMgr : public JsiSkWrappingSkPtrHostObject { return jsi::String::createFromUtf8(runtime, _systemFontFamilies[systemIndex]); } - throw jsi::JSError(runtime, "Font family index out of bounds: " + - std::to_string(i) + " (total families: " + - std::to_string(baseFamilyCount + - _systemFontFamilies.size()) + - ")"); + throw jsi::JSError( + runtime, + "Font family index out of bounds: " + std::to_string(i) + + " (total families: " + + std::to_string(baseFamilyCount + _systemFontFamilies.size()) + ")"); } JSI_HOST_FUNCTION(matchFamilyStyle) { auto name = arguments[0].asString(runtime).utf8(runtime); + // Resolve font family aliases (e.g., "System" -> ".AppleSystemUIFont" on + // iOS) + auto resolvedName = getContext()->resolveFontFamily(name); auto fontStyle = JsiSkFontStyle::fromValue(runtime, arguments[1]); - auto typeface = getObject()->matchFamilyStyle(name.c_str(), *fontStyle); + auto typeface = + getObject()->matchFamilyStyle(resolvedName.c_str(), *fontStyle); auto hostObjectInstance = std::make_shared(getContext(), std::move(typeface)); return JSI_CREATE_HOST_OBJECT_WITH_MEMORY_PRESSURE( diff --git a/packages/skia/cpp/rnskia/RNSkPlatformContext.h b/packages/skia/cpp/rnskia/RNSkPlatformContext.h index 6de41e51d9..076d59d28d 100644 --- a/packages/skia/cpp/rnskia/RNSkPlatformContext.h +++ b/packages/skia/cpp/rnskia/RNSkPlatformContext.h @@ -152,6 +152,15 @@ class RNSkPlatformContext { */ virtual std::vector getSystemFontFamilies() { return {}; } + /** + * Resolve font family aliases to actual font family names. + * For example, "System" on iOS resolves to ".AppleSystemUIFont". + * Returns the input unchanged if no mapping exists. + */ + virtual std::string resolveFontFamily(const std::string &familyName) { + return familyName; + } + /** * Creates an skImage containing the screenshot of a native view and its * children. From 7f5ecec077b2e32d52e2892fd217ee58255174d3 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Fri, 12 Dec 2025 17:22:48 +0100 Subject: [PATCH 2/3] :green_heart: --- .../skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx | 4 ++-- .../skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx b/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx index 6d4bb4a3d0..96b743b605 100644 --- a/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx +++ b/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { checkImage } from "../../../__tests__/setup"; +import { checkImage, itRunsE2eOnly } from "../../../__tests__/setup"; import { Image } from "../../components"; import { BirdGIF, importSkia, surface } from "../setup"; @@ -27,7 +27,7 @@ describe("Animated Images", () => { }); expect(result).toEqual(true); }); - it("should decode gifs (2)", async () => { + itRunsE2eOnly("should decode gifs (2)", async () => { const result = await surface.eval((Skia) => { const data = Skia.Data.fromBase64( "R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" diff --git a/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx b/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx index 42e5b169bd..3e91b1f91c 100644 --- a/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx +++ b/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx @@ -1,7 +1,8 @@ +import { itRunsE2eOnly } from "../../../__tests__/setup"; import { surface } from "../setup"; describe("Data Encoding", () => { - it("encodeToBytes() from CPU image", async () => { + itRunsE2eOnly("encodeToBytes() from CPU image", async () => { const result = await surface.eval((Skia) => { const data = Skia.Data.fromBase64( "R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" @@ -20,7 +21,7 @@ describe("Data Encoding", () => { 1, 43, 48, 0, 62, 135, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, ]); }); - it("encodeToBase64() from CPU image", async () => { + itRunsE2eOnly("encodeToBase64() from CPU image", async () => { const result = await surface.eval((Skia) => { const data = Skia.Data.fromBase64( "R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" From fe1469103462f32fa67036a2461175aede6f14f9 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Fri, 12 Dec 2025 17:23:23 +0100 Subject: [PATCH 3/3] :wrench: --- packages/skia/src/renderer/__tests__/FitBox.spec.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/skia/src/renderer/__tests__/FitBox.spec.tsx b/packages/skia/src/renderer/__tests__/FitBox.spec.tsx index 7e2a1fe3ec..b14c437a08 100644 --- a/packages/skia/src/renderer/__tests__/FitBox.spec.tsx +++ b/packages/skia/src/renderer/__tests__/FitBox.spec.tsx @@ -667,7 +667,6 @@ describe("FitBox", () => { const { Skia } = importSkia(); const path = Skia.Path.MakeFromSVGString(skiaLogo)!; const bounds = path.computeTightBounds(); - console.log(bounds.x, bounds.y, bounds.width, bounds.height); expect(bounds.x).toBeCloseTo(324, 2); expect(bounds.y).toBeCloseTo(222, 2); expect(bounds.width).toBeCloseTo(631.09, 2);