/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qt_windows.h" #include #include "qfont_p.h" #include "qfontengine_p.h" #include "qpaintdevice.h" #include "qlibrary.h" #include "qabstractfileengine.h" #include "qendian.h" #ifdef Q_OS_WINCE # include #endif QT_BEGIN_NAMESPACE extern HDC shared_dc(); // common dc for all fonts #ifdef MAKE_TAG #undef MAKE_TAG #endif // GetFontData expects the tags in little endian ;( #define MAKE_TAG(ch1, ch2, ch3, ch4) (\ (((quint32)(ch4)) << 24) | \ (((quint32)(ch3)) << 16) | \ (((quint32)(ch2)) << 8) | \ ((quint32)(ch1)) \ ) static HFONT stock_sysfont = 0; static bool localizedName(const QString &name) { const QChar *c = name.unicode(); for(int i = 0; i < name.length(); ++i) { if(c[i].unicode() >= 0x100) return true; } return false; } static inline quint16 getUShort(const unsigned char *p) { quint16 val; val = *p++ << 8; val |= *p; return val; } static QString getEnglishName(const uchar *table, quint32 bytes) { QString i18n_name; enum { NameRecordSize = 12, FamilyId = 1, MS_LangIdEnglish = 0x009 }; // get the name table quint16 count; quint16 string_offset; const unsigned char *names; int microsoft_id = -1; int apple_id = -1; int unicode_id = -1; if(getUShort(table) != 0) goto error; count = getUShort(table+2); string_offset = getUShort(table+4); names = table + 6; if(string_offset >= bytes || 6 + count*NameRecordSize > string_offset) goto error; for(int i = 0; i < count; ++i) { // search for the correct name entry quint16 platform_id = getUShort(names + i*NameRecordSize); quint16 encoding_id = getUShort(names + 2 + i*NameRecordSize); quint16 language_id = getUShort(names + 4 + i*NameRecordSize); quint16 name_id = getUShort(names + 6 + i*NameRecordSize); if(name_id != FamilyId) continue; enum { PlatformId_Unicode = 0, PlatformId_Apple = 1, PlatformId_Microsoft = 3 }; quint16 length = getUShort(names + 8 + i*NameRecordSize); quint16 offset = getUShort(names + 10 + i*NameRecordSize); if(DWORD(string_offset + offset + length) >= bytes) continue; if ((platform_id == PlatformId_Microsoft && (encoding_id == 0 || encoding_id == 1)) && (language_id & 0x3ff) == MS_LangIdEnglish && microsoft_id == -1) microsoft_id = i; // not sure if encoding id 4 for Unicode is utf16 or ucs4... else if(platform_id == PlatformId_Unicode && encoding_id < 4 && unicode_id == -1) unicode_id = i; else if(platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0) apple_id = i; } { bool unicode = false; int id = -1; if(microsoft_id != -1) { id = microsoft_id; unicode = true; } else if(apple_id != -1) { id = apple_id; unicode = false; } else if (unicode_id != -1) { id = unicode_id; unicode = true; } if(id != -1) { quint16 length = getUShort(names + 8 + id*NameRecordSize); quint16 offset = getUShort(names + 10 + id*NameRecordSize); if(unicode) { // utf16 length /= 2; i18n_name.resize(length); QChar *uc = (QChar *) i18n_name.unicode(); const unsigned char *string = table + string_offset + offset; for(int i = 0; i < length; ++i) uc[i] = getUShort(string + 2*i); } else { // Apple Roman i18n_name.resize(length); QChar *uc = (QChar *) i18n_name.unicode(); const unsigned char *string = table + string_offset + offset; for(int i = 0; i < length; ++i) uc[i] = QLatin1Char(string[i]); } } } error: //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data()); return i18n_name; } static QString getEnglishName(const QString &familyName) { QString i18n_name; HDC hdc = GetDC( 0 ); HFONT hfont; QT_WA( { LOGFONTW lf; memset( &lf, 0, sizeof( LOGFONTW ) ); memcpy( lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length())*sizeof(QChar) ); lf.lfCharSet = DEFAULT_CHARSET; hfont = CreateFontIndirectW( &lf ); }, { LOGFONTA lf; memset( &lf, 0, sizeof( LOGFONTA ) ); QByteArray lfam = familyName.toLocal8Bit(); memcpy( lf.lfFaceName, lfam, qMin(LF_FACESIZE, lfam.size()) ); lf.lfCharSet = DEFAULT_CHARSET; hfont = CreateFontIndirectA( &lf ); } ); if(!hfont) { ReleaseDC(0, hdc); return QString(); } HGDIOBJ oldobj = SelectObject( hdc, hfont ); const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' ); // get the name table unsigned char *table = 0; DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 ); if ( bytes == GDI_ERROR ) { // ### Unused variable /* int err = GetLastError(); */ goto error; } table = new unsigned char[bytes]; GetFontData(hdc, name_tag, 0, table, bytes); if ( bytes == GDI_ERROR ) goto error; i18n_name = getEnglishName(table, bytes); error: delete [] table; SelectObject( hdc, oldobj ); DeleteObject( hfont ); ReleaseDC( 0, hdc ); //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data()); return i18n_name; } static void getFontSignature(const QString &familyName, NEWTEXTMETRICEX *textmetric, FONTSIGNATURE *signature) { QT_WA({ Q_UNUSED(familyName); *signature = textmetric->ntmFontSig; }, { // the textmetric structure we get from EnumFontFamiliesEx on Win9x has // a FONTSIGNATURE, but that one is uninitialized and doesn't work. Have to go // the hard way and load the font to find out. HDC hdc = GetDC(0); LOGFONTA lf; memset(&lf, 0, sizeof(LOGFONTA)); QByteArray lfam = familyName.toLocal8Bit(); memcpy(lf.lfFaceName, lfam.data(), qMin(LF_FACESIZE, lfam.length())); lf.lfCharSet = DEFAULT_CHARSET; HFONT hfont = CreateFontIndirectA(&lf); HGDIOBJ oldobj = SelectObject(hdc, hfont); GetTextCharsetInfo(hdc, signature, 0); SelectObject(hdc, oldobj); DeleteObject(hfont); ReleaseDC(0, hdc); }); } static void addFontToDatabase(QString familyName, const QString &scriptName, TEXTMETRIC *textmetric, const FONTSIGNATURE *signature, int type) { const int script = -1; const QString foundryName; Q_UNUSED(script); bool italic = false; int weight; bool fixed; bool ttf; bool scalable; int size; // QString escript = QString::fromUtf16((ushort *)f->elfScript); // qDebug("script=%s", escript.latin1()); QT_WA({ NEWTEXTMETRIC *tm = (NEWTEXTMETRIC *)textmetric; fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH); ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE); scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); size = scalable ? SMOOTH_SCALABLE : tm->tmHeight; italic = tm->tmItalic; weight = tm->tmWeight; } , { NEWTEXTMETRICA *tm = (NEWTEXTMETRICA *)textmetric; fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH); ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE); scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); size = scalable ? SMOOTH_SCALABLE : tm->tmHeight; italic = tm->tmItalic; weight = tm->tmWeight; }); // the "@family" fonts are just the same as "family". Ignore them. if (familyName[0] != QLatin1Char('@') && !familyName.startsWith(QLatin1String("WST_"))) { QtFontStyle::Key styleKey; styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal; if (weight < 400) styleKey.weight = QFont::Light; else if (weight < 600) styleKey.weight = QFont::Normal; else if (weight < 700) styleKey.weight = QFont::DemiBold; else if (weight < 800) styleKey.weight = QFont::Bold; else styleKey.weight = QFont::Black; QtFontFamily *family = privateDb()->family(familyName, true); if(ttf && localizedName(familyName) && family->english_name.isEmpty()) family->english_name = getEnglishName(familyName); QtFontFoundry *foundry = family->foundry(foundryName, true); QtFontStyle *style = foundry->style(styleKey, true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); // add fonts windows can generate for us: if (styleKey.weight <= QFont::DemiBold) { QtFontStyle::Key key(styleKey); key.weight = QFont::Bold; QtFontStyle *style = foundry->style(key, true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } if (styleKey.style != QFont::StyleItalic) { QtFontStyle::Key key(styleKey); key.style = QFont::StyleItalic; QtFontStyle *style = foundry->style(key, true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } if (styleKey.weight <= QFont::DemiBold && styleKey.style != QFont::StyleItalic) { QtFontStyle::Key key(styleKey); key.weight = QFont::Bold; key.style = QFont::StyleItalic; QtFontStyle *style = foundry->style(key, true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } family->fixedPitch = fixed; if (!family->writingSystemCheck && type & TRUETYPE_FONTTYPE) { quint32 unicodeRange[4] = { signature->fsUsb[0], signature->fsUsb[1], signature->fsUsb[2], signature->fsUsb[3] }; #ifdef Q_OS_WINCE if (signature->fsUsb[0] == 0) { // If the unicode ranges bit mask is zero then // EnumFontFamiliesEx failed to determine it properly. // In this case we just pretend that the font supports all languages. unicodeRange[0] = 0xbfffffff; // second most significant bit must be zero unicodeRange[1] = 0xffffffff; unicodeRange[2] = 0xffffffff; unicodeRange[3] = 0xffffffff; } #endif quint32 codePageRange[2] = { signature->fsCsb[0], signature->fsCsb[1] }; QList systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); for (int i = 0; i < systems.count(); ++i) family->writingSystems[systems.at(i)] = QtFontFamily::Supported; } else if (!family->writingSystemCheck) { //qDebug("family='%s' script=%s", family->name.latin1(), script.latin1()); if (scriptName == QLatin1String("Western") || scriptName == QLatin1String("Baltic") || scriptName == QLatin1String("Central European") || scriptName == QLatin1String("Turkish") || scriptName == QLatin1String("Vietnamese")) family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Thai")) family->writingSystems[QFontDatabase::Thai] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Symbol") || scriptName == QLatin1String("Other")) family->writingSystems[QFontDatabase::Symbol] = QtFontFamily::Supported; else if (scriptName == QLatin1String("OEM/Dos")) family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported; else if (scriptName == QLatin1String("CHINESE_GB2312")) family->writingSystems[QFontDatabase::SimplifiedChinese] = QtFontFamily::Supported; else if (scriptName == QLatin1String("CHINESE_BIG5")) family->writingSystems[QFontDatabase::TraditionalChinese] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Cyrillic")) family->writingSystems[QFontDatabase::Cyrillic] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Hangul")) family->writingSystems[QFontDatabase::Korean] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Hebrew")) family->writingSystems[QFontDatabase::Hebrew] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Greek")) family->writingSystems[QFontDatabase::Greek] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Japanese")) family->writingSystems[QFontDatabase::Japanese] = QtFontFamily::Supported; else if (scriptName == QLatin1String("Arabic")) family->writingSystems[QFontDatabase::Arabic] = QtFontFamily::Supported; } } } static int CALLBACK storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric, int type, LPARAM /*p*/) { QString familyName; QT_WA({ familyName = QString::fromUtf16((ushort*)f->elfLogFont.lfFaceName); },{ ENUMLOGFONTEXA *fa = (ENUMLOGFONTEXA *)f; familyName = QString::fromLocal8Bit(fa->elfLogFont.lfFaceName); }); QString script = QT_WA_INLINE(QString::fromUtf16((const ushort *)f->elfScript), QString::fromLocal8Bit((const char *)((ENUMLOGFONTEXA *)f)->elfScript)); FONTSIGNATURE signature; getFontSignature(familyName, textmetric, &signature); // NEWTEXTMETRICEX is a NEWTEXTMETRIC, which according to the documentation is // identical to a TEXTMETRIC except for the last four members, which we don't use // anyway addFontToDatabase(familyName, script, (TEXTMETRIC *)textmetric, &signature, type); // keep on enumerating return 1; } static void populate_database(const QString& fam) { QFontDatabasePrivate *d = privateDb(); if (!d) return; QtFontFamily *family = 0; if(!fam.isEmpty()) { family = d->family(fam); if(family && family->loaded) return; } else if (d->count) { return; } HDC dummy = GetDC(0); QT_WA({ LOGFONT lf; lf.lfCharSet = DEFAULT_CHARSET; if (fam.isNull()) { lf.lfFaceName[0] = 0; } else { memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded } lf.lfPitchAndFamily = 0; EnumFontFamiliesEx(dummy, &lf, (FONTENUMPROC)storeFont, (LPARAM)privateDb(), 0); } , { LOGFONTA lf; lf.lfCharSet = DEFAULT_CHARSET; if (fam.isNull()) { lf.lfFaceName[0] = 0; } else { QByteArray lname = fam.toLocal8Bit(); memcpy(lf.lfFaceName,lname.data(), qMin(lname.length()+1,32)); // 32 = Windows hard-coded } lf.lfPitchAndFamily = 0; EnumFontFamiliesExA(dummy, &lf, (FONTENUMPROCA)storeFont, (LPARAM)privateDb(), 0); }); ReleaseDC(0, dummy); for (int i = 0; i < d->applicationFonts.count(); ++i) { QFontDatabasePrivate::ApplicationFont fnt = d->applicationFonts.at(i); if (!fnt.memoryFont) continue; for (int j = 0; j < fnt.families.count(); ++j) { const QString familyName = fnt.families.at(j); HDC hdc = GetDC(0); HFONT hfont; QT_WA({ LOGFONTW lf; memset(&lf, 0, sizeof(LOGFONTW)); memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.size())); lf.lfCharSet = DEFAULT_CHARSET; hfont = CreateFontIndirectW(&lf); } , { LOGFONTA lf; memset(&lf, 0, sizeof(LOGFONTA)); QByteArray lfam = familyName.toLocal8Bit(); memcpy(lf.lfFaceName, lfam.data(), qMin(LF_FACESIZE, lfam.length())); lf.lfCharSet = DEFAULT_CHARSET; hfont = CreateFontIndirectA(&lf); }); HGDIOBJ oldobj = SelectObject(hdc, hfont); TEXTMETRIC textMetrics; GetTextMetrics(hdc, &textMetrics); addFontToDatabase(familyName, QString(), &textMetrics, &fnt.signatures.at(j), TRUETYPE_FONTTYPE); SelectObject(hdc, oldobj); DeleteObject(hfont); ReleaseDC(0, hdc); } } if(!fam.isEmpty()) { family = d->family(fam); if(family) { if(!family->writingSystemCheck) { } family->loaded = true; } } } static void initializeDb() { QFontDatabasePrivate *db = privateDb(); if (!db || db->count) return; populate_database(QString()); #ifdef QFONTDATABASE_DEBUG // print the database for (int f = 0; f < db->count; f++) { QtFontFamily *family = db->families[f]; qDebug(" %s: %p", family->name.latin1(), family); populate_database(family->name); #if 0 qDebug(" scripts supported:"); for (int i = 0; i < QUnicodeTables::ScriptCount; i++) if(family->writingSystems[i] & QtFontFamily::Supported) qDebug(" %d", i); for (int fd = 0; fd < family->count; fd++) { QtFontFoundry *foundry = family->foundries[fd]; qDebug(" %s", foundry->name.latin1()); for (int s = 0; s < foundry->count; s++) { QtFontStyle *style = foundry->styles[s]; qDebug(" style: style=%d weight=%d smooth=%d", style->key.style, style->key.weight, style->smoothScalable ); if(!style->smoothScalable) { for(int i = 0; i < style->count; ++i) { qDebug(" %d", style->pixelSizes[i].pixelSize); } } } } #endif } #endif // QFONTDATABASE_DEBUG } static inline void load(const QString &family = QString(), int = -1) { populate_database(family); } // -------------------------------------------------------------------------------------- // font loader // -------------------------------------------------------------------------------------- static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, const QFontPrivate *fp) { fe->fontDef = request; // most settings are equal HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fp->hdc) ? fp->hdc : shared_dc(); SelectObject(dc, fe->hfont); QT_WA({ TCHAR n[64]; GetTextFaceW(dc, 64, n); fe->fontDef.family = QString::fromUtf16((ushort*)n); fe->fontDef.fixedPitch = !(fe->tm.w.tmPitchAndFamily & TMPF_FIXED_PITCH); } , { char an[64]; GetTextFaceA(dc, 64, an); fe->fontDef.family = QString::fromLocal8Bit(an); fe->fontDef.fixedPitch = !(fe->tm.a.tmPitchAndFamily & TMPF_FIXED_PITCH); }); if (fe->fontDef.pointSize < 0) { fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / fp->dpi; } else if (fe->fontDef.pixelSize == -1) { fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * fp->dpi / 72.); } } static const char *other_tryFonts[] = { "Arial", "MS UI Gothic", "Gulim", "SimSun", "PMingLiU", "Arial Unicode MS", 0 }; static const char *jp_tryFonts [] = { "MS UI Gothic", "Arial", "Gulim", "SimSun", "PMingLiU", "Arial Unicode MS", 0 }; static const char *ch_CN_tryFonts [] = { "SimSun", "Arial", "PMingLiU", "Gulim", "MS UI Gothic", "Arial Unicode MS", 0 }; static const char *ch_TW_tryFonts [] = { "PMingLiU", "Arial", "SimSun", "Gulim", "MS UI Gothic", "Arial Unicode MS", 0 }; static const char *kr_tryFonts[] = { "Gulim", "Arial", "PMingLiU", "SimSun", "MS UI Gothic", "Arial Unicode MS", 0 }; static const char **tryFonts = 0; static inline HFONT systemFont() { if (stock_sysfont == 0) stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT); return stock_sysfont; } #if !defined(DEFAULT_GUI_FONT) #define DEFAULT_GUI_FONT 17 #endif static QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &request, const QtFontDesc *desc, const QStringList &family_list) { LOGFONT lf; memset(&lf, 0, sizeof(LOGFONT)); bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fp->hdc; HDC hdc = shared_dc(); QString font_name = desc->family->name; if (useDevice) { hdc = fp->hdc; font_name = request.family; } bool stockFont = false; HFONT hfont = 0; if (fp->rawMode) { // will choose a stock font int f, deffnt; // ### why different? if ((QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) || QSysInfo::WindowsVersion == QSysInfo::WV_32s) deffnt = SYSTEM_FONT; else deffnt = DEFAULT_GUI_FONT; QString fam = desc->family->name.toLower(); if (fam == QLatin1String("default")) f = deffnt; else if (fam == QLatin1String("system")) f = SYSTEM_FONT; #ifndef Q_OS_WINCE else if (fam == QLatin1String("system_fixed")) f = SYSTEM_FIXED_FONT; else if (fam == QLatin1String("ansi_fixed")) f = ANSI_FIXED_FONT; else if (fam == QLatin1String("ansi_var")) f = ANSI_VAR_FONT; else if (fam == QLatin1String("device_default")) f = DEVICE_DEFAULT_FONT; else if (fam == QLatin1String("oem_fixed")) f = OEM_FIXED_FONT; #endif else if (fam[0] == QLatin1Char('#')) f = fam.right(fam.length()-1).toInt(); else f = deffnt; hfont = (HFONT)GetStockObject(f); if (!hfont) { qErrnoWarning("QFontEngine::loadEngine: GetStockObject failed"); hfont = systemFont(); } stockFont = true; } else { int hint = FF_DONTCARE; switch (request.styleHint) { case QFont::Helvetica: hint = FF_SWISS; break; case QFont::Times: hint = FF_ROMAN; break; case QFont::Courier: hint = FF_MODERN; break; case QFont::OldEnglish: hint = FF_DECORATIVE; break; case QFont::System: hint = FF_MODERN; break; default: break; } lf.lfHeight = -request.pixelSize; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; if (desc->style->key.weight == 50) lf.lfWeight = FW_DONTCARE; else lf.lfWeight = (desc->style->key.weight*900)/99; lf.lfItalic = (desc->style->key.style != QFont::StyleNormal); lf.lfCharSet = DEFAULT_CHARSET; int strat = OUT_DEFAULT_PRECIS; if (request.styleStrategy & QFont::PreferBitmap) { strat = OUT_RASTER_PRECIS; #ifndef Q_OS_WINCE } else if (request.styleStrategy & QFont::PreferDevice) { strat = OUT_DEVICE_PRECIS; } else if (request.styleStrategy & QFont::PreferOutline) { QT_WA({ strat = OUT_OUTLINE_PRECIS; } , { strat = OUT_TT_PRECIS; }); } else if (request.styleStrategy & QFont::ForceOutline) { strat = OUT_TT_ONLY_PRECIS; #endif } lf.lfOutPrecision = strat; int qual = DEFAULT_QUALITY; if (request.styleStrategy & QFont::PreferMatch) qual = DRAFT_QUALITY; #ifndef Q_OS_WINCE else if (request.styleStrategy & QFont::PreferQuality) qual = PROOF_QUALITY; #endif if (request.styleStrategy & QFont::PreferAntialias) { if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) qual = 5; // == CLEARTYPE_QUALITY; else qual = ANTIALIASED_QUALITY; } else if (request.styleStrategy & QFont::NoAntialias) { qual = NONANTIALIASED_QUALITY; } lf.lfQuality = qual; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH | hint; QString fam = font_name; if(fam.isEmpty()) fam = QLatin1String("MS Sans Serif"); if ((fam == QLatin1String("MS Sans Serif")) && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) { fam = QLatin1String("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale } if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap)) fam = QLatin1String("Courier New"); QT_WA({ memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded hfont = CreateFontIndirect(&lf); } , { // LOGFONTA and LOGFONTW are binary compatible QByteArray lname = fam.toLocal8Bit(); memcpy(lf.lfFaceName,lname.data(), qMin(lname.length()+1,32)); // 32 = Windows hard-coded hfont = CreateFontIndirectA((LOGFONTA*)&lf); }); if (!hfont) qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed"); stockFont = (hfont == 0); bool ttf = false; int avWidth = 0; BOOL res; HGDIOBJ oldObj = SelectObject(hdc, hfont); QT_WA({ TEXTMETRICW tm; res = GetTextMetricsW(hdc, &tm); avWidth = tm.tmAveCharWidth; ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; } , { TEXTMETRICA tm; res = GetTextMetricsA(hdc, &tm); avWidth = tm.tmAveCharWidth; ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; }); SelectObject(hdc, oldObj); if (hfont && (!ttf || request.stretch != 100)) { DeleteObject(hfont); if (!res) qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); lf.lfWidth = avWidth * request.stretch/100; QT_WA({ hfont = CreateFontIndirect(&lf); } , { hfont = CreateFontIndirectA((LOGFONTA*)&lf); }); if (!hfont) qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); } #ifndef Q_OS_WINCE if (hfont == 0) { hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); stockFont = true; } #else if (hfont == 0) { hfont = (HFONT)GetStockObject(SYSTEM_FONT); stockFont = true; } #endif } QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); // Also check for OpenType tables when using complex scripts // ### TODO: This only works for scripts that require OpenType. More generally // for scripts that do not require OpenType we should just look at the list of // supported writing systems in the font's OS/2 table. if (scriptRequiresOpenType(script)) { HB_Face hbFace = few->harfbuzzFace(); if (!hbFace || !hbFace->supported_scripts[script]) { FM_DEBUG(" OpenType support missing for script\n"); delete few; return 0; } } QFontEngine *fe = few; initFontInfo(few, request, fp); if(script == QUnicodeTables::Common && !(request.styleStrategy & QFont::NoFontMerging) && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) { if(!tryFonts) { LANGID lid = GetUserDefaultLangID(); switch( lid&0xff ) { case LANG_CHINESE: // Chinese (Taiwan) if ( lid == 0x0804 ) // Taiwan tryFonts = ch_TW_tryFonts; else tryFonts = ch_CN_tryFonts; break; case LANG_JAPANESE: tryFonts = jp_tryFonts; break; case LANG_KOREAN: tryFonts = kr_tryFonts; break; default: tryFonts = other_tryFonts; break; } } QStringList fm = QFontDatabase().families(); QStringList list = family_list; const char **tf = tryFonts; while(tf && *tf) { if(fm.contains(QLatin1String(*tf))) list << QLatin1String(*tf); ++tf; } QFontEngine *mfe = new QFontEngineMultiWin(few, list); mfe->fontDef = fe->fontDef; fe = mfe; } return fe; } const char *styleHint(const QFontDef &request) { const char *stylehint = 0; switch (request.styleHint) { case QFont::SansSerif: stylehint = "Arial"; break; case QFont::Serif: stylehint = "Times New Roman"; break; case QFont::TypeWriter: stylehint = "Courier New"; break; default: if (request.fixedPitch) stylehint = "Courier New"; break; } return stylehint; } static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &req) { // list of families to try QStringList family_list = familyList(req); if(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based && req.family.toLower() == QLatin1String("ms sans serif")) { // small hack for Dos based machines to get the right font for non // latin text when using the default font. family_list << QLatin1String("Arial"); } const char *stylehint = styleHint(d->request); if (stylehint) family_list << QLatin1String(stylehint); // append the default fallback font for the specified script // family_list << ... ; ########### // add the default family QString defaultFamily = QApplication::font().family(); if (! family_list.contains(defaultFamily)) family_list << defaultFamily; // add QFont::defaultFamily() to the list, for compatibility with // previous versions family_list << QApplication::font().defaultFamily(); // null family means find the first font matching the specified script family_list << QString(); QtFontDesc desc; QFontEngine *fe = 0; QList blacklistedFamilies; while (!fe) { for (int i = 0; i < family_list.size(); ++i) { QString family, foundry; parseFontName(family_list.at(i), foundry, family); FM_DEBUG("loadWin: >>>>>>>>>>>>>>trying to match '%s'", family.toLatin1().data()); QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies); if (desc.family) break; } if (!desc.family) break; fe = loadEngine(script, d, req, &desc, family_list); if (!fe) blacklistedFamilies.append(desc.familyIndex); } return fe; } void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks if (!qApp) qWarning("QFontDatabase::load: Must construct QApplication first"); Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount); // normalize the request to get better caching QFontDef req = d->request; if (req.pixelSize <= 0) req.pixelSize = qMax(1, qRound(req.pointSize * d->dpi / 72.)); req.pointSize = 0; if (req.weight == 0) req.weight = QFont::Normal; if (req.stretch == 0) req.stretch = 100; QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen); if (!d->engineData) getEngineData(d, key); // the cached engineData could have already loaded the engine we want if (d->engineData->engines[script]) return; QFontEngine *fe = QFontCache::instance()->findEngine(key); // set it to the actual pointsize, so QFontInfo will do the right thing req.pointSize = req.pixelSize*72./d->dpi; if (!fe) { if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { fe = new QTestFontEngine(req.pixelSize); fe->fontDef = req; } else { QMutexLocker locker(fontDatabaseMutex()); if (!privateDb()->count) initializeDb(); fe = loadWin(d, script, req); } if (!fe) { fe = new QFontEngineBox(req.pixelSize); fe->fontDef = QFontDef(); } } d->engineData->engines[script] = fe; fe->ref.ref(); QFontCache::instance()->insertEngine(key, fe); } #if !defined(FR_PRIVATE) #define FR_PRIVATE 0x10 #endif typedef int (WINAPI *PtrAddFontResourceExW)(LPCWSTR, DWORD, PVOID); typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *); typedef BOOL (WINAPI *PtrRemoveFontResourceExW)(LPCWSTR, DWORD, PVOID); typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE); static QList getTrueTypeFontOffsets(const uchar *fontData) { QList offsets; const quint32 headerTag = *reinterpret_cast(fontData); if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { if (headerTag != MAKE_TAG(0, 1, 0, 0) && headerTag != MAKE_TAG('O', 'T', 'T', 'O') && headerTag != MAKE_TAG('t', 'r', 'u', 'e') && headerTag != MAKE_TAG('t', 'y', 'p', '1')) return offsets; offsets << 0; return offsets; } const quint32 numFonts = qFromBigEndian(fontData + 8); for (uint i = 0; i < numFonts; ++i) { offsets << qFromBigEndian(fontData + 12 + i * 4); } return offsets; } static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) { const quint16 numTables = qFromBigEndian(data + 4); for (uint i = 0; i < numTables; ++i) { const quint32 offset = 12 + 16 * i; if (*reinterpret_cast(data + offset) == tag) { *table = fileBegin + qFromBigEndian(data + offset + 8); *length = qFromBigEndian(data + offset + 12); return; } } *table = 0; *length = 0; return; } static void getFamiliesAndSignatures(const QByteArray &fontData, QFontDatabasePrivate::ApplicationFont *appFont) { const uchar *data = reinterpret_cast(fontData.constData()); QList offsets = getTrueTypeFontOffsets(data); if (offsets.isEmpty()) return; for (int i = 0; i < offsets.count(); ++i) { const uchar *font = data + offsets.at(i); const uchar *table; quint32 length; getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); if (!table) continue; QString name = getEnglishName(table, length); if (name.isEmpty()) continue; appFont->families << name; FONTSIGNATURE signature; getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); if (table && length >= 86) { // See also qfontdatabase_mac.cpp, offsets taken from OS/2 table in the TrueType spec signature.fsUsb[0] = qFromBigEndian(table + 42); signature.fsUsb[1] = qFromBigEndian(table + 46); signature.fsUsb[2] = qFromBigEndian(table + 50); signature.fsUsb[3] = qFromBigEndian(table + 54); signature.fsCsb[0] = qFromBigEndian(table + 78); signature.fsCsb[1] = qFromBigEndian(table + 82); } appFont->signatures << signature; } } static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) { if(!fnt->data.isEmpty()) { #ifndef Q_OS_WINCE PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"), "AddFontMemResourceEx"); if (!ptrAddFontMemResourceEx) return; #endif getFamiliesAndSignatures(fnt->data, fnt); if (fnt->families.isEmpty()) return; #ifdef Q_OS_WINCE HANDLE handle = 0; { #ifdef QT_NO_TEMPORARYFILE TCHAR lpBuffer[MAX_PATH]; GetTempPath(MAX_PATH, lpBuffer); QString s = QString::fromUtf16((const ushort *) lpBuffer); QFile tempfile(s + QLatin1String("/font") + QString::number(GetTickCount()) + QLatin1String(".ttf")); if (!tempfile.open(QIODevice::ReadWrite)) #else QTemporaryFile tempfile(QLatin1String("XXXXXXXX.ttf")); if (!tempfile.open()) #endif return; if (tempfile.write(fnt->data) == -1) return; #ifndef QT_NO_TEMPORARYFILE tempfile.setAutoRemove(false); #endif fnt->fileName = QFileInfo(tempfile.fileName()).absoluteFilePath(); } if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) { QFile(fnt->fileName).remove(); return; } #else DWORD dummy = 0; HANDLE handle = ptrAddFontMemResourceEx((void *)fnt->data.constData(), fnt->data.size(), 0, &dummy); if (handle == 0) return; #endif fnt->handle = handle; fnt->data = QByteArray(); fnt->memoryFont = true; } else { QFile f(fnt->fileName); if (!f.open(QIODevice::ReadOnly)) return; QByteArray data = f.readAll(); f.close(); getFamiliesAndSignatures(data, fnt); #ifdef Q_OS_WINCE QFileInfo fileinfo(fnt->fileName); fnt->fileName = fileinfo.absoluteFilePath(); if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) return; #else // supported from 2000 on, so no need to deal with the *A variant PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"), "AddFontResourceExW"); if (!ptrAddFontResourceExW) return; if (ptrAddFontResourceExW((LPCWSTR)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0) return; #endif fnt->memoryFont = false; } } bool QFontDatabase::removeApplicationFont(int handle) { QMutexLocker locker(fontDatabaseMutex()); QFontDatabasePrivate *db = privateDb(); if (handle < 0 || handle >= db->applicationFonts.count()) return false; const QFontDatabasePrivate::ApplicationFont font = db->applicationFonts.at(handle); db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont(); if (font.memoryFont) { #ifdef Q_OS_WINCE bool removeSucceeded = RemoveFontResource((LPCWSTR)font.fileName.utf16()); QFile tempfile(font.fileName); tempfile.remove(); if (!removeSucceeded) return false; #else PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); if (!ptrRemoveFontMemResourceEx) return false; if (!ptrRemoveFontMemResourceEx(font.handle)) return false; #endif } else { #ifdef Q_OS_WINCE if (!RemoveFontResource((LPCWSTR)font.fileName.utf16())) return false; #else PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"), "RemoveFontResourceExW"); if (!ptrRemoveFontResourceExW) return false; if (!ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0)) return false; #endif } db->invalidate(); return true; } bool QFontDatabase::removeAllApplicationFonts() { QMutexLocker locker(fontDatabaseMutex()); QFontDatabasePrivate *db = privateDb(); for (int i = 0; i < db->applicationFonts.count(); ++i) if (!removeApplicationFont(i)) return false; return true; } bool QFontDatabase::supportsThreadedFontRendering() { return true; } QT_END_NAMESPACE