From 25bbbe19b22c2a29a882310072581758de08c2e9 Mon Sep 17 00:00:00 2001 From: MichelMichels Date: Wed, 8 Jun 2022 21:08:44 +0200 Subject: [PATCH 1/2] FontResolver class code cleanup --- PdfSharpCore/Internal/FontFamilyModel.cs | 1 - PdfSharpCore/Utils/FontFileInfo.cs | 38 +++ PdfSharpCore/Utils/FontResolver.cs | 307 +++++++++++------------ 3 files changed, 178 insertions(+), 168 deletions(-) create mode 100644 PdfSharpCore/Utils/FontFileInfo.cs diff --git a/PdfSharpCore/Internal/FontFamilyModel.cs b/PdfSharpCore/Internal/FontFamilyModel.cs index 02d326c9..98afde81 100644 --- a/PdfSharpCore/Internal/FontFamilyModel.cs +++ b/PdfSharpCore/Internal/FontFamilyModel.cs @@ -6,7 +6,6 @@ namespace PdfSharpCore.Internal public class FontFamilyModel { public string Name { get; set; } - public Dictionary FontFiles = new Dictionary(); public bool IsStyleAvailable(XFontStyle fontStyle) diff --git a/PdfSharpCore/Utils/FontFileInfo.cs b/PdfSharpCore/Utils/FontFileInfo.cs new file mode 100644 index 00000000..3ab08d57 --- /dev/null +++ b/PdfSharpCore/Utils/FontFileInfo.cs @@ -0,0 +1,38 @@ +using PdfSharpCore.Drawing; +using SixLabors.Fonts; + +namespace PdfSharpCore.Utils +{ + internal struct FontFileInfo + { + private FontFileInfo(string path, FontDescription fontDescription) + { + Path = path; + FontDescription = fontDescription; + } + + public string Path { get; } + public FontDescription FontDescription { get; } + public string FamilyName => FontDescription.FontFamilyInvariantCulture; + + public XFontStyle GuessFontStyle() + { + switch (FontDescription.Style) + { + case FontStyle.Bold: + return XFontStyle.Bold; + case FontStyle.Italic: + return XFontStyle.Italic; + case FontStyle.BoldItalic: + return XFontStyle.BoldItalic; + default: + return XFontStyle.Regular; + } + } + public static FontFileInfo Load(string path) + { + var fontDescription = FontDescription.LoadDescription(path); + return new FontFileInfo(path, fontDescription); + } + } +} \ No newline at end of file diff --git a/PdfSharpCore/Utils/FontResolver.cs b/PdfSharpCore/Utils/FontResolver.cs index f233a7d0..586c87e4 100644 --- a/PdfSharpCore/Utils/FontResolver.cs +++ b/PdfSharpCore/Utils/FontResolver.cs @@ -1,232 +1,205 @@ - -using System.Linq; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; - -using PdfSharpCore.Internal; -using PdfSharpCore.Drawing; +using PdfSharpCore.Drawing; using PdfSharpCore.Fonts; - -using SixLabors.Fonts; - +using PdfSharpCore.Internal; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; namespace PdfSharpCore.Utils { - - - public class FontResolver - : IFontResolver + public class FontResolver : IFontResolver { - public string DefaultFontName => "Arial"; - - private static readonly Dictionary InstalledFonts = new Dictionary(); - - private static readonly string[] SSupportedFonts; - - public FontResolver() - { - } + private static readonly List installedFonts = new List(); + private static readonly string[] installedFontFilePaths; static FontResolver() { - string fontDir; - - bool isOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX); - if (isOSX) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - fontDir = "/Library/Fonts/"; - SSupportedFonts = System.IO.Directory.GetFiles(fontDir, "*.ttf", System.IO.SearchOption.AllDirectories); - SetupFontsFiles(SSupportedFonts); - return; + installedFontFilePaths = LoadWindowsFonts(); } - - bool isLinux = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux); - if (isLinux) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - SSupportedFonts = LinuxSystemFontResolver.Resolve(); - SetupFontsFiles(SSupportedFonts); - return; + installedFontFilePaths = LoadOSXFonts(); } - - bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); - if (isWindows) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - fontDir = System.Environment.ExpandEnvironmentVariables(@"%SystemRoot%\Fonts"); - var fontPaths = new List(); - - var systemFontPaths = System.IO.Directory.GetFiles(fontDir, "*.ttf", System.IO.SearchOption.AllDirectories); - fontPaths.AddRange(systemFontPaths); - - var appdataFontDir = System.Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Microsoft\Windows\Fonts"); - if(System.IO.Directory.Exists(appdataFontDir)) - { - var appdataFontPaths = System.IO.Directory.GetFiles(appdataFontDir, "*.ttf", System.IO.SearchOption.AllDirectories); - fontPaths.AddRange(appdataFontPaths); - } - - SSupportedFonts = fontPaths.ToArray(); - SetupFontsFiles(SSupportedFonts); - return; + installedFontFilePaths = LinuxSystemFontResolver.Resolve(); + } + else + { + throw new NotImplementedException($"FontResolver not implemented for this platform (PdfSharpCore.Utils.FontResolver.cs)."); } - throw new System.NotImplementedException("FontResolver not implemented for this platform (PdfSharpCore.Utils.FontResolver.cs)."); + SetupFontFiles(); } + public string DefaultFontName => "Arial"; + public bool NullIfFontNotFound { get; set; } = false; - private readonly struct FontFileInfo + public virtual byte[] GetFont(string faceName) { - private FontFileInfo(string path, FontDescription fontDescription) + using (var ms = new MemoryStream()) { - this.Path = path; - this.FontDescription = fontDescription; - } - - public string Path { get; } - - public FontDescription FontDescription { get; } - - public string FamilyName => this.FontDescription.FontFamilyInvariantCulture; - + string ttfPathFile = ""; + try + { + ttfPathFile = installedFontFilePaths + .ToList() + .First(x => x.ToLower().Contains(Path.GetFileName(faceName).ToLower())); - public XFontStyle GuessFontStyle() - { - switch (this.FontDescription.Style) + if (File.Exists(ttfPathFile)) + { + using (var fileStream = File.OpenRead(ttfPathFile)) + { + fileStream.CopyTo(ms); + ms.Position = 0; + return ms.ToArray(); + } + } + else + { + throw new FileNotFoundException(ttfPathFile); + } + } + catch (Exception) { - case FontStyle.Bold: - return XFontStyle.Bold; - case FontStyle.Italic: - return XFontStyle.Italic; - case FontStyle.BoldItalic: - return XFontStyle.BoldItalic; - default: - return XFontStyle.Regular; + throw new Exception($"Font file with name {faceName} and path {ttfPathFile} not found."); } } - - public static FontFileInfo Load(string path) + } + public virtual FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) + { + if (installedFonts.Count == 0) { - FontDescription fontDescription = FontDescription.LoadDescription(path); - return new FontFileInfo(path, fontDescription); + throw new FileNotFoundException("No Fonts installed on this device!"); } - } - - public static void SetupFontsFiles(string[] sSupportedFonts) - { - List tempFontInfoList = new List(); - foreach (string fontPathFile in sSupportedFonts) + var fontFamily = installedFonts.Find(x => x.Name.ToLower().Equals(familyName.ToLower())); + if (fontFamily != null) { - try + if (isBold && isItalic) { - FontFileInfo fontInfo = FontFileInfo.Load(fontPathFile); - Debug.WriteLine(fontPathFile); - tempFontInfoList.Add(fontInfo); + if (fontFamily.FontFiles.TryGetValue(XFontStyle.BoldItalic, out string boldItalicFile)) + { + return new FontResolverInfo(Path.GetFileName(boldItalicFile)); + } } - catch (System.Exception e) + else if (isBold) { - System.Console.Error.WriteLine(e); + if (fontFamily.FontFiles.TryGetValue(XFontStyle.Bold, out string boldFile)) + { + return new FontResolverInfo(Path.GetFileName(boldFile)); + } } - } - - // Deserialize all font families - foreach (IGrouping familyGroup in tempFontInfoList.GroupBy(info => info.FamilyName)) - try + else if (isItalic) { - string familyName = familyGroup.Key; - FontFamilyModel family = DeserializeFontFamily(familyName, familyGroup); - InstalledFonts.Add(familyName.ToLower(), family); + if (fontFamily.FontFiles.TryGetValue(XFontStyle.Italic, out string italicFile)) + { + return new FontResolverInfo(Path.GetFileName(italicFile)); + } } - catch (System.Exception e) + + if (fontFamily.FontFiles.TryGetValue(XFontStyle.Regular, out string regularFile)) { - System.Console.Error.WriteLine(e); + return new FontResolverInfo(Path.GetFileName(regularFile)); } - } + return new FontResolverInfo(Path.GetFileName(fontFamily.FontFiles.First().Value)); + } - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private static FontFamilyModel DeserializeFontFamily(string fontFamilyName, IEnumerable fontList) + if (NullIfFontNotFound) + { + return null; + } + + var firstFontPath = installedFonts.First().FontFiles.First().Value; + return new FontResolverInfo(Path.GetFileName(firstFontPath)); + } + + private static string[] LoadOSXFonts() { - FontFamilyModel font = new FontFamilyModel { Name = fontFamilyName }; + var fontDirectory = "/Library/Fonts/"; + return Directory.GetFiles(fontDirectory, "*.ttf", SearchOption.AllDirectories); + } + private static string[] LoadWindowsFonts() + { + var fontPaths = new List(); - // there is only one font - if (fontList.Count() == 1) - font.FontFiles.Add(XFontStyle.Regular, fontList.First().Path); - else + var systemFontPath = @"%SystemRoot%\Fonts"; + var localAppDataFontPath = @"%LOCALAPPDATA%\Microsoft\Windows\Fonts"; + + var systemFontDirectory = Environment.ExpandEnvironmentVariables(systemFontPath); + var systemFontPaths = Directory.GetFiles(systemFontDirectory, "*.ttf", SearchOption.AllDirectories); + fontPaths.AddRange(systemFontPaths); + + var appdataFontDirectory = Environment.ExpandEnvironmentVariables(localAppDataFontPath); + if (Directory.Exists(appdataFontDirectory)) { - foreach (FontFileInfo info in fontList) - { - XFontStyle style = info.GuessFontStyle(); - if (!font.FontFiles.ContainsKey(style)) - font.FontFiles.Add(style, info.Path); - } + var appdataFontPaths = Directory.GetFiles(appdataFontDirectory, "*.ttf", SearchOption.AllDirectories); + fontPaths.AddRange(appdataFontPaths); } - return font; + return fontPaths.ToArray(); } - - public virtual byte[] GetFont(string faceFileName) + private static void SetupFontFiles() { - using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) + var fontFiles = new List(); + foreach (var fontFilePath in installedFontFilePaths) { - string ttfPathFile = ""; try { - ttfPathFile = SSupportedFonts.ToList().First(x => x.ToLower().Contains( - System.IO.Path.GetFileName(faceFileName).ToLower()) - ); - - using (System.IO.Stream ttf = System.IO.File.OpenRead(ttfPathFile)) - { - ttf.CopyTo(ms); - ms.Position = 0; - return ms.ToArray(); - } + var fontFileInfo = FontFileInfo.Load(fontFilePath); + fontFiles.Add(fontFileInfo); } - catch (System.Exception e) + catch (Exception e) { - System.Console.WriteLine(e); - throw new System.Exception("No Font File Found - " + faceFileName + " - " + ttfPathFile); + Console.Error.WriteLine(e); } } - } - - public bool NullIfFontNotFound { get; set; } = false; - public virtual FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) - { - if (InstalledFonts.Count == 0) - throw new System.IO.FileNotFoundException("No Fonts installed on this device!"); - - if (InstalledFonts.TryGetValue(familyName.ToLower(), out FontFamilyModel family)) + foreach (var familyGroup in fontFiles.GroupBy(info => info.FamilyName)) { - if (isBold && isItalic) + try { - if (family.FontFiles.TryGetValue(XFontStyle.BoldItalic, out string boldItalicFile)) - return new FontResolverInfo(System.IO.Path.GetFileName(boldItalicFile)); + var familyName = familyGroup.Key; + FontFamilyModel family = DeserializeFontFamily(familyName, familyGroup); + installedFonts.Add(family); } - else if (isBold) + catch (Exception e) { - if (family.FontFiles.TryGetValue(XFontStyle.Bold, out string boldFile)) - return new FontResolverInfo(System.IO.Path.GetFileName(boldFile)); + Console.Error.WriteLine(e); } - else if (isItalic) + } + } + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private static FontFamilyModel DeserializeFontFamily(string familyName, IEnumerable fontFiles) + { + var fontFamilyModel = new FontFamilyModel + { + Name = familyName + }; + + if (fontFiles.Count() == 1) + { + fontFamilyModel.FontFiles.Add(XFontStyle.Regular, fontFiles.First().Path); + } + else + { + foreach (var info in fontFiles) { - if (family.FontFiles.TryGetValue(XFontStyle.Italic, out string italicFile)) - return new FontResolverInfo(System.IO.Path.GetFileName(italicFile)); + var style = info.GuessFontStyle(); + if (!fontFamilyModel.FontFiles.ContainsKey(style)) + { + fontFamilyModel.FontFiles.Add(style, info.Path); + } } - - if (family.FontFiles.TryGetValue(XFontStyle.Regular, out string regularFile)) - return new FontResolverInfo(System.IO.Path.GetFileName(regularFile)); - - return new FontResolverInfo(System.IO.Path.GetFileName(family.FontFiles.First().Value)); } - if (NullIfFontNotFound) - return null; - - string ttfFile = InstalledFonts.First().Value.FontFiles.First().Value; - return new FontResolverInfo(System.IO.Path.GetFileName(ttfFile)); + return fontFamilyModel; } } } From 91c7cd50d0c5b36fc41269889270bf8004491fcb Mon Sep 17 00:00:00 2001 From: MichelMichels Date: Wed, 8 Jun 2022 21:31:15 +0200 Subject: [PATCH 2/2] Removed static constructor --- PdfSharpCore/Utils/FontResolver.cs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/PdfSharpCore/Utils/FontResolver.cs b/PdfSharpCore/Utils/FontResolver.cs index 586c87e4..2494619d 100644 --- a/PdfSharpCore/Utils/FontResolver.cs +++ b/PdfSharpCore/Utils/FontResolver.cs @@ -12,11 +12,18 @@ namespace PdfSharpCore.Utils { public class FontResolver : IFontResolver { + private static bool _isInitialized; + private static readonly List installedFonts = new List(); - private static readonly string[] installedFontFilePaths; + private static string[] installedFontFilePaths; - static FontResolver() + public FontResolver() { + if(_isInitialized) + { + return; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { installedFontFilePaths = LoadWindowsFonts(); @@ -35,6 +42,8 @@ static FontResolver() } SetupFontFiles(); + + _isInitialized = true; } public string DefaultFontName => "Arial"; @@ -44,16 +53,16 @@ public virtual byte[] GetFont(string faceName) { using (var ms = new MemoryStream()) { - string ttfPathFile = ""; + var fontFilepath = string.Empty; try { - ttfPathFile = installedFontFilePaths - .ToList() - .First(x => x.ToLower().Contains(Path.GetFileName(faceName).ToLower())); + var fontFileName = Path.GetFileName(faceName).ToLower(); + fontFilepath = installedFontFilePaths + .First(x => x.ToLower().Contains(fontFileName)); - if (File.Exists(ttfPathFile)) + if (File.Exists(fontFilepath)) { - using (var fileStream = File.OpenRead(ttfPathFile)) + using (var fileStream = File.OpenRead(fontFilepath)) { fileStream.CopyTo(ms); ms.Position = 0; @@ -62,12 +71,12 @@ public virtual byte[] GetFont(string faceName) } else { - throw new FileNotFoundException(ttfPathFile); + throw new FileNotFoundException(fontFilepath); } } catch (Exception) { - throw new Exception($"Font file with name {faceName} and path {ttfPathFile} not found."); + throw new Exception($"Font file with name {faceName} and path {fontFilepath} not found."); } } }