diff --git a/.github/workflows/macos-static-avalonia-native.yml b/.github/workflows/macos-static-avalonia-native.yml
index 00a1364..4d34cc5 100644
--- a/.github/workflows/macos-static-avalonia-native.yml
+++ b/.github/workflows/macos-static-avalonia-native.yml
@@ -45,6 +45,9 @@ jobs:
- name: Build macOS static graphics libraries
run: bash ./scripts/build-macos-static-graphics.sh all
+ - name: Build macOS font preinit object
+ run: bash ./scripts/build-macos-font-preinit.sh
+
- name: List native outputs
run: find "External/NativeStatic/${{ matrix.rid }}/native" -maxdepth 1 -type f -print
diff --git a/.github/workflows/nuget-static-graphics.yml b/.github/workflows/nuget-static-graphics.yml
index 4398728..bd26cf9 100644
--- a/.github/workflows/nuget-static-graphics.yml
+++ b/.github/workflows/nuget-static-graphics.yml
@@ -136,6 +136,11 @@ jobs:
TARGET_CPU: ${{ matrix.target_cpu }}
run: scripts/build-macos-static-graphics.sh all
+ - name: Build macOS font preinit object
+ env:
+ RID: ${{ matrix.rid }}
+ run: bash scripts/build-macos-font-preinit.sh
+
- name: Upload macOS static archives
uses: actions/upload-artifact@v4
with:
diff --git a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj
index 31b8b03..fa75f67 100644
--- a/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj
+++ b/NuGet/StaticGraphics/Shiroka.Avalonia.StaticLink.csproj
@@ -6,7 +6,7 @@
false
false
Shiroka.Avalonia.StaticLink
- 3.119.2-7151.7
+ 3.119.2-7151.9
greepar
greepar
Static SkiaSharp, HarfBuzzSharp, and ANGLE libraries for Avalonia NativeAOT single-file publishing.
@@ -27,6 +27,7 @@
+
diff --git a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs
new file mode 100644
index 0000000..fa4c29e
--- /dev/null
+++ b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.MacOSFontWarmup.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Shiroka.Avalonia.StaticLink;
+
+internal static class MacOSFontWarmup
+{
+ [ModuleInitializer]
+ internal static void Initialize()
+ {
+ if (!OperatingSystem.IsMacOS())
+ {
+ return;
+ }
+
+ try
+ {
+ var nsApplication = objc_getClass("NSApplication");
+ var sharedApplication = sel_registerName("sharedApplication");
+ _ = objc_msgSend(nsApplication, sharedApplication);
+
+ var nsFont = objc_getClass("NSFont");
+ var systemFontOfSize = sel_registerName("systemFontOfSize:");
+ var systemFontOfSizeWidth = sel_registerName("systemFontOfSize:width:");
+
+ _ = objc_msgSend(nsFont, systemFontOfSize, 13.0);
+ _ = objc_msgSend(nsFont, systemFontOfSizeWidth, 13.0, 0.0);
+ }
+ catch
+ {
+ // Never fail application startup from the defensive macOS font-cache warmup.
+ }
+ }
+
+ [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_getClass")]
+ private static extern IntPtr objc_getClass([MarshalAs(UnmanagedType.LPUTF8Str)] string name);
+
+ [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "sel_registerName")]
+ private static extern IntPtr sel_registerName([MarshalAs(UnmanagedType.LPUTF8Str)] string name);
+
+ [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
+ private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
+
+ [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
+ private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value);
+
+ [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint = "objc_msgSend")]
+ private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, double value, double width);
+}
diff --git a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets
index 847690a..59e8fbb 100644
--- a/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets
+++ b/NuGet/StaticGraphics/buildTransitive/Shiroka.Avalonia.StaticLink.targets
@@ -49,9 +49,11 @@
+
+
diff --git a/scripts/build-macos-font-preinit.sh b/scripts/build-macos-font-preinit.sh
new file mode 100644
index 0000000..e386ad2
--- /dev/null
+++ b/scripts/build-macos-font-preinit.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+RID="${RID:-osx-arm64}"
+OUTPUT_DIR="${OUTPUT_DIR:-$PWD/External/NativeStatic/$RID/native}"
+
+case "$RID" in
+ osx-arm64) ARCH="arm64" ;;
+ osx-x64) ARCH="x86_64" ;;
+ *) echo "Unsupported RID: $RID" >&2; exit 2 ;;
+esac
+
+mkdir -p "$OUTPUT_DIR"
+
+src="$OUTPUT_DIR/macos_font_preinit.m"
+cat > "$src" <<'OBJC'
+#import
+
+__attribute__((constructor(101)))
+static void ShirokaAvaloniaStaticLinkPreinitializeAppKitFonts(void)
+{
+ @autoreleasepool
+ {
+ [NSApplication sharedApplication];
+ [NSFont systemFontOfSize:13.0];
+ [NSFont systemFontOfSize:13.0 width:0.0];
+ }
+}
+OBJC
+
+clang -arch "$ARCH" \
+ -mmacosx-version-min=12.0 \
+ -fobjc-arc \
+ -c "$src" \
+ -o "$OUTPUT_DIR/macos_font_preinit.o"
+
+rm -f "$src"
+echo "Wrote $OUTPUT_DIR/macos_font_preinit.o"