/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** 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. ** ** 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. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qrawfont_p.h" #if !defined(QT_NO_RAWFONT) #include <private/qsystemlibrary_p.h> #if !defined(QT_NO_DIRECTWRITE) # include "qfontenginedirectwrite_p.h" # include <dwrite.h> #endif QT_BEGIN_NAMESPACE namespace { template<typename T> struct BigEndian { quint8 data[sizeof(T)]; operator T() const { T littleEndian = 0; for (int i = 0; i < int(sizeof(T)); ++i) littleEndian |= data[i] << ((sizeof(T) - i - 1) * 8); return littleEndian; } BigEndian<T> &operator=(const T &t) { for (int i = 0; i < int(sizeof(T)); ++i) data[i] = ((t >> (sizeof(T) - i - 1) * 8) & 0xff); return *this; } }; # pragma pack(1) // Common structure for all formats of the "name" table struct NameTable { BigEndian<quint16> format; BigEndian<quint16> count; BigEndian<quint16> stringOffset; }; struct NameRecord { BigEndian<quint16> platformID; BigEndian<quint16> encodingID; BigEndian<quint16> languageID; BigEndian<quint16> nameID; BigEndian<quint16> length; BigEndian<quint16> offset; }; struct OffsetSubTable { BigEndian<quint32> scalerType; BigEndian<quint16> numTables; BigEndian<quint16> searchRange; BigEndian<quint16> entrySelector; BigEndian<quint16> rangeShift; }; struct TableDirectory { BigEndian<quint32> identifier; BigEndian<quint32> checkSum; BigEndian<quint32> offset; BigEndian<quint32> length; }; struct OS2Table { BigEndian<quint16> version; BigEndian<qint16> avgCharWidth; BigEndian<quint16> weightClass; BigEndian<quint16> widthClass; BigEndian<quint16> type; BigEndian<qint16> subscriptXSize; BigEndian<qint16> subscriptYSize; BigEndian<qint16> subscriptXOffset; BigEndian<qint16> subscriptYOffset; BigEndian<qint16> superscriptXSize; BigEndian<qint16> superscriptYSize; BigEndian<qint16> superscriptXOffset; BigEndian<qint16> superscriptYOffset; BigEndian<qint16> strikeOutSize; BigEndian<qint16> strikeOutPosition; BigEndian<qint16> familyClass; quint8 panose[10]; BigEndian<quint32> unicodeRanges[4]; quint8 vendorID[4]; BigEndian<quint16> selection; BigEndian<quint16> firstCharIndex; BigEndian<quint16> lastCharIndex; BigEndian<qint16> typoAscender; BigEndian<qint16> typoDescender; BigEndian<qint16> typoLineGap; BigEndian<quint16> winAscent; BigEndian<quint16> winDescent; BigEndian<quint32> codepageRanges[2]; BigEndian<qint16> height; BigEndian<qint16> capHeight; BigEndian<quint16> defaultChar; BigEndian<quint16> breakChar; BigEndian<quint16> maxContext; }; # pragma pack() class EmbeddedFont { public: EmbeddedFont(const QByteArray &fontData) : m_fontData(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; }; TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) { Q_ASSERT(tagName.size() == 4); const BigEndian<quint32> *tagIdPtr = reinterpret_cast<const BigEndian<quint32> *>(tagName.constData()); quint32 tagId = *tagIdPtr; OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); TableDirectory *nameTableDirectoryEntry = 0; for (int i=0; i<offsetSubTable->numTables; ++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<NameTable *>(m_fontData.data() + nameTableDirectoryEntry->offset); NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); for (int i=0; i<nameTable->count; ++i, ++nameRecord) { if (nameRecord->nameID == 1 && nameRecord->platformID == 3 // Windows && nameRecord->languageID == 0x0409) { // US English const void *ptr = reinterpret_cast<const quint8 *>(nameTable) + nameTable->stringOffset + nameRecord->offset; const BigEndian<quint16> *s = reinterpret_cast<const BigEndian<quint16> *>(ptr); const BigEndian<quint16> *e = s + nameRecord->length / sizeof(quint16); while (s != e) name += QChar(*s++); 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<NameTable *>(newNameTable.data()); nameTable->count = requiredRecordCount; nameTable->stringOffset = sizeOfHeader; NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); for (int i=0; i<requiredRecordCount; ++i, nameRecord++) { nameRecord->nameID = 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<quint16> *stringStorage = reinterpret_cast<BigEndian<quint16> *>(nameRecord); const quint16 *sourceString = newFamilyName.utf16(); for (int i=0; i<newFamilyName.size(); ++i) stringStorage[i] = sourceString[i]; stringStorage += newFamilyName.size(); sourceString = regularString.utf16(); for (int i=0; i<regularString.size(); ++i) stringStorage[i] = sourceString[i]; } quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); quint32 *tableEnd = reinterpret_cast<quint32 *>(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<const void *, QByteArray> 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<void * const *>(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<IUnknown **>(&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); typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *); static PtrAddFontMemResourceEx ptrAddFontMemResourceEx = 0; typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE); static PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = 0; static void resolveGdi32() { static bool triedResolve = false; if (!triedResolve) { QSystemLibrary gdi32(QLatin1String("gdi32")); if (gdi32.load()) { ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)gdi32.resolve("AddFontMemResourceEx"); ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)gdi32.resolve("RemoveFontMemResourceEx"); } triedResolve = true; } } void QRawFontPrivate::platformCleanUp() { if (fontHandle != NULL) { if (ptrRemoveFontMemResourceEx) ptrRemoveFontMemResourceEx(fontHandle); fontHandle = NULL; } } void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) { EmbeddedFont font(fontData); #if !defined(QT_NO_DIRECTWRITE) if (hintingPreference == QFont::PreferDefaultHinting || hintingPreference == QFont::PreferFullHinting) #endif { GUID guid; CoCreateGuid(&guid); QString uniqueFamilyName = QLatin1Char('f') + QString::number(guid.Data1, 36) + QLatin1Char('-') + QString::number(guid.Data2, 36) + QLatin1Char('-') + QString::number(guid.Data3, 36) + QLatin1Char('-') + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); QString actualFontName = font.changeFamilyName(uniqueFamilyName); if (actualFontName.isEmpty()) { qWarning("QRawFont::platformLoadFromData: Can't change family name of font"); return; } Q_ASSERT(fontHandle == NULL); resolveGdi32(); if (ptrAddFontMemResourceEx && ptrRemoveFontMemResourceEx) { DWORD count = 0; QByteArray newFontData = font.data(); fontHandle = ptrAddFontMemResourceEx((void *)newFontData.constData(), newFontData.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 static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName; 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<IUnknown **>(&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<const OS2Table *>(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); } } } QT_END_NAMESPACE #endif // QT_NO_RAWFONT