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