vdr
1.7.27
|
00001 /* 00002 * font.c: Font handling for the DVB On Screen Display 00003 * 00004 * See the main source file 'vdr.c' for copyright information and 00005 * how to reach the author. 00006 * 00007 * BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya. 00008 * 00009 * $Id: font.c 2.10 2012/03/02 10:47:45 kls Exp $ 00010 */ 00011 00012 #include "font.h" 00013 #include <ctype.h> 00014 #include <fontconfig/fontconfig.h> 00015 #ifdef BIDI 00016 #include <fribidi.h> 00017 #endif 00018 #include <ft2build.h> 00019 #include FT_FREETYPE_H 00020 #include "config.h" 00021 #include "osd.h" 00022 #include "tools.h" 00023 00024 const char *DefaultFontOsd = "Sans Serif:Bold"; 00025 const char *DefaultFontSml = "Sans Serif"; 00026 const char *DefaultFontFix = "Courier:Bold"; 00027 00028 // --- cFreetypeFont --------------------------------------------------------- 00029 00030 #define KERNING_UNKNOWN (-10000) 00031 00032 struct tKerning { 00033 uint prevSym; 00034 int kerning; 00035 tKerning(uint PrevSym, int Kerning = 0) { prevSym = PrevSym; kerning = Kerning; } 00036 }; 00037 00038 class cGlyph : public cListObject { 00039 private: 00040 uint charCode; 00041 uchar *bitmap; 00042 int advanceX; 00043 int advanceY; 00044 int left; 00045 int top; 00046 int width; 00047 int rows; 00048 int pitch; 00049 cVector<tKerning> kerningCache; 00050 public: 00051 cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData); 00052 virtual ~cGlyph(); 00053 uint CharCode(void) const { return charCode; } 00054 uchar *Bitmap(void) const { return bitmap; } 00055 int AdvanceX(void) const { return advanceX; } 00056 int AdvanceY(void) const { return advanceY; } 00057 int Left(void) const { return left; } 00058 int Top(void) const { return top; } 00059 int Width(void) const { return width; } 00060 int Rows(void) const { return rows; } 00061 int Pitch(void) const { return pitch; } 00062 int GetKerningCache(uint PrevSym) const; 00063 void SetKerningCache(uint PrevSym, int Kerning); 00064 }; 00065 00066 cGlyph::cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData) 00067 { 00068 charCode = CharCode; 00069 advanceX = GlyphData->advance.x >> 6; 00070 advanceY = GlyphData->advance.y >> 6; 00071 left = GlyphData->bitmap_left; 00072 top = GlyphData->bitmap_top; 00073 width = GlyphData->bitmap.width; 00074 rows = GlyphData->bitmap.rows; 00075 pitch = GlyphData->bitmap.pitch; 00076 bitmap = MALLOC(uchar, rows * pitch); 00077 memcpy(bitmap, GlyphData->bitmap.buffer, rows * pitch); 00078 } 00079 00080 cGlyph::~cGlyph() 00081 { 00082 free(bitmap); 00083 } 00084 00085 int cGlyph::GetKerningCache(uint PrevSym) const 00086 { 00087 for (int i = kerningCache.Size(); --i > 0; ) { 00088 if (kerningCache[i].prevSym == PrevSym) 00089 return kerningCache[i].kerning; 00090 } 00091 return KERNING_UNKNOWN; 00092 } 00093 00094 void cGlyph::SetKerningCache(uint PrevSym, int Kerning) 00095 { 00096 kerningCache.Append(tKerning(PrevSym, Kerning)); 00097 } 00098 00099 class cFreetypeFont : public cFont { 00100 private: 00101 cString fontName; 00102 int size; 00103 int height; 00104 int bottom; 00105 FT_Library library; 00106 FT_Face face; 00107 mutable cList<cGlyph> glyphCacheMonochrome; 00108 mutable cList<cGlyph> glyphCacheAntiAliased; 00109 int Bottom(void) const { return bottom; } 00110 int Kerning(cGlyph *Glyph, uint PrevSym) const; 00111 cGlyph* Glyph(uint CharCode, bool AntiAliased = false) const; 00112 public: 00113 cFreetypeFont(const char *Name, int CharHeight, int CharWidth = 0); 00114 virtual ~cFreetypeFont(); 00115 virtual const char *FontName(void) const { return fontName; } 00116 virtual int Size(void) const { return size; } 00117 virtual int Width(uint c) const; 00118 virtual int Width(const char *s) const; 00119 virtual int Height(void) const { return height; } 00120 virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const; 00121 virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const; 00122 }; 00123 00124 cFreetypeFont::cFreetypeFont(const char *Name, int CharHeight, int CharWidth) 00125 { 00126 fontName = Name; 00127 size = CharHeight; 00128 height = 0; 00129 bottom = 0; 00130 int error = FT_Init_FreeType(&library); 00131 if (!error) { 00132 error = FT_New_Face(library, Name, 0, &face); 00133 if (!error) { 00134 if (face->num_fixed_sizes && face->available_sizes) { // fixed font 00135 // TODO what exactly does all this mean? 00136 height = face->available_sizes->height; 00137 for (uint sym ='A'; sym < 'z'; sym++) { // search for descender for fixed font FIXME 00138 FT_UInt glyph_index = FT_Get_Char_Index(face, sym); 00139 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); 00140 if (!error) { 00141 error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); 00142 if (!error) { 00143 if (face->glyph->bitmap.rows-face->glyph->bitmap_top > bottom) 00144 bottom = face->glyph->bitmap.rows-face->glyph->bitmap_top; 00145 } 00146 else 00147 esyslog("ERROR: FreeType: error %d in FT_Render_Glyph", error); 00148 } 00149 else 00150 esyslog("ERROR: FreeType: error %d in FT_Load_Glyph", error); 00151 } 00152 } 00153 else { 00154 error = FT_Set_Char_Size(face, // handle to face object 00155 CharWidth * 64, // CharWidth in 1/64th of points 00156 CharHeight * 64, // CharHeight in 1/64th of points 00157 0, // horizontal device resolution 00158 0); // vertical device resolution 00159 if (!error) { 00160 height = (face->size->metrics.ascender - face->size->metrics.descender + 63) / 64; 00161 bottom = abs((face->size->metrics.descender - 63) / 64); 00162 } 00163 else 00164 esyslog("ERROR: FreeType: error %d during FT_Set_Char_Size (font = %s)\n", error, Name); 00165 } 00166 } 00167 else 00168 esyslog("ERROR: FreeType: load error %d (font = %s)", error, Name); 00169 } 00170 else 00171 esyslog("ERROR: FreeType: initialization error %d (font = %s)", error, Name); 00172 } 00173 00174 cFreetypeFont::~cFreetypeFont() 00175 { 00176 FT_Done_Face(face); 00177 FT_Done_FreeType(library); 00178 } 00179 00180 int cFreetypeFont::Kerning(cGlyph *Glyph, uint PrevSym) const 00181 { 00182 int kerning = 0; 00183 if (Glyph && PrevSym) { 00184 kerning = Glyph->GetKerningCache(PrevSym); 00185 if (kerning == KERNING_UNKNOWN) { 00186 FT_Vector delta; 00187 FT_UInt glyph_index = FT_Get_Char_Index(face, Glyph->CharCode()); 00188 FT_UInt glyph_index_prev = FT_Get_Char_Index(face, PrevSym); 00189 FT_Get_Kerning(face, glyph_index_prev, glyph_index, FT_KERNING_DEFAULT, &delta); 00190 kerning = delta.x / 64; 00191 Glyph->SetKerningCache(PrevSym, kerning); 00192 } 00193 } 00194 return kerning; 00195 } 00196 00197 cGlyph* cFreetypeFont::Glyph(uint CharCode, bool AntiAliased) const 00198 { 00199 // Non-breaking space: 00200 if (CharCode == 0xA0) 00201 CharCode = 0x20; 00202 00203 // Lookup in cache: 00204 cList<cGlyph> *glyphCache = AntiAliased ? &glyphCacheAntiAliased : &glyphCacheMonochrome; 00205 for (cGlyph *g = glyphCache->First(); g; g = glyphCache->Next(g)) { 00206 if (g->CharCode() == CharCode) 00207 return g; 00208 } 00209 00210 FT_UInt glyph_index = FT_Get_Char_Index(face, CharCode); 00211 00212 // Load glyph image into the slot (erase previous one): 00213 int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); 00214 if (error) 00215 esyslog("ERROR: FreeType: error during FT_Load_Glyph"); 00216 else { 00217 #if ((FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 7) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 2 && FREETYPE_PATCH <= 1))// TODO workaround for bug? which one? 00218 if (AntiAliased || CharCode == 32) 00219 #else 00220 if (AntiAliased) 00221 #endif 00222 error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); 00223 else 00224 error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); 00225 if (error) 00226 esyslog("ERROR: FreeType: error during FT_Render_Glyph %d, %d\n", CharCode, glyph_index); 00227 else { //new bitmap 00228 cGlyph *Glyph = new cGlyph(CharCode, face->glyph); 00229 glyphCache->Add(Glyph); 00230 return Glyph; 00231 } 00232 } 00233 #define UNKNOWN_GLYPH_INDICATOR '?' 00234 if (CharCode != UNKNOWN_GLYPH_INDICATOR) 00235 return Glyph(UNKNOWN_GLYPH_INDICATOR, AntiAliased); 00236 return NULL; 00237 } 00238 00239 int cFreetypeFont::Width(uint c) const 00240 { 00241 cGlyph *g = Glyph(c, Setup.AntiAlias); 00242 return g ? g->AdvanceX() : 0; 00243 } 00244 00245 int cFreetypeFont::Width(const char *s) const 00246 { 00247 int w = 0; 00248 if (s) { 00249 #ifdef BIDI 00250 cString bs = Bidi(s); 00251 s = bs; 00252 #endif 00253 uint prevSym = 0; 00254 while (*s) { 00255 int sl = Utf8CharLen(s); 00256 uint sym = Utf8CharGet(s, sl); 00257 s += sl; 00258 cGlyph *g = Glyph(sym, Setup.AntiAlias); 00259 if (g) 00260 w += g->AdvanceX() + Kerning(g, prevSym); 00261 prevSym = sym; 00262 } 00263 } 00264 return w; 00265 } 00266 00267 #define MAX_BLEND_LEVELS 256 00268 00269 void cFreetypeFont::DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const 00270 { 00271 if (s && height) { // checking height to make sure we actually have a valid font 00272 #ifdef BIDI 00273 cString bs = Bidi(s); 00274 s = bs; 00275 #endif 00276 bool AntiAliased = Setup.AntiAlias && Bitmap->Bpp() >= 8; 00277 bool TransparentBackground = ColorBg == clrTransparent; 00278 int16_t BlendLevelIndex[MAX_BLEND_LEVELS]; // tIndex is 8 bit unsigned, so a negative value can be used to mark unused entries 00279 if (AntiAliased && !TransparentBackground) 00280 memset(BlendLevelIndex, 0xFF, sizeof(BlendLevelIndex)); // initializes the array with negative values 00281 tIndex fg = Bitmap->Index(ColorFg); 00282 uint prevSym = 0; 00283 while (*s) { 00284 int sl = Utf8CharLen(s); 00285 uint sym = Utf8CharGet(s, sl); 00286 s += sl; 00287 cGlyph *g = Glyph(sym, AntiAliased); 00288 if (!g) 00289 continue; 00290 int kerning = Kerning(g, prevSym); 00291 prevSym = sym; 00292 uchar *buffer = g->Bitmap(); 00293 int symWidth = g->Width(); 00294 if (Width && x + symWidth + g->Left() + kerning - 1 > Width) 00295 break; // we don't draw partial characters 00296 if (x + symWidth + g->Left() + kerning > 0) { 00297 for (int row = 0; row < g->Rows(); row++) { 00298 for (int pitch = 0; pitch < g->Pitch(); pitch++) { 00299 uchar bt = *(buffer + (row * g->Pitch() + pitch)); 00300 if (AntiAliased) { 00301 if (bt > 0x00) { 00302 int px = x + pitch + g->Left() + kerning; 00303 int py = y + row + (height - Bottom() - g->Top()); 00304 tColor bg; 00305 if (bt == 0xFF) 00306 bg = fg; 00307 else if (TransparentBackground) 00308 bg = Bitmap->Index(Bitmap->Blend(ColorFg, Bitmap->GetColor(px, py), bt)); 00309 else if (BlendLevelIndex[bt] >= 0) 00310 bg = BlendLevelIndex[bt]; 00311 else 00312 bg = BlendLevelIndex[bt] = Bitmap->Index(Bitmap->Blend(ColorFg, ColorBg, bt)); 00313 Bitmap->SetIndex(px, py, bg); 00314 } 00315 } 00316 else { //monochrome rendering 00317 for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) { 00318 if (bt & 0x80) 00319 Bitmap->SetIndex(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top()), fg); 00320 bt <<= 1; 00321 } 00322 } 00323 } 00324 } 00325 } 00326 x += g->AdvanceX() + kerning; 00327 if (x > Bitmap->Width() - 1) 00328 break; 00329 } 00330 } 00331 } 00332 00333 void cFreetypeFont::DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const 00334 { 00335 if (s && height) { // checking height to make sure we actually have a valid font 00336 #ifdef BIDI 00337 cString bs = Bidi(s); 00338 s = bs; 00339 #endif 00340 bool AntiAliased = Setup.AntiAlias; 00341 uint prevSym = 0; 00342 while (*s) { 00343 int sl = Utf8CharLen(s); 00344 uint sym = Utf8CharGet(s, sl); 00345 s += sl; 00346 cGlyph *g = Glyph(sym, AntiAliased); 00347 if (!g) 00348 continue; 00349 int kerning = Kerning(g, prevSym); 00350 prevSym = sym; 00351 uchar *buffer = g->Bitmap(); 00352 int symWidth = g->Width(); 00353 if (Width && x + symWidth + g->Left() + kerning - 1 > Width) 00354 break; // we don't draw partial characters 00355 if (x + symWidth + g->Left() + kerning > 0) { 00356 for (int row = 0; row < g->Rows(); row++) { 00357 for (int pitch = 0; pitch < g->Pitch(); pitch++) { 00358 uchar bt = *(buffer + (row * g->Pitch() + pitch)); 00359 if (AntiAliased) { 00360 if (bt > 0x00) 00361 Pixmap->DrawPixel(cPoint(x + pitch + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), AlphaBlend(ColorFg, ColorBg, bt)); 00362 } 00363 else { //monochrome rendering 00364 for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) { 00365 if (bt & 0x80) 00366 Pixmap->DrawPixel(cPoint(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), ColorFg); 00367 bt <<= 1; 00368 } 00369 } 00370 } 00371 } 00372 } 00373 x += g->AdvanceX() + kerning; 00374 if (x > Pixmap->DrawPort().Width() - 1) 00375 break; 00376 } 00377 } 00378 } 00379 00380 // --- cDummyFont ------------------------------------------------------------ 00381 00382 // A dummy font, in case there are no fonts installed: 00383 00384 class cDummyFont : public cFont { 00385 public: 00386 virtual int Width(uint c) const { return 10; } 00387 virtual int Width(const char *s) const { return 50; } 00388 virtual int Height(void) const { return 20; } 00389 virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {} 00390 virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}; 00391 }; 00392 00393 // --- cFont ----------------------------------------------------------------- 00394 00395 cFont *cFont::fonts[eDvbFontSize] = { NULL }; 00396 00397 void cFont::SetFont(eDvbFont Font, const char *Name, int CharHeight) 00398 { 00399 cFont *f = CreateFont(Name, constrain(CharHeight, MINFONTSIZE, MAXFONTSIZE)); 00400 if (!f || !f->Height()) 00401 f = new cDummyFont; 00402 delete fonts[Font]; 00403 fonts[Font] = f; 00404 } 00405 00406 const cFont *cFont::GetFont(eDvbFont Font) 00407 { 00408 if (Setup.UseSmallFont == 0 && Font == fontSml) 00409 Font = fontOsd; 00410 else if (Setup.UseSmallFont == 2) 00411 Font = fontSml; 00412 if (!fonts[Font]) { 00413 switch (Font) { 00414 case fontOsd: SetFont(Font, Setup.FontOsd, Setup.FontOsdSize); break; 00415 case fontSml: SetFont(Font, Setup.FontSml, Setup.FontSmlSize); break; 00416 case fontFix: SetFont(Font, Setup.FontFix, Setup.FontFixSize); break; 00417 default: esyslog("ERROR: unknown Font %d (%s %d)", Font, __FUNCTION__, __LINE__); 00418 } 00419 } 00420 return fonts[Font]; 00421 } 00422 00423 cFont *cFont::CreateFont(const char *Name, int CharHeight, int CharWidth) 00424 { 00425 cString fn = GetFontFileName(Name); 00426 if (*fn) 00427 return new cFreetypeFont(fn, CharHeight, CharWidth); 00428 return NULL; 00429 } 00430 00431 bool cFont::GetAvailableFontNames(cStringList *FontNames, bool Monospaced) 00432 { 00433 if (!FontNames->Size()) { 00434 FcInit(); 00435 FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, NULL); 00436 FcPattern *pat = FcPatternCreate(); 00437 FcPatternAddBool(pat, FC_SCALABLE, FcTrue); 00438 if (Monospaced) 00439 FcPatternAddInteger(pat, FC_SPACING, FC_MONO); 00440 FcFontSet* fontset = FcFontList(NULL, pat, os); 00441 for (int i = 0; i < fontset->nfont; i++) { 00442 char *s = (char *)FcNameUnparse(fontset->fonts[i]); 00443 if (s) { 00444 // Strip i18n stuff: 00445 char *c = strchr(s, ':'); 00446 if (c) { 00447 char *p = strchr(c + 1, ','); 00448 if (p) 00449 *p = 0; 00450 } 00451 char *p = strchr(s, ','); 00452 if (p) { 00453 if (c) 00454 memmove(p, c, strlen(c) + 1); 00455 else 00456 *p = 0; 00457 } 00458 // Make it user presentable: 00459 s = strreplace(s, "\\", ""); // '-' is escaped 00460 s = strreplace(s, "style=", ""); 00461 FontNames->Append(s); // takes ownership of s 00462 } 00463 } 00464 FcFontSetDestroy(fontset); 00465 FcPatternDestroy(pat); 00466 FcObjectSetDestroy(os); 00467 //FcFini(); // older versions of fontconfig are broken - and FcInit() can be called more than once 00468 FontNames->Sort(); 00469 } 00470 return FontNames->Size() > 0; 00471 } 00472 00473 cString cFont::GetFontFileName(const char *FontName) 00474 { 00475 cString FontFileName; 00476 if (FontName) { 00477 char *fn = strdup(FontName); 00478 fn = strreplace(fn, ":", ":style="); 00479 fn = strreplace(fn, "-", "\\-"); 00480 FcInit(); 00481 FcPattern *pat = FcNameParse((FcChar8 *)fn); 00482 FcPatternAddBool(pat, FC_SCALABLE, FcTrue); 00483 FcConfigSubstitute(NULL, pat, FcMatchPattern); 00484 FcDefaultSubstitute(pat); 00485 FcFontSet *fontset = FcFontSort(NULL, pat, FcFalse, NULL, NULL); 00486 if (fontset) { 00487 for (int i = 0; i < fontset->nfont; i++) { 00488 FcBool scalable; 00489 FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable); 00490 if (scalable) { 00491 FcChar8 *s = NULL; 00492 FcPatternGetString(fontset->fonts[i], FC_FILE, 0, &s); 00493 FontFileName = (char *)s; 00494 break; 00495 } 00496 } 00497 FcFontSetDestroy(fontset); 00498 } 00499 else 00500 esyslog("ERROR: no usable font found for '%s'", FontName); 00501 FcPatternDestroy(pat); 00502 free(fn); 00503 //FcFini(); // older versions of fontconfig are broken - and FcInit() can be called more than once 00504 } 00505 return FontFileName; 00506 } 00507 00508 #ifdef BIDI 00509 cString cFont::Bidi(const char *Ltr) 00510 { 00511 if (cCharSetConv::SystemCharacterTable()) { // bidi requires UTF-8 00512 fribidi_set_mirroring(true); 00513 fribidi_set_reorder_nsm(false); 00514 FriBidiCharSet fribidiCharset = FRIBIDI_CHAR_SET_UTF8; 00515 int LtrLen = strlen(Ltr); 00516 FriBidiCharType Base = FRIBIDI_TYPE_L; 00517 FriBidiChar *Logical = MALLOC(FriBidiChar, LtrLen + 1) ; 00518 int RtlLen = fribidi_charset_to_unicode(fribidiCharset, const_cast<char *>(Ltr), LtrLen, Logical); 00519 FriBidiChar *Visual = MALLOC(FriBidiChar, LtrLen + 1) ; 00520 char *Rtl = NULL; 00521 bool ok = fribidi_log2vis(Logical, RtlLen, &Base, Visual, NULL, NULL, NULL); 00522 if (ok) { 00523 fribidi_remove_bidi_marks(Visual, RtlLen, NULL, NULL, NULL); 00524 Rtl = MALLOC(char, RtlLen * 4 + 1); 00525 fribidi_unicode_to_charset(fribidiCharset, Visual, RtlLen, Rtl); 00526 } 00527 free(Logical); 00528 free(Visual); 00529 if (ok) 00530 return cString(Rtl, true); 00531 } 00532 return cString(Ltr); 00533 } 00534 #endif 00535 00536 // --- cTextWrapper ---------------------------------------------------------- 00537 00538 cTextWrapper::cTextWrapper(void) 00539 { 00540 text = eol = NULL; 00541 lines = 0; 00542 lastLine = -1; 00543 } 00544 00545 cTextWrapper::cTextWrapper(const char *Text, const cFont *Font, int Width) 00546 { 00547 text = NULL; 00548 Set(Text, Font, Width); 00549 } 00550 00551 cTextWrapper::~cTextWrapper() 00552 { 00553 free(text); 00554 } 00555 00556 void cTextWrapper::Set(const char *Text, const cFont *Font, int Width) 00557 { 00558 free(text); 00559 text = Text ? strdup(Text) : NULL; 00560 eol = NULL; 00561 lines = 0; 00562 lastLine = -1; 00563 if (!text) 00564 return; 00565 lines = 1; 00566 if (Width <= 0) 00567 return; 00568 00569 char *Blank = NULL; 00570 char *Delim = NULL; 00571 int w = 0; 00572 00573 stripspace(text); // strips trailing newlines 00574 00575 for (char *p = text; *p; ) { 00576 int sl = Utf8CharLen(p); 00577 uint sym = Utf8CharGet(p, sl); 00578 if (sym == '\n') { 00579 lines++; 00580 w = 0; 00581 Blank = Delim = NULL; 00582 p++; 00583 continue; 00584 } 00585 else if (sl == 1 && isspace(sym)) 00586 Blank = p; 00587 int cw = Font->Width(sym); 00588 if (w + cw > Width) { 00589 if (Blank) { 00590 *Blank = '\n'; 00591 p = Blank; 00592 continue; 00593 } 00594 else { 00595 // Here's the ugly part, where we don't have any whitespace to 00596 // punch in a newline, so we need to make room for it: 00597 if (Delim) 00598 p = Delim + 1; // let's fall back to the most recent delimiter 00599 char *s = MALLOC(char, strlen(text) + 2); // The additional '\n' plus the terminating '\0' 00600 int l = p - text; 00601 strncpy(s, text, l); 00602 s[l] = '\n'; 00603 strcpy(s + l + 1, p); 00604 free(text); 00605 text = s; 00606 p = text + l; 00607 continue; 00608 } 00609 } 00610 else 00611 w += cw; 00612 if (strchr("-.,:;!?_", *p)) { 00613 Delim = p; 00614 Blank = NULL; 00615 } 00616 p += sl; 00617 } 00618 } 00619 00620 const char *cTextWrapper::Text(void) 00621 { 00622 if (eol) { 00623 *eol = '\n'; 00624 eol = NULL; 00625 } 00626 return text; 00627 } 00628 00629 const char *cTextWrapper::GetLine(int Line) 00630 { 00631 char *s = NULL; 00632 if (Line < lines) { 00633 if (eol) { 00634 *eol = '\n'; 00635 if (Line == lastLine + 1) 00636 s = eol + 1; 00637 eol = NULL; 00638 } 00639 if (!s) { 00640 s = text; 00641 for (int i = 0; i < Line; i++) { 00642 s = strchr(s, '\n'); 00643 if (s) 00644 s++; 00645 else 00646 break; 00647 } 00648 } 00649 if (s) { 00650 if ((eol = strchr(s, '\n')) != NULL) 00651 *eol = 0; 00652 } 00653 lastLine = Line; 00654 } 00655 return s; 00656 }