using System.Linq; using System.Text; using FontStashSharp; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; using MLEM.Font; namespace MLEM.Extended.Font { /// public class GenericStashFont : GenericFont { /// /// The that is being wrapped by this generic font /// public readonly SpriteFontBase Font; /// public override GenericFont Bold { get; } /// public override GenericFont Italic { get; } /// public override float LineHeight { get; } /// /// Creates a new generic font using . /// Optionally, a bold and italic version of the font can be supplied. /// /// /// doesn't expose a text-independent line height (https://github.com/rds1983/FontStashSharp/blob/main/src/FontStashSharp/DynamicSpriteFont.cs#L130). /// Since exposes , there is somewhat of an incompatibility between the two. /// Because of this, uses a heuristic to determine a text-independent line height based on the tallest character out of a set of predetermined characters (spaces, numbers and uppercase and lowercase A through Z). /// Because this heuristic is just that, and because it excludes non-latin characters, the desired line height can be specified using , overriding the default heuristic. /// /// The font to wrap /// A bold version of the font /// An italic version of the font /// The line height that should be used for instead of the heuristic described in the remarks public GenericStashFont(SpriteFontBase font, SpriteFontBase bold = null, SpriteFontBase italic = null, float? lineHeight = null) { this.Font = font; this.LineHeight = lineHeight ?? CalculateLineHeight(font); this.Bold = bold != null ? new GenericStashFont(bold, null, null, lineHeight) : this; this.Italic = italic != null ? new GenericStashFont(italic, null, null, lineHeight) : this; } /// public override void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { this.Font.DrawText(batch, text, position, color, scale, rotation, origin, layerDepth); } /// public override void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { this.Font.DrawText(batch, text, position, color, scale, rotation, origin, layerDepth); } /// protected override float MeasureChar(char c) { return this.Font.MeasureString(c.ToCachedString()).X; } private static float CalculateLineHeight(SpriteFontBase font) { if (font is StaticSpriteFont s) { // this is the same calculation used internally by StaticSpriteFont return s.FontSize + s.LineSpacing; } else { // use a heuristic to determine the text-independent line heights as described in the constructor remarks return new[] {' ', '\n', OneEmSpace, Zwsp, Nbsp} .Concat(Enumerable.Range('a', 'z' - 'a' + 1).SelectMany(c => new[] {(char) c, char.ToUpper((char) c)})) .Concat(Enumerable.Range('0', '9' - '0' + 1).Select(c => (char) c)) .Select(c => font.MeasureString(c.ToString()).Y).Max(); } } } }