00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #define GETTEXT_DOMAIN "wesnoth-lib"
00023
00024 #include "global.hpp"
00025
00026 #include "font.hpp"
00027 #include "gettext.hpp"
00028 #include "marked-up_text.hpp"
00029 #include "serialization/string_utils.hpp"
00030 #include "video.hpp"
00031 #include "wml_exception.hpp"
00032
00033 namespace font {
00034
00035
00036
00037
00038
00039 const char LARGE_TEXT='*', SMALL_TEXT='`',
00040 BOLD_TEXT='~', NORMAL_TEXT='{',
00041 NULL_MARKUP='^',
00042 BLACK_TEXT='}', GRAY_TEXT='|',
00043 GOOD_TEXT='@', BAD_TEXT='#',
00044 GREEN_TEXT='@', RED_TEXT='#',
00045 COLOR_TEXT='<', IMAGE='&';
00046
00047 const std::string weapon = "<245,230,193>",
00048 weapon_details = "<166,146,117>",
00049 unit_type = "<245,230,193>",
00050 race = "<166,146,117>";
00051
00052 const SDL_Color
00053 weapon_color = { 245, 230, 193, 255 },
00054 good_dmg_color = { 130, 240, 50, 255 },
00055 bad_dmg_color = { 250, 140, 80, 255 },
00056 weapon_details_color = { 166, 146, 117, 255 },
00057 unit_type_color = { 245, 230, 193, 255 },
00058 race_color = { 166, 146, 117, 255 };
00059
00060 const std::string weapon_numbers_sep = "–", weapon_details_sep = "–";
00061
00062 std::string::const_iterator parse_markup(std::string::const_iterator i1,
00063 std::string::const_iterator i2,
00064 int* font_size,
00065 SDL_Color* color, int* style)
00066 {
00067 while(i1 != i2) {
00068 switch(*i1) {
00069 case '\\':
00070
00071
00072 break;
00073 case BAD_TEXT:
00074 if (color) *color = BAD_COLOR;
00075 break;
00076 case GOOD_TEXT:
00077 if (color) *color = GOOD_COLOR;
00078 break;
00079 case NORMAL_TEXT:
00080 if (color) *color = NORMAL_COLOR;
00081 break;
00082 case BLACK_TEXT:
00083 if (color) *color = BLACK_COLOR;
00084 break;
00085 case GRAY_TEXT:
00086 if (color) *color = GRAY_COLOR;
00087 break;
00088 case LARGE_TEXT:
00089 if (font_size) *font_size += 2;
00090 break;
00091 case SMALL_TEXT:
00092 if (font_size) *font_size -= 2;
00093 break;
00094 case BOLD_TEXT:
00095 if (style) *style |= TTF_STYLE_BOLD;
00096 break;
00097 case NULL_MARKUP:
00098 return i1+1;
00099 case COLOR_TEXT:
00100 {
00101 std::string::const_iterator start = i1;
00102
00103
00104 ++i1;
00105 Uint8 red=0, green=0, blue=0, temp=0;
00106 while (i1 != i2 && *i1 >= '0' && *i1<='9') {
00107 temp*=10;
00108 temp += lexical_cast<int, char>(*i1);
00109 ++i1;
00110 }
00111 red=temp;
00112 temp=0;
00113 if (i1 != i2 && ',' == (*i1)) {
00114 ++i1;
00115 while(i1 != i2 && *i1 >= '0' && *i1<='9'){
00116 temp*=10;
00117 temp += lexical_cast<int, char>(*i1);
00118 ++i1;
00119 }
00120 green=temp;
00121 temp=0;
00122 }
00123 if (i1 != i2 && ',' == (*i1)) {
00124 ++i1;
00125 while(i1 != i2 && *i1 >= '0' && *i1<='9'){
00126 temp*=10;
00127 temp += lexical_cast<int, char>(*i1);
00128 ++i1;
00129 }
00130 }
00131 blue=temp;
00132 if (i1 != i2 && '>' == (*i1)) {
00133 SDL_Color temp_color = {red, green, blue, 0};
00134 if (color) *color = temp_color;
00135 } else {
00136
00137 return start;
00138 }
00139 if (i1 == i2) return i1;
00140 break;
00141 }
00142 default:
00143 return i1;
00144 }
00145 ++i1;
00146 }
00147 return i1;
00148 }
00149
00150 std::string del_tags(const std::string& text){
00151 int ignore_int;
00152 SDL_Color ignore_color;
00153 std::vector<std::string> lines = utils::split(text, '\n', 0);
00154 std::vector<std::string>::iterator line;
00155 for(line = lines.begin(); line != lines.end(); ++line) {
00156 std::string::const_iterator i1 = line->begin(),
00157 i2 = line->end();
00158 *line = std::string(parse_markup(i1,i2,&ignore_int,&ignore_color,&ignore_int),i2);
00159 }
00160 return utils::join(lines, "\n");
00161 }
00162
00163 std::string color2markup(const SDL_Color &color)
00164 {
00165 std::stringstream markup;
00166
00167
00168 markup << "<"
00169 << static_cast<int>(color.r) << ","
00170 << static_cast<int>(color.g) << ","
00171 << static_cast<int>(color.b) << ">";
00172 return markup.str();
00173 }
00174
00175 std::string color2hexa(const SDL_Color &color)
00176 {
00177 char buf[7];
00178 sprintf(buf, "%02x%02x%02x", color.r, color.g, color.b);
00179 return buf;
00180 }
00181
00182 std::string span_color(const SDL_Color &color)
00183 {
00184 return "<span foreground=\"#" + font::color2hexa(color) + "\">";
00185 }
00186
00187 SDL_Rect text_area(const std::string& text, int size, int style)
00188 {
00189 const SDL_Rect area = {0,0,10000,10000};
00190 return draw_text(NULL, area, size, font::NORMAL_COLOR, text, 0, 0, false, style);
00191 }
00192
00193 SDL_Rect draw_text(surface dst, const SDL_Rect& area, int size,
00194 const SDL_Color& color, const std::string& txt,
00195 int x, int y, bool use_tooltips, int style)
00196 {
00197
00198
00199 static const std::string blank_text(" ");
00200 const std::string& text = txt.empty() ? blank_text : txt;
00201
00202 SDL_Rect res;
00203 res.x = x;
00204 res.y = y;
00205 res.w = 0;
00206 res.h = 0;
00207
00208 std::string::const_iterator i1 = text.begin();
00209 std::string::const_iterator i2 = std::find(i1,text.end(),'\n');
00210 for(;;) {
00211 SDL_Color col = color;
00212 int sz = size;
00213 int text_style = style;
00214
00215 i1 = parse_markup(i1,i2,&sz,&col,&text_style);
00216
00217 if(i1 != i2) {
00218 std::string new_string = utils::unescape(std::string(i1, i2));
00219
00220 const SDL_Rect rect = draw_text_line(dst, area, sz, col, new_string, x, y, use_tooltips, text_style);
00221 if(rect.w > res.w) {
00222 res.w = rect.w;
00223 }
00224
00225 res.h += rect.h;
00226 y += rect.h;
00227 }
00228
00229 if(i2 == text.end()) {
00230 break;
00231 }
00232
00233 i1 = i2+1;
00234 i2 = std::find(i1,text.end(),'\n');
00235 }
00236
00237 return res;
00238 }
00239
00240 SDL_Rect draw_text(CVideo* gui, const SDL_Rect& area, int size,
00241 const SDL_Color& color, const std::string& txt,
00242 int x, int y, bool use_tooltips, int style)
00243 {
00244 return draw_text(gui != NULL ? gui->getSurface() : NULL, area, size, color, txt, x, y, use_tooltips, style);
00245 }
00246
00247 bool is_format_char(char c)
00248 {
00249 switch(c) {
00250 case LARGE_TEXT:
00251 case SMALL_TEXT:
00252 case GOOD_TEXT:
00253 case BAD_TEXT:
00254 case NORMAL_TEXT:
00255 case BLACK_TEXT:
00256 case GRAY_TEXT:
00257 case BOLD_TEXT:
00258 case NULL_MARKUP:
00259 return true;
00260 default:
00261 return false;
00262 }
00263 }
00264
00265 bool is_cjk_char(const wchar_t c)
00266 {
00267
00268
00269
00270
00271
00272
00273
00274
00275 const unsigned int ch = static_cast<unsigned int>(c);
00276
00277
00278
00279 if (ch < 0x2e80) return false;
00280
00281 return
00282
00283 (ch >= 0x4e00 && ch < 0x9fcf) ||
00284 (ch >= 0x3400 && ch < 0x4dbf) ||
00285 (ch >= 0x20000 && ch < 0x2a6df) ||
00286 (ch >= 0xf900 && ch < 0xfaff) ||
00287 (ch >= 0x3190 && ch < 0x319f) ||
00288
00289
00290 (ch >= 0x2e80 && ch < 0x2eff) ||
00291 (ch >= 0x2f00 && ch < 0x2fdf) ||
00292 (ch >= 0x31c0 && ch < 0x31ef) ||
00293
00294
00295 (ch >= 0x3104 && ch < 0x312e) ||
00296 (ch >= 0x31a0 && ch < 0x31bb) ||
00297
00298
00299 (ch >= 0xa490 && ch < 0xa4c7) ||
00300 (ch >= 0xa000 && ch < 0xa48d) ||
00301
00302
00303 (ch >= 0x3040 && ch <= 0x309f) ||
00304 (ch >= 0x30a0 && ch <= 0x30ff) ||
00305 (ch >= 0x1b000 && ch <= 0x1b001) ||
00306
00307
00308 (ch >= 0x31f0 && ch <= 0x31ff) ||
00309
00310
00311 (ch >= 0xac00 && ch < 0xd7af) ||
00312 (ch >= 0x1100 && ch <= 0x11ff) ||
00313 (ch >= 0xa960 && ch <= 0xa97c) ||
00314 (ch >= 0xd7b0 && ch <= 0xd7fb) ||
00315
00316
00317 (ch >= 0x3000 && ch < 0x303f) ||
00318
00319
00320 (ch >= 0xff00 && ch < 0xffef);
00321 }
00322 static void cut_word(std::string& line, std::string& word, int font_size, int style, int max_width)
00323 {
00324 std::string tmp = line;
00325 utils::utf8_iterator tc(word);
00326 bool first = true;
00327
00328 for(;tc != utils::utf8_iterator::end(word); ++tc) {
00329 tmp.append(tc.substr().first, tc.substr().second);
00330 SDL_Rect tsize = line_size(tmp, font_size, style);
00331 if(tsize.w > max_width) {
00332 const std::string& w = word;
00333 if(line.empty() && first) {
00334 line += std::string(w.begin(), tc.substr().second);
00335 word = std::string(tc.substr().second, w.end());
00336 } else {
00337 line += std::string(w.begin(), tc.substr().first);
00338 word = std::string(tc.substr().first, w.end());
00339 }
00340 break;
00341 }
00342 first = false;
00343 }
00344 }
00345
00346 namespace {
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363 inline bool no_break_after(const wchar_t ch)
00364 {
00365 return
00366
00367
00368
00369 ch == 0x2018 || ch == 0x201c || ch == 0x3008 || ch == 0x300a || ch == 0x300c ||
00370 ch == 0x300e || ch == 0x3010 || ch == 0x3014 || ch == 0xff08 || ch == 0xff3b ||
00371 ch == 0xff5b ||
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381 ch == 0x3016 || ch == 0x301a || ch == 0x301d;
00382 }
00383
00384 inline bool no_break_before(const wchar_t ch)
00385 {
00386 return
00387
00388
00389
00390 ch == 0x2019 || ch == 0x201d || ch == 0x2026 || ch == 0x3001 || ch == 0x3002 ||
00391 ch == 0x3005 || ch == 0x3009 || ch == 0x300b || ch == 0x300d || ch == 0x300f ||
00392 ch == 0x3011 || ch == 0x3015 || ch == 0x3041 || ch == 0x3043 || ch == 0x3045 ||
00393 ch == 0x3047 || ch == 0x3049 || ch == 0x3063 || ch == 0x3083 || ch == 0x3085 ||
00394 ch == 0x3087 || ch == 0x308e || ch == 0x309d || ch == 0x309e || ch == 0x30a1 ||
00395 ch == 0x30a3 || ch == 0x30a5 || ch == 0x30a7 || ch == 0x30a9 || ch == 0x30c3 ||
00396 ch == 0x30e3 || ch == 0x30e5 || ch == 0x30e7 || ch == 0x30ee || ch == 0x30f5 ||
00397 ch == 0x30f6 || ch == 0x30fb || ch == 0x30fc || ch == 0x30fd || ch == 0x30fe ||
00398 ch == 0xff01 || ch == 0xff09 || ch == 0xff0c || ch == 0xff0e || ch == 0xff1a ||
00399 ch == 0xff1b || ch == 0xff1f || ch == 0xff3d || ch == 0xff5d ||
00400
00401
00402 ch == 0x31f0 || ch == 0x31f1 || ch == 0x31f2 || ch == 0x31f3 || ch == 0x31f4 ||
00403 ch == 0x31f5 || ch == 0x31f6 || ch == 0x31f7 || ch == 0x31f8 || ch == 0x31f9 ||
00404 ch == 0x31fa || ch == 0x31fb || ch == 0x31fc || ch == 0x31fd || ch == 0x31fe ||
00405 ch == 0x31ff ||
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 ch == 0x301c || ch == 0xff0d || ch == 0xff64 || ch == 0xff65 || ch == 0x3017 ||
00418 ch == 0x301b || ch == 0x301e;
00419 }
00420
00421 inline bool break_before(const wchar_t ch)
00422 {
00423 if(no_break_before(ch))
00424 return false;
00425
00426 return is_cjk_char(ch);
00427 }
00428
00429 inline bool break_after(const wchar_t ch)
00430 {
00431 if(no_break_after(ch))
00432 return false;
00433
00434 return is_cjk_char(ch);
00435 }
00436
00437 }
00438
00439 std::string word_wrap_text(const std::string& unwrapped_text, int font_size,
00440 int max_width, int max_height, int max_lines, bool partial_line)
00441 {
00442 VALIDATE(max_width > 0, _("The maximum text width is less than 1."));
00443
00444 utils::utf8_iterator ch(unwrapped_text);
00445 std::string current_word;
00446 std::string current_line;
00447 size_t line_width = 0;
00448 size_t current_height = 0;
00449 bool line_break = false;
00450 bool first = true;
00451 bool start_of_line = true;
00452 std::string wrapped_text;
00453 std::string format_string;
00454 SDL_Color color;
00455 int font_sz = font_size;
00456 int style = TTF_STYLE_NORMAL;
00457 utils::utf8_iterator end = utils::utf8_iterator::end(unwrapped_text);
00458
00459 while(1) {
00460 if(start_of_line) {
00461 line_width = 0;
00462 format_string.clear();
00463 while(ch != end && *ch < static_cast<wchar_t>(0x100)
00464 && is_format_char(*ch) && !ch.next_is_end()) {
00465
00466 format_string.append(ch.substr().first, ch.substr().second);
00467 ++ch;
00468 }
00469
00470
00471 font_sz = font_size;
00472 style = TTF_STYLE_NORMAL;
00473 parse_markup(format_string.begin(),format_string.end(),&font_sz,&color,&style);
00474 current_line.clear();
00475 start_of_line = false;
00476 }
00477
00478
00479 if(current_word.empty() && ch == end) {
00480 break;
00481 } else if(current_word.empty()) {
00482 if(*ch == ' ' || *ch == '\n') {
00483 current_word = *ch;
00484 ++ch;
00485 } else {
00486 wchar_t previous = 0;
00487 for(;ch != utils::utf8_iterator::end(unwrapped_text) &&
00488 *ch != ' ' && *ch != '\n'; ++ch) {
00489
00490 if(!current_word.empty() &&
00491 break_before(*ch) &&
00492 !no_break_after(previous))
00493 break;
00494
00495 if(!current_word.empty() &&
00496 break_after(previous) &&
00497 !no_break_before(*ch))
00498 break;
00499
00500 current_word.append(ch.substr().first, ch.substr().second);
00501
00502 previous = *ch;
00503 }
00504 }
00505 }
00506
00507 if(current_word == "\n") {
00508 line_break = true;
00509 current_word.clear();
00510 start_of_line = true;
00511 } else {
00512
00513 const size_t word_width = line_size(current_word, font_sz, style).w;
00514
00515 line_width += word_width;
00516
00517 if(static_cast<long>(line_width) > max_width) {
00518 if (!partial_line && static_cast<long>(word_width) > max_width) {
00519 cut_word(current_line,
00520 current_word, font_sz, style, max_width);
00521 }
00522 if(current_word == " ")
00523 current_word = "";
00524 line_break = true;
00525 } else {
00526 current_line += current_word;
00527 current_word = "";
00528 }
00529 }
00530
00531 if(line_break || (current_word.empty() && ch == end)) {
00532 SDL_Rect size = line_size(current_line, font_sz, style);
00533 if(max_height > 0 && current_height + size.h >= size_t(max_height)) {
00534 return wrapped_text;
00535 }
00536
00537 if(!first) {
00538 wrapped_text += '\n';
00539 }
00540
00541 wrapped_text += format_string + current_line;
00542 current_line.clear();
00543 line_width = 0;
00544 current_height += size.h;
00545 line_break = false;
00546 first = false;
00547
00548 if(--max_lines == 0) {
00549 return wrapped_text;
00550 }
00551 }
00552 }
00553 return wrapped_text;
00554 }
00555
00556 SDL_Rect draw_wrapped_text(CVideo* gui, const SDL_Rect& area, int font_size,
00557 const SDL_Color& color, const std::string& text,
00558 int x, int y, int max_width)
00559 {
00560 std::string wrapped_text = word_wrap_text(text, font_size, max_width);
00561 return font::draw_text(gui, area, font_size, color, wrapped_text, x, y, false);
00562 }
00563
00564 }
00565