Check which glyphs are in each font, instead of using a list hardcoded in WML
This is a "it's known to be broken, so let's rip it out and put in a minimal replacement" change. Updated raw pointers to smart pointers just because, updated the docs a bit, and ended up with a big change. Fixes the most visible part of #5194, where Chinese needed DroidSansFallbackFull to be loaded before DroidSansJapanese. The removed code in `char_block_map::insert` and `char_block_map::compress` had a bug that triggered when one font had a contiguous range of codepoints that was a superset of several ranges in another font - this meant it treated the first font containing U+4E00 as having the whole CJK Unified Ideographs block. Remove the now-unused font codepoints WML. There is no schema change for this, it seems the data/hardwired/fonts.cfg file isn't checked by the validation. Optimise calling set_font_list with the same list (but possibly reordered), by reusing the already-loaded fonts.
This commit is contained in:
parent
d2bc8e264f
commit
01424cc853
11 changed files with 220 additions and 338 deletions
|
@ -5,6 +5,9 @@
|
|||
### Language and i18n
|
||||
* Updated translations: British English, Czech, French, Japanese, Polish,
|
||||
Portuguese (Brazil)
|
||||
* The font-handling now checks which glyphs are in each font, instead of using a list hardcoded in WML.
|
||||
* A bug in the removed code treated the first font containing U+4E00 as having the whole CJK Unified Ideographs block.
|
||||
* Fixes a bug where many Chinese characters were invisible if DroidSansJapanese was loaded before DroidSansFallbackFull (issue #5194).
|
||||
### Units
|
||||
* Added Tusker line - Gorer and Tusklet (by TSI 2009)
|
||||
* Revised Falcon and Elder Falcon sprites
|
||||
|
@ -34,6 +37,8 @@
|
|||
* Implement new GUI2 widget userdata for working with custom dialogs.
|
||||
* Renamed wesnoth.show_dialog to gui.show_dialog. The preshow and postshow now take a single parameter - a widget userdata.
|
||||
* All other dialog functions (such as wesnoth.get_dialog_value and wesnoth.set_dialog_value) are now deprecated.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Removed font-analysis utils: codecomp codeextract codeglyphs codelist
|
||||
### Terrain
|
||||
* New wall variation: Overgrown stone walls ('Xof')
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -35,7 +35,6 @@ struct subset_descriptor
|
|||
: name()
|
||||
, bold_name()
|
||||
, italic_name()
|
||||
, present_codepoints()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -43,7 +42,6 @@ struct subset_descriptor
|
|||
: name(font["name"].str())
|
||||
, bold_name()
|
||||
, italic_name()
|
||||
, present_codepoints()
|
||||
{
|
||||
if (font.has_attribute("bold_name")) {
|
||||
bold_name = font["bold_name"].str();
|
||||
|
@ -52,29 +50,11 @@ struct subset_descriptor
|
|||
if (font.has_attribute("italic_name")) {
|
||||
italic_name = font["italic_name"].str();
|
||||
}
|
||||
|
||||
std::vector<std::string> ranges = utils::split(font["codepoints"]);
|
||||
|
||||
for (const std::string & i : ranges) {
|
||||
std::vector<std::string> r = utils::split(i, '-');
|
||||
if(r.size() == 1) {
|
||||
std::size_t r1 = lexical_cast_default<std::size_t>(r[0], 0);
|
||||
present_codepoints.emplace_back(r1, r1);
|
||||
} else if(r.size() == 2) {
|
||||
std::size_t r1 = lexical_cast_default<std::size_t>(r[0], 0);
|
||||
std::size_t r2 = lexical_cast_default<std::size_t>(r[1], 0);
|
||||
|
||||
present_codepoints.emplace_back(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // end namespace font
|
||||
|
|
|
@ -27,12 +27,19 @@
|
|||
* Note: This is specific to SDL_TTF code path
|
||||
*/
|
||||
|
||||
namespace font {
|
||||
|
||||
// Signed int. Negative values mean "no subset".
|
||||
namespace font
|
||||
{
|
||||
/**
|
||||
* Font family, acts an an enumeration with each font loaded by stl_ttf::set_font_list getting an
|
||||
* individual value. The values do not necessarily correspond to the order of the list passed to
|
||||
* stl_ttf::set_font_list, all positive values should be treated as opaque data.
|
||||
*
|
||||
* Negative values are returned by sdl_ttf::split_text to denote chunks which can't be handled with
|
||||
* the available fonts.
|
||||
*/
|
||||
typedef int subset_id;
|
||||
|
||||
// Used as a key in the font table, which caches the get_font results.
|
||||
// Used as a key in requests to the functions in sdl_text.hpp (and the font table in sdl_text.cpp's implementation)
|
||||
struct font_id
|
||||
{
|
||||
explicit font_id(subset_id subset, int size) : subset(subset), size(size), style(TTF_STYLE_NORMAL) {}
|
||||
|
@ -49,17 +56,23 @@ struct font_id
|
|||
|
||||
subset_id subset;
|
||||
int size;
|
||||
/**
|
||||
* Bitmask of the values TTF_STYLE_BOLD, TTF_STYLE_ITALIC.
|
||||
*/
|
||||
int style;
|
||||
};
|
||||
|
||||
/***
|
||||
/**
|
||||
* A string that should be rendered with a single font. Longer texts that need
|
||||
* characters from multiple fonts are cut into these sub-strings.
|
||||
*
|
||||
* Text chunk is used by text_surfaces and these are cached sometimes.
|
||||
*/
|
||||
struct text_chunk
|
||||
{
|
||||
text_chunk(subset_id subset)
|
||||
text_chunk(subset_id subset, std::string&& text)
|
||||
: subset(subset)
|
||||
, text()
|
||||
, text(std::move(text))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
#include "font/text_surface.hpp"
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "font/marked-up_text.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "log.hpp"
|
||||
#include "font/marked-up_text.hpp"
|
||||
#include "preferences/general.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
|
@ -43,122 +43,82 @@ static lg::log_domain log_font("font");
|
|||
#define WRN_FT LOG_STREAM(warn, log_font)
|
||||
#define ERR_FT LOG_STREAM(err, log_font)
|
||||
|
||||
namespace font {
|
||||
|
||||
/***
|
||||
* Caches used to speed up font rendering
|
||||
*/
|
||||
|
||||
namespace font
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// 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;
|
||||
std::shared_ptr<TTF_Font> font;
|
||||
int style;
|
||||
};
|
||||
|
||||
static std::map<font_id, ttf_record> font_table;
|
||||
|
||||
// The indices in these vectors correspond to the font_id.subset values in font_table.
|
||||
static std::vector<std::string> font_names;
|
||||
static std::vector<std::string> bold_names;
|
||||
static std::vector<std::string> italic_names;
|
||||
|
||||
struct char_block_map
|
||||
struct family_record
|
||||
{
|
||||
char_block_map()
|
||||
: cbmap()
|
||||
{
|
||||
}
|
||||
std::shared_ptr<const TTF_Font> font;
|
||||
subset_id subset;
|
||||
std::string name;
|
||||
};
|
||||
/**
|
||||
* Used for implementing find_font_containing, the elements are in the same order as the arguments
|
||||
* to set_font_list(). The fonts here are a subset of those in font_table, because
|
||||
* find_font_containing doesn't need size-specific instances of a font.
|
||||
*
|
||||
* In most locales, the subset_ids will match the indices into this vector. This is only a
|
||||
* coincidence, and it won't be true (at the time of writing) in Chinese.
|
||||
*
|
||||
* \todo Are all variants of a font guaranteed to have exactly the same glyphs? For example, might
|
||||
* an italic variant only contain the glyphs which are major improvements on an automatic skew of
|
||||
* the non-italic version?
|
||||
*/
|
||||
std::vector<family_record> family_table;
|
||||
|
||||
typedef std::pair<int, subset_id> block_t;
|
||||
typedef std::map<int, block_t> cbmap_t;
|
||||
cbmap_t cbmap;
|
||||
/** Associates not-associated parts of a range with a new font. */
|
||||
void insert(int first, int last, subset_id id)
|
||||
{
|
||||
if (first > last) return;
|
||||
cbmap_t::iterator i = cbmap.lower_bound(first);
|
||||
// At this point, either first <= i->first or i is past the end.
|
||||
if (i != cbmap.begin()) {
|
||||
cbmap_t::iterator j = i;
|
||||
--j;
|
||||
if (first <= j->second.first /* prev.last */) {
|
||||
insert(j->second.first + 1, last, id);
|
||||
return;
|
||||
}
|
||||
const auto no_font_found = family_record{nullptr, -1, ""};
|
||||
/**
|
||||
* Given a unicode code point, returns the first (using the order passed to set_font_list) font
|
||||
* that includes that code point. Returns no_font_found if none of the known fonts contain this value.
|
||||
*/
|
||||
const family_record& find_font_containing(int ch)
|
||||
{
|
||||
for(const auto& i : family_table) {
|
||||
if(TTF_GlyphIsProvided(i.font.get(), ch)) {
|
||||
return i;
|
||||
}
|
||||
if (i != cbmap.end()) {
|
||||
if (/* next.first */ i->first <= last) {
|
||||
insert(first, i->first - 1, id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
cbmap.emplace(first, block_t(last, id));
|
||||
}
|
||||
/**
|
||||
* Compresses map by merging consecutive ranges with the same font, even
|
||||
* if there is some unassociated ranges in-between.
|
||||
*/
|
||||
void compress()
|
||||
LOG_FT << "Glyph " << ch << " not provided by any font\n";
|
||||
return no_font_found;
|
||||
}
|
||||
|
||||
// cache sizes of small text
|
||||
typedef std::map<std::string, SDL_Rect> line_size_cache_map;
|
||||
|
||||
// map of styles -> sizes -> cache
|
||||
static std::map<int, std::map<int, line_size_cache_map>> line_size_cache;
|
||||
|
||||
/**
|
||||
* Destructor for using std::unique_ptr or std::shared_ptr as an RAII holder for a TTF_Font.
|
||||
*/
|
||||
struct font_deleter
|
||||
{
|
||||
void operator()(TTF_Font* font)
|
||||
{
|
||||
LOG_FT << "Font map size before compression: " << cbmap.size() << " ranges\n";
|
||||
cbmap_t::iterator i = cbmap.begin(), e = cbmap.end();
|
||||
while (i != e) {
|
||||
cbmap_t::iterator j = i;
|
||||
++j;
|
||||
if (j == e || i->second.second != j->second.second) {
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
i->second.first = j->second.first;
|
||||
cbmap.erase(j);
|
||||
}
|
||||
LOG_FT << "Font map size after compression: " << cbmap.size() << " ranges\n";
|
||||
}
|
||||
subset_id get_id(int ch)
|
||||
{
|
||||
cbmap_t::iterator i = cbmap.upper_bound(ch);
|
||||
// At this point, either ch < i->first or i is past the end.
|
||||
if (i != cbmap.begin()) {
|
||||
--i;
|
||||
if (ch <= i->second.first /* prev.last */)
|
||||
return i->second.second;
|
||||
}
|
||||
return -1;
|
||||
if(font != nullptr)
|
||||
TTF_CloseFont(font);
|
||||
}
|
||||
};
|
||||
|
||||
static char_block_map char_blocks;
|
||||
|
||||
//cache sizes of small text
|
||||
typedef std::map<std::string,SDL_Rect> line_size_cache_map;
|
||||
|
||||
//map of styles -> sizes -> cache
|
||||
static std::map<int,std::map<int,line_size_cache_map>> line_size_cache;
|
||||
|
||||
typedef std::map<std::pair<std::string, int>, TTF_Font*> open_font_cache;
|
||||
open_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)
|
||||
std::shared_ptr<TTF_Font> open_font(const std::string& fname, int size)
|
||||
{
|
||||
const std::pair<std::string, int> key = std::make_pair(fname, size);
|
||||
const open_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.emplace(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;
|
||||
|
@ -185,18 +145,21 @@ static TTF_Font* open_font_impl(const std::string & fname, int size) {
|
|||
}
|
||||
|
||||
filesystem::rwops_ptr rwops = filesystem::make_read_RWops(name);
|
||||
TTF_Font* font = TTF_OpenFontRW(rwops.release(), true, size); // SDL takes ownership of rwops
|
||||
std::unique_ptr<TTF_Font, font_deleter> font;
|
||||
font.reset(TTF_OpenFontRW(rwops.release(), true, size)); // SDL takes ownership of rwops
|
||||
if(font == nullptr) {
|
||||
ERR_FT << "Failed opening font: '" << fname << "'\n";
|
||||
ERR_FT << "Failed opening font: '" << fname << "'\n";
|
||||
ERR_FT << "TTF_OpenFont: " << TTF_GetError() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DBG_FT << "Opened a font: " << fname << std::endl;
|
||||
DBG_FT << "Opened a font: " << fname << ", in size " << size << std::endl;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// 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
|
||||
|
@ -204,15 +167,10 @@ static TTF_Font* open_font_impl(const std::string & fname, int size) {
|
|||
// rapidly correct the remaining styling using SDL_TTF.
|
||||
//
|
||||
// Uses the font table for caching.
|
||||
TTF_Font* sdl_ttf::get_font(font_id id)
|
||||
std::shared_ptr<TTF_Font> sdl_ttf::get_font(font_id id)
|
||||
{
|
||||
const auto it = font_table.find(id);
|
||||
if(it != font_table.end()) {
|
||||
if (it->second.font != nullptr) {
|
||||
// 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);
|
||||
}
|
||||
if(it != font_table.end() && it->second.font != nullptr) {
|
||||
return it->second.font;
|
||||
}
|
||||
|
||||
|
@ -223,39 +181,43 @@ TTF_Font* sdl_ttf::get_font(font_id id)
|
|||
}
|
||||
|
||||
// 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};
|
||||
if((id.style & TTF_STYLE_ITALIC) && italic_names[id.subset].size()) {
|
||||
if(auto font = open_font(italic_names[id.subset], id.size)) {
|
||||
ttf_record rec{font, TTF_STYLE_ITALIC};
|
||||
// The next line adds bold if needed
|
||||
TTF_SetFontStyle(font.get(), id.style ^ TTF_STYLE_ITALIC);
|
||||
font_table.emplace(id, rec);
|
||||
return sdl_ttf::get_font(id);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// 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};
|
||||
if((id.style & TTF_STYLE_BOLD) && bold_names[id.subset].size()) {
|
||||
if(auto font = open_font(bold_names[id.subset], id.size)) {
|
||||
ttf_record rec{font, TTF_STYLE_BOLD};
|
||||
// The next line adds italic if needed
|
||||
TTF_SetFontStyle(font.get(), id.style ^ TTF_STYLE_BOLD);
|
||||
font_table.emplace(id, rec);
|
||||
return sdl_ttf::get_font(id);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// 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};
|
||||
if(font_names[id.subset].size()) {
|
||||
if(auto font = open_font(font_names[id.subset], id.size)) {
|
||||
ttf_record rec{font, TTF_STYLE_NORMAL};
|
||||
TTF_SetFontStyle(font.get(), id.style);
|
||||
font_table.emplace(id, rec);
|
||||
return sdl_ttf::get_font(id);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to find a font.
|
||||
ttf_record rec {nullptr, TTF_STYLE_NORMAL};
|
||||
ttf_record rec{nullptr, TTF_STYLE_NORMAL};
|
||||
font_table.emplace(id, rec);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Interface to SDL_TTF
|
||||
*/
|
||||
|
@ -407,10 +369,10 @@ SDL_Rect draw_text_line(surface& gui_surface, const SDL_Rect& area, int size,
|
|||
int get_max_height(int size)
|
||||
{
|
||||
// Only returns the maximal size of the first font
|
||||
TTF_Font* const font = sdl_ttf::get_font(font_id(0, size));
|
||||
const auto font = sdl_ttf::get_font(font_id(0, size));
|
||||
if(font == nullptr)
|
||||
return 0;
|
||||
return TTF_FontHeight(font);
|
||||
return TTF_FontHeight(font.get());
|
||||
}
|
||||
|
||||
int line_width(const std::string& line, int font_size, int style)
|
||||
|
@ -471,20 +433,12 @@ std::string make_text_ellipsis(const std::string &text, int font_size,
|
|||
return text; // Should not happen
|
||||
}
|
||||
|
||||
void cache_mode(CACHE mode)
|
||||
{
|
||||
if(mode == CACHE_LOBBY) {
|
||||
text_cache::resize(1000);
|
||||
} else {
|
||||
text_cache::resize(50);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Initialize and destruction
|
||||
*/
|
||||
|
||||
sdl_ttf::sdl_ttf() {
|
||||
sdl_ttf::sdl_ttf()
|
||||
{
|
||||
const int res = TTF_Init();
|
||||
if(res == -1) {
|
||||
ERR_FT << "Could not initialize SDL_TTF" << std::endl;
|
||||
|
@ -494,94 +448,139 @@ sdl_ttf::sdl_ttf() {
|
|||
}
|
||||
}
|
||||
|
||||
static void clear_fonts() {
|
||||
for(const auto & i : open_fonts) {
|
||||
TTF_CloseFont(i.second);
|
||||
}
|
||||
|
||||
open_fonts.clear();
|
||||
|
||||
static void clear_fonts()
|
||||
{
|
||||
// Ensure that the shared_ptr<TTF_Font>s' destructors run before TTF_Quit().
|
||||
font_table.clear();
|
||||
family_table.clear();
|
||||
|
||||
font_names.clear();
|
||||
bold_names.clear();
|
||||
italic_names.clear();
|
||||
|
||||
char_blocks.cbmap.clear();
|
||||
line_size_cache.clear();
|
||||
}
|
||||
|
||||
sdl_ttf::~sdl_ttf() {
|
||||
sdl_ttf::~sdl_ttf()
|
||||
{
|
||||
clear_fonts();
|
||||
TTF_Quit();
|
||||
}
|
||||
|
||||
//sets the font list to be used.
|
||||
// sets the font list to be used.
|
||||
void sdl_ttf::set_font_list(const std::vector<subset_descriptor>& fontlist)
|
||||
{
|
||||
// Wesnoth's startup sequence usually loads the same set of fonts twice.
|
||||
// See if we can use the already-loaded fonts.
|
||||
if(!font_names.empty()) {
|
||||
std::vector<family_record> reordered_family_table;
|
||||
bool found_all_fonts = true;
|
||||
for(const auto& f : fontlist) {
|
||||
// Ignore fonts if the font file doesn't exist - this matches the behavior of when we
|
||||
// can't reuse the already-loaded fonts.
|
||||
if(!check_font_file(f.name))
|
||||
continue;
|
||||
const auto& old_record = std::find_if(
|
||||
family_table.cbegin(), family_table.cend(), [&f](family_record x) { return f.name == x.name; });
|
||||
if(old_record == family_table.cend()) {
|
||||
found_all_fonts = false;
|
||||
break;
|
||||
}
|
||||
reordered_family_table.emplace_back(*old_record);
|
||||
}
|
||||
if(found_all_fonts) {
|
||||
std::swap(family_table, reordered_family_table);
|
||||
DBG_FT << "Reordered the font list, the order is now:\n";
|
||||
for(const auto& x : family_table) {
|
||||
DBG_FT << "[" << x.subset << "]:\t\tbase:\t'" << x.name << "'\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The existing fonts weren't sufficient, or this is the first time that this function has been
|
||||
// called. Load all the fonts from scratch.
|
||||
clear_fonts();
|
||||
|
||||
for(const auto & f : fontlist) {
|
||||
if (!check_font_file(f.name)) continue;
|
||||
// To access TTF_GlyphIsProvided, we need to create instances of each font. Choose a size that
|
||||
// the GUI will want to use.
|
||||
const auto default_size = preferences::font_scaled(font::SIZE_NORMAL);
|
||||
|
||||
for(const auto& f : fontlist) {
|
||||
if(!check_font_file(f.name))
|
||||
continue;
|
||||
// Insert fonts only if the font file exists
|
||||
const subset_id subset = font_names.size();
|
||||
font_names.push_back(f.name);
|
||||
|
||||
if (f.bold_name && check_font_file(*f.bold_name)) {
|
||||
if(f.bold_name && check_font_file(*f.bold_name)) {
|
||||
bold_names.push_back(*f.bold_name);
|
||||
} else {
|
||||
bold_names.emplace_back();
|
||||
}
|
||||
|
||||
if (f.italic_name && check_font_file(*f.italic_name)) {
|
||||
if(f.italic_name && check_font_file(*f.italic_name)) {
|
||||
italic_names.push_back(*f.italic_name);
|
||||
} else {
|
||||
italic_names.emplace_back();
|
||||
}
|
||||
|
||||
for (const subset_descriptor::range &cp_range : f.present_codepoints) {
|
||||
char_blocks.insert(cp_range.first, cp_range.second, subset);
|
||||
}
|
||||
auto font = sdl_ttf::get_font(font_id{subset, default_size});
|
||||
family_table.push_back(family_record{std::move(font), subset, f.name});
|
||||
}
|
||||
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 (std::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";
|
||||
for(std::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";
|
||||
}
|
||||
}
|
||||
|
||||
//Splits the UTF-8 text into text_chunks using the same font.
|
||||
std::vector<text_chunk> sdl_ttf::split_text(const std::string& utf8_text) {
|
||||
text_chunk current_chunk(0);
|
||||
/**
|
||||
* Splits the UTF-8 text into text_chunks using the same font.
|
||||
*
|
||||
* This uses a greedy-match - once we've found the start of a chunk,
|
||||
* include as many characters as we can in the same chunk.
|
||||
*
|
||||
* If we've got a fallback font that contains all characters, and a
|
||||
* preferred font that will only contains some of them, this means that
|
||||
* we minimize the number of times that we switch from one font to the
|
||||
* other - once we've had to use the fallback, keep using it.
|
||||
*
|
||||
* This also means that combining characters such as U+308 or U+FE00 are
|
||||
* kept with the character that they should be modifying.
|
||||
*/
|
||||
std::vector<text_chunk> sdl_ttf::split_text(const std::string& utf8_text)
|
||||
{
|
||||
std::vector<text_chunk> chunks;
|
||||
|
||||
if (utf8_text.empty())
|
||||
if(utf8_text.empty())
|
||||
return chunks;
|
||||
|
||||
try {
|
||||
utf8::iterator ch(utf8_text);
|
||||
int sub = char_blocks.get_id(*ch);
|
||||
if (sub >= 0) current_chunk.subset = sub;
|
||||
for(utf8::iterator end = utf8::iterator::end(utf8_text); ch != end; ++ch)
|
||||
{
|
||||
sub = char_blocks.get_id(*ch);
|
||||
if (sub >= 0 && sub != current_chunk.subset) {
|
||||
chunks.push_back(current_chunk);
|
||||
current_chunk.text.clear();
|
||||
current_chunk.subset = sub;
|
||||
const auto end = utf8::iterator::end(utf8_text);
|
||||
auto chunk_start = utf8::iterator(utf8_text);
|
||||
while(chunk_start != end) {
|
||||
auto& family = find_font_containing(*chunk_start);
|
||||
if(family.subset >= 0) {
|
||||
auto ch = chunk_start;
|
||||
auto last_in_chunk = chunk_start;
|
||||
while(ch != end && TTF_GlyphIsProvided(family.font.get(), *ch)) {
|
||||
last_in_chunk = ch;
|
||||
++ch;
|
||||
}
|
||||
chunks.emplace_back(
|
||||
family.subset, std::string{chunk_start.substr().first, last_in_chunk.substr().second});
|
||||
chunk_start = ch;
|
||||
} else {
|
||||
++chunk_start;
|
||||
}
|
||||
current_chunk.text.append(ch.substr().first, ch.substr().second);
|
||||
}
|
||||
if (!current_chunk.text.empty()) {
|
||||
chunks.push_back(current_chunk);
|
||||
}
|
||||
}
|
||||
catch(utf8::invalid_utf8_exception&) {
|
||||
} catch(utf8::invalid_utf8_exception&) {
|
||||
WRN_FT << "Invalid UTF-8 string: \"" << utf8_text << "\"" << std::endl;
|
||||
}
|
||||
return chunks;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "font_description.hpp"
|
||||
#include "color.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
@ -55,33 +56,32 @@ SDL_Rect line_size(const std::string& line, int font_size, int style=TTF_STYLE_N
|
|||
std::string make_text_ellipsis(const std::string& text, int font_size, int max_width,
|
||||
int style = TTF_STYLE_NORMAL);
|
||||
|
||||
|
||||
enum CACHE { CACHE_LOBBY, CACHE_GAME };
|
||||
void cache_mode(CACHE mode);
|
||||
|
||||
|
||||
|
||||
|
||||
/***
|
||||
* RAII object which initializes and destroys SDL_TTF, and manages caches of open fonts
|
||||
* Object which initializes and destroys SDL_TTF, and manages caches of open fonts.
|
||||
*
|
||||
* This isn't properly self-contained, the .cpp file (and the implementations
|
||||
* of the other functions in this .hpp file) expect that there will be exactly
|
||||
* one instance of this object.
|
||||
*/
|
||||
struct sdl_ttf {
|
||||
sdl_ttf();
|
||||
~sdl_ttf();
|
||||
struct sdl_ttf
|
||||
{
|
||||
sdl_ttf();
|
||||
~sdl_ttf();
|
||||
|
||||
sdl_ttf(const sdl_ttf &) = delete;
|
||||
sdl_ttf & operator = (const sdl_ttf &) = delete;
|
||||
sdl_ttf(const sdl_ttf&) = delete;
|
||||
sdl_ttf& operator=(const sdl_ttf&) = delete;
|
||||
|
||||
// Load a font
|
||||
static TTF_Font * get_font(font_id);
|
||||
// Load a font
|
||||
static std::shared_ptr<TTF_Font> get_font(font_id);
|
||||
|
||||
// Set the list of fonts
|
||||
static void set_font_list(const std::vector<subset_descriptor>& fontlist);
|
||||
|
||||
// Split a utf8 string into text_chunks
|
||||
static std::vector<text_chunk> split_text(const std::string & utf8_text);
|
||||
/**
|
||||
* Set the list of fonts. The order denotes the priority - if text could be rendered with more than one of these
|
||||
* fonts, the one given earlier will be used.
|
||||
*/
|
||||
static void set_font_list(const std::vector<subset_descriptor>& fontlist);
|
||||
|
||||
// Split a utf8 string into text_chunks
|
||||
static std::vector<text_chunk> split_text(const std::string& utf8_text);
|
||||
};
|
||||
|
||||
|
||||
} // end namespace font
|
||||
|
|
|
@ -136,13 +136,13 @@ void text_surface::measure() const
|
|||
|
||||
for(const text_chunk& chunk : chunks_)
|
||||
{
|
||||
TTF_Font* ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
|
||||
auto ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
|
||||
if(ttfont == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int w, h;
|
||||
TTF_SizeUTF8(ttfont, chunk.text.c_str(), &w, &h);
|
||||
TTF_SizeUTF8(ttfont.get(), chunk.text.c_str(), &w, &h);
|
||||
w_ += w;
|
||||
h_ = std::max<int>(h_, h);
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ const std::vector<surface>& text_surface::get_surfaces() const
|
|||
|
||||
for(const text_chunk& chunk : chunks_)
|
||||
{
|
||||
TTF_Font* ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
|
||||
auto ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
|
||||
|
||||
surface s = surface(TTF_RenderUTF8_Blended(ttfont, chunk.text.c_str(), color_.to_sdl()));
|
||||
surface s = surface(TTF_RenderUTF8_Blended(ttfont.get(), chunk.text.c_str(), color_.to_sdl()));
|
||||
if(s)
|
||||
surfs_.push_back(s);
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# codecomp
|
||||
# compare list of codepoints with a baseline list
|
||||
|
||||
while (<>) {
|
||||
# next unless /codepoints=\"(.*)\"/;
|
||||
# push @cs, [ split /,/, $1 ];
|
||||
push @cs, [ split /,/ ];
|
||||
push @fs, $ARGV;
|
||||
}
|
||||
# hardcode DejaVu codepoint list for now, as baseline
|
||||
# use codeextract to update, this list is from DejaVu 2.2
|
||||
@c = split /,/, '32-126,160-387,390-411,413-414,420-421,423-425,427-430,433,437-438,443,448-468,470,477,479,482-483,490-493,497-499,536-539,555,557-559,561-563,567-569,592-696,699-700,702-705,710-712,716,720-723,726,728-734,736-745,768-819,825-831,865,884-885,890,894,900-906,908,910-929,931-974,976-993,1008-1119,1168-1175,1178-1179,1184-1191,1196-1199,1202-1207,1210-1211,1217-1218,1221-1224,1227-1228,1232-1241,1244-1245,1254-1259,7426,7432-7433,7444,7455,7543,7692-7693,7698-7699,7716-7717,7734-7737,7740-7741,7745-7751,7754-7755,7767,7770-7773,7777-7779,7788-7789,7792-7793,7806-7813,7864-7865,7882-7885,7908-7909,7922-7923,8208-8213,8216-8218,8220-8230,8240-8241,8249-8250,8252-8253,8263-8265,8304,8308-8313,8319,8364,8373,8451,8482,8486-8487,8490-8491,8498,8523,8704,8706-8707,8710-8711,8719-8723,8725,8727-8730,8733-8735,8743-8749,8776,8800-8801,8803-8805,8962,8976-8977,8984-8985,8997,9085,9251,9472-9631,9674,9688,9702,9784,10731,10764-10766,64256-64260,65533';
|
||||
|
||||
#foreach $d ( @cs ) {
|
||||
# print join(',', @$d), "\n";
|
||||
#}
|
||||
|
||||
foreach $d ( @cs ) {
|
||||
$i = 0; $j = 0;
|
||||
my @e;
|
||||
# compare @{$d} and @c, want to find all intersections
|
||||
while ( $c[$i] and $d->[$j] ) {
|
||||
($a,$b) = split /-/, $c[$i];
|
||||
$b = $a unless $b;
|
||||
($x,$y) = split /-/, $d->[$j];
|
||||
$y = $x unless $y;
|
||||
if ( $x <= $b and $a <= $y ) { # intersection
|
||||
my $min = ($a > $x) ? $a : $x;
|
||||
my $max = ($b < $y) ? $b : $y;
|
||||
push @e, $min . (($min != $max) ? "-$max" : '');
|
||||
}
|
||||
++$i if $b <= $y;
|
||||
++$j if $b >= $y;
|
||||
# if $b == $y advance both
|
||||
}
|
||||
push @es, [ @e ];
|
||||
}
|
||||
$i = 0;
|
||||
while ( $es[$i] ) {
|
||||
# if *equal* is flagged, then this codepoint list is covered by DejaVu
|
||||
print "*equal* " if join(',', @{$es[$i]}) eq join(',', @{$cs[$i]});
|
||||
print $fs[$i], ' = ', join(',', @{$es[$i]}), "\n";
|
||||
++$i;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#!/usr/bin/perl -n
|
||||
# codeextract
|
||||
# usage: codeextract status.txt | codelist
|
||||
# extracts list of font codes from DejaVu font status.txt file, as a
|
||||
# list of decimal integers suitable as input to codelist
|
||||
|
||||
next if $. < 7; # skip first 6 lines
|
||||
s/ .*//; # get rid of stuff other than codepoint
|
||||
s/^U\+//; # set up number
|
||||
print hex $_, "\n";
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
# codeglyphs
|
||||
# usage: codeglyphs en_GB | codelist
|
||||
# (this should yield something like 10,32-95,97-126,235,243,252)
|
||||
# run this in the main wesnoth directory
|
||||
# expects as input a language code used in LINGUAS
|
||||
# extracts list of glyphs used in translation files, suitable as input
|
||||
# to codelist
|
||||
|
||||
cat `find po -name "$1.po" -print` | \
|
||||
perl -ne 'BEGIN { use Unicode::String } $u = Unicode::String->new($_); push @u, $u->unpack; END { print join("\n",@u), "\n" }' | \
|
||||
sort -nu
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# codelist
|
||||
# given list of integers, one per line, outputs a minimal list of ranges
|
||||
# describing the list
|
||||
|
||||
# this is useful for codepoints that are in a font, based on list of codes
|
||||
# output format is suitable for codepoints="" in Wesnoth fonts.cfg
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def rangeify(lst):
|
||||
"""
|
||||
Turn ranges of adjacent ints in a list into [start, end] list elements.
|
||||
"""
|
||||
lst.sort()
|
||||
lst.append(None)
|
||||
while True:
|
||||
for i in range(len(lst) - 1):
|
||||
if isinstance(lst[i], int) and lst[i + 1] == lst[i] + 1:
|
||||
lst = lst[:i] + [[lst[i], lst[i + 1]]] + lst[i + 2:]
|
||||
break
|
||||
elif isinstance(lst[i], list) and lst[i + 1] == lst[i][-1] + 1:
|
||||
lst[i][1] = lst[i + 1]
|
||||
lst = lst[:i + 1] + lst[i + 2:]
|
||||
break
|
||||
else:
|
||||
break
|
||||
lst.pop()
|
||||
return lst
|
||||
|
||||
|
||||
def printbyrange(lst):
|
||||
out = ""
|
||||
for elt in lst:
|
||||
if isinstance(elt, int):
|
||||
out += repr(elt) + ","
|
||||
else:
|
||||
out += "%d-%d," % tuple(elt)
|
||||
return out[:-1]
|
||||
|
||||
codepoints = [int(x.strip()) for x in sys.stdin.readlines()]
|
||||
print(printbyrange(rangeify(codepoints)))
|
Loading…
Add table
Reference in a new issue