From 75dd9c87ae3b52510ea30b7e4ff972d7c3aa0d17 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 18 Mar 2025 02:49:25 -0300 Subject: [PATCH 1/4] Preload fonts in use as per CSS definitions --- src/Elastic.Markdown/Helpers/Preloader.cs | 37 +++++++++++++++++++ .../Slices/Layout/_Head.cshtml | 7 +++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/Elastic.Markdown/Helpers/Preloader.cs diff --git a/src/Elastic.Markdown/Helpers/Preloader.cs b/src/Elastic.Markdown/Helpers/Preloader.cs new file mode 100644 index 000000000..2c6a4d7a6 --- /dev/null +++ b/src/Elastic.Markdown/Helpers/Preloader.cs @@ -0,0 +1,37 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Diagnostics; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Elastic.Markdown.Helpers; + +public static partial class FontPreloader +{ + private static List? FontUriCache = null!; + + public static async Task> GetFontUrisAsync() => FontUriCache ??= await LoadFontUrisAsync(); + public static async Task> LoadFontUrisAsync() + { + FontUriCache = []; + var assembly = Assembly.GetExecutingAssembly(); + var stylesResourceName = assembly.GetManifestResourceNames().First(n => n.EndsWith("styles.css")); + + using var cssFileStream = new StreamReader(assembly.GetManifestResourceStream(stylesResourceName)!); + + var cssFile = await cssFileStream.ReadToEndAsync(); + var matches = FontUriRegex().Matches(cssFile); + + foreach (Match match in matches) + { + if (match.Success) + FontUriCache.Add($"/_static/{match.Groups[1].Value}"); + } + return FontUriCache; + } + + [GeneratedRegex(@"url\([""']?([^""'\)]+)[""']?\)", RegexOptions.Multiline | RegexOptions.Compiled)] + private static partial Regex FontUriRegex(); +} diff --git a/src/Elastic.Markdown/Slices/Layout/_Head.cshtml b/src/Elastic.Markdown/Slices/Layout/_Head.cshtml index 8efac7915..42fee55e0 100644 --- a/src/Elastic.Markdown/Slices/Layout/_Head.cshtml +++ b/src/Elastic.Markdown/Slices/Layout/_Head.cshtml @@ -1,7 +1,12 @@ @inherits RazorSlice +@using Elastic.Markdown.Helpers @Model.Title - + + @foreach (var fontFile in await FontPreloader.GetFontUrisAsync()) + { + + } @await RenderPartialAsync(_Favicon.Create()) From e315a22484d992b1741ca22d20a00f0b15a54923 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 18 Mar 2025 09:47:38 -0300 Subject: [PATCH 2/4] Adjust FontUriCache typing and stricten Regex --- src/Elastic.Markdown/Helpers/Preloader.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Elastic.Markdown/Helpers/Preloader.cs b/src/Elastic.Markdown/Helpers/Preloader.cs index 2c6a4d7a6..50b1f22b3 100644 --- a/src/Elastic.Markdown/Helpers/Preloader.cs +++ b/src/Elastic.Markdown/Helpers/Preloader.cs @@ -2,6 +2,7 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; using System.Text.RegularExpressions; @@ -10,12 +11,12 @@ namespace Elastic.Markdown.Helpers; public static partial class FontPreloader { - private static List? FontUriCache = null!; + private static IReadOnlyCollection? FontUriCache = null!; - public static async Task> GetFontUrisAsync() => FontUriCache ??= await LoadFontUrisAsync(); - public static async Task> LoadFontUrisAsync() + public static async Task> GetFontUrisAsync() => FontUriCache ??= await LoadFontUrisAsync(); + public static async Task> LoadFontUrisAsync() { - FontUriCache = []; + var cachedFontUris = new List(); var assembly = Assembly.GetExecutingAssembly(); var stylesResourceName = assembly.GetManifestResourceNames().First(n => n.EndsWith("styles.css")); @@ -27,11 +28,12 @@ public static async Task> LoadFontUrisAsync() foreach (Match match in matches) { if (match.Success) - FontUriCache.Add($"/_static/{match.Groups[1].Value}"); + cachedFontUris.Add($"/_static/{match.Groups[1].Value}"); } + FontUriCache = cachedFontUris; return FontUriCache; } - [GeneratedRegex(@"url\([""']?([^""'\)]+)[""']?\)", RegexOptions.Multiline | RegexOptions.Compiled)] + [GeneratedRegex(@"url\([""']?([^""'\)]+?\.(woff2|ttf|otf))[""']?\)", RegexOptions.Multiline | RegexOptions.Compiled)] private static partial Regex FontUriRegex(); } From 109190e17237dbb4c54fa828be2740f469e3a305 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 18 Mar 2025 09:52:01 -0300 Subject: [PATCH 3/4] Readjust link header ordering. --- src/Elastic.Markdown/Slices/Layout/_Head.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Slices/Layout/_Head.cshtml b/src/Elastic.Markdown/Slices/Layout/_Head.cshtml index 42fee55e0..849b0188c 100644 --- a/src/Elastic.Markdown/Slices/Layout/_Head.cshtml +++ b/src/Elastic.Markdown/Slices/Layout/_Head.cshtml @@ -2,11 +2,11 @@ @using Elastic.Markdown.Helpers @Model.Title - @foreach (var fontFile in await FontPreloader.GetFontUrisAsync()) { } + @await RenderPartialAsync(_Favicon.Create()) From ffd674026278b2f768fd8a5aa5b1542ab681a662 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Tue, 18 Mar 2025 09:54:52 -0300 Subject: [PATCH 4/4] Fix accessor to private. --- src/Elastic.Markdown/Helpers/Preloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Helpers/Preloader.cs b/src/Elastic.Markdown/Helpers/Preloader.cs index 50b1f22b3..53086a4ce 100644 --- a/src/Elastic.Markdown/Helpers/Preloader.cs +++ b/src/Elastic.Markdown/Helpers/Preloader.cs @@ -14,7 +14,7 @@ public static partial class FontPreloader private static IReadOnlyCollection? FontUriCache = null!; public static async Task> GetFontUrisAsync() => FontUriCache ??= await LoadFontUrisAsync(); - public static async Task> LoadFontUrisAsync() + private static async Task> LoadFontUrisAsync() { var cachedFontUris = new List(); var assembly = Assembly.GetExecutingAssembly();