基于字体句柄(HFONT)获取字体文件名 [英] Get a font filename based on the font handle (HFONT)

查看:2708
本文介绍了基于字体句柄(HFONT)获取字体文件名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到一个情况,我们需要知道一个字体的文件名当前正在使用 QFont 。知道一个 QFont 可以给我们的字体系列和Windows HFONT 句柄。



字体系列是不够的,因为操作类似 Bold Italic 选择不同的字体文件。 (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 该字体文件为了区分相同文件大小的字体。



添加字体



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

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

字体文件名查找
$ b

现在我们只需要知道 HFONT 句柄即可查找字体的确切文件名:

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


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 <path>\arial.ttf:

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

while this code sample should give us <path>\arialbi.ttf

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

解决方案

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

这篇关于基于字体句柄(HFONT)获取字体文件名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆