| File: | font.c |
| Location: | line 605, column 13 |
| Description: | Null pointer passed as an argument to a 'nonnull' parameter |
| 1 | /* | |||
| 2 | * font.c: Font handling for the DVB On Screen Display | |||
| 3 | * | |||
| 4 | * See the main source file 'vdr.c' for copyright information and | |||
| 5 | * how to reach the author. | |||
| 6 | * | |||
| 7 | * BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya. | |||
| 8 | * | |||
| 9 | * $Id: font.c 3.2 2014/01/07 12:19:45 kls Exp $ | |||
| 10 | */ | |||
| 11 | ||||
| 12 | #include "font.h" | |||
| 13 | #include <ctype.h> | |||
| 14 | #include <fontconfig/fontconfig.h> | |||
| 15 | #ifdef BIDI | |||
| 16 | #include <fribidi.h> | |||
| 17 | #endif | |||
| 18 | #include <ft2build.h> | |||
| 19 | #include FT_FREETYPE_H<freetype.h> | |||
| 20 | #include "config.h" | |||
| 21 | #include "osd.h" | |||
| 22 | #include "tools.h" | |||
| 23 | ||||
| 24 | const char *DefaultFontOsd = "Sans Serif:Bold"; | |||
| 25 | const char *DefaultFontSml = "Sans Serif"; | |||
| 26 | const char *DefaultFontFix = "Courier:Bold"; | |||
| 27 | ||||
| 28 | // --- cFreetypeFont --------------------------------------------------------- | |||
| 29 | ||||
| 30 | #define KERNING_UNKNOWN(-10000) (-10000) | |||
| 31 | ||||
| 32 | struct tKerning { | |||
| 33 | uint prevSym; | |||
| 34 | int kerning; | |||
| 35 | tKerning(uint PrevSym, int Kerning = 0) { prevSym = PrevSym; kerning = Kerning; } | |||
| 36 | }; | |||
| 37 | ||||
| 38 | class cGlyph : public cListObject { | |||
| 39 | private: | |||
| 40 | uint charCode; | |||
| 41 | uchar *bitmap; | |||
| 42 | int advanceX; | |||
| 43 | int advanceY; | |||
| 44 | int left; ///< The bitmap's left bearing expressed in integer pixels. | |||
| 45 | int top; ///< The bitmap's top bearing expressed in integer pixels. | |||
| 46 | int width; ///< The number of pixels per bitmap row. | |||
| 47 | int rows; ///< The number of bitmap rows. | |||
| 48 | int pitch; ///< The pitch's absolute value is the number of bytes taken by one bitmap row, including padding. | |||
| 49 | cVector<tKerning> kerningCache; | |||
| 50 | public: | |||
| 51 | cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData); | |||
| 52 | virtual ~cGlyph(); | |||
| 53 | uint CharCode(void) const { return charCode; } | |||
| 54 | uchar *Bitmap(void) const { return bitmap; } | |||
| 55 | int AdvanceX(void) const { return advanceX; } | |||
| 56 | int AdvanceY(void) const { return advanceY; } | |||
| 57 | int Left(void) const { return left; } | |||
| 58 | int Top(void) const { return top; } | |||
| 59 | int Width(void) const { return width; } | |||
| 60 | int Rows(void) const { return rows; } | |||
| 61 | int Pitch(void) const { return pitch; } | |||
| 62 | int GetKerningCache(uint PrevSym) const; | |||
| 63 | void SetKerningCache(uint PrevSym, int Kerning); | |||
| 64 | }; | |||
| 65 | ||||
| 66 | cGlyph::cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData) | |||
| 67 | { | |||
| 68 | charCode = CharCode; | |||
| 69 | advanceX = GlyphData->advance.x >> 6; | |||
| 70 | advanceY = GlyphData->advance.y >> 6; | |||
| 71 | left = GlyphData->bitmap_left; | |||
| 72 | top = GlyphData->bitmap_top; | |||
| 73 | width = GlyphData->bitmap.width; | |||
| 74 | rows = GlyphData->bitmap.rows; | |||
| 75 | pitch = GlyphData->bitmap.pitch; | |||
| 76 | bitmap = MALLOC(uchar, rows * pitch)(uchar *)malloc(sizeof(uchar) * (rows * pitch)); | |||
| 77 | memcpy(bitmap, GlyphData->bitmap.buffer, rows * pitch); | |||
| 78 | } | |||
| 79 | ||||
| 80 | cGlyph::~cGlyph() | |||
| 81 | { | |||
| 82 | free(bitmap); | |||
| 83 | } | |||
| 84 | ||||
| 85 | int cGlyph::GetKerningCache(uint PrevSym) const | |||
| 86 | { | |||
| 87 | for (int i = kerningCache.Size(); --i > 0; ) { | |||
| 88 | if (kerningCache[i].prevSym == PrevSym) | |||
| 89 | return kerningCache[i].kerning; | |||
| 90 | } | |||
| 91 | return KERNING_UNKNOWN(-10000); | |||
| 92 | } | |||
| 93 | ||||
| 94 | void cGlyph::SetKerningCache(uint PrevSym, int Kerning) | |||
| 95 | { | |||
| 96 | kerningCache.Append(tKerning(PrevSym, Kerning)); | |||
| 97 | } | |||
| 98 | ||||
| 99 | class cFreetypeFont : public cFont { | |||
| 100 | private: | |||
| 101 | cString fontName; | |||
| 102 | int size; | |||
| 103 | int height; | |||
| 104 | int bottom; | |||
| 105 | FT_Library library; ///< Handle to library | |||
| 106 | FT_Face face; ///< Handle to face object | |||
| 107 | mutable cList<cGlyph> glyphCacheMonochrome; | |||
| 108 | mutable cList<cGlyph> glyphCacheAntiAliased; | |||
| 109 | int Bottom(void) const { return bottom; } | |||
| 110 | int Kerning(cGlyph *Glyph, uint PrevSym) const; | |||
| 111 | cGlyph* Glyph(uint CharCode, bool AntiAliased = false) const; | |||
| 112 | public: | |||
| 113 | cFreetypeFont(const char *Name, int CharHeight, int CharWidth = 0); | |||
| 114 | virtual ~cFreetypeFont(); | |||
| 115 | virtual const char *FontName(void) const { return fontName; } | |||
| 116 | virtual int Size(void) const { return size; } | |||
| 117 | virtual int Width(uint c) const; | |||
| 118 | virtual int Width(const char *s) const; | |||
| 119 | virtual int Height(void) const { return height; } | |||
| 120 | virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const; | |||
| 121 | virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const; | |||
| 122 | }; | |||
| 123 | ||||
| 124 | cFreetypeFont::cFreetypeFont(const char *Name, int CharHeight, int CharWidth) | |||
| 125 | { | |||
| 126 | fontName = Name; | |||
| 127 | size = CharHeight; | |||
| 128 | height = 0; | |||
| 129 | bottom = 0; | |||
| 130 | int error = FT_Init_FreeType(&library); | |||
| 131 | if (!error) { | |||
| 132 | error = FT_New_Face(library, Name, 0, &face); | |||
| 133 | if (!error) { | |||
| 134 | if (face->num_fixed_sizes && face->available_sizes) { // fixed font | |||
| 135 | // TODO what exactly does all this mean? | |||
| 136 | height = face->available_sizes->height; | |||
| 137 | for (uint sym ='A'; sym < 'z'; sym++) { // search for descender for fixed font FIXME | |||
| 138 | FT_UInt glyph_index = FT_Get_Char_Index(face, sym); | |||
| 139 | error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT0x0); | |||
| 140 | if (!error) { | |||
| 141 | error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); | |||
| 142 | if (!error) { | |||
| 143 | if (face->glyph->bitmap.rows-face->glyph->bitmap_top > bottom) | |||
| 144 | bottom = face->glyph->bitmap.rows-face->glyph->bitmap_top; | |||
| 145 | } | |||
| 146 | else | |||
| 147 | esyslog("ERROR: FreeType: error %d in FT_Render_Glyph", error)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: error %d in FT_Render_Glyph" , error) : void() ); | |||
| 148 | } | |||
| 149 | else | |||
| 150 | esyslog("ERROR: FreeType: error %d in FT_Load_Glyph", error)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: error %d in FT_Load_Glyph" , error) : void() ); | |||
| 151 | } | |||
| 152 | } | |||
| 153 | else { | |||
| 154 | error = FT_Set_Char_Size(face, // handle to face object | |||
| 155 | CharWidth * 64, // CharWidth in 1/64th of points | |||
| 156 | CharHeight * 64, // CharHeight in 1/64th of points | |||
| 157 | 0, // horizontal device resolution | |||
| 158 | 0); // vertical device resolution | |||
| 159 | if (!error) { | |||
| 160 | height = (face->size->metrics.ascender - face->size->metrics.descender + 63) / 64; | |||
| 161 | bottom = abs((face->size->metrics.descender - 63) / 64); | |||
| 162 | } | |||
| 163 | else | |||
| 164 | esyslog("ERROR: FreeType: error %d during FT_Set_Char_Size (font = %s)\n", error, Name)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: error %d during FT_Set_Char_Size (font = %s)\n" , error, Name) : void() ); | |||
| 165 | } | |||
| 166 | } | |||
| 167 | else | |||
| 168 | esyslog("ERROR: FreeType: load error %d (font = %s)", error, Name)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: load error %d (font = %s)" , error, Name) : void() ); | |||
| 169 | } | |||
| 170 | else | |||
| 171 | esyslog("ERROR: FreeType: initialization error %d (font = %s)", error, Name)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: initialization error %d (font = %s)" , error, Name) : void() ); | |||
| 172 | } | |||
| 173 | ||||
| 174 | cFreetypeFont::~cFreetypeFont() | |||
| 175 | { | |||
| 176 | FT_Done_Face(face); | |||
| 177 | FT_Done_FreeType(library); | |||
| 178 | } | |||
| 179 | ||||
| 180 | int cFreetypeFont::Kerning(cGlyph *Glyph, uint PrevSym) const | |||
| 181 | { | |||
| 182 | int kerning = 0; | |||
| 183 | if (Glyph && PrevSym) { | |||
| 184 | kerning = Glyph->GetKerningCache(PrevSym); | |||
| 185 | if (kerning == KERNING_UNKNOWN(-10000)) { | |||
| 186 | FT_Vector delta; | |||
| 187 | FT_UInt glyph_index = FT_Get_Char_Index(face, Glyph->CharCode()); | |||
| 188 | FT_UInt glyph_index_prev = FT_Get_Char_Index(face, PrevSym); | |||
| 189 | FT_Get_Kerning(face, glyph_index_prev, glyph_index, FT_KERNING_DEFAULT, &delta); | |||
| 190 | kerning = delta.x / 64; | |||
| 191 | Glyph->SetKerningCache(PrevSym, kerning); | |||
| 192 | } | |||
| 193 | } | |||
| 194 | return kerning; | |||
| 195 | } | |||
| 196 | ||||
| 197 | cGlyph* cFreetypeFont::Glyph(uint CharCode, bool AntiAliased) const | |||
| 198 | { | |||
| 199 | // Non-breaking space: | |||
| 200 | if (CharCode == 0xA0) | |||
| 201 | CharCode = 0x20; | |||
| 202 | ||||
| 203 | // Lookup in cache: | |||
| 204 | cList<cGlyph> *glyphCache = AntiAliased ? &glyphCacheAntiAliased : &glyphCacheMonochrome; | |||
| 205 | for (cGlyph *g = glyphCache->First(); g; g = glyphCache->Next(g)) { | |||
| 206 | if (g->CharCode() == CharCode) | |||
| 207 | return g; | |||
| 208 | } | |||
| 209 | ||||
| 210 | FT_UInt glyph_index = FT_Get_Char_Index(face, CharCode); | |||
| 211 | ||||
| 212 | // Load glyph image into the slot (erase previous one): | |||
| 213 | int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT0x0); | |||
| 214 | if (error) | |||
| 215 | esyslog("ERROR: FreeType: error during FT_Load_Glyph")void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: error during FT_Load_Glyph" ) : void() ); | |||
| 216 | else { | |||
| 217 | #if ((FREETYPE_MAJOR2 == 2 && FREETYPE_MINOR5 == 1 && FREETYPE_PATCH2 >= 7) || (FREETYPE_MAJOR2 == 2 && FREETYPE_MINOR5 == 2 && FREETYPE_PATCH2 <= 1))// TODO workaround for bug? which one? | |||
| 218 | if (AntiAliased || CharCode == 32) | |||
| 219 | #else | |||
| 220 | if (AntiAliased) | |||
| 221 | #endif | |||
| 222 | error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); | |||
| 223 | else | |||
| 224 | error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); | |||
| 225 | if (error) | |||
| 226 | esyslog("ERROR: FreeType: error during FT_Render_Glyph %d, %d\n", CharCode, glyph_index)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: FreeType: error during FT_Render_Glyph %d, %d\n" , CharCode, glyph_index) : void() ); | |||
| 227 | else { //new bitmap | |||
| 228 | cGlyph *Glyph = new cGlyph(CharCode, face->glyph); | |||
| 229 | glyphCache->Add(Glyph); | |||
| 230 | return Glyph; | |||
| 231 | } | |||
| 232 | } | |||
| 233 | #define UNKNOWN_GLYPH_INDICATOR'?' '?' | |||
| 234 | if (CharCode != UNKNOWN_GLYPH_INDICATOR'?') | |||
| 235 | return Glyph(UNKNOWN_GLYPH_INDICATOR'?', AntiAliased); | |||
| 236 | return NULL__null; | |||
| 237 | } | |||
| 238 | ||||
| 239 | int cFreetypeFont::Width(uint c) const | |||
| 240 | { | |||
| 241 | cGlyph *g = Glyph(c, Setup.AntiAlias); | |||
| 242 | return g ? g->AdvanceX() : 0; | |||
| 243 | } | |||
| 244 | ||||
| 245 | int cFreetypeFont::Width(const char *s) const | |||
| 246 | { | |||
| 247 | int w = 0; | |||
| 248 | if (s) { | |||
| 249 | #ifdef BIDI | |||
| 250 | cString bs = Bidi(s); | |||
| 251 | s = bs; | |||
| 252 | #endif | |||
| 253 | uint prevSym = 0; | |||
| 254 | while (*s) { | |||
| 255 | int sl = Utf8CharLen(s); | |||
| 256 | uint sym = Utf8CharGet(s, sl); | |||
| 257 | s += sl; | |||
| 258 | cGlyph *g = Glyph(sym, Setup.AntiAlias); | |||
| 259 | if (g) | |||
| 260 | w += g->AdvanceX() + Kerning(g, prevSym); | |||
| 261 | prevSym = sym; | |||
| 262 | } | |||
| 263 | } | |||
| 264 | return w; | |||
| 265 | } | |||
| 266 | ||||
| 267 | #define MAX_BLEND_LEVELS256 256 | |||
| 268 | ||||
| 269 | void cFreetypeFont::DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const | |||
| 270 | { | |||
| 271 | if (s && height) { // checking height to make sure we actually have a valid font | |||
| 272 | #ifdef BIDI | |||
| 273 | cString bs = Bidi(s); | |||
| 274 | s = bs; | |||
| 275 | #endif | |||
| 276 | bool AntiAliased = Setup.AntiAlias && Bitmap->Bpp() >= 8; | |||
| 277 | bool TransparentBackground = ColorBg == clrTransparent; | |||
| 278 | int16_t BlendLevelIndex[MAX_BLEND_LEVELS256]; // tIndex is 8 bit unsigned, so a negative value can be used to mark unused entries | |||
| 279 | if (AntiAliased && !TransparentBackground) | |||
| 280 | memset(BlendLevelIndex, 0xFF, sizeof(BlendLevelIndex)); // initializes the array with negative values | |||
| 281 | tIndex fg = Bitmap->Index(ColorFg); | |||
| 282 | uint prevSym = 0; | |||
| 283 | while (*s) { | |||
| 284 | int sl = Utf8CharLen(s); | |||
| 285 | uint sym = Utf8CharGet(s, sl); | |||
| 286 | s += sl; | |||
| 287 | cGlyph *g = Glyph(sym, AntiAliased); | |||
| 288 | if (!g) | |||
| 289 | continue; | |||
| 290 | int kerning = Kerning(g, prevSym); | |||
| 291 | prevSym = sym; | |||
| 292 | uchar *buffer = g->Bitmap(); | |||
| 293 | int symWidth = g->Width(); | |||
| 294 | if (Width && x + symWidth + g->Left() + kerning - 1 > Width) | |||
| 295 | break; // we don't draw partial characters | |||
| 296 | if (x + symWidth + g->Left() + kerning > 0) { | |||
| 297 | for (int row = 0; row < g->Rows(); row++) { | |||
| 298 | for (int pitch = 0; pitch < g->Pitch(); pitch++) { | |||
| 299 | uchar bt = *(buffer + (row * g->Pitch() + pitch)); | |||
| 300 | if (AntiAliased) { | |||
| 301 | if (bt > 0x00) { | |||
| 302 | int px = x + pitch + g->Left() + kerning; | |||
| 303 | int py = y + row + (height - Bottom() - g->Top()); | |||
| 304 | tColor bg; | |||
| 305 | if (bt == 0xFF) | |||
| 306 | bg = fg; | |||
| 307 | else if (TransparentBackground) | |||
| 308 | bg = Bitmap->Index(Bitmap->Blend(ColorFg, Bitmap->GetColor(px, py), bt)); | |||
| 309 | else if (BlendLevelIndex[bt] >= 0) | |||
| 310 | bg = BlendLevelIndex[bt]; | |||
| 311 | else | |||
| 312 | bg = BlendLevelIndex[bt] = Bitmap->Index(Bitmap->Blend(ColorFg, ColorBg, bt)); | |||
| 313 | Bitmap->SetIndex(px, py, bg); | |||
| 314 | } | |||
| 315 | } | |||
| 316 | else { //monochrome rendering | |||
| 317 | for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) { | |||
| 318 | if (bt & 0x80) | |||
| 319 | Bitmap->SetIndex(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top()), fg); | |||
| 320 | bt <<= 1; | |||
| 321 | } | |||
| 322 | } | |||
| 323 | } | |||
| 324 | } | |||
| 325 | } | |||
| 326 | x += g->AdvanceX() + kerning; | |||
| 327 | if (x > Bitmap->Width() - 1) | |||
| 328 | break; | |||
| 329 | } | |||
| 330 | } | |||
| 331 | } | |||
| 332 | ||||
| 333 | void cFreetypeFont::DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const | |||
| 334 | { | |||
| 335 | if (s && height) { // checking height to make sure we actually have a valid font | |||
| 336 | #ifdef BIDI | |||
| 337 | cString bs = Bidi(s); | |||
| 338 | s = bs; | |||
| 339 | #endif | |||
| 340 | bool AntiAliased = Setup.AntiAlias; | |||
| 341 | uint prevSym = 0; | |||
| 342 | while (*s) { | |||
| 343 | int sl = Utf8CharLen(s); | |||
| 344 | uint sym = Utf8CharGet(s, sl); | |||
| 345 | s += sl; | |||
| 346 | cGlyph *g = Glyph(sym, AntiAliased); | |||
| 347 | if (!g) | |||
| 348 | continue; | |||
| 349 | int kerning = Kerning(g, prevSym); | |||
| 350 | prevSym = sym; | |||
| 351 | uchar *buffer = g->Bitmap(); | |||
| 352 | int symWidth = g->Width(); | |||
| 353 | if (Width && x + symWidth + g->Left() + kerning - 1 > Width) | |||
| 354 | break; // we don't draw partial characters | |||
| 355 | if (x + symWidth + g->Left() + kerning > 0) { | |||
| 356 | for (int row = 0; row < g->Rows(); row++) { | |||
| 357 | for (int pitch = 0; pitch < g->Pitch(); pitch++) { | |||
| 358 | uchar bt = *(buffer + (row * g->Pitch() + pitch)); | |||
| 359 | if (AntiAliased) { | |||
| 360 | if (bt > 0x00) | |||
| 361 | Pixmap->DrawPixel(cPoint(x + pitch + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), AlphaBlend(ColorFg, ColorBg, bt)); | |||
| 362 | } | |||
| 363 | else { //monochrome rendering | |||
| 364 | for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) { | |||
| 365 | if (bt & 0x80) | |||
| 366 | Pixmap->DrawPixel(cPoint(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), ColorFg); | |||
| 367 | bt <<= 1; | |||
| 368 | } | |||
| 369 | } | |||
| 370 | } | |||
| 371 | } | |||
| 372 | } | |||
| 373 | x += g->AdvanceX() + kerning; | |||
| 374 | if (x > Pixmap->DrawPort().Width() - 1) | |||
| 375 | break; | |||
| 376 | } | |||
| 377 | } | |||
| 378 | } | |||
| 379 | ||||
| 380 | // --- cDummyFont ------------------------------------------------------------ | |||
| 381 | ||||
| 382 | // A dummy font, in case there are no fonts installed: | |||
| 383 | ||||
| 384 | class cDummyFont : public cFont { | |||
| 385 | private: | |||
| 386 | int height; | |||
| 387 | public: | |||
| 388 | cDummyFont(int CharHeight) { height = CharHeight; } | |||
| 389 | virtual int Width(uint c) const { return height; } | |||
| 390 | virtual int Width(const char *s) const { return height; } | |||
| 391 | virtual int Height(void) const { return height; } | |||
| 392 | virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {} | |||
| 393 | virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}; | |||
| 394 | }; | |||
| 395 | ||||
| 396 | // --- cFont ----------------------------------------------------------------- | |||
| 397 | ||||
| 398 | cFont *cFont::fonts[eDvbFontSize(fontSml + 1)] = { NULL__null }; | |||
| 399 | ||||
| 400 | void cFont::SetFont(eDvbFont Font, const char *Name, int CharHeight) | |||
| 401 | { | |||
| 402 | delete fonts[Font]; | |||
| 403 | fonts[Font] = CreateFont(Name, constrain(CharHeight, MINFONTSIZE10, MAXFONTSIZE64)); | |||
| 404 | } | |||
| 405 | ||||
| 406 | const cFont *cFont::GetFont(eDvbFont Font) | |||
| 407 | { | |||
| 408 | if (Setup.UseSmallFont == 0 && Font == fontSml) | |||
| 409 | Font = fontOsd; | |||
| 410 | else if (Setup.UseSmallFont == 2) | |||
| 411 | Font = fontSml; | |||
| 412 | if (!fonts[Font]) { | |||
| 413 | switch (Font) { | |||
| 414 | case fontOsd: SetFont(Font, Setup.FontOsd, Setup.FontOsdSize); break; | |||
| 415 | case fontSml: SetFont(Font, Setup.FontSml, min(Setup.FontSmlSize, Setup.FontOsdSize)); break; | |||
| 416 | case fontFix: SetFont(Font, Setup.FontFix, Setup.FontFixSize); break; | |||
| 417 | default: esyslog("ERROR: unknown Font %d (%s %d)", Font, __FUNCTION__, __LINE__)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown Font %d (%s %d)" , Font, __FUNCTION__, 417) : void() ); | |||
| 418 | } | |||
| 419 | } | |||
| 420 | return fonts[Font]; | |||
| 421 | } | |||
| 422 | ||||
| 423 | cFont *cFont::CreateFont(const char *Name, int CharHeight, int CharWidth) | |||
| 424 | { | |||
| 425 | cString fn = GetFontFileName(Name); | |||
| 426 | cFont *f = *fn ? new cFreetypeFont(fn, CharHeight, CharWidth) : NULL__null; | |||
| 427 | if (!f || !f->Height()) | |||
| 428 | f = new cDummyFont(CharHeight); | |||
| 429 | return f; | |||
| 430 | } | |||
| 431 | ||||
| 432 | bool cFont::GetAvailableFontNames(cStringList *FontNames, bool Monospaced) | |||
| 433 | { | |||
| 434 | if (!FontNames->Size()) { | |||
| 435 | FcInit(); | |||
| 436 | FcObjectSet *os = FcObjectSetBuild(FC_FAMILY"family", FC_STYLE"style", NULL__null); | |||
| 437 | FcPattern *pat = FcPatternCreate(); | |||
| 438 | FcPatternAddBool(pat, FC_SCALABLE"scalable", FcTrue1); | |||
| 439 | if (Monospaced) | |||
| 440 | FcPatternAddInteger(pat, FC_SPACING"spacing", FC_MONO100); | |||
| 441 | FcFontSet* fontset = FcFontList(NULL__null, pat, os); | |||
| 442 | for (int i = 0; i < fontset->nfont; i++) { | |||
| 443 | char *s = (char *)FcNameUnparse(fontset->fonts[i]); | |||
| 444 | if (s) { | |||
| 445 | // Strip i18n stuff: | |||
| 446 | char *c = strchr(s, ':'); | |||
| 447 | if (c) { | |||
| 448 | char *p = strchr(c + 1, ','); | |||
| 449 | if (p) | |||
| 450 | *p = 0; | |||
| 451 | } | |||
| 452 | char *p = strchr(s, ','); | |||
| 453 | if (p) { | |||
| 454 | if (c) | |||
| 455 | memmove(p, c, strlen(c) + 1); | |||
| 456 | else | |||
| 457 | *p = 0; | |||
| 458 | } | |||
| 459 | // Make it user presentable: | |||
| 460 | s = strreplace(s, "\\", ""); // '-' is escaped | |||
| 461 | s = strreplace(s, "style=", ""); | |||
| 462 | FontNames->Append(s); // takes ownership of s | |||
| 463 | } | |||
| 464 | } | |||
| 465 | FcFontSetDestroy(fontset); | |||
| 466 | FcPatternDestroy(pat); | |||
| 467 | FcObjectSetDestroy(os); | |||
| 468 | //FcFini(); // older versions of fontconfig are broken - and FcInit() can be called more than once | |||
| 469 | FontNames->Sort(); | |||
| 470 | } | |||
| 471 | return FontNames->Size() > 0; | |||
| 472 | } | |||
| 473 | ||||
| 474 | cString cFont::GetFontFileName(const char *FontName) | |||
| 475 | { | |||
| 476 | cString FontFileName; | |||
| 477 | if (FontName) { | |||
| 478 | char *fn = strdup(FontName); | |||
| 479 | fn = strreplace(fn, ":", ":style="); | |||
| 480 | fn = strreplace(fn, "-", "\\-"); | |||
| 481 | FcInit(); | |||
| 482 | FcPattern *pat = FcNameParse((FcChar8 *)fn); | |||
| 483 | FcPatternAddBool(pat, FC_SCALABLE"scalable", FcTrue1); | |||
| 484 | FcConfigSubstitute(NULL__null, pat, FcMatchPattern); | |||
| 485 | FcDefaultSubstitute(pat); | |||
| 486 | FcResult fresult; | |||
| 487 | FcFontSet *fontset = FcFontSort(NULL__null, pat, FcFalse0, NULL__null, &fresult); | |||
| 488 | if (fontset) { | |||
| 489 | for (int i = 0; i < fontset->nfont; i++) { | |||
| 490 | FcBool scalable; | |||
| 491 | FcPatternGetBool(fontset->fonts[i], FC_SCALABLE"scalable", 0, &scalable); | |||
| 492 | if (scalable) { | |||
| 493 | FcChar8 *s = NULL__null; | |||
| 494 | FcPatternGetString(fontset->fonts[i], FC_FILE"file", 0, &s); | |||
| 495 | FontFileName = (char *)s; | |||
| 496 | break; | |||
| 497 | } | |||
| 498 | } | |||
| 499 | FcFontSetDestroy(fontset); | |||
| 500 | } | |||
| 501 | else | |||
| 502 | esyslog("ERROR: no usable font found for '%s'", FontName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: no usable font found for '%s'" , FontName) : void() ); | |||
| 503 | FcPatternDestroy(pat); | |||
| 504 | free(fn); | |||
| 505 | //FcFini(); // older versions of fontconfig are broken - and FcInit() can be called more than once | |||
| 506 | } | |||
| 507 | return FontFileName; | |||
| 508 | } | |||
| 509 | ||||
| 510 | #ifdef BIDI | |||
| 511 | cString cFont::Bidi(const char *Ltr) | |||
| 512 | { | |||
| 513 | if (!cCharSetConv::SystemCharacterTable()) { // bidi requires UTF-8 | |||
| 514 | fribidi_set_mirroring(true); | |||
| 515 | fribidi_set_reorder_nsm(false); | |||
| 516 | FriBidiCharSet fribidiCharset = FRIBIDI_CHAR_SET_UTF8; | |||
| 517 | int LtrLen = strlen(Ltr); | |||
| 518 | FriBidiCharType Base = FRIBIDI_TYPE_L; | |||
| 519 | FriBidiChar *Logical = MALLOC(FriBidiChar, LtrLen + 1)(FriBidiChar *)malloc(sizeof(FriBidiChar) * (LtrLen + 1)) ; | |||
| 520 | int RtlLen = fribidi_charset_to_unicode(fribidiCharset, const_cast<char *>(Ltr), LtrLen, Logical); | |||
| 521 | FriBidiChar *Visual = MALLOC(FriBidiChar, LtrLen + 1)(FriBidiChar *)malloc(sizeof(FriBidiChar) * (LtrLen + 1)) ; | |||
| 522 | char *Rtl = NULL__null; | |||
| 523 | bool ok = fribidi_log2vis(Logical, RtlLen, &Base, Visual, NULL__null, NULL__null, NULL__null); | |||
| 524 | if (ok) { | |||
| 525 | fribidi_remove_bidi_marks(Visual, RtlLen, NULL__null, NULL__null, NULL__null); | |||
| 526 | Rtl = MALLOC(char, RtlLen * 4 + 1)(char *)malloc(sizeof(char) * (RtlLen * 4 + 1)); | |||
| 527 | fribidi_unicode_to_charset(fribidiCharset, Visual, RtlLen, Rtl); | |||
| 528 | } | |||
| 529 | free(Logical); | |||
| 530 | free(Visual); | |||
| 531 | if (ok) | |||
| 532 | return cString(Rtl, true); | |||
| 533 | } | |||
| 534 | return cString(Ltr); | |||
| 535 | } | |||
| 536 | #endif | |||
| 537 | ||||
| 538 | // --- cTextWrapper ---------------------------------------------------------- | |||
| 539 | ||||
| 540 | cTextWrapper::cTextWrapper(void) | |||
| 541 | { | |||
| 542 | text = eol = NULL__null; | |||
| 543 | lines = 0; | |||
| 544 | lastLine = -1; | |||
| 545 | } | |||
| 546 | ||||
| 547 | cTextWrapper::cTextWrapper(const char *Text, const cFont *Font, int Width) | |||
| 548 | { | |||
| 549 | text = NULL__null; | |||
| 550 | Set(Text, Font, Width); | |||
| 551 | } | |||
| 552 | ||||
| 553 | cTextWrapper::~cTextWrapper() | |||
| 554 | { | |||
| 555 | free(text); | |||
| 556 | } | |||
| 557 | ||||
| 558 | void cTextWrapper::Set(const char *Text, const cFont *Font, int Width) | |||
| 559 | { | |||
| 560 | free(text); | |||
| 561 | text = Text ? strdup(Text) : NULL__null; | |||
| ||||
| 562 | eol = NULL__null; | |||
| 563 | lines = 0; | |||
| 564 | lastLine = -1; | |||
| 565 | if (!text) | |||
| 566 | return; | |||
| 567 | lines = 1; | |||
| 568 | if (Width <= 0) | |||
| 569 | return; | |||
| 570 | ||||
| 571 | char *Blank = NULL__null; | |||
| 572 | char *Delim = NULL__null; | |||
| 573 | int w = 0; | |||
| 574 | ||||
| 575 | stripspace(text); // strips trailing newlines | |||
| 576 | ||||
| 577 | for (char *p = text; *p; ) { | |||
| 578 | int sl = Utf8CharLen(p); | |||
| 579 | uint sym = Utf8CharGet(p, sl); | |||
| 580 | if (sym == '\n') { | |||
| 581 | lines++; | |||
| 582 | w = 0; | |||
| 583 | Blank = Delim = NULL__null; | |||
| 584 | p++; | |||
| 585 | continue; | |||
| 586 | } | |||
| 587 | else if (sl == 1 && isspace(sym)) | |||
| 588 | Blank = p; | |||
| 589 | int cw = Font->Width(sym); | |||
| 590 | if (w + cw > Width) { | |||
| 591 | if (Blank) { | |||
| 592 | *Blank = '\n'; | |||
| 593 | p = Blank; | |||
| 594 | continue; | |||
| 595 | } | |||
| 596 | else if (w > 0) { // there has to be at least one character before the newline | |||
| 597 | // Here's the ugly part, where we don't have any whitespace to | |||
| 598 | // punch in a newline, so we need to make room for it: | |||
| 599 | if (Delim) | |||
| 600 | p = Delim + 1; // let's fall back to the most recent delimiter | |||
| 601 | char *s = MALLOC(char, strlen(text) + 2)(char *)malloc(sizeof(char) * (strlen(text) + 2)); // The additional '\n' plus the terminating '\0' | |||
| 602 | int l = p - text; | |||
| 603 | strncpy(s, text, l); | |||
| 604 | s[l] = '\n'; | |||
| 605 | strcpy(s + l + 1, p); | |||
| ||||
| 606 | free(text); | |||
| 607 | text = s; | |||
| 608 | p = text + l; | |||
| 609 | continue; | |||
| 610 | } | |||
| 611 | } | |||
| 612 | w += cw; | |||
| 613 | if (strchr("-.,:;!?_", *p)) { | |||
| 614 | Delim = p; | |||
| 615 | Blank = NULL__null; | |||
| 616 | } | |||
| 617 | p += sl; | |||
| 618 | } | |||
| 619 | } | |||
| 620 | ||||
| 621 | const char *cTextWrapper::Text(void) | |||
| 622 | { | |||
| 623 | if (eol) { | |||
| 624 | *eol = '\n'; | |||
| 625 | eol = NULL__null; | |||
| 626 | } | |||
| 627 | return text; | |||
| 628 | } | |||
| 629 | ||||
| 630 | const char *cTextWrapper::GetLine(int Line) | |||
| 631 | { | |||
| 632 | char *s = NULL__null; | |||
| 633 | if (Line < lines) { | |||
| 634 | if (eol) { | |||
| 635 | *eol = '\n'; | |||
| 636 | if (Line == lastLine + 1) | |||
| 637 | s = eol + 1; | |||
| 638 | eol = NULL__null; | |||
| 639 | } | |||
| 640 | if (!s) { | |||
| 641 | s = text; | |||
| 642 | for (int i = 0; i < Line; i++) { | |||
| 643 | s = strchr(s, '\n'); | |||
| 644 | if (s) | |||
| 645 | s++; | |||
| 646 | else | |||
| 647 | break; | |||
| 648 | } | |||
| 649 | } | |||
| 650 | if (s) { | |||
| 651 | if ((eol = strchr(s, '\n')) != NULL__null) | |||
| 652 | *eol = 0; | |||
| 653 | } | |||
| 654 | lastLine = Line; | |||
| 655 | } | |||
| 656 | return s; | |||
| 657 | } |