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

source code of qtbase/src/gui/text/qfontengine.cpp