| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtGui module 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qwindowsfontengine_p.h" |
| #include "qwindowsnativeimage_p.h" |
| #include "qwindowsfontdatabase_p.h" |
| #include <QtCore/qt_windows.h> |
| #include "qwindowsfontenginedirectwrite_p.h" |
| |
| #include <QtGui/qpa/qplatformintegration.h> |
| #include <QtGui/private/qtextengine_p.h> // glyph_metrics_t |
| #include <QtGui/private/qguiapplication_p.h> |
| #include <QtGui/QPaintDevice> |
| #include <QtGui/QBitmap> |
| #include <QtGui/QPainter> |
| #include <QtGui/private/qpainter_p.h> |
| #include <QtGui/QPaintEngine> |
| #include <QtGui/private/qpaintengine_raster_p.h> |
| |
| #include <QtCore/QtEndian> |
| #include <QtCore/QFile> |
| #include <QtCore/qmath.h> |
| #include <QtCore/QTextStream> |
| #include <QtCore/QThreadStorage> |
| #include <QtCore/private/qsystemlibrary_p.h> |
| #include <QtCore/private/qstringiterator_p.h> |
| |
| #include <QtCore/QDebug> |
| |
| #include <limits.h> |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| # include <dwrite.h> |
| # include <comdef.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| //### mingw needed define |
| #ifndef TT_PRIM_CSPLINE |
| #define TT_PRIM_CSPLINE 3 |
| #endif |
| |
| // GetFontData expects the tags in little endian ;( |
| #define MAKE_LITTLE_ENDIAN_TAG(ch1, ch2, ch3, ch4) (\ |
| (((quint32)(ch4)) << 24) | \ |
| (((quint32)(ch3)) << 16) | \ |
| (((quint32)(ch2)) << 8) | \ |
| ((quint32)(ch1)) \ |
| ) |
| |
| // common DC for all fonts |
| |
| typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); |
| static PtrGetCharWidthI ptrGetCharWidthI = 0; |
| static bool resolvedGetCharWidthI = false; |
| |
| static void resolveGetCharWidthI() |
| { |
| if (resolvedGetCharWidthI) |
| return; |
| resolvedGetCharWidthI = true; |
| ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QStringLiteral("gdi32"), "GetCharWidthI"); |
| } |
| |
| // general font engine |
| |
| QFixed QWindowsFontEngine::lineThickness() const |
| { |
| if(lineWidth > 0) |
| return lineWidth; |
| |
| return QFontEngine::lineThickness(); |
| } |
| |
| static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) |
| { |
| const auto size = GetOutlineTextMetrics(hdc, 0, nullptr); |
| auto otm = reinterpret_cast<OUTLINETEXTMETRIC *>(malloc(size)); |
| GetOutlineTextMetrics(hdc, size, otm); |
| return otm; |
| } |
| |
| bool QWindowsFontEngine::hasCFFTable() const |
| { |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; |
| } |
| |
| bool QWindowsFontEngine::hasCMapTable() const |
| { |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('c', 'm', 'a', 'p'), 0, 0, 0) != GDI_ERROR; |
| } |
| |
| static inline QString stringFromOutLineTextMetric(const OUTLINETEXTMETRIC *otm, PSTR offset) |
| { |
| const uchar *p = reinterpret_cast<const uchar *>(otm) + quintptr(offset); |
| return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(p)); |
| } |
| |
| void QWindowsFontEngine::getCMap() |
| { |
| ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE) || hasCMapTable(); |
| |
| cffTable = hasCFFTable(); |
| |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| bool symb = false; |
| if (ttf) { |
| cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); |
| cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), |
| cmapTable.size(), &symb, &cmapSize); |
| } |
| if (!cmap) { |
| ttf = false; |
| symb = false; |
| } |
| symbol = symb; |
| designToDevice = 1; |
| _faceId.index = 0; |
| if(cmap) { |
| OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); |
| unitsPerEm = int(otm->otmEMSquare); |
| const QFixed unitsPerEmF(unitsPerEm); |
| designToDevice = unitsPerEmF / QFixed::fromReal(fontDef.pixelSize); |
| x_height = int(otm->otmsXHeight); |
| loadKerningPairs(designToDevice); |
| _faceId.filename = QFile::encodeName(stringFromOutLineTextMetric(otm, otm->otmpFullName)); |
| lineWidth = otm->otmsUnderscoreSize; |
| fsType = otm->otmfsType; |
| free(otm); |
| |
| } else { |
| unitsPerEm = tm.tmHeight; |
| } |
| } |
| |
| int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const |
| { |
| int glyph_pos = 0; |
| { |
| if (symbol) { |
| QStringIterator it(str, str + numChars); |
| while (it.hasNext()) { |
| const uint uc = it.next(); |
| glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); |
| if(!glyphs->glyphs[glyph_pos] && uc < 0x100) |
| glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000); |
| ++glyph_pos; |
| } |
| } else if (ttf) { |
| QStringIterator it(str, str + numChars); |
| while (it.hasNext()) { |
| const uint uc = it.next(); |
| glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); |
| ++glyph_pos; |
| } |
| } else { |
| QStringIterator it(str, str + numChars); |
| while (it.hasNext()) { |
| const uint uc = it.next(); |
| if (uc >= tm.tmFirstChar && uc <= tm.tmLastChar) |
| glyphs->glyphs[glyph_pos] = uc; |
| else |
| glyphs->glyphs[glyph_pos] = 0; |
| ++glyph_pos; |
| } |
| } |
| } |
| glyphs->numGlyphs = glyph_pos; |
| return glyph_pos; |
| } |
| |
| /*! |
| \class QWindowsFontEngine |
| \brief Standard Windows font engine. |
| \internal |
| |
| Will probably be superseded by a common Free Type font engine in Qt 5.X. |
| */ |
| |
| QWindowsFontEngine::QWindowsFontEngine(const QString &name, |
| LOGFONT lf, |
| const QSharedPointer<QWindowsFontEngineData> &fontEngineData) |
| : QFontEngine(Win), |
| m_fontEngineData(fontEngineData), |
| _name(name), |
| m_logfont(lf), |
| ttf(0), |
| hasOutline(0) |
| { |
| qCDebug(lcQpaFonts) << __FUNCTION__ << name << lf.lfHeight; |
| hfont = CreateFontIndirect(&m_logfont); |
| if (!hfont) { |
| qErrnoWarning("%s: CreateFontIndirect failed for family '%s'", __FUNCTION__, qPrintable(name)); |
| hfont = QWindowsFontDatabase::systemFont(); |
| } |
| |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| const BOOL res = GetTextMetrics(hdc, &tm); |
| if (!res) { |
| qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); |
| ZeroMemory(&tm, sizeof(TEXTMETRIC)); |
| } |
| |
| fontDef.pixelSize = -lf.lfHeight; |
| fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); |
| |
| cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; |
| getCMap(); |
| |
| if (!resolvedGetCharWidthI) |
| resolveGetCharWidthI(); |
| |
| // ### Properties accessed by QWin32PrintEngine (QtPrintSupport) |
| QVariantMap userData; |
| userData.insert(QStringLiteral("logFont"), QVariant::fromValue(m_logfont)); |
| userData.insert(QStringLiteral("hFont"), QVariant::fromValue(hfont)); |
| userData.insert(QStringLiteral("trueType"), QVariant(bool(ttf))); |
| setUserData(userData); |
| |
| hasUnreliableOutline = (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) == 0; |
| } |
| |
| QWindowsFontEngine::~QWindowsFontEngine() |
| { |
| if (designAdvances) |
| free(designAdvances); |
| |
| if (widthCache) |
| free(widthCache); |
| |
| // make sure we aren't by accident still selected |
| SelectObject(m_fontEngineData->hdc, QWindowsFontDatabase::systemFont()); |
| |
| if (!DeleteObject(hfont)) |
| qErrnoWarning("%s: QFontEngineWin: failed to delete font...", __FUNCTION__); |
| qCDebug(lcQpaFonts) << __FUNCTION__ << _name; |
| |
| if (!uniqueFamilyName.isEmpty()) { |
| if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { |
| QPlatformFontDatabase *pfdb = pi->fontDatabase(); |
| static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(uniqueFamilyName); |
| } |
| } |
| } |
| |
| glyph_t QWindowsFontEngine::glyphIndex(uint ucs4) const |
| { |
| glyph_t glyph = 0; |
| |
| if (symbol) { |
| glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); |
| if (glyph == 0 && ucs4 < 0x100) |
| glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4 + 0xf000); |
| } else if (ttf) { |
| glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); |
| } else if (ucs4 >= tm.tmFirstChar && ucs4 <= tm.tmLastChar) { |
| glyph = ucs4; |
| } |
| |
| return glyph; |
| } |
| |
| HGDIOBJ QWindowsFontEngine::selectDesignFont() const |
| { |
| LOGFONT f = m_logfont; |
| f.lfHeight = -unitsPerEm; |
| f.lfWidth = 0; |
| HFONT designFont = CreateFontIndirect(&f); |
| return SelectObject(m_fontEngineData->hdc, designFont); |
| } |
| |
| bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const |
| { |
| Q_ASSERT(glyphs->numGlyphs >= *nglyphs); |
| if (*nglyphs < len) { |
| *nglyphs = len; |
| return false; |
| } |
| |
| glyphs->numGlyphs = *nglyphs; |
| *nglyphs = getGlyphIndexes(str, len, glyphs); |
| |
| if (!(flags & GlyphIndicesOnly)) |
| recalcAdvances(glyphs, flags); |
| |
| return true; |
| } |
| |
| inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) |
| { |
| if (ptrGetCharWidthI) |
| ptrGetCharWidthI(hdc, glyph, 1, 0, &width); |
| } |
| |
| void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const |
| { |
| HGDIOBJ oldFont = 0; |
| HDC hdc = m_fontEngineData->hdc; |
| if (ttf && (flags & DesignMetrics)) { |
| for(int i = 0; i < glyphs->numGlyphs; i++) { |
| unsigned int glyph = glyphs->glyphs[i]; |
| if(int(glyph) >= designAdvancesSize) { |
| const int newSize = int(glyph + 256) >> 8 << 8; |
| designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed))); |
| Q_CHECK_PTR(designAdvances); |
| for(int i = designAdvancesSize; i < newSize; ++i) |
| designAdvances[i] = -1000000; |
| designAdvancesSize = newSize; |
| } |
| if (designAdvances[glyph] < -999999) { |
| if (!oldFont) |
| oldFont = selectDesignFont(); |
| |
| int width = 0; |
| calculateTTFGlyphWidth(hdc, glyph, width); |
| designAdvances[glyph] = QFixed(width) / designToDevice; |
| } |
| glyphs->advances[i] = designAdvances[glyph]; |
| } |
| if(oldFont) |
| DeleteObject(SelectObject(hdc, oldFont)); |
| } else { |
| for(int i = 0; i < glyphs->numGlyphs; i++) { |
| unsigned int glyph = glyphs->glyphs[i]; |
| |
| if (glyph >= widthCacheSize) { |
| const uint newSize = (glyph + 256) >> 8 << 8; |
| widthCache = reinterpret_cast<unsigned char *>(realloc(widthCache, newSize * sizeof(QFixed))); |
| Q_CHECK_PTR(widthCache); |
| memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); |
| widthCacheSize = newSize; |
| } |
| glyphs->advances[i] = widthCache[glyph]; |
| // font-width cache failed |
| if (glyphs->advances[i].value() == 0) { |
| int width = 0; |
| if (!oldFont) |
| oldFont = SelectObject(hdc, hfont); |
| |
| if (!ttf) { |
| QChar ch[2] = { ushort(glyph), 0 }; |
| int chrLen = 1; |
| if (QChar::requiresSurrogates(glyph)) { |
| ch[0] = QChar::highSurrogate(glyph); |
| ch[1] = QChar::lowSurrogate(glyph); |
| ++chrLen; |
| } |
| SIZE size = {0, 0}; |
| GetTextExtentPoint32(hdc, reinterpret_cast<const wchar_t *>(ch), chrLen, &size); |
| width = size.cx; |
| } else { |
| calculateTTFGlyphWidth(hdc, glyph, width); |
| } |
| glyphs->advances[i] = width; |
| // if glyph's within cache range, store it for later |
| if (width > 0 && width < 0x100) |
| widthCache[glyph] = uchar(width); |
| } |
| } |
| |
| if (oldFont) |
| SelectObject(hdc, oldFont); |
| } |
| } |
| |
| glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs) |
| { |
| if (glyphs.numGlyphs == 0) |
| return glyph_metrics_t(); |
| |
| QFixed w = 0; |
| for (int i = 0; i < glyphs.numGlyphs; ++i) |
| w += glyphs.effectiveAdvance(i); |
| |
| return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); |
| } |
| |
| bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const |
| { |
| Q_ASSERT(metrics != 0); |
| |
| HDC hdc = m_fontEngineData->hdc; |
| |
| GLYPHMETRICS gm; |
| DWORD res = 0; |
| MAT2 mat; |
| mat.eM11.value = mat.eM22.value = 1; |
| mat.eM11.fract = mat.eM22.fract = 0; |
| mat.eM21.value = mat.eM12.value = 0; |
| mat.eM21.fract = mat.eM12.fract = 0; |
| |
| if (t.type() > QTransform::TxTranslate) { |
| // We need to set the transform using the HDC's world |
| // matrix rather than using the MAT2 above, because the |
| // results provided when transforming via MAT2 does not |
| // match the glyphs that are drawn using a WorldTransform |
| XFORM xform; |
| xform.eM11 = FLOAT(t.m11()); |
| xform.eM12 = FLOAT(t.m12()); |
| xform.eM21 = FLOAT(t.m21()); |
| xform.eM22 = FLOAT(t.m22()); |
| xform.eDx = 0; |
| xform.eDy = 0; |
| SetGraphicsMode(hdc, GM_ADVANCED); |
| SetWorldTransform(hdc, &xform); |
| } |
| |
| uint format = GGO_METRICS; |
| if (ttf) |
| format |= GGO_GLYPH_INDEX; |
| res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat); |
| |
| if (t.type() > QTransform::TxTranslate) { |
| XFORM xform; |
| xform.eM11 = xform.eM22 = 1; |
| xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0; |
| SetWorldTransform(hdc, &xform); |
| SetGraphicsMode(hdc, GM_COMPATIBLE); |
| } |
| |
| if (res != GDI_ERROR) { |
| *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, |
| int(gm.gmBlackBoxX), int(gm.gmBlackBoxY), |
| gm.gmCellIncX, gm.gmCellIncY); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| glyph_metrics_t QWindowsFontEngine::boundingBox(glyph_t glyph, const QTransform &t) |
| { |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| |
| glyph_metrics_t glyphMetrics; |
| bool success = getOutlineMetrics(glyph, t, &glyphMetrics); |
| |
| if (!ttf && !success) { |
| // Bitmap fonts |
| wchar_t ch = wchar_t(glyph); |
| ABCFLOAT abc; |
| GetCharABCWidthsFloat(hdc, ch, ch, &abc); |
| int width = qRound(abc.abcfB); |
| |
| return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); |
| } |
| |
| return glyphMetrics; |
| } |
| |
| QFixed QWindowsFontEngine::ascent() const |
| { |
| return tm.tmAscent; |
| } |
| |
| QFixed QWindowsFontEngine::descent() const |
| { |
| return tm.tmDescent; |
| } |
| |
| QFixed QWindowsFontEngine::leading() const |
| { |
| return tm.tmExternalLeading; |
| } |
| |
| namespace { |
| # pragma pack(1) |
| |
| struct OS2Table |
| { |
| quint16 version; |
| qint16 avgCharWidth; |
| quint16 weightClass; |
| quint16 widthClass; |
| quint16 type; |
| qint16 subscriptXSize; |
| qint16 subscriptYSize; |
| qint16 subscriptXOffset; |
| qint16 subscriptYOffset; |
| qint16 superscriptXSize; |
| qint16 superscriptYSize; |
| qint16 superscriptXOffset; |
| qint16 superscriptYOffset; |
| qint16 strikeOutSize; |
| qint16 strikeOutPosition; |
| qint16 familyClass; |
| quint8 panose[10]; |
| quint32 unicodeRanges[4]; |
| quint8 vendorID[4]; |
| quint16 selection; |
| quint16 firstCharIndex; |
| quint16 lastCharIndex; |
| qint16 typoAscender; |
| qint16 typoDescender; |
| qint16 typoLineGap; |
| quint16 winAscent; |
| quint16 winDescent; |
| quint32 codepageRanges[2]; |
| qint16 height; |
| qint16 capHeight; |
| quint16 defaultChar; |
| quint16 breakChar; |
| quint16 maxContext; |
| }; |
| |
| # pragma pack() |
| } |
| |
| QFixed QWindowsFontEngine::capHeight() const |
| { |
| const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); |
| if (size_t(tableData.size()) >= sizeof(OS2Table)) { |
| const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData()); |
| if (qFromBigEndian<quint16>(table->version) >= 2) { |
| qint16 capHeight = qFromBigEndian<qint16>(table->capHeight); |
| if (capHeight > 0) |
| return QFixed(capHeight) / designToDevice; |
| } |
| } |
| return calculatedCapHeight(); |
| } |
| |
| QFixed QWindowsFontEngine::xHeight() const |
| { |
| if(x_height >= 0) |
| return x_height; |
| return QFontEngine::xHeight(); |
| } |
| |
| QFixed QWindowsFontEngine::averageCharWidth() const |
| { |
| return tm.tmAveCharWidth; |
| } |
| |
| qreal QWindowsFontEngine::maxCharWidth() const |
| { |
| return tm.tmMaxCharWidth; |
| } |
| |
| enum { max_font_count = 256 }; |
| static const ushort char_table[] = { |
| 40, |
| 67, |
| 70, |
| 75, |
| 86, |
| 88, |
| 89, |
| 91, |
| 102, |
| 114, |
| 124, |
| 127, |
| 205, |
| 645, |
| 884, |
| 922, |
| 1070, |
| 12386, |
| 0 |
| }; |
| |
| static const int char_table_entries = sizeof(char_table)/sizeof(ushort); |
| |
| #ifndef Q_CC_MINGW |
| void QWindowsFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) |
| { |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| |
| if (ttf) { |
| ABC abcWidths; |
| GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); |
| if (leftBearing) |
| *leftBearing = abcWidths.abcA; |
| if (rightBearing) |
| *rightBearing = abcWidths.abcC; |
| } else { |
| QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); |
| } |
| } |
| #endif // Q_CC_MINGW |
| |
| bool QWindowsFontEngine::hasUnreliableGlyphOutline() const |
| { |
| return hasUnreliableOutline || QFontEngine::hasUnreliableGlyphOutline(); |
| } |
| |
| qreal QWindowsFontEngine::minLeftBearing() const |
| { |
| if (lbearing == SHRT_MIN) |
| minRightBearing(); // calculates both |
| |
| return lbearing; |
| } |
| |
| qreal QWindowsFontEngine::minRightBearing() const |
| { |
| if (rbearing == SHRT_MIN) { |
| int ml = 0; |
| int mr = 0; |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| if (ttf) { |
| ABC *abc = 0; |
| int n = tm.tmLastChar - tm.tmFirstChar; |
| if (n <= max_font_count) { |
| abc = new ABC[n+1]; |
| GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc); |
| } else { |
| abc = new ABC[char_table_entries+1]; |
| for(int i = 0; i < char_table_entries; i++) |
| GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i); |
| n = char_table_entries; |
| } |
| ml = abc[0].abcA; |
| mr = abc[0].abcC; |
| for (int i = 1; i < n; i++) { |
| if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { |
| ml = qMin(ml,abc[i].abcA); |
| mr = qMin(mr,abc[i].abcC); |
| } |
| } |
| delete [] abc; |
| } else { |
| ABCFLOAT *abc = 0; |
| int n = tm.tmLastChar - tm.tmFirstChar+1; |
| if (n <= max_font_count) { |
| abc = new ABCFLOAT[n]; |
| GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc); |
| } else { |
| abc = new ABCFLOAT[char_table_entries]; |
| for(int i = 0; i < char_table_entries; i++) |
| GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i); |
| n = char_table_entries; |
| } |
| float fml = abc[0].abcfA; |
| float fmr = abc[0].abcfC; |
| for (int i=1; i<n; i++) { |
| if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) { |
| fml = qMin(fml,abc[i].abcfA); |
| fmr = qMin(fmr,abc[i].abcfC); |
| } |
| } |
| ml = int(fml - 0.9999); |
| mr = int(fmr - 0.9999); |
| delete [] abc; |
| } |
| lbearing = ml; |
| rbearing = mr; |
| } |
| |
| return rbearing; |
| } |
| |
| static inline double qt_fixed_to_double(const FIXED &p) { |
| return ((p.value << 16) + p.fract) / 65536.0; |
| } |
| |
| static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale, qreal stretch) { |
| return QPointF(qt_fixed_to_double(pt.x) * scale * stretch, -qt_fixed_to_double(pt.y) * scale); |
| } |
| |
| #ifndef GGO_UNHINTED |
| #define GGO_UNHINTED 0x0100 |
| #endif |
| |
| static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, |
| QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, |
| qreal scale = 1.0, qreal stretch = 1.0) |
| { |
| MAT2 mat; |
| mat.eM11.value = mat.eM22.value = 1; |
| mat.eM11.fract = mat.eM22.fract = 0; |
| mat.eM21.value = mat.eM12.value = 0; |
| mat.eM21.fract = mat.eM12.fract = 0; |
| |
| GLYPHMETRICS gMetric; |
| memset(&gMetric, 0, sizeof(GLYPHMETRICS)); |
| |
| if (metric) { |
| // If metrics requested, retrieve first using GGO_METRICS, because the returned |
| // values are incorrect for OpenType PS fonts if obtained at the same time as the |
| // glyph paths themselves (ie. with GGO_NATIVE as the format). |
| uint format = GGO_METRICS; |
| if (ttf) |
| format |= GGO_GLYPH_INDEX; |
| if (GetGlyphOutline(hdc, glyph, format, &gMetric, 0, 0, &mat) == GDI_ERROR) |
| return false; |
| // #### obey scale |
| *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, |
| int(gMetric.gmBlackBoxX), int(gMetric.gmBlackBoxY), |
| gMetric.gmCellIncX, gMetric.gmCellIncY); |
| } |
| |
| uint glyphFormat = GGO_NATIVE; |
| |
| if (ttf) |
| glyphFormat |= GGO_GLYPH_INDEX; |
| |
| const DWORD bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); |
| if (bufferSize == GDI_ERROR) |
| return false; |
| |
| char *dataBuffer = new char[bufferSize]; |
| DWORD ret = GDI_ERROR; |
| ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); |
| if (ret == GDI_ERROR) { |
| delete [] dataBuffer; |
| return false; |
| } |
| |
| DWORD offset = 0; |
| DWORD headerOffset = 0; |
| |
| QPointF oset = position.toPointF(); |
| while (headerOffset < bufferSize) { |
| const TTPOLYGONHEADER *ttph = reinterpret_cast<const TTPOLYGONHEADER *>(dataBuffer + headerOffset); |
| |
| QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale, stretch)); |
| path->moveTo(lastPoint + oset); |
| offset += sizeof(TTPOLYGONHEADER); |
| while (offset < headerOffset + ttph->cb) { |
| const TTPOLYCURVE *curve = reinterpret_cast<const TTPOLYCURVE *>(dataBuffer + offset); |
| switch (curve->wType) { |
| case TT_PRIM_LINE: { |
| for (int i=0; i<curve->cpfx; ++i) { |
| QPointF p = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset; |
| path->lineTo(p); |
| } |
| break; |
| } |
| case TT_PRIM_QSPLINE: { |
| const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1); |
| QPointF prev(elm.x, elm.y); |
| QPointF endPoint; |
| for (int i=0; i<curve->cpfx - 1; ++i) { |
| QPointF p1 = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset; |
| QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale, stretch) + oset; |
| if (i < curve->cpfx - 2) { |
| endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2); |
| } else { |
| endPoint = p2; |
| } |
| |
| path->quadTo(p1, endPoint); |
| prev = endPoint; |
| } |
| |
| break; |
| } |
| case TT_PRIM_CSPLINE: { |
| for (int i=0; i<curve->cpfx; ) { |
| QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
| QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
| QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
| path->cubicTo(p2, p3, p4); |
| } |
| break; |
| } |
| default: |
| qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case"); |
| } |
| offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX); |
| } |
| path->closeSubpath(); |
| headerOffset += ttph->cb; |
| } |
| delete [] dataBuffer; |
| |
| return true; |
| } |
| |
| void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, |
| QPainterPath *path, QTextItem::RenderFlags) |
| { |
| LOGFONT lf = m_logfont; |
| // The sign must be negative here to make sure we match against character height instead of |
| // hinted cell height. This ensures that we get linear matching, and we need this for |
| // paths since we later on apply a scaling transform to the glyph outline to get the |
| // font at the correct pixel size. |
| lf.lfHeight = -unitsPerEm; |
| lf.lfWidth = 0; |
| HFONT hf = CreateFontIndirect(&lf); |
| HDC hdc = m_fontEngineData->hdc; |
| HGDIOBJ oldfont = SelectObject(hdc, hf); |
| |
| qreal scale = qreal(fontDef.pixelSize) / unitsPerEm; |
| qreal stretch = fontDef.stretch ? qreal(fontDef.stretch) / 100 : 1.0; |
| for(int i = 0; i < nglyphs; ++i) { |
| if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, |
| scale, stretch)) { |
| // Some windows fonts, like "Modern", are vector stroke |
| // fonts, which are reported as TMPF_VECTOR but do not |
| // support GetGlyphOutline, and thus we set this bit so |
| // that addOutLineToPath can check it and return safely... |
| hasOutline = false; |
| break; |
| } |
| } |
| DeleteObject(SelectObject(hdc, oldfont)); |
| } |
| |
| void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, |
| QPainterPath *path, QTextItem::RenderFlags flags) |
| { |
| if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { |
| hasOutline = true; |
| QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); |
| if (hasOutline) { |
| // has_outline is set to false if addGlyphToPath gets |
| // false from GetGlyphOutline, meaning its not an outline |
| // font. |
| return; |
| } |
| } |
| QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); |
| } |
| |
| QFontEngine::FaceId QWindowsFontEngine::faceId() const |
| { |
| return _faceId; |
| } |
| |
| QT_BEGIN_INCLUDE_NAMESPACE |
| #include <qdebug.h> |
| QT_END_INCLUDE_NAMESPACE |
| |
| int QWindowsFontEngine::synthesized() const |
| { |
| if(synthesized_flags == -1) { |
| synthesized_flags = 0; |
| if(ttf) { |
| const DWORD HEAD = MAKE_LITTLE_ENDIAN_TAG('h', 'e', 'a', 'd'); |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| uchar data[4]; |
| GetFontData(hdc, HEAD, 44, &data, 4); |
| USHORT macStyle = qt_getUShort(data); |
| if (tm.tmItalic && !(macStyle & 2)) |
| synthesized_flags = SynthesizedItalic; |
| if (fontDef.stretch != 100 && ttf) |
| synthesized_flags |= SynthesizedStretch; |
| if (tm.tmWeight >= 500 && tm.tmWeight < 750 && !(macStyle & 1)) |
| synthesized_flags |= SynthesizedBold; |
| //qDebug() << "font is" << _name << |
| // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; |
| } |
| } |
| return synthesized_flags; |
| } |
| |
| QFixed QWindowsFontEngine::emSquareSize() const |
| { |
| return unitsPerEm; |
| } |
| |
| QFontEngine::Properties QWindowsFontEngine::properties() const |
| { |
| LOGFONT lf = m_logfont; |
| lf.lfHeight = unitsPerEm; |
| HFONT hf = CreateFontIndirect(&lf); |
| HDC hdc = m_fontEngineData->hdc; |
| HGDIOBJ oldfont = SelectObject(hdc, hf); |
| OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); |
| Properties p; |
| p.emSquare = unitsPerEm; |
| p.italicAngle = otm->otmItalicAngle; |
| const QByteArray name = stringFromOutLineTextMetric(otm, otm->otmpFamilyName).toLatin1() |
| + stringFromOutLineTextMetric(otm, otm->otmpStyleName).toLatin1(); |
| p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(name); |
| p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top, |
| otm->otmrcFontBox.right - otm->otmrcFontBox.left, |
| otm->otmrcFontBox.top - otm->otmrcFontBox.bottom); |
| p.ascent = otm->otmAscent; |
| p.descent = -otm->otmDescent; |
| p.leading = int(otm->otmLineGap); |
| p.capHeight = 0; |
| p.lineWidth = otm->otmsUnderscoreSize; |
| free(otm); |
| DeleteObject(SelectObject(hdc, oldfont)); |
| return p; |
| } |
| |
| void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) |
| { |
| LOGFONT lf = m_logfont; |
| lf.lfHeight = -unitsPerEm; |
| int flags = synthesized(); |
| if(flags & SynthesizedItalic) |
| lf.lfItalic = false; |
| lf.lfWidth = 0; |
| HFONT hf = CreateFontIndirect(&lf); |
| HDC hdc = m_fontEngineData->hdc; |
| HGDIOBJ oldfont = SelectObject(hdc, hf); |
| QFixedPoint p; |
| p.x = 0; |
| p.y = 0; |
| addGlyphToPath(glyph, p, hdc, path, ttf, metrics); |
| DeleteObject(SelectObject(hdc, oldfont)); |
| } |
| |
| bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const |
| { |
| if (!ttf && !cffTable) |
| return false; |
| HDC hdc = m_fontEngineData->hdc; |
| SelectObject(hdc, hfont); |
| DWORD t = qbswap<quint32>(tag); |
| *length = GetFontData(hdc, t, 0, buffer, *length); |
| Q_ASSERT(*length == GDI_ERROR || int(*length) > 0); |
| return *length != GDI_ERROR; |
| } |
| |
| #if !defined(CLEARTYPE_QUALITY) |
| # define CLEARTYPE_QUALITY 5 |
| #endif |
| |
| QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, |
| const QTransform &t, |
| QImage::Format mask_format) |
| { |
| Q_UNUSED(mask_format) |
| glyph_metrics_t gm = boundingBox(glyph); |
| |
| // printf(" -> for glyph %4x\n", glyph); |
| |
| int gx = gm.x.toInt(); |
| int gy = gm.y.toInt(); |
| int iw = gm.width.toInt(); |
| int ih = gm.height.toInt(); |
| |
| if (iw <= 0 || ih <= 0) |
| return 0; |
| |
| bool has_transformation = t.type() > QTransform::TxTranslate; |
| |
| unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; |
| XFORM xform; |
| |
| if (has_transformation) { |
| xform.eM11 = FLOAT(t.m11()); |
| xform.eM12 = FLOAT(t.m12()); |
| xform.eM21 = FLOAT(t.m21()); |
| xform.eM22 = FLOAT(t.m22()); |
| xform.eDx = margin; |
| xform.eDy = margin; |
| |
| const HDC hdc = m_fontEngineData->hdc; |
| |
| SetGraphicsMode(hdc, GM_ADVANCED); |
| SetWorldTransform(hdc, &xform); |
| HGDIOBJ old_font = SelectObject(hdc, font); |
| |
| const UINT ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0); |
| GLYPHMETRICS tgm; |
| MAT2 mat; |
| memset(&mat, 0, sizeof(mat)); |
| mat.eM11.value = mat.eM22.value = 1; |
| |
| const DWORD result = GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); |
| |
| XFORM identity = {1, 0, 0, 1, 0, 0}; |
| SetWorldTransform(hdc, &identity); |
| SetGraphicsMode(hdc, GM_COMPATIBLE); |
| SelectObject(hdc, old_font); |
| |
| if (result == GDI_ERROR) { |
| const int errorCode = int(GetLastError()); |
| qErrnoWarning(errorCode, "QWinFontEngine: unable to query transformed glyph metrics (GetGlyphOutline() failed, error %d)...", errorCode); |
| return 0; |
| } |
| |
| iw = int(tgm.gmBlackBoxX); |
| ih = int(tgm.gmBlackBoxY); |
| |
| xform.eDx -= tgm.gmptGlyphOrigin.x; |
| xform.eDy += tgm.gmptGlyphOrigin.y; |
| } |
| |
| // The padding here needs to be kept in sync with the values in alphaMapBoundingBox. |
| QWindowsNativeImage *ni = new QWindowsNativeImage(iw + 2 * margin, |
| ih + 2 * margin, |
| QWindowsNativeImage::systemFormat()); |
| |
| /*If cleartype is enabled we use the standard system format even on Windows CE |
| and not the special textbuffer format we have to use if cleartype is disabled*/ |
| |
| ni->image().fill(0xffffffff); |
| |
| HDC hdc = ni->hdc(); |
| |
| SelectObject(hdc, GetStockObject(NULL_BRUSH)); |
| SelectObject(hdc, GetStockObject(BLACK_PEN)); |
| SetTextColor(hdc, RGB(0,0,0)); |
| SetBkMode(hdc, TRANSPARENT); |
| SetTextAlign(hdc, TA_BASELINE); |
| |
| HGDIOBJ old_font = SelectObject(hdc, font); |
| |
| if (has_transformation) { |
| SetGraphicsMode(hdc, GM_ADVANCED); |
| SetWorldTransform(hdc, &xform); |
| ExtTextOut(hdc, 0, 0, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); |
| } else { |
| ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); |
| } |
| |
| SelectObject(hdc, old_font); |
| return ni; |
| } |
| |
| glyph_metrics_t QWindowsFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat format) |
| { |
| int margin = 0; |
| if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) |
| margin = glyphMargin(QFontEngine::Format_A32); |
| glyph_metrics_t gm = boundingBox(glyph, matrix); |
| gm.width += margin * 2; |
| gm.height += margin * 2; |
| return gm; |
| } |
| |
| QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) |
| { |
| HFONT font = hfont; |
| |
| bool clearTypeTemporarilyDisabled = (m_fontEngineData->clearTypeEnabled && m_logfont.lfQuality != NONANTIALIASED_QUALITY); |
| if (clearTypeTemporarilyDisabled) { |
| LOGFONT lf = m_logfont; |
| lf.lfQuality = ANTIALIASED_QUALITY; |
| font = CreateFontIndirect(&lf); |
| } |
| QImage::Format mask_format = QWindowsNativeImage::systemFormat(); |
| mask_format = QImage::Format_RGB32; |
| |
| const QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); |
| if (mask == 0) { |
| if (m_fontEngineData->clearTypeEnabled) |
| DeleteObject(font); |
| return QImage(); |
| } |
| |
| QImage alphaMap(mask->width(), mask->height(), QImage::Format_Alpha8); |
| |
| |
| // Copy data... Cannot use QPainter here as GDI has messed up the |
| // Alpha channel of the ni.image pixels... |
| for (int y=0; y<mask->height(); ++y) { |
| uchar *dest = alphaMap.scanLine(y); |
| if (mask->image().format() == QImage::Format_RGB16) { |
| const qint16 *src = reinterpret_cast<const qint16 *>(mask->image().constScanLine(y)); |
| for (int x=0; x<mask->width(); ++x) |
| dest[x] = 255 - qGray(src[x]); |
| } else { |
| const uint *src = reinterpret_cast<const uint *>(mask->image().constScanLine(y)); |
| for (int x=0; x<mask->width(); ++x) { |
| if (QWindowsNativeImage::systemFormat() == QImage::Format_RGB16) |
| dest[x] = 255 - qGray(src[x]); |
| else |
| dest[x] = 255 - (m_fontEngineData->pow_gamma[qGray(src[x])] * 255. / 2047.); |
| } |
| } |
| } |
| |
| // Cleanup... |
| delete mask; |
| if (clearTypeTemporarilyDisabled) { |
| DeleteObject(font); |
| } |
| |
| return alphaMap; |
| } |
| |
| #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C |
| #define SPI_SETFONTSMOOTHINGCONTRAST 0x200D |
| |
| QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTransform &t) |
| { |
| HFONT font = hfont; |
| |
| UINT contrast; |
| SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); |
| SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(1000)), 0); |
| |
| int margin = glyphMargin(QFontEngine::Format_A32); |
| QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); |
| SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(contrast)), 0); |
| |
| if (mask == 0) |
| return QImage(); |
| |
| // Gracefully handle the odd case when the display is 16-bit |
| const QImage source = mask->image().depth() == 32 |
| ? mask->image() |
| : mask->image().convertToFormat(QImage::Format_RGB32); |
| |
| QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32); |
| for (int y=0; y<mask->height(); ++y) { |
| auto dest = reinterpret_cast<uint *>(rgbMask.scanLine(y)); |
| const uint *src = reinterpret_cast<const uint *>(source.constScanLine(y)); |
| for (int x=0; x<mask->width(); ++x) { |
| dest[x] = 0xffffffff - (0x00ffffff & src[x]); |
| } |
| } |
| |
| delete mask; |
| |
| return rgbMask; |
| } |
| |
| QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const |
| { |
| QFontDef request = fontDef; |
| QString actualFontName = request.family; |
| if (!uniqueFamilyName.isEmpty()) |
| request.family = uniqueFamilyName; |
| request.pixelSize = pixelSize; |
| const QString faceName = QString::fromWCharArray(m_logfont.lfFaceName); |
| |
| QFontEngine *fontEngine = |
| QWindowsFontDatabase::createEngine(request, faceName, |
| QWindowsFontDatabase::defaultVerticalDPI(), |
| m_fontEngineData); |
| if (fontEngine) { |
| fontEngine->fontDef.family = actualFontName; |
| if (!uniqueFamilyName.isEmpty()) { |
| static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); |
| if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { |
| QPlatformFontDatabase *pfdb = pi->fontDatabase(); |
| static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(uniqueFamilyName); |
| } |
| } |
| } |
| return fontEngine; |
| } |
| |
| Qt::HANDLE QWindowsFontEngine::handle() const |
| { |
| return hfont; |
| } |
| |
| void QWindowsFontEngine::initFontInfo(const QFontDef &request, |
| int dpi) |
| { |
| fontDef = request; // most settings are equal |
| HDC dc = m_fontEngineData->hdc; |
| SelectObject(dc, hfont); |
| wchar_t n[64]; |
| GetTextFace(dc, 64, n); |
| fontDef.family = QString::fromWCharArray(n); |
| fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); |
| if (fontDef.pointSize < 0) { |
| fontDef.pointSize = fontDef.pixelSize * 72. / dpi; |
| } else if (fontDef.pixelSize == -1) { |
| fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); |
| } |
| } |
| |
| bool QWindowsFontEngine::supportsTransformation(const QTransform &transform) const |
| { |
| // Support all transformations for ttf files, and translations for raster fonts |
| return ttf || transform.type() <= QTransform::TxTranslate; |
| } |
| |
| QT_END_NAMESPACE |