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