通过遍历注册表遍历windows truetype字体,找出字体名和字体文件的对应关系。
这个代码并没有找出字体名的本地化的名字。尝试使用摘录改编自Qt的qt_getEnglishName,但是GetFontData返回GDI_ERROR,失败。
下面是代码,测试通过。
#include <iostream>
#include <vector>
#include <Windows.h>
#include <string>
#include <locale.h>
std::string ws2s(const std::wstring& ws)
{
std::string curLocale = setlocale(LC_ALL, NULL); // curLocale = "C";
setlocale(LC_ALL, "chs");
const wchar_t* _Source = ws.c_str();
size_t _Dsize = 2 * ws.size() + 1;
char *_Dest = new char[_Dsize];
memset(_Dest,0,_Dsize);
size_t out;
wcstombs_s(&out, _Dest, _Dsize, _Source,_Dsize);
std::string result = _Dest;
delete []_Dest;
setlocale(LC_ALL, curLocale.c_str());
return result;
}
// copy and adapt from https://github.com/GiovanniDicanio/WinReg/blob/master/WinReg/WinReg.hpp
// call RegCloseKey(hKey) after usage.
HKEY RegOpenKeyForRead(
const HKEY hKeyParent,
const std::string& subKey
)
{
HKEY hKey = nullptr;
LONG retCode = RegOpenKeyExA(
hKeyParent,
subKey.c_str(),
REG_NONE, // default options
KEY_READ,
&hKey
);
if (retCode != ERROR_SUCCESS)
{
throw std::runtime_error("RegOpenKeyEx failed.");
}
// Take ownership of the newly created key
return hKey;
}
std::string RegGetStringValue(HKEY hKey, const std::string& valueName)
{
// Get the size of the result string
DWORD dataSize = 0; // size of data, in bytes
constexpr DWORD flags = RRF_RT_REG_SZ;
LONG retCode = RegGetValueA(
hKey,
nullptr, // no subkey
valueName.c_str(),
flags,
nullptr, // type not required
nullptr, // output buffer not needed now
&dataSize
);
if (retCode != ERROR_SUCCESS)
{
return "";
}
// Allocate a string of proper size.
// Note that dataSize is in bytes and includes the terminating NUL;
// we have to convert the size from bytes to wchar_ts for wstring::resize.
std::string result(dataSize / sizeof(char), ' ');
// Call RegGetValue for the second time to read the string's content
retCode = RegGetValueA(
hKey,
nullptr, // no subkey
valueName.c_str(),
flags,
nullptr, // type not required
&result[0], // output buffer
&dataSize
);
if (retCode != ERROR_SUCCESS)
{
return "";
}
// Remove the NUL terminator scribbled by RegGetValue from the wstring
result.resize((dataSize / sizeof(char)) - 1);
return result;
}
void CloseRegKey(HKEY* key) {
RegCloseKey(*key);
}
void trim(std::string &s)
{
if (s.empty())
{
return;
}
s.erase(0, s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
}
std::vector<std::pair<std::string, std::string>> EnumTrueTypeFonts()
{
HKEY hKey = RegOpenKeyForRead(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
std::unique_ptr<HKEY, decltype(&CloseRegKey)> wrapper(&hKey, CloseRegKey);
// Get useful enumeration info, like the total number of values
// and the maximum length of the value names
DWORD valueCount = 0;
DWORD maxValueNameLen = 0;
LONG retCode = RegQueryInfoKeyA(
hKey,
nullptr, // no user-defined class
nullptr, // no user-defined class size
nullptr, // reserved
nullptr, // no subkey count
nullptr, // no subkey max length
nullptr, // no subkey class length
&valueCount,
&maxValueNameLen,
nullptr, // no max value length
nullptr, // no security descriptor
nullptr // no last write time
);
if (retCode != ERROR_SUCCESS)
{
throw std::runtime_error("RegQueryInfoKey failed while preparing for value enumeration.");
}
// NOTE: According to the MSDN documentation, the size returned for value name max length
// does *not* include the terminating NUL, so let's add +1 to take it into account
// when I allocate the buffer for reading value names.
maxValueNameLen++;
// Preallocate a buffer for the value names
auto nameBuffer = std::make_unique<char[]>(maxValueNameLen);
// The value names and types will be stored here
std::vector<std::pair<std::string, std::string>> valueInfo;
// Reserve room in the vector to speed up the following insertion loop
valueInfo.reserve(valueCount);
// Enumerate all the values
for (DWORD index = 0; index < valueCount; index++)
{
// Get the name and the type of the current value
DWORD valueNameLen = maxValueNameLen;
DWORD valueType = 0;
retCode = RegEnumValueA(
hKey,
index,
nameBuffer.get(),
&valueNameLen,
nullptr, // reserved
&valueType,
nullptr, // no data
nullptr // no data size
);
if (retCode != ERROR_SUCCESS)
{
throw std::runtime_error("Cannot enumerate values: RegEnumValue failed.");
}
if (valueType == REG_SZ) {
// On success, the RegEnumValue API writes the length of the
// value name in the valueNameLen output parameter
// (not including the terminating NUL).
// So we can build a wstring based on that
std::string valueName{ nameBuffer.get(), valueNameLen };
auto pos = valueName.rfind("(TrueType)");
if (pos != std::string::npos) {
const auto value = RegGetStringValue(hKey, nameBuffer.get());
valueName = valueName.substr(0, pos);
// string.split
size_t pos1 = 0, pos2;
while (true)
{
pos2 = valueName.find('&', pos1);
auto valueName2 = valueName.substr(pos1, pos2 == std::string::npos? pos2 : pos2 - pos1);
trim(valueName2);
valueInfo.emplace_back(valueName2, value);
if (pos2 == std::string::npos)
break;
pos1 = pos2 + 1;
}
}
}
}
return valueInfo;
}
int main()
{
auto fonts = EnumTrueTypeFonts();
for (const auto& v : fonts)
{
std::cout << v.first.c_str() << "\t=>\t" << v.second.c_str() << std::endl;
}
}
输出:
Arial => arial.ttf
Arial Black => ariblk.ttf
Arial Bold => arialbd.ttf
Arial Bold Italic => arialbi.ttf
Arial Italic => ariali.ttf
Bahnschrift => bahnschrift.ttf
Calibri => calibri.ttf
Calibri Bold => calibrib.ttf
...