Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.