diff --git a/Tasks/StableFontResolver.cs b/Tasks/StableFontResolver.cs new file mode 100644 index 0000000..b0fa783 --- /dev/null +++ b/Tasks/StableFontResolver.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using PdfSharp.Fonts; +using System.Globalization; + +namespace Logof_Client; + +public class StableFontResolver : IFontResolver +{ + // family -> style -> bytes + private readonly Dictionary> _families = new(StringComparer.OrdinalIgnoreCase); + private readonly List _orderedFamilies = new(); + + public StableFontResolver(string fontsDirectory) + { + try + { + if (string.IsNullOrEmpty(fontsDirectory)) return; + if (!Directory.Exists(fontsDirectory)) return; + + var files = Directory.EnumerateFiles(fontsDirectory, "*.ttf"); + foreach (var f in files) + { + try + { + var baseName = Path.GetFileNameWithoutExtension(f); + if (string.IsNullOrEmpty(baseName)) continue; + + // Try to split family and style by hyphen or underscore + var family = baseName; + var style = "Regular"; + + var idx = baseName.IndexOf('-'); + if (idx < 0) idx = baseName.IndexOf('_'); + if (idx > 0) + { + family = baseName.Substring(0, idx); + style = baseName.Substring(idx + 1); + } + + // Normalize common style names + style = NormalizeStyle(style); + + var data = File.ReadAllBytes(f); + if (data == null || data.Length == 0) continue; + + if (!_families.TryGetValue(family, out var dict)) + { + dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + _families[family] = dict; + _orderedFamilies.Add(family); + } + + if (!dict.ContainsKey(style)) + dict[style] = data; + } + catch + { + // ignore individual font load errors + } + } + } + catch + { + // ignore resolver init errors + } + } + + private static string NormalizeStyle(string s) + { + if (string.IsNullOrEmpty(s)) return "Regular"; + s = s.ToLowerInvariant(); + if (s.Contains("bold") && s.Contains("italic")) return "BoldItalic"; + if (s.Contains("bold")) return "Bold"; + if (s.Contains("italic") || s.Contains("oblique")) return "Italic"; + if (s.Contains("regular") || s == "r") return "Regular"; + return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s); + } + + public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) + { + // If requested family exists, pick corresponding style if available + string familyToUse = null; + if (!string.IsNullOrEmpty(familyName) && _families.ContainsKey(familyName)) + familyToUse = familyName; + + if (familyToUse == null && _orderedFamilies.Count > 0) + familyToUse = _orderedFamilies[0]; + + if (familyToUse == null) + return new FontResolverInfo("Arial"); + + var style = "Regular"; + if (isBold && isItalic) style = "BoldItalic"; + else if (isBold) style = "Bold"; + else if (isItalic) style = "Italic"; + + // Face name format: Family#Style + return new FontResolverInfo($"{familyToUse}#{style}"); + } + + public byte[] GetFont(string faceName) + { + if (string.IsNullOrEmpty(faceName)) return null; + + // faceName expected as "Family#Style" or just "Family" + var family = faceName; + var style = "Regular"; + var idx = faceName.IndexOf('#'); + if (idx >= 0) + { + family = faceName.Substring(0, idx); + style = faceName.Substring(idx + 1); + } + + style = NormalizeStyle(style); + + if (_families.TryGetValue(family, out var dict)) + { + // Try exact style + if (dict.TryGetValue(style, out var data)) return data; + + // Fallback order + if (style != "Regular" && dict.TryGetValue("Regular", out data)) return data; + if (dict.TryGetValue("Bold", out data)) return data; + if (dict.TryGetValue("Italic", out data)) return data; + + // Any available + foreach (var kv in dict) return kv.Value; + } + + return null; + } +}