根据字体句柄(HFONT)获取字体文件名

我遇到了一种情况,我们需要知道QFont正在使用的字体的文件名。 知道一个QFont可以给我们的字体家族和WindowsHFONT句柄。

字体系列是不够的,因为操纵样式像BoldItalic可以导致Windowsselect不同的字体文件。 (fe arial.ttf,arialbd.ttf,arialbi.ttf,ariali.ttf)。

这个代码示例应该给我们<path>\arial.ttf

 QFont font("Arial", 12); FindFontFileName(font.handle()); 

而这个代码示例应该给我们<path>\arialbi.ttf

 QFont font("Arial", 12); font.setStyle(QFont::StyleItalic); font.setWeight(QFont::Bold); FindFontFileName(font.handle()); 

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是从字体文件中读取的随机部分,以区分相同文件大小的字体。 你也可以使用完整文件的哈希(fe MD5)来建立这个。

添加字体

将单个字体添加到此列表的方法非常简单:

 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)); } 

Qt的方式来添加所有的Windows字体到查找表是这样的:

 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助手

然后,我们为创建DC的GetFontData函数创建一个包装函数,通过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(); }