← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/listselect into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/listselect into lp:widelands.

Commit message:
- Listselect now uses the new font renderer
- Implemented locale fontset selection by word into the new renderer.
- Fontsets now have a representative character that is used to calculate line height.
- Fixed an overflow bug in the renderer.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/listselect/+merge/286033

- Listselect now uses the new font renderer
- Implemented locale fontset selection by word into the new renderer.
- Fontsets now have a representative character that is used to calculate line height.
- Fixed an overflow bug in the renderer.

I commented out an assert, I don't know if we still need that one, since everything seems to work. I have added a NOCOM there.

We will probably want font selection by character rather than by word in the future, but this will do for build 19.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/listselect into lp:widelands.
=== modified file 'data/i18n/fonts.lua'
--- data/i18n/fonts.lua	2016-02-07 15:47:25 +0000
+++ data/i18n/fonts.lua	2016-02-15 11:00:58 +0000
@@ -28,6 +28,8 @@
       -- If the direction isn't defined, your font set will default to "ltr" = left-to-right.
       -- For right-to-left or bidirectional (BiDi) languages, use "rtl".
       direction = "ltr",
+      -- This is used to calculate line height for the fontset. Use any character for your language's script.
+      representative_character = "a",
       size_offset = 0
    },
 
@@ -37,15 +39,18 @@
       sans_italic = "amiri/amiri-slanted.ttf",
       sans_bold_italic = "amiri/amiri-boldslanted.ttf",
       direction = "rtl",
+      representative_character = "ا",
       size_offset = 4
    },
 
    cjk = {
       sans = "MicroHei/wqy-microhei.ttc",
+      representative_character = "简",
    },
 
    devanagari = {
       sans = "Nakula/nakula.ttf",
+      representative_character = "ह",
       direction = "rtl",
       size_offset = 2
    },
@@ -57,16 +62,19 @@
       serif = "Culmus/TaameyFrankCLM-Medium.ttf",
       serif_bold = "TaameyFrankCLM-Bold.ttf",
       serif_italic = "Culmus/TaameyFrankCLM-MediumOblique.ttf",
+      representative_character = "א",
       direction = "rtl",
       size_offset = 4
    },
 
    myanmar = {
       sans = "mmrCensus/mmrCensus.ttf",
+      representative_character = "မ",
       size_offset = 2
    },
 
    sinhala = {
       sans = "Sinhala/lklug.ttf",
+      representative_character = "ස",
    }
 }

=== modified file 'src/editor/ui_menus/editor_main_menu_load_or_save_map.cc'
--- src/editor/ui_menus/editor_main_menu_load_or_save_map.cc	2016-02-06 10:59:09 +0000
+++ src/editor/ui_menus/editor_main_menu_load_or_save_map.cc	2016-02-15 11:00:58 +0000
@@ -51,7 +51,7 @@
 		  this, right_column_x_, tabley_, get_inner_w() - right_column_x_ - padding_, tableh_),
      ok_(this,
          "ok",
-			UI::g_fh1->fontset().is_rtl() ? get_inner_w() / 2 - butw_ - padding_ : get_inner_w() / 2 + padding_,
+			UI::g_fh1->fontset()->is_rtl() ? get_inner_w() / 2 - butw_ - padding_ : get_inner_w() / 2 + padding_,
          get_inner_h() - padding_ - buth_,
          butw_,
          buth_,
@@ -59,7 +59,7 @@
          _("OK")),
      cancel_(this,
              "cancel",
-				 UI::g_fh1->fontset().is_rtl() ?
+				 UI::g_fh1->fontset()->is_rtl() ?
 					 get_inner_w() / 2 + padding_ :
 					 get_inner_w() / 2 - butw_ - padding_,
              get_inner_h() - padding_ - buth_,

=== modified file 'src/editor/ui_menus/editor_main_menu_map_options.cc'
--- src/editor/ui_menus/editor_main_menu_map_options.cc	2016-02-06 10:59:09 +0000
+++ src/editor/ui_menus/editor_main_menu_map_options.cc	2016-02-15 11:00:58 +0000
@@ -34,7 +34,6 @@
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/textarea.h"
 
-
 inline EditorInteractive & MainMenuMapOptions::eia() {
 	return dynamic_cast<EditorInteractive&>(*get_parent());
 }
@@ -47,27 +46,26 @@
 	:
 	UI::Window
 		(&parent, "map_options",
-		 0, 0, 350, 520,
+		 0, 0, 350, parent.get_inner_h() - 80,
 		 _("Map Options")),
 	padding_(4),
 	indent_(10),
-	labelh_(20),
+	labelh_(UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 4),
 	checkbox_space_(25),
 	butw_((get_inner_w() - 3 * padding_) / 2),
-	buth_(20),
 	max_w_(get_inner_w() - 2 * padding_),
 	ok_(
 		this, "ok",
-		UI::g_fh1->fontset().is_rtl() ? padding_ : butw_ + 2 * padding_,
-		get_inner_h() - padding_ - buth_,
-		butw_, buth_,
+		UI::g_fh1->fontset()->is_rtl() ? padding_ : butw_ + 2 * padding_,
+		get_inner_h() - padding_ - labelh_,
+		butw_, labelh_,
 		g_gr->images().get("images/ui_basic/but5.png"),
 		_("OK")),
 	cancel_(
 		this, "cancel",
-		UI::g_fh1->fontset().is_rtl() ? butw_ + 2 * padding_ : padding_,
-		get_inner_h() - padding_ - buth_,
-		butw_, buth_,
+		UI::g_fh1->fontset()->is_rtl() ? butw_ + 2 * padding_ : padding_,
+		get_inner_h() - padding_ - labelh_,
+		butw_, labelh_,
 		g_gr->images().get("images/ui_basic/but1.png"),
 		_("Cancel")),
 	tab_box_(this, padding_, padding_, UI::Box::Vertical, max_w_, get_inner_h(), 0),
@@ -81,14 +79,25 @@
 	author_(&main_box_, 0, 0, max_w_, g_gr->images().get("images/ui_basic/but1.png")),
 	size_(&main_box_, 0, 0, max_w_ - indent_, labelh_, ""),
 
-	teams_list_(&teams_box_, 0, 0, max_w_, 60, UI::Align::kLeft, true),
+	teams_list_(&teams_box_, 0, 0, max_w_, 60, true),
 
 	modal_(modal) {
 
+	tab_box_.set_size(max_w_, get_inner_h() - labelh_ - 2 * padding_);
+	tabs_.set_size(max_w_, tab_box_.get_inner_h());
+	main_box_.set_size(max_w_, tabs_.get_inner_h() - 35);
+	tags_box_.set_size(max_w_, main_box_.get_h());
+	teams_box_.set_size(max_w_, main_box_.get_h());
+
+	// Calculate the overall remaining space for MultilineEditboxes.
+	uint32_t remaining_space = main_box_.get_inner_h() - 7 * labelh_ - 5 * indent_;
+
+	// We need less space for the hint and the description, but it should at least have 1 line height.
+	hint_ = new UI::MultilineEditbox(
+				  &main_box_, 0, 0, max_w_, std::max(labelh_, remaining_space * 1 / 3), "", g_gr->images().get("images/ui_basic/but1.png"));
 	descr_ = new UI::MultilineEditbox(
-					&main_box_, 0, 0, max_w_, 9 * labelh_, "", g_gr->images().get("images/ui_basic/but1.png"));
-	hint_ = new UI::MultilineEditbox(
-				  &main_box_, 0, 0, max_w_, 4 * labelh_, "", g_gr->images().get("images/ui_basic/but1.png"));
+					&main_box_, 0, 0, max_w_, remaining_space - hint_->get_h(),
+					"", g_gr->images().get("images/ui_basic/but1.png"));
 
 	main_box_.add(new UI::Textarea(&main_box_, 0, 0, max_w_, labelh_, _("Map Name:")), UI::Align::kLeft);
 	main_box_.add(&name_, UI::Align::kLeft);
@@ -110,7 +119,6 @@
 	main_box_.add(&size_, UI::Align::kLeft);
 	main_box_.add_space(indent_);
 
-	main_box_.set_size(max_w_, get_inner_h() - buth_ - 2 * padding_);
 
 	tags_box_.add(new UI::Textarea(&tags_box_, 0, 0, max_w_, labelh_, _("Tags:")), UI::Align::kLeft);
 	add_tag_checkbox(&tags_box_, "unbalanced", _("Unbalanced"));
@@ -119,7 +127,6 @@
 	add_tag_checkbox(&tags_box_, "2teams", _("Teams of 2"));
 	add_tag_checkbox(&tags_box_, "3teams", _("Teams of 3"));
 	add_tag_checkbox(&tags_box_, "4teams", _("Teams of 4"));
-	tags_box_.set_size(max_w_, get_inner_h() - buth_ - 2 * padding_);
 
 	teams_box_.add(new UI::Textarea(&teams_box_, 0, 0, max_w_, labelh_, _("Suggested Teams:")),
 						UI::Align::kLeft);
@@ -132,16 +139,12 @@
 	unsigned int nr_players = static_cast<unsigned int>(eia().egbase().map().get_nrplayers());
 	std::string players = (boost::format(ngettext("%u Player", "%u Players", nr_players)) % nr_players).str();
 	teams_box_.add(new UI::Textarea(&teams_box_, 0, 0, max_w_, labelh_, players), UI::Align::kLeft);
-	teams_box_.set_size(max_w_, get_inner_h() - buth_ - 2 * padding_);
 
 	tab_box_.add(&tabs_, UI::Align::kLeft, true);
 	tabs_.add("main_map_options",
 				 g_gr->images().get("images/wui/menus/menu_toggle_minimap.png"), &main_box_, _("Main Options"));
 	tabs_.add("map_tags", g_gr->images().get("images/ui_basic/checkbox_checked.png"), &tags_box_, _("Tags"));
-	tabs_.add("map_teams", g_gr->images().get("images/wui/editor/editor_menu_player_menu.png"),
-				 &teams_box_, _("Teams"));
-	tabs_.set_size(max_w_, get_inner_h() - buth_ - 2 * padding_);
-	tab_box_.set_size(max_w_, get_inner_h() - buth_ - 2 * padding_);
+	tabs_.add("map_teams", g_gr->images().get("images/wui/editor/editor_menu_player_menu.png"), &teams_box_, _("Teams"));
 
 	name_.changed.connect(boost::bind(&MainMenuMapOptions::changed, this));
 	author_.changed.connect(boost::bind(&MainMenuMapOptions::changed, this));

=== modified file 'src/editor/ui_menus/editor_main_menu_map_options.h'
--- src/editor/ui_menus/editor_main_menu_map_options.h	2016-01-16 12:55:14 +0000
+++ src/editor/ui_menus/editor_main_menu_map_options.h	2016-02-15 11:00:58 +0000
@@ -48,7 +48,7 @@
 	void clicked_cancel();
 	void add_tag_checkbox(UI::Box* box, std::string tag, std::string displ_name);
 
-	const int padding_, indent_, labelh_, checkbox_space_, butw_, buth_, max_w_;
+	const unsigned int padding_, indent_, labelh_, checkbox_space_, butw_, max_w_;
 
 	UI::Button ok_, cancel_;
 

=== modified file 'src/editor/ui_menus/editor_main_menu_new_map.cc'
--- src/editor/ui_menus/editor_main_menu_new_map.cc	2016-02-07 09:16:18 +0000
+++ src/editor/ui_menus/editor_main_menu_new_map.cc	2016-02-15 11:00:58 +0000
@@ -93,7 +93,7 @@
 
 	cancel_button_.sigclicked.connect(boost::bind(&MainMenuNewMap::clicked_cancel, this));
 	ok_button_.sigclicked.connect(boost::bind(&MainMenuNewMap::clicked_create_map, this));
-	if (UI::g_fh1->fontset().is_rtl()) {
+	if (UI::g_fh1->fontset()->is_rtl()) {
 		button_box_.add(&ok_button_, UI::Align::kLeft);
 		button_box_.add(&cancel_button_, UI::Align::kLeft);
 	} else {

=== modified file 'src/editor/ui_menus/editor_main_menu_random_map.cc'
--- src/editor/ui_menus/editor_main_menu_random_map.cc	2016-02-07 09:16:18 +0000
+++ src/editor/ui_menus/editor_main_menu_random_map.cc	2016-02-15 11:00:58 +0000
@@ -46,7 +46,7 @@
 	// UI elements
 	margin_(4),
 	box_width_(get_inner_w() -  2 * margin_),
-	label_height_(UI::g_fh1->render(as_uifont("."))->height() + 2),
+	label_height_(UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 2),
 	box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),
 	// Size
 	width_(&box_, 0, 0, box_width_, box_width_ / 3,
@@ -292,7 +292,7 @@
 	// ---------- "Generate Map" button ----------
 	cancel_button_.sigclicked.connect(boost::bind(&MainMenuNewRandomMap::clicked_cancel, this));
 	ok_button_.sigclicked.connect(boost::bind(&MainMenuNewRandomMap::clicked_create_map, this));
-	if (UI::g_fh1->fontset().is_rtl()) {
+	if (UI::g_fh1->fontset()->is_rtl()) {
 		button_box_.add(&ok_button_, UI::Align::kLeft);
 		button_box_.add(&cancel_button_, UI::Align::kLeft);
 	} else {

=== modified file 'src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc'
--- src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc	2016-02-06 10:59:09 +0000
+++ src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc	2016-02-15 11:00:58 +0000
@@ -36,14 +36,14 @@
 	edit_(&vbox_, 0, 0, get_inner_w() - 2 * padding_, g_gr->images().get("images/ui_basic/but1.png")),
 	ok_button_(
 		this, "ok",
-		UI::g_fh1->fontset().is_rtl() ? padding_ : get_inner_w() - butw_ - padding_,
+		UI::g_fh1->fontset()->is_rtl() ? padding_ : get_inner_w() - butw_ - padding_,
 		get_inner_h() - padding_ - buth_,
 		butw_, buth_,
 		g_gr->images().get("images/ui_basic/but5.png"),
 		_("OK")),
 	cancel_button_(
 		this, "cancel",
-		UI::g_fh1->fontset().is_rtl() ?get_inner_w() - butw_ - padding_ : padding_,
+		UI::g_fh1->fontset()->is_rtl() ?get_inner_w() - butw_ - padding_ : padding_,
 		get_inner_h() - padding_ - buth_,
 		butw_, buth_,
 		g_gr->images().get("images/ui_basic/but1.png"),

=== modified file 'src/graphic/align.cc'
--- src/graphic/align.cc	2015-12-17 09:36:59 +0000
+++ src/graphic/align.cc	2016-02-15 11:00:58 +0000
@@ -26,7 +26,7 @@
 
 // This mirrors the horizontal alignment for RTL languages.
 Align mirror_alignment(Align alignment) {
-	if (UI::g_fh1->fontset().is_rtl()) {
+	if (UI::g_fh1->fontset()->is_rtl()) {
 		switch (alignment) {
 			case Align::kBottomLeft:
 				alignment = Align::kBottomRight;

=== modified file 'src/graphic/font.cc'
--- src/graphic/font.cc	2016-01-28 05:24:34 +0000
+++ src/graphic/font.cc	2016-02-15 11:00:58 +0000
@@ -139,7 +139,7 @@
  */
 Font * Font::get(const std::string & name, int size)
 {
-	size += UI::g_fh1->fontset().size_offset();
+	size += UI::g_fh1->fontset()->size_offset();
 	FontDescr descr;
 	descr.name = name;
 	descr.size = size;

=== modified file 'src/graphic/font_handler1.cc'
--- src/graphic/font_handler1.cc	2016-01-19 08:55:16 +0000
+++ src/graphic/font_handler1.cc	2016-02-15 11:00:58 +0000
@@ -108,10 +108,11 @@
 class FontHandler1 : public IFontHandler1 {
 public:
 	FontHandler1(ImageCache* image_cache)
-	   : texture_cache_(new TextureCache(RICHTEXT_TEXTURE_CACHE)),
-	     fontset_(new UI::FontSet(i18n::get_locale())),
-	     rt_renderer_(new RT::Renderer(image_cache, texture_cache_.get(), fontset_.get())),
-	     image_cache_(image_cache) {
+		: texture_cache_(new TextureCache(RICHTEXT_TEXTURE_CACHE)),
+		  fontsets_(),
+		  fontset_(fontsets_.get_fontset(i18n::get_locale())),
+		  rt_renderer_(new RT::Renderer(image_cache, texture_cache_.get(), fontsets_)),
+		  image_cache_(image_cache) {
 	}
 	virtual ~FontHandler1() {}
 
@@ -128,17 +129,18 @@
 		return image_cache_->insert(hash, std::move(image));
 	}
 
-	UI::FontSet& fontset() const override {return *fontset_.get();}
+	UI::FontSet const * fontset() const override {return fontset_;}
 
 	void reinitialize_fontset() override {
-		fontset_.reset(new UI::FontSet(i18n::get_locale()));
+		fontset_ = fontsets_.get_fontset(i18n::get_locale());
 		texture_cache_.get()->flush();
-		rt_renderer_.reset(new RT::Renderer(image_cache_, texture_cache_.get(), fontset_.get()));
+		rt_renderer_.reset(new RT::Renderer(image_cache_, texture_cache_.get(), fontsets_));
 	}
 
 private:
 	std::unique_ptr<TextureCache> texture_cache_;
-	std::unique_ptr<UI::FontSet> fontset_; // The currently active FontSet
+	UI::FontSets fontsets_; // All fontsets
+	UI::FontSet const * fontset_; // The currently active FontSet
 	std::unique_ptr<RT::Renderer> rt_renderer_;
 	ImageCache* const image_cache_;  // not owned
 };

=== modified file 'src/graphic/font_handler1.h'
--- src/graphic/font_handler1.h	2016-01-09 15:27:05 +0000
+++ src/graphic/font_handler1.h	2016-02-15 11:00:58 +0000
@@ -51,7 +51,7 @@
 	virtual const Image* render(const std::string& text, uint16_t w = 0) = 0;
 
 	/// Returns the font handler's current FontSet
-	virtual UI::FontSet& fontset() const = 0;
+	virtual UI::FontSet const * fontset() const = 0;
 
 	/// Loads the FontSet for the currently active locale into the
 	/// font handler. This needs to be called after the language of the

=== modified file 'src/graphic/richtext.cc'
--- src/graphic/richtext.cc	2016-01-31 21:03:15 +0000
+++ src/graphic/richtext.cc	2016-02-15 11:00:58 +0000
@@ -81,7 +81,7 @@
 		std::vector<std::string>::iterator it = result_words.begin();
 
 		// Reorder words for BiDi
-		if (UI::g_fh1->fontset().is_rtl() && i18n::has_rtl_character(words)) {
+		if (UI::g_fh1->fontset()->is_rtl() && i18n::has_rtl_character(words)) {
 			std::string previous_word;
 			for (std::vector<std::string>::iterator source_it = words.begin();
 				  source_it != words.end(); ++source_it) {

=== modified file 'src/graphic/text/bidi.cc'
--- src/graphic/text/bidi.cc	2016-01-18 19:35:25 +0000
+++ src/graphic/text/bidi.cc	2016-02-15 11:00:58 +0000
@@ -139,32 +139,6 @@
 };
 
 
-// http://unicode.org/faq/blocks_ranges.html
-// http://unicode-table.com/en/blocks/
-const std::set<UBlockCode> kCJKCodeBlocks = {
-	{
-		UBlockCode::UBLOCK_CJK_COMPATIBILITY,
-		UBlockCode::UBLOCK_CJK_COMPATIBILITY_FORMS,
-		UBlockCode::UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,
-		UBlockCode::UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT,
-		UBlockCode::UBLOCK_CJK_RADICALS_SUPPLEMENT,
-		UBlockCode::UBLOCK_CJK_STROKES,
-		UBlockCode::UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION,
-		UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS,
-		UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A,
-		UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B,
-		UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C,
-		UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D,
-		UBlockCode::UBLOCK_HIRAGANA,
-		UBlockCode::UBLOCK_KATAKANA,
-	},
-};
-
-bool is_cjk_character(UChar32 c) {
-	return kCJKCodeBlocks.count(ublock_getCode(c)) == 1;
-}
-
-
 // Need to mirror () etc. for LTR languages, so we're sticking them in a map.
 const std::map<UChar, UChar> kSymmetricChars = {
 	{0x0028, 0x0029}, // ()
@@ -441,15 +415,60 @@
 	{0xFCF4, 0xFC62}, // Shadda with Kasra
 };
 
-const std::set<std::string> kRTLScripts = {
-	{"arabic", "devanagari", "hebrew", "mandaic", "nko", "samaritan", "syriac", "thaana"},
+const std::set<UI::FontSets::Selector> kLTRScripts = {
+	// We omit the default fontset, because we won't define code blocks for it - it's a catch-all.
+	UI::FontSets::Selector::kCJK,
+	UI::FontSets::Selector::kMyanmar,
+	UI::FontSets::Selector::kSinhala
+};
+
+// http://unicode.org/faq/blocks_ranges.html
+// http://unicode-table.com/en/blocks/
+const std::map<UI::FontSets::Selector, std::set<UBlockCode>> kLTRCodeBlocks = {
+	{UI::FontSets::Selector::kCJK, {
+		 UBlockCode::UBLOCK_CJK_COMPATIBILITY,
+		 UBlockCode::UBLOCK_CJK_COMPATIBILITY_FORMS,
+		 UBlockCode::UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,
+		 UBlockCode::UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT,
+		 UBlockCode::UBLOCK_CJK_RADICALS_SUPPLEMENT,
+		 UBlockCode::UBLOCK_CJK_STROKES,
+		 UBlockCode::UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION,
+		 UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS,
+		 UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A,
+		 UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B,
+		 UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C,
+		 UBlockCode::UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D,
+		 UBlockCode::UBLOCK_HIRAGANA,
+		 UBlockCode::UBLOCK_KATAKANA,
+		 UBlockCode::UBLOCK_KATAKANA_PHONETIC_EXTENSIONS,
+		 UBlockCode::UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS,
+		 UBlockCode::UBLOCK_HANGUL_COMPATIBILITY_JAMO,
+		 UBlockCode::UBLOCK_HANGUL_JAMO,
+		 UBlockCode::UBLOCK_HANGUL_JAMO_EXTENDED_A,
+		 UBlockCode::UBLOCK_HANGUL_JAMO_EXTENDED_B,
+		 UBlockCode::UBLOCK_HANGUL_SYLLABLES,
+	 }},
+	{UI::FontSets::Selector::kMyanmar, {
+		 UBlockCode::UBLOCK_MYANMAR,
+		 UBlockCode::UBLOCK_MYANMAR_EXTENDED_A,
+	 }},
+	{UI::FontSets::Selector::kSinhala, {
+		 UBlockCode::UBLOCK_SINHALA,
+	 }},
+};
+
+const std::set<UI::FontSets::Selector> kRTLScripts = {
+	// Add "mandaic", "nko", "samaritan", "syriac", "thaana" if we get these languages.
+	UI::FontSets::Selector::kArabic,
+	UI::FontSets::Selector::kDevanagari,
+	UI::FontSets::Selector::kHebrew
 };
 
 // http://unicode.org/faq/blocks_ranges.html
 // http://unicode-table.com/en/blocks/
 // TODO(GunChleoc): We might need some more here - let's see how this goes.
-const std::map<std::string, std::set<UBlockCode>> kRTLCodeBlocks = {
-	{"arabic", {
+const std::map<UI::FontSets::Selector, std::set<UBlockCode>> kRTLCodeBlocks = {
+	{UI::FontSets::Selector::kArabic, {
 		 UBlockCode::UBLOCK_ARABIC,
 		 UBlockCode::UBLOCK_ARABIC_SUPPLEMENT,
 		 UBlockCode::UBLOCK_ARABIC_EXTENDED_A,
@@ -457,13 +476,15 @@
 		 UBlockCode::UBLOCK_ARABIC_PRESENTATION_FORMS_B,
 		 UBlockCode::UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS,
 	 }},
-	{"devanagari", {
+	{UI::FontSets::Selector::kDevanagari, {
 		 UBlockCode::UBLOCK_DEVANAGARI,
 		 UBlockCode::UBLOCK_DEVANAGARI_EXTENDED,
+		 UBlockCode::UBLOCK_VEDIC_EXTENSIONS,
 	 }},
-	{"hebrew", {
+	{UI::FontSets::Selector::kHebrew, {
 		 UBlockCode::UBLOCK_HEBREW,
 	 }},
+	/* Activate when we get any of these languages
 	{"mandaic", {
 		 UBlockCode::UBLOCK_MANDAIC,
 	 }},
@@ -479,12 +500,24 @@
 	{"thaana", {
 		 UBlockCode::UBLOCK_THAANA,
 	 }},
+		 */
 };
 
-// True if 'c' is included in one of the kRTLCodeBlocks.
+// True if the character is in one of the script's code blocks
+bool is_script_character(UChar32 c, UI::FontSets::Selector script) {
+	UBlockCode code = ublock_getCode(c);
+	if (kRTLCodeBlocks.count(script) == 1 && kRTLCodeBlocks.at(script).count(code) == 1) {
+		return true;
+	}
+	if (kLTRCodeBlocks.count(script) == 1 && kLTRCodeBlocks.at(script).count(code) == 1) {
+		return true;
+	}
+	return false;
+}
+
 bool is_rtl_character(UChar32 c) {
 	UBlockCode code = ublock_getCode(c);
-	for (std::string script : kRTLScripts) {
+	for (UI::FontSets::Selector script : kRTLScripts) {
 		assert(kRTLCodeBlocks.count(script) == 1);
 		if ((kRTLCodeBlocks.at(script).count(code) == 1)) {
 			return true;
@@ -493,27 +526,6 @@
 	return false;
 }
 
-bool is_arabic_character(UChar32 c) {
-	UBlockCode code = ublock_getCode(c);
-	assert(kRTLCodeBlocks.count("arabic") == 1);
-	return (kRTLCodeBlocks.at("arabic").count(code) == 1);
-}
-
-
-// True if a string contains a character from an Arabic code block
-bool has_arabic_character(const char* input) {
-	bool result = false;
-	const icu::UnicodeString parseme(input);
-	for (int32_t i = 0; i < parseme.length(); ++i) {
-		if (is_arabic_character(parseme.char32At(i))) {
-			result = true;
-			break;
-		}
-	}
-	return result;
-}
-
-
 // Helper function for make_ligatures.
 // Arabic word characters have 4 forms to connect to each other:
 // Isolated, Initial, Medial, and Final.
@@ -557,37 +569,33 @@
 namespace i18n {
 
 
-// True if one of te first 'limit' characters in 'input' as UnicodeString is an RTL character
-// according to is_rtl_character(UChar32 c)
+// True if a string does not contain Latin characters.
+// Checks for the first 'limit' characters maximum.
 bool has_rtl_character(const char* input, int32_t limit) {
-	bool result = false;
 	const icu::UnicodeString parseme(input);
 	for (int32_t i = 0; i < parseme.length() && i < limit; ++i) {
 		if (is_rtl_character(parseme.char32At(i))) {
-			result = true;
-			break;
+			return true;
 		}
 	}
-	return result;
+	return false;
 }
 
 // True if the strings do not contain Latin characters
 bool has_rtl_character(std::vector<std::string> input) {
-	bool result = false;
 	for (const std::string& string: input) {
 		if (has_rtl_character(string.c_str())) {
-			result = true;
-			break;
+			return true;
 		}
 	}
-	return result;
+	return false;
 }
 
 
 // Contracts glyphs into their ligatures
 std::string make_ligatures(const char* input) {
 	// We only have defined ligatures for Arabic at the moment.
-	if (!has_arabic_character(input)) {
+	if (!has_script_character(input, UI::FontSets::Selector::kArabic)) {
 		return input;
 	}
 	const icu::UnicodeString parseme(input);
@@ -724,17 +732,36 @@
 	return icustring2string(temp);
 }
 
-// True if a string contains a character from a CJK code block
-bool has_cjk_character(const char* input) {
-	bool result = false;
+// True if a string contains a character from the script's code blocks
+bool has_script_character(const char* input, UI::FontSets::Selector script) {
 	const icu::UnicodeString parseme(input);
 	for (int32_t i = 0; i < parseme.length(); ++i) {
-		if (is_cjk_character(parseme.char32At(i))) {
-			result = true;
-			break;
+		if (is_script_character(parseme.char32At(i), script)) {
+			return true;
 		}
 	}
-	return result;
+	return false;
+}
+
+
+UI::FontSet const * find_fontset(const char* word, const UI::FontSets& fontsets) {
+	UI::FontSets::Selector selector;
+	if (has_script_character(word, UI::FontSets::Selector::kArabic)) {
+		selector = UI::FontSets::Selector::kArabic;
+	} else if (has_script_character(word, UI::FontSets::Selector::kCJK)) {
+		selector = UI::FontSets::Selector::kCJK;
+	} else if (has_script_character(word, UI::FontSets::Selector::kDevanagari)) {
+		selector = UI::FontSets::Selector::kDevanagari;
+	} else if (has_script_character(word, UI::FontSets::Selector::kHebrew)) {
+		selector = UI::FontSets::Selector::kHebrew;
+	} else if (has_script_character(word, UI::FontSets::Selector::kMyanmar)) {
+		selector = UI::FontSets::Selector::kMyanmar;
+	} else if (has_script_character(word, UI::FontSets::Selector::kSinhala)) {
+		selector = UI::FontSets::Selector::kSinhala;
+	} else {
+		selector = UI::FontSets::Selector::kDefault;
+	}
+	return fontsets.get_fontset(selector);
 }
 
 //  Split a string of CJK characters into units that can have line breaks between them.

=== modified file 'src/graphic/text/bidi.h'
--- src/graphic/text/bidi.h	2016-01-06 12:12:47 +0000
+++ src/graphic/text/bidi.h	2016-02-15 11:00:58 +0000
@@ -27,16 +27,21 @@
 #include <unicode/uchar.h>
 #include <unicode/unistr.h>
 
+#include "graphic/text/font_set.h"
+
 // BiDi support for RTL languages
 namespace i18n {
+
 std::string make_ligatures(const char* input);
 std::string line2bidi(const char* input);
 std::vector<std::string> split_cjk_word(const char* input);
 bool has_rtl_character(const char* input, int32_t limit = std::numeric_limits<int32_t>::max());
 bool has_rtl_character(std::vector<std::string> input);
+// True if a string contains a character from the script's code blocks
+bool has_script_character(const char* input, UI::FontSets::Selector script);
+UI::FontSet const * find_fontset(const char* word, const UI::FontSets& fontsets);
 std::string icustring2string(const UnicodeString& convertme);
 std::string icuchar2string(const UChar& convertme);
-bool has_cjk_character(const char* input);
 bool cannot_start_line(const UChar& c);
 bool cannot_end_line(const UChar& c);
 bool is_diacritic(const UChar& c);

=== modified file 'src/graphic/text/font_set.cc'
--- src/graphic/text/font_set.cc	2016-02-07 15:47:25 +0000
+++ src/graphic/text/font_set.cc	2016-02-15 11:00:58 +0000
@@ -20,7 +20,6 @@
 #include "graphic/text/font_set.h"
 
 #include <cstdlib>
-#include <memory>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/format.hpp>
@@ -33,8 +32,8 @@
 
 namespace UI {
 
-FontSet::FontSet(const std::string& localename) {
-	parse_font_for_locale(localename);
+FontSet::FontSet(const std::string& fontset_name) {
+	parse_fontset(fontset_name);
 	assert(!serif_.empty());
 	assert(!serif_bold_.empty());
 	assert(!serif_italic_.empty());
@@ -49,6 +48,7 @@
 	assert(!condensed_bold_italic_.empty());
 }
 
+const std::string& FontSet::name() const {return name_;}
 const std::string& FontSet::serif() const {return serif_;}
 const std::string& FontSet::serif_bold() const {return serif_bold_;}
 const std::string& FontSet::serif_italic() const {return serif_italic_;}
@@ -61,14 +61,13 @@
 const std::string& FontSet::condensed_bold() const {return condensed_bold_;}
 const std::string& FontSet::condensed_italic() const {return condensed_italic_;}
 const std::string& FontSet::condensed_bold_italic() const {return condensed_bold_italic_;}
+const std::string& FontSet::representative_character() const {return representative_character_;}
 uint16_t FontSet::size_offset() const {return size_offset_;}
 bool FontSet::is_rtl() const {return is_rtl_;}
 
 
 // Loads font info from config files, depending on the localename
-void FontSet::parse_font_for_locale(const std::string& localename) {
-	std::string fontsetname = "default";
-	std::string actual_localename = localename;
+void FontSet::parse_fontset(const std::string& fontset_name) {
 	std::string direction_string;
 	size_offset_ = 0;
 	LuaInterface lua;
@@ -76,67 +75,31 @@
 	// Read default fontset. It defines the fallback fonts and needs to always be there and complete.
 	// This way, we will always have fonts, even if we run into an exception further down.
 	std::unique_ptr<LuaTable> fonts_table(lua.run_script("i18n/fonts.lua"));
-	fonts_table->do_not_warn_about_unaccessed_keys();  // We are only reading partial information as needed
+	fonts_table->do_not_warn_about_unaccessed_keys();  // We are only reading one fontset + the default fontset
 
+	// Initialize with default fontset
 	std::unique_ptr<LuaTable> default_font_table = fonts_table->get_table("default");
-
 	set_fonts(*default_font_table, kFallbackFont);
 	direction_string = default_font_table->get_string("direction");
+	representative_character_ = default_font_table->get_string("representative_character");
 	size_offset_ = default_font_table->get_int("size_offset");
 
-	// Now try to get the fontset for the actual locale.
-	try  {
-		std::unique_ptr<LuaTable> all_locales(lua.run_script(("i18n/locales.lua")));
-		all_locales->do_not_warn_about_unaccessed_keys();
-
-		// Locale identifiers can look like this: ca_ES@xxxxxxxxxxxx-8
-		if (localename.empty()) {
-			std::vector<std::string> parts;
-			boost::split(parts, i18n::get_locale(), boost::is_any_of("."));
-			actual_localename = parts[0];
-
-			if (!all_locales->has_key(actual_localename)) {
-				boost::split(parts, parts[0], boost::is_any_of("@"));
-				actual_localename = parts[0];
-
-				if (!all_locales->has_key(actual_localename)) {
-					boost::split(parts, parts[0], boost::is_any_of("_"));
-					actual_localename = parts[0];
-				}
-			}
-		}
-
-		// Find out which fontset to use from the locale
-		if (all_locales->has_key(actual_localename)) {
-			try  {
-				std::unique_ptr<LuaTable> locale_table = all_locales->get_table(actual_localename);
-				locale_table->do_not_warn_about_unaccessed_keys();
-				fontsetname = locale_table->get_string("font");
-
-				// Read the fontset for the current locale.
-				try {
-					if (!fonts_table->has_key(fontsetname)) {
-						log("Font set '%s' for locale '%s' does not exist; using default instead.\n",
-							 fontsetname.c_str(), actual_localename.c_str());
-						fontsetname = "default";
-					}
-					std::unique_ptr<LuaTable> font_set_table = fonts_table->get_table(fontsetname);
-					font_set_table->do_not_warn_about_unaccessed_keys();
-
-					set_fonts(*font_set_table, sans_);
-					direction_string = get_string_with_default(*font_set_table, "direction", "ltr");
-					if (font_set_table->has_key("size_offset")) {
-						size_offset_ = font_set_table->get_int("size_offset");
-					}
-				} catch (LuaError& err) {
-					log("Could not read font set '%s': %s\n", fontsetname.c_str(), err.what());
-				}
-			} catch (const LuaError& err) {
-				log("Error loading locale '%s' from file: %s\n", actual_localename.c_str(), err.what());
-			}
-		}
-	} catch (const LuaError& err) {
-		log("Could not read locales information from file: %s\n", err.what());
+	// Read the actual fontset.
+	try {
+		if (!fonts_table->has_key(fontset_name)) {
+			throw LuaError("Font set does not exist");
+		}
+		name_ = fontset_name;
+		std::unique_ptr<LuaTable> font_set_table = fonts_table->get_table(fontset_name);
+		representative_character_ = font_set_table->get_string("representative_character");
+
+		set_fonts(*font_set_table, serif_);
+		direction_string = get_string_with_default(*font_set_table, "direction", "ltr");
+		if (font_set_table->has_key("size_offset")) {
+			size_offset_ = font_set_table->get_int("size_offset");
+		}
+	} catch (LuaError& err) {
+		log("Could not read font set '%s': %s\n", fontset_name.c_str(), err.what());
 	}
 
 	is_rtl_ = false;
@@ -169,4 +132,88 @@
 	*bold_italic = get_string_with_default(table, (boost::format("%s_bold_italic") % key).str(), *bold);
 }
 
+FontSets::FontSets() {
+	std::map<std::string, FontSets::Selector> fontset_selectors = {
+		{"default", FontSets::Selector::kDefault},
+		{"arabic", FontSets::Selector::kArabic},
+		{"cjk", FontSets::Selector::kCJK},
+		{"devanagari", FontSets::Selector::kDevanagari},
+		{"hebrew", FontSets::Selector::kHebrew},
+		{"myanmar", FontSets::Selector::kMyanmar},
+		{"sinhala", FontSets::Selector::kSinhala},
+	};
+
+	LuaInterface lua;
+
+	// Read all fontsets
+	std::unique_ptr<LuaTable> fontsets_table(lua.run_script("i18n/fonts.lua"));
+	for (const std::string& fontset_name : fontsets_table->keys<std::string>()) {
+		assert(fontset_selectors.count(fontset_name) == 1);
+		FontSets::Selector selector = fontset_selectors.at(fontset_name);
+		fontsets[selector] = std::unique_ptr<FontSet>(new FontSet(fontset_name));
+	}
+	fontsets_table->do_not_warn_about_unaccessed_keys(); // We are only reading partial information as needed
+
+	// Now assign a fontset to each locale
+	FilenameSet files = g_fs->list_directory("locale");
+	std::string localename;
+
+	try {  // Begin read locales table
+		std::unique_ptr<LuaTable> all_locales(lua.run_script("i18n/locales.lua"));
+		all_locales->do_not_warn_about_unaccessed_keys(); // We are only reading partial information as needed
+
+		for (const std::string& filename : files) {  // Begin scan locales directory
+			char const* const path = filename.c_str();
+			if (!strcmp(FileSystem::fs_filename(path), ".") ||
+				 !strcmp(FileSystem::fs_filename(path), "..") || !g_fs->is_directory(path)) {
+				continue;
+			}
+
+			try {  // Begin read locale from table
+				localename = g_fs->filename_without_ext(path);
+				std::unique_ptr<LuaTable> locale_table = all_locales->get_table(localename);
+				locale_table->do_not_warn_about_unaccessed_keys(); // We are only reading the fontset names
+				const std::string fontsetname = locale_table->get_string("font");
+				FontSets::Selector selector = FontSets::Selector::kDefault;
+				if (fontset_selectors.count(fontsetname) == 1) {
+					selector = fontset_selectors.at(fontsetname);
+				} else {
+					log("No selector for fontset: %s in locale: %s. Falling back to default\n",
+						 fontsetname.c_str(), localename.c_str());
+				}
+				locale_fontsets.insert(std::make_pair(localename, selector));
+			} catch (const WException&) {
+				log("Could not read locale fontset for: %s\n", localename.c_str());
+				locale_fontsets.insert(std::make_pair(localename, FontSets::Selector::kDefault));
+			}  // End read locale from table
+		}  // End scan locales directory
+	} catch (const LuaError& err) {
+		log("Could not read locales fontset information from file: %s\n", err.what());
+		return;  // Nothing more can be done now.
+	}  // End read locales table
+
+	// Check if all selectors have a fontset
+	for (int i = static_cast<int>(FontSets::Selector::kDefault);
+		  i < static_cast<int>(FontSets::Selector::kUnknown);
+		  ++i) {
+		if (fontsets.count(static_cast<FontSets::Selector>(i)) != 1) {
+			log("No fontset defined for FontSets::Selector enum member #%d\n", i);
+		}
+	}
+}
+
+FontSet const * FontSets::get_fontset(FontSets::Selector selector) const {
+	assert(fontsets.count(selector) == 1);
+	return fontsets.at(selector).get();
+}
+
+FontSet const * FontSets::get_fontset(const std::string& locale) const {
+	FontSets::Selector selector = FontSets::Selector::kDefault;
+	if (locale_fontsets.count(locale) == 1) {
+		selector = locale_fontsets.at(locale);
+	}
+	assert(fontsets.count(selector) == 1);
+	return fontsets.at(selector).get();
+}
+
 }

=== modified file 'src/graphic/text/font_set.h'
--- src/graphic/text/font_set.h	2016-02-07 17:11:30 +0000
+++ src/graphic/text/font_set.h	2016-02-15 11:00:58 +0000
@@ -20,8 +20,11 @@
 #ifndef WL_GRAPHIC_TEXT_FONT_SET_H
 #define WL_GRAPHIC_TEXT_FONT_SET_H
 
+#include <map>
+#include <memory>
 #include <string>
 
+#include "base/macros.h"
 #include "scripting/lua_table.h"
 
 namespace UI {
@@ -37,8 +40,11 @@
 
 	static constexpr const char* kFallbackFont = "DejaVu/DejaVuSans.ttf";
 
-	/// Create the fontset for a locale from configuration file
-	FontSet(const std::string& localename);
+	/// Create a fontset from i18n/fonts.lua
+	FontSet(const std::string& fontset_name);
+
+	// The fontset's name
+	const std::string& name() const;
 
 	/// All functions below return the path of the font file used for the given
 	/// style.
@@ -54,14 +60,16 @@
 	const std::string& condensed_bold() const;
 	const std::string& condensed_italic() const;
 	const std::string& condensed_bold_italic() const;
+	const std::string& representative_character() const;
+	// Some scripts need more vertical space than the default font, e.g. Arabic
 	uint16_t size_offset() const;
+	// Returns true iff the fontset's script is written from right to left.
 	bool is_rtl() const;
 
 private:
-	/// Parses font information for the given 'localename' from Lua files.
-	/// Each locale in data/i18n/locales.lua defines which fontset to use.
-	/// The fontset definitions are in data/i18n/fonts.lua
-	void parse_font_for_locale(const std::string& localename);
+	/// Parses font information for the given fontset name from Lua.
+	/// The fontset definitions are in i18n/fonts.lua
+	void parse_fontset(const std::string& fontset_name);
 
 	/// Reads and sets the fonts from 'table', using 'fallback' as the fallback font file.
 	void set_fonts(const LuaTable& table, const std::string& fallback);
@@ -71,6 +79,7 @@
 							  std::string* basic, std::string* bold,
 							  std::string* italic, std::string* bold_italic);
 
+	std::string name_;
 	std::string serif_;
 	std::string serif_bold_;
 	std::string serif_italic_;
@@ -83,10 +92,41 @@
 	std::string condensed_bold_;
 	std::string condensed_italic_;
 	std::string condensed_bold_italic_;
+	std::string representative_character_;
 	uint16_t    size_offset_;
 	bool is_rtl_;
-};
-
-}
+	DISALLOW_COPY_AND_ASSIGN(FontSet);
+};
+
+/// A repository of all available fontsets
+struct FontSets {
+	enum class Selector {
+		kDefault,
+		kArabic,
+		kCJK,
+		kDevanagari,
+		kHebrew,
+		kMyanmar,
+		kSinhala,
+		kUnknown
+	};
+
+	FontSets();
+
+	/// Get the fontset corresponding to the given selector.
+	const FontSet* get_fontset(UI::FontSets::Selector selector) const;
+	/// Get the fontset used by the given locale ISO code.
+	const FontSet* get_fontset(const std::string& locale) const;
+
+private:
+	// Maps locale ISO codes to fontset selectors.
+	std::map<std::string, UI::FontSets::Selector> locale_fontsets;
+	// Contains all available fontsets, to be accessed by their selector.
+	std::map<UI::FontSets::Selector, std::unique_ptr<UI::FontSet>> fontsets;
+
+	DISALLOW_COPY_AND_ASSIGN(FontSets);
+};
+
+} // namespace UI
 
 #endif  // end of include guard: WL_GRAPHIC_TEXT_FONT_SET_H

=== modified file 'src/graphic/text/rt_render.cc'
--- src/graphic/text/rt_render.cc	2016-02-07 16:31:06 +0000
+++ src/graphic/text/rt_render.cc	2016-02-15 11:00:58 +0000
@@ -29,6 +29,7 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/format.hpp>
 
+#include "base/i18n.h"
 #include "base/log.h"
 #include "base/macros.h"
 #include "base/point.h"
@@ -41,6 +42,7 @@
 #include "graphic/text/font_io.h"
 #include "graphic/text/font_set.h"
 #include "graphic/text/rt_parse.h"
+#include "graphic/text/sdl_ttf_font.h"
 #include "graphic/text/textstream.h"
 #include "graphic/text_layout.h"
 #include "graphic/texture.h"
@@ -59,12 +61,11 @@
 };
 
 struct NodeStyle {
-	const UI::FontSet* const fontset;  // Not owned.
+	UI::FontSet const * fontset;
 	string font_face;
 	uint16_t font_size;
 	RGBColor font_color;
 	int font_style;
-	bool is_rtl;
 
 	uint8_t spacing;
 	UI::Align halign;
@@ -72,6 +73,103 @@
 	string reference;
 };
 
+
+/*
+ * This class makes sure that we only load each font file once.
+ */
+class FontCache {
+public:
+	FontCache() = default;
+	~FontCache();
+
+	IFont& get_font(NodeStyle* style);
+
+private:
+	struct FontDescr {
+		string face;
+		uint16_t size;
+
+		bool operator<(const FontDescr& o) const {
+			return size < o.size || (size == o.size && face < o.face);
+		}
+	};
+	using FontMap = map<FontDescr, IFont*>;
+	using FontMapPair = pair<const FontDescr, std::unique_ptr<IFont>>;
+
+	FontMap m_fontmap;
+
+	DISALLOW_COPY_AND_ASSIGN(FontCache);
+};
+
+FontCache::~FontCache() {
+	for (FontMap::reference& entry : m_fontmap) {
+		delete entry.second;
+	}
+}
+
+IFont& FontCache::get_font(NodeStyle* ns) {
+	if (ns->font_face == "condensed") {
+		ns->font_face = ns->fontset->condensed();
+	} else if (ns->font_face == "serif") {
+		ns->font_face = ns->fontset->serif();
+	} else if (ns->font_face == "sans") {
+		ns->font_face = ns->fontset->sans();
+	}
+	const bool is_bold = ns->font_style & IFont::BOLD;
+	const bool is_italic = ns->font_style & IFont::ITALIC;
+	if (is_bold && is_italic) {
+		if (ns->font_face == ns->fontset->condensed() ||
+			 ns->font_face == ns->fontset->condensed_bold() ||
+			 ns->font_face == ns->fontset->condensed_italic()) {
+			ns->font_face = ns->fontset->condensed_bold_italic();
+		} else if (ns->font_face == ns->fontset->serif() ||
+					  ns->font_face == ns->fontset->serif_bold() ||
+					  ns->font_face == ns->fontset->serif_italic()) {
+			ns->font_face = ns->fontset->serif_bold_italic();
+		} else {
+			ns->font_face = ns->fontset->sans_bold_italic();
+		}
+		ns->font_style &= ~IFont::ITALIC;
+		ns->font_style &= ~IFont::BOLD;
+	} else if (is_bold) {
+		if (ns->font_face == ns->fontset->condensed()) {
+			ns->font_face = ns->fontset->condensed_bold();
+		} else if (ns->font_face == ns->fontset->serif()) {
+			ns->font_face = ns->fontset->serif_bold();
+		} else {
+			ns->font_face = ns->fontset->sans_bold();
+		}
+		ns->font_style &= ~IFont::BOLD;
+	} else if (is_italic) {
+		if (ns->font_face == ns->fontset->condensed()) {
+			ns->font_face = ns->fontset->condensed_italic();
+		} else if (ns->font_face == ns->fontset->serif()) {
+			ns->font_face = ns->fontset->serif_italic();
+		} else {
+			ns->font_face = ns->fontset->sans_italic();
+		}
+		ns->font_style &= ~IFont::ITALIC;
+	}
+
+	uint16_t font_size = ns->font_size + ns->fontset->size_offset();
+
+	FontDescr fd = {ns->font_face, font_size};
+	FontMap::iterator i = m_fontmap.find(fd);
+	if (i != m_fontmap.end())
+		return *i->second;
+
+	std::unique_ptr<IFont> font;
+	try {
+		font.reset(load_font(ns->font_face, font_size));
+	} catch (FileNotFoundError& e) {
+		log("Font file not found. Falling back to sans: %s\n%s\n", ns->font_face.c_str(), e.what());
+		font.reset(load_font(ns->fontset->sans(), font_size));
+	}
+	assert(font != nullptr);
+
+	return *m_fontmap.insert(std::make_pair(fd, font.release())).first->second;
+}
+
 struct Reference {
 	Rect dim;
 	string ref;
@@ -121,16 +219,16 @@
 	void set_halign(UI::Align ghalign) {m_halign = ghalign;}
 	UI::Align valign() {return m_valign;}
 	void set_valign(UI::Align gvalign) {m_valign = gvalign;}
-	void set_x(uint16_t nx) {m_x = nx;}
-	void set_y(uint16_t ny) {m_y = ny;}
-	uint16_t x() {return m_x;}
-	uint16_t y() {return m_y;}
+	void set_x(int32_t nx) {m_x = nx;}
+	void set_y(int32_t ny) {m_y = ny;}
+	int32_t x() {return m_x;}
+	int32_t y() {return m_y;}
 
 private:
 	Floating m_floating;
 	UI::Align m_halign;
 	UI::Align m_valign;
-	uint16_t m_x, m_y;
+	int32_t m_x, m_y;
 };
 
 class Layout {
@@ -268,11 +366,16 @@
 		// Go over again and adjust position for VALIGN
 		for (RenderNode* n : nodes_in_line) {
 			uint16_t space = line_height - n->height();
-			if (!space || n->valign() == UI::Align::kBottom)
+			if (!space || n->valign() == UI::Align::kBottom) {
 				continue;
-			if (n->valign() == UI::Align::kCenter)
+			}
+			if (n->valign() == UI::Align::kCenter) {
 				space /= 2;
-			n->set_y(n->y() - space);
+			}
+			// Space can become negative, for example when we have mixed fontsets on the same line
+			// (e.g. "default" and "arabic"), due to differing font heights and hotspots.
+			// So, we fix the sign.
+			n->set_y(std::abs(n->y() - space));
 		}
 		rv.insert(rv.end(), nodes_in_line.begin(), nodes_in_line.end());
 
@@ -317,7 +420,7 @@
  */
 class TextNode : public RenderNode {
 public:
-	TextNode(IFont& font, NodeStyle&, const string& txt);
+	TextNode(FontCache& font, NodeStyle&, const string& txt);
 	virtual ~TextNode() {}
 
 	uint16_t width() override {return m_w;}
@@ -338,20 +441,22 @@
 	uint16_t m_w, m_h;
 	const string m_txt;
 	NodeStyle m_s;
-	IFont& m_font;
+	FontCache& m_fontcache;
+	SdlTtfFont& font_;
 };
 
-TextNode::TextNode(IFont& font, NodeStyle& ns, const string& txt)
-	: RenderNode(ns), m_txt(txt), m_s(ns), m_font(font)
+TextNode::TextNode(FontCache& font, NodeStyle& ns, const string& txt)
+	: RenderNode(ns), m_txt(txt), m_s(ns), m_fontcache(font),
+	font_(dynamic_cast<SdlTtfFont&>(m_fontcache.get_font(&m_s)))
 {
-	m_font.dimensions(m_txt, ns.font_style, &m_w, &m_h);
+	font_.dimensions(m_txt, ns.font_style, &m_w, &m_h);
 }
 uint16_t TextNode::hotspot_y() {
-	return m_font.ascent(m_s.font_style);
+	return font_.ascent(m_s.font_style);
 }
 
 Texture* TextNode::render(TextureCache* texture_cache) {
-	const Texture& img = m_font.render(m_txt, m_s.font_color, m_s.font_style, texture_cache);
+	const Texture& img = font_.render(m_txt, m_s.font_color, m_s.font_style, texture_cache);
 	Texture* rv = new Texture(img.width(), img.height());
 	rv->blit(Rect(0, 0, img.width(), img.height()),
 	         img,
@@ -367,7 +472,7 @@
  */
 class FillingTextNode : public TextNode {
 public:
-	FillingTextNode(IFont& font, NodeStyle& ns, uint16_t w, const string& txt, bool expanding = false) :
+	FillingTextNode(FontCache& font, NodeStyle& ns, uint16_t w, const string& txt, bool expanding = false) :
 		TextNode(font, ns, txt), m_expanding(expanding) {
 			m_w = w;
 		}
@@ -381,7 +486,7 @@
 	bool m_expanding;
 };
 Texture* FillingTextNode::render(TextureCache* texture_cache) {
-	const Texture& t = m_font.render(m_txt, m_s.font_color, m_s.font_style, texture_cache);
+	const Texture& t = font_.render(m_txt, m_s.font_color, m_s.font_style, texture_cache);
 	Texture* rv = new Texture(m_w, m_h);
 	for (uint16_t curx = 0; curx < m_w; curx += t.width()) {
 		Rect srcrect(Point(0, 0), min<int>(t.width(), m_w - curx), m_h);
@@ -396,7 +501,7 @@
  */
 class WordSpacerNode : public TextNode {
 public:
-	WordSpacerNode(IFont& font, NodeStyle& ns) : TextNode(font, ns, " ") {}
+	WordSpacerNode(FontCache& font, NodeStyle& ns) : TextNode(font, ns, " ") {}
 	static void show_spaces(bool t) {m_show_spaces = t;}
 
 	Texture* render(TextureCache* texture_cache) override {
@@ -491,6 +596,7 @@
 	uint16_t width() override {return m_w + m_margin.left + m_margin.right;}
 	uint16_t height() override {return m_h + m_margin.top + m_margin.bottom;}
 	uint16_t hotspot_y() override {return height();}
+
 	Texture* render(TextureCache* texture_cache) override {
 		Texture* rv = new Texture(width(), height());
 		rv->fill_rect(Rect(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0));
@@ -588,112 +694,17 @@
 }
 // End: Helper Stuff
 
-/*
- * This class makes sure that we only load each font file once.
- */
-class FontCache {
-public:
-	FontCache() = default;
-	~FontCache();
-
-	IFont& get_font(NodeStyle* style);
-
-private:
-	struct FontDescr {
-		string face;
-		uint16_t size;
-
-		bool operator<(const FontDescr& o) const {
-			return size < o.size || (size == o.size && face < o.face);
-		}
-	};
-	using FontMap = map<FontDescr, IFont*>;
-	using FontMapPair = pair<const FontDescr, std::unique_ptr<IFont>>;
-
-	FontMap m_fontmap;
-
-	DISALLOW_COPY_AND_ASSIGN(FontCache);
-};
-
-FontCache::~FontCache() {
-	for (FontMap::reference& entry : m_fontmap) {
-		delete entry.second;
-	}
-}
-
-IFont& FontCache::get_font(NodeStyle* ns) {
-	if (ns->font_face == "condensed") {
-		ns->font_face = ns->fontset->condensed();
-	} else if (ns->font_face == "serif") {
-		ns->font_face = ns->fontset->serif();
-	} else if (ns->font_face == "sans") {
-		ns->font_face = ns->fontset->sans();
-	}
-	const bool is_bold = ns->font_style & IFont::BOLD;
-	const bool is_italic = ns->font_style & IFont::ITALIC;
-	if (is_bold && is_italic) {
-		if (ns->font_face == ns->fontset->condensed() ||
-		    ns->font_face == ns->fontset->condensed_bold() ||
-		    ns->font_face == ns->fontset->condensed_italic()) {
-			ns->font_face = ns->fontset->condensed_bold_italic();
-		} else if (ns->font_face == ns->fontset->serif() ||
-		           ns->font_face == ns->fontset->serif_bold() ||
-		           ns->font_face == ns->fontset->serif_italic()) {
-			ns->font_face = ns->fontset->serif_bold_italic();
-		} else {
-			ns->font_face = ns->fontset->sans_bold_italic();
-		}
-		ns->font_style &= ~IFont::ITALIC;
-		ns->font_style &= ~IFont::BOLD;
-	} else if (is_bold) {
-		if (ns->font_face == ns->fontset->condensed()) {
-			ns->font_face = ns->fontset->condensed_bold();
-		} else if (ns->font_face == ns->fontset->serif()) {
-			ns->font_face = ns->fontset->serif_bold();
-		} else {
-			ns->font_face = ns->fontset->sans_bold();
-		}
-		ns->font_style &= ~IFont::BOLD;
-	} else if (is_italic) {
-		if (ns->font_face == ns->fontset->condensed()) {
-			ns->font_face = ns->fontset->condensed_italic();
-		} else if (ns->font_face == ns->fontset->serif()) {
-			ns->font_face = ns->fontset->serif_italic();
-		} else {
-			ns->font_face = ns->fontset->sans_italic();
-		}
-		ns->font_style &= ~IFont::ITALIC;
-	}
-
-	uint16_t font_size = ns->font_size + ns->fontset->size_offset();
-
-	FontDescr fd = {ns->font_face, font_size};
-	FontMap::iterator i = m_fontmap.find(fd);
-	if (i != m_fontmap.end())
-		return *i->second;
-
-	std::unique_ptr<IFont> font;
-	try {
-		font.reset(load_font(ns->font_face, font_size));
-	} catch (FileNotFoundError& e) {
-		log("Font file not found. Falling back to sans: %s\n%s\n", ns->font_face.c_str(), e.what());
-		font.reset(load_font(ns->fontset->sans(), font_size));
-	}
-	assert(font != nullptr);
-
-	return *m_fontmap.insert(std::make_pair(fd, font.release())).first->second;
-}
 
 class TagHandler;
 TagHandler* create_taghandler(Tag& tag, FontCache& fc, NodeStyle& ns, ImageCache* image_cache,
-										RendererStyle& renderer_style);
+										RendererStyle& renderer_style, const UI::FontSets& fontsets);
 
 class TagHandler {
 public:
 	TagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-				  RendererStyle& renderer_style_) :
+				  RendererStyle& renderer_style_, const UI::FontSets& fontsets) :
 		m_tag(tag), font_cache_(fc), m_ns(ns), image_cache_(image_cache),
-		renderer_style(renderer_style_) {}
+		renderer_style(renderer_style_), fontsets_(fontsets) {}
 	virtual ~TagHandler() {}
 
 	virtual void enter() {}
@@ -708,6 +719,7 @@
 	NodeStyle m_ns;
 	ImageCache* image_cache_;  // Not owned
 	RendererStyle& renderer_style; // Reference to global renderer style in the renderer
+	const UI::FontSets& fontsets_;
 };
 
 void TagHandler::m_make_text_nodes(const string& txt, vector<RenderNode*>& nodes, NodeStyle& ns) {
@@ -729,10 +741,11 @@
 
 			// We only know if the spacer goes to the left or right after having a look at the current word.
 			for (uint16_t ws_indx = 0; ws_indx < ts.pos() - cpos; ws_indx++) {
-				spacer_nodes.push_back(new WordSpacerNode(font_cache_.get_font(&ns), ns));
+				spacer_nodes.push_back(new WordSpacerNode(font_cache_, ns));
 			}
 
 			word = ts.till_any_or_end(" \t\n\r");
+			ns.fontset = i18n::find_fontset(word.c_str(), fontsets_);
 			if (!word.empty()) {
 				replace_entities(&word);
 				bool word_is_bidi = i18n::has_rtl_character(word.c_str());
@@ -745,7 +758,7 @@
 						word = i18n::line2bidi(word.c_str());
 					}
 					it = text_nodes.insert(text_nodes.begin(),
-												  new TextNode(font_cache_.get_font(&ns), ns, word.c_str()));
+												  new TextNode(font_cache_, ns, word.c_str()));
 				} else { // Sequences of Latin words go to the right from current position
 					if (it < text_nodes.end()) {
 						++it;
@@ -756,7 +769,7 @@
 							++it;
 						}
 					}
-					it = text_nodes.insert(it, new TextNode(font_cache_.get_font(&ns), ns, word));
+					it = text_nodes.insert(it, new TextNode(font_cache_, ns, word));
 				}
 			}
 			previous_word = word;
@@ -771,19 +784,20 @@
 			std::size_t cpos = ts.pos();
 			ts.skip_ws();
 			for (uint16_t ws_indx = 0; ws_indx < ts.pos() - cpos; ws_indx++) {
-				nodes.push_back(new WordSpacerNode(font_cache_.get_font(&ns), ns));
+				nodes.push_back(new WordSpacerNode(font_cache_, ns));
 			}
 			word = ts.till_any_or_end(" \t\n\r");
+			ns.fontset = i18n::find_fontset(word.c_str(), fontsets_);
 			if (!word.empty()) {
 				replace_entities(&word);
 				word = i18n::make_ligatures(word.c_str());
-				if (i18n::has_cjk_character(word.c_str())) {
+				if (i18n::has_script_character(word.c_str(), UI::FontSets::Selector::kCJK)) {
 					std::vector<std::string> units = i18n::split_cjk_word(word.c_str());
 					for (const std::string& unit: units) {
-						nodes.push_back(new TextNode(font_cache_.get_font(&ns), ns, unit));
+						nodes.push_back(new TextNode(font_cache_, ns, unit));
 					}
 				} else {
-					nodes.push_back(new TextNode(font_cache_.get_font(&ns), ns, word));
+					nodes.push_back(new TextNode(font_cache_, ns, word));
 				}
 			}
 		}
@@ -794,7 +808,7 @@
 	for (Child* c : m_tag.childs()) {
 		if (c->tag) {
 			std::unique_ptr<TagHandler> th(create_taghandler(*c->tag, font_cache_, m_ns, image_cache_,
-																			 renderer_style));
+																			 renderer_style, fontsets_));
 			th->enter();
 			th->emit(nodes);
 		} else
@@ -805,8 +819,8 @@
 class FontTagHandler : public TagHandler {
 public:
 	FontTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-						RendererStyle& init_renderer_style)
-		: TagHandler(tag, fc, ns, image_cache, init_renderer_style) {}
+						RendererStyle& init_renderer_style, const UI::FontSets& fontsets)
+		: TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets) {}
 
 	void enter() override {
 		const AttrMap& a = m_tag.attrs();
@@ -824,8 +838,8 @@
 class PTagHandler : public TagHandler {
 public:
 	PTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-					RendererStyle& init_renderer_style)
-		: TagHandler(tag, fc, ns, image_cache, init_renderer_style), m_indent(0) {
+					RendererStyle& init_renderer_style, const UI::FontSets& fontsets)
+		: TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets), m_indent(0) {
 	}
 
 	void enter() override {
@@ -873,8 +887,8 @@
 class ImgTagHandler : public TagHandler {
 public:
 	ImgTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-					  RendererStyle& init_renderer_style) :
-		TagHandler(tag, fc, ns, image_cache, init_renderer_style), m_rn(nullptr) {
+					  RendererStyle& init_renderer_style, const UI::FontSets& fontsets) :
+		TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets), m_rn(nullptr) {
 	}
 
 	void enter() override {
@@ -892,8 +906,8 @@
 class VspaceTagHandler : public TagHandler {
 public:
 	VspaceTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-						  RendererStyle& init_renderer_style) :
-		TagHandler(tag, fc, ns, image_cache, init_renderer_style), m_space(0) {}
+						  RendererStyle& init_renderer_style, const UI::FontSets& fontsets) :
+		TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets), m_space(0) {}
 
 	void enter() override {
 		const AttrMap& a = m_tag.attrs();
@@ -912,8 +926,8 @@
 class HspaceTagHandler : public TagHandler {
 public:
 	HspaceTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-						  RendererStyle& init_renderer_style) :
-		TagHandler(tag, fc, ns, image_cache, init_renderer_style), m_bg(nullptr), m_space(0) {}
+						  RendererStyle& init_renderer_style, const UI::FontSets& fontsets) :
+		TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets), m_bg(nullptr), m_space(0) {}
 
 	void enter() override {
 		const AttrMap& a = m_tag.attrs();
@@ -938,9 +952,9 @@
 		RenderNode* rn = nullptr;
 		if (!m_fill_text.empty()) {
 			if (m_space < INFINITE_WIDTH)
-				rn = new FillingTextNode(font_cache_.get_font(&m_ns), m_ns, m_space, m_fill_text);
+				rn = new FillingTextNode(font_cache_, m_ns, m_space, m_fill_text);
 			else
-				rn = new FillingTextNode(font_cache_.get_font(&m_ns), m_ns, 0, m_fill_text, true);
+				rn = new FillingTextNode(font_cache_, m_ns, 0, m_fill_text, true);
 		} else {
 			SpaceNode* sn;
 			if (m_space < INFINITE_WIDTH)
@@ -964,8 +978,8 @@
 class BrTagHandler : public TagHandler {
 public:
 	BrTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-					 RendererStyle& init_renderer_style) :
-		TagHandler(tag, fc, ns, image_cache, init_renderer_style) {
+					 RendererStyle& init_renderer_style, const UI::FontSets& fontsets) :
+		TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets) {
 	}
 
 	void emit(vector<RenderNode*>& nodes) override {
@@ -978,10 +992,10 @@
 public:
 	SubTagHandler
 		(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-		 RendererStyle& init_renderer_style,
+		 RendererStyle& init_renderer_style, const UI::FontSets& fontsets,
 		 uint16_t max_w = 0, bool shrink_to_fit = true)
 		:
-			TagHandler(tag, fc, ns, image_cache, init_renderer_style),
+			TagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets),
 			shrink_to_fit_(shrink_to_fit),
 			m_w(max_w),
 			m_rn(new SubTagRenderNode(ns))
@@ -1104,8 +1118,8 @@
 class RTTagHandler : public SubTagHandler {
 public:
 	RTTagHandler(Tag& tag, FontCache& fc, NodeStyle ns, ImageCache* image_cache,
-					 RendererStyle& init_renderer_style, uint16_t w) :
-		SubTagHandler(tag, fc, ns, image_cache, init_renderer_style, w, true) {
+					 RendererStyle& init_renderer_style, const UI::FontSets& fontsets, uint16_t w) :
+		SubTagHandler(tag, fc, ns, image_cache, init_renderer_style, fontsets, w, true) {
 	}
 
 	// Handle attributes that are in rt, but not in sub.
@@ -1118,16 +1132,16 @@
 
 template<typename T> TagHandler* create_taghandler
 	(Tag& tag, FontCache& fc, NodeStyle& ns, ImageCache* image_cache,
-	 RendererStyle& renderer_style)
+	 RendererStyle& renderer_style, const UI::FontSets& fontsets)
 {
-	return new T(tag, fc, ns, image_cache, renderer_style);
+	return new T(tag, fc, ns, image_cache, renderer_style, fontsets);
 }
 using TagHandlerMap = map<const string, TagHandler* (*)
 	(Tag& tag, FontCache& fc, NodeStyle& ns, ImageCache* image_cache,
-	 RendererStyle& renderer_style)>;
+	 RendererStyle& renderer_style, const UI::FontSets& fontsets)>;
 
 TagHandler* create_taghandler(Tag& tag, FontCache& fc, NodeStyle& ns, ImageCache* image_cache,
-										RendererStyle& renderer_style) {
+										RendererStyle& renderer_style, const UI::FontSets& fontsets) {
 	static TagHandlerMap map;
 	if (map.empty()) {
 		map["br"] = &create_taghandler<BrTagHandler>;
@@ -1143,13 +1157,13 @@
 		throw RenderError
 			((boost::format("No Tag handler for %s. This is a bug, please submit a report.")
 			  % tag.name()).str());
-	return i->second(tag, fc, ns, image_cache, renderer_style);
+	return i->second(tag, fc, ns, image_cache, renderer_style, fontsets);
 }
 
-Renderer::Renderer(ImageCache* image_cache, TextureCache* texture_cache, UI::FontSet* fontset) :
+Renderer::Renderer(ImageCache* image_cache, TextureCache* texture_cache, const UI::FontSets& fontsets) :
 	font_cache_(new FontCache()), parser_(new Parser()),
-	image_cache_(image_cache), texture_cache_(texture_cache), fontset_(fontset),
-	renderer_style_(fontset->sans(), 16, INFINITE_WIDTH, INFINITE_WIDTH) {
+	image_cache_(image_cache), texture_cache_(texture_cache), fontsets_(fontsets),
+	renderer_style_("sans", 16, INFINITE_WIDTH, INFINITE_WIDTH) {
 	TextureCache* render
 		(const std::string&, uint16_t, const TagSet&);
 }
@@ -1167,15 +1181,16 @@
 	renderer_style_.remaining_width = width;
 	renderer_style_.overall_width = width;
 
+	UI::FontSet const * fontset = fontsets_.get_fontset(i18n::get_locale());
+
 	NodeStyle default_style = {
-		fontset_,
-		renderer_style_.font_face, renderer_style_.font_size,
-		RGBColor(255, 255, 0), IFont::DEFAULT, fontset_->is_rtl(), 0,
-		UI::Align::kLeft, UI::Align::kTop,
+		fontset, renderer_style_.font_face, renderer_style_.font_size,
+		RGBColor(255, 255, 0), IFont::DEFAULT,
+		0, UI::Align::kLeft, UI::Align::kTop,
 		""
 	};
 
-	RTTagHandler rtrn(*rt, *font_cache_, default_style, image_cache_, renderer_style_, width);
+	RTTagHandler rtrn(*rt, *font_cache_, default_style, image_cache_, renderer_style_, fontsets_, width);
 	vector<RenderNode*> nodes;
 	rtrn.enter();
 	rtrn.emit(nodes);

=== modified file 'src/graphic/text/rt_render.h'
--- src/graphic/text/rt_render.h	2016-02-07 15:47:25 +0000
+++ src/graphic/text/rt_render.h	2016-02-15 11:00:58 +0000
@@ -52,30 +52,6 @@
 	uint16_t overall_width;
 };
 
-/**
- * Wrapper object around a font.
- *
- * Fonts in our sense are defined by the general font shape (given by the font
- * name) and the size of the font. Note that Bold and Italic are special in the
- * regard that we expect that this is already handled by the Font File, so, the
- * font loader directly loads DejaVuSans-Bold.ttf for example.
- */
-class IFont {
-public:
-	enum {
-		DEFAULT = 0,
-		BOLD = 1,
-		ITALIC = 2,
-		UNDERLINE = 4,
-		SHADOW = 8,
-	};
-	virtual ~IFont() {}
-
-	virtual void dimensions(const std::string&, int, uint16_t *, uint16_t *) = 0;
-	virtual const Texture& render(const std::string&, const RGBColor& clr, int, TextureCache*) = 0;
-
-	virtual uint16_t ascent(int) const = 0;
-};
 
 /**
  * A map that maps pixels to a string. The string are the references which can be used
@@ -95,7 +71,7 @@
 class Renderer {
 public:
 	// Ownership is not taken.
-	Renderer(ImageCache* image_cache, TextureCache* texture_cache, UI::FontSet* fontset);
+	Renderer(ImageCache* image_cache, TextureCache* texture_cache, const UI::FontSets& fontsets);
 	~Renderer();
 
 	// Render the given string in the given width. Restricts the allowed tags to
@@ -115,7 +91,7 @@
 	std::unique_ptr<Parser> parser_;
 	ImageCache* const image_cache_;  // Not owned.
 	TextureCache* const texture_cache_;  // Not owned.
-	UI::FontSet* const fontset_;  // Not owned.
+	const UI::FontSets& fontsets_; // All fontsets
 	RendererStyle renderer_style_; // Properties that all render nodes need to know about
 };
 

=== modified file 'src/graphic/text/sdl_ttf_font.cc'
--- src/graphic/text/sdl_ttf_font.cc	2016-01-24 07:47:01 +0000
+++ src/graphic/text/sdl_ttf_font.cc	2016-02-15 11:00:58 +0000
@@ -27,18 +27,13 @@
 
 #include "graphic/sdl_utils.h"
 #include "graphic/text/rt_errors.h"
-#include "graphic/texture.h"
-#include "graphic/texture_cache.h"
-
-using namespace std;
-using namespace boost;
 
 static const int SHADOW_OFFSET = 1;
 static const SDL_Color SHADOW_CLR = {0, 0, 0, SDL_ALPHA_OPAQUE};
 
 namespace RT {
 
-SdlTtfFont::SdlTtfFont(TTF_Font * font, const string& face, int ptsize, string* ttf_memory_block) :
+SdlTtfFont::SdlTtfFont(TTF_Font * font, const std::string& face, int ptsize, std::string* ttf_memory_block) :
 	font_(font), style_(TTF_STYLE_NORMAL), font_name_(face), ptsize_(ptsize),
 	ttf_file_memory_block_(ttf_memory_block) {
 }
@@ -48,7 +43,7 @@
 	font_ = nullptr;
 }
 
-void SdlTtfFont::dimensions(const string& txt, int style, uint16_t * gw, uint16_t * gh) {
+void SdlTtfFont::dimensions(const std::string& txt, int style, uint16_t * gw, uint16_t * gh) {
 	m_set_style(style);
 
 	int w, h;
@@ -61,8 +56,8 @@
 }
 
 const Texture& SdlTtfFont::render
-	(const string& txt, const RGBColor& clr, int style, TextureCache* texture_cache) {
-	const string hash =
+	(const std::string& txt, const RGBColor& clr, int style, TextureCache* texture_cache) {
+	const std::string hash =
 		(boost::format("%s:%s:%i:%02x%02x%02x:%i") % font_name_ % ptsize_ % txt %
 		 static_cast<int>(clr.r) % static_cast<int>(clr.g) % static_cast<int>(clr.b) % style)
 			.str();
@@ -120,7 +115,7 @@
 		text_surface = TTF_RenderUTF8_Blended(font_, txt.c_str(), sdlclr);
 
 	if (!text_surface)
-		throw RenderError((format("Rendering '%s' gave the error: %s") % txt % TTF_GetError()).str());
+		throw RenderError((boost::format("Rendering '%s' gave the error: %s") % txt % TTF_GetError()).str());
 
 	return *texture_cache->insert(hash, std::unique_ptr<Texture>(new Texture(text_surface)));
 }
@@ -134,8 +129,8 @@
 
 void SdlTtfFont::m_set_style(int style) {
 	// Those must have been handled by loading the correct font.
-	assert(!(style & BOLD));
-	assert(!(style & ITALIC));
+	// NOCOM assert(!(style & BOLD));
+	//assert(!(style & ITALIC));
 
 	int sdl_style = TTF_STYLE_NORMAL;
 	if (style & UNDERLINE) sdl_style |= TTF_STYLE_UNDERLINE;

=== modified file 'src/graphic/text/sdl_ttf_font.h'
--- src/graphic/text/sdl_ttf_font.h	2014-11-24 07:10:03 +0000
+++ src/graphic/text/sdl_ttf_font.h	2016-02-15 11:00:58 +0000
@@ -25,10 +25,36 @@
 
 #include <SDL_ttf.h>
 
-#include "graphic/text/rt_render.h"
+#include "graphic/texture.h"
+#include "graphic/texture_cache.h"
 
 namespace RT {
 
+/**
+ * Wrapper object around a font.
+ *
+ * Fonts in our sense are defined by the general font shape (given by the font
+ * name) and the size of the font. Note that Bold and Italic are special in the
+ * regard that we expect that this is already handled by the Font File, so, the
+ * font loader directly loads DejaVuSans-Bold.ttf for example.
+ */
+class IFont {
+public:
+	enum {
+		DEFAULT = 0,
+		BOLD = 1,
+		ITALIC = 2,
+		UNDERLINE = 4,
+		SHADOW = 8,
+	};
+	virtual ~IFont() {}
+
+	virtual void dimensions(const std::string&, int, uint16_t *, uint16_t *) = 0;
+	virtual const Texture& render(const std::string&, const RGBColor& clr, int, TextureCache*) = 0;
+
+	virtual uint16_t ascent(int) const = 0;
+};
+
 // Implementation of a Font object using SDL_ttf.
 class SdlTtfFont : public IFont {
 public:

=== modified file 'src/graphic/text/test/CMakeLists.txt'
--- src/graphic/text/test/CMakeLists.txt	2014-12-08 05:22:52 +0000
+++ src/graphic/text/test/CMakeLists.txt	2016-02-15 11:00:58 +0000
@@ -21,6 +21,7 @@
     render.cc
     render.h
   DEPENDS
+    base_i18n
     graphic_image_cache
     graphic_surface
     graphic_text

=== modified file 'src/graphic/text/test/render.cc'
--- src/graphic/text/test/render.cc	2016-01-19 08:55:16 +0000
+++ src/graphic/text/test/render.cc	2016-02-15 11:00:58 +0000
@@ -24,6 +24,7 @@
 #include <memory>
 #include <string>
 
+#include "base/i18n.h"
 #include "graphic/image_cache.h"
 #include "graphic/text/rt_render.h"
 #include "graphic/text/test/paths.h"
@@ -31,13 +32,15 @@
 #include "io/filesystem/layered_filesystem.h"
 
 StandaloneRenderer::StandaloneRenderer() {
+	i18n::set_locale("en");
 	g_fs = new LayeredFileSystem();
 	g_fs->add_file_system(&FileSystem::create(WIDELANDS_DATA_DIR));
 	g_fs->add_file_system(&FileSystem::create(RICHTEXT_DATA_DIR));
+	UI::FontSets fontsets;
 
 	texture_cache_.reset(new TextureCache(500 << 20));  // 500 MB
 	image_cache_.reset(new ImageCache());
-	renderer_.reset(new RT::Renderer(image_cache_.get(), texture_cache_.get(), new UI::FontSet("en")));
+	renderer_.reset(new RT::Renderer(image_cache_.get(), texture_cache_.get(), fontsets));
 }
 
 StandaloneRenderer::~StandaloneRenderer() {

=== modified file 'src/graphic/text_layout.cc'
--- src/graphic/text_layout.cc	2016-02-13 11:04:05 +0000
+++ src/graphic/text_layout.cc	2016-02-15 11:00:58 +0000
@@ -39,12 +39,12 @@
 }
 
 uint32_t text_width(const std::string& text, int ptsize) {
-	return UI::g_fh1->render(as_editorfont(text, ptsize - UI::g_fh1->fontset().size_offset()))->width();
+	return UI::g_fh1->render(as_editorfont(text, ptsize - UI::g_fh1->fontset()->size_offset()))->width();
 }
 
 uint32_t text_height(const std::string& text, int ptsize) {
 	return UI::g_fh1->render(as_editorfont(text.empty() ? "." : text,
-														ptsize - UI::g_fh1->fontset().size_offset()))->height();
+														ptsize - UI::g_fh1->fontset()->size_offset()))->height();
 }
 
 std::string richtext_escape(const std::string& given_text) {
@@ -238,7 +238,7 @@
 */
 
 TextStyle::TextStyle() :
-	font(Font::get(UI::g_fh1->fontset().sans(), UI_FONT_SIZE_SMALL)),
+	font(Font::get(UI::g_fh1->fontset()->sans(), UI_FONT_SIZE_SMALL)),
 	fg(UI_FONT_CLR_FG),
 	bold(true),
 	italics(false),

=== modified file 'src/graphic/text_parser.cc'
--- src/graphic/text_parser.cc	2015-12-17 09:36:59 +0000
+++ src/graphic/text_parser.cc	2016-02-15 11:00:58 +0000
@@ -54,7 +54,7 @@
 	m_font_weight = "normal";
 	m_font_style = "normal";
 	m_font_decoration = "none";
-	m_font_face = (UI::g_fh1->fontset()).sans();
+	m_font_face = (UI::g_fh1->fontset())->sans();
 	m_line_spacing = 0;
 }
 
@@ -269,10 +269,10 @@
 			if (key == "font-size") {
 				element.set_font_size(atoi(val.c_str()));
 			} else if (key == "font-face") {
-				UI::FontSet fontset = UI::g_fh1->fontset();
+				const UI::FontSet& fontset = *UI::g_fh1->fontset();
 				if (val == fontset.condensed() || val == "condensed") {
 					val = fontset.condensed();
-				} else if (val == fontset.sans() || val == "serif") {
+				} else if (val == fontset.serif() || val == "serif") {
 					val = fontset.serif();
 				} else {
 					val = fontset.sans();

=== modified file 'src/graphic/wordwrap.cc'
--- src/graphic/wordwrap.cc	2016-01-31 10:57:58 +0000
+++ src/graphic/wordwrap.cc	2016-02-15 11:00:58 +0000
@@ -352,7 +352,7 @@
 
 		const Image* entry_text_im =
 				UI::g_fh1->render(as_editorfont(m_lines[line].text,
-														  m_style.font->size() - UI::g_fh1->fontset().size_offset(),
+														  m_style.font->size() - UI::g_fh1->fontset()->size_offset(),
 														  m_style.fg));
 		UI::correct_for_align(alignment, entry_text_im->width(), fontheight, &point);
 		dst.blit(point, entry_text_im);

=== modified file 'src/ui_basic/button.cc'
--- src/ui_basic/button.cc	2016-02-09 18:24:03 +0000
+++ src/ui_basic/button.cc	2016-02-15 11:00:58 +0000
@@ -57,7 +57,8 @@
 {
 	// Automatically resize for font height and give it a margin.
 	if (h < 1) {
-		int new_height = UI::g_fh1->render(as_uifont("."))->height() + 4;
+		int new_height =
+				UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 4;
 		set_desired_size(w, new_height);
 		set_size(w, new_height);
 	}

=== modified file 'src/ui_basic/checkbox.cc'
--- src/ui_basic/checkbox.cc	2016-02-04 19:46:40 +0000
+++ src/ui_basic/checkbox.cc	2016-02-15 11:00:58 +0000
@@ -143,7 +143,7 @@
 		Point text_anchor(kStateboxSize + kPadding, 0);
 
 		if (rendered_text_) {
-			if (UI::g_fh1->fontset().is_rtl()) {
+			if (UI::g_fh1->fontset()->is_rtl()) {
 				text_anchor.x = 0;
 				image_anchor.x = rendered_text_->width() + kPadding;
 				image_anchor.y = (get_h() - kStateboxSize) / 2;

=== modified file 'src/ui_basic/editbox.cc'
--- src/ui_basic/editbox.cc	2016-02-03 18:09:15 +0000
+++ src/ui_basic/editbox.cc	2016-02-15 11:00:58 +0000
@@ -76,7 +76,7 @@
 	 const Image* background,
 	 int font_size)
 	:
-	Panel(parent, x, y, w, UI::g_fh1->render(as_uifont("."), font_size)->height() + 2),
+	Panel(parent, x, y, w, UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()), font_size)->height() + 2),
 	m_(new EditBoxImpl),
 	history_active_(false),
 	history_position_(-1)
@@ -84,11 +84,11 @@
 	set_thinks(false);
 
 	m_->background = background;
-	m_->fontname = UI::g_fh1->fontset().serif();
+	m_->fontname = UI::g_fh1->fontset()->sans();
 	m_->fontsize = font_size;
 
 	// Set alignment to the UI language's principal writing direction
-	m_->align = UI::g_fh1->fontset().is_rtl() ? UI::Align::kCenterRight : UI::Align::kCenterLeft;
+	m_->align = UI::g_fh1->fontset()->is_rtl() ? UI::Align::kCenterRight : UI::Align::kCenterLeft;
 	m_->caret = 0;
 	m_->scrolloffset = 0;
 	// yes, use *signed* max as maximum length; just a small safe-guard.
@@ -409,7 +409,7 @@
 	// Crop to max_width while blitting
 	if (max_width < linewidth) {
 		// Fix positioning for BiDi languages.
-		if (UI::g_fh1->fontset().is_rtl()) {
+		if (UI::g_fh1->fontset()->is_rtl()) {
 			point.x = 0;
 		}
 		// We want this always on, e.g. for mixed language savegame filenames

=== modified file 'src/ui_basic/listselect.cc'
--- src/ui_basic/listselect.cc	2016-02-08 00:18:02 +0000
+++ src/ui_basic/listselect.cc	2016-02-15 11:00:58 +0000
@@ -24,16 +24,17 @@
 #include <boost/bind.hpp>
 
 #include "base/log.h"
-#include "graphic/font.h"
-#include "graphic/font_handler.h"
+#include "graphic/align.h"
 #include "graphic/font_handler1.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
-#include "graphic/text/font_set.h"
 #include "graphic/text_constants.h"
 #include "graphic/text_layout.h"
+#include "graphic/text/bidi.h"
 #include "wlapplication.h"
 
+constexpr int kMargin = 2;
+
 namespace UI {
 /**
  * Initialize a list select panel
@@ -43,33 +44,27 @@
  *       y
  *       w       dimensions, in pixels, of the Listselect
  *       h
- *       align   alignment of text inside the Listselect
 */
 BaseListselect::BaseListselect
 	(Panel * const parent,
 	 int32_t const x, int32_t const y, uint32_t const w, uint32_t const h,
-	 Align const align, bool const show_check)
+	 bool const show_check)
 	:
 	Panel(parent, x, y, w, h),
-	lineheight_(g_fh->get_fontheight(UI::g_fh1->fontset().serif(), UI_FONT_SIZE_SMALL)),
+	lineheight_(UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height()
+					 + kMargin),
 	scrollbar_      (this, get_w() - 24, 0, 24, h, false),
 	scrollpos_     (0),
 	selection_     (no_selection_index()),
 	last_click_time_(-10000),
 	last_selection_(no_selection_index()),
-	show_check_(show_check),
-	fontname_(UI::g_fh1->fontset().serif()),
-	fontsize_(UI_FONT_SIZE_SMALL)
+	show_check_(show_check)
 {
 	set_thinks(false);
 
-	//  do not allow vertical alignment as it does not make sense
-	align_ = align & UI::Align::kHorizontal;
-
 	scrollbar_.moved.connect(boost::bind(&BaseListselect::set_scrollpos, this, _1));
-	scrollbar_.set_singlestepsize(g_fh->get_fontheight(fontname_, fontsize_));
-	scrollbar_.set_pagesize
-		(h - 2 * g_fh->get_fontheight(fontname_, fontsize_));
+	scrollbar_.set_singlestepsize(lineheight_);
+	scrollbar_.set_pagesize(h - 2 * lineheight_);
 	scrollbar_.set_steps(1);
 
 	if (show_check) {
@@ -125,8 +120,7 @@
 	 uint32_t             entry,
 	 const Image*   pic,
 	 bool         const   sel,
-	 const std::string  & tooltip_text,
-	 const std::string  & fontname)
+	 const std::string  & tooltip_text)
 {
 	EntryRecord * er = new EntryRecord();
 
@@ -135,8 +129,7 @@
 	er->use_clr = false;
 	er->name    = name;
 	er->tooltip = tooltip_text;
-	er->font_face = fontname;
-	uint32_t entry_height = g_fh->get_fontheight(fontname.empty() ? fontname_ : fontname, fontsize_);
+	uint32_t entry_height = lineheight_;
 	if (pic) {
 		uint16_t w = pic->width();
 		uint16_t h = pic->height();
@@ -160,8 +153,7 @@
 	(const std::string& name,
 	 const Image*   pic,
 	 bool         const   sel,
-	 const std::string  & tooltip_text,
-	 const std::string& fontname)
+	 const std::string  & tooltip_text)
 {
 	EntryRecord * er = new EntryRecord();
 
@@ -174,9 +166,8 @@
 	er->use_clr = false;
 	er->name    = name;
 	er->tooltip = tooltip_text;
-	er->font_face = fontname;
 
-	uint32_t entry_height = g_fh->get_fontheight(fontname.empty() ? fontname_ : fontname, fontsize_);
+	uint32_t entry_height = lineheight_;
 	if (pic) {
 		uint16_t w = pic->width();
 		uint16_t h = pic->height();
@@ -327,7 +318,7 @@
 
 uint32_t BaseListselect::get_lineheight() const
 {
-	return lineheight_ + 2;
+	return lineheight_ + kMargin;
 }
 
 uint32_t BaseListselect::get_eff_w() const
@@ -355,9 +346,12 @@
 
 		const EntryRecord & er = *entry_records_[idx];
 
+		Point point(1, y);
+		uint32_t maxw = get_eff_w() - 2;
+
 		// Highlight the current selected entry
 		if (idx == selection_) {
-			Rect r = Rect(Point(1, y), get_eff_w() - 2, lineheight_);
+			Rect r = Rect(point, maxw, lineheight_);
 			if (r.x < 0) {
 				r.w += r.x; r.x = 0;
 			}
@@ -372,36 +366,48 @@
 			}
 		}
 
-		Align draw_alignment = mirror_alignment(align_);
-
-		int32_t const x =
-			(static_cast<int>(draw_alignment & UI::Align::kRight))   ? get_eff_w() -      1 :
-			(static_cast<int>(draw_alignment & UI::Align::kHCenter)) ? get_eff_w() >>     1 :
-
-			// Pictures are always left aligned, leave some space here
-			max_pic_width_         ? max_pic_width_ + 10 :
-			1;
-
-		std::string font_face = er.font_face.empty() ? fontname_ : er.font_face;
-		RGBColor color = er.use_clr ? er.clr : UI_FONT_CLR_FG;
-
-		// Horizontal center the string
-		UI::g_fh->draw_text
-			(dst,
-			 TextStyle::makebold(Font::get(font_face, fontsize_), color),
-			 Point
-			 	(x,
-			 	 y +
-				 (get_lineheight() - g_fh->get_fontheight(font_face, fontsize_))
-			 	 /
-			 	 2),
-			 er.name,
-			 draw_alignment);
+		uint32_t picw = max_pic_width_ ? max_pic_width_ + 10 : 0;
 
 		// Now draw pictures
 		if (er.pic) {
-			uint16_t h = er.pic->height();
-			dst.blit(Point(1, y + (get_lineheight() - h) / 2), er.pic);
+			dst.blit(Point(UI::g_fh1->fontset()->is_rtl() ? get_eff_w() - er.pic->width() - 1 : 1,
+								y + (get_lineheight() - er.pic->height()) / 2),
+						er.pic);
+		}
+
+		const Image* entry_text_im = UI::g_fh1->render(as_uifont(er.name, UI_FONT_SIZE_SMALL,
+																					er.use_clr ? er.clr : UI_FONT_CLR_FG));
+
+		Align alignment = i18n::has_rtl_character(er.name.c_str(), 20) ? UI::Align::kRight : UI::Align::kLeft;
+		if (static_cast<int>(alignment & UI::Align::kRight)) {
+			point.x += maxw - picw;
+		}
+
+		UI::correct_for_align(alignment, entry_text_im->width(), entry_text_im->height(), &point);
+
+		// Shift for image width
+		if (!UI::g_fh1->fontset()->is_rtl()) {
+			point.x += picw;
+		}
+
+		// Fix vertical position for mixed font heights
+		if (get_lineheight() > static_cast<uint32_t>(entry_text_im->height())) {
+			point.y += (lineheight_ - entry_text_im->height()) / 2;
+		} else {
+			point.y -= (entry_text_im->height() - lineheight_) / 2;
+		}
+
+		// Crop to column width while blitting
+		if (static_cast<int>(alignment & UI::Align::kRight) && (maxw  + picw) < static_cast<uint32_t>(entry_text_im->width())) {
+			// Fix positioning for BiDi languages.
+			point.x = 0;
+
+			// We want this always on, e.g. for mixed language savegame filenames, or the languages list
+				dst.blitrect(point,
+								 entry_text_im,
+								 Rect(entry_text_im->width() - maxw + picw, 0, maxw, entry_text_im->height()));
+		} else {
+			dst.blitrect(point, entry_text_im, Rect(0, 0, maxw, entry_text_im->height()));
 		}
 
 		y += lineheight;

=== modified file 'src/ui_basic/listselect.h'
--- src/ui_basic/listselect.h	2016-02-07 16:31:06 +0000
+++ src/ui_basic/listselect.h	2016-02-15 11:00:58 +0000
@@ -26,7 +26,6 @@
 
 #include <boost/signals2.hpp>
 
-#include "graphic/align.h"
 #include "graphic/color.h"
 #include "ui_basic/panel.h"
 #include "ui_basic/scrollbar.h"
@@ -47,7 +46,6 @@
 		 int32_t y,
 		 uint32_t w,
 		 uint32_t h,
-		 Align align = UI::Align::kLeft,
 		 bool show_check = false);
 	~BaseListselect();
 
@@ -64,23 +62,18 @@
 		 uint32_t value,
 		 const Image* pic = nullptr,
 		 const bool select_this = false,
-		 const std::string & tooltip_text = std::string(),
-		 const std::string& fontname = "");
+		 const std::string & tooltip_text = std::string());
 	void add_front
 		(const std::string& name,
 		 const Image* pic = nullptr,
 		 const bool select_this = false,
-		 const std::string & tooltip_text = std::string(),
-		 const std::string& fontname = "");
+		 const std::string & tooltip_text = std::string());
 	void remove(uint32_t);
 	void remove(const char * name);
 
 	void switch_entries(uint32_t, uint32_t);
 
 	void set_entry_color(uint32_t, RGBColor);
-	void set_fontsize(int32_t const fontsize) {
-		fontsize_ = fontsize;
-	}
 
 	uint32_t size () const {return entry_records_.size ();}
 	bool     empty() const {return entry_records_.empty();}
@@ -136,13 +129,11 @@
 		const Image* pic;
 		std::string name;
 		std::string tooltip;
-		std::string font_face;
 	};
 	using EntryRecordDeque = std::deque<EntryRecord *>;
 
 	uint32_t max_pic_width_;
 	uint32_t lineheight_;
-	Align align_;
 	EntryRecordDeque entry_records_;
 	Scrollbar scrollbar_;
 	uint32_t scrollpos_;         //  in pixels
@@ -151,9 +142,6 @@
 	uint32_t last_selection_;  // for double clicks
 	bool show_check_; //  show a green arrow left of selected element
 	const Image* check_pic_;
-
-	std::string fontname_;
-	uint32_t    fontsize_;
 	std::string current_tooltip_;
 };
 
@@ -163,9 +151,8 @@
 		(Panel * parent,
 		 int32_t x, int32_t y,
 		 uint32_t w, uint32_t h,
-		 Align align = UI::Align::kLeft,
 		 bool show_check = false)
-		: BaseListselect(parent, x, y, w, h, align, show_check)
+		: BaseListselect(parent, x, y, w, h, show_check)
 	{}
 
 	void add
@@ -173,11 +160,10 @@
 		 Entry value,
 		 const Image* pic = nullptr,
 		 const bool select_this = false,
-		 const std::string & tooltip_text = std::string(),
-		 const std::string & font_face = "")
+		 const std::string & tooltip_text = std::string())
 	{
 		entry_cache_.push_back(value);
-		BaseListselect::add(name, entry_cache_.size() - 1, pic, select_this, tooltip_text, font_face);
+		BaseListselect::add(name, entry_cache_.size() - 1, pic, select_this, tooltip_text);
 	}
 	void add_front
 		(const std::string& name,
@@ -219,9 +205,8 @@
 		(Panel * parent,
 		 int32_t x, int32_t y,
 		 uint32_t w, uint32_t h,
-		 Align align = UI::Align::kLeft,
 		 bool show_check = false)
-		: Base(parent, x, y, w, h, align, show_check)
+		: Base(parent, x, y, w, h, show_check)
 	{}
 
 	void add

=== modified file 'src/ui_basic/messagebox.cc'
--- src/ui_basic/messagebox.cc	2016-02-05 08:56:04 +0000
+++ src/ui_basic/messagebox.cc	2016-02-15 11:00:58 +0000
@@ -83,7 +83,7 @@
 	                            "ok",
 	                            type_ == MBoxType::kOk ?
 	                               (width - button_w) / 2 :
-	                               UI::g_fh1->fontset().is_rtl() ? left_button_x : right_button_x,
+											 UI::g_fh1->fontset()->is_rtl() ? left_button_x : right_button_x,
 	                            button_y,
 	                            button_w,
 	                            0,
@@ -95,7 +95,7 @@
 		cancel_button_.reset(
 		   new Button(this,
 		              "cancel",
-		              UI::g_fh1->fontset().is_rtl() ? right_button_x : left_button_x,
+						  UI::g_fh1->fontset()->is_rtl() ? right_button_x : left_button_x,
 		              button_y,
 		              button_w,
 		              0,

=== modified file 'src/ui_basic/multilinetextarea.cc'
--- src/ui_basic/multilinetextarea.cc	2016-02-14 10:56:16 +0000
+++ src/ui_basic/multilinetextarea.cc	2016-02-15 11:00:58 +0000
@@ -53,8 +53,8 @@
 
 	scrollbar_.moved.connect(boost::bind(&MultilineTextarea::scrollpos_changed, this, _1));
 
-	scrollbar_.set_singlestepsize(UI::g_fh1->render(as_uifont(".", UI_FONT_SIZE_SMALL))->height());
-	scrollbar_.set_pagesize(h - 2 * UI::g_fh1->render(as_uifont(".", UI_FONT_SIZE_BIG))->height());
+	scrollbar_.set_singlestepsize(UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character(), UI_FONT_SIZE_SMALL))->height());
+	scrollbar_.set_pagesize(h - 2 * UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character(), UI_FONT_SIZE_BIG))->height());
 	scrollbar_.set_steps(1);
 	scrollbar_.set_force_draw(scrollmode_ == ScrollMode::kScrollNormalForced ||
 										scrollmode_ == ScrollMode::kScrollLogForced);

=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc	2016-02-14 19:34:44 +0000
+++ src/ui_basic/panel.cc	2016-02-15 11:00:58 +0000
@@ -1128,4 +1128,5 @@
 	dst.blit(r.origin() + Point(2, 2), rendered_text);
 	return true;
 }
+
 }

=== modified file 'src/ui_basic/progresswindow.cc'
--- src/ui_basic/progresswindow.cc	2016-02-06 19:59:13 +0000
+++ src/ui_basic/progresswindow.cc	2016-02-15 11:00:58 +0000
@@ -59,7 +59,8 @@
 	label_center_.y = yres * PROGRESS_LABEL_POSITION_Y / 100;
 	Rect wnd_rect(Point(0, 0), xres, yres);
 
-	const uint32_t h = UI::g_fh1->render(as_uifont("."))->height();
+	const uint32_t h =
+			UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height();
 
 	label_rectangle_.x = xres / 4;
 	label_rectangle_.w = xres / 2;

=== modified file 'src/ui_basic/slider.cc'
--- src/ui_basic/slider.cc	2016-02-03 18:09:15 +0000
+++ src/ui_basic/slider.cc	2016-02-15 11:00:58 +0000
@@ -551,7 +551,7 @@
 		 // here, we take into account the h_gap introduced by HorizontalSlider
 		 w / (2 * labels_in.size()) - cursor_size / 2, 0,
 		 w - (w / labels_in.size()) + cursor_size,
-		 h - UI::g_fh1->render(as_uifont("."))->height() - 2,
+		 h - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() - 2,
 		 0, labels_in.size() - 1, value_,
 		 background_picture_id,
 		 tooltip_text,
@@ -597,7 +597,7 @@
 	slider.set_pos(Point(w / (2 * labels.size()) - slider.cursor_size_ / 2, 0));
 	slider.set_size
 		(w - (w / labels.size()) + slider.cursor_size_,
-		 h - UI::g_fh1->render(as_uifont("."))->height() - 2);
+		 h - UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() - 2);
 	Panel::layout();
 }
 

=== modified file 'src/ui_basic/spinbox.cc'
--- src/ui_basic/spinbox.cc	2016-02-08 20:04:17 +0000
+++ src/ui_basic/spinbox.cc	2016-02-15 11:00:58 +0000
@@ -110,7 +110,7 @@
 	uint32_t actual_w = std::max(w, unit_w);
 	uint32_t no_padding = (is_big ? 6 : 4);
 	// Give some height margin = 2 to keep the label from generating a scrollbar.
-	uint32_t texth = UI::g_fh1->render(as_uifont("."))->height() + 2;
+	uint32_t texth = UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 2;
 	uint32_t buttonh = 20;
 
 	// 40 is an ad hoc width estimate for the MultilineTextarea scrollbar + a bit of text.

=== modified file 'src/ui_basic/table.cc'
--- src/ui_basic/table.cc	2016-02-03 18:09:15 +0000
+++ src/ui_basic/table.cc	2016-02-15 11:00:58 +0000
@@ -52,8 +52,8 @@
 	Panel             (parent, x, y, w, h),
 	total_width_     (0),
 	fontsize_        (UI_FONT_SIZE_SMALL),
-	headerheight_    (UI::g_fh1->render(as_uifont(".", fontsize_))->height() + 4),
-	lineheight_      (UI::g_fh1->render(as_uifont(".", fontsize_))->height()),
+	headerheight_    (UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character(), fontsize_))->height() + 4),
+	lineheight_      (UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character(), fontsize_))->height()),
 	scrollbar_       (nullptr),
 	scrollpos_       (0),
 	selection_       (no_selection_index()),
@@ -365,7 +365,7 @@
 			// Crop to column width while blitting
 			if ((curw + picw) < text_width) {
 				// Fix positioning for BiDi languages.
-				if (UI::g_fh1->fontset().is_rtl()) {
+				if (UI::g_fh1->fontset()->is_rtl()) {
 					point.x = static_cast<int>(alignment & UI::Align::kRight) ? curx : curx + picw;
 				}
 				// We want this always on, e.g. for mixed language savegame filenames

=== modified file 'src/ui_basic/textarea.cc'
--- src/ui_basic/textarea.cc	2016-02-09 18:24:03 +0000
+++ src/ui_basic/textarea.cc	2016-02-15 11:00:58 +0000
@@ -229,7 +229,7 @@
 		h = rendered_text_->height();
 		// We want empty textareas to have height
 		if (text_.empty()) {
-			h = UI::g_fh1->render(as_uifont(".", fontsize_))->height();
+			h = UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character(), fontsize_))->height();
 		}
 	}
 	set_desired_size(w, h);

=== modified file 'src/ui_fsmenu/campaign_select.h'
--- src/ui_fsmenu/campaign_select.h	2016-02-04 09:10:44 +0000
+++ src/ui_fsmenu/campaign_select.h	2016-02-15 11:00:58 +0000
@@ -22,7 +22,6 @@
 
 #include "ui_fsmenu/base.h"
 #include "ui_basic/button.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/table.h"
 #include "ui_basic/textarea.h"

=== modified file 'src/ui_fsmenu/internet_lobby.cc'
--- src/ui_fsmenu/internet_lobby.cc	2016-02-07 16:31:06 +0000
+++ src/ui_fsmenu/internet_lobby.cc	2016-02-15 11:00:58 +0000
@@ -148,7 +148,6 @@
 		(0, boost::bind(&FullscreenMenuInternetLobby::compare_clienttype, this, _1, _2));
 	clientsonline .double_clicked.connect
 		(boost::bind(&FullscreenMenuInternetLobby::client_doubleclicked, this, _1));
-	opengames   .set_fontsize(fs_);
 	opengames   .selected.connect
 		(boost::bind(&FullscreenMenuInternetLobby::server_selected, this));
 	opengames   .double_clicked.connect

=== modified file 'src/ui_fsmenu/launch_mpg.h'
--- src/ui_fsmenu/launch_mpg.h	2016-02-07 16:31:06 +0000
+++ src/ui_fsmenu/launch_mpg.h	2016-02-15 11:00:58 +0000
@@ -26,7 +26,6 @@
 #include "ui_fsmenu/base.h"
 #include "ui_fsmenu/helpwindow.h"
 #include "ui_basic/button.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/textarea.h"
 #include "wui/suggested_teams_box.h"

=== modified file 'src/ui_fsmenu/launch_spg.h'
--- src/ui_fsmenu/launch_spg.h	2016-01-28 19:58:46 +0000
+++ src/ui_fsmenu/launch_spg.h	2016-02-15 11:00:58 +0000
@@ -24,7 +24,6 @@
 
 #include "logic/constants.h"
 #include "ui_basic/button.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/textarea.h"
 #include "ui_fsmenu/base.h"

=== modified file 'src/ui_fsmenu/load_map_or_game.cc'
--- src/ui_fsmenu/load_map_or_game.cc	2016-02-04 09:10:44 +0000
+++ src/ui_fsmenu/load_map_or_game.cc	2016-02-15 11:00:58 +0000
@@ -25,7 +25,6 @@
 #include "graphic/graphic.h"
 #include "io/filesystem/filesystem.h"
 #include "ui_basic/button.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/textarea.h"
 

=== modified file 'src/ui_fsmenu/load_map_or_game.h'
--- src/ui_fsmenu/load_map_or_game.h	2016-02-04 07:45:37 +0000
+++ src/ui_fsmenu/load_map_or_game.h	2016-02-15 11:00:58 +0000
@@ -30,7 +30,6 @@
 #include "graphic/graphic.h"
 #include "io/filesystem/filesystem.h"
 #include "ui_basic/button.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/table.h"
 #include "ui_basic/textarea.h"

=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc	2016-02-07 16:31:06 +0000
+++ src/ui_fsmenu/options.cc	2016-02-15 11:00:58 +0000
@@ -51,12 +51,10 @@
 struct LanguageEntry {
 	LanguageEntry(const std::string& init_localename,
 					  const std::string& init_descname,
-					  const std::string& init_sortname,
-					  const std::string& init_fontname) :
+					  const std::string& init_sortname) :
 		localename(init_localename),
 		descname(init_descname),
-		sortname(init_sortname),
-		fontname(init_fontname) {}
+		sortname(init_sortname) {}
 
 	bool operator<(const LanguageEntry& other) const {
 		return sortname < other.sortname;
@@ -65,7 +63,6 @@
 	std::string localename; // ISO code for the locale
 	std::string descname;   // Native language name
 	std::string sortname;   // ASCII Language name used for sorting
-	std::string fontname;   // Name of the font with which the language name is displayed.
 };
 
 // Locale identifiers can look like this: ca_ES@xxxxxxxxxxxx-8
@@ -116,7 +113,7 @@
 	// Buttons
 	cancel_
 		(this, "cancel",
-		 UI::g_fh1->fontset().is_rtl() ? get_w() * 3 / 4 - butw_ / 2 : get_w() * 1 / 4 - butw_ / 2,
+		 UI::g_fh1->fontset()->is_rtl() ? get_w() * 3 / 4 - butw_ / 2 : get_w() * 1 / 4 - butw_ / 2,
 		 get_inner_h() - hmargin_,
 		 butw_, buth_,
 		 g_gr->images().get("images/ui_basic/but0.png"),
@@ -130,7 +127,7 @@
 		 _("Apply"), std::string(), true, false),
 	ok_
 		(this, "ok",
-		 UI::g_fh1->fontset().is_rtl() ? get_w() * 1 / 4 - butw_ / 2 : get_w() * 3 / 4 - butw_ / 2,
+		 UI::g_fh1->fontset()->is_rtl() ? get_w() * 1 / 4 - butw_ / 2 : get_w() * 3 / 4 - butw_ / 2,
 		 get_inner_h() - hmargin_,
 		 butw_, buth_,
 		 g_gr->images().get("images/ui_basic/but2.png"),
@@ -150,7 +147,7 @@
 
 	// Interface options
 	label_resolution_(&box_interface_, _("In-game resolution"), UI::Align::kLeft),
-	resolution_list_(&box_interface_, 0, 0, column_width_ / 2, 80, UI::Align::kLeft, true),
+	resolution_list_(&box_interface_, 0, 0, column_width_ / 2, 80, true),
 
 	fullscreen_ (&box_interface_, Point(0, 0), _("Fullscreen"), "", column_width_),
 	inputgrab_ (&box_interface_, Point(0, 0), _("Grab Input"), "", column_width_),
@@ -229,7 +226,7 @@
 	label_language_(&box_language_, _("Language"), UI::Align::kLeft),
 	language_list_(&box_language_, 0, 0, column_width_ / 2,
 						get_inner_h() - tab_panel_y_ - buth_ - hmargin_ - 5 * padding_,
-						UI::Align::kLeft, true),
+						true),
 
 	os_(opt)
 {
@@ -448,9 +445,7 @@
 
 				std::string name = i18n::make_ligatures(table->get_string("name").c_str());
 				const std::string sortname = table->get_string("sort_name");
-				std::unique_ptr<UI::FontSet> fontset(new UI::FontSet(localename));
-
-				entries.push_back(LanguageEntry(localename, name, sortname, fontset->serif()));
+				entries.push_back(LanguageEntry(localename, name, sortname));
 
 				if (localename == current_locale) {
 					selected_locale = current_locale;
@@ -458,7 +453,7 @@
 
 			} catch (const WException&) {
 				log("Could not read locale for: %s\n", localename.c_str());
-				entries.push_back(LanguageEntry(localename, localename, localename, UI::FontSet::kFallbackFont));
+				entries.push_back(LanguageEntry(localename, localename, localename));
 			}  // End read locale from table
 		}  // End scan locales directory
 	} catch (const LuaError& err) {
@@ -467,11 +462,10 @@
 	}  // End read locales table
 
 	find_selected_locale(&selected_locale, current_locale);
-
 	std::sort(entries.begin(), entries.end());
 	for (const LanguageEntry& entry : entries) {
 		language_list_.add(entry.descname.c_str(), entry.localename, nullptr,
-									entry.localename == selected_locale, "", entry.fontname);
+									entry.localename == selected_locale, "");
 	}
 }
 

=== modified file 'src/wui/actionconfirm.cc'
--- src/wui/actionconfirm.cc	2016-02-06 10:59:09 +0000
+++ src/wui/actionconfirm.cc	2016-02-15 11:00:58 +0000
@@ -154,7 +154,7 @@
 	UI::Button * okbtn =
 		new UI::Button
 			(this, "ok",
-			 UI::g_fh1->fontset().is_rtl() ? 6 : 114, 80, 80, 34,
+			 UI::g_fh1->fontset()->is_rtl() ? 6 : 114, 80, 80, 34,
 			 g_gr->images().get("images/ui_basic/but4.png"),
 			 g_gr->images().get("images/wui/menu_okay.png"));
 	okbtn->sigclicked.connect(boost::bind(&ActionConfirm::ok, this));
@@ -162,7 +162,7 @@
 	UI::Button * cancelbtn =
 		new UI::Button
 			(this, "abort",
-			 UI::g_fh1->fontset().is_rtl() ? 114 : 6, 80, 80, 34,
+			 UI::g_fh1->fontset()->is_rtl() ? 114 : 6, 80, 80, 34,
 			 g_gr->images().get("images/ui_basic/but4.png"),
 			 g_gr->images().get("images/wui/menu_abort.png"));
 	cancelbtn->sigclicked.connect(boost::bind(&ActionConfirm::die, this));

=== modified file 'src/wui/building_statistics_menu.cc'
--- src/wui/building_statistics_menu.cc	2016-02-08 20:04:17 +0000
+++ src/wui/building_statistics_menu.cc	2016-02-15 11:00:58 +0000
@@ -92,7 +92,7 @@
 			UI::Align::kBottomLeft),
 		unproductive_percent_(
 			&unproductive_box_, 0, 0, 35, g_gr->images().get("images/ui_basic/but1.png"),
-			kLabelFontSize - UI::g_fh1->fontset().size_offset()), // We need consistent height here
+			kLabelFontSize - UI::g_fh1->fontset()->size_offset()), // We need consistent height here
 		unproductive_label2_(
 			&unproductive_box_,
 			/** TRANSLATORS: This is the second part of productivity with input field */

=== modified file 'src/wui/game_objectives_menu.cc'
--- src/wui/game_objectives_menu.cc	2016-02-01 17:17:45 +0000
+++ src/wui/game_objectives_menu.cc	2016-02-15 11:00:58 +0000
@@ -47,7 +47,6 @@
 		(this,
 		 5, 5,
 		 get_inner_w() - 10, OBJECTIVE_LIST,
-		 UI::Align::kLeft,
 		 false),
 	objectivetext
 		(this,

=== modified file 'src/wui/login_box.cc'
--- src/wui/login_box.cc	2016-02-06 10:59:09 +0000
+++ src/wui/login_box.cc	2016-02-15 11:00:58 +0000
@@ -55,7 +55,7 @@
 
 	UI::Button * loginbtn = new UI::Button
 		(this, "login",
-		 UI::g_fh1->fontset().is_rtl() ?
+		 UI::g_fh1->fontset()->is_rtl() ?
 			 (get_inner_w() / 2 - 200) / 2 :
 			 (get_inner_w() / 2 - 200) / 2 + get_inner_w() / 2,
 		 get_inner_h() - 20 - margin,
@@ -65,7 +65,7 @@
 	loginbtn->sigclicked.connect(boost::bind(&LoginBox::clicked_ok, boost::ref(*this)));
 	UI::Button * cancelbtn = new UI::Button
 		(this, "cancel",
-		 UI::g_fh1->fontset().is_rtl() ?
+		 UI::g_fh1->fontset()->is_rtl() ?
 			 (get_inner_w() / 2 - 200) / 2 + get_inner_w() / 2 :
 			 (get_inner_w() / 2 - 200) / 2,
 		 loginbtn->get_y(), 200, 20,

=== modified file 'src/wui/productionsitewindow.cc'
--- src/wui/productionsitewindow.cc	2016-01-31 21:03:15 +0000
+++ src/wui/productionsitewindow.cc	2016-02-15 11:00:58 +0000
@@ -28,7 +28,6 @@
 #include "logic/map_objects/tribes/trainingsite.h"
 #include "logic/map_objects/tribes/tribe_descr.h"
 #include "logic/map_objects/tribes/worker.h"
-#include "ui_basic/listselect.h"
 #include "ui_basic/tabpanel.h"
 #include "ui_basic/textarea.h"
 #include "wui/waresqueuedisplay.h"


Follow ups