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
437
439{
440 QByteArray os2 = getSfntTable(QFont::Tag("OS/2").value());
441 if (os2.size() >= 78) {
442 auto ptr = os2.constData();
443 quint16 fsSelection = qFromBigEndian<quint16>(ptr + 62);
444 qint16 typoAscent = qFromBigEndian<qint16>(ptr + 68);
445 qint16 typoDescent = qFromBigEndian<qint16>(ptr + 70);
446 qint16 typoLineGap = qFromBigEndian<qint16>(ptr + 72);
447 quint16 winAscent = qFromBigEndian<quint16>(ptr + 74);
448 quint16 winDescent = qFromBigEndian<quint16>(ptr + 76);
449
450 enum { USE_TYPO_METRICS = 0x80 };
451 QFixed unitsPerEm = emSquareSize();
452 if (preferTypoLineMetrics() || fsSelection & USE_TYPO_METRICS) {
453 // Some fonts may have invalid OS/2 data. We detect this and bail out.
454 if (typoAscent == 0 && typoDescent == 0)
455 return false;
456 m_ascent = QFixed::fromReal(typoAscent * fontDef.pixelSize) / unitsPerEm;
457 m_descent = -QFixed::fromReal(typoDescent * fontDef.pixelSize) / unitsPerEm;
458 m_leading = QFixed::fromReal(typoLineGap * fontDef.pixelSize) / unitsPerEm;
459 } else {
460 // Some fonts may have invalid OS/2 data. We detect this and bail out.
461 if (winAscent == 0 && winDescent == 0)
462 return false;
463 m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize) / unitsPerEm;
464 m_descent = QFixed::fromReal(winDescent * fontDef.pixelSize) / unitsPerEm;
465 m_leading = QFixed{};
466 }
467
468 return true;
469 }
470
471 return false;
472}
473
475{
478
479 return m_leading;
480}
481
483{
486
487 return m_ascent;
488}
489
491{
494
495 return m_descent;
496}
497
499{
500 if (m_minLeftBearing == kBearingNotInitialized)
501 minRightBearing(); // Initializes both (see below)
502
503 return m_minLeftBearing;
504}
505
506#define q16Dot16ToFloat(i) ((i) / 65536.0)
507
508#define kMinLeftSideBearingOffset 12
509#define kMinRightSideBearingOffset 14
510
512{
513 if (m_minRightBearing == kBearingNotInitialized) {
514
515 // Try the 'hhea' font table first, which covers the entire font
516 QByteArray hheaTable = getSfntTable(QFont::Tag("hhea").value());
517 if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) {
518 const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData());
519 Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0);
520
521 qint16 minLeftSideBearing = qFromBigEndian<qint16>(tableData + kMinLeftSideBearingOffset);
522 qint16 minRightSideBearing = qFromBigEndian<qint16>(tableData + kMinRightSideBearingOffset);
523
524 // The table data is expressed as FUnits, meaning we have to take the number
525 // of units per em into account. Since pixelSize already has taken DPI into
526 // account we can use that directly instead of the point size.
527 int unitsPerEm = emSquareSize().toInt();
528 qreal funitToPixelFactor = fontDef.pixelSize / unitsPerEm;
529
530 // Some fonts on OS X (such as Gurmukhi Sangam MN, Khmer MN, Lao Sangam MN, etc.), have
531 // invalid values for their NBSPACE left bearing, causing the 'hhea' minimum bearings to
532 // be way off. We detect this by assuming that the minimum bearsings are within a certain
533 // range of the em square size.
534 static const int largestValidBearing = 4 * unitsPerEm;
535
536 if (qAbs(minLeftSideBearing) < largestValidBearing)
537 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
538 if (qAbs(minRightSideBearing) < largestValidBearing)
539 m_minRightBearing = minRightSideBearing * funitToPixelFactor;
540 }
541
542 // Fallback in case of missing 'hhea' table (bitmap fonts e.g.) or broken 'hhea' values
543 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) {
544
545 // To balance performance and correctness we only look at a subset of the
546 // possible glyphs in the font, based on which characters are more likely
547 // to have a left or right bearing.
548 static const ushort characterSubset[] = {
549 '(', 'C', 'F', 'K', 'V', 'X', 'Y', ']', '_', 'f', 'r', '|',
550 127, 205, 645, 884, 922, 1070, 12386
551 };
552
553 // The font may have minimum bearings larger than 0, so we have to start at the max
554 m_minLeftBearing = m_minRightBearing = std::numeric_limits<qreal>::max();
555
556 for (uint i = 0; i < (sizeof(characterSubset) / sizeof(ushort)); ++i) {
557 const glyph_t glyph = glyphIndex(characterSubset[i]);
558 if (!glyph)
559 continue;
560
561 glyph_metrics_t glyphMetrics = const_cast<QFontEngine *>(this)->boundingBox(glyph);
562
563 // Glyphs with no contours shouldn't contribute to bearings
564 if (!glyphMetrics.width || !glyphMetrics.height)
565 continue;
566
567 m_minLeftBearing = qMin(m_minLeftBearing, glyphMetrics.leftBearing().toReal());
568 m_minRightBearing = qMin(m_minRightBearing, glyphMetrics.rightBearing().toReal());
569 }
570 }
571
572 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized)
573 qWarning() << "Failed to compute left/right minimum bearings for"
574 << fontDef.families.first();
575 }
576
577 return m_minRightBearing;
578}
579
581{
582 QFixed w;
583 for (int i = 0; i < glyphs.numGlyphs; ++i)
584 w += glyphs.effectiveAdvance(i);
585 const QFixed leftBearing = firstLeftBearing(glyphs);
586 const QFixed rightBearing = lastRightBearing(glyphs);
587 return glyph_metrics_t(leftBearing, -(ascent()), w - leftBearing - rightBearing, ascent() + descent(), w, 0);
588}
589
591{
592 glyph_metrics_t overall;
593
594 QFixed ymax = 0;
595 QFixed xmax = 0;
596 for (int i = 0; i < glyphs.numGlyphs; i++) {
597 // If shaping has found this should be ignored, ignore it.
598 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
599 continue;
600 glyph_metrics_t bb = boundingBox(glyphs.glyphs[i]);
601 QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x;
602 QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
603 overall.x = qMin(overall.x, x);
604 overall.y = qMin(overall.y, y);
605 xmax = qMax(xmax, x.ceil() + bb.width);
606 ymax = qMax(ymax, y.ceil() + bb.height);
607 overall.xoff += glyphs.effectiveAdvance(i);
608 overall.yoff += bb.yoff;
609 }
610 overall.height = qMax(overall.height, ymax - overall.y);
611 overall.width = xmax - overall.x;
612
613 return overall;
614}
615
616
618 QTextItem::RenderFlags flags)
619{
620 if (!glyphs.numGlyphs)
621 return;
622
623 QVarLengthArray<QFixedPoint> positions;
624 QVarLengthArray<glyph_t> positioned_glyphs;
626 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
627 addGlyphsToPath(positioned_glyphs.data(), positions.data(), positioned_glyphs.size(), path, flags);
628}
629
630#define GRID(x, y) grid[(y)*(w+1) + (x)]
631#define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7)))
632
633enum { EdgeRight = 0x1,
634 EdgeDown = 0x2,
635 EdgeLeft = 0x4,
636 EdgeUp = 0x8
638
639static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
640{
641 Q_UNUSED(h);
642
643 path->moveTo(x + x0, y + y0);
644 while (GRID(x, y)) {
645 if (GRID(x, y) & EdgeRight) {
646 while (GRID(x, y) & EdgeRight) {
647 GRID(x, y) &= ~EdgeRight;
648 ++x;
649 }
650 Q_ASSERT(x <= w);
651 path->lineTo(x + x0, y + y0);
652 continue;
653 }
654 if (GRID(x, y) & EdgeDown) {
655 while (GRID(x, y) & EdgeDown) {
656 GRID(x, y) &= ~EdgeDown;
657 ++y;
658 }
659 Q_ASSERT(y <= h);
660 path->lineTo(x + x0, y + y0);
661 continue;
662 }
663 if (GRID(x, y) & EdgeLeft) {
664 while (GRID(x, y) & EdgeLeft) {
665 GRID(x, y) &= ~EdgeLeft;
666 --x;
667 }
668 Q_ASSERT(x >= 0);
669 path->lineTo(x + x0, y + y0);
670 continue;
671 }
672 if (GRID(x, y) & EdgeUp) {
673 while (GRID(x, y) & EdgeUp) {
674 GRID(x, y) &= ~EdgeUp;
675 --y;
676 }
677 Q_ASSERT(y >= 0);
678 path->lineTo(x + x0, y + y0);
679 continue;
680 }
681 }
682 path->closeSubpath();
683}
684
685Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
686{
687 uint *grid = new uint[(w+1)*(h+1)];
688 // set up edges
689 for (int y = 0; y <= h; ++y) {
690 for (int x = 0; x <= w; ++x) {
691 bool topLeft = (x == 0 || y == 0) ? false : SET(x - 1, y - 1);
692 bool topRight = (x == w || y == 0) ? false : SET(x, y - 1);
693 bool bottomLeft = (x == 0 || y == h) ? false : SET(x - 1, y);
694 bool bottomRight = (x == w || y == h) ? false : SET(x, y);
695
696 GRID(x, y) = 0;
697 if ((!topRight) & bottomRight)
698 GRID(x, y) |= EdgeRight;
699 if ((!bottomRight) & bottomLeft)
700 GRID(x, y) |= EdgeDown;
701 if ((!bottomLeft) & topLeft)
702 GRID(x, y) |= EdgeLeft;
703 if ((!topLeft) & topRight)
704 GRID(x, y) |= EdgeUp;
705 }
706 }
707
708 // collect edges
709 for (int y = 0; y < h; ++y) {
710 for (int x = 0; x < w; ++x) {
711 if (!GRID(x, y))
712 continue;
713 // found start of a contour, follow it
714 collectSingleContour(x0, y0, grid, x, y, w, h, path);
715 }
716 }
717 delete [] grid;
718}
719
720#undef GRID
721#undef SET
722
723
725 QPainterPath *path, QTextItem::RenderFlags flags)
726{
727// TODO what to do with 'flags' ??
729 QFixed advanceX = QFixed::fromReal(x);
730 QFixed advanceY = QFixed::fromReal(y);
731 for (int i=0; i < glyphs.numGlyphs; ++i) {
733 if (metrics.width.value() == 0 || metrics.height.value() == 0) {
734 advanceX += glyphs.advances[i];
735 continue;
736 }
737 const QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]);
738
739 const int w = alphaMask.width();
740 const int h = alphaMask.height();
741 const qsizetype srcBpl = alphaMask.bytesPerLine();
743 if (alphaMask.depth() == 1) {
744 bitmap = alphaMask;
745 } else {
747 const uchar *imageData = alphaMask.bits();
748 const qsizetype destBpl = bitmap.bytesPerLine();
749 uchar *bitmapData = bitmap.bits();
750
751 for (int yi = 0; yi < h; ++yi) {
752 const uchar *src = imageData + yi*srcBpl;
753 uchar *dst = bitmapData + yi*destBpl;
754 for (int xi = 0; xi < w; ++xi) {
755 const int byte = xi / 8;
756 const int bit = xi % 8;
757 if (bit == 0)
758 dst[byte] = 0;
759 if (src[xi])
760 dst[byte] |= 128 >> bit;
761 }
762 }
763 }
764 const uchar *bitmap_data = bitmap.constBits();
765 QFixedPoint offset = glyphs.offsets[i];
766 advanceX += offset.x;
767 advanceY += offset.y;
768 qt_addBitmapToPath((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, bitmap.bytesPerLine(), w, h, path);
769 advanceX += glyphs.advances[i];
770 }
771}
772
774 QPainterPath *path, QTextItem::RenderFlags flags)
775{
776 qreal x = positions[0].x.toReal();
777 qreal y = positions[0].y.toReal();
779
780 for (int i = 0; i < nGlyphs - 1; ++i) {
781 g.glyphs[i] = glyphs[i];
782 g.advances[i] = positions[i + 1].x - positions[i].x;
783 }
784 g.glyphs[nGlyphs - 1] = glyphs[nGlyphs - 1];
785 g.advances[nGlyphs - 1] = QFixed::fromReal(maxCharWidth());
786
788}
789
790QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/)
791{
792 // For font engines don't support subpixel positioning
793 return alphaMapForGlyph(glyph);
794}
795
797{
798 QImage i = alphaMapForGlyph(glyph);
799 if (t.type() > QTransform::TxTranslate)
800 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
801 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
802
803 return i;
804}
805
807{
809 return alphaMapForGlyph(glyph, t);
810
811 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
812 if (t.type() > QTransform::TxTranslate)
813 i = i.transformed(t).convertToFormat(QImage::Format_Alpha8);
814 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
815
816 return i;
817}
818
819QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, const QFixedPoint &/*subPixelPosition*/, const QTransform &t)
820{
821 const QImage alphaMask = alphaMapForGlyph(glyph, t);
822 QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
823
824 for (int y=0; y<alphaMask.height(); ++y) {
825 uint *dst = (uint *) rgbMask.scanLine(y);
826 const uchar *src = alphaMask.constScanLine(y);
827 for (int x=0; x<alphaMask.width(); ++x) {
828 int val = src[x];
829 dst[x] = qRgb(val, val, val);
830 }
831 }
832
833 return rgbMask;
834}
835
836QImage QFontEngine::bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform&, const QColor &)
837{
838 Q_UNUSED(subPixelPosition);
839
840 return QImage();
841}
842
844{
848 return QFixedPoint();
849 }
850
851 auto f = [&](QFixed v) {
852 if (v != 0) {
853 v = v - v.floor() + QFixed::fromFixed(1);
854 QFixed fraction = (v * m_subPixelPositionCount).floor();
855 v = fraction / QFixed(m_subPixelPositionCount);
856 }
857 return v;
858 };
859
860 return QFixedPoint(f(position.x), f(position.y));
861}
862
864 const QFixedPoint &,
866 const QTransform &)
867{
868 return nullptr;
869}
870
872{
873 glyph_metrics_t gm = boundingBox(glyph);
874 int glyph_x = qFloor(gm.x.toReal());
875 int glyph_y = qFloor(gm.y.toReal());
876 int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x;
877 int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
878
879 if (glyph_width <= 0 || glyph_height <= 0)
880 return QImage();
881 QFixedPoint pt;
882 pt.x = -glyph_x;
883 pt.y = -glyph_y; // the baseline
885 path.setFillRule(Qt::WindingFill);
886 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
888 QPainter p(&im);
889 p.setRenderHint(QPainter::Antialiasing);
890 addGlyphsToPath(&glyph, &pt, 1, &path, { });
891 p.setPen(Qt::NoPen);
892 p.setBrush(Qt::black);
893 p.drawPath(path);
894 p.end();
895
896 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
897
898 for (int y=0; y<im.height(); ++y) {
899 uchar *dst = (uchar *) alphaMap.scanLine(y);
900 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
901 for (int x=0; x<im.width(); ++x)
902 dst[x] = qAlpha(src[x]);
903 }
904
905 return alphaMap;
906}
907
911
913{
918 p.ascent = ascent();
919 p.descent = descent();
920 p.leading = leading();
921 p.emSquare = p.ascent;
922 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
923 p.italicAngle = 0;
924 p.capHeight = p.ascent;
925 p.lineWidth = lineThickness();
926 return p;
927}
928
930{
931 *metrics = boundingBox(glyph);
933 p.x = 0;
934 p.y = 0;
935 addGlyphsToPath(&glyph, &p, 1, path, QFlag(0));
936}
937
950{
951 Q_UNUSED(tag);
954 return false;
955}
956
958{
960 uint len = 0;
961 if (!getSfntTableData(tag, nullptr, &len))
962 return table;
964 if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
965 return QByteArray();
966 return table;
967}
968
969void QFontEngine::clearGlyphCache(const void *context)
970{
971 m_glyphCaches.remove(context);
972}
973
975{
977
978 GlyphCaches &caches = m_glyphCaches[context];
979 for (auto & e : caches) {
980 if (cache == e.cache.data())
981 return;
982 }
983
984 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
985 // and limits memory use when there is continuous or random rotation
986 if (caches.size() == 4)
987 caches.pop_back();
988
989 GlyphCacheEntry entry;
990 entry.cache = cache;
991 caches.push_front(entry);
992
993}
994
997 const QTransform &transform,
998 const QColor &color) const
999{
1001 if (caches == m_glyphCaches.cend())
1002 return nullptr;
1003
1004 for (auto &e : *caches) {
1005 QFontEngineGlyphCache *cache = e.cache.data();
1006 if (format == cache->glyphFormat()
1007 && (format != Format_ARGB || color == cache->color())
1009 return cache;
1010 }
1011 }
1012
1013 return nullptr;
1014}
1015
1016static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1017{
1018 uint left_right = (left << 16) + right;
1019
1020 left = 0, right = numPairs - 1;
1021 while (left <= right) {
1022 int middle = left + ( ( right - left ) >> 1 );
1023
1024 if (pairs[middle].left_right == left_right)
1025 return pairs[middle].adjust;
1026
1027 if (pairs[middle].left_right < left_right)
1028 left = middle + 1;
1029 else
1030 right = middle - 1;
1031 }
1032 return 0;
1033}
1034
1035void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1036{
1037 int numPairs = kerning_pairs.size();
1038 if (!numPairs)
1039 return;
1040
1041 const KernPair *pairs = kerning_pairs.constData();
1042
1043 if (flags & DesignMetrics) {
1044 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1045 glyphs->advances[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
1046 } else {
1047 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1048 glyphs->advances[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
1049 }
1050}
1051
1053{
1054 kerning_pairs.clear();
1055
1056 QByteArray tab = getSfntTable(QFont::Tag("kern").value());
1057 if (tab.isEmpty())
1058 return;
1059
1060 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1061 const uchar *end = table + tab.size();
1062
1063 quint16 version;
1064 if (!qSafeFromBigEndian(table, end, &version))
1065 return;
1066
1067 if (version != 0) {
1068// qDebug("wrong version");
1069 return;
1070 }
1071
1072 quint16 numTables;
1073 if (!qSafeFromBigEndian(table + 2, end, &numTables))
1074 return;
1075
1076 {
1077 int offset = 4;
1078 for(int i = 0; i < numTables; ++i) {
1079 const uchar *header = table + offset;
1080
1081 quint16 version;
1082 if (!qSafeFromBigEndian(header, end, &version))
1083 goto end;
1084
1086 if (!qSafeFromBigEndian(header + 2, end, &length))
1087 goto end;
1088
1089 quint16 coverage;
1090 if (!qSafeFromBigEndian(header + 4, end, &coverage))
1091 goto end;
1092
1093// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1094 if (version == 0 && coverage == 0x0001) {
1095 if (offset + length > tab.size()) {
1096// qDebug("length ouf ot bounds");
1097 goto end;
1098 }
1099 const uchar *data = table + offset + 6;
1100
1101 quint16 nPairs;
1102 if (!qSafeFromBigEndian(data, end, &nPairs))
1103 goto end;
1104
1105 if (nPairs * 6 + 8 > length - 6) {
1106// qDebug("corrupt table!");
1107 // corrupt table
1108 goto end;
1109 }
1110
1111 int off = 8;
1112 for(int i = 0; i < nPairs; ++i) {
1114
1115 quint16 tmp;
1116 if (!qSafeFromBigEndian(data + off, end, &tmp))
1117 goto end;
1118
1119 p.left_right = uint(tmp) << 16;
1120 if (!qSafeFromBigEndian(data + off + 2, end, &tmp))
1121 goto end;
1122
1123 p.left_right |= tmp;
1124
1125 if (!qSafeFromBigEndian(data + off + 4, end, &tmp))
1126 goto end;
1127
1128 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1129 kerning_pairs.append(p);
1130 off += 6;
1131 }
1132 }
1133 offset += length;
1134 }
1135 }
1136end:
1137 std::sort(kerning_pairs.begin(), kerning_pairs.end());
1138// for (int i = 0; i < kerning_pairs.count(); ++i)
1139// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1140}
1141
1142
1144{
1145 QByteArray maxpTable = getSfntTable(QFont::Tag("maxp").value());
1146 if (maxpTable.size() < 6)
1147 return 0;
1148
1149 const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1150 const uchar *end = source + maxpTable.size();
1151
1152 quint16 count = 0;
1154 return count;
1155}
1156
1158{
1159 return nullptr;
1160}
1161
1162const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1163{
1164 const uchar *header = table;
1165 const uchar *endPtr = table + tableSize;
1166
1167 // version check
1168 quint16 version;
1169 if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
1170 return nullptr;
1171
1172 quint16 numTables;
1173 if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
1174 return nullptr;
1175
1176 const uchar *maps = table + 4;
1177
1178 enum {
1179 Invalid,
1180 AppleRoman,
1181 Symbol,
1182 Unicode11,
1183 Unicode,
1184 MicrosoftUnicode,
1185 MicrosoftUnicodeExtended
1186 };
1187
1188 int symbolTable = -1;
1189 int tableToUse = -1;
1190 int score = Invalid;
1191 for (int n = 0; n < numTables; ++n) {
1192 quint16 platformId = 0;
1193 if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
1194 return nullptr;
1195
1196 quint16 platformSpecificId = 0;
1197 if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
1198 return nullptr;
1199
1200 switch (platformId) {
1201 case 0: // Unicode
1202 if (score < Unicode &&
1203 (platformSpecificId == 0 ||
1204 platformSpecificId == 2 ||
1205 platformSpecificId == 3)) {
1206 tableToUse = n;
1207 score = Unicode;
1208 } else if (score < Unicode11 && platformSpecificId == 1) {
1209 tableToUse = n;
1210 score = Unicode11;
1211 }
1212 break;
1213 case 1: // Apple
1214 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1215 tableToUse = n;
1216 score = AppleRoman;
1217 }
1218 break;
1219 case 3: // Microsoft
1220 switch (platformSpecificId) {
1221 case 0:
1222 symbolTable = n;
1223 if (score < Symbol) {
1224 tableToUse = n;
1225 score = Symbol;
1226 }
1227 break;
1228 case 1:
1229 if (score < MicrosoftUnicode) {
1230 tableToUse = n;
1231 score = MicrosoftUnicode;
1232 }
1233 break;
1234 case 0xa:
1235 if (score < MicrosoftUnicodeExtended) {
1236 tableToUse = n;
1237 score = MicrosoftUnicodeExtended;
1238 }
1239 break;
1240 default:
1241 break;
1242 }
1243 break;
1244 default:
1245 break;
1246 }
1247 }
1248 if (tableToUse < 0)
1249 return nullptr;
1250
1251resolveTable:
1252 *isSymbolFont = (symbolTable > -1);
1253
1254 quint32 unicode_table = 0;
1255 if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1256 return nullptr;
1257
1258 if (!unicode_table)
1259 return nullptr;
1260
1261 // get the header of the unicode table
1262 header = table + unicode_table;
1263
1265 if (!qSafeFromBigEndian(header, endPtr, &format))
1266 return nullptr;
1267
1269 if (format < 8) {
1270 quint16 tmp;
1271 if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1272 return nullptr;
1273 length = tmp;
1274 } else {
1275 if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1276 return nullptr;
1277 }
1278
1279 if (table + unicode_table + length > endPtr)
1280 return nullptr;
1281 *cmapSize = length;
1282
1283 // To support symbol fonts that contain a unicode table for the symbol area
1284 // we check the cmap tables and fall back to symbol font unless that would
1285 // involve losing information from the unicode table
1286 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1287 const uchar *selectedTable = table + unicode_table;
1288
1289 // Check that none of the latin1 range are in the unicode table
1290 bool unicodeTableHasLatin1 = false;
1291 for (int uc=0x00; uc<0x100; ++uc) {
1292 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1293 unicodeTableHasLatin1 = true;
1294 break;
1295 }
1296 }
1297
1298 // Check that at least one symbol char is in the unicode table
1299 bool unicodeTableHasSymbols = false;
1300 if (!unicodeTableHasLatin1) {
1301 for (int uc=0xf000; uc<0xf100; ++uc) {
1302 if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1303 unicodeTableHasSymbols = true;
1304 break;
1305 }
1306 }
1307 }
1308
1309 // Fall back to symbol table
1310 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1311 tableToUse = symbolTable;
1312 score = Symbol;
1313 goto resolveTable;
1314 }
1315 }
1316
1317 return table + unicode_table;
1318}
1319
1320quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1321{
1322 const uchar *end = cmap + cmapSize;
1323 quint16 format = 0;
1324 if (!qSafeFromBigEndian(cmap, end, &format))
1325 return 0;
1326
1327 if (format == 0) {
1328 const uchar *ptr = cmap + 6 + unicode;
1329 if (unicode < 256 && ptr < end)
1330 return quint32(*ptr);
1331 } else if (format == 4) {
1332 /* some fonts come with invalid cmap tables, where the last segment
1333 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1334 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1335 by returning 0 for 0xffff
1336 */
1337 if (unicode >= 0xffff)
1338 return 0;
1339
1340 quint16 segCountX2 = 0;
1341 if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1342 return 0;
1343
1344 const unsigned char *ends = cmap + 14;
1345
1346 int i = 0;
1347 for (; i < segCountX2/2; ++i) {
1348 quint16 codePoint = 0;
1349 if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1350 return 0;
1351 if (codePoint >= unicode)
1352 break;
1353 }
1354
1355 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1356
1357 quint16 startIndex = 0;
1358 if (!qSafeFromBigEndian(idx, end, &startIndex))
1359 return 0;
1360 if (startIndex > unicode)
1361 return 0;
1362
1363 idx += segCountX2;
1364
1365 quint16 tmp = 0;
1366 if (!qSafeFromBigEndian(idx, end, &tmp))
1367 return 0;
1368 qint16 idDelta = qint16(tmp);
1369
1370 idx += segCountX2;
1371
1372 quint16 idRangeoffset_t = 0;
1373 if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1374 return 0;
1375
1376 quint16 glyphIndex = 0;
1377 if (idRangeoffset_t) {
1378 quint16 id = 0;
1379 if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1380 return 0;
1381
1382 if (id)
1383 glyphIndex = (idDelta + id) % 0x10000;
1384 else
1385 glyphIndex = 0;
1386 } else {
1387 glyphIndex = (idDelta + unicode) % 0x10000;
1388 }
1389 return glyphIndex;
1390 } else if (format == 6) {
1391 quint16 tableSize = 0;
1392 if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1393 return 0;
1394
1395 quint16 firstCode6 = 0;
1396 if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1397 return 0;
1398 if (unicode < firstCode6)
1399 return 0;
1400
1401 quint16 entryCount6 = 0;
1402 if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1403 return 0;
1404 if (entryCount6 * 2 + 10 > tableSize)
1405 return 0;
1406
1407 quint16 sentinel6 = firstCode6 + entryCount6;
1408 if (unicode >= sentinel6)
1409 return 0;
1410
1411 quint16 entryIndex6 = unicode - firstCode6;
1412
1413 quint16 index = 0;
1414 qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1415 return index;
1416 } else if (format == 12) {
1417 quint32 nGroups = 0;
1418 if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1419 return 0;
1420
1421 cmap += 16; // move to start of groups
1422
1423 int left = 0, right = nGroups - 1;
1424 while (left <= right) {
1425 int middle = left + ( ( right - left ) >> 1 );
1426
1427 quint32 startCharCode = 0;
1428 if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1429 return 0;
1430
1431 if (unicode < startCharCode)
1432 right = middle - 1;
1433 else {
1434 quint32 endCharCode = 0;
1435 if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1436 return 0;
1437
1438 if (unicode <= endCharCode) {
1439 quint32 index = 0;
1440 if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1441 return 0;
1442
1443 return index + unicode - startCharCode;
1444 }
1445 left = middle + 1;
1446 }
1447 }
1448 } else {
1449 qDebug("cmap table of format %d not implemented", format);
1450 }
1451
1452 return 0;
1453}
1454
1456{
1457 QByteArray f = family;
1458 f.replace(' ', "");
1459 f.replace('(', "");
1460 f.replace(')', "");
1461 f.replace('<', "");
1462 f.replace('>', "");
1463 f.replace('[', "");
1464 f.replace(']', "");
1465 f.replace('{', "");
1466 f.replace('}', "");
1467 f.replace('/', "");
1468 f.replace('%', "");
1469 return f;
1470}
1471
1472// Allow font engines (e.g. Windows) that can not reliably create
1473// outline paths for distance-field rendering to switch the scene
1474// graph over to native text rendering.
1476{
1477 // Color glyphs (Emoji) are generally not suited for outlining
1479}
1480
1482{
1483 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1484 glyph_t glyph = glyphs.glyphs[i];
1485 glyph_metrics_t gi = boundingBox(glyph);
1486 if (gi.isValid() && gi.width > 0)
1487 return gi.leftBearing();
1488 }
1489 return 0;
1490}
1491
1493{
1494 if (glyphs.numGlyphs >= 1) {
1495 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1496 glyph_metrics_t gi = boundingBox(glyph);
1497 if (gi.isValid())
1498 return gi.rightBearing();
1499 }
1500 return 0;
1501}
1502
1503
1504QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1505{
1506}
1507
1508QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1509 : cache(o.cache)
1510{
1511}
1512
1513QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1514{
1515}
1516
1517QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1518{
1519 cache = o.cache;
1520 return *this;
1521}
1522
1523// ------------------------------------------------------------------
1524// The box font engine
1525// ------------------------------------------------------------------
1526
1528 : QFontEngine(Box),
1529 _size(size)
1530{
1531 cache_cost = sizeof(QFontEngineBox);
1532}
1533
1535 : QFontEngine(type),
1536 _size(size)
1537{
1538 cache_cost = sizeof(QFontEngineBox);
1539}
1540
1544
1546{
1547 Q_UNUSED(ucs4);
1548 return 1;
1549}
1550
1551int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1552{
1553 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1554 if (*nglyphs < len) {
1555 *nglyphs = len;
1556 return -1;
1557 }
1558
1559 int ucs4Length = 0;
1561 while (it.hasNext()) {
1562 it.advance();
1563 glyphs->glyphs[ucs4Length++] = 1;
1564 }
1565
1566 *nglyphs = ucs4Length;
1567 glyphs->numGlyphs = ucs4Length;
1568
1569 if (!(flags & GlyphIndicesOnly))
1570 recalcAdvances(glyphs, flags);
1571
1572 return *nglyphs;
1573}
1574
1575void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1576{
1577 for (int i = 0; i < glyphs->numGlyphs; i++)
1578 glyphs->advances[i] = _size;
1579}
1580
1581void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1582{
1583 if (!glyphs.numGlyphs)
1584 return;
1585
1586 QVarLengthArray<QFixedPoint> positions;
1587 QVarLengthArray<glyph_t> positioned_glyphs;
1589 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1590
1591 QSize s(_size - 3, _size - 3);
1592 for (int k = 0; k < positions.size(); k++)
1593 path->addRect(QRectF(positions[k].toPointF(), s));
1594}
1595
1597{
1598 glyph_metrics_t overall;
1599 overall.width = _size*glyphs.numGlyphs;
1600 overall.height = _size;
1601 overall.xoff = overall.width;
1602 return overall;
1603}
1604
1606{
1607 if (!ti.glyphs.numGlyphs)
1608 return;
1609
1610 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1611 QSize s(_size - 3, _size - 3);
1612
1613 QVarLengthArray<QFixedPoint> positions;
1614 QVarLengthArray<glyph_t> glyphs;
1616 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1617 if (glyphs.size() == 0)
1618 return;
1619
1620
1621 QPainter *painter = p->painter();
1622 painter->save();
1624 QPen pen = painter->pen();
1625 pen.setWidthF(lineThickness().toReal());
1626 painter->setPen(pen);
1627 for (int k = 0; k < positions.size(); k++)
1628 painter->drawRect(QRectF(positions[k].toPointF(), s));
1629 painter->restore();
1630}
1631
1633{
1634 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1635}
1636
1638{
1639 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1640 return fe;
1641}
1642
1644{
1645 return _size;
1646}
1647
1649{
1650 return _size;
1651}
1652
1654{
1655 return 0;
1656}
1657
1659{
1660 QFixed l = _size * QFixed::fromReal(qreal(0.15));
1661 return l.ceil();
1662}
1663
1665{
1666 return _size;
1667}
1668
1669bool QFontEngineBox::canRender(const QChar *, int) const
1670{
1671 return true;
1672}
1673
1675{
1676 QImage image(_size, _size, QImage::Format_Alpha8);
1677 image.fill(0);
1678
1679 uchar *bits = image.bits();
1680 for (int i=2; i <= _size-3; ++i) {
1681 bits[i + 2 * image.bytesPerLine()] = 255;
1682 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1683 bits[2 + i * image.bytesPerLine()] = 255;
1684 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1685 }
1686 return image;
1687}
1688
1689// ------------------------------------------------------------------
1690// Multi engine
1691// ------------------------------------------------------------------
1692
1694{ return glyph >> 24; }
1695
1696// strip high byte from glyph
1697static inline glyph_t stripped(glyph_t glyph)
1698{ return glyph & 0x00ffffff; }
1699
1701 : QFontEngine(Multi),
1702 m_fallbackFamilies(fallbackFamilies),
1703 m_script(script),
1704 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1705{
1707
1708 if (m_fallbackFamilies.isEmpty()) {
1709 // defer obtaining the fallback families until loadEngine(1)
1710 m_fallbackFamilies << QString();
1711 }
1712
1713 m_engines.resize(m_fallbackFamilies.size() + 1);
1714
1715 engine->ref.ref();
1716 m_engines[0] = engine;
1717
1718 fontDef = engine->fontDef;
1719 cache_cost = engine->cache_cost;
1720}
1721
1723{
1724 for (int i = 0; i < m_engines.size(); ++i) {
1725 QFontEngine *fontEngine = m_engines.at(i);
1726 if (fontEngine && !fontEngine->ref.deref())
1727 delete fontEngine;
1728 }
1729}
1730
1731QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script);
1732
1734{
1736 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1737 styleHint = QFont::TypeWriter;
1738
1740 QFont::Style(fontDef.style), styleHint,
1741 QChar::Script(m_script)));
1742}
1743
1745{
1746 Q_ASSERT(!m_fallbackFamiliesQueried);
1747
1748 m_fallbackFamilies = fallbackFamilies;
1749 if (m_fallbackFamilies.isEmpty()) {
1750 // turns out we lied about having any fallback at all
1751 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
1752 QFontEngine *engine = m_engines.at(0);
1753 engine->ref.ref();
1754 m_engines[1] = engine;
1755 m_fallbackFamilies << fontDef.families.constFirst();
1756 } else {
1757 m_engines.resize(m_fallbackFamilies.size() + 1);
1758 }
1759
1760 m_fallbackFamiliesQueried = true;
1761}
1762
1764{
1765 if (!m_fallbackFamiliesQueried && at > 0)
1767 Q_ASSERT(at < m_engines.size());
1768 if (!m_engines.at(at)) {
1770 if (!engine)
1773 engine->ref.ref();
1774 m_engines[at] = engine;
1775 }
1776}
1777
1779{
1781 request.styleStrategy |= QFont::NoFontMerging;
1782 request.families = QStringList(fallbackFamilyAt(at - 1));
1783
1784 // At this point, the main script of the text has already been considered
1785 // when fetching the list of fallback families from the database, and the
1786 // info about the actual script of the characters may have been discarded,
1787 // so we do not check for writing system support, but instead just load
1788 // the family indiscriminately.
1789 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QChar::Script_Common)) {
1790 engine->fontDef.weight = request.weight;
1791 if (request.style > QFont::StyleNormal)
1792 engine->fontDef.style = request.style;
1793 return engine;
1794 }
1795
1796 return nullptr;
1797}
1798
1800{
1801 glyph_t glyph = engine(0)->glyphIndex(ucs4);
1802 if (glyph == 0 && !isIgnorableChar(ucs4)) {
1803 if (!m_fallbackFamiliesQueried)
1804 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1805 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
1806 QFontEngine *engine = m_engines.at(x);
1807 if (!engine) {
1809 continue;
1810 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
1811 engine = m_engines.at(x);
1812 }
1813 Q_ASSERT(engine != nullptr);
1814 if (engine->type() == Box)
1815 continue;
1816
1817 glyph = engine->glyphIndex(ucs4);
1818 if (glyph != 0) {
1819 // set the high byte to indicate which engine the glyph came from
1820 glyph |= (x << 24);
1821 break;
1822 }
1823 }
1824 }
1825
1826 return glyph;
1827}
1828
1830 QGlyphLayout *glyphs, int *nglyphs,
1831 QFontEngine::ShaperFlags flags) const
1832{
1833 const int originalNumGlyphs = glyphs->numGlyphs;
1834 int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
1835 if (mappedGlyphCount < 0)
1836 return -1;
1837
1838 // If ContextFontMerging is set and the match for the string was incomplete, we try all
1839 // fallbacks on the full string until we find the best match.
1840 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
1841 if (contextFontMerging) {
1842 QVarLengthGlyphLayoutArray tempLayout(len);
1843 if (!m_fallbackFamiliesQueried)
1844 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1845
1846 int maxGlyphCount = 0;
1847 uchar engineIndex = 0;
1848 for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
1849 int numGlyphs = len;
1850 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
1851 maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
1852
1853 // If we found a better match, we copy data into the main QGlyphLayout
1854 if (maxGlyphCount > mappedGlyphCount) {
1855 *nglyphs = numGlyphs;
1856 glyphs->numGlyphs = originalNumGlyphs;
1857 glyphs->copy(&tempLayout);
1858 engineIndex = x;
1859 if (maxGlyphCount == numGlyphs)
1860 break;
1861 }
1862 }
1863
1864 if (engineIndex > 0) {
1865 for (int y = 0; y < glyphs->numGlyphs; ++y) {
1866 if (glyphs->glyphs[y] != 0)
1867 glyphs->glyphs[y] |= (engineIndex << 24);
1868 }
1869 } else {
1870 contextFontMerging = false;
1871 }
1872
1873 mappedGlyphCount = maxGlyphCount;
1874 }
1875
1876 // Fill in missing glyphs by going through string one character at the time and finding
1877 // the first viable fallback.
1878 int glyph_pos = 0;
1880
1881 int lastFallback = -1;
1882 char32_t previousUcs4 = 0;
1883 while (it.hasNext()) {
1884 const char32_t ucs4 = it.peekNext();
1885
1886 // If we applied a fallback font to previous glyph, and the current is either
1887 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
1888 // to get the correct shaping rules applied.
1889 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
1890 QFontEngine *engine = m_engines.at(lastFallback);
1891 glyph_t glyph = engine->glyphIndex(ucs4);
1892 if (glyph != 0) {
1893 glyphs->glyphs[glyph_pos] = glyph;
1894 if (!(flags & GlyphIndicesOnly)) {
1895 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
1896 engine->recalcAdvances(&g, flags);
1897 }
1898
1899 // set the high byte to indicate which engine the glyph came from
1900 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
1901 } else {
1902 lastFallback = -1;
1903 }
1904 } else {
1905 lastFallback = -1;
1906 }
1907
1908 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
1909 if (!m_fallbackFamiliesQueried)
1910 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1911 for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) {
1912 QFontEngine *engine = m_engines.at(x);
1913 if (!engine) {
1915 continue;
1916 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
1917 engine = m_engines.at(x);
1918 if (!engine)
1919 continue;
1920 }
1921 Q_ASSERT(engine != nullptr);
1922 if (engine->type() == Box)
1923 continue;
1924
1925 glyph_t glyph = engine->glyphIndex(ucs4);
1926 if (glyph != 0) {
1927 glyphs->glyphs[glyph_pos] = glyph;
1928 if (!(flags & GlyphIndicesOnly)) {
1929 QGlyphLayout g = glyphs->mid(glyph_pos, 1);
1930 engine->recalcAdvances(&g, flags);
1931 }
1932
1933 lastFallback = x;
1934
1935 // set the high byte to indicate which engine the glyph came from
1936 glyphs->glyphs[glyph_pos] |= (x << 24);
1937 break;
1938 }
1939 }
1940
1941 // For variant-selectors, they are modifiers to the previous character. If we
1942 // end up with different font selections for the selector and the character it
1943 // modifies, we try applying the selector font to the preceding character as well
1944 const int variantSelectorBlock = 0xFE00;
1945 if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
1946 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
1947 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
1948
1949 if (selectorFontEngine != precedingCharacterFontEngine) {
1950 // Emoji variant selectors are specially handled and should affect font
1951 // selection. If VS-16 is used, then this means we want to select a color
1952 // font. If the selected font is already a color font, we do not need search
1953 // again. If the VS-15 is used, then this means we want to select a non-color
1954 // font. If the selected font is not a color font, we don't do anything.
1955 const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
1956 const bool colorFont = selectedEngine->isColorFont();
1957 const char32_t vs15 = 0xFE0E;
1958 const char32_t vs16 = 0xFE0F;
1959 bool adaptVariantSelector = ucs4 < vs15
1960 || (ucs4 == vs15 && colorFont)
1961 || (ucs4 == vs16 && !colorFont);
1962
1963 if (adaptVariantSelector) {
1964 QFontEngine *engine = m_engines.at(selectorFontEngine);
1965 glyph_t glyph = engine->glyphIndex(previousUcs4);
1966 if (glyph != 0) {
1967 glyphs->glyphs[glyph_pos - 1] = glyph;
1968 if (!(flags & GlyphIndicesOnly)) {
1969 QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
1970 engine->recalcAdvances(&g, flags);
1971 }
1972
1973 // set the high byte to indicate which engine the glyph came from
1974 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
1975 }
1976 }
1977 }
1978 }
1979 }
1980
1981 it.advance();
1982 ++glyph_pos;
1983 previousUcs4 = ucs4;
1984 }
1985
1986 *nglyphs = glyph_pos;
1987 glyphs->numGlyphs = glyph_pos;
1988 return mappedGlyphCount;
1989}
1990
1992{
1993 Q_UNUSED(at);
1994 Q_UNUSED(ucs4);
1995 return true;
1996}
1997
1999{
2000 if (glyphs.numGlyphs <= 0)
2001 return glyph_metrics_t();
2002
2003 glyph_metrics_t overall;
2004
2005 int which = highByte(glyphs.glyphs[0]);
2006 int start = 0;
2007 int end, i;
2008 for (end = 0; end < glyphs.numGlyphs; ++end) {
2009 const int e = highByte(glyphs.glyphs[end]);
2010 if (e == which)
2011 continue;
2012
2013 // set the high byte to zero
2014 for (i = start; i < end; ++i)
2015 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2016
2017 // merge the bounding box for this run
2018 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2019
2020 overall.x = qMin(overall.x, gm.x);
2021 overall.y = qMin(overall.y, gm.y);
2022 overall.width = overall.xoff + gm.width;
2023 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2024 qMin(overall.y, gm.y);
2025 overall.xoff += gm.xoff;
2026 overall.yoff += gm.yoff;
2027
2028 // reset the high byte for all glyphs
2029 const int hi = which << 24;
2030 for (i = start; i < end; ++i)
2031 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2032
2033 // change engine
2034 start = end;
2035 which = e;
2036 }
2037
2038 // set the high byte to zero
2039 for (i = start; i < end; ++i)
2040 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2041
2042 // merge the bounding box for this run
2043 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2044
2045 overall.x = qMin(overall.x, gm.x);
2046 overall.y = qMin(overall.y, gm.y);
2047 overall.width = overall.xoff + gm.width;
2048 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2049 qMin(overall.y, gm.y);
2050 overall.xoff += gm.xoff;
2051 overall.yoff += gm.yoff;
2052
2053 // reset the high byte for all glyphs
2054 const int hi = which << 24;
2055 for (i = start; i < end; ++i)
2056 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2057
2058 return overall;
2059}
2060
2061void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2062{
2063 int which = highByte(glyph);
2064 ensureEngineAt(which);
2065 engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2066}
2067
2069 QPainterPath *path, QTextItem::RenderFlags flags)
2070{
2071 if (glyphs.numGlyphs <= 0)
2072 return;
2073
2074 int which = highByte(glyphs.glyphs[0]);
2075 int start = 0;
2076 int end, i;
2078 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2079 x += glyphs.advances[gl].toReal();
2080 }
2081 for (end = 0; end < glyphs.numGlyphs; ++end) {
2082 const int e = highByte(glyphs.glyphs[end]);
2083 if (e == which)
2084 continue;
2085
2087 for (i = start; i < end; ++i)
2088 x -= glyphs.advances[i].toReal();
2089 }
2090
2091 // set the high byte to zero
2092 for (i = start; i < end; ++i)
2093 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2094 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2095 // reset the high byte for all glyphs and update x and y
2096 const int hi = which << 24;
2097 for (i = start; i < end; ++i)
2098 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2099
2100 if (!(flags & QTextItem::RightToLeft)) {
2101 for (i = start; i < end; ++i)
2102 x += glyphs.advances[i].toReal();
2103 }
2104
2105 // change engine
2106 start = end;
2107 which = e;
2108 }
2109
2111 for (i = start; i < end; ++i)
2112 x -= glyphs.advances[i].toReal();
2113 }
2114
2115 // set the high byte to zero
2116 for (i = start; i < end; ++i)
2117 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2118
2119 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2120
2121 // reset the high byte for all glyphs
2122 const int hi = which << 24;
2123 for (i = start; i < end; ++i)
2124 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2125}
2126
2127void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2128{
2129 if (glyphs->numGlyphs <= 0)
2130 return;
2131
2132 int which = highByte(glyphs->glyphs[0]);
2133 int start = 0;
2134 int end, i;
2135 for (end = 0; end < glyphs->numGlyphs; ++end) {
2136 const int e = highByte(glyphs->glyphs[end]);
2137 if (e == which)
2138 continue;
2139
2140 // set the high byte to zero
2141 for (i = start; i < end; ++i)
2142 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2143
2144 QGlyphLayout offs = glyphs->mid(start, end - start);
2145 engine(which)->recalcAdvances(&offs, flags);
2146
2147 // reset the high byte for all glyphs and update x and y
2148 const int hi = which << 24;
2149 for (i = start; i < end; ++i)
2150 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2151
2152 // change engine
2153 start = end;
2154 which = e;
2155 }
2156
2157 // set the high byte to zero
2158 for (i = start; i < end; ++i)
2159 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2160
2161 QGlyphLayout offs = glyphs->mid(start, end - start);
2162 engine(which)->recalcAdvances(&offs, flags);
2163
2164 // reset the high byte for all glyphs
2165 const int hi = which << 24;
2166 for (i = start; i < end; ++i)
2167 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2168}
2169
2170void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2171{
2172 if (glyphs->numGlyphs <= 0)
2173 return;
2174
2175 int which = highByte(glyphs->glyphs[0]);
2176 int start = 0;
2177 int end, i;
2178 for (end = 0; end < glyphs->numGlyphs; ++end) {
2179 const int e = highByte(glyphs->glyphs[end]);
2180 if (e == which)
2181 continue;
2182
2183 // set the high byte to zero
2184 for (i = start; i < end; ++i)
2185 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2186
2187 QGlyphLayout offs = glyphs->mid(start, end - start);
2188 engine(which)->doKerning(&offs, flags);
2189
2190 // reset the high byte for all glyphs and update x and y
2191 const int hi = which << 24;
2192 for (i = start; i < end; ++i)
2193 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2194
2195 // change engine
2196 start = end;
2197 which = e;
2198 }
2199
2200 // set the high byte to zero
2201 for (i = start; i < end; ++i)
2202 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2203
2204 QGlyphLayout offs = glyphs->mid(start, end - start);
2205 engine(which)->doKerning(&offs, flags);
2206
2207 // reset the high byte for all glyphs
2208 const int hi = which << 24;
2209 for (i = start; i < end; ++i)
2210 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2211}
2212
2214{
2215 const int which = highByte(glyph);
2216 return engine(which)->boundingBox(stripped(glyph));
2217}
2218
2220{ return engine(0)->ascent(); }
2221
2223{ return engine(0)->capHeight(); }
2224
2226{ return engine(0)->descent(); }
2227
2229{
2230 return engine(0)->leading();
2231}
2232
2234{
2235 return engine(0)->xHeight();
2236}
2237
2239{
2240 return engine(0)->averageCharWidth();
2241}
2242
2244{
2245 return engine(0)->lineThickness();
2246}
2247
2249{
2250 return engine(0)->underlinePosition();
2251}
2252
2254{
2255 return engine(0)->maxCharWidth();
2256}
2257
2259{
2260 return engine(0)->minLeftBearing();
2261}
2262
2264{
2265 return engine(0)->minRightBearing();
2266}
2267
2268bool QFontEngineMulti::canRender(const QChar *string, int len) const
2269{
2270 if (engine(0)->canRender(string, len))
2271 return true;
2272
2273 int nglyphs = len;
2274
2275 QVarLengthArray<glyph_t> glyphs(nglyphs);
2276
2278 g.numGlyphs = nglyphs;
2279 g.glyphs = glyphs.data();
2280 if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2281 Q_UNREACHABLE();
2282
2283 for (int i = 0; i < nglyphs; i++) {
2284 if (glyphs[i] == 0)
2285 return false;
2286 }
2287
2288 return true;
2289}
2290
2291/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2292 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2293
2295{
2296 const int which = highByte(glyph);
2297 return engine(which)->alphaMapForGlyph(stripped(glyph));
2298}
2299
2301{
2302 const int which = highByte(glyph);
2303 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2304}
2305
2307{
2308 const int which = highByte(glyph);
2309 return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2310}
2311
2313 const QFixedPoint &subPixelPosition,
2314 const QTransform &t)
2315{
2316 const int which = highByte(glyph);
2317 return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2318}
2319
2321 const QFixedPoint &subPixelPosition,
2322 const QTransform &t)
2323{
2324 const int which = highByte(glyph);
2325 return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2326}
2327
2328/*
2329 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2330
2331 The purpose of this is to provide the necessary font fallbacks when drawing complex
2332 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2333 the same raw font over and over again, we want to cache the corresponding multi font engine
2334 as it may contain fallback font engines already.
2335*/
2337{
2338 QFontEngine *engine = nullptr;
2339 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2341 // We can't rely on the fontDef (and hence the cache Key)
2342 // alone to distinguish webfonts, since these should not be
2343 // accidentally shared, even if the resulting fontcache key
2344 // is strictly identical. See:
2345 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2346 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2347 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2348 end = fc->engineCache.end();
2349 while (it != end && it.key() == key) {
2350 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2351 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2352 if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2353 engine = cachedEngine;
2354 fc->updateHitCountAndTimeStamp(it.value());
2355 break;
2356 }
2357 ++it;
2358 }
2359 if (!engine) {
2360 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QChar::Script(script));
2361 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2362 }
2364 return engine;
2365}
2366
2368 : QFontEngineBox(TestFontEngine, size)
2369{}
2370
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:495
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:340
\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:3353
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
bool preferTypoLineMetrics() 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
@ PreferTypoLineMetrics
Definition qfont.h:51
@ ContextFontMerging
Definition qfont.h:50
@ NoFontMerging
Definition qfont.h:52
Style
This enum describes the different styles of glyphs that are used to display text.
Definition qfont.h:77
@ StyleNormal
Definition qfont.h:78
static QGuiApplicationPrivate * instance()
\inmodule QtCore
Definition qhash.h:1146
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:959
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1300
const_iterator cend() const noexcept
Definition qhash.h:1219
\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:398
const_reference at(qsizetype i) const noexcept
Definition qlist.h:447
void resize(qsizetype size)
Definition qlist.h:404
\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:521
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:160
\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
QList< QString > QStringList
Constructs a string list that contains the given string, str.
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:165
#define qWarning
Definition qlogging.h:167
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
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
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:216
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