vendor: import KillerPDF 1.7.5 source (GPL-3.0) as the base for MMD PDF
This commit is contained in:
+247
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user