summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Knight <andrew.knight@digia.com>2014-03-15 16:49:22 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-03-18 21:37:55 +0100
commit9827f6d19817d2653ea4fbf5df30d7bb2fb6f787 (patch)
tree000b1e993b5e352a4b0bc726a3e05eda4b1cd1a6
parent336cb75d8f1de45215ed6e871cc5121a3e12c111 (diff)
WinRT: Load system fonts using DirectWrite
System-installed TrueType fonts can be read into memory and loaded into the FreeType font engine. This allows the application to be packaged without fonts, but does not work on Windows Phone where DirectWrite is not supported. Every single-file TrueType font is registered with the font database, and then loaded into memory at the point that the font is actually used. Task-number: QTBUG-37230 Change-Id: I804116e37a874cd146a0653ba4cc018f8b1cd6a4 Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
-rw-r--r--src/plugins/platforms/winrt/qwinrtfontdatabase.cpp314
-rw-r--r--src/plugins/platforms/winrt/qwinrtfontdatabase.h19
-rw-r--r--src/plugins/platforms/winrt/winrt.pro5
3 files changed, 337 insertions, 1 deletions
diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp
index c7fa339fad..3da87de708 100644
--- a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp
+++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp
@@ -44,6 +44,14 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
+#ifndef Q_OS_WINPHONE
+#include <QtCore/QUuid>
+#include <QtGui/private/qfontengine_ft_p.h>
+#include <dwrite_1.h>
+#include <wrl.h>
+using namespace Microsoft::WRL;
+#endif // !Q_OS_WINPHONE
+
QT_BEGIN_NAMESPACE
QString QWinRTFontDatabase::fontDir() const
@@ -54,11 +62,315 @@ QString QWinRTFontDatabase::fontDir() const
const QString applicationDirPath = QCoreApplication::applicationDirPath();
fontDirectory = applicationDirPath + QLatin1String("/fonts");
if (!QFile::exists(fontDirectory)) {
- qWarning("No fonts directory found in application package.");
+#ifndef Q_OS_WINPHONE
+ if (m_fonts.isEmpty())
+#endif
+ qWarning("No fonts directory found in application package.");
fontDirectory = applicationDirPath;
}
}
return fontDirectory;
}
+#ifndef Q_OS_WINPHONE
+
+QWinRTFontDatabase::~QWinRTFontDatabase()
+{
+ foreach (IDWriteFontFile *fontFile, m_fonts.keys())
+ fontFile->Release();
+}
+
+QFont QWinRTFontDatabase::defaultFont() const
+{
+ return QFont(QStringLiteral("Segoe UI"));
+}
+
+void QWinRTFontDatabase::populateFontDatabase()
+{
+ ComPtr<IDWriteFactory1> factory;
+ HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory1), &factory);
+ if (FAILED(hr)) {
+ qWarning("Failed to create DirectWrite factory: %s", qPrintable(qt_error_string(hr)));
+ QBasicFontDatabase::populateFontDatabase();
+ return;
+ }
+
+ ComPtr<IDWriteFontCollection> fontCollection;
+ hr = factory->GetSystemFontCollection(&fontCollection);
+ if (FAILED(hr)) {
+ qWarning("Failed to open system font collection: %s", qPrintable(qt_error_string(hr)));
+ QBasicFontDatabase::populateFontDatabase();
+ return;
+ }
+
+ int fontFamilyCount = fontCollection->GetFontFamilyCount();
+ for (int i = 0; i < fontFamilyCount; ++i) {
+ ComPtr<IDWriteFontFamily> fontFamily;
+ hr = fontCollection->GetFontFamily(i, &fontFamily);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font family: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+
+ ComPtr<IDWriteLocalizedStrings> names;
+ hr = fontFamily->GetFamilyNames(&names);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font family names: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ quint32 familyNameLength;
+ hr = names->GetStringLength(0, &familyNameLength);
+ if (FAILED(hr)) {
+ qWarning("Unable to get family name length: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ QVector<wchar_t> familyBuffer(familyNameLength + 1);
+ hr = names->GetString(0, familyBuffer.data(), familyBuffer.size());
+ if (FAILED(hr)) {
+ qWarning("Unable to create font family name: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ QString familyName = QString::fromWCharArray(familyBuffer.data(), familyNameLength);
+
+ int fontCount = fontFamily->GetFontCount();
+ for (int j = 0; j < fontCount; ++j) {
+ ComPtr<IDWriteFont> font;
+ hr = fontFamily->GetFont(j, &font);
+ if (FAILED(hr)) {
+ qWarning("Unable to get base font: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+
+ ComPtr<IDWriteFontFace> baseFontFace;
+ hr = font->CreateFontFace(&baseFontFace);
+ if (FAILED(hr)) {
+ qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ ComPtr<IDWriteFontFace1> fontFace;
+ hr = baseFontFace.As(&fontFace);
+ if (FAILED(hr)) {
+ qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+
+ // Only try to load true-type fonts
+ DWRITE_FONT_FACE_TYPE type = fontFace->GetType();
+ if (!(type == DWRITE_FONT_FACE_TYPE_TRUETYPE
+ || type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION)) {
+ continue;
+ }
+
+ // We can't deal with multi-file fonts
+ quint32 fileCount;
+ hr = fontFace->GetFiles(&fileCount, NULL);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ if (fileCount != 1) // Should not happen as we only look at TT fonts
+ continue;
+
+ ComPtr<IDWriteLocalizedStrings> informationalStrings;
+ BOOL exists;
+ hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER,
+ &informationalStrings, &exists);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ QString foundryName;
+ if (exists) {
+ quint32 length;
+ hr = informationalStrings->GetStringLength(0, &length);
+ if (FAILED(hr))
+ qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr)));
+ if (SUCCEEDED(hr)) {
+ QVector<wchar_t> buffer(length + 1);
+ hr = informationalStrings->GetString(0, buffer.data(), buffer.size());
+ if (FAILED(hr))
+ qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr)));
+ if (SUCCEEDED(hr))
+ foundryName = QString::fromWCharArray(buffer.data(), length);
+ }
+ }
+
+ QFont::Weight weight;
+ switch (font->GetWeight()) {
+ case DWRITE_FONT_WEIGHT_THIN:
+ case DWRITE_FONT_WEIGHT_EXTRA_LIGHT:
+ case DWRITE_FONT_WEIGHT_LIGHT:
+ case DWRITE_FONT_WEIGHT_SEMI_LIGHT:
+ weight = QFont::Light;
+ break;
+ default:
+ case DWRITE_FONT_WEIGHT_NORMAL:
+ case DWRITE_FONT_WEIGHT_MEDIUM:
+ weight = QFont::Normal;
+ break;
+ case DWRITE_FONT_WEIGHT_DEMI_BOLD:
+ weight = QFont::DemiBold;
+ break;
+ case DWRITE_FONT_WEIGHT_BOLD:
+ case DWRITE_FONT_WEIGHT_EXTRA_BOLD:
+ weight = QFont::Bold;
+ break;
+ case DWRITE_FONT_WEIGHT_BLACK:
+ case DWRITE_FONT_WEIGHT_EXTRA_BLACK:
+ weight = QFont::Black;
+ break;
+ }
+
+ QFont::Style style;
+ switch (font->GetStyle()) {
+ default:
+ case DWRITE_FONT_STYLE_NORMAL:
+ style = QFont::StyleNormal;
+ break;
+ case DWRITE_FONT_STYLE_OBLIQUE:
+ style = QFont::StyleOblique;
+ break;
+ case DWRITE_FONT_STYLE_ITALIC:
+ style = QFont::StyleItalic;
+ break;
+ }
+
+ QFont::Stretch stretch;
+ switch (font->GetStretch()) {
+ default:
+ case DWRITE_FONT_STRETCH_UNDEFINED:
+ case DWRITE_FONT_STRETCH_NORMAL:
+ stretch = QFont::Unstretched;
+ break;
+ case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
+ stretch = QFont::UltraCondensed;
+ break;
+ case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
+ stretch = QFont::ExtraCondensed;
+ break;
+ case DWRITE_FONT_STRETCH_CONDENSED:
+ stretch = QFont::Condensed;
+ break;
+ case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
+ stretch = QFont::SemiCondensed;
+ break;
+ case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
+ stretch = QFont::SemiExpanded;
+ break;
+ case DWRITE_FONT_STRETCH_EXPANDED:
+ stretch = QFont::Expanded;
+ break;
+ case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
+ stretch = QFont::ExtraExpanded;
+ break;
+ case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
+ stretch = QFont::UltraExpanded;
+ break;
+ }
+
+ const bool fixedPitch = fontFace->IsMonospacedFont();
+
+ quint32 unicodeRange[4];
+ quint32 actualRangeCount;
+ hr = fontFace->GetUnicodeRanges(
+ 2, reinterpret_cast<DWRITE_UNICODE_RANGE *>(unicodeRange), &actualRangeCount);
+ if (FAILED(hr) && hr != E_NOT_SUFFICIENT_BUFFER) { // Ignore insufficient buffer; we only need 4 indices
+ qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+ quint32 codePageRange[2] = { 0, 0 };
+ QSupportedWritingSystems writingSystems =
+ QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
+
+ IDWriteFontFile *fontFile;
+ hr = fontFace->GetFiles(&fileCount, &fontFile);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr)));
+ continue;
+ }
+
+ FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() };
+ m_fonts.insert(fontFile, description);
+ registerFont(familyName, QString(), foundryName, weight, style, stretch,
+ true, true, 0, fixedPitch, writingSystems, fontFile);
+ }
+ }
+
+ QBasicFontDatabase::populateFontDatabase();
+}
+
+QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
+{
+ IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle);
+ if (!m_fonts.contains(fontFile))
+ return QBasicFontDatabase::fontEngine(fontDef, handle);
+
+ const void *referenceKey;
+ quint32 referenceKeySize;
+ HRESULT hr = fontFile->GetReferenceKey(&referenceKey, &referenceKeySize);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file reference key: %s", qPrintable(qt_error_string(hr)));
+ return 0;
+ }
+
+ ComPtr<IDWriteFontFileLoader> loader;
+ hr = fontFile->GetLoader(&loader);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file loader: %s", qPrintable(qt_error_string(hr)));
+ return 0;
+ }
+
+ ComPtr<IDWriteFontFileStream> stream;
+ hr =loader->CreateStreamFromKey(referenceKey, referenceKeySize, &stream);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file stream: %s", qPrintable(qt_error_string(hr)));
+ return 0;
+ }
+
+ quint64 fileSize;
+ hr = stream->GetFileSize(&fileSize);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file size: %s", qPrintable(qt_error_string(hr)));
+ return 0;
+ }
+
+ const void *data;
+ void *context;
+ hr = stream->ReadFileFragment(&data, 0, fileSize, &context);
+ if (FAILED(hr)) {
+ qWarning("Unable to get font file data: %s", qPrintable(qt_error_string(hr)));
+ return 0;
+ }
+ const QByteArray fontData((const char *)data, fileSize);
+ stream->ReleaseFileFragment(context);
+
+ QFontEngine::FaceId faceId;
+ const FontDescription description = m_fonts.value(fontFile);
+ faceId.uuid = description.uuid;
+ faceId.index = description.index;
+ const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
+ QFontEngineFT::GlyphFormat format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
+ QFontEngineFT *engine = new QFontEngineFT(fontDef);
+ if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
+ delete engine;
+ return 0;
+ }
+
+ return engine;
+}
+
+void QWinRTFontDatabase::releaseHandle(void *handle)
+{
+ IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle);
+ if (m_fonts.contains(fontFile)) {
+ m_fonts.remove(fontFile);
+ fontFile->Release();
+ return;
+ }
+
+ QBasicFontDatabase::releaseHandle(handle);
+}
+
+#endif // !Q_OS_WINPHONE
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h
index 49e32470c2..6f194a10cc 100644
--- a/src/plugins/platforms/winrt/qwinrtfontdatabase.h
+++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h
@@ -46,10 +46,29 @@
QT_BEGIN_NAMESPACE
+#ifndef Q_OS_WINPHONE
+struct IDWriteFontFile;
+
+struct FontDescription
+{
+ quint32 index;
+ QByteArray uuid;
+};
+#endif
+
class QWinRTFontDatabase : public QBasicFontDatabase
{
public:
QString fontDir() const;
+#ifndef Q_OS_WINPHONE
+ ~QWinRTFontDatabase();
+ QFont defaultFont() const Q_DECL_OVERRIDE;
+ void populateFontDatabase() Q_DECL_OVERRIDE;
+ QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE;
+ void releaseHandle(void *handle) Q_DECL_OVERRIDE;
+private:
+ QHash<IDWriteFontFile *, FontDescription> m_fonts;
+#endif // !Q_OS_WINPHONE
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro
index 6e3cfb1d20..306bbc8174 100644
--- a/src/plugins/platforms/winrt/winrt.pro
+++ b/src/plugins/platforms/winrt/winrt.pro
@@ -11,6 +11,11 @@ DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ GL_GLEXT_PROTOTYPES
LIBS += $$QMAKE_LIBS_CORE
+!winphone {
+ LIBS += -ldwrite
+ INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/freetype/include
+}
+
SOURCES = \
main.cpp \
qwinrtbackingstore.cpp \