using System;
using System.IO;
using Avalonia.Media;
using HarfBuzzSharp;
namespace Avalonia.UnitTests
{
public class HarfBuzzGlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
private Blob _blob;
public HarfBuzzGlyphTypefaceImpl(Stream data)
{
_blob = Blob.FromStream(data);
Face = new Face(_blob, 0);
Font = new Font(Face);
Font.SetFunctionsOpenType();
Font.GetScale(out var scale, out _);
const double defaultFontRenderingEmSize = 12.0;
var metrics = Font.OpenTypeMetrics;
Metrics = new FontMetrics
{
DesignEmHeight = (short)scale,
Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * scale),
Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * scale),
LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * scale),
UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * scale),
UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * scale),
StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * scale),
StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * scale),
IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'))
};
GlyphCount = Face.GlyphCount;
}
public FontMetrics Metrics { get; }
public Face Face { get; }
public Font Font { get; }
public int GlyphCount { get; set; }
public FontSimulations FontSimulations { get; }
public string FamilyName => "$Default";
public FontWeight Weight { get; }
public FontStyle Style { get; }
public FontStretch Stretch { get; }
///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
{
return (ushort)glyph;
}
return 0;
}
public bool TryGetGlyph(uint codepoint,out ushort glyph)
{
glyph = 0;
if (Font.TryGetGlyph(codepoint, out var glyphId))
{
glyph = (ushort)glyphId;
return true;
}
return false;
}
///
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);
}
public bool TryGetTable(uint tag, out byte[] table)
{
table = null;
var blob = Face.ReferenceTable(tag);
if (blob.Length > 0)
{
table = blob.AsSpan().ToArray();
return true;
}
return false;
}
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);
}
public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
{
metrics = default;
if (!Font.TryGetGlyphExtents(glyph, out var extents))
{
return false;
}
metrics = new GlyphMetrics
{
XBearing = extents.XBearing,
YBearing = extents.YBearing,
Width = extents.Width,
Height = extents.Height
};
return true;
}
}
}