Skip to content

Commit 95d246d

Browse files
feat: Lookup font in text render based on family and style name (#4509)
Fixes #4502. This PR lets you specify fonts (for `IBA::render_text()` or `oiiotool --text`) by family and style name, rather than only by filename of the font file. The implementation is to add `init_font_families()` function, that iterates over the available font files, reads family and style name via the freetype API and caches them. The function is called from `resolve_font()` on demand. Added a new test to the oiiotool-text folder, that loads a font by the name "Droid Serif Bold". --------- Signed-off-by: peter.horvath <[email protected]>
1 parent c8e4ffe commit 95d246d

File tree

4 files changed

+113
-6
lines changed

4 files changed

+113
-6
lines changed

src/libOpenImageIO/imagebufalgo_draw.cpp

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
4.56 KB
Binary file not shown.

testsuite/oiiotool-text/ref/out.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ Comparing "textshadowed.tif" and "ref/textshadowed.tif"
66
PASS
77
Comparing "textalpha.tif" and "ref/textalpha.tif"
88
PASS
9+
Comparing "fontbyfamily.tif" and "ref/fontbyfamily.tif"
10+
PASS
911
Comparing "unicode.tif" and "ref/unicode.tif"
1012
PASS

testsuite/oiiotool-text/run.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@
3838
"--text:x=20:y=120:color=.5,.5,.5,.5:size=50 \"Hello, world\" "
3939
"-d uint8 -o textalpha.tif")
4040

41+
# test lookup font by family and style name
42+
command += oiiotool ("--create 320x240 3 "
43+
"\"--text:x=25:y=120:font=Droid Serif Bold:size=40\" \"Hello, world\" "
44+
"-d uint8 -o fontbyfamily.tif")
45+
4146
# Outputs to check against references
42-
outputs = [ "text.tif", "aligned.tif", "textshadowed.tif", "textalpha.tif" ]
47+
outputs = [ "text.tif", "aligned.tif", "textshadowed.tif", "textalpha.tif", "fontbyfamily.tif" ]
4348

4449
# Test Unicode characters. But only when not on Windows, because I just
4550
# can't figure out how to get Unicode chars on the command line properly

0 commit comments

Comments
 (0)