Skip to content

Commit

Permalink
add support for styled fonts outside of SDL TTF in GUI1, help
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cbeck88 committed Mar 7, 2015
1 parent 1f3e143 commit cd54ee0
Showing 1 changed file with 152 additions and 79 deletions.
231 changes: 152 additions & 79 deletions src/font.cpp
Expand Up @@ -34,6 +34,7 @@
#include "serialization/unicode.hpp"

#include <boost/foreach.hpp>
#include <boost/optional.hpp>

#include <list>
#include <set>
Expand Down Expand Up @@ -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_id, TTF_Font*> font_table;
static std::vector<std::string> font_names;
static std::vector<std::string> bold_names;
static std::vector<std::string> italic_names;

struct text_chunk
{
Expand Down Expand Up @@ -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<font_id, TTF_Font*>::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<font_id,TTF_Font*>(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<font_id, TTF_Font*>::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<font_id,TTF_Font*>(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<font_id,TTF_Font*>(id, NULL));
return get_font(id); // Call recursively now, which will trigger to use SDL TTF styling.
}

TTF_SetFontStyle(font,TTF_STYLE_NORMAL);

Expand All @@ -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()
Expand Down Expand Up @@ -423,43 +457,72 @@ struct subset_descriptor
}

std::string name;
boost::optional<std::string> bold_name; //If we are using another font for styled characters in this font, rather than SDL TTF method
boost::optional<std::string> italic_name;

typedef std::pair<int, int> range;
std::vector<range> 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<subset_descriptor>& fontlist)
{
clear_fonts();

std::vector<subset_descriptor>::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},
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -660,10 +723,7 @@ std::vector<surface> 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())
Expand Down Expand Up @@ -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<std::string> ranges = utils::split(font["codepoints"]);

for(std::vector<std::string>::const_iterator itor = ranges.begin();
Expand Down Expand Up @@ -1475,6 +1542,12 @@ bool load_font_config()
std::set<std::string> 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"];
Expand Down

0 comments on commit cd54ee0

Please sign in to comment.