/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite 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 Technology Preview License Agreement accompanying ** this package. ** ** 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.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qrawfont_p.h" #include #if !defined(QT_NO_DIRECTWRITE) # include "qfontenginedirectwrite_p.h" # include #endif #if !defined(QT_NO_RAWFONT) QT_BEGIN_NAMESPACE namespace { template struct BigEndian { quint8 data[sizeof(T)]; operator T() const { T littleEndian = 0; for (int i=0; i &operator=(const T &t) { for (int i=0; i> (sizeof(T) - i - 1) * 8) & 0xff); } return *this; } }; # pragma pack(1) // Common structure for all formats of the "name" table struct NameTable { BigEndian format; BigEndian count; BigEndian stringOffset; }; struct NameRecord { BigEndian platformID; BigEndian encodingID; BigEndian languageID; BigEndian nameID; BigEndian length; BigEndian offset; }; struct OffsetSubTable { BigEndian scalerType; BigEndian numTables; BigEndian searchRange; BigEndian entrySelector; BigEndian rangeShift; }; struct TableDirectory { BigEndian identifier; BigEndian checkSum; BigEndian offset; BigEndian length; }; struct OS2Table { BigEndian version; BigEndian avgCharWidth; BigEndian weightClass; BigEndian widthClass; BigEndian type; BigEndian subscriptXSize; BigEndian subscriptYSize; BigEndian subscriptXOffset; BigEndian subscriptYOffset; BigEndian superscriptXSize; BigEndian superscriptYSize; BigEndian superscriptXOffset; BigEndian superscriptYOffset; BigEndian strikeOutSize; BigEndian strikeOutPosition; BigEndian familyClass; quint8 panose[10]; BigEndian unicodeRanges[4]; quint8 vendorID[4]; BigEndian selection; BigEndian firstCharIndex; BigEndian lastCharIndex; BigEndian typoAscender; BigEndian typoDescender; BigEndian typoLineGap; BigEndian winAscent; BigEndian winDescent; BigEndian codepageRanges[2]; BigEndian height; BigEndian capHeight; BigEndian defaultChar; BigEndian breakChar; BigEndian maxContext; }; # pragma pack() class EmbeddedFont { public: EmbeddedFont(const QByteArray &fontData); QString changeFamilyName(const QString &newFamilyName); QByteArray data() const { return m_fontData; } TableDirectory *tableDirectoryEntry(const QByteArray &tagName); QString familyName(TableDirectory *nameTableDirectory = 0); private: QByteArray m_fontData; }; EmbeddedFont::EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) { } TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) { Q_ASSERT(tagName.size() == 4); const BigEndian *tagIdPtr = reinterpret_cast *>(tagName.constData()); quint32 tagId = *tagIdPtr; OffsetSubTable *offsetSubTable = reinterpret_cast(m_fontData.data()); TableDirectory *tableDirectory = reinterpret_cast(offsetSubTable + 1); TableDirectory *nameTableDirectoryEntry = 0; for (int i=0; inumTables; ++i, ++tableDirectory) { if (tableDirectory->identifier == tagId) { nameTableDirectoryEntry = tableDirectory; break; } } return nameTableDirectoryEntry; } QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) { QString name; if (nameTableDirectoryEntry == 0) nameTableDirectoryEntry = tableDirectoryEntry("name"); if (nameTableDirectoryEntry != 0) { NameTable *nameTable = reinterpret_cast(m_fontData.data() + nameTableDirectoryEntry->offset); NameRecord *nameRecord = reinterpret_cast(nameTable + 1); for (int i=0; icount; ++i, ++nameRecord) { if (nameRecord->nameID == 1 && nameRecord->platformID == 3 // Windows && nameRecord->languageID == 0x0409) { // US English const void *ptr = reinterpret_cast(nameTable) + nameTable->stringOffset + nameRecord->offset; const BigEndian *s = reinterpret_cast *>(ptr); for (int j=0; jlength / sizeof(quint16); ++j) name += QChar(s[j]); break; } } } return name; } QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) { TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); if (nameTableDirectoryEntry == 0) return QString(); QString oldFamilyName = familyName(nameTableDirectoryEntry); // Reserve size for name table header, five required name records and string const int requiredRecordCount = 5; quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; int newFamilyNameSize = newFamilyName.size() * sizeof(quint16); const QString regularString = QString::fromLatin1("Regular"); int regularStringSize = regularString.size() * sizeof(quint16); // Align table size of table to 32 bits (pad with 0) int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; QByteArray newNameTable(fullSize, char(0)); { NameTable *nameTable = reinterpret_cast(newNameTable.data()); nameTable->count = requiredRecordCount; nameTable->stringOffset = sizeOfHeader; NameRecord *nameRecord = reinterpret_cast(nameTable + 1); for (int i=0; inameID = nameIds[i]; nameRecord->encodingID = 1; nameRecord->languageID = 0x0409; nameRecord->platformID = 3; nameRecord->length = newFamilyNameSize; // Special case for sub-family if (nameIds[i] == 4) { nameRecord->offset = newFamilyNameSize; nameRecord->length = regularStringSize; } } // nameRecord now points to string data BigEndian *stringStorage = reinterpret_cast *>(nameRecord); const quint16 *sourceString = newFamilyName.utf16(); for (int i=0; i(newNameTable.data()); quint32 *tableEnd = reinterpret_cast(newNameTable.data() + fullSize); quint32 checkSum = 0; while (p < tableEnd) checkSum += *(p++); nameTableDirectoryEntry->checkSum = checkSum; nameTableDirectoryEntry->offset = m_fontData.size(); nameTableDirectoryEntry->length = fullSize; m_fontData.append(newNameTable); return oldFamilyName; } #if !defined(QT_NO_DIRECTWRITE) class DirectWriteFontFileStream: public IDWriteFontFileStream { public: DirectWriteFontFileStream(const QByteArray &fontData) : m_fontData(fontData) , m_referenceCount(0) { } ~DirectWriteFontFileStream() { } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, UINT64 fragmentSize, OUT void **fragmentContext); void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); private: QByteArray m_fontData; ULONG m_referenceCount; }; HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) { if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { *object = this; AddRef(); return S_OK; } else { *object = NULL; return E_NOINTERFACE; } } ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() { return InterlockedIncrement(&m_referenceCount); } ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() { ULONG newCount = InterlockedDecrement(&m_referenceCount); if (newCount == 0) delete this; return newCount; } HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( const void **fragmentStart, UINT64 fileOffset, UINT64 fragmentSize, OUT void **fragmentContext) { *fragmentContext = NULL; if (fragmentSize + fileOffset <= m_fontData.size()) { *fragmentStart = m_fontData.data() + fileOffset; return S_OK; } else { *fragmentStart = NULL; return E_FAIL; } } void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) { } HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) { *fileSize = m_fontData.size(); return S_OK; } HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) { *lastWriteTime = 0; return E_NOTIMPL; } class DirectWriteFontFileLoader: public IDWriteFontFileLoader { public: DirectWriteFontFileLoader() : m_referenceCount(0) {} ~DirectWriteFontFileLoader() { } inline void addKey(const void *key, const QByteArray &fontData) { Q_ASSERT(!m_fontDatas.contains(key)); m_fontDatas.insert(key, fontData); } inline void removeKey(const void *key) { m_fontDatas.remove(key); } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, UINT32 fontFileReferenceKeySize, OUT IDWriteFontFileStream **fontFileStream); private: ULONG m_referenceCount; QHash m_fontDatas; }; HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, void **object) { if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { *object = this; AddRef(); return S_OK; } else { *object = NULL; return E_NOINTERFACE; } } ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() { return InterlockedIncrement(&m_referenceCount); } ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() { ULONG newCount = InterlockedDecrement(&m_referenceCount); if (newCount == 0) delete this; return newCount; } HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( void const *fontFileReferenceKey, UINT32 fontFileReferenceKeySize, IDWriteFontFileStream **fontFileStream) { Q_UNUSED(fontFileReferenceKeySize); if (fontFileReferenceKeySize != sizeof(const void *)) { qWarning("DirectWriteFontFileLoader::CreateStreamFromKey: Wrong key size"); return E_FAIL; } const void *key = *reinterpret_cast(fontFileReferenceKey); *fontFileStream = NULL; if (!m_fontDatas.contains(key)) return E_FAIL; QByteArray fontData = m_fontDatas.value(key); DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); stream->AddRef(); *fontFileStream = stream; return S_OK; } class CustomFontFileLoader { public: CustomFontFileLoader() : m_directWriteFactory(0), m_directWriteFontFileLoader(0) { HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&m_directWriteFactory)); if (FAILED(hres)) { qErrnoWarning(hres, "CustomFontFileLoader::CustomFontFileLoader: " "DWriteCreateFactory failed."); } else { m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); } } ~CustomFontFileLoader() { if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); if (m_directWriteFactory != 0) m_directWriteFactory->Release(); } void addKey(const void *key, const QByteArray &fontData) { if (m_directWriteFontFileLoader != 0) m_directWriteFontFileLoader->addKey(key, fontData); } void removeKey(const void *key) { if (m_directWriteFontFileLoader != 0) m_directWriteFontFileLoader->removeKey(key); } IDWriteFontFileLoader *loader() const { return m_directWriteFontFileLoader; } private: IDWriteFactory *m_directWriteFactory; DirectWriteFontFileLoader *m_directWriteFontFileLoader; }; #endif } // Anonymous namespace // From qfontdatabase_win.cpp extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); // From qfontdatabase.cpp extern QFont::Weight weightFromInteger(int weight); void QRawFontPrivate::platformCleanUp() { if (fontHandle != NULL) { if (ptrRemoveFontMemResourceEx == NULL) { void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); ptrRemoveFontMemResourceEx = reinterpret_cast(func); } if (ptrRemoveFontMemResourceEx == NULL) { qWarning("QRawFont::platformCleanUp: Can't find RemoveFontMemResourceEx in gdi32"); fontHandle = NULL; } else { ptrRemoveFontMemResourceEx(fontHandle); fontHandle = NULL; } } } void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData, int pixelSize, QFont::HintingPreference hintingPreference) { QByteArray fontData(_fontData); EmbeddedFont font(fontData); #if !defined(QT_NO_DIRECTWRITE) if (hintingPreference == QFont::PreferDefaultHinting || hintingPreference == QFont::PreferFullHinting) #endif { GUID guid; CoCreateGuid(&guid); uniqueFamilyName = QString::fromLatin1("f") + QString::number(guid.Data1, 36) + QLatin1Char('-') + QString::number(guid.Data2, 36) + QLatin1Char('-') + QString::number(guid.Data3, 36) + QLatin1Char('-') + QString::number(*reinterpret_cast(guid.Data4), 36); QString actualFontName = font.changeFamilyName(uniqueFamilyName); if (actualFontName.isEmpty()) { qWarning("QRawFont::platformLoadFromData: Can't change family name of font"); return; } if (ptrAddFontMemResourceEx == NULL || ptrRemoveFontMemResourceEx == NULL) { void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); ptrRemoveFontMemResourceEx = reinterpret_cast(func); func = QSystemLibrary::resolve(QLatin1String("gdi32"), "AddFontMemResourceEx"); ptrAddFontMemResourceEx = reinterpret_cast(func); } Q_ASSERT(fontHandle == NULL); if (ptrAddFontMemResourceEx != NULL && ptrRemoveFontMemResourceEx != NULL) { DWORD count = 0; fontData = font.data(); fontHandle = ptrAddFontMemResourceEx(fontData.data(), fontData.size(), 0, &count); if (count == 0 && fontHandle != NULL) { ptrRemoveFontMemResourceEx(fontHandle); fontHandle = NULL; } } if (fontHandle == NULL) { qWarning("QRawFont::platformLoadFromData: AddFontMemResourceEx failed"); } else { QFontDef request; request.family = uniqueFamilyName; request.pixelSize = pixelSize; request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; request.hintingPreference = hintingPreference; fontEngine = qt_load_font_engine_win(request); if (request.family != fontEngine->fontDef.family) { qWarning("QRawFont::platformLoadFromData: Failed to load font. " "Got fallback instead: %s", qPrintable(fontEngine->fontDef.family)); if (fontEngine->cache_count == 0 && fontEngine->ref == 0) delete fontEngine; fontEngine = 0; } else { Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0); // Override the generated font name fontEngine->fontDef.family = actualFontName; fontEngine->ref.ref(); } } } #if !defined(QT_NO_DIRECTWRITE) else { CustomFontFileLoader fontFileLoader; fontFileLoader.addKey(this, fontData); IDWriteFactory *factory = NULL; HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&factory)); if (FAILED(hres)) { qErrnoWarning(hres, "QRawFont::platformLoadFromData: DWriteCreateFactory failed"); return; } IDWriteFontFile *fontFile = NULL; void *key = this; hres = factory->CreateCustomFontFileReference(&key, sizeof(void *), fontFileLoader.loader(), &fontFile); if (FAILED(hres)) { qErrnoWarning(hres, "QRawFont::platformLoadFromData: " "CreateCustomFontFileReference failed"); factory->Release(); return; } BOOL isSupportedFontType; DWRITE_FONT_FILE_TYPE fontFileType; DWRITE_FONT_FACE_TYPE fontFaceType; UINT32 numberOfFaces; fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); if (!isSupportedFontType) { fontFile->Release(); factory->Release(); return; } IDWriteFontFace *directWriteFontFace = NULL; hres = factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, &directWriteFontFace); if (FAILED(hres)) { qErrnoWarning(hres, "QRawFont::platformLoadFromData: CreateFontFace failed"); fontFile->Release(); factory->Release(); return; } fontFile->Release(); fontEngine = new QFontEngineDirectWrite(factory, directWriteFontFace, pixelSize); // Get font family from font data fontEngine->fontDef.family = font.familyName(); fontEngine->ref.ref(); directWriteFontFace->Release(); factory->Release(); } #endif // Get style and weight info if (fontEngine != 0) { TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); if (os2TableEntry != 0) { const OS2Table *os2Table = reinterpret_cast(fontData.constData() + os2TableEntry->offset); bool italic = os2Table->selection & 1; bool oblique = os2Table->selection & 128; if (italic) fontEngine->fontDef.style = QFont::StyleItalic; else if (oblique) fontEngine->fontDef.style = QFont::StyleOblique; else fontEngine->fontDef.style = QFont::StyleNormal; fontEngine->fontDef.weight = weightFromInteger(os2Table->weightClass); } } } void QRawFontPrivate::platformSetPixelSize(int pixelSize) { if (fontEngine == NULL) return; QFontEngine *oldFontEngine = fontEngine; #if !defined(QT_NO_DIRECTWRITE) if (fontEngine->type() == QFontEngine::Win) #endif { QFontDef request = fontEngine->fontDef; QString actualFontName = request.family; if (!uniqueFamilyName.isEmpty()) request.family = uniqueFamilyName; request.pixelSize = pixelSize; fontEngine = qt_load_font_engine_win(request); if (fontEngine != NULL) { fontEngine->fontDef.family = actualFontName; fontEngine->ref.ref(); } } #if !defined(QT_NO_DIRECTWRITE) else { QFontEngineDirectWrite *dWriteFE = static_cast(fontEngine); fontEngine = new QFontEngineDirectWrite(dWriteFE->m_directWriteFactory, dWriteFE->m_directWriteFontFace, pixelSize); fontEngine->fontDef = dWriteFE->fontDef; fontEngine->fontDef.pixelSize = pixelSize; fontEngine->ref.ref(); } #endif Q_ASSERT(fontEngine != oldFontEngine); oldFontEngine->ref.deref(); if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0) delete oldFontEngine; } QT_END_NAMESPACE #endif // QT_NO_RAWFONT