Get a font filename based on the font handle (HFONT)

匿名 (未验证) 提交于 2019-12-03 01:08:02

问题:

I came across a situation where we needed to know the filename of a font currently in use by a QFont. Knowing that a QFont can give us the font family and Windows HFONT handle.

The font family is not enough, because manipulating styles like Bold or Italic can result in Windows selecting a different font file. (f.e. arial.ttf, arialbd.ttf, arialbi.ttf, ariali.ttf).

This code sample should give us \arial.ttf:

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

while this code sample should give us \arialbi.ttf

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

回答1:

The Windows API Font and Text Functions doesn't contain a function that returns the filename of a font. So a more creative solution has to be worked out.

The solution is to use the GetFontData function, which will give us the exact copy of the original font file. The only thing that's left is comparing this data with the contents of all installed/known fonts.

Lookup table

We will first create a lookup table (FontList) of all installed/known fonts:

#define FONT_FINGERPRINT_SIZE    256 struct FontListItem {     std::string FileName;     int FingerPrintOffset;     char FingerPrint[FONT_FINGERPRINT_SIZE]; };  std::multimap > FontList;

The FingerPrint is a random part read from the font file in order to distinguish between fonts of the same filesize. You could also use a hash (f.e. MD5) of the complete file to establish this.

Adding fonts

Method for adding a single font to this list is pretty straightforward:

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  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 >(fileSize, fontListItem)); }

A Qt way to add all Windows fonts to the lookup table goes like this:

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

File enumeration can also be done using FindFirstFile/FindNextFile Windows API functions, but would be less readable for the purpose of this answer.

GetFontData helper

Then we create a wrapper function for the GetFontData function that creates a DC, selects the font by the HFONT handle and returns the fonts data:

bool GetFontData(const HFONT fontHandle, std::vector& 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; }

Font filename lookup

Now we're all set for looking up the exact filename of a font by only knowing the HFONT handle:

std::string FindFontFileName(const HFONT fontHandle) {     std::vector 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(); }


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!