diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 91654390..d2d86f15 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,41 +1,41 @@ - - - - 8.0.3 - 8.0.1 - 8.0.1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + 8.0.3 + 8.0.1 + 8.0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs index ab342412..719eec7d 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs @@ -372,36 +372,31 @@ void Initialize() /// void InitializeFromGdi() { - try + if (GdiFontFamily != null!) + { + // Create font based on its family. + GdiFont = new Font(GdiFontFamily, (float)_emSize, (GdiFontStyle)_style, GraphicsUnit.World); + } + + if (GdiFont != null!) { - Locks.EnterFontFactory(); - if (GdiFontFamily != null!) - { - // Create font based on its family. - GdiFont = new Font(GdiFontFamily, (float)_emSize, (GdiFontStyle)_style, GraphicsUnit.World); - } - - if (GdiFont != null!) - { #if DEBUG_ - string name1 = _gdiFont.Name; - string name2 = _gdiFont.OriginalFontName; - string name3 = _gdiFont.SystemFontName; + string name1 = _gdiFont.Name; + string name2 = _gdiFont.OriginalFontName; + string name3 = _gdiFont.SystemFontName; #endif - _familyName = GdiFont.FontFamily.Name; - // TODO_OLD: _glyphTypeface = XGlyphTypeface.GetOrCreateFrom(_gdiFont); - } - else - { - Debug.Assert(false); - } - - if (GlyphTypeface == null!) - GlyphTypeface = XGlyphTypeface.GetOrCreateFromGdi(GdiFont); - - CreateDescriptorAndInitializeFontMetrics(); + _familyName = GdiFont.FontFamily.Name; + // TODO_OLD: _glyphTypeface = XGlyphTypeface.GetOrCreateFrom(_gdiFont); } - finally { Locks.ExitFontFactory(); } + else + { + Debug.Assert(false); + } + + if (GlyphTypeface == null!) + GlyphTypeface = XGlyphTypeface.GetOrCreateFromGdi(GdiFont); + + CreateDescriptorAndInitializeFontMetrics(); } #endif diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs index d7b6c902..8ad3955e 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs @@ -138,131 +138,129 @@ internal static XGlyphTypeface GetOrCreateFrom(string familyName, FontResolvingO // Check cache for requested type face. string typefaceKey = ComputeGtfKey(familyName, fontResolvingOptions); XGlyphTypeface? glyphTypeface; - try + if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) { - // Lock around TryGetGlyphTypeface and AddGlyphTypeface. - Locks.EnterFontFactory(); - if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) - { - // Just return existing one. - return glyphTypeface; - } + // Just return existing one. + return glyphTypeface; + } + + //// Resolve typeface by FontFactory. If no success, try fallback font resolver. + //var fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, false) ?? + // FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, true); + FontResolverInfo? fontResolverInfo = null; + try // Custom font resolvers may throw an exception. + { + // Try custom font resolver. + fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, false); + } + catch (Exception ex) + { + LogErrorBecauseFontResolverThrowsException(familyName, ex); + } - //// Resolve typeface by FontFactory. If no success, try fallback font resolver. - //var fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, false) ?? - // FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, true); - FontResolverInfo? fontResolverInfo = null; - try // Custom font resolvers may throw an exception. + if (fontResolverInfo == null) + { + try // Custom fallback font resolvers may throw an exception. { - // Try custom font resolver. - fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, false); + // Try fallback font resolver. + fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, true); } catch (Exception ex) { LogErrorBecauseFontResolverThrowsException(familyName, ex); } + } - if (fontResolverInfo == null) - { - try // Custom fallback font resolvers may throw an exception. - { - // Try fallback font resolver. - fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey, true); - } - catch (Exception ex) - { - LogErrorBecauseFontResolverThrowsException(familyName, ex); - } - } - - void LogErrorBecauseFontResolverThrowsException(string name, Exception ex) - => PdfSharpLogHost.Logger.LogError("A font resolver cannot resolve font family '{}' and throws an exception, " + - "but it must return null if the font cannot be resolved. Exception text: " + ex.Message, name); + void LogErrorBecauseFontResolverThrowsException(string name, Exception ex) + => PdfSharpLogHost.Logger.LogError( + "A font resolver cannot resolve font family '{}' and throws an exception, " + + "but it must return null if the font cannot be resolved. Exception text: " + ex.Message, name); - if (fontResolverInfo == null) - { - // No fallback - just stop. + if (fontResolverInfo == null) + { + // No fallback - just stop. #if CORE - if (GlobalFontSettings.FontResolver is null) - { - throw new InvalidOperationException( - $"No appropriate font found for family name '{familyName}'. " + - "Implement IFontResolver and assign to 'GlobalFontSettings.FontResolver' to use fonts. " + - $"See {UrlLiterals.LinkToFontResolving}"); - } -#endif - throw new InvalidOperationException($"No appropriate font found for family name '{familyName}'."); + if (GlobalFontSettings.FontResolver is null) + { + throw new InvalidOperationException( + $"No appropriate font found for family name '{familyName}'. " + + "Implement IFontResolver and assign to 'GlobalFontSettings.FontResolver' to use fonts. " + + $"See {UrlLiterals.LinkToFontResolving}"); } +#endif + throw new InvalidOperationException($"No appropriate font found for family name '{familyName}'."); + } #if GDI - GdiFont gdiFont = default!; + GdiFont gdiFont = default!; #endif #if WPF - // ReSharper disable once TooWideLocalVariableScope - // ReSharper disable once RedundantAssignment - WpfFontFamily? wpfFontFamily = null; - WpfTypeface? wpfTypeface = null; - WpfGlyphTypeface? wpfGlyphTypeface = null; + // ReSharper disable once TooWideLocalVariableScope + // ReSharper disable once RedundantAssignment + WpfFontFamily? wpfFontFamily = null; + WpfTypeface? wpfTypeface = null; + WpfGlyphTypeface? wpfGlyphTypeface = null; #endif #if WUI - // Nothing to do. + // Nothing to do. #endif - // Now create the font family at the first. - XFontFamily? fontFamily; - if (fontResolverInfo is PlatformFontResolverInfo platformFontResolverInfo) - { - // Case: fontResolverInfo was created by platform font resolver - // and contains platform specific objects that are reused. + // Now create the font family at the first. + XFontFamily? fontFamily; + if (fontResolverInfo is PlatformFontResolverInfo platformFontResolverInfo) + { + // Case: fontResolverInfo was created by platform font resolver + // and contains platform specific objects that are reused. #if CORE - // Get or create font family for custom font resolver retrieved font source. - fontFamily = XFontFamily.GetOrCreateFontFamily(familyName); - //// Cannot come here - //fontFamily = null; - //Debug.Assert(false); + // Get or create font family for custom font resolver retrieved font source. + fontFamily = XFontFamily.GetOrCreateFontFamily(familyName); + //// Cannot come here + //fontFamily = null; + //Debug.Assert(false); #endif #if GDI - // Reuse GDI+ font from platform font resolver. - gdiFont = platformFontResolverInfo.GdiFont; - fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); + // Reuse GDI+ font from platform font resolver. + gdiFont = platformFontResolverInfo.GdiFont; + fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); #endif #if WPF - // Reuse WPF font family created from platform font resolver. - wpfFontFamily = platformFontResolverInfo.WpfFontFamily; - wpfTypeface = platformFontResolverInfo.WpfTypeface; - wpfGlyphTypeface = platformFontResolverInfo.WpfGlyphTypeface; - fontFamily = XFontFamily.GetOrCreateFromWpf(wpfFontFamily); + // Reuse WPF font family created from platform font resolver. + wpfFontFamily = platformFontResolverInfo.WpfFontFamily; + wpfTypeface = platformFontResolverInfo.WpfTypeface; + wpfGlyphTypeface = platformFontResolverInfo.WpfGlyphTypeface; + fontFamily = XFontFamily.GetOrCreateFromWpf(wpfFontFamily); #endif #if WUI - fontFamily = null; + fontFamily = null; #endif - } - else - { - // Case: fontResolverInfo was created by custom font resolver. + } + else + { + // Case: fontResolverInfo was created by custom font resolver. - // Get or create font family for custom font resolver retrieved font source. - fontFamily = XFontFamily.GetOrCreateFontFamily(familyName); - } + // Get or create font family for custom font resolver retrieved font source. + fontFamily = XFontFamily.GetOrCreateFontFamily(familyName); + } - // We have a valid font resolver info. That means we also have an XFontSource object loaded in the cache. - XFontSource fontSource = FontFactory.GetFontSourceByFontName(fontResolverInfo.FaceName); - Debug.Assert(fontSource != null); + // We have a valid font resolver info. That means we also have an XFontSource object loaded in the cache. + XFontSource fontSource = FontFactory.GetFontSourceByFontName(fontResolverInfo.FaceName); + Debug.Assert(fontSource != null); - // Each font source already contains its OpenTypeFontFace. + // Each font source already contains its OpenTypeFontFace. #if CORE - glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations); + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations); #endif #if GDI - glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, gdiFont); + glyphTypeface = + new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, gdiFont); #endif #if WPF - glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, wpfTypeface, wpfGlyphTypeface); + glyphTypeface = + new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, wpfTypeface, wpfGlyphTypeface); #endif #if WUI - glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations); + glyphTypeface = + new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations); #endif - GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); - } - finally { Locks.ExitFontFactory(); } + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); return glyphTypeface; } @@ -274,33 +272,27 @@ void LogErrorBecauseFontResolverThrowsException(string name, Exception ex) public static XGlyphTypeface GetOrCreateFromGdi(GdiFont gdiFont) { XGlyphTypeface? glyphTypeface; - try + string typefaceKey = ComputeGtfKey(gdiFont); + if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) { - // Lock around TryGetGlyphTypeface and AddGlyphTypeface. - Locks.EnterFontFactory(); - string typefaceKey = ComputeGtfKey(gdiFont); - if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) - { - // We have the glyph typeface already in cache. - return glyphTypeface; - } + // We have the glyph typeface already in cache. + return glyphTypeface; + } - var fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); - Debug.Assert(fontFamily != null); - var fontSource = XFontSource.GetOrCreateFromGdi(typefaceKey, gdiFont); - Debug.Assert(fontSource != null); + var fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); + Debug.Assert(fontFamily != null); + var fontSource = XFontSource.GetOrCreateFromGdi(typefaceKey, gdiFont); + Debug.Assert(fontSource != null); - // Check if styles must be simulated. - XStyleSimulations styleSimulations = XStyleSimulations.None; - if (gdiFont.Bold && !fontSource.FontFace.os2.IsBold) - styleSimulations |= XStyleSimulations.BoldSimulation; - if (gdiFont.Italic && !fontSource.FontFace.os2.IsItalic) - styleSimulations |= XStyleSimulations.ItalicSimulation; + // Check if styles must be simulated. + XStyleSimulations styleSimulations = XStyleSimulations.None; + if (gdiFont.Bold && !fontSource.FontFace.os2.IsBold) + styleSimulations |= XStyleSimulations.BoldSimulation; + if (gdiFont.Italic && !fontSource.FontFace.os2.IsItalic) + styleSimulations |= XStyleSimulations.ItalicSimulation; - glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, styleSimulations, gdiFont); - GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); - } - finally { Locks.ExitFontFactory(); } + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, styleSimulations, gdiFont); + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); return glyphTypeface; } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs index 13e04a2c..e8e85a2b 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs @@ -1,10 +1,10 @@ // PDFsharp - A .NET library for processing PDF // See the LICENSE file in the solution root for more information. +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Text; using PdfSharp.Drawing; -using PdfSharp.Internal; namespace PdfSharp.Fonts.OpenType { @@ -15,24 +15,13 @@ static class GlyphTypefaceCache { public static bool TryGetGlyphTypeface(string key, [MaybeNullWhen(false)] out XGlyphTypeface glyphTypeface) { - try - { - Locks.EnterFontFactory(); - bool result = Globals.Global.Fonts.GlyphTypefacesByKey.TryGetValue(key, out glyphTypeface); - return result; - } - finally { Locks.ExitFontFactory(); } + return Globals.Global.Fonts.GlyphTypefacesByKey.TryGetValue(key, out glyphTypeface); } public static void AddGlyphTypeface(XGlyphTypeface glyphTypeface) { - try - { - Locks.EnterFontFactory(); - Debug.Assert(!Globals.Global.Fonts.GlyphTypefacesByKey.ContainsKey(glyphTypeface.Key)); - Globals.Global.Fonts.GlyphTypefacesByKey.Add(glyphTypeface.Key, glyphTypeface); - } - finally { Locks.ExitFontFactory(); } + Debug.Assert(!Globals.Global.Fonts.GlyphTypefacesByKey.ContainsKey(glyphTypeface.Key)); + Globals.Global.Fonts.GlyphTypefacesByKey.TryAdd(glyphTypeface.Key, glyphTypeface); } internal static void Reset() @@ -45,7 +34,7 @@ internal static string GetCacheState() var state = new StringBuilder(); state.Append("====================\n"); state.Append("Glyph typefaces by name\n"); - Dictionary.KeyCollection familyKeys = Globals.Global.Fonts.GlyphTypefacesByKey.Keys; + var familyKeys = Globals.Global.Fonts.GlyphTypefacesByKey.Keys; int count = familyKeys.Count; string[] keys = new string[count]; familyKeys.CopyTo(keys, 0); @@ -67,7 +56,7 @@ partial class FontStorage /// /// Maps typeface key to glyph typeface. /// - public readonly Dictionary GlyphTypefacesByKey = new(StringComparer.Ordinal); + public readonly ConcurrentDictionary GlyphTypefacesByKey = new(StringComparer.Ordinal); } } } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs index 99ad3332..3d9ac654 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs @@ -1,6 +1,7 @@ // PDFsharp - A .NET library for processing PDF // See the LICENSE file in the solution root for more information. +using System.Collections.Concurrent; using System.Text; using PdfSharp.Internal; using System.Diagnostics.CodeAnalysis; @@ -21,13 +22,7 @@ public static bool TryGetFontFace(string key, [MaybeNullWhen(false)] out OpenTypeFontFace fontFace) { - try - { - Locks.EnterFontFactory(); - var result = Globals.Global.Fonts.FontFaceCache.TryGetValue(key, out fontFace); - return result; - } - finally { Locks.ExitFontFactory(); } + return Globals.Global.Fonts.FontFaceCache.TryGetValue(key, out fontFace); } /// @@ -37,31 +32,21 @@ public static bool TryGetFontFace(ulong checkSum, [MaybeNullWhen(false)] out OpenTypeFontFace fontFace) { - try - { - Locks.EnterFontFactory(); - var result = Globals.Global.Fonts.FontFacesByCheckSum.TryGetValue(checkSum, out fontFace); - return result; - } - finally { Locks.ExitFontFactory(); } + return Globals.Global.Fonts.FontFacesByCheckSum.TryGetValue(checkSum, out fontFace); } public static OpenTypeFontFace AddFontFace(OpenTypeFontFace fontFace) { - try + if (TryGetFontFace(fontFace.FullFaceName, out var fontFaceCheck)) { - Locks.EnterFontFactory(); - if (TryGetFontFace(fontFace.FullFaceName, out var fontFaceCheck)) - { - if (fontFaceCheck.CheckSum != fontFace.CheckSum) - throw new InvalidOperationException("OpenTypeFontFace with same signature but different bytes."); - return fontFaceCheck; - } - Globals.Global.Fonts.FontFaceCache.Add(fontFace.FullFaceName, fontFace); - Globals.Global.Fonts.FontFacesByCheckSum.Add(fontFace.CheckSum, fontFace); - return fontFace; + if (fontFaceCheck.CheckSum != fontFace.CheckSum) + throw new InvalidOperationException("OpenTypeFontFace with same signature but different bytes."); + return fontFaceCheck; } - finally { Locks.ExitFontFactory(); } + + Globals.Global.Fonts.FontFaceCache.TryAdd(fontFace.FullFaceName, fontFace); + Globals.Global.Fonts.FontFacesByCheckSum.TryAdd(fontFace.CheckSum, fontFace); + return fontFace; } internal static void Reset() @@ -75,7 +60,7 @@ internal static string GetCacheState() StringBuilder state = new StringBuilder(); state.Append("====================\n"); state.Append("OpenType font faces by name\n"); - Dictionary.KeyCollection familyKeys = Globals.Global.Fonts.FontFaceCache.Keys; + var familyKeys = Globals.Global.Fonts.FontFaceCache.Keys; int count = familyKeys.Count; string[] keys = new string[count]; familyKeys.CopyTo(keys, 0); @@ -105,12 +90,12 @@ partial class FontStorage /// /// Maps face name to OpenType font face. /// - public readonly Dictionary FontFaceCache = []; + public readonly ConcurrentDictionary FontFaceCache = []; /// /// Maps font source key to OpenType font face. /// - public readonly Dictionary FontFacesByCheckSum = []; + public readonly ConcurrentDictionary FontFacesByCheckSum = []; } } } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs index 19007492..a337b370 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs @@ -1,9 +1,9 @@ // PDFsharp - A .NET library for processing PDF // See the LICENSE file in the solution root for more information. +using System.Collections.Concurrent; using PdfSharp.Drawing; using PdfSharp.Fonts.OpenType; -using PdfSharp.Internal; namespace PdfSharp.Fonts { @@ -25,18 +25,13 @@ public static FontDescriptor GetOrCreateDescriptorFor(XFont font) //FontSelector1 selector = new FontSelector1(font); string fontDescriptorKey = font.GlyphTypeface.Key; - try - { - var cache = Globals.Global.Fonts.FontDescriptorCache; - Locks.EnterFontFactory(); - if (cache.TryGetValue(fontDescriptorKey, out var descriptor)) - return descriptor; - - descriptor = new OpenTypeDescriptor(fontDescriptorKey, font); - cache.Add(fontDescriptorKey, descriptor); + var cache = Globals.Global.Fonts.FontDescriptorCache; + if (cache.TryGetValue(fontDescriptorKey, out var descriptor)) return descriptor; - } - finally { Locks.ExitFontFactory(); } + + descriptor = new OpenTypeDescriptor(fontDescriptorKey, font); + cache.TryAdd(fontDescriptorKey, descriptor); + return descriptor; } public static FontDescriptor GetOrCreateDescriptorFor(XGlyphTypeface glyphTypeface) @@ -44,18 +39,13 @@ public static FontDescriptor GetOrCreateDescriptorFor(XGlyphTypeface glyphTypefa glyphTypeface.CheckVersion(); string fontDescriptorKey = glyphTypeface.Key; - try - { - var cache = Globals.Global.Fonts.FontDescriptorCache; - Locks.EnterFontFactory(); - if (cache.TryGetValue(fontDescriptorKey, out var descriptor)) - return descriptor; - - descriptor = new OpenTypeDescriptor(fontDescriptorKey, glyphTypeface); - cache.Add(fontDescriptorKey, descriptor); + var cache = Globals.Global.Fonts.FontDescriptorCache; + if (cache.TryGetValue(fontDescriptorKey, out var descriptor)) return descriptor; - } - finally { Locks.ExitFontFactory(); } + + descriptor = new OpenTypeDescriptor(fontDescriptorKey, glyphTypeface); + cache.TryAdd(fontDescriptorKey, descriptor); + return descriptor; } /// @@ -69,23 +59,19 @@ public static FontDescriptor GetOrCreateDescriptor(string fontFamilyName, XFontS //FontSelector1 selector = new FontSelector1(fontFamilyName, style); string fontDescriptorKey = FontDescriptor.ComputeFdKey(fontFamilyName, style); - try + var cache = Globals.Global.Fonts.FontDescriptorCache; + if (!cache.TryGetValue(fontDescriptorKey, out var descriptor)) { - var cache = Globals.Global.Fonts.FontDescriptorCache; - Locks.EnterFontFactory(); - if (!cache.TryGetValue(fontDescriptorKey, out var descriptor)) - { - var font = new XFont(fontFamilyName, 10, style); - descriptor = GetOrCreateDescriptorFor(font); - // ReSharper disable once CanSimplifyDictionaryLookupWithTryAdd because there is not TryAdd in .NET Framework - if (cache.ContainsKey(fontDescriptorKey)) - _ = typeof(int); // Just a NOP for a break point. - else - cache.Add(fontDescriptorKey, descriptor); - } - return descriptor; + var font = new XFont(fontFamilyName, 10, style); + descriptor = GetOrCreateDescriptorFor(font); + // ReSharper disable once CanSimplifyDictionaryLookupWithTryAdd because there is not TryAdd in .NET Framework + if (cache.ContainsKey(fontDescriptorKey)) + _ = typeof(int); // Just a NOP for a break point. + else + cache.TryAdd(fontDescriptorKey, descriptor); } - finally { Locks.ExitFontFactory(); } + + return descriptor; } internal static void Reset() @@ -104,7 +90,7 @@ partial class FontStorage /// /// Maps font descriptor key to font descriptor which is currently only an OpenTypeFontDescriptor. /// - public readonly Dictionary FontDescriptorCache = []; + public readonly ConcurrentDictionary FontDescriptorCache = []; } } } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs index 78257168..21a4a15e 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs @@ -1,6 +1,7 @@ // PDFsharp - A .NET library for processing PDF // See the LICENSE file in the solution root for more information. +using System.Collections.Concurrent; using System.Text; #if GDI using System.Drawing; @@ -18,7 +19,6 @@ using PdfSharp.Drawing; using PdfSharp.Fonts.Internal; using PdfSharp.Fonts.OpenType; -using PdfSharp.Internal; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; using PdfSharp.Logging; @@ -42,78 +42,74 @@ static class FontFactory /// /// Information about the typeface, or null if no typeface can be found. /// - public static FontResolverInfo? ResolveTypeface(string familyName, FontResolvingOptions fontResolvingOptions, string typefaceKey, bool useFallbackFontResolver) + public static FontResolverInfo? ResolveTypeface(string familyName, FontResolvingOptions fontResolvingOptions, + string typefaceKey, bool useFallbackFontResolver) { if (String.IsNullOrEmpty(typefaceKey)) typefaceKey = XGlyphTypeface.ComputeGtfKey(familyName, fontResolvingOptions); - try - { - var fontResolverInfosByName = Globals.Global.Fonts.FontResolverInfosByName; - var fontSourcesByName = Globals.Global.Fonts.FontSourcesByName; + var fontResolverInfosByName = Globals.Global.Fonts.FontResolverInfosByName; - Locks.EnterFontFactory(); - // Was this typeface requested before? - if (fontResolverInfosByName.TryGetValue(typefaceKey, out var fontResolverInfo)) - return fontResolverInfo; + // Was this typeface requested before? + if (fontResolverInfosByName.TryGetValue(typefaceKey, out var fontResolverInfo)) + return fontResolverInfo; - // Case: This typeface was not yet resolved before. + // Case: This typeface was not yet resolved before. - // Choose the font resolver to invoke. - IFontResolver? fontResolver; - if (useFallbackFontResolver) - { - // Use fallback font resolver if one is set. - fontResolver = GlobalFontSettings.FallbackFontResolver; + // Choose the font resolver to invoke. + IFontResolver? fontResolver; + if (useFallbackFontResolver) + { + // Use fallback font resolver if one is set. + fontResolver = GlobalFontSettings.FallbackFontResolver; - if (fontResolver != null) - { - // Set a flag to prevent the case that a font resolver used as fall back font resolver - // illegally tries to invoke PlatformFontResolver.ResolveTypeface. - _fallbackFontResolverInvoked = true; - } - } - else + if (fontResolver != null) { - // Use regular font resolver if one is set. - fontResolver = GlobalFontSettings.FontResolver; + // Set a flag to prevent the case that a font resolver used as fall back font resolver + // illegally tries to invoke PlatformFontResolver.ResolveTypeface. + _fallbackFontResolverInvoked = true; } + } + else + { + // Use regular font resolver if one is set. + fontResolver = GlobalFontSettings.FontResolver; + } - if (fontResolver != null) - { - // Case: Use custom font resolver. - fontResolverInfo = fontResolver.ResolveTypeface(familyName, fontResolvingOptions.IsBold, - fontResolvingOptions.IsItalic); + if (fontResolver != null) + { + // Case: Use custom font resolver. + fontResolverInfo = fontResolver.ResolveTypeface(familyName, fontResolvingOptions.IsBold, + fontResolvingOptions.IsItalic); - // If resolved by custom font resolver register info and font source. - // If the custom font resolver calls the platform font resolver registration is already done. - if (fontResolverInfo != null && fontResolverInfo is not PlatformFontResolverInfo) - { - RegisterResolverResult(fontResolver, familyName, fontResolvingOptions, fontResolverInfo, typefaceKey); - } - } - else + // If resolved by custom font resolver register info and font source. + // If the custom font resolver calls the platform font resolver registration is already done. + if (fontResolverInfo != null && fontResolverInfo is not PlatformFontResolverInfo) { - // Case: There was no custom font resolver set. - // Use platform font resolver. - // If it was successful resolver info and font source are cached - // automatically by PlatformFontResolver.ResolveTypeface. - if (!useFallbackFontResolver) - { - fontResolverInfo = PlatformFontResolver.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey); - } + RegisterResolverResult(fontResolver, familyName, fontResolvingOptions, fontResolverInfo, + typefaceKey); } - - // Return value is null if the typeface could not be resolved. - // In this case PDFsharp throws an exception. - return fontResolverInfo; } - finally + else { - _fallbackFontResolverInvoked = false; - Locks.ExitFontFactory(); + // Case: There was no custom font resolver set. + // Use platform font resolver. + // If it was successful resolver info and font source are cached + // automatically by PlatformFontResolver.ResolveTypeface. + if (!useFallbackFontResolver) + { + fontResolverInfo = + PlatformFontResolver.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey); + } } + + // Return value is null if the typeface could not be resolved. + // In this case PDFsharp throws an exception. + + _fallbackFontResolverInvoked = false; + return fontResolverInfo; } + static bool _fallbackFontResolverInvoked; /// @@ -125,7 +121,8 @@ static class FontFactory /// /// /// - internal static void RegisterResolverResult(IFontResolver fontResolver, string familyName, FontResolvingOptions fontResolvingOptions, + internal static void RegisterResolverResult(IFontResolver fontResolver, string familyName, + FontResolvingOptions fontResolvingOptions, FontResolverInfo fontResolverInfo, string typefaceKey) { #if CORE && DEBUG @@ -136,88 +133,82 @@ internal static void RegisterResolverResult(IFontResolver fontResolver, string f if (platformInfo) Debug.Assert(fontResolver is WindowsPlatformFontResolver); #endif - try - { - var fontResolverInfosByName = Globals.Global.Fonts.FontResolverInfosByName; - var fontSourcesByName = Globals.Global.Fonts.FontSourcesByName; - - Locks.EnterFontFactory(); + var fontResolverInfosByName = Globals.Global.Fonts.FontResolverInfosByName; + var fontSourcesByName = Globals.Global.Fonts.FontSourcesByName; - // OverrideStyleSimulations is true only for internal quality tests. - // With this code we can simulate bold and/or italic for a font face even if - // a native font face exists. This is done only vor internal testing. - if (fontResolvingOptions.OverrideStyleSimulations) - { - // Override style simulation returned by CFR or OSFR. - // We do not create a new object here to preserve the fact that the font resolver info - // comes from a platform font resolver. - fontResolverInfo.MustSimulateBold = fontResolvingOptions.MustSimulateBold; - fontResolverInfo.MustSimulateItalic = fontResolvingOptions.MustSimulateItalic; - //fontResolverInfo = new FontResolverInfo(fontResolverInfo.FaceName, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, fontResolverInfo.CollectionNumber); - } + // OverrideStyleSimulations is true only for internal quality tests. + // With this code we can simulate bold and/or italic for a font face even if + // a native font face exists. This is done only vor internal testing. + if (fontResolvingOptions.OverrideStyleSimulations) + { + // Override style simulation returned by CFR or OSFR. + // We do not create a new object here to preserve the fact that the font resolver info + // comes from a platform font resolver. + fontResolverInfo.MustSimulateBold = fontResolvingOptions.MustSimulateBold; + fontResolverInfo.MustSimulateItalic = fontResolvingOptions.MustSimulateItalic; + //fontResolverInfo = new FontResolverInfo(fontResolverInfo.FaceName, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, fontResolverInfo.CollectionNumber); + } - // The typeface key was never resolved before, because otherwise we would not come here. - // But it is possible that it was mapped to the same resolver info as another typeface earlier. - string resolverInfoKey = fontResolverInfo.Key; - if (fontResolverInfosByName.TryGetValue(resolverInfoKey, out var existingFontResolverInfo)) - { - // Case: A new typeface was resolved to the same info as a previous one. - // Discard new object and reuse previous one. - fontResolverInfo = existingFontResolverInfo; - // Associate existing resolver info with the new typeface key. - fontResolverInfosByName.Add(typefaceKey, fontResolverInfo); + // The typeface key was never resolved before, because otherwise we would not come here. + // But it is possible that it was mapped to the same resolver info as another typeface earlier. + string resolverInfoKey = fontResolverInfo.Key; + if (fontResolverInfosByName.TryGetValue(resolverInfoKey, out var existingFontResolverInfo)) + { + // Case: A new typeface was resolved to the same info as a previous one. + // Discard new object and reuse previous one. + fontResolverInfo = existingFontResolverInfo; + // Associate existing resolver info with the new typeface key. + fontResolverInfosByName.TryAdd(typefaceKey, fontResolverInfo); #if DEBUG_ // The font source should exist. Debug.Assert(fontResolverInfosByName.ContainsKey(fontResolverInfo.FaceName)); #endif + } + else + { + // Case: No such font resolver info exists. + // Add typeface key to fontResolverInfosByName. + // Thereby resolving a typeface with the same key is not needed anymore. + fontResolverInfosByName.TryAdd(typefaceKey, fontResolverInfo); // Map TFK immediately to FRI. + // Add resolver info key to fontResolverInfosByName. + // Thereby a typeface with the same resolver info as a previous one is not cached twice. + fontResolverInfosByName.TryAdd(resolverInfoKey, fontResolverInfo); + + // Create font source if it does not yet exist. + // The face name is considered to be unique. So check if it already exists. + // Note that different resolver infos may map to the same font face because + // of style simulation. + if (fontSourcesByName.TryGetValue(fontResolverInfo.FaceName, out _)) + { + // Case: The font source exists, because a previous font resolver info comes + // with the same face name, but was different in style simulation flags. + // Nothing to do. } else { - // Case: No such font resolver info exists. - // Add typeface key to fontResolverInfosByName. - // Thereby resolving a typeface with the same key is not needed anymore. - fontResolverInfosByName.Add(typefaceKey, fontResolverInfo); // Map TFK immediately to FRI. - // Add resolver info key to fontResolverInfosByName. - // Thereby a typeface with the same resolver info as a previous one is not cached twice. - fontResolverInfosByName.Add(resolverInfoKey, fontResolverInfo); - - // Create font source if it does not yet exist. - // The face name is considered to be unique. So check if it already exists. - // Note that different resolver infos may map to the same font face because - // of style simulation. - if (fontSourcesByName.TryGetValue(fontResolverInfo.FaceName, out _)) + // Case: Get font from custom font resolver and create font source. + byte[]? bytes = fontResolver.GetFont(fontResolverInfo.FaceName); + if (bytes == null || bytes.Length == 0) { - // Case: The font source exists, because a previous font resolver info comes - // with the same face name, but was different in style simulation flags. - // Nothing to do. + var message = + $"The custom font resolver '{fontResolver.GetType().FullName}' resolved for typeface '{familyName}" + + $"{(fontResolvingOptions.IsItalic ? " italic" : "")}{(fontResolvingOptions.IsBold ? " bold" : "")}' " + + $"the face name '{fontResolverInfo.FaceName}', but returned no bytes for this name. " + + "This is most likely a bug in your custom font resolver."; + PdfSharpLogHost.Logger.LogCritical(message); + throw new InvalidOperationException(message); } - else - { - // Case: Get font from custom font resolver and create font source. - byte[]? bytes = fontResolver.GetFont(fontResolverInfo.FaceName); - if (bytes == null || bytes.Length == 0) - { - var message = - $"The custom font resolver '{fontResolver.GetType().FullName}' resolved for typeface '{familyName}" + - $"{(fontResolvingOptions.IsItalic ? " italic" : "")}{(fontResolvingOptions.IsBold ? " bold" : "")}' " + - $"the face name '{fontResolverInfo.FaceName}', but returned no bytes for this name. " + - "This is most likely a bug in your custom font resolver."; - PdfSharpLogHost.Logger.LogCritical(message); - throw new InvalidOperationException(message); - } - // Create a new font source if no such one exists. It can already exist if a - // custom font resolver returns the same bytes for more than one face name. - var fontSource = XFontSource.GetOrCreateFrom(bytes); + // Create a new font source if no such one exists. It can already exist if a + // custom font resolver returns the same bytes for more than one face name. + var fontSource = XFontSource.GetOrCreateFrom(bytes); - // Add font source’s font resolver name if it is different to the face name. - if (String.Compare(fontResolverInfo.FaceName, fontSource.FontName, - StringComparison.OrdinalIgnoreCase) != 0) - fontSourcesByName.Add(fontResolverInfo.FaceName, fontSource); - } + // Add font source’s font resolver name if it is different to the face name. + if (String.Compare(fontResolverInfo.FaceName, fontSource.FontName, + StringComparison.OrdinalIgnoreCase) != 0) + fontSourcesByName.TryAdd(fontResolverInfo.FaceName, fontSource); } } - finally { Locks.ExitFontFactory(); } } #if GDI /// @@ -226,31 +217,27 @@ internal static void RegisterResolverResult(IFontResolver fontResolver, string f // ReSharper disable once UnusedMember.Global public static XFontSource RegisterFontFace_unused(byte[] fontBytes) { - try + var fontSourcesByName = Globals.Global.Fonts.FontSourcesByName; + var fontSourcesByKey = Globals.Global.Fonts.FontSourcesByKey; + + ulong key = FontHelper.CalcChecksum(fontBytes); + if (fontSourcesByKey.TryGetValue(key, out var fontSource)) { - var fontSourcesByName = Globals.Global.Fonts.FontSourcesByName; - var fontSourcesByKey = Globals.Global.Fonts.FontSourcesByKey; + throw new InvalidOperationException("Font face already registered."); + } - Locks.EnterFontFactory(); - ulong key = FontHelper.CalcChecksum(fontBytes); - if (fontSourcesByKey.TryGetValue(key, out var fontSource)) - { - throw new InvalidOperationException("Font face already registered."); - } - fontSource = XFontSource.GetOrCreateFrom(fontBytes); - Debug.Assert(fontSourcesByKey.ContainsKey(key)); - Debug.Assert(fontSource.FontFace != null); + fontSource = XFontSource.GetOrCreateFrom(fontBytes); + Debug.Assert(fontSourcesByKey.ContainsKey(key)); + Debug.Assert(fontSource.FontFace != null); - //fontSource.FontFace = new OpenTypeFontFace(fontSource); - //FontSourcesByKey.Add(checksum, fontSource); - //FontSourcesByFontName.Add(fontSource.FontName, fontSource); + //fontSource.FontFace = new OpenTypeFontFace(fontSource); + //FontSourcesByKey.Add(checksum, fontSource); + //FontSourcesByFontName.Add(fontSource.FontName, fontSource); - XGlyphTypeface glyphTypeface = new XGlyphTypeface(fontSource); - fontSourcesByName.Add(glyphTypeface.Key, fontSource); - GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); - return fontSource; - } - finally { Locks.ExitFontFactory(); } + XGlyphTypeface glyphTypeface = new XGlyphTypeface(fontSource); + fontSourcesByName.TryAdd(glyphTypeface.Key, fontSource); + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); + return fontSource; } #endif @@ -318,8 +305,8 @@ internal static void CacheFontResolverInfo(string typefaceKey, FontResolverInfo throw new InvalidOperationException($"A font resolver already exists with the specified key '{fontResolverInfo.Key}'."); } // Add to both dictionaries. - fontResolverInfosByName.Add(typefaceKey, fontResolverInfo); - fontResolverInfosByName.Add(fontResolverInfo.Key, fontResolverInfo); + fontResolverInfosByName.TryAdd(typefaceKey, fontResolverInfo); + fontResolverInfosByName.TryAdd(fontResolverInfo.Key, fontResolverInfo); } /// @@ -327,12 +314,9 @@ internal static void CacheFontResolverInfo(string typefaceKey, FontResolverInfo /// public static XFontSource CacheFontSource(XFontSource fontSource) { - try + // Check whether an identical font source with a different face name already exists. + if (Globals.Global.Fonts.FontSourcesByKey.TryGetValue(fontSource.Key, out var existingFontSource)) { - Locks.EnterFontFactory(); - // Check whether an identical font source with a different face name already exists. - if (Globals.Global.Fonts.FontSourcesByKey.TryGetValue(fontSource.Key, out var existingFontSource)) - { #if DEBUG // Fonts have same length and check sum. Now check byte by byte identity. int length = fontSource.Bytes.Length; @@ -347,26 +331,25 @@ public static XFontSource CacheFontSource(XFontSource fontSource) } Debug.Assert(existingFontSource.FontFace != null); #endif - return existingFontSource; + return existingFontSource; - //FontsAreNotIdentical: - //// Incredible rare case: Two different fonts have the same size and check sum. - //// Give the new one a new key until it do not clash with an existing one. - //while (FontSourcesByKey.ContainsKey(fontSource.Key)) - // fontSource.IncrementKey(); - } + //FontsAreNotIdentical: + //// Incredible rare case: Two different fonts have the same size and check sum. + //// Give the new one a new key until it do not clash with an existing one. + //while (FontSourcesByKey.ContainsKey(fontSource.Key)) + // fontSource.IncrementKey(); + } - OpenTypeFontFace? fontFace = fontSource.FontFace; - if (fontFace == null!) - { - // Create OpenType font face for this font source. - fontSource.FontFace = new OpenTypeFontFace(fontSource); - } - Globals.Global.Fonts.FontSourcesByKey.Add(fontSource.Key, fontSource); - Globals.Global.Fonts.FontSourcesByName.Add(fontSource.FontName, fontSource); - return fontSource; + OpenTypeFontFace? fontFace = fontSource.FontFace; + if (fontFace == null!) + { + // Create OpenType font face for this font source. + fontSource.FontFace = new OpenTypeFontFace(fontSource); } - finally { Locks.ExitFontFactory(); } + + Globals.Global.Fonts.FontSourcesByKey.TryAdd(fontSource.Key, fontSource); + Globals.Global.Fonts.FontSourcesByName.TryAdd(fontSource.FontName, fontSource); + return fontSource; } /// @@ -409,21 +392,16 @@ public static XFontSource CacheNewFontSource(string typefaceKey, XFontSource fon fontSource.FontFace = fontFace; // Also sets the font name in fontSource } - Globals.Global.Fonts.FontSourcesByName.Add(typefaceKey, fontSource); - Globals.Global.Fonts.FontSourcesByName.Add(fontSource.FontName, fontSource); - Globals.Global.Fonts.FontSourcesByKey.Add(fontSource.Key, fontSource); + Globals.Global.Fonts.FontSourcesByName.TryAdd(typefaceKey, fontSource); + Globals.Global.Fonts.FontSourcesByName.TryAdd(fontSource.FontName, fontSource); + Globals.Global.Fonts.FontSourcesByKey.TryAdd(fontSource.Key, fontSource); return fontSource; } public static void CacheExistingFontSourceWithNewTypefaceKey(string typefaceKey, XFontSource fontSource) { - try - { - Locks.EnterFontFactory(); - Globals.Global.Fonts.FontSourcesByName.Add(typefaceKey, fontSource); - } - finally { Locks.ExitFontFactory(); } + Globals.Global.Fonts.FontSourcesByName.TryAdd(typefaceKey, fontSource); } public static void CheckInvocationOfPlatformFontResolver() @@ -456,7 +434,7 @@ internal static string GetFontCachesState() // FontResolverInfo by name. state.Append("====================\n"); state.Append("Font resolver info by name\n"); - Dictionary.KeyCollection keyCollection = Globals.Global.Fonts.FontResolverInfosByName.Keys; + var keyCollection = Globals.Global.Fonts.FontResolverInfosByName.Keys; count = keyCollection.Count; keys = new string[count]; keyCollection.CopyTo(keys, 0); @@ -467,14 +445,14 @@ internal static string GetFontCachesState() // FontSource by key. state.Append("Font source by key and name\n"); - Dictionary.KeyCollection fontSourceKeys = Globals.Global.Fonts.FontSourcesByKey.Keys; + var fontSourceKeys = Globals.Global.Fonts.FontSourcesByKey.Keys; count = fontSourceKeys.Count; ulong[] ulKeys = new ulong[count]; fontSourceKeys.CopyTo(ulKeys, 0); Array.Sort(ulKeys, (x, y) => x == y ? 0 : (x > y ? 1 : -1)); foreach (ulong ul in ulKeys) state.AppendFormat(" {0}: {1}\n", ul, Globals.Global.Fonts.FontSourcesByKey[ul].DebuggerDisplay); - Dictionary.KeyCollection fontSourceNames = Globals.Global.Fonts.FontSourcesByName.Keys; + var fontSourceNames = Globals.Global.Fonts.FontSourcesByName.Keys; count = fontSourceNames.Count; keys = new string[count]; fontSourceNames.CopyTo(keys, 0); @@ -506,18 +484,18 @@ partial class FontStorage /// Maps typeface key (TFK) to font resolver info (FRI) and /// maps font resolver key to font resolver info. /// - public readonly Dictionary FontResolverInfosByName = new(StringComparer.Ordinal); + public readonly ConcurrentDictionary FontResolverInfosByName = new(StringComparer.Ordinal); /// /// Maps typeface key or font name to font source. /// - public readonly Dictionary FontSourcesByName = new(StringComparer.OrdinalIgnoreCase); + public readonly ConcurrentDictionary FontSourcesByName = new(StringComparer.OrdinalIgnoreCase); /// /// Maps font source key (FSK) to font source. /// The key is a simple hash code of the font face data. /// - public readonly Dictionary FontSourcesByKey = []; + public readonly ConcurrentDictionary FontSourcesByKey = []; } } } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyCache.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyCache.cs index de77d6a5..038b5d66 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyCache.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyCache.cs @@ -1,6 +1,7 @@ // PDFsharp - A .NET library for processing PDF // See the LICENSE file in the solution root for more information. +using System.Collections.Concurrent; using System.Text; #if GDI using GdiFontFamily = System.Drawing.FontFamily; @@ -10,7 +11,6 @@ #if WPF using WpfFontFamily = System.Windows.Media.FontFamily; #endif -using PdfSharp.Internal; using PdfSharp.Fonts; namespace PdfSharp.Fonts @@ -22,13 +22,8 @@ static class FontFamilyCache { public static FontFamilyInternal? GetFamilyByName(string familyName) { - try - { - Locks.EnterFontFactory(); - Globals.Global.Fonts.FontFamiliesByName.TryGetValue(familyName, out var family); - return family; - } - finally { Locks.ExitFontFactory(); } + Globals.Global.Fonts.FontFamiliesByName.TryGetValue(familyName, out var family); + return family; } /// @@ -36,22 +31,18 @@ static class FontFamilyCache /// public static FontFamilyInternal CacheOrGetFontFamily(FontFamilyInternal fontFamily) { - try + // Recall that a font family is uniquely identified by its case-insensitive name. + if (Globals.Global.Fonts.FontFamiliesByName.TryGetValue(fontFamily.Name, out var existingFontFamily)) { - Locks.EnterFontFactory(); - // Recall that a font family is uniquely identified by its case-insensitive name. - if (Globals.Global.Fonts.FontFamiliesByName.TryGetValue(fontFamily.Name, out var existingFontFamily)) - { #if DEBUG if (fontFamily.Name == "xxx") _ = typeof(int); #endif - return existingFontFamily; - } - Globals.Global.Fonts.FontFamiliesByName.Add(fontFamily.Name, fontFamily); - return fontFamily; + return existingFontFamily; } - finally { Locks.ExitFontFactory(); } + + Globals.Global.Fonts.FontFamiliesByName.TryAdd(fontFamily.Name, fontFamily); + return fontFamily; } internal static void Reset() @@ -64,7 +55,7 @@ internal static string GetCacheState() var state = new StringBuilder(); state.Append("====================\n"); state.Append("Font families by name\n"); - Dictionary.KeyCollection familyKeys = Globals.Global.Fonts.FontFamiliesByName.Keys; + var familyKeys = Globals.Global.Fonts.FontFamiliesByName.Keys; int count = familyKeys.Count; string[] keys = new string[count]; familyKeys.CopyTo(keys, 0); @@ -86,7 +77,7 @@ partial class FontStorage /// /// Maps family name to internal font family. /// - public readonly Dictionary FontFamiliesByName = []; + public readonly ConcurrentDictionary FontFamiliesByName = []; } } } diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyInternal.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyInternal.cs index 821a202c..56086c1d 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyInternal.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Fonts/FontFamilyInternal.cs @@ -91,31 +91,22 @@ public sealed class FontFamilyInternal internal static FontFamilyInternal GetOrCreateFromName(string familyName, bool createPlatformObject) { - try + var family = FontFamilyCache.GetFamilyByName(familyName); + if (family == null) { - Locks.EnterFontFactory(); - var family = FontFamilyCache.GetFamilyByName(familyName); - if (family == null) - { - family = new FontFamilyInternal(familyName, createPlatformObject); - family = FontFamilyCache.CacheOrGetFontFamily(family); - } - return family; + family = new FontFamilyInternal(familyName, createPlatformObject); + family = FontFamilyCache.CacheOrGetFontFamily(family); } - finally { Locks.ExitFontFactory(); } + + return family; } #if GDI internal static FontFamilyInternal GetOrCreateFromGdi(GdiFontFamily gdiFontFamily) { - try - { - Locks.EnterFontFactory(); - FontFamilyInternal fontFamily = new FontFamilyInternal(gdiFontFamily); - fontFamily = FontFamilyCache.CacheOrGetFontFamily(fontFamily); - return fontFamily; - } - finally { Locks.ExitFontFactory(); } + FontFamilyInternal fontFamily = new FontFamilyInternal(gdiFontFamily); + fontFamily = FontFamilyCache.CacheOrGetFontFamily(fontFamily); + return fontFamily; } #endif