A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

172 lines
6.0 KiB

using System.IO;
using System.Linq;
using Avalonia.Base.UnitTests.Media.Fonts.Tables;
using Avalonia.Media;
using Avalonia.Media.Fonts;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Base.UnitTests.Media.Fonts.TestInfrastructure
{
/// <summary>
/// Self-tests for the <see cref="SyntheticFont"/> / <see cref="BigEndianBuffer"/> test
/// infrastructure. These must stay green: they prove the harness produces fonts the real
/// pipeline accepts and that its mutations behave as documented, so that font-robustness
/// tests built on top can attribute failures to the code under test, not the harness.
/// </summary>
public class SyntheticFontTests
{
[Fact]
public void FromAsset_Produces_A_Loadable_Typeface()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
var typeface = font.TryCreateGlyphTypeface();
Assert.NotNull(typeface);
Assert.True(typeface!.GlyphCount > 0);
Assert.True(typeface.CharacterToGlyphMap.ContainsGlyph('A'));
}
[Fact]
public void FromAsset_Parses_The_Core_Sfnt_Tables()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
// A static TrueType font carries at least these.
Assert.True(font.Contains("head"));
Assert.True(font.Contains("maxp"));
Assert.True(font.Contains("cmap"));
Assert.True(font.Contains("hhea"));
Assert.True(font.Contains("hmtx"));
Assert.True(font.Contains("glyf"));
Assert.True(font.Contains("loca"));
}
[Fact]
public void Variable_Font_Carries_The_Variation_Tables()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterVariable);
Assert.True(font.Contains("fvar"));
Assert.True(font.Contains("HVAR"));
Assert.True(font.Contains("gvar"));
}
[Fact]
public void Truncate_Shortens_The_Table()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
var originalLength = font.TableLength("post");
font.Truncate("post", 4);
Assert.Equal(4, font.TableLength("post"));
Assert.True(originalLength > 4);
}
[Fact]
public void Remove_Drops_The_Table()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
Assert.True(font.Contains("post"));
font.Remove("post");
Assert.False(font.Contains("post"));
}
[Fact]
public void PatchUInt16_Writes_Big_Endian_At_The_Given_Offset()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
// head.unitsPerEm lives at offset 18.
font.PatchUInt16("head", 18, 0x0801);
var head = font.GetTable("head");
Assert.Equal(0x08, head[18]);
Assert.Equal(0x01, head[19]);
}
[Fact]
public void ToPlatformTypeface_Snapshots_The_Tables_At_Call_Time()
{
var font = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
var headTag = OpenTypeTag.Parse("head");
var beforeMutation = font.GetTable("head");
var platformTypeface = font.ToPlatformTypeface();
// Mutate the source after taking the snapshot.
font.PatchUInt16("head", 18, 0x1234);
Assert.True(platformTypeface.TryGetTable(headTag, out var snapshot));
// The snapshot reflects the pre-mutation bytes; the source reflects the mutation.
Assert.Equal(beforeMutation, snapshot.ToArray());
Assert.NotEqual(beforeMutation, font.GetTable("head"));
}
[Fact]
public void ToBytes_RoundTrips_Through_The_Real_Sfnt_Parser()
{
// ToBytes() must emit a directory the production UnmanagedFontMemory parser
// accepts. CustomPlatformTypeface wraps UnmanagedFontMemory.LoadFromStream.
var rebuilt = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular).ToBytes();
var platformTypeface = new CustomPlatformTypeface(new MemoryStream(rebuilt));
var typeface = GlyphTypeface.TryCreate(platformTypeface);
Assert.NotNull(typeface);
Assert.True(typeface!.GlyphCount > 0);
Assert.True(typeface.CharacterToGlyphMap.ContainsGlyph('A'));
}
[Fact]
public void ToBytes_RoundTrips_Through_FromBytes_Preserving_Tables()
{
var original = SyntheticFont.FromAsset(SyntheticFont.Assets.InterRegular);
var reparsed = SyntheticFont.FromBytes(original.ToBytes());
Assert.Equal(
original.Tags.Select(t => (uint)t).OrderBy(t => t),
reparsed.Tags.Select(t => (uint)t).OrderBy(t => t));
// Table contents survive the round-trip byte-for-byte.
Assert.Equal(original.GetTable("maxp"), reparsed.GetTable("maxp"));
}
[Fact]
public void BigEndianBuffer_Writes_Big_Endian_And_Patches_Reserved_Offsets()
{
var buffer = new BigEndianBuffer();
buffer.UInt16(0x1234);
var offsetPos = buffer.ReserveOffset32();
buffer.UInt8(0xAB);
buffer.PatchUInt32(offsetPos, 0xDEADBEEF);
Assert.Equal(
new byte[] { 0x12, 0x34, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB },
buffer.ToArray());
}
[Fact]
public void BigEndianBuffer_F2Dot14_And_Tag_Encode_Correctly()
{
var buffer = new BigEndianBuffer();
buffer.F2Dot14(1.0); // 1.0 == 0x4000 in F2DOT14
buffer.Tag("head");
var bytes = buffer.ToArray();
Assert.Equal(new byte[] { 0x40, 0x00 }, bytes[..2]);
Assert.Equal(new byte[] { (byte)'h', (byte)'e', (byte)'a', (byte)'d' }, bytes[2..6]);
}
}
}