@@ -741,6 +741,12 @@ static const char* font_dir_suffixes[]
741741 " share/fonts" , " share/Fonts" , " share/fonts/OpenImageIO" };
742742// static const char* font_extensions[] = { "", ".ttf", ".ttc", ".pfa", ".pfb" };
743743
744+ // list of available font families
745+ static std::vector<std::string> s_font_families;
746+ // available font styles per families
747+ static std::unordered_map<std::string, std::vector<std::string>> s_font_styles;
748+ // font filenames per family and style (e.g. "Arial Italic")
749+ static std::unordered_map<std::string, std::string> s_font_filename_per_family;
744750
745751
746752// Add one dir to font_search_dirs, if the dir exists.
@@ -950,6 +956,86 @@ text_size_from_unicode(cspan<uint32_t> utext, FT_Face face, int fontsize)
950956}
951957
952958
959+ // Read available font families and styles.
960+ static void
961+ init_font_families ()
962+ {
963+ // skip if already initialized
964+ if (!s_font_families.empty ())
965+ return ;
966+
967+ // If we know FT is broken, don't bother trying again
968+ if (ft_broken)
969+ return ;
970+
971+ // If FT not yet initialized, do it now.
972+ if (!ft_library) {
973+ if (FT_Init_FreeType (&ft_library)) {
974+ ft_broken = true ;
975+ return ;
976+ }
977+ }
978+
979+ // read available fonts
980+ std::unordered_set<std::string> font_family_set;
981+ std::unordered_map<std::string, std::unordered_set<std::string>>
982+ font_style_set;
983+ const std::vector<std::string>& font_files = pvt::font_file_list ();
984+ for (const std::string& filename : font_files) {
985+ // Load the font.
986+ FT_Face face;
987+ int error = FT_New_Face (ft_library, filename.c_str (),
988+ 0 /* face index */ , &face);
989+ if (error)
990+ continue ;
991+
992+ // Ignore if the font fmaily name is not defined.
993+ if (!face->family_name ) {
994+ FT_Done_Face (face);
995+ continue ;
996+ }
997+
998+ // Store the font family.
999+ std::string family = std::string (face->family_name );
1000+ font_family_set.insert (family);
1001+
1002+ // Store the font style.
1003+ std::string style = face->style_name ? std::string (face->style_name )
1004+ : std::string ();
1005+ if (!style.empty ()) {
1006+ std::unordered_set<std::string>& styles = font_style_set[family];
1007+ styles.insert (style);
1008+ }
1009+
1010+ // Store the filename. Use the family and style as the key (e.g. "Arial Italic").
1011+ std::string font_name = family;
1012+ if (!style.empty ())
1013+ font_name += " " + style;
1014+ s_font_filename_per_family[font_name] = filename;
1015+
1016+ // Store regular fonts also with the family name only (e.g. "Arial Regular" as "Arial").
1017+ if (style == " Regular" )
1018+ s_font_filename_per_family[family] = filename;
1019+
1020+ FT_Done_Face (face);
1021+ }
1022+
1023+ // Sort font families.
1024+ s_font_families = std::vector<std::string>(font_family_set.begin (),
1025+ font_family_set.end ());
1026+ std::sort (s_font_families.begin (), s_font_families.end ());
1027+
1028+ // Sort font styles.
1029+ for (auto it : font_style_set) {
1030+ const std::string& family = it.first ;
1031+ std::unordered_set<std::string>& styles_set = it.second ;
1032+ std::vector<std::string> styles (styles_set.begin (), styles_set.end ());
1033+ std::sort (styles.begin (), styles.end ());
1034+ s_font_styles[family] = styles;
1035+ }
1036+ }
1037+
1038+
9531039// Given font name, resolve it to an existing font filename.
9541040// If found, return true and put the resolved filename in result.
9551041// If not found, return false and put an error message in result.
@@ -988,12 +1074,26 @@ resolve_font(string_view font_, std::string& result)
9881074 result = " Could not set default font face" ;
9891075 return false ;
9901076 }
991- }
992- if (!Filesystem::is_regular (font)) {
1077+ } else if (Filesystem::is_regular (font)) {
1078+ // directly specified filename -- use it
1079+ } else {
9931080 // A font name was specified but it's not a full path, look for it
994- auto f = font_file_map.find (font);
995- if (f != font_file_map.end ()) {
996- font = f->second ;
1081+ std::string f;
1082+
1083+ // first look for a font with the given family and style
1084+ init_font_families ();
1085+ if (s_font_filename_per_family.find (font)
1086+ != s_font_filename_per_family.end ())
1087+ f = s_font_filename_per_family[font];
1088+
1089+ // then look for a font with the given filename
1090+ if (f.empty ()) {
1091+ if (font_file_map.find (font) != font_file_map.end ())
1092+ f = font_file_map[font];
1093+ }
1094+
1095+ if (!f.empty ()) {
1096+ font = f;
9971097 } else {
9981098 result = Strutil::fmt::format (" Could not find font \" {}\" " , font);
9991099 return false ;
0 commit comments