@@ -12,6 +12,7 @@ using FreeType
12
12
13
13
const REGULAR_STYLES = " regular" , " normal" , " medium" , " standard" , " roman" , " book"
14
14
const FT_LIB = FT_Library[C_NULL ]
15
+ const LIB_LOCK = ReentrantLock ()
15
16
const VALID_FONTPATHS = String[]
16
17
17
18
struct FontExtent{T}
23
24
24
25
mutable struct FTFont
25
26
ft_ptr:: FT_Face
27
+ lock:: ReentrantLock # lock this for the duration of any FT operation on ft_ptr
26
28
function FTFont (ft_ptr:: FT_Face )
27
- face = new (ft_ptr)
28
- finalizer (
29
- face -> (face. ft_ptr != C_NULL && FT_LIB[1 ] != C_NULL ) && FT_Done_Face (face),
30
- face,
31
- )
29
+ face = new (ft_ptr, ReentrantLock ())
30
+ finalizer (safe_free, face)
32
31
face
33
32
end
34
33
end
35
- FTFont (path:: String ) = FTFont (newface (path))
34
+
35
+ function safe_free (face:: FTFont )
36
+ @lock face. lock begin
37
+ (face. ft_ptr != C_NULL && FT_LIB[1 ] != C_NULL ) && FT_Done_Face (face)
38
+ end
39
+ end
40
+
41
+ FTFont (path:: String ) = FTFont (new_face (path))
36
42
FTFont (:: Nothing ) = nothing
37
43
38
44
family_name (font:: FTFont ) = lowercase (ft_property (font, :family_name ))
@@ -43,9 +49,9 @@ Base.propertynames(font::FTFont) = fieldnames(FT_FaceRec)
43
49
Base. cconvert (:: Type{FT_Face} , font:: FTFont ) = font
44
50
Base. unsafe_convert (:: Type{FT_Face} , font:: FTFont ) = font. ft_ptr
45
51
46
- function ft_property (font :: FTFont , fieldname:: Symbol )
47
- fontrect = unsafe_load (font . ft_ptr)
48
- if (field = getfield (fontrect , fieldname)) isa Ptr{FT_String}
52
+ function ft_property (face :: FTFont , fieldname:: Symbol )
53
+ font_rect = @lock face . lock unsafe_load (face . ft_ptr)
54
+ if (field = getfield (font_rect , fieldname)) isa Ptr{FT_String}
49
55
field == C_NULL && return " "
50
56
unsafe_string (field)
51
57
else
@@ -60,10 +66,10 @@ Base.show(io::IO, font::FTFont) = print(
60
66
61
67
check_error (err, error_msg) = err == 0 || error (" $error_msg with error: $err " )
62
68
63
- function newface (facename, faceindex :: Real = 0 , ftlib = FT_LIB)
69
+ function new_face (name, index :: Real = 0 , ftlib = FT_LIB)
64
70
face = Ref {FT_Face} ()
65
- err = FT_New_Face (ftlib[1 ], facename , Int32 (faceindex ), face)
66
- check_error (err, " Couldn't load font $facename " )
71
+ err = @lock LIB_LOCK FT_New_Face (ftlib[1 ], name , Int32 (index ), face)
72
+ check_error (err, " Couldn't load font $name " )
67
73
face[]
68
74
end
69
75
@@ -87,11 +93,12 @@ fallback_fonts() =
87
93
const FT_FONTS = Dict {String,FTFont} ()
88
94
89
95
"""
90
- Match a font using the user-specified search string. Each part of the search string
91
- is searched in the family name first which has to match once to include the font
92
- in the candidate list. For fonts with a family match the style
93
- name is matched next. For fonts with the same family and style name scores, regular
94
- fonts are preferred (any font that is "regular", "normal", "medium", "standard" or "roman")
96
+ Match a font using the user-specified search string.
97
+ Each part of the search string is searched in the family name first,
98
+ which has to match once to include the font in the candidate list.
99
+ For fonts with a family match the style name is matched next.
100
+ For fonts with the same family and style name scores, regular fonts are preferred
101
+ (any font that is "regular", "normal", "medium", "standard" or "roman"),
95
102
and as a last tie-breaker, shorter overall font names are preferred.
96
103
97
104
Example:
@@ -163,7 +170,7 @@ function find_font(searchstring::String; additional_fonts::String = "")
163
170
# we can compare all four tuple elements of the score at once in order of importance:
164
171
# 1. number of family match characters
165
172
# 2. number of style match characters
166
- # 3. is font a "regular" style variant?
173
+ # 3. is font a "regular" style variant ?
167
174
# 4. the negative length of the font name, the shorter the better
168
175
if (family_match_score = first (score)) > 0 && score > best_score
169
176
best_fpath = fpath
@@ -201,34 +208,39 @@ FontExtent(func::Function, ext::FontExtent) = FontExtent(
201
208
)
202
209
203
210
function set_pixelsize (face:: FTFont , size:: Integer )
204
- check_error (FT_Set_Pixel_Sizes (face, size, size), " Couldn't set pixelsize" )
211
+ @lock face. lock check_error (
212
+ FT_Set_Pixel_Sizes (face, size, size),
213
+ " Couldn't set pixelsize" ,
214
+ )
205
215
size
206
216
end
207
217
208
- glyph_index (face:: FTFont , glyphname:: String ):: UInt64 = FT_Get_Name_Index (face, glyphname)
209
- glyph_index (face:: FTFont , char:: Char ):: UInt64 = FT_Get_Char_Index (face, char)
218
+ glyph_index (face:: FTFont , glyphname:: String ):: UInt64 =
219
+ @lock face. lock FT_Get_Name_Index (face, glyphname)
220
+ glyph_index (face:: FTFont , char:: Char ):: UInt64 =
221
+ @lock face. lock FT_Get_Char_Index (face, char)
210
222
glyph_index (face:: FTFont , idx:: Integer ) = UInt64 (idx)
211
223
212
224
function kerning (face:: FTFont , glyphspecs... )
213
225
i1, i2 = glyph_index .(Ref (face), glyphspecs)
214
226
kerning2d = Ref {FT_Vector} ()
215
- err = FT_Get_Kerning (face, i1, i2, FT_KERNING_DEFAULT, kerning2d)
216
- # can error if font has no kerning! Since that's somewhat expected, we just return 0
227
+ err = @lock face . lock FT_Get_Kerning (face, i1, i2, FT_KERNING_DEFAULT, kerning2d)
228
+ # can error if font has no kerning ! Since that's somewhat expected, we just return 0
217
229
err == 0 || return SVector (0.0 , 0.0 )
218
230
divisor = 64 # 64 since metrics are in 1/64 units (units to 26.6 fractional pixels)
219
231
SVector (kerning2d[]. x / divisor, kerning2d[]. y / divisor)
220
232
end
221
233
222
234
function load_glyph (face:: FTFont , glyph)
223
235
gi = glyph_index (face, glyph)
224
- err = FT_Load_Glyph (face, gi, FT_LOAD_RENDER)
236
+ err = @lock face . lock FT_Load_Glyph (face, gi, FT_LOAD_RENDER)
225
237
check_error (err, " Could not load glyph $(repr (glyph)) from $face to render." )
226
238
end
227
239
228
240
function load_glyph (face:: FTFont , glyph, pixelsize:: Integer ; set_pix = true )
229
241
set_pix && set_pixelsize (face, pixelsize)
230
242
load_glyph (face, glyph)
231
- gl = unsafe_load (ft_property (face, :glyph ))
243
+ gl = @lock face . lock unsafe_load (ft_property (face, :glyph ))
232
244
@assert gl. format == FT_GLYPH_FORMAT_BITMAP
233
245
gl
234
246
end
@@ -258,7 +270,7 @@ one_or_typemax(::Type{T}) where {T<:Union{Real,Colorant}} =
258
270
259
271
"""
260
272
render_string!(img::AbstractMatrix, str::String, face, pixelsize, y0, x0;
261
- fcolor=one_or_typemax(T), bcolor=zero(T), halign=:hleft, valign=:vbaseline) -> Matrix
273
+ fcolor=one_or_typemax(T), bcolor=zero(T), halign=:hleft, valign=:vbaseline) -> Matrix
262
274
Render `str` into `img` using the font `face` of size `pixelsize` at coordinates `y0,x0`.
263
275
Uses the conventions of freetype.org/freetype2/docs/glyphs/glyphs-3.html
264
276
# Arguments
@@ -414,16 +426,22 @@ function UnicodePlots.render_string!(
414
426
end
415
427
416
428
function ft_init ()
417
- FT_LIB[1 ] != C_NULL && error (" Freetype already initialized. init() called two times ?" )
418
- FT_Init_FreeType (FT_LIB) == 0
429
+ @lock LIB_LOCK begin
430
+ FT_LIB[1 ] != C_NULL &&
431
+ error (" Freetype already initialized. init() called two times ?" )
432
+ FT_Init_FreeType (FT_LIB) == 0
433
+ end
419
434
end
420
435
421
436
function ft_done ()
422
- FT_LIB[1 ] == C_NULL &&
423
- error (" Library == CNULL. done() called before init(), or done called two times ?" )
424
- err = FT_Done_FreeType (FT_LIB[1 ])
425
- FT_LIB[1 ] = C_NULL
426
- err == 0
437
+ @lock LIB_LOCK begin
438
+ FT_LIB[1 ] == C_NULL && error (
439
+ " Library == CNULL. done() called before init(), or done called two times ?" ,
440
+ )
441
+ err = FT_Done_FreeType (FT_LIB[1 ])
442
+ FT_LIB[1 ] = C_NULL
443
+ err == 0
444
+ end
427
445
end
428
446
429
447
add_recursive (result, path) =
@@ -441,11 +459,11 @@ function __init__()
441
459
# so we supply a way to help it with an environment variable.
442
460
font_paths = if Sys. isapple () # COV_EXCL_LINE
443
461
[
444
- " /Library/Fonts" , # Additional fonts that can be used by all users. This is generally where fonts go if they are to be used by other applications.
445
- joinpath (homedir (), " Library/Fonts" ), # Fonts specific to each user.
446
- " /Network/Library/Fonts" , # Fonts shared for users on a network
447
- " /System/Library/Fonts" , # System specific fonts
448
- " /System/Library/Fonts/Supplemental" , # new location since Catalina
462
+ " /Library/Fonts" , # additional fonts that can be used by all users: this is generally where fonts go if they are to be used by other applications
463
+ joinpath (homedir (), " Library/Fonts" ), # fonts specific to each user
464
+ " /Network/Library/Fonts" , # fonts shared for users on a network
465
+ " /System/Library/Fonts" , # system specific fonts
466
+ " /System/Library/Fonts/Supplemental" , # new location since Catalina
449
467
]
450
468
elseif Sys. iswindows () # COV_EXCL_LINE
451
469
[
0 commit comments