最近在移植Qt到一个商用的小众系统上,Qt版本是Qt5.6.1,Qt是不支持这个系统的,在移植过程中遇到了很多的问题,这里对字体移植遇的问题进行一下记录。(这个办法是可以使用qpf2字体的,但是是不是最好的办法还不知道,希望可以帮助到需要的人,也希望有人一起讨论)
1.问题:
首先是报找不到字体,当字体的路径设置正确后。程序在调用setText相关方法的时候,整个系统就会dump掉,这个系统经常用dump,内存分配上可能有一些问题。如果字体的路径设置不正确系统倒是可以正确运行,最后经过查找代码和参考网上的经验决定使用qpf字体(优点就是省去了Qt解析字体这个过程,占用内存小(我怀疑系统dump是存上有问题),缺点是qpf字体字号大小是固定的,他是一个二进制的文件)。
2.进展:
Qt Help文档说明Qt是支持qpf字体的,在Qt4版本之后支持qpf2字体。把字库下的所有字体删除只留下qpf2字体还是没有办法显示(在PC机上和目标板上尝试都是不可以的)。在网上查到资料说是Qt5虽然说是支持qpf2,但是实际上是个BUG,实现根本不支持(我查看代码后发现,是一个虚函数,当调用子类的方法时就不支持qpf2,当调用父类的方法时就支持qpf2,下面会说一下这个问题),没有找到解决办法。
于是查看源码和PC跟踪程序执行过程。
3.解决:
在字体相关初始化的过程中会调用qt-everywhere-opensource-src-5.6.1\qtbase\src\platformsupport\fontdatabases\fontconfig\qfontconfigdatabase.cpp文件中的
initializeDb()方法
源码如下:
static void initializeDb()
{
QFontDatabasePrivate *db = privateDb();
// init by asking for the platformfontdb for the first time or afterinvalidation
if (!db->count)
QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFontDatabase();
if (db->reregisterAppFonts) {
for (int i = 0; i < db->applicationFonts.count(); i++) {
if (!db->applicationFonts.at(i).families.isEmpty())
registerFont(&db->applicationFonts[i]);
}
db->reregisterAppFonts = false;
}
}
populateFontDatabase()这个方法有两处实现分别见下面的源码,而initializeDb()方法中调用的实际是populateFontDatabase();方法实现际调用的是QBasicFontDatabase类中实现的。
qtbase/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp中
和
qtbase/src/gui/text/qplatformfontdatabase.cpp中
在类QBasicFontDatabase定义中可以看到QBasicFontDatabase是QPlatformFontDatabase子类
class QBasicFontDatabase : publicQPlatformFontDatabase
{
public:
// void populateFontDatabase() Q_DECL_OVERRIDE; //change on_ose
/* QFontEngine *fontEngine(const QFontDef &fontDef, void *handle)Q_DECL_OVERRIDE;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize,QFont::HintingPreference hintingPreference) Q_DECL_OVERRIDE;
QStringList addApplicationFont(const QByteArray &fontData, constQString &fileName) Q_DECL_OVERRIDE;
void releaseHandle(void *handle) Q_DECL_OVERRIDE;
static QStringList addTTFile(const QByteArray &fontData, constQByteArray &file);
*/
};
下面给出QBasicFontDatabase和QPlatformFontDatabase类中populateFontDatabase()方法的实现:
1.
voidQBasicFontDatabase::populateFontDatabase()
{
QString fontpath = fontDir();
QDir dir(fontpath);
if (!dir.exists()) {
qWarning("QFontDatabase: Cannot find font directory %s - is Qtinstalled correctly?",
qPrintable(fontpath));
return;
}
QStringList nameFilters;
nameFilters << QLatin1String("*.ttf")
<< QLatin1String("*.ttc")
<<QLatin1String("*.pfa")
<<QLatin1String("*.pfb")
<<QLatin1String("*.otf");
foreach (const QFileInfo &fi, dir.entryInfoList(nameFilters,QDir::Files)) {
const QByteArray file = QFile::encodeName(fi.absoluteFilePath());
QBasicFontDatabase::addTTFile(QByteArray(), file);
}
}
2.
voidQPlatformFontDatabase::populateFontDatabase()
{
QString fontpath = fontDir();
if(!QFile::exists(fontpath)) {
qWarning("QFontDatabase: Cannot find font directory '%s' - is Qtinstalled correctly?",
qPrintable(QDir::toNativeSeparators(fontpath)));
return;
}
QDir dir(fontpath);
dir.setNameFilters(QStringList() <<QLatin1String("*.qpf2"));
dir.refresh();
for (int i = 0; i < int(dir.count()); ++i) {
const QByteArray fileName =QFile::encodeName(dir.absoluteFilePath(dir[i]));
QFile file(QString::fromLocal8Bit(fileName));
if (file.open(QFile::ReadOnly)) {
const QByteArray fileData = file.readAll();
QByteArray *fileDataPtr = new QByteArray(fileData);
registerQPF2Font(fileData, fileDataPtr);
}
}
}
从两个类的方法中可以看到QBasicFontDatabase::populateFontDatabase()中是在对ttf等字体进行加载。而QPlatformFontDatabase::populateFontDatabase()中是对qpf2字体进行加载。所以这就解释了为什么Qt5.6.1不能使用qpf2字体。这个加载qpf2字体的方法没有被调用啊。肯定怎么也支持不了qpf2。所以果断的将QBasicFontDatabase这个类相关的文件注释掉(这样程序在运行的时候就自动调用了父类也就是QPlatformFontDatabase::populateFontDatabase()方法,这个方法是个虚函数)。这里包括qbasicfontdatabase_p.h中QBasicFontDatabase类的定义和qbasicfontdatabase.cpp整个文件的内容(#if 0 .......#endif即可)。
重新编译Qt源码,将qpf2文件放在字体目录下(lib/fonts/dejavu_sans_11_50.qpf2)。开机运行生程序可显示字符不dump掉了。剩下的工作就是制作字体了。