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 return index + unicode - startCharCode;
1697 }
1698 left = middle + 1;
1699 }
1700 }
1701 } else {
1702 qDebug("cmap table of format %d not implemented", format);
1703 }
1704
1705 return 0;
1706}
1707
1708QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1709{
1710 QByteArray f = family;
1711 f.replace(' ', "");
1712 f.replace('(', "");
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 return f;
1723}
1724
1725// Allow font engines (e.g. Windows) that can not reliably create
1726// outline paths for distance-field rendering to switch the scene
1727// graph over to native text rendering.
1728bool QFontEngine::hasUnreliableGlyphOutline() const
1729{
1730 // Color glyphs (Emoji) are generally not suited for outlining
1731 return glyphFormat == QFontEngine::Format_ARGB;
1732}
1733
1734QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1735{
1736 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1737 glyph_t glyph = glyphs.glyphs[i];
1738 glyph_metrics_t gi = boundingBox(glyph);
1739 if (gi.isValid() && gi.width > 0)
1740 return gi.leftBearing();
1741 }
1742 return 0;
1743}
1744
1745QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1746{
1747 if (glyphs.numGlyphs >= 1) {
1748 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1749 glyph_metrics_t gi = boundingBox(glyph);
1750 if (gi.isValid())
1751 return gi.rightBearing();
1752 }
1753 return 0;
1754}
1755
1756QList<QFontVariableAxis> QFontEngine::variableAxes() const
1757{
1758 return QList<QFontVariableAxis>{};
1759}
1760
1761QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1762{
1763}
1764
1765QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1766 : cache(o.cache)
1767{
1768}
1769
1770QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1771{
1772}
1773
1774QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1775{
1776 cache = o.cache;
1777 return *this;
1778}
1779
1780bool QFontEngine::disableEmojiSegmenter()
1781{
1782#if defined(QT_NO_EMOJISEGMENTER)
1783 return true;
1784#else
1785 static const bool sDisableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
1786 return sDisableEmojiSegmenter;
1787#endif
1788}
1789
1790int QFontEngine::maxCachedGlyphSize()
1791{
1792 static const int maxCachedSize = []{
1793 if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE"))
1794 return env;
1796 }();
1797
1798 return maxCachedSize;
1799}
1800
1801// ------------------------------------------------------------------
1802// The box font engine
1803// ------------------------------------------------------------------
1804
1805QFontEngineBox::QFontEngineBox(int size)
1806 : QFontEngine(Box),
1807 _size(size)
1808{
1809 cache_cost = sizeof(QFontEngineBox);
1810}
1811
1812QFontEngineBox::QFontEngineBox(Type type, int size)
1813 : QFontEngine(type),
1814 _size(size)
1815{
1816 cache_cost = sizeof(QFontEngineBox);
1817}
1818
1819QFontEngineBox::~QFontEngineBox()
1820{
1821}
1822
1823glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1824{
1825 Q_UNUSED(ucs4);
1826 return 1;
1827}
1828
1829int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1830{
1831 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1832 if (*nglyphs < len) {
1833 *nglyphs = len;
1834 return -1;
1835 }
1836
1837 int ucs4Length = 0;
1838 QStringIterator it(str, str + len);
1839 while (it.hasNext()) {
1840 it.advance();
1841 glyphs->glyphs[ucs4Length++] = 1;
1842 }
1843
1844 *nglyphs = ucs4Length;
1845 glyphs->numGlyphs = ucs4Length;
1846
1847 if (!(flags & GlyphIndicesOnly))
1848 recalcAdvances(glyphs, flags);
1849
1850 return *nglyphs;
1851}
1852
1853void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1854{
1855 for (int i = 0; i < glyphs->numGlyphs; i++)
1856 glyphs->advances[i] = _size;
1857}
1858
1859void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1860{
1861 if (!glyphs.numGlyphs)
1862 return;
1863
1864 QVarLengthArray<QFixedPoint> positions;
1865 QVarLengthArray<glyph_t> positioned_glyphs;
1866 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1867 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1868
1869 QSize s(_size - 3, _size - 3);
1870 for (int k = 0; k < positions.size(); k++)
1871 path->addRect(QRectF(positions[k].toPointF(), s));
1872}
1873
1874glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1875{
1876 glyph_metrics_t overall;
1877 overall.width = _size*glyphs.numGlyphs;
1878 overall.height = _size;
1879 overall.xoff = overall.width;
1880 return overall;
1881}
1882
1883void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1884{
1885 if (!ti.glyphs.numGlyphs)
1886 return;
1887
1888 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1889 QSize s(_size - 3, _size - 3);
1890
1891 QVarLengthArray<QFixedPoint> positions;
1892 QVarLengthArray<glyph_t> glyphs;
1893 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1894 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1895 if (glyphs.size() == 0)
1896 return;
1897
1898
1899 QPainter *painter = p->painter();
1900 painter->save();
1901 painter->setBrush(Qt::NoBrush);
1902 QPen pen = painter->pen();
1903 pen.setWidthF(lineThickness().toReal());
1904 painter->setPen(pen);
1905 for (int k = 0; k < positions.size(); k++)
1906 painter->drawRect(QRectF(positions[k].toPointF(), s));
1907 painter->restore();
1908}
1909
1910glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1911{
1912 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1913}
1914
1915QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1916{
1917 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1918 return fe;
1919}
1920
1921QFixed QFontEngineBox::ascent() const
1922{
1923 return _size;
1924}
1925
1926QFixed QFontEngineBox::capHeight() const
1927{
1928 return _size;
1929}
1930
1931QFixed QFontEngineBox::descent() const
1932{
1933 return 0;
1934}
1935
1936QFixed QFontEngineBox::leading() const
1937{
1938 QFixed l = _size * QFixed::fromReal(qreal(0.15));
1939 return l.ceil();
1940}
1941
1942qreal QFontEngineBox::maxCharWidth() const
1943{
1944 return _size;
1945}
1946
1947bool QFontEngineBox::canRender(const QChar *, int) const
1948{
1949 return true;
1950}
1951
1952QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1953{
1954 QImage image(_size, _size, QImage::Format_Alpha8);
1955 image.fill(0);
1956
1957 uchar *bits = image.bits();
1958 for (int i=2; i <= _size-3; ++i) {
1959 bits[i + 2 * image.bytesPerLine()] = 255;
1960 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1961 bits[2 + i * image.bytesPerLine()] = 255;
1962 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1963 }
1964 return image;
1965}
1966
1967// ------------------------------------------------------------------
1968// Multi engine
1969// ------------------------------------------------------------------
1970
1971uchar QFontEngineMulti::highByte(glyph_t glyph)
1972{ return glyph >> 24; }
1973
1974// strip high byte from glyph
1975static inline glyph_t stripped(glyph_t glyph)
1976{ return glyph & 0x00ffffff; }
1977
1978QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1979 : QFontEngine(Multi),
1980 m_fallbackFamilies(fallbackFamilies),
1981 m_script(script),
1982 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1983{
1984 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1985
1986 if (m_fallbackFamilies.isEmpty()) {
1987 // defer obtaining the fallback families until loadEngine(1)
1988 m_fallbackFamilies << QString();
1989 }
1990
1991 m_engines.resize(m_fallbackFamilies.size() + 1);
1992
1993 engine->ref.ref();
1994 m_engines[0] = engine;
1995
1996 fontDef = engine->fontDef;
1997 cache_cost = engine->cache_cost;
1998}
1999
2000QFontEngineMulti::~QFontEngineMulti()
2001{
2002 for (int i = 0; i < m_engines.size(); ++i) {
2003 QFontEngine *fontEngine = m_engines.at(i);
2004 if (fontEngine && !fontEngine->ref.deref())
2005 delete fontEngine;
2006 }
2007}
2008
2009QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
2010
2011void QFontEngineMulti::ensureFallbackFamiliesQueried()
2012{
2013 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
2014 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
2015 styleHint = QFont::TypeWriter;
2016
2017 setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family(),
2018 QFont::Style(fontDef.style), styleHint,
2019 QFontDatabasePrivate::ExtendedScript(m_script)));
2020}
2021
2022void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
2023{
2024 Q_ASSERT(!m_fallbackFamiliesQueried);
2025
2026 m_fallbackFamilies = fallbackFamilies;
2027 if (m_fallbackFamilies.isEmpty()) {
2028 // turns out we lied about having any fallback at all
2029 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
2030 QFontEngine *engine = m_engines.at(0);
2031 engine->ref.ref();
2032 m_engines[1] = engine;
2033 m_fallbackFamilies << fontDef.family();
2034 } else {
2035 m_engines.resize(m_fallbackFamilies.size() + 1);
2036 }
2037
2038 m_fallbackFamiliesQueried = true;
2039}
2040
2041void QFontEngineMulti::ensureEngineAt(int at)
2042{
2043 if (!m_fallbackFamiliesQueried && at > 0)
2044 ensureFallbackFamiliesQueried();
2045 Q_ASSERT(at < m_engines.size());
2046 if (!m_engines.at(at)) {
2047 QFontEngine *engine = loadEngine(at);
2048 if (!engine)
2049 engine = new QFontEngineBox(fontDef.pixelSize);
2050 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
2051 engine->ref.ref();
2052 m_engines[at] = engine;
2053 }
2054}
2055
2056QFontEngine *QFontEngineMulti::loadEngine(int at)
2057{
2058 QFontDef request(fontDef);
2059 request.styleStrategy |= QFont::NoFontMerging;
2060 request.families = QStringList(fallbackFamilyAt(at - 1));
2061
2062 // At this point, the main script of the text has already been considered
2063 // when fetching the list of fallback families from the database, and the
2064 // info about the actual script of the characters may have been discarded,
2065 // so we do not check for writing system support, but instead just load
2066 // the family indiscriminately.
2067 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
2068 engine->fontDef.weight = request.weight;
2069 if (request.style > QFont::StyleNormal)
2070 engine->fontDef.style = request.style;
2071 return engine;
2072 }
2073
2074 return nullptr;
2075}
2076
2077int QFontEngineMulti::glyphCount() const
2078{
2079 return engine(0)->glyphCount();
2080}
2081
2082glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
2083{
2084 glyph_t glyph = engine(0)->glyphIndex(ucs4);
2085 if (glyph == 0 && !isIgnorableChar(ucs4)) {
2086 if (!m_fallbackFamiliesQueried)
2087 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2088 for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2089 QFontEngine *engine = m_engines.at(x);
2090 if (!engine) {
2091 if (!shouldLoadFontEngineForCharacter(x, ucs4))
2092 continue;
2093 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2094 engine = m_engines.at(x);
2095 }
2096 Q_ASSERT(engine != nullptr);
2097 if (engine->type() == Box)
2098 continue;
2099
2100 glyph = engine->glyphIndex(ucs4);
2101 if (glyph != 0) {
2102 // set the high byte to indicate which engine the glyph came from
2103 glyph |= (x << 24);
2104 break;
2105 }
2106 }
2107 }
2108
2109 return glyph;
2110}
2111
2112QString QFontEngineMulti::glyphName(glyph_t glyph) const
2113{
2114 const int which = highByte(glyph);
2115 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(which);
2116 return engine(which)->glyphName(stripped(glyph));
2117}
2118
2119glyph_t QFontEngineMulti::findGlyph(QLatin1StringView name) const
2120{
2121 return engine(0)->findGlyph(name);
2122}
2123
2124int QFontEngineMulti::stringToCMap(const QChar *str, int len,
2125 QGlyphLayout *glyphs, int *nglyphs,
2126 QFontEngine::ShaperFlags flags) const
2127{
2128 qCDebug(lcTextFontCmap) << "Mapping characters in"
2129 << QString(str, len)
2130 << "; Shaper flags:" << flags;
2131
2132 const int originalNumGlyphs = glyphs->numGlyphs;
2133 int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
2134 qCDebug(lcTextFontCmap) << " Main font mapped"
2135 << mappedGlyphCount
2136 << "of"
2137 << (nglyphs != nullptr ? *nglyphs : 0)
2138 << "characters";
2139
2140 if (mappedGlyphCount < 0)
2141 return -1;
2142
2143 // If ContextFontMerging is set and the match for the string was incomplete, we try all
2144 // fallbacks on the full string until we find the best match.
2145 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
2146 if (contextFontMerging) {
2147 qCDebug(lcTextFontCmap) << " Context font merging, first pass";
2148
2149 QVarLengthGlyphLayoutArray tempLayout(len);
2150 if (!m_fallbackFamiliesQueried)
2151 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2152
2153 int maxGlyphCount = 0;
2154 uchar engineIndex = 0;
2155 for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2156 int numGlyphs = len;
2157 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2158 maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
2159 qCDebug(lcTextFontCmap) << " Fallback" << x << "mapped" << maxGlyphCount;
2160
2161 // If we found a better match, we copy data into the main QGlyphLayout
2162 if (maxGlyphCount > mappedGlyphCount) {
2163 qCDebug(lcTextFontCmap) << " Better match!";
2164 *nglyphs = numGlyphs;
2165 glyphs->numGlyphs = originalNumGlyphs;
2166 glyphs->copy(&tempLayout);
2167 engineIndex = x;
2168 if (maxGlyphCount == numGlyphs)
2169 break;
2170 } else {
2171 qCDebug(lcTextFontCmap) << " No improvement";
2172 }
2173 }
2174
2175 if (engineIndex > 0) {
2176 for (int y = 0; y < glyphs->numGlyphs; ++y) {
2177 if (glyphs->glyphs[y] != 0)
2178 glyphs->glyphs[y] |= (engineIndex << 24);
2179 }
2180 } else {
2181 contextFontMerging = false;
2182 }
2183
2184 mappedGlyphCount = maxGlyphCount;
2185 }
2186
2187 // Fill in missing glyphs by going through string one character at the time and finding
2188 // the first viable fallback.
2189 int glyph_pos = 0;
2190 QStringIterator it(str, str + len);
2191
2192 const bool enableVariationSelectorHack = disableEmojiSegmenter();
2193 char32_t previousUcs4 = 0;
2194
2195 int lastFallback = -1;
2196 while (it.hasNext()) {
2197 const char32_t ucs4 = it.peekNext();
2198
2199 // If we applied a fallback font to previous glyph, and the current is either
2200 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
2201 // to get the correct shaping rules applied.
2202 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
2203 QFontEngine *engine = m_engines.at(lastFallback);
2204 glyph_t glyph = engine->glyphIndex(ucs4);
2205 if (glyph != 0) {
2206 qCDebug(lcTextFontCmap) << " Applying ZWJ/ZWNJ special case for"
2207 << QString::number(ucs4, 16)
2208 << ", lastFallback:" << lastFallback;
2209
2210 glyphs->glyphs[glyph_pos] = glyph;
2211 if (!(flags & GlyphIndicesOnly)) {
2212 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2213 engine->recalcAdvances(&g, flags);
2214 }
2215
2216 // set the high byte to indicate which engine the glyph came from
2217 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
2218 } else {
2219 lastFallback = -1;
2220 }
2221 } else {
2222 lastFallback = -1;
2223 }
2224
2225 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
2226 qCDebug(lcTextFontCmap) << " Unmapped character"
2227 << QString::number(ucs4, 16);
2228
2229 if (!m_fallbackFamiliesQueried)
2230 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2231 for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2232 QFontEngine *engine = m_engines.at(x);
2233 if (!engine) {
2234 if (!shouldLoadFontEngineForCharacter(x, ucs4)) {
2235 qCDebug(lcTextFontCmap) << " Skipping fallback #" << x;
2236 continue;
2237 }
2238 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2239 engine = m_engines.at(x);
2240 if (!engine)
2241 continue;
2242 }
2243 Q_ASSERT(engine != nullptr);
2244 if (engine->type() == Box)
2245 continue;
2246
2247 glyph_t glyph = engine->glyphIndex(ucs4);
2248 qCDebug(lcTextFontCmap) << " Fallback #" << x
2249 << ", family:" << engine->fontDef.families
2250 << ", styleName:" << engine->fontDef.styleName
2251 << ", style:" << engine->fontDef.style
2252 << ", weight:" << engine->fontDef.weight
2253 << ", glyph mapping:" << glyph;
2254 if (glyph != 0) {
2255 glyphs->glyphs[glyph_pos] = glyph;
2256 if (!(flags & GlyphIndicesOnly)) {
2257 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2258 engine->recalcAdvances(&g, flags);
2259 }
2260
2261 lastFallback = x;
2262
2263 // set the high byte to indicate which engine the glyph came from
2264 glyphs->glyphs[glyph_pos] |= (x << 24);
2265 break;
2266 }
2267 }
2268
2269 // For variant-selectors, they are modifiers to the previous character. If we
2270 // end up with different font selections for the selector and the character it
2271 // modifies, we try applying the selector font to the preceding character as well
2272 const int variantSelectorBlock = 0xFE00;
2273 if (enableVariationSelectorHack && (ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
2274 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
2275 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
2276
2277 if (selectorFontEngine != precedingCharacterFontEngine) {
2278 // Emoji variant selectors are specially handled and should affect font
2279 // selection. If VS-16 is used, then this means we want to select a color
2280 // font. If the selected font is already a color font, we do not need search
2281 // again. If the VS-15 is used, then this means we want to select a non-color
2282 // font. If the selected font is not a color font, we don't do anything.
2283 const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
2284 const bool colorFont = selectedEngine->isColorFont();
2285 const char32_t vs15 = 0xFE0E;
2286 const char32_t vs16 = 0xFE0F;
2287 bool adaptVariantSelector = ucs4 < vs15
2288 || (ucs4 == vs15 && colorFont)
2289 || (ucs4 == vs16 && !colorFont);
2290
2291 if (adaptVariantSelector) {
2292 QFontEngine *engine = m_engines.at(selectorFontEngine);
2293 glyph_t glyph = engine->glyphIndex(previousUcs4);
2294 if (glyph != 0) {
2295 glyphs->glyphs[glyph_pos - 1] = glyph;
2296 if (!(flags & GlyphIndicesOnly)) {
2297 QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
2298 engine->recalcAdvances(&g, flags);
2299 }
2300
2301 // set the high byte to indicate which engine the glyph came from
2302 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
2303 }
2304 }
2305 }
2306 }
2307 }
2308
2309 it.advance();
2310 ++glyph_pos;
2311
2312 previousUcs4 = ucs4;
2313 }
2314
2315 *nglyphs = glyph_pos;
2316 glyphs->numGlyphs = glyph_pos;
2317 return mappedGlyphCount;
2318}
2319
2320bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
2321{
2322 Q_UNUSED(at);
2323 Q_UNUSED(ucs4);
2324 return true;
2325}
2326
2327glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2328{
2329 if (glyphs.numGlyphs <= 0)
2330 return glyph_metrics_t();
2331
2332 glyph_metrics_t overall;
2333
2334 int which = highByte(glyphs.glyphs[0]);
2335 int start = 0;
2336 int end, i;
2337 for (end = 0; end < glyphs.numGlyphs; ++end) {
2338 const int e = highByte(glyphs.glyphs[end]);
2339 if (e == which)
2340 continue;
2341
2342 // set the high byte to zero
2343 for (i = start; i < end; ++i)
2344 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2345
2346 // merge the bounding box for this run
2347 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2348
2349 overall.x = qMin(overall.x, gm.x);
2350 overall.y = qMin(overall.y, gm.y);
2351 overall.width = overall.xoff + gm.width;
2352 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2353 qMin(overall.y, gm.y);
2354 overall.xoff += gm.xoff;
2355 overall.yoff += gm.yoff;
2356
2357 // reset the high byte for all glyphs
2358 const int hi = which << 24;
2359 for (i = start; i < end; ++i)
2360 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2361
2362 // change engine
2363 start = end;
2364 which = e;
2365 }
2366
2367 // set the high byte to zero
2368 for (i = start; i < end; ++i)
2369 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2370
2371 // merge the bounding box for this run
2372 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2373
2374 overall.x = qMin(overall.x, gm.x);
2375 overall.y = qMin(overall.y, gm.y);
2376 overall.width = overall.xoff + gm.width;
2377 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2378 qMin(overall.y, gm.y);
2379 overall.xoff += gm.xoff;
2380 overall.yoff += gm.yoff;
2381
2382 // reset the high byte for all glyphs
2383 const int hi = which << 24;
2384 for (i = start; i < end; ++i)
2385 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2386
2387 return overall;
2388}
2389
2390void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2391{
2392 int which = highByte(glyph);
2393 ensureEngineAt(which);
2394 engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2395}
2396
2397void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2398 QPainterPath *path, QTextItem::RenderFlags flags)
2399{
2400 if (glyphs.numGlyphs <= 0)
2401 return;
2402
2403 int which = highByte(glyphs.glyphs[0]);
2404 int start = 0;
2405 int end, i;
2406 if (flags & QTextItem::RightToLeft) {
2407 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2408 x += glyphs.advances[gl].toReal();
2409 }
2410 for (end = 0; end < glyphs.numGlyphs; ++end) {
2411 const int e = highByte(glyphs.glyphs[end]);
2412 if (e == which)
2413 continue;
2414
2415 if (flags & QTextItem::RightToLeft) {
2416 for (i = start; i < end; ++i)
2417 x -= glyphs.advances[i].toReal();
2418 }
2419
2420 // set the high byte to zero
2421 for (i = start; i < end; ++i)
2422 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2423 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2424 // reset the high byte for all glyphs and update x and y
2425 const int hi = which << 24;
2426 for (i = start; i < end; ++i)
2427 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2428
2429 if (!(flags & QTextItem::RightToLeft)) {
2430 for (i = start; i < end; ++i)
2431 x += glyphs.advances[i].toReal();
2432 }
2433
2434 // change engine
2435 start = end;
2436 which = e;
2437 }
2438
2439 if (flags & QTextItem::RightToLeft) {
2440 for (i = start; i < end; ++i)
2441 x -= glyphs.advances[i].toReal();
2442 }
2443
2444 // set the high byte to zero
2445 for (i = start; i < end; ++i)
2446 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2447
2448 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2449
2450 // reset the high byte for all glyphs
2451 const int hi = which << 24;
2452 for (i = start; i < end; ++i)
2453 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2454}
2455
2456void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2457{
2458 if (glyphs->numGlyphs <= 0)
2459 return;
2460
2461 int which = highByte(glyphs->glyphs[0]);
2462 int start = 0;
2463 int end, i;
2464 for (end = 0; end < glyphs->numGlyphs; ++end) {
2465 const int e = highByte(glyphs->glyphs[end]);
2466 if (e == which)
2467 continue;
2468
2469 // set the high byte to zero
2470 for (i = start; i < end; ++i)
2471 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2472
2473 QGlyphLayout offs = glyphs->mid(start, end - start);
2474 engine(which)->recalcAdvances(&offs, flags);
2475
2476 // reset the high byte for all glyphs and update x and y
2477 const int hi = which << 24;
2478 for (i = start; i < end; ++i)
2479 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2480
2481 // change engine
2482 start = end;
2483 which = e;
2484 }
2485
2486 // set the high byte to zero
2487 for (i = start; i < end; ++i)
2488 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2489
2490 QGlyphLayout offs = glyphs->mid(start, end - start);
2491 engine(which)->recalcAdvances(&offs, flags);
2492
2493 // reset the high byte for all glyphs
2494 const int hi = which << 24;
2495 for (i = start; i < end; ++i)
2496 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2497}
2498
2499void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2500{
2501 if (glyphs->numGlyphs <= 0)
2502 return;
2503
2504 int which = highByte(glyphs->glyphs[0]);
2505 int start = 0;
2506 int end, i;
2507 for (end = 0; end < glyphs->numGlyphs; ++end) {
2508 const int e = highByte(glyphs->glyphs[end]);
2509 if (e == which)
2510 continue;
2511
2512 // set the high byte to zero
2513 for (i = start; i < end; ++i)
2514 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2515
2516 QGlyphLayout offs = glyphs->mid(start, end - start);
2517 engine(which)->doKerning(&offs, flags);
2518
2519 // reset the high byte for all glyphs and update x and y
2520 const int hi = which << 24;
2521 for (i = start; i < end; ++i)
2522 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2523
2524 // change engine
2525 start = end;
2526 which = e;
2527 }
2528
2529 // set the high byte to zero
2530 for (i = start; i < end; ++i)
2531 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2532
2533 QGlyphLayout offs = glyphs->mid(start, end - start);
2534 engine(which)->doKerning(&offs, flags);
2535
2536 // reset the high byte for all glyphs
2537 const int hi = which << 24;
2538 for (i = start; i < end; ++i)
2539 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2540}
2541
2542glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2543{
2544 const int which = highByte(glyph);
2545 return engine(which)->boundingBox(stripped(glyph));
2546}
2547
2548QFixed QFontEngineMulti::emSquareSize() const
2549{ return engine(0)->emSquareSize(); }
2550
2551QFixed QFontEngineMulti::ascent() const
2552{ return engine(0)->ascent(); }
2553
2554QFixed QFontEngineMulti::capHeight() const
2555{ return engine(0)->capHeight(); }
2556
2557QFixed QFontEngineMulti::descent() const
2558{ return engine(0)->descent(); }
2559
2560QFixed QFontEngineMulti::leading() const
2561{
2562 return engine(0)->leading();
2563}
2564
2565QFixed QFontEngineMulti::xHeight() const
2566{
2567 return engine(0)->xHeight();
2568}
2569
2570QFixed QFontEngineMulti::averageCharWidth() const
2571{
2572 return engine(0)->averageCharWidth();
2573}
2574
2575QFixed QFontEngineMulti::lineThickness() const
2576{
2577 return engine(0)->lineThickness();
2578}
2579
2580QFixed QFontEngineMulti::underlinePosition() const
2581{
2582 return engine(0)->underlinePosition();
2583}
2584
2585qreal QFontEngineMulti::maxCharWidth() const
2586{
2587 return engine(0)->maxCharWidth();
2588}
2589
2590qreal QFontEngineMulti::minLeftBearing() const
2591{
2592 return engine(0)->minLeftBearing();
2593}
2594
2595qreal QFontEngineMulti::minRightBearing() const
2596{
2597 return engine(0)->minRightBearing();
2598}
2599
2600bool QFontEngineMulti::canRender(const QChar *string, int len) const
2601{
2602 if (engine(0)->canRender(string, len))
2603 return true;
2604
2605 int nglyphs = len;
2606
2607 QVarLengthArray<glyph_t> glyphs(nglyphs);
2608
2609 QGlyphLayout g;
2610 g.numGlyphs = nglyphs;
2611 g.glyphs = glyphs.data();
2612 if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2613 Q_UNREACHABLE();
2614
2615 for (int i = 0; i < nglyphs; i++) {
2616 if (glyphs[i] == 0)
2617 return false;
2618 }
2619
2620 return true;
2621}
2622
2623/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2624 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2625
2626QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2627{
2628 const int which = highByte(glyph);
2629 return engine(which)->alphaMapForGlyph(stripped(glyph));
2630}
2631
2632QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2633{
2634 const int which = highByte(glyph);
2635 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2636}
2637
2638QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2639{
2640 const int which = highByte(glyph);
2641 return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2642}
2643
2644QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2645 const QFixedPoint &subPixelPosition,
2646 const QTransform &t)
2647{
2648 const int which = highByte(glyph);
2649 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2650}
2651
2652QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2653 const QFixedPoint &subPixelPosition,
2654 const QTransform &t)
2655{
2656 const int which = highByte(glyph);
2657 return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2658}
2659
2660QList<QFontVariableAxis> QFontEngineMulti::variableAxes() const
2661{
2662 return engine(0)->variableAxes();
2663}
2664
2665/*
2666 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2667
2668 The purpose of this is to provide the necessary font fallbacks when drawing complex
2669 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2670 the same raw font over and over again, we want to cache the corresponding multi font engine
2671 as it may contain fallback font engines already.
2672*/
2673QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2674{
2675 QFontEngine *engine = nullptr;
2676 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2677 QFontCache *fc = QFontCache::instance();
2678 // We can't rely on the fontDef (and hence the cache Key)
2679 // alone to distinguish webfonts, since these should not be
2680 // accidentally shared, even if the resulting fontcache key
2681 // is strictly identical. See:
2682 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2683 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2684 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2685 end = fc->engineCache.end();
2686 while (it != end && it.key() == key) {
2687 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2688 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2689 if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2690 engine = cachedEngine;
2691 fc->updateHitCountAndTimeStamp(it.value());
2692 break;
2693 }
2694 ++it;
2695 }
2696 if (!engine) {
2697 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
2698 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2699 }
2700 Q_ASSERT(engine);
2701 return engine;
2702}
2703
2704QTestFontEngine::QTestFontEngine(int size)
2705 : QFontEngineBox(TestFontEngine, size)
2706{}
2707
2708QT_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