Skip to content

Commit 2b72273

Browse files
committed
Update PDFsharp to 6.2.4 and add FontResolver
1 parent 2283fc5 commit 2b72273

22 files changed

Lines changed: 1345 additions & 23 deletions

‎Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs‎

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212

1313
using PdfSharp.Drawing;
1414
using PdfSharp.Pdf;
15-
using System.Drawing;
16-
using System.Drawing.Text;
15+
using System;
1716
using System.IO;
1817
using TheArtOfDev.HtmlRenderer.Adapters;
1918
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
19+
using TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution;
2020
using TheArtOfDev.HtmlRenderer.PdfSharp.Utilities;
2121

2222
namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters
@@ -41,14 +41,16 @@ internal sealed class PdfSharpAdapter : RAdapter
4141
/// </summary>
4242
private PdfSharpAdapter()
4343
{
44+
var fontResolver = FontResolver.Register();
45+
4446
AddFontFamilyMapping("monospace", "Courier New");
4547
AddFontFamilyMapping("Helvetica", "Arial");
4648

47-
var families = new InstalledFontCollection();
48-
49-
foreach (var family in families.Families)
49+
var fontFamilies = fontResolver.DiscoverFontFamilies();
50+
51+
foreach (var fontFamily in fontFamilies)
5052
{
51-
AddFontFamily(new FontFamilyAdapter(new XFontFamily(family.Name)));
53+
AddFontFamily(new FontFamilyAdapter(new XFontFamily(fontFamily)));
5254
}
5355
}
5456

@@ -64,8 +66,23 @@ protected override RColor GetColorInt(string colorName)
6466
{
6567
try
6668
{
67-
var color = Color.FromKnownColor((KnownColor)System.Enum.Parse(typeof(KnownColor), colorName, true));
68-
return Utils.Convert(color);
69+
var colorResourceManager = new XColorResourceManager();
70+
71+
var knownColors = XColorResourceManager.GetKnownColors(true);
72+
73+
foreach (var knownColor in knownColors)
74+
{
75+
var name = colorResourceManager.ToColorName(knownColor);
76+
if (!string.Equals(name, colorName, StringComparison.OrdinalIgnoreCase))
77+
{
78+
continue;
79+
}
80+
81+
var xColor = XColor.FromKnownColor(knownColor);
82+
return xColor.IsEmpty ? RColor.Empty : Utils.Convert(xColor);
83+
}
84+
85+
return RColor.Empty;
6986
}
7087
catch
7188
{
@@ -119,14 +136,14 @@ protected override RImage ImageFromStreamInt(Stream memoryStream)
119136

120137
protected override RFont CreateFontInt(string family, double size, RFontStyle style)
121138
{
122-
var fontStyle = (XFontStyle)((int)style);
139+
var fontStyle = Utils.Convert(style);
123140
var xFont = new XFont(family, size, fontStyle, new XPdfFontOptions(PdfFontEncoding.Unicode));
124141
return new FontAdapter(xFont);
125142
}
126143

127144
protected override RFont CreateFontInt(RFontFamily family, double size, RFontStyle style)
128145
{
129-
var fontStyle = (XFontStyle)((int)style);
146+
var fontStyle = Utils.Convert(style);
130147
var xFont = new XFont(((FontFamilyAdapter)family).FontFamily.Name, size, fontStyle, new XPdfFontOptions(PdfFontEncoding.Unicode));
131148
return new FontAdapter(xFont);
132149
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
using TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution.Parsing;
7+
8+
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution.Discovery
9+
{
10+
internal static class DirectoryFontDiscovery
11+
{
12+
public static List<FontInfo> DiscoverFontFilesFromDirectories(List<string> customFontDirectories)
13+
{
14+
var fontDirectories = new List<string>();
15+
16+
// Add platform-specific directories
17+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
18+
{
19+
fontDirectories.Add(Environment.GetFolderPath(Environment.SpecialFolder.Fonts));
20+
}
21+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
22+
{
23+
fontDirectories.AddRange(new[]
24+
{
25+
"/usr/share/fonts",
26+
"/usr/local/share/fonts",
27+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".fonts"),
28+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local/share/fonts")
29+
});
30+
}
31+
32+
// Add custom directories
33+
fontDirectories.AddRange(customFontDirectories);
34+
35+
return DiscoverFontFilesInDirectories(fontDirectories);
36+
}
37+
38+
public static List<FontInfo> DiscoverFontFilesInDirectories(List<string> directories)
39+
{
40+
var fontInfos = new List<FontInfo>();
41+
42+
foreach (var directory in directories)
43+
{
44+
if (!Directory.Exists(directory))
45+
{
46+
continue;
47+
}
48+
49+
try
50+
{
51+
fontInfos.AddRange(FontDiscoveryService.SupportedFontExtensions.SelectMany(e => Directory.GetFiles(directory, $"*{e}", SearchOption.AllDirectories))
52+
.Select(f => new FontInfo
53+
{
54+
Name = Path.GetFileNameWithoutExtension(f),
55+
FilePath = f
56+
}));
57+
}
58+
catch
59+
{
60+
// Silently continue on directory access errors
61+
}
62+
}
63+
64+
return fontInfos;
65+
}
66+
}
67+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Collections.Generic;
2+
using System.Runtime.InteropServices;
3+
4+
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution.Discovery
5+
{
6+
internal class FontDiscoveryService
7+
{
8+
private readonly List<string> _customFontDirectories = new List<string>();
9+
10+
public static string[] SupportedFontExtensions { get; } = new[] { ".ttf", ".otf" };
11+
12+
public List<FontInfo> DiscoverFontInfos()
13+
{
14+
var fontInfos = new List<FontInfo>();
15+
16+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
17+
{
18+
fontInfos = WindowsFontDiscovery.DiscoverFontInfos();
19+
}
20+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
21+
{
22+
fontInfos = LinuxFontDiscovery.DiscoverFontFiles();
23+
}
24+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
25+
{
26+
fontInfos = MacOsFontDiscovery.DiscoverFontFiles();
27+
}
28+
29+
// Always use directory scan as fallback
30+
fontInfos.AddRange(DirectoryFontDiscovery.DiscoverFontFilesFromDirectories(_customFontDirectories));
31+
32+
return fontInfos;
33+
}
34+
35+
public void RegisterCustomFontDirectory(string fontDirectory)
36+
{
37+
if (_customFontDirectories.Contains(fontDirectory))
38+
{
39+
return;
40+
}
41+
42+
_customFontDirectories.Add(fontDirectory);
43+
}
44+
}
45+
}
46+
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
7+
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution.Discovery
8+
{
9+
internal static class LinuxFontDiscovery
10+
{
11+
public static List<FontInfo> DiscoverFontFiles()
12+
{
13+
var fontInfos = new List<FontInfo>();
14+
15+
if (!IsFontConfigAvailable())
16+
{
17+
return fontInfos;
18+
}
19+
20+
try
21+
{
22+
var startInfo = new ProcessStartInfo
23+
{
24+
FileName = "fc-list",
25+
Arguments = ":",
26+
RedirectStandardOutput = true,
27+
RedirectStandardError = true,
28+
UseShellExecute = false,
29+
CreateNoWindow = true
30+
};
31+
32+
using (var process = Process.Start(startInfo))
33+
{
34+
if (process == null)
35+
{
36+
return fontInfos;
37+
}
38+
39+
var output = process.StandardOutput.ReadToEnd();
40+
process.WaitForExit();
41+
42+
if (process.ExitCode != 0 || string.IsNullOrWhiteSpace(output))
43+
{
44+
return fontInfos;
45+
}
46+
47+
var lines = output.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
48+
foreach (var line in lines)
49+
{
50+
var fontFamilies = DiscoverFontFile(line);
51+
52+
if (fontFamilies.Count > 0)
53+
{
54+
fontInfos.AddRange(fontFamilies);
55+
}
56+
}
57+
}
58+
}
59+
catch
60+
{
61+
// Silently continue on errors
62+
}
63+
64+
return fontInfos;
65+
}
66+
67+
private static bool IsFontConfigAvailable()
68+
{
69+
try
70+
{
71+
var startInfo = new ProcessStartInfo
72+
{
73+
FileName = "which",
74+
Arguments = "fc-list",
75+
RedirectStandardOutput = true,
76+
RedirectStandardError = true,
77+
UseShellExecute = false,
78+
CreateNoWindow = true
79+
};
80+
81+
using (var process = Process.Start(startInfo))
82+
{
83+
if (process == null)
84+
{
85+
return false;
86+
}
87+
88+
process.WaitForExit();
89+
return process.ExitCode == 0;
90+
}
91+
}
92+
catch
93+
{
94+
return false;
95+
}
96+
}
97+
98+
private static List<FontInfo> DiscoverFontFile(string line)
99+
{
100+
var fonts = new List<FontInfo>();
101+
102+
// Parse fc-list output: "path: family1,family2:style=style"
103+
var parts = line.Split(new[] { ':' }, 2, StringSplitOptions.None);
104+
if (parts.Length < 2)
105+
{
106+
return fonts;
107+
}
108+
109+
var fontFilePath = parts[0].Trim();
110+
var families = parts[1].Trim().Split(',');
111+
112+
if (!File.Exists(fontFilePath) ||
113+
!FontDiscoveryService.SupportedFontExtensions.Any(e => fontFilePath.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
114+
{
115+
return fonts;
116+
}
117+
118+
foreach (var family in families)
119+
{
120+
var familyName = family.Trim();
121+
if (!string.IsNullOrEmpty(familyName) && File.Exists(fontFilePath))
122+
{
123+
fonts.Add(new FontInfo { Name = familyName, FilePath = fontFilePath });
124+
}
125+
}
126+
return fonts;
127+
}
128+
}
129+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution.Discovery
6+
{
7+
internal static class MacOsFontDiscovery
8+
{
9+
public static List<FontInfo> DiscoverFontFiles()
10+
{
11+
var fontDirectories = new List<string>
12+
{
13+
"/System/Library/Fonts",
14+
"/Library/Fonts",
15+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library/Fonts")
16+
};
17+
18+
return DirectoryFontDiscovery.DiscoverFontFilesInDirectories(fontDirectories);
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)