mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-18 21:50:25 +01:00
Improve SkiaEncoder's font handling (#13231)
* Improve SkiaEncoder's font handling Our previous approach didn’t work with some complex library names, even when the required fonts were present, because the font handling logic was too simplistic. Modern Unicode and the fonts have become quite complex, making it challenging to implement it correctly. This improved implementation still isn’t the most correct way, but it’s better than it used to be. It now falls back to multiple fonts to find the best one and also handles extended grapheme clusters that were incorrectly processed before. * Fix space * Remove redundant comment * Make _typefaces an array * Make Measure and Draw text function name clear * Fix rename
This commit is contained in:
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BlurHashSharp.SkiaSharp;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
@@ -24,6 +25,7 @@ public class SkiaEncoder : IImageEncoder
|
||||
private readonly ILogger<SkiaEncoder> _logger;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private static readonly SKImageFilter _imageFilter;
|
||||
private static readonly SKTypeface[] _typefaces;
|
||||
|
||||
#pragma warning disable CA1810
|
||||
static SkiaEncoder()
|
||||
@@ -46,6 +48,21 @@ public class SkiaEncoder : IImageEncoder
|
||||
kernelOffset,
|
||||
SKShaderTileMode.Clamp,
|
||||
true);
|
||||
|
||||
// Initialize the list of typefaces
|
||||
// We have to statically build a list of typefaces because MatchCharacter only accepts a single character or code point
|
||||
// But in reality a human-readable character (grapheme cluster) could be multiple code points. For example, 🚵🏻♀️ is a single emoji but 5 code points (U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F)
|
||||
_typefaces =
|
||||
[
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, '鸡'), // CJK Simplified Chinese
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, '雞'), // CJK Traditional Chinese
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ノ'), // CJK Japanese
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, '각'), // CJK Korean
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 128169), // Emojis, 128169 is the 💩emoji
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ז'), // Hebrew
|
||||
SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ي'), // Arabic
|
||||
SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) // Default font
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,6 +114,11 @@ public class SkiaEncoder : IImageEncoder
|
||||
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
|
||||
=> new HashSet<ImageFormat> { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png, ImageFormat.Svg };
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default typeface to use.
|
||||
/// </summary>
|
||||
public static SKTypeface DefaultTypeFace => _typefaces.Last();
|
||||
|
||||
/// <summary>
|
||||
/// Check if the native lib is available.
|
||||
/// </summary>
|
||||
@@ -705,4 +727,22 @@ public class SkiaEncoder : IImageEncoder
|
||||
_logger.LogError(ex, "Error drawing indicator overlay");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the typeface that contains the glyph for the given character.
|
||||
/// </summary>
|
||||
/// <param name="c">The text character.</param>
|
||||
/// <returns>The typeface contains the character.</returns>
|
||||
public static SKTypeface? GetFontForCharacter(string c)
|
||||
{
|
||||
foreach (var typeface in _typefaces)
|
||||
{
|
||||
if (typeface.ContainsGlyphs(c))
|
||||
{
|
||||
return typeface;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user