/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia 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. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qrawfont_p.h" #if !defined(QT_NO_RAWFONT) #include #if !defined(QT_NO_DIRECTWRITE) # include "qfontenginedirectwrite_p.h" # include #endif QT_BEGIN_NAMESPACE namespace { template 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 &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 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) : 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 *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); const BigEndian *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(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); 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(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->ref == 0) delete fontEngine; fontEngine = 0; } else { Q_ASSERT(fontEngine->ref == 0); // Override the generated font name static_cast(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(&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); } } } QT_END_NAMESPACE #endif // QT_NO_RAWFONT