Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qfontengine_ft.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
4#include "qdir.h"
5#include "qmetatype.h"
6#include "qtextstream.h"
7#include "qvariant.h"
8#include "qfontengine_ft_p.h"
9#include "private/qimage_p.h"
10#include <private/qstringiterator_p.h>
11#include <qguiapplication.h>
12#include <qscreen.h>
13#include <qpa/qplatformscreen.h>
14#include <QtCore/QUuid>
15#include <QtGui/QPainterPath>
16
17#ifndef QT_NO_FREETYPE
18
19#include "qfile.h"
20#include "qfileinfo.h"
22#include "qthreadstorage.h"
23#include <qmath.h>
24#include <qendian.h>
25
26#include <memory>
27
28#include <ft2build.h>
29#include FT_FREETYPE_H
30#include FT_OUTLINE_H
31#include FT_SYNTHESIS_H
32#include FT_TRUETYPE_TABLES_H
33#include FT_TYPE1_TABLES_H
34#include FT_GLYPH_H
35#include FT_MODULE_H
36#include FT_LCD_FILTER_H
37
38#if defined(FT_CONFIG_OPTIONS_H)
39#include FT_CONFIG_OPTIONS_H
40#endif
41
42#if defined(FT_FONT_FORMATS_H)
43#include FT_FONT_FORMATS_H
44#endif
45
46#ifdef QT_LINUXBASE
47#include FT_ERRORS_H
48#endif
49
50#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
51# define QT_MAX_CACHED_GLYPH_SIZE 64
52#endif
53
55
56using namespace Qt::StringLiterals;
57
58#define FLOOR(x) ((x) & -64)
59#define CEIL(x) (((x)+63) & -64)
60#define TRUNC(x) ((x) >> 6)
61#define ROUND(x) (((x)+32) & -64)
62
64{
65 FT_Face face = (FT_Face)user_data;
66
67 bool result = false;
68 if (FT_IS_SFNT(face)) {
69 FT_ULong len = *length;
70 result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok;
71 *length = len;
72 Q_ASSERT(!result || int(*length) > 0);
73 }
74
75 return result;
76}
77
79
81#ifdef Q_OS_WIN
83#else
85#endif
86
87// -------------------------- Freetype support ------------------------------
88
90{
91public:
94 { }
96
97 struct FaceStyle {
100
102 : faceFileName(std::move(faceFileName)),
103 styleName(std::move(styleName))
104 {}
105 };
106
107 FT_Library library;
110};
111
113{
115 iter.value()->cleanup();
116 faces.clear();
117 FT_Done_FreeType(library);
118 library = nullptr;
119}
120
121inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
122{
123 return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName;
124}
125
126inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
127{
128 return qHashMulti(seed, style.faceFileName, style.styleName);
129}
130
132
134{
135 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
136 if (!freetypeData)
137 freetypeData = new QtFreetypeData;
138 if (!freetypeData->library) {
139 FT_Init_FreeType(&freetypeData->library);
140#if defined(FT_FONT_FORMATS_H)
141 // Freetype defaults to disabling stem-darkening on CFF, we re-enable it.
142 FT_Bool no_darkening = false;
143 FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening);
144#endif
145 }
146 return freetypeData;
147}
148
149FT_Library qt_getFreetype()
150{
151 QtFreetypeData *freetypeData = qt_getFreetypeData();
152 Q_ASSERT(freetypeData->library);
153 return freetypeData->library;
154}
155
157{
158 int fsType = 0;
159 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
160 if (os2)
161 fsType = os2->fsType;
162 return fsType;
163}
164
165int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
166{
167 if (int error = FT_Load_Glyph(face, glyph, flags))
168 return error;
169
170 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
172
173 *nPoints = face->glyph->outline.n_points;
174 if (!(*nPoints))
175 return Err_Ok;
176
177 if (point > *nPoints)
179
180 *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x);
181 *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y);
182
183 return Err_Ok;
184}
185
187{
188#ifdef FT_HAS_COLOR
189 return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
190#else
191 return false;
192#endif
193}
194
196
197/*
198 * One font file can contain more than one font (bold/italic for example)
199 * find the right one and return it.
200 *
201 * Returns the freetype face or 0 in case of an empty file or any other problems
202 * (like not being able to open the file)
203 */
205 const QByteArray &fontData)
206{
207 if (face_id.filename.isEmpty() && fontData.isEmpty())
208 return nullptr;
209
210 QtFreetypeData *freetypeData = qt_getFreetypeData();
211
212 QFreetypeFace *freetype = freetypeData->faces.value(face_id, nullptr);
213 if (freetype) {
214 freetype->ref.ref();
215 } else {
216 const auto deleter = [](QFreetypeFace *f) { delete f; };
217 std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter);
218 FT_Face face;
219 if (!face_id.filename.isEmpty()) {
221 if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
222 // from qfontdatabase.cpp
223 QByteArray idx = face_id.filename;
224 idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/'
225 bool ok = false;
226 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
227 if (!ok)
228 newFreetype->fontData = QByteArray();
229 } else if (!QFileInfo(fileName).isNativePath()) {
232 return nullptr;
233 }
234 newFreetype->fontData = file.readAll();
235 }
236 } else {
237 newFreetype->fontData = fontData;
238 }
239 if (!newFreetype->fontData.isEmpty()) {
240 if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) {
241 return nullptr;
242 }
243 } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
244 return nullptr;
245 }
246 newFreetype->face = face;
247
248 newFreetype->ref.storeRelaxed(1);
249 newFreetype->xsize = 0;
250 newFreetype->ysize = 0;
251 newFreetype->matrix.xx = 0x10000;
252 newFreetype->matrix.yy = 0x10000;
253 newFreetype->matrix.xy = 0;
254 newFreetype->matrix.yx = 0;
255 newFreetype->unicode_map = nullptr;
256 newFreetype->symbol_map = nullptr;
257
258 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
259
260 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
261 FT_CharMap cm = newFreetype->face->charmaps[i];
262 switch(cm->encoding) {
263 case FT_ENCODING_UNICODE:
264 newFreetype->unicode_map = cm;
265 break;
266 case FT_ENCODING_APPLE_ROMAN:
267 case FT_ENCODING_ADOBE_LATIN_1:
268 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
269 newFreetype->unicode_map = cm;
270 break;
271 case FT_ENCODING_ADOBE_CUSTOM:
272 case FT_ENCODING_MS_SYMBOL:
273 if (!newFreetype->symbol_map)
274 newFreetype->symbol_map = cm;
275 break;
276 default:
277 break;
278 }
279 }
280
281 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
282 FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
283
284 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
285 QT_TRY {
286 freetypeData->faces.insert(face_id, newFreetype.get());
287 } QT_CATCH(...) {
288 newFreetype.release()->release(face_id);
289 // we could return null in principle instead of throwing
291 }
292 freetype = newFreetype.release();
293 }
294 return freetype;
295}
296
297void QFreetypeFace::cleanup()
298{
299 hbFace.reset();
300 FT_Done_Face(face);
301 face = nullptr;
302}
303
305{
306 if (!ref.deref()) {
307 if (face) {
308 QtFreetypeData *freetypeData = qt_getFreetypeData();
309
310 cleanup();
311
312 auto it = freetypeData->faces.constFind(face_id);
313 if (it != freetypeData->faces.constEnd())
314 freetypeData->faces.erase(it);
315
316 if (freetypeData->faces.isEmpty()) {
317 FT_Done_FreeType(freetypeData->library);
318 freetypeData->library = nullptr;
319 }
320 }
321
322 delete this;
323 }
324}
325
326static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
327{
328 FT_Library library = qt_getFreetype();
329
330 int faceIndex = 0;
331 int numFaces = 0;
332
333 do {
334 FT_Face face;
335
336 FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face);
337 if (error != FT_Err_Ok) {
338 qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error;
339 break;
340 }
341
342 QString faceStyleName = QString::fromLatin1(face->style_name);
343 numFaces = face->num_faces;
344
345 FT_Done_Face(face);
346
347 if (faceStyleName == styleName)
348 return faceIndex;
349 } while (++faceIndex < numFaces);
350
351 // Fall back to the first font face in the file
352 return 0;
353}
354
355int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
356{
357 QtFreetypeData *freetypeData = qt_getFreetypeData();
358
359 // Try to get from cache
360 QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName);
361 int faceIndex = freetypeData->faceIndices.value(faceStyle, -1);
362
363 if (faceIndex >= 0)
364 return faceIndex;
365
366 faceIndex = computeFaceIndex(faceFileName, styleName);
367
368 freetypeData->faceIndices.insert(faceStyle, faceIndex);
369
370 return faceIndex;
371}
372
373void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
374{
375 *ysize = qRound(fontDef.pixelSize * 64);
376 *xsize = *ysize * fontDef.stretch / 100;
377 *scalableBitmapScaleFactor = 1;
378 *outline_drawing = false;
379
380 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
381 int best = 0;
382 if (!isScalableBitmap()) {
383 /*
384 * Bitmap only faces must match exactly, so find the closest
385 * one (height dominant search)
386 */
387 for (int i = 1; i < face->num_fixed_sizes; i++) {
388 if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
389 qAbs(*ysize - face->available_sizes[best].y_ppem) ||
390 (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
391 qAbs(*ysize - face->available_sizes[best].y_ppem) &&
392 qAbs(*xsize - face->available_sizes[i].x_ppem) <
393 qAbs(*xsize - face->available_sizes[best].x_ppem))) {
394 best = i;
395 }
396 }
397 } else {
398 // Select the shortest bitmap strike whose height is larger than the desired height
399 for (int i = 1; i < face->num_fixed_sizes; i++) {
400 if (face->available_sizes[i].y_ppem < *ysize) {
401 if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
402 best = i;
403 } else if (face->available_sizes[best].y_ppem < *ysize) {
404 best = i;
405 } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
406 best = i;
407 }
408 }
409 }
410
411 // According to freetype documentation we must use FT_Select_Size
412 // to make sure we can select the desired bitmap strike index
413 if (FT_Select_Size(face, best) == 0) {
414 if (isScalableBitmap())
415 *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height);
416 *xsize = face->available_sizes[best].x_ppem;
417 *ysize = face->available_sizes[best].y_ppem;
418 } else {
419 *xsize = *ysize = 0;
420 }
421 } else {
422 *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
423 }
424}
425
427{
429 p.postscriptName = FT_Get_Postscript_Name(face);
430 PS_FontInfoRec font_info;
431 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
432 p.copyright = font_info.notice;
433 if (FT_IS_SCALABLE(face)) {
434 p.ascent = face->ascender;
435 p.descent = -face->descender;
436 p.leading = face->height - face->ascender + face->descender;
437 p.emSquare = face->units_per_EM;
438 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
439 face->bbox.xMax - face->bbox.xMin,
440 face->bbox.yMax - face->bbox.yMin);
441 } else {
442 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
443 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
444 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
445 p.emSquare = face->size->metrics.y_ppem;
446// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
447 p.boundingBox = QRectF(0, -p.ascent.toReal(),
448 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
449 }
450 p.italicAngle = 0;
451 p.capHeight = p.ascent;
452 p.lineWidth = face->underline_thickness;
453
454 return p;
455}
456
458{
460}
461
462/* Some fonts (such as MingLiu rely on hinting to scale different
463 components to their correct sizes. While this is really broken (it
464 should be done in the component glyph itself, not the hinter) we
465 will have to live with it.
466
467 This means we can not use FT_LOAD_NO_HINTING to get the glyph
468 outline. All we can do is to load the unscaled glyph and scale it
469 down manually when required.
470*/
471static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
472{
473 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
474 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
475 FT_Vector *p = g->outline.points;
476 const FT_Vector *e = p + g->outline.n_points;
477 while (p < e) {
478 p->x = FT_MulFix(p->x, x_scale);
479 p->y = FT_MulFix(p->y, y_scale);
480 ++p;
481 }
482}
483
484#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug
485void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
486{
487 const qreal factor = 1/64.;
488 scaleOutline(face, g, x_scale, y_scale);
489
490 QPointF cp = point.toPointF();
491
492 // convert the outline to a painter path
493 int i = 0;
494 for (int j = 0; j < g->outline.n_contours; ++j) {
495 int last_point = g->outline.contours[j];
496 GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point;
497 QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
498 if (!(g->outline.tags[i] & 1)) { // start point is not on curve:
499 if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve:
500 GLYPH2PATH_DEBUG() << " start and end point are not on curve";
501 start = (QPointF(g->outline.points[last_point].x*factor,
502 -g->outline.points[last_point].y*factor) + start) / 2.0;
503 } else {
504 GLYPH2PATH_DEBUG() << " end point is on curve, start is not";
505 start = QPointF(g->outline.points[last_point].x*factor,
506 -g->outline.points[last_point].y*factor);
507 }
508 --i; // to use original start point as control point below
509 }
510 start += cp;
511 GLYPH2PATH_DEBUG() << " start at" << start;
512
513 path->moveTo(start);
514 QPointF c[4];
515 c[0] = start;
516 int n = 1;
517 while (i < last_point) {
518 ++i;
519 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
520 GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i]
521 << ": on curve =" << (bool)(g->outline.tags[i] & 1);
522 ++n;
523 switch (g->outline.tags[i] & 3) {
524 case 2:
525 // cubic bezier element
526 if (n < 4)
527 continue;
528 c[3] = (c[3] + c[2])/2;
529 --i;
530 break;
531 case 0:
532 // quadratic bezier element
533 if (n < 3)
534 continue;
535 c[3] = (c[1] + c[2])/2;
536 c[2] = (2*c[1] + c[3])/3;
537 c[1] = (2*c[1] + c[0])/3;
538 --i;
539 break;
540 case 1:
541 case 3:
542 if (n == 2) {
543 GLYPH2PATH_DEBUG() << " lineTo" << c[1];
544 path->lineTo(c[1]);
545 c[0] = c[1];
546 n = 1;
547 continue;
548 } else if (n == 3) {
549 c[3] = c[2];
550 c[2] = (2*c[1] + c[3])/3;
551 c[1] = (2*c[1] + c[0])/3;
552 }
553 break;
554 }
555 GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3];
556 path->cubicTo(c[1], c[2], c[3]);
557 c[0] = c[3];
558 n = 1;
559 }
560
561 if (n == 1) {
562 GLYPH2PATH_DEBUG() << " closeSubpath";
563 path->closeSubpath();
564 } else {
565 c[3] = start;
566 if (n == 2) {
567 c[2] = (2*c[1] + c[3])/3;
568 c[1] = (2*c[1] + c[0])/3;
569 }
570 GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3];
571 path->cubicTo(c[1], c[2], c[3]);
572 }
573 ++i;
574 }
575}
576
577extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
578
579void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
580{
581 if (slot->format != FT_GLYPH_FORMAT_BITMAP
582 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
583 return;
584
585 QPointF cp = point.toPointF();
586 qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
587 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
588}
589
590static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
591{
592 const int offs = bgr ? -1 : 1;
593 const int w = width * 3;
594 while (height--) {
595 uint *dd = dst;
596 for (int x = 0; x < w; x += 3) {
597 uchar red = src[x + 1 - offs];
598 uchar green = src[x + 1];
599 uchar blue = src[x + 1 + offs];
600 *dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
601 }
602 dst += width;
603 src += src_pitch;
604 }
605}
606
607static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
608{
609 const int offs = bgr ? -src_pitch : src_pitch;
610 while (height--) {
611 for (int x = 0; x < width; x++) {
612 uchar red = src[x + src_pitch - offs];
613 uchar green = src[x + src_pitch];
614 uchar blue = src[x + src_pitch + offs];
615 *dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
616 }
617 src += 3*src_pitch;
618 }
619}
620
622{
623 static int type = -1;
624 if (type == -1) {
627 }
628 return static_cast<QFontEngine::SubpixelAntialiasingType>(type);
629}
630
632{
633 auto engine = std::make_unique<QFontEngineFT>(fontDef);
634
637
638 if (antialias) {
642 engine->subpixelType = QFontEngine::Subpixel_None;
643 } else {
645 engine->subpixelType = subpixelType;
646 }
647 }
648
649 if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
650 qWarning("QFontEngineFT: Failed to create FreeType font engine");
651 return nullptr;
652 }
653
654 engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
655 return engine.release();
656}
657
658namespace {
659 class QFontEngineFTRawData: public QFontEngineFT
660 {
661 public:
662 QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef)
663 {
664 }
665
666 void updateFamilyNameAndStyle()
667 {
668 fontDef.families = QStringList(QString::fromLatin1(freetype->face->family_name));
669
670 if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
671 fontDef.style = QFont::StyleItalic;
672
673 if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
674 fontDef.weight = QFont::Bold;
675 }
676
677 bool initFromData(const QByteArray &fontData)
678 {
679 FaceId faceId;
680 faceId.filename = "";
681 faceId.index = 0;
682 faceId.uuid = QUuid::createUuid().toByteArray();
683
684 return init(faceId, true, Format_None, fontData);
685 }
686 };
687}
688
690{
692 fontDef.pixelSize = pixelSize;
694 fontDef.hintingPreference = hintingPreference;
695
696 QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
697 if (!fe->initFromData(fontData)) {
698 delete fe;
699 return nullptr;
700 }
701
702 fe->updateFamilyNameAndStyle();
703 fe->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
704
705 return fe;
706}
707
709 : QFontEngine(Freetype)
710{
711 fontDef = fd;
712 matrix.xx = 0x10000;
713 matrix.yy = 0x10000;
714 matrix.xy = 0;
715 matrix.yx = 0;
716 cache_cost = 100 * 1024;
717 kerning_pairs_loaded = false;
718 transform = false;
719 embolden = false;
720 obliquen = false;
721 antialias = true;
722 freetype = nullptr;
723 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
726 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
727 defaultFormat = Format_None;
728 embeddedbitmap = false;
729 const QByteArray env = qgetenv("QT_NO_FT_CACHE");
730 cacheEnabled = env.isEmpty() || env.toInt() == 0;
732 forceAutoHint = false;
733 stemDarkeningDriver = false;
734}
735
737{
738 if (freetype)
739 freetype->release(face_id);
740}
741
742bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
743 const QByteArray &fontData)
744{
746}
747
748static void dont_delete(void*) {}
749
750bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
751 QFreetypeFace *freetypeFace)
752{
753 freetype = freetypeFace;
754 if (!freetype) {
755 xsize = 0;
756 ysize = 0;
757 return false;
758 }
759 defaultFormat = format;
760 this->antialias = antialias;
761
762 if (!antialias)
764 else
765 glyphFormat = defaultFormat;
766
767 face_id = faceId;
768
769 symbol = freetype->symbol_map != nullptr;
770 PS_FontInfoRec psrec;
771 // don't assume that type1 fonts are symbol fonts by default
772 if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
773 symbol = !fontDef.families.isEmpty() && bool(fontDef.families.first().contains("symbol"_L1, Qt::CaseInsensitive));
774 }
775
776 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
777
778 FT_Face face = lockFace();
779
780 if (FT_IS_SCALABLE(face)) {
781 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
782 if (fake_oblique)
783 obliquen = true;
784 FT_Set_Transform(face, &matrix, nullptr);
786 // fake bold
787 if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) {
788 if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
789 if (os2->usWeightClass < 700 &&
790 (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
791 embolden = true;
792 }
793 }
794 }
795 // underline metrics
796 line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
797 QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
798 underline_position = center_position - line_thickness / 2;
799 } else {
800 // ad hoc algorithm
801 int score = fontDef.weight * fontDef.pixelSize;
802 line_thickness = score / 7000;
803 // looks better with thicker line for small pointsizes
804 if (line_thickness < 2 && score >= 1050)
805 line_thickness = 2;
806 underline_position = ((line_thickness * 2) + 3) / 6;
807
808 cacheEnabled = false;
809 if (isScalableBitmap())
810 glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
811 }
812 if (line_thickness < 1)
813 line_thickness = 1;
814
815 metrics = face->size->metrics;
816
817 /*
818 TrueType fonts with embedded bitmaps may have a bitmap font specific
819 ascent/descent in the EBLC table. There is no direct public API
820 to extract those values. The only way we've found is to trick freetype
821 into thinking that it's not a scalable font in FT_Select_Size so that
822 the metrics are retrieved from the bitmap strikes.
823 */
824 if (FT_IS_SCALABLE(face)) {
825 for (int i = 0; i < face->num_fixed_sizes; ++i) {
826 if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) {
827 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
828
829 FT_Select_Size(face, i);
830 if (face->size->metrics.ascender + face->size->metrics.descender > 0) {
831 FT_Pos leading = metrics.height - metrics.ascender + metrics.descender;
832 metrics.ascender = face->size->metrics.ascender;
833 metrics.descender = face->size->metrics.descender;
834 if (metrics.descender > 0
835 && QString::fromUtf8(face->family_name) == "Courier New"_L1) {
836 metrics.descender *= -1;
837 }
838 metrics.height = metrics.ascender - metrics.descender + leading;
839 }
840 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
841
842 face->face_flags |= FT_FACE_FLAG_SCALABLE;
843 break;
844 }
845 }
846 }
847#if defined(FT_FONT_FORMATS_H)
848 const char *fmt = FT_Get_Font_Format(face);
849 if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
850 FT_Bool no_stem_darkening = true;
851 FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
852 if (err == FT_Err_Ok)
853 stemDarkeningDriver = !no_stem_darkening;
854 else
855 stemDarkeningDriver = false;
856 }
857#endif
858
860
861 if (!freetype->hbFace) {
864 (void)harfbuzzFace(); // populates face_
865 freetype->hbFace = std::move(face_);
866 } else {
867 Q_ASSERT(!face_);
868 }
869 // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
870 face_ = Holder(freetype->hbFace.get(), dont_delete);
871
872 unlockFace();
873
875 return true;
876}
877
879{
880 switch (hintingPreference) {
883 break;
886 break;
889 break;
892 break;
893 }
894}
895
897{
898 default_hint_style = style;
899}
900
902{
903 return stemDarkeningDriver;
904}
905
906int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
907 bool &hsubpixel, int &vfactor) const
908{
909 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
910 int load_target = default_hint_style == HintLight
911 ? FT_LOAD_TARGET_LIGHT
912 : FT_LOAD_TARGET_NORMAL;
913
914 if (format == Format_Mono) {
915 load_target = FT_LOAD_TARGET_MONO;
916 } else if (format == Format_A32) {
918 hsubpixel = true;
920 vfactor = 3;
921 } else if (format == Format_ARGB) {
922#ifdef FT_LOAD_COLOR
923 load_flags |= FT_LOAD_COLOR;
924#endif
925 }
926
927 if (set && set->outline_drawing)
928 load_flags |= FT_LOAD_NO_BITMAP;
929
930 if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing))
931 load_flags |= FT_LOAD_NO_HINTING;
932 else
933 load_flags |= load_target;
934
935 if (forceAutoHint)
936 load_flags |= FT_LOAD_FORCE_AUTOHINT;
937
938 return load_flags;
939}
940
942{
943 // false if exceeds QFontEngineFT::Glyph metrics
944 return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
945}
946
947static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
948{
949 int l, r, t, b;
950 FT_Vector vector;
951 vector.x = *left;
952 vector.y = *top;
953 FT_Vector_Transform(&vector, matrix);
954 l = r = vector.x;
955 t = b = vector.y;
956 vector.x = *right;
957 vector.y = *top;
958 FT_Vector_Transform(&vector, matrix);
959 if (l > vector.x) l = vector.x;
960 if (r < vector.x) r = vector.x;
961 if (t < vector.y) t = vector.y;
962 if (b > vector.y) b = vector.y;
963 vector.x = *right;
964 vector.y = *bottom;
965 FT_Vector_Transform(&vector, matrix);
966 if (l > vector.x) l = vector.x;
967 if (r < vector.x) r = vector.x;
968 if (t < vector.y) t = vector.y;
969 if (b > vector.y) b = vector.y;
970 vector.x = *left;
971 vector.y = *bottom;
972 FT_Vector_Transform(&vector, matrix);
973 if (l > vector.x) l = vector.x;
974 if (r < vector.x) r = vector.x;
975 if (t < vector.y) t = vector.y;
976 if (b > vector.y) b = vector.y;
977 *left = l;
978 *right = r;
979 *top = t;
980 *bottom = b;
981}
982
984 const QFixedPoint &subPixelPosition,
986 bool fetchMetricsOnly,
987 bool disableOutlineDrawing) const
988{
989// Q_ASSERT(freetype->lock == 1);
990
991 if (format == Format_None)
992 format = defaultFormat != Format_None ? defaultFormat : Format_Mono;
994
995 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : nullptr;
996 if (g && g->format == format && (fetchMetricsOnly || g->data))
997 return g;
998
999 if (!g && set && set->isGlyphMissing(glyph))
1000 return &emptyGlyph;
1001
1002
1003 FT_Face face = freetype->face;
1004
1005 FT_Matrix matrix = freetype->matrix;
1006
1007 FT_Vector v;
1008 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
1009 v.y = format == Format_Mono ? 0 : FT_Pos(-subPixelPosition.y.value());
1010 FT_Set_Transform(face, &matrix, &v);
1011
1012 bool hsubpixel = false;
1013 int vfactor = 1;
1014 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
1015
1016 bool transform = matrix.xx != 0x10000
1017 || matrix.yy != 0x10000
1018 || matrix.xy != 0
1019 || matrix.yx != 0;
1020
1022 load_flags |= FT_LOAD_NO_BITMAP;
1023
1024 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
1025 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
1026 load_flags &= ~FT_LOAD_NO_BITMAP;
1027 err = FT_Load_Glyph(face, glyph, load_flags);
1028 }
1029 if (err == FT_Err_Too_Few_Arguments) {
1030 // this is an error in the bytecode interpreter, just try to run without it
1031 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1032 err = FT_Load_Glyph(face, glyph, load_flags);
1033 } else if (err == FT_Err_Execution_Too_Long) {
1034 // This is an error in the bytecode, probably a web font made by someone who
1035 // didn't test bytecode hinting at all so disable for it for all glyphs.
1036 qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1037 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1038 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1039 err = FT_Load_Glyph(face, glyph, load_flags);
1040 }
1041 if (err != FT_Err_Ok) {
1042 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1043 if (set)
1044 set->setGlyphMissing(glyph);
1045 return &emptyGlyph;
1046 }
1047
1048 FT_GlyphSlot slot = face->glyph;
1049
1050 if (embolden)
1051 FT_GlyphSlot_Embolden(slot);
1052 if (obliquen) {
1053 FT_GlyphSlot_Oblique(slot);
1054
1055 // While Embolden alters the metrics of the slot, oblique does not, so we need
1056 // to fix this ourselves.
1057 transform = true;
1058 FT_Matrix m;
1059 m.xx = 0x10000;
1060 m.yx = 0x0;
1061 m.xy = 0x6000;
1062 m.yy = 0x10000;
1063
1064 FT_Matrix_Multiply(&m, &matrix);
1065 }
1066
1068 info.linearAdvance = slot->linearHoriAdvance >> 10;
1069 info.xOff = TRUNC(ROUND(slot->advance.x));
1070 info.yOff = 0;
1071
1072 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1073 int left = slot->metrics.horiBearingX;
1074 int right = slot->metrics.horiBearingX + slot->metrics.width;
1075 int top = slot->metrics.horiBearingY;
1076 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1077
1078 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1080
1081 left = FLOOR(left);
1082 right = CEIL(right);
1083 bottom = FLOOR(bottom);
1084 top = CEIL(top);
1085
1086 info.x = TRUNC(left);
1087 info.y = TRUNC(top);
1088 info.width = TRUNC(right - left);
1089 info.height = TRUNC(top - bottom);
1090
1091 // If any of the metrics are too large to fit, don't cache them
1092 // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
1094 return nullptr;
1095
1096 g = new Glyph;
1097 g->data = nullptr;
1098 g->linearAdvance = info.linearAdvance;
1099 g->width = info.width;
1100 g->height = info.height;
1101 g->x = info.x;
1102 g->y = info.y;
1103 g->advance = info.xOff;
1104 g->format = format;
1105
1106 if (set)
1107 set->setGlyph(glyph, subPixelPosition, g);
1108
1109 return g;
1110 }
1111
1112 int glyph_buffer_size = 0;
1113 std::unique_ptr<uchar[]> glyph_buffer;
1114 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
1115 switch (format) {
1116 case Format_Mono:
1117 renderMode = FT_RENDER_MODE_MONO;
1118 break;
1119 case Format_A32:
1120 if (!hsubpixel && vfactor == 1) {
1121 qWarning("Format_A32 requested, but subpixel layout is unknown.");
1122 return nullptr;
1123 }
1124
1125 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
1126 break;
1127 case Format_A8:
1128 case Format_ARGB:
1129 break;
1130 default:
1131 Q_UNREACHABLE();
1132 }
1133 FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
1134
1135 err = FT_Render_Glyph(slot, renderMode);
1136 if (err != FT_Err_Ok)
1137 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1138
1139 FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
1140
1141 info.height = slot->bitmap.rows;
1142 info.width = slot->bitmap.width;
1143 info.x = slot->bitmap_left;
1144 info.y = slot->bitmap_top;
1145 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
1146 info.width = info.width / 3;
1147 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
1148 info.height = info.height / vfactor;
1149
1150 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1151 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
1152
1153 glyph_buffer_size = info.height * pitch;
1154 glyph_buffer.reset(new uchar[glyph_buffer_size]);
1155
1156 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
1157 uchar *src = slot->bitmap.buffer;
1158 uchar *dst = glyph_buffer.get();
1159 int h = slot->bitmap.rows;
1160 // Some fonts return bitmaps even when we requested something else:
1161 if (format == Format_Mono) {
1162 int bytes = ((info.width + 7) & ~7) >> 3;
1163 while (h--) {
1164 memcpy (dst, src, bytes);
1165 dst += pitch;
1166 src += slot->bitmap.pitch;
1167 }
1168 } else if (format == Format_A8) {
1169 while (h--) {
1170 for (int x = 0; x < int{info.width}; x++)
1171 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
1172 dst += pitch;
1173 src += slot->bitmap.pitch;
1174 }
1175 } else {
1176 while (h--) {
1177 uint *dd = reinterpret_cast<uint *>(dst);
1178 for (int x = 0; x < int{info.width}; x++)
1179 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
1180 dst += pitch;
1181 src += slot->bitmap.pitch;
1182 }
1183 }
1184 } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
1186 uchar *src = slot->bitmap.buffer;
1187 uchar *dst = glyph_buffer.get();
1188 int h = slot->bitmap.rows;
1189 while (h--) {
1190#if Q_BYTE_ORDER == Q_BIG_ENDIAN
1191 const quint32 *srcPixel = (const quint32 *)src;
1192 quint32 *dstPixel = (quint32 *)dst;
1193 for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
1194 const quint32 pixel = *srcPixel;
1195 *dstPixel = qbswap(pixel);
1196 }
1197#else
1198 memcpy(dst, src, slot->bitmap.width * 4);
1199#endif
1200 dst += slot->bitmap.pitch;
1201 src += slot->bitmap.pitch;
1202 }
1203 info.linearAdvance = info.xOff = slot->bitmap.width;
1204 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1206 uchar *src = slot->bitmap.buffer;
1207 uchar *dst = glyph_buffer.get();
1208 int h = slot->bitmap.rows;
1209 int bytes = info.width;
1210 while (h--) {
1211 memcpy (dst, src, bytes);
1212 dst += pitch;
1213 src += slot->bitmap.pitch;
1214 }
1215 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
1217 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
1218 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
1220 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
1221 } else {
1222 qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
1223 return nullptr;
1224 }
1225
1226 if (!g) {
1227 g = new Glyph;
1228 g->data = nullptr;
1229 }
1230
1231 g->linearAdvance = info.linearAdvance;
1232 g->width = info.width;
1233 g->height = info.height;
1234 g->x = info.x;
1235 g->y = info.y;
1236 g->advance = info.xOff;
1237 g->format = format;
1238 delete [] g->data;
1239 g->data = glyph_buffer.release();
1240
1241 if (set)
1242 set->setGlyph(glyph, subPixelPosition, g);
1243
1244 return g;
1245}
1246
1248{
1249 return face_id;
1250}
1251
1253{
1255 if (p.postscriptName.isEmpty()) {
1257 }
1258
1259 return freetype->properties();
1260}
1261
1263{
1264 if (FT_IS_SCALABLE(freetype->face))
1265 return freetype->face->units_per_EM;
1266 else
1267 return freetype->face->size->metrics.y_ppem;
1268}
1269
1271{
1273}
1274
1276{
1277 int s = 0;
1278 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
1280 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
1281 s |= SynthesizedBold;
1282 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
1284 return s;
1285}
1286
1288{
1290 m_descent = QFixed::fromFixed(-metrics.descender);
1291 m_leading = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
1292
1294
1295 if (scalableBitmapScaleFactor != 1) {
1296 m_ascent *= scalableBitmapScaleFactor;
1297 m_descent *= scalableBitmapScaleFactor;
1298 m_leading *= scalableBitmapScaleFactor;
1299 }
1300}
1301
1303{
1304 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1305 if (os2 && os2->version >= 2) {
1306 lockFace();
1307 QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
1308 unlockFace();
1309 return answer;
1310 }
1311 return calculatedCapHeight();
1312}
1313
1315{
1316 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1317 if (os2 && os2->sxHeight) {
1318 lockFace();
1319 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
1320 unlockFace();
1321 return answer;
1322 }
1323
1324 return QFontEngine::xHeight();
1325}
1326
1328{
1329 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1330 if (os2 && os2->xAvgCharWidth) {
1331 lockFace();
1332 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
1333 unlockFace();
1334 return answer;
1335 }
1336
1338}
1339
1341{
1342 QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
1343 if (scalableBitmapScaleFactor != 1)
1344 max_advance *= scalableBitmapScaleFactor;
1345 return max_advance.toReal();
1346}
1347
1349{
1350 return line_thickness;
1351}
1352
1354{
1355 return underline_position;
1356}
1357
1358void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const
1359{
1360 if (!kerning_pairs_loaded) {
1361 kerning_pairs_loaded = true;
1362 lockFace();
1363 if (freetype->face->size->metrics.x_ppem != 0) {
1364 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
1365 unlockFace();
1366 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
1367 } else {
1368 unlockFace();
1369 }
1370 }
1371
1372 if (shouldUseDesignMetrics(flags))
1374 else
1375 flags &= ~DesignMetrics;
1376
1378}
1379
1380static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
1381{
1382 FT_Matrix m;
1383
1384 m.xx = FT_Fixed(matrix.m11() * 65536);
1385 m.xy = FT_Fixed(-matrix.m21() * 65536);
1386 m.yx = FT_Fixed(-matrix.m12() * 65536);
1387 m.yy = FT_Fixed(matrix.m22() * 65536);
1388
1389 return m;
1390}
1391
1392QFontEngineFT::QGlyphSet *QFontEngineFT::TransformedGlyphSets::findSet(const QTransform &matrix, const QFontDef &fontDef)
1393{
1394 FT_Matrix m = QTransformToFTMatrix(matrix);
1395
1396 int i = 0;
1397 for (; i < nSets; ++i) {
1398 QGlyphSet *g = sets[i];
1399 if (!g)
1400 break;
1401 if (g->transformationMatrix.xx == m.xx
1402 && g->transformationMatrix.xy == m.xy
1403 && g->transformationMatrix.yx == m.yx
1404 && g->transformationMatrix.yy == m.yy) {
1405
1406 // found a match, move it to the front
1407 moveToFront(i);
1408 return g;
1409 }
1410 }
1411
1412 // don't cache more than nSets transformations
1413 if (i == nSets)
1414 // reuse the last set
1415 --i;
1416 moveToFront(nSets - 1);
1417 if (!sets[0])
1418 sets[0] = new QGlyphSet;
1419 QGlyphSet *gs = sets[0];
1420 gs->clear();
1421 gs->transformationMatrix = m;
1422 gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.determinant()) > QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE;
1423 Q_ASSERT(gs != nullptr);
1424
1425 return gs;
1426}
1427
1428void QFontEngineFT::TransformedGlyphSets::moveToFront(int i)
1429{
1430 QGlyphSet *g = sets[i];
1431 while (i > 0) {
1432 sets[i] = sets[i - 1];
1433 --i;
1434 }
1435 sets[0] = g;
1436}
1437
1438
1440{
1441 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
1442 return nullptr;
1443
1444 // FT_Set_Transform only supports scalable fonts
1445 if (!FT_IS_SCALABLE(freetype->face))
1446 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : nullptr;
1447
1448 return transformedGlyphSets.findSet(matrix, fontDef);
1449}
1450
1452{
1453 FT_Face face = lockFace(Unscaled);
1454 FT_Set_Transform(face, nullptr, nullptr);
1455 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1456
1457 int left = face->glyph->metrics.horiBearingX;
1458 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
1459 int top = face->glyph->metrics.horiBearingY;
1460 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
1461
1462 QFixedPoint p;
1463 p.x = 0;
1464 p.y = 0;
1465
1470 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
1471
1472 if (!FT_IS_SCALABLE(freetype->face))
1474 else
1475 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
1476
1477 FT_Set_Transform(face, &freetype->matrix, nullptr);
1478 unlockFace();
1479}
1480
1482{
1483 return transform.type() <= QTransform::TxRotate;
1484}
1485
1486void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1487{
1488 if (!glyphs.numGlyphs)
1489 return;
1490
1491 if (FT_IS_SCALABLE(freetype->face)) {
1493 } else {
1495 QVarLengthArray<glyph_t> positioned_glyphs;
1497 matrix.translate(x, y);
1498 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1499
1500 FT_Face face = lockFace(Unscaled);
1501 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1502 FT_UInt glyph = positioned_glyphs[gl];
1503 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
1505 }
1506 unlockFace();
1507 }
1508}
1509
1511 QPainterPath *path, QTextItem::RenderFlags)
1512{
1513 FT_Face face = lockFace(Unscaled);
1514
1515 for (int gl = 0; gl < numGlyphs; gl++) {
1516 FT_UInt glyph = glyphs[gl];
1517
1518 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1519
1520 FT_GlyphSlot g = face->glyph;
1521 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
1522 continue;
1523 if (embolden)
1524 FT_GlyphSlot_Embolden(g);
1525 if (obliquen)
1526 FT_GlyphSlot_Oblique(g);
1527 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
1528 }
1529 unlockFace();
1530}
1531
1533{
1534 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
1535 if (glyph == 0) {
1536 FT_Face face = freetype->face;
1537 glyph = FT_Get_Char_Index(face, ucs4);
1538 if (glyph == 0) {
1539 // Certain fonts don't have no-break space and tab,
1540 // while we usually want to render them as space
1541 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
1542 glyph = FT_Get_Char_Index(face, QChar::Space);
1543 } else if (freetype->symbol_map) {
1544 // Symbol fonts can have more than one CMAPs, FreeType should take the
1545 // correct one for us by default, so we always try FT_Get_Char_Index
1546 // first. If it didn't work (returns 0), we will explicitly set the
1547 // CMAP to symbol font one and try again. symbol_map is not always the
1548 // correct one because in certain fonts like Wingdings symbol_map only
1549 // contains PUA codepoints instead of the common ones.
1550 FT_Set_Charmap(face, freetype->symbol_map);
1551 glyph = FT_Get_Char_Index(face, ucs4);
1552 FT_Set_Charmap(face, freetype->unicode_map);
1553 if (!glyph && symbol && ucs4 < 0x100)
1554 glyph = FT_Get_Char_Index(face, ucs4 + 0xf000);
1555 }
1556 }
1558 freetype->cmapCache[ucs4] = glyph;
1559 }
1560
1561 return glyph;
1562}
1563
1564bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
1565 QFontEngine::ShaperFlags flags) const
1566{
1567 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1568 if (*nglyphs < len) {
1569 *nglyphs = len;
1570 return false;
1571 }
1572
1573 int glyph_pos = 0;
1574 if (freetype->symbol_map) {
1575 FT_Face face = freetype->face;
1577 while (it.hasNext()) {
1578 uint uc = it.next();
1579 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1580 if ( !glyphs->glyphs[glyph_pos] ) {
1581 // Symbol fonts can have more than one CMAPs, FreeType should take the
1582 // correct one for us by default, so we always try FT_Get_Char_Index
1583 // first. If it didn't work (returns 0), we will explicitly set the
1584 // CMAP to symbol font one and try again. symbol_map is not always the
1585 // correct one because in certain fonts like Wingdings symbol_map only
1586 // contains PUA codepoints instead of the common ones.
1587 glyph_t glyph = FT_Get_Char_Index(face, uc);
1588 // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
1589 // while we usually want to render them as space
1590 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1591 uc = 0x20;
1592 glyph = FT_Get_Char_Index(face, uc);
1593 }
1594 if (!glyph) {
1595 FT_Set_Charmap(face, freetype->symbol_map);
1596 glyph = FT_Get_Char_Index(face, uc);
1597 FT_Set_Charmap(face, freetype->unicode_map);
1598 if (!glyph && symbol && uc < 0x100)
1599 glyph = FT_Get_Char_Index(face, uc + 0xf000);
1600 }
1601 glyphs->glyphs[glyph_pos] = glyph;
1603 freetype->cmapCache[uc] = glyph;
1604 }
1605 ++glyph_pos;
1606 }
1607 } else {
1608 FT_Face face = freetype->face;
1610 while (it.hasNext()) {
1611 uint uc = it.next();
1612 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1613 if (!glyphs->glyphs[glyph_pos]) {
1614 {
1615 redo:
1616 glyph_t glyph = FT_Get_Char_Index(face, uc);
1617 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1618 uc = 0x20;
1619 goto redo;
1620 }
1621 glyphs->glyphs[glyph_pos] = glyph;
1623 freetype->cmapCache[uc] = glyph;
1624 }
1625 }
1626 ++glyph_pos;
1627 }
1628 }
1629
1630 *nglyphs = glyph_pos;
1631 glyphs->numGlyphs = glyph_pos;
1632
1633 if (!(flags & GlyphIndicesOnly))
1634 recalcAdvances(glyphs, flags);
1635
1636 return true;
1637}
1638
1639bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
1640{
1641 if (!FT_IS_SCALABLE(freetype->face))
1642 return false;
1643
1645}
1646
1647QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
1648{
1649 return m * scalableBitmapScaleFactor;
1650}
1651
1652glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m, const QTransform &t) const
1653{
1654 QTransform trans;
1655 trans.setMatrix(t.m11(), t.m12(), t.m13(),
1656 t.m21(), t.m22(), t.m23(),
1657 0, 0, t.m33());
1658 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
1659 trans.scale(scaleFactor, scaleFactor);
1660
1661 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
1662 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
1663
1664 rect = trans.mapRect(rect);
1665 offset = trans.map(offset);
1666
1669 metrics.y = QFixed::fromReal(rect.y());
1670 metrics.width = QFixed::fromReal(rect.width());
1671 metrics.height = QFixed::fromReal(rect.height());
1672 metrics.xoff = QFixed::fromReal(offset.x());
1673 metrics.yoff = QFixed::fromReal(offset.y());
1674 return metrics;
1675}
1676
1677void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1678{
1679 FT_Face face = nullptr;
1680 bool design = shouldUseDesignMetrics(flags);
1681 for (int i = 0; i < glyphs->numGlyphs; i++) {
1682 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : nullptr;
1683 // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
1684 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
1685 if (g && g->format == acceptableFormat) {
1686 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1687 } else {
1688 if (!face)
1689 face = lockFace();
1690 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1691 glyphs->glyphs[i],
1692 QFixedPoint(),
1694 true);
1695 if (g)
1696 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1697 else
1698 glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
1699 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
1700 if (!cacheEnabled && g != &emptyGlyph)
1701 delete g;
1702 }
1703
1704 if (scalableBitmapScaleFactor != 1)
1705 glyphs->advances[i] *= scalableBitmapScaleFactor;
1706 }
1707 if (face)
1708 unlockFace();
1709}
1710
1712{
1713 FT_Face face = nullptr;
1714
1715 glyph_metrics_t overall;
1716 // initialize with line height, we get the same behaviour on all platforms
1717 if (!isScalableBitmap()) {
1718 overall.y = -ascent();
1719 overall.height = ascent() + descent();
1720 } else {
1721 overall.y = QFixed::fromFixed(-metrics.ascender);
1722 overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
1723 }
1724
1725 QFixed ymax = 0;
1726 QFixed xmax = 0;
1727 for (int i = 0; i < glyphs.numGlyphs; i++) {
1728 // If shaping has found this should be ignored, ignore it.
1729 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
1730 continue;
1731 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : nullptr;
1732 if (!g) {
1733 if (!face)
1734 face = lockFace();
1735 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1736 glyphs.glyphs[i],
1737 QFixedPoint(),
1739 true);
1740 }
1741 if (g) {
1742 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
1743 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
1744 overall.x = qMin(overall.x, x);
1745 overall.y = qMin(overall.y, y);
1746 xmax = qMax(xmax, x.ceil() + g->width);
1747 ymax = qMax(ymax, y.ceil() + g->height);
1748 if (!cacheEnabled && g != &emptyGlyph)
1749 delete g;
1750 } else {
1751 int left = FLOOR(face->glyph->metrics.horiBearingX);
1752 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1753 int top = CEIL(face->glyph->metrics.horiBearingY);
1754 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1755
1756 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
1757 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
1758 overall.x = qMin(overall.x, x);
1759 overall.y = qMin(overall.y, y);
1760 xmax = qMax(xmax, x + TRUNC(right - left));
1761 ymax = qMax(ymax, y + TRUNC(top - bottom));
1762 }
1763 overall.xoff += glyphs.effectiveAdvance(i);
1764 }
1765 overall.height = qMax(overall.height, ymax - overall.y);
1766 overall.width = xmax - overall.x;
1767
1768 if (face)
1769 unlockFace();
1770
1771 if (isScalableBitmap())
1772 overall = scaledBitmapMetrics(overall, QTransform());
1773 return overall;
1774}
1775
1777{
1778 FT_Face face = nullptr;
1779 glyph_metrics_t overall;
1780 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : nullptr;
1781 if (!g) {
1782 face = lockFace();
1783 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1784 glyph,
1785 QFixedPoint(),
1787 true);
1788 }
1789 if (g) {
1790 overall.x = g->x;
1791 overall.y = -g->y;
1792 overall.width = g->width;
1793 overall.height = g->height;
1794 overall.xoff = g->advance;
1795 if (!cacheEnabled && g != &emptyGlyph)
1796 delete g;
1797 } else {
1798 int left = FLOOR(face->glyph->metrics.horiBearingX);
1799 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1800 int top = CEIL(face->glyph->metrics.horiBearingY);
1801 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1802
1803 overall.width = TRUNC(right-left);
1804 overall.height = TRUNC(top-bottom);
1805 overall.x = TRUNC(left);
1806 overall.y = -TRUNC(top);
1807 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1808 }
1809 if (face)
1810 unlockFace();
1811
1812 if (isScalableBitmap())
1813 overall = scaledBitmapMetrics(overall, QTransform());
1814 return overall;
1815}
1816
1818{
1820}
1821
1823 const QFixedPoint &subPixelPosition,
1824 const QTransform &matrix,
1826{
1827 // When rendering glyphs into a cache via the alphaMap* functions, we disable
1828 // outline drawing. To ensure the bounding box matches the rendered glyph, we
1829 // need to do the same here.
1830
1831 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
1832 && matrix.type() > QTransform::TxTranslate;
1833 if (needsImageTransform && format == QFontEngine::Format_Mono)
1835 Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true);
1836
1837 glyph_metrics_t overall;
1838 if (g) {
1839 overall.x = g->x;
1840 overall.y = -g->y;
1841 overall.width = g->width;
1842 overall.height = g->height;
1843 overall.xoff = g->advance;
1844 if (!cacheEnabled && g != &emptyGlyph)
1845 delete g;
1846 } else {
1847 FT_Face face = lockFace();
1848 int left = FLOOR(face->glyph->metrics.horiBearingX);
1849 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1850 int top = CEIL(face->glyph->metrics.horiBearingY);
1851 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1852
1853 overall.width = TRUNC(right-left);
1854 overall.height = TRUNC(top-bottom);
1855 overall.x = TRUNC(left);
1856 overall.y = -TRUNC(top);
1857 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1858 unlockFace();
1859 }
1860
1861 if (isScalableBitmap() || needsImageTransform)
1862 overall = scaledBitmapMetrics(overall, matrix);
1863 return overall;
1864}
1865
1867{
1868 if (glyph == nullptr || glyph->height == 0 || glyph->width == 0)
1869 return QImage();
1870
1872 int bytesPerLine = -1;
1873 switch (glyphFormat) {
1876 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
1877 break;
1880 bytesPerLine = (glyph->width + 3) & ~3;
1881 break;
1884 bytesPerLine = glyph->width * 4;
1885 break;
1886 default:
1887 Q_UNREACHABLE();
1888 };
1889
1890 QImage img(static_cast<const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
1892 img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent.
1893 return img;
1894}
1895
1897 const QFixedPoint &subPixelPosition,
1898 QFontEngine::GlyphFormat neededFormat,
1899 const QTransform &t)
1900{
1902
1903 if (isBitmapFont())
1904 neededFormat = Format_Mono;
1905 else if (neededFormat == Format_None && defaultFormat != Format_None)
1906 neededFormat = defaultFormat;
1907 else if (neededFormat == Format_None)
1908 neededFormat = Format_A8;
1909
1910 Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t);
1911 if (!glyph || !glyph->width || !glyph->height)
1912 return nullptr;
1913
1914 return glyph;
1915}
1916
1917static inline bool is2dRotation(const QTransform &t)
1918{
1919 return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21())
1920 && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0));
1921}
1922
1924 const QFixedPoint &subPixelPosition,
1926 const QTransform &t,
1927 bool fetchBoundingBox,
1928 bool disableOutlineDrawing)
1929{
1930 QGlyphSet *glyphSet = loadGlyphSet(t);
1931 if (glyphSet != nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
1932 return nullptr;
1933
1934 Glyph *glyph = glyphSet != nullptr ? glyphSet->getGlyph(g, subPixelPosition) : nullptr;
1935 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
1936 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
1937 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
1938 default_hint_style = HintNone; // disable hinting if the glyphs are transformed
1939
1940 lockFace();
1941 FT_Matrix m = this->matrix;
1942 FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t);
1943 FT_Matrix_Multiply(&ftMatrix, &m);
1944 freetype->matrix = m;
1945 glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing);
1946 unlockFace();
1947 }
1948
1949 return glyph;
1950}
1951
1953{
1954 return alphaMapForGlyph(g, subPixelPosition, QTransform());
1955}
1956
1958 const QFixedPoint &subPixelPosition,
1959 const QTransform &t)
1960{
1961 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
1962 && t.type() > QTransform::TxTranslate;
1963 const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
1964
1965 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
1966
1967 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
1968 if (needsImageTransform)
1969 img = img.transformed(t, Qt::SmoothTransformation);
1970 else
1971 img = img.copy();
1972
1973 if (!cacheEnabled && glyph != &emptyGlyph)
1974 delete glyph;
1975
1976 return img;
1977}
1978
1980 const QFixedPoint &subPixelPosition,
1981 const QTransform &t)
1982{
1983 if (t.type() > QTransform::TxRotate)
1984 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1985
1986 const GlyphFormat neededFormat = Format_A32;
1987
1988 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
1989
1990 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
1991 img = img.copy();
1992
1993 if (!cacheEnabled && glyph != &emptyGlyph)
1994 delete glyph;
1995
1996 if (!img.isNull())
1997 return img;
1998
1999 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2000}
2001
2003 const QFixedPoint &subPixelPosition,
2004 const QTransform &t,
2005 const QColor &color)
2006{
2007 Q_UNUSED(color);
2008
2009 Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t);
2010 if (glyph == nullptr)
2011 return QImage();
2012
2013 QImage img;
2014 if (defaultFormat == GlyphFormat::Format_ARGB)
2015 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
2016 else if (defaultFormat == GlyphFormat::Format_Mono)
2017 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
2018
2019 if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) {
2020 QTransform trans(t);
2021 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2022 trans.scale(scaleFactor, scaleFactor);
2023 img = img.transformed(trans, Qt::SmoothTransformation);
2024 }
2025
2026 if (!cacheEnabled && glyph != &emptyGlyph)
2027 delete glyph;
2028
2029 return img;
2030}
2031
2033{
2034 defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
2035}
2036
2038{
2039 int count = 0;
2040 FT_Face face = lockFace();
2041 if (face) {
2042 count = face->num_glyphs;
2043 unlockFace();
2044 }
2045 return count;
2046}
2047
2049{
2050 freetype->lock();
2051 FT_Face face = freetype->face;
2052 if (scale == Unscaled) {
2053 if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
2054 freetype->xsize = face->units_per_EM << 6;
2055 freetype->ysize = face->units_per_EM << 6;
2056 }
2057 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
2058 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
2059 freetype->xsize = xsize;
2060 freetype->ysize = ysize;
2061 }
2062 if (freetype->matrix.xx != matrix.xx ||
2063 freetype->matrix.yy != matrix.yy ||
2064 freetype->matrix.xy != matrix.xy ||
2065 freetype->matrix.yx != matrix.yx) {
2067 FT_Set_Transform(face, &freetype->matrix, nullptr);
2068 }
2069
2070 return face;
2071}
2072
2074{
2075 freetype->unlock();
2076}
2077
2079{
2080 return freetype->face;
2081}
2082
2083
2085 : outline_drawing(false)
2086{
2087 transformationMatrix.xx = 0x10000;
2088 transformationMatrix.yy = 0x10000;
2089 transformationMatrix.xy = 0;
2090 transformationMatrix.yx = 0;
2091 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
2092 fast_glyph_count = 0;
2093}
2094
2096{
2097 clear();
2098}
2099
2101{
2102 if (fast_glyph_count > 0) {
2103 for (int i = 0; i < 256; ++i) {
2104 if (fast_glyph_data[i]) {
2105 delete fast_glyph_data[i];
2106 fast_glyph_data[i] = nullptr;
2107 }
2108 }
2109 fast_glyph_count = 0;
2110 }
2111 qDeleteAll(glyph_data);
2112 glyph_data.clear();
2113}
2114
2116 const QFixedPoint &subPixelPosition)
2117{
2118 if (useFastGlyphData(index, subPixelPosition)) {
2119 if (fast_glyph_data[index]) {
2120 delete fast_glyph_data[index];
2121 fast_glyph_data[index] = nullptr;
2122 if (fast_glyph_count > 0)
2123 --fast_glyph_count;
2124 }
2125 } else {
2126 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
2127 }
2128}
2129
2131 const QFixedPoint &subPixelPosition,
2132 Glyph *glyph)
2133{
2134 if (useFastGlyphData(index, subPixelPosition)) {
2135 if (!fast_glyph_data[index])
2136 ++fast_glyph_count;
2137 fast_glyph_data[index] = glyph;
2138 } else {
2139 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
2140 }
2141}
2142
2143int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
2144{
2145 lockFace();
2146 bool hsubpixel = true;
2147 int vfactor = 1;
2148 int load_flags = loadFlags(nullptr, Format_A8, flags, hsubpixel, vfactor);
2149 int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
2150 unlockFace();
2151 return result;
2152}
2153
2155{
2156 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
2157 return false;
2158
2159 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
2160 // will be using it
2161 freetype->ref.ref();
2162
2165 antialias = fe->antialias;
2166 transform = fe->transform;
2167 embolden = fe->embolden;
2168 obliquen = fe->obliquen;
2172
2173 return true;
2174}
2175
2177{
2178 QFontDef fontDef(this->fontDef);
2179 fontDef.pixelSize = pixelSize;
2181 if (!fe->initFromFontEngine(this)) {
2182 delete fe;
2183 return nullptr;
2184 } else {
2185 return fe;
2186 }
2187}
2188
2190{
2191 return non_locked_face();
2192}
2193
2195
2196#endif // QT_NO_FREETYPE
bool ref() noexcept
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:170
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
QByteArray & remove(qsizetype index, qsizetype len)
Removes len bytes from the array, starting at index position pos, and returns a reference to the arra...
\inmodule QtCore
Definition qchar.h:48
@ Nbsp
Definition qchar.h:57
@ Tabulation
Definition qchar.h:52
@ Space
Definition qchar.h:56
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
bool expectsGammaCorrectedBlending() const override
glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
void setDefaultHintStyle(HintStyle style) override
HintStyle default_hint_style
glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) override
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) override
bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override
void initializeHeightMetrics() const override
QGlyphSet * loadGlyphSet(const QTransform &matrix)
QFixed underlinePosition() const override
QFixed lineThickness() const override
QFixed emSquareSize() const override
bool initFromFontEngine(const QFontEngineFT *fontEngine)
QFreetypeFace * freetype
QFontEngine * cloneWithSize(qreal pixelSize) const override
void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override
QImage alphaMapForGlyph(glyph_t g) override
QFontEngineFT(const QFontDef &fd)
bool isScalableBitmap() const
QFixed xHeight() const override
FT_Face non_locked_face() const
bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat=Format_None, const QByteArray &fontData=QByteArray())
void removeGlyphFromCache(glyph_t glyph) override
qreal maxCharWidth() const override
void doKerning(QGlyphLayout *, ShaperFlags) const override
void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference)
Qt::HANDLE handle() const override
QFontEngine::Properties properties() const override
QFontEngine::FaceId faceId() const override
Glyph * loadGlyphFor(glyph_t g, const QFixedPoint &subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox=false, bool disableOutlineDrawing=false)
Glyph * loadGlyph(uint glyph, const QFixedPoint &subPixelPosition, GlyphFormat format=Format_None, bool fetchMetricsOnly=false, bool disableOutlineDrawing=false) const
int glyphCount() const override
static QFontEngineFT * create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData=QByteArray())
bool isBitmapFont() const
Glyph * glyphData(glyph_t glyph, const QFixedPoint &subPixelPosition, GlyphFormat neededFormat, const QTransform &t) override
glyph_t glyphIndex(uint ucs4) const override
void unlockFace() const
void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
int synthesized() const override
QFixed capHeight() const override
QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override
QImage bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color) override
virtual ~QFontEngineFT()
SubpixelAntialiasingType subpixelType
QFixed averageCharWidth() const override
bool getSfntTableData(uint tag, uchar *buffer, uint *length) const override
Returns true if the font table idetified by tag exists in the font; returns false otherwise.
bool supportsTransformation(const QTransform &transform) const override
FT_Face lockFace(Scaling scale=Scaled) const
void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const override
void * get() const noexcept
void reset() noexcept
virtual QFixed descent() const
void loadKerningPairs(QFixed scalingFactor)
QFixed calculatedCapHeight() const
static QByteArray convertToPostscriptFontFamilyName(const QByteArray &fontFamily)
int m_subPixelPositionCount
QFontDef fontDef
virtual QFixed ascent() const
struct QFontEngine::FaceData faceData
virtual QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t)
virtual QFixed averageCharWidth() const
virtual void initializeHeightMetrics() const
virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags)
virtual void doKerning(QGlyphLayout *, ShaperFlags) const
void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, QVarLengthArray< glyph_t > &glyphs_out, QVarLengthArray< QFixedPoint > &positions)
GlyphFormat glyphFormat
virtual QFixed leading() const
void * harfbuzzFace() const
virtual QFixed xHeight() const
HintingPreference
Definition qfont.h:52
@ PreferNoHinting
Definition qfont.h:54
@ PreferFullHinting
Definition qfont.h:56
@ PreferVerticalHinting
Definition qfont.h:55
@ PreferDefaultHinting
Definition qfont.h:53
@ Unstretched
Definition qfont.h:86
@ NoSubpixelAntialias
Definition qfont.h:46
@ NoAntialias
Definition qfont.h:45
@ Bold
Definition qfont.h:67
@ StyleItalic
Definition qfont.h:75
@ StyleNormal
Definition qfont.h:74
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
static int getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
FT_CharMap symbol_map
FT_CharMap unicode_map
static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
int fsType() const
static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
bool isScalableBitmap() const
glyph_t cmapCache[cmapCacheSize]
static QFreetypeFace * getFace(const QFontEngine::FaceId &face_id, const QByteArray &fontData=QByteArray())
void release(const QFontEngine::FaceId &face_id)
QFontEngine::Properties properties() const
void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
bool getSfntTable(uint tag, uchar *buffer, uint *length) const
QScreen * primaryScreen
the primary (or default) screen of the application.
\inmodule QtCore
Definition qhash.h:1135
\inmodule QtCore
Definition qhash.h:818
const_iterator cbegin() const noexcept
Definition qhash.h:1204
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
iterator erase(const_iterator it)
Definition qhash.h:1223
T value(const Key &key) const noexcept
Definition qhash.h:1044
const_iterator cend() const noexcept
Definition qhash.h:1208
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:949
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:926
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtGui
Definition qimage.h:37
QImage copy(const QRect &rect=QRect()) const
Returns a sub-area of the image as a new image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Alpha8
Definition qimage.h:65
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
\inmodule QtGui
virtual SubpixelAntialiasingType subpixelAntialiasingTypeHint() const
Returns a hint about this screen's subpixel layout structure.
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
\inmodule QtCore\reentrant
Definition qrect.h:483
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
QPlatformScreen * handle() const
Get the platform screen handle.
Definition qscreen.cpp:83
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setMatrix(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
Sets the matrix elements to the specified values, m11, m12, m13 m21, m22, m23 m31,...
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QUuid createUuid()
On any platform other than Windows, this function returns a new UUID with variant QUuid::DCE and vers...
Definition quuid.cpp:947
QByteArray toByteArray(StringFormat mode=WithBraces) const
Definition quuid.cpp:643
QHash< QFontEngine::FaceId, QFreetypeFace * > faces
QHash< FaceStyle, int > faceIndices
QString str
[2]
b clear()
qDeleteAll(list.begin(), list.end())
double e
QSet< QString >::iterator it
rect
[4]
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ SmoothTransformation
@ white
Definition qnamespace.h:30
void * HANDLE
@ CaseInsensitive
int qstrncmp(const char *str1, const char *str2, size_t len)
AudioChannelLayoutTag tag
static const QCssKnownValue positions[NumKnownPositionModes - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
DBusConnection const char DBusError * error
constexpr T qbswap(T source)
Definition qendian.h:103
#define QT_RETHROW
#define QT_CATCH(A)
#define QT_TRY
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
static QFontEngineFT::Glyph emptyGlyph
void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
#define FLOOR(x)
static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint()
static FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
static void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
QtFreetypeData * qt_getFreetypeData()
#define CEIL(x)
static bool is2dRotation(const QTransform &t)
static QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat)
size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length)
static const QFontEngine::HintStyle ftInitialDefaultHintStyle
static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
FT_Library qt_getFreetype()
#define GLYPH2PATH_DEBUG
QByteArray qt_fontdata_from_index(int)
static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
#define QT_MAX_CACHED_GLYPH_SIZE
static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
static void dont_delete(void *)
#define ROUND(x)
static bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
Q_GUI_EXPORT FT_Library qt_getFreetype()
@ Err_Invalid_SubTable
@ Err_Ok
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define TRUNC(x)
constexpr QtPrivate::QHashMultiReturnType< T... > qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v< QtPrivate::QNothrowHashable< T >... >)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLboolean GLboolean GLboolean b
GLenum GLsizeiptr const void GLsizei faceIndex
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint index
[2]
GLboolean r
[2]
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLenum const void GLbitfield GLsizei numGlyphs
GLenum face
GLfloat GLfloat f
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLint left
GLenum type
GLenum GLenum dst
GLint GLint bottom
GLuint GLfloat x0
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLint ref
GLfloat n
GLint GLsizei GLsizei GLenum format
GLenum GLsizeiptr const void * fontData
GLuint GLfloat GLfloat y0
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
GLbyte GLbyte blue
Definition qopenglext.h:385
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLenum GLsizei len
GLuint GLenum matrix
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLbyte green
Definition qopenglext.h:385
GLenum GLenum GLenum GLenum GLenum scale
#define QT_MAX_CACHED_GLYPH_SIZE
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
unsigned int glyph_t
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
size_t quintptr
Definition qtypes.h:72
unsigned int uint
Definition qtypes.h:29
double qreal
Definition qtypes.h:92
QVideoFrameFormat::PixelFormat fmt
QFuture< QSet< QChar > > set
[10]
QList< int > vector
[14]
QFile file
[0]
QFileInfo info(fileName)
[8]
QObject::connect nullptr
QJSEngine engine
[0]
QGraphicsSvgItem * red
QFixed y
Definition qfixed_p.h:163
constexpr QPointF toPointF() const
Definition qfixed_p.h:166
QFixed x
Definition qfixed_p.h:162
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
static constexpr QFixed fromFixed(int fixed)
Definition qfixed_p.h:36
constexpr int value() const
Definition qfixed_p.h:38
constexpr QFixed round() const
Definition qfixed_p.h:45
constexpr QFixed ceil() const
Definition qfixed_p.h:47
constexpr qreal toReal() const
Definition qfixed_p.h:42
uint hintingPreference
Definition qfont_p.h:66
uint stretch
Definition qfont_p.h:64
uint style
Definition qfont_p.h:65
uint styleStrategy
Definition qfont_p.h:63
qreal pixelSize
Definition qfont_p.h:60
uint weight
Definition qfont_p.h:69
QStringList families
Definition qfont_p.h:54
QString styleName
Definition qfont_p.h:55
void setGlyph(glyph_t index, const QFixedPoint &spp, Glyph *glyph)
void removeGlyphFromCache(glyph_t index, const QFixedPoint &subPixelPosition)
Glyph * getGlyph(glyph_t index, const QFixedPoint &subPixelPositionX=QFixedPoint()) const
qt_get_font_table_func_t get_font_table
unsigned short height
unsigned short width
QFixed effectiveAdvance(int item) const
QGlyphAttributes * attributes
glyph_t * glyphs
QFixedPoint * offsets
QFixed * advances
FaceStyle(QString faceFileName, QString styleName)