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