From cd54ee0d52de8d68f1ac259cc74b737aeeddaa68 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Sat, 7 Mar 2015 14:02:09 -0500 Subject: [PATCH] add support for styled fonts outside of SDL TTF in GUI1, help This commit allows bold and italic font files to be specified in the fonts.cfg file. When attribute "bold_name" is set to the name of a font file, this font will be used in place of SDL TTF bold- styling applied to the base font. Similarly for "italic_name". This commit assumes that the code points for the styled versions are the same as for the unstyled version. If that assumption is not true for some font that we care about then this commit will likely have to be revised. --- src/font.cpp | 231 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 152 insertions(+), 79 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index 89246f2b8c8b..570c358d3fa9 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -34,6 +34,7 @@ #include "serialization/unicode.hpp" #include +#include #include #include @@ -66,22 +67,26 @@ typedef int subset_id; struct font_id { - font_id(subset_id subset, int size) : subset(subset), size(size) {} + font_id(subset_id subset, int size) : subset(subset), size(size), style(TTF_STYLE_NORMAL) {} + font_id(subset_id subset, int size, int style) : subset(subset), size(size), style(style) {} bool operator==(const font_id& o) const { - return subset == o.subset && size == o.size; + return subset == o.subset && size == o.size && style == o.style; } bool operator<(const font_id& o) const { - return subset < o.subset || (subset == o.subset && size < o.size); + return subset < o.subset || (subset == o.subset && size < o.size) || (subset == o.subset && size == o.size && style < o.style); } subset_id subset; int size; + int style; }; static std::map font_table; static std::vector font_names; +static std::vector bold_names; +static std::vector italic_names; struct text_chunk { @@ -233,26 +238,99 @@ static TTF_Font* open_font(const std::string& fname, int size) SDL_RWops *rwops = filesystem::load_RWops(name); TTF_Font* font = TTF_OpenFontRW(rwops, true, size); // SDL takes ownership of rwops if(font == NULL) { - ERR_FT << "Failed opening font: TTF_OpenFont: " << TTF_GetError() << std::endl; + ERR_FT << "Failed opening font: '" << fname << "'\n"; + ERR_FT << "TTF_OpenFont: " << TTF_GetError() << std::endl; return NULL; } + DBG_FT << "Opened a font: " << fname << std::endl; + return font; } +// Gets an appropriately configured TTF Font, for this font size and style. +// Loads fonts if necessary. For styled fonts, we search for a ``shipped'' +// version of the font which is prestyled. If this fails we use the basic +// version, plus SDL TTF styling. +// +// Uses the font table for caching. +// If a font is missing from font table, we try to load it. If there is an +// entry but it is a null pointer, it means we failed to load so don't keep +// trying. +// If a styled font has a null pointer, then in that case we try to fall +// back to TTF styling. static TTF_Font* get_font(font_id id) { const std::map::iterator it = font_table.find(id); - if(it != font_table.end()) + if(it != font_table.end() && it->second != NULL) { + TTF_SetFontStyle(it->second, TTF_STYLE_NORMAL); //If we are supposed to use TTF styling, this entry would have been NULL. return it->second; + } - if(id.subset < 0 || size_t(id.subset) >= font_names.size()) - return NULL; + if (id.style == TTF_STYLE_NORMAL) { + if (it != font_table.end()) { // If we are missing an unstyled font, there is no fallback. + return NULL; + } - TTF_Font* font = open_font(font_names[id.subset], id.size); + if(id.subset < 0 || size_t(id.subset) >= font_names.size()) { + return NULL; + } - if(font == NULL) + TTF_Font* font = open_font(font_names[id.subset], id.size); + + if (font) { + TTF_SetFontStyle(font, TTF_STYLE_NORMAL); + } + + LOG_FT << "Inserting font...\n"; + font_table.insert(std::pair(id, font)); // If loading fails, we want to insert a NULL entry here. + return font; + } + + if(it != font_table.end()) // if we inserted a NULL record for a styled font, fallback to use SDL TTF to style the font. + { + int desired_style = id.style; + id.style = TTF_STYLE_NORMAL; + const std::map::iterator it2 = font_table.find(id); + + if (it2 != font_table.end() && it2->second != NULL) { + TTF_SetFontStyle(it2->second, desired_style); + return it2->second; + } else { + return NULL; + } + } + + // We didn't find any record and this is a styled font, so this is the first time we are requesting it. We need to either + // find the styled version, or make a NULL entry. (Otherwise we will do constant file seeking instead of using SDL TTF.) + if(id.subset < 0 || size_t(id.subset) >= font_names.size()) { return NULL; + } + + std::string fname; + switch (id.style) { + case TTF_STYLE_BOLD: + fname = bold_names[id.subset]; + break; + case TTF_STYLE_ITALIC: + fname = italic_names[id.subset]; + break; + default: + fname = ""; //assert(false); + }; + + if (!fname.size()) { // There was no styled font file specified. See code that is loading font config. + font_table.insert(std::pair(id, NULL)); + return get_font(id); // Call recursively now, which will trigger to use SDL TTF styling. + } + + DBG_FT << "Trying to load a styled font: fname = '" << fname << "'\n"; + + TTF_Font* font = open_font(fname, id.size); + if (font == NULL) { // Failed to load + font_table.insert(std::pair(id, NULL)); + return get_font(id); // Call recursively now, which will trigger to use SDL TTF styling. + } TTF_SetFontStyle(font,TTF_STYLE_NORMAL); @@ -269,56 +347,12 @@ static void clear_fonts() font_table.clear(); font_names.clear(); + bold_names.clear(); + italic_names.clear(); char_blocks.cbmap.clear(); line_size_cache.clear(); } -namespace { - -struct font_style_setter -{ - font_style_setter(TTF_Font* font, int style) : font_(font), old_style_(0) - { - if(style == 0) { - style = TTF_STYLE_NORMAL; - } - - old_style_ = TTF_GetFontStyle(font_); - - // I thought I had killed this. Now that we ship SDL_TTF, we - // should fix the bug directly in SDL_ttf instead of disabling - // features. -- Ayin 25/2/2005 -#if 0 - //according to the SDL_ttf documentation, combinations of - //styles may cause SDL_ttf to segfault. We work around this - //here by disallowing combinations of styles - - if((style&TTF_STYLE_UNDERLINE) != 0) { - //style = TTF_STYLE_NORMAL; //TTF_STYLE_UNDERLINE; - style = TTF_STYLE_UNDERLINE; - } else if((style&TTF_STYLE_BOLD) != 0) { - style = TTF_STYLE_BOLD; - } else if((style&TTF_STYLE_ITALIC) != 0) { - //style = TTF_STYLE_NORMAL; //TTF_STYLE_ITALIC; - style = TTF_STYLE_ITALIC; - } -#endif - - TTF_SetFontStyle(font_, style); - } - - ~font_style_setter() - { - TTF_SetFontStyle(font_,old_style_); - } - -private: - TTF_Font* font_; - int old_style_; -}; - -} - namespace font { std::string describe_versions() @@ -423,10 +457,34 @@ struct subset_descriptor } std::string name; + boost::optional bold_name; //If we are using another font for styled characters in this font, rather than SDL TTF method + boost::optional italic_name; + typedef std::pair range; std::vector present_codepoints; }; +static bool check_font_file(std::string name) { + if(game_config::path.empty() == false) { + if(!filesystem::file_exists(game_config::path + "/fonts/" + name)) { + if(!filesystem::file_exists("fonts/" + name)) { + if(!filesystem::file_exists(name)) { + WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl; + return false; + } + } + } + } else { + if(!filesystem::file_exists("fonts/" + name)) { + if(!filesystem::file_exists(name)) { + WRN_FT << "Failed opening font file '" << name << "': No such file or directory" << std::endl; + return false; + } + } + } + return true; +} + //sets the font list to be used. static void set_font_list(const std::vector& fontlist) { @@ -434,32 +492,37 @@ static void set_font_list(const std::vector& fontlist) std::vector::const_iterator itor; for(itor = fontlist.begin(); itor != fontlist.end(); ++itor) { + if (!check_font_file(itor->name)) continue; // Insert fonts only if the font file exists - if(game_config::path.empty() == false) { - if(!filesystem::file_exists(game_config::path + "/fonts/" + itor->name)) { - if(!filesystem::file_exists("fonts/" + itor->name)) { - if(!filesystem::file_exists(itor->name)) { - WRN_FT << "Failed opening font file '" << itor->name << "': No such file or directory" << std::endl; - continue; - } - } - } - } else { - if(!filesystem::file_exists("fonts/" + itor->name)) { - if(!filesystem::file_exists(itor->name)) { - WRN_FT << "Failed opening font file '" << itor->name << "': No such file or directory" << std::endl; - continue; - } - } - } const subset_id subset = font_names.size(); font_names.push_back(itor->name); + if (itor->bold_name && check_font_file(*itor->bold_name)) { + bold_names.push_back(*itor->bold_name); + } else { + bold_names.push_back(""); + } + + if (itor->italic_name && check_font_file(*itor->italic_name)) { + italic_names.push_back(*itor->italic_name); + } else { + italic_names.push_back(""); + } + BOOST_FOREACH(const subset_descriptor::range &cp_range, itor->present_codepoints) { char_blocks.insert(cp_range.first, cp_range.second, subset); } } char_blocks.compress(); + + assert(font_names.size() == bold_names.size()); + assert(font_names.size() == italic_names.size()); + + DBG_FT << "Set the font list. The styled font families are:\n"; + + for (size_t i = 0; i < font_names.size(); ++i) { + DBG_FT << "[" << i << "]:\t\tbase:\t'" << font_names[i] << "'\tbold:\t'" << bold_names[i] << "'\titalic:\t'" << italic_names[i] << "'\n"; + } } const SDL_Color NORMAL_COLOR = {0xDD,0xDD,0xDD,0}, @@ -614,10 +677,10 @@ void text_surface::measure() const BOOST_FOREACH(text_chunk const &chunk, chunks_) { - TTF_Font* ttfont = get_font(font_id(chunk.subset, font_size_)); - if(ttfont == NULL) + TTF_Font* ttfont = get_font(font_id(chunk.subset, font_size_, style_)); + if(ttfont == NULL) { continue; - font_style_setter const style_setter(ttfont, style_); + } int w, h; TTF_SizeUTF8(ttfont, chunk.text.c_str(), &w, &h); @@ -660,10 +723,7 @@ std::vector const &text_surface::get_surfaces() const BOOST_FOREACH(text_chunk const &chunk, chunks_) { - TTF_Font* ttfont = get_font(font_id(chunk.subset, font_size_)); - if (ttfont == NULL) - continue; - font_style_setter const style_setter(ttfont, style_); + TTF_Font* ttfont = get_font(font_id(chunk.subset, font_size_, style_)); surface s = surface(TTF_RenderUTF8_Blended(ttfont, chunk.text.c_str(), color_)); if(!s.null()) @@ -1419,9 +1479,16 @@ static bool add_font_to_fontlist(const config &fonts_config, if (!font) { return false; } + //DBG_FT << "Adding a font record: " << font.debug() << std::endl; fontlist.push_back(font::subset_descriptor()); fontlist.back().name = name; + if (font.has_attribute("bold_name")) { + fontlist.back().bold_name = font["bold_name"].str(); + } + if (font.has_attribute("italic_name")) { + fontlist.back().italic_name = font["italic_name"].str(); + } std::vector ranges = utils::split(font["codepoints"]); for(std::vector::const_iterator itor = ranges.begin(); @@ -1475,6 +1542,12 @@ bool load_font_config() std::set known_fonts; BOOST_FOREACH(const config &font, fonts_config.child_range("font")) { known_fonts.insert(font["name"]); + if (font.has_attribute("bold_name")) { + known_fonts.insert(font["bold_name"]); + } + if (font.has_attribute("italic_name")) { + known_fonts.insert(font["italic_name"]); + } } family_order = fonts_config["family_order"];