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

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