#20 start adding system font support

This commit is contained in:
Eliot Jones
2018-11-29 21:22:01 +00:00
parent 06bee446d8
commit 89bd649ced
2 changed files with 350 additions and 0 deletions

View File

@@ -0,0 +1,291 @@
namespace UglyToad.PdfPig.Fonts.SystemFonts
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using IO;
using TrueType;
using TrueType.Parser;
internal class SystemFontFinder
{
private readonly TrueTypeFontParser trueTypeFontParser;
private readonly Lazy<IReadOnlyList<SystemFontRecord>> availableFonts;
private readonly Dictionary<string, IFont> cache = new Dictionary<string, IFont>();
public SystemFontFinder(TrueTypeFontParser trueTypeFontParser)
{
this.trueTypeFontParser = trueTypeFontParser;
ISystemFontLister lister;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
lister = new WindowsSystemFontLister();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
lister = new MacSystemFontLister();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
lister = new LinuxSystemFontLister();
}
else
{
throw new NotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}.");
}
availableFonts = new Lazy<IReadOnlyList<SystemFontRecord>>(() => lister.GetAllFonts().ToList());
}
public TrueTypeFontProgram GetTrueTypeFont(string name)
{
foreach (var record in availableFonts.Value)
{
if (record.Type == SystemFontType.TrueType)
{
using (var fileStream = File.OpenRead(record.Path))
{
var input = new StreamInputBytes(fileStream);
var trueType = trueTypeFontParser.Parse(new TrueTypeDataBytes(input));
//TODO: cache.
}
}
else if (record.Type == SystemFontType.OpenType)
{
throw new NotImplementedException($"TODO: OpenType fonts. Found system font: {record.Path}.");
}
}
return null;
}
}
internal interface ISystemFontLister
{
IEnumerable<SystemFontRecord> GetAllFonts();
}
internal class WindowsSystemFontLister : ISystemFontLister
{
public IEnumerable<SystemFontRecord> GetAllFonts()
{
// TODO: Could use System.Drawing InstalledFontCollection to do this?
var winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
var fonts = Path.Combine(winDir, "Fonts");
if (Directory.Exists(fonts))
{
var files = Directory.GetFiles(fonts);
foreach (var file in files)
{
if (SystemFontRecord.TryCreate(file, out var record))
{
yield return record;
}
}
}
var psFonts = Path.Combine(winDir, "PSFonts");
if (Directory.Exists(psFonts))
{
var files = Directory.GetFiles(fonts);
foreach (var file in files)
{
if (SystemFontRecord.TryCreate(file, out var record))
{
yield return record;
}
}
}
}
}
internal class LinuxSystemFontLister : ISystemFontLister
{
public IEnumerable<SystemFontRecord> GetAllFonts()
{
var directories = new List<string>
{
"/usr/local/fonts", // local
"/usr/local/share/fonts", // local shared
"/usr/share/fonts", // system
"/usr/X11R6/lib/X11/fonts" // X
};
try
{
var folder = Environment.GetEnvironmentVariable("$HOME");
if (!string.IsNullOrWhiteSpace(folder))
{
directories.Add($"{folder}/.fonts");
}
}
catch
{
// ignored
}
foreach (var directory in directories)
{
try
{
if (!Directory.Exists(directory))
{
continue;
}
}
catch
{
continue;
}
string[] files;
try
{
files = Directory.GetFiles(directory);
}
catch
{
continue;
}
foreach (var file in files)
{
if (SystemFontRecord.TryCreate(file, out var record))
{
yield return record;
}
}
}
}
}
internal class MacSystemFontLister : ISystemFontLister
{
public IEnumerable<SystemFontRecord> GetAllFonts()
{
var directories = new List<string>
{
"/Library/Fonts/", // local
"/System/Library/Fonts/", // system
"/Network/Library/Fonts/" // network
};
try
{
var folder = Environment.GetEnvironmentVariable("$HOME");
if (!string.IsNullOrWhiteSpace(folder))
{
directories.Add($"{folder}/Library/Fonts");
}
}
catch
{
// ignored
}
foreach (var directory in directories)
{
try
{
if (!Directory.Exists(directory))
{
continue;
}
}
catch
{
continue;
}
string[] files;
try
{
files = Directory.GetFiles(directory);
}
catch
{
continue;
}
foreach (var file in files)
{
if (SystemFontRecord.TryCreate(file, out var record))
{
yield return record;
}
}
}
}
}
internal struct SystemFontRecord
{
public string Path { get; }
public SystemFontType Type { get; }
public SystemFontRecord(string path, SystemFontType type)
{
Path = path ?? throw new ArgumentNullException(nameof(path));
Type = type;
}
public static bool TryCreate(string path, out SystemFontRecord type)
{
type = default(SystemFontRecord);
SystemFontType fontType;
if (path.EndsWith(".ttf"))
{
fontType = SystemFontType.TrueType;
}
else if (path.EndsWith(".otf"))
{
fontType = SystemFontType.OpenType;
}
else if (path.EndsWith(".ttc"))
{
fontType = SystemFontType.TrueTypeCollection;
}
else if (path.EndsWith(".otc"))
{
fontType = SystemFontType.OpenTypeCollection;
}
else if (path.EndsWith(".pfb"))
{
fontType = SystemFontType.Type1;
}
else
{
return false;
}
type = new SystemFontRecord(path, fontType);
return true;
}
}
internal enum SystemFontType
{
Unknown = 0,
TrueType = 1,
OpenType = 2,
Type1 = 3,
TrueTypeCollection = 4,
OpenTypeCollection = 5
}
}

View File

@@ -0,0 +1,59 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
internal class NameTable
{
public string Tag => TrueTypeHeaderTable.Name;
public TrueTypeHeaderTable DirectoryTable { get; }
public static void Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
{
data.Seek(table.Offset);
var format = data.ReadUnsignedShort();
var count = data.ReadUnsignedShort();
var stringOffset = data.ReadUnsignedShort();
var names = new NameRecord[count];
for (var i = 0; i < count; i++)
{
names[i] = NameRecord.Read(data);
}
}
public struct NameRecord
{
public int PlatformId { get; }
public int PlatformSpecificId { get; }
public int LanguageId { get; }
public int NameId { get; }
public int Length { get; }
public int Offset { get; }
public NameRecord(int platformId, int platformSpecificId, int languageId, int nameId, int length, int offset)
{
PlatformId = platformId;
PlatformSpecificId = platformSpecificId;
LanguageId = languageId;
NameId = nameId;
Length = length;
Offset = offset;
}
public static NameRecord Read(TrueTypeDataBytes data)
{
return new NameRecord(data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort());
}
}
}
}