#if SKIA using FastReport.Fonts; using SkiaSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Text; using System.IO; using System.Runtime.InteropServices; namespace FastReport { public static partial class FontManager { // do not remove private static readonly CharacterMatcher characterMatcher = new(); private static readonly FontFamilyMatcher1 fontFamilyMatcher1 = new(); // Note to future implementors: // this might be added to gdi/uniscribe as well, but it produces not-so-good results, // because we cannot apply this to text measure phase, only to pdf render phase. private static List FallbackFonts { get; } = new(); /// /// Adds a new fallback font. /// /// The font name, e.g. "Noto Sans CJK JP". /// /// Fallback font is used to display characters that not present in the original font. It is often used for CJK. /// For example, you may add "Noto Sans CJK JP" fallback. User defined fallback fonts will then be checked first. /// public static void AddFallbackFont(string fontName) { FallbackFonts.Add(new(fontName)); } /// /// Removes specified fallback font. /// /// The font name. public static void RemoveFallbackFont(string fontName) { for (int i = 0; i < FallbackFonts.Count; i++) { if (FallbackFonts[i].Name == fontName) { FallbackFonts.RemoveAt(i); i--; } } } /// /// Clears all fallback fonts. /// public static void ClearFallbackFonts() { FallbackFonts.Clear(); } private static void AddFontInternal(byte[] buffer, PrivateFontCollection collection) { var ms = new MemoryStream(buffer); using (FontStream fs = new FontStream(ms)) { var list = TrueTypeCollection.CreateFonts(fs); for (int ttcIndex = 0; ttcIndex < list.Count; ttcIndex++) { var ttf = list[ttcIndex]; collection.AddFont(buffer, ttf.Family, ttcIndex); } } } /// /// Adds a font from a memory buffer. /// /// The buffer containing font data. public static void AddFont(byte[] buffer) { AddFontInternal(buffer, PrivateFontCollection); } /// /// Adds a font contained in system memory. /// /// The memory address of the font to add. /// The memory length of the font to add. public static void AddFont(IntPtr memory, int length) { byte[] buffer = new byte[length]; Marshal.Copy(memory, buffer, 0, length); AddFont(buffer); } /// /// Adds a font from a stream. /// /// The stream containing font data. public static void AddFont(Stream stream) { var ms = new MemoryStream(); stream.CopyTo(ms); AddFont(ms.ToArray()); } /// /// Adds a font from a file. /// /// The name of the font file. /// true if file exists, otherwise false. public static bool AddFont(string fileName) { if (File.Exists(fileName)) { AddFont(File.ReadAllBytes(fileName)); return true; } else { Debug.WriteLine($"Font file not found {fileName}"); } return false; } /// /// Adds a font from a file to a temporary font list. /// /// The name of the font file. /// true if file exists, otherwise false. public static bool AddTempFont(string fileName) { if (File.Exists(fileName)) { TemporaryFontCollection ??= new PrivateFontCollection(); AddFontInternal(File.ReadAllBytes(fileName), TemporaryFontCollection); return true; } else { Debug.WriteLine($"Font file not found {fileName}"); } return false; } /// /// Adds a font from a stream to a temporary font list. /// /// The stream containing font data. public static void AddTempFont(Stream stream) { TemporaryFontCollection ??= new PrivateFontCollection(); var ms = new MemoryStream(); stream.CopyTo(ms); AddFontInternal(ms.ToArray(), TemporaryFontCollection); } /// /// Clears temporary fonts. /// public static void ClearTempFonts() { var collection = TemporaryFontCollection; TemporaryFontCollection = null; collection?.Dispose(); } // Defines a font fallback item. private class FontFallback { private FontFamily _family; public string Name { get; } // The font family, if found. May be null. public FontFamily FontFamily => _family ?? FindFontFamily(Name, SearchScope.NonInstalled); public FontFallback(string name) { Name = name; // do initial search in installed fonts. Other collections should be checked later. _family = FindFontFamily(name, SearchScope.Installed); } } // used by the font fallback code from RichTextKit to find a typeface that contains a given character private class CharacterMatcher : Topten.RichTextKit.ICharacterMatcher { private readonly Topten.RichTextKit.ICharacterMatcher oldCharacterMatcher; public CharacterMatcher() { oldCharacterMatcher = Topten.RichTextKit.FontFallback.CharacterMatcher; Topten.RichTextKit.FontFallback.CharacterMatcher = this; } public SKTypeface MatchCharacter(string familyName, int weight, int width, SKFontStyleSlant slant, string[] bcp47, int character) { // check user defined fallbacks first foreach (var ff in FallbackFonts) { var tf = ff.FontFamily?.GetTypeface(weight, width, slant); if (tf != null && tf.ContainsGlyph(character)) { return tf; } } // check private fonts if (TemporaryFontCollection != null) { foreach (var f in TemporaryFontCollection.Families) { var tf = f.GetTypeface(weight, width, slant); if (tf.ContainsGlyph(character)) { return tf; } } } foreach (var f in PrivateFontCollection.Families) { var tf = f.GetTypeface(weight, width, slant); if (tf.ContainsGlyph(character)) { return tf; } } // do default check return oldCharacterMatcher?.MatchCharacter(familyName, weight, width, slant, bcp47, character); } } // used in the FontFamily to look up family name in all font collections private class FontFamilyMatcher1 : FontFamily.IFontFamilyMatcher { public FontFamilyMatcher1() { FontFamily.FontFamilyMatcher = this; } public FontFamily GetFontFamilyOrDefault(string name) => FontManager.GetFontFamilyOrDefault(name); public FontFamily GetFontFamilyForTypeface(SKTypeface typeface, FontStyle style) { var family = TemporaryFontCollection?.Find(typeface, style); family ??= PrivateFontCollection.Find(typeface, style); family ??= InstalledFontCollection.Find(typeface, style); return family; } } } } #endif