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