Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsfontengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
8#include <QtCore/qt_windows.h>
9#if QT_CONFIG(directwrite)
10# include "qwindowsfontenginedirectwrite_p.h"
11#endif
12#include <QtGui/qpa/qplatformintegration.h>
13#include <QtGui/private/qtextengine_p.h> // glyph_metrics_t
14#include <QtGui/private/qguiapplication_p.h>
15#include <QtGui/QPaintDevice>
16#include <QtGui/QBitmap>
17#include <QtGui/QPainter>
18#include <QtGui/private/qpainter_p.h>
19#include <QtGui/QPaintEngine>
20#include <QtGui/private/qpaintengine_raster_p.h>
21#include <QtGui/private/qtgui-config_p.h>
22
23#include <QtCore/QtEndian>
24#include <QtCore/QFile>
25#include <QtCore/qmath.h>
26#include <QtCore/QTextStream>
27#include <QtCore/QThreadStorage>
28#include <QtCore/private/qsystemlibrary_p.h>
29#include <QtCore/private/qstringiterator_p.h>
30
31#include <QtCore/QDebug>
32
33#include <limits.h>
34
35#if QT_CONFIG(directwrite)
36# include <dwrite.h>
37# include <comdef.h>
38#endif
39
40QT_BEGIN_NAMESPACE
41
42QT_IMPL_METATYPE_EXTERN(HFONT)
43QT_IMPL_METATYPE_EXTERN(LOGFONT)
44
45//### mingw needed define
46#ifndef TT_PRIM_CSPLINE
47#define TT_PRIM_CSPLINE 3
48#endif
49
50// GetFontData expects the tags in little endian ;(
51#define MAKE_LITTLE_ENDIAN_TAG(ch1, ch2, ch3, ch4) (
52 (((quint32)(ch4)) << 24) |
53 (((quint32)(ch3)) << 16) |
54 (((quint32)(ch2)) << 8) |
55 ((quint32)(ch1))
56 )
57
58// common DC for all fonts
59
60// general font engine
61
62QFixed QWindowsFontEngine::lineThickness() const
63{
64 if (lineWidth > 0)
65 return lineWidth;
66
67 return QFontEngine::lineThickness();
68}
69
70static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc)
71{
72 const auto size = GetOutlineTextMetrics(hdc, 0, nullptr);
73 if (size == 0)
74 return nullptr;
75 auto otm = reinterpret_cast<OUTLINETEXTMETRIC *>(malloc(size));
76 if (otm == nullptr)
77 return nullptr;
78 if (GetOutlineTextMetrics(hdc, size, otm) == 0) {
79 free(otm);
80 return nullptr;
81 }
82 return otm;
83}
84
85bool QWindowsFontEngine::hasCFFTable() const
86{
87 HDC hdc = m_fontEngineData->hdc;
88 SelectObject(hdc, hfont);
89 return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR;
90}
91
92bool QWindowsFontEngine::hasCMapTable() const
93{
94 HDC hdc = m_fontEngineData->hdc;
95 SelectObject(hdc, hfont);
96 return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('c', 'm', 'a', 'p'), 0, 0, 0) != GDI_ERROR;
97}
98
99static inline QString stringFromOutLineTextMetric(const OUTLINETEXTMETRIC *otm, PSTR offset)
100{
101 const uchar *p = reinterpret_cast<const uchar *>(otm) + quintptr(offset);
102 return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(p));
103}
104
105void QWindowsFontEngine::getCMap()
106{
107 ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE) || hasCMapTable();
108
109 cffTable = hasCFFTable();
110
111 HDC hdc = m_fontEngineData->hdc;
112 SelectObject(hdc, hfont);
113 bool symb = false;
114 if (ttf) {
115 cmapTable = getSfntTable(QFont::Tag("cmap").value());
116 cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()),
117 cmapTable.size(), &symb, &cmapSize);
118 }
119 if (!cmap) {
120 ttf = false;
121 symb = false;
122 }
123 symbol = symb;
124 designToDevice = 1;
125 _faceId.index = 0;
126 OUTLINETEXTMETRIC *otm = cmap != nullptr ? getOutlineTextMetric(hdc) : nullptr;
127 if (otm != nullptr) {
128 unitsPerEm = int(otm->otmEMSquare);
129 const QFixed unitsPerEmF(unitsPerEm);
130 designToDevice = unitsPerEmF / QFixed::fromReal(fontDef.pixelSize);
131 x_height = int(otm->otmsXHeight);
132 loadKerningPairs(designToDevice);
133 _faceId.filename = QFile::encodeName(stringFromOutLineTextMetric(otm, otm->otmpFullName));
134 lineWidth = otm->otmsUnderscoreSize;
135 fsType = otm->otmfsType;
136 free(otm);
137
138 } else {
139 unitsPerEm = tm.tmHeight;
140 }
141}
142
143int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const
144{
145 *mappedGlyphs = 0;
146 int glyph_pos = 0;
147 {
148 if (symbol) {
149 QStringIterator it(str, str + numChars);
150 while (it.hasNext()) {
151 const uint uc = it.next();
152 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
153 if (!glyphs->glyphs[glyph_pos] && uc < 0x100)
154 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000);
155 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
156 (*mappedGlyphs)++;
157 ++glyph_pos;
158 }
159 } else if (ttf) {
160 QStringIterator it(str, str + numChars);
161 while (it.hasNext()) {
162 const uint uc = it.next();
163 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc);
164 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
165 (*mappedGlyphs)++;
166 ++glyph_pos;
167 }
168 } else {
169 QStringIterator it(str, str + numChars);
170 while (it.hasNext()) {
171 const uint uc = it.next();
172 if (uc >= tm.tmFirstChar && uc <= tm.tmLastChar)
173 glyphs->glyphs[glyph_pos] = uc;
174 else
175 glyphs->glyphs[glyph_pos] = 0;
176 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
177 (*mappedGlyphs)++;
178 ++glyph_pos;
179 }
180 }
181 }
182 glyphs->numGlyphs = glyph_pos;
183 return glyph_pos;
184}
185
186/*!
187 \class QWindowsFontEngine
188 \brief Standard Windows font engine.
189 \internal
190
191 Will probably be superseded by a common Free Type font engine in Qt 5.X.
192*/
193
194QWindowsFontEngine::QWindowsFontEngine(const QString &name,
195 LOGFONT lf,
196 const QSharedPointer<QWindowsFontEngineData> &fontEngineData)
197 : QFontEngine(Win),
198 m_fontEngineData(fontEngineData),
199 _name(name),
200 m_logfont(lf),
201 ttf(0),
202 hasOutline(0)
203{
204 qCDebug(lcQpaFonts) << __FUNCTION__ << name << lf.lfHeight;
205 hfont = CreateFontIndirect(&m_logfont);
206 if (!hfont) {
207 qErrnoWarning("%s: CreateFontIndirect failed for family '%s'", __FUNCTION__, qPrintable(name));
208 hfont = QWindowsFontDatabase::systemFont();
209 }
210
211 HDC hdc = m_fontEngineData->hdc;
212 SelectObject(hdc, hfont);
213 const BOOL res = GetTextMetrics(hdc, &tm);
214 if (!res) {
215 qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__);
216 ZeroMemory(&tm, sizeof(TEXTMETRIC));
217 }
218
219 fontDef.pixelSize = -lf.lfHeight;
220 fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
221
222 cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000;
223 getCMap();
224
225 hasUnreliableOutline = (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) == 0;
226}
227
228QWindowsFontEngine::~QWindowsFontEngine()
229{
230 if (designAdvances)
231 free(designAdvances);
232
233 if (widthCache)
234 free(widthCache);
235
236 // make sure we aren't by accident still selected
237 SelectObject(m_fontEngineData->hdc, QWindowsFontDatabase::systemFont());
238
239 if (!DeleteObject(hfont))
240 qErrnoWarning("%s: QFontEngineWin: failed to delete font...", __FUNCTION__);
241 qCDebug(lcQpaFonts) << __FUNCTION__ << _name;
242
243 if (!uniqueFamilyName.isEmpty()) {
244 if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) {
245 QPlatformFontDatabase *pfdb = pi->fontDatabase();
246 static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(uniqueFamilyName);
247 }
248 }
249}
250
251glyph_t QWindowsFontEngine::glyphIndex(uint ucs4) const
252{
253 glyph_t glyph = 0;
254
255 if (symbol) {
256 glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4);
257 if (glyph == 0 && ucs4 < 0x100)
258 glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4 + 0xf000);
259 } else if (ttf) {
260 glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4);
261 } else if (ucs4 >= tm.tmFirstChar && ucs4 <= tm.tmLastChar) {
262 glyph = ucs4;
263 }
264
265 return glyph;
266}
267
268HGDIOBJ QWindowsFontEngine::selectDesignFont() const
269{
270 LOGFONT f = m_logfont;
271 f.lfHeight = -unitsPerEm;
272 f.lfWidth = 0;
273 HFONT designFont = CreateFontIndirect(&f);
274 return SelectObject(m_fontEngineData->hdc, designFont);
275}
276
277int QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
278{
279 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
280 if (*nglyphs < len) {
281 *nglyphs = len;
282 return false;
283 }
284
285 glyphs->numGlyphs = *nglyphs;
286 int mappedGlyphs;
287 *nglyphs = getGlyphIndexes(str, len, glyphs, &mappedGlyphs);
288
289 if (!(flags & GlyphIndicesOnly))
290 recalcAdvances(glyphs, flags);
291
292 return mappedGlyphs;
293}
294
295inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width)
296{
297 GetCharWidthI(hdc, glyph, 1, 0, &width);
298}
299
300void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
301{
302 HGDIOBJ oldFont = 0;
303 HDC hdc = m_fontEngineData->hdc;
304 // Sanity check: For TrueType fonts, the glyphs are glyph indexes, which are 16-bit. For other
305 // fonts, they are Unicode code points. Clamp to avoid overflow when growing the caches below.
306 const uint maxGlyph = ttf ? 0xffff : uint(QChar::LastValidCodePoint);
307 if (ttf && (flags & DesignMetrics)) {
308 for(int i = 0; i < glyphs->numGlyphs; i++) {
309 unsigned int glyph = qMin(glyphs->glyphs[i], maxGlyph);
310 if (int(glyph) >= designAdvancesSize) {
311 const int newSize = int(glyph + 256) >> 8 << 8;
312 designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed)));
313 Q_CHECK_PTR(designAdvances);
314 for(int i = designAdvancesSize; i < newSize; ++i)
315 designAdvances[i] = -1000000;
316 designAdvancesSize = newSize;
317 }
318 if (designAdvances[glyph] < -999999) {
319 if (!oldFont)
320 oldFont = selectDesignFont();
321
322 int width = 0;
323 calculateTTFGlyphWidth(hdc, glyph, width);
324 designAdvances[glyph] = QFixed(width) / designToDevice;
325 }
326 glyphs->advances[i] = designAdvances[glyph];
327 }
328 if (oldFont)
329 DeleteObject(SelectObject(hdc, oldFont));
330 } else {
331 for(int i = 0; i < glyphs->numGlyphs; i++) {
332 unsigned int glyph = qMin(glyphs->glyphs[i], maxGlyph);
333
334 if (glyph >= widthCacheSize) {
335 const uint newSize = (glyph + 256) >> 8 << 8;
336 widthCache = reinterpret_cast<unsigned char *>(realloc(widthCache, newSize));
337 Q_CHECK_PTR(widthCache);
338 memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize);
339 widthCacheSize = newSize;
340 }
341 glyphs->advances[i] = widthCache[glyph];
342 // font-width cache failed
343 if (glyphs->advances[i].value() == 0) {
344 int width = 0;
345 if (!oldFont)
346 oldFont = SelectObject(hdc, hfont);
347
348 if (!ttf) {
349 QChar ch[2] = { ushort(glyph), u'\0' };
350 int chrLen = 1;
351 if (QChar::requiresSurrogates(glyph)) {
352 ch[0] = QChar::highSurrogate(glyph);
353 ch[1] = QChar::lowSurrogate(glyph);
354 ++chrLen;
355 }
356 SIZE size = {0, 0};
357 GetTextExtentPoint32(hdc, reinterpret_cast<const wchar_t *>(ch), chrLen, &size);
358 width = size.cx;
359 } else {
360 calculateTTFGlyphWidth(hdc, glyph, width);
361 }
362 glyphs->advances[i] = width;
363 // if glyph's within cache range, store it for later
364 if (width > 0 && width < 0x100)
365 widthCache[glyph] = uchar(width);
366 }
367 }
368
369 if (oldFont)
370 SelectObject(hdc, oldFont);
371 }
372}
373
374bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const
375{
376 Q_ASSERT(metrics != 0);
377
378 HDC hdc = m_fontEngineData->hdc;
379
380 GLYPHMETRICS gm;
381 DWORD res = 0;
382 MAT2 mat;
383 mat.eM11.value = mat.eM22.value = 1;
384 mat.eM11.fract = mat.eM22.fract = 0;
385 mat.eM21.value = mat.eM12.value = 0;
386 mat.eM21.fract = mat.eM12.fract = 0;
387
388 if (t.type() > QTransform::TxTranslate) {
389 // We need to set the transform using the HDC's world
390 // matrix rather than using the MAT2 above, because the
391 // results provided when transforming via MAT2 does not
392 // match the glyphs that are drawn using a WorldTransform
393 XFORM xform;
394 xform.eM11 = FLOAT(t.m11());
395 xform.eM12 = FLOAT(t.m12());
396 xform.eM21 = FLOAT(t.m21());
397 xform.eM22 = FLOAT(t.m22());
398 xform.eDx = 0;
399 xform.eDy = 0;
400 SetGraphicsMode(hdc, GM_ADVANCED);
401 SetWorldTransform(hdc, &xform);
402 }
403
404 uint format = GGO_METRICS;
405 if (ttf)
406 format |= GGO_GLYPH_INDEX;
407 res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat);
408
409 if (t.type() > QTransform::TxTranslate) {
410 XFORM xform;
411 xform.eM11 = xform.eM22 = 1;
412 xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0;
413 SetWorldTransform(hdc, &xform);
414 SetGraphicsMode(hdc, GM_COMPATIBLE);
415 }
416
417 if (res != GDI_ERROR) {
418 *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
419 int(gm.gmBlackBoxX), int(gm.gmBlackBoxY),
420 gm.gmCellIncX, gm.gmCellIncY);
421 return true;
422 } else {
423 return false;
424 }
425}
426
427glyph_metrics_t QWindowsFontEngine::boundingBox(glyph_t glyph, const QTransform &t)
428{
429 HDC hdc = m_fontEngineData->hdc;
430 SelectObject(hdc, hfont);
431
432 glyph_metrics_t glyphMetrics;
433 bool success = getOutlineMetrics(glyph, t, &glyphMetrics);
434
435 if (!ttf && !success) {
436 // Bitmap fonts
437 wchar_t ch = wchar_t(glyph);
438 ABCFLOAT abc;
439 GetCharABCWidthsFloat(hdc, ch, ch, &abc);
440 int width = qRound(abc.abcfB);
441
442 return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t);
443 }
444
445 return glyphMetrics;
446}
447
448namespace {
449# pragma pack(1)
450
451 struct OS2Table
452 {
453 quint16 version;
454 qint16 avgCharWidth;
455 quint16 weightClass;
456 quint16 widthClass;
457 quint16 type;
458 qint16 subscriptXSize;
459 qint16 subscriptYSize;
460 qint16 subscriptXOffset;
461 qint16 subscriptYOffset;
462 qint16 superscriptXSize;
463 qint16 superscriptYSize;
464 qint16 superscriptXOffset;
465 qint16 superscriptYOffset;
466 qint16 strikeOutSize;
467 qint16 strikeOutPosition;
468 qint16 familyClass;
469 quint8 panose[10];
470 quint32 unicodeRanges[4];
471 quint8 vendorID[4];
472 quint16 selection;
473 quint16 firstCharIndex;
474 quint16 lastCharIndex;
475 qint16 typoAscender;
476 qint16 typoDescender;
477 qint16 typoLineGap;
478 quint16 winAscent;
479 quint16 winDescent;
480 quint32 codepageRanges[2];
481 qint16 height;
482 qint16 capHeight;
483 quint16 defaultChar;
484 quint16 breakChar;
485 quint16 maxContext;
486 };
487
488# pragma pack()
489}
490
491QFixed QWindowsFontEngine::capHeight() const
492{
493 const QByteArray tableData = getSfntTable(QFont::Tag("OS/2").value());
494 if (size_t(tableData.size()) >= sizeof(OS2Table)) {
495 const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData());
496 if (qFromBigEndian<quint16>(table->version) >= 2) {
497 qint16 capHeight = qFromBigEndian<qint16>(table->capHeight);
498 if (capHeight > 0)
499 return QFixed(capHeight) / designToDevice;
500 }
501 }
502 return calculatedCapHeight();
503}
504
505QFixed QWindowsFontEngine::xHeight() const
506{
507 if (x_height >= 0)
508 return x_height;
509 return QFontEngine::xHeight();
510}
511
512QFixed QWindowsFontEngine::averageCharWidth() const
513{
514 return tm.tmAveCharWidth;
515}
516
517qreal QWindowsFontEngine::maxCharWidth() const
518{
519 return tm.tmMaxCharWidth;
520}
521
522enum { max_font_count = 256 };
523static const ushort char_table[] = {
524 40,
525 67,
526 70,
527 75,
528 86,
529 88,
530 89,
531 91,
532 102,
533 114,
534 124,
535 127,
536 205,
537 645,
538 884,
539 922,
540 1070,
541 12386,
542 0
543};
544
545static const int char_table_entries = sizeof(char_table)/sizeof(ushort);
546
547#ifndef Q_CC_MINGW
548void QWindowsFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
549{
550 HDC hdc = m_fontEngineData->hdc;
551 SelectObject(hdc, hfont);
552
553 if (ttf) {
554 ABC abcWidths;
555 GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths);
556 if (leftBearing)
557 *leftBearing = abcWidths.abcA;
558 if (rightBearing)
559 *rightBearing = abcWidths.abcC;
560 } else {
561 QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing);
562 }
563}
564#endif // Q_CC_MINGW
565
567{
568 m_ascent = tm.tmAscent;
569 m_descent = tm.tmDescent;
570 m_leading = tm.tmExternalLeading;
571
572 QFontEngine::initializeHeightMetrics();
573}
574
576{
577 return hasUnreliableOutline || QFontEngine::hasUnreliableGlyphOutline();
578}
579
581{
582 if (lbearing == SHRT_MIN)
583 minRightBearing(); // calculates both
584
585 return lbearing;
586}
587
589{
590 if (rbearing == SHRT_MIN) {
591 int ml = 0;
592 int mr = 0;
593 HDC hdc = m_fontEngineData->hdc;
594 SelectObject(hdc, hfont);
595 if (ttf) {
596 ABC *abc = nullptr;
597 int n = tm.tmLastChar - tm.tmFirstChar;
598 if (n <= max_font_count) {
599 abc = new ABC[n+1];
600 GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc);
601 } else {
602 abc = new ABC[char_table_entries+1];
603 for(int i = 0; i < char_table_entries; i++)
604 GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i);
606 }
607 ml = abc[0].abcA;
608 mr = abc[0].abcC;
609 for (int i = 1; i < n; i++) {
610 if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) {
611 ml = qMin(ml,abc[i].abcA);
612 mr = qMin(mr,abc[i].abcC);
613 }
614 }
615 delete [] abc;
616 } else {
617 ABCFLOAT *abc = 0;
618 int n = tm.tmLastChar - tm.tmFirstChar+1;
619 if (n <= max_font_count) {
620 abc = new ABCFLOAT[n];
621 GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc);
622 } else {
623 abc = new ABCFLOAT[char_table_entries];
624 for(int i = 0; i < char_table_entries; i++)
625 GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i);
627 }
628 float fml = abc[0].abcfA;
629 float fmr = abc[0].abcfC;
630 for (int i=1; i<n; i++) {
631 if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) {
632 fml = qMin(fml,abc[i].abcfA);
633 fmr = qMin(fmr,abc[i].abcfC);
634 }
635 }
636 ml = qFloor(fml);
637 mr = qFloor(fmr);
638 delete [] abc;
639 }
640 lbearing = ml;
641 rbearing = mr;
642 }
643
644 return rbearing;
645}
646
647static inline double qt_fixed_to_double(const FIXED &p) {
648 return ((p.value << 16) + p.fract) / 65536.0;
649}
650
651static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale, qreal stretch) {
652 return QPointF(qt_fixed_to_double(pt.x) * scale * stretch, -qt_fixed_to_double(pt.y) * scale);
653}
654
655#ifndef GGO_UNHINTED
656#define GGO_UNHINTED 0x0100
657#endif
658
659static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc,
660 QPainterPath *path, bool ttf, glyph_metrics_t *metric = nullptr,
661 qreal scale = 1.0, qreal stretch = 1.0)
662{
663 MAT2 mat;
664 mat.eM11.value = mat.eM22.value = 1;
665 mat.eM11.fract = mat.eM22.fract = 0;
666 mat.eM21.value = mat.eM12.value = 0;
667 mat.eM21.fract = mat.eM12.fract = 0;
668
669 GLYPHMETRICS gMetric;
670 memset(&gMetric, 0, sizeof(GLYPHMETRICS));
671
672 if (metric) {
673 // If metrics requested, retrieve first using GGO_METRICS, because the returned
674 // values are incorrect for OpenType PS fonts if obtained at the same time as the
675 // glyph paths themselves (ie. with GGO_NATIVE as the format).
676 uint format = GGO_METRICS;
677 if (ttf)
678 format |= GGO_GLYPH_INDEX;
679 if (GetGlyphOutline(hdc, glyph, format, &gMetric, 0, 0, &mat) == GDI_ERROR)
680 return false;
681 // #### obey scale
682 *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y,
683 int(gMetric.gmBlackBoxX), int(gMetric.gmBlackBoxY),
684 gMetric.gmCellIncX, gMetric.gmCellIncY);
685 }
686
687 uint glyphFormat = GGO_NATIVE;
688
689 if (ttf)
690 glyphFormat |= GGO_GLYPH_INDEX;
691
692 const DWORD bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat);
693 if (bufferSize == GDI_ERROR)
694 return false;
695
696 char *dataBuffer = new char[bufferSize];
697 DWORD ret = GDI_ERROR;
698 ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat);
699 if (ret == GDI_ERROR) {
700 delete [] dataBuffer;
701 return false;
702 }
703
704 DWORD offset = 0;
705 DWORD headerOffset = 0;
706
707 QPointF oset = position.toPointF();
708 while (headerOffset < bufferSize) {
709 const TTPOLYGONHEADER *ttph = reinterpret_cast<const TTPOLYGONHEADER *>(dataBuffer + headerOffset);
710
711 QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale, stretch));
712 path->moveTo(lastPoint + oset);
713 offset += sizeof(TTPOLYGONHEADER);
714 while (offset < headerOffset + ttph->cb) {
715 const TTPOLYCURVE *curve = reinterpret_cast<const TTPOLYCURVE *>(dataBuffer + offset);
716 switch (curve->wType) {
717 case TT_PRIM_LINE: {
718 for (int i=0; i<curve->cpfx; ++i) {
719 QPointF p = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset;
720 path->lineTo(p);
721 }
722 break;
723 }
724 case TT_PRIM_QSPLINE: {
725 const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1);
726 QPointF prev(elm.x, elm.y);
727 QPointF endPoint;
728 for (int i=0; i<curve->cpfx - 1; ++i) {
729 QPointF p1 = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset;
730 QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale, stretch) + oset;
731 if (i < curve->cpfx - 2) {
732 endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2);
733 } else {
734 endPoint = p2;
735 }
736
737 path->quadTo(p1, endPoint);
738 prev = endPoint;
739 }
740
741 break;
742 }
743 case TT_PRIM_CSPLINE: {
744 for (int i=0; i<curve->cpfx; ) {
745 QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset;
746 QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset;
747 QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset;
748 path->cubicTo(p2, p3, p4);
749 }
750 break;
751 }
752 default:
753 qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case");
754 }
755 offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX);
756 }
757 path->closeSubpath();
758 headerOffset += ttph->cb;
759 }
760 delete [] dataBuffer;
761
762 return true;
763}
764
765void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
766 QPainterPath *path, QTextItem::RenderFlags)
767{
768 LOGFONT lf = m_logfont;
769 // The sign must be negative here to make sure we match against character height instead of
770 // hinted cell height. This ensures that we get linear matching, and we need this for
771 // paths since we later on apply a scaling transform to the glyph outline to get the
772 // font at the correct pixel size.
773 lf.lfHeight = -unitsPerEm;
774 lf.lfWidth = 0;
775 HFONT hf = CreateFontIndirect(&lf);
776 HDC hdc = m_fontEngineData->hdc;
777 HGDIOBJ oldfont = SelectObject(hdc, hf);
778
779 qreal scale = qreal(fontDef.pixelSize) / unitsPerEm;
780 qreal stretch = fontDef.stretch ? qreal(fontDef.stretch) / 100 : 1.0;
781 for(int i = 0; i < nglyphs; ++i) {
782 if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0,
783 scale, stretch)) {
784 // Some windows fonts, like "Modern", are vector stroke
785 // fonts, which are reported as TMPF_VECTOR but do not
786 // support GetGlyphOutline, and thus we set this bit so
787 // that addOutLineToPath can check it and return safely...
788 hasOutline = false;
789 break;
790 }
791 }
792 DeleteObject(SelectObject(hdc, oldfont));
793}
794
795void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
796 QPainterPath *path, QTextItem::RenderFlags flags)
797{
798 if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) {
799 hasOutline = true;
800 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
801 if (hasOutline) {
802 // has_outline is set to false if addGlyphToPath gets
803 // false from GetGlyphOutline, meaning its not an outline
804 // font.
805 return;
806 }
807 }
808 QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags);
809}
810
812{
813 return _faceId;
814}
815
816QT_BEGIN_INCLUDE_NAMESPACE
817#include <qdebug.h>
818QT_END_INCLUDE_NAMESPACE
819
821{
822 if (synthesized_flags == -1) {
823 synthesized_flags = 0;
824 if (ttf) {
825 const DWORD HEAD = MAKE_LITTLE_ENDIAN_TAG('h', 'e', 'a', 'd');
826 HDC hdc = m_fontEngineData->hdc;
827 SelectObject(hdc, hfont);
828 uchar data[4];
829 if (GetFontData(hdc, HEAD, 44, &data, 4) != GDI_ERROR) {
830 USHORT macStyle = qt_getUShort(data);
831 if (tm.tmItalic && !(macStyle & 2))
832 synthesized_flags = SynthesizedItalic;
833 if (tm.tmWeight >= 500 && tm.tmWeight < 750 && !(macStyle & 1))
834 synthesized_flags |= SynthesizedBold;
835 //qDebug() << "font is" << _name <<
836 // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags;
837 }
838 if (fontDef.stretch != 100 && ttf)
839 synthesized_flags |= SynthesizedStretch;
840 }
841 }
842 return synthesized_flags;
843}
844
846{
847 return unitsPerEm;
848}
849
851{
852 LOGFONT lf = m_logfont;
853 lf.lfHeight = unitsPerEm;
854 HFONT hf = CreateFontIndirect(&lf);
855 HDC hdc = m_fontEngineData->hdc;
856 HGDIOBJ oldfont = SelectObject(hdc, hf);
857 OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc);
858 if (otm == nullptr) {
859 DeleteObject(SelectObject(hdc, oldfont));
860 return QFontEngine::properties();
861 }
862 Properties p;
863 p.emSquare = unitsPerEm;
864 p.italicAngle = otm->otmItalicAngle;
865 const QByteArray name = stringFromOutLineTextMetric(otm, otm->otmpFamilyName).toLatin1()
866 + stringFromOutLineTextMetric(otm, otm->otmpStyleName).toLatin1();
867 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(name);
868 p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top,
869 otm->otmrcFontBox.right - otm->otmrcFontBox.left,
870 otm->otmrcFontBox.top - otm->otmrcFontBox.bottom);
871 p.ascent = otm->otmAscent;
872 p.descent = -otm->otmDescent;
873 p.leading = int(otm->otmLineGap);
874 p.capHeight = 0;
875 p.lineWidth = otm->otmsUnderscoreSize;
876 free(otm);
877 DeleteObject(SelectObject(hdc, oldfont));
878 return p;
879}
880
881void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
882{
883 LOGFONT lf = m_logfont;
884 lf.lfHeight = -unitsPerEm;
885 int flags = synthesized();
886 if (flags & SynthesizedItalic)
887 lf.lfItalic = false;
888 lf.lfWidth = 0;
889 HFONT hf = CreateFontIndirect(&lf);
890 HDC hdc = m_fontEngineData->hdc;
891 HGDIOBJ oldfont = SelectObject(hdc, hf);
892 QFixedPoint p;
893 p.x = 0;
894 p.y = 0;
895 addGlyphToPath(glyph, p, hdc, path, ttf, metrics);
896 DeleteObject(SelectObject(hdc, oldfont));
897}
898
899bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
900{
901 if (!ttf && !cffTable)
902 return false;
903 HDC hdc = m_fontEngineData->hdc;
904 SelectObject(hdc, hfont);
905 DWORD t = qbswap<quint32>(tag);
906 *length = GetFontData(hdc, t, 0, buffer, *length);
907 return *length != GDI_ERROR && *length > 0;
908}
909
910#if !defined(CLEARTYPE_QUALITY)
911# define CLEARTYPE_QUALITY 5
912#endif
913
914QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, int margin,
915 const QTransform &t,
916 QImage::Format mask_format)
917{
918 Q_UNUSED(mask_format);
919 glyph_metrics_t gm = boundingBox(glyph);
920
921// printf(" -> for glyph %4x\n", glyph);
922
923 int gx = gm.x.toInt();
924 int gy = gm.y.toInt();
925 int iw = gm.width.toInt();
926 int ih = gm.height.toInt();
927
928 if (iw <= 0 || ih <= 0)
929 return 0;
930
931 bool has_transformation = t.type() > QTransform::TxTranslate;
932
933 unsigned int options = ttf ? ETO_GLYPH_INDEX : 0;
934 XFORM xform;
935
936 if (has_transformation) {
937 xform.eM11 = FLOAT(t.m11());
938 xform.eM12 = FLOAT(t.m12());
939 xform.eM21 = FLOAT(t.m21());
940 xform.eM22 = FLOAT(t.m22());
941 xform.eDx = margin;
942 xform.eDy = margin;
943
944 const HDC hdc = m_fontEngineData->hdc;
945
946 SetGraphicsMode(hdc, GM_ADVANCED);
947 SetWorldTransform(hdc, &xform);
948 HGDIOBJ old_font = SelectObject(hdc, font);
949
950 const UINT ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0);
951 GLYPHMETRICS tgm;
952 MAT2 mat;
953 memset(&mat, 0, sizeof(mat));
954 mat.eM11.value = mat.eM22.value = 1;
955
956 const DWORD result = GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat);
957
958 XFORM identity = {1, 0, 0, 1, 0, 0};
959 SetWorldTransform(hdc, &identity);
960 SetGraphicsMode(hdc, GM_COMPATIBLE);
961 SelectObject(hdc, old_font);
962
963 if (result == GDI_ERROR) {
964 const int errorCode = int(GetLastError());
965 qErrnoWarning(errorCode, "QWinFontEngine: unable to query transformed glyph metrics (GetGlyphOutline() failed, error %d)...", errorCode);
966 return 0;
967 }
968
969 iw = int(tgm.gmBlackBoxX);
970 ih = int(tgm.gmBlackBoxY);
971
972 xform.eDx -= tgm.gmptGlyphOrigin.x;
973 xform.eDy += tgm.gmptGlyphOrigin.y;
974 }
975
976 if (glyphBoundingBoxExceedsLimit(iw, ih, effectivePixelSizeSquared(t)))
977 return nullptr;
978
979 // The padding here needs to be kept in sync with the values in alphaMapBoundingBox.
980 QWindowsNativeImage *ni = new QWindowsNativeImage(iw + 2 * margin,
981 ih + 2 * margin,
982 QWindowsNativeImage::systemFormat());
983
984 /*If cleartype is enabled we use the standard system format even on Windows CE
985 and not the special textbuffer format we have to use if cleartype is disabled*/
986
987 ni->image().fill(0xffffffff);
988
989 HDC hdc = ni->hdc();
990
991 SelectObject(hdc, GetStockObject(NULL_BRUSH));
992 SelectObject(hdc, GetStockObject(BLACK_PEN));
993 SetTextColor(hdc, RGB(0,0,0));
994 SetBkMode(hdc, TRANSPARENT);
995 SetTextAlign(hdc, TA_BASELINE);
996
997 HGDIOBJ old_font = SelectObject(hdc, font);
998
999 if (has_transformation) {
1000 SetGraphicsMode(hdc, GM_ADVANCED);
1001 SetWorldTransform(hdc, &xform);
1002 ExtTextOut(hdc, 0, 0, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0);
1003 } else {
1004 ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0);
1005 }
1006
1007 SelectObject(hdc, old_font);
1008 return ni;
1009}
1010
1012 const QFixedPoint &,
1013 const QTransform &matrix,
1014 GlyphFormat format)
1015{
1016 int margin = 0;
1017 if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB)
1018 margin = glyphMargin(QFontEngine::Format_A32);
1019 glyph_metrics_t gm = boundingBox(glyph, matrix);
1020 gm.width += margin * 2;
1021 gm.height += margin * 2;
1022 return gm;
1023}
1024
1025QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xform)
1026{
1027 HFONT font = hfont;
1028
1029 bool clearTypeTemporarilyDisabled = (m_fontEngineData->clearTypeEnabled && m_logfont.lfQuality != NONANTIALIASED_QUALITY);
1030 if (clearTypeTemporarilyDisabled) {
1031 LOGFONT lf = m_logfont;
1032 lf.lfQuality = ANTIALIASED_QUALITY;
1033 font = CreateFontIndirect(&lf);
1034 }
1035 QImage::Format mask_format = QWindowsNativeImage::systemFormat();
1036 mask_format = QImage::Format_RGB32;
1037
1038 const QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format);
1039 if (mask == 0) {
1040 if (m_fontEngineData->clearTypeEnabled)
1041 DeleteObject(font);
1042 return QImage();
1043 }
1044
1045 QImage alphaMap(mask->width(), mask->height(), QImage::Format_Alpha8);
1046
1047
1048 // Copy data... Cannot use QPainter here as GDI has messed up the
1049 // Alpha channel of the ni.image pixels...
1050 for (int y=0; y<mask->height(); ++y) {
1051 uchar *dest = alphaMap.scanLine(y);
1052 if (mask->image().format() == QImage::Format_RGB16) {
1053 const qint16 *src = reinterpret_cast<const qint16 *>(mask->image().constScanLine(y));
1054 for (int x=0; x<mask->width(); ++x)
1055 dest[x] = 255 - qGray(src[x]);
1056 } else {
1057 const uint *src = reinterpret_cast<const uint *>(mask->image().constScanLine(y));
1058 for (int x=0; x<mask->width(); ++x) {
1059 if (QWindowsNativeImage::systemFormat() == QImage::Format_RGB16)
1060 dest[x] = 255 - qGray(src[x]);
1061 else
1062 dest[x] = 255 - (m_fontEngineData->pow_gamma[qGray(src[x])] * 255. / 2047.);
1063 }
1064 }
1065 }
1066
1067 // Cleanup...
1068 delete mask;
1069 if (clearTypeTemporarilyDisabled) {
1070 DeleteObject(font);
1071 }
1072
1073 return alphaMap;
1074}
1075
1076#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
1077#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D
1078
1080 const QFixedPoint &,
1081 const QTransform &t)
1082{
1083 HFONT font = hfont;
1084
1085 UINT contrast;
1086 SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0);
1087 SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(1000)), 0);
1088
1089 int margin = glyphMargin(QFontEngine::Format_A32);
1090 QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32);
1091 SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(contrast)), 0);
1092
1093 if (mask == 0)
1094 return QImage();
1095
1096 // Gracefully handle the odd case when the display is 16-bit
1097 const QImage source = mask->image().depth() == 32
1098 ? mask->image()
1099 : mask->image().convertToFormat(QImage::Format_RGB32);
1100
1101 QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32);
1102 for (int y=0; y<mask->height(); ++y) {
1103 auto dest = reinterpret_cast<uint *>(rgbMask.scanLine(y));
1104 const uint *src = reinterpret_cast<const uint *>(source.constScanLine(y));
1105 for (int x=0; x<mask->width(); ++x) {
1106 dest[x] = 0xffffffff - (0x00ffffff & src[x]);
1107 }
1108 }
1109
1110 delete mask;
1111
1112 return rgbMask;
1113}
1114
1116{
1117 QFontDef request = fontDef;
1118 QString actualFontName = request.family();
1119 if (!uniqueFamilyName.isEmpty())
1120 request.families = QStringList(uniqueFamilyName);
1121 request.pixelSize = pixelSize;
1122 const QString faceName = QString::fromWCharArray(m_logfont.lfFaceName);
1123
1124 QFontEngine *fontEngine =
1125 QWindowsFontDatabase::createEngine(request, faceName,
1126 QWindowsFontDatabase::defaultVerticalDPI(),
1127 m_fontEngineData);
1128 if (fontEngine) {
1129 fontEngine->fontDef.families = QStringList(actualFontName);
1130 if (!uniqueFamilyName.isEmpty()) {
1131 static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName);
1132 if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) {
1133 QPlatformFontDatabase *pfdb = pi->fontDatabase();
1134 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(uniqueFamilyName);
1135 }
1136 }
1137 }
1138 return fontEngine;
1139}
1140
1142{
1143 return hfont;
1144}
1145
1146void QWindowsFontEngine::initFontInfo(const QFontDef &request,
1147 int dpi)
1148{
1149 fontDef = request; // most settings are equal
1150 HDC dc = m_fontEngineData->hdc;
1151 SelectObject(dc, hfont);
1152 wchar_t n[64];
1153 GetTextFace(dc, 64, n);
1154 fontDef.families = QStringList(QString::fromWCharArray(n));
1155 fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
1156 if (fontDef.pointSize < 0) {
1157 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1158 } else if (fontDef.pixelSize == -1) {
1159 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1160 }
1161}
1162
1163bool QWindowsFontEngine::supportsTransformation(const QTransform &transform) const
1164{
1165 // Support all transformations for ttf files, and translations for raster fonts
1166 return ttf || transform.type() <= QTransform::TxTranslate;
1167}
1168
1169QT_END_NAMESPACE
Standard Windows font engine.
void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
FaceId faceId() const override
QImage alphaMapForGlyph(glyph_t, const QTransform &xform) override
void initFontInfo(const QFontDef &request, int dpi)
void initializeHeightMetrics() const override
QFixed emSquareSize() const override
glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &, const QTransform &matrix, GlyphFormat) override
qreal minLeftBearing() const override
bool hasUnreliableGlyphOutline() const override
QFontEngine * cloneWithSize(qreal pixelSize) const override
void getGlyphBearings(glyph_t glyph, qreal *leftBearing=nullptr, qreal *rightBearing=nullptr) override
QImage alphaRGBMapForGlyph(glyph_t t, const QFixedPoint &subPixelPosition, const QTransform &xform) override
bool supportsTransformation(const QTransform &transform) const override
qreal minRightBearing() const override
Qt::HANDLE handle() const override
void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override
Properties properties() const override
int synthesized() const override
static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, QPainterPath *path, bool ttf, glyph_metrics_t *metric=nullptr, qreal scale=1.0, qreal stretch=1.0)
#define TT_PRIM_CSPLINE
static const int char_table_entries
#define SPI_SETFONTSMOOTHINGCONTRAST
#define MAKE_LITTLE_ENDIAN_TAG(ch1, ch2, ch3, ch4)
static double qt_fixed_to_double(const FIXED &p)
static QPointF qt_to_qpointf(const POINTFX &pt, qreal scale, qreal stretch)
static const ushort char_table[]
#define SPI_GETFONTSMOOTHINGCONTRAST