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