mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
#20 start adding system font support
This commit is contained in:
291
src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs
Normal file
291
src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
59
src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs
Normal file
59
src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user