H. Utku Maden
a1f4e6a4dc
Due to unforseen naming conflicts, the project has been rebranded under the ReFuel umbrealla and will now be referred to as Dashboard from now on. Other changes will occur to suit the library more for the engine whilst keeping the freestanding nature of the library. Rename folder. Rename to Dashboard.OpenTK Rename to Dashboard.Media.Defaults. Do the last renames and path fixes.
168 lines
6.5 KiB
C#
168 lines
6.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using ReFuel.FreeType;
|
|
using Dashboard.Media.Font;
|
|
using Dashboard.PAL;
|
|
|
|
namespace Dashboard.Media.Defaults.Linux
|
|
{
|
|
/// <summary>
|
|
/// Font database for Linux libfontconfig systems.
|
|
/// </summary>
|
|
public class FontConfigFontDatabase : IFontDataBase
|
|
{
|
|
private Dictionary<FontFace, FileInfo> FilesMap { get; } = new Dictionary<FontFace, FileInfo>();
|
|
private Dictionary<string, List<FontFace>> ByFamily { get; } = new Dictionary<string, List<FontFace>>();
|
|
private Dictionary<SystemFontFamily, FontFace> SystemFonts { get; } = new Dictionary<SystemFontFamily, FontFace>();
|
|
private List<FontFace> All { get; } = new List<FontFace>();
|
|
|
|
IEnumerable<FontFace> IFontDataBase.All => this.All;
|
|
|
|
public FontConfigFontDatabase()
|
|
{
|
|
if (!FontConfig.Exists)
|
|
{
|
|
throw new NotSupportedException("This host doesn't have fontconfig installed.");
|
|
}
|
|
|
|
FcConfig config = FontConfig.ConfigGetCurrent();
|
|
FcPattern pattern = FontConfig.PatternCreate();
|
|
FcObjectSet os = FontConfig.ObjectSetBuild(FontConfig.FAMILY, FontConfig.STYLE, FontConfig.FILE);
|
|
FcFontSet fs = FontConfig.FontList(config, pattern, os);
|
|
|
|
for (int i = 0; i < fs.NFont; i++)
|
|
{
|
|
FcPattern current = fs[i];
|
|
|
|
if (
|
|
FontConfig.PatternGetString(current, FontConfig.FAMILY, 0, out IntPtr pFamily) != FcResult.Match ||
|
|
FontConfig.PatternGetString(current, FontConfig.STYLE, 0, out IntPtr pStyle) != FcResult.Match)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string family = Marshal.PtrToStringUTF8(pFamily);
|
|
string style = Marshal.PtrToStringUTF8(pStyle);
|
|
|
|
FontFace face = FontFace.Parse(family, style);
|
|
|
|
FontConfig.PatternGetString(current, FontConfig.FILE, 0, out IntPtr pFile);
|
|
string file = Marshal.PtrToStringAnsi(pFile);
|
|
|
|
AddFont(face, new FileInfo(file));
|
|
}
|
|
|
|
FontConfig.FontSetDestroy(fs);
|
|
FontConfig.ObjectSetDestroy(os);
|
|
FontConfig.PatternDestroy(pattern);
|
|
|
|
(FontFace, FileInfo) serif = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SerifFont, LinuxFonts.DefaultSerifFamilies, this);
|
|
SystemFonts[SystemFontFamily.Serif] = serif.Item1;
|
|
(FontFace, FileInfo) sans = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SansFont, LinuxFonts.DefaultSansFamilies, this);
|
|
SystemFonts[SystemFontFamily.Sans] = sans.Item1;
|
|
(FontFace, FileInfo) mono = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.MonospaceFont, LinuxFonts.DefaultMonospaceFamilies, this);
|
|
SystemFonts[SystemFontFamily.Monospace] = mono.Item1;
|
|
(FontFace, FileInfo) cursive = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.CursiveFont, LinuxFonts.DefaultCursiveFamilies, this);
|
|
SystemFonts[SystemFontFamily.Cursive] = cursive.Item1;
|
|
(FontFace, FileInfo) fantasy = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.FantasyFont, LinuxFonts.DefaultFantasyFamilies, this);
|
|
SystemFonts[SystemFontFamily.Fantasy] = fantasy.Item1;
|
|
|
|
AddFont(serif.Item1, serif.Item2);
|
|
AddFont(sans.Item1, sans.Item2);
|
|
AddFont(mono.Item1, mono.Item2);
|
|
AddFont(cursive.Item1, cursive.Item2);
|
|
AddFont(fantasy.Item1, fantasy.Item2);
|
|
|
|
}
|
|
|
|
private void AddFont(FontFace face, FileInfo file)
|
|
{
|
|
if (!All.Contains(face))
|
|
All.Add(face);
|
|
|
|
FilesMap.TryAdd(face, file);
|
|
|
|
if (!ByFamily.TryGetValue(face.Family, out List<FontFace> siblings))
|
|
{
|
|
siblings = new List<FontFace>();
|
|
ByFamily.Add(face.Family, siblings);
|
|
}
|
|
|
|
if (!siblings.Contains(face))
|
|
siblings.Add(face);
|
|
}
|
|
|
|
public IEnumerable<FontFace> Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All)
|
|
{
|
|
// A bit scuffed and LINQ heavy but it should work.
|
|
IEnumerable<FontFace> candidates;
|
|
|
|
if (criteria.HasFlag(FontMatchCriteria.Family))
|
|
{
|
|
List<FontFace> siblings;
|
|
|
|
if (!ByFamily.TryGetValue(prototype.Family, out siblings))
|
|
{
|
|
return Enumerable.Empty<FontFace>();
|
|
}
|
|
|
|
candidates = siblings;
|
|
}
|
|
else
|
|
{
|
|
candidates = All;
|
|
}
|
|
|
|
return
|
|
candidates
|
|
.Where(x =>
|
|
implies(criteria.HasFlag(FontMatchCriteria.Slant), prototype.Slant == x.Slant) ||
|
|
implies(criteria.HasFlag(FontMatchCriteria.Weight), prototype.Weight == x.Weight) ||
|
|
implies(criteria.HasFlag(FontMatchCriteria.Stretch), prototype.Stretch == x.Stretch)
|
|
)
|
|
.OrderByDescending(x =>
|
|
|
|
(prototype.Slant == x.Slant ? 1 : 0) +
|
|
(prototype.Weight == x.Weight ? 1 : 0) +
|
|
(prototype.Stretch == x.Stretch ? 1 : 0) +
|
|
confidence(prototype.Family, x.Family) * 3
|
|
);
|
|
|
|
// a => b = a'+b
|
|
static bool implies(bool a, bool b)
|
|
{
|
|
return !a || b;
|
|
}
|
|
|
|
static int confidence(string target, string testee)
|
|
{
|
|
int i;
|
|
for (i = 0; i < target.Length && i < testee.Length && target[i] == testee[i]; i++);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
public FileInfo FontFileInfo(FontFace face)
|
|
{
|
|
if (FilesMap.TryGetValue(face, out FileInfo info))
|
|
return info;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public Stream Open(FontFace face)
|
|
{
|
|
return FontFileInfo(face)?.OpenRead() ?? throw new FileNotFoundException();
|
|
}
|
|
|
|
public FontFace GetSystemFontFace(SystemFontFamily family)
|
|
{
|
|
return SystemFonts[family];
|
|
}
|
|
}
|
|
} |