diff engine/core/video/fonts/fontbase.cpp @ 228:756b895e1dab

Merged unicode-support back into trunk. Now all GUI/visible strings should be unicode. Internal strings unchanged. Remember to use a font that actually has the desired codepoints. Current default unicode policiy is 'ignore'.
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Sat, 21 Mar 2009 10:38:11 +0000
parents 9a05ba6735b1
children 7887f3854862
line wrap: on
line diff
--- a/engine/core/video/fonts/fontbase.cpp	Fri Mar 20 15:17:28 2009 +0000
+++ b/engine/core/video/fonts/fontbase.cpp	Sat Mar 21 10:38:11 2009 +0000
@@ -33,6 +33,7 @@
 // Second block: files included from the same folder
 #include "util/structures/rect.h"
 #include "util/base/exception.h"
+#include "util/utf8/utf8.h"
 #include "video/image.h"
 #include "video/renderbackend.h"
 
@@ -78,12 +79,31 @@
 	}
 
 	int FontBase::getStringIndexAt(const std::string &text, int x) {
-		for (int i = 0; i < static_cast<int>(text.size()); ++i) {
-			if (getWidth(text.substr(0,i)) > x) {
-				return i-1;
+		assert( utf8::is_valid(text.begin(), text.end()) );
+		std::string::const_iterator cur;
+		if (text.size() == 0) return 0;
+		if (x <= 0) return 0;
+
+		cur = text.begin();
+
+		utf8::next(cur, text.end());
+
+		std::string buff;
+		while(cur != text.end()) {
+			buff = std::string(text.begin(), cur);
+
+			if (getWidth(buff) > x) {
+				return buff.size();
+			} else {
+				utf8::next(cur, text.end());
 			}
 		}
-		return text.length();
+
+		if (x > getWidth(text)) {
+			return text.size();
+		} else {
+			return buff.size();
+		}
 	}
 
 	Image* FontBase::getAsImage(const std::string& text) {
@@ -97,37 +117,39 @@
 	}
 
 	Image* FontBase::getAsImageMultiline(const std::string& text) {
+		const uint8_t newline_utf8 = '\n';
+		uint32_t newline;
+		utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
+		//std::cout << "Text:" << text << std::endl;
 		Image* image = m_pool.getRenderedText(this, text);
 		if (!image) {
 			std::vector<SDL_Surface*> lines;
-
+			std::string::const_iterator it = text.begin();
 			// split text as needed
-			std::string::size_type pos, last_pos = 0;
-			int length = 0;
 			int render_width = 0, render_height = 0;
 			do {
-				pos = text.find('\n', last_pos);
-				if (pos != std::string::npos) {
-					length = pos - last_pos;
-				} else {
-					length = text.size() - last_pos;
+				uint32_t codepoint = 0;
+				std::string line;
+				while( codepoint != newline && it != text.end() )
+				{
+					codepoint = utf8::next(it,text.end());
+					if( codepoint != newline )
+						utf8::append(codepoint, back_inserter(line));
 				}
-				std::string sub = text.substr(last_pos, length);
-				std::cerr << "substring: " << sub << "\n";
-				SDL_Surface* text_surface = renderString(sub);
+				//std::cout << "Line:" << line << std::endl;
+				SDL_Surface* text_surface = renderString(line);
 				if (text_surface->w > render_width) {
 					render_width = text_surface->w;
 				}
 				lines.push_back(text_surface);
-				last_pos = pos + 1;
-			} while (pos != std::string::npos);
+			} while (it != text.end());
+			
 			render_height = (getRowSpacing() + getHeight()) * lines.size();
 			SDL_Surface* final_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
 				render_width,render_height,32,
 				RMASK, GMASK, BMASK ,AMASK);
 			if (!final_surface) {
-				std::cerr << "CreateRGBSurface failed: " << SDL_GetError() << "\n";
-				exit(0);
+				throw SDLException(std::string("CreateRGBSurface failed: ") + SDL_GetError());
 			}
 			SDL_FillRect(final_surface, 0, 0x00000000);
 			int ypos = 0;
@@ -147,16 +169,20 @@
 	}
 
 	std::string FontBase::splitTextToWidth (const std::string& text, int render_width) {
+		const uint32_t whitespace = ' ';
+		const uint8_t newline_utf8 = '\n';
+		uint32_t newline;
+		utf8::utf8to32(&newline_utf8,&newline_utf8 + 1,&newline);
 		if (render_width <= 0 || text.empty()) { 
 			return text;
 		}
 		std::string output;
 		std::string line;
-		std::string::size_type pos = 0;
-		std::list<std::pair<size_t,size_t> > break_pos;
+		std::string::const_iterator pos = text.begin();
+		std::list<std::pair<size_t,std::string::const_iterator> > break_pos;
 		bool firstLine = true;
 
-		while( pos < text.length() )
+		while( pos != text.end() )
 		{
 			break_pos.clear();
 			if( !firstLine ) {
@@ -166,17 +192,17 @@
 			}
 
 			bool haveNewLine = false;
-			while( getWidth(line) < render_width && pos < text.length() )
+			while( getWidth(line) < render_width && pos != text.end() )
 			{
-				if (text.at(pos) == ' ' && !line.empty())
+				uint32_t codepoint = utf8::next(pos, text.end());
+				if (codepoint == whitespace && !line.empty())
 					break_pos.push_back( std::make_pair(line.length(),pos) );
-				line.push_back( text.at(pos) );
-				++pos;
+
+				if( codepoint != newline )
+					utf8::append(codepoint, back_inserter(line) );
 
 				// Special case: Already newlines in string:
-				if( text.at(pos-1) == '\n' ) {
-					if( line[line.size()-1] == '\n' )
-						line.erase(line.size()-1);
+				if( codepoint == newline ) {
 					output.append(line);
 					line = "";
 					haveNewLine = true;
@@ -186,25 +212,26 @@
 			if( haveNewLine )
 				continue;
 
-			if( pos >= text.length())
+			if( pos == text.end() )
 				break;
 
 			if( break_pos.empty() ) {
 				// No break position and line length smaller than 2
 				// means the renderwidth is really screwed. Just continue
 				// appending single character lines.
-				if( line.length() <= 1 ) {
+				if( utf8::distance(line.begin(),line.end()) <= 1 ) {
 					output.append(line);
 					continue;
 				}
 
 				// We can't do hyphenation here,
 				// so we just retreat one character :-(
-				line.erase(line.length() - 1);
-				--pos;
+				// FIXME
+				//line = line.erase(line.length() - 1);
+				//--pos;
 			} else {
 				line = line.substr(0,break_pos.back().first);
-				pos = break_pos.back().second + 1;
+				pos = break_pos.back().second;
 			}
 			output.append(line);
 		}