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