Added better font search stuff.
This commit is contained in:
parent
66644be699
commit
d831c9b72d
13
Quik.Media.Defaults/EnvironmentVariables.cs
Normal file
13
Quik.Media.Defaults/EnvironmentVariables.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Quik.Media.Defaults
|
||||
{
|
||||
internal static class EnvironmentVariables
|
||||
{
|
||||
public const string SerifFont = "QUIK_SERIF_FONT";
|
||||
public const string SansFont = "QUIK_SANS_FONT";
|
||||
public const string MonospaceFont = "QUIK_MONOSPACE_FONT";
|
||||
public const string CursiveFont = "QUIK_CURSIVE_FONT";
|
||||
public const string FantasyFont = "QUIK_FANTASY_FONT";
|
||||
|
||||
public const string FallbackFontDatabase = "QUIK_FALLBACK_FONT_DB";
|
||||
}
|
||||
}
|
275
Quik.Media.Defaults/Fallback/FallbackFontDatabase.cs
Normal file
275
Quik.Media.Defaults/Fallback/FallbackFontDatabase.cs
Normal file
@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using Quik.FreeType;
|
||||
using Quik.Media.Font;
|
||||
using Quik.PAL;
|
||||
using Quik.Media.Defaults.Linux;
|
||||
|
||||
namespace Quik.Media.Defaults.Fallback
|
||||
{
|
||||
public class FallbackFontDatabase : IFontDataBase
|
||||
{
|
||||
private readonly string DbPath =
|
||||
Environment.GetEnvironmentVariable(EnvironmentVariables.FallbackFontDatabase) ??
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "QUIK/fontdb.json");
|
||||
|
||||
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 FallbackFontDatabase(bool rebuild = false)
|
||||
{
|
||||
// Load existing database if desired.
|
||||
List<DbEntry> database;
|
||||
|
||||
if(!rebuild)
|
||||
{
|
||||
database = LoadDatabase();
|
||||
}
|
||||
else
|
||||
{
|
||||
database = new List<DbEntry>();
|
||||
}
|
||||
|
||||
VerifyDatabase(database);
|
||||
FlushDatabase(database);
|
||||
|
||||
(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);
|
||||
|
||||
database.ForEach(x => AddFont(x.Face, new FileInfo(x.FilePath)));
|
||||
}
|
||||
|
||||
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 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 FontFace GetSystemFontFace(SystemFontFamily family)
|
||||
{
|
||||
return SystemFonts[family];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private List<DbEntry> LoadDatabase()
|
||||
{
|
||||
FileInfo info = new FileInfo(DbPath);
|
||||
|
||||
if (!info.Exists)
|
||||
return new List<DbEntry>();
|
||||
|
||||
using Stream str = info.OpenRead();
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<DbEntry>>(str);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<DbEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyDatabase(List<DbEntry> db)
|
||||
{
|
||||
// Very slow way to do this but how many fonts could a system have on average?
|
||||
Dictionary<string, DbEntry> entires = new Dictionary<string, DbEntry>();
|
||||
|
||||
foreach (DbEntry entry in db)
|
||||
{
|
||||
FileInfo info = new FileInfo(entry.FilePath);
|
||||
|
||||
// Reprocess fonts that appear like this.
|
||||
if (!info.Exists) continue;
|
||||
else if (info.LastWriteTime > entry.AccessTime) continue;
|
||||
}
|
||||
|
||||
string fontpath = null;
|
||||
try
|
||||
{
|
||||
fontpath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
|
||||
if (string.IsNullOrEmpty(fontpath))
|
||||
throw new Exception();
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (string path in FontPaths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
fontpath = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rip
|
||||
if (string.IsNullOrEmpty(fontpath))
|
||||
return;
|
||||
}
|
||||
|
||||
SearchPathForFonts(entires, fontpath);
|
||||
|
||||
db.Clear();
|
||||
db.AddRange(entires.Values);
|
||||
}
|
||||
|
||||
private static void SearchPathForFonts(Dictionary<string, DbEntry> entries, string path)
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(path);
|
||||
|
||||
foreach (FileInfo file in dir.EnumerateFiles())
|
||||
{
|
||||
SearchFileForFonts(entries, file);
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo directory in dir.EnumerateDirectories())
|
||||
{
|
||||
SearchPathForFonts(entries, directory.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SearchFileForFonts(Dictionary<string, DbEntry> entries, FileInfo file)
|
||||
{
|
||||
if (entries.ContainsKey(file.FullName))
|
||||
return;
|
||||
|
||||
if (FT.NewFace(FTProvider.Ft, file.FullName, 0, out FTFace face) != FTError.None)
|
||||
return;
|
||||
|
||||
FontFace facename = FontFace.Parse(face.FamilyName, face.StyleName);
|
||||
|
||||
DbEntry entry = new DbEntry(facename, file.FullName);
|
||||
entries.Add(file.FullName, entry);
|
||||
FT.DoneFace(face);
|
||||
}
|
||||
|
||||
private void FlushDatabase(List<DbEntry> db)
|
||||
{
|
||||
FileInfo info = new FileInfo(DbPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(DbPath));
|
||||
using Stream str = info.OpenWrite();
|
||||
JsonSerializer.Serialize(str, db);
|
||||
}
|
||||
|
||||
private static readonly string[] FontPaths = new string[] {
|
||||
"/usr/share/fonts",
|
||||
};
|
||||
|
||||
[JsonSerializable(typeof(DbEntry))]
|
||||
private class DbEntry
|
||||
{
|
||||
[JsonIgnore] public FontFace Face => new FontFace(Family, Slant, Weight, Stretch);
|
||||
public string Family { get; set; }
|
||||
public FontSlant Slant { get; set; }
|
||||
public FontWeight Weight { get; set; }
|
||||
public FontStretch Stretch { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public DateTime AccessTime { get; set; }
|
||||
|
||||
public DbEntry() {}
|
||||
public DbEntry(FontFace face, string path)
|
||||
{
|
||||
Family = face.Family;
|
||||
Slant = face.Slant;
|
||||
Weight = face.Weight;
|
||||
Stretch = face.Stretch;
|
||||
FilePath = path;
|
||||
AccessTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
93
Quik.Media.Defaults/FontDatabaseProvider.cs
Normal file
93
Quik.Media.Defaults/FontDatabaseProvider.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Quik.FreeType;
|
||||
using Quik.Media.Defaults.Fallback;
|
||||
using Quik.Media.Defaults.Linux;
|
||||
using Quik.Media.Font;
|
||||
using Quik.PAL;
|
||||
|
||||
namespace Quik.Media.Defaults
|
||||
{
|
||||
public static class FontDataBaseProvider
|
||||
{
|
||||
public static IFontDataBase Instance { get; }
|
||||
|
||||
static FontDataBaseProvider()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: add as other operating systems are supported.
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Instance = new FontConfigFontDatabase();
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = new FallbackFontDatabase();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new NotSupportedException("Could not load a suitable font database implementation.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static (FontFace, FileInfo) ResolveSystemFont(string envVar, string defaults, IFontDataBase db)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
string user = Environment.GetEnvironmentVariable(envVar);
|
||||
if (user != null)
|
||||
{
|
||||
builder.Append(user);
|
||||
builder.Append(':');
|
||||
}
|
||||
|
||||
builder.Append(defaults);
|
||||
|
||||
string[] list = builder.ToString().Split(':');
|
||||
|
||||
foreach (string item in list)
|
||||
{
|
||||
if (File.Exists(item))
|
||||
{
|
||||
// Process file.
|
||||
if (FT.NewFace(FTProvider.Ft, item, 0, out FTFace ftface) != FTError.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FontFace face = FontFace.Parse(ftface.FamilyName, ftface.StyleName);
|
||||
FT.DoneFace(ftface);
|
||||
|
||||
return (face, new FileInfo(item));
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<FontFace> faces = db.Search(
|
||||
new FontFace(item, FontSlant.Normal, FontWeight.Normal, FontStretch.Normal),
|
||||
FontMatchCriteria.Family);
|
||||
|
||||
if (faces.Any())
|
||||
{
|
||||
FontFace face = faces.First();
|
||||
return (face, db.FontFileInfo(face));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FontFace face = db.GetSystemFontFace(SystemFontFamily.Sans);
|
||||
return (face, db.FontFileInfo(face));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new NotImplementedException("No fallback font yet.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Quik.FreeType;
|
||||
using Quik.Media.Font;
|
||||
using Quik.PAL;
|
||||
|
||||
@ -13,10 +16,12 @@ namespace Quik.Media.Defaults.Linux
|
||||
/// </summary>
|
||||
public class FontConfigFontDatabase : IFontDataBase
|
||||
{
|
||||
private IReadOnlyDictionary<FontFace, FileInfo> FilesMap { get; }
|
||||
private IReadOnlyDictionary<string, IReadOnlyList<FontFace>> ByFamily { get; }
|
||||
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>();
|
||||
|
||||
public IEnumerable<FontFace> All { get; }
|
||||
IEnumerable<FontFace> IFontDataBase.All => this.All;
|
||||
|
||||
public FontConfigFontDatabase()
|
||||
{
|
||||
@ -25,10 +30,6 @@ namespace Quik.Media.Defaults.Linux
|
||||
throw new NotSupportedException("This host doesn't have fontconfig installed.");
|
||||
}
|
||||
|
||||
List<FontFace> faces = new List<FontFace>();
|
||||
Dictionary<FontFace, FileInfo> files = new Dictionary<FontFace, FileInfo>();
|
||||
Dictionary<string, List<FontFace>> byFamily = new Dictionary<string, List<FontFace>>();
|
||||
|
||||
FcConfig config = FontConfig.ConfigGetCurrent();
|
||||
FcPattern pattern = FontConfig.PatternCreate();
|
||||
FcObjectSet os = FontConfig.ObjectSetBuild(FontConfig.FAMILY, FontConfig.STYLE, FontConfig.FILE);
|
||||
@ -49,32 +50,51 @@ namespace Quik.Media.Defaults.Linux
|
||||
string style = Marshal.PtrToStringUTF8(pStyle);
|
||||
|
||||
FontFace face = FontFace.Parse(family, style);
|
||||
faces.Add(face);
|
||||
|
||||
if (!byFamily.TryGetValue(face.Family, out List<FontFace> siblings))
|
||||
{
|
||||
siblings = new List<FontFace>();
|
||||
byFamily.Add(face.Family, siblings);
|
||||
}
|
||||
siblings.Add(face);
|
||||
|
||||
FontConfig.PatternGetString(current, FontConfig.FILE, 0, out IntPtr pFile);
|
||||
string file = Marshal.PtrToStringAnsi(pFile);
|
||||
|
||||
files.TryAdd(face, new FileInfo(file));
|
||||
AddFont(face, new FileInfo(file));
|
||||
}
|
||||
|
||||
FontConfig.FontSetDestroy(fs);
|
||||
FontConfig.ObjectSetDestroy(os);
|
||||
FontConfig.PatternDestroy(pattern);
|
||||
|
||||
All = faces.ToArray();
|
||||
FilesMap = files;
|
||||
ByFamily = new Dictionary<string, IReadOnlyList<FontFace>>(
|
||||
byFamily.Select(x =>
|
||||
new KeyValuePair<string, IReadOnlyList<FontFace>>(x.Key, x.Value)
|
||||
)
|
||||
);
|
||||
(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)
|
||||
@ -84,7 +104,7 @@ namespace Quik.Media.Defaults.Linux
|
||||
|
||||
if (criteria.HasFlag(FontMatchCriteria.Family))
|
||||
{
|
||||
IReadOnlyList<FontFace> siblings;
|
||||
List<FontFace> siblings;
|
||||
|
||||
if (!ByFamily.TryGetValue(prototype.Family, out siblings))
|
||||
{
|
||||
@ -139,5 +159,10 @@ namespace Quik.Media.Defaults.Linux
|
||||
{
|
||||
return FontFileInfo(face)?.OpenRead() ?? throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
public FontFace GetSystemFontFace(SystemFontFamily family)
|
||||
{
|
||||
return SystemFonts[family];
|
||||
}
|
||||
}
|
||||
}
|
11
Quik.Media.Defaults/Linux/LinuxFonts.cs
Normal file
11
Quik.Media.Defaults/Linux/LinuxFonts.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Quik.Media.Defaults.Linux
|
||||
{
|
||||
internal static class LinuxFonts
|
||||
{
|
||||
public const string DefaultSerifFamilies = "Noto Serif:Nimbus Roman:Liberation Serif:FreeSerif:Times:Times New Roman";
|
||||
public const string DefaultSansFamilies = "Noto Sans:Nimbus Sans:Droid Sans:Liberation Sans:FreeSans:Helvetica Neue:Helvetica:Arial";
|
||||
public const string DefaultMonospaceFamilies = "Noto Mono:Nimbus Mono PS:Liberation Mono:DejaVu Mono:FreeMono:Lucida Console:Consolas:Courier:Courier New";
|
||||
public const string DefaultCursiveFamilies = "";
|
||||
public const string DefaultFantasyFamilies = "";
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
@ -7,10 +7,13 @@
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Quik.FreeType" Version="1.1.0" />
|
||||
<PackageReference Include="Quik.StbImage" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Quik\Quik.csproj" />
|
||||
<ProjectReference Include="..\Quik.StbImage\Quik.StbImage.csproj" />
|
||||
<ProjectReference Include="..\Quik.FreeType\Quik.FreeType.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Quik.Media.Font;
|
||||
|
||||
namespace Quik.Media.Font
|
||||
{
|
||||
|
26
Quik/Media/Font/SystemFontFamily.cs
Normal file
26
Quik/Media/Font/SystemFontFamily.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace Quik.Media.Font
|
||||
{
|
||||
public enum SystemFontFamily
|
||||
{
|
||||
/// <summary>
|
||||
/// A font with serifs, like Times New Roman.
|
||||
/// </summary>
|
||||
Serif,
|
||||
/// <summary>
|
||||
/// A font without serifs, like Helvetica or Arial.
|
||||
/// </summary>
|
||||
Sans,
|
||||
/// <summary>
|
||||
/// A monospace font like Courier New.
|
||||
/// </summary>
|
||||
Monospace,
|
||||
/// <summary>
|
||||
/// A cursive font like Lucida Handwriting.
|
||||
/// </summary>
|
||||
Cursive,
|
||||
/// <summary>
|
||||
/// An immature font like Comic Sans or Papyrus, nghehehehe.
|
||||
/// </summary>
|
||||
Fantasy
|
||||
}
|
||||
}
|
@ -29,6 +29,12 @@ namespace Quik.PAL
|
||||
/// </summary>
|
||||
IEnumerable<FontFace> All { get; }
|
||||
|
||||
public FontFace Serif => GetSystemFontFace(SystemFontFamily.Serif);
|
||||
public FontFace Sans => GetSystemFontFace(SystemFontFamily.Sans);
|
||||
public FontFace Monospace => GetSystemFontFace(SystemFontFamily.Monospace);
|
||||
public FontFace Cursive => GetSystemFontFace(SystemFontFamily.Cursive);
|
||||
public FontFace Fantasy => GetSystemFontFace(SystemFontFamily.Fantasy);
|
||||
|
||||
/// <summary>
|
||||
/// Search for the given font face.
|
||||
/// </summary>
|
||||
@ -50,5 +56,12 @@ namespace Quik.PAL
|
||||
/// <param name="face">The font face to open.</param>
|
||||
/// <returns>The stream to the font face.</returns>
|
||||
Stream Open(FontFace face);
|
||||
|
||||
/// <summary>
|
||||
/// Get a system font family.
|
||||
/// </summary>
|
||||
/// <param name="family">The family type to look up.</param>
|
||||
/// <returns>The name of a font in this family.</returns>
|
||||
FontFace GetSystemFontFace(SystemFontFamily family);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<LangVersion>8</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user