using System; using System.Collections.Generic; using System.IO; using Quik.Typography; namespace Quik.FreeType { public class FreeTypeFontManager : IQuikFontManager, IDisposable { public QuikContext Context { get; set; } internal FTLibrary _library; private Dictionary _cache = new Dictionary(); private Dictionary _fonts= new Dictionary(); public event FreeTypeFontManagerFontFinder FontNotFound; public FreeTypeFontManager() { FT.InitFreeType(out _library); // FIXME: There are operating system specific ways to achieve this. This is // definitely not the best way to do this. // Scan the fonts folder and build up a font cache. string path = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); if (string.IsNullOrEmpty(path)) { if (OperatingSystem.IsLinux()) { path = "/usr/share/fonts"; } else if (OperatingSystem.IsWindows()) { path = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts"); } // For macOS I don't know. Too bad. } DirectoryInfo directory = new DirectoryInfo(path); ScanDirectoryForFonts(directory); } private void ScanDirectoryForFonts(DirectoryInfo directory) { foreach (FileSystemInfo node in directory.GetFileSystemInfos()) { if (node.Attributes.HasFlag(FileAttributes.Directory)) { ScanDirectoryForFonts(node as DirectoryInfo); } else { ScanFileForFonts(node as FileInfo); } } } private void ScanFileForFonts(FileInfo file) { if (file is null) return; if (FT.NewFace(_library, file.FullName, 0, out FTFace face) == FTError.None) { FontCacheEntry entry; string name = face.FamilyName; string style = face.StyleName; if (name is null) goto done_face; if (_cache.ContainsKey(name)) { entry = _cache[name]; } else { entry = new FontCacheEntry(); entry.Family = name; _cache[name] = entry; } switch (style.ToLowerInvariant()) { case "regular": entry.Regular = file; break; case "bold": entry.Bold = file; break; case "italic": entry.Italic = file; break; } done_face: FT.DoneFace(face); } } public void Clear() { // Nothing to do. } public QuikFont GetFont(QuikFontStyle fontStyle) { FreeTypeFont font; if (_fonts.TryGetValue(fontStyle, out font)) return font; FileInfo file = FindFont(fontStyle); if (file == null) { FontNotFound?.Invoke(fontStyle, ref file); if (file == null) { throw new Exception("Could not find the font you are looking for."); } } font = new FreeTypeFont(this, file, fontStyle); _fonts.Add(fontStyle, font); return font; } private FileInfo FindFont(QuikFontStyle fontStyle) { FontCacheEntry entry; if (_cache.TryGetValue(fontStyle.Family, out entry)) { switch (fontStyle.Type) { case QuikFontType.Normal: return entry.Regular; case QuikFontType.Bold: return entry.Bold; case QuikFontType.Italic: return entry.Italic; } } return null; } // IDisposable private void ReleaseUnmanagedResources() { FT.DoneFreeType(_library); } public void Dispose() { ReleaseUnmanagedResources(); GC.SuppressFinalize(this); } ~FreeTypeFontManager() { ReleaseUnmanagedResources(); } private class FontCacheEntry { public string Family; public FileInfo Regular; public FileInfo Bold; public FileInfo Italic; } } public delegate void FreeTypeFontManagerFontFinder(QuikFontStyle style, ref FileInfo info); }