Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2147,7 +2147,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: 0502faf46cab8fb89cfc7bf3e6c6109b6ef9b5de
React-microtasksnativemodule: 663bc64e3a96c5fc91081923ae7481adc1359a78
react-native-safe-area-context: 286b3e7b5589795bb85ffc38faf4c0706c48a092
react-native-skia: 051de3960d565ea3e2440cfcf5f919bc95617ad3
react-native-skia: 7dcf4a234b9dd58233fd6b5d0cab641b8aee1d57
React-NativeModulesApple: 16fbd5b040ff6c492dacc361d49e63cba7a6a7a1
React-perflogger: ab51b7592532a0ea45bf6eed7e6cae14a368b678
React-performancetimeline: bc2e48198ec814d578ac8401f65d78a574358203
Expand Down
Binary file added apps/example/src/Tests/assets/Amiri-Regular.ttf
Binary file not shown.
6 changes: 6 additions & 0 deletions apps/example/src/Tests/useAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export const useAssets = () => {
require("./assets/DIN-Medium.ttf"),
errorHandler
);
const Amiri = useTypeface(
require("./assets/Amiri-Regular.ttf"),
errorHandler
);
if (error) {
throw new Error("Failed to load assets: " + error.message);
}
Expand All @@ -61,6 +65,7 @@ export const useAssets = () => {
!NotoSansSCRegular ||
!UberMoveMediumMono ||
!DinMedium ||
!Amiri ||
!skiaLogoJpeg ||
!skiaLogoPng ||
!mask
Expand All @@ -74,6 +79,7 @@ export const useAssets = () => {
NotoSansSCRegular,
UberMoveMediumMono,
DinMedium,
Amiri,
oslo,
skiaLogoJpeg,
skiaLogoPng,
Expand Down
1 change: 1 addition & 0 deletions packages/skia/src/__tests__/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const E2E = process.env.E2E === "true";
export const CI = process.env.CI === "true";
export const WEB = process.env.WEB === "true";
export const itFailsE2e = E2E ? it.failing : it;
export const itSkipsCanvasKit = WEB || !E2E ? it.skip : it;
export const itRunsE2eOnly = E2E && !WEB ? it : it.skip;
export const itRunsNodeOnly = E2E ? it.skip : it;
export const itRunsCIAndNodeOnly = CI || !E2E ? it : it.skip;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 61 additions & 2 deletions packages/skia/src/renderer/__tests__/e2e/Text.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ import {
checkImage,
itRunsCIAndNodeOnly,
itRunsE2eOnly,
itSkipsCanvasKit,
} from "../../../__tests__/setup";
import { Fill, Group, Rect, Text, TextPath } from "../../components";
import { fonts, importSkia, surface } from "../setup";
import { fonts, importSkia, resolveFile, surface } from "../setup";

const RobotoMedium = Array.from(
resolveFile("skia/__tests__/assets/Roboto-Medium.ttf")
);

const Amiri = Array.from(
resolveFile("skia/__tests__/assets/Amiri-Regular.ttf")
);

describe("Text", () => {
// The NotoColorEmoji font is not supported on iOS
Expand Down Expand Up @@ -86,7 +95,7 @@ describe("Text", () => {
checkImage(image, `snapshots/text/text-path1-${surface.OS}.png`);
});

it("Should draw text along a path", async () => {
itSkipsCanvasKit("Should draw text along a path", async () => {
const font = fonts.NotoSansSCRegular;
const { Skia } = importSkia();
const path = Skia.Path.Make();
Expand All @@ -106,4 +115,54 @@ describe("Text", () => {
);
checkImage(image, `snapshots/text/text-path2-${surface.OS}.png`);
});

itSkipsCanvasKit(
"Should create a path from text with Roboto font",
async () => {
const img = await surface.drawOffscreen(
(Skia, canvas, ctx) => {
const roboto = Skia.Typeface.MakeFreeTypeFaceFromData(
Skia.Data.fromBytes(new Uint8Array(ctx.RobotoMedium))
)!;
const font = Skia.Font(roboto, 64);
const path = Skia.Path.MakeFromText("Hello", 0, 64, font);
if (path) {
const paint = Skia.Paint();
paint.setColor(Skia.Color("black"));
canvas.drawColor(Skia.Color("white"));
canvas.drawPath(path, paint);
}
},
{
RobotoMedium,
}
);
checkImage(img, `snapshots/text/path-from-text-${surface.OS}.png`);
}
);

itSkipsCanvasKit(
"Should create a path from Arabic text with Amiri font",
async () => {
const img = await surface.drawOffscreen(
(Skia, canvas, ctx) => {
const amiri = Skia.Typeface.MakeFreeTypeFaceFromData(
Skia.Data.fromBytes(new Uint8Array(ctx.Amiri))
)!;
const font = Skia.Font(amiri, 64);
const path = Skia.Path.MakeFromText("مرحبا", 0, 64, font);
if (path) {
const paint = Skia.Paint();
paint.setColor(Skia.Color("black"));
canvas.drawColor(Skia.Color("white"));
canvas.drawPath(path, paint);
}
},
{
Amiri,
}
);
checkImage(img, `snapshots/text/path-from-arabic-text-${surface.OS}.png`);
}
);
});
93 changes: 92 additions & 1 deletion packages/skia/src/renderer/__tests__/e2e/TextPath.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import React from "react";

import { checkImage } from "../../../__tests__/setup";
import { Fill, Group, TextPath, fitbox } from "../../components";
import { fonts, importSkia, surface } from "../setup";
import { fonts, importSkia, resolveFile, surface } from "../setup";

const Amiri = Array.from(
resolveFile("skia/__tests__/assets/Amiri-Regular.ttf")
);

describe("Text Paths", () => {
// The NotoColorEmoji font is not supported on iOS
Expand Down Expand Up @@ -40,4 +44,91 @@ describe("Text Paths", () => {
);
checkImage(image, `snapshots/text/text-path-bug-${surface.OS}.png`);
});

it("Should draw Arabic text along a path", async () => {
const img = await surface.drawOffscreen(
(Skia, canvas, ctx) => {
const amiri = Skia.Typeface.MakeFreeTypeFaceFromData(
Skia.Data.fromBytes(new Uint8Array(ctx.Amiri))
)!;
const font = Skia.Font(amiri, 24);

const path = Skia.Path.Make();
path.moveTo(20, 120);
path.quadTo(ctx.width / 2, 20, ctx.width - 20, 120);

const paint = Skia.Paint();
paint.setColor(Skia.Color("black"));

canvas.drawColor(Skia.Color("white"));

const glyphIds = font.getGlyphIDs(
"بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ"
);
const positions = font.getGlyphWidths(glyphIds);
let x = 0;
const glyphPositions = positions.map((width) => {
const pos = x;
x += width;
return pos;
});

const pm = Skia.ContourMeasureIter(path, false, 1);
const contour = pm.next();
if (contour) {
for (let i = 0; i < glyphIds.length; i++) {
const pos = glyphPositions[i];
const posTan = contour.getPosTan(pos);
if (posTan) {
canvas.save();
canvas.translate(posTan[0].x, posTan[0].y);
canvas.rotate(
(Math.atan2(posTan[1].y, posTan[1].x) * 180) / Math.PI,
0,
0
);
canvas.drawGlyphs(
[glyphIds[i]],
[{ x: 0, y: 0 }],
0,
0,
font,
paint
);
canvas.restore();
}
}
}
},
{
Amiri,
width: surface.width,
}
);
checkImage(img, `snapshots/text/text-path-arabic-${surface.OS}.png`);
});

it("Should draw Arabic text along a path with TextPath component", async () => {
const { Skia } = importSkia();
const font = fonts.Amiri;

const path = Skia.Path.Make();
path.moveTo(20, 120);
path.quadTo(surface.width / 2, 20, surface.width - 20, 120);

const image = await surface.draw(
<>
<Fill color="white" />
<TextPath
font={font}
path={path}
text="بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ"
/>
</>
);
checkImage(
image,
`snapshots/text/text-path-arabic-component-${surface.OS}.png`
);
});
});
4 changes: 4 additions & 0 deletions packages/skia/src/renderer/__tests__/setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export let fonts: {
NotoSansSCRegular: SkFont;
UberMoveMediumMono: SkFont;
DinMedium: SkFont;
Amiri: SkFont;
};

export let dataAssets: {
Expand Down Expand Up @@ -73,6 +74,7 @@ beforeAll(async () => {
fontSize
);
const DinMedium = loadFont("skia/__tests__/assets/DIN-Medium.ttf", fontSize);
const Amiri = loadFont("skia/__tests__/assets/Amiri-Regular.ttf", fontSize);
const oslo = loadImage("skia/__tests__/assets/oslo.jpg");
const skiaLogoPng = loadImage("skia/__tests__/assets/skia_logo.png");
const skiaLogoJpeg = loadImage("skia/__tests__/assets/skia_logo_jpeg.jpg");
Expand All @@ -91,6 +93,7 @@ beforeAll(async () => {
NotoSansSCRegular,
UberMoveMediumMono,
DinMedium,
Amiri,
};
assets.set(mask, "mask");
assets.set(oslo, "oslo");
Expand All @@ -99,6 +102,7 @@ beforeAll(async () => {
assets.set(NotoSansSCRegular, "NotoSansSCRegular");
assets.set(UberMoveMediumMono, "UberMoveMediumMono");
assets.set(DinMedium, "DinMedium");
assets.set(Amiri, "Amiri");
assets.set(skiaLogoPng, "skiaLogoPng");
assets.set(skiaLogoJpeg, "skiaLogoJpeg");
});
Expand Down
5 changes: 4 additions & 1 deletion packages/skia/src/views/SkiaPictureView.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,10 @@ export const SkiaPictureView = (props: SkiaPictureViewProps) => {
const { debug = false, ref: _ref, ...viewProps } = props;
return (
<Platform.View {...viewProps} onLayout={onLayoutEvent}>
<canvas ref={canvasRef} style={{ display: "flex", flex: 1 }} />
<canvas
ref={canvasRef}
style={{ display: "block", width: "100%", height: "100%" }}
/>
</Platform.View>
);
};
Loading