vendor: import KillerPDF 1.7.5 source (GPL-3.0) as the base for MMD PDF

This commit is contained in:
2026-08-27 06:58:22 +02:00
commit 532485a830
577 changed files with 149058 additions and 0 deletions
+247
View File
@@ -0,0 +1,247 @@
using System.Linq;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using PdfSharpCore.Internal;
using PdfSharpCore.Drawing;
using PdfSharpCore.Fonts;
using SixLabors.Fonts;
namespace PdfSharpCore.Utils
{
public class FontResolver
: IFontResolver
{
public string DefaultFontName => "Arial";
private static readonly Dictionary<string, FontFamilyModel> InstalledFonts = new Dictionary<string, FontFamilyModel>();
private static readonly string[] SSupportedFonts;
public FontResolver()
{
}
static FontResolver()
{
string fontDir;
bool isOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX);
if (isOSX)
{
fontDir = "/Library/Fonts/";
SSupportedFonts = System.IO.Directory.GetFiles(fontDir, "*.ttf", System.IO.SearchOption.AllDirectories);
SetupFontsFiles(SSupportedFonts);
return;
}
bool isLinux = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux);
if (isLinux)
{
SSupportedFonts = LinuxSystemFontResolver.Resolve();
SetupFontsFiles(SSupportedFonts);
return;
}
bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
if (isWindows)
{
fontDir = System.Environment.ExpandEnvironmentVariables(@"%SystemRoot%\Fonts");
var fontPaths = new List<string>();
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;
}
throw new System.NotImplementedException("FontResolver not implemented for this platform (PdfSharpCore.Utils.FontResolver.cs).");
}
private readonly struct FontFileInfo
{
private FontFileInfo(string path, FontDescription fontDescription)
{
this.Path = path;
this.FontDescription = fontDescription;
}
public string Path { get; }
public FontDescription FontDescription { get; }
public string FamilyName => this.FontDescription.FontFamilyInvariantCulture;
public XFontStyle GuessFontStyle()
{
switch (this.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)
{
FontDescription fontDescription = FontDescription.LoadDescription(path);
return new FontFileInfo(path, fontDescription);
}
}
public static void SetupFontsFiles(string[] sSupportedFonts)
{
List<FontFileInfo> tempFontInfoList = new List<FontFileInfo>();
foreach (string fontPathFile in sSupportedFonts)
{
try
{
FontFileInfo fontInfo = FontFileInfo.Load(fontPathFile);
Debug.WriteLine(fontPathFile);
tempFontInfoList.Add(fontInfo);
}
// The e variable only exists under DEBUG: naming it unconditionally leaves Release
// builds with CS0168 (declared but never used), which is what the split clause avoids.
#if DEBUG
catch (System.Exception e)
{
System.Console.Error.WriteLine(e);
}
#else
catch (System.Exception)
{
}
#endif
}
// Deserialize all font families
foreach (IGrouping<string, FontFileInfo> familyGroup in tempFontInfoList.GroupBy(info => info.FamilyName))
try
{
string familyName = familyGroup.Key;
FontFamilyModel family = DeserializeFontFamily(familyName, familyGroup);
InstalledFonts.Add(familyName.ToLower(), family);
}
// Split clause for the same CS0168 reason as the load loop above.
#if DEBUG
catch (System.Exception e)
{
System.Console.Error.WriteLine(e);
}
#else
catch (System.Exception)
{
}
#endif
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private static FontFamilyModel DeserializeFontFamily(string fontFamilyName, IEnumerable<FontFileInfo> fontList)
{
FontFamilyModel font = new FontFamilyModel { Name = fontFamilyName };
// there is only one font
if (fontList.Count() == 1)
font.FontFiles.Add(XFontStyle.Regular, fontList.First().Path);
else
{
foreach (FontFileInfo info in fontList)
{
XFontStyle style = info.GuessFontStyle();
if (!font.FontFiles.ContainsKey(style))
font.FontFiles.Add(style, info.Path);
}
}
return font;
}
public virtual byte[] GetFont(string faceFileName)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
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();
}
}
catch (System.Exception e)
{
System.Console.WriteLine(e);
throw new System.Exception("No Font File Found - " + faceFileName + " - " + ttfPathFile);
}
}
}
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))
{
if (isBold && isItalic)
{
if (family.FontFiles.TryGetValue(XFontStyle.BoldItalic, out string boldItalicFile))
return new FontResolverInfo(System.IO.Path.GetFileName(boldItalicFile));
}
else if (isBold)
{
if (family.FontFiles.TryGetValue(XFontStyle.Bold, out string boldFile))
return new FontResolverInfo(System.IO.Path.GetFileName(boldFile));
}
else if (isItalic)
{
if (family.FontFiles.TryGetValue(XFontStyle.Italic, out string italicFile))
return new FontResolverInfo(System.IO.Path.GetFileName(italicFile));
}
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));
}
}
}
+77
View File
@@ -0,0 +1,77 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using MigraDocCore.DocumentObjectModel.MigraDoc.DocumentObjectModel.Shapes;
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
namespace PdfSharpCore.Utils
{
public class ImageSharpImageSource<TPixel> : ImageSource where TPixel : unmanaged, IPixel<TPixel>
{
public static IImageSource FromImageSharpImage(Image<TPixel> image, IImageFormat imgFormat, int? quality = 75)
{
var _path = "*" + Guid.NewGuid().ToString("B");
return new ImageSharpImageSourceImpl<TPixel>(_path, image, (int)quality, imgFormat is PngFormat);
}
protected override IImageSource FromBinaryImpl(string name, Func<byte[]> imageSource, int? quality = 75)
{
var image = Image.Load<TPixel>(imageSource.Invoke(), out IImageFormat imgFormat);
return new ImageSharpImageSourceImpl<TPixel>(name, image, (int)quality, imgFormat is PngFormat);
}
protected override IImageSource FromFileImpl(string path, int? quality = 75)
{
var image = Image.Load<TPixel>(path, out IImageFormat imgFormat);
return new ImageSharpImageSourceImpl<TPixel>(path, image, (int) quality, imgFormat is PngFormat);
}
protected override IImageSource FromStreamImpl(string name, Func<Stream> imageStream, int? quality = 75)
{
using (var stream = imageStream.Invoke())
{
var image = Image.Load<TPixel>(stream, out IImageFormat imgFormat);
return new ImageSharpImageSourceImpl<TPixel>(name, image, (int)quality, imgFormat is PngFormat);
}
}
private class ImageSharpImageSourceImpl<TPixel2> : IImageSource where TPixel2 : unmanaged, IPixel<TPixel2>
{
private Image<TPixel2> Image { get; }
private readonly int _quality;
public int Width => Image.Width;
public int Height => Image.Height;
public string Name { get; }
public bool Transparent { get; internal set; }
public ImageSharpImageSourceImpl(string name, Image<TPixel2> image, int quality, bool isTransparent)
{
Name = name;
Image = image;
_quality = quality;
Transparent = isTransparent;
}
public void SaveAsJpeg(MemoryStream ms)
{
Image.SaveAsJpeg(ms, new JpegEncoder() { Quality = this._quality });
}
public void Dispose()
{
Image.Dispose();
}
public void SaveAsPdfBitmap(MemoryStream ms)
{
BmpEncoder bmp = new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 };
Image.Save(ms, bmp);
}
}
}
}
@@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace PdfSharpCore.Utils
{
public static class LinuxSystemFontResolver
{
const string libfontconfig = "libfontconfig.so.1";
[DllImport(libfontconfig)] private static extern IntPtr FcInitLoadConfigAndFonts();
static readonly Lazy<IntPtr> fcConfig = new Lazy<IntPtr>(FcInitLoadConfigAndFonts);
[DllImport(libfontconfig)] public static extern FcPatternHandle FcPatternCreate();
[DllImport(libfontconfig)] public static extern int FcPatternGetString(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string obj, int n, ref IntPtr s);
[DllImport(libfontconfig)] public static extern void FcPatternDestroy(IntPtr pattern);
public class FcPatternHandle : SafeHandle
{
FcPatternHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid => this.handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
FcPatternDestroy(this.handle);
return true;
}
}
[DllImport(libfontconfig)] public static extern FcObjectSetHandle FcObjectSetCreate();
[DllImport(libfontconfig)] public static extern int FcObjectSetAdd(FcObjectSetHandle os, [MarshalAs(UnmanagedType.LPStr)] string obj);
[DllImport(libfontconfig)] public static extern void FcObjectSetDestroy(IntPtr os);
public class FcObjectSetHandle : SafeHandle
{
FcObjectSetHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid => this.handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
FcObjectSetDestroy(this.handle);
return true;
}
public static FcObjectSetHandle Create(params string[] objs)
{
var os = FcObjectSetCreate();
foreach (var obj in objs)
FcObjectSetAdd(os, obj);
FcObjectSetAdd(os, "");
return os;
}
}
[DllImport(libfontconfig)] public static extern FcFontSetHandle FcFontList(IntPtr config, FcPatternHandle pattern, FcObjectSetHandle os);
[DllImport(libfontconfig)] public static extern void FcFontSetDestroy(IntPtr fs);
public struct FcFontSet
{
public int nfont;
public int sfont;
public IntPtr fonts;
}
public class FcFontSetHandle : SafeHandle
{
FcFontSetHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid => this.handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
FcFontSetDestroy(this.handle);
return true;
}
public FcFontSet Read()
{
return Marshal.PtrToStructure<FcFontSet>(this.handle);
}
}
static string GetString(IntPtr handle, string obj)
{
var ptr = IntPtr.Zero;
var result = FcPatternGetString(handle, obj, 0, ref ptr);
if (result == 0)
return Marshal.PtrToStringAnsi(ptr);
else
return null;
}
static IEnumerable<string> ResolveFontConfig()
{
var config = fcConfig.Value;
using (var pattern = FcPatternCreate())
using (var os = FcObjectSetHandle.Create("family", "style", "file"))
using (var fs = FcFontList(config, pattern, os))
{
var fset = fs.Read();
for (int index = 0; index < fset.nfont; index++)
{
var font = Marshal.ReadIntPtr(fset.fonts, index * Marshal.SizeOf<IntPtr>());
var family = GetString(font, "family");
var style = GetString(font, "style");
var file = GetString(font, "file");
if (family is null || style is null || file is null)
continue;
yield return file;
}
}
}
public static string[] Resolve()
{
try
{
return ResolveFontConfig().Where(x => x.EndsWith(".ttf", StringComparison.OrdinalIgnoreCase)).ToArray();
}
// The ex variable only exists under DEBUG: naming it unconditionally leaves Release
// builds with CS0168 (declared but never used), which is what the split clause avoids.
#if DEBUG
catch(Exception ex)
{
Console.Error.WriteLine(ex.ToString());
return ResolveFallback().Where(x => x.EndsWith(".ttf", StringComparison.OrdinalIgnoreCase)).ToArray();
}
#else
catch(Exception)
{
return ResolveFallback().Where(x => x.EndsWith(".ttf", StringComparison.OrdinalIgnoreCase)).ToArray();
}
#endif
}
static IEnumerable<string> ResolveFallback()
{
var fontList = new List<string>();
void AddFontsToFontList(string path)
{
if (!Directory.Exists(path))
return;
foreach (string subDir in Directory.EnumerateDirectories(path, "*", SearchOption.AllDirectories))
fontList.AddRange(Directory.EnumerateFiles(subDir, "*", SearchOption.AllDirectories));
}
var hs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var path in SearchPaths())
{
if (hs.Contains(path))
continue;
hs.Add(path);
AddFontsToFontList(path);
}
return fontList.ToArray();
}
static IEnumerable<string> SearchPaths()
{
var dirs = new List<string>();
try
{
Regex confRegex = new Regex("<dir>(?<dir>.*)</dir>", RegexOptions.Compiled);
using (var reader = new StreamReader(File.OpenRead("/etc/fonts/fonts.conf")))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Match match = confRegex.Match(line);
if (!match.Success)
continue;
string path = match.Groups["dir"].Value.Trim();
if (path.StartsWith("~"))
{
path = Environment.GetEnvironmentVariable("HOME") + path.Substring(1);
}
dirs.Add(path);
} // Whend
} // End Using reader
}
// Split clause for the same CS0168 reason as Resolve() above.
#if DEBUG
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
Console.Error.WriteLine(ex.StackTrace);
}
#else
catch (Exception)
{
}
#endif
dirs.Add("/usr/share/fonts");
dirs.Add("/usr/local/share/fonts");
dirs.Add(Environment.GetEnvironmentVariable("HOME") + "/.fonts");
return dirs;
}
}
}