Qt5.6.1如何使用qpf2字型
最近在移植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掉了。剩下的工作就是製作字型了。