17
17
using System . Text ;
18
18
using System . Threading ;
19
19
using SkiaSharp ;
20
+ using SkiaSharp . HarfBuzz ;
20
21
using UglyToad . PdfPig . Core ;
21
22
using UglyToad . PdfPig . Fonts . SystemFonts ;
22
23
using UglyToad . PdfPig . PdfFonts ;
23
24
24
25
namespace UglyToad . PdfPig . Rendering . Skia . Helpers
25
26
{
26
- internal sealed class FontCache : IDisposable
27
+ internal sealed class SkiaFontCache : IDisposable
27
28
{
28
29
private readonly ConcurrentDictionary < IFont , ConcurrentDictionary < int , Lazy < SKPath > > > _cache =
29
30
new ConcurrentDictionary < IFont , ConcurrentDictionary < int , Lazy < SKPath > > > ( ) ;
30
31
31
- private readonly ConcurrentDictionary < string , SKTypeface > _typefaces =
32
- new ConcurrentDictionary < string , SKTypeface > ( ) ;
32
+ private readonly ConcurrentDictionary < string , SkiaFontCacheItem > _typefaces =
33
+ new ConcurrentDictionary < string , SkiaFontCacheItem > ( ) ;
33
34
34
35
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim ( ) ;
35
36
36
37
private readonly SKFontManager _skFontManager = SKFontManager . CreateDefault ( ) ;
37
38
38
- public SKTypeface GetTypefaceOrFallback ( IFont font , string unicode )
39
+ internal sealed class SkiaFontCacheItem : IDisposable
40
+ {
41
+ public SkiaFontCacheItem ( SKTypeface typeface )
42
+ {
43
+ if ( typeface is null )
44
+ {
45
+ throw new ArgumentNullException ( nameof ( typeface ) ) ;
46
+ }
47
+
48
+ Typeface = typeface ;
49
+ Shaper = new SKShaper ( Typeface ) ;
50
+ }
51
+
52
+ public SKTypeface Typeface { get ; }
53
+
54
+ public SKShaper Shaper { get ; }
55
+
56
+ public void Dispose ( )
57
+ {
58
+ Typeface . Dispose ( ) ;
59
+ Shaper . Dispose ( ) ;
60
+ }
61
+ }
62
+
63
+ private static string GetFontKey ( IFont font )
64
+ {
65
+ if ( string . IsNullOrEmpty ( font . Name ? . Data ) )
66
+ {
67
+ throw new NullReferenceException ( "The font's name is null." ) ;
68
+ }
69
+
70
+ return $ "{ font . Name . Data } |{ ( font . Details . IsBold ? ( byte ) 1 : ( byte ) 0 ) } |{ ( font . Details . IsItalic ? ( byte ) 1 : ( byte ) 0 ) } ";
71
+ }
72
+
73
+ public SkiaFontCacheItem GetTypefaceOrFallback ( IFont font , string unicode )
39
74
{
40
75
if ( IsDisposed ( ) )
41
76
{
42
- throw new ObjectDisposedException ( nameof ( FontCache ) ) ;
77
+ throw new ObjectDisposedException ( nameof ( SkiaFontCache ) ) ;
43
78
}
44
79
45
80
_lock . EnterReadLock ( ) ;
46
81
try
47
82
{
48
83
if ( IsDisposed ( ) )
49
84
{
50
- throw new ObjectDisposedException ( nameof ( FontCache ) ) ;
85
+ throw new ObjectDisposedException ( nameof ( SkiaFontCache ) ) ;
51
86
}
52
87
53
- using ( var style = font . Details . GetFontStyle ( ) )
54
- {
55
- var codepoint = BitConverter . ToInt32 ( Encoding . UTF32 . GetBytes ( unicode ) , 0 ) ;
56
-
57
- if ( _typefaces . TryGetValue ( font . Name , out SKTypeface drawTypeface ) && drawTypeface != null &&
58
- ( string . IsNullOrWhiteSpace ( unicode ) ||
59
- drawTypeface . ContainsGlyph ( codepoint ) ) ) // Check if can render
60
- {
61
- if ( FontStyleEquals ( drawTypeface . FontStyle , style ) )
62
- {
63
- return drawTypeface ;
64
- }
88
+ string fontKey = GetFontKey ( font ) ;
65
89
66
- drawTypeface = _skFontManager . MatchFamily ( drawTypeface . FamilyName , style ) ;
90
+ var codepoint = BitConverter . ToInt32 ( Encoding . UTF32 . GetBytes ( unicode ) , 0 ) ;
67
91
68
- if ( drawTypeface != null )
69
- {
70
- return drawTypeface ;
71
- }
72
- }
92
+ if ( _typefaces . TryGetValue ( fontKey , out SkiaFontCacheItem skiaFontCacheItem ) &&
93
+ ( string . IsNullOrWhiteSpace ( unicode ) || skiaFontCacheItem . Typeface . ContainsGlyph ( codepoint ) ) ) // Check if can render
94
+ {
95
+ return skiaFontCacheItem ;
96
+ }
73
97
98
+ using ( var style = font . Details . GetFontStyle ( ) )
99
+ {
74
100
string cleanFontName = font . GetCleanFontName ( ) ;
75
101
76
- drawTypeface = SKTypeface . FromFamilyName ( cleanFontName , style ) ;
77
-
78
- if ( drawTypeface . FamilyName . Equals ( SKTypeface . Default . FamilyName ) )
102
+ var typeface = SKTypeface . FromFamilyName ( cleanFontName , style ) ;
103
+ if ( typeface . FamilyName . Equals ( SKTypeface . Default . FamilyName ) )
79
104
{
80
105
var trueTypeFont = SystemFontFinder . Instance . GetTrueTypeFont ( cleanFontName ) ;
81
106
82
- if ( trueTypeFont != null &&
83
- ! string . IsNullOrEmpty ( trueTypeFont . TableRegister . NameTable . FontFamilyName ) )
107
+ string ? fontFamilyName = trueTypeFont ? . TableRegister ? . NameTable ? . FontFamilyName ;
108
+
109
+ if ( ! string . IsNullOrEmpty ( fontFamilyName ) )
84
110
{
85
- drawTypeface . Dispose ( ) ;
86
- drawTypeface =
87
- SKTypeface . FromFamilyName ( trueTypeFont . TableRegister . NameTable . FontFamilyName , style ) ;
111
+ typeface . Dispose ( ) ;
112
+ typeface = SKTypeface . FromFamilyName ( fontFamilyName , style ) ;
88
113
}
89
114
}
90
115
91
116
// Fallback font
92
117
// https://github.com/mono/SkiaSharp/issues/232
93
- if ( ! string . IsNullOrWhiteSpace ( unicode ) && ! drawTypeface . ContainsGlyph ( codepoint ) )
118
+ if ( ! string . IsNullOrWhiteSpace ( unicode ) && ! typeface . ContainsGlyph ( codepoint ) )
94
119
{
95
120
var fallback = _skFontManager . MatchCharacter ( codepoint ) ; // Access violation here
96
121
if ( fallback != null )
97
122
{
98
- drawTypeface . Dispose ( ) ;
99
- drawTypeface = _skFontManager . MatchFamily ( fallback . FamilyName , style ) ;
123
+ typeface . Dispose ( ) ;
124
+ typeface = _skFontManager . MatchFamily ( fallback . FamilyName , style ) ;
100
125
}
101
126
}
102
127
103
- _typefaces [ font . Name ] = drawTypeface ;
128
+ skiaFontCacheItem = new SkiaFontCacheItem ( typeface ) ;
129
+
130
+ System . Diagnostics . Debug . Assert ( ! _typefaces . ContainsKey ( fontKey ) ) ;
104
131
105
- return drawTypeface ;
132
+ _typefaces [ fontKey ] = skiaFontCacheItem ;
133
+
134
+ return skiaFontCacheItem ;
106
135
}
107
136
}
108
137
finally
@@ -114,13 +143,6 @@ public SKTypeface GetTypefaceOrFallback(IFont font, string unicode)
114
143
}
115
144
}
116
145
117
- private static bool FontStyleEquals ( SKFontStyle fontStyle1 , SKFontStyle fontStyle2 )
118
- {
119
- return fontStyle1 . Width == fontStyle2 . Width &&
120
- fontStyle1 . Weight == fontStyle2 . Weight &&
121
- fontStyle1 . Slant == fontStyle2 . Slant ;
122
- }
123
-
124
146
private static SKPath GetPathInternal ( IFont font , int code )
125
147
{
126
148
// TODO - check if font can even have path info
0 commit comments