Merge pull request #382 from cbeck88/font_family
add support for styled font families in GUI1, help dialogs
This commit is contained in:
commit
1bc7471822
4 changed files with 197 additions and 109 deletions
|
@ -9,6 +9,8 @@
|
|||
|
||||
[font]
|
||||
name="DejaVuSans.ttf"
|
||||
bold_name="DejaVuSans-Bold.ttf"
|
||||
italic_name="DejaVuSans-Oblique.ttf"
|
||||
codepoints="32-126,160-745,748-750,755,759,768-847,849-851,855-856,858,860-866,880-887,890-894,900-906,908,910-929,931-1317,1329-1366,1369-1375,1377-1415,1417-1418,1456-1475,1478-1479,1488-1514,1520-1524,1542-1543,1545-1546,1548,1557,1563,1567,1569-1594,1600-1621,1623,1626,1632-1648,1652,1657-1727,1734,1740,1742,1749,1776-1785,1984-2023,2027-2037,2040-2042,3647,3713-3714,3716,3719-3720,3722,3725,3732-3735,3737-3743,3745-3747,3749,3751,3754-3755,3757-3769,3771-3773,3776-3780,3782,3784-3789,3792-3801,3804-3805,4256-4293,4304-4348,5121-5127,5129-5147,5149-5173,5175-5194,5196-5202,5204-5309,5312-5354,5356-5383,5392-5438,5440-5456,5458-5482,5492-5509,5514-5526,5536-5551,5598,5601,5702-5703,5742-5750,5760-5788,7424-7531,7543-7544,7547,7549,7557,7579-7615,7620-7625,7680-7931,7936-7957,7960-7965,7968-8005,8008-8013,8016-8023,8025,8027,8029,8031-8061,8064-8116,8118-8132,8134-8147,8150-8155,8157-8175,8178-8180,8182-8190,8192-8292,8298-8305,8308-8334,8336-8348,8352-8373,8376-8378,8400-8401,8406-8407,8411-8412,8417,8448-8457,8459-8521,8523,8526,8528-8581,8585,8592-8981,8984-8985,8988-8993,8996-9004,9013,9015-9022,9025-9028,9031-9033,9035-9037,9040,9042-9044,9047-9052,9054-9056,9059-9061,9064-9065,9067-9072,9075-9082,9085,9088-9091,9095-9099,9108-9109,9115-9134,9166-9167,9187,9189,9192,9250-9251,9312-9321,9472-9884,9888-9912,9920-9923,9954,9985-9988,9990-9993,9996-10023,10025-10059,10061,10063-10066,10070,10072-10078,10081-10132,10136-10159,10161-10175,10181-10182,10208,10214-10219,10224-10623,10627-10628,10702-10709,10731,10746-10747,10752-10754,10764-10780,10799,10858-10859,10877-10912,10926-10938,11001-11002,11008-11034,11039-11044,11091-11092,11360-11383,11385-11391,11520-11557,11568-11621,11631,11800,11807,11810-11813,11822,19904-19967,42564-42567,42572-42573,42576-42577,42580-42583,42594-42606,42634-42637,42644-42645,42760-42774,42779-42783,42786-42817,42822-42827,42830-42835,42838-42839,42852-42857,42875-42876,42880-42887,42889-42894,42896-42897,42912-42922,43002-43007,57344-57373,61184-61209,61440-61443,61960,61962,61973-61975,61978-61979,62047,62464-62502,62504,62917,63172-63176,63185,63188,64256-64262,64275-64279,64285-64335,64338-64419,64426-64429,64467-64470,64473-64474,64488-64489,64508-64511,65024-65039,65056-65059,65136-65140,65142-65276,65279,65529-65533,66304-66334,66336-66339,119552-119638,119808-119892,119894-119963,120120-120121,120123-120126,120128-120132,120134,120138-120144,120146-120171,120224-120485,120488-120779,120782-120831,127024-127123,127136-127150,127153-127166,127169-127183,127185-127199,128045-128046,128049,128053,128512-128547,128549-128555,128557-128576"
|
||||
[/font]
|
||||
[font]
|
||||
|
|
BIN
fonts/DejaVuSans-Bold.ttf
Normal file
BIN
fonts/DejaVuSans-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/DejaVuSans-Oblique.ttf
Normal file
BIN
fonts/DejaVuSans-Oblique.ttf
Normal file
Binary file not shown.
304
src/font.cpp
304
src/font.cpp
|
@ -34,6 +34,7 @@
|
|||
#include "serialization/unicode.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
@ -64,24 +65,41 @@ static lg::log_domain log_font("font");
|
|||
// Signed int. Negative values mean "no subset".
|
||||
typedef int subset_id;
|
||||
|
||||
// Used as a key in the font table, which caches the get_font results.
|
||||
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;
|
||||
// Record stored in the font table.
|
||||
// If the record for font_id (FOO, Bold + Underline) is a record (BAR, Bold),
|
||||
// it means that BAR is a Bold-styled version of FOO which we shipped with the
|
||||
// game, and now SDL_TTF should be used to style BAR as underline for the final results.
|
||||
struct ttf_record
|
||||
{
|
||||
TTF_Font* font;
|
||||
int style;
|
||||
};
|
||||
|
||||
typedef std::map<font_id, ttf_record> tfont_table;
|
||||
|
||||
static tfont_table 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
|
||||
{
|
||||
|
@ -203,8 +221,28 @@ static std::vector<text_chunk> split_text(std::string const & utf8_text) {
|
|||
return chunks;
|
||||
}
|
||||
|
||||
typedef std::map<std::pair<std::string, int>, TTF_Font*> topen_font_cache;
|
||||
topen_font_cache open_fonts;
|
||||
|
||||
static TTF_Font* open_font_impl(const std::string & , int);
|
||||
|
||||
// A wrapper which caches the results of open_font_impl.
|
||||
// Note that clear_fonts() is responsible to clean up all of these font pointers,
|
||||
// so to avoid memory leaks fonts should only be opened from this function.
|
||||
static TTF_Font* open_font(const std::string& fname, int size)
|
||||
{
|
||||
const std::pair<std::string, int> key = std::make_pair(fname, size);
|
||||
const topen_font_cache::iterator it = open_fonts.find(key);
|
||||
if (it != open_fonts.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
TTF_Font* result = open_font_impl(fname, size);
|
||||
open_fonts.insert(std::make_pair(key, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static TTF_Font* open_font_impl(const std::string & fname, int size) {
|
||||
std::string name;
|
||||
if(!game_config::path.empty()) {
|
||||
name = game_config::path + "/fonts/" + fname;
|
||||
|
@ -233,92 +271,91 @@ 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 find the closest
|
||||
// thing which we did ship, and store a record of this, which allows to
|
||||
// rapidly correct the remaining styling using SDL_TTF.
|
||||
//
|
||||
// Uses the font table for caching.
|
||||
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())
|
||||
return it->second;
|
||||
const std::map<font_id, ttf_record>::iterator it = font_table.find(id);
|
||||
if(it != font_table.end()) {
|
||||
if (it->second.font != NULL) {
|
||||
// If we found a valid record, use SDL_TTF to add in the difference
|
||||
// between its intrinsic style and the desired style.
|
||||
TTF_SetFontStyle(it->second.font, it->second.style ^ id.style);
|
||||
}
|
||||
return it->second.font;
|
||||
}
|
||||
|
||||
if(id.subset < 0 || size_t(id.subset) >= font_names.size())
|
||||
// There's no record, so we need to try to find a solution for this font
|
||||
// and make a record of it. If the indices are out of bounds don't bother though.
|
||||
if(id.subset < 0 || size_t(id.subset) >= font_names.size()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TTF_Font* font = open_font(font_names[id.subset], id.size);
|
||||
// Favor to use the shipped Italic font over bold if both are present and are needed.
|
||||
if ((id.style & TTF_STYLE_ITALIC) && italic_names[id.subset].size()) {
|
||||
if (TTF_Font* font = open_font(italic_names[id.subset], id.size)) {
|
||||
ttf_record rec = {font, TTF_STYLE_ITALIC};
|
||||
font_table.insert(std::make_pair(id, rec));
|
||||
return get_font(id);
|
||||
}
|
||||
}
|
||||
|
||||
if(font == NULL)
|
||||
return NULL;
|
||||
// Now see if the shipped Bold font is useful and available.
|
||||
if ((id.style & TTF_STYLE_BOLD) && bold_names[id.subset].size()) {
|
||||
if (TTF_Font* font = open_font(bold_names[id.subset], id.size)) {
|
||||
ttf_record rec = {font, TTF_STYLE_BOLD};
|
||||
font_table.insert(std::make_pair(id, rec));
|
||||
return get_font(id);
|
||||
}
|
||||
}
|
||||
|
||||
TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
|
||||
// Try just to use the basic version of the font then.
|
||||
if (font_names[id.subset].size()) {
|
||||
if(TTF_Font* font = open_font(font_names[id.subset], id.size)) {
|
||||
ttf_record rec = {font, TTF_STYLE_NORMAL};
|
||||
font_table.insert(std::make_pair(id, rec));
|
||||
return get_font(id);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_FT << "Inserting font...\n";
|
||||
font_table.insert(std::pair<font_id,TTF_Font*>(id, font));
|
||||
return font;
|
||||
// Failed to find a font.
|
||||
ttf_record rec = {NULL, TTF_STYLE_NORMAL};
|
||||
font_table.insert(std::make_pair(id, rec));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void clear_fonts()
|
||||
{
|
||||
for(std::map<font_id,TTF_Font*>::iterator i = font_table.begin(); i != font_table.end(); ++i) {
|
||||
for(topen_font_cache::iterator i = open_fonts.begin(); i != open_fonts.end(); ++i) {
|
||||
TTF_CloseFont(i->second);
|
||||
}
|
||||
open_fonts.clear();
|
||||
|
||||
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()
|
||||
|
@ -422,11 +459,67 @@ struct subset_descriptor
|
|||
{
|
||||
}
|
||||
|
||||
subset_descriptor(const config &);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
font::subset_descriptor::subset_descriptor(const config & font)
|
||||
: name(font["name"].str())
|
||||
, bold_name()
|
||||
, italic_name()
|
||||
, present_codepoints()
|
||||
{
|
||||
if (font.has_attribute("bold_name")) {
|
||||
bold_name = font["bold_name"].str();
|
||||
}
|
||||
|
||||
if (font.has_attribute("italic_name")) {
|
||||
italic_name = font["italic_name"].str();
|
||||
}
|
||||
|
||||
std::vector<std::string> ranges = utils::split(font["codepoints"]);
|
||||
|
||||
BOOST_FOREACH(const std::string & i, ranges) {
|
||||
std::vector<std::string> r = utils::split(i, '-');
|
||||
if(r.size() == 1) {
|
||||
size_t r1 = lexical_cast_default<size_t>(r[0], 0);
|
||||
present_codepoints.push_back(std::pair<size_t, size_t>(r1, r1));
|
||||
} else if(r.size() == 2) {
|
||||
size_t r1 = lexical_cast_default<size_t>(r[0], 0);
|
||||
size_t r2 = lexical_cast_default<size_t>(r[1], 0);
|
||||
|
||||
present_codepoints.push_back(std::pair<size_t, size_t>(r1, r2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -434,32 +527,37 @@ static void set_font_list(const std::vector<subset_descriptor>& fontlist)
|
|||
|
||||
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},
|
||||
|
@ -614,10 +712,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 +758,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())
|
||||
|
@ -1416,30 +1511,15 @@ static bool add_font_to_fontlist(const config &fonts_config,
|
|||
std::vector<font::subset_descriptor>& fontlist, const std::string& name)
|
||||
{
|
||||
const config &font = fonts_config.find_child("font", "name", name);
|
||||
if (!font)
|
||||
if (!font) {
|
||||
return false;
|
||||
|
||||
fontlist.push_back(font::subset_descriptor());
|
||||
fontlist.back().name = name;
|
||||
std::vector<std::string> ranges = utils::split(font["codepoints"]);
|
||||
|
||||
for(std::vector<std::string>::const_iterator itor = ranges.begin();
|
||||
itor != ranges.end(); ++itor) {
|
||||
|
||||
std::vector<std::string> r = utils::split(*itor, '-');
|
||||
if(r.size() == 1) {
|
||||
size_t r1 = lexical_cast_default<size_t>(r[0], 0);
|
||||
fontlist.back().present_codepoints.push_back(std::pair<size_t, size_t>(r1, r1));
|
||||
} else if(r.size() == 2) {
|
||||
size_t r1 = lexical_cast_default<size_t>(r[0], 0);
|
||||
size_t r2 = lexical_cast_default<size_t>(r[1], 0);
|
||||
|
||||
fontlist.back().present_codepoints.push_back(std::pair<size_t, size_t>(r1, r2));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//DBG_FT << "Adding a font record: " << font.debug() << std::endl;
|
||||
|
||||
fontlist.push_back(font::subset_descriptor(font));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace font {
|
||||
|
||||
|
@ -1474,6 +1554,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"];
|
||||
|
|
Loading…
Add table
Reference in a new issue