The Windows API
字体和文本函数 中没有包含返回字体文件名的函数。因此,需要想出更有创意的解决方案。
解决方案是使用
GetFontData
函数,它将给我们原始字体文件的精确副本。唯一剩下的就是将这些数据与所有已安装/已知字体的内容进行比较。
查找表
我们首先将创建一个查找表(
FontList
),其中包含所有已安装/已知字体的信息:
#define FONT_FINGERPRINT_SIZE 256
struct FontListItem
{
std::string FileName;
int FingerPrintOffset;
char FingerPrint[FONT_FINGERPRINT_SIZE];
};
std::multimap< size_t, std::shared_ptr<FontListItem> > FontList;
"FingerPrint" 是从字体文件中读取的随机部分,用于区分大小相同的字体。您也可以使用文件的完整哈希值(例如 SHA1)来建立这个区分。
添加字体
将单个字体添加到列表中的方法非常简单:
void AddFontToList(const std::string& fontFileName)
{
std::ifstream file(fontFileName, std::ios::binary | std::ios::ate);
if (!file.is_open())
return;
size_t fileSize = file.tellg();
if (fileSize < FONT_FINGERPRINT_SIZE)
return;
std::shared_ptr<FontListItem> fontListItem(new FontListItem());
fontListItem->FileName = fontFileName;
fontListItem->FingerPrintOffset = rand() % (fileSize - FONT_FINGERPRINT_SIZE);
file.seekg(fontListItem->FingerPrintOffset);
file.read(fontListItem->FingerPrint, FONT_FINGERPRINT_SIZE);
FontList.insert(std::pair<size_t, std::shared_ptr<FontListItem> >(fileSize, fontListItem));
}
添加所有Windows字体到查找表的Qt方法如下:
const QDir dir(QString(getenv("WINDIR")) + "\\fonts");
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
foreach (const QFileInfo fileInfo, dir.entryInfoList())
AddFontToList(fileInfo.absoluteFilePath().toUtf8().constData());
文件枚举也可以使用
FindFirstFile
/
FindNextFile
Windows API函数进行,但对于本回答的目的来说会更加难以阅读。
GetFontData助手
然后我们创建一个包装函数用于
GetFontData
函数,它会创建一个DC,通过
HFONT
句柄选择字体并返回字体数据:
bool GetFontData(const HFONT fontHandle, std::vector<char>& data)
{
bool result = false;
HDC hdc = ::CreateCompatibleDC(NULL);
if (hdc != NULL)
{
::SelectObject(hdc, fontHandle);
const size_t size = ::GetFontData(hdc, 0, 0, NULL, 0);
if (size > 0)
{
char* buffer = new char[size];
if (::GetFontData(hdc, 0, 0, buffer, size) == size)
{
data.resize(size);
memcpy(&data[0], buffer, size);
result = true;
}
delete[] buffer;
}
::DeleteDC(hdc);
}
return result;
}
字体文件名查找
现在我们已经准备好通过仅知道 HFONT
句柄来查找字体的确切文件名:
std::string FindFontFileName(const HFONT fontHandle)
{
std::vector<char> data;
if (GetFontData(fontHandle, data))
{
for (auto i = FontList.lower_bound(data.size()); i != FontList.upper_bound(data.size()); ++i)
{
if (memcmp(&data[i->second->FingerPrintOffset], i->second->FingerPrint, FONT_FINGERPRINT_SIZE) == 0)
return i->second->FileName;
}
}
return std::string();
}
GetObject
检索LOGFONT
即可在给定 HFONT 时检索字体系列,这很简单。但请不要将此解释为您的建议无用或不受欢迎——我也对问题和答案进行了投票支持。 - Cody Gray