From ccb0c6ffe75c23865bbc2c70c76da27fca4a7920 Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Sun, 28 Apr 2024 13:45:46 +0300 Subject: [PATCH] Add new font search criterea. --- Quik/Media/Font/FontFace.cs | 240 +++++++++++++++++++++++++++++++++ Quik/Media/Font/FontSlant.cs | 9 ++ Quik/Media/Font/FontStretch.cs | 18 +++ Quik/Media/Font/FontWeight.cs | 22 +++ Quik/Media/FontInfo.cs | 44 ------ Quik/Media/FontStyle.cs | 14 -- Quik/Media/QFont.cs | 11 +- Quik/Media/QGlyphMetrics.cs | 7 - Quik/PAL/IFontDatabase.cs | 54 ++++++++ Quik/QuikStyle.cs | 10 +- 10 files changed, 356 insertions(+), 73 deletions(-) create mode 100644 Quik/Media/Font/FontFace.cs create mode 100644 Quik/Media/Font/FontSlant.cs create mode 100644 Quik/Media/Font/FontStretch.cs create mode 100644 Quik/Media/Font/FontWeight.cs delete mode 100644 Quik/Media/FontInfo.cs delete mode 100644 Quik/Media/FontStyle.cs create mode 100644 Quik/PAL/IFontDatabase.cs diff --git a/Quik/Media/Font/FontFace.cs b/Quik/Media/Font/FontFace.cs new file mode 100644 index 0000000..a7d6d61 --- /dev/null +++ b/Quik/Media/Font/FontFace.cs @@ -0,0 +1,240 @@ +using System; +using System.Net.WebSockets; +using System.Text; +using Quik.Media.Font; + +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; + } + } + } +} \ No newline at end of file diff --git a/Quik/Media/Font/FontSlant.cs b/Quik/Media/Font/FontSlant.cs new file mode 100644 index 0000000..b5697c3 --- /dev/null +++ b/Quik/Media/Font/FontSlant.cs @@ -0,0 +1,9 @@ +namespace Quik.Media.Font +{ + public enum FontSlant + { + Normal = 0, + Italic = 1, + Oblique = 2, + } +} \ No newline at end of file diff --git a/Quik/Media/Font/FontStretch.cs b/Quik/Media/Font/FontStretch.cs new file mode 100644 index 0000000..0d50b3b --- /dev/null +++ b/Quik/Media/Font/FontStretch.cs @@ -0,0 +1,18 @@ +namespace Quik.Media.Font +{ + /// + /// Enumeration of font stretch values. + /// + public enum FontStretch + { + UltraCondensed = 500, + ExtraCondensed = 625, + Condensed = 750, + SemiCondensed = 875, + Normal = 1000, + SemiExpanded = 1125, + Expanded = 1250, + ExtraExpanded = 1500, + UltraExpanded = 2000, + } +} \ No newline at end of file diff --git a/Quik/Media/Font/FontWeight.cs b/Quik/Media/Font/FontWeight.cs new file mode 100644 index 0000000..5c0c722 --- /dev/null +++ b/Quik/Media/Font/FontWeight.cs @@ -0,0 +1,22 @@ +using System; + +namespace Quik.Media.Font +{ + public enum FontWeight + { + _100 = 100, + _200 = 200, + _300 = 300, + _400 = 400, + _500 = 500, + _600 = 600, + _700 = 700, + _800 = 800, + _900 = 900, + + Thin = _100, + Normal = _400, + Bold = _700, + Heavy = _900, + } +} \ No newline at end of file diff --git a/Quik/Media/FontInfo.cs b/Quik/Media/FontInfo.cs deleted file mode 100644 index cdc3653..0000000 --- a/Quik/Media/FontInfo.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace Quik.Media -{ - public struct FontInfo : IEquatable - { - public string Family { get; } - public FontStyle Style { get; } - - public override string ToString() - { - return $"{Family} {Style}"; - } - - public override int GetHashCode() - { - return Family.GetHashCode() ^ - (Style.GetHashCode() * 3976061); - } - - public static bool operator==(FontInfo a, FontInfo b) - { - return (a.Style == b.Style) && - (a.Family == a.Family); - } - - public static bool operator!=(FontInfo a, FontInfo b) - { - return (a.Style != b.Style) || - (a.Family != b.Family); - } - - public bool Equals(FontInfo other) - { - return this == other; - } - - public override bool Equals(object obj) - { - return (obj.GetType() == typeof(FontInfo)) && - this == (FontInfo)obj; - } - } -} \ No newline at end of file diff --git a/Quik/Media/FontStyle.cs b/Quik/Media/FontStyle.cs deleted file mode 100644 index 79a4728..0000000 --- a/Quik/Media/FontStyle.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Quik.Media -{ - [Flags] - public enum FontStyle - { - Italic = 1 << 0, - Bold = 1 << 1, - - Normal = 0, - BoldItalic = Italic | Bold, - } -} \ No newline at end of file diff --git a/Quik/Media/QFont.cs b/Quik/Media/QFont.cs index 493aa94..ac42f84 100644 --- a/Quik/Media/QFont.cs +++ b/Quik/Media/QFont.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using Quik.Media; +using Quik.Media.Font; namespace Quik.Media { @@ -7,9 +10,11 @@ namespace Quik.Media /// public abstract class QFont : IDisposable { - public abstract FontInfo Info { get; } - public string Family => Info.Family; - public FontStyle Style => Info.Style; + public abstract FontFace Face { get; } + public string Family => Face.Family; + public FontSlant Slant => Face.Slant; + public FontWeight Weight => Face.Weight; + public FontStretch Stretch => Face.Stretch; public abstract bool HasRune(int rune); public abstract QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options); diff --git a/Quik/Media/QGlyphMetrics.cs b/Quik/Media/QGlyphMetrics.cs index 84a5872..ccf3d7c 100644 --- a/Quik/Media/QGlyphMetrics.cs +++ b/Quik/Media/QGlyphMetrics.cs @@ -10,11 +10,6 @@ namespace Quik.Media /// public int Rune { get; } - // /// - // /// Location of the glyph on the atlas. - // /// - // public QRectangle Location { get; } - /// /// Size of the glyph in units. /// @@ -37,14 +32,12 @@ namespace Quik.Media public QGlyphMetrics( int character, - // QRectangle location, QVec2 size, QVec2 horizontalBearing, QVec2 verticalBearing, QVec2 advance) { Rune = character; - // Location = location; Size = size; HorizontalBearing = horizontalBearing; VerticalBearing = verticalBearing; diff --git a/Quik/PAL/IFontDatabase.cs b/Quik/PAL/IFontDatabase.cs new file mode 100644 index 0000000..43e1b33 --- /dev/null +++ b/Quik/PAL/IFontDatabase.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Quik.Media.Font; + +namespace Quik.PAL +{ + /// + /// Flags that effect font search criterea. + /// + [Flags] + public enum FontMatchCriteria + { + None = 0, + Family = 1 << 0, + Slant = 1 << 1, + Weight = 1 << 2, + Stretch = 1 << 3, + All = Family | Slant | Weight | Stretch, + } + + /// + /// An abstraction over the system font database. + /// + public interface IFontDataBase + { + /// + /// All the fonts installed in the system. + /// + IEnumerable All { get; } + + /// + /// Search for the given font face. + /// + /// The font face prototype. + /// The match criteria + /// A list of fonts sorted by the closest match first. + IEnumerable Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All); + + /// + /// Get the font face file info if it exists. + /// + /// The face to look for. + /// The file info if it exists. + FileInfo FontFileInfo(FontFace face); + + /// + /// Open a font face. + /// + /// The font face to open. + /// The stream to the font face. + Stream Open(FontFace face); + } +} \ No newline at end of file diff --git a/Quik/QuikStyle.cs b/Quik/QuikStyle.cs index ef880a0..acdc2e3 100644 --- a/Quik/QuikStyle.cs +++ b/Quik/QuikStyle.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using Quik.Media; -using Quik.Typography; +using Quik.Media.Font; namespace Quik { @@ -109,9 +109,9 @@ namespace Quik set => this["list-marker-position"] = value; } - public QuikTexture ListMarkerImage + public QImage ListMarkerImage { - get => (QuikTexture)this["list-marker-image"]; + get => (QImage)this["list-marker-image"]; set => this["list-marker-image"] = value; } @@ -127,9 +127,9 @@ namespace Quik set => this["stroke-color"] = value; } - public FontInfo Font + public FontFace Font { - get => (FontInfo)this["font"]; + get => (FontFace)this["font"]; set => this["font"] = value; }