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

前端 未结 1 1604
太阳男子
太阳男子 2020-12-16 04:05

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 Windo

相关标签:
1条回答
  • 2020-12-16 04:44

    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< size_t, std::shared_ptr<FontListItem> > 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 < 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));
    }
    

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

    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<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();
    }
    
    0 讨论(0)
提交回复
热议问题