16 #define GETTEXT_DOMAIN "wesnoth-lib"
36 #include <boost/optional.hpp>
43 #include <cairo-features.h>
45 #ifdef CAIRO_HAS_WIN32_FONT
47 #undef CAIRO_HAS_FT_FONT
50 #ifdef CAIRO_HAS_FT_FONT
51 #include <fontconfig/fontconfig.h>
54 #if !defined(CAIRO_HAS_FT_FONT) && !defined(CAIRO_HAS_WIN32_FONT)
56 #error unable to find font loading tools.
60 #define DBG_FT LOG_STREAM(debug, log_font)
61 #define LOG_FT LOG_STREAM(info, log_font)
62 #define WRN_FT LOG_STREAM(warn, log_font)
63 #define ERR_FT LOG_STREAM(err, log_font)
136 if (first > last)
return;
139 if (i != cbmap.begin()) {
143 insert(j->second.first + 1, last,
id);
147 if (i != cbmap.end()) {
148 if ( i->first <= last) {
149 insert(first, i->first - 1,
id);
153 cbmap.insert(std::make_pair(first,
block_t(last,
id)));
161 LOG_FT <<
"Font map size before compression: " << cbmap.size() <<
" ranges\n";
166 if (j ==
e || i->second.second != j->second.second) {
170 i->second.first = j->second.first;
173 LOG_FT <<
"Font map size after compression: " << cbmap.size() <<
" ranges\n";
179 if (i != cbmap.begin()) {
181 if (ch <= i->second.first )
182 return i->second.second;
199 std::vector<text_chunk> chunks;
201 if (utf8_text.empty())
207 if (sub >= 0) current_chunk.
subset =
sub;
210 sub = char_blocks.
get_id(*ch);
211 if (sub >= 0 && sub != current_chunk.
subset) {
212 chunks.push_back(current_chunk);
213 current_chunk.
text.clear();
218 if (!current_chunk.
text.empty()) {
219 chunks.push_back(current_chunk);
223 WRN_FT <<
"Invalid UTF-8 string: \"" << utf8_text <<
"\"" << std::endl;
238 const std::pair<std::string, int> key = std::make_pair(fname, size);
245 open_fonts.insert(std::make_pair(key, result));
254 name =
"fonts/" + fname;
258 ERR_FT <<
"Failed opening font: '" << name <<
"': No such file or directory" << std::endl;
265 name =
"fonts/" + fname;
268 ERR_FT <<
"Failed opening font: '" << name <<
"': No such file or directory" << std::endl;
276 TTF_Font*
font = TTF_OpenFontRW(rwops,
true, size);
277 if(font ==
nullptr) {
278 ERR_FT <<
"Failed opening font: '" << fname <<
"'\n";
279 ERR_FT <<
"TTF_OpenFont: " << TTF_GetError() << std::endl;
283 DBG_FT <<
"Opened a font: " << fname << std::endl;
299 if (it->second.font !=
nullptr) {
302 TTF_SetFontStyle(it->second.font, it->second.style ^
id.style);
304 return it->second.font;
309 if(
id.subset < 0 ||
size_t(
id.subset) >= font_names.size()) {
314 if ((
id.style & TTF_STYLE_ITALIC) && italic_names[
id.subset].size()) {
323 if ((
id.style & TTF_STYLE_BOLD) && bold_names[
id.subset].
size()) {
332 if (font_names[
id.subset].
size()) {
349 TTF_CloseFont(
i->second);
357 italic_names.clear();
359 char_blocks.
cbmap.clear();
360 line_size_cache.clear();
367 const int res = TTF_Init();
369 ERR_FT <<
"Could not initialize true type fonts" << std::endl;
372 LOG_FT <<
"Initialized true type fonts\n";
394 #ifdef CAIRO_HAS_FT_FONT
395 if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
398 ERR_FT <<
"Could not load the true type fonts" << std::endl;
402 if(!FcConfigParseAndLoad(FcConfigGetCurrent(),
403 reinterpret_cast<const FcChar8*>((
game_config::path +
"/fonts/fonts.conf").c_str()),
406 ERR_FT <<
"Could not load local font configuration\n";
410 LOG_FT <<
"Local font configuration loaded\n";
414 #if CAIRO_HAS_WIN32_FONT
416 std::vector<std::string> files;
421 if(file.substr(file.length() - 4) ==
".ttf" || file.substr(file.length() - 4) ==
".ttc")
423 const std::wstring wfile =
unicode_cast<std::wstring>(file);
424 AddFontResourceExW(wfile.c_str(), FR_PRIVATE,
nullptr);
433 #ifdef CAIRO_HAS_FT_FONT
434 FcConfigAppFontClear(FcConfigGetCurrent());
437 #if CAIRO_HAS_WIN32_FONT
439 std::vector<std::string> files;
443 if(file.substr(file.length() - 4) ==
".ttf" || file.substr(file.length() - 4) ==
".ttc")
445 const std::wstring wfile =
unicode_cast<std::wstring>(file);
446 RemoveFontResourceExW(wfile.c_str(), FR_PRIVATE,
nullptr);
474 :
name(font[
"name"].str())
477 , present_codepoints()
487 std::vector<std::string> ranges =
utils::split(font[
"codepoints"]);
492 size_t r1 = lexical_cast_default<size_t>(r[0], 0);
494 }
else if(r.size() == 2) {
495 size_t r1 = lexical_cast_default<size_t>(r[0], 0);
496 size_t r2 = lexical_cast_default<size_t>(r[1], 0);
508 WRN_FT <<
"Failed opening font file '" << name <<
"': No such file or directory" << std::endl;
516 WRN_FT <<
"Failed opening font file '" << name <<
"': No such file or directory" << std::endl;
529 std::vector<subset_descriptor>::const_iterator
itor;
530 for(itor = fontlist.begin(); itor != fontlist.end(); ++
itor) {
533 const subset_id subset = font_names.size();
534 font_names.push_back(itor->name);
537 bold_names.push_back(*itor->bold_name);
539 bold_names.push_back(
"");
543 italic_names.push_back(*itor->italic_name);
545 italic_names.push_back(
"");
549 char_blocks.
insert(cp_range.first, cp_range.second, subset);
554 assert(font_names.size() == bold_names.size());
555 assert(font_names.size() == italic_names.size());
557 DBG_FT <<
"Set the font list. The styled font families are:\n";
559 for (
size_t i = 0;
i < font_names.size(); ++
i) {
560 DBG_FT <<
"[" <<
i <<
"]:\t\tbase:\t'" << font_names[
i] <<
"'\tbold:\t'" << bold_names[
i] <<
"'\titalic:\t'" << italic_names[
i] <<
"'\n";
580 static const size_t max_text_line_width = 4096;
586 text_surface(
int size, SDL_Color
color,
int style);
589 void measure()
const;
590 size_t width()
const;
593 bool is_rtl()
const {
return is_rtl_; }
595 std::vector<surface>
const & get_surfaces()
const;
620 void text_surface::bidi_cvt()
622 char *c_str =
const_cast<char *
>(
str_.c_str());
623 FriBidiStrIndex
len =
str_.length();
624 FriBidiChar *bidi_logical =
new FriBidiChar[len + 2];
625 FriBidiChar *bidi_visual =
new FriBidiChar[len + 2];
626 char *utf8str =
new char[4*len + 1];
627 FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
631 n = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, c_str, len, bidi_logical);
632 fribidi_log2vis(bidi_logical, n, &base_dir, bidi_visual,
nullptr,
nullptr,
nullptr);
634 fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, bidi_visual, n, utf8str);
635 is_rtl_ = base_dir == FRIBIDI_TYPE_RTL;
637 delete[] bidi_logical;
638 delete[] bidi_visual;
644 SDL_Color
color,
int style) :
665 text_surface::text_surface(
int size, SDL_Color
color,
int style) :
682 void text_surface::set_text(
std::string const &str)
694 void text_surface::hash()
697 for(std::string::const_iterator it =
str_.begin(), it_end =
str_.end(); it != it_end; ++it)
698 h = ((h << 9) | (h >> (
sizeof(
int) * 8 - 9))) ^ (*it);
702 void text_surface::measure()
const
710 if(ttfont ==
nullptr) {
715 TTF_SizeUTF8(ttfont, chunk.text.c_str(), &
w, &
h);
717 h_ = std::max<int>(
h_,
h);
741 std::vector<surface>
const &text_surface::get_surfaces()
const
750 if(
width() > max_text_line_width)
768 static text_surface &
find(text_surface
const &
t);
769 static void resize(
unsigned int size);
771 typedef std::list< text_surface > text_list;
779 void text_cache::resize(
unsigned int size)
782 << size <<
" items in cache: " <<
cache_.size() <<
'\n';
784 while(size <
cache_.size()) {
793 static size_t lookup_ = 0, hit_ = 0;
804 if (++lookup_ % 1000 == 0) {
805 DBG_FT <<
"Text cache: " << lookup_ <<
" lookups, " << (hit_ / 10) <<
"% hits\n";
816 const std::vector<std::string> lines =
utils::split(text,
'\n', 0);
817 std::vector<std::vector<surface> >
surfaces;
818 surfaces.reserve(lines.size());
821 for(std::vector< std::string >::const_iterator ln = lines.begin(), ln_end = lines.end(); ln != ln_end; ++ln) {
824 int text_style = style;
826 std::string::const_iterator after_markup = use_markup ?
827 parse_markup(ln->begin(), ln->end(), &sz,
nullptr, &text_style) : ln->begin();
828 text_surface txt_surf(sz, color, text_style);
830 if (after_markup == ln->end() && (ln+1 != ln_end || lines.begin()+1 == ln_end)) {
833 txt_surf.set_text(
" ");
834 }
else if (after_markup == ln->begin()) {
836 txt_surf.set_text(*ln);
839 txt_surf.set_text(line);
843 const std::vector<surface>&
res = cached_surf.get_surfaces();
846 surfaces.push_back(res);
847 width = std::max<size_t>(cached_surf.width(),
width);
848 height += cached_surf.height();
852 if (surfaces.empty()) {
854 }
else if (surfaces.size() == 1 && surfaces.front().size() == 1) {
865 for(std::vector< std::vector<surface> >::const_iterator
i = surfaces.begin(),
866 i_end = surfaces.end();
i != i_end; ++
i) {
870 for(std::vector<surface>::const_iterator j =
i->begin(),
871 j_end =
i->end(); j != j_end; ++j) {
874 sdl_blit(*j,
nullptr, res, &dstrect);
876 height = std::max<size_t>((*j)->h,
height);
889 return render_text(str, size, color, style,
false);
894 int x,
int y,
bool use_tooltips,
int style)
897 if (gui_surface.
null()) {
898 text_surface
const &u =
text_cache::find(text_surface(text, size, color, style));
910 if(surface ==
nullptr) {
919 if(getenv(
"NO_RTL") ==
nullptr) {
920 bool is_rtl =
text_cache::find(text_surface(text, size, color, style)).is_rtl();
922 dest.x = area.x + area.w - surface->w - (x - area.x);
926 dest.x = (area.w/2)-(surface->w/2);
930 dest.y = (area.h/2)-(surface->h/2);
938 if(dest.x + dest.w > area.x + area.w) {
939 dest.w = area.x + area.w - dest.x;
942 if(dest.y + dest.h > area.y + area.h) {
943 dest.h = area.y + area.h - dest.y;
946 if(gui_surface !=
nullptr) {
950 sdl_blit(surface,&src,gui_surface,&dest);
966 return TTF_FontHeight(font);
971 return line_size(line,font_size,style).w;
978 const line_size_cache_map::const_iterator
i = cache.find(line);
979 if(i != cache.end()) {
985 const SDL_Color col = { 0, 0, 0, 0 };
986 text_surface
s(line, font_size, col, style);
992 cache.insert(std::pair<std::string,SDL_Rect>(line,res));
997 int max_width,
int style)
1001 if (
line_width(text, font_size, style) <= max_width)
1003 if(
line_width(ellipsis, font_size, style) > max_width)
1014 if (
line_width(tmp + ellipsis, font_size, style) > max_width) {
1015 return current_substring +
ellipsis;
1018 current_substring.append(itor.
substr().first, itor.
substr().second);
1028 std::vector<font::subset_descriptor>& fontlist,
const std::string&
name)
1055 if(cfg_path.empty()) {
1056 ERR_FT <<
"could not resolve path to fonts.cfg, file not found\n";
1063 ERR_FT <<
"could not read fonts.cfg:\n"
1072 std::set<std::string> known_fonts;
1074 known_fonts.insert(
font[
"name"]);
1075 if (
font.has_attribute(
"bold_name")) {
1076 known_fonts.insert(
font[
"bold_name"]);
1078 if (
font.has_attribute(
"italic_name")) {
1079 known_fonts.insert(
font[
"italic_name"]);
1083 family_order_sans = fonts_config[
"family_order"];
1084 family_order_mono = fonts_config[
"family_order_monospace"];
1086 if(family_order_mono.empty()) {
1087 ERR_FT <<
"No monospace font family order defined, falling back to sans serif order\n";
1088 family_order_mono = family_order_sans;
1091 const std::vector<std::string> font_order =
utils::split(fonts_config[
"order"]);
1092 std::vector<font::subset_descriptor> fontlist;
1093 std::vector<std::string>::const_iterator
font;
1094 for(font = font_order.begin(); font != font_order.end(); ++font) {
1096 known_fonts.erase(*font);
1098 std::set<std::string>::const_iterator kfont;
1099 for(kfont = known_fonts.begin(); kfont != known_fonts.end(); ++kfont) {
1103 if(fontlist.empty())
1114 return family_order_mono;
1116 return family_order_sans;
1123 text_cache::resize(1000);
1125 text_cache::resize(50);
const SDL_Color BIGMAP_COLOR
const SDL_Color BUTTON_COLOR
child_itors child_range(const std::string &key)
static unsigned int max_size_
std::pair< int, subset_id > block_t
family_class
Font classes for get_font_families().
const SDL_Color TITLE_COLOR
const SDL_Color PETRIFIED_COLOR
static TTF_Font * get_font(font_id id)
std::vector< surface > surfs_
const SDL_Color BLACK_COLOR
static l_noret error(LoadState *S, const char *why)
bool operator==(text_chunk const &t) const
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
static surface render_text(const std::string &text, int fontsize, const SDL_Color &color, int style, bool use_markup)
bool operator!=(text_chunk const &t) const
SDL_Rect line_size(const std::string &line, int font_size, int style)
Determine the size of a line of text given a certain font size.
bool operator<(const font_id &o) const
static std::map< int, std::map< int, line_size_cache_map > > line_size_cache
GLint GLint GLint GLint GLint GLint y
static std::vector< std::string > font_names
bool operator!=(const config &a, const config &b)
const SDL_Color NORMAL_COLOR
Definitions for the interface to Wesnoth Markup Language (WML).
font_id(subset_id subset, int size, int style)
const SDL_Color GOOD_COLOR
static void set_font_list(const std::vector< subset_descriptor > &fontlist)
static std::vector< std::string > bold_names
const SDL_Color DISABLED_COLOR
const t_string & get_font_families(family_class fclass)
Returns the currently defined fonts.
GLsizei const char ** path
void insert(int first, int last, subset_id id)
Associates not-associated parts of a range with a new font.
std::map< font_id, ttf_record > tfont_table
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
static TTF_Font * open_font(const std::string &fname, int size)
const SDL_Color BAD_COLOR
std::map< int, block_t > cbmap_t
std::vector< text_chunk > chunks_
GLubyte GLubyte GLubyte GLubyte w
static std::vector< text_chunk > split_text(std::string const &utf8_text)
int font_scaled(int size)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
static bool check_font_file(std::string name)
static char_block_map char_blocks
std::vector< range > present_codepoints
void compress()
Compresses map by merging consecutive ranges with the same font, even if there is some unassociated r...
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
int get_max_height(int size)
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2, int *font_size, SDL_Color *color, int *style)
Parses the markup-tags at the front of a string.
topen_font_cache open_fonts
void update_font_path() const
Updates the font path, when initialized it sets the fontpath to game_config::path.
static std::vector< std::string > italic_names
const std::vector< std::string > & get_binary_paths(const std::string &type)
Returns a vector with all possible paths to a given type of binary, e.g.
void init() const
Initializes the font path.
std::pair< int, int > range
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
std::map< std::string, tfilter >::iterator itor
Thrown by operations encountering invalid UTF-8 data.
std::string get_wml_location(const std::string &filename, const std::string ¤t_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
void cache_mode(CACHE mode)
std::map< std::string, SDL_Rect > line_size_cache_map
SDL_Color inverse(const SDL_Color &color)
const GLvdpauSurfaceNV * surfaces
bool operator==(const config &a, const config &b)
const SDL_Color LABEL_COLOR
static void clear_fonts()
const SDL_Color LOBBY_COLOR
GLfloat GLfloat GLfloat GLfloat h
GLint GLint GLint GLint GLint x
static TTF_Font * open_font_impl(const std::string &, int)
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
GLdouble GLdouble GLdouble r
static iterator_base end(const string_type &str)
static tfont_table font_table
void deinit() const
Deinitializes the font path.
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
GLuint const GLchar * name
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
bool operator==(const font_id &o) const
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
SDL_Rect draw_text_line(surface &gui_surface, const SDL_Rect &area, int size, const SDL_Color &color, const std::string &text, int x, int y, bool use_tooltips, int style)
Contains the SDL_Rect helper code.
bool has_attribute(const std::string &key) const
bool find(E event, F functor)
Tests whether an event handler is available.
std::map< std::pair< std::string, int >, TTF_Font * > topen_font_cache
const std::pair< typename string_type::const_iterator, typename string_type::const_iterator > & substr() const
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
const SDL_Color GRAY_COLOR
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Standard logging facilities (interface).
surface create_compatible_surface(const surface &surf, int width, int height)
boost::optional< std::string > bold_name
static bool add_font_to_fontlist(const config &fonts_config, std::vector< font::subset_descriptor > &fontlist, const std::string &name)
GLint GLint GLint GLint GLint GLint GLsizei width
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
text_chunk(subset_id subset)
const SDL_Color YELLOW_COLOR
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
SDL_RWops * load_RWops(const std::string &path)
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha)
A config object defines a single node in a WML file, with access to child nodes.
surface get_rendered_text(const std::string &str, int size, const SDL_Color &color, int style)
Compatibility layer for using SDL 1.2 and 2.0.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
font_id(subset_id subset, int size)
GLsizei const GLcharARB ** string
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
static lg::log_domain log_font("font")
boost::optional< std::string > italic_name