From cbcc6c5c5be7ac5fc1b32d706720c8006fcecdf8 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 6 Nov 2023 22:50:00 +0100 Subject: [PATCH 1/6] [CoreText] Implement CTFontDescriptor.MatchFontDescriptors. Fixes #19397. Fixes https://github.com/xamarin/xamarin-macios/issues/19397. --- src/CoreText/CTFont.Generator.cs | 2 + src/CoreText/CTFontDescriptor.cs | 67 ++++++++++++++----- src/bgen/Generator.cs | 6 ++ src/coretext.cs | 40 +++++++++++ .../BlittablePInvokes.KnownFailures.cs | 1 - .../CoreText/FontDescriptorTest.cs | 28 ++++++++ .../common-CoreText.ignore | 8 --- tests/xtro-sharpie/common-CoreText.ignore | 8 --- 8 files changed, 126 insertions(+), 34 deletions(-) diff --git a/src/CoreText/CTFont.Generator.cs b/src/CoreText/CTFont.Generator.cs index e6f64835f52b..7426e2bf125b 100644 --- a/src/CoreText/CTFont.Generator.cs +++ b/src/CoreText/CTFont.Generator.cs @@ -32,4 +32,6 @@ namespace CoreText { public partial class CTFont : NativeObject { } + public partial class CTFontDescriptor : NativeObject { + } } diff --git a/src/CoreText/CTFontDescriptor.cs b/src/CoreText/CTFontDescriptor.cs index 846c397ae580..e75fc034e5c9 100644 --- a/src/CoreText/CTFontDescriptor.cs +++ b/src/CoreText/CTFontDescriptor.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.InteropServices; using ObjCRuntime; @@ -390,7 +391,7 @@ public CTFontDescriptorAttributes (NSDictionary dictionary) [SupportedOSPlatform ("macos")] [SupportedOSPlatform ("tvos")] #endif - public class CTFontDescriptor : NativeObject { + public partial class CTFontDescriptor : NativeObject { [Preserve (Conditional = true)] internal CTFontDescriptor (NativeHandle handle, bool owns) : base (handle, owns, true) @@ -738,17 +739,30 @@ public CTFontDescriptor (CTFontDescriptorAttributes attributes) [SupportedOSPlatform ("maccatalyst")] [SupportedOSPlatform ("tvos")] #endif -#if NET [DllImport (Constants.CoreTextLibrary)] - [return: MarshalAs (UnmanagedType.I1)] - static unsafe extern bool CTFontDescriptorMatchFontDescriptorsWithProgressHandler (IntPtr descriptors, IntPtr mandatoryAttributes, - delegate* unmanaged progressHandler); + static unsafe extern byte CTFontDescriptorMatchFontDescriptorsWithProgressHandler (IntPtr descriptors, IntPtr mandatoryAttributes, BlockLiteral* progressBlock); + + public delegate bool CTFontDescriptorProgressHandler (CTFontDescriptorMatchingState state, CTFontDescriptorMatchingProgress progress); + +#if !NET + delegate byte ct_font_desctiptor_progress_handler_t (IntPtr block, CTFontDescriptorMatchingState state, IntPtr progress); + static ct_font_desctiptor_progress_handler_t static_MatchFontDescriptorsHandler = MatchFontDescriptorsHandler; + + [MonoPInvokeCallback (typeof (ct_font_desctiptor_progress_handler_t))] #else - [DllImport (Constants.CoreTextLibrary)] - [return: MarshalAs (UnmanagedType.I1)] - static extern bool CTFontDescriptorMatchFontDescriptorsWithProgressHandler (IntPtr descriptors, IntPtr mandatoryAttributes, - Func progressHandler); + [UnmanagedCallersOnly] #endif + static byte MatchFontDescriptorsHandler (IntPtr block, CTFontDescriptorMatchingState state, IntPtr progress) + { + var del = BlockLiteral.GetTarget (block); + if (del is not null) { + var progressDictionary = Runtime.GetNSObject (progress)!; + var strongDictionary = new CTFontDescriptorMatchingProgress (progressDictionary); + var rv = del (state, strongDictionary); + return rv ? (byte) 1 : (byte) 0; + } + return 0; + } #if NET [SupportedOSPlatform ("macos")] @@ -756,16 +770,35 @@ public CTFontDescriptor (CTFontDescriptorAttributes attributes) [SupportedOSPlatform ("maccatalyst")] [SupportedOSPlatform ("tvos")] #endif - public static bool MatchFontDescriptors (CTFontDescriptor [] descriptors, NSSet mandatoryAttributes, Func progressHandler) + public static bool MatchFontDescriptors (CTFontDescriptor [] descriptors, NSSet? mandatoryAttributes, CTFontDescriptorProgressHandler progressHandler) + { + if (descriptors is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (descriptors)); + + if (progressHandler is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (progressHandler)); + + unsafe { +#if NET + delegate* unmanaged trampoline = &MatchFontDescriptorsHandler; + using var block = new BlockLiteral (trampoline, progressHandler, typeof (CTFontDescriptor), nameof (MatchFontDescriptorsHandler)); +#else + using var block = new BlockLiteral (); + block.SetupBlockUnsafe (static_MatchFontDescriptorsHandler, progressHandler); +#endif + using var descriptorsArray = NSArray.FromNSObjects (descriptors); + var rv = CTFontDescriptorMatchFontDescriptorsWithProgressHandler (descriptorsArray.GetHandle (), mandatoryAttributes.GetHandle (), &block); + return rv != 0; + } + } + +#if !XAMCORE_5_0 + [EditorBrowsable (EditorBrowsableState.Never)] + [Obsolete ("Use 'MatchFontDescriptors (CTFontDescriptor[], NSSet, CTFontDescriptorProgressHandler)' instead.")] + public static bool MatchFontDescriptors (CTFontDescriptor [] descriptors, NSSet? mandatoryAttributes, Func progressHandler) { - // FIXME: the P/Invoke used below is wrong, it expects a block, not a function pointer. - // throwing a NIE instead of crashing until this is implemented properly. throw new NotImplementedException (); - // var ma = mandatoryAttributes is null ? IntPtr.Zero : mandatoryAttributes.Handle; - // // FIXME: SIGSEGV probably due to mandatoryAttributes mismatch - // using (var ar = CFArray.FromNativeObjects (descriptors)) { - // return CTFontDescriptorMatchFontDescriptorsWithProgressHandler (ar.Handle, ma, progressHandler); - // } } +#endif } } diff --git a/src/bgen/Generator.cs b/src/bgen/Generator.cs index 3cda24b27877..ec6fa282dc7f 100644 --- a/src/bgen/Generator.cs +++ b/src/bgen/Generator.cs @@ -1951,6 +1951,9 @@ void GenerateStrongDictionaryTypes () } else if (elementType == TypeManager.System_String) { getter = "GetArray ({0}, (ptr) => CFString.FromHandle (ptr)!)"; setter = "SetArrayValue ({0}, value)"; + } else if (elementType.Name == "CTFontDescriptor") { + getter = "GetArray ({0}, (ptr) => new CTFontDescriptor (ptr, false))"; + setter = "SetArrayValue ({0}, value)"; } else { throw new BindingException (1033, true, pi.PropertyType, dictType, pi.Name); } @@ -1982,6 +1985,9 @@ void GenerateStrongDictionaryTypes () } else if (pi.PropertyType.Name == "CGImageSource") { getter = "GetNativeValue<" + pi.PropertyType + "> ({0})"; setter = "SetNativeValue ({0}, value)"; + } else if (pi.PropertyType.Name == "CTFontDescriptor") { + getter = "GetNativeValue<" + pi.PropertyType + "> ({0})"; + setter = "SetNativeValue ({0}, value)"; } else { throw new BindingException (1033, true, pi.PropertyType, dictType, pi.Name); } diff --git a/src/coretext.cs b/src/coretext.cs index 1e72615dfaf8..0d92a561e643 100644 --- a/src/coretext.cs +++ b/src/coretext.cs @@ -221,6 +221,46 @@ interface CTFontDescriptorAttributeKey { NSString Enabled { get; } } + [Internal] + [Static] + interface CTFontDescriptorMatchingKeys { + [Field ("kCTFontDescriptorMatchingSourceDescriptor")] + NSString SourceDescriptorKey { get; } + + [Field ("kCTFontDescriptorMatchingDescriptors")] + NSString DescriptorsKey { get; } + + [Field ("kCTFontDescriptorMatchingResult")] + NSString ResultKey { get; } + + [Field ("kCTFontDescriptorMatchingPercentage")] + NSString PercentageKey { get; } + + [Field ("kCTFontDescriptorMatchingCurrentAssetSize")] + NSString CurrentAssetSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingTotalDownloadedSize")] + NSString TotalDownloadedSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingTotalAssetSize")] + NSString TotalAssetSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingError")] + NSString ErrorKey { get; } + } + + [StrongDictionary ("CTFontDescriptorMatchingKeys")] + interface CTFontDescriptorMatchingProgress { + CTFontDescriptor SourceDescriptor { get; } + CTFontDescriptor[] Descriptors { get; } + CTFontDescriptor[] Result { get; } + double Percentage { get; } + long CurrentAssetSize { get; } + long TotalDownloadedSize { get; } + long TotalAssetSize { get; } + NSError Error { get; } + } + [Static] interface CTTextTabOptionKey { [Field ("kCTTabColumnTerminatorsAttributeName")] diff --git a/tests/cecil-tests/BlittablePInvokes.KnownFailures.cs b/tests/cecil-tests/BlittablePInvokes.KnownFailures.cs index 6cd1237099d6..244b4d7171e3 100644 --- a/tests/cecil-tests/BlittablePInvokes.KnownFailures.cs +++ b/tests/cecil-tests/BlittablePInvokes.KnownFailures.cs @@ -207,7 +207,6 @@ public partial class BlittablePInvokes { "System.Boolean CoreServices.FSEvent::FSEventsPurgeEventsForDeviceUpToEventId(System.UInt64,System.UInt64)", "System.Boolean CoreServices.FSEventStream::FSEventStreamStart(System.IntPtr)", "System.Boolean CoreText.CTFont::CTFontGetGlyphsForCharacters(System.IntPtr,System.Char[],System.UInt16[],System.IntPtr)", - "System.Boolean CoreText.CTFontDescriptor::CTFontDescriptorMatchFontDescriptorsWithProgressHandler(System.IntPtr,System.IntPtr,method System.Boolean *(CoreText.CTFontDescriptorMatchingState,System.IntPtr))", "System.Boolean CoreText.CTFontManager::CTFontManagerIsSupportedFont(System.IntPtr)", "System.Boolean CoreText.CTFontManager::CTFontManagerRegisterFontsForURL(System.IntPtr,CoreText.CTFontManagerScope,System.IntPtr&)", "System.Boolean CoreText.CTFontManager::CTFontManagerRegisterFontsForURLs(System.IntPtr,CoreText.CTFontManagerScope,System.IntPtr&)", diff --git a/tests/monotouch-test/CoreText/FontDescriptorTest.cs b/tests/monotouch-test/CoreText/FontDescriptorTest.cs index 11ca7e10670d..bf7ba8647871 100644 --- a/tests/monotouch-test/CoreText/FontDescriptorTest.cs +++ b/tests/monotouch-test/CoreText/FontDescriptorTest.cs @@ -8,6 +8,9 @@ // using System; +using System.Threading; +using System.Threading.Tasks; + using Foundation; using CoreText; #if MONOMAC @@ -71,5 +74,30 @@ public void WithFeature () Assert.That (set_feature.FeatureWeak, Is.EqualTo ((int) CTFontFeatureLigatures.Selector.RareLigaturesOn), "#2"); } } + + [Test] + public void MatchFontDescriptors () + { + var fda1 = new CTFontDescriptorAttributes () { + Name = "Apple-Chancery", + }; + using var desc1 = new CTFontDescriptor (fda1); + var tcs = new TaskCompletionSource (); + var rv = CTFontDescriptor.MatchFontDescriptors (new CTFontDescriptor [] { desc1 }, null, (CTFontDescriptorMatchingState state, CTFontDescriptorMatchingProgress progress) => + { + try { + if (state == CTFontDescriptorMatchingState.Finished) { + Assert.AreEqual (1, progress.Result.Length, "Result.Length"); + Assert.AreEqual (fda1.Name, progress.Result [0].GetAttributes ().Name, "Result[0].Name"); + tcs.TrySetResult (true); + } + } catch (Exception e) { + tcs.TrySetException (e); + } + return true; + }); + Assert.IsTrue (rv, "Return value"); + TestRuntime.RunAsync (TimeSpan.FromSeconds (30), tcs.Task); + } } } diff --git a/tests/xtro-sharpie/api-annotations-dotnet/common-CoreText.ignore b/tests/xtro-sharpie/api-annotations-dotnet/common-CoreText.ignore index abed0d85385d..58153965d318 100644 --- a/tests/xtro-sharpie/api-annotations-dotnet/common-CoreText.ignore +++ b/tests/xtro-sharpie/api-annotations-dotnet/common-CoreText.ignore @@ -4,14 +4,6 @@ !missing-enum! CTRubyAlignment not bound !missing-enum! CTRubyOverhang not bound !missing-enum! CTRubyPosition not bound -!missing-field! kCTFontDescriptorMatchingCurrentAssetSize not bound -!missing-field! kCTFontDescriptorMatchingDescriptors not bound -!missing-field! kCTFontDescriptorMatchingError not bound -!missing-field! kCTFontDescriptorMatchingPercentage not bound -!missing-field! kCTFontDescriptorMatchingResult not bound -!missing-field! kCTFontDescriptorMatchingSourceDescriptor not bound -!missing-field! kCTFontDescriptorMatchingTotalAssetSize not bound -!missing-field! kCTFontDescriptorMatchingTotalDownloadedSize not bound !missing-field! kCTFontDownloadableAttribute not bound !missing-field! kCTFontDownloadedAttribute not bound !missing-field! kCTFontManagerRegisteredFontsChangedNotification not bound diff --git a/tests/xtro-sharpie/common-CoreText.ignore b/tests/xtro-sharpie/common-CoreText.ignore index ad6875377fb1..f1e24da856de 100644 --- a/tests/xtro-sharpie/common-CoreText.ignore +++ b/tests/xtro-sharpie/common-CoreText.ignore @@ -24,14 +24,6 @@ !missing-field! kCTFontCollectionRemoveDuplicatesOption not bound !missing-field! kCTFontCopyrightNameKey not bound !missing-field! kCTFontDescriptionNameKey not bound -!missing-field! kCTFontDescriptorMatchingCurrentAssetSize not bound -!missing-field! kCTFontDescriptorMatchingDescriptors not bound -!missing-field! kCTFontDescriptorMatchingError not bound -!missing-field! kCTFontDescriptorMatchingPercentage not bound -!missing-field! kCTFontDescriptorMatchingResult not bound -!missing-field! kCTFontDescriptorMatchingSourceDescriptor not bound -!missing-field! kCTFontDescriptorMatchingTotalAssetSize not bound -!missing-field! kCTFontDescriptorMatchingTotalDownloadedSize not bound !missing-field! kCTFontDesignerNameKey not bound !missing-field! kCTFontDesignerURLNameKey not bound !missing-field! kCTFontDisplayNameAttribute not bound From 49d44175b2c58ce9adf997fecfe0bd0adf69ca05 Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Tue, 7 Nov 2023 09:49:29 +0000 Subject: [PATCH 2/6] Auto-format source code --- tests/monotouch-test/CoreText/FontDescriptorTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/monotouch-test/CoreText/FontDescriptorTest.cs b/tests/monotouch-test/CoreText/FontDescriptorTest.cs index bf7ba8647871..356c8da3419e 100644 --- a/tests/monotouch-test/CoreText/FontDescriptorTest.cs +++ b/tests/monotouch-test/CoreText/FontDescriptorTest.cs @@ -83,8 +83,7 @@ public void MatchFontDescriptors () }; using var desc1 = new CTFontDescriptor (fda1); var tcs = new TaskCompletionSource (); - var rv = CTFontDescriptor.MatchFontDescriptors (new CTFontDescriptor [] { desc1 }, null, (CTFontDescriptorMatchingState state, CTFontDescriptorMatchingProgress progress) => - { + var rv = CTFontDescriptor.MatchFontDescriptors (new CTFontDescriptor [] { desc1 }, null, (CTFontDescriptorMatchingState state, CTFontDescriptorMatchingProgress progress) => { try { if (state == CTFontDescriptorMatchingState.Finished) { Assert.AreEqual (1, progress.Result.Length, "Result.Length"); From 03d41956f6a21654f83bed8edcd23943f10a6379 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 7 Nov 2023 12:45:32 +0100 Subject: [PATCH 3/6] Shuffle some code. --- src/coretext.cs | 80 ++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/coretext.cs b/src/coretext.cs index 0d92a561e643..ac5c946b7b80 100644 --- a/src/coretext.cs +++ b/src/coretext.cs @@ -221,46 +221,6 @@ interface CTFontDescriptorAttributeKey { NSString Enabled { get; } } - [Internal] - [Static] - interface CTFontDescriptorMatchingKeys { - [Field ("kCTFontDescriptorMatchingSourceDescriptor")] - NSString SourceDescriptorKey { get; } - - [Field ("kCTFontDescriptorMatchingDescriptors")] - NSString DescriptorsKey { get; } - - [Field ("kCTFontDescriptorMatchingResult")] - NSString ResultKey { get; } - - [Field ("kCTFontDescriptorMatchingPercentage")] - NSString PercentageKey { get; } - - [Field ("kCTFontDescriptorMatchingCurrentAssetSize")] - NSString CurrentAssetSizeKey { get; } - - [Field ("kCTFontDescriptorMatchingTotalDownloadedSize")] - NSString TotalDownloadedSizeKey { get; } - - [Field ("kCTFontDescriptorMatchingTotalAssetSize")] - NSString TotalAssetSizeKey { get; } - - [Field ("kCTFontDescriptorMatchingError")] - NSString ErrorKey { get; } - } - - [StrongDictionary ("CTFontDescriptorMatchingKeys")] - interface CTFontDescriptorMatchingProgress { - CTFontDescriptor SourceDescriptor { get; } - CTFontDescriptor[] Descriptors { get; } - CTFontDescriptor[] Result { get; } - double Percentage { get; } - long CurrentAssetSize { get; } - long TotalDownloadedSize { get; } - long TotalAssetSize { get; } - NSError Error { get; } - } - [Static] interface CTTextTabOptionKey { [Field ("kCTTabColumnTerminatorsAttributeName")] @@ -365,6 +325,46 @@ interface CTFontCollectionOptionKey { } #endif + [Internal] + [Static] + interface CTFontDescriptorMatchingKeys { + [Field ("kCTFontDescriptorMatchingSourceDescriptor")] + NSString SourceDescriptorKey { get; } + + [Field ("kCTFontDescriptorMatchingDescriptors")] + NSString DescriptorsKey { get; } + + [Field ("kCTFontDescriptorMatchingResult")] + NSString ResultKey { get; } + + [Field ("kCTFontDescriptorMatchingPercentage")] + NSString PercentageKey { get; } + + [Field ("kCTFontDescriptorMatchingCurrentAssetSize")] + NSString CurrentAssetSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingTotalDownloadedSize")] + NSString TotalDownloadedSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingTotalAssetSize")] + NSString TotalAssetSizeKey { get; } + + [Field ("kCTFontDescriptorMatchingError")] + NSString ErrorKey { get; } + } + + [StrongDictionary ("CTFontDescriptorMatchingKeys")] + interface CTFontDescriptorMatchingProgress { + CTFontDescriptor SourceDescriptor { get; } + CTFontDescriptor[] Descriptors { get; } + CTFontDescriptor[] Result { get; } + double Percentage { get; } + long CurrentAssetSize { get; } + long TotalDownloadedSize { get; } + long TotalAssetSize { get; } + NSError Error { get; } + } + [Static] [Partial] interface CTStringAttributeKey { From 9a66af484cc5b00f84546deb055be12a1a21d03b Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Tue, 7 Nov 2023 12:38:31 +0000 Subject: [PATCH 4/6] Auto-format source code --- src/coretext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coretext.cs b/src/coretext.cs index ac5c946b7b80..b385e27da5cf 100644 --- a/src/coretext.cs +++ b/src/coretext.cs @@ -356,8 +356,8 @@ interface CTFontDescriptorMatchingKeys { [StrongDictionary ("CTFontDescriptorMatchingKeys")] interface CTFontDescriptorMatchingProgress { CTFontDescriptor SourceDescriptor { get; } - CTFontDescriptor[] Descriptors { get; } - CTFontDescriptor[] Result { get; } + CTFontDescriptor [] Descriptors { get; } + CTFontDescriptor [] Result { get; } double Percentage { get; } long CurrentAssetSize { get; } long TotalDownloadedSize { get; } From b9f17a9dc3e22d6b41321a10503bef761b6bd642 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 7 Nov 2023 17:40:47 +0100 Subject: [PATCH 5/6] Make optimizable --- src/CoreText/CTFontDescriptor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CoreText/CTFontDescriptor.cs b/src/CoreText/CTFontDescriptor.cs index e75fc034e5c9..511c9264908e 100644 --- a/src/CoreText/CTFontDescriptor.cs +++ b/src/CoreText/CTFontDescriptor.cs @@ -770,6 +770,7 @@ static byte MatchFontDescriptorsHandler (IntPtr block, CTFontDescriptorMatchingS [SupportedOSPlatform ("maccatalyst")] [SupportedOSPlatform ("tvos")] #endif + [BindingImpl (BindingImplOptions.Optimizable)] public static bool MatchFontDescriptors (CTFontDescriptor [] descriptors, NSSet? mandatoryAttributes, CTFontDescriptorProgressHandler progressHandler) { if (descriptors is null) From 52260c6a08409ab0d7b463a66e25f67afb591da7 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 8 Nov 2023 08:44:10 +0100 Subject: [PATCH 6/6] Use a font that exists on every platform. --- tests/monotouch-test/CoreText/FontDescriptorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/monotouch-test/CoreText/FontDescriptorTest.cs b/tests/monotouch-test/CoreText/FontDescriptorTest.cs index 356c8da3419e..8aa2d6327300 100644 --- a/tests/monotouch-test/CoreText/FontDescriptorTest.cs +++ b/tests/monotouch-test/CoreText/FontDescriptorTest.cs @@ -79,7 +79,7 @@ public void WithFeature () public void MatchFontDescriptors () { var fda1 = new CTFontDescriptorAttributes () { - Name = "Apple-Chancery", + Name = "Helvetica", }; using var desc1 = new CTFontDescriptor (fda1); var tcs = new TaskCompletionSource ();