Skip to content

Commit fe72afb

Browse files
committed
improved font selection for Unix (no fallback)
1 parent 35bd9a4 commit fe72afb

File tree

2 files changed

+93
-115
lines changed

2 files changed

+93
-115
lines changed

modules/ILL/ILL.moon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module_version = "1.7.2"
1+
module_version = "1.7.3"
22

33
haveDepCtrl, DependencyControl = pcall require, "l0.DependencyControl"
44

modules/ILL/ILL/Font/Unx.moon

Lines changed: 92 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -565,40 +565,49 @@ class FreeType extends Init
565565
init: =>
566566
unless has_freetype
567567
error "freetype library couldn't be loaded", 2
568-
568+
@face_cache = {}
569569
-- Check that the font has a bold and italic variant if necessary
570570
@found_bold, @found_italic = false, false
571-
572-
-- Get the font path
573-
font_path = @getFontPath!
574-
unless font_path
575-
error "Couldn't find #{@family} among your fonts"
576-
577571
-- Init FreeType
578-
@library = new "FT_Library[1]"
579-
err = freetype.FT_Init_FreeType @library
580-
581-
if err != 0
582-
error "Failed to load freetype library"
583-
584-
ffi.gc @library, (lib) -> freetype.FT_Done_FreeType lib[0]
585-
572+
@library = @getLib!
573+
-- Get the font path
574+
font = @fontSelect!
586575
-- Load font face
587-
@face = new "FT_Face[1]"
588-
err = freetype.FT_New_Face @library[0], font_path, 0, @face
589-
590-
if err != 0
591-
error "Failed to load freetype face"
592-
593-
ffi.gc @face, (face) -> freetype.FT_Done_Face face[0]
594-
576+
@face = @getFaceFromPath font.path
595577
set_font_metrics @face[0]
596578
ass_face_set_size @face[0], @size
597-
598579
@ascender, @descender = ass_font_get_asc_desc @face[0]
599580
@height = @ascender + @descender
600581
@weight = tonumber ass_face_get_weight @face[0]
601582

583+
-- Get FreeType library
584+
getLib: =>
585+
library = new "FT_Library[1]"
586+
err = freetype.FT_Init_FreeType library
587+
if err != 0
588+
error "Failed to load freetype library"
589+
ffi.gc library, (lib) -> freetype.FT_Done_FreeType lib[0]
590+
return library
591+
592+
-- Get face from path
593+
getFaceFromPath: (path) =>
594+
face = @face_cache[path]
595+
return face if face
596+
face = new "FT_Face[1]"
597+
err = freetype.FT_New_Face @library[0], path, 0, face
598+
if err != 0
599+
error "Failed to load freetype face"
600+
ffi.gc face, (f) -> freetype.FT_Done_Face f[0]
601+
@face_cache[path] = face
602+
return face
603+
604+
-- Check if a glyph exists for a given codepoint in a specific font path
605+
hasGlyphFromPath: (path, codepoint) =>
606+
return true if codepoint == 0
607+
face = @getFaceFromPath path
608+
return false unless face
609+
freetype.FT_Get_Char_Index(face[0], codepoint) != 0
610+
602611
-- Callback to access the glyphs for each character
603612
callBackCharsGlyph: (text, callback) =>
604613
face_size = @face[0].size.face
@@ -633,7 +642,6 @@ class FreeType extends Init
633642
paths, x = {}, 0
634643
@callBackCharsGlyph text, (glyph) ->
635644
build, path = {}, {}
636-
-- FIXME
637645
if @bold and @weight < 700 and not @found_bold
638646
ass_glyph_embolden glyph
639647
if @italic and not @found_italic
@@ -710,7 +718,7 @@ class FreeType extends Init
710718
table.insert paths, table.concat path, " "
711719
if @underline or @strikeout
712720
table.insert paths, ass_get_glyph_outline @face[0], @underline, @strikeout, x, @ascender * FONT_UPSCALE
713-
x += tonumber(glyph.metrics.horiAdvance) + (@hspace * FONT_UPSCALE)
721+
x += tonumber(glyph.metrics.horiAdvance) + (@hspace * FONT_UPSCALE) - (0.1 * FONT_UPSCALE)
714722
return table.concat paths, " "
715723

716724
-- Gets the complete list of fonts available on the system
@@ -755,96 +763,66 @@ class FreeType extends Init
755763
-- Return collected fonts
756764
return fonts
757765

766+
-- Normalize a string for font matching
767+
norm: (s) ->
768+
return "" unless s
769+
s\lower!\gsub("[%s%-%_]+", " ")\match "^%s*(.-)%s*$"
770+
771+
-- Check if a style is bold
772+
is_bold: (s) ->
773+
z = FreeType.norm s
774+
z\find("bold") or z\find("black") or z\find("heavy") or z\find("semibold") or z\find("demibold") or z\find("demi")
775+
776+
-- Check if a style is italic
777+
is_italic: (s) ->
778+
z = FreeType.norm s
779+
z\find("italic") or z\find("oblique")
780+
781+
-- Check if a style is regular
782+
is_regular: (s) -> not FreeType.is_bold(s) and not FreeType.is_italic(s)
783+
784+
-- Score a font based on bold and italic requirements
785+
scoreFont: (font, bold, italic) ->
786+
style = FreeType.norm font.style
787+
font_bold = style\find("bold") or style\find("black") or style\find("heavy") or style\find("semibold") or style\find("demi")
788+
font_italic = style\find("italic") or style\find("oblique")
789+
score = 0
790+
if bold == font_bold and italic == font_italic
791+
return 0
792+
if bold and not font_bold
793+
score += 200
794+
if not bold and font_bold
795+
score += 300
796+
if italic and not font_italic
797+
score += 200
798+
if not italic and font_italic
799+
score += 300
800+
if style\find "condensed"
801+
score += 20
802+
if style\find "narrow"
803+
score += 10
804+
return score
805+
758806
-- Gets the directory path of the best matching font for the requested family
759-
getFontPath: =>
807+
fontSelect: =>
760808
fonts = @getFonts!
761-
762-
norm = (s) ->
763-
return "" unless s
764-
return s\lower!\gsub("%s+", " ")\match "^%s*(.-)%s*$"
765-
766-
-- remove only style tokens
767-
strip_style_words = (s) ->
768-
return "" unless s
769-
s = norm s\gsub "[%s%-_]+", " "
770-
-- words that represent style
771-
style_words = {
772-
"bold",
773-
"black",
774-
"heavy",
775-
"semibold",
776-
"demibold",
777-
"demi",
778-
"italic",
779-
"oblique",
780-
"regular",
781-
"light",
782-
"medium",
783-
"thin",
784-
"condensed",
785-
"narrow"
786-
}
787-
style_set = {}
788-
for i = 1, #style_words
789-
style_set[style_words[i]] = true
790-
out = {}
791-
for token in s\gmatch "%S+"
792-
unless style_set[token]
793-
table.insert out, token
794-
return table.concat out, " "
795-
796-
family = strip_style_words @family
797-
is_bold = (s) -> s\lower!\find("bold") != nil or s\find("black") != nil or s\find("heavy") != nil or s\find("semibold") != nil or s\find("demibold") != nil or s\find("demi") != nil
798-
is_italic = (s) -> s\lower!\find("italic") != nil or s\find("oblique") != nil
799-
is_regular = (s) -> not is_bold(s) and not is_italic(s)
800-
801-
candidates = {}
802-
for i = 1, #fonts
809+
family = FreeType.norm @family
810+
best, best_score = nil, math.huge
811+
for i = 1, fonts.n
803812
font = fonts[i]
804-
if norm(font.name) == family
805-
table.insert candidates, font
806-
807-
return false if #candidates == 0
808-
809-
score_font = (font) ->
810-
style = norm font.style
811-
score = 0
812-
font_bold = is_bold style
813-
font_italic = is_italic style
814-
-- exact match
815-
if bold == font_bold and italic == font_italic
816-
score += 100
817-
-- fallbacks
818-
if bold and italic
819-
if font_bold and not font_italic
820-
score += 70
821-
if font_italic and not font_bold
822-
score += 60
823-
elseif bold and not italic
824-
if font_bold
825-
score += 80
826-
elseif not bold and italic
827-
if font_italic
828-
score += 80
829-
else
830-
if is_regular style
831-
score += 50
832-
-- light penalties
833-
if style\find "condensed"
834-
score -= 5
835-
if style\find "narrow"
836-
score -= 2
837-
return score
838-
839-
table.sort candidates, (a, b) -> score_font(b) < score_font(a)
840-
chosen = candidates[1]
841-
842-
style = norm chosen.style
843-
844-
@found_italic = is_italic style
845-
@found_bold = is_bold style
846-
@found_regular = is_regular style
847-
848-
return chosen.path
813+
if FreeType.norm(font.name) != family and FreeType.norm(font.longname) != family
814+
continue
815+
unless @hasGlyphFromPath font.path, UTF8.charcodepoint "a"
816+
continue
817+
score = FreeType.scoreFont font, @bold, @italic
818+
if score < best_score
819+
best = font
820+
best_score = score
821+
break if score == 0
822+
unless best
823+
error "Couldn't find #{@family} among your fonts"
824+
@found_italic = FreeType.is_italic best.style
825+
@found_bold = FreeType.is_bold best.style
826+
return best
849827

850828
{:FreeType}

0 commit comments

Comments
 (0)