Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfontengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <qdebug.h>
6#include <private/qfontengine_p.h>
7#include <private/qfontengineglyphcache_p.h>
8#include <private/qguiapplication_p.h>
9
10#include <qpa/qplatformfontdatabase.h>
11#include <qpa/qplatformintegration.h>
12
13#include "qbitmap.h"
14#include "qpainter.h"
15#include "qpainterpath.h"
17#include "qtextengine_p.h"
18#include <qmath.h>
19#include <qendian.h>
20#include <private/qstringiterator_p.h>
21
22#if QT_CONFIG(harfbuzz)
23# include "qharfbuzzng_p.h"
24# include <hb-ot.h>
25#endif
26
27#include <algorithm>
28#include <limits.h>
29
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(this);
128#endif
129}
130
131QFontEngine::~QFontEngine()
132{
133#ifdef QT_BUILD_INTERNAL
134 if (enginesCollector)
135 enginesCollector->removeOne(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(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(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(QFont::Tag("mort").value(), nullptr, &lenMort)
192 || getSfntTableData(QFont::Tag("morx").value(), nullptr, &lenMorx)) {
193 return true;
194 }
195
196 if (hb_face_t *face = hb_qt_face_get_for_engine(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(hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
201 &script_count, script_tags,
202 nullptr, nullptr);
203
204 if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, nullptr, 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(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(matrix);
228 }
229 return metrics;
230}
231
232QFixed QFontEngine::calculatedCapHeight() const
233{
234 const glyph_t glyph = glyphIndex('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('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('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(matrix.dx());
275 ypos = QFixed::fromReal(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(glyphs.justifications[i].space_18d6);
286 totalKashidas += glyphs.justifications[i].nKashidas;
287 }
288 positions.resize(glyphs.numGlyphs+totalKashidas);
289 glyphs_out.resize(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(gpos.x());
305 gpos_y = QFixed::fromReal(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(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(gpos.x());
332 gpos_y = QFixed::fromReal(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(glyphs.justifications[i].space_18d6);
341 }
342 ++i;
343 }
344 } else {
345 positions.resize(glyphs.numGlyphs);
346 glyphs_out.resize(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(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(gpos.x());
367 positions[current].y = QFixed::fromReal(gpos.y());
368 glyphs_out[current] = glyphs.glyphs[i];
369 xpos += glyphs.advances[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
370 ++current;
371 }
372 ++i;
373 }
374 }
375 }
376 positions.resize(current);
377 glyphs_out.resize(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(QFont::Tag("hhea").value());
393 if (hhea.size() >= 10) {
394 auto ptr = hhea.constData();
395 qint16 ascent = qFromBigEndian<qint16>(ptr + 4);
396 qint16 descent = qFromBigEndian<qint16>(ptr + 6);
397 qint16 leading = qFromBigEndian<qint16>(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 = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
406 if (ascent > limitForQFixed || descent > limitForQFixed || leading > limitForQFixed)
407 return false;
408 m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize / unitsPerEm);
409 m_descent = -QFixed::fromReal(descent * fontDef.pixelSize / unitsPerEm);
410 m_leading = QFixed::fromReal(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(QFont::Tag("EBLC").value()).isEmpty()
422 || !getSfntTable(QFont::Tag("CBLC").value()).isEmpty()
423 || !getSfntTable(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("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(QFont::Tag("OS/2").value());
457 if (os2.size() >= 78) {
458 auto ptr = os2.constData();
459 quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62);
460 qint16 typoAscent = qFromBigEndian<qint16>(ptr + 68);
461 qint16 typoDescent = qFromBigEndian<qint16>(ptr + 70);
462 qint16 typoLineGap = qFromBigEndian<qint16>(ptr + 72);
463 quint16 winAscent = qFromBigEndian<quint16>(ptr + 74);
464 quint16 winDescent = qFromBigEndian<quint16>(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 = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
474 if (typoAscent > limitForQFixed || typoDescent > limitForQFixed
475 || typoLineGap > limitForQFixed)
476 return false;
477 m_ascent = QFixed::fromReal(typoAscent * fontDef.pixelSize / unitsPerEm);
478 m_descent = -QFixed::fromReal(typoDescent * fontDef.pixelSize / unitsPerEm);
479 m_leading = QFixed::fromReal(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 = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize;
485 if (winAscent > limitForQFixed || winDescent > limitForQFixed)
486 return false;
487 m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize / unitsPerEm);
488 m_descent = QFixed::fromReal(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(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>(tableData + kMinLeftSideBearingOffset);
553 qint16 minRightSideBearing = qFromBigEndian<qint16>(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(minLeftSideBearing) < largestValidBearing)
568 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
569 if (qAbs(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(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(m_minLeftBearing, glyphMetrics.leftBearing().toReal());
599 m_minRightBearing = qMin(m_minRightBearing, 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(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(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(overall.x, x);
635 overall.y = qMin(overall.y, y);
636 xmax = qMax(xmax, x.ceil() + bb.width);
637 ymax = qMax(ymax, y.ceil() + bb.height);
638 overall.xoff += glyphs.effectiveAdvance(i);
639 overall.yoff += bb.yoff;
640 }
641 overall.height = qMax(overall.height, 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(x, y);
657 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
658 addGlyphsToPath(positioned_glyphs.data(), positions.data(), 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 + x0, 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 + x0, 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 + x0, 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 + x0, 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 + x0, 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(x);
761 QFixed advanceY = QFixed::fromReal(y);
762 for (int i=0; i < glyphs.numGlyphs; ++i) {
763 glyph_metrics_t metrics = boundingBox(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((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, 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(maxCharWidth());
817
818 addBitmapFontToPath(x, y, 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(t).convertToFormat(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(t).convertToFormat(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(val, val, 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(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(const_cast<QFontEngine *>(engine));
909 if (hb_font_t *hbFont = hb_font_create(hbFace)) {
910 // explicitly use OpenType handlers for this font
911 hb_ot_font_set_funcs(hbFont);
912
913 result = func(hbFont);
914
915 hb_font_destroy(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(this, [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(hbFont, index, name, sizeof(name)))
935 result = QString::fromLatin1(name);
936 return result;
937 });
938#endif
939
940 if (result.isEmpty())
941 result = index ? u"gid%1"_s.arg(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(this, [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(hbFont, name.constData(), name.size(), &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(gid)) {
970 bool ok;
971 result = name.slice(gid.size()).toUInt(&ok);
972 if (ok && result < glyph_t(glyphCount()))
973 return result;
974 } else if (name.startsWith(uni)) {
975 bool ok;
976 const uint ucs4 = name.slice(uni.size()).toUInt(&ok, 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(gm.x.toReal());
992 int glyph_y = qFloor(gm.y.toReal());
993 int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
994 int glyph_height = qCeil((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(Qt::transparent);
1005 QPainter p(&im);
1006 p.setRenderHint(QPainter::Antialiasing);
1007 addGlyphsToPath(&glyph, &pt, 1, &path, { });
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, 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(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(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(&glyph, &p, 1, path, QFlag(0));
1059}
1060
1061/*!
1062 \internal
1063 Returns \c true if the font table idetified by \a tag exists in the font;
1064 returns \c false otherwise.
1065
1066 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
1067 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
1068 of the buffer, passed in \a length, is sufficient to store the font table data,
1069 also copies the font table data to \a buffer.
1070
1071 Note: returning \c false when the font table exists could lead to an undefined behavior.
1072*/
1073bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1074{
1075 Q_UNUSED(tag);
1076 Q_UNUSED(buffer);
1077 Q_UNUSED(length);
1078 return false;
1079}
1080
1081QByteArray QFontEngine::getSfntTable(uint tag) const
1082{
1083 QByteArray table;
1084 uint len = 0;
1085 if (!getSfntTableData(tag, nullptr, &len))
1086 return table;
1087 table.resize(len);
1088 if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
1089 return QByteArray();
1090 return table;
1091}
1092
1093void QFontEngine::clearGlyphCache(const void *context)
1094{
1095 m_glyphCaches.remove(context);
1096}
1097
1098void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
1099{
1100 Q_ASSERT(cache);
1101
1102 GlyphCaches &caches = m_glyphCaches[context];
1103 for (auto & e : caches) {
1104 if (cache == e.cache.data())
1105 return;
1106 }
1107
1108 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
1109 // and limits memory use when there is continuous or random rotation
1110 if (caches.size() == 4)
1111 caches.pop_back();
1112
1113 GlyphCacheEntry entry;
1114 entry.cache = cache;
1115 caches.push_front(entry);
1116
1117}
1118
1119QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
1120 GlyphFormat format,
1121 const QTransform &transform,
1122 const QColor &color) const
1123{
1124 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(context);
1125 if (caches == m_glyphCaches.cend())
1126 return nullptr;
1127
1128 for (auto &e : *caches) {
1129 QFontEngineGlyphCache *cache = e.cache.data();
1130 if (format == cache->glyphFormat()
1131 && (format != Format_ARGB || color == cache->color())
1132 && qtransform_equals_no_translate(cache->m_transform, transform)) {
1133 return cache;
1134 }
1135 }
1136
1137 return nullptr;
1138}
1139
1140static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1141{
1142 uint left_right = (left << 16) + right;
1143
1144 left = 0, right = numPairs - 1;
1145 while (left <= right) {
1146 int middle = left + ( ( right - left ) >> 1 );
1147
1148 if (pairs[middle].left_right == left_right)
1149 return pairs[middle].adjust;
1150
1151 if (pairs[middle].left_right < left_right)
1152 left = middle + 1;
1153 else
1154 right = middle - 1;
1155 }
1156 return 0;
1157}
1158
1159void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1160{
1161 int numPairs = kerning_pairs.size();
1162 if (!numPairs)
1163 return;
1164
1165 const KernPair *pairs = kerning_pairs.constData();
1166
1167 if (flags & DesignMetrics) {
1168 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1169 glyphs->advances[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
1170 } else {
1171 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1172 glyphs->advances[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
1173 }
1174}
1175
1176void QFontEngine::loadKerningPairs(QFixed scalingFactor)
1177{
1178 kerning_pairs.clear();
1179
1180 QByteArray tab = getSfntTable(QFont::Tag("kern").value());
1181 if (tab.isEmpty())
1182 return;
1183
1184 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1185 const uchar *end = table + tab.size();
1186
1187 quint16 version;
1188 if (!qSafeFromBigEndian(table, end, &version))
1189 return;
1190
1191 if (version != 0) {
1192// qDebug("wrong version");
1193 return;
1194 }
1195
1196 quint16 numTables;
1197 if (!qSafeFromBigEndian(table + 2, end, &numTables))
1198 return;
1199
1200 {
1201 int offset = 4;
1202 for(int i = 0; i < numTables; ++i) {
1203 const uchar *header = table + offset;
1204
1205 quint16 version;
1206 if (!qSafeFromBigEndian(header, end, &version))
1207 goto end;
1208
1209 quint16 length;
1210 if (!qSafeFromBigEndian(header + 2, end, &length))
1211 goto end;
1212
1213 quint16 coverage;
1214 if (!qSafeFromBigEndian(header + 4, end, &coverage))
1215 goto end;
1216
1217// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1218 if (version == 0 && coverage == 0x0001) {
1219 if (offset + length > tab.size()) {
1220// qDebug("length ouf ot bounds");
1221 goto end;
1222 }
1223 const uchar *data = table + offset + 6;
1224
1225 quint16 nPairs;
1226 if (!qSafeFromBigEndian(data, end, &nPairs))
1227 goto end;
1228
1229 if (nPairs * 6 + 8 > length - 6) {
1230// qDebug("corrupt table!");
1231 // corrupt table
1232 goto end;
1233 }
1234
1235 int off = 8;
1236 for(int i = 0; i < nPairs; ++i) {
1237 QFontEngine::KernPair p;
1238
1239 quint16 tmp;
1240 if (!qSafeFromBigEndian(data + off, end, &tmp))
1241 goto end;
1242
1243 p.left_right = uint(tmp) << 16;
1244 if (!qSafeFromBigEndian(data + off + 2, end, &tmp))
1245 goto end;
1246
1247 p.left_right |= tmp;
1248
1249 if (!qSafeFromBigEndian(data + off + 4, end, &tmp))
1250 goto end;
1251
1252 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1253 kerning_pairs.append(p);
1254 off += 6;
1255 }
1256 }
1257 offset += length;
1258 }
1259 }
1260end:
1261 std::sort(kerning_pairs.begin(), kerning_pairs.end());
1262// for (int i = 0; i < kerning_pairs.count(); ++i)
1263// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1264}
1265
1266
1267int QFontEngine::glyphCount() const
1268{
1269 QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1270 if (maxpTable.size() < 6)
1271 return 0;
1272
1273 const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1274 const uchar *end = source + maxpTable.size();
1275
1276 quint16 count = 0;
1277 qSafeFromBigEndian(source, end, &count);
1278 return count;
1279}
1280
1281Qt::HANDLE QFontEngine::handle() const
1282{
1283 return nullptr;
1284}
1285
1286const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1287{
1288 const uchar *header = table;
1289 const uchar *endPtr = table + tableSize;
1290
1291 // version check
1292 quint16 version;
1293 if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
1294 return nullptr;
1295
1296 quint16 numTables;
1297 if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
1298 return nullptr;
1299
1300 const uchar *maps = table + 4;
1301
1302 enum {
1303 Invalid,
1304 AppleRoman,
1305 Symbol,
1306 Unicode11,
1307 Unicode,
1308 MicrosoftUnicode,
1309 MicrosoftUnicodeExtended
1310 };
1311
1312 int symbolTable = -1;
1313 int tableToUse = -1;
1314 int score = Invalid;
1315 for (int n = 0; n < numTables; ++n) {
1316 quint16 platformId = 0;
1317 if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
1318 return nullptr;
1319
1320 quint16 platformSpecificId = 0;
1321 if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
1322 return nullptr;
1323
1324 switch (platformId) {
1325 case 0: // Unicode
1326 if (score < Unicode &&
1327 (platformSpecificId == 0 ||
1328 platformSpecificId == 2 ||
1329 platformSpecificId == 3)) {
1330 tableToUse = n;
1331 score = Unicode;
1332 } else if (score < Unicode11 && platformSpecificId == 1) {
1333 tableToUse = n;
1334 score = Unicode11;
1335 }
1336 break;
1337 case 1: // Apple
1338 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1339 tableToUse = n;
1340 score = AppleRoman;
1341 }
1342 break;
1343 case 3: // Microsoft
1344 switch (platformSpecificId) {
1345 case 0:
1346 symbolTable = n;
1347 if (score < Symbol) {
1348 tableToUse = n;
1349 score = Symbol;
1350 }
1351 break;
1352 case 1:
1353 if (score < MicrosoftUnicode) {
1354 tableToUse = n;
1355 score = MicrosoftUnicode;
1356 }
1357 break;
1358 case 0xa:
1359 if (score < MicrosoftUnicodeExtended) {
1360 tableToUse = n;
1361 score = MicrosoftUnicodeExtended;
1362 }
1363 break;
1364 default:
1365 break;
1366 }
1367 break;
1368 default:
1369 break;
1370 }
1371 }
1372 if (tableToUse < 0)
1373 return nullptr;
1374
1375resolveTable:
1376 *isSymbolFont = (symbolTable > -1);
1377
1378 quint32 unicode_table = 0;
1379 if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1380 return nullptr;
1381
1382 if (!unicode_table)
1383 return nullptr;
1384
1385 // get the header of the unicode table
1386 header = table + unicode_table;
1387
1388 quint16 format;
1389 if (!qSafeFromBigEndian(header, endPtr, &format))
1390 return nullptr;
1391
1392 quint32 length;
1393 if (format < 8) {
1394 quint16 tmp;
1395 if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1396 return nullptr;
1397 length = tmp;
1398 } else {
1399 if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1400 return nullptr;
1401 }
1402
1403 if (table + unicode_table + length > endPtr)
1404 return nullptr;
1405 *cmapSize = length;
1406
1407 // To support symbol fonts that contain a unicode table for the symbol area
1408 // we check the cmap tables and fall back to symbol font unless that would
1409 // involve losing information from the unicode table
1410 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1411 const uchar *selectedTable = table + unicode_table;
1412
1413 // Check that none of the latin1 range are in the unicode table
1414 bool unicodeTableHasLatin1 = false;
1415 for (int uc=0x00; uc<0x100; ++uc) {
1416 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1417 unicodeTableHasLatin1 = true;
1418 break;
1419 }
1420 }
1421
1422 // Check that at least one symbol char is in the unicode table
1423 bool unicodeTableHasSymbols = false;
1424 if (!unicodeTableHasLatin1) {
1425 for (int uc=0xf000; uc<0xf100; ++uc) {
1426 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1427 unicodeTableHasSymbols = true;
1428 break;
1429 }
1430 }
1431 }
1432
1433 // Fall back to symbol table
1434 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1435 tableToUse = symbolTable;
1436 score = Symbol;
1437 goto resolveTable;
1438 }
1439 }
1440
1441 return table + unicode_table;
1442}
1443
1444quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1445{
1446 const uchar *end = cmap + cmapSize;
1447 quint16 format = 0;
1448 if (!qSafeFromBigEndian(cmap, end, &format))
1449 return 0;
1450
1451 if (format == 0) {
1452 const uchar *ptr = cmap + 6 + unicode;
1453 if (unicode < 256 && ptr < end)
1454 return quint32(*ptr);
1455 } else if (format == 4) {
1456 /* some fonts come with invalid cmap tables, where the last segment
1457 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1458 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1459 by returning 0 for 0xffff
1460 */
1461 if (unicode >= 0xffff)
1462 return 0;
1463
1464 quint16 segCountX2 = 0;
1465 if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1466 return 0;
1467
1468 const unsigned char *ends = cmap + 14;
1469
1470 int i = 0;
1471 for (; i < segCountX2/2; ++i) {
1472 quint16 codePoint = 0;
1473 if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1474 return 0;
1475 if (codePoint >= unicode)
1476 break;
1477 }
1478
1479 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1480
1481 quint16 startIndex = 0;
1482 if (!qSafeFromBigEndian(idx, end, &startIndex))
1483 return 0;
1484 if (startIndex > unicode)
1485 return 0;
1486
1487 idx += segCountX2;
1488
1489 quint16 tmp = 0;
1490 if (!qSafeFromBigEndian(idx, end, &tmp))
1491 return 0;
1492 qint16 idDelta = qint16(tmp);
1493
1494 idx += segCountX2;
1495
1496 quint16 idRangeoffset_t = 0;
1497 if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1498 return 0;
1499
1500 quint16 glyphIndex = 0;
1501 if (idRangeoffset_t) {
1502 quint16 id = 0;
1503 if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1504 return 0;
1505
1506 if (id)
1507 glyphIndex = (idDelta + id) % 0x10000;
1508 else
1509 glyphIndex = 0;
1510 } else {
1511 glyphIndex = (idDelta + unicode) % 0x10000;
1512 }
1513 return glyphIndex;
1514 } else if (format == 6) {
1515 quint16 tableSize = 0;
1516 if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1517 return 0;
1518
1519 quint16 firstCode6 = 0;
1520 if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1521 return 0;
1522 if (unicode < firstCode6)
1523 return 0;
1524
1525 quint16 entryCount6 = 0;
1526 if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1527 return 0;
1528 if (entryCount6 * 2 + 10 > tableSize)
1529 return 0;
1530
1531 quint16 sentinel6 = firstCode6 + entryCount6;
1532 if (unicode >= sentinel6)
1533 return 0;
1534
1535 quint16 entryIndex6 = unicode - firstCode6;
1536
1537 quint16 index = 0;
1538 qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1539 return index;
1540 } else if (format == 12) {
1541 quint32 nGroups = 0;
1542 if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1543 return 0;
1544
1545 cmap += 16; // move to start of groups
1546
1547 int left = 0, right = nGroups - 1;
1548 while (left <= right) {
1549 int middle = left + ( ( right - left ) >> 1 );
1550
1551 quint32 startCharCode = 0;
1552 if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1553 return 0;
1554
1555 if (unicode < startCharCode)
1556 right = middle - 1;
1557 else {
1558 quint32 endCharCode = 0;
1559 if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1560 return 0;
1561
1562 if (unicode <= endCharCode) {
1563 quint32 index = 0;
1564 if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1565 return 0;
1566
1567 return index + unicode - startCharCode;
1568 }
1569 left = middle + 1;
1570 }
1571 }
1572 } else {
1573 qDebug("cmap table of format %d not implemented", format);
1574 }
1575
1576 return 0;
1577}
1578
1579QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1580{
1581 QByteArray f = family;
1582 f.replace(' ', "");
1583 f.replace('(', "");
1584 f.replace(')', "");
1585 f.replace('<', "");
1586 f.replace('>', "");
1587 f.replace('[', "");
1588 f.replace(']', "");
1589 f.replace('{', "");
1590 f.replace('}', "");
1591 f.replace('/', "");
1592 f.replace('%', "");
1593 return f;
1594}
1595
1596// Allow font engines (e.g. Windows) that can not reliably create
1597// outline paths for distance-field rendering to switch the scene
1598// graph over to native text rendering.
1599bool QFontEngine::hasUnreliableGlyphOutline() const
1600{
1601 // Color glyphs (Emoji) are generally not suited for outlining
1602 return glyphFormat == QFontEngine::Format_ARGB;
1603}
1604
1605QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1606{
1607 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1608 glyph_t glyph = glyphs.glyphs[i];
1609 glyph_metrics_t gi = boundingBox(glyph);
1610 if (gi.isValid() && gi.width > 0)
1611 return gi.leftBearing();
1612 }
1613 return 0;
1614}
1615
1616QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1617{
1618 if (glyphs.numGlyphs >= 1) {
1619 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1620 glyph_metrics_t gi = boundingBox(glyph);
1621 if (gi.isValid())
1622 return gi.rightBearing();
1623 }
1624 return 0;
1625}
1626
1627QList<QFontVariableAxis> QFontEngine::variableAxes() const
1628{
1629 return QList<QFontVariableAxis>{};
1630}
1631
1632QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1633{
1634}
1635
1636QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1637 : cache(o.cache)
1638{
1639}
1640
1641QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1642{
1643}
1644
1645QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1646{
1647 cache = o.cache;
1648 return *this;
1649}
1650
1651bool QFontEngine::disableEmojiSegmenter()
1652{
1653#if defined(QT_NO_EMOJISEGMENTER)
1654 return true;
1655#else
1656 static const bool sDisableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
1657 return sDisableEmojiSegmenter;
1658#endif
1659}
1660
1661// ------------------------------------------------------------------
1662// The box font engine
1663// ------------------------------------------------------------------
1664
1665QFontEngineBox::QFontEngineBox(int size)
1666 : QFontEngine(Box),
1667 _size(size)
1668{
1669 cache_cost = sizeof(QFontEngineBox);
1670}
1671
1672QFontEngineBox::QFontEngineBox(Type type, int size)
1673 : QFontEngine(type),
1674 _size(size)
1675{
1676 cache_cost = sizeof(QFontEngineBox);
1677}
1678
1679QFontEngineBox::~QFontEngineBox()
1680{
1681}
1682
1683glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1684{
1685 Q_UNUSED(ucs4);
1686 return 1;
1687}
1688
1689int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1690{
1691 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1692 if (*nglyphs < len) {
1693 *nglyphs = len;
1694 return -1;
1695 }
1696
1697 int ucs4Length = 0;
1698 QStringIterator it(str, str + len);
1699 while (it.hasNext()) {
1700 it.advance();
1701 glyphs->glyphs[ucs4Length++] = 1;
1702 }
1703
1704 *nglyphs = ucs4Length;
1705 glyphs->numGlyphs = ucs4Length;
1706
1707 if (!(flags & GlyphIndicesOnly))
1708 recalcAdvances(glyphs, flags);
1709
1710 return *nglyphs;
1711}
1712
1713void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1714{
1715 for (int i = 0; i < glyphs->numGlyphs; i++)
1716 glyphs->advances[i] = _size;
1717}
1718
1719void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1720{
1721 if (!glyphs.numGlyphs)
1722 return;
1723
1724 QVarLengthArray<QFixedPoint> positions;
1725 QVarLengthArray<glyph_t> positioned_glyphs;
1726 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1727 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1728
1729 QSize s(_size - 3, _size - 3);
1730 for (int k = 0; k < positions.size(); k++)
1731 path->addRect(QRectF(positions[k].toPointF(), s));
1732}
1733
1734glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1735{
1736 glyph_metrics_t overall;
1737 overall.width = _size*glyphs.numGlyphs;
1738 overall.height = _size;
1739 overall.xoff = overall.width;
1740 return overall;
1741}
1742
1743void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1744{
1745 if (!ti.glyphs.numGlyphs)
1746 return;
1747
1748 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1749 QSize s(_size - 3, _size - 3);
1750
1751 QVarLengthArray<QFixedPoint> positions;
1752 QVarLengthArray<glyph_t> glyphs;
1753 QTransform matrix = QTransform::fromTranslate(x, y - _size);
1754 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1755 if (glyphs.size() == 0)
1756 return;
1757
1758
1759 QPainter *painter = p->painter();
1760 painter->save();
1761 painter->setBrush(Qt::NoBrush);
1762 QPen pen = painter->pen();
1763 pen.setWidthF(lineThickness().toReal());
1764 painter->setPen(pen);
1765 for (int k = 0; k < positions.size(); k++)
1766 painter->drawRect(QRectF(positions[k].toPointF(), s));
1767 painter->restore();
1768}
1769
1770glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1771{
1772 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1773}
1774
1775QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1776{
1777 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1778 return fe;
1779}
1780
1781QFixed QFontEngineBox::ascent() const
1782{
1783 return _size;
1784}
1785
1786QFixed QFontEngineBox::capHeight() const
1787{
1788 return _size;
1789}
1790
1791QFixed QFontEngineBox::descent() const
1792{
1793 return 0;
1794}
1795
1796QFixed QFontEngineBox::leading() const
1797{
1798 QFixed l = _size * QFixed::fromReal(qreal(0.15));
1799 return l.ceil();
1800}
1801
1802qreal QFontEngineBox::maxCharWidth() const
1803{
1804 return _size;
1805}
1806
1807bool QFontEngineBox::canRender(const QChar *, int) const
1808{
1809 return true;
1810}
1811
1812QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1813{
1814 QImage image(_size, _size, QImage::Format_Alpha8);
1815 image.fill(0);
1816
1817 uchar *bits = image.bits();
1818 for (int i=2; i <= _size-3; ++i) {
1819 bits[i + 2 * image.bytesPerLine()] = 255;
1820 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1821 bits[2 + i * image.bytesPerLine()] = 255;
1822 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1823 }
1824 return image;
1825}
1826
1827// ------------------------------------------------------------------
1828// Multi engine
1829// ------------------------------------------------------------------
1830
1831uchar QFontEngineMulti::highByte(glyph_t glyph)
1832{ return glyph >> 24; }
1833
1834// strip high byte from glyph
1835static inline glyph_t stripped(glyph_t glyph)
1836{ return glyph & 0x00ffffff; }
1837
1838QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1839 : QFontEngine(Multi),
1840 m_fallbackFamilies(fallbackFamilies),
1841 m_script(script),
1842 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1843{
1844 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1845
1846 if (m_fallbackFamilies.isEmpty()) {
1847 // defer obtaining the fallback families until loadEngine(1)
1848 m_fallbackFamilies << QString();
1849 }
1850
1851 m_engines.resize(m_fallbackFamilies.size() + 1);
1852
1853 engine->ref.ref();
1854 m_engines[0] = engine;
1855
1856 fontDef = engine->fontDef;
1857 cache_cost = engine->cache_cost;
1858}
1859
1860QFontEngineMulti::~QFontEngineMulti()
1861{
1862 for (int i = 0; i < m_engines.size(); ++i) {
1863 QFontEngine *fontEngine = m_engines.at(i);
1864 if (fontEngine && !fontEngine->ref.deref())
1865 delete fontEngine;
1866 }
1867}
1868
1869QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
1870
1871void QFontEngineMulti::ensureFallbackFamiliesQueried()
1872{
1873 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
1874 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1875 styleHint = QFont::TypeWriter;
1876
1877 setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.families.constFirst(),
1878 QFont::Style(fontDef.style), styleHint,
1879 QFontDatabasePrivate::ExtendedScript(m_script)));
1880}
1881
1882void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
1883{
1884 Q_ASSERT(!m_fallbackFamiliesQueried);
1885
1886 m_fallbackFamilies = fallbackFamilies;
1887 if (m_fallbackFamilies.isEmpty()) {
1888 // turns out we lied about having any fallback at all
1889 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
1890 QFontEngine *engine = m_engines.at(0);
1891 engine->ref.ref();
1892 m_engines[1] = engine;
1893 m_fallbackFamilies << fontDef.families.constFirst();
1894 } else {
1895 m_engines.resize(m_fallbackFamilies.size() + 1);
1896 }
1897
1898 m_fallbackFamiliesQueried = true;
1899}
1900
1901void QFontEngineMulti::ensureEngineAt(int at)
1902{
1903 if (!m_fallbackFamiliesQueried && at > 0)
1904 ensureFallbackFamiliesQueried();
1905 Q_ASSERT(at < m_engines.size());
1906 if (!m_engines.at(at)) {
1907 QFontEngine *engine = loadEngine(at);
1908 if (!engine)
1909 engine = new QFontEngineBox(fontDef.pixelSize);
1910 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1911 engine->ref.ref();
1912 m_engines[at] = engine;
1913 }
1914}
1915
1916QFontEngine *QFontEngineMulti::loadEngine(int at)
1917{
1918 QFontDef request(fontDef);
1919 request.styleStrategy |= QFont::NoFontMerging;
1920 request.families = QStringList(fallbackFamilyAt(at - 1));
1921
1922 // At this point, the main script of the text has already been considered
1923 // when fetching the list of fallback families from the database, and the
1924 // info about the actual script of the characters may have been discarded,
1925 // so we do not check for writing system support, but instead just load
1926 // the family indiscriminately.
1927 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
1928 engine->fontDef.weight = request.weight;
1929 if (request.style > QFont::StyleNormal)
1930 engine->fontDef.style = request.style;
1931 return engine;
1932 }
1933
1934 return nullptr;
1935}
1936
1937int QFontEngineMulti::glyphCount() const
1938{
1939 return engine(0)->glyphCount();
1940}
1941
1942glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
1943{
1944 glyph_t glyph = engine(0)->glyphIndex(ucs4);
1945 if (glyph == 0 && !isIgnorableChar(ucs4)) {
1946 if (!m_fallbackFamiliesQueried)
1947 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1948 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
1949 QFontEngine *engine = m_engines.at(x);
1950 if (!engine) {
1951 if (!shouldLoadFontEngineForCharacter(x, ucs4))
1952 continue;
1953 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
1954 engine = m_engines.at(x);
1955 }
1956 Q_ASSERT(engine != nullptr);
1957 if (engine->type() == Box)
1958 continue;
1959
1960 glyph = engine->glyphIndex(ucs4);
1961 if (glyph != 0) {
1962 // set the high byte to indicate which engine the glyph came from
1963 glyph |= (x << 24);
1964 break;
1965 }
1966 }
1967 }
1968
1969 return glyph;
1970}
1971
1972QString QFontEngineMulti::glyphName(glyph_t glyph) const
1973{
1974 const int which = highByte(glyph);
1975 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(which);
1976 return engine(which)->glyphName(stripped(glyph));
1977}
1978
1979glyph_t QFontEngineMulti::findGlyph(QLatin1StringView name) const
1980{
1981 return engine(0)->findGlyph(name);
1982}
1983
1984int QFontEngineMulti::stringToCMap(const QChar *str, int len,
1985 QGlyphLayout *glyphs, int *nglyphs,
1986 QFontEngine::ShaperFlags flags) const
1987{
1988 const int originalNumGlyphs = glyphs->numGlyphs;
1989 int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
1990 if (mappedGlyphCount < 0)
1991 return -1;
1992
1993 // If ContextFontMerging is set and the match for the string was incomplete, we try all
1994 // fallbacks on the full string until we find the best match.
1995 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
1996 if (contextFontMerging) {
1997 QVarLengthGlyphLayoutArray tempLayout(len);
1998 if (!m_fallbackFamiliesQueried)
1999 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2000
2001 int maxGlyphCount = 0;
2002 uchar engineIndex = 0;
2003 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
2004 int numGlyphs = len;
2005 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2006 maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
2007
2008 // If we found a better match, we copy data into the main QGlyphLayout
2009 if (maxGlyphCount > mappedGlyphCount) {
2010 *nglyphs = numGlyphs;
2011 glyphs->numGlyphs = originalNumGlyphs;
2012 glyphs->copy(&tempLayout);
2013 engineIndex = x;
2014 if (maxGlyphCount == numGlyphs)
2015 break;
2016 }
2017 }
2018
2019 if (engineIndex > 0) {
2020 for (int y = 0; y < glyphs->numGlyphs; ++y) {
2021 if (glyphs->glyphs[y] != 0)
2022 glyphs->glyphs[y] |= (engineIndex << 24);
2023 }
2024 } else {
2025 contextFontMerging = false;
2026 }
2027
2028 mappedGlyphCount = maxGlyphCount;
2029 }
2030
2031 // Fill in missing glyphs by going through string one character at the time and finding
2032 // the first viable fallback.
2033 int glyph_pos = 0;
2034 QStringIterator it(str, str + len);
2035
2036 const bool enableVariationSelectorHack = disableEmojiSegmenter();
2037 char32_t previousUcs4 = 0;
2038
2039 int lastFallback = -1;
2040 while (it.hasNext()) {
2041 const char32_t ucs4 = it.peekNext();
2042
2043 // If we applied a fallback font to previous glyph, and the current is either
2044 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
2045 // to get the correct shaping rules applied.
2046 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
2047 QFontEngine *engine = m_engines.at(lastFallback);
2048 glyph_t glyph = engine->glyphIndex(ucs4);
2049 if (glyph != 0) {
2050 glyphs->glyphs[glyph_pos] = glyph;
2051 if (!(flags & GlyphIndicesOnly)) {
2052 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2053 engine->recalcAdvances(&g, flags);
2054 }
2055
2056 // set the high byte to indicate which engine the glyph came from
2057 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
2058 } else {
2059 lastFallback = -1;
2060 }
2061 } else {
2062 lastFallback = -1;
2063 }
2064
2065 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
2066 if (!m_fallbackFamiliesQueried)
2067 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2068 for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
2069 QFontEngine *engine = m_engines.at(x);
2070 if (!engine) {
2071 if (!shouldLoadFontEngineForCharacter(x, ucs4))
2072 continue;
2073 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2074 engine = m_engines.at(x);
2075 if (!engine)
2076 continue;
2077 }
2078 Q_ASSERT(engine != nullptr);
2079 if (engine->type() == Box)
2080 continue;
2081
2082 glyph_t glyph = engine->glyphIndex(ucs4);
2083 if (glyph != 0) {
2084 glyphs->glyphs[glyph_pos] = glyph;
2085 if (!(flags & GlyphIndicesOnly)) {
2086 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2087 engine->recalcAdvances(&g, flags);
2088 }
2089
2090 lastFallback = x;
2091
2092 // set the high byte to indicate which engine the glyph came from
2093 glyphs->glyphs[glyph_pos] |= (x << 24);
2094 break;
2095 }
2096 }
2097
2098 // For variant-selectors, they are modifiers to the previous character. If we
2099 // end up with different font selections for the selector and the character it
2100 // modifies, we try applying the selector font to the preceding character as well
2101 const int variantSelectorBlock = 0xFE00;
2102 if (enableVariationSelectorHack && (ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
2103 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
2104 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
2105
2106 if (selectorFontEngine != precedingCharacterFontEngine) {
2107 // Emoji variant selectors are specially handled and should affect font
2108 // selection. If VS-16 is used, then this means we want to select a color
2109 // font. If the selected font is already a color font, we do not need search
2110 // again. If the VS-15 is used, then this means we want to select a non-color
2111 // font. If the selected font is not a color font, we don't do anything.
2112 const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
2113 const bool colorFont = selectedEngine->isColorFont();
2114 const char32_t vs15 = 0xFE0E;
2115 const char32_t vs16 = 0xFE0F;
2116 bool adaptVariantSelector = ucs4 < vs15
2117 || (ucs4 == vs15 && colorFont)
2118 || (ucs4 == vs16 && !colorFont);
2119
2120 if (adaptVariantSelector) {
2121 QFontEngine *engine = m_engines.at(selectorFontEngine);
2122 glyph_t glyph = engine->glyphIndex(previousUcs4);
2123 if (glyph != 0) {
2124 glyphs->glyphs[glyph_pos - 1] = glyph;
2125 if (!(flags & GlyphIndicesOnly)) {
2126 QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
2127 engine->recalcAdvances(&g, flags);
2128 }
2129
2130 // set the high byte to indicate which engine the glyph came from
2131 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
2132 }
2133 }
2134 }
2135 }
2136 }
2137
2138 it.advance();
2139 ++glyph_pos;
2140
2141 previousUcs4 = ucs4;
2142 }
2143
2144 *nglyphs = glyph_pos;
2145 glyphs->numGlyphs = glyph_pos;
2146 return mappedGlyphCount;
2147}
2148
2149bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
2150{
2151 Q_UNUSED(at);
2152 Q_UNUSED(ucs4);
2153 return true;
2154}
2155
2156glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2157{
2158 if (glyphs.numGlyphs <= 0)
2159 return glyph_metrics_t();
2160
2161 glyph_metrics_t overall;
2162
2163 int which = highByte(glyphs.glyphs[0]);
2164 int start = 0;
2165 int end, i;
2166 for (end = 0; end < glyphs.numGlyphs; ++end) {
2167 const int e = highByte(glyphs.glyphs[end]);
2168 if (e == which)
2169 continue;
2170
2171 // set the high byte to zero
2172 for (i = start; i < end; ++i)
2173 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2174
2175 // merge the bounding box for this run
2176 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2177
2178 overall.x = qMin(overall.x, gm.x);
2179 overall.y = qMin(overall.y, gm.y);
2180 overall.width = overall.xoff + gm.width;
2181 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2182 qMin(overall.y, gm.y);
2183 overall.xoff += gm.xoff;
2184 overall.yoff += gm.yoff;
2185
2186 // reset the high byte for all glyphs
2187 const int hi = which << 24;
2188 for (i = start; i < end; ++i)
2189 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2190
2191 // change engine
2192 start = end;
2193 which = e;
2194 }
2195
2196 // set the high byte to zero
2197 for (i = start; i < end; ++i)
2198 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2199
2200 // merge the bounding box for this run
2201 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2202
2203 overall.x = qMin(overall.x, gm.x);
2204 overall.y = qMin(overall.y, gm.y);
2205 overall.width = overall.xoff + gm.width;
2206 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2207 qMin(overall.y, gm.y);
2208 overall.xoff += gm.xoff;
2209 overall.yoff += gm.yoff;
2210
2211 // reset the high byte for all glyphs
2212 const int hi = which << 24;
2213 for (i = start; i < end; ++i)
2214 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2215
2216 return overall;
2217}
2218
2219void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2220{
2221 int which = highByte(glyph);
2222 ensureEngineAt(which);
2223 engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2224}
2225
2226void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2227 QPainterPath *path, QTextItem::RenderFlags flags)
2228{
2229 if (glyphs.numGlyphs <= 0)
2230 return;
2231
2232 int which = highByte(glyphs.glyphs[0]);
2233 int start = 0;
2234 int end, i;
2235 if (flags & QTextItem::RightToLeft) {
2236 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2237 x += glyphs.advances[gl].toReal();
2238 }
2239 for (end = 0; end < glyphs.numGlyphs; ++end) {
2240 const int e = highByte(glyphs.glyphs[end]);
2241 if (e == which)
2242 continue;
2243
2244 if (flags & QTextItem::RightToLeft) {
2245 for (i = start; i < end; ++i)
2246 x -= glyphs.advances[i].toReal();
2247 }
2248
2249 // set the high byte to zero
2250 for (i = start; i < end; ++i)
2251 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2252 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2253 // reset the high byte for all glyphs and update x and y
2254 const int hi = which << 24;
2255 for (i = start; i < end; ++i)
2256 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2257
2258 if (!(flags & QTextItem::RightToLeft)) {
2259 for (i = start; i < end; ++i)
2260 x += glyphs.advances[i].toReal();
2261 }
2262
2263 // change engine
2264 start = end;
2265 which = e;
2266 }
2267
2268 if (flags & QTextItem::RightToLeft) {
2269 for (i = start; i < end; ++i)
2270 x -= glyphs.advances[i].toReal();
2271 }
2272
2273 // set the high byte to zero
2274 for (i = start; i < end; ++i)
2275 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2276
2277 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2278
2279 // reset the high byte for all glyphs
2280 const int hi = which << 24;
2281 for (i = start; i < end; ++i)
2282 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2283}
2284
2285void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2286{
2287 if (glyphs->numGlyphs <= 0)
2288 return;
2289
2290 int which = highByte(glyphs->glyphs[0]);
2291 int start = 0;
2292 int end, i;
2293 for (end = 0; end < glyphs->numGlyphs; ++end) {
2294 const int e = highByte(glyphs->glyphs[end]);
2295 if (e == which)
2296 continue;
2297
2298 // set the high byte to zero
2299 for (i = start; i < end; ++i)
2300 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2301
2302 QGlyphLayout offs = glyphs->mid(start, end - start);
2303 engine(which)->recalcAdvances(&offs, flags);
2304
2305 // reset the high byte for all glyphs and update x and y
2306 const int hi = which << 24;
2307 for (i = start; i < end; ++i)
2308 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2309
2310 // change engine
2311 start = end;
2312 which = e;
2313 }
2314
2315 // set the high byte to zero
2316 for (i = start; i < end; ++i)
2317 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2318
2319 QGlyphLayout offs = glyphs->mid(start, end - start);
2320 engine(which)->recalcAdvances(&offs, flags);
2321
2322 // reset the high byte for all glyphs
2323 const int hi = which << 24;
2324 for (i = start; i < end; ++i)
2325 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2326}
2327
2328void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2329{
2330 if (glyphs->numGlyphs <= 0)
2331 return;
2332
2333 int which = highByte(glyphs->glyphs[0]);
2334 int start = 0;
2335 int end, i;
2336 for (end = 0; end < glyphs->numGlyphs; ++end) {
2337 const int e = highByte(glyphs->glyphs[end]);
2338 if (e == which)
2339 continue;
2340
2341 // set the high byte to zero
2342 for (i = start; i < end; ++i)
2343 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2344
2345 QGlyphLayout offs = glyphs->mid(start, end - start);
2346 engine(which)->doKerning(&offs, flags);
2347
2348 // reset the high byte for all glyphs and update x and y
2349 const int hi = which << 24;
2350 for (i = start; i < end; ++i)
2351 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2352
2353 // change engine
2354 start = end;
2355 which = e;
2356 }
2357
2358 // set the high byte to zero
2359 for (i = start; i < end; ++i)
2360 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2361
2362 QGlyphLayout offs = glyphs->mid(start, end - start);
2363 engine(which)->doKerning(&offs, flags);
2364
2365 // reset the high byte for all glyphs
2366 const int hi = which << 24;
2367 for (i = start; i < end; ++i)
2368 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2369}
2370
2371glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2372{
2373 const int which = highByte(glyph);
2374 return engine(which)->boundingBox(stripped(glyph));
2375}
2376
2377QFixed QFontEngineMulti::emSquareSize() const
2378{ return engine(0)->emSquareSize(); }
2379
2380QFixed QFontEngineMulti::ascent() const
2381{ return engine(0)->ascent(); }
2382
2383QFixed QFontEngineMulti::capHeight() const
2384{ return engine(0)->capHeight(); }
2385
2386QFixed QFontEngineMulti::descent() const
2387{ return engine(0)->descent(); }
2388
2389QFixed QFontEngineMulti::leading() const
2390{
2391 return engine(0)->leading();
2392}
2393
2394QFixed QFontEngineMulti::xHeight() const
2395{
2396 return engine(0)->xHeight();
2397}
2398
2399QFixed QFontEngineMulti::averageCharWidth() const
2400{
2401 return engine(0)->averageCharWidth();
2402}
2403
2404QFixed QFontEngineMulti::lineThickness() const
2405{
2406 return engine(0)->lineThickness();
2407}
2408
2409QFixed QFontEngineMulti::underlinePosition() const
2410{
2411 return engine(0)->underlinePosition();
2412}
2413
2414qreal QFontEngineMulti::maxCharWidth() const
2415{
2416 return engine(0)->maxCharWidth();
2417}
2418
2419qreal QFontEngineMulti::minLeftBearing() const
2420{
2421 return engine(0)->minLeftBearing();
2422}
2423
2424qreal QFontEngineMulti::minRightBearing() const
2425{
2426 return engine(0)->minRightBearing();
2427}
2428
2429bool QFontEngineMulti::canRender(const QChar *string, int len) const
2430{
2431 if (engine(0)->canRender(string, len))
2432 return true;
2433
2434 int nglyphs = len;
2435
2436 QVarLengthArray<glyph_t> glyphs(nglyphs);
2437
2438 QGlyphLayout g;
2439 g.numGlyphs = nglyphs;
2440 g.glyphs = glyphs.data();
2441 if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2442 Q_UNREACHABLE();
2443
2444 for (int i = 0; i < nglyphs; i++) {
2445 if (glyphs[i] == 0)
2446 return false;
2447 }
2448
2449 return true;
2450}
2451
2452/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2453 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2454
2455QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2456{
2457 const int which = highByte(glyph);
2458 return engine(which)->alphaMapForGlyph(stripped(glyph));
2459}
2460
2461QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2462{
2463 const int which = highByte(glyph);
2464 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2465}
2466
2467QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2468{
2469 const int which = highByte(glyph);
2470 return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2471}
2472
2473QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2474 const QFixedPoint &subPixelPosition,
2475 const QTransform &t)
2476{
2477 const int which = highByte(glyph);
2478 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2479}
2480
2481QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2482 const QFixedPoint &subPixelPosition,
2483 const QTransform &t)
2484{
2485 const int which = highByte(glyph);
2486 return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2487}
2488
2489QList<QFontVariableAxis> QFontEngineMulti::variableAxes() const
2490{
2491 return engine(0)->variableAxes();
2492}
2493
2494/*
2495 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2496
2497 The purpose of this is to provide the necessary font fallbacks when drawing complex
2498 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2499 the same raw font over and over again, we want to cache the corresponding multi font engine
2500 as it may contain fallback font engines already.
2501*/
2502QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2503{
2504 QFontEngine *engine = nullptr;
2505 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2506 QFontCache *fc = QFontCache::instance();
2507 // We can't rely on the fontDef (and hence the cache Key)
2508 // alone to distinguish webfonts, since these should not be
2509 // accidentally shared, even if the resulting fontcache key
2510 // is strictly identical. See:
2511 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2512 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2513 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2514 end = fc->engineCache.end();
2515 while (it != end && it.key() == key) {
2516 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2517 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2518 if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2519 engine = cachedEngine;
2520 fc->updateHitCountAndTimeStamp(it.value());
2521 break;
2522 }
2523 ++it;
2524 }
2525 if (!engine) {
2526 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
2527 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2528 }
2529 Q_ASSERT(engine);
2530 return engine;
2531}
2532
2533QTestFontEngine::QTestFontEngine(int size)
2534 : QFontEngineBox(TestFontEngine, size)
2535{}
2536
2537QT_END_NAMESPACE
friend class QFontEngine
Definition qpainter.h:432
Combined button and popup list for selecting options.
QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script)
static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
static bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *output)
static QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
#define SET(x, y)
static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
static glyph_t stripped(glyph_t glyph)
Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
#define kBearingNotInitialized
#define GRID(x, y)
static bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
#define kMinLeftSideBearingOffset
#define q16Dot16ToFloat(i)
@ EdgeUp
@ EdgeRight
@ EdgeDown
@ EdgeLeft
#define kMinRightSideBearingOffset