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 | } |