Skip to content

Commit 072414b

Browse files
authored
fix(🍏): map iOS font names more closely to what React Native does (#3603)
1 parent 6d5770c commit 072414b

File tree

8 files changed

+51
-13
lines changed

8 files changed

+51
-13
lines changed

apps/example/src/Examples/API/FontMgr.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ export const FontMgr = () => {
3535
.fill(0)
3636
.map((_, i) => customFontMgr.getFamilyName(i));
3737
const titleFont = matchFont({
38-
fontFamily: "Helvetica",
38+
fontFamily: "System",
3939
fontSize: titleFontSize,
40-
fontWeight: "bold",
40+
fontWeight: "normal",
4141
});
4242
const subtitleFont = matchFont();
4343
return (

packages/skia/apple/RNSkApplePlatformContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class RNSkApplePlatformContext : public RNSkPlatformContext {
7575

7676
std::vector<std::string> getSystemFontFamilies() override;
7777

78+
std::string resolveFontFamily(const std::string &familyName) override;
79+
7880
private:
7981
ViewScreenshotService *_screenshotService;
8082

packages/skia/apple/RNSkApplePlatformContext.mm

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,29 @@
346346
return families;
347347
}
348348

349+
std::string
350+
RNSkApplePlatformContext::resolveFontFamily(const std::string &familyName) {
351+
// Handle special font family names like React Native does
352+
// See: RCTFont.mm in React Native
353+
if (familyName == "System" || familyName == "system" ||
354+
familyName == "sans-serif") {
355+
return ".AppleSystemUIFont";
356+
}
357+
if (familyName == "SystemCondensed" || familyName == "system-condensed") {
358+
// Return system font - condensed trait is handled via font style
359+
return ".AppleSystemUIFont";
360+
}
361+
// CSS generic font families
362+
if (familyName == "serif") {
363+
return "Times New Roman";
364+
}
365+
if (familyName == "monospace") {
366+
return "Courier New";
367+
}
368+
// Return as-is if no mapping exists
369+
return familyName;
370+
}
371+
349372
void RNSkApplePlatformContext::runOnMainThread(std::function<void()> func) {
350373
dispatch_async(dispatch_get_main_queue(), ^{
351374
func();

packages/skia/cpp/api/JsiSkFontMgr.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,21 @@ class JsiSkFontMgr : public JsiSkWrappingSkPtrHostObject<SkFontMgr> {
4848
return jsi::String::createFromUtf8(runtime,
4949
_systemFontFamilies[systemIndex]);
5050
}
51-
throw jsi::JSError(runtime, "Font family index out of bounds: " +
52-
std::to_string(i) + " (total families: " +
53-
std::to_string(baseFamilyCount +
54-
_systemFontFamilies.size()) +
55-
")");
51+
throw jsi::JSError(
52+
runtime,
53+
"Font family index out of bounds: " + std::to_string(i) +
54+
" (total families: " +
55+
std::to_string(baseFamilyCount + _systemFontFamilies.size()) + ")");
5656
}
5757

5858
JSI_HOST_FUNCTION(matchFamilyStyle) {
5959
auto name = arguments[0].asString(runtime).utf8(runtime);
60+
// Resolve font family aliases (e.g., "System" -> ".AppleSystemUIFont" on
61+
// iOS)
62+
auto resolvedName = getContext()->resolveFontFamily(name);
6063
auto fontStyle = JsiSkFontStyle::fromValue(runtime, arguments[1]);
61-
auto typeface = getObject()->matchFamilyStyle(name.c_str(), *fontStyle);
64+
auto typeface =
65+
getObject()->matchFamilyStyle(resolvedName.c_str(), *fontStyle);
6266
auto hostObjectInstance =
6367
std::make_shared<JsiSkTypeface>(getContext(), std::move(typeface));
6468
return JSI_CREATE_HOST_OBJECT_WITH_MEMORY_PRESSURE(

packages/skia/cpp/rnskia/RNSkPlatformContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ class RNSkPlatformContext {
152152
*/
153153
virtual std::vector<std::string> getSystemFontFamilies() { return {}; }
154154

155+
/**
156+
* Resolve font family aliases to actual font family names.
157+
* For example, "System" on iOS resolves to ".AppleSystemUIFont".
158+
* Returns the input unchanged if no mapping exists.
159+
*/
160+
virtual std::string resolveFontFamily(const std::string &familyName) {
161+
return familyName;
162+
}
163+
155164
/**
156165
* Creates an skImage containing the screenshot of a native view and its
157166
* children.

packages/skia/src/renderer/__tests__/FitBox.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,6 @@ describe("FitBox", () => {
667667
const { Skia } = importSkia();
668668
const path = Skia.Path.MakeFromSVGString(skiaLogo)!;
669669
const bounds = path.computeTightBounds();
670-
console.log(bounds.x, bounds.y, bounds.width, bounds.height);
671670
expect(bounds.x).toBeCloseTo(324, 2);
672671
expect(bounds.y).toBeCloseTo(222, 2);
673672
expect(bounds.width).toBeCloseTo(631.09, 2);

packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22

3-
import { checkImage } from "../../../__tests__/setup";
3+
import { checkImage, itRunsE2eOnly } from "../../../__tests__/setup";
44
import { Image } from "../../components";
55
import { BirdGIF, importSkia, surface } from "../setup";
66

@@ -27,7 +27,7 @@ describe("Animated Images", () => {
2727
});
2828
expect(result).toEqual(true);
2929
});
30-
it("should decode gifs (2)", async () => {
30+
itRunsE2eOnly("should decode gifs (2)", async () => {
3131
const result = await surface.eval((Skia) => {
3232
const data = Skia.Data.fromBase64(
3333
"R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="

packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { itRunsE2eOnly } from "../../../__tests__/setup";
12
import { surface } from "../setup";
23

34
describe("Data Encoding", () => {
4-
it("encodeToBytes() from CPU image", async () => {
5+
itRunsE2eOnly("encodeToBytes() from CPU image", async () => {
56
const result = await surface.eval((Skia) => {
67
const data = Skia.Data.fromBase64(
78
"R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="
@@ -20,7 +21,7 @@ describe("Data Encoding", () => {
2021
1, 43, 48, 0, 62, 135, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
2122
]);
2223
});
23-
it("encodeToBase64() from CPU image", async () => {
24+
itRunsE2eOnly("encodeToBase64() from CPU image", async () => {
2425
const result = await surface.eval((Skia) => {
2526
const data = Skia.Data.fromBase64(
2627
"R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="

0 commit comments

Comments
 (0)