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:
Steve Cotton 2020-10-10 00:36:12 +02:00 committed by Steve Cotton
parent d2bc8e264f
commit 01424cc853
11 changed files with 220 additions and 338 deletions

View file

@ -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

View file

@ -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

View file

@ -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))
{
}

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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";

View file

@ -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

View file

@ -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)))