SystemFontCollection.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Linq;
  6. using Avalonia.Platform;
  7. namespace Avalonia.Media.Fonts
  8. {
  9. internal class SystemFontCollection : FontCollectionBase, IFontCollection2
  10. {
  11. private readonly FontManager _fontManager;
  12. private readonly List<string> _familyNames;
  13. public SystemFontCollection(FontManager fontManager)
  14. {
  15. _fontManager = fontManager;
  16. _familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames().Where(x=> !string.IsNullOrEmpty(x)).ToList();
  17. }
  18. public override Uri Key => FontManager.SystemFontsKey;
  19. public override FontFamily this[int index]
  20. {
  21. get
  22. {
  23. var familyName = _familyNames[index];
  24. return new FontFamily(familyName);
  25. }
  26. }
  27. public override int Count => _familyNames.Count;
  28. public override IEnumerator<FontFamily> GetEnumerator()
  29. {
  30. foreach (var familyName in _familyNames)
  31. {
  32. yield return new FontFamily(familyName);
  33. }
  34. }
  35. public override bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
  36. FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
  37. {
  38. glyphTypeface = null;
  39. var typeface = GetImplicitTypeface(new Typeface(familyName, style, weight, stretch), out familyName);
  40. style = typeface.Style;
  41. weight = typeface.Weight;
  42. stretch = typeface.Stretch;
  43. var key = new FontCollectionKey(style, weight, stretch);
  44. if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
  45. {
  46. if (glyphTypefaces.TryGetValue(key, out glyphTypeface))
  47. {
  48. return glyphTypeface != null;
  49. }
  50. }
  51. glyphTypefaces ??= _glyphTypefaceCache.GetOrAdd(familyName,
  52. (_) => new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>());
  53. //Try to create the glyph typeface via system font manager
  54. if (!_fontManager.PlatformImpl.TryCreateGlyphTypeface(familyName, style, weight, stretch,
  55. out glyphTypeface))
  56. {
  57. glyphTypefaces.TryAdd(key, null);
  58. return false;
  59. }
  60. var createdKey =
  61. new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch);
  62. //No exact match
  63. if (createdKey != key)
  64. {
  65. //Add the created glyph typeface to the cache so we can match it.
  66. glyphTypefaces.TryAdd(createdKey, glyphTypeface);
  67. //Try to find nearest match if possible
  68. if (TryGetNearestMatch(glyphTypefaces, key, out var nearestMatch))
  69. {
  70. glyphTypeface = nearestMatch;
  71. }
  72. //Try to create a synthetic glyph typeface
  73. if (TryCreateSyntheticGlyphTypeface(glyphTypeface, style, weight, stretch, out var syntheticGlyphTypeface))
  74. {
  75. glyphTypeface = syntheticGlyphTypeface;
  76. return true;
  77. }
  78. }
  79. glyphTypefaces.TryAdd(key, glyphTypeface);
  80. return glyphTypeface != null;
  81. }
  82. public override void Initialize(IFontManagerImpl fontManager)
  83. {
  84. //We initialize the system font collection during construction.
  85. }
  86. public void AddCustomFontSource(Uri source)
  87. {
  88. if (source is null)
  89. {
  90. return;
  91. }
  92. LoadGlyphTypefaces(_fontManager.PlatformImpl, source);
  93. }
  94. private void LoadGlyphTypefaces(IFontManagerImpl fontManager, Uri source)
  95. {
  96. var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
  97. var fontAssets = FontFamilyLoader.LoadFontAssets(source);
  98. foreach (var fontAsset in fontAssets)
  99. {
  100. var stream = assetLoader.Open(fontAsset);
  101. if (!fontManager.TryCreateGlyphTypeface(stream, FontSimulations.None, out var glyphTypeface))
  102. {
  103. continue;
  104. }
  105. //Add TypographicFamilyName to the cache
  106. if (glyphTypeface is IGlyphTypeface2 glyphTypeface2 && !string.IsNullOrEmpty(glyphTypeface2.TypographicFamilyName))
  107. {
  108. AddGlyphTypefaceByFamilyName(glyphTypeface2.TypographicFamilyName, glyphTypeface);
  109. }
  110. AddGlyphTypefaceByFamilyName(glyphTypeface.FamilyName, glyphTypeface);
  111. }
  112. return;
  113. void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
  114. {
  115. var typefaces = _glyphTypefaceCache.GetOrAdd(familyName,
  116. x =>
  117. {
  118. _familyNames.Insert(0, familyName);
  119. return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
  120. });
  121. typefaces.TryAdd(
  122. new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch),
  123. glyphTypeface);
  124. }
  125. }
  126. public bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList<Typeface>? familyTypefaces)
  127. {
  128. familyTypefaces = null;
  129. if (_fontManager.PlatformImpl is IFontManagerImpl2 fontManagerImpl2)
  130. {
  131. return fontManagerImpl2.TryGetFamilyTypefaces(familyName, out familyTypefaces);
  132. }
  133. return false;
  134. }
  135. }
  136. }