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