From f23a16d3e6f3cf1757bc779ea6fb36664ded004a Mon Sep 17 00:00:00 2001 From: firedef Date: Mon, 14 Mar 2022 16:07:33 +0300 Subject: [PATCH] add fonts manager --- SomeChartsAvaloniaExamples/Program.cs | 3 + .../src/elements/ElementsExamples.cs | 3 + .../src/elements/charts/pie/PieChart.cs | 6 +- SomeChartsUi/src/ui/canvas/ChartsCanvas.cs | 37 ++++++++ SomeChartsUi/src/ui/text/Font.cs | 64 ++++++++++---- SomeChartsUi/src/ui/text/FontFamily.cs | 27 ++++++ SomeChartsUi/src/ui/text/Fonts.cs | 84 +++++++++++++++++++ 7 files changed, 204 insertions(+), 20 deletions(-) create mode 100644 SomeChartsUi/src/ui/text/FontFamily.cs create mode 100644 SomeChartsUi/src/ui/text/Fonts.cs diff --git a/SomeChartsAvaloniaExamples/Program.cs b/SomeChartsAvaloniaExamples/Program.cs index e3c6663..13f5de1 100644 --- a/SomeChartsAvaloniaExamples/Program.cs +++ b/SomeChartsAvaloniaExamples/Program.cs @@ -1,5 +1,8 @@ using System; +using System.Linq; +using FreeTypeSharp.Native; using SomeChartsAvaloniaExamples.elements; +using SomeChartsUi.ui.text; namespace SomeChartsAvaloniaExamples; diff --git a/SomeChartsAvaloniaExamples/src/elements/ElementsExamples.cs b/SomeChartsAvaloniaExamples/src/elements/ElementsExamples.cs index 7e36845..c4723a7 100644 --- a/SomeChartsAvaloniaExamples/src/elements/ElementsExamples.cs +++ b/SomeChartsAvaloniaExamples/src/elements/ElementsExamples.cs @@ -10,6 +10,7 @@ using SomeChartsUi.elements.other; using SomeChartsUi.themes.colors; using SomeChartsUi.themes.themes; using SomeChartsUi.ui.elements; +using SomeChartsUi.ui.text; using SomeChartsUi.utils.shaders; using SomeChartsUiAvalonia.controls.gl; using SomeChartsUiAvalonia.controls.skia; @@ -198,6 +199,8 @@ public static class ElementsExamples { public static void RunPieChart() { AvaloniaRunUtils.RunAfterStart(() => { + Fonts.FetchAvailableFontsFromPath("data/"); + AvaloniaGlChartsCanvas canvas = AvaloniaRunUtils.AddGlCanvas(); const int rulerOffset = 1_000_000; diff --git a/SomeChartsUi/src/elements/charts/pie/PieChart.cs b/SomeChartsUi/src/elements/charts/pie/PieChart.cs index daa1f2f..24fc72c 100644 --- a/SomeChartsUi/src/elements/charts/pie/PieChart.cs +++ b/SomeChartsUi/src/elements/charts/pie/PieChart.cs @@ -24,10 +24,8 @@ public class PieChart : RenderableBase, IDownsample { public PieChart(ChartsCanvas owner) : base(owner) { _textMesh = canvas.factory.CreateTextMesh(this); - uint resolution = 32; - font = Font.LoadFromPath("data/FiraCode-VariableFont_wght.ttf", renderer.owner, resolution); - Font fallbackFont = Font.LoadFromPath("data/NotoSansJP-Regular.otf", renderer.owner, resolution); - font.fallbacks.Add(fallbackFont); + + font = canvas.GetDefaultFont(); } public float downsampleMultiplier { get; set; } = .5f; diff --git a/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs b/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs index 7f4b55b..7d67ab8 100644 --- a/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs +++ b/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs @@ -1,6 +1,7 @@ using SomeChartsUi.backends; using SomeChartsUi.ui.canvas.controls; using SomeChartsUi.ui.layers; +using SomeChartsUi.ui.text; namespace SomeChartsUi.ui.canvas; @@ -9,6 +10,9 @@ public class ChartsCanvas { public readonly ChartCanvasRenderer renderer; public readonly ChartCanvasTransform transform; + public List fallbackFonts = new(); + public Font? defaultFont; + public ChartCanvasControllerBase? controller; public TimeSpan renderTime; @@ -20,6 +24,7 @@ public class ChartsCanvas { backend.renderer = renderer; this.factory = factory; } + private List layers => renderer.layers; private Dictionary layerNames => renderer.layerNames; @@ -38,4 +43,36 @@ public class ChartsCanvas { public CanvasLayer? GetLayer(string name) => layerNames.TryGetValue(name, out int i) ? layers[i] : null; public CanvasLayer GetLayer(int i) => layers[i]; + + public void LoadDefaultFonts() { + // some asian fonts + AddFallbackFromName("NotoSansJP"); + + defaultFont ??= LoadAny("OpenSans", "Comfortaa", "NotoSans"); + } + + private void AddFallbackFromName(string name) { + Font? f = Font.TryLoad(name, this); + if (f == null) return; + fallbackFonts.Add(f); + } + + private void AddFallbackFromPath(string path) { + fallbackFonts.Add(Font.LoadFromPath(path, this)); + } + + private Font LoadAny(params string[] names) { + Font? f; + foreach (string name in names) { + f = Font.TryLoad(name, this); + if (f != null) return f.WithFallbacks(fallbackFonts); + } + + throw new FileNotFoundException($"not found any of fonts: \n{string.Join("\n", names)}"); + } + + public Font GetDefaultFont() { + if (defaultFont == null) LoadDefaultFonts(); + return defaultFont!; + } } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/text/Font.cs b/SomeChartsUi/src/ui/text/Font.cs index 3ce3694..8ccd2bf 100644 --- a/SomeChartsUi/src/ui/text/Font.cs +++ b/SomeChartsUi/src/ui/text/Font.cs @@ -4,27 +4,59 @@ using SomeChartsUi.ui.canvas; namespace SomeChartsUi.ui.text; -public class Font { - public string family; - public bool isBold; - public bool isExpanded; - public bool isItalic; - public FontTextures textures; - public List fallbacks = new(); +public record Font(string name, bool isBold, bool isItalic, bool isExpanded, FontTextures textures, ChartsCanvas _owner) { + private static List _loadedFonts = new(); - public Font(string family, bool isBold, bool isItalic, bool isExpanded, FontTextures textures) { - this.family = family; - this.isBold = isBold; - this.isItalic = isItalic; - this.isExpanded = isExpanded; - this.textures = textures; - } + public string name = name; + public bool isBold = isBold; + public bool isExpanded = isExpanded; + public bool isItalic = isItalic; + public FontTextures textures = textures; + public List fallbacks = new(); + private ChartsCanvas _owner = _owner; public static Font LoadFromPath(string path, ChartsCanvas canvas, uint resolution = 32) { - using FileStream fs = new(path, FileMode.Open); + string name = Path.GetFileName(path)[..^4]; + Font? existingFont = TryGetLoadedFont(name); + if (existingFont != null) return existingFont; + + using FileStream fs = new(path, FileMode.Open, FileAccess.Read); FT.FT_New_Face(FreeType.ftLib.Native, path, 0, out IntPtr face).CheckError(); FreeTypeFaceFacade faceF = new(FreeType.ftLib, face); - return new("todo", false, false, false, canvas.factory.CreateFontTextureAtlas(faceF, resolution)); + Font font = new(name, false, false, false, canvas.factory.CreateFontTextureAtlas(faceF, resolution), canvas); + _loadedFonts.Add(font); + return font; + } + + public static Font? TryGetLoadedFont(string name) => _loadedFonts.FirstOrDefault(v => v.name == name); + + public static Font? TryLoad(string name, ChartsCanvas canvas, uint resolution = 32) { + Font? existingFont = TryGetLoadedFont(name); + if (existingFont != null) return existingFont; + + string? path = Fonts.TryFind(name); + return path == null ? null : LoadFromPath(path, canvas, resolution); + } + + public Font WithFallback(Font fallback) { + fallbacks.Add(fallback); + return this; + } + + public Font WithFallbacks(IEnumerable fallback) { + fallbacks.AddRange(fallback); + return this; + } + + public Font WithFallbackPath(string path) { + fallbacks.Add(LoadFromPath(path, _owner)); + return this; + } + + public Font WithFallbackName(string name) { + Font? fallback = TryLoad(name, _owner); + if (fallback != null) fallbacks.Add(fallback); + return this; } } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/text/FontFamily.cs b/SomeChartsUi/src/ui/text/FontFamily.cs new file mode 100644 index 0000000..18349c1 --- /dev/null +++ b/SomeChartsUi/src/ui/text/FontFamily.cs @@ -0,0 +1,27 @@ +using FreeTypeSharp.Native; +using SomeChartsUi.ui.canvas; + +namespace SomeChartsUi.ui.text; + +public class FontFamily { + public List fonts = new(); +} + +public class FontFamilyItem { + private Font? _font; + + public string name; + public string path; + + public FontFamilyItem(string name, string path) { + this.name = name; + this.path = path; + } + + public Font GetFont(ChartsCanvas canvas) { + if (_font != null) return _font; + _font = Font.LoadFromPath(path, canvas); + + return _font; + } +} \ No newline at end of file diff --git a/SomeChartsUi/src/ui/text/Fonts.cs b/SomeChartsUi/src/ui/text/Fonts.cs new file mode 100644 index 0000000..2cacb8a --- /dev/null +++ b/SomeChartsUi/src/ui/text/Fonts.cs @@ -0,0 +1,84 @@ +namespace SomeChartsUi.ui.text; + +public static class Fonts { + public static List availableFonts; + public static HashSet loadedPaths = new(); + + static Fonts() { + FetchAvailableFonts(); + } + + private static void FetchAvailableFonts() { + availableFonts = new(); + + List paths = new(); + paths.Add(Environment.GetFolderPath(Environment.SpecialFolder.Fonts)); + + // Linux additional paths for fonts + // '~/.fonts' already added using environment special folder + if (Environment.OSVersion.Platform == PlatformID.Unix) { + paths.Add("/usr/share/fonts"); + paths.Add("/usr/local/share/fonts"); + } + + foreach (string path in paths) + FetchAvailableFontsFromPath(path); + } + + public static void FetchAvailableFontsFromPath(string path) { + if (!Directory.Exists(path)) return; + if (loadedPaths.Contains(path)) return; + + string[] ttfPaths = Directory.GetFiles(path, "*.ttf", SearchOption.AllDirectories); + string[] otfPaths = Directory.GetFiles(path, "*.otf", SearchOption.AllDirectories); + + AddFonts(ttfPaths); + AddFonts(otfPaths); + + loadedPaths.Add(path); + } + + public static void AddFonts(string[] paths) { + foreach (string path in paths) AddFont(path); + } + + public static void AddFont(string path) { + string name = Path.GetFileName(path)[..^4]; + AddFont(name, path); + } + + public static void AddFont(string name, string path) { + if (availableFonts.FindIndex(v => string.Equals(v.path, path, StringComparison.CurrentCultureIgnoreCase)) != -1) return; + availableFonts.Add(new(name, path)); + } + + public static string? TryFindExact(string name) => availableFonts.FirstOrDefault(v => string.Equals(v.name, name, StringComparison.CurrentCultureIgnoreCase))?.path; + + public static string? TryFind(string name) { + string? result = TryFindExact(name); + if (result != null || name.Contains('-')) return result; + + result = TryFindExact($"{name}-regular"); + if (result != null) return result; + + return availableFonts.FirstOrDefault(v => string.Equals(v.name.Split('-', 2)[0], name, StringComparison.CurrentCultureIgnoreCase))?.path; + } + + public static FontFamily GetFamily(string name) { + name = name.Split('-', 2)[0]; + FontFamily family = new(); + + IEnumerable fonts = availableFonts.Where(v => string.Equals(v.name.Split('-', 2)[0], name, StringComparison.CurrentCultureIgnoreCase)); + foreach ((string fName, string fPath) in fonts) { + FontFamilyItem item = new(fName, fPath); + family.fonts.Add(item); + } + + return family; + } +} + +public record InstalledFontData(string name, string path) { + public string name = name; + public string path = path; +} \ No newline at end of file