根据字体句柄 (HFONT) 获取字体文件名
Posted
技术标签:
【中文标题】根据字体句柄 (HFONT) 获取字体文件名【英文标题】:Get a font filename based on the font handle (HFONT) 【发布时间】:2013-05-27 09:06:48 【问题描述】:我遇到了一种情况,我们需要知道QFont
当前使用的字体的文件名。知道QFont
可以为我们提供字体系列和Windows HFONT
句柄。
字体系列是不够的,因为处理Bold
或Italic
等样式可能会导致Windows 选择不同的字体文件。 (例如 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());
【问题讨论】:
有很多人想要这样做:Get a font filename based on Font Name and Style (Bold/Italic); Get a font filepath from name and style in C++/Windows。很奇怪,因为我想不出需要它的情况。对我来说似乎是糟糕的设计。 @CodyGray:在我们的应用程序中,用户将自己的字体添加到库中。用户使用这些字体创建内容(他们只选择字体系列并应用一些样式,就像在您最喜欢的文本编辑器中一样)。在用户将内容部署到远程目标时,应用程序必须知道要在部署中包含哪些字体。 不确定这是否使它不重复。在给定 HFONT 时检索字体系列很简单,只需调用GetObject
即可检索 LOGFONT
。但不要将其解释为您的建议没有用或不受欢迎——我也对问题和答案都投了赞成票。
这也是我想做的事情。我的原因是为我们的字体密集型软件应用产品解决客户的字体安装问题。尽管字体通常位于官方的 Windows 字体文件夹中,但它们并非必须如此。字体管理产品可以移动它们。还有一些安装字体的产品做的不对。最后,人们会弄乱他们的注册表。顺便说一句,Mac OSX 有一个 API 可以做同样的事情。
@CodeGray 用于在PDF文件中嵌入字体,例如需要知道字体文件的名称。
【参考方案1】:
Windows API Font and Text Functions 不包含返回字体文件名的函数。所以必须想出一个更有创意的解决方案。
解决方案是使用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();
【讨论】:
请注意,并非所有字体都存储在 %WINDIR%\Fonts 中。字体路径存储在注册表设置中,HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts
- 有关详细信息,请参阅***.com/a/39093093/1836776,包括为什么存储在该注册表项中的字体名称不可靠。以上是关于根据字体句柄 (HFONT) 获取字体文件名的主要内容,如果未能解决你的问题,请参考以下文章