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);
384 Win32FontCache::~Win32FontCache()
388 DeleteObject(this->font);
391 void Win32FontCache::SetFontSize(
FontSize fs,
int pixels)
395 int scaled_height =
ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
396 pixels = scaled_height;
398 HFONT temp = CreateFontIndirect(&this->logfont);
399 if (temp !=
nullptr) {
400 HGDIOBJ old = SelectObject(this->
dc, temp);
402 UINT size = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
403 LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)
AllocaM(BYTE, size);
404 GetOutlineTextMetrics(this->
dc, size, otm);
412 SelectObject(
dc, old);
421 this->logfont.lfHeight = -pixels;
422 this->logfont.lfWidth = 0;
423 this->logfont.lfOutPrecision = ANTIALIASED_QUALITY;
425 if (this->font !=
nullptr) {
427 DeleteObject(this->font);
429 this->font = CreateFontIndirect(&this->logfont);
430 this->
old_font = SelectObject(this->
dc, this->font);
433 UINT otmSize = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
434 POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)
AllocaM(BYTE, otmSize);
435 GetOutlineTextMetrics(this->
dc, otmSize, otm);
438 this->
ascender = otm->otmTextMetrics.tmAscent;
439 this->
descender = otm->otmTextMetrics.tmDescent;
441 this->
glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
442 this->
glyph_size.cy = otm->otmTextMetrics.tmHeight;
444 Debug(fontcache, 2,
"Loaded font '{}' with size {}",
FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels);
453 if (this->font !=
nullptr) this->SetFontSize(this->fs, this->
req_size);
458 const Sprite *Win32FontCache::InternalGetGlyph(
GlyphID key,
bool aa)
461 MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
465 byte *bmp =
AllocaM(
byte, size);
466 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
468 if (size == GDI_ERROR) {
474 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0,
nullptr, &mat);
475 if (size == GDI_ERROR)
usererror(
"Unable to render font glyph");
477 GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
482 uint width = std::max(1U, (uint)gm.gmBlackBoxX + shadow);
483 uint
height = std::max(1U, (uint)gm.gmBlackBoxY + shadow);
493 sprite.
width = width;
495 sprite.
x_offs = gm.gmptGlyphOrigin.x;
504 uint pitch =
Align(aa ? gm.gmBlackBoxX : std::max((gm.gmBlackBoxX + 7u) / 8u, 1u), 4);
508 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
509 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
510 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
511 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
512 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
518 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
519 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
520 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
521 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
522 sprite.
data[x + y * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
528 GlyphEntry new_glyph;
530 new_glyph.
width = gm.gmCellIncX;
532 this->SetGlyphPtr(key, &new_glyph);
534 return new_glyph.sprite;
539 assert(IsPrintable(key));
541 if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
547 if (key >= 0x010000U) {
548 chars[0] = (wchar_t)(((key - 0x010000U) >> 10) + 0xD800);
549 chars[1] = (wchar_t)(((key - 0x010000U) & 0x3FF) + 0xDC00);
551 chars[0] = (wchar_t)(key & 0xFFFF);
554 WORD glyphs[2] = { 0, 0 };
555 GetGlyphIndicesW(this->
dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
557 return glyphs[0] != 0xFFFF ? glyphs[0] : 0;
560 const void *Win32FontCache::InternalGetFontTable(uint32 tag,
size_t &length)
562 DWORD len = GetFontData(this->
dc, tag, 0,
nullptr, 0);
564 void *result =
nullptr;
565 if (len != GDI_ERROR && len > 0) {
566 result = MallocT<BYTE>(len);
567 GetFontData(this->
dc, tag, 0, result, len);
583 static const char *SIZE_TO_NAME[] = {
"medium",
"small",
"large",
"mono" };
591 default: NOT_REACHED();
596 const char *font_name =
settings->font.c_str();
599 logfont.lfPitchAndFamily = fs ==
FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
600 logfont.lfCharSet = DEFAULT_CHARSET;
601 logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
602 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
604 if (
settings->os_handle !=
nullptr) {
605 logfont = *(
const LOGFONT *)
settings->os_handle;
606 }
else if (strchr(font_name,
'.') !=
nullptr) {
609 wchar_t fontPath[MAX_PATH] = {};
617 if (!full_font.empty()) {
622 if (fontPath[0] != 0) {
623 if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
627 typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
628 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetProcAddress(
"GetFontResourceInfoW");
630 if (GetFontResourceInfo !=
nullptr) {
633 if (GetFontResourceInfo(fontPath, &len,
nullptr, 2) && len >=
sizeof(LOGFONT)) {
634 LOGFONT *buf = (LOGFONT *)
AllocaM(
byte, len);
635 if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
642 if (logfont.lfFaceName[0] == 0) {
643 wchar_t fname[_MAX_FNAME];
644 _wsplitpath(fontPath,
nullptr,
nullptr, fname,
nullptr);
646 wcsncpy_s(logfont.lfFaceName,
lengthof(logfont.lfFaceName), fname, _TRUNCATE);
647 logfont.lfWeight = strcasestr(font_name,
" bold") !=
nullptr || strcasestr(font_name,
"-bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
650 ShowInfoF(
"Unable to load file '%s' for %s font, using default windows font selection instead", font_name, SIZE_TO_NAME[fs]);
655 if (logfont.lfFaceName[0] == 0) {
656 logfont.lfWeight = strcasestr(font_name,
" bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
660 HFONT font = CreateFontIndirect(&logfont);
661 if (font ==
nullptr) {
662 ShowInfoF(
"Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", font_name, SIZE_TO_NAME[fs], GetLastError());