タイトルの通りです。システムロケールに依存せずに Windows フォントの英語名を列挙する。休日の頭の体操として、やってみました。バリアブルフォントの取り扱いが面倒くさいことを除けば、name テーブルを参照するだけの教科書通りの処理ですね。応用すれば、任意ロケールのフォント名を取得することも難しくありません。さて、この後は頭を切り替えて、絵でも描きましょうかね。
#define UNICODE #define _UNICODE #include <windows.h> #include <io.h> #include <fcntl.h> #include <iostream> #include <map> #include <set> #include <string> #include <vector> #pragma comment(lib, "gdi32") #pragma comment(lib, "user32") namespace { using TString = std::basic_string<TCHAR>; using FontList = std::set<TString>; constexpr auto tag(unsigned a, unsigned b, unsigned c, unsigned d) { return (a << 0) + (b << 8) + (c << 16) + (d << 24); } std::vector<BYTE> getFontData(const LOGFONT& lf, DWORD table, bool ignoreError = false) { std::vector<BYTE> data; HDC dc = nullptr, offscreenDc = nullptr; HGDIOBJ oldFont = nullptr; try { do { dc = GetDC(nullptr); offscreenDc = CreateCompatibleDC(dc); oldFont = SelectObject(offscreenDc, CreateFontIndirect(&lf)); auto size = GetFontData(offscreenDc, table, 0, nullptr, 0); if (size == GDI_ERROR) { if (ignoreError) break; throw std::exception("GetFontData failured."); } data.resize(size); size = GetFontData(offscreenDc, table, 0, data.data(), size); if (size == GDI_ERROR) { if (ignoreError) break; throw std::exception("GetFontData failured."); } } while (0); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } if (oldFont) DeleteObject(SelectObject(offscreenDc, oldFont)); if (offscreenDc) DeleteDC(offscreenDc); if (dc) ReleaseDC(nullptr, dc); return data; } TString getName(unsigned platformId, const BYTE* data, unsigned length) { TString name; switch (platformId) { case 0: // Unicode: Probably, needs to handle the original data as a UTF-8 text. for (auto i = 0u; i < length; ++i) { name.push_back(data[i]); } std::cerr << "!! Probably, should be processed as a UTF-8 text !!" << std::endl; break; case 1: // Machintosh: Omitted in this sample since it is too complex. std::cerr << "!! Inored, should be processed as per the encoding ID !!" << std::endl; break; case 3: // Windows: Handle the original data as a UTF-16BE text. for (auto i = 0u; i < (length / 2); ++i) { name.push_back((data[(i * 2) + 0] << 8) | data[(i * 2) + 1]); } break; } return name; } int CALLBACK enumProc(const LOGFONT* lf, const TEXTMETRIC* tm, DWORD fType, LPARAM lp) { auto lfexdv = reinterpret_cast<const ENUMLOGFONTEXDV*>(lf); auto lfex = &lfexdv->elfEnumLogfontEx; auto fontList = reinterpret_cast<FontList*>(lp); try { if ((lfex->elfFullName[0] != '\0') && (lfex->elfFullName[0] != '@') && (fType != RASTER_FONTTYPE)) { // Enumerate variations std::set<unsigned> subfamilyNameIds; auto data = getFontData(*lf, tag('f', 'v', 'a', 'r'), true); if (data.size() > 0) { auto majorVersion = (data.at( 0) << 8) | data.at( 1); auto minorVersion = (data.at( 2) << 8) | data.at( 3); auto axesArrayOffset = (data.at( 4) << 8) | data.at( 5); auto reserved = (data.at( 6) << 8) | data.at( 7); auto axisCount = (data.at( 8) << 8) | data.at( 9); auto axisSize = (data.at(10) << 8) | data.at(11); auto instanceCount = (data.at(12) << 8) | data.at(13); auto instanceSize = (data.at(14) << 8) | data.at(15); auto c = 16; c += axisCount * axisSize; // Skip the array of VariationAxisRecord/ for (auto i = 0u; i < instanceCount; ++i, c += instanceSize) { auto subfamilyNameId = (data.at(c) << 8) | data.at(c + 1); subfamilyNameIds.emplace(subfamilyNameId); } } // Enumerate names TString familyName; std::set<TString> subfamilyNames; data = getFontData(*lf, tag('n', 'a', 'm', 'e')); if (data.size() > 0) { auto formatSelector = (data.at(0) << 8) | data.at(1); // Always 0 auto nameRecordsCount = (data.at(2) << 8) | data.at(3); auto storageOffset = (data.at(4) << 8) | data.at(5); auto c = 6; for (auto i = 0u; i < nameRecordsCount; ++i, c += 12) { auto platformId = (data.at(c + 0) << 8) | data.at(c + 1); auto encodingId = (data.at(c + 2) << 8) | data.at(c + 3); auto languageId = (data.at(c + 4) << 8) | data.at(c + 5); auto nameId = (data.at(c + 6) << 8) | data.at(c + 7); auto length = (data.at(c + 8) << 8) | data.at(c + 9); auto stringOffset = (data.at(c + 10) << 8) | data.at(c + 11); // from 'storageOffset' //wprintf(TEXT("%u, %u, %u, %u, %u, %s\n"), formatSelector, // platformId, encodingId, languageId, nameId, // getName(platformId, data.data() + storageOffset + stringOffset, length).c_str()); if (languageId == 0x0409) { if (nameId == 1) { // Font family name auto family = getName(platformId, data.data() + storageOffset + stringOffset, length); if (!family.empty()) { familyName = family; } } if (subfamilyNameIds.count(nameId)) { // Font subfamily name for variation auto style = getName(platformId, data.data() + storageOffset + stringOffset, length); if (!style.empty()) { subfamilyNames.emplace(style); } } } } } // Generate font names if (!familyName.empty()) { fontList->emplace(familyName); for (auto&& subfamilyName : subfamilyNames) { fontList->emplace(familyName + TEXT(" ") + subfamilyName); } } else { wprintf(TEXT("%s does not have an English family name.\n"), lfex->elfFullName); } } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 1; } } // namespace int main() { auto oldMode = _setmode(_fileno(stdout), _O_U8TEXT); auto dc = GetDC(nullptr); LOGFONT lf = {}; lf.lfCharSet = DEFAULT_CHARSET; FontList fontList; EnumFontFamiliesEx(dc, &lf, enumProc, reinterpret_cast<LPARAM>(&fontList), 0); ReleaseDC(nullptr, dc); for (auto&& font : fontList) { wprintf(TEXT("%s\n"), font.c_str()); } fflush(stdout); _setmode(_fileno(stdout), oldMode); return 0; }
私の自宅の PC では下記のような出力が得られました。.fon などの一部フォント形式は上手く処理できませんが、一般的な TrueType や OpenType は問題なく処理できるようです。よかったよかった。
Modern does not have an English family name. Roman does not have an English family name. Script does not have an English family name. Adobe Arabic Adobe Caslon Pro Adobe Caslon Pro Bold Adobe Devanagari Adobe Fan Heiti Std B Adobe Fangsong Std R Adobe Garamond Pro Adobe Garamond Pro Bold Adobe Gothic Std B Adobe Gurmukhi .. Bahnschrift Bahnschrift Bold Bahnschrift Bold Condensed Bahnschrift Bold SemiCondensed Bahnschrift Condensed Bahnschrift Light Bahnschrift Light Condensed Bahnschrift Light SemiCondensed Bahnschrift Regular Bahnschrift SemiBold Bahnschrift SemiBold Condensed Bahnschrift SemiBold SemiCondensed Bahnschrift SemiCondensed Bahnschrift SemiLight Bahnschrift SemiLight Condensed Bahnschrift SemiLight SemiCondensed .. MS Gothic MS Mincho MS Outlook MS PGothic MS PMincho ..