Add new font search criterea.

This commit is contained in:
H. Utku Maden 2024-04-28 13:45:46 +03:00
parent bc3dcff3ea
commit ccb0c6ffe7
10 changed files with 356 additions and 73 deletions

240
Quik/Media/Font/FontFace.cs Normal file

@ -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<FontFace>
{
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);
}
/// <summary>
/// Try to convert a token that represents a font slant into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="slant">The resulting slant.</param>
/// <returns>True if it matched any.</returns>
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;
}
}
/// <summary>
/// Try to convert a token that represents a font weight into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="weight">The resulting weight.</param>
/// <returns>True if it matched any.</returns>
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;
}
}
/// <summary>
/// Try to convert a token that represents a font stretch into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="stretch">The resulting stretch.</param>
/// <returns>True if it matched any.</returns>
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;
}
}
}
}

@ -0,0 +1,9 @@
namespace Quik.Media.Font
{
public enum FontSlant
{
Normal = 0,
Italic = 1,
Oblique = 2,
}
}

@ -0,0 +1,18 @@
namespace Quik.Media.Font
{
/// <summary>
/// Enumeration of font stretch values.
/// </summary>
public enum FontStretch
{
UltraCondensed = 500,
ExtraCondensed = 625,
Condensed = 750,
SemiCondensed = 875,
Normal = 1000,
SemiExpanded = 1125,
Expanded = 1250,
ExtraExpanded = 1500,
UltraExpanded = 2000,
}
}

@ -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,
}
}

@ -1,44 +0,0 @@
using System;
namespace Quik.Media
{
public struct FontInfo : IEquatable<FontInfo>
{
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;
}
}
}

@ -1,14 +0,0 @@
using System;
namespace Quik.Media
{
[Flags]
public enum FontStyle
{
Italic = 1 << 0,
Bold = 1 << 1,
Normal = 0,
BoldItalic = Italic | Bold,
}
}

@ -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
/// </summary>
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);

@ -10,11 +10,6 @@ namespace Quik.Media
/// </summary>
public int Rune { get; }
// /// <summary>
// /// Location of the glyph on the atlas.
// /// </summary>
// public QRectangle Location { get; }
/// <summary>
/// Size of the glyph in units.
/// </summary>
@ -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;

54
Quik/PAL/IFontDatabase.cs Normal file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.IO;
using Quik.Media.Font;
namespace Quik.PAL
{
/// <summary>
/// Flags that effect font search criterea.
/// </summary>
[Flags]
public enum FontMatchCriteria
{
None = 0,
Family = 1 << 0,
Slant = 1 << 1,
Weight = 1 << 2,
Stretch = 1 << 3,
All = Family | Slant | Weight | Stretch,
}
/// <summary>
/// An abstraction over the system font database.
/// </summary>
public interface IFontDataBase
{
/// <summary>
/// All the fonts installed in the system.
/// </summary>
IEnumerable<FontFace> All { get; }
/// <summary>
/// Search for the given font face.
/// </summary>
/// <param name="prototype">The font face prototype.</param>
/// <param name="criteria">The match criteria</param>
/// <returns>A list of fonts sorted by the closest match first.</returns>
IEnumerable<FontFace> Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All);
/// <summary>
/// Get the font face file info if it exists.
/// </summary>
/// <param name="face">The face to look for.</param>
/// <returns>The file info if it exists.</returns>
FileInfo FontFileInfo(FontFace face);
/// <summary>
/// Open a font face.
/// </summary>
/// <param name="face">The font face to open.</param>
/// <returns>The stream to the font face.</returns>
Stream Open(FontFace face);
}
}

@ -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;
}