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 if (t.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(glyph, t))
855 return QImage();
856
857 QImage i = alphaMapForGlyph(glyph);
858 if (t.type() > QTransform::TxTranslate)
859 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
860 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
861
862 return i;
863}
864
865QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t)
866{
867 if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions())
868 return alphaMapForGlyph(glyph, t);
869
870 if (t.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(glyph, t))
871 return QImage();
872
873 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
874 if (t.type() > QTransform::TxTranslate)
875 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
876 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
877
878 return i;
879}
880
881QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &t)
882{
883 const QImage alphaMask = alphaMapForGlyph(glyph, t);
884 QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
885
886 for (int y=0; y<alphaMask.height(); ++y) {
887 uint *dst = (uint *) rgbMask.scanLine(y);
888 const uchar *src = alphaMask.constScanLine(y);
889 for (int x=0; x<alphaMask.width(); ++x) {
890 int val = src[x];
891 dst[x] = qRgb(val, val, val);
892 }
893 }
894
895 return rgbMask;
896}
897
898QImage QFontEngine::bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform&, const QColor &)
899{
900 Q_UNUSED(subPixelPosition);
901
902 return QImage();
903}
904
905QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const
906{
907 if (m_subPixelPositionCount <= 1
908 || (!supportsHorizontalSubPixelPositions()
909 && !supportsVerticalSubPixelPositions())) {
910 return QFixedPoint();
911 }
912
913 auto f = [&](QFixed v) {
914 if (v != 0) {
915 v = v - v.floor() + QFixed::fromFixed(1);
916 QFixed fraction = (v * m_subPixelPositionCount).floor();
917 v = fraction / QFixed(m_subPixelPositionCount);
918 }
919 return v;
920 };
921
922 return QFixedPoint(f(position.x), f(position.y));
923}
924
925QFontEngine::Glyph *QFontEngine::glyphData(glyph_t,
926 const QFixedPoint &,
927 QFontEngine::GlyphFormat,
928 const QTransform &)
929{
930 return nullptr;
931}
932
933#if QT_CONFIG(harfbuzz)
934template <typename Functor>
935auto queryHarfbuzz(const QFontEngine *engine, Functor &&func)
936{
937 decltype(func(nullptr)) result = {};
938
939 hb_face_t *hbFace = hb_qt_face_get_for_engine(const_cast<QFontEngine *>(engine));
940 if (hb_font_t *hbFont = hb_font_create(hbFace)) {
941 // explicitly use OpenType handlers for this font
942 hb_ot_font_set_funcs(hbFont);
943
944 result = func(hbFont);
945
946 hb_font_destroy(hbFont);
947 }
948
949 return result;
950}
951#endif
952
953QString QFontEngine::glyphName(glyph_t index) const
954{
955 QString result;
956 if (index >= glyph_t(glyphCount()))
957 return result;
958
959#if QT_CONFIG(harfbuzz)
960 result = queryHarfbuzz(this, [index](hb_font_t *hbFont){
961 QString result;
962 // According to the OpenType specification, glyph names are limited to 63
963 // characters and can only contain (a subset of) ASCII.
964 char name[64];
965 if (hb_font_get_glyph_name(hbFont, index, name, sizeof(name)))
966 result = QString::fromLatin1(name);
967 return result;
968 });
969#endif
970
971 if (result.isEmpty())
972 result = index ? u"gid%1"_s.arg(index) : u".notdef"_s;
973 return result;
974}
975
976glyph_t QFontEngine::findGlyph(QLatin1StringView name) const
977{
978 glyph_t result = 0;
979
980#if QT_CONFIG(harfbuzz)
981 result = queryHarfbuzz(this, [name](hb_font_t *hbFont){
982 // glyph names are all ASCII, so latin1 is fine here.
983 hb_codepoint_t glyph;
984 if (hb_font_get_glyph_from_name(hbFont, name.constData(), name.size(), &glyph))
985 return glyph_t(glyph);
986 return glyph_t(0);
987 });
988#else // if we are here, no point in trying again if we already tried harfbuzz
989 if (!result) {
990 for (glyph_t index = 0; index < uint(glyphCount()); ++index) {
991 if (name == glyphName(index))
992 return index;
993 }
994 }
995#endif
996
997 if (!result) {
998 constexpr auto gid = "gid"_L1;
999 constexpr auto uni = "uni"_L1;
1000 if (name.startsWith(gid)) {
1001 bool ok;
1002 result = name.slice(gid.size()).toUInt(&ok);
1003 if (ok && result < glyph_t(glyphCount()))
1004 return result;
1005 } else if (name.startsWith(uni)) {
1006 bool ok;
1007 const uint ucs4 = name.slice(uni.size()).toUInt(&ok, 16);
1008 if (ok) {
1009 result = glyphIndex(ucs4);
1010 if (result > 0 && result < glyph_t(glyphCount()))
1011 return result;
1012 }
1013 }
1014 }
1015
1016 return result;
1017}
1018
1019// Maximum glyph size (in pixels per side) we are willing to rasterize. Defaults
1020// to 4096; overridable via QT_MAX_GLYPH_SIZE, where a value <= 0 disables the check.
1021static int maxGlyphSize()
1022{
1023 static const int size = []() {
1024 bool ok = false;
1025 const int value = qEnvironmentVariableIntValue("QT_MAX_GLYPH_SIZE", &ok);
1026 return ok ? value : 4096;
1027 }();
1028 return size;
1029}
1030
1031qreal QFontEngine::effectivePixelSizeSquared(const QTransform &transform) const
1032{
1033 return fontDef.pixelSize * fontDef.pixelSize * qAbs(transform.determinant());
1034}
1035
1036bool QFontEngine::glyphBoundingBoxExceedsLimit(qreal glyphWidth,
1037 qreal glyphHeight,
1038 qreal effectivePixelSizeSquared)
1039{
1040 const int limit = maxGlyphSize();
1041 if (limit <= 0) // the check is disabled
1042 return false;
1043
1044 const qreal limitSquared = qreal(limit) * limit;
1045
1046 // The font size itself is beyond what we are willing to rasterize.
1047 bool hasExceeded = (effectivePixelSizeSquared > limitSquared);
1048
1049 if (!hasExceeded) {
1050 // Otherwise a glyph may extend up to 16x the effective pixel size. We never reject sizes less
1051 // than 256 pixels on this basis (to leave room for oversized glyphs at small sizes). This
1052 // leaves space for glyphs that stretch outside the em square while still protecting against
1053 // dramatically malformed glyphs.
1054 constexpr qreal glyphSizeFactorSquared = 16 * 16;
1055 constexpr qreal glyphSizeFloorSquared = 256 * 256;
1056 const qreal maxDimensionSquared =
1057 qMin(limitSquared,
1058 qMax(glyphSizeFloorSquared, glyphSizeFactorSquared * effectivePixelSizeSquared));
1059
1060 hasExceeded = glyphWidth * glyphWidth > maxDimensionSquared
1061 || glyphHeight * glyphHeight > maxDimensionSquared;
1062 }
1063
1064 if (hasExceeded) {
1065 static bool warned = false;
1066 if (!warned) {
1067 warned = true;
1068 qCWarning(lcFontMatch,
1069 "Glyph with bounding box (%.0fx%.0f pixels) and requested effective pixel "
1070 "size of %.0f is too large for rasterizer. Set QT_MAX_GLYPH_SIZE=0 to "
1071 "disable the limit.",
1072 glyphWidth, glyphHeight, sqrt(effectivePixelSizeSquared));
1073 }
1074 }
1075
1076 return hasExceeded;
1077}
1078
1079bool QFontEngine::glyphTransformExceedsLimit(glyph_t glyph, const QTransform &transform)
1080{
1081 const glyph_metrics_t gm = boundingBox(glyph, transform);
1082 return glyphBoundingBoxExceedsLimit(gm.width.toReal(), gm.height.toReal(),
1083 effectivePixelSizeSquared(transform));
1084}
1085
1086QImage QFontEngine::renderedPathForGlyph(glyph_t glyph, const QColor &color)
1087{
1088 glyph_metrics_t gm = boundingBox(glyph);
1089 int glyph_x = qFloor(gm.x.toReal());
1090 int glyph_y = qFloor(gm.y.toReal());
1091 int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
1092 int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
1093
1094 if (glyph_width <= 0
1095 || glyph_height <= 0
1096 || glyphBoundingBoxExceedsLimit(glyph_width,
1097 glyph_height,
1098 fontDef.pixelSize * fontDef.pixelSize)) {
1099 return QImage();
1100 }
1101
1102 QFixedPoint pt;
1103 pt.x = -glyph_x;
1104 pt.y = -glyph_y; // the baseline
1105 QPainterPath path;
1106 path.setFillRule(Qt::WindingFill);
1107 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
1108 im.fill(Qt::transparent);
1109 QPainter p(&im);
1110 p.setRenderHint(QPainter::Antialiasing);
1111 addGlyphsToPath(&glyph, &pt, 1, &path, { });
1112 p.setPen(Qt::NoPen);
1113 p.setBrush(color);
1114 p.drawPath(path);
1115 p.end();
1116
1117 return im;
1118}
1119
1120QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
1121{
1122 QImage im = renderedPathForGlyph(glyph, Qt::black);
1123 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
1124
1125 for (int y=0; y<im.height(); ++y) {
1126 uchar *dst = (uchar *) alphaMap.scanLine(y);
1127 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
1128 for (int x=0; x<im.width(); ++x)
1129 dst[x] = qAlpha(src[x]);
1130 }
1131
1132 return alphaMap;
1133}
1134
1135void QFontEngine::removeGlyphFromCache(glyph_t)
1136{
1137}
1138
1139QFontEngine::Properties QFontEngine::properties() const
1140{
1141 Properties p;
1142 p.postscriptName =
1143 QFontEngine::convertToPostscriptFontFamilyName(fontDef.family().toUtf8()) + '-'
1144 + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight);
1145 p.ascent = ascent();
1146 p.descent = descent();
1147 p.leading = leading();
1148 p.emSquare = p.ascent;
1149 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
1150 p.italicAngle = 0;
1151 p.capHeight = p.ascent;
1152 p.lineWidth = lineThickness();
1153 return p;
1154}
1155
1156void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1157{
1158 *metrics = boundingBox(glyph);
1159 QFixedPoint p;
1160 p.x = 0;
1161 p.y = 0;
1162 addGlyphsToPath(&glyph, &p, 1, path, QFlag(0));
1163}
1164
1165/*!
1166 \internal
1167 Returns \c true if the font table idetified by \a tag exists in the font;
1168 returns \c false otherwise.
1169
1170 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
1171 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
1172 of the buffer, passed in \a length, is sufficient to store the font table data,
1173 also copies the font table data to \a buffer.
1174
1175 Note: returning \c false when the font table exists could lead to an undefined behavior.
1176*/
1177bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1178{
1179 Q_UNUSED(tag);
1180 Q_UNUSED(buffer);
1181 Q_UNUSED(length);
1182 return false;
1183}
1184
1185QByteArray QFontEngine::getSfntTable(uint tag) const
1186{
1187 QByteArray table;
1188 uint len = 0;
1189 if (!getSfntTableData(tag, nullptr, &len))
1190 return table;
1191 table.resize(len);
1192 if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
1193 return QByteArray();
1194 return table;
1195}
1196
1197void QFontEngine::clearGlyphCache(const void *context)
1198{
1199 m_glyphCaches.remove(context);
1200}
1201
1202void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
1203{
1204 Q_ASSERT(cache);
1205
1206 GlyphCaches &caches = m_glyphCaches[context];
1207 for (auto & e : caches) {
1208 if (cache == e.cache.data())
1209 return;
1210 }
1211
1212 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
1213 // and limits memory use when there is continuous or random rotation
1214 if (caches.size() == 4)
1215 caches.pop_back();
1216
1217 GlyphCacheEntry entry;
1218 entry.cache = cache;
1219 caches.push_front(entry);
1220
1221}
1222
1223QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
1224 GlyphFormat format,
1225 const QTransform &transform,
1226 const QColor &color) const
1227{
1228 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(context);
1229 if (caches == m_glyphCaches.cend())
1230 return nullptr;
1231
1232 for (auto &e : *caches) {
1233 QFontEngineGlyphCache *cache = e.cache.data();
1234 if (format == cache->glyphFormat()
1235 && (format != Format_ARGB || color == cache->color())
1236 && qtransform_equals_no_translate(cache->m_transform, transform)) {
1237 return cache;
1238 }
1239 }
1240
1241 return nullptr;
1242}
1243
1244static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1245{
1246 uint left_right = (left << 16) + right;
1247
1248 left = 0, right = numPairs - 1;
1249 while (left <= right) {
1250 int middle = left + ( ( right - left ) >> 1 );
1251
1252 if (pairs[middle].left_right == left_right)
1253 return pairs[middle].adjust;
1254
1255 if (pairs[middle].left_right < left_right)
1256 left = middle + 1;
1257 else
1258 right = middle - 1;
1259 }
1260 return 0;
1261}
1262
1263void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1264{
1265 int numPairs = kerning_pairs.size();
1266 if (!numPairs)
1267 return;
1268
1269 const KernPair *pairs = kerning_pairs.constData();
1270
1271 if (flags & DesignMetrics) {
1272 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1273 glyphs->advances[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
1274 } else {
1275 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1276 glyphs->advances[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
1277 }
1278}
1279
1280void QFontEngine::loadKerningPairs(QFixed scalingFactor)
1281{
1282 kerning_pairs.clear();
1283
1284 QByteArray tab = getSfntTable(QFont::Tag("kern").value());
1285 if (tab.isEmpty())
1286 return;
1287
1288 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1289 const uchar *end = table + tab.size();
1290
1291 quint16 version;
1292 if (!qSafeFromBigEndian(table, end, &version))
1293 return;
1294
1295 if (version != 0) {
1296// qDebug("wrong version");
1297 return;
1298 }
1299
1300 quint16 numTables;
1301 if (!qSafeFromBigEndian(table + 2, end, &numTables))
1302 return;
1303
1304 {
1305 int offset = 4;
1306 for(int i = 0; i < numTables; ++i) {
1307 const uchar *header = table + offset;
1308
1309 quint16 version;
1310 if (!qSafeFromBigEndian(header, end, &version))
1311 goto end;
1312
1313 quint16 length;
1314 if (!qSafeFromBigEndian(header + 2, end, &length))
1315 goto end;
1316
1317 quint16 coverage;
1318 if (!qSafeFromBigEndian(header + 4, end, &coverage))
1319 goto end;
1320
1321// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1322 if (version == 0 && coverage == 0x0001) {
1323 if (offset + length > tab.size()) {
1324// qDebug("length ouf ot bounds");
1325 goto end;
1326 }
1327 const uchar *data = table + offset + 6;
1328
1329 quint16 nPairs;
1330 if (!qSafeFromBigEndian(data, end, &nPairs))
1331 goto end;
1332
1333 if (nPairs * 6 + 8 > length - 6) {
1334// qDebug("corrupt table!");
1335 // corrupt table
1336 goto end;
1337 }
1338
1339 int off = 8;
1340 for(int i = 0; i < nPairs; ++i) {
1341 QFontEngine::KernPair p;
1342
1343 quint16 tmp;
1344 if (!qSafeFromBigEndian(data + off, end, &tmp))
1345 goto end;
1346
1347 p.left_right = uint(tmp) << 16;
1348 if (!qSafeFromBigEndian(data + off + 2, end, &tmp))
1349 goto end;
1350
1351 p.left_right |= tmp;
1352
1353 if (!qSafeFromBigEndian(data + off + 4, end, &tmp))
1354 goto end;
1355
1356 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1357 kerning_pairs.append(p);
1358 off += 6;
1359 }
1360 }
1361 offset += length;
1362 }
1363 }
1364end:
1365 std::sort(kerning_pairs.begin(), kerning_pairs.end());
1366// for (int i = 0; i < kerning_pairs.count(); ++i)
1367// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1368}
1369
1370bool QFontEngine::hasHinting() const
1371{
1372 if (m_hasHinting < 0) {
1373 m_hasHinting = false;
1374
1375 const QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1376 if (maxpTable.size() >= 28) {
1377 const uchar *start = reinterpret_cast<const uchar *>(maxpTable.constData());
1378 const quint32 version = qFromBigEndian<quint32>(start);
1379
1380 if (version == 0x00010000) {
1381 const quint16 maxSizeOfInstructions = qFromBigEndian<quint16>(start + 26);
1382 m_hasHinting = maxSizeOfInstructions > 0;
1383 } else {
1384 // If unrecognized maxp table, assume we might have hinting
1385 qDebug("maxp table of format %d not implemented. Assuming hinting exists.",
1386 version);
1387 m_hasHinting = true;
1388 }
1389 }
1390 }
1391
1392 return m_hasHinting;
1393}
1394
1395int QFontEngine::glyphCount() const
1396{
1397 QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1398 if (maxpTable.size() < 6)
1399 return 0;
1400
1401 const uchar *start = reinterpret_cast<const uchar *>(maxpTable.constData());
1402 const uchar *end = start + maxpTable.size();
1403 const uchar *source = reinterpret_cast<const uchar *>(start + 4);
1404
1405 quint16 count = 0;
1406 qSafeFromBigEndian(source, end, &count);
1407 return count;
1408}
1409
1410Qt::HANDLE QFontEngine::handle() const
1411{
1412 return nullptr;
1413}
1414
1415const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1416{
1417 const uchar *header = table;
1418 const uchar *endPtr = table + tableSize;
1419
1420 // version check
1421 quint16 version;
1422 if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
1423 return nullptr;
1424
1425 quint16 numTables;
1426 if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
1427 return nullptr;
1428
1429 const uchar *maps = table + 4;
1430
1431 enum {
1432 Invalid,
1433 AppleRoman,
1434 Symbol,
1435 Unicode11,
1436 Unicode,
1437 MicrosoftUnicode,
1438 MicrosoftUnicodeExtended
1439 };
1440
1441 int symbolTable = -1;
1442 int tableToUse = -1;
1443 int score = Invalid;
1444 for (int n = 0; n < numTables; ++n) {
1445 quint16 platformId = 0;
1446 if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
1447 return nullptr;
1448
1449 quint16 platformSpecificId = 0;
1450 if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
1451 return nullptr;
1452
1453 switch (platformId) {
1454 case 0: // Unicode
1455 if (score < Unicode &&
1456 (platformSpecificId == 0 ||
1457 platformSpecificId == 2 ||
1458 platformSpecificId == 3)) {
1459 tableToUse = n;
1460 score = Unicode;
1461 } else if (score < Unicode11 && platformSpecificId == 1) {
1462 tableToUse = n;
1463 score = Unicode11;
1464 }
1465 break;
1466 case 1: // Apple
1467 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1468 tableToUse = n;
1469 score = AppleRoman;
1470 }
1471 break;
1472 case 3: // Microsoft
1473 switch (platformSpecificId) {
1474 case 0:
1475 symbolTable = n;
1476 if (score < Symbol) {
1477 tableToUse = n;
1478 score = Symbol;
1479 }
1480 break;
1481 case 1:
1482 if (score < MicrosoftUnicode) {
1483 tableToUse = n;
1484 score = MicrosoftUnicode;
1485 }
1486 break;
1487 case 0xa:
1488 if (score < MicrosoftUnicodeExtended) {
1489 tableToUse = n;
1490 score = MicrosoftUnicodeExtended;
1491 }
1492 break;
1493 default:
1494 break;
1495 }
1496 break;
1497 default:
1498 break;
1499 }
1500 }
1501 if (tableToUse < 0)
1502 return nullptr;
1503
1504resolveTable:
1505 *isSymbolFont = (symbolTable > -1);
1506
1507 quint32 unicode_table = 0;
1508 if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1509 return nullptr;
1510
1511 if (!unicode_table)
1512 return nullptr;
1513
1514 // get the header of the unicode table
1515 header = table + unicode_table;
1516
1517 quint16 format;
1518 if (!qSafeFromBigEndian(header, endPtr, &format))
1519 return nullptr;
1520
1521 quint32 length;
1522 if (format < 8) {
1523 quint16 tmp;
1524 if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1525 return nullptr;
1526 length = tmp;
1527 } else {
1528 if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1529 return nullptr;
1530 }
1531
1532 if (table + unicode_table + length > endPtr)
1533 return nullptr;
1534 *cmapSize = length;
1535
1536 // To support symbol fonts that contain a unicode table for the symbol area
1537 // we check the cmap tables and fall back to symbol font unless that would
1538 // involve losing information from the unicode table
1539 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1540 const uchar *selectedTable = table + unicode_table;
1541
1542 // Check that none of the latin1 range are in the unicode table
1543 bool unicodeTableHasLatin1 = false;
1544 for (int uc=0x00; uc<0x100; ++uc) {
1545 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1546 unicodeTableHasLatin1 = true;
1547 break;
1548 }
1549 }
1550
1551 // Check that at least one symbol char is in the unicode table
1552 bool unicodeTableHasSymbols = false;
1553 if (!unicodeTableHasLatin1) {
1554 for (int uc=0xf000; uc<0xf100; ++uc) {
1555 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1556 unicodeTableHasSymbols = true;
1557 break;
1558 }
1559 }
1560 }
1561
1562 // Fall back to symbol table
1563 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1564 tableToUse = symbolTable;
1565 score = Symbol;
1566 goto resolveTable;
1567 }
1568 }
1569
1570 return table + unicode_table;
1571}
1572
1573quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1574{
1575 const uchar *end = cmap + cmapSize;
1576 quint16 format = 0;
1577 if (!qSafeFromBigEndian(cmap, end, &format))
1578 return 0;
1579
1580 if (format == 0) {
1581 const uchar *ptr = cmap + 6 + unicode;
1582 if (unicode < 256 && ptr < end)
1583 return quint32(*ptr);
1584 } else if (format == 4) {
1585 /* some fonts come with invalid cmap tables, where the last segment
1586 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1587 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1588 by returning 0 for 0xffff
1589 */
1590 if (unicode >= 0xffff)
1591 return 0;
1592
1593 quint16 segCountX2 = 0;
1594 if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1595 return 0;
1596
1597 const unsigned char *ends = cmap + 14;
1598
1599 int i = 0;
1600 for (; i < segCountX2/2; ++i) {
1601 quint16 codePoint = 0;
1602 if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1603 return 0;
1604 if (codePoint >= unicode)
1605 break;
1606 }
1607
1608 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1609
1610 quint16 startIndex = 0;
1611 if (!qSafeFromBigEndian(idx, end, &startIndex))
1612 return 0;
1613 if (startIndex > unicode)
1614 return 0;
1615
1616 idx += segCountX2;
1617
1618 quint16 tmp = 0;
1619 if (!qSafeFromBigEndian(idx, end, &tmp))
1620 return 0;
1621 qint16 idDelta = qint16(tmp);
1622
1623 idx += segCountX2;
1624
1625 quint16 idRangeoffset_t = 0;
1626 if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1627 return 0;
1628
1629 quint16 glyphIndex = 0;
1630 if (idRangeoffset_t) {
1631 quint16 id = 0;
1632 if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1633 return 0;
1634
1635 if (id)
1636 glyphIndex = (idDelta + id) % 0x10000;
1637 else
1638 glyphIndex = 0;
1639 } else {
1640 glyphIndex = (idDelta + unicode) % 0x10000;
1641 }
1642 return glyphIndex;
1643 } else if (format == 6) {
1644 quint16 tableSize = 0;
1645 if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1646 return 0;
1647
1648 quint16 firstCode6 = 0;
1649 if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1650 return 0;
1651 if (unicode < firstCode6)
1652 return 0;
1653
1654 quint16 entryCount6 = 0;
1655 if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1656 return 0;
1657 if (entryCount6 * 2 + 10 > tableSize)
1658 return 0;
1659
1660 quint16 sentinel6 = firstCode6 + entryCount6;
1661 if (unicode >= sentinel6)
1662 return 0;
1663
1664 quint16 entryIndex6 = unicode - firstCode6;
1665
1666 quint16 index = 0;
1667 qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1668 return index;
1669 } else if (format == 12) {
1670 quint32 nGroups = 0;
1671 if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1672 return 0;
1673
1674 cmap += 16; // move to start of groups
1675
1676 int left = 0, right = nGroups - 1;
1677 while (left <= right) {
1678 int middle = left + ( ( right - left ) >> 1 );
1679
1680 quint32 startCharCode = 0;
1681 if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1682 return 0;
1683
1684 if (unicode < startCharCode)
1685 right = middle - 1;
1686 else {
1687 quint32 endCharCode = 0;
1688 if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1689 return 0;
1690
1691 if (unicode <= endCharCode) {
1692 quint32 index = 0;
1693 if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1694 return 0;
1695
1696 // Sanity check: No truetype glyph index is >= 0xffff currently, so any value
1697 // above this could result in undefined behavior later.
1698 return std::clamp(index + unicode - startCharCode, quint32(0), quint32(0xffff));
1699 }
1700 left = middle + 1;
1701 }
1702 }
1703 } else {
1704 qDebug("cmap table of format %d not implemented", format);
1705 }
1706
1707 return 0;
1708}
1709
1710QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1711{
1712 QByteArray f = family;
1713 f.replace(' ', "");
1714 f.replace('(', "");
1715 f.replace(')', "");
1716 f.replace('<', "");
1717 f.replace('>', "");
1718 f.replace('[', "");
1719 f.replace(']', "");
1720 f.replace('{', "");
1721 f.replace('}', "");
1722 f.replace('/', "");
1723 f.replace('%', "");
1724 return f;
1725}
1726
1727// Allow font engines (e.g. Windows) that can not reliably create
1728// outline paths for distance-field rendering to switch the scene
1729// graph over to native text rendering.
1730bool QFontEngine::hasUnreliableGlyphOutline() const
1731{
1732 // Color glyphs (Emoji) are generally not suited for outlining
1733 return glyphFormat == QFontEngine::Format_ARGB;
1734}
1735
1736QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1737{
1738 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1739 glyph_t glyph = glyphs.glyphs[i];
1740 glyph_metrics_t gi = boundingBox(glyph);
1741 if (gi.isValid() && gi.width > 0)
1742 return gi.leftBearing();
1743 }
1744 return 0;
1745}
1746
1747QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1748{
1749 if (glyphs.numGlyphs >= 1) {
1750 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1751 glyph_metrics_t gi = boundingBox(glyph);
1752 if (gi.isValid())
1753 return gi.rightBearing();
1754 }
1755 return 0;
1756}
1757
1758QList<QFontVariableAxis> QFontEngine::variableAxes() const
1759{
1760 return QList<QFontVariableAxis>{};
1761}
1762
1763QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1764{
1765}
1766
1767QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1768 : cache(o.cache)
1769{
1770}
1771
1772QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1773{
1774}
1775
1776QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1777{
1778 cache = o.cache;
1779 return *this;
1780}
1781
1782bool QFontEngine::disableEmojiSegmenter()
1783{
1784#if defined(QT_NO_EMOJISEGMENTER)
1785 return true;
1786#else
1787 static const bool sDisableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
1788 return sDisableEmojiSegmenter;
1789#endif
1790}
1791
1792int QFontEngine::maxCachedGlyphSize()
1793{
1794 static const int maxCachedSize = []{
1795 if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE"))
1796 return env;
1798 }();
1799
1800 return maxCachedSize;
1801}
1802
1803// ------------------------------------------------------------------
1804// The box font engine
1805// ------------------------------------------------------------------
1806
1807QFontEngineBox::QFontEngineBox(int size)
1808 : QFontEngine(Box),
1809 _size(size)
1810{
1811 cache_cost = sizeof(QFontEngineBox);
1812}
1813
1814QFontEngineBox::QFontEngineBox(Type type, int size)
1815 : QFontEngine(type),
1816 _size(size)
1817{
1818 cache_cost = sizeof(QFontEngineBox);
1819}
1820
1821QFontEngineBox::~QFontEngineBox()
1822{
1823}
1824
1825glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1826{
1827 Q_UNUSED(ucs4);
1828 return 1;
1829}
1830
1831int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1832{
1833 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1834 if (*nglyphs < len) {
1835 *nglyphs = len;
1836 return -1;
1837 }
1838
1839 int ucs4Length = 0;
1840 QStringIterator it(str, str + len);
1841 while (it.hasNext()) {
1842 it.advance();
1843 glyphs->glyphs[ucs4Length++] = 1;
1844 }
1845
1846 *nglyphs = ucs4Length;
1847 glyphs->numGlyphs = ucs4Length;
1848
1849 if (!(flags & GlyphIndicesOnly))
1850 recalcAdvances(glyphs, flags);
1851
1852 return *nglyphs;
1853}
1854
1855void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1856{
1857 for (int i = 0; i < glyphs->numGlyphs; i++)
1858 glyphs->advances[i] = _size;
1859}
1860
1861void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1862{
1863 if (!glyphs.numGlyphs)
1864 return;
1865
1866 QVarLengthArray<QFixedPoint> positions;
1867 QVarLengthArray<glyph_t> positioned_glyphs;
1868 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1869 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1870
1871 QSize s(_size - 3, _size - 3);
1872 for (int k = 0; k < positions.size(); k++)
1873 path->addRect(QRectF(positions[k].toPointF(), s));
1874}
1875
1876glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1877{
1878 glyph_metrics_t overall;
1879 overall.width = _size*glyphs.numGlyphs;
1880 overall.height = _size;
1881 overall.xoff = overall.width;
1882 return overall;
1883}
1884
1885void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1886{
1887 if (!ti.glyphs.numGlyphs)
1888 return;
1889
1890 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1891 QSize s(_size - 3, _size - 3);
1892
1893 QVarLengthArray<QFixedPoint> positions;
1894 QVarLengthArray<glyph_t> glyphs;
1895 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1896 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1897 if (glyphs.size() == 0)
1898 return;
1899
1900
1901 QPainter *painter = p->painter();
1902 painter->save();
1903 painter->setBrush(Qt::NoBrush);
1904 QPen pen = painter->pen();
1905 pen.setWidthF(lineThickness().toReal());
1906 painter->setPen(pen);
1907 for (int k = 0; k < positions.size(); k++)
1908 painter->drawRect(QRectF(positions[k].toPointF(), s));
1909 painter->restore();
1910}
1911
1912glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1913{
1914 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1915}
1916
1917QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1918{
1919 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1920 return fe;
1921}
1922
1923QFixed QFontEngineBox::ascent() const
1924{
1925 return _size;
1926}
1927
1928QFixed QFontEngineBox::capHeight() const
1929{
1930 return _size;
1931}
1932
1933QFixed QFontEngineBox::descent() const
1934{
1935 return 0;
1936}
1937
1938QFixed QFontEngineBox::leading() const
1939{
1940 QFixed l = _size * QFixed::fromReal(qreal(0.15));
1941 return l.ceil();
1942}
1943
1944qreal QFontEngineBox::maxCharWidth() const
1945{
1946 return _size;
1947}
1948
1949bool QFontEngineBox::canRender(const QChar *, int) const
1950{
1951 return true;
1952}
1953
1954QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1955{
1956 QImage image(_size, _size, QImage::Format_Alpha8);
1957 image.fill(0);
1958
1959 uchar *bits = image.bits();
1960 for (int i=2; i <= _size-3; ++i) {
1961 bits[i + 2 * image.bytesPerLine()] = 255;
1962 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1963 bits[2 + i * image.bytesPerLine()] = 255;
1964 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1965 }
1966 return image;
1967}
1968
1969// ------------------------------------------------------------------
1970// Multi engine
1971// ------------------------------------------------------------------
1972
1973uchar QFontEngineMulti::highByte(glyph_t glyph)
1974{ return glyph >> 24; }
1975
1976// strip high byte from glyph
1977static inline glyph_t stripped(glyph_t glyph)
1978{ return glyph & 0x00ffffff; }
1979
1980QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1981 : QFontEngine(Multi),
1982 m_fallbackFamilies(fallbackFamilies),
1983 m_script(script),
1984 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1985{
1986 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1987
1988 if (m_fallbackFamilies.isEmpty()) {
1989 // defer obtaining the fallback families until loadEngine(1)
1990 m_fallbackFamilies << QString();
1991 }
1992
1993 m_engines.resize(m_fallbackFamilies.size() + 1);
1994
1995 engine->ref.ref();
1996 m_engines[0] = engine;
1997
1998 fontDef = engine->fontDef;
1999 cache_cost = engine->cache_cost;
2000}
2001
2002QFontEngineMulti::~QFontEngineMulti()
2003{
2004 for (int i = 0; i < m_engines.size(); ++i) {
2005 QFontEngine *fontEngine = m_engines.at(i);
2006 if (fontEngine && !fontEngine->ref.deref())
2007 delete fontEngine;
2008 }
2009}
2010
2011QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
2012
2013void QFontEngineMulti::ensureFallbackFamiliesQueried()
2014{
2015 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
2016 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
2017 styleHint = QFont::TypeWriter;
2018
2019 setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family(),
2020 QFont::Style(fontDef.style), styleHint,
2021 QFontDatabasePrivate::ExtendedScript(m_script)));
2022}
2023
2024void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
2025{
2026 Q_ASSERT(!m_fallbackFamiliesQueried);
2027
2028 m_fallbackFamilies = fallbackFamilies;
2029 if (m_fallbackFamilies.isEmpty()) {
2030 // turns out we lied about having any fallback at all
2031 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
2032 QFontEngine *engine = m_engines.at(0);
2033 engine->ref.ref();
2034 m_engines[1] = engine;
2035 m_fallbackFamilies << fontDef.family();
2036 } else {
2037 m_engines.resize(m_fallbackFamilies.size() + 1);
2038 }
2039
2040 m_fallbackFamiliesQueried = true;
2041}
2042
2043void QFontEngineMulti::ensureEngineAt(int at)
2044{
2045 if (!m_fallbackFamiliesQueried && at > 0)
2046 ensureFallbackFamiliesQueried();
2047 Q_ASSERT(at < m_engines.size());
2048 if (!m_engines.at(at)) {
2049 QFontEngine *engine = loadEngine(at);
2050 if (!engine)
2051 engine = new QFontEngineBox(fontDef.pixelSize);
2052 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
2053 engine->ref.ref();
2054 m_engines[at] = engine;
2055 }
2056}
2057
2058QFontEngine *QFontEngineMulti::loadEngine(int at)
2059{
2060 QFontDef request(fontDef);
2061 request.styleStrategy |= QFont::NoFontMerging;
2062 request.families = QStringList(fallbackFamilyAt(at - 1));
2063
2064 // At this point, the main script of the text has already been considered
2065 // when fetching the list of fallback families from the database, and the
2066 // info about the actual script of the characters may have been discarded,
2067 // so we do not check for writing system support, but instead just load
2068 // the family indiscriminately.
2069 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
2070 engine->fontDef.weight = request.weight;
2071 if (request.style > QFont::StyleNormal)
2072 engine->fontDef.style = request.style;
2073 return engine;
2074 }
2075
2076 return nullptr;
2077}
2078
2079int QFontEngineMulti::glyphCount() const
2080{
2081 return engine(0)->glyphCount();
2082}
2083
2084glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
2085{
2086 glyph_t glyph = engine(0)->glyphIndex(ucs4);
2087 if (glyph == 0 && !isIgnorableChar(ucs4)) {
2088 if (!m_fallbackFamiliesQueried)
2089 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2090 for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2091 QFontEngine *engine = m_engines.at(x);
2092 if (!engine) {
2093 if (!shouldLoadFontEngineForCharacter(x, ucs4))
2094 continue;
2095 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2096 engine = m_engines.at(x);
2097 }
2098 Q_ASSERT(engine != nullptr);
2099 if (engine->type() == Box)
2100 continue;
2101
2102 glyph = engine->glyphIndex(ucs4);
2103 if (glyph != 0) {
2104 // set the high byte to indicate which engine the glyph came from
2105 glyph |= (x << 24);
2106 break;
2107 }
2108 }
2109 }
2110
2111 return glyph;
2112}
2113
2114QString QFontEngineMulti::glyphName(glyph_t glyph) const
2115{
2116 const int which = highByte(glyph);
2117 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(which);
2118 return engine(which)->glyphName(stripped(glyph));
2119}
2120
2121glyph_t QFontEngineMulti::findGlyph(QLatin1StringView name) const
2122{
2123 return engine(0)->findGlyph(name);
2124}
2125
2126int QFontEngineMulti::stringToCMap(const QChar *str, int len,
2127 QGlyphLayout *glyphs, int *nglyphs,
2128 QFontEngine::ShaperFlags flags) const
2129{
2130 qCDebug(lcTextFontCmap) << "Mapping characters in"
2131 << QString(str, len)
2132 << "; Shaper flags:" << flags;
2133
2134 const int originalNumGlyphs = glyphs->numGlyphs;
2135 int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
2136 qCDebug(lcTextFontCmap) << " Main font mapped"
2137 << mappedGlyphCount
2138 << "of"
2139 << (nglyphs != nullptr ? *nglyphs : 0)
2140 << "characters";
2141
2142 if (mappedGlyphCount < 0)
2143 return -1;
2144
2145 // If ContextFontMerging is set and the match for the string was incomplete, we try all
2146 // fallbacks on the full string until we find the best match.
2147 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
2148 if (contextFontMerging) {
2149 qCDebug(lcTextFontCmap) << " Context font merging, first pass";
2150
2151 QVarLengthGlyphLayoutArray tempLayout(len);
2152 if (!m_fallbackFamiliesQueried)
2153 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2154
2155 int maxGlyphCount = 0;
2156 uchar engineIndex = 0;
2157 for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2158 int numGlyphs = len;
2159 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2160 maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
2161 qCDebug(lcTextFontCmap) << " Fallback" << x << "mapped" << maxGlyphCount;
2162
2163 // If we found a better match, we copy data into the main QGlyphLayout
2164 if (maxGlyphCount > mappedGlyphCount) {
2165 qCDebug(lcTextFontCmap) << " Better match!";
2166 *nglyphs = numGlyphs;
2167 glyphs->numGlyphs = originalNumGlyphs;
2168 glyphs->copy(&tempLayout);
2169 engineIndex = x;
2170 if (maxGlyphCount == numGlyphs)
2171 break;
2172 } else {
2173 qCDebug(lcTextFontCmap) << " No improvement";
2174 }
2175 }
2176
2177 if (engineIndex > 0) {
2178 for (int y = 0; y < glyphs->numGlyphs; ++y) {
2179 if (glyphs->glyphs[y] != 0)
2180 glyphs->glyphs[y] |= (engineIndex << 24);
2181 }
2182 } else {
2183 contextFontMerging = false;
2184 }
2185
2186 mappedGlyphCount = maxGlyphCount;
2187 }
2188
2189 // Fill in missing glyphs by going through string one character at the time and finding
2190 // the first viable fallback.
2191 int glyph_pos = 0;
2192 QStringIterator it(str, str + len);
2193
2194 const bool enableVariationSelectorHack = disableEmojiSegmenter();
2195 char32_t previousUcs4 = 0;
2196
2197 int lastFallback = -1;
2198 while (it.hasNext()) {
2199 const char32_t ucs4 = it.peekNext();
2200
2201 // If we applied a fallback font to previous glyph, and the current is either
2202 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
2203 // to get the correct shaping rules applied.
2204 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
2205 QFontEngine *engine = m_engines.at(lastFallback);
2206 glyph_t glyph = engine->glyphIndex(ucs4);
2207 if (glyph != 0) {
2208 qCDebug(lcTextFontCmap) << " Applying ZWJ/ZWNJ special case for"
2209 << QString::number(ucs4, 16)
2210 << ", lastFallback:" << lastFallback;
2211
2212 glyphs->glyphs[glyph_pos] = glyph;
2213 if (!(flags & GlyphIndicesOnly)) {
2214 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2215 engine->recalcAdvances(&g, flags);
2216 }
2217
2218 // set the high byte to indicate which engine the glyph came from
2219 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
2220 } else {
2221 lastFallback = -1;
2222 }
2223 } else {
2224 lastFallback = -1;
2225 }
2226
2227 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
2228 qCDebug(lcTextFontCmap) << " Unmapped character"
2229 << QString::number(ucs4, 16);
2230
2231 if (!m_fallbackFamiliesQueried)
2232 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2233 for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2234 QFontEngine *engine = m_engines.at(x);
2235 if (!engine) {
2236 if (!shouldLoadFontEngineForCharacter(x, ucs4)) {
2237 qCDebug(lcTextFontCmap) << " Skipping fallback #" << x;
2238 continue;
2239 }
2240 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2241 engine = m_engines.at(x);
2242 if (!engine)
2243 continue;
2244 }
2245 Q_ASSERT(engine != nullptr);
2246 if (engine->type() == Box)
2247 continue;
2248
2249 glyph_t glyph = engine->glyphIndex(ucs4);
2250 qCDebug(lcTextFontCmap) << " Fallback #" << x
2251 << ", family:" << engine->fontDef.families
2252 << ", styleName:" << engine->fontDef.styleName
2253 << ", style:" << engine->fontDef.style
2254 << ", weight:" << engine->fontDef.weight
2255 << ", glyph mapping:" << glyph;
2256 if (glyph != 0) {
2257 glyphs->glyphs[glyph_pos] = glyph;
2258 if (!(flags & GlyphIndicesOnly)) {
2259 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2260 engine->recalcAdvances(&g, flags);
2261 }
2262
2263 lastFallback = x;
2264
2265 // set the high byte to indicate which engine the glyph came from
2266 glyphs->glyphs[glyph_pos] |= (x << 24);
2267 break;
2268 }
2269 }
2270
2271 // For variant-selectors, they are modifiers to the previous character. If we
2272 // end up with different font selections for the selector and the character it
2273 // modifies, we try applying the selector font to the preceding character as well
2274 const int variantSelectorBlock = 0xFE00;
2275 if (enableVariationSelectorHack && (ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
2276 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
2277 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
2278
2279 if (selectorFontEngine != precedingCharacterFontEngine) {
2280 // Emoji variant selectors are specially handled and should affect font
2281 // selection. If VS-16 is used, then this means we want to select a color
2282 // font. If the selected font is already a color font, we do not need search
2283 // again. If the VS-15 is used, then this means we want to select a non-color
2284 // font. If the selected font is not a color font, we don't do anything.
2285 const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
2286 const bool colorFont = selectedEngine->isColorFont();
2287 const char32_t vs15 = 0xFE0E;
2288 const char32_t vs16 = 0xFE0F;
2289 bool adaptVariantSelector = ucs4 < vs15
2290 || (ucs4 == vs15 && colorFont)
2291 || (ucs4 == vs16 && !colorFont);
2292
2293 if (adaptVariantSelector) {
2294 QFontEngine *engine = m_engines.at(selectorFontEngine);
2295 glyph_t glyph = engine->glyphIndex(previousUcs4);
2296 if (glyph != 0) {
2297 glyphs->glyphs[glyph_pos - 1] = glyph;
2298 if (!(flags & GlyphIndicesOnly)) {
2299 QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
2300 engine->recalcAdvances(&g, flags);
2301 }
2302
2303 // set the high byte to indicate which engine the glyph came from
2304 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
2305 }
2306 }
2307 }
2308 }
2309 }
2310
2311 it.advance();
2312 ++glyph_pos;
2313
2314 previousUcs4 = ucs4;
2315 }
2316
2317 *nglyphs = glyph_pos;
2318 glyphs->numGlyphs = glyph_pos;
2319 return mappedGlyphCount;
2320}
2321
2322bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
2323{
2324 Q_UNUSED(at);
2325 Q_UNUSED(ucs4);
2326 return true;
2327}
2328
2329glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2330{
2331 if (glyphs.numGlyphs <= 0)
2332 return glyph_metrics_t();
2333
2334 glyph_metrics_t overall;
2335
2336 int which = highByte(glyphs.glyphs[0]);
2337 int start = 0;
2338 int end, i;
2339 for (end = 0; end < glyphs.numGlyphs; ++end) {
2340 const int e = highByte(glyphs.glyphs[end]);
2341 if (e == which)
2342 continue;
2343
2344 // set the high byte to zero
2345 for (i = start; i < end; ++i)
2346 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2347
2348 // merge the bounding box for this run
2349 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2350
2351 overall.x = qMin(overall.x, gm.x);
2352 overall.y = qMin(overall.y, gm.y);
2353 overall.width = overall.xoff + gm.width;
2354 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2355 qMin(overall.y, gm.y);
2356 overall.xoff += gm.xoff;
2357 overall.yoff += gm.yoff;
2358
2359 // reset the high byte for all glyphs
2360 const int hi = which << 24;
2361 for (i = start; i < end; ++i)
2362 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2363
2364 // change engine
2365 start = end;
2366 which = e;
2367 }
2368
2369 // set the high byte to zero
2370 for (i = start; i < end; ++i)
2371 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2372
2373 // merge the bounding box for this run
2374 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2375
2376 overall.x = qMin(overall.x, gm.x);
2377 overall.y = qMin(overall.y, gm.y);
2378 overall.width = overall.xoff + gm.width;
2379 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2380 qMin(overall.y, gm.y);
2381 overall.xoff += gm.xoff;
2382 overall.yoff += gm.yoff;
2383
2384 // reset the high byte for all glyphs
2385 const int hi = which << 24;
2386 for (i = start; i < end; ++i)
2387 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2388
2389 return overall;
2390}
2391
2392void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2393{
2394 int which = highByte(glyph);
2395 ensureEngineAt(which);
2396 engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2397}
2398
2399void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2400 QPainterPath *path, QTextItem::RenderFlags flags)
2401{
2402 if (glyphs.numGlyphs <= 0)
2403 return;
2404
2405 int which = highByte(glyphs.glyphs[0]);
2406 int start = 0;
2407 int end, i;
2408 if (flags & QTextItem::RightToLeft) {
2409 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2410 x += glyphs.advances[gl].toReal();
2411 }
2412 for (end = 0; end < glyphs.numGlyphs; ++end) {
2413 const int e = highByte(glyphs.glyphs[end]);
2414 if (e == which)
2415 continue;
2416
2417 if (flags & QTextItem::RightToLeft) {
2418 for (i = start; i < end; ++i)
2419 x -= glyphs.advances[i].toReal();
2420 }
2421
2422 // set the high byte to zero
2423 for (i = start; i < end; ++i)
2424 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2425 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2426 // reset the high byte for all glyphs and update x and y
2427 const int hi = which << 24;
2428 for (i = start; i < end; ++i)
2429 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2430
2431 if (!(flags & QTextItem::RightToLeft)) {
2432 for (i = start; i < end; ++i)
2433 x += glyphs.advances[i].toReal();
2434 }
2435
2436 // change engine
2437 start = end;
2438 which = e;
2439 }
2440
2441 if (flags & QTextItem::RightToLeft) {
2442 for (i = start; i < end; ++i)
2443 x -= glyphs.advances[i].toReal();
2444 }
2445
2446 // set the high byte to zero
2447 for (i = start; i < end; ++i)
2448 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2449
2450 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2451
2452 // reset the high byte for all glyphs
2453 const int hi = which << 24;
2454 for (i = start; i < end; ++i)
2455 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2456}
2457
2458void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2459{
2460 if (glyphs->numGlyphs <= 0)
2461 return;
2462
2463 int which = highByte(glyphs->glyphs[0]);
2464 int start = 0;
2465 int end, i;
2466 for (end = 0; end < glyphs->numGlyphs; ++end) {
2467 const int e = highByte(glyphs->glyphs[end]);
2468 if (e == which)
2469 continue;
2470
2471 // set the high byte to zero
2472 for (i = start; i < end; ++i)
2473 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2474
2475 QGlyphLayout offs = glyphs->mid(start, end - start);
2476 engine(which)->recalcAdvances(&offs, flags);
2477
2478 // reset the high byte for all glyphs and update x and y
2479 const int hi = which << 24;
2480 for (i = start; i < end; ++i)
2481 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2482
2483 // change engine
2484 start = end;
2485 which = e;
2486 }
2487
2488 // set the high byte to zero
2489 for (i = start; i < end; ++i)
2490 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2491
2492 QGlyphLayout offs = glyphs->mid(start, end - start);
2493 engine(which)->recalcAdvances(&offs, flags);
2494
2495 // reset the high byte for all glyphs
2496 const int hi = which << 24;
2497 for (i = start; i < end; ++i)
2498 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2499}
2500
2501void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2502{
2503 if (glyphs->numGlyphs <= 0)
2504 return;
2505
2506 int which = highByte(glyphs->glyphs[0]);
2507 int start = 0;
2508 int end, i;
2509 for (end = 0; end < glyphs->numGlyphs; ++end) {
2510 const int e = highByte(glyphs->glyphs[end]);
2511 if (e == which)
2512 continue;
2513
2514 // set the high byte to zero
2515 for (i = start; i < end; ++i)
2516 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2517
2518 QGlyphLayout offs = glyphs->mid(start, end - start);
2519 engine(which)->doKerning(&offs, flags);
2520
2521 // reset the high byte for all glyphs and update x and y
2522 const int hi = which << 24;
2523 for (i = start; i < end; ++i)
2524 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2525
2526 // change engine
2527 start = end;
2528 which = e;
2529 }
2530
2531 // set the high byte to zero
2532 for (i = start; i < end; ++i)
2533 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2534
2535 QGlyphLayout offs = glyphs->mid(start, end - start);
2536 engine(which)->doKerning(&offs, flags);
2537
2538 // reset the high byte for all glyphs
2539 const int hi = which << 24;
2540 for (i = start; i < end; ++i)
2541 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2542}
2543
2544glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2545{
2546 const int which = highByte(glyph);
2547 return engine(which)->boundingBox(stripped(glyph));
2548}
2549
2550QFixed QFontEngineMulti::emSquareSize() const
2551{ return engine(0)->emSquareSize(); }
2552
2553QFixed QFontEngineMulti::ascent() const
2554{ return engine(0)->ascent(); }
2555
2556QFixed QFontEngineMulti::capHeight() const
2557{ return engine(0)->capHeight(); }
2558
2559QFixed QFontEngineMulti::descent() const
2560{ return engine(0)->descent(); }
2561
2562QFixed QFontEngineMulti::leading() const
2563{
2564 return engine(0)->leading();
2565}
2566
2567QFixed QFontEngineMulti::xHeight() const
2568{
2569 return engine(0)->xHeight();
2570}
2571
2572QFixed QFontEngineMulti::averageCharWidth() const
2573{
2574 return engine(0)->averageCharWidth();
2575}
2576
2577QFixed QFontEngineMulti::lineThickness() const
2578{
2579 return engine(0)->lineThickness();
2580}
2581
2582QFixed QFontEngineMulti::underlinePosition() const
2583{
2584 return engine(0)->underlinePosition();
2585}
2586
2587qreal QFontEngineMulti::maxCharWidth() const
2588{
2589 return engine(0)->maxCharWidth();
2590}
2591
2592qreal QFontEngineMulti::minLeftBearing() const
2593{
2594 return engine(0)->minLeftBearing();
2595}
2596
2597qreal QFontEngineMulti::minRightBearing() const
2598{
2599 return engine(0)->minRightBearing();
2600}
2601
2602bool QFontEngineMulti::canRender(const QChar *string, int len) const
2603{
2604 if (engine(0)->canRender(string, len))
2605 return true;
2606
2607 int nglyphs = len;
2608
2609 QVarLengthArray<glyph_t> glyphs(nglyphs);
2610
2611 QGlyphLayout g;
2612 g.numGlyphs = nglyphs;
2613 g.glyphs = glyphs.data();
2614 if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2615 Q_UNREACHABLE();
2616
2617 for (int i = 0; i < nglyphs; i++) {
2618 if (glyphs[i] == 0)
2619 return false;
2620 }
2621
2622 return true;
2623}
2624
2625/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2626 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2627
2628QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2629{
2630 const int which = highByte(glyph);
2631 return engine(which)->alphaMapForGlyph(stripped(glyph));
2632}
2633
2634QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2635{
2636 const int which = highByte(glyph);
2637 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2638}
2639
2640QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2641{
2642 const int which = highByte(glyph);
2643 return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2644}
2645
2646QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2647 const QFixedPoint &subPixelPosition,
2648 const QTransform &t)
2649{
2650 const int which = highByte(glyph);
2651 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2652}
2653
2654QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2655 const QFixedPoint &subPixelPosition,
2656 const QTransform &t)
2657{
2658 const int which = highByte(glyph);
2659 return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2660}
2661
2662QList<QFontVariableAxis> QFontEngineMulti::variableAxes() const
2663{
2664 return engine(0)->variableAxes();
2665}
2666
2667/*
2668 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2669
2670 The purpose of this is to provide the necessary font fallbacks when drawing complex
2671 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2672 the same raw font over and over again, we want to cache the corresponding multi font engine
2673 as it may contain fallback font engines already.
2674*/
2675QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2676{
2677 QFontEngine *engine = nullptr;
2678 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2679 QFontCache *fc = QFontCache::instance();
2680 // We can't rely on the fontDef (and hence the cache Key)
2681 // alone to distinguish webfonts, since these should not be
2682 // accidentally shared, even if the resulting fontcache key
2683 // is strictly identical. See:
2684 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2685 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2686 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2687 end = fc->engineCache.end();
2688 while (it != end && it.key() == key) {
2689 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2690 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2691 if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2692 engine = cachedEngine;
2693 fc->updateHitCountAndTimeStamp(it.value());
2694 break;
2695 }
2696 ++it;
2697 }
2698 if (!engine) {
2699 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
2700 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2701 }
2702 Q_ASSERT(engine);
2703 return engine;
2704}
2705
2706QTestFontEngine::QTestFontEngine(int size)
2707 : QFontEngineBox(TestFontEngine, size)
2708{}
2709
2710QT_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 int maxGlyphSize()
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