通过Freetype库获得truetype字体宽度,如果有需要改进之处,请不吝赐教。
很抱歉,这是用古老的MFC写的,但是你应该很容易的替换成标准库的字符串和容器。
#include <iostream>
#include <afx.h>
#include <afxtempl.h>
#include <shlobj_core.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
class FreeType
{
static inline void DoneFace(FT_Face face)
{
FT_Done_Face(face);
}
public:
FreeType()
: m_library(nullptr)
, m_timer(0)
{
FT_Error error = FT_Init_FreeType(&m_library);
if (error) {
}
}
FT_Face NewFace(const CString& fontFile);
void DoneFaces()
{
POSITION pos = m_faces.GetStartPosition();
CString key;
FT_Face face;
while (pos != NULL)
{
m_faces.GetNextAssoc(pos, key, face);
FreeType::DoneFace(face);
}
m_faces.RemoveAll();
KillTimer(NULL, m_timer);
m_timer = 0;
}
static inline void SetCharSize(FT_Face face,
FT_F26Dot6 char_width,
FT_F26Dot6 char_height,
FT_UInt horz_resolution,
FT_UInt vert_resolution)
{
FT_Set_Char_Size(face, char_width, char_height, horz_resolution, vert_resolution);
}
static inline void SetPixelSizes(FT_Face face,
FT_UInt pixel_width,
FT_UInt pixel_height)
{
FT_Set_Pixel_Sizes(face, pixel_width, pixel_height);
}
static inline void LoadChar(FT_Face face,
FT_ULong char_code,
FT_Int32 load_flags) // FT_LOAD_DEFAULT, FT_LOAD_COMPUTE_METRICS
{
FT_Load_Char(face, char_code, load_flags);
}
static inline FT_Glyph GetGlyph(FT_GlyphSlot glyph)
{
FT_Glyph res;
FT_Get_Glyph(glyph, &res);
return res;
}
static inline void DoneGlyph(FT_Glyph glyph)
{
FT_Done_Glyph(glyph);
}
~FreeType()
{
DoneFaces();
FT_Done_FreeType(m_library);
}
FT_Library m_library;
CMap<CString, LPCTSTR, FT_Face, FT_Face> m_faces;
UINT_PTR m_timer;
};
FreeType g_freetype;
void __stdcall DoneFacesTimerproc(
HWND Arg1,
UINT Arg2,
UINT_PTR Arg3,
DWORD Arg4
)
{
g_freetype.DoneFaces();
}
FT_Face FreeType::NewFace(const CString& fontFile)
{
FT_Face face;
BOOL found = m_faces.Lookup(fontFile, face);
if (found)
return face;
LPITEMIDLIST ppidl;
TCHAR lpsbuf[255];
SHGetSpecialFolderLocation(NULL, CSIDL_FONTS, &ppidl);
SHGetPathFromIDList(ppidl, lpsbuf);
CoTaskMemFree(ppidl);
const size_t len = _tcslen(lpsbuf);
if (len >= sizeof(lpsbuf) - 1)
// can't go on
return nullptr;
lpsbuf[len] = '\\';
lpsbuf[len + 1] = '\0';
FT_Error error = FT_New_Face(m_library,
CT2A(CString(lpsbuf) + fontFile),
0,
&face);
if (error == FT_Err_Cannot_Open_Resource)
{
// 试一试.ttc文件
if (fontFile.Right(4).MakeLower() == _T(".ttf"))
{
CString fontFile2 = fontFile.Left(fontFile.GetLength() - 4) + _T(".ttc");
error = FT_New_Face(m_library,
CT2A(CString(lpsbuf) + fontFile2),
0,
&face);
}
}
if (error)
{
return nullptr;
}
m_faces[fontFile] = face;
// 只要NewFace在超时时间内调用,计时器会被重置。
SetTimer(NULL, m_timer, 5000, DoneFacesTimerproc);
return face;
}
std::tuple<double, double, double> GetTextWidth(const CString& fontFile, double textHeight, const CString& text2)
{
double Ascent = textHeight;
double Descent = 0, width = 0;
FT_Face face = g_freetype.NewFace(fontFile);
if (face == nullptr)
return { 0.0, 0.0, 0.0 };
// 放大倍数
double ratio = 640.0 / textHeight;
auto fontHeight = FT_UInt(640);
//const double* LookForWidthTableTtfFile(const CString& ttfFace);
//const auto widthTable = LookForWidthTableTtfFile(pTSD->FontFile);
double whratio;
//if (widthTable)
// whratio = widthTable[96] / 100.0;
//else
whratio = 1.4382;
auto fontWidth = FT_UInt(fontHeight * whratio); // 经验系数
FreeType::SetPixelSizes(face, fontWidth, fontHeight);
CStringW text(CT2W(text2).operator LPWSTR());
size_t len = text.GetLength();
int xmin = 0, ymin = 0, xmax = 0;
for (size_t i = 0; i < len; ++i)
{
FreeType::LoadChar(face, text[i], FT_LOAD_COMPUTE_METRICS);
FT_Glyph_Metrics metrics = face->glyph->metrics;
if (i == 0)
{
xmin = 0; // metrics.horiBearingX;
ymin = metrics.horiBearingY - metrics.height;
}
else
{
ymin = min(ymin, metrics.horiBearingY - metrics.height);
}
if (i == len - 1)
{
xmax += metrics.horiAdvance; // metrics.horiBearingX + metrics.width;
}
else
{
xmax += metrics.horiAdvance;
}
}
// 为了提高程序性能,face被缓存。在空闲时用一个计时器来释放faces。
//FreeType::DoneFace(face);
// 缩小ratio倍
// 再除以64,因为FT_Pos是26.6定点小数
width = (xmax - xmin) / ratio / 64.0 * widthFactor;
Descent = -ymin / ratio / 64.0;
return { width, Ascent, Descent };
}
int main()
{
auto tuple = GetTextWidth("simsun.ttc", 10, "中国前进");
std::cout << tuple.get<0> << std::endl;
std::cout << tuple.get<2> << std::endl;
}
输出
57.5
0.984375