#include "precomp.h" // ============================================================================================================================================================ // Font abstract class + statics to install TT fonts and Bitmap fonts // ============================================================================================================================================================ #include "freetypefont.h" #include #include #include #include #define DO_KERNING // because Keith say so. // local prototypes static const wchar_t *find_break(void *f, const wchar_t *str, int width, int antialias); #define M_PI 3.14159 #define M_2PI (M_PI*2) #define FAUX_BOLD_RATIO 0.1f #define FAUX_ITALIC_DEGREES 12 // This was necessary in Freetype 2.1.3 and bellow, but let us rejoice, they've fixed it //#define NEED_SMALL_KERNING_HACK static int freetype_width(void *data, const wchar_t *str, int len, int fixed, int antialias); /********************************************************************** * * FreeType Lib * **********************************************************************/ int (*FOLDSTRING)( DWORD dwMapFlags, LPCWSTR lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest )=0; FreeTypeFont::FreeTypeFont() { if (!FOLDSTRING) { HMODULE lib = LoadLibraryA("kernel32.dll"); if (lib) { //*(void **)&FOLDSTRING = GetProcAddress(lib, "FoldStringW"); } FreeLibrary(lib); } fontbuffer = NULL; font = NULL; curboldstrength = 2; } int FreeTypeFont::addFontResource2(void *data, int datalen, const wchar_t *name) { optionsitem = NULL; if (FT_Init_FreeType(&flib)) { DebugString("FreeType: Cannot load freetype!\n"); return 0; } last_encoding = -1; facename = name; fontbuffer = (char *)data; fontbufferlen = datalen; if (FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font)) { DebugString("FreeType: Cannot load font!\n"); return 0; } updateCharmap(); return 1; } int FreeTypeFont::addFontResource(OSFILETYPE file, const wchar_t *name) { optionsitem = NULL; if (FT_Init_FreeType(&flib)) { DebugString("FreeType: Cannot load freetype!\n"); return 0; } last_encoding = -1; fontbufferlen = (int)FGETSIZE(file); fontbuffer = (char *)WASABI_API_MEMMGR->sysMalloc(fontbufferlen); FREAD(fontbuffer, fontbufferlen, 1, file); facename = name; if (FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font)) { DebugString("FreeType: Cannot load font!\n"); return 0; } updateCharmap(); return 1; } FreeTypeFont::~FreeTypeFont() { if (fontbuffer) { WASABI_API_MEMMGR->sysFree(fontbuffer); FT_Done_Face(font); FT_Done_FreeType(flib); } } void FreeTypeFont::updateCharmap() { if (optionsitem == NULL) { const GUID options_guid = { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }; optionsitem = WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid); } int i = 0; if (optionsitem) i = optionsitem->getDataAsInt( L"Character mapping", 0); if (i != last_encoding) { FT_Done_Face(font); FT_Done_FreeType(flib); FT_Init_FreeType(&flib); FT_New_Memory_Face(flib, (FT_Byte *)fontbuffer, fontbufferlen, 0, &font); switch (i) { case -1: break; case 0: FT_Select_Charmap(font, FT_ENCODING_UNICODE); break; case 1: FT_Select_Charmap(font, FT_ENCODING_APPLE_ROMAN); break; case 2: FT_Select_Charmap(font, FT_ENCODING_ADOBE_LATIN_1); break; case 3: FT_Select_Charmap(font, FT_ENCODING_ADOBE_STANDARD); break; case 4: FT_Select_Charmap(font, FT_ENCODING_ADOBE_CUSTOM); break; case 5: FT_Select_Charmap(font, FT_ENCODING_ADOBE_EXPERT); break; case 6: FT_Select_Charmap(font, FT_ENCODING_SJIS); break; case 7: FT_Select_Charmap(font, FT_ENCODING_BIG5); break; case 8: FT_Select_Charmap(font, FT_ENCODING_WANSUNG); break; case 9: FT_Select_Charmap(font, FT_ENCODING_JOHAB); break; } } last_encoding = i; } int FreeTypeFont::tweakSize(const wchar_t *face, int size) { if (WCSCASEEQLSAFE(face, L"nonstep")) return size-1; if (WCSCASEEQLSAFE(face, L"04B_08__")) return size+3; if (WCSCASEEQLSAFE(face, L"Blocky")) return size+6; if (WCSCASEEQLSAFE(face, L"04b_03b_")) return size+3; if (WCSCASEEQLSAFE(face, L"04b_09__")) return size+3; if (WCSCASEEQLSAFE(face, L"04b_21")) return size+3; if (WCSCASEEQLSAFE(face, L"Radiosta")) return size+2; if (WCSCASEEQLSAFE(face, L"ETHNOCENTRIC")) return size+3; if (WCSCASEEQLSAFE(face, L"ETHNOCEN")) return size+3; if (WCSCASEEQLSAFE(face, L"pixel")) return size+3; return size; } #define VERTICAL_TPADDING 2 #define VERTICAL_BPADDING 0 #define HORIZONTAL_LPADDING -1 #define HORIZONTAL_RPADDING 1 void FreeTypeFont::prepareCanvas(api_canvas *c, int size, int bold, int opaque, int underline, int italic, COLORREF color, COLORREF bkcolor, const wchar_t *txt, int width, int height) { // Our "size" variable is fun to calculate! //int vRez = GetDeviceCaps(c->getHDC(), LOGPIXELSY); // this needs to be a Canvas method or something. int fsize = tweakSize(facename, size); int nHeight = MulDiv(fsize << 6, 72, 96); FT_Set_Char_Size(font, 0, nHeight, 0, 0); updateCharmap(); font->style_flags = 0; if (bold) { font->style_flags |= FT_STYLE_FLAG_BOLD; curboldstrength = bold;//(bold && c->getTextAntialias()) ? 2 : bold; } if (italic) font->style_flags |= FT_STYLE_FLAG_ITALIC; if (underline) font->underline_thickness = 1; else font->underline_thickness = 0; if (height == -1 || width == -1) { getTextExtent(c, txt, &width, &height, size, bold, underline, italic, 0); } blt = new SkinBitmap(width+1, height, color & 0xffffff); blt->setHasAlpha(1); } void FreeTypeFont::restoreCanvas(api_canvas *c, int x, int y) { unsigned int *bits = (unsigned int *)blt->getBits(); int n = blt->getWidth() * blt->getHeight(); #ifndef NO_MMX if (Blenders::MMX_AVAILABLE()) for (int i = 0; i < n; i++) *bits++ = Blenders::BLEND_MUL_MMX(*bits | 0xff000000, *bits >> 24); else #endif for (int i = 0; i < n; i++) *bits++ = Blenders::BLEND_MUL(*bits | 0xff000000, *bits >> 24); #ifndef NO_MMX Blenders::BLEND_MMX_END(); #endif blt->blit(c, x + HORIZONTAL_LPADDING, y + VERTICAL_TPADDING); delete blt; blt = NULL; } int FreeTypeFont::getAscent() { FT_Glyph glyph; FT_BBox box; FT_Load_Glyph(font, FT_Get_Char_Index(font, 'M'), FT_LOAD_DEFAULT); FT_Get_Glyph(font->glyph, &glyph); FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box); FT_Done_Glyph(glyph); return box.yMax >> 6; } void FreeTypeFont::drawText(int x, int y, const wchar_t *wtxt, int len, COLORREF color, int antialias) { POINT pen = { x << 6, y}; // we start left to right direction = 1; WCHAR *neutral = NULL; WCHAR *rtlstr = NULL; wchar_t *freeme = 0; /* TODO: change this to be AutoChar ucs4(txt, 12000, WC_COMPOSITECHECK); // convert from UTF-16 to 'raw' Unicode However, we have to re-write the LTR part of the code below */ if (FOLDSTRING) { wchar_t *txt = WMALLOC((len+1)); FOLDSTRING(MAP_PRECOMPOSED, wtxt, -1, txt, len+1); freeme=txt; wtxt=txt; } lastchar = 0; for (int i=0 ; *wtxt && igetBits(); int width = blt->getWidth(); int height = blt->getHeight(); x0; FT_BitmapGlyph ftg; int glyph_index = FT_Get_Char_Index(font, c); FT_Vector delta = { 0, 0 }; #ifdef DO_KERNING if (lastchar && FT_HAS_KERNING(font)) { FT_Get_Kerning(font, lastchar, glyph_index, FT_KERNING_DEFAULT, &delta); x0 += delta.x; } #endif int rc = FT_Load_Glyph(font, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); if (rc) return 0; rc = FT_Get_Glyph(font->glyph, (FT_Glyph*)&ftg); if (rc) return 0; if (font->style_flags & FT_STYLE_FLAG_ITALIC) { FT_Matrix mat; // sets up the matrix, 0 degrees double sintheta = sin(0.0); double costheta = cos(0.0); mat.xx = (FT_Fixed)(costheta * (1<<16)); mat.xy = (FT_Fixed)(sintheta * (1<<16)); mat.yx = -mat.xy; mat.yy = mat.xx; // shear the vectors for italic, 10 to 12 deg suggested FT_Fixed f = (FT_Fixed)(tan(M_2PI/(360/FAUX_ITALIC_DEGREES)) * (1<<16)); mat.xy += FT_MulFix(f, mat.xx); mat.yy += FT_MulFix(f, mat.yx); // do the transform FT_Vector v = {0,0}; FT_Vector_Transform(&v, &mat); FT_Glyph_Transform((FT_Glyph)ftg, &mat, &v); } // get the glyph rc = FT_Glyph_To_Bitmap((FT_Glyph*)&ftg, antialias?ft_render_mode_normal:ft_render_mode_mono, NULL, 1); if (rc) { FT_Done_Glyph((FT_Glyph)ftg); return 0; } FT_Bitmap *bmp = &ftg->bitmap; int r = 1; if (font->style_flags & FT_STYLE_FLAG_BOLD) r = MAX((int)((float)(font->glyph->advance.x >> 6) * FAUX_BOLD_RATIO), 2); int ys = MAX(0, y0 - ftg->top); int ye = MIN(height, (int)(y0 + bmp->rows - ftg->top)); int xs = MAX(0, (x0 >> 6) + ftg->left); int xe = MIN(width, (int)((x0 >> 6) + ftg->left + bmp->width)); unsigned char *_bmpbits = bmp->buffer + (ys + ftg->top - y0) * bmp->pitch + (xs - ftg->left - (x0 >> 6)); unsigned int *_linebits = (unsigned int *)(bits + ys * width + xs); if (ftg->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { for (int z = 0; z < r; z++) { unsigned char *bmpbits = _bmpbits; unsigned int *linebits = _linebits + z; for (int y = ys; y < ye; y++) { if (z > 0) { for (int x = xs; x < xe; x++) { if (x != width-z) { *linebits |= overlay((int)(*linebits >> 24), (int)*bmpbits, curboldstrength, r, z) << 24; linebits++; bmpbits++; } else { linebits++; bmpbits++; } } } else { for (int x = xs; x < xe; x++) *linebits++ |= *bmpbits++ << 24; } bmpbits += bmp->pitch - (xe - xs); linebits += width - (xe - xs); } } } else if (ftg->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { for (int z = 0; z < r; z++) { unsigned char *bmpbits = _bmpbits; unsigned int *linebits = _linebits + z; for (int y = ys; y < ye; y++) { for (int x = xs; x < xe; x++) { /* if (y == ys || y == ye-1 || x == xs || x == xe-1) *linebits++ |= 0xFF << 24; else linebits++;*/ int byte = (x-xs)>>3; *linebits++ |= (*(bmpbits + byte) & (1 << (7-((x-xs)-(byte<<3)))) ? 0xFF : 0) << 24; } bmpbits += bmp->pitch; linebits += width - (xe - xs); } } } else { // simply draw rectangles for (int z = 0; z < r; z++) { unsigned char *bmpbits = _bmpbits; unsigned int *linebits = _linebits + z; for (int y = ys; y < ye; y++) { for (int x = xs; x < xe; x++) { if (y == ys || y == ye-1 || x == xs || x == xe-1) *linebits++ |= 0xFF << 24; else linebits++; } bmpbits += bmp->pitch; linebits += width - (xe - xs); } } } FT_Done_Glyph((FT_Glyph)ftg); lastchar = glyph_index; return font->glyph->advance.x + delta.x + ((r - 1) << 6); } void FreeTypeFont::textOut(api_canvas *c, int x, int y, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); y++; x++; prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt); int maxheight = getAscent(); drawText(0, maxheight, txt, (int)wcslen(txt), color, antialias); /* POINT pen = { 0, maxheight }; lastchar = 0; for (; *txt; txt++) { pen.x += drawChar(pen.x, pen.y, *txt, color); }*/ restoreCanvas(c, x + xoffset , y + yoffset); } void FreeTypeFont::textOut2(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); y++; x++; prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h); int maxheight = getAscent(); int width = getTextWidth(c,txt,size,bold,underline,italic,antialias); int xstart = 0; if (align == DT_RIGHT) { xstart = w - width; } else if (align == DT_CENTER) { xstart = (w - width) / 2; } drawText(xstart, maxheight, txt, (int)wcslen(txt), color, antialias); /* POINT pen = { xstart << 6, maxheight }; lastchar = 0; for (; *txt; txt++) { pen.x += drawChar(pen.x, pen.y, *txt, color); }*/ restoreCanvas(c, x + xoffset , y + yoffset); } void FreeTypeFont::textOutEllipsed(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); y++; x++; if (txt == NULL) return; RECT r; r.left = x+xoffset; r.top = y+yoffset; r.right = r.left + w; r.bottom = r.top + h; prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h); int len = ((int)wcslen(txt) + 3); wchar_t *tmp = (wchar_t *)MALLOC(sizeof(wchar_t) * len); wcsncpy(tmp, txt, len); int width, height; width = getTextWidth(c, txt, size, bold, underline, italic, (int)antialias); height = getAscent(); int dddw = getTextWidth(c, L"...", size, bold, underline, italic, antialias); if (width > r.right - r.left) { wchar_t *p = tmp + wcslen(tmp); width = r.right - r.left - dddw; while(p > tmp && getTextWidth(c,tmp,size,bold,underline,italic,antialias) > width) { *p-- = '\0'; } wcscpy(p, L"..."); } drawText(0, height, tmp, (int)wcslen(tmp), color, antialias); /* POINT pen = { 0, height }; lastchar = 0; for (char *p = tmp; *p; p++) { pen.x += drawChar(pen.x, pen.y, *p, color); }*/ FREE(tmp); restoreCanvas(c, r.left, r.top); } static int freetype_width(void *data, const wchar_t *str, int len, int fixed, int antialias) { FT_Face font = (FT_Face)data; int w = 0; int prev, index; const wchar_t *p; int count = 0; for (p = str; *p && count < len;) { FT_Vector delta = { 0, 0 }; FT_BitmapGlyph ftg; index = FT_Get_Char_Index(font, *p); #ifdef DO_KERNING if (w > 0 && FT_HAS_KERNING(font)) { FT_Get_Kerning(font, prev, index, ft_kerning_default, &delta); } #endif int rc = FT_Load_Glyph(font, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); if (rc) { p++; count++; continue; } w += (delta.x + font->glyph->advance.x); prev = index; rc = FT_Get_Glyph(font->glyph, (FT_Glyph*)&ftg); if (rc) { p++; count++; continue; } if (font->style_flags & FT_STYLE_FLAG_ITALIC) { FT_Matrix mat; // sets up the matrix, 0 degrees double sintheta = sin(0.0); double costheta = cos(0.0); mat.xx = (FT_Fixed)(costheta * (1<<16)); mat.xy = (FT_Fixed)(sintheta * (1<<16)); mat.yx = -mat.xy; mat.yy = mat.xx; // shear the vectors for italic, 10 to 12 deg suggested FT_Fixed f = (FT_Fixed)(tan(M_2PI/(360/FAUX_ITALIC_DEGREES)) * (1<<16)); mat.xy += FT_MulFix(f, mat.xx); mat.yy += FT_MulFix(f, mat.yx); // do the transform FT_Vector v = {0,0}; FT_Vector_Transform(&v, &mat); FT_Glyph_Transform((FT_Glyph)ftg, &mat, &v); } // get the glyph rc = FT_Glyph_To_Bitmap((FT_Glyph*)&ftg, antialias?ft_render_mode_normal:ft_render_mode_mono, NULL, 1); if (rc) { FT_Done_Glyph((FT_Glyph)ftg); p++; count++; continue; } FT_Bitmap *bmp = &ftg->bitmap; int ys = MAX(0, ftg->top); int ye = bmp->rows - ftg->top; int xs = MAX(0, ftg->left); int xe = ftg->left + bmp->width; FT_Done_Glyph((FT_Glyph)ftg); int r = 1; if (font->style_flags & FT_STYLE_FLAG_BOLD) { r = MAX((int)((float)(font->glyph->advance.x >> 6) * FAUX_BOLD_RATIO), 2); } w += ((r - 1) << 6); p++; count++; } if (fixed) return w; return (w >> 6); } void FreeTypeFont::textOutWrapped(api_canvas *c, int x, int y, int w, int h, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); y++; x++; prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, w, h); int ascent = getAscent(); int descent = getTextHeight2(c, size, bold, underline, italic, antialias); descent -= ascent; int yoff = ascent; const wchar_t *cur = txt, *next; int length = (int)wcslen(txt); //NO. //for(int yoff = ascent; for(yoff = ascent; yoff < h; yoff += ascent + descent) { next = find_break(font, cur, w, antialias); /* POINT pen = { 0, yoff }; lastchar = 0; for (; cur < next; cur++) { pen.x += drawChar(pen.x, pen.y, *cur, color); }*/ drawText(0, yoff, cur, (int)(next-cur), color, antialias); cur = next; while (cur && *cur && *cur == ' ') cur++; if (*cur == '\r' || *cur == '\n') cur++; if (cur >= txt + length) break; } restoreCanvas(c, x + xoffset , y + yoffset); } void FreeTypeFont::textOutWrappedPathed(api_canvas *c, int x, int y, int w, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); DebugString("writeme -- FreeTypeFont::textOutWrappedPathed...\n"); } void FreeTypeFont::textOutCentered(api_canvas *c, RECT *r, const wchar_t *txt, int size, int bold, int opaque, int underline, int italic, int align, COLORREF color, COLORREF bkcolor, int xoffset, int yoffset, int antialias) { color = RGBTOBGR(color); bkcolor = RGBTOBGR(bkcolor); r->top++; r->left++; RECT rr = *r; rr.left += xoffset; rr.right += xoffset; rr.top += yoffset; rr.bottom += yoffset; prepareCanvas(c, size, bold, opaque, underline, italic, color, bkcolor, txt, rr.right - rr.left, rr.bottom - rr.top); int width, height; height = getAscent(); width = getTextWidth(c, txt, size, bold, underline, italic, (int)antialias); drawText(((rr.right - rr.left - width) / 2), height, txt, (int)wcslen(txt), color, (int)antialias); /* POINT pen = { ((rr.right - rr.left - width) / 2) << 6, height }; lastchar = 0; for (; *txt; txt++) { pen.x += drawChar(pen.x, pen.y, *txt, color); }*/ restoreCanvas(c, rr.left, rr.top); } int FreeTypeFont::getTextWidth(api_canvas *c, const wchar_t *text, int size, int bold, int underline, int italic, int antialiased) { int w; updateCharmap(); getTextExtent(c, text, &w, NULL, size, bold, underline, italic, antialiased); return w; } int FreeTypeFont::getTextHeight(api_canvas *c, const wchar_t *text, int size, int bold, int underline, int italic, int antialiased) { int h; updateCharmap(); getTextExtent(c, text, NULL, &h, size, bold, underline, italic, antialiased); { // calcul for multiline text const wchar_t *p=text; int n=0; while(p && *p!=0) if(*p++=='\n') n++; if(n) h*=(n+1); } return h; } int FreeTypeFont::getTextHeight2(api_canvas *c, int size, int bold, int underline, int italic, int antialiased) { return getTextHeight(c, L"Mg", size, bold, underline, italic, antialiased); } void FreeTypeFont::getTextExtent(api_canvas *c, const wchar_t *txt, int *w, int *h, int size, int bold, int underline, int italic, int antialias) { updateCharmap(); // Our "size" variable is fun to calculate! //int vRez = GetDeviceCaps(c->getHDC(), LOGPIXELSY); // this needs to be a Canvas method or something. int fsize = tweakSize(facename, size); int nHeight = MulDiv(fsize << 6, 72, 96); FT_Set_Char_Size(font, 0, nHeight, 0, 0); font->style_flags = 0; if (bold) font->style_flags |= FT_STYLE_FLAG_BOLD; if (italic) font->style_flags |= FT_STYLE_FLAG_ITALIC; if (underline) font->underline_thickness = 1; else font->underline_thickness = 0; SIZE rsize={0,0}; ASSERT(txt != NULL); if (*txt == 0) { if (w != NULL) *w = 0; if (h != NULL) *h = 0; return; } FT_BBox box; FT_Glyph glyph; int minh = 0, maxh = 0; if (w) *w = freetype_width(font, txt, (int)wcslen(txt), 0, antialias) + HORIZONTAL_RPADDING + HORIZONTAL_LPADDING; if (h) { FT_Load_Glyph(font, FT_Get_Char_Index(font, 'M'), FT_LOAD_DEFAULT); FT_Get_Glyph(font->glyph, &glyph); FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box); maxh = box.yMax; FT_Done_Glyph(glyph); FT_Load_Glyph(font, FT_Get_Char_Index(font, 'g'), FT_LOAD_DEFAULT); FT_Get_Glyph(font->glyph, &glyph); FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &box); minh = box.yMin; FT_Done_Glyph(glyph); *h = ((maxh - minh) >> 6) + VERTICAL_TPADDING + VERTICAL_BPADDING; } } int FreeTypeFont::isBitmap() { return 0; } static const wchar_t *find_break(void *f, const wchar_t *str, int width, int antialias) { const wchar_t *softret, *lastsoft, *hardret; if (freetype_width(f, str, (int)wcslen(str), 0, antialias) <= width) return str + wcslen(str); for(hardret = str; *hardret; hardret ++) if (*hardret == '\r' || *hardret == '\n') break; if (hardret && freetype_width(f, str, (int)(hardret - str), 0, antialias) <= width) { return hardret; } for(softret = str; *softret && !isspace(*softret); softret++) ; if (freetype_width(f, str, (int)(softret - str), 0, antialias) <= width) { do { lastsoft = softret; for(softret = lastsoft+1; *softret && !isspace(*softret); softret++) ; } while (lastsoft && *lastsoft && freetype_width(f, str, (int)(softret - str), 0, antialias) <= width); softret = lastsoft; } else { for(softret = str; *softret; softret++) if (freetype_width(f, str, (int)(softret - str), 0, antialias) > width) break; softret--; } return softret; }