UNPKG

@shopify/react-native-skia

Version:

High-performance React Native Graphics using Skia

135 lines (120 loc) 3.32 kB
/*global SkiaApi*/ import { useEffect, useMemo, useState } from "react"; import { Skia } from "../Skia"; import { FontSlant } from "../types"; import type { DataModule, DataSourceParam, SkFontMgr } from "../types"; import { Platform } from "../../Platform"; import type { SkTypefaceFontProvider } from "../types/Paragraph/TypefaceFontProvider"; import { useTypeface } from "./Typeface"; const defaultFontSize = 14; /** * Returns a Skia Font object * */ export const useFont = ( font: DataSourceParam, size = defaultFontSize, onError?: (err: Error) => void ) => { const typeface = useTypeface(font, onError); return useMemo(() => { if (typeface) { return Skia.Font(typeface, size); } else { return null; } }, [size, typeface]); }; type Slant = "normal" | "italic" | "oblique"; type Weight = | "normal" | "bold" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900"; interface RNFontStyle { fontFamily: string; fontSize: number; fontStyle: Slant; fontWeight: Weight; } const defaultFontStyle: RNFontStyle = { fontFamily: "System", fontSize: defaultFontSize, fontStyle: "normal", fontWeight: "normal", }; const slant = (s: Slant) => { if (s === "italic") { return FontSlant.Italic; } else if (s === "oblique") { return FontSlant.Oblique; } else { return FontSlant.Upright; } }; const weight = (fontWeight: Weight) => { switch (fontWeight) { case "normal": return 400; case "bold": return 700; default: return parseInt(fontWeight, 10); } }; export const matchFont = ( inputStyle: Partial<RNFontStyle> = {}, fontMgr: SkFontMgr = Skia.FontMgr.System() ) => { const fontStyle = { ...defaultFontStyle, ...inputStyle, }; const style = { weight: weight(fontStyle.fontWeight), width: 5, slant: slant(fontStyle.fontStyle), }; const typeface = fontMgr.matchFamilyStyle(fontStyle.fontFamily, style); return Skia.Font(typeface, fontStyle.fontSize); }; export const listFontFamilies = (fontMgr: SkFontMgr = Skia.FontMgr.System()) => new Array(fontMgr.countFamilies()) .fill(0) .map((_, i) => fontMgr.getFamilyName(i)); const loadTypefaces = (typefacesToLoad: Record<string, DataModule[]>) => { const promises = Object.keys(typefacesToLoad).flatMap((familyName) => { return typefacesToLoad[familyName].map((typefaceToLoad) => { return Skia.Data.fromURI(Platform.resolveAsset(typefaceToLoad)).then( (data) => { const tf = Skia.Typeface.MakeFreeTypeFaceFromData(data); if (tf === null) { throw new Error(`Couldn't create typeface for ${familyName}`); } return [familyName, tf] as const; } ); }); }); return Promise.all(promises); }; export const useFonts = (sources: Record<string, DataModule[]>) => { const [fontMgr, setFontMgr] = useState<null | SkTypefaceFontProvider>(null); useEffect(() => { loadTypefaces(sources).then((result) => { const fMgr = Skia.TypefaceFontProvider.Make(); result.forEach(([familyName, typeface]) => { fMgr.registerFont(typeface, familyName); }); setFontMgr(fMgr); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return fontMgr; };