using System; using System.Text; namespace Quik.Media.Font { public readonly struct FontFace : IEquatable { public string Family { get; } public FontSlant Slant { get; } public FontWeight Weight { get; } public FontStretch Stretch { get; } public FontFace(string family, FontSlant slant, FontWeight weight, FontStretch stretch) { Family = family; Slant = slant; Weight = weight; Stretch = stretch; } public override string ToString() { StringBuilder builder = new StringBuilder(Family); if (Slant != FontSlant.Normal) { builder.Append(' '); builder.Append(Slant); } if (Stretch != FontStretch.Normal) { builder.Append(' '); builder.Append(Stretch); } if (Weight != FontWeight.Normal) { builder.Append(' '); builder.Append(Weight); } if (Slant == FontSlant.Normal && Stretch == FontStretch.Normal && Weight == FontWeight.Normal) { builder.Append(" Regular"); } return builder.ToString(); } public override int GetHashCode() { return HashCode.Combine(Family, Slant, Weight, Stretch); } public static bool operator==(FontFace a, FontFace b) { return (a.Slant == b.Slant) && (a.Weight == b.Weight) && (a.Stretch == b.Stretch) && (a.Family == a.Family); } public static bool operator!=(FontFace a, FontFace b) { return (a.Slant != b.Slant) || (a.Weight != b.Weight) || (a.Stretch != b.Stretch) || (a.Family != b.Family); } public bool Equals(FontFace other) { return this == other; } public override bool Equals(object? obj) { return (obj?.GetType() == typeof(FontFace)) && this == (FontFace)obj; } public static FontFace Parse(string family, string style) { FontSlant slant = FontSlant.Normal; FontWeight weight = FontWeight.Normal; FontStretch stretch = FontStretch.Normal; string[] tokens = style.Split(' '); foreach (string token in tokens) { /**/ if (TryParseSlant(token, out FontSlant xslant)) slant = xslant; else if (TryParseWeight(token, out FontWeight xweight)) weight = xweight; else if (TryParseStretch(token, out FontStretch xstretch)) stretch = xstretch; } return new FontFace(family, slant, weight, stretch); } public static FontFace Parse(string face) { StringBuilder family = new StringBuilder(); FontSlant slant = FontSlant.Normal; FontWeight weight = FontWeight.Normal; FontStretch stretch = FontStretch.Normal; string[] tokens = face.Split(' '); foreach (string token in tokens) { string xtoken = token.ToLower(); if (xtoken == "regular" || xtoken == "normal") { continue; } else if (TryParseSlant(xtoken, out FontSlant xslant)) slant = xslant; else if (TryParseWeight(xtoken, out FontWeight xweight)) weight = xweight; else if (TryParseStretch(xtoken, out FontStretch xstretch)) stretch = xstretch; else { family.Append(token); } } return new FontFace(family.ToString(), slant, weight, stretch); } /// /// Try to convert a token that represents a font slant into its enum. /// /// The token to interpret. /// The resulting slant. /// True if it matched any. public static bool TryParseSlant(string token, out FontSlant slant) { switch (token.ToLower()) { case "italic": slant = FontSlant.Italic; return true; case "oblique": slant = FontSlant.Oblique; return true; default: slant = FontSlant.Normal; return false; } } /// /// Try to convert a token that represents a font weight into its enum. /// /// The token to interpret. /// The resulting weight. /// True if it matched any. public static bool TryParseWeight(string token, out FontWeight weight) { switch (token.ToLower()) { case "thin": weight = FontWeight.Thin; return true; case "extralight": case "ultralight": weight = FontWeight._200; return true; case "light": case "demilight": case "semilight": weight = FontWeight._300; return true; case "demibold": case "semibold": weight = FontWeight._600; return true; case "bold": weight = FontWeight._700; return true; case "extrabold": case "ultrabold": weight = FontWeight._800; return true; case "heavy": case "extrablack": case "black": case "ultrablack": weight = FontWeight._900; return true; default: weight = FontWeight.Normal; return false; } } /// /// Try to convert a token that represents a font stretch into its enum. /// /// The token to interpret. /// The resulting stretch. /// True if it matched any. public static bool TryParseStretch(string token, out FontStretch stretch) { switch (token.ToLower()) { case "ultracondensed": stretch = FontStretch.UltraCondensed; return true; case "extracondensed": stretch = FontStretch.ExtraCondensed; return true; case "condensed": stretch = FontStretch.Condensed; return true; case "semicondensed": case "demicondensed": stretch = FontStretch.SemiCondensed; return true; case "semiexpanded": case "demiexpanded": stretch = FontStretch.SemiExpanded; return true; case "expanded": stretch = FontStretch.Expanded; return true; case "extraexpanded": stretch = FontStretch.ExtraExpanded; return true; case "ultraexpanded": stretch = FontStretch.UltraExpanded; return true; default: stretch = FontStretch.Normal; return false; } } } }