1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 */
21 
22 /*
23  *  sdl_fonts.cpp - SDL font handling
24  *
25  *  Written in 2000 by Christian Bauer
26  */
27 
28 #include "cseries.h"
29 #include "sdl_fonts.h"
30 #include "byte_swapping.h"
31 #include "game_errors.h"
32 #include "resource_manager.h"
33 #include "FileHandler.h"
34 #include "Logging.h"
35 
36 #include <SDL_endian.h>
37 #include <vector>
38 #include <map>
39 
40 #include <boost/tokenizer.hpp>
41 #include <string>
42 
43 #ifndef NO_STD_NAMESPACE
44 using std::vector;
45 using std::pair;
46 using std::map;
47 #endif
48 
49 #include <boost/tuple/tuple_comparison.hpp>
50 #include "preferences.h" // smooth_font
51 #include "AlephSansMono-Bold.h"
52 #include "ProFontAO.h"
53 
54 #include "CourierPrime.h"
55 #include "CourierPrimeBold.h"
56 #include "CourierPrimeItalic.h"
57 #include "CourierPrimeBoldItalic.h"
58 
59 // Global variables
60 typedef pair<int, int> id_and_size_t;
61 typedef map<id_and_size_t, sdl_font_info *> font_list_t;
62 static font_list_t font_list;				// List of all loaded fonts
63 
64 typedef pair<TTF_Font *, int> ref_counted_ttf_font_t;
65 typedef map<ttf_font_key_t, ref_counted_ttf_font_t> ttf_font_list_t;
66 static ttf_font_list_t ttf_font_list;
67 
68 // From shell_sdl.cpp
69 extern vector<DirectorySpecifier> data_search_path;
70 
71 
72 /*
73  *  Initialize font management
74  */
75 
76 typedef struct builtin_font
77 {
78 	std::string name;
79 	unsigned char *data;
80 	unsigned int size;
81 } builtin_font_t;
82 
83 static builtin_font_t builtin_fontspecs[] = {
84 	{ "mono", aleph_sans_mono_bold, sizeof(aleph_sans_mono_bold) },
85 	{ "Monaco", pro_font_ao, sizeof(pro_font_ao) },
86 	{ "Courier Prime", courier_prime, sizeof(courier_prime) },
87 	{ "Courier Prime Bold", courier_prime_bold, sizeof(courier_prime_bold) },
88 	{ "Courier Prime Italic", courier_prime_italic, sizeof(courier_prime_italic) },
89 	{" Courier Prime Bold Italic", courier_prime_bold_italic, sizeof(courier_prime_bold_italic) }
90 };
91 
92 #define NUMBER_OF_BUILTIN_FONTS sizeof(builtin_fontspecs) / sizeof(builtin_font)
93 
94 typedef std::map<std::string, builtin_font_t> builtin_fonts_t;
95 builtin_fonts_t builtin_fonts;
96 
initialize_fonts(bool last_chance)97 void initialize_fonts(bool last_chance)
98 {
99         logContext("initializing fonts");
100 
101 	// Initialize builtin TTF fonts
102 	for (int j = 0; j < NUMBER_OF_BUILTIN_FONTS; ++j)
103 		builtin_fonts[builtin_fontspecs[j].name] = builtin_fontspecs[j];
104 
105 	// Open font resource files
106 	bool found = false;
107 	vector<DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
108 	while (i != end) {
109 		FileSpecifier fonts = *i + "Fonts";
110 
111 		if (open_res_file(fonts))
112 			found = true;
113 
114 		if (!found)
115 		{
116 			fonts = *i + "Fonts.fntA";
117 			if (open_res_file(fonts))
118 				found = true;
119 		}
120 		i++;
121 	}
122 }
123 
124 
125 /*
126  *  Load font from resources and allocate sdl_font_info
127  */
128 
load_sdl_font(const TextSpec & spec)129 sdl_font_info *load_sdl_font(const TextSpec &spec)
130 {
131 	sdl_font_info *info = NULL;
132 
133 	// Look for ID/size in list of loaded fonts
134 	id_and_size_t id_and_size(spec.font, spec.size);
135 	font_list_t::const_iterator it = font_list.find(id_and_size);
136 	if (it != font_list.end()) {	// already loaded
137 		info = it->second;
138 		info->ref_count++;
139 		return info;
140 	}
141 
142 	// Load font family resource
143 	LoadedResource fond;
144 	if (!get_resource(FOUR_CHARS_TO_INT('F', 'O', 'N', 'D'), spec.font, fond)) {
145 		fprintf(stderr, "Font family resource for font ID %d not found\n", spec.font);
146 		return NULL;
147 	}
148 	SDL_RWops *p = SDL_RWFromMem(fond.GetPointer(), (int)fond.GetLength());
149 	assert(p);
150 
151 	// Look for font size in association table
152 	SDL_RWseek(p, 52, SEEK_SET);
153 	int num_assoc = SDL_ReadBE16(p) + 1;
154 	while (num_assoc--) {
155 		int size = SDL_ReadBE16(p);
156 		SDL_ReadBE16(p); // skip style
157 		int id = SDL_ReadBE16(p);
158 		if (size == spec.size) {
159 
160 			// Size found, load bitmap font resource
161 			info = new sdl_font_info;
162 			if (!get_resource(FOUR_CHARS_TO_INT('N', 'F', 'N', 'T'), id, info->rsrc))
163 				get_resource(FOUR_CHARS_TO_INT('F', 'O', 'N', 'T'), id, info->rsrc);
164 			if (info->rsrc.IsLoaded()) {
165 
166 				// Found, switch stream to font resource
167 				SDL_RWclose(p);
168 				p = SDL_RWFromMem(info->rsrc.GetPointer(), (int)info->rsrc.GetLength());
169 				assert(p);
170 				void *font_ptr = info->rsrc.GetPointer(true);
171 
172 				// Read font information
173 				SDL_RWseek(p, 2, SEEK_CUR);
174 				info->first_character = static_cast<uint8>(SDL_ReadBE16(p));
175 				info->last_character = static_cast<uint8>(SDL_ReadBE16(p));
176 				SDL_RWseek(p, 2, SEEK_CUR);
177 				info->maximum_kerning = SDL_ReadBE16(p);
178 				SDL_RWseek(p, 2, SEEK_CUR);
179 				info->rect_width = SDL_ReadBE16(p);
180 				info->rect_height = SDL_ReadBE16(p);
181 				SDL_RWseek(p, 2, SEEK_CUR);
182 				info->ascent = SDL_ReadBE16(p);
183 				info->descent = SDL_ReadBE16(p);
184 				info->leading = SDL_ReadBE16(p);
185 				int bytes_per_row = SDL_ReadBE16(p) * 2;
186 
187 				//printf(" first %d, last %d, max_kern %d, rect_w %d, rect_h %d, ascent %d, descent %d, leading %d, bytes_per_row %d\n",
188 				//	info->first_character, info->last_character, info->maximum_kerning,
189 				//	info->rect_width, info->rect_height, info->ascent, info->descent, info->leading, bytes_per_row);
190 
191 				// Convert bitmap to pixmap (1 byte/pixel)
192 				info->bytes_per_row = bytes_per_row * 8;
193 				uint8 *src = (uint8 *)font_ptr + SDL_RWtell(p);
194 				uint8 *dst = info->pixmap = (uint8 *)malloc(info->rect_height * info->bytes_per_row);
195 				assert(dst);
196 				for (int y=0; y<info->rect_height; y++) {
197 					for (int x=0; x<bytes_per_row; x++) {
198 						uint8 b = *src++;
199 						*dst++ = (b & 0x80) ? 0xff : 0x00;
200 						*dst++ = (b & 0x40) ? 0xff : 0x00;
201 						*dst++ = (b & 0x20) ? 0xff : 0x00;
202 						*dst++ = (b & 0x10) ? 0xff : 0x00;
203 						*dst++ = (b & 0x08) ? 0xff : 0x00;
204 						*dst++ = (b & 0x04) ? 0xff : 0x00;
205 						*dst++ = (b & 0x02) ? 0xff : 0x00;
206 						*dst++ = (b & 0x01) ? 0xff : 0x00;
207 					}
208 				}
209 				SDL_RWseek(p, info->rect_height * bytes_per_row, SEEK_CUR);
210 
211 				// Set table pointers
212 				int table_size = info->last_character - info->first_character + 3;	// Tables contain 2 additional entries
213 				info->location_table = (uint16 *)((uint8 *)font_ptr + SDL_RWtell(p));
214 				byte_swap_memory(info->location_table, _2byte, table_size);
215 				SDL_RWseek(p, table_size * 2, SEEK_CUR);
216 				info->width_table = (int8 *)font_ptr + SDL_RWtell(p);
217 
218 				// Add font information to list of known fonts
219 				info->ref_count++;
220 				font_list[id_and_size] = info;
221 
222 			} else {
223 				delete info;
224 				info = NULL;
225 				fprintf(stderr, "Bitmap font resource ID %d not found\n", id);
226 			}
227 		}
228 	}
229 
230 	// Free resources
231 	SDL_RWclose(p);
232 	return info;
233 }
234 
load_ttf_font(const std::string & path,uint16 style,int16 size)235 static TTF_Font *load_ttf_font(const std::string& path, uint16 style, int16 size)
236 {
237 	// already loaded? increment reference counter and return pointer
238 	ttf_font_key_t search_key(path, style, size);
239 	ttf_font_list_t::iterator it = ttf_font_list.find(search_key);
240 	if (it != ttf_font_list.end())
241 	{
242 		TTF_Font *font = it->second.first;
243 		it->second.second++;
244 
245 		return font;
246 	}
247 
248 	TTF_Font *font = 0;
249 	builtin_fonts_t::iterator j = builtin_fonts.find(path);
250 	if (j != builtin_fonts.end())
251 	{
252 		font = TTF_OpenFontRW(SDL_RWFromConstMem(j->second.data, j->second.size), 0, size);
253 	}
254 	else
255 	{
256 		short SavedType, SavedError = get_game_error(&SavedType);
257 
258 		FileSpecifier fileSpec(path);
259 		OpenedFile file;
260 		if (fileSpec.Open(file))
261 		{
262 			font = TTF_OpenFontRW(file.TakeRWops(), 1, size);
263 		}
264 
265 		set_game_error(SavedType, SavedError);
266 	}
267 
268 	if (font)
269 	{
270 		int ttf_style = TTF_STYLE_NORMAL;
271 		if (style & styleBold)
272 			ttf_style |= TTF_STYLE_BOLD;
273 		if (style & styleItalic)
274 			ttf_style |= TTF_STYLE_ITALIC;
275 
276 		TTF_SetFontStyle(font, ttf_style);
277 #ifdef TTF_HINTING_LIGHT
278 		if (environment_preferences->smooth_text)
279 			TTF_SetFontHinting(font, TTF_HINTING_LIGHT);
280 		else
281 			TTF_SetFontHinting(font, TTF_HINTING_MONO);
282 #endif
283 
284 		ttf_font_key_t key(path, style, size);
285 		ref_counted_ttf_font_t value(font, 1);
286 
287 		ttf_font_list[key] = value;
288 	}
289 
290 	return font;
291 }
292 
locate_font(const std::string & path)293 static const char *locate_font(const std::string& path)
294 {
295 	builtin_fonts_t::iterator j = builtin_fonts.find(path);
296 	if (j != builtin_fonts.end() || path == "")
297 	{
298 		return path.c_str();
299 	}
300 	else
301 	{
302 		static FileSpecifier file;
303 		if (file.SetNameWithPath(path.c_str()))
304 			return file.GetPath();
305 		else
306 			return "";
307 	}
308 }
309 
load_font(const TextSpec & spec)310 font_info *load_font(const TextSpec &spec) {
311 //	return static_cast<font_info*>(load_font(spec));
312 
313 	if (spec.normal != "")
314 	{
315 		std::string file;
316 		file = locate_font(spec.normal);
317 		TTF_Font *font = load_ttf_font(file, 0, spec.size);
318 		if (font)
319 		{
320 			ttf_font_info *info = new ttf_font_info;
321 			info->m_adjust_height = spec.adjust_height;
322 			info->m_styles[styleNormal] = font;
323 			info->m_keys[styleNormal] = ttf_font_key_t(file, 0, spec.size);
324 
325 			// load bold face
326 			file = locate_font(spec.bold);
327 			font = load_ttf_font(file, styleNormal, spec.size);
328 			if (font)
329 			{
330 				info->m_styles[styleBold] = font;
331 				info->m_keys[styleBold] = ttf_font_key_t(file, styleNormal, spec.size);
332 			}
333 			else
334 			{
335 				file = locate_font(spec.normal);
336 				font = load_ttf_font(file, styleBold, spec.size);
337 				assert(font); // I loaded you once, you should load again
338 				info->m_styles[styleBold] = font;
339 				info->m_keys[styleBold] = ttf_font_key_t(file, styleBold, spec.size);
340 			}
341 
342 			// oblique
343 			file = locate_font(spec.oblique);
344 			font = load_ttf_font(file, styleNormal, spec.size);
345 			if (font)
346 			{
347 				info->m_styles[styleItalic] = font;
348 				info->m_keys[styleItalic] = ttf_font_key_t(file, styleNormal, spec.size);
349 			}
350 			else
351 			{
352 				file = locate_font(spec.normal);
353 				font = load_ttf_font(file, styleItalic, spec.size);
354 				assert(font); // same as above
355 				info->m_styles[styleItalic] = font;
356 				info->m_keys[styleItalic] = ttf_font_key_t(file, styleItalic, spec.size);
357 			}
358 
359 			// bold oblique
360 			file = locate_font(spec.bold_oblique);
361 			font = load_ttf_font(file, styleNormal, spec.size);
362 			if (font)
363 			{
364 				info->m_styles[styleBold | styleItalic] = font;
365 				info->m_keys[styleBold | styleItalic] = ttf_font_key_t(file, styleNormal, spec.size);
366 			}
367 			else
368 			{
369 				// try boldening the oblique
370 				file = locate_font(spec.oblique);
371 				font = load_ttf_font(file, styleBold, spec.size);
372 				if (font)
373 				{
374 					info->m_styles[styleBold | styleItalic] = font;
375 					info->m_keys[styleBold | styleItalic] = ttf_font_key_t(file, styleBold, spec.size);
376 				}
377 				else
378 				{
379 					// try obliquing the bold!
380 					file = locate_font(spec.bold);
381 					font = load_ttf_font(file, styleItalic, spec.size);
382 					if (font)
383 					{
384 						info->m_styles[styleBold | styleItalic] = font;
385 						info->m_keys[styleBold | styleItalic] = ttf_font_key_t(file, styleItalic, spec.size);
386 					}
387 					else
388 					{
389 						file = locate_font(spec.normal);
390 						font = load_ttf_font(file, styleBold | styleItalic, spec.size);
391 						assert(font);
392 						info->m_styles[styleBold | styleItalic] = font;
393 						info->m_keys[styleBold | styleItalic] = ttf_font_key_t(file, styleBold | styleItalic, spec.size);
394 					}
395 				}
396 			}
397 
398 
399 			return info;
400 		}
401 		else if (spec.font != -1)
402 		{
403 			return static_cast<font_info *>(load_sdl_font(spec));
404 		}
405 		else
406 			return 0;
407 	}
408 	else
409         if (spec.font != -1)
410 	{
411 		return static_cast<font_info *>(load_sdl_font(spec));
412 	}
413 	else
414 		return 0;
415 }
416 
417 
418 /*
419  *  Unload font, free sdl_font_info
420  */
421 
_unload()422 void sdl_font_info::_unload()
423 {
424 	// Look for font in list of loaded fonts
425 	font_list_t::const_iterator i = font_list.begin(), end = font_list.end();
426 	while (i != end) {
427 		if (i->second == this) {
428 
429 			// Found, decrement reference counter and delete
430 			ref_count--;
431 			if (ref_count <= 0) {
432 				delete this; // !
433 				font_list.erase(i->first);
434 				return;
435 			}
436 		}
437 		i++;
438 	}
439 }
440 
_unload()441 void ttf_font_info::_unload()
442 {
443 	for (int i = 0; i < styleUnderline; ++i)
444 	{
445 		ttf_font_list_t::iterator it = ttf_font_list.find(m_keys[i]);
446 		if (it != ttf_font_list.end())
447 		{
448 			--(it->second.second);
449 			if (it->second.second <= 0)
450 			{
451 				TTF_CloseFont(it->second.first);
452 				ttf_font_list.erase(m_keys[i]);
453 			}
454 		}
455 
456 		m_styles[i] = 0;
457 	}
458 
459 	delete this;
460 }
461 
unload_font(font_info * info)462 void unload_font(font_info *info)
463 {
464 	info->_unload();
465 }
466 
char_width(uint8 c,uint16 style) const467 int8 sdl_font_info::char_width(uint8 c, uint16 style) const
468 {
469 	if (c < first_character || c > last_character)
470 		return 0;
471 	int8 width = width_table[(c - first_character) * 2 + 1] + ((style & styleBold) ? 1 : 0);
472 	if (width == -1)	// non-existant character
473 		width = width_table[(last_character - first_character + 1) * 2 + 1] + ((style & styleBold) ? 1 : 0);
474 	return width;
475 }
476 
_text_width(const char * text,uint16 style,bool) const477 uint16 sdl_font_info::_text_width(const char *text, uint16 style, bool) const
478 {
479 	int width = 0;
480 	char c;
481 	while ((c = *text++) != 0)
482 		width += char_width(c, style);
483 	assert(0 <= width);
484 	assert(width == static_cast<int>(static_cast<uint16>(width)));
485 	return width;
486 }
487 
_text_width(const char * text,size_t length,uint16 style,bool) const488 uint16 sdl_font_info::_text_width(const char *text, size_t length, uint16 style, bool) const
489 {
490 	int width = 0;
491 	while (length--)
492 		width += char_width(*text++, style);
493 	assert(0 <= width);
494 	assert(width == static_cast<int>(static_cast<uint16>(width)));
495 	return width;
496 }
497 
_trunc_text(const char * text,int max_width,uint16 style) const498 int sdl_font_info::_trunc_text(const char *text, int max_width, uint16 style) const
499 {
500 	int width = 0;
501 	int num = 0;
502 	char c;
503 	while ((c = *text++) != 0) {
504 		width += char_width(c, style);
505 		if (width > max_width)
506 			break;
507 		num++;
508 	}
509 	return num;
510 }
511 
512 // sdl_font_info::_draw_text is in screen_drawing.cpp
513 
char_width(uint8 c,uint16 style) const514 int8 ttf_font_info::char_width(uint8 c, uint16 style) const
515 {
516 	int advance;
517 	TTF_GlyphMetrics(get_ttf(style), mac_roman_to_unicode(static_cast<char>(c)), 0, 0, 0, 0, &advance);
518 
519 	return advance;
520 }
_text_width(const char * text,uint16 style,bool utf8) const521 uint16 ttf_font_info::_text_width(const char *text, uint16 style, bool utf8) const
522 {
523 	return _text_width(text, strlen(text), style, utf8);
524 }
525 
_text_width(const char * text,size_t length,uint16 style,bool utf8) const526 uint16 ttf_font_info::_text_width(const char *text, size_t length, uint16 style, bool utf8) const
527 {
528 	int width = 0;
529 	if (utf8)
530 	{
531 		char *temp = process_printable(text, length);
532 		TTF_SizeUTF8(get_ttf(style), temp, &width, 0);
533 	}
534 	else
535 	{
536 		uint16 *temp = process_macroman(text, length);
537 		TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
538 	}
539 
540 	return width;
541 }
542 
_trunc_text(const char * text,int max_width,uint16 style) const543 int ttf_font_info::_trunc_text(const char *text, int max_width, uint16 style) const
544 {
545 	int width;
546 	static uint16 temp[1024];
547 	mac_roman_to_unicode(text, temp, 1024);
548 	TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
549 	if (width < max_width) return strlen(text);
550 
551 	int num = strlen(text) - 1;
552 
553 	while (num > 0 && width > max_width)
554 	{
555 		num--;
556 		temp[num] = 0x0;
557 		TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
558 	}
559 
560 	return num;
561 }
562 
563 // ttf_font_info::_draw_text is in screen_drawing.cpp
564 
process_printable(const char * src,int len) const565 char *ttf_font_info::process_printable(const char *src, int len) const
566 {
567 	static char dst[1024];
568 	if (len > 1023) len = 1023;
569 	char *p = dst;
570 	while (*src && len-- > 0)
571 	{
572 		if ((unsigned char) *src >= ' ') *p++ = *src;
573 		src++;
574 	}
575 
576 	*p = '\0';
577 	return dst;
578 }
579 
process_macroman(const char * src,int len) const580 uint16 *ttf_font_info::process_macroman(const char *src, int len) const
581 {
582 	static uint16 dst[1024];
583 	if (len > 1023) len = 1023;
584 	uint16 *p = dst;
585 	while (*src && len-- > 0)
586 	{
587 		if ((unsigned char) *src >= ' ') *p++ = mac_roman_to_unicode(*src);
588 		else if ((unsigned char) *src == '\t')
589 			*p++ = ' ';
590 
591 		src++;
592 	}
593 
594 	*p = 0x0;
595 	return dst;
596 }
597 
text_width(const char * text,uint16 style,bool utf8) const598 uint16 font_info::text_width(const char *text, uint16 style, bool utf8) const
599 {
600 	if (style & styleShadow)
601 		return _text_width(text, style, utf8) + 1;
602 	else
603 		return _text_width(text, style, utf8);
604 }
605 
text_width(const char * text,size_t length,uint16 style,bool utf8) const606 uint16 font_info::text_width(const char *text, size_t length, uint16 style, bool utf8) const
607 {
608 	if (style & styleShadow)
609 		return _text_width(text, length, style, utf8) + 1;
610 	else
611 		return _text_width(text, length, style, utf8);
612 }
613 
style_code(char c)614 static 	inline bool style_code(char c)
615 {
616 	switch(tolower(c)) {
617 	case 'p':
618 	case 'b':
619 	case 'i':
620 	case 'l':
621 	case 'r':
622 	case 'c':
623 	case 's':
624 		return true;
625 	default:
626 		return false;
627 	}
628 }
629 
630 class style_separator
631 {
632 public:
operator ()(std::string::const_iterator & next,std::string::const_iterator end,std::string & token)633 	bool operator() (std::string::const_iterator& next, std::string::const_iterator end, std::string& token)
634 	{
635 		if (next == end) return false;
636 
637 		token = std::string();
638 
639 		// if we start with a token, return it
640 		if (*next == '|' && next + 1 != end && style_code(*(next + 1)))
641 		{
642 			token += *next;
643 			++next;
644 			token += *next;
645 			++next;
646 			return true;
647 		}
648 
649 		token += *next;
650 		++next;
651 
652 		// add characters until we hit a token
653 		for (;next != end && !(*next == '|' && next + 1 != end && style_code(*(next + 1))); ++next)
654 		{
655 			token += *next;
656 		}
657 
658 		return true;
659 	}
660 
reset()661 	void reset() { }
662 
663 };
664 
is_style_token(const std::string & token)665 static inline bool is_style_token(const std::string& token)
666 {
667 	return (token.size() == 2 && token[0] == '|' && style_code(token[1]));
668 }
669 
update_style(uint16 & style,const std::string & token)670 static void update_style(uint16& style, const std::string& token)
671 {
672 	if (tolower(token[1]) == 'p')
673 		style &= ~(styleBold | styleItalic);
674 	else if (tolower(token[1]) == 'b')
675 	{
676 		style |= styleBold;
677 		style &= ~styleItalic;
678 	}
679 	else if (tolower(token[1]) == 'i')
680 	{
681 		style |= styleItalic;
682 		style &= ~styleBold;
683 	}
684 }
685 
686 
draw_styled_text(SDL_Surface * s,const std::string & text,size_t length,int x,int y,uint32 pixel,uint16 style,bool utf8) const687 int font_info::draw_styled_text(SDL_Surface *s, const std::string& text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
688 {
689 	int width = 0;
690 	boost::tokenizer<style_separator> tok(text.begin(), text.begin() + length);
691 	for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
692 	{
693 		if (is_style_token(*it))
694 		{
695 			update_style(style, *it);
696 		}
697 		else
698 		{
699 			if (style & styleShadow)
700 			{
701 				_draw_text(s, it->c_str(), it->size(), x + width + 1, y + 1, SDL_MapRGB(s->format, 0x0, 0x0, 0x0), style, utf8);
702 			}
703 			width += _draw_text(s, it->c_str(), it->size(), x + width, y, pixel, style, utf8);
704 		}
705 	}
706 
707 	return width;
708 }
709 
styled_text_width(const std::string & text,size_t length,uint16 style,bool utf8) const710 int font_info::styled_text_width(const std::string& text, size_t length, uint16 style, bool utf8) const
711 {
712 	int width = 0;
713 	boost::tokenizer<style_separator> tok(text.begin(), text.begin() + length);
714 	for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
715 	{
716 		if (is_style_token(*it))
717 		{
718 			update_style(style, *it);
719 		}
720 		else
721 		{
722 			width += _text_width(it->c_str(), it->length(), style, utf8);
723 		}
724 	}
725 
726 	if (style & styleShadow)
727 		return width + 1;
728 	else
729 		return width;
730 }
731 
trunc_styled_text(const std::string & text,int max_width,uint16 style) const732 int font_info::trunc_styled_text(const std::string& text, int max_width, uint16 style) const
733 {
734 	if (style & styleShadow)
735 	{
736 		max_width -= 1;
737 		style &= (~styleShadow);
738 	}
739 
740 	int length = 0;
741 	boost::tokenizer<style_separator> tok(text);
742 	for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
743 	{
744 		if (is_style_token(*it))
745 		{
746 			update_style(style, *it);
747 			length += 2;
748 		}
749 		else
750 		{
751 			int additional_length = _trunc_text(it->c_str(), max_width, style);
752 			max_width -= _text_width(it->c_str(), additional_length, style);
753 			length += additional_length;
754 			if (additional_length < it->size())
755 				return length;
756 		}
757 	}
758 
759 	return length;
760 }
761 
style_at(const std::string & text,std::string::const_iterator pos,uint16 style) const762 std::string font_info::style_at(const std::string& text, std::string::const_iterator pos, uint16 style) const
763 {
764 	boost::tokenizer<style_separator> tok(text.begin(), pos);
765 	for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
766 	{
767 		if (is_style_token(*it))
768 			update_style(style, *it);
769 	}
770 
771 	if (style & styleBold)
772 		return string("|b");
773 	else if (style & styleItalic)
774 		return string("|i");
775 	else
776 		return string();
777 }
778 
draw_text(SDL_Surface * s,const char * text,size_t length,int x,int y,uint32 pixel,uint16 style,bool utf8) const779 int font_info::draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
780 {
781 	if (style & styleShadow)
782 	{
783 		_draw_text(s, text, length, x + 1, y + 1, SDL_MapRGB(s->format, 0x0, 0x0, 0x0), style, utf8);
784 	}
785 
786 	return _draw_text(s, text, length, x, y, pixel, style, utf8);
787 }
788 
trunc_text(const char * text,int max_width,uint16 style) const789 int font_info::trunc_text(const char *text, int max_width, uint16 style) const
790 {
791 	if (style & styleShadow)
792 		return _trunc_text(text, max_width - 1, style);
793 	else
794 		return _trunc_text(text, max_width, style);
795 }
796