using System.Reflection; using System.Text; namespace JobTrackerApi.Services; // Self-contained document fonts keep local-file PDF export and srcDoc preview on the same metrics. internal static class CvDocumentFonts { private static readonly Dictionary Styles = new() { ["Selawik"] = Faces("selawik", ("selawk.ttf", "400", "normal"), ("selawksb.ttf", "600", "normal"), ("selawkb.ttf", "700", "normal")), ["Arimo"] = Faces("arimo", ("Arimo-Regular.ttf", "400", "normal"), ("Arimo-Italic.ttf", "400", "italic"), ("Arimo-Bold.ttf", "700", "normal"), ("Arimo-BoldItalic.ttf", "700", "italic")), ["Gelasio"] = Faces("gelasio", ("Gelasio-Regular.ttf", "400", "normal"), ("Gelasio-Italic.ttf", "400", "italic"), ("Gelasio-SemiBold.ttf", "600", "normal"), ("Gelasio-SemiBoldItalic.ttf", "600", "italic"), ("Gelasio-Bold.ttf", "700", "normal"), ("Gelasio-BoldItalic.ttf", "700", "italic")), ["Roboto"] = Faces("roboto", ("Roboto-Regular.ttf", "400", "normal"), ("Roboto-Italic.ttf", "400", "italic"), ("Roboto-Medium.ttf", "500", "normal"), ("Roboto-MediumItalic.ttf", "500", "italic"), ("Roboto-Bold.ttf", "700", "normal"), ("Roboto-BoldItalic.ttf", "700", "italic")), ["Poppins"] = Faces("poppins", ("Poppins-Regular.ttf", "400", "normal"), ("Poppins-Italic.ttf", "400", "italic"), ("Poppins-SemiBold.ttf", "600", "normal"), ("Poppins-SemiBoldItalic.ttf", "600", "italic"), ("Poppins-Bold.ttf", "700", "normal"), ("Poppins-BoldItalic.ttf", "700", "italic")), ["Roboto Mono"] = Faces("robotomono", ("RobotoMono-Regular.ttf", "400", "normal"), ("RobotoMono-Italic.ttf", "400", "italic"), ("RobotoMono-Medium.ttf", "500", "normal"), ("RobotoMono-MediumItalic.ttf", "500", "italic"), ("RobotoMono-Bold.ttf", "700", "normal"), ("RobotoMono-BoldItalic.ttf", "700", "italic")), }; internal static string Family(string stack) => stack.TrimStart() switch { var s when s.StartsWith("Georgia", StringComparison.Ordinal) => "Gelasio", var s when s.StartsWith("'Roboto Mono'", StringComparison.Ordinal) || s.StartsWith("Consolas", StringComparison.Ordinal) => "Roboto Mono", var s when s.StartsWith("'Roboto'", StringComparison.Ordinal) => "Roboto", var s when s.StartsWith("'Poppins'", StringComparison.Ordinal) => "Poppins", var s when s.StartsWith("'Segoe UI'", StringComparison.Ordinal) => "Selawik", _ => "Arimo", }; internal static string Stack(string original) => $"'{Family(original)}', {original}"; internal static string Embed(params string[] stacks) => string.Concat(stacks.Select(Family).Distinct().Select(family => Styles[family])); private static string Faces(string family, params (string File, string Weight, string Style)[] faces) { var names = new Dictionary { ["selawik"] = "Selawik", ["arimo"] = "Arimo", ["gelasio"] = "Gelasio", ["roboto"] = "Roboto", ["poppins"] = "Poppins", ["robotomono"] = "Roboto Mono" }; var css = new StringBuilder(); using var license = new StreamReader(Resource(family + ".OFL.txt")); css.Append("/*\n").Append(license.ReadToEnd()).Append("\n*/\n"); foreach (var face in faces) { using var stream = Resource(face.File); using var bytes = new MemoryStream(); stream.CopyTo(bytes); css.Append($"@font-face{{font-family:'{names[family]}';font-style:{face.Style};font-weight:{face.Weight};font-display:block;src:url(data:font/ttf;base64,{Convert.ToBase64String(bytes.ToArray())}) format('truetype');}}\n"); } return css.ToString(); } private static Stream Resource(string name) => typeof(CvDocumentFonts).Assembly.GetManifestResourceStream("CvFonts." + name) ?? throw new InvalidOperationException("Required document font resource is missing: " + name); }