Skip to content

Conversation

Gillibald
Copy link
Contributor

@Gillibald Gillibald commented Oct 3, 2025

What does the pull request do?

  • Move common members to FontCollectionBase
  • Add multiple protected members for more customization capabilities
  • Introduce Typeface.Normalize and replace GetImplicityTypeface
  • Fixes name table loading for macintosh fonts
  • Introduce FontCollectionBase.TryAddFontSource

What is the current behavior?

Example for TryAddFontSource

class CustomFontCollection(Uri key) : FontCollectionBase
{
    public override Uri Key { get; } = key;
}
var fontCollection = new CustomFontCollection(new Uri("fonts:custom", UriKind.Absolute));
fontManager.AddFontCollection(fontCollection);
var source = new Uri("my/path/to/source", UriKind.Absolute);
Assert.True(fontCollection.TryAddFontSource(fontUri));

How was the solution implemented (if it's not obvious)?

  • Sorted Insertion of Font Families

    • When adding a new FontFamily in AddGlyphTypeface, we now use a BinarySearch to determine the correct insertion index for faster lookup in the future.
    • This guarantees that the internal _fontFamilies list remains sorted by name (case-insensitive).
  • Efficient Prefix Search in TryGetGlyphTypeface

    • Replaced the linear search with a binary search to quickly locate the first font family whose name starts with the requested prefix.

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

@Gillibald Gillibald added enhancement backport-candidate-11.3.x Consider this PR for backporting to 11.3 branch labels Oct 3, 2025
maxkatz6
maxkatz6 previously approved these changes Oct 3, 2025
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059162-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald Gillibald changed the title Make TryGetNearestMatch and GetImplicitTypeface public [WIP] Make TryGetNearestMatch and GetImplicitTypeface public Oct 3, 2025
Introduce CustomFontCollection
@Gillibald Gillibald force-pushed the feature/publicFontCollectionHelper branch from 85b525f to c06c943 Compare October 6, 2025 08:13
@Gillibald Gillibald changed the title [WIP] Make TryGetNearestMatch and GetImplicitTypeface public [WIP] Introduce CustomFontCollection Oct 6, 2025
@Gillibald Gillibald added the needs-api-review The PR adds new public APIs that should be reviewed. label Oct 6, 2025
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059223-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald
Copy link
Contributor Author

Gillibald commented Oct 6, 2025

API diff between 12.0.999-cibuild0059262-alpha and 12.0.999

Avalonia.Base (net6.0, net8.0, netstandard2.0)

  namespace Avalonia.Media
  {
      public sealed class Typeface
      {
+         public Avalonia.Media.Typeface Normalize(out string normalizedFamilyName);
      }
  }
  namespace Avalonia.Media.Fonts
  {
      public class EmbeddedFontCollection : Avalonia.Media.Fonts.FontCollectionBase
      {
-         public override System.Collections.Generic.IEnumerator<Avalonia.Media.FontFamily> GetEnumerator();
-         public bool TryGetFamilyTypefaces(string familyName, out System.Collections.Generic.IReadOnlyList<Avalonia.Media.Typeface?>? familyTypefaces);
-         public override bool TryGetGlyphTypeface(string familyName, Avalonia.Media.FontStyle style, Avalonia.Media.FontWeight weight, Avalonia.Media.FontStretch stretch, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
-         public override int Count { get; }
-         public override Avalonia.Media.FontFamily this[int index] {
-             get { }
-         }
      }
      public abstract class FontCollectionBase : Avalonia.Media.Fonts.IFontCollection
      {
+         public virtual System.Collections.Generic.IEnumerator<Avalonia.Media.FontFamily> GetEnumerator();
-         public abstract System.Collections.Generic.IEnumerator<Avalonia.Media.FontFamily> GetEnumerator();
-         public abstract void Initialize(Avalonia.Platform.IFontManagerImpl fontManager);
+         public virtual void Initialize(Avalonia.Platform.IFontManagerImpl fontManagerImpl);
-         public abstract bool TryGetGlyphTypeface(string familyName, Avalonia.Media.FontStyle style, Avalonia.Media.FontWeight weight, Avalonia.Media.FontStretch stretch, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
+         public virtual bool TryGetGlyphTypeface(string familyName, Avalonia.Media.FontStyle style, Avalonia.Media.FontWeight weight, Avalonia.Media.FontStretch stretch, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
-         public abstract int Count { get; }
+         public virtual int Count { get; }
-         public abstract Avalonia.Media.FontFamily this[int index] { get; }
+         public virtual Avalonia.Media.FontFamily this[int index] {
+             get { }
+         }
+         protected void AddFontFamily(Avalonia.Media.FontFamily fontFamily);
+         public bool TryAddFontSource(System.Uri source);
+         public bool TryAddGlyphTypeface(Avalonia.Media.IGlyphTypeface glyphTypeface);
+         public bool TryAddGlyphTypeface(System.IO.Stream stream);
+         protected bool TryAddGlyphTypeface(string familyName, Avalonia.Media.Fonts.FontCollectionKey key, Avalonia.Media.IGlyphTypeface? glyphTypeface);
+         public virtual bool TryGetFamilyTypefaces(string familyName, out System.Collections.Generic.IReadOnlyList<Avalonia.Media.Typeface?>? familyTypefaces);
+         protected bool TryGetGlyphTypeface(string familyName, Avalonia.Media.Fonts.FontCollectionKey key, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
+         protected bool? TryGetNearestMatch(System.Collections.Generic.IDictionary<Avalonia.Media.Fonts.FontCollectionKey, Avalonia.Media.IGlyphTypeface> glyphTypefaces, Avalonia.Media.Fonts.FontCollectionKey? key, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
+         public bool TryGetNearestMatch(string familyName, Avalonia.Media.FontStyle style, Avalonia.Media.FontWeight weight, Avalonia.Media.FontStretch stretch, out Avalonia.Media.IGlyphTypeface? glyphTypeface);
+         protected Avalonia.Platform.IFontManagerImpl FontManagerImpl { get; }
      }
      public static class FontFamilyLoader
      {
+         public static bool IsFontFile(string filePath);
      }
  }

@Gillibald Gillibald changed the title [WIP] Introduce CustomFontCollection Introduce CustomFontCollection Oct 6, 2025
@miloush
Copy link
Contributor

miloush commented Oct 6, 2025

It might also be useful to add whole font collection into another

@Gillibald
Copy link
Contributor Author

A copy can be done via:

var other = new CustomFontCollection(new Uri("fonts:other", UriKind.Absolute));

foreach (var family in families)
{
    var familyTypefaces = family.FamilyTypefaces;

    foreach(var typeface in familyTypefaces)
    {
        other.TryAddGlyphTypeface(typeface.GlyphTypeface);
    }
}

But we can also introduce some API to make this easier

@miloush
Copy link
Contributor

miloush commented Oct 6, 2025

I was thinking of IDWriteFontSetBuilder.AddFontSet equivalent but the collections don't have that anyway.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059227-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald Gillibald changed the title Introduce CustomFontCollection Improve FontCollection customization Oct 8, 2025
…tCollectionHelper

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059256-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald Gillibald changed the title Improve FontCollection customization [WIP] Improve FontCollection customization Oct 9, 2025
Introduce TryAddFontSource
…bald/Avalonia into feature/publicFontCollectionHelper
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059270-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald Gillibald changed the title [WIP] Improve FontCollection customization Improve FontCollection customization Oct 9, 2025
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059276-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0059356-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-candidate-11.3.x Consider this PR for backporting to 11.3 branch enhancement needs-api-review The PR adds new public APIs that should be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants