10 #include "../../stdafx.h"
11 #include "../../debug.h"
13 #include "../../blitter/factory.hpp"
14 #include "../../core/alloc_func.hpp"
15 #include "../../core/math_func.hpp"
16 #include "../../fileio_func.h"
17 #include "../../fontdetection.h"
18 #include "../../fontcache.h"
19 #include "../../string_func.h"
20 #include "../../strings_func.h"
21 #include "../../zoom_func.h"
23 #include "../../table/control_codes.h"
28 #undef small // Say what, Windows?
35 #include FT_FREETYPE_H
37 extern FT_Library _library;
51 static char short_path[MAX_PATH];
52 wchar_t short_path_w[MAX_PATH];
53 GetShortPathName(long_path, short_path_w,
lengthof(short_path_w));
54 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path,
lengthof(short_path),
nullptr,
nullptr);
66 static const wchar_t *FONT_DIR_NT = L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
69 FT_Error err = FT_Err_Cannot_Open_Resource;
72 wchar_t vbuffer[MAX_PATH], dbuffer[256];
74 const char *font_path;
78 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey);
80 if (ret != ERROR_SUCCESS) {
81 DEBUG(freetype, 0,
"Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
86 wchar_t *font_namep = wcsdup(
OTTD2FS(font_name));
88 for (index = 0;; index++) {
93 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen,
nullptr,
nullptr, (
byte *)dbuffer, &dbuflen);
94 if (ret != ERROR_SUCCESS)
goto registry_no_font_found;
105 s = wcschr(vbuffer, L
'(');
106 if (s !=
nullptr) s[-1] =
'\0';
108 if (wcschr(vbuffer, L
'&') ==
nullptr) {
109 if (wcsicmp(vbuffer, font_namep) == 0)
break;
111 if (wcsstr(vbuffer, font_namep) !=
nullptr)
break;
115 if (!SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_FONTS,
nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
116 DEBUG(freetype, 0,
"SHGetFolderPath cannot return fonts directory");
124 path_len = wcslen(vbuffer) + wcslen(dbuffer) + 2;
125 pathbuf =
AllocaM(
wchar_t, path_len);
126 _snwprintf(pathbuf, path_len, L
"%s\\%s", vbuffer, dbuffer);
133 err = FT_New_Face(_library, font_path, index, face);
134 if (err != FT_Err_Ok)
break;
136 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
138 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
139 err = FT_Err_Cannot_Open_Resource;
141 }
while ((FT_Long)++index != (*face)->num_faces);
145 registry_no_font_found:
166 static char font_name[MAX_PATH];
167 const char *ret_font_name =
nullptr;
173 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
175 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
176 if (font ==
nullptr)
goto err1;
179 oldfont = SelectObject(dc, font);
180 dw = GetFontData(dc,
'eman', 0,
nullptr, 0);
181 if (dw == GDI_ERROR)
goto err2;
183 buf = MallocT<byte>(dw);
184 dw = GetFontData(dc,
'eman', 0, buf, dw);
185 if (dw == GDI_ERROR)
goto err3;
187 format = buf[pos++] << 8;
188 format += buf[pos++];
190 count = buf[pos++] << 8;
192 stringOffset = buf[pos++] << 8;
193 stringOffset += buf[pos++];
194 for (uint i = 0; i < count; i++) {
195 platformId = buf[pos++] << 8;
196 platformId += buf[pos++];
197 encodingId = buf[pos++] << 8;
198 encodingId += buf[pos++];
199 languageId = buf[pos++] << 8;
200 languageId += buf[pos++];
201 nameId = buf[pos++] << 8;
202 nameId += buf[pos++];
207 length = buf[pos++] << 8;
208 length += buf[pos++];
209 offset = buf[pos++] << 8;
210 offset += buf[pos++];
213 length = std::min<uint16>(length, MAX_PATH - 1);
214 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
215 font_name[length] =
'\0';
217 if ((platformId == 1 && languageId == 0) ||
218 (platformId == 3 && languageId == 0x0409)) {
219 ret_font_name = font_name;
227 SelectObject(dc, oldfont);
228 ReleaseDC(
nullptr, dc);
231 return ret_font_name ==
nullptr ?
FS2OTTD((
const wchar_t *)logfont->elfFullName) : ret_font_name;
242 FontList() : fonts(
nullptr), items(0), capacity(0) { };
245 if (this->fonts ==
nullptr)
return;
247 for (uint i = 0; i < this->items; i++) {
248 free(this->fonts[i]);
254 bool Add(
const wchar_t *font) {
255 for (uint i = 0; i < this->items; i++) {
256 if (wcscmp(this->fonts[i], font) == 0)
return false;
259 if (this->items == this->capacity) {
260 this->capacity += 10;
261 this->fonts =
ReallocT(this->fonts, this->capacity);
264 this->fonts[this->items++] = wcsdup(font);
272 LOCALESIGNATURE locale;
277 static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
282 if (!info->fonts.Add((
const wchar_t *)logfont->elfFullName))
return 1;
284 if (!(type & TRUETYPE_FONTTYPE))
return 1;
286 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
288 if (info->callback->
Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
291 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
294 memset(&fs, 0,
sizeof(fs));
295 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
296 if (font !=
nullptr) {
297 HDC dc = GetDC(
nullptr);
298 HGDIOBJ oldfont = SelectObject(dc, font);
299 GetTextCharsetInfo(dc, &fs, 0);
300 SelectObject(dc, oldfont);
301 ReleaseDC(
nullptr, dc);
304 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0)
return 1;
307 char font_name[MAX_PATH];
313 strecpy(font_name + strlen(font_name) + 1, english_name,
lastof(font_name));
316 bool ft_init = _library !=
nullptr;
320 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) &&
GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
326 FT_Done_FreeType(_library);
330 if (!found)
return 1;
332 const char *english_name = font_name;
335 info->callback->
SetFontNames(info->settings, font_name, &logfont->elfLogFont);
337 DEBUG(freetype, 1,
"Fallback font: %s (%s)", font_name, english_name);
343 DEBUG(freetype, 1,
"Trying fallback fonts");
345 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale,
sizeof(langInfo.locale) /
sizeof(
wchar_t)) == 0) {
347 DEBUG(freetype, 1,
"Can't get locale info for fallback font (langid=0x%x)", winlangid);
351 langInfo.callback = callback;
355 font.lfCharSet = DEFAULT_CHARSET;
356 font.lfFaceName[0] =
'\0';
357 font.lfPitchAndFamily = 0;
359 HDC dc = GetDC(
nullptr);
360 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
361 ReleaseDC(
nullptr, dc);
366 #ifndef ANTIALIASED_QUALITY
367 #define ANTIALIASED_QUALITY 4
378 this->
dc = CreateCompatibleDC(
nullptr);
379 this->SetFontSize(
fs, pixels);
382 Win32FontCache::~Win32FontCache()
386 DeleteObject(this->font);
389 void Win32FontCache::SetFontSize(
FontSize fs,
int pixels)
393 int scaled_height =
ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
394 pixels = scaled_height;
396 HFONT temp = CreateFontIndirect(&this->logfont);
397 if (temp !=
nullptr) {
398 HGDIOBJ old = SelectObject(this->
dc, temp);
400 UINT size = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
401 LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)
AllocaM(BYTE, size);
402 GetOutlineTextMetrics(this->
dc, size, otm);
409 SelectObject(
dc, old);
418 this->logfont.lfHeight = -pixels;
419 this->logfont.lfWidth = 0;
420 this->logfont.lfOutPrecision = ANTIALIASED_QUALITY;
422 if (this->font !=
nullptr) {
424 DeleteObject(this->font);
426 this->font = CreateFontIndirect(&this->logfont);
427 this->
old_font = SelectObject(this->
dc, this->font);
430 UINT otmSize = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
431 POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)
AllocaM(BYTE, otmSize);
432 GetOutlineTextMetrics(this->
dc, otmSize, otm);
435 this->
ascender = otm->otmTextMetrics.tmAscent;
436 this->
descender = otm->otmTextMetrics.tmDescent;
438 this->
glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
439 this->
glyph_size.cy = otm->otmTextMetrics.tmHeight;
441 DEBUG(freetype, 2,
"Loaded font '%s' with size %d",
FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels);
450 if (this->font !=
nullptr) this->SetFontSize(this->fs, this->
req_size);
455 const Sprite *Win32FontCache::InternalGetGlyph(
GlyphID key,
bool aa)
458 MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
462 byte *bmp =
AllocaM(
byte, size);
463 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
465 if (size == GDI_ERROR) {
471 size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0,
nullptr, &mat);
472 if (size == GDI_ERROR)
usererror(
"Unable to render font glyph");
474 GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
478 uint width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs ==
FS_NORMAL));
479 uint
height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs ==
FS_NORMAL));
489 sprite.
width = width;
491 sprite.
x_offs = gm.gmptGlyphOrigin.x;
500 uint pitch =
Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4);
504 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
505 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
506 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
507 sprite.
data[1 + x + (1 + y) * sprite.
width].
m = SHADOW_COLOUR;
508 sprite.
data[1 + x + (1 + y) * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
514 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
515 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
516 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
517 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
518 sprite.
data[x + y * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
524 GlyphEntry new_glyph;
526 new_glyph.
width = gm.gmCellIncX;
528 this->SetGlyphPtr(key, &new_glyph);
530 return new_glyph.sprite;
535 assert(IsPrintable(key));
537 if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
543 if (key >= 0x010000U) {
544 chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800);
545 chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00);
547 chars[0] = (WCHAR)(key & 0xFFFF);
550 WORD glyphs[2] = { 0, 0 };
551 GetGlyphIndicesW(this->
dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
553 return glyphs[0] != 0xFFFF ? glyphs[0] : 0;
556 const void *Win32FontCache::InternalGetFontTable(uint32 tag,
size_t &length)
558 DWORD len = GetFontData(this->
dc, tag, 0,
nullptr, 0);
560 void *result =
nullptr;
561 if (len != GDI_ERROR && len > 0) {
562 result = MallocT<BYTE>(len);
563 GetFontData(this->
dc, tag, 0, result, len);
579 static const char *SIZE_TO_NAME[] = {
"medium",
"small",
"large",
"mono" };
587 default: NOT_REACHED();
594 logfont.lfPitchAndFamily = fs ==
FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
595 logfont.lfCharSet = DEFAULT_CHARSET;
596 logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
597 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
599 if (
settings->os_handle !=
nullptr) {
600 logfont = *(
const LOGFONT *)
settings->os_handle;
601 }
else if (strchr(
settings->font,
'.') !=
nullptr) {
604 wchar_t fontPath[MAX_PATH] = {};
612 if (!full_font.empty()) {
617 if (fontPath[0] != 0) {
618 if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
621 typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
622 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(L
"Gdi32"),
"GetFontResourceInfoW");
624 if (GetFontResourceInfo !=
nullptr) {
627 if (GetFontResourceInfo(fontPath, &len,
nullptr, 2) && len >=
sizeof(LOGFONT)) {
628 LOGFONT *buf = (LOGFONT *)
AllocaM(
byte, len);
629 if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
636 if (logfont.lfFaceName[0] == 0) {
637 wchar_t fname[_MAX_FNAME];
638 _wsplitpath(fontPath,
nullptr,
nullptr, fname,
nullptr);
640 wcsncpy_s(logfont.lfFaceName,
lengthof(logfont.lfFaceName), fname, _TRUNCATE);
641 logfont.lfWeight = strcasestr(
settings->font,
" bold") !=
nullptr || strcasestr(
settings->font,
"-bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
644 ShowInfoF(
"Unable to load file '%s' for %s font, using default windows font selection instead",
settings->font, SIZE_TO_NAME[fs]);
649 if (logfont.lfFaceName[0] == 0) {
650 logfont.lfWeight = strcasestr(
settings->font,
" bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
654 HFONT font = CreateFontIndirect(&logfont);
655 if (font ==
nullptr) {
656 ShowInfoF(
"Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead",
settings->font, SIZE_TO_NAME[fs], GetLastError());