10 #include "../../stdafx.h"
11 #include "../../debug.h"
12 #include "../../blitter/factory.hpp"
13 #include "../../core/alloc_func.hpp"
14 #include "../../core/math_func.hpp"
15 #include "../../fileio_func.h"
16 #include "../../fontdetection.h"
17 #include "../../fontcache.h"
18 #include "../../fontcache/truetypefontcache.h"
19 #include "../../string_func.h"
20 #include "../../strings_func.h"
21 #include "../../zoom_func.h"
24 #include "../../table/control_codes.h"
29 #undef small // Say what, Windows?
36 #include FT_FREETYPE_H
38 extern FT_Library _library;
52 static char short_path[MAX_PATH];
53 wchar_t short_path_w[MAX_PATH];
54 GetShortPathName(long_path, short_path_w,
lengthof(short_path_w));
55 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path,
lengthof(short_path),
nullptr,
nullptr);
67 static const wchar_t *FONT_DIR_NT = L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
70 FT_Error err = FT_Err_Cannot_Open_Resource;
73 wchar_t vbuffer[MAX_PATH], dbuffer[256];
75 const char *font_path;
79 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey);
81 if (ret != ERROR_SUCCESS) {
82 Debug(fontcache, 0,
"Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
87 wchar_t *font_namep = wcsdup(
OTTD2FS(font_name).c_str());
89 for (index = 0;; index++) {
94 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen,
nullptr,
nullptr, (
byte *)dbuffer, &dbuflen);
95 if (ret != ERROR_SUCCESS)
goto registry_no_font_found;
106 s = wcschr(vbuffer, L
'(');
107 if (s !=
nullptr) s[-1] =
'\0';
109 if (wcschr(vbuffer, L
'&') ==
nullptr) {
110 if (wcsicmp(vbuffer, font_namep) == 0)
break;
112 if (wcsstr(vbuffer, font_namep) !=
nullptr)
break;
116 if (!SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_FONTS,
nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
117 Debug(fontcache, 0,
"SHGetFolderPath cannot return fonts directory");
125 path_len = wcslen(vbuffer) + wcslen(dbuffer) + 2;
126 pathbuf =
AllocaM(
wchar_t, path_len);
127 _snwprintf(pathbuf, path_len, L
"%s\\%s", vbuffer, dbuffer);
134 err = FT_New_Face(_library, font_path, index, face);
135 if (err != FT_Err_Ok)
break;
137 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
139 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
140 err = FT_Err_Cannot_Open_Resource;
142 }
while ((FT_Long)++index != (*face)->num_faces);
146 registry_no_font_found:
167 static char font_name[MAX_PATH];
168 const char *ret_font_name =
nullptr;
174 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
176 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
177 if (font ==
nullptr)
goto err1;
180 oldfont = SelectObject(dc, font);
181 dw = GetFontData(dc,
'eman', 0,
nullptr, 0);
182 if (dw == GDI_ERROR)
goto err2;
184 buf = MallocT<byte>(dw);
185 dw = GetFontData(dc,
'eman', 0, buf, dw);
186 if (dw == GDI_ERROR)
goto err3;
188 format = buf[pos++] << 8;
189 format += buf[pos++];
191 count = buf[pos++] << 8;
193 stringOffset = buf[pos++] << 8;
194 stringOffset += buf[pos++];
195 for (uint i = 0; i < count; i++) {
196 platformId = buf[pos++] << 8;
197 platformId += buf[pos++];
198 encodingId = buf[pos++] << 8;
199 encodingId += buf[pos++];
200 languageId = buf[pos++] << 8;
201 languageId += buf[pos++];
202 nameId = buf[pos++] << 8;
203 nameId += buf[pos++];
208 length = buf[pos++] << 8;
209 length += buf[pos++];
210 offset = buf[pos++] << 8;
211 offset += buf[pos++];
214 length = std::min<uint16>(length, MAX_PATH - 1);
215 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
216 font_name[length] =
'\0';
218 if ((platformId == 1 && languageId == 0) ||
219 (platformId == 3 && languageId == 0x0409)) {
220 ret_font_name = font_name;
228 SelectObject(dc, oldfont);
229 ReleaseDC(
nullptr, dc);
232 return ret_font_name ==
nullptr ?
FS2OTTD((
const wchar_t *)logfont->elfFullName) : std::string(ret_font_name);
243 FontList() : fonts(
nullptr), items(0), capacity(0) { };
246 if (this->fonts ==
nullptr)
return;
248 for (uint i = 0; i < this->items; i++) {
249 free(this->fonts[i]);
255 bool Add(
const wchar_t *font) {
256 for (uint i = 0; i < this->items; i++) {
257 if (wcscmp(this->fonts[i], font) == 0)
return false;
260 if (this->items == this->capacity) {
261 this->capacity += 10;
262 this->fonts =
ReallocT(this->fonts, this->capacity);
265 this->fonts[this->items++] = wcsdup(font);
273 LOCALESIGNATURE locale;
278 static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
283 if (!info->fonts.Add((
const wchar_t *)logfont->elfFullName))
return 1;
285 if (!(type & TRUETYPE_FONTTYPE))
return 1;
287 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
289 if (info->callback->
Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
292 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
295 memset(&fs, 0,
sizeof(fs));
296 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
297 if (font !=
nullptr) {
298 HDC dc = GetDC(
nullptr);
299 HGDIOBJ oldfont = SelectObject(dc, font);
300 GetTextCharsetInfo(dc, &fs, 0);
301 SelectObject(dc, oldfont);
302 ReleaseDC(
nullptr, dc);
305 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0)
return 1;
308 char font_name[MAX_PATH];
314 strecpy(font_name + strlen(font_name) + 1, english_name.c_str(),
lastof(font_name));
317 bool ft_init = _library !=
nullptr;
321 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) &&
GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
327 FT_Done_FreeType(_library);
331 if (!found)
return 1;
333 const char *english_name = font_name;
336 info->callback->
SetFontNames(info->settings, font_name, &logfont->elfLogFont);
338 Debug(fontcache, 1,
"Fallback font: {} ({})", font_name, english_name);
344 Debug(fontcache, 1,
"Trying fallback fonts");
346 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale,
sizeof(langInfo.locale) /
sizeof(
wchar_t)) == 0) {
348 Debug(fontcache, 1,
"Can't get locale info for fallback font (langid=0x{:x})", winlangid);
352 langInfo.callback = callback;
356 font.lfCharSet = DEFAULT_CHARSET;
357 font.lfFaceName[0] =
'\0';
358 font.lfPitchAndFamily = 0;
360 HDC dc = GetDC(
nullptr);
361 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
362 ReleaseDC(
nullptr, dc);
367 #ifndef ANTIALIASED_QUALITY
368 #define ANTIALIASED_QUALITY 4
379 this->
dc = CreateCompatibleDC(
nullptr);
380 this->SetFontSize(
fs, pixels);
383 Win32FontCache::~Win32FontCache()
387 DeleteObject(this->font);
390 void Win32FontCache::SetFontSize(
FontSize fs,
int pixels)
394 int scaled_height =
ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
395 pixels = scaled_height;
397 HFONT temp = CreateFontIndirect(&this->logfont);
398 if (temp !=
nullptr) {
399 HGDIOBJ old = SelectObject(this->
dc, temp);
401 UINT size = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
402 LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)
AllocaM(BYTE, size);
403 GetOutlineTextMetrics(this->
dc, size, otm);
411 SelectObject(
dc, old);
420 this->logfont.lfHeight = -pixels;
421 this->logfont.lfWidth = 0;
422 this->logfont.lfOutPrecision = ANTIALIASED_QUALITY;
424 if (this->font !=
nullptr) {
426 DeleteObject(this->font);
428 this->font = CreateFontIndirect(&this->logfont);
429 this->
old_font = SelectObject(this->
dc, this->font);
432 UINT otmSize = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
433 POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)
AllocaM(BYTE, otmSize);
434 GetOutlineTextMetrics(this->
dc, otmSize, otm);
437 this->
ascender = otm->otmTextMetrics.tmAscent;
438 this->
descender = otm->otmTextMetrics.tmDescent;
440 this->
glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
441 this->
glyph_size.cy = otm->otmTextMetrics.tmHeight;
443 this->
fontname =
FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFaceName));
445 Debug(fontcache, 2,
"Loaded font '{}' with size {}", this->
fontname, pixels);
454 if (this->font !=
nullptr) this->SetFontSize(this->fs, this->
req_size);
459 const Sprite *Win32FontCache::InternalGetGlyph(
GlyphID key,
bool aa)
462 MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
466 byte *bmp =
AllocaM(
byte, size);
467 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
469 if (size == GDI_ERROR) {
475 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0,
nullptr, &mat);
476 if (size == GDI_ERROR)
usererror(
"Unable to render font glyph");
478 GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
483 uint width = std::max(1U, (uint)gm.gmBlackBoxX + shadow);
484 uint
height = std::max(1U, (uint)gm.gmBlackBoxY + shadow);
494 sprite.
width = width;
496 sprite.
x_offs = gm.gmptGlyphOrigin.x;
505 uint pitch =
Align(aa ? gm.gmBlackBoxX : std::max((gm.gmBlackBoxX + 7u) / 8u, 1u), 4);
509 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
510 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
511 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
512 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
513 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
519 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
520 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
521 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
522 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
523 sprite.
data[x + y * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
529 GlyphEntry new_glyph;
531 new_glyph.
width = gm.gmCellIncX;
533 this->SetGlyphPtr(key, &new_glyph);
535 return new_glyph.sprite;
540 assert(IsPrintable(key));
542 if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
548 if (key >= 0x010000U) {
549 chars[0] = (wchar_t)(((key - 0x010000U) >> 10) + 0xD800);
550 chars[1] = (wchar_t)(((key - 0x010000U) & 0x3FF) + 0xDC00);
552 chars[0] = (wchar_t)(key & 0xFFFF);
555 WORD glyphs[2] = { 0, 0 };
556 GetGlyphIndicesW(this->
dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
558 return glyphs[0] != 0xFFFF ? glyphs[0] : 0;
561 const void *Win32FontCache::InternalGetFontTable(uint32 tag,
size_t &length)
563 DWORD len = GetFontData(this->
dc, tag, 0,
nullptr, 0);
565 void *result =
nullptr;
566 if (len != GDI_ERROR && len > 0) {
567 result = MallocT<BYTE>(len);
568 GetFontData(this->
dc, tag, 0, result, len);
588 const char *font_name =
settings->font.c_str();
591 logfont.lfPitchAndFamily = fs ==
FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
592 logfont.lfCharSet = DEFAULT_CHARSET;
593 logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
594 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
596 if (
settings->os_handle !=
nullptr) {
597 logfont = *(
const LOGFONT *)
settings->os_handle;
598 }
else if (strchr(font_name,
'.') !=
nullptr) {
601 wchar_t fontPath[MAX_PATH] = {};
609 if (!full_font.empty()) {
614 if (fontPath[0] != 0) {
615 if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
619 typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
620 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetProcAddress(
"GetFontResourceInfoW");
622 if (GetFontResourceInfo !=
nullptr) {
625 if (GetFontResourceInfo(fontPath, &len,
nullptr, 2) && len >=
sizeof(LOGFONT)) {
626 LOGFONT *buf = (LOGFONT *)
AllocaM(
byte, len);
627 if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
634 if (logfont.lfFaceName[0] == 0) {
635 wchar_t fname[_MAX_FNAME];
636 _wsplitpath(fontPath,
nullptr,
nullptr, fname,
nullptr);
638 wcsncpy_s(logfont.lfFaceName,
lengthof(logfont.lfFaceName), fname, _TRUNCATE);
639 logfont.lfWeight = strcasestr(font_name,
" bold") !=
nullptr || strcasestr(font_name,
"-bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
642 ShowInfoF(
"Unable to load file '%s' for %s font, using default windows font selection instead", font_name, FontSizeToName(fs));
647 if (logfont.lfFaceName[0] == 0) {
648 logfont.lfWeight = strcasestr(font_name,
" bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
652 HFONT font = CreateFontIndirect(&logfont);
653 if (font ==
nullptr) {
654 ShowInfoF(
"Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());