using System; using System.IO; using Avalonia.Platform; using HarfBuzzSharp; namespace Avalonia.UnitTests { public class HarfBuzzGlyphTypefaceImpl : IGlyphTypefaceImpl { private bool _isDisposed; private Blob _blob; public HarfBuzzGlyphTypefaceImpl(Stream data, bool isFakeBold = false, bool isFakeItalic = false) { _blob = Blob.FromStream(data); Face = new Face(_blob, 0); Font = new Font(Face); Font.SetFunctionsOpenType(); Font.GetScale(out var scale, out _); DesignEmHeight = (short)scale; var metrics = Font.OpenTypeMetrics; const double defaultFontRenderingEmSize = 12.0; Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * DesignEmHeight); Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * DesignEmHeight); LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * DesignEmHeight); UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * DesignEmHeight); UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * DesignEmHeight); StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * DesignEmHeight); StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * DesignEmHeight); IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b')); IsFakeBold = isFakeBold; IsFakeItalic = isFakeItalic; } public Face Face { get; } public Font Font { get; } /// public short DesignEmHeight { get; } /// public int Ascent { get; } /// public int Descent { get; } /// public int LineGap { get; } /// public int UnderlinePosition { get; } /// public int UnderlineThickness { get; } /// public int StrikethroughPosition { get; } /// public int StrikethroughThickness { get; } /// public bool IsFixedPitch { get; } public bool IsFakeBold { get; } public bool IsFakeItalic { get; } /// public ushort GetGlyph(uint codepoint) { if (Font.TryGetGlyph(codepoint, out var glyph)) { return (ushort)glyph; } return 0; } /// public ushort[] GetGlyphs(ReadOnlySpan codepoints) { var glyphs = new ushort[codepoints.Length]; for (var i = 0; i < codepoints.Length; i++) { if (Font.TryGetGlyph(codepoints[i], out var glyph)) { glyphs[i] = (ushort)glyph; } } return glyphs; } /// public int GetGlyphAdvance(ushort glyph) { return Font.GetHorizontalGlyphAdvance(glyph); } /// public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var glyphIndices = new uint[glyphs.Length]; for (var i = 0; i < glyphs.Length; i++) { glyphIndices[i] = glyphs[i]; } return Font.GetHorizontalGlyphAdvances(glyphIndices); } private void Dispose(bool disposing) { if (_isDisposed) { return; } _isDisposed = true; if (!disposing) { return; } Font?.Dispose(); Face?.Dispose(); _blob?.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }