Translations update
[openttd/fttd.git] / src / font.cpp
bloba11bb91ea45ddb953ef9fa48fed3f3834dffc2ba
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file font.cpp Cache for characters from fonts. */
10 #include "stdafx.h"
11 #include "debug.h"
12 #include "font.h"
13 #include "blitter/blitter.h"
14 #include "core/math_func.hpp"
15 #include "string.h"
16 #include "strings_func.h"
17 #include "zoom_type.h"
18 #include "gfx_layout.h"
19 #include "zoom_func.h"
21 #include "table/sprites.h"
22 #include "table/control_codes.h"
24 static const int MAX_FONT_SIZE = 72; ///< Maximum font size.
26 /** Default heights for the different sizes of fonts. */
27 static const int _default_font_height[FS_END] = {10, 6, 18, 10};
28 static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8};
30 #ifdef WITH_FREETYPE
32 /**
33 * Encapsulate accesses to FreeType. This allows us to declare a static
34 * handle below that will be autodestructed on program termination.
36 class TTDFreeTypeHandle {
37 FT_Library library;
39 TTDFreeTypeHandle (const TTDFreeTypeHandle &) DELETED;
41 TTDFreeTypeHandle & operator = (const TTDFreeTypeHandle &) DELETED;
43 public:
44 CONSTEXPR TTDFreeTypeHandle (void) : library (NULL)
48 operator bool (void) const
50 return this->library != NULL;
53 bool init (void)
55 assert (!*this);
56 return FT_Init_FreeType (&this->library) == FT_Err_Ok;
59 void done (void)
61 assert (*this);
62 FT_Done_FreeType (this->library);
63 this->library = NULL;
66 ~TTDFreeTypeHandle()
68 if (*this) this->done();
71 FT_Error new_face (const char *path, FT_Face *face, FT_Long index = 0)
73 assert (*this);
74 return FT_New_Face (this->library, path, index, face);
78 static TTDFreeTypeHandle library;
80 FreeTypeSettings _freetype;
82 static const byte FACE_COLOUR = 1;
83 static const byte SHADOW_COLOUR = 2;
85 #endif /* WITH_FREETYPE */
88 /** Rebuild the glyph width cache of the font. */
89 void FontCache::RebuildWidthCache (void)
91 for (uint i = 0; i != 224; i++) {
92 this->glyph_widths[i] = this->GetGlyphWidth (this->MapCharToGlyph (i + 32));
95 byte widest_digit = 9;
96 byte digit_width = this->glyph_widths['9' - 32];
97 for (byte i = 8; i > 0; i--) {
98 byte w = this->glyph_widths[i + '0' - 32];
99 if (w > digit_width) {
100 widest_digit = i;
101 digit_width = w;
104 this->widest_digit_nonnull = widest_digit;
106 byte w = this->glyph_widths['0' - 32];
107 if (w > digit_width) {
108 widest_digit = 0;
109 digit_width = w;
111 this->widest_digit = widest_digit;
112 this->digit_width = digit_width;
115 /** Reset font metrics of the font. */
116 void FontCache::ResetFontMetrics (void)
118 FontSize fs = this->fs;
119 int height = _default_font_height[fs];
120 int ascender = _default_font_ascender[fs];
121 this->height = ScaleGUITrad (height);
122 this->ascender = ScaleGUITrad (ascender);
123 this->descender = ScaleGUITrad (ascender - height);
124 this->units_per_em = 1;
125 this->RebuildWidthCache();
129 * Create a new font cache.
130 * @param fs The size of the font.
132 FontCache::FontCache (FontSize fs) :
133 #ifdef WITH_FREETYPE
134 missing_sprite (NULL), font_tables(), face (NULL),
135 #endif /* WITH_FREETYPE */
136 fs (fs)
138 memset (this->spriteid_map, 0, sizeof(this->spriteid_map));
139 #ifdef WITH_FREETYPE
140 memset (this->sprite_map, 0, sizeof(this->sprite_map));
141 #endif /* WITH_FREETYPE */
143 this->ResetFontMetrics();
144 this->InitializeUnicodeGlyphMap();
147 /** Clean everything up. */
148 FontCache::~FontCache()
150 #ifdef WITH_FREETYPE
151 this->UnloadFreeTypeFont();
152 #endif /* WITH_FREETYPE */
153 this->ClearGlyphToSpriteMap();
158 * Get height of a character for a given font size.
159 * @param size Font size to get height of
160 * @return Height of characters in the given font (pixels)
162 int GetCharacterHeight(FontSize size)
164 return FontCache::Get(size)->GetHeight();
168 SpriteID FontCache::GetUnicodeGlyph (GlyphID key) const
170 SpriteID *p = this->spriteid_map[GB(key, 8, 8)];
171 return (p == NULL) ? 0 : p[GB(key, 0, 8)];
174 void FontCache::SetUnicodeGlyph (GlyphID key, SpriteID sprite)
176 SpriteID **p = &this->spriteid_map[GB(key, 8, 8)];
177 if (*p == NULL) *p = xcalloct<SpriteID>(256);
178 (*p)[GB(key, 0, 8)] = sprite;
181 void FontCache::InitializeUnicodeGlyphMap (void)
183 static const uint ASCII_LETTERSTART = 32; ///< First printable ASCII letter.
185 /* Font sprites are contiguous and arranged by font size. */
186 static const uint delta = 256 - ASCII_LETTERSTART;
187 assert_compile (SPR_ASCII_SPACE_SMALL == SPR_ASCII_SPACE + delta);
188 assert_compile (SPR_ASCII_SPACE_BIG == SPR_ASCII_SPACE + 2 * delta);
190 /* Clear out existing glyph map if it exists */
191 this->ClearGlyphToSpriteMap();
193 assert_compile (FS_NORMAL == 0);
194 assert_compile (FS_SMALL == 1);
195 assert_compile (FS_LARGE == 2);
196 assert_compile (FS_MONO == 3);
198 /* (this->fs % 3) maps FS_MONO to FS_NORMAL. */
199 SpriteID base = SPR_ASCII_SPACE + (this->fs % 3) * delta - ASCII_LETTERSTART;
201 for (uint i = ASCII_LETTERSTART; i < 256; i++) {
202 SpriteID sprite = base + i;
203 if (!SpriteExists(sprite)) continue;
204 this->SetUnicodeGlyph(i, sprite);
205 this->SetUnicodeGlyph(i + SCC_SPRITE_START, sprite);
208 /* Glyphs to be accessed through an SCC_* enum entry only. */
209 static const byte clear_list[] = {
210 0xAA, // Feminine ordinal indicator / Down arrow
211 0xAC, // Not sign / Tick mark
212 0xAF, // Macron / Right arrow
213 0xB4, // Acute accent / Train symbol
214 0xB5, // Micro sign / Truck symbol
215 0xB6, // Pilcrow sign / Bus symbol
216 0xB7, // Middle dot / Aircraft symbol
217 0xB8, // Cedilla / Ship symbol
218 0xB9, // Superscript 1 / Superscript -1
219 0xBC, // One quarter / Small up arrow
220 0xBD, // One half / Small down arrow
223 for (uint i = 0; i < lengthof(clear_list); i++) {
224 this->SetUnicodeGlyph (clear_list[i], 0);
227 /* Default unicode mapping table for sprite based glyphs.
228 * This table allows us use unicode characters even though the glyphs
229 * don't exist, or are in the wrong place, in the standard sprite
230 * fonts. This is not used for FreeType rendering */
231 static const uint16 translation_map[][2] = {
232 { 0x00A0, 0x20 }, // Non-breaking space / Up arrow
233 { 0x00AD, 0x20 }, // Soft hyphen / X mark
234 { 0x0178, 0x9F }, // Capital letter Y with diaeresis
235 { 0x010D, 0x63 }, // Small letter c with caron
238 for (uint i = 0; i < lengthof(translation_map); i++) {
239 WChar code = translation_map[i][0];
240 byte key = translation_map[i][1];
241 this->SetUnicodeGlyph (code, base + key);
246 * Clear the glyph to sprite mapping.
248 void FontCache::ClearGlyphToSpriteMap (void)
250 for (uint i = 0; i < 256; i++) {
251 free (this->spriteid_map[i]);
252 this->spriteid_map[i] = NULL;
256 SpriteID FontCache::GetGlyphSprite (GlyphID key) const
258 SpriteID sprite = this->GetUnicodeGlyph(key);
259 if (sprite == 0) sprite = this->GetUnicodeGlyph('?');
260 return sprite;
264 #ifdef WITH_FREETYPE
266 /** Get the FreeType settings struct for a given font size. */
267 static FreeTypeSubSetting *GetFreeTypeSettings (FontSize fs)
269 switch (fs) {
270 default: NOT_REACHED();
271 case FS_NORMAL: return &_freetype.medium;
272 case FS_SMALL: return &_freetype.small;
273 case FS_LARGE: return &_freetype.large;
274 case FS_MONO: return &_freetype.mono;
279 * Set the right font names.
280 * @param settings The settings to modify.
281 * @param font_name The new font name.
282 * @param searcher The searcher that says whether we are dealing with a monospaced font.
284 static void SetFontNames (FreeTypeSettings *settings, const char *font_name,
285 const MissingGlyphSearcher *searcher)
287 if (searcher->Monospace()) {
288 bstrcpy (settings->mono.font, font_name);
289 } else {
290 bstrcpy (settings->small.font, font_name);
291 bstrcpy (settings->medium.font, font_name);
292 bstrcpy (settings->large.font, font_name);
297 * Get the font loaded into a Freetype face by using a font-name.
298 * If no appropriate font is found, the function returns an error.
301 #ifdef WIN32
303 /* ========================================================================================
304 * Windows support
305 * ======================================================================================== */
307 #include <vector>
308 #include "core/pointer.h"
309 #include "core/alloc_func.hpp"
310 #include <windows.h>
311 #include <shlobj.h> /* SHGetFolderPath */
312 #include "os/windows/win32.h"
315 * Get the short DOS 8.3 format for paths.
316 * FreeType doesn't support Unicode filenames and Windows' fopen (as used
317 * by FreeType) doesn't support UTF-8 filenames. So we have to convert the
318 * filename into something that isn't UTF-8 but represents the Unicode file
319 * name. This is the short DOS 8.3 format. This does not contain any
320 * characters that fopen doesn't support.
321 * @param long_path the path in system encoding.
322 * @return the short path in ANSI (ASCII).
324 static const char *GetShortPath (const TCHAR *long_path)
326 static char short_path[MAX_PATH];
327 #ifdef UNICODE
328 WCHAR short_path_w[MAX_PATH];
329 GetShortPathName(long_path, short_path_w, lengthof(short_path_w));
330 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), NULL, NULL);
331 #else
332 /* Technically not needed, but do it for consistency. */
333 GetShortPathName(long_path, short_path, lengthof(short_path));
334 #endif
335 return short_path;
338 /* Get the font file to be loaded into Freetype by looping the registry
339 * location where windows lists all installed fonts. Not very nice, will
340 * surely break if the registry path changes, but it works. Much better
341 * solution would be to use CreateFont, and extract the font data from it
342 * by GetFontData. The problem with this is that the font file needs to be
343 * kept in memory then until the font is no longer needed. This could mean
344 * an additional memory usage of 30MB (just for fonts!) when using an eastern
345 * font for all font sizes */
346 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
347 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
348 static FT_Error GetFontByFaceName (const char *font_name, const char *alt_name, FT_Face *face)
350 FT_Error err = FT_Err_Cannot_Open_Resource;
351 HKEY hKey;
352 LONG ret;
353 TCHAR vbuffer[MAX_PATH], dbuffer[256];
354 TCHAR *pathbuf;
355 const char *font_path;
356 uint index;
357 size_t path_len;
359 /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
360 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
361 * to retrieve the windows version, we'll just query both */
362 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
363 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
365 if (ret != ERROR_SUCCESS) {
366 DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
367 return err;
370 /* Convert font name to file system encoding. */
371 TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
373 for (index = 0;; index++) {
374 TCHAR *s;
375 DWORD vbuflen = lengthof(vbuffer);
376 DWORD dbuflen = lengthof(dbuffer);
378 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
379 if (ret != ERROR_SUCCESS) goto registry_no_font_found;
381 /* The font names in the registry are of the following 3 forms:
382 * - ADMUI3.fon
383 * - Book Antiqua Bold (TrueType)
384 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
385 * We will strip the font-type '()' if any and work with the font name
386 * itself, which must match exactly; if...
387 * TTC files, font files which contain more than one font are separated
388 * by '&'. Our best bet will be to do substr match for the fontname
389 * and then let FreeType figure out which index to load */
390 s = _tcschr(vbuffer, _T('('));
391 if (s != NULL) s[-1] = '\0';
393 if (_tcschr(vbuffer, _T('&')) == NULL) {
394 if (_tcsicmp(vbuffer, font_namep) == 0) break;
395 } else {
396 if (_tcsstr(vbuffer, font_namep) != NULL) break;
400 if (!SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
401 DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
402 goto folder_error;
405 /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
406 * contain multiple fonts inside this single file. GetFontData however
407 * returns the whole file, so we need to check each font inside to get the
408 * proper font. */
409 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
410 pathbuf = AllocaM(TCHAR, path_len);
411 _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
413 /* Convert the path into something that FreeType understands. */
414 font_path = GetShortPath(pathbuf);
416 index = 0;
417 do {
418 err = library.new_face (font_path, face, index);
419 if (err != FT_Err_Ok) break;
421 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
422 /* Try english name if font name failed */
423 if (alt_name != NULL && strncasecmp(alt_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
424 err = FT_Err_Cannot_Open_Resource;
426 } while ((FT_Long)++index != (*face)->num_faces);
429 folder_error:
430 registry_no_font_found:
431 free(font_namep);
432 RegCloseKey(hKey);
433 return err;
436 static FT_Error GetFontByFaceName (const char *font_name, FT_Face *face)
438 return GetFontByFaceName (font_name, NULL, face);
442 * Get the English font name for a buffer of font data (see below).
443 * @param buf the buffer with the font data
444 * @param len the length of the buffer
445 * @param fontname the buffer where to store the English name
446 * @return whether the name was found
448 static bool GetEnglishFontName (const byte *buf, size_t len,
449 char (&fontname) [MAX_PATH])
451 if (len < 6) return NULL;
453 if ((buf[0] != 0) || (buf[1] != 0)) return NULL;
455 uint count = (buf[2] << 8) + buf[3];
456 if (count > (len - 6) / 12) return NULL;
458 size_t data_offset = (buf[4] << 8) + buf[5];
459 if (data_offset > len) return NULL;
460 const byte *data = buf + data_offset;
461 size_t data_len = len - data_offset;
463 for (buf += 6; count > 0; count--, buf += 12) {
464 uint platform = (buf[0] << 8) + buf[1];
465 /* ignore encoding (bytes 2 and 3) */
466 uint language = (buf[4] << 8) + buf[5];
467 if ((platform != 1 || language != 0) && // Macintosh English
468 (platform != 3 || language != 0x0409)) { // Microsoft English (US)
469 continue;
472 uint name = (buf[6] << 8) + buf[7];
473 if (name != 1) continue;
475 uint offset = (buf[10] << 8) + buf[11];
476 if (offset > data_len) continue;
478 uint length = (buf[8] << 8) + buf[9];
479 if (length > (data_len - offset)) continue;
481 bstrfmt (fontname, "%.*s", length, data + offset);
482 return true;
485 return false;
489 * Fonts can have localised names and when the system locale is the same as
490 * one of those localised names Windows will always return that localised name
491 * instead of allowing to get the non-localised (English US) name of the font.
492 * This will later on give problems as freetype uses the non-localised name of
493 * the font and we need to compare based on that name.
494 * Windows furthermore DOES NOT have an API to get the non-localised name nor
495 * can we override the system locale. This means that we have to actually read
496 * the font itself to gather the font name we want.
497 * Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
498 * @param logfont the font information to get the english name of.
499 * @param fontname the buffer where to store the English name
501 static void GetEnglishFontName (const ENUMLOGFONTEX *logfont,
502 char (&fontname) [MAX_PATH])
504 bool found = false;
506 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
507 if (font != NULL) {
508 HDC dc = GetDC (NULL);
509 HGDIOBJ oldfont = SelectObject (dc, font);
510 DWORD dw = GetFontData (dc, 'eman', 0, NULL, 0);
511 if (dw != GDI_ERROR) {
512 byte *buf = xmalloct<byte>(dw);
513 if (GetFontData (dc, 'eman', 0, buf, dw) != GDI_ERROR) {
514 found = GetEnglishFontName (buf, dw, fontname);
516 free(buf);
518 SelectObject (dc, oldfont);
519 ReleaseDC (NULL, dc);
520 DeleteObject (font);
523 if (!found) {
524 bstrcpy (fontname, WIDE_TO_MB((const TCHAR*)logfont->elfFullName));
528 struct EFCParam {
529 FreeTypeSettings *settings;
530 LOCALESIGNATURE locale;
531 MissingGlyphSearcher *callback;
532 std::vector <ttd_unique_free_ptr <TCHAR> > fonts;
535 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
537 EFCParam *info = (EFCParam *)lParam;
539 /* Skip duplicates */
540 const TCHAR *fname = (const TCHAR*)logfont->elfFullName;
541 for (uint i = 0; i < info->fonts.size(); i++) {
542 if (_tcscmp (info->fonts[i].get(), fname) == 0) return 1;
544 info->fonts.push_back (ttd_unique_free_ptr<TCHAR> (_tcsdup (fname)));
545 /* Only use TrueType fonts */
546 if (!(type & TRUETYPE_FONTTYPE)) return 1;
547 /* Don't use SYMBOL fonts */
548 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
549 /* Use monospaced fonts when asked for it. */
550 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
552 /* The font has to have at least one of the supported locales to be usable. */
553 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
554 /* On win9x metric->ntmFontSig seems to contain garbage. */
555 FONTSIGNATURE fs;
556 memset(&fs, 0, sizeof(fs));
557 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
558 if (font != NULL) {
559 HDC dc = GetDC(NULL);
560 HGDIOBJ oldfont = SelectObject(dc, font);
561 GetTextCharsetInfo(dc, &fs, 0);
562 SelectObject(dc, oldfont);
563 ReleaseDC(NULL, dc);
564 DeleteObject(font);
566 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
569 char font_name[MAX_PATH];
570 convert_from_fs (fname, font_name, lengthof(font_name));
572 /* Add english name after font name */
573 char english_name [MAX_PATH];
574 GetEnglishFontName (logfont, english_name);
576 /* Check whether we can actually load the font. */
577 bool ft_init = library;
578 bool found = false;
579 FT_Face face;
580 /* Init FreeType if needed. */
581 if ((ft_init || library.init()) && GetFontByFaceName (font_name, english_name, &face) == FT_Err_Ok) {
582 FT_Done_Face(face);
583 found = true;
585 if (!ft_init) {
586 /* Uninit FreeType if we did the init. */
587 library.done();
590 if (!found) return 1;
592 SetFontNames (info->settings, font_name, info->callback);
593 if (info->callback->FindMissingGlyphs()) return 1;
594 DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
595 return 0; // stop enumerating
598 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
600 DEBUG(freetype, 1, "Trying fallback fonts");
601 EFCParam langInfo;
602 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
603 /* Invalid langid or some other mysterious error, can't determine fallback font. */
604 DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
605 return false;
607 langInfo.settings = settings;
608 langInfo.callback = callback;
610 LOGFONT font;
611 /* Enumerate all fonts. */
612 font.lfCharSet = DEFAULT_CHARSET;
613 font.lfFaceName[0] = '\0';
614 font.lfPitchAndFamily = 0;
616 HDC dc = GetDC(NULL);
617 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
618 ReleaseDC(NULL, dc);
619 return ret == 0;
622 #elif defined(__APPLE__) /* end ifdef Win32 */
624 /* ========================================================================================
625 * OSX support
626 * ======================================================================================== */
628 #include "os/macosx/macos.h"
630 static FT_Error GetFontByFaceName (const char *font_name, FT_Face *face)
632 FT_Error err = FT_Err_Cannot_Open_Resource;
634 /* Get font reference from name. */
635 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
636 ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
637 CFRelease(name);
638 if (font == kInvalidFont) return err;
640 /* Get a file system reference for the font. */
641 FSRef ref;
642 OSStatus os_err = -1;
643 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
644 if (MacOSVersionIsAtLeast(10, 5, 0)) {
645 os_err = ATSFontGetFileReference(font, &ref);
646 } else
647 #endif
649 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
650 /* This type was introduced with the 10.5 SDK. */
651 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
652 #define ATSFSSpec FSSpec
653 #endif
654 FSSpec spec;
655 os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
656 if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
657 #endif
660 if (os_err == noErr) {
661 /* Get unix path for file. */
662 UInt8 file_path[PATH_MAX];
663 if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
664 DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
665 err = library.new_face ((const char *)file_path, face);
669 return err;
672 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
674 bool result = false;
676 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
677 if (MacOSVersionIsAtLeast(10, 5, 0)) {
678 /* Determine fallback font using CoreText. This uses the language isocode
679 * to find a suitable font. CoreText is available from 10.5 onwards. */
680 char lang[16];
681 if (strcmp(language_isocode, "zh_TW") == 0) {
682 /* Traditional Chinese */
683 bstrcpy (lang, "zh-Hant");
684 } else if (strcmp(language_isocode, "zh_CN") == 0) {
685 /* Simplified Chinese */
686 bstrcpy (lang, "zh-Hans");
687 } else {
688 /* Just copy the first part of the isocode. */
689 bstrcpy (lang, language_isocode);
690 char *sep = strchr(lang, '_');
691 if (sep != NULL) *sep = '\0';
694 /* Create a font descriptor matching the wanted language and latin (english) glyphs. */
695 CFStringRef lang_codes[2];
696 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
697 lang_codes[1] = CFSTR("en");
698 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
699 CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
700 CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
701 CFRelease(lang_arr);
702 CFRelease(lang_attribs);
703 CFRelease(lang_codes[0]);
705 /* Get array of all font descriptors for the wanted language. */
706 CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
707 CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
708 CFRelease(mandatory_attribs);
709 CFRelease(lang_desc);
711 for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) {
712 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
714 /* Get font traits. */
715 CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
716 CTFontSymbolicTraits symbolic_traits;
717 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
718 CFRelease(traits);
720 /* Skip symbol fonts and vertical fonts. */
721 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
722 /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
723 if (symbolic_traits & kCTFontBoldTrait) continue;
724 /* Select monospaced fonts if asked for. */
725 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
727 /* Get font name. */
728 char name[128];
729 CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
730 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
731 CFRelease(font_name);
733 /* There are some special fonts starting with an '.' and the last
734 * resort font that aren't usable. Skip them. */
735 if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
737 /* Save result. */
738 SetFontNames (settings, name, callback);
739 if (!callback->FindMissingGlyphs()) {
740 DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
741 result = true;
742 break;
745 if (descs != NULL) CFRelease(descs);
746 } else
747 #endif
749 /* Create a font iterator and iterate over all fonts that
750 * are available to the application. */
751 ATSFontIterator itr;
752 ATSFontRef font;
753 ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsDefaultScope, &itr);
754 while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
755 /* Get font name. */
756 char name[128];
757 CFStringRef font_name;
758 ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
759 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
761 bool monospace = IsMonospaceFont(font_name);
762 CFRelease(font_name);
764 /* Select monospaced fonts if asked for. */
765 if (monospace != callback->Monospace()) continue;
767 /* We only want the base font and not bold or italic variants. */
768 if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue;
770 /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
771 if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
773 /* Save result. */
774 SetFontNames (settings, name, callback);
775 if (!callback->FindMissingGlyphs()) {
776 DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
777 result = true;
778 break;
781 ATSFontIteratorRelease(&itr);
784 if (!result) {
785 /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
786 * supports. If we didn't find any other font, just try it, maybe we get lucky. */
787 SetFontNames (settings, "Arial Unicode MS", callback);
788 result = !callback->FindMissingGlyphs();
791 callback->FindMissingGlyphs();
792 return result;
795 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
797 #include <fontconfig/fontconfig.h>
799 /* ========================================================================================
800 * FontConfig (unix) support
801 * ======================================================================================== */
803 static FT_Error GetFontByFaceName (const char *font_name, FT_Face *face)
805 if (!FcInit()) {
806 ShowInfoF("Unable to load font configuration");
807 return FT_Err_Cannot_Open_Resource;
810 /* Split & strip the font's style */
811 char *font_family = xstrdup (font_name);
812 char *font_style = strchr (font_family, ',');
813 if (font_style != NULL) {
814 font_style[0] = '\0';
815 font_style++;
816 while (*font_style == ' ' || *font_style == '\t') font_style++;
819 /* Resolve the name and populate the information structure */
820 FcPattern *pat = FcNameParse ((FcChar8*)font_family);
821 if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
822 FcConfigSubstitute(0, pat, FcMatchPattern);
823 FcDefaultSubstitute(pat);
824 FcFontSet *fs = FcFontSetCreate();
825 FcResult result;
826 FcPattern *match = FcFontMatch (0, pat, &result);
828 FT_Error err = FT_Err_Cannot_Open_Resource;
829 if (fs != NULL && match != NULL) {
830 int i;
831 FcChar8 *family;
832 FcChar8 *style;
833 FcChar8 *file;
834 FcFontSetAdd(fs, match);
836 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
837 /* Try the new filename */
838 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
839 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
840 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
842 /* The correct style? */
843 if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
845 /* Font config takes the best shot, which, if the family name is spelled
846 * wrongly a 'random' font, so check whether the family name is the
847 * same as the supplied name */
848 if (strcasecmp(font_family, (char*)family) == 0) {
849 err = library.new_face ((char *)file, face);
855 free(font_family);
856 FcPatternDestroy(pat);
857 FcFontSetDestroy(fs);
858 FcFini();
860 return err;
863 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
865 if (!FcInit()) return false;
867 bool ret = false;
869 /* Fontconfig doesn't handle full language isocodes, only the part
870 * before the _ of e.g. en_GB is used, so "remove" everything after
871 * the _. */
872 char lang[16];
873 bstrfmt (lang, ":lang=%s", language_isocode);
874 char *split = strchr(lang, '_');
875 if (split != NULL) *split = '\0';
877 /* First create a pattern to match the wanted language. */
878 FcPattern *pat = FcNameParse((FcChar8*)lang);
879 /* We only want to know the filename. */
880 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
881 /* Get the list of filenames matching the wanted language. */
882 FcFontSet *fs = FcFontList(NULL, pat, os);
884 /* We don't need these anymore. */
885 FcObjectSetDestroy(os);
886 FcPatternDestroy(pat);
888 if (fs != NULL) {
889 int best_weight = -1;
890 const char *best_font = NULL;
892 for (int i = 0; i < fs->nfont; i++) {
893 FcPattern *font = fs->fonts[i];
895 FcChar8 *file = NULL;
896 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
897 if (res != FcResultMatch || file == NULL) {
898 continue;
901 /* Get a font with the right spacing .*/
902 int value = 0;
903 FcPatternGetInteger(font, FC_SPACING, 0, &value);
904 if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
906 /* Do not use those that explicitly say they're slanted. */
907 FcPatternGetInteger(font, FC_SLANT, 0, &value);
908 if (value != 0) continue;
910 /* We want the fatter font as they look better at small sizes. */
911 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
912 if (value <= best_weight) continue;
914 SetFontNames (settings, (const char*)file, callback);
916 bool missing = callback->FindMissingGlyphs();
917 DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
919 if (!missing) {
920 best_weight = value;
921 best_font = (const char *)file;
925 if (best_font != NULL) {
926 ret = true;
927 SetFontNames (settings, best_font, callback);
928 InitFreeType(callback->Monospace());
931 /* Clean up the list of filenames. */
932 FcFontSetDestroy(fs);
935 FcFini();
936 return ret;
939 #else /* without WITH_FONTCONFIG */
941 static inline FT_Error GetFontByFaceName (const char *font_name, FT_Face *face)
943 return FT_Err_Cannot_Open_Resource;
946 bool SetFallbackFont (FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
948 return false;
951 #endif /* WITH_FONTCONFIG */
954 * Loads the freetype font.
955 * First type to load the fontname as if it were a path. If that fails,
956 * try to resolve the filename of the font using fontconfig, where the
957 * format is 'font family name' or 'font family name, font style'.
959 void FontCache::LoadFreeTypeFont (void)
961 this->UnloadFreeTypeFont();
962 this->ResetFontMetrics();
964 assert (this->face == NULL);
965 assert (this->font_tables.Length() == 0);
967 FontSize fs = this->fs;
968 FreeTypeSubSetting *settings = GetFreeTypeSettings (fs);
970 if (StrEmpty(settings->font)) return;
972 if (!library) {
973 if (!library.init()) {
974 ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
975 return;
978 DEBUG(freetype, 2, "Initialized");
981 FT_Face face = NULL;
982 FT_Error err = library.new_face (settings->font, &face);
984 if (err != FT_Err_Ok) err = GetFontByFaceName (settings->font, &face);
986 if (err == FT_Err_Ok) {
987 DEBUG(freetype, 2, "Requested '%s', using '%s %s'", settings->font, face->family_name, face->style_name);
989 /* Attempt to select the unicode character map */
990 err = FT_Select_Charmap (face, ft_encoding_unicode);
991 if (err == FT_Err_Ok) goto found_face; // Success
993 if (err == FT_Err_Invalid_CharMap_Handle) {
994 /* Try to pick a different character map instead. We default to
995 * the first map, but platform_id 0 encoding_id 0 should also
996 * be unicode (strange system...) */
997 FT_CharMap found = face->charmaps[0];
998 int i;
1000 for (i = 0; i < face->num_charmaps; i++) {
1001 FT_CharMap charmap = face->charmaps[i];
1002 if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
1003 found = charmap;
1007 if (found != NULL) {
1008 err = FT_Set_Charmap (face, found);
1009 if (err == FT_Err_Ok) goto found_face;
1014 FT_Done_Face(face);
1016 static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
1017 ShowInfoF ("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", settings->font, SIZE_TO_NAME[fs], err);
1018 return;
1020 found_face:
1021 assert(face != NULL);
1022 this->face = face;
1024 int pixels = settings->size;
1025 if (pixels == 0) {
1026 /* Try to determine a good height based on the minimal height recommended by the font. */
1027 pixels = _default_font_height[fs];
1029 TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table (face, ft_sfnt_head);
1030 if (head != NULL) {
1031 /* Font height is minimum height plus the difference between the default
1032 * height for this font size and the small size. */
1033 int diff = _default_font_height[fs] - _default_font_height[FS_SMALL];
1034 pixels = Clamp (min (head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[fs], MAX_FONT_SIZE);
1038 err = FT_Set_Pixel_Sizes (face, 0, pixels);
1039 if (err != FT_Err_Ok) {
1041 /* Find nearest size to that requested */
1042 FT_Bitmap_Size *bs = face->available_sizes;
1043 int i = face->num_fixed_sizes;
1044 if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
1045 int n = bs->height;
1046 FT_Int chosen = 0;
1047 for (; --i; bs++) {
1048 if (abs (pixels - bs->height) >= abs (pixels - n)) continue;
1049 n = bs->height;
1050 chosen = face->num_fixed_sizes - i;
1053 /* Don't use FT_Set_Pixel_Sizes here - it might give us another
1054 * error, even though the size is available (FS#5885). */
1055 err = FT_Select_Size (face, chosen);
1059 if (err == FT_Err_Ok) {
1060 this->units_per_em = this->face->units_per_EM;
1061 this->ascender = this->face->size->metrics.ascender >> 6;
1062 this->descender = this->face->size->metrics.descender >> 6;
1063 this->height = this->ascender - this->descender;
1064 } else {
1065 /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
1066 DEBUG(freetype, 0, "Font size selection failed. Using FontCache defaults.");
1070 /* *Clear the glyph cache. */
1071 void FontCache::ClearGlyphCache (void)
1073 this->missing_sprite = NULL;
1074 for (int i = 0; i < 256; i++) {
1075 if (this->sprite_map[i] == NULL) continue;
1077 for (int j = 0; j < 256; j++) {
1078 free (this->sprite_map[i][j].sprite);
1081 free (this->sprite_map[i]);
1082 this->sprite_map[i] = NULL;
1086 /** Unload the freetype font. */
1087 void FontCache::UnloadFreeTypeFont (void)
1089 if (this->face == NULL) return;
1091 this->ClearGlyphCache();
1093 for (FontTable::iterator iter = this->font_tables.Begin(); iter != this->font_tables.End(); iter++) {
1094 free(iter->second.second);
1097 this->font_tables.Clear();
1099 FT_Done_Face(this->face);
1100 this->face = NULL;
1103 #endif /* WITH_FREETYPE */
1106 /** Reset cached glyphs. */
1107 void FontCache::ClearFontCache (void)
1109 #ifdef WITH_FREETYPE
1110 if (this->face == NULL) {
1111 this->ResetFontMetrics();
1112 } else {
1113 this->ClearGlyphCache();
1114 this->RebuildWidthCache();
1116 #else /* WITH_FREETYPE */
1117 this->ResetFontMetrics();
1118 #endif /* WITH_FREETYPE */
1120 Layouter::ResetFontCache(this->fs);
1124 #ifdef WITH_FREETYPE
1126 static void *AllocateFont(size_t size)
1128 return xmalloc (size);
1131 /* Check if a glyph should be rendered with antialiasing */
1132 static bool GetFontAAState(FontSize size)
1134 /* AA is only supported for 32 bpp */
1135 if (Blitter::get()->screen_depth != 32) return false;
1136 return GetFreeTypeSettings(size)->aa;
1139 static Sprite *MakeBuiltinQuestionMark (void)
1141 /* The font misses the '?' character. Use built-in sprite.
1142 * Note: We cannot use the baseset as this also has to work
1143 * in the bootstrap GUI. */
1144 #define CPSET { 0, 0, 0, 0, 1 }
1145 #define CP___ { 0, 0, 0, 0, 0 }
1146 static Blitter::RawSprite::Pixel builtin_questionmark_data[10 * 8] = {
1147 CP___, CP___, CPSET, CPSET, CPSET, CPSET, CP___, CP___,
1148 CP___, CPSET, CPSET, CP___, CP___, CPSET, CPSET, CP___,
1149 CP___, CP___, CP___, CP___, CP___, CPSET, CPSET, CP___,
1150 CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, CP___,
1151 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
1152 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
1153 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
1154 CP___, CP___, CP___, CP___, CP___, CP___, CP___, CP___,
1155 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
1156 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
1158 #undef CPSET
1159 #undef CP___
1160 static const Blitter::RawSprite builtin_questionmark = {
1161 builtin_questionmark_data,
1162 10, // height
1163 8, // width
1164 0, // x_offs
1165 0, // y_offs
1168 Sprite *spr = Blitter::get()->encode (&builtin_questionmark, true, AllocateFont);
1169 assert (spr != NULL);
1170 return spr;
1173 /** Copy the pixels from a glyph rendered by FreeType into a RawSprite. */
1174 static inline void CopyGlyphPixels (Blitter::RawSprite::Pixel *data,
1175 uint width, FT_GlyphSlot slot, byte colour, bool aa = false)
1177 for (uint y = 0; y < (unsigned int)slot->bitmap.rows; y++) {
1178 for (uint x = 0; x < (unsigned int)slot->bitmap.width; x++) {
1179 byte a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] :
1180 HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8)) ? 0xFF : 0;
1181 if (a > 0) {
1182 data[x + y * width].m = colour;
1183 data[x + y * width].a = a;
1189 const FontCache::GlyphEntry *FontCache::GetGlyphPtr (GlyphID key)
1191 if (key == 0) {
1192 const GlyphEntry *glyph = this->missing_sprite;
1193 if (glyph != NULL) return glyph;
1195 GlyphID question_glyph = this->MapCharToGlyph ('?');
1196 if (question_glyph != 0) {
1197 /* Use '?' for missing characters. */
1198 glyph = this->GetGlyphPtr (question_glyph);
1199 this->missing_sprite = glyph;
1200 return glyph;
1203 /* The font misses the '?' character. We handle this below. */
1206 /* Check for the glyph in our cache */
1207 GlyphEntry *p = this->sprite_map[GB(key, 8, 8)];
1208 if (p != NULL) {
1209 p += GB(key, 0, 8);
1210 if (p->sprite != NULL) return p;
1211 } else {
1212 DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), this->fs);
1213 p = xcalloct<GlyphEntry>(256);
1214 this->sprite_map[GB(key, 8, 8)] = p;
1215 p += GB(key, 0, 8);
1218 DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, this->fs);
1220 if (key == 0) {
1221 /* The font misses the '?' character. Use built-in sprite. */
1222 this->missing_sprite = p;
1223 Sprite *spr = MakeBuiltinQuestionMark();
1224 p->sprite = spr;
1225 p->width = spr->width + (this->fs != FS_NORMAL);
1226 return p;
1229 FT_GlyphSlot slot = this->face->glyph;
1231 bool aa = GetFontAAState(this->fs);
1233 FT_Load_Glyph(this->face, key, FT_LOAD_DEFAULT);
1234 FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
1236 /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
1237 aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
1239 /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
1240 unsigned int width = max(1U, (unsigned int)slot->bitmap.width + (this->fs == FS_NORMAL));
1241 unsigned int height = max(1U, (unsigned int)slot->bitmap.rows + (this->fs == FS_NORMAL));
1243 /* Limit glyph size to prevent overflows later on. */
1244 if (width > 256 || height > 256) usererror("Font glyph is too large");
1246 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it.
1247 * Use a static buffer to prevent repeated allocation/deallocation. */
1248 static ReusableBuffer <Blitter::RawSprite::Pixel> buffer;
1249 Blitter::RawSprite sprite;
1250 sprite.data = buffer.ZeroAllocate (width * height);
1251 sprite.width = width;
1252 sprite.height = height;
1253 sprite.x_offs = slot->bitmap_left;
1254 sprite.y_offs = this->ascender - slot->bitmap_top;
1256 /* Draw shadow for medium size */
1257 if (this->fs == FS_NORMAL && !aa) {
1258 CopyGlyphPixels (sprite.data + width + 1, width, slot, SHADOW_COLOUR);
1261 CopyGlyphPixels (sprite.data, width, slot, FACE_COLOUR, aa);
1263 p->sprite = Blitter::get()->encode (&sprite, true, AllocateFont);
1264 p->width = slot->advance.x >> 6;
1265 return p;
1268 #endif /* WITH_FREETYPE */
1270 const Sprite *FontCache::GetGlyph (GlyphID key)
1272 #ifdef WITH_FREETYPE
1273 if ((this->face != NULL) && ((key & SPRITE_GLYPH) == 0)) {
1274 return this->GetGlyphPtr(key)->sprite;
1276 #endif /* WITH_FREETYPE */
1278 return GetSprite (this->GetGlyphSprite (key), ST_FONT);
1281 bool FontCache::GetDrawGlyphShadow (void) const
1283 #ifdef WITH_FREETYPE
1284 return this->fs == FS_NORMAL && GetFontAAState (FS_NORMAL);
1285 #else /* WITH_FREETYPE */
1286 return false;
1287 #endif /* WITH_FREETYPE */
1290 uint FontCache::GetGlyphWidth (GlyphID key)
1292 #ifdef WITH_FREETYPE
1293 if ((this->face != NULL) && ((key & SPRITE_GLYPH) == 0)) {
1294 return this->GetGlyphPtr(key)->width;
1296 #endif /* WITH_FREETYPE */
1298 SpriteID sprite = this->GetGlyphSprite (key);
1299 return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + ScaleGUITrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
1302 GlyphID FontCache::MapCharToGlyph (WChar key) const
1304 assert(IsPrintable(key));
1306 #ifdef WITH_FREETYPE
1307 if ((this->face != NULL)
1308 && (key < SCC_SPRITE_START || key > SCC_SPRITE_END)) {
1309 return FT_Get_Char_Index (this->face, key);
1311 #endif /* WITH_FREETYPE */
1313 return SPRITE_GLYPH | key;
1316 const void *FontCache::GetFontTable (uint32 tag, size_t &length)
1318 #ifdef WITH_FREETYPE
1319 if (this->face != NULL) {
1320 const FontTable::iterator iter = this->font_tables.Find(tag);
1321 if (iter != this->font_tables.End()) {
1322 length = iter->second.first;
1323 return iter->second.second;
1326 FT_ULong len = 0;
1327 FT_Byte *result = NULL;
1329 FT_Load_Sfnt_Table (this->face, tag, 0, NULL, &len);
1331 if (len > 0) {
1332 result = xmalloct<FT_Byte>(len);
1333 FT_Load_Sfnt_Table (this->face, tag, 0, result, &len);
1335 length = len;
1337 this->font_tables.Insert (tag, SmallPair<size_t, const void *>(length, result));
1338 return result;
1340 #endif /* WITH_FREETYPE */
1342 length = 0;
1343 return NULL;
1346 const char *FontCache::GetFontName (void) const
1348 #ifdef WITH_FREETYPE
1349 if (this->face != NULL) return this->face->family_name;
1350 #endif /* WITH_FREETYPE */
1352 return "sprite";
1355 /** Compute the broadest n-digit value in this font. */
1356 uint64 FontCache::GetBroadestValue (uint n) const
1358 uint d = this->widest_digit;
1360 if (n <= 1) return d;
1362 uint64 val = this->widest_digit_nonnull;
1363 do {
1364 val = 10 * val + d;
1365 } while (--n > 1);
1366 return val;
1370 FontCache FontCache::cache [FS_END] = {
1371 FontCache (FS_NORMAL),
1372 FontCache (FS_SMALL),
1373 FontCache (FS_LARGE),
1374 FontCache (FS_MONO),
1378 #ifdef WITH_FREETYPE
1381 * (Re)initialize the freetype related things, i.e. load the non-sprite fonts.
1382 * @param monospace Whether to initialise the monospace or regular fonts.
1384 void InitFreeType(bool monospace)
1386 for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
1387 if (monospace != (fs == FS_MONO)) continue;
1388 FontCache::Get(fs)->LoadFreeTypeFont();
1392 #endif /* WITH_FREETYPE */