Code refactorization + added support for multi typeface font types
This commit is contained in:
Родитель
8a64abaac2
Коммит
8db283d559
|
@ -13,11 +13,11 @@ namespace QuestPDF.Examples
|
|||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
FontManager.RegisterFontType(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 100)
|
||||
.PageSize(400, 200)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ namespace QuestPDF.Examples
|
|||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("Libre Barcode 39").Size(64));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
|
@ -8,27 +9,39 @@ namespace QuestPDF.Drawing
|
|||
{
|
||||
public static class FontManager
|
||||
{
|
||||
private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new ConcurrentDictionary<string, FontStyleSet>();
|
||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
|
||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
|
||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new();
|
||||
private static ConcurrentDictionary<string, SKPaint> Paints = new();
|
||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new();
|
||||
|
||||
private static void RegisterFontType(string fontName, SKTypeface typeface)
|
||||
private static void RegisterFontType(SKData fontData, string? customName = null)
|
||||
{
|
||||
FontStyleSet set = StyleSets.GetOrAdd(fontName, _ => new FontStyleSet());
|
||||
set.Add(typeface);
|
||||
foreach (var index in Enumerable.Range(0, 256))
|
||||
{
|
||||
var typeface = SKTypeface.FromData(fontData, index);
|
||||
|
||||
if (typeface == null)
|
||||
break;
|
||||
|
||||
var typefaceName = customName ?? typeface.FamilyName;
|
||||
|
||||
var fontStyleSet = StyleSets.GetOrAdd(typefaceName, _ => new FontStyleSet());
|
||||
fontStyleSet.Add(typeface);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFontType(Stream stream) overload.")]
|
||||
public static void RegisterFontType(string fontName, Stream stream)
|
||||
{
|
||||
SKTypeface typeface = SKTypeface.FromStream(stream);
|
||||
RegisterFontType(fontName, typeface);
|
||||
using var fontData = SKData.Create(stream);
|
||||
RegisterFontType(fontData);
|
||||
RegisterFontType(fontData, customName: fontName);
|
||||
}
|
||||
|
||||
public static void RegisterFontType(Stream stream)
|
||||
{
|
||||
SKTypeface typeface = SKTypeface.FromStream(stream);
|
||||
RegisterFontType(typeface.FamilyName, typeface);
|
||||
using var fontData = SKData.Create(stream);
|
||||
RegisterFontType(fontData);
|
||||
}
|
||||
|
||||
internal static SKPaint ColorToPaint(this string color)
|
||||
|
@ -54,29 +67,23 @@ namespace QuestPDF.Drawing
|
|||
{
|
||||
Color = SKColor.Parse(style.Color),
|
||||
Typeface = GetTypeface(style),
|
||||
TextSize = (style.Size ?? 12),
|
||||
TextSize = style.Size ?? 12,
|
||||
TextEncoding = SKTextEncoding.Utf32
|
||||
};
|
||||
}
|
||||
|
||||
static SKTypeface GetTypeface(TextStyle style)
|
||||
{
|
||||
SKFontStyleWeight weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
|
||||
SKFontStyleWidth width = SKFontStyleWidth.Normal;
|
||||
SKFontStyleSlant slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
|
||||
var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
|
||||
SKFontStyle skFontStyle = new SKFontStyle(weight, width, slant);
|
||||
var skFontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
|
||||
|
||||
FontStyleSet set;
|
||||
if (StyleSets.TryGetValue(style.FontType, out set))
|
||||
{
|
||||
if (StyleSets.TryGetValue(style.FontType, out var set))
|
||||
return set.Match(skFontStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SKTypeface.FromFamilyName(style.FontType, skFontStyle)
|
||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
||||
}
|
||||
|
||||
return SKTypeface.FromFamilyName(style.FontType, skFontStyle)
|
||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found. Please consider installing the font file on your system or loading it from a file using the FontManager.RegisterFontType() static method.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,18 +7,18 @@ namespace QuestPDF.Drawing
|
|||
{
|
||||
internal class FontStyleSet
|
||||
{
|
||||
private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles = new ConcurrentDictionary<SKFontStyle, SKTypeface>();
|
||||
private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles { get; } = new();
|
||||
|
||||
public void Add(SKTypeface typeface)
|
||||
{
|
||||
SKFontStyle style = typeface.FontStyle;
|
||||
Styles.AddOrUpdate(style, (_) => typeface, (_, _) => typeface);
|
||||
var style = typeface.FontStyle;
|
||||
Styles.AddOrUpdate(style, _ => typeface, (_, _) => typeface);
|
||||
}
|
||||
|
||||
public SKTypeface Match(SKFontStyle target)
|
||||
public SKTypeface? Match(SKFontStyle target)
|
||||
{
|
||||
SKFontStyle bestStyle = null;
|
||||
SKTypeface bestTypeface = null;
|
||||
SKFontStyle? bestStyle = null;
|
||||
SKTypeface? bestTypeface = null;
|
||||
|
||||
foreach (var entry in Styles)
|
||||
{
|
||||
|
@ -40,40 +40,55 @@ namespace QuestPDF.Drawing
|
|||
};
|
||||
|
||||
// Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
|
||||
internal static bool IsBetterMatch(SKFontStyle target, SKFontStyle a, SKFontStyle b)
|
||||
internal static bool IsBetterMatch(SKFontStyle? target, SKFontStyle? a, SKFontStyle? b)
|
||||
{
|
||||
// A font is better than no font
|
||||
if (b == null) return true;
|
||||
if (a == null) return false;
|
||||
if (b == null)
|
||||
return true;
|
||||
|
||||
if (a == null)
|
||||
return false;
|
||||
|
||||
// First check font width
|
||||
// For normal and condensed widths prefer smaller widths
|
||||
// For expanded widths prefer larger widths
|
||||
if (target.Width <= (int)SKFontStyleWidth.Normal)
|
||||
{
|
||||
if (a.Width <= target.Width && b.Width > target.Width) return true;
|
||||
if (a.Width > target.Width && b.Width <= target.Width) return false;
|
||||
if (a.Width <= target.Width && b.Width > target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width > target.Width && b.Width <= target.Width)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Width >= target.Width && b.Width < target.Width) return true;
|
||||
if (a.Width < target.Width && b.Width >= target.Width) return false;
|
||||
if (a.Width >= target.Width && b.Width < target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width < target.Width && b.Width >= target.Width)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest match
|
||||
int widthDifferenceA = Math.Abs(a.Width - target.Width);
|
||||
int widthDifferenceB = Math.Abs(b.Width - target.Width);
|
||||
var widthDifferenceA = Math.Abs(a.Width - target.Width);
|
||||
var widthDifferenceB = Math.Abs(b.Width - target.Width);
|
||||
|
||||
if (widthDifferenceA < widthDifferenceB) return true;
|
||||
if (widthDifferenceB < widthDifferenceA) return false;
|
||||
if (widthDifferenceA < widthDifferenceB)
|
||||
return true;
|
||||
|
||||
if (widthDifferenceB < widthDifferenceA)
|
||||
return false;
|
||||
|
||||
// Prefer closest slant based on provided fallback list
|
||||
List<SKFontStyleSlant> slantFallback = SlantFallbacks[target.Slant];
|
||||
int slantIndexA = slantFallback.IndexOf(a.Slant);
|
||||
int slantIndexB = slantFallback.IndexOf(b.Slant);
|
||||
var slantFallback = SlantFallbacks[target.Slant];
|
||||
var slantIndexA = slantFallback.IndexOf(a.Slant);
|
||||
var slantIndexB = slantFallback.IndexOf(b.Slant);
|
||||
|
||||
if (slantIndexA < slantIndexB) return true;
|
||||
if (slantIndexB < slantIndexA) return false;
|
||||
if (slantIndexA < slantIndexB)
|
||||
return true;
|
||||
|
||||
if (slantIndexB < slantIndexA)
|
||||
return false;
|
||||
|
||||
// Check weight last
|
||||
// For thin (<400) weights, prefer thinner weights
|
||||
|
@ -83,24 +98,33 @@ namespace QuestPDF.Drawing
|
|||
|
||||
if (target.Weight >= 400 && target.Weight <= 500)
|
||||
{
|
||||
if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500)) return true;
|
||||
if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500)) return false;
|
||||
if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500))
|
||||
return true;
|
||||
|
||||
if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.Weight < 450)
|
||||
{
|
||||
if (a.Weight <= target.Weight && b.Weight > target.Weight) return true;
|
||||
if (a.Weight > target.Weight && b.Weight <= target.Weight) return false;
|
||||
if (a.Weight <= target.Weight && b.Weight > target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight > target.Weight && b.Weight <= target.Weight)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Weight >= target.Weight && b.Weight < target.Weight) return true;
|
||||
if (a.Weight < target.Weight && b.Weight >= target.Weight) return false;
|
||||
if (a.Weight >= target.Weight && b.Weight < target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight < target.Weight && b.Weight >= target.Weight)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest weight
|
||||
int weightDifferenceA = Math.Abs(a.Weight - target.Weight);
|
||||
int weightDifferenceB = Math.Abs(b.Weight - target.Weight);
|
||||
var weightDifferenceA = Math.Abs(a.Weight - target.Weight);
|
||||
var weightDifferenceB = Math.Abs(b.Weight - target.Weight);
|
||||
|
||||
return weightDifferenceA < weightDifferenceB;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче