Dashboard/Quik.FreeType/FreeTypeFontManager.cs
H. Utku Maden 9339295378 Push all uncommitted changes.
I have had a long break from this project due to other higher priority
things going on in my life. Big changes inbound.
2023-05-13 16:17:57 +03:00

177 lines
5.1 KiB
C#

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<string, FontCacheEntry> _cache = new Dictionary<string, FontCacheEntry>();
private Dictionary<QuikFontStyle, FreeTypeFont> _fonts= new Dictionary<QuikFontStyle, FreeTypeFont>();
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);
}