From a3960b30c04d5313b0bfb3d57a7da35af5e8af3b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 29 Mar 2026 14:28:41 -0400 Subject: [PATCH] Backport pull request #16369 from jellyfin/release-10.11.z Fix nullref ex in font handling Original-merge: 41c2d51d8cb9b4f9bdf81be6e73f7ae2d447a8d7 Merged-by: Bond-009 Backported-by: Bond_009 --- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 57 +++++++++++++++--------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index c6eab92ead..ade993d927 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -25,7 +25,7 @@ public class SkiaEncoder : IImageEncoder private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private static readonly SKImageFilter _imageFilter; - private static readonly SKTypeface[] _typefaces; + private static readonly SKTypeface?[] _typefaces = InitializeTypefaces(); /// /// The default sampling options, equivalent to old high quality filter settings when upscaling. @@ -37,9 +37,7 @@ public class SkiaEncoder : IImageEncoder /// public static readonly SKSamplingOptions DefaultSamplingOptions; -#pragma warning disable CA1810 static SkiaEncoder() -#pragma warning restore CA1810 { var kernel = new[] { @@ -59,21 +57,6 @@ public class SkiaEncoder : IImageEncoder 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 - ]; - // use cubic for upscaling UpscaleSamplingOptions = new SKSamplingOptions(SKCubicResampler.Mitchell); // use bilinear for everything else @@ -132,7 +115,7 @@ public class SkiaEncoder : IImageEncoder /// /// Gets the default typeface to use. /// - public static SKTypeface DefaultTypeFace => _typefaces.Last(); + public static SKTypeface? DefaultTypeFace => _typefaces.Last(); /// /// Check if the native lib is available. @@ -152,6 +135,40 @@ public class SkiaEncoder : IImageEncoder } } + /// + /// 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) + /// + /// The list of typefaces. + private static SKTypeface?[] InitializeTypefaces() + { + int[] chars = [ + 'ιΈ‘', // CJK Simplified Chinese + 'ι›ž', // CJK Traditional Chinese + 'γƒŽ', // CJK Japanese + '각', // CJK Korean + 128169, // Emojis, 128169 is the Pile of Poo (πŸ’©) emoji + 'Χ–', // Hebrew + 'ي' // Arabic + ]; + var fonts = new List(chars.Length + 1); + foreach (var ch in chars) + { + var font = SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, ch); + if (font is not null) + { + fonts.Add(font); + } + } + + // Default font + fonts.Add(SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) + ?? SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'a')); + + return fonts.ToArray(); + } + /// /// Convert a to a . /// @@ -809,7 +826,7 @@ public class SkiaEncoder : IImageEncoder { foreach (var typeface in _typefaces) { - if (typeface.ContainsGlyphs(c)) + if (typeface is not null && typeface.ContainsGlyphs(c)) { return typeface; }