/**************************************************************************** ** ** 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 QtGui module 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$ ** ****************************************************************************/ #if _WIN32_WINNT < 0x0500 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #include "qfontengine_p.h" #include "qtextengine_p.h" #include #include "qt_windows.h" #include #include #include #include #include #include #include #include #include #include #include #include "qpaintengine.h" #include "qvarlengtharray.h" #include #include #if defined(Q_WS_WINCE) #include "qguifunctions_wince.h" #endif //### mingw needed define #ifndef TT_PRIM_CSPLINE #define TT_PRIM_CSPLINE 3 #endif #ifdef MAKE_TAG #undef MAKE_TAG #endif // GetFontData expects the tags in little endian ;( #define MAKE_TAG(ch1, ch2, ch3, ch4) (\ (((quint32)(ch4)) << 24) | \ (((quint32)(ch3)) << 16) | \ (((quint32)(ch2)) << 8) | \ ((quint32)(ch1)) \ ) // common DC for all fonts QT_BEGIN_NAMESPACE class QtHDC { HDC _hdc; public: QtHDC() { HDC displayDC = GetDC(0); _hdc = CreateCompatibleDC(displayDC); ReleaseDC(0, displayDC); } ~QtHDC() { if (_hdc) DeleteDC(_hdc); } HDC hdc() const { return _hdc; } }; #ifndef QT_NO_THREAD Q_GLOBAL_STATIC(QThreadStorage, local_shared_dc) HDC shared_dc() { QtHDC *&hdc = local_shared_dc()->localData(); if (!hdc) hdc = new QtHDC; return hdc->hdc(); } #else HDC shared_dc() { return 0; } #endif #ifndef Q_WS_WINCE 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(QLatin1String("gdi32"), "GetCharWidthI"); } #endif // !defined(Q_WS_WINCE) // defined in qtextengine_win.cpp typedef void *SCRIPT_CACHE; typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *); extern fScriptFreeCache ScriptFreeCache; static inline quint32 getUInt(unsigned char *p) { quint32 val; val = *p++ << 24; val |= *p++ << 16; val |= *p++ << 8; val |= *p; return val; } static inline quint16 getUShort(unsigned char *p) { quint16 val; val = *p++ << 8; val |= *p; return val; } // general font engine QFixed QFontEngineWin::lineThickness() const { if(lineWidth > 0) return lineWidth; return QFontEngine::lineThickness(); } static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) { int size; size = GetOutlineTextMetrics(hdc, 0, 0); OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size); GetOutlineTextMetrics(hdc, size, otm); return otm; } void QFontEngineWin::getCMap() { ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); HDC hdc = shared_dc(); SelectObject(hdc, hfont); bool symb = false; if (ttf) { cmapTable = getSfntTable(qbswap(MAKE_TAG('c', 'm', 'a', 'p'))); int size = 0; cmap = QFontEngine::getCMap(reinterpret_cast(cmapTable.constData()), cmapTable.size(), &symb, &size); } if (!cmap) { ttf = false; symb = false; } symbol = symb; designToDevice = 1; _faceId.index = 0; if(cmap) { OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); unitsPerEm = otm->otmEMSquare; x_height = (int)otm->otmsXHeight; loadKerningPairs(designToDevice); _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); lineWidth = otm->otmsUnderscoreSize; fsType = otm->otmfsType; free(otm); } else { unitsPerEm = tm.tmHeight; } } inline unsigned int getChar(const QChar *str, int &i, const int len) { unsigned int uc = str[i].unicode(); if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { uint low = str[i+1].unicode(); if (low >= 0xdc00 && low < 0xe000) { uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; ++i; } } return uc; } int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const { int i = 0; int glyph_pos = 0; if (mirrored) { #if defined(Q_WS_WINCE) { #else if (symbol) { for (; i < numChars; ++i, ++glyph_pos) { unsigned int uc = getChar(str, i, numChars); glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); if (!glyphs->glyphs[glyph_pos] && uc < 0x100) glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); } } else if (ttf) { for (; i < numChars; ++i, ++glyph_pos) { unsigned int uc = getChar(str, i, numChars); glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc)); } } else { #endif wchar_t first = tm.tmFirstChar; wchar_t last = tm.tmLastChar; for (; i < numChars; ++i, ++glyph_pos) { uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); if ( #ifdef Q_WS_WINCE tm.tmFirstChar > 60000 || // see line 375 #endif ucs >= first && ucs <= last) glyphs->glyphs[glyph_pos] = ucs; else glyphs->glyphs[glyph_pos] = 0; } } } else { #if defined(Q_WS_WINCE) { #else if (symbol) { for (; i < numChars; ++i, ++glyph_pos) { unsigned int uc = getChar(str, i, numChars); glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc); if(!glyphs->glyphs[glyph_pos] && uc < 0x100) glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); } } else if (ttf) { for (; i < numChars; ++i, ++glyph_pos) { unsigned int uc = getChar(str, i, numChars); glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); } } else { #endif wchar_t first = tm.tmFirstChar; wchar_t last = tm.tmLastChar; for (; i < numChars; ++i, ++glyph_pos) { uint uc = getChar(str, i, numChars); if ( #ifdef Q_WS_WINCE tm.tmFirstChar > 60000 || // see comment in QFontEngineWin #endif uc >= first && uc <= last) glyphs->glyphs[glyph_pos] = uc; else glyphs->glyphs[glyph_pos] = 0; } } } glyphs->numGlyphs = glyph_pos; return glyph_pos; } QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont, LOGFONT lf) { //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight); _name = name; cmap = 0; hfont = _hfont; logfont = lf; HDC hdc = shared_dc(); SelectObject(hdc, hfont); this->stockFont = stockFont; fontDef.pixelSize = -lf.lfHeight; lbearing = SHRT_MIN; rbearing = SHRT_MIN; synthesized_flags = -1; lineWidth = -1; x_height = -1; BOOL res = GetTextMetrics(hdc, &tm); fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); if (!res) { qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); ZeroMemory(&tm, sizeof(TEXTMETRIC)); } cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; getCMap(); widthCache = 0; widthCacheSize = 0; designAdvances = 0; designAdvancesSize = 0; #ifndef Q_WS_WINCE if (!resolvedGetCharWidthI) resolveGetCharWidthI(); #endif } QFontEngineWin::~QFontEngineWin() { if (designAdvances) free(designAdvances); if (widthCache) free(widthCache); // make sure we aren't by accident still selected SelectObject(shared_dc(), (HFONT)GetStockObject(SYSTEM_FONT)); if (!stockFont) { if (!DeleteObject(hfont)) qErrnoWarning("QFontEngineWin: failed to delete non-stock font..."); } } HGDIOBJ QFontEngineWin::selectDesignFont() const { LOGFONT f = logfont; f.lfHeight = unitsPerEm; HFONT designFont = CreateFontIndirect(&f); return SelectObject(shared_dc(), designFont); } bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const { if (*nglyphs < len) { *nglyphs = len; return false; } *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft); if (flags & QTextEngine::GlyphIndicesOnly) return true; recalcAdvances(glyphs, flags); return true; } inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) { #if defined(Q_WS_WINCE) GetCharWidth32(hdc, glyph, glyph, &width); #else if (ptrGetCharWidthI) ptrGetCharWidthI(hdc, glyph, 1, 0, &width); #endif } void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const { HGDIOBJ oldFont = 0; HDC hdc = shared_dc(); if (ttf && (flags & QTextEngine::DesignMetrics)) { for(int i = 0; i < glyphs->numGlyphs; i++) { unsigned int glyph = glyphs->glyphs[i]; if(int(glyph) >= designAdvancesSize) { int newSize = (glyph + 256) >> 8 << 8; designAdvances = q_check_ptr((QFixed *)realloc(designAdvances, newSize*sizeof(QFixed))); 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_x[i] = designAdvances[glyph]; glyphs->advances_y[i] = 0; } if(oldFont) DeleteObject(SelectObject(hdc, oldFont)); } else { for(int i = 0; i < glyphs->numGlyphs; i++) { unsigned int glyph = glyphs->glyphs[i]; glyphs->advances_y[i] = 0; if (glyph >= widthCacheSize) { int newSize = (glyph + 256) >> 8 << 8; widthCache = q_check_ptr((unsigned char *)realloc(widthCache, newSize*sizeof(QFixed))); memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); widthCacheSize = newSize; } glyphs->advances_x[i] = widthCache[glyph]; // font-width cache failed if (glyphs->advances_x[i] == 0) { int width = 0; if (!oldFont) oldFont = SelectObject(hdc, hfont); if (!ttf) { QChar ch[2] = { ushort(glyph), 0 }; int chrLen = 1; if (glyph > 0xffff) { ch[0] = QChar::highSurrogate(glyph); ch[1] = QChar::lowSurrogate(glyph); ++chrLen; } SIZE size = {0, 0}; GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size); width = size.cx; } else { calculateTTFGlyphWidth(hdc, glyph, width); } glyphs->advances_x[i] = width; // if glyph's within cache range, store it for later if (width > 0 && width < 0x100) widthCache[glyph] = width; } } if (oldFont) SelectObject(hdc, oldFont); } } glyph_metrics_t QFontEngineWin::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); } #ifndef Q_WS_WINCE bool QFontEngineWin::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const { Q_ASSERT(metrics != 0); HDC hdc = shared_dc(); 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 = t.m11(); xform.eM12 = t.m12(); xform.eM21 = t.m21(); xform.eM22 = 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; } } #endif glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t) { #ifndef Q_WS_WINCE HDC hdc = shared_dc(); SelectObject(hdc, hfont); glyph_metrics_t glyphMetrics; bool success = getOutlineMetrics(glyph, t, &glyphMetrics); if (!ttf && !success) { // Bitmap fonts wchar_t ch = 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; #else HDC hdc = shared_dc(); HGDIOBJ oldFont = SelectObject(hdc, hfont); ABC abc; int width; int advance; #ifdef GWES_MGTT // true type fonts if (GetCharABCWidths(hdc, glyph, glyph, &abc)) { width = qAbs(abc.abcA) + abc.abcB + qAbs(abc.abcC); advance = abc.abcA + abc.abcB + abc.abcC; } else #endif #if defined(GWES_MGRAST) || defined(GWES_MGRAST2) // raster fonts if (GetCharWidth32(hdc, glyph, glyph, &width)) { advance = width; } else #endif { // fallback width = tm.tmMaxCharWidth; advance = width; } SelectObject(hdc, oldFont); return glyph_metrics_t(0, -tm.tmAscent, width, tm.tmHeight, advance, 0).transformed(t); #endif } QFixed QFontEngineWin::ascent() const { return tm.tmAscent; } QFixed QFontEngineWin::descent() const { // ### we substract 1 to even out the historical +1 in QFontMetrics's // ### height=asc+desc+1 equation. Fix in Qt5. return tm.tmDescent - 1; } QFixed QFontEngineWin::leading() const { return tm.tmExternalLeading; } QFixed QFontEngineWin::xHeight() const { if(x_height >= 0) return x_height; return QFontEngine::xHeight(); } QFixed QFontEngineWin::averageCharWidth() const { return tm.tmAveCharWidth; } qreal QFontEngineWin::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 QFontEngineWin::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) { HDC hdc = shared_dc(); SelectObject(hdc, hfont); #ifndef Q_WS_WINCE if (ttf) #endif { ABC abcWidths; GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); if (leftBearing) *leftBearing = abcWidths.abcA; if (rightBearing) *rightBearing = abcWidths.abcC; } #ifndef Q_WS_WINCE else { QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); } #endif } #endif // Q_CC_MINGW qreal QFontEngineWin::minLeftBearing() const { if (lbearing == SHRT_MIN) minRightBearing(); // calculates both return lbearing; } qreal QFontEngineWin::minRightBearing() const { #ifdef Q_WS_WINCE if (rbearing == SHRT_MIN) { int ml = 0; int mr = 0; HDC hdc = shared_dc(); 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; } lbearing = ml; rbearing = mr; } return rbearing; #else if (rbearing == SHRT_MIN) { int ml = 0; int mr = 0; HDC hdc = shared_dc(); 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 string->unicode() || tm.tmLastChar < string->unicode()) return false; } } return true; } QFontEngine::Type QFontEngineWin::type() const { return QFontEngine::Win; } 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) { return QPointF(qt_fixed_to_double(pt.x) * scale, -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) { #if defined(Q_WS_WINCE) Q_UNUSED(glyph); Q_UNUSED(hdc); #endif 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; uint glyphFormat = GGO_NATIVE; if (ttf) glyphFormat |= GGO_GLYPH_INDEX; GLYPHMETRICS gMetric; memset(&gMetric, 0, sizeof(GLYPHMETRICS)); int bufferSize = GDI_ERROR; #if !defined(Q_WS_WINCE) bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); #endif if ((DWORD)bufferSize == GDI_ERROR) { return false; } void *dataBuffer = new char[bufferSize]; DWORD ret = GDI_ERROR; #if !defined(Q_WS_WINCE) ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); #endif if (ret == GDI_ERROR) { delete [](char *)dataBuffer; return false; } if(metric) { // #### obey scale *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, (int)gMetric.gmBlackBoxX, (int)gMetric.gmBlackBoxY, gMetric.gmCellIncX, gMetric.gmCellIncY); } int offset = 0; int headerOffset = 0; TTPOLYGONHEADER *ttph = 0; QPointF oset = position.toPointF(); while (headerOffset < bufferSize) { ttph = (TTPOLYGONHEADER*)((char *)dataBuffer + headerOffset); QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale)); path->moveTo(lastPoint + oset); offset += sizeof(TTPOLYGONHEADER); TTPOLYCURVE *curve; while (offsetcb)) { curve = (TTPOLYCURVE*)((char*)(dataBuffer) + offset); switch (curve->wType) { case TT_PRIM_LINE: { for (int i=0; icpfx; ++i) { QPointF p = qt_to_qpointf(curve->apfx[i], scale) + 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; icpfx - 1; ++i) { QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset; QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + 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; icpfx; ) { QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset; QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset; QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + 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 [] (char*)dataBuffer; return true; } void QFontEngineWin::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags) { LOGFONT lf = 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 = shared_dc(); HGDIOBJ oldfont = SelectObject(hdc, hf); for(int i = 0; i < nglyphs; ++i) { if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, qreal(fontDef.pixelSize) / unitsPerEm)) { // 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 QFontEngineWin::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) { #if !defined(Q_WS_WINCE) 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; } } #endif QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); } QFontEngine::FaceId QFontEngineWin::faceId() const { return _faceId; } QT_BEGIN_INCLUDE_NAMESPACE #include QT_END_INCLUDE_NAMESPACE int QFontEngineWin::synthesized() const { if(synthesized_flags == -1) { synthesized_flags = 0; if(ttf) { const DWORD HEAD = MAKE_TAG('h', 'e', 'a', 'd'); HDC hdc = shared_dc(); SelectObject(hdc, hfont); uchar data[4]; GetFontData(hdc, HEAD, 44, &data, 4); USHORT macStyle = getUShort(data); if (tm.tmItalic && !(macStyle & 2)) synthesized_flags = SynthesizedItalic; if (fontDef.stretch != 100 && ttf) synthesized_flags |= SynthesizedStretch; if (tm.tmWeight >= 500 && !(macStyle & 1)) synthesized_flags |= SynthesizedBold; //qDebug() << "font is" << _name << // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; } } return synthesized_flags; } QFixed QFontEngineWin::emSquareSize() const { return unitsPerEm; } QFontEngine::Properties QFontEngineWin::properties() const { LOGFONT lf = logfont; lf.lfHeight = unitsPerEm; HFONT hf = CreateFontIndirect(&lf); HDC hdc = shared_dc(); HGDIOBJ oldfont = SelectObject(hdc, hf); OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); Properties p; p.emSquare = unitsPerEm; p.italicAngle = otm->otmItalicAngle; p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1(); p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1(); p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(p.postscriptName); 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 QFontEngineWin::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) { LOGFONT lf = logfont; lf.lfHeight = unitsPerEm; int flags = synthesized(); if(flags & SynthesizedItalic) lf.lfItalic = false; lf.lfWidth = 0; HFONT hf = CreateFontIndirect(&lf); HDC hdc = shared_dc(); 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 QFontEngineWin::getSfntTableData(uint tag, uchar *buffer, uint *length) const { if (!ttf) return false; HDC hdc = shared_dc(); SelectObject(hdc, hfont); DWORD t = qbswap(tag); *length = GetFontData(hdc, t, 0, buffer, *length); return *length != GDI_ERROR; } #if !defined(CLEARTYPE_QUALITY) # define CLEARTYPE_QUALITY 5 #endif extern bool qt_cleartype_enabled; QNativeImage *QFontEngineWin::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 || iw <= 0) return 0; bool has_transformation = t.type() > QTransform::TxTranslate; #ifndef Q_WS_WINCE unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; XFORM xform; if (has_transformation) { xform.eM11 = t.m11(); xform.eM12 = t.m12(); xform.eM21 = t.m21(); xform.eM22 = t.m22(); xform.eDx = margin; xform.eDy = margin; QtHDC qthdc; HDC hdc = qthdc.hdc(); SetGraphicsMode(hdc, GM_ADVANCED); SetWorldTransform(hdc, &xform); HGDIOBJ old_font = SelectObject(hdc, font); int 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; if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == GDI_ERROR) { qWarning("QWinFontEngine: unable to query transformed glyph metrics..."); return 0; } iw = tgm.gmBlackBoxX; ih = tgm.gmBlackBoxY; xform.eDx -= tgm.gmptGlyphOrigin.x; xform.eDy += tgm.gmptGlyphOrigin.y; SetGraphicsMode(hdc, GM_COMPATIBLE); SelectObject(hdc, old_font); } #else // else winc unsigned int options = 0; #ifdef DEBUG Q_ASSERT(!has_transformation); #else Q_UNUSED(has_transformation); #endif #endif QNativeImage *ni = new QNativeImage(iw + 2 * margin + 4, ih + 2 * margin + 4, QNativeImage::systemFormat(), !qt_cleartype_enabled); /*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); #ifndef Q_OS_WINCE if (has_transformation) { SetGraphicsMode(hdc, GM_ADVANCED); SetWorldTransform(hdc, &xform); ExtTextOut(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0); } else #endif { ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0); } SelectObject(hdc, old_font); return ni; } extern uint qt_pow_gamma[256]; QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) { HFONT font = hfont; if (qt_cleartype_enabled) { LOGFONT lf = logfont; lf.lfQuality = ANTIALIASED_QUALITY; font = CreateFontIndirect(&lf); } QImage::Format mask_format = QNativeImage::systemFormat(); #ifndef Q_OS_WINCE mask_format = QImage::Format_RGB32; #endif QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); if (mask == 0) return QImage(); QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8); // ### This part is kinda pointless, but we'll crash later if we don't because some // code paths expects there to be colortables for index8-bit... QVector colors(256); for (int i=0; i<256; ++i) colors[i] = qRgba(0, 0, 0, i); indexed.setColorTable(colors); // Copy data... Cannot use QPainter here as GDI has messed up the // Alpha channel of the ni.image pixels... for (int y=0; yheight(); ++y) { uchar *dest = indexed.scanLine(y); if (mask->image.format() == QImage::Format_RGB16) { const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y); for (int x=0; xwidth(); ++x) dest[x] = 255 - qGray(src[x]); } else { const uint *src = (uint *) ((const QImage &) mask->image).scanLine(y); for (int x=0; xwidth(); ++x) { #ifdef Q_OS_WINCE dest[x] = 255 - qGray(src[x]); #else if (QNativeImage::systemFormat() == QImage::Format_RGB16) dest[x] = 255 - qGray(src[x]); else dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.); #endif } } } // Cleanup... delete mask; if (qt_cleartype_enabled) { DeleteObject(font); } return indexed; } #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C #define SPI_SETFONTSMOOTHINGCONTRAST 0x200D QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) { HFONT font = hfont; int contrast; SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); QNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 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; yheight(); ++y) { uint *dest = (uint *) rgbMask.scanLine(y); const uint *src = (uint *) source.scanLine(y); for (int x=0; xwidth(); ++x) { dest[x] = 0xffffffff - (0x00ffffff & src[x]); } } delete mask; return rgbMask; } // -------------------------------------- Multi font engine QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks) : QFontEngineMulti(fallbacks.size()+1), fallbacks(fallbacks) { engines[0] = first; first->ref.ref(); fontDef = engines[0]->fontDef; cache_cost = first->cache_cost; } void QFontEngineMultiWin::loadEngine(int at) { Q_ASSERT(at < engines.size()); Q_ASSERT(engines.at(at) == 0); QString fam = fallbacks.at(at-1); LOGFONT lf = static_cast(engines.at(0))->logfont; memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded HFONT hfont = CreateFontIndirect(&lf); bool stockFont = false; if (hfont == 0) { hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); stockFont = true; } engines[at] = new QFontEngineWin(fam, hfont, stockFont, lf); engines[at]->ref.ref(); engines[at]->fontDef = fontDef; // TODO: increase cost in QFontCache for the font engine loaded here } QT_END_NAMESPACE