Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfontengine.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// Qt-Security score:critical reason:data-parser
4
5#include <qdebug.h>
6#include <private/qfontengine_p.h>
7#include <private/qfontengineglyphcache_p.h>
8#include <private/qguiapplication_p.h>
9
10#include <qpa/qplatformfontdatabase.h>
11#include <qpa/qplatformintegration.h>
12
13#include "qbitmap.h"
14#include "qpainter.h"
15#include "qpainterpath.h"
17#include "qtextengine_p.h"
18#include <qmath.h>
19#include <qendian.h>
20#include <private/qstringiterator_p.h>
21
22#if QT_CONFIG(harfbuzz)
23# include "qharfbuzzng_p.h"
24# include <hb-ot.h>
25#endif
26
27#include <algorithm>
28#include <limits.h>
29
30#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
31# define QT_MAX_CACHED_GLYPH_SIZE 64
32#endif
33
35
36Q_LOGGING_CATEGORY(lcColrv1, "qt.text.font.colrv1")
37Q_LOGGING_CATEGORY(lcTextFontCmap, "qt.text.font.cmap")
38
39using namespace Qt::StringLiterals;
40
41static inline bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
42{
43 if (a.type() <= QTransform::TxTranslate && b.type() <= QTransform::TxTranslate) {
44 return true;
45 } else {
46 // We always use paths for perspective text anyway, so no
47 // point in checking the full matrix...
48 Q_ASSERT(a.type() < QTransform::TxProject);
49 Q_ASSERT(b.type() < QTransform::TxProject);
50
51 return a.m11() == b.m11()
52 && a.m12() == b.m12()
53 && a.m21() == b.m21()
54 && a.m22() == b.m22();
55 }
56}
57
58template<typename T>
59static inline bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *output)
60{
61 if (source + sizeof(T) > end)
62 return false;
63
64 *output = qFromBigEndian<T>(source);
65 return true;
66}
67
68int QFontEngine::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
69{
70 Q_UNUSED(glyph);
71 Q_UNUSED(flags);
72 Q_UNUSED(point);
73 Q_UNUSED(xpos);
74 Q_UNUSED(ypos);
75 Q_UNUSED(nPoints);
76 return Err_Not_Covered;
77}
78
79static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
80{
81 QFontEngine *fe = (QFontEngine *)user_data;
82 return fe->getSfntTableData(tag, buffer, length);
83}
84
85
86#ifdef QT_BUILD_INTERNAL
87// for testing purpose only, not thread-safe!
88static QList<QFontEngine *> *enginesCollector = nullptr;
89
90Q_AUTOTEST_EXPORT void QFontEngine_startCollectingEngines()
91{
92 delete enginesCollector;
93 enginesCollector = new QList<QFontEngine *>();
94}
95
96Q_AUTOTEST_EXPORT QList<QFontEngine *> QFontEngine_stopCollectingEngines()
97{
98 Q_ASSERT(enginesCollector);
99 QList<QFontEngine *> ret = *enginesCollector;
100 delete enginesCollector;
101 enginesCollector = nullptr;
102 return ret;
103}
104#endif // QT_BUILD_INTERNAL
105
106
107// QFontEngine
108
109#define kBearingNotInitialized std::numeric_limits<qreal>::max()
110
111QFontEngine::QFontEngine(Type type)
112 : m_type(type), ref(0),
113 font_(),
114 face_(),
115 m_heightMetricsQueried(false),
116 m_minLeftBearing(kBearingNotInitialized),
117 m_minRightBearing(kBearingNotInitialized)
118{
119 faceData.user_data = this;
120 faceData.get_font_table = qt_get_font_table_default;
121
122 cache_cost = 0;
123 fsType = 0;
124 symbol = false;
125 isSmoothlyScalable = false;
126
127 glyphFormat = Format_None;
128 m_subPixelPositionCount = 0;
129
130#ifdef QT_BUILD_INTERNAL
131 if (enginesCollector)
132 enginesCollector->append(this);
133#endif
134}
135
136QFontEngine::~QFontEngine()
137{
138#ifdef QT_BUILD_INTERNAL
139 if (enginesCollector)
140 enginesCollector->removeOne(this);
141#endif
142}
143
144QFixed QFontEngine::lineThickness() const
145{
146 // ad hoc algorithm
147 int score = fontDef.weight * fontDef.pixelSize / 10;
148 int lw = score / 700;
149
150 // looks better with thicker line for small pointsizes
151 if (lw < 2 && score >= 1050) lw = 2;
152 if (lw == 0) lw = 1;
153
154 return lw;
155}
156
157QFixed QFontEngine::underlinePosition() const
158{
159 return ((lineThickness() * 2) + 3) / 6;
160}
161
162void *QFontEngine::harfbuzzFont() const
163{
164 Q_ASSERT(type() != QFontEngine::Multi);
165#if QT_CONFIG(harfbuzz)
166 return hb_qt_font_get_for_engine(const_cast<QFontEngine *>(this));
167#else
168 return nullptr;
169#endif
170}
171
172void *QFontEngine::harfbuzzFace() const
173{
174 Q_ASSERT(type() != QFontEngine::Multi);
175#if QT_CONFIG(harfbuzz)
176 return hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this));
177#else
178 return nullptr;
179#endif
180}
181
182bool QFontEngine::supportsScript(QChar::Script script) const
183{
184 if (type() <= QFontEngine::Multi)
185 return true;
186
187 // ### TODO: This only works for scripts that require OpenType. More generally
188 // for scripts that do not require OpenType we should just look at the list of
189 // supported writing systems in the font's OS/2 table.
190 if (!scriptRequiresOpenType(script))
191 return true;
192
193#if QT_CONFIG(harfbuzz)
194 // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table
195 uint lenMort = 0, lenMorx = 0;
196 if (getSfntTableData(QFont::Tag("mort").value(), nullptr, &lenMort)
197 || getSfntTableData(QFont::Tag("morx").value(), nullptr, &lenMorx)) {
198 return true;
199 }
200
201 if (hb_face_t *face = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(this))) {
202 unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
203 hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
204
205 hb_ot_tags_from_script_and_language(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
206 &script_count, script_tags,
207 nullptr, nullptr);
208
209 if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, nullptr))
210 return true;
211 }
212#endif
213 return false;
214}
215
216bool QFontEngine::canRender(const QChar *str, int len) const
217{
218 QStringIterator it(str, str + len);
219 while (it.hasNext()) {
220 if (glyphIndex(it.next()) == 0)
221 return false;
222 }
223
224 return true;
225}
226
227glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
228{
229 glyph_metrics_t metrics = boundingBox(glyph);
230
231 if (matrix.type() > QTransform::TxTranslate) {
232 return metrics.transformed(matrix);
233 }
234 return metrics;
235}
236
237QFixed QFontEngine::calculatedCapHeight() const
238{
239 const glyph_t glyph = glyphIndex('H');
240 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
241 return bb.height;
242}
243
244QFixed QFontEngine::xHeight() const
245{
246 const glyph_t glyph = glyphIndex('x');
247 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
248 return bb.height;
249}
250
251QFixed QFontEngine::averageCharWidth() const
252{
253 const glyph_t glyph = glyphIndex('x');
254 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
255 return bb.xoff;
256}
257
258bool QFontEngine::supportsTransformation(const QTransform &transform) const
259{
260 return transform.type() < QTransform::TxProject;
261}
262
263bool QFontEngine::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
264{
265 Q_UNUSED(format);
266 return true;
267}
268
269void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
270 QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
271{
272 QFixed xpos;
273 QFixed ypos;
274
275 const bool transform = matrix.m11() != 1.
276 || matrix.m12() != 0.
277 || matrix.m21() != 0.
278 || matrix.m22() != 1.;
279 if (!transform) {
280 xpos = QFixed::fromReal(matrix.dx());
281 ypos = QFixed::fromReal(matrix.dy());
282 }
283
284 int current = 0;
285 if (flags & QTextItem::RightToLeft) {
286 int i = glyphs.numGlyphs;
287 int totalKashidas = 0;
288 while(i--) {
289 if (glyphs.attributes[i].dontPrint)
290 continue;
291 xpos += glyphs.advances[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
292 totalKashidas += glyphs.justifications[i].nKashidas;
293 }
294 positions.resize(glyphs.numGlyphs+totalKashidas);
295 glyphs_out.resize(glyphs.numGlyphs+totalKashidas);
296
297 i = 0;
298 while(i < glyphs.numGlyphs) {
299 if (glyphs.attributes[i].dontPrint) {
300 ++i;
301 continue;
302 }
303 xpos -= glyphs.advances[i];
304
305 QFixed gpos_x = xpos + glyphs.offsets[i].x;
306 QFixed gpos_y = ypos + glyphs.offsets[i].y;
307 if (transform) {
308 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
309 gpos = gpos * matrix;
310 gpos_x = QFixed::fromReal(gpos.x());
311 gpos_y = QFixed::fromReal(gpos.y());
312 }
313 positions[current].x = gpos_x;
314 positions[current].y = gpos_y;
315 glyphs_out[current] = glyphs.glyphs[i];
316 ++current;
317 if (glyphs.justifications[i].nKashidas) {
318 QChar ch = u'\x640'; // Kashida character
319
320 glyph_t kashidaGlyph = glyphIndex(ch.unicode());
321 QFixed kashidaWidth;
322
323 QGlyphLayout g;
324 g.numGlyphs = 1;
325 g.glyphs = &kashidaGlyph;
326 g.advances = &kashidaWidth;
327 recalcAdvances(&g, { });
328
329 for (uint k = 0; k < glyphs.justifications[i].nKashidas; ++k) {
330 xpos -= kashidaWidth;
331
332 QFixed gpos_x = xpos + glyphs.offsets[i].x;
333 QFixed gpos_y = ypos + glyphs.offsets[i].y;
334 if (transform) {
335 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
336 gpos = gpos * matrix;
337 gpos_x = QFixed::fromReal(gpos.x());
338 gpos_y = QFixed::fromReal(gpos.y());
339 }
340 positions[current].x = gpos_x;
341 positions[current].y = gpos_y;
342 glyphs_out[current] = kashidaGlyph;
343 ++current;
344 }
345 } else {
346 xpos -= QFixed::fromFixed(glyphs.justifications[i].space_18d6);
347 }
348 ++i;
349 }
350 } else {
351 positions.resize(glyphs.numGlyphs);
352 glyphs_out.resize(glyphs.numGlyphs);
353 int i = 0;
354 if (!transform) {
355 while (i < glyphs.numGlyphs) {
356 if (!glyphs.attributes[i].dontPrint) {
357 positions[current].x = xpos + glyphs.offsets[i].x;
358 positions[current].y = ypos + glyphs.offsets[i].y;
359 glyphs_out[current] = glyphs.glyphs[i];
360 xpos += glyphs.advances[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
361 ++current;
362 }
363 ++i;
364 }
365 } else {
366 while (i < glyphs.numGlyphs) {
367 if (!glyphs.attributes[i].dontPrint) {
368 QFixed gpos_x = xpos + glyphs.offsets[i].x;
369 QFixed gpos_y = ypos + glyphs.offsets[i].y;
370 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
371 gpos = gpos * matrix;
372 positions[current].x = QFixed::fromReal(gpos.x());
373 positions[current].y = QFixed::fromReal(gpos.y());
374 glyphs_out[current] = glyphs.glyphs[i];
375 xpos += glyphs.advances[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
376 ++current;
377 }
378 ++i;
379 }
380 }
381 }
382 positions.resize(current);
383 glyphs_out.resize(current);
384 Q_ASSERT(positions.size() == glyphs_out.size());
385}
386
387void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
388{
389 glyph_metrics_t gi = boundingBox(glyph);
390 if (leftBearing != nullptr)
391 *leftBearing = gi.leftBearing().toReal();
392 if (rightBearing != nullptr)
393 *rightBearing = gi.rightBearing().toReal();
394}
395
396bool QFontEngine::processHheaTable() const
397{
398 QByteArray hhea = getSfntTable(QFont::Tag("hhea").value());
399 if (hhea.size() >= 10) {
400 auto ptr = hhea.constData();
401 qint16 ascent = qFromBigEndian<qint16>(ptr + 4);
402 qint16 descent = qFromBigEndian<qint16>(ptr + 6);
403 qint16 leading = qFromBigEndian<qint16>(ptr + 8);
404
405 // Some fonts may have invalid HHEA data. We detect this and bail out.
406 if (ascent == 0 && descent == 0)
407 return false;
408
409 const qreal unitsPerEm = emSquareSize().toReal();
410 // Bail out if values are too large for QFixed
411 const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
412 if (ascent > limitForQFixed || descent > limitForQFixed || leading > limitForQFixed)
413 return false;
414 m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize / unitsPerEm);
415 m_descent = -QFixed::fromReal(descent * fontDef.pixelSize / unitsPerEm);
416 m_leading = QFixed::fromReal(leading * fontDef.pixelSize / unitsPerEm);
417
418 return true;
419 }
420
421 return false;
422}
423
424void QFontEngine::initializeHeightMetrics() const
425{
426 bool hasEmbeddedBitmaps =
427 !getSfntTable(QFont::Tag("EBLC").value()).isEmpty()
428 || !getSfntTable(QFont::Tag("CBLC").value()).isEmpty()
429 || !getSfntTable(QFont::Tag("bdat").value()).isEmpty();
430
431 // When porting applications from Qt 5 to Qt 6, users have noticed differences in line
432 // metrics due to the effort to consolidate these across platforms instead of using the
433 // system values directly. This environment variable gives a "last resort" for those users
434 // to tell Qt to prefer the metrics we get from the system, despite the fact these being
435 // inconsistent across platforms.
436 static bool useSystemLineMetrics = qEnvironmentVariableIntValue("QT_USE_SYSTEM_LINE_METRICS") > 0;
437 if (!hasEmbeddedBitmaps && !useSystemLineMetrics) {
438 // Get HHEA table values if available
439 processHheaTable();
440
441 // Allow OS/2 metrics to override if present
442 processOS2Table();
443
444 if (!supportsSubPixelPositions()) {
445 const QFixed actualHeight = m_ascent + m_descent + m_leading;
446 m_ascent = m_ascent.round();
447 m_descent = m_descent.round();
448 m_leading = actualHeight.round() - m_ascent - m_descent;
449 }
450 }
451
452 m_heightMetricsQueried = true;
453}
454
455bool QFontEngine::preferTypoLineMetrics() const
456{
457 return (fontDef.styleStrategy & QFont::PreferTypoLineMetrics) != 0;
458}
459
460bool QFontEngine::processOS2Table() const
461{
462 QByteArray os2 = getSfntTable(QFont::Tag("OS/2").value());
463 if (os2.size() >= 78) {
464 auto ptr = os2.constData();
465 quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62);
466 qint16 typoAscent = qFromBigEndian<qint16>(ptr + 68);
467 qint16 typoDescent = qFromBigEndian<qint16>(ptr + 70);
468 qint16 typoLineGap = qFromBigEndian<qint16>(ptr + 72);
469 quint16 winAscent = qFromBigEndian<quint16>(ptr + 74);
470 quint16 winDescent = qFromBigEndian<quint16>(ptr + 76);
471
472 enum { USE_TYPO_METRICS = 0x80 };
473 const qreal unitsPerEm = emSquareSize().toReal();
474 if (preferTypoLineMetrics() || fsSelection & USE_TYPO_METRICS) {
475 // Some fonts may have invalid OS/2 data. We detect this and bail out.
476 if (typoAscent == 0 && typoDescent == 0)
477 return false;
478 // Bail out if values are too large for QFixed
479 const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
480 if (typoAscent > limitForQFixed || typoDescent > limitForQFixed
481 || typoLineGap > limitForQFixed)
482 return false;
483 m_ascent = QFixed::fromReal(typoAscent * fontDef.pixelSize / unitsPerEm);
484 m_descent = -QFixed::fromReal(typoDescent * fontDef.pixelSize / unitsPerEm);
485 m_leading = QFixed::fromReal(typoLineGap * fontDef.pixelSize / unitsPerEm);
486 } else {
487 // Some fonts may have invalid OS/2 data. We detect this and bail out.
488 if (winAscent == 0 && winDescent == 0)
489 return false;
490 const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
491 if (winAscent > limitForQFixed || winDescent > limitForQFixed)
492 return false;
493 m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize / unitsPerEm);
494 m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize / unitsPerEm);
495 m_leading = QFixed{};
496 }
497
498 return true;
499 }
500
501 return false;
502}
503
504QFixed QFontEngine::leading() const
505{
506 if (!m_heightMetricsQueried)
507 initializeHeightMetrics();
508
509 return m_leading;
510}
511
512
513QFixed QFontEngine::emSquareSize() const
514{
515 qCWarning(lcQpaFonts) << "Font engine does not reimplement emSquareSize(). Returning minimum value.";
516 return 16;
517}
518
519QFixed QFontEngine::ascent() const
520{
521 if (!m_heightMetricsQueried)
522 initializeHeightMetrics();
523
524 return m_ascent;
525}
526
527QFixed QFontEngine::descent() const
528{
529 if (!m_heightMetricsQueried)
530 initializeHeightMetrics();
531
532 return m_descent;
533}
534
535qreal QFontEngine::minLeftBearing() const
536{
537 if (m_minLeftBearing == kBearingNotInitialized)
538 minRightBearing(); // Initializes both (see below)
539
540 return m_minLeftBearing;
541}
542
543#define q16Dot16ToFloat(i) ((i) / 65536.0)
544
545#define kMinLeftSideBearingOffset 12
546#define kMinRightSideBearingOffset 14
547
548qreal QFontEngine::minRightBearing() const
549{
550 if (m_minRightBearing == kBearingNotInitialized) {
551
552 // Try the 'hhea' font table first, which covers the entire font
553 QByteArray hheaTable = getSfntTable(QFont::Tag("hhea").value());
554 if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) {
555 const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData());
556 Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0);
557
558 qint16 minLeftSideBearing = qFromBigEndian<qint16>(tableData + kMinLeftSideBearingOffset);
559 qint16 minRightSideBearing = qFromBigEndian<qint16>(tableData + kMinRightSideBearingOffset);
560
561 // The table data is expressed as FUnits, meaning we have to take the number
562 // of units per em into account. Since pixelSize already has taken DPI into
563 // account we can use that directly instead of the point size.
564 int unitsPerEm = emSquareSize().toInt();
565 qreal funitToPixelFactor = fontDef.pixelSize / unitsPerEm;
566
567 // Some fonts on OS X (such as Gurmukhi Sangam MN, Khmer MN, Lao Sangam MN, etc.), have
568 // invalid values for their NBSPACE left bearing, causing the 'hhea' minimum bearings to
569 // be way off. We detect this by assuming that the minimum bearsings are within a certain
570 // range of the em square size.
571 static const int largestValidBearing = 4 * unitsPerEm;
572
573 if (qAbs(minLeftSideBearing) < largestValidBearing)
574 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
575 if (qAbs(minRightSideBearing) < largestValidBearing)
576 m_minRightBearing = minRightSideBearing * funitToPixelFactor;
577 }
578
579 // Fallback in case of missing 'hhea' table (bitmap fonts e.g.) or broken 'hhea' values
580 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) {
581
582 // To balance performance and correctness we only look at a subset of the
583 // possible glyphs in the font, based on which characters are more likely
584 // to have a left or right bearing.
585 static const ushort characterSubset[] = {
586 '(', 'C', 'F', 'K', 'V', 'X', 'Y', ']', '_', 'f', 'r', '|',
587 127, 205, 645, 884, 922, 1070, 12386
588 };
589
590 // The font may have minimum bearings larger than 0, so we have to start at the max
591 m_minLeftBearing = m_minRightBearing = std::numeric_limits<qreal>::max();
592
593 for (uint i = 0; i < (sizeof(characterSubset) / sizeof(ushort)); ++i) {
594 const glyph_t glyph = glyphIndex(characterSubset[i]);
595 if (!glyph)
596 continue;
597
598 glyph_metrics_t glyphMetrics = const_cast<QFontEngine *>(this)->boundingBox(glyph);
599
600 // Glyphs with no contours shouldn't contribute to bearings
601 if (!glyphMetrics.width || !glyphMetrics.height)
602 continue;
603
604 m_minLeftBearing = qMin(m_minLeftBearing, glyphMetrics.leftBearing().toReal());
605 m_minRightBearing = qMin(m_minRightBearing, glyphMetrics.rightBearing().toReal());
606 }
607 }
608
609 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized)
610 qWarning() << "Failed to compute left/right minimum bearings for"
611 << fontDef.family();
612 }
613
614 return m_minRightBearing;
615}
616
617glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs)
618{
619 QFixed w;
620 for (int i = 0; i < glyphs.numGlyphs; ++i)
621 w += glyphs.effectiveAdvance(i);
622 const QFixed leftBearing = firstLeftBearing(glyphs);
623 const QFixed rightBearing = lastRightBearing(glyphs);
624 return glyph_metrics_t(leftBearing, -(ascent()), w - leftBearing - rightBearing, ascent() + descent(), w, 0);
625}
626
627glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs, QTextItem::RenderFlags flags)
628{
629 glyph_metrics_t overall;
630
631 const bool isRtl = flags & QTextItem::RightToLeft;
632 if (isRtl) {
633 for (int i = 0; i < glyphs.numGlyphs; i++) {
634 // If shaping has found this should be ignored, ignore it.
635 if (glyphs.attributes[i].dontPrint)
636 continue;
637
638 overall.xoff += glyphs.effectiveAdvance(i);
639 }
640 }
641
642 QFixed ymax = 0;
643 QFixed xmax = 0;
644 for (int i = 0; i < glyphs.numGlyphs; i++) {
645 // If shaping has found this should be ignored, ignore it.
646 if (glyphs.attributes[i].dontPrint)
647 continue;
648
649 glyph_metrics_t bb = boundingBox(glyphs.glyphs[i]);
650
651 if (isRtl)
652 overall.xoff -= glyphs.effectiveAdvance(i);
653
654 QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x;
655 QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
656
657 overall.x = qMin(overall.x, x);
658 overall.y = qMin(overall.y, y);
659 xmax = qMax(xmax, x.ceil() + bb.width);
660 ymax = qMax(ymax, y.ceil() + bb.height);
661
662 if (!isRtl)
663 overall.xoff += glyphs.effectiveAdvance(i);
664 overall.yoff += bb.yoff;
665 }
666 overall.height = qMax(overall.height, ymax - overall.y);
667 overall.width = xmax - overall.x;
668
669 return overall;
670}
671
672
673void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
674 QTextItem::RenderFlags flags)
675{
676 if (!glyphs.numGlyphs)
677 return;
678
679 QVarLengthArray<QFixedPoint> positions;
680 QVarLengthArray<glyph_t> positioned_glyphs;
681 QTransform matrix = QTransform::fromTranslate(x, y);
682 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
683 addGlyphsToPath(positioned_glyphs.data(), positions.data(), positioned_glyphs.size(), path, flags);
684}
685
686#define GRID(x, y) grid[(y)*(w+1) + (x)]
687#define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7)))
688
689enum { EdgeRight = 0x1,
690 EdgeDown = 0x2,
691 EdgeLeft = 0x4,
692 EdgeUp = 0x8
693};
694
695static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
696{
697 Q_UNUSED(h);
698
699 path->moveTo(x + x0, y + y0);
700 while (GRID(x, y)) {
701 if (GRID(x, y) & EdgeRight) {
702 while (GRID(x, y) & EdgeRight) {
703 GRID(x, y) &= ~EdgeRight;
704 ++x;
705 }
706 Q_ASSERT(x <= w);
707 path->lineTo(x + x0, y + y0);
708 continue;
709 }
710 if (GRID(x, y) & EdgeDown) {
711 while (GRID(x, y) & EdgeDown) {
712 GRID(x, y) &= ~EdgeDown;
713 ++y;
714 }
715 Q_ASSERT(y <= h);
716 path->lineTo(x + x0, y + y0);
717 continue;
718 }
719 if (GRID(x, y) & EdgeLeft) {
720 while (GRID(x, y) & EdgeLeft) {
721 GRID(x, y) &= ~EdgeLeft;
722 --x;
723 }
724 Q_ASSERT(x >= 0);
725 path->lineTo(x + x0, y + y0);
726 continue;
727 }
728 if (GRID(x, y) & EdgeUp) {
729 while (GRID(x, y) & EdgeUp) {
730 GRID(x, y) &= ~EdgeUp;
731 --y;
732 }
733 Q_ASSERT(y >= 0);
734 path->lineTo(x + x0, y + y0);
735 continue;
736 }
737 }
738 path->closeSubpath();
739}
740
741Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
742{
743 uint *grid = new uint[(w+1)*(h+1)];
744 // set up edges
745 for (int y = 0; y <= h; ++y) {
746 for (int x = 0; x <= w; ++x) {
747 bool topLeft = (x == 0 || y == 0) ? false : SET(x - 1, y - 1);
748 bool topRight = (x == w || y == 0) ? false : SET(x, y - 1);
749 bool bottomLeft = (x == 0 || y == h) ? false : SET(x - 1, y);
750 bool bottomRight = (x == w || y == h) ? false : SET(x, y);
751
752 GRID(x, y) = 0;
753 if ((!topRight) & bottomRight)
754 GRID(x, y) |= EdgeRight;
755 if ((!bottomRight) & bottomLeft)
756 GRID(x, y) |= EdgeDown;
757 if ((!bottomLeft) & topLeft)
758 GRID(x, y) |= EdgeLeft;
759 if ((!topLeft) & topRight)
760 GRID(x, y) |= EdgeUp;
761 }
762 }
763
764 // collect edges
765 for (int y = 0; y < h; ++y) {
766 for (int x = 0; x < w; ++x) {
767 if (!GRID(x, y))
768 continue;
769 // found start of a contour, follow it
770 collectSingleContour(x0, y0, grid, x, y, w, h, path);
771 }
772 }
773 delete [] grid;
774}
775
776#undef GRID
777#undef SET
778
779
780void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
781 QPainterPath *path, QTextItem::RenderFlags flags)
782{
783// TODO what to do with 'flags' ??
784 Q_UNUSED(flags);
785 QFixed advanceX = QFixed::fromReal(x);
786 QFixed advanceY = QFixed::fromReal(y);
787 for (int i=0; i < glyphs.numGlyphs; ++i) {
788 glyph_metrics_t metrics = boundingBox(glyphs.glyphs[i]);
789 if (metrics.width.value() == 0 || metrics.height.value() == 0) {
790 advanceX += glyphs.advances[i];
791 continue;
792 }
793 const QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]);
794
795 const int w = alphaMask.width();
796 const int h = alphaMask.height();
797 const qsizetype srcBpl = alphaMask.bytesPerLine();
798 QImage bitmap;
799 if (alphaMask.depth() == 1) {
800 bitmap = alphaMask;
801 } else {
802 bitmap = QImage(w, h, QImage::Format_Mono);
803 const uchar *imageData = alphaMask.bits();
804 const qsizetype destBpl = bitmap.bytesPerLine();
805 uchar *bitmapData = bitmap.bits();
806
807 for (int yi = 0; yi < h; ++yi) {
808 const uchar *src = imageData + yi*srcBpl;
809 uchar *dst = bitmapData + yi*destBpl;
810 for (int xi = 0; xi < w; ++xi) {
811 const int byte = xi / 8;
812 const int bit = xi % 8;
813 if (bit == 0)
814 dst[byte] = 0;
815 if (src[xi])
816 dst[byte] |= 128 >> bit;
817 }
818 }
819 }
820 const uchar *bitmap_data = bitmap.constBits();
821 QFixedPoint offset = glyphs.offsets[i];
822 advanceX += offset.x;
823 advanceY += offset.y;
824 qt_addBitmapToPath((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, bitmap.bytesPerLine(), w, h, path);
825 advanceX += glyphs.advances[i];
826 }
827}
828
829void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
830 QPainterPath *path, QTextItem::RenderFlags flags)
831{
832 qreal x = positions[0].x.toReal();
833 qreal y = positions[0].y.toReal();
834 QVarLengthGlyphLayoutArray g(nGlyphs);
835
836 for (int i = 0; i < nGlyphs - 1; ++i) {
837 g.glyphs[i] = glyphs[i];
838 g.advances[i] = positions[i + 1].x - positions[i].x;
839 }
840 g.glyphs[nGlyphs - 1] = glyphs[nGlyphs - 1];
841 g.advances[nGlyphs - 1] = QFixed::fromReal(maxCharWidth());
842
843 addBitmapFontToPath(x, y, g, path, flags);
844}
845
846QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/)
847{
848 // For font engines don't support subpixel positioning
849 return alphaMapForGlyph(glyph);
850}
851
852QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
853{
854 QImage i = alphaMapForGlyph(glyph);
855 if (t.type() > QTransform::TxTranslate)
856 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
857 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
858
859 return i;
860}
861
862QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t)
863{
864 if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions())
865 return alphaMapForGlyph(glyph, t);
866
867 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
868 if (t.type() > QTransform::TxTranslate)
869 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
870 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
871
872 return i;
873}
874
875QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &t)
876{
877 const QImage alphaMask = alphaMapForGlyph(glyph, t);
878 QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
879
880 for (int y=0; y<alphaMask.height(); ++y) {
881 uint *dst = (uint *) rgbMask.scanLine(y);
882 const uchar *src = alphaMask.constScanLine(y);
883 for (int x=0; x<alphaMask.width(); ++x) {
884 int val = src[x];
885 dst[x] = qRgb(val, val, val);
886 }
887 }
888
889 return rgbMask;
890}
891
892QImage QFontEngine::bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform&, const QColor &)
893{
894 Q_UNUSED(subPixelPosition);
895
896 return QImage();
897}
898
899QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const
900{
901 if (m_subPixelPositionCount <= 1
902 || (!supportsHorizontalSubPixelPositions()
903 && !supportsVerticalSubPixelPositions())) {
904 return QFixedPoint();
905 }
906
907 auto f = [&](QFixed v) {
908 if (v != 0) {
909 v = v - v.floor() + QFixed::fromFixed(1);
910 QFixed fraction = (v * m_subPixelPositionCount).floor();
911 v = fraction / QFixed(m_subPixelPositionCount);
912 }
913 return v;
914 };
915
916 return QFixedPoint(f(position.x), f(position.y));
917}
918
919QFontEngine::Glyph *QFontEngine::glyphData(glyph_t,
920 const QFixedPoint &,
921 QFontEngine::GlyphFormat,
922 const QTransform &)
923{
924 return nullptr;
925}
926
927#if QT_CONFIG(harfbuzz)
928template <typename Functor>
929auto queryHarfbuzz(const QFontEngine *engine, Functor &&func)
930{
931 decltype(func(nullptr)) result = {};
932
933 hb_face_t *hbFace = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(engine));
934 if (hb_font_t *hbFont = hb_font_create(hbFace)) {
935 // explicitly use OpenType handlers for this font
936 hb_ot_font_set_funcs(hbFont);
937
938 result = func(hbFont);
939
940 hb_font_destroy(hbFont);
941 }
942
943 return result;
944}
945#endif
946
947QString QFontEngine::glyphName(glyph_t index) const
948{
949 QString result;
950 if (index >= glyph_t(glyphCount()))
951 return result;
952
953#if QT_CONFIG(harfbuzz)
954 result = queryHarfbuzz(this, [index](hb_font_t *hbFont){
955 QString result;
956 // According to the OpenType specification, glyph names are limited to 63
957 // characters and can only contain (a subset of) ASCII.
958 char name[64];
959 if (hb_font_get_glyph_name(hbFont, index, name, sizeof(name)))
960 result = QString::fromLatin1(name);
961 return result;
962 });
963#endif
964
965 if (result.isEmpty())
966 result = index ? u"gid%1"_s.arg(index) : u".notdef"_s;
967 return result;
968}
969
970glyph_t QFontEngine::findGlyph(QLatin1StringView name) const
971{
972 glyph_t result = 0;
973
974#if QT_CONFIG(harfbuzz)
975 result = queryHarfbuzz(this, [name](hb_font_t *hbFont){
976 // glyph names are all ASCII, so latin1 is fine here.
977 hb_codepoint_t glyph;
978 if (hb_font_get_glyph_from_name(hbFont, name.constData(), name.size(), &glyph))
979 return glyph_t(glyph);
980 return glyph_t(0);
981 });
982#else // if we are here, no point in trying again if we already tried harfbuzz
983 if (!result) {
984 for (glyph_t index = 0; index < uint(glyphCount()); ++index) {
985 if (name == glyphName(index))
986 return index;
987 }
988 }
989#endif
990
991 if (!result) {
992 constexpr auto gid = "gid"_L1;
993 constexpr auto uni = "uni"_L1;
994 if (name.startsWith(gid)) {
995 bool ok;
996 result = name.slice(gid.size()).toUInt(&ok);
997 if (ok && result < glyph_t(glyphCount()))
998 return result;
999 } else if (name.startsWith(uni)) {
1000 bool ok;
1001 const uint ucs4 = name.slice(uni.size()).toUInt(&ok, 16);
1002 if (ok) {
1003 result = glyphIndex(ucs4);
1004 if (result > 0 && result < glyph_t(glyphCount()))
1005 return result;
1006 }
1007 }
1008 }
1009
1010 return result;
1011}
1012
1013QImage QFontEngine::renderedPathForGlyph(glyph_t glyph, const QColor &color)
1014{
1015 glyph_metrics_t gm = boundingBox(glyph);
1016 int glyph_x = qFloor(gm.x.toReal());
1017 int glyph_y = qFloor(gm.y.toReal());
1018 int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
1019 int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
1020
1021 if (glyph_width <= 0 || glyph_height <= 0)
1022 return QImage();
1023 QFixedPoint pt;
1024 pt.x = -glyph_x;
1025 pt.y = -glyph_y; // the baseline
1026 QPainterPath path;
1027 path.setFillRule(Qt::WindingFill);
1028 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
1029 im.fill(Qt::transparent);
1030 QPainter p(&im);
1031 p.setRenderHint(QPainter::Antialiasing);
1032 addGlyphsToPath(&glyph, &pt, 1, &path, { });
1033 p.setPen(Qt::NoPen);
1034 p.setBrush(color);
1035 p.drawPath(path);
1036 p.end();
1037
1038 return im;
1039}
1040
1041QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
1042{
1043 QImage im = renderedPathForGlyph(glyph, Qt::black);
1044 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
1045
1046 for (int y=0; y<im.height(); ++y) {
1047 uchar *dst = (uchar *) alphaMap.scanLine(y);
1048 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
1049 for (int x=0; x<im.width(); ++x)
1050 dst[x] = qAlpha(src[x]);
1051 }
1052
1053 return alphaMap;
1054}
1055
1056void QFontEngine::removeGlyphFromCache(glyph_t)
1057{
1058}
1059
1060QFontEngine::Properties QFontEngine::properties() const
1061{
1062 Properties p;
1063 p.postscriptName =
1064 QFontEngine::convertToPostscriptFontFamilyName(fontDef.family().toUtf8()) + '-'
1065 + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight);
1066 p.ascent = ascent();
1067 p.descent = descent();
1068 p.leading = leading();
1069 p.emSquare = p.ascent;
1070 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
1071 p.italicAngle = 0;
1072 p.capHeight = p.ascent;
1073 p.lineWidth = lineThickness();
1074 return p;
1075}
1076
1077void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1078{
1079 *metrics = boundingBox(glyph);
1080 QFixedPoint p;
1081 p.x = 0;
1082 p.y = 0;
1083 addGlyphsToPath(&glyph, &p, 1, path, QFlag(0));
1084}
1085
1086/*!
1087 \internal
1088 Returns \c true if the font table idetified by \a tag exists in the font;
1089 returns \c false otherwise.
1090
1091 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
1092 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
1093 of the buffer, passed in \a length, is sufficient to store the font table data,
1094 also copies the font table data to \a buffer.
1095
1096 Note: returning \c false when the font table exists could lead to an undefined behavior.
1097*/
1098bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1099{
1100 Q_UNUSED(tag);
1101 Q_UNUSED(buffer);
1102 Q_UNUSED(length);
1103 return false;
1104}
1105
1106QByteArray QFontEngine::getSfntTable(uint tag) const
1107{
1108 QByteArray table;
1109 uint len = 0;
1110 if (!getSfntTableData(tag, nullptr, &len))
1111 return table;
1112 table.resize(len);
1113 if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
1114 return QByteArray();
1115 return table;
1116}
1117
1118void QFontEngine::clearGlyphCache(const void *context)
1119{
1120 m_glyphCaches.remove(context);
1121}
1122
1123void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
1124{
1125 Q_ASSERT(cache);
1126
1127 GlyphCaches &caches = m_glyphCaches[context];
1128 for (auto & e : caches) {
1129 if (cache == e.cache.data())
1130 return;
1131 }
1132
1133 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
1134 // and limits memory use when there is continuous or random rotation
1135 if (caches.size() == 4)
1136 caches.pop_back();
1137
1138 GlyphCacheEntry entry;
1139 entry.cache = cache;
1140 caches.push_front(entry);
1141
1142}
1143
1144QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
1145 GlyphFormat format,
1146 const QTransform &transform,
1147 const QColor &color) const
1148{
1149 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(context);
1150 if (caches == m_glyphCaches.cend())
1151 return nullptr;
1152
1153 for (auto &e : *caches) {
1154 QFontEngineGlyphCache *cache = e.cache.data();
1155 if (format == cache->glyphFormat()
1156 && (format != Format_ARGB || color == cache->color())
1157 && qtransform_equals_no_translate(cache->m_transform, transform)) {
1158 return cache;
1159 }
1160 }
1161
1162 return nullptr;
1163}
1164
1165static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1166{
1167 uint left_right = (left << 16) + right;
1168
1169 left = 0, right = numPairs - 1;
1170 while (left <= right) {
1171 int middle = left + ( ( right - left ) >> 1 );
1172
1173 if (pairs[middle].left_right == left_right)
1174 return pairs[middle].adjust;
1175
1176 if (pairs[middle].left_right < left_right)
1177 left = middle + 1;
1178 else
1179 right = middle - 1;
1180 }
1181 return 0;
1182}
1183
1184void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1185{
1186 int numPairs = kerning_pairs.size();
1187 if (!numPairs)
1188 return;
1189
1190 const KernPair *pairs = kerning_pairs.constData();
1191
1192 if (flags & DesignMetrics) {
1193 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1194 glyphs->advances[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
1195 } else {
1196 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1197 glyphs->advances[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
1198 }
1199}
1200
1201void QFontEngine::loadKerningPairs(QFixed scalingFactor)
1202{
1203 kerning_pairs.clear();
1204
1205 QByteArray tab = getSfntTable(QFont::Tag("kern").value());
1206 if (tab.isEmpty())
1207 return;
1208
1209 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1210 const uchar *end = table + tab.size();
1211
1212 quint16 version;
1213 if (!qSafeFromBigEndian(table, end, &version))
1214 return;
1215
1216 if (version != 0) {
1217// qDebug("wrong version");
1218 return;
1219 }
1220
1221 quint16 numTables;
1222 if (!qSafeFromBigEndian(table + 2, end, &numTables))
1223 return;
1224
1225 {
1226 int offset = 4;
1227 for(int i = 0; i < numTables; ++i) {
1228 const uchar *header = table + offset;
1229
1230 quint16 version;
1231 if (!qSafeFromBigEndian(header, end, &version))
1232 goto end;
1233
1234 quint16 length;
1235 if (!qSafeFromBigEndian(header + 2, end, &length))
1236 goto end;
1237
1238 quint16 coverage;
1239 if (!qSafeFromBigEndian(header + 4, end, &coverage))
1240 goto end;
1241
1242// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1243 if (version == 0 && coverage == 0x0001) {
1244 if (offset + length > tab.size()) {
1245// qDebug("length ouf ot bounds");
1246 goto end;
1247 }
1248 const uchar *data = table + offset + 6;
1249
1250 quint16 nPairs;
1251 if (!qSafeFromBigEndian(data, end, &nPairs))
1252 goto end;
1253
1254 if (nPairs * 6 + 8 > length - 6) {
1255// qDebug("corrupt table!");
1256 // corrupt table
1257 goto end;
1258 }
1259
1260 int off = 8;
1261 for(int i = 0; i < nPairs; ++i) {
1262 QFontEngine::KernPair p;
1263
1264 quint16 tmp;
1265 if (!qSafeFromBigEndian(data + off, end, &tmp))
1266 goto end;
1267
1268 p.left_right = uint(tmp) << 16;
1269 if (!qSafeFromBigEndian(data + off + 2, end, &tmp))
1270 goto end;
1271
1272 p.left_right |= tmp;
1273
1274 if (!qSafeFromBigEndian(data + off + 4, end, &tmp))
1275 goto end;
1276
1277 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1278 kerning_pairs.append(p);
1279 off += 6;
1280 }
1281 }
1282 offset += length;
1283 }
1284 }
1285end:
1286 std::sort(kerning_pairs.begin(), kerning_pairs.end());
1287// for (int i = 0; i < kerning_pairs.count(); ++i)
1288// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1289}
1290
1291bool QFontEngine::hasHinting() const
1292{
1293 if (m_hasHinting < 0) {
1294 m_hasHinting = false;
1295
1296 const QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1297 if (maxpTable.size() >= 28) {
1298 const uchar *start = reinterpret_cast<const uchar *>(maxpTable.constData());
1299 const quint32 version = qFromBigEndian<quint32>(start);
1300
1301 if (version == 0x00010000) {
1302 const quint16 maxSizeOfInstructions = qFromBigEndian<quint16>(start + 26);
1303 m_hasHinting = maxSizeOfInstructions > 0;
1304 } else {
1305 // If unrecognized maxp table, assume we might have hinting
1306 qDebug("maxp table of format %d not implemented. Assuming hinting exists.",
1307 version);
1308 m_hasHinting = true;
1309 }
1310 }
1311 }
1312
1313 return m_hasHinting;
1314}
1315
1316int QFontEngine::glyphCount() const
1317{
1318 QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1319 if (maxpTable.size() < 6)
1320 return 0;
1321
1322 const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1323 const uchar *end = source + maxpTable.size();
1324
1325 quint16 count = 0;
1326 qSafeFromBigEndian(source, end, &count);
1327 return count;
1328}
1329
1330Qt::HANDLE QFontEngine::handle() const
1331{
1332 return nullptr;
1333}
1334
1335const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1336{
1337 const uchar *header = table;
1338 const uchar *endPtr = table + tableSize;
1339
1340 // version check
1341 quint16 version;
1342 if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
1343 return nullptr;
1344
1345 quint16 numTables;
1346 if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
1347 return nullptr;
1348
1349 const uchar *maps = table + 4;
1350
1351 enum {
1352 Invalid,
1353 AppleRoman,
1354 Symbol,
1355 Unicode11,
1356 Unicode,
1357 MicrosoftUnicode,
1358 MicrosoftUnicodeExtended
1359 };
1360
1361 int symbolTable = -1;
1362 int tableToUse = -1;
1363 int score = Invalid;
1364 for (int n = 0; n < numTables; ++n) {
1365 quint16 platformId = 0;
1366 if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
1367 return nullptr;
1368
1369 quint16 platformSpecificId = 0;
1370 if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
1371 return nullptr;
1372
1373 switch (platformId) {
1374 case 0: // Unicode
1375 if (score < Unicode &&
1376 (platformSpecificId == 0 ||
1377 platformSpecificId == 2 ||
1378 platformSpecificId == 3)) {
1379 tableToUse = n;
1380 score = Unicode;
1381 } else if (score < Unicode11 && platformSpecificId == 1) {
1382 tableToUse = n;
1383 score = Unicode11;
1384 }
1385 break;
1386 case 1: // Apple
1387 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1388 tableToUse = n;
1389 score = AppleRoman;
1390 }
1391 break;
1392 case 3: // Microsoft
1393 switch (platformSpecificId) {
1394 case 0:
1395 symbolTable = n;
1396 if (score < Symbol) {
1397 tableToUse = n;
1398 score = Symbol;
1399 }
1400 break;
1401 case 1:
1402 if (score < MicrosoftUnicode) {
1403 tableToUse = n;
1404 score = MicrosoftUnicode;
1405 }
1406 break;
1407 case 0xa:
1408 if (score < MicrosoftUnicodeExtended) {
1409 tableToUse = n;
1410 score = MicrosoftUnicodeExtended;
1411 }
1412 break;
1413 default:
1414 break;
1415 }
1416 break;
1417 default:
1418 break;
1419 }
1420 }
1421 if (tableToUse < 0)
1422 return nullptr;
1423
1424resolveTable:
1425 *isSymbolFont = (symbolTable > -1);
1426
1427 quint32 unicode_table = 0;
1428 if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1429 return nullptr;
1430
1431 if (!unicode_table)
1432 return nullptr;
1433
1434 // get the header of the unicode table
1435 header = table + unicode_table;
1436
1437 quint16 format;
1438 if (!qSafeFromBigEndian(header, endPtr, &format))
1439 return nullptr;
1440
1441 quint32 length;
1442 if (format < 8) {
1443 quint16 tmp;
1444 if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1445 return nullptr;
1446 length = tmp;
1447 } else {
1448 if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1449 return nullptr;
1450 }
1451
1452 if (table + unicode_table + length > endPtr)
1453 return nullptr;
1454 *cmapSize = length;
1455
1456 // To support symbol fonts that contain a unicode table for the symbol area
1457 // we check the cmap tables and fall back to symbol font unless that would
1458 // involve losing information from the unicode table
1459 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1460 const uchar *selectedTable = table + unicode_table;
1461
1462 // Check that none of the latin1 range are in the unicode table
1463 bool unicodeTableHasLatin1 = false;
1464 for (int uc=0x00; uc<0x100; ++uc) {
1465 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1466 unicodeTableHasLatin1 = true;
1467 break;
1468 }
1469 }
1470
1471 // Check that at least one symbol char is in the unicode table
1472 bool unicodeTableHasSymbols = false;
1473 if (!unicodeTableHasLatin1) {
1474 for (int uc=0xf000; uc<0xf100; ++uc) {
1475 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1476 unicodeTableHasSymbols = true;
1477 break;
1478 }
1479 }
1480 }
1481
1482 // Fall back to symbol table
1483 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1484 tableToUse = symbolTable;
1485 score = Symbol;
1486 goto resolveTable;
1487 }
1488 }
1489
1490 return table + unicode_table;
1491}
1492
1493quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1494{
1495 const uchar *end = cmap + cmapSize;
1496 quint16 format = 0;
1497 if (!qSafeFromBigEndian(cmap, end, &format))
1498 return 0;
1499
1500 if (format == 0) {
1501 const uchar *ptr = cmap + 6 + unicode;
1502 if (unicode < 256 && ptr < end)
1503 return quint32(*ptr);
1504 } else if (format == 4) {
1505 /* some fonts come with invalid cmap tables, where the last segment
1506 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1507 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1508 by returning 0 for 0xffff
1509 */
1510 if (unicode >= 0xffff)
1511 return 0;
1512
1513 quint16 segCountX2 = 0;
1514 if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1515 return 0;
1516
1517 const unsigned char *ends = cmap + 14;
1518
1519 int i = 0;
1520 for (; i < segCountX2/2; ++i) {
1521 quint16 codePoint = 0;
1522 if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1523 return 0;
1524 if (codePoint >= unicode)
1525 break;
1526 }
1527
1528 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1529
1530 quint16 startIndex = 0;
1531 if (!qSafeFromBigEndian(idx, end, &startIndex))
1532 return 0;
1533 if (startIndex > unicode)
1534 return 0;
1535
1536 idx += segCountX2;
1537
1538 quint16 tmp = 0;
1539 if (!qSafeFromBigEndian(idx, end, &tmp))
1540 return 0;
1541 qint16 idDelta = qint16(tmp);
1542
1543 idx += segCountX2;
1544
1545 quint16 idRangeoffset_t = 0;
1546 if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1547 return 0;
1548
1549 quint16 glyphIndex = 0;
1550 if (idRangeoffset_t) {
1551 quint16 id = 0;
1552 if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1553 return 0;
1554
1555 if (id)
1556 glyphIndex = (idDelta + id) % 0x10000;
1557 else
1558 glyphIndex = 0;
1559 } else {
1560 glyphIndex = (idDelta + unicode) % 0x10000;
1561 }
1562 return glyphIndex;
1563 } else if (format == 6) {
1564 quint16 tableSize = 0;
1565 if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1566 return 0;
1567
1568 quint16 firstCode6 = 0;
1569 if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1570 return 0;
1571 if (unicode < firstCode6)
1572 return 0;
1573
1574 quint16 entryCount6 = 0;
1575 if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1576 return 0;
1577 if (entryCount6 * 2 + 10 > tableSize)
1578 return 0;
1579
1580 quint16 sentinel6 = firstCode6 + entryCount6;
1581 if (unicode >= sentinel6)
1582 return 0;
1583
1584 quint16 entryIndex6 = unicode - firstCode6;
1585
1586 quint16 index = 0;
1587 qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1588 return index;
1589 } else if (format == 12) {
1590 quint32 nGroups = 0;
1591 if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1592 return 0;
1593
1594 cmap += 16; // move to start of groups
1595
1596 int left = 0, right = nGroups - 1;
1597 while (left <= right) {
1598 int middle = left + ( ( right - left ) >> 1 );
1599
1600 quint32 startCharCode = 0;
1601 if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1602 return 0;
1603
1604 if (unicode < startCharCode)
1605 right = middle - 1;
1606 else {
1607 quint32 endCharCode = 0;
1608 if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1609 return 0;
1610
1611 if (unicode <= endCharCode) {
1612 quint32 index = 0;
1613 if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1614 return 0;
1615
1616 return index + unicode - startCharCode;
1617 }
1618 left = middle + 1;
1619 }
1620 }
1621 } else {
1622 qDebug("cmap table of format %d not implemented", format);
1623 }
1624
1625 return 0;
1626}
1627
1628QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1629{
1630 QByteArray f = family;
1631 f.replace(' ', "");
1632 f.replace('(', "");
1633 f.replace(')', "");
1634 f.replace('<', "");
1635 f.replace('>', "");
1636 f.replace('[', "");
1637 f.replace(']', "");
1638 f.replace('{', "");
1639 f.replace('}', "");
1640 f.replace('/', "");
1641 f.replace('%', "");
1642 return f;
1643}
1644
1645// Allow font engines (e.g. Windows) that can not reliably create
1646// outline paths for distance-field rendering to switch the scene
1647// graph over to native text rendering.
1648bool QFontEngine::hasUnreliableGlyphOutline() const
1649{
1650 // Color glyphs (Emoji) are generally not suited for outlining
1651 return glyphFormat == QFontEngine::Format_ARGB;
1652}
1653
1654QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1655{
1656 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1657 glyph_t glyph = glyphs.glyphs[i];
1658 glyph_metrics_t gi = boundingBox(glyph);
1659 if (gi.isValid() && gi.width > 0)
1660 return gi.leftBearing();
1661 }
1662 return 0;
1663}
1664
1665QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1666{
1667 if (glyphs.numGlyphs >= 1) {
1668 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1669 glyph_metrics_t gi = boundingBox(glyph);
1670 if (gi.isValid())
1671 return gi.rightBearing();
1672 }
1673 return 0;
1674}
1675
1676QList<QFontVariableAxis> QFontEngine::variableAxes() const
1677{
1678 return QList<QFontVariableAxis>{};
1679}
1680
1681QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1682{
1683}
1684
1685QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1686 : cache(o.cache)
1687{
1688}
1689
1690QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1691{
1692}
1693
1694QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1695{
1696 cache = o.cache;
1697 return *this;
1698}
1699
1700bool QFontEngine::disableEmojiSegmenter()
1701{
1702#if defined(QT_NO_EMOJISEGMENTER)
1703 return true;
1704#else
1705 static const bool sDisableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
1706 return sDisableEmojiSegmenter;
1707#endif
1708}
1709
1710int QFontEngine::maxCachedGlyphSize()
1711{
1712 static const int maxCachedSize = []{
1713 if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE"))
1714 return env;
1716 }();
1717
1718 return maxCachedSize;
1719}
1720
1721// ------------------------------------------------------------------
1722// The box font engine
1723// ------------------------------------------------------------------
1724
1725QFontEngineBox::QFontEngineBox(int size)
1726 : QFontEngine(Box),
1727 _size(size)
1728{
1729 cache_cost = sizeof(QFontEngineBox);
1730}
1731
1732QFontEngineBox::QFontEngineBox(Type type, int size)
1733 : QFontEngine(type),
1734 _size(size)
1735{
1736 cache_cost = sizeof(QFontEngineBox);
1737}
1738
1739QFontEngineBox::~QFontEngineBox()
1740{
1741}
1742
1743glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1744{
1745 Q_UNUSED(ucs4);
1746 return 1;
1747}
1748
1749int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1750{
1751 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1752 if (*nglyphs < len) {
1753 *nglyphs = len;
1754 return -1;
1755 }
1756
1757 int ucs4Length = 0;
1758 QStringIterator it(str, str + len);
1759 while (it.hasNext()) {
1760 it.advance();
1761 glyphs->glyphs[ucs4Length++] = 1;
1762 }
1763
1764 *nglyphs = ucs4Length;
1765 glyphs->numGlyphs = ucs4Length;
1766
1767 if (!(flags & GlyphIndicesOnly))
1768 recalcAdvances(glyphs, flags);
1769
1770 return *nglyphs;
1771}
1772
1773void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1774{
1775 for (int i = 0; i < glyphs->numGlyphs; i++)
1776 glyphs->advances[i] = _size;
1777}
1778
1779void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1780{
1781 if (!glyphs.numGlyphs)
1782 return;
1783
1784 QVarLengthArray<QFixedPoint> positions;
1785 QVarLengthArray<glyph_t> positioned_glyphs;
1786 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1787 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1788
1789 QSize s(_size - 3, _size - 3);
1790 for (int k = 0; k < positions.size(); k++)
1791 path->addRect(QRectF(positions[k].toPointF(), s));
1792}
1793
1794glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1795{
1796 glyph_metrics_t overall;
1797 overall.width = _size*glyphs.numGlyphs;
1798 overall.height = _size;
1799 overall.xoff = overall.width;
1800 return overall;
1801}
1802
1803void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1804{
1805 if (!ti.glyphs.numGlyphs)
1806 return;
1807
1808 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1809 QSize s(_size - 3, _size - 3);
1810
1811 QVarLengthArray<QFixedPoint> positions;
1812 QVarLengthArray<glyph_t> glyphs;
1813 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1814 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1815 if (glyphs.size() == 0)
1816 return;
1817
1818
1819 QPainter *painter = p->painter();
1820 painter->save();
1821 painter->setBrush(Qt::NoBrush);
1822 QPen pen = painter->pen();
1823 pen.setWidthF(lineThickness().toReal());
1824 painter->setPen(pen);
1825 for (int k = 0; k < positions.size(); k++)
1826 painter->drawRect(QRectF(positions[k].toPointF(), s));
1827 painter->restore();
1828}
1829
1830glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1831{
1832 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1833}
1834
1835QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1836{
1837 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1838 return fe;
1839}
1840
1841QFixed QFontEngineBox::ascent() const
1842{
1843 return _size;
1844}
1845
1846QFixed QFontEngineBox::capHeight() const
1847{
1848 return _size;
1849}
1850
1851QFixed QFontEngineBox::descent() const
1852{
1853 return 0;
1854}
1855
1856QFixed QFontEngineBox::leading() const
1857{
1858 QFixed l = _size * QFixed::fromReal(qreal(0.15));
1859 return l.ceil();
1860}
1861
1862qreal QFontEngineBox::maxCharWidth() const
1863{
1864 return _size;
1865}
1866
1867bool QFontEngineBox::canRender(const QChar *, int) const
1868{
1869 return true;
1870}
1871
1872QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1873{
1874 QImage image(_size, _size, QImage::Format_Alpha8);
1875 image.fill(0);
1876
1877 uchar *bits = image.bits();
1878 for (int i=2; i <= _size-3; ++i) {
1879 bits[i + 2 * image.bytesPerLine()] = 255;
1880 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1881 bits[2 + i * image.bytesPerLine()] = 255;
1882 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1883 }
1884 return image;
1885}
1886
1887// ------------------------------------------------------------------
1888// Multi engine
1889// ------------------------------------------------------------------
1890
1891uchar QFontEngineMulti::highByte(glyph_t glyph)
1892{ return glyph >> 24; }
1893
1894// strip high byte from glyph
1895static inline glyph_t stripped(glyph_t glyph)
1896{ return glyph & 0x00ffffff; }
1897
1898QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1899 : QFontEngine(Multi),
1900 m_fallbackFamilies(fallbackFamilies),
1901 m_script(script),
1902 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1903{
1904 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1905
1906 if (m_fallbackFamilies.isEmpty()) {
1907 // defer obtaining the fallback families until loadEngine(1)
1908 m_fallbackFamilies << QString();
1909 }
1910
1911 m_engines.resize(m_fallbackFamilies.size() + 1);
1912
1913 engine->ref.ref();
1914 m_engines[0] = engine;
1915
1916 fontDef = engine->fontDef;
1917 cache_cost = engine->cache_cost;
1918}
1919
1920QFontEngineMulti::~QFontEngineMulti()
1921{
1922 for (int i = 0; i < m_engines.size(); ++i) {
1923 QFontEngine *fontEngine = m_engines.at(i);
1924 if (fontEngine && !fontEngine->ref.deref())
1925 delete fontEngine;
1926 }
1927}
1928
1929QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
1930
1931void QFontEngineMulti::ensureFallbackFamiliesQueried()
1932{
1933 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
1934 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1935 styleHint = QFont::TypeWriter;
1936
1937 setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family(),
1938 QFont::Style(fontDef.style), styleHint,
1939 QFontDatabasePrivate::ExtendedScript(m_script)));
1940}
1941
1942void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
1943{
1944 Q_ASSERT(!m_fallbackFamiliesQueried);
1945
1946 m_fallbackFamilies = fallbackFamilies;
1947 if (m_fallbackFamilies.isEmpty()) {
1948 // turns out we lied about having any fallback at all
1949 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
1950 QFontEngine *engine = m_engines.at(0);
1951 engine->ref.ref();
1952 m_engines[1] = engine;
1953 m_fallbackFamilies << fontDef.family();
1954 } else {
1955 m_engines.resize(m_fallbackFamilies.size() + 1);
1956 }
1957
1958 m_fallbackFamiliesQueried = true;
1959}
1960
1961void QFontEngineMulti::ensureEngineAt(int at)
1962{
1963 if (!m_fallbackFamiliesQueried && at > 0)
1964 ensureFallbackFamiliesQueried();
1965 Q_ASSERT(at < m_engines.size());
1966 if (!m_engines.at(at)) {
1967 QFontEngine *engine = loadEngine(at);
1968 if (!engine)
1969 engine = new QFontEngineBox(fontDef.pixelSize);
1970 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1971 engine->ref.ref();
1972 m_engines[at] = engine;
1973 }
1974}
1975
1976QFontEngine *QFontEngineMulti::loadEngine(int at)
1977{
1978 QFontDef request(fontDef);
1979 request.styleStrategy |= QFont::NoFontMerging;
1980 request.families = QStringList(fallbackFamilyAt(at - 1));
1981
1982 // At this point, the main script of the text has already been considered
1983 // when fetching the list of fallback families from the database, and the
1984 // info about the actual script of the characters may have been discarded,
1985 // so we do not check for writing system support, but instead just load
1986 // the family indiscriminately.
1987 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
1988 engine->fontDef.weight = request.weight;
1989 if (request.style > QFont::StyleNormal)
1990 engine->fontDef.style = request.style;
1991 return engine;
1992 }
1993
1994 return nullptr;
1995}
1996
1997int QFontEngineMulti::glyphCount() const
1998{
1999 return engine(0)->glyphCount();
2000}
2001
2002glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
2003{
2004 glyph_t glyph = engine(0)->glyphIndex(ucs4);
2005 if (glyph == 0 && !isIgnorableChar(ucs4)) {
2006 if (!m_fallbackFamiliesQueried)
2007 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2008 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
2009 QFontEngine *engine = m_engines.at(x);
2010 if (!engine) {
2011 if (!shouldLoadFontEngineForCharacter(x, ucs4))
2012 continue;
2013 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2014 engine = m_engines.at(x);
2015 }
2016 Q_ASSERT(engine != nullptr);
2017 if (engine->type() == Box)
2018 continue;
2019
2020 glyph = engine->glyphIndex(ucs4);
2021 if (glyph != 0) {
2022 // set the high byte to indicate which engine the glyph came from
2023 glyph |= (x << 24);
2024 break;
2025 }
2026 }
2027 }
2028
2029 return glyph;
2030}
2031
2032QString QFontEngineMulti::glyphName(glyph_t glyph) const
2033{
2034 const int which = highByte(glyph);
2035 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(which);
2036 return engine(which)->glyphName(stripped(glyph));
2037}
2038
2039glyph_t QFontEngineMulti::findGlyph(QLatin1StringView name) const
2040{
2041 return engine(0)->findGlyph(name);
2042}
2043
2044int QFontEngineMulti::stringToCMap(const QChar *str, int len,
2045 QGlyphLayout *glyphs, int *nglyphs,
2046 QFontEngine::ShaperFlags flags) const
2047{
2048 qCDebug(lcTextFontCmap) << "Mapping characters in"
2049 << QString(str, len)
2050 << "; Shaper flags:" << flags;
2051
2052 const int originalNumGlyphs = glyphs->numGlyphs;
2053 int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
2054 qCDebug(lcTextFontCmap) << " Main font mapped"
2055 << mappedGlyphCount
2056 << "of"
2057 << (nglyphs != nullptr ? *nglyphs : 0)
2058 << "characters";
2059
2060 if (mappedGlyphCount < 0)
2061 return -1;
2062
2063 // If ContextFontMerging is set and the match for the string was incomplete, we try all
2064 // fallbacks on the full string until we find the best match.
2065 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
2066 if (contextFontMerging) {
2067 qCDebug(lcTextFontCmap) << " Context font merging, first pass";
2068
2069 QVarLengthGlyphLayoutArray tempLayout(len);
2070 if (!m_fallbackFamiliesQueried)
2071 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2072
2073 int maxGlyphCount = 0;
2074 uchar engineIndex = 0;
2075 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
2076 int numGlyphs = len;
2077 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2078 maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
2079 qCDebug(lcTextFontCmap) << " Fallback" << x << "mapped" << maxGlyphCount;
2080
2081 // If we found a better match, we copy data into the main QGlyphLayout
2082 if (maxGlyphCount > mappedGlyphCount) {
2083 qCDebug(lcTextFontCmap) << " Better match!";
2084 *nglyphs = numGlyphs;
2085 glyphs->numGlyphs = originalNumGlyphs;
2086 glyphs->copy(&tempLayout);
2087 engineIndex = x;
2088 if (maxGlyphCount == numGlyphs)
2089 break;
2090 } else {
2091 qCDebug(lcTextFontCmap) << " No improvement";
2092 }
2093 }
2094
2095 if (engineIndex > 0) {
2096 for (int y = 0; y < glyphs->numGlyphs; ++y) {
2097 if (glyphs->glyphs[y] != 0)
2098 glyphs->glyphs[y] |= (engineIndex << 24);
2099 }
2100 } else {
2101 contextFontMerging = false;
2102 }
2103
2104 mappedGlyphCount = maxGlyphCount;
2105 }
2106
2107 // Fill in missing glyphs by going through string one character at the time and finding
2108 // the first viable fallback.
2109 int glyph_pos = 0;
2110 QStringIterator it(str, str + len);
2111
2112 const bool enableVariationSelectorHack = disableEmojiSegmenter();
2113 char32_t previousUcs4 = 0;
2114
2115 int lastFallback = -1;
2116 while (it.hasNext()) {
2117 const char32_t ucs4 = it.peekNext();
2118
2119 // If we applied a fallback font to previous glyph, and the current is either
2120 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
2121 // to get the correct shaping rules applied.
2122 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
2123 QFontEngine *engine = m_engines.at(lastFallback);
2124 glyph_t glyph = engine->glyphIndex(ucs4);
2125 if (glyph != 0) {
2126 qCDebug(lcTextFontCmap) << " Applying ZWJ/ZWNJ special case for"
2127 << QString::number(ucs4, 16)
2128 << ", lastFallback:" << lastFallback;
2129
2130 glyphs->glyphs[glyph_pos] = glyph;
2131 if (!(flags & GlyphIndicesOnly)) {
2132 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2133 engine->recalcAdvances(&g, flags);
2134 }
2135
2136 // set the high byte to indicate which engine the glyph came from
2137 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
2138 } else {
2139 lastFallback = -1;
2140 }
2141 } else {
2142 lastFallback = -1;
2143 }
2144
2145 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
2146 qCDebug(lcTextFontCmap) << " Unmapped character"
2147 << QString::number(ucs4, 16);
2148
2149 if (!m_fallbackFamiliesQueried)
2150 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2151 for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
2152 QFontEngine *engine = m_engines.at(x);
2153 if (!engine) {
2154 if (!shouldLoadFontEngineForCharacter(x, ucs4)) {
2155 qCDebug(lcTextFontCmap) << " Skipping fallback #" << x;
2156 continue;
2157 }
2158 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2159 engine = m_engines.at(x);
2160 if (!engine)
2161 continue;
2162 }
2163 Q_ASSERT(engine != nullptr);
2164 if (engine->type() == Box)
2165 continue;
2166
2167 glyph_t glyph = engine->glyphIndex(ucs4);
2168 qCDebug(lcTextFontCmap) << " Fallback #" << x
2169 << ", family:" << engine->fontDef.families
2170 << ", styleName:" << engine->fontDef.styleName
2171 << ", style:" << engine->fontDef.style
2172 << ", weight:" << engine->fontDef.weight
2173 << ", glyph mapping:" << glyph;
2174 if (glyph != 0) {
2175 glyphs->glyphs[glyph_pos] = glyph;
2176 if (!(flags & GlyphIndicesOnly)) {
2177 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2178 engine->recalcAdvances(&g, flags);
2179 }
2180
2181 lastFallback = x;
2182
2183 // set the high byte to indicate which engine the glyph came from
2184 glyphs->glyphs[glyph_pos] |= (x << 24);
2185 break;
2186 }
2187 }
2188
2189 // For variant-selectors, they are modifiers to the previous character. If we
2190 // end up with different font selections for the selector and the character it
2191 // modifies, we try applying the selector font to the preceding character as well
2192 const int variantSelectorBlock = 0xFE00;
2193 if (enableVariationSelectorHack && (ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
2194 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
2195 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
2196
2197 if (selectorFontEngine != precedingCharacterFontEngine) {
2198 // Emoji variant selectors are specially handled and should affect font
2199 // selection. If VS-16 is used, then this means we want to select a color
2200 // font. If the selected font is already a color font, we do not need search
2201 // again. If the VS-15 is used, then this means we want to select a non-color
2202 // font. If the selected font is not a color font, we don't do anything.
2203 const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
2204 const bool colorFont = selectedEngine->isColorFont();
2205 const char32_t vs15 = 0xFE0E;
2206 const char32_t vs16 = 0xFE0F;
2207 bool adaptVariantSelector = ucs4 < vs15
2208 || (ucs4 == vs15 && colorFont)
2209 || (ucs4 == vs16 && !colorFont);
2210
2211 if (adaptVariantSelector) {
2212 QFontEngine *engine = m_engines.at(selectorFontEngine);
2213 glyph_t glyph = engine->glyphIndex(previousUcs4);
2214 if (glyph != 0) {
2215 glyphs->glyphs[glyph_pos - 1] = glyph;
2216 if (!(flags & GlyphIndicesOnly)) {
2217 QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
2218 engine->recalcAdvances(&g, flags);
2219 }
2220
2221 // set the high byte to indicate which engine the glyph came from
2222 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
2223 }
2224 }
2225 }
2226 }
2227 }
2228
2229 it.advance();
2230 ++glyph_pos;
2231
2232 previousUcs4 = ucs4;
2233 }
2234
2235 *nglyphs = glyph_pos;
2236 glyphs->numGlyphs = glyph_pos;
2237 return mappedGlyphCount;
2238}
2239
2240bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
2241{
2242 Q_UNUSED(at);
2243 Q_UNUSED(ucs4);
2244 return true;
2245}
2246
2247glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2248{
2249 if (glyphs.numGlyphs <= 0)
2250 return glyph_metrics_t();
2251
2252 glyph_metrics_t overall;
2253
2254 int which = highByte(glyphs.glyphs[0]);
2255 int start = 0;
2256 int end, i;
2257 for (end = 0; end < glyphs.numGlyphs; ++end) {
2258 const int e = highByte(glyphs.glyphs[end]);
2259 if (e == which)
2260 continue;
2261
2262 // set the high byte to zero
2263 for (i = start; i < end; ++i)
2264 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2265
2266 // merge the bounding box for this run
2267 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2268
2269 overall.x = qMin(overall.x, gm.x);
2270 overall.y = qMin(overall.y, gm.y);
2271 overall.width = overall.xoff + gm.width;
2272 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2273 qMin(overall.y, gm.y);
2274 overall.xoff += gm.xoff;
2275 overall.yoff += gm.yoff;
2276
2277 // reset the high byte for all glyphs
2278 const int hi = which << 24;
2279 for (i = start; i < end; ++i)
2280 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2281
2282 // change engine
2283 start = end;
2284 which = e;
2285 }
2286
2287 // set the high byte to zero
2288 for (i = start; i < end; ++i)
2289 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2290
2291 // merge the bounding box for this run
2292 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2293
2294 overall.x = qMin(overall.x, gm.x);
2295 overall.y = qMin(overall.y, gm.y);
2296 overall.width = overall.xoff + gm.width;
2297 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2298 qMin(overall.y, gm.y);
2299 overall.xoff += gm.xoff;
2300 overall.yoff += gm.yoff;
2301
2302 // reset the high byte for all glyphs
2303 const int hi = which << 24;
2304 for (i = start; i < end; ++i)
2305 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2306
2307 return overall;
2308}
2309
2310void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2311{
2312 int which = highByte(glyph);
2313 ensureEngineAt(which);
2314 engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2315}
2316
2317void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2318 QPainterPath *path, QTextItem::RenderFlags flags)
2319{
2320 if (glyphs.numGlyphs <= 0)
2321 return;
2322
2323 int which = highByte(glyphs.glyphs[0]);
2324 int start = 0;
2325 int end, i;
2326 if (flags & QTextItem::RightToLeft) {
2327 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2328 x += glyphs.advances[gl].toReal();
2329 }
2330 for (end = 0; end < glyphs.numGlyphs; ++end) {
2331 const int e = highByte(glyphs.glyphs[end]);
2332 if (e == which)
2333 continue;
2334
2335 if (flags & QTextItem::RightToLeft) {
2336 for (i = start; i < end; ++i)
2337 x -= glyphs.advances[i].toReal();
2338 }
2339
2340 // set the high byte to zero
2341 for (i = start; i < end; ++i)
2342 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2343 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2344 // reset the high byte for all glyphs and update x and y
2345 const int hi = which << 24;
2346 for (i = start; i < end; ++i)
2347 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2348
2349 if (!(flags & QTextItem::RightToLeft)) {
2350 for (i = start; i < end; ++i)
2351 x += glyphs.advances[i].toReal();
2352 }
2353
2354 // change engine
2355 start = end;
2356 which = e;
2357 }
2358
2359 if (flags & QTextItem::RightToLeft) {
2360 for (i = start; i < end; ++i)
2361 x -= glyphs.advances[i].toReal();
2362 }
2363
2364 // set the high byte to zero
2365 for (i = start; i < end; ++i)
2366 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2367
2368 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2369
2370 // reset the high byte for all glyphs
2371 const int hi = which << 24;
2372 for (i = start; i < end; ++i)
2373 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2374}
2375
2376void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2377{
2378 if (glyphs->numGlyphs <= 0)
2379 return;
2380
2381 int which = highByte(glyphs->glyphs[0]);
2382 int start = 0;
2383 int end, i;
2384 for (end = 0; end < glyphs->numGlyphs; ++end) {
2385 const int e = highByte(glyphs->glyphs[end]);
2386 if (e == which)
2387 continue;
2388
2389 // set the high byte to zero
2390 for (i = start; i < end; ++i)
2391 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2392
2393 QGlyphLayout offs = glyphs->mid(start, end - start);
2394 engine(which)->recalcAdvances(&offs, flags);
2395
2396 // reset the high byte for all glyphs and update x and y
2397 const int hi = which << 24;
2398 for (i = start; i < end; ++i)
2399 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2400
2401 // change engine
2402 start = end;
2403 which = e;
2404 }
2405
2406 // set the high byte to zero
2407 for (i = start; i < end; ++i)
2408 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2409
2410 QGlyphLayout offs = glyphs->mid(start, end - start);
2411 engine(which)->recalcAdvances(&offs, flags);
2412
2413 // reset the high byte for all glyphs
2414 const int hi = which << 24;
2415 for (i = start; i < end; ++i)
2416 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2417}
2418
2419void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2420{
2421 if (glyphs->numGlyphs <= 0)
2422 return;
2423
2424 int which = highByte(glyphs->glyphs[0]);
2425 int start = 0;
2426 int end, i;
2427 for (end = 0; end < glyphs->numGlyphs; ++end) {
2428 const int e = highByte(glyphs->glyphs[end]);
2429 if (e == which)
2430 continue;
2431
2432 // set the high byte to zero
2433 for (i = start; i < end; ++i)
2434 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2435
2436 QGlyphLayout offs = glyphs->mid(start, end - start);
2437 engine(which)->doKerning(&offs, flags);
2438
2439 // reset the high byte for all glyphs and update x and y
2440 const int hi = which << 24;
2441 for (i = start; i < end; ++i)
2442 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2443
2444 // change engine
2445 start = end;
2446 which = e;
2447 }
2448
2449 // set the high byte to zero
2450 for (i = start; i < end; ++i)
2451 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2452
2453 QGlyphLayout offs = glyphs->mid(start, end - start);
2454 engine(which)->doKerning(&offs, flags);
2455
2456 // reset the high byte for all glyphs
2457 const int hi = which << 24;
2458 for (i = start; i < end; ++i)
2459 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2460}
2461
2462glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2463{
2464 const int which = highByte(glyph);
2465 return engine(which)->boundingBox(stripped(glyph));
2466}
2467
2468QFixed QFontEngineMulti::emSquareSize() const
2469{ return engine(0)->emSquareSize(); }
2470
2471QFixed QFontEngineMulti::ascent() const
2472{ return engine(0)->ascent(); }
2473
2474QFixed QFontEngineMulti::capHeight() const
2475{ return engine(0)->capHeight(); }
2476
2477QFixed QFontEngineMulti::descent() const
2478{ return engine(0)->descent(); }
2479
2480QFixed QFontEngineMulti::leading() const
2481{
2482 return engine(0)->leading();
2483}
2484
2485QFixed QFontEngineMulti::xHeight() const
2486{
2487 return engine(0)->xHeight();
2488}
2489
2490QFixed QFontEngineMulti::averageCharWidth() const
2491{
2492 return engine(0)->averageCharWidth();
2493}
2494
2495QFixed QFontEngineMulti::lineThickness() const
2496{
2497 return engine(0)->lineThickness();
2498}
2499
2500QFixed QFontEngineMulti::underlinePosition() const
2501{
2502 return engine(0)->underlinePosition();
2503}
2504
2505qreal QFontEngineMulti::maxCharWidth() const
2506{
2507 return engine(0)->maxCharWidth();
2508}
2509
2510qreal QFontEngineMulti::minLeftBearing() const
2511{
2512 return engine(0)->minLeftBearing();
2513}
2514
2515qreal QFontEngineMulti::minRightBearing() const
2516{
2517 return engine(0)->minRightBearing();
2518}
2519
2520bool QFontEngineMulti::canRender(const QChar *string, int len) const
2521{
2522 if (engine(0)->canRender(string, len))
2523 return true;
2524
2525 int nglyphs = len;
2526
2527 QVarLengthArray<glyph_t> glyphs(nglyphs);
2528
2529 QGlyphLayout g;
2530 g.numGlyphs = nglyphs;
2531 g.glyphs = glyphs.data();
2532 if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2533 Q_UNREACHABLE();
2534
2535 for (int i = 0; i < nglyphs; i++) {
2536 if (glyphs[i] == 0)
2537 return false;
2538 }
2539
2540 return true;
2541}
2542
2543/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2544 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2545
2546QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2547{
2548 const int which = highByte(glyph);
2549 return engine(which)->alphaMapForGlyph(stripped(glyph));
2550}
2551
2552QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2553{
2554 const int which = highByte(glyph);
2555 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2556}
2557
2558QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2559{
2560 const int which = highByte(glyph);
2561 return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2562}
2563
2564QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2565 const QFixedPoint &subPixelPosition,
2566 const QTransform &t)
2567{
2568 const int which = highByte(glyph);
2569 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2570}
2571
2572QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2573 const QFixedPoint &subPixelPosition,
2574 const QTransform &t)
2575{
2576 const int which = highByte(glyph);
2577 return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2578}
2579
2580QList<QFontVariableAxis> QFontEngineMulti::variableAxes() const
2581{
2582 return engine(0)->variableAxes();
2583}
2584
2585/*
2586 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2587
2588 The purpose of this is to provide the necessary font fallbacks when drawing complex
2589 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2590 the same raw font over and over again, we want to cache the corresponding multi font engine
2591 as it may contain fallback font engines already.
2592*/
2593QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2594{
2595 QFontEngine *engine = nullptr;
2596 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2597 QFontCache *fc = QFontCache::instance();
2598 // We can't rely on the fontDef (and hence the cache Key)
2599 // alone to distinguish webfonts, since these should not be
2600 // accidentally shared, even if the resulting fontcache key
2601 // is strictly identical. See:
2602 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2603 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2604 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2605 end = fc->engineCache.end();
2606 while (it != end && it.key() == key) {
2607 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2608 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2609 if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2610 engine = cachedEngine;
2611 fc->updateHitCountAndTimeStamp(it.value());
2612 break;
2613 }
2614 ++it;
2615 }
2616 if (!engine) {
2617 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
2618 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2619 }
2620 Q_ASSERT(engine);
2621 return engine;
2622}
2623
2624QTestFontEngine::QTestFontEngine(int size)
2625 : QFontEngineBox(TestFontEngine, size)
2626{}
2627
2628QT_END_NAMESPACE
friend class QFontEngine
Definition qpainter.h:433
Combined button and popup list for selecting options.
QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script)
static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
static bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *output)
static QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
#define SET(x, y)
static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
static glyph_t stripped(glyph_t glyph)
Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
#define kBearingNotInitialized
#define QT_MAX_CACHED_GLYPH_SIZE
#define GRID(x, y)
static bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
#define kMinLeftSideBearingOffset
#define q16Dot16ToFloat(i)
@ EdgeUp
@ EdgeRight
@ EdgeDown
@ EdgeLeft
#define kMinRightSideBearingOffset