diff -r a94304ec498e SDL_ttf.c --- a/SDL_ttf.c Wed Feb 13 15:04:41 2019 +0100 +++ b/SDL_ttf.c Wed Feb 20 11:53:09 2019 -0500 @@ -3116,4 +3116,155 @@ return (int)(delta.x >> 6); } +static int TTF_Measure_Internal(TTF_Font *font, + const char *text, const str_type_t str_type, + int width, int *extent) +{ + int x = 0; + int pos_x, pos_y; + int minx = 0, maxx = 0; + int miny = 0, maxy = 0; + Uint8 *utf8_alloc = NULL; + c_glyph *glyph; + size_t textlen; + int skip_first = 1; + FT_UInt prev_index = 0; + FT_Pos prev_delta = 0; + int prev_advance = 0; + int result = 0; + int cw = 0; + + TTF_CHECK_INITIALIZED(-1); + TTF_CHECK_POINTER(font, -1); + TTF_CHECK_POINTER(text, -1); + + /* Convert input string to default encoding UTF-8 */ + if (str_type == STR_TEXT) { + utf8_alloc = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + LATIN1_to_UTF8(text, utf8_alloc); + text = (const char *)utf8_alloc; + } else if (str_type == STR_UNICODE) { + const Uint16 *text16 = (const Uint16 *) text; + utf8_alloc = SDL_stack_alloc(Uint8, UCS2_to_UTF8_len(text16)); + if (utf8_alloc == NULL) { + SDL_OutOfMemory(); + goto failure; + } + UCS2_to_UTF8(text16, utf8_alloc); + text = (const char *)utf8_alloc; + } + + maxy = font->height; + + /* Reset buffer */ + font->pos_len = 0; + + /* Load each character and sum it's bounding box */ + textlen = SDL_strlen(text); + while (textlen > 0) { + Uint32 c = UTF8_getch(&text, &textlen); + FT_UInt idx = get_char_index(font, c); + + if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { + continue; + } + if (Find_GlyphByIndex(font, idx, 0, 0, 0, 0, &glyph, NULL) < 0) { + goto failure; + } + + /* Realloc, if needed */ + if (font->pos_len >= font->pos_max) { + void *saved = font->pos_buf; + font->pos_max *= 2; + font->pos_buf = SDL_realloc(font->pos_buf, font->pos_max * sizeof (font->pos_buf[0])); + if (font->pos_buf == NULL) { + font->pos_buf = saved; + TTF_SetError("Out of memory"); + goto failure; + } + } + + /* Compute positions */ + x += prev_advance; + prev_advance = glyph->advance; + if (font->use_kerning) { + if (prev_index && glyph->index) { + FT_Vector delta; + FT_Get_Kerning(font->face, prev_index, glyph->index, FT_KERNING_UNFITTED, &delta); + x += delta.x; + } + prev_index = glyph->index; + } + /* FT SUBPIXEL : LCD_MODE_LIGHT_SUBPIXEL */ + if (font->render_subpixel) { + x += prev_delta; + /* Increment by prev_glyph->lsb_delta - prev_glyph->rsb_delta; */ + prev_delta = glyph->subpixel.lsb_minus_rsb; + } else { + /* FT KERNING_MODE_SMART: Use `lsb_delta' and `rsb_delta' to improve integer positioning of glyphs */ + if (skip_first) { + skip_first = 0; + } else { + if (prev_delta - glyph->kerning_smart.lsb_delta > 32 ) { + x -= 64; + } else if (prev_delta - glyph->kerning_smart.lsb_delta < -31 ) { + x += 64; + } + } + prev_delta = glyph->kerning_smart.rsb_delta; + x = ((x + 32) & -64); /* ROUND() */ + } + + /* Compute positions where to copy the glyph bitmap */ + pos_x = x; + pos_y = F26Dot6(font->ascent); + + /* Store things for Render_Line() */ + font->pos_buf[font->pos_len].x = pos_x; + font->pos_buf[font->pos_len].y = pos_y; + font->pos_buf[font->pos_len].index = idx; + font->pos_len += 1; + + /* Compute previsionnal global bounding box */ + pos_x = FT_FLOOR(pos_x) + glyph->sz_left; + pos_y = FT_FLOOR(pos_y) - glyph->sz_top; + + minx = SDL_min(minx, pos_x); + maxx = SDL_max(maxx, pos_x + glyph->sz_width); + miny = SDL_min(miny, pos_y); + maxy = SDL_max(maxy, pos_y + glyph->sz_rows); + + cw = SDL_max(maxx, FT_FLOOR(x + prev_advance)) - minx; + if (cw >= width) + break; + ++result; + } + *extent = cw; + if (utf8_alloc) SDL_stack_free(utf8_alloc); + return result; +failure: + *extent = 0; + if (utf8_alloc) SDL_stack_free(utf8_alloc); + return 0; +} + +int TTF_MeasureText(TTF_Font *font, const char *text, int width, int *extent) +{ + return TTF_Measure_Internal(font, text, STR_TEXT, width, extent); +} + +int TTF_MeasureUTF8(TTF_Font *font, const char *text, int width, int *extent) +{ + return TTF_Measure_Internal(font, text, STR_UTF8, width, extent); +} + +int TTF_MeasureUNICODE(TTF_Font *font, const Uint16 *text, int width, int *extent) +{ + return TTF_Measure_Internal(font, (const char *) text, STR_UNICODE, width, extent); +} + /* vi: set ts=4 sw=4 expandtab: */ diff -r a94304ec498e SDL_ttf.h --- a/SDL_ttf.h Wed Feb 13 15:04:41 2019 +0100 +++ b/SDL_ttf.h Wed Feb 20 11:53:09 2019 -0500 @@ -316,6 +316,17 @@ #define TTF_SetError SDL_SetError #define TTF_GetError SDL_GetError +/* Get the measurement string of text without rendering + in: + width - in pixels to measure this text + out: + result - latest position in text inside width + extent - latest calculated width(more or equal to width) +*/ +extern DECLSPEC int SDLCALL TTF_MeasureText(TTF_Font *font, const char *text, int width, int *extent); +extern DECLSPEC int SDLCALL TTF_MeasureUTF8(TTF_Font *font, const char *text, int width, int *extent); +extern DECLSPEC int SDLCALL TTF_MeasureUNICODE(TTF_Font *font, const Uint16 *text, int width, int *extent); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus }