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
29QT_BEGIN_NAMESPACE
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...
38 Q_ASSERT(a.type() < QTransform::TxProject);
39 Q_ASSERT(b.type() < QTransform::TxProject);
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);
61 Q_UNUSED(flags);
62 Q_UNUSED(point);
63 Q_UNUSED(xpos);
64 Q_UNUSED(ypos);
65 Q_UNUSED(nPoints);
66 return Err_Not_Covered;
67}
68
69static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
70{
71 QFontEngine *fe = (QFontEngine *)user_data;
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
101QFontEngine::QFontEngine(Type type)
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
117 glyphFormat = Format_None;
118 m_subPixelPositionCount = 0;
119
120#ifdef QT_BUILD_INTERNAL
121 if (enginesCollector)
122 enginesCollector->append(t: this);
123#endif
124}
125
126QFontEngine::~QFontEngine()
127{
128#ifdef QT_BUILD_INTERNAL
129 if (enginesCollector)
130 enginesCollector->removeOne(t: this);
131#endif
132}
133
134QFixed QFontEngine::lineThickness() const
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
147QFixed QFontEngine::underlinePosition() const
148{
149 return ((lineThickness() * 2) + 3) / 6;
150}
151
152void *QFontEngine::harfbuzzFont() const
153{
154 Q_ASSERT(type() != QFontEngine::Multi);
155#if QT_CONFIG(harfbuzz)
156 return hb_qt_font_get_for_engine(fe: const_cast<QFontEngine *>(this));
157#else
158 return nullptr;
159#endif
160}
161
162void *QFontEngine::harfbuzzFace() const
163{
164 Q_ASSERT(type() != QFontEngine::Multi);
165#if QT_CONFIG(harfbuzz)
166 return hb_qt_face_get_for_engine(fe: 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(tag: QFont::Tag("mort").value(), buffer: nullptr, length: &lenMort)
187 || getSfntTableData(tag: QFont::Tag("morx").value(), buffer: nullptr, length: &lenMorx)) {
188 return true;
189 }
190
191 if (hb_face_t *face = hb_qt_face_get_for_engine(fe: 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(script: hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
196 script_count: &script_count, script_tags,
197 language_count: nullptr, language_tags: nullptr);
198
199 if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, script_index: nullptr, chosen_script: nullptr))
200 return true;
201 }
202#endif
203 return false;
204}
205
206bool QFontEngine::canRender(const QChar *str, int len) const
207{
208 QStringIterator it(str, str + len);
209 while (it.hasNext()) {
210 if (glyphIndex(ucs4: it.next()) == 0)
211 return false;
212 }
213
214 return true;
215}
216
217glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
218{
219 glyph_metrics_t metrics = boundingBox(glyph);
220
221 if (matrix.type() > QTransform::TxTranslate) {
222 return metrics.transformed(xform: matrix);
223 }
224 return metrics;
225}
226
227QFixed QFontEngine::calculatedCapHeight() const
228{
229 const glyph_t glyph = glyphIndex(ucs4: 'H');
230 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
231 return bb.height;
232}
233
234QFixed QFontEngine::xHeight() const
235{
236 const glyph_t glyph = glyphIndex(ucs4: 'x');
237 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
238 return bb.height;
239}
240
241QFixed QFontEngine::averageCharWidth() const
242{
243 const glyph_t glyph = glyphIndex(ucs4: 'x');
244 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
245 return bb.xoff;
246}
247
248bool QFontEngine::supportsTransformation(const QTransform &transform) const
249{
250 return transform.type() < QTransform::TxProject;
251}
252
253bool QFontEngine::expectsGammaCorrectedBlending() const
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(r: matrix.dx());
270 ypos = QFixed::fromReal(r: matrix.dy());
271 }
272
273 int current = 0;
274 if (flags & QTextItem::RightToLeft) {
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(fixed: glyphs.justifications[i].space_18d6);
281 totalKashidas += glyphs.justifications[i].nKashidas;
282 }
283 positions.resize(sz: glyphs.numGlyphs+totalKashidas);
284 glyphs_out.resize(sz: 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(r: gpos.x());
300 gpos_y = QFixed::fromReal(r: 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(ucs4: ch.unicode());
310 QFixed kashidaWidth;
311
312 QGlyphLayout g;
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(r: gpos.x());
327 gpos_y = QFixed::fromReal(r: 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 {
335 xpos -= QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
336 }
337 ++i;
338 }
339 } else {
340 positions.resize(sz: glyphs.numGlyphs);
341 glyphs_out.resize(sz: 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(fixed: 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(r: gpos.x());
362 positions[current].y = QFixed::fromReal(r: gpos.y());
363 glyphs_out[current] = glyphs.glyphs[i];
364 xpos += glyphs.advances[i] + QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
365 ++current;
366 }
367 ++i;
368 }
369 }
370 }
371 positions.resize(sz: current);
372 glyphs_out.resize(sz: 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
385bool QFontEngine::processHheaTable() const
386{
387 QByteArray hhea = getSfntTable(tag: QFont::Tag("hhea").value());
388 if (hhea.size() >= 10) {
389 auto ptr = hhea.constData();
390 qint16 ascent = qFromBigEndian<qint16>(src: ptr + 4);
391 qint16 descent = qFromBigEndian<qint16>(src: ptr + 6);
392 qint16 leading = qFromBigEndian<qint16>(src: 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();
399 m_ascent = QFixed::fromReal(r: ascent * fontDef.pixelSize) / unitsPerEm;
400 m_descent = -QFixed::fromReal(r: descent * fontDef.pixelSize) / unitsPerEm;
401
402 m_leading = QFixed::fromReal(r: leading * fontDef.pixelSize) / unitsPerEm;
403
404 return true;
405 }
406
407 return false;
408}
409
410void QFontEngine::initializeHeightMetrics() const
411{
412 bool hasEmbeddedBitmaps =
413 !getSfntTable(tag: QFont::Tag("EBLC").value()).isEmpty()
414 || !getSfntTable(tag: QFont::Tag("CBLC").value()).isEmpty()
415 || !getSfntTable(tag: QFont::Tag("bdat").value()).isEmpty();
416 if (!hasEmbeddedBitmaps) {
417 // Get HHEA table values if available
418 processHheaTable();
419
420 // Allow OS/2 metrics to override if present
421 processOS2Table();
422
423 if (!supportsSubPixelPositions()) {
424 m_ascent = m_ascent.round();
425 m_descent = m_descent.round();
426 m_leading = m_leading.round();
427 }
428 }
429
430 m_heightMetricsQueried = true;
431}
432
433bool QFontEngine::preferTypoLineMetrics() const
434{
435 return (fontDef.styleStrategy & QFont::PreferTypoLineMetrics) != 0;
436}
437
438bool QFontEngine::processOS2Table() const
439{
440 QByteArray os2 = getSfntTable(tag: QFont::Tag("OS/2").value());
441 if (os2.size() >= 78) {
442 auto ptr = os2.constData();
443 quint16 fsSelection = qFromBigEndian<quint16>(src: ptr + 62);
444 qint16 typoAscent = qFromBigEndian<qint16>(src: ptr + 68);
445 qint16 typoDescent = qFromBigEndian<qint16>(src: ptr + 70);
446 qint16 typoLineGap = qFromBigEndian<qint16>(src: ptr + 72);
447 quint16 winAscent = qFromBigEndian<quint16>(src: ptr + 74);
448 quint16 winDescent = qFromBigEndian<quint16>(src: 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(r: typoAscent * fontDef.pixelSize) / unitsPerEm;
457 m_descent = -QFixed::fromReal(r: typoDescent * fontDef.pixelSize) / unitsPerEm;
458 m_leading = QFixed::fromReal(r: 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(r: winAscent * fontDef.pixelSize) / unitsPerEm;
464 m_descent = QFixed::fromReal(r: winDescent * fontDef.pixelSize) / unitsPerEm;
465 m_leading = QFixed{};
466 }
467
468 return true;
469 }
470
471 return false;
472}
473
474QFixed QFontEngine::leading() const
475{
476 if (!m_heightMetricsQueried)
477 initializeHeightMetrics();
478
479 return m_leading;
480}
481
482QFixed QFontEngine::ascent() const
483{
484 if (!m_heightMetricsQueried)
485 initializeHeightMetrics();
486
487 return m_ascent;
488}
489
490QFixed QFontEngine::descent() const
491{
492 if (!m_heightMetricsQueried)
493 initializeHeightMetrics();
494
495 return m_descent;
496}
497
498qreal QFontEngine::minLeftBearing() const
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
511qreal QFontEngine::minRightBearing() const
512{
513 if (m_minRightBearing == kBearingNotInitialized) {
514
515 // Try the 'hhea' font table first, which covers the entire font
516 QByteArray hheaTable = getSfntTable(tag: 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>(src: tableData + kMinLeftSideBearingOffset);
522 qint16 minRightSideBearing = qFromBigEndian<qint16>(src: 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(t: minLeftSideBearing) < largestValidBearing)
537 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
538 if (qAbs(t: 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(ucs4: 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(a: m_minLeftBearing, b: glyphMetrics.leftBearing().toReal());
568 m_minRightBearing = qMin(a: m_minRightBearing, b: 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
580glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs)
581{
582 QFixed w;
583 for (int i = 0; i < glyphs.numGlyphs; ++i)
584 w += glyphs.effectiveAdvance(item: 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
590glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
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(glyph: 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(a: overall.x, b: x);
604 overall.y = qMin(a: overall.y, b: y);
605 xmax = qMax(a: xmax, b: x.ceil() + bb.width);
606 ymax = qMax(a: ymax, b: y.ceil() + bb.height);
607 overall.xoff += glyphs.effectiveAdvance(item: i);
608 overall.yoff += bb.yoff;
609 }
610 overall.height = qMax(a: overall.height, b: ymax - overall.y);
611 overall.width = xmax - overall.x;
612
613 return overall;
614}
615
616
617void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
618 QTextItem::RenderFlags flags)
619{
620 if (!glyphs.numGlyphs)
621 return;
622
623 QVarLengthArray<QFixedPoint> positions;
624 QVarLengthArray<glyph_t> positioned_glyphs;
625 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y);
626 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
627 addGlyphsToPath(glyphs: positioned_glyphs.data(), positions: positions.data(), nglyphs: 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
637};
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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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
724void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
725 QPainterPath *path, QTextItem::RenderFlags flags)
726{
727// TODO what to do with 'flags' ??
728 Q_UNUSED(flags);
729 QFixed advanceX = QFixed::fromReal(r: x);
730 QFixed advanceY = QFixed::fromReal(r: y);
731 for (int i=0; i < glyphs.numGlyphs; ++i) {
732 glyph_metrics_t metrics = boundingBox(glyph: glyphs.glyphs[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();
742 QImage bitmap;
743 if (alphaMask.depth() == 1) {
744 bitmap = alphaMask;
745 } else {
746 bitmap = QImage(w, h, QImage::Format_Mono);
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(x0: (advanceX + metrics.x).toReal(), y0: (advanceY + metrics.y).toReal(), image_data: bitmap_data, bpl: bitmap.bytesPerLine(), w, h, path);
769 advanceX += glyphs.advances[i];
770 }
771}
772
773void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
774 QPainterPath *path, QTextItem::RenderFlags flags)
775{
776 qreal x = positions[0].x.toReal();
777 qreal y = positions[0].y.toReal();
778 QVarLengthGlyphLayoutArray g(nGlyphs);
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(r: maxCharWidth());
786
787 addBitmapFontToPath(x, y, glyphs: g, path, flags);
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
796QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
797{
798 QImage i = alphaMapForGlyph(glyph);
799 if (t.type() > QTransform::TxTranslate)
800 i = i.transformed(matrix: t).convertToFormat(f: QImage::Format_Alpha8);
801 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
802
803 return i;
804}
805
806QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t)
807{
808 if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions())
809 return alphaMapForGlyph(glyph, t);
810
811 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
812 if (t.type() > QTransform::TxTranslate)
813 i = i.transformed(matrix: t).convertToFormat(f: 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(r: val, g: val, b: 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
843QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const
844{
845 if (m_subPixelPositionCount <= 1
846 || (!supportsHorizontalSubPixelPositions()
847 && !supportsVerticalSubPixelPositions())) {
848 return QFixedPoint();
849 }
850
851 auto f = [&](QFixed v) {
852 if (v != 0) {
853 v = v - v.floor() + QFixed::fromFixed(fixed: 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
863QFontEngine::Glyph *QFontEngine::glyphData(glyph_t,
864 const QFixedPoint &,
865 QFontEngine::GlyphFormat,
866 const QTransform &)
867{
868 return nullptr;
869}
870
871QImage QFontEngine::renderedPathForGlyph(glyph_t glyph, const QColor &color)
872{
873 glyph_metrics_t gm = boundingBox(glyph);
874 int glyph_x = qFloor(v: gm.x.toReal());
875 int glyph_y = qFloor(v: gm.y.toReal());
876 int glyph_width = qCeil(v: (gm.x + gm.width).toReal()) - glyph_x;
877 int glyph_height = qCeil(v: (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
884 QPainterPath path;
885 path.setFillRule(Qt::WindingFill);
886 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
887 im.fill(color: Qt::transparent);
888 QPainter p(&im);
889 p.setRenderHint(hint: QPainter::Antialiasing);
890 addGlyphsToPath(glyphs: &glyph, positions: &pt, nGlyphs: 1, path: &path, flags: { });
891 p.setPen(Qt::NoPen);
892 p.setBrush(color);
893 p.drawPath(path);
894 p.end();
895
896 return im;
897}
898
899QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
900{
901 QImage im = renderedPathForGlyph(glyph, color: Qt::black);
902 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
903
904 for (int y=0; y<im.height(); ++y) {
905 uchar *dst = (uchar *) alphaMap.scanLine(y);
906 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
907 for (int x=0; x<im.width(); ++x)
908 dst[x] = qAlpha(rgb: src[x]);
909 }
910
911 return alphaMap;
912}
913
914void QFontEngine::removeGlyphFromCache(glyph_t)
915{
916}
917
918QFontEngine::Properties QFontEngine::properties() const
919{
920 Properties p;
921 p.postscriptName =
922 QFontEngine::convertToPostscriptFontFamilyName(fontFamily: fontDef.families.first().toUtf8()) + '-'
923 + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight);
924 p.ascent = ascent();
925 p.descent = descent();
926 p.leading = leading();
927 p.emSquare = p.ascent;
928 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
929 p.italicAngle = 0;
930 p.capHeight = p.ascent;
931 p.lineWidth = lineThickness();
932 return p;
933}
934
935void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
936{
937 *metrics = boundingBox(glyph);
938 QFixedPoint p;
939 p.x = 0;
940 p.y = 0;
941 addGlyphsToPath(glyphs: &glyph, positions: &p, nGlyphs: 1, path, flags: QFlag(0));
942}
943
944/*!
945 Returns \c true if the font table idetified by \a tag exists in the font;
946 returns \c false otherwise.
947
948 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
949 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
950 of the buffer, passed in \a length, is sufficient to store the font table data,
951 also copies the font table data to \a buffer.
952
953 Note: returning \c false when the font table exists could lead to an undefined behavior.
954*/
955bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
956{
957 Q_UNUSED(tag);
958 Q_UNUSED(buffer);
959 Q_UNUSED(length);
960 return false;
961}
962
963QByteArray QFontEngine::getSfntTable(uint tag) const
964{
965 QByteArray table;
966 uint len = 0;
967 if (!getSfntTableData(tag, buffer: nullptr, length: &len))
968 return table;
969 table.resize(size: len);
970 if (!getSfntTableData(tag, buffer: reinterpret_cast<uchar *>(table.data()), length: &len))
971 return QByteArray();
972 return table;
973}
974
975void QFontEngine::clearGlyphCache(const void *context)
976{
977 m_glyphCaches.remove(key: context);
978}
979
980void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
981{
982 Q_ASSERT(cache);
983
984 GlyphCaches &caches = m_glyphCaches[context];
985 for (auto & e : caches) {
986 if (cache == e.cache.data())
987 return;
988 }
989
990 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
991 // and limits memory use when there is continuous or random rotation
992 if (caches.size() == 4)
993 caches.pop_back();
994
995 GlyphCacheEntry entry;
996 entry.cache = cache;
997 caches.push_front(x: entry);
998
999}
1000
1001QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
1002 GlyphFormat format,
1003 const QTransform &transform,
1004 const QColor &color) const
1005{
1006 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(key: context);
1007 if (caches == m_glyphCaches.cend())
1008 return nullptr;
1009
1010 for (auto &e : *caches) {
1011 QFontEngineGlyphCache *cache = e.cache.data();
1012 if (format == cache->glyphFormat()
1013 && (format != Format_ARGB || color == cache->color())
1014 && qtransform_equals_no_translate(a: cache->m_transform, b: transform)) {
1015 return cache;
1016 }
1017 }
1018
1019 return nullptr;
1020}
1021
1022static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1023{
1024 uint left_right = (left << 16) + right;
1025
1026 left = 0, right = numPairs - 1;
1027 while (left <= right) {
1028 int middle = left + ( ( right - left ) >> 1 );
1029
1030 if (pairs[middle].left_right == left_right)
1031 return pairs[middle].adjust;
1032
1033 if (pairs[middle].left_right < left_right)
1034 left = middle + 1;
1035 else
1036 right = middle - 1;
1037 }
1038 return 0;
1039}
1040
1041void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1042{
1043 int numPairs = kerning_pairs.size();
1044 if (!numPairs)
1045 return;
1046
1047 const KernPair *pairs = kerning_pairs.constData();
1048
1049 if (flags & DesignMetrics) {
1050 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1051 glyphs->advances[i] += kerning(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs);
1052 } else {
1053 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1054 glyphs->advances[i] += qRound(f: kerning(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs));
1055 }
1056}
1057
1058void QFontEngine::loadKerningPairs(QFixed scalingFactor)
1059{
1060 kerning_pairs.clear();
1061
1062 QByteArray tab = getSfntTable(tag: QFont::Tag("kern").value());
1063 if (tab.isEmpty())
1064 return;
1065
1066 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1067 const uchar *end = table + tab.size();
1068
1069 quint16 version;
1070 if (!qSafeFromBigEndian(source: table, end, output: &version))
1071 return;
1072
1073 if (version != 0) {
1074// qDebug("wrong version");
1075 return;
1076 }
1077
1078 quint16 numTables;
1079 if (!qSafeFromBigEndian(source: table + 2, end, output: &numTables))
1080 return;
1081
1082 {
1083 int offset = 4;
1084 for(int i = 0; i < numTables; ++i) {
1085 const uchar *header = table + offset;
1086
1087 quint16 version;
1088 if (!qSafeFromBigEndian(source: header, end, output: &version))
1089 goto end;
1090
1091 quint16 length;
1092 if (!qSafeFromBigEndian(source: header + 2, end, output: &length))
1093 goto end;
1094
1095 quint16 coverage;
1096 if (!qSafeFromBigEndian(source: header + 4, end, output: &coverage))
1097 goto end;
1098
1099// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1100 if (version == 0 && coverage == 0x0001) {
1101 if (offset + length > tab.size()) {
1102// qDebug("length ouf ot bounds");
1103 goto end;
1104 }
1105 const uchar *data = table + offset + 6;
1106
1107 quint16 nPairs;
1108 if (!qSafeFromBigEndian(source: data, end, output: &nPairs))
1109 goto end;
1110
1111 if (nPairs * 6 + 8 > length - 6) {
1112// qDebug("corrupt table!");
1113 // corrupt table
1114 goto end;
1115 }
1116
1117 int off = 8;
1118 for(int i = 0; i < nPairs; ++i) {
1119 QFontEngine::KernPair p;
1120
1121 quint16 tmp;
1122 if (!qSafeFromBigEndian(source: data + off, end, output: &tmp))
1123 goto end;
1124
1125 p.left_right = uint(tmp) << 16;
1126 if (!qSafeFromBigEndian(source: data + off + 2, end, output: &tmp))
1127 goto end;
1128
1129 p.left_right |= tmp;
1130
1131 if (!qSafeFromBigEndian(source: data + off + 4, end, output: &tmp))
1132 goto end;
1133
1134 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1135 kerning_pairs.append(t: p);
1136 off += 6;
1137 }
1138 }
1139 offset += length;
1140 }
1141 }
1142end:
1143 std::sort(first: kerning_pairs.begin(), last: kerning_pairs.end());
1144// for (int i = 0; i < kerning_pairs.count(); ++i)
1145// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1146}
1147
1148
1149int QFontEngine::glyphCount() const
1150{
1151 QByteArray maxpTable = getSfntTable(tag: QFont::Tag("maxp").value());
1152 if (maxpTable.size() < 6)
1153 return 0;
1154
1155 const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1156 const uchar *end = source + maxpTable.size();
1157
1158 quint16 count = 0;
1159 qSafeFromBigEndian(source, end, output: &count);
1160 return count;
1161}
1162
1163Qt::HANDLE QFontEngine::handle() const
1164{
1165 return nullptr;
1166}
1167
1168const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1169{
1170 const uchar *header = table;
1171 const uchar *endPtr = table + tableSize;
1172
1173 // version check
1174 quint16 version;
1175 if (!qSafeFromBigEndian(source: header, end: endPtr, output: &version) || version != 0)
1176 return nullptr;
1177
1178 quint16 numTables;
1179 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &numTables))
1180 return nullptr;
1181
1182 const uchar *maps = table + 4;
1183
1184 enum {
1185 Invalid,
1186 AppleRoman,
1187 Symbol,
1188 Unicode11,
1189 Unicode,
1190 MicrosoftUnicode,
1191 MicrosoftUnicodeExtended
1192 };
1193
1194 int symbolTable = -1;
1195 int tableToUse = -1;
1196 int score = Invalid;
1197 for (int n = 0; n < numTables; ++n) {
1198 quint16 platformId = 0;
1199 if (!qSafeFromBigEndian(source: maps + 8 * n, end: endPtr, output: &platformId))
1200 return nullptr;
1201
1202 quint16 platformSpecificId = 0;
1203 if (!qSafeFromBigEndian(source: maps + 8 * n + 2, end: endPtr, output: &platformSpecificId))
1204 return nullptr;
1205
1206 switch (platformId) {
1207 case 0: // Unicode
1208 if (score < Unicode &&
1209 (platformSpecificId == 0 ||
1210 platformSpecificId == 2 ||
1211 platformSpecificId == 3)) {
1212 tableToUse = n;
1213 score = Unicode;
1214 } else if (score < Unicode11 && platformSpecificId == 1) {
1215 tableToUse = n;
1216 score = Unicode11;
1217 }
1218 break;
1219 case 1: // Apple
1220 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1221 tableToUse = n;
1222 score = AppleRoman;
1223 }
1224 break;
1225 case 3: // Microsoft
1226 switch (platformSpecificId) {
1227 case 0:
1228 symbolTable = n;
1229 if (score < Symbol) {
1230 tableToUse = n;
1231 score = Symbol;
1232 }
1233 break;
1234 case 1:
1235 if (score < MicrosoftUnicode) {
1236 tableToUse = n;
1237 score = MicrosoftUnicode;
1238 }
1239 break;
1240 case 0xa:
1241 if (score < MicrosoftUnicodeExtended) {
1242 tableToUse = n;
1243 score = MicrosoftUnicodeExtended;
1244 }
1245 break;
1246 default:
1247 break;
1248 }
1249 break;
1250 default:
1251 break;
1252 }
1253 }
1254 if (tableToUse < 0)
1255 return nullptr;
1256
1257resolveTable:
1258 *isSymbolFont = (symbolTable > -1);
1259
1260 quint32 unicode_table = 0;
1261 if (!qSafeFromBigEndian(source: maps + 8 * tableToUse + 4, end: endPtr, output: &unicode_table))
1262 return nullptr;
1263
1264 if (!unicode_table)
1265 return nullptr;
1266
1267 // get the header of the unicode table
1268 header = table + unicode_table;
1269
1270 quint16 format;
1271 if (!qSafeFromBigEndian(source: header, end: endPtr, output: &format))
1272 return nullptr;
1273
1274 quint32 length;
1275 if (format < 8) {
1276 quint16 tmp;
1277 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &tmp))
1278 return nullptr;
1279 length = tmp;
1280 } else {
1281 if (!qSafeFromBigEndian(source: header + 4, end: endPtr, output: &length))
1282 return nullptr;
1283 }
1284
1285 if (table + unicode_table + length > endPtr)
1286 return nullptr;
1287 *cmapSize = length;
1288
1289 // To support symbol fonts that contain a unicode table for the symbol area
1290 // we check the cmap tables and fall back to symbol font unless that would
1291 // involve losing information from the unicode table
1292 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1293 const uchar *selectedTable = table + unicode_table;
1294
1295 // Check that none of the latin1 range are in the unicode table
1296 bool unicodeTableHasLatin1 = false;
1297 for (int uc=0x00; uc<0x100; ++uc) {
1298 if (getTrueTypeGlyphIndex(cmap: selectedTable, cmapSize: length, unicode: uc) != 0) {
1299 unicodeTableHasLatin1 = true;
1300 break;
1301 }
1302 }
1303
1304 // Check that at least one symbol char is in the unicode table
1305 bool unicodeTableHasSymbols = false;
1306 if (!unicodeTableHasLatin1) {
1307 for (int uc=0xf000; uc<0xf100; ++uc) {
1308 if (getTrueTypeGlyphIndex(cmap: selectedTable, cmapSize: length, unicode: uc) != 0) {
1309 unicodeTableHasSymbols = true;
1310 break;
1311 }
1312 }
1313 }
1314
1315 // Fall back to symbol table
1316 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1317 tableToUse = symbolTable;
1318 score = Symbol;
1319 goto resolveTable;
1320 }
1321 }
1322
1323 return table + unicode_table;
1324}
1325
1326quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1327{
1328 const uchar *end = cmap + cmapSize;
1329 quint16 format = 0;
1330 if (!qSafeFromBigEndian(source: cmap, end, output: &format))
1331 return 0;
1332
1333 if (format == 0) {
1334 const uchar *ptr = cmap + 6 + unicode;
1335 if (unicode < 256 && ptr < end)
1336 return quint32(*ptr);
1337 } else if (format == 4) {
1338 /* some fonts come with invalid cmap tables, where the last segment
1339 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1340 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1341 by returning 0 for 0xffff
1342 */
1343 if (unicode >= 0xffff)
1344 return 0;
1345
1346 quint16 segCountX2 = 0;
1347 if (!qSafeFromBigEndian(source: cmap + 6, end, output: &segCountX2))
1348 return 0;
1349
1350 const unsigned char *ends = cmap + 14;
1351
1352 int i = 0;
1353 for (; i < segCountX2/2; ++i) {
1354 quint16 codePoint = 0;
1355 if (!qSafeFromBigEndian(source: ends + 2 * i, end, output: &codePoint))
1356 return 0;
1357 if (codePoint >= unicode)
1358 break;
1359 }
1360
1361 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1362
1363 quint16 startIndex = 0;
1364 if (!qSafeFromBigEndian(source: idx, end, output: &startIndex))
1365 return 0;
1366 if (startIndex > unicode)
1367 return 0;
1368
1369 idx += segCountX2;
1370
1371 quint16 tmp = 0;
1372 if (!qSafeFromBigEndian(source: idx, end, output: &tmp))
1373 return 0;
1374 qint16 idDelta = qint16(tmp);
1375
1376 idx += segCountX2;
1377
1378 quint16 idRangeoffset_t = 0;
1379 if (!qSafeFromBigEndian(source: idx, end, output: &idRangeoffset_t))
1380 return 0;
1381
1382 quint16 glyphIndex = 0;
1383 if (idRangeoffset_t) {
1384 quint16 id = 0;
1385 if (!qSafeFromBigEndian(source: idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, output: &id))
1386 return 0;
1387
1388 if (id)
1389 glyphIndex = (idDelta + id) % 0x10000;
1390 else
1391 glyphIndex = 0;
1392 } else {
1393 glyphIndex = (idDelta + unicode) % 0x10000;
1394 }
1395 return glyphIndex;
1396 } else if (format == 6) {
1397 quint16 tableSize = 0;
1398 if (!qSafeFromBigEndian(source: cmap + 2, end, output: &tableSize))
1399 return 0;
1400
1401 quint16 firstCode6 = 0;
1402 if (!qSafeFromBigEndian(source: cmap + 6, end, output: &firstCode6))
1403 return 0;
1404 if (unicode < firstCode6)
1405 return 0;
1406
1407 quint16 entryCount6 = 0;
1408 if (!qSafeFromBigEndian(source: cmap + 8, end, output: &entryCount6))
1409 return 0;
1410 if (entryCount6 * 2 + 10 > tableSize)
1411 return 0;
1412
1413 quint16 sentinel6 = firstCode6 + entryCount6;
1414 if (unicode >= sentinel6)
1415 return 0;
1416
1417 quint16 entryIndex6 = unicode - firstCode6;
1418
1419 quint16 index = 0;
1420 qSafeFromBigEndian(source: cmap + 10 + (entryIndex6 * 2), end, output: &index);
1421 return index;
1422 } else if (format == 12) {
1423 quint32 nGroups = 0;
1424 if (!qSafeFromBigEndian(source: cmap + 12, end, output: &nGroups))
1425 return 0;
1426
1427 cmap += 16; // move to start of groups
1428
1429 int left = 0, right = nGroups - 1;
1430 while (left <= right) {
1431 int middle = left + ( ( right - left ) >> 1 );
1432
1433 quint32 startCharCode = 0;
1434 if (!qSafeFromBigEndian(source: cmap + 12 * middle, end, output: &startCharCode))
1435 return 0;
1436
1437 if (unicode < startCharCode)
1438 right = middle - 1;
1439 else {
1440 quint32 endCharCode = 0;
1441 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 4, end, output: &endCharCode))
1442 return 0;
1443
1444 if (unicode <= endCharCode) {
1445 quint32 index = 0;
1446 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 8, end, output: &index))
1447 return 0;
1448
1449 return index + unicode - startCharCode;
1450 }
1451 left = middle + 1;
1452 }
1453 }
1454 } else {
1455 qDebug(msg: "cmap table of format %d not implemented", format);
1456 }
1457
1458 return 0;
1459}
1460
1461QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1462{
1463 QByteArray f = family;
1464 f.replace(before: ' ', after: "");
1465 f.replace(before: '(', after: "");
1466 f.replace(before: ')', after: "");
1467 f.replace(before: '<', after: "");
1468 f.replace(before: '>', after: "");
1469 f.replace(before: '[', after: "");
1470 f.replace(before: ']', after: "");
1471 f.replace(before: '{', after: "");
1472 f.replace(before: '}', after: "");
1473 f.replace(before: '/', after: "");
1474 f.replace(before: '%', after: "");
1475 return f;
1476}
1477
1478// Allow font engines (e.g. Windows) that can not reliably create
1479// outline paths for distance-field rendering to switch the scene
1480// graph over to native text rendering.
1481bool QFontEngine::hasUnreliableGlyphOutline() const
1482{
1483 // Color glyphs (Emoji) are generally not suited for outlining
1484 return glyphFormat == QFontEngine::Format_ARGB;
1485}
1486
1487QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1488{
1489 for (int i = 0; i < glyphs.numGlyphs; ++i) {
1490 glyph_t glyph = glyphs.glyphs[i];
1491 glyph_metrics_t gi = boundingBox(glyph);
1492 if (gi.isValid() && gi.width > 0)
1493 return gi.leftBearing();
1494 }
1495 return 0;
1496}
1497
1498QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1499{
1500 if (glyphs.numGlyphs >= 1) {
1501 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1502 glyph_metrics_t gi = boundingBox(glyph);
1503 if (gi.isValid())
1504 return gi.rightBearing();
1505 }
1506 return 0;
1507}
1508
1509
1510QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1511{
1512}
1513
1514QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1515 : cache(o.cache)
1516{
1517}
1518
1519QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1520{
1521}
1522
1523QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1524{
1525 cache = o.cache;
1526 return *this;
1527}
1528
1529// ------------------------------------------------------------------
1530// The box font engine
1531// ------------------------------------------------------------------
1532
1533QFontEngineBox::QFontEngineBox(int size)
1534 : QFontEngine(Box),
1535 _size(size)
1536{
1537 cache_cost = sizeof(QFontEngineBox);
1538}
1539
1540QFontEngineBox::QFontEngineBox(Type type, int size)
1541 : QFontEngine(type),
1542 _size(size)
1543{
1544 cache_cost = sizeof(QFontEngineBox);
1545}
1546
1547QFontEngineBox::~QFontEngineBox()
1548{
1549}
1550
1551glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1552{
1553 Q_UNUSED(ucs4);
1554 return 1;
1555}
1556
1557int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1558{
1559 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1560 if (*nglyphs < len) {
1561 *nglyphs = len;
1562 return -1;
1563 }
1564
1565 int ucs4Length = 0;
1566 QStringIterator it(str, str + len);
1567 while (it.hasNext()) {
1568 it.advance();
1569 glyphs->glyphs[ucs4Length++] = 1;
1570 }
1571
1572 *nglyphs = ucs4Length;
1573 glyphs->numGlyphs = ucs4Length;
1574
1575 if (!(flags & GlyphIndicesOnly))
1576 recalcAdvances(glyphs, flags);
1577
1578 return *nglyphs;
1579}
1580
1581void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1582{
1583 for (int i = 0; i < glyphs->numGlyphs; i++)
1584 glyphs->advances[i] = _size;
1585}
1586
1587void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1588{
1589 if (!glyphs.numGlyphs)
1590 return;
1591
1592 QVarLengthArray<QFixedPoint> positions;
1593 QVarLengthArray<glyph_t> positioned_glyphs;
1594 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1595 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
1596
1597 QSize s(_size - 3, _size - 3);
1598 for (int k = 0; k < positions.size(); k++)
1599 path->addRect(rect: QRectF(positions[k].toPointF(), s));
1600}
1601
1602glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1603{
1604 glyph_metrics_t overall;
1605 overall.width = _size*glyphs.numGlyphs;
1606 overall.height = _size;
1607 overall.xoff = overall.width;
1608 return overall;
1609}
1610
1611void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1612{
1613 if (!ti.glyphs.numGlyphs)
1614 return;
1615
1616 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1617 QSize s(_size - 3, _size - 3);
1618
1619 QVarLengthArray<QFixedPoint> positions;
1620 QVarLengthArray<glyph_t> glyphs;
1621 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1622 ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions);
1623 if (glyphs.size() == 0)
1624 return;
1625
1626
1627 QPainter *painter = p->painter();
1628 painter->save();
1629 painter->setBrush(Qt::NoBrush);
1630 QPen pen = painter->pen();
1631 pen.setWidthF(lineThickness().toReal());
1632 painter->setPen(pen);
1633 for (int k = 0; k < positions.size(); k++)
1634 painter->drawRect(rect: QRectF(positions[k].toPointF(), s));
1635 painter->restore();
1636}
1637
1638glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1639{
1640 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1641}
1642
1643QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1644{
1645 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1646 return fe;
1647}
1648
1649QFixed QFontEngineBox::ascent() const
1650{
1651 return _size;
1652}
1653
1654QFixed QFontEngineBox::capHeight() const
1655{
1656 return _size;
1657}
1658
1659QFixed QFontEngineBox::descent() const
1660{
1661 return 0;
1662}
1663
1664QFixed QFontEngineBox::leading() const
1665{
1666 QFixed l = _size * QFixed::fromReal(r: qreal(0.15));
1667 return l.ceil();
1668}
1669
1670qreal QFontEngineBox::maxCharWidth() const
1671{
1672 return _size;
1673}
1674
1675bool QFontEngineBox::canRender(const QChar *, int) const
1676{
1677 return true;
1678}
1679
1680QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1681{
1682 QImage image(_size, _size, QImage::Format_Alpha8);
1683 image.fill(pixel: 0);
1684
1685 uchar *bits = image.bits();
1686 for (int i=2; i <= _size-3; ++i) {
1687 bits[i + 2 * image.bytesPerLine()] = 255;
1688 bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1689 bits[2 + i * image.bytesPerLine()] = 255;
1690 bits[_size - 3 + i * image.bytesPerLine()] = 255;
1691 }
1692 return image;
1693}
1694
1695// ------------------------------------------------------------------
1696// Multi engine
1697// ------------------------------------------------------------------
1698
1699uchar QFontEngineMulti::highByte(glyph_t glyph)
1700{ return glyph >> 24; }
1701
1702// strip high byte from glyph
1703static inline glyph_t stripped(glyph_t glyph)
1704{ return glyph & 0x00ffffff; }
1705
1706QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1707 : QFontEngine(Multi),
1708 m_fallbackFamilies(fallbackFamilies),
1709 m_script(script),
1710 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1711{
1712 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1713
1714 if (m_fallbackFamilies.isEmpty()) {
1715 // defer obtaining the fallback families until loadEngine(1)
1716 m_fallbackFamilies << QString();
1717 }
1718
1719 m_engines.resize(size: m_fallbackFamilies.size() + 1);
1720
1721 engine->ref.ref();
1722 m_engines[0] = engine;
1723
1724 fontDef = engine->fontDef;
1725 cache_cost = engine->cache_cost;
1726}
1727
1728QFontEngineMulti::~QFontEngineMulti()
1729{
1730 for (int i = 0; i < m_engines.size(); ++i) {
1731 QFontEngine *fontEngine = m_engines.at(i);
1732 if (fontEngine && !fontEngine->ref.deref())
1733 delete fontEngine;
1734 }
1735}
1736
1737QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script);
1738
1739void QFontEngineMulti::ensureFallbackFamiliesQueried()
1740{
1741 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
1742 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1743 styleHint = QFont::TypeWriter;
1744
1745 setFallbackFamiliesList(qt_fallbacksForFamily(family: fontDef.families.constFirst(),
1746 style: QFont::Style(fontDef.style), styleHint,
1747 script: QChar::Script(m_script)));
1748}
1749
1750void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
1751{
1752 Q_ASSERT(!m_fallbackFamiliesQueried);
1753
1754 m_fallbackFamilies = fallbackFamilies;
1755 if (m_fallbackFamilies.isEmpty()) {
1756 // turns out we lied about having any fallback at all
1757 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
1758 QFontEngine *engine = m_engines.at(i: 0);
1759 engine->ref.ref();
1760 m_engines[1] = engine;
1761 m_fallbackFamilies << fontDef.families.constFirst();
1762 } else {
1763 m_engines.resize(size: m_fallbackFamilies.size() + 1);
1764 }
1765
1766 m_fallbackFamiliesQueried = true;
1767}
1768
1769void QFontEngineMulti::ensureEngineAt(int at)
1770{
1771 if (!m_fallbackFamiliesQueried && at > 0)
1772 ensureFallbackFamiliesQueried();
1773 Q_ASSERT(at < m_engines.size());
1774 if (!m_engines.at(i: at)) {
1775 QFontEngine *engine = loadEngine(at);
1776 if (!engine)
1777 engine = new QFontEngineBox(fontDef.pixelSize);
1778 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1779 engine->ref.ref();
1780 m_engines[at] = engine;
1781 }
1782}
1783
1784QFontEngine *QFontEngineMulti::loadEngine(int at)
1785{
1786 QFontDef request(fontDef);
1787 request.styleStrategy |= QFont::NoFontMerging;
1788 request.families = QStringList(fallbackFamilyAt(at: at - 1));
1789
1790 // At this point, the main script of the text has already been considered
1791 // when fetching the list of fallback families from the database, and the
1792 // info about the actual script of the characters may have been discarded,
1793 // so we do not check for writing system support, but instead just load
1794 // the family indiscriminately.
1795 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, script: QChar::Script_Common)) {
1796 engine->fontDef.weight = request.weight;
1797 if (request.style > QFont::StyleNormal)
1798 engine->fontDef.style = request.style;
1799 return engine;
1800 }
1801
1802 return nullptr;
1803}
1804
1805glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
1806{
1807 glyph_t glyph = engine(at: 0)->glyphIndex(ucs4);
1808 if (glyph == 0 && !isIgnorableChar(ucs4)) {
1809 if (!m_fallbackFamiliesQueried)
1810 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1811 for (int x = 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1812 QFontEngine *engine = m_engines.at(i: x);
1813 if (!engine) {
1814 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1815 continue;
1816 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1817 engine = m_engines.at(i: x);
1818 }
1819 Q_ASSERT(engine != nullptr);
1820 if (engine->type() == Box)
1821 continue;
1822
1823 glyph = engine->glyphIndex(ucs4);
1824 if (glyph != 0) {
1825 // set the high byte to indicate which engine the glyph came from
1826 glyph |= (x << 24);
1827 break;
1828 }
1829 }
1830 }
1831
1832 return glyph;
1833}
1834
1835int QFontEngineMulti::stringToCMap(const QChar *str, int len,
1836 QGlyphLayout *glyphs, int *nglyphs,
1837 QFontEngine::ShaperFlags flags) const
1838{
1839 const int originalNumGlyphs = glyphs->numGlyphs;
1840 int mappedGlyphCount = engine(at: 0)->stringToCMap(str, len, glyphs, nglyphs, flags);
1841 if (mappedGlyphCount < 0)
1842 return -1;
1843
1844 // If ContextFontMerging is set and the match for the string was incomplete, we try all
1845 // fallbacks on the full string until we find the best match.
1846 bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
1847 if (contextFontMerging) {
1848 QVarLengthGlyphLayoutArray tempLayout(len);
1849 if (!m_fallbackFamiliesQueried)
1850 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1851
1852 int maxGlyphCount = 0;
1853 uchar engineIndex = 0;
1854 for (int x = 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1855 int numGlyphs = len;
1856 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1857 maxGlyphCount = engine(at: x)->stringToCMap(str, len, glyphs: &tempLayout, nglyphs: &numGlyphs, flags);
1858
1859 // If we found a better match, we copy data into the main QGlyphLayout
1860 if (maxGlyphCount > mappedGlyphCount) {
1861 *nglyphs = numGlyphs;
1862 glyphs->numGlyphs = originalNumGlyphs;
1863 glyphs->copy(other: &tempLayout);
1864 engineIndex = x;
1865 if (maxGlyphCount == numGlyphs)
1866 break;
1867 }
1868 }
1869
1870 if (engineIndex > 0) {
1871 for (int y = 0; y < glyphs->numGlyphs; ++y) {
1872 if (glyphs->glyphs[y] != 0)
1873 glyphs->glyphs[y] |= (engineIndex << 24);
1874 }
1875 } else {
1876 contextFontMerging = false;
1877 }
1878
1879 mappedGlyphCount = maxGlyphCount;
1880 }
1881
1882 // Fill in missing glyphs by going through string one character at the time and finding
1883 // the first viable fallback.
1884 int glyph_pos = 0;
1885 QStringIterator it(str, str + len);
1886
1887 int lastFallback = -1;
1888 char32_t previousUcs4 = 0;
1889 while (it.hasNext()) {
1890 const char32_t ucs4 = it.peekNext();
1891
1892 // If we applied a fallback font to previous glyph, and the current is either
1893 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
1894 // to get the correct shaping rules applied.
1895 if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
1896 QFontEngine *engine = m_engines.at(i: lastFallback);
1897 glyph_t glyph = engine->glyphIndex(ucs4);
1898 if (glyph != 0) {
1899 glyphs->glyphs[glyph_pos] = glyph;
1900 if (!(flags & GlyphIndicesOnly)) {
1901 QGlyphLayout g = glyphs->mid(position: glyph_pos, n: 1);
1902 engine->recalcAdvances(&g, flags);
1903 }
1904
1905 // set the high byte to indicate which engine the glyph came from
1906 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
1907 } else {
1908 lastFallback = -1;
1909 }
1910 } else {
1911 lastFallback = -1;
1912 }
1913
1914 if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
1915 if (!m_fallbackFamiliesQueried)
1916 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1917 for (int x = contextFontMerging ? 0 : 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1918 QFontEngine *engine = m_engines.at(i: x);
1919 if (!engine) {
1920 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1921 continue;
1922 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1923 engine = m_engines.at(i: x);
1924 if (!engine)
1925 continue;
1926 }
1927 Q_ASSERT(engine != nullptr);
1928 if (engine->type() == Box)
1929 continue;
1930
1931 glyph_t glyph = engine->glyphIndex(ucs4);
1932 if (glyph != 0) {
1933 glyphs->glyphs[glyph_pos] = glyph;
1934 if (!(flags & GlyphIndicesOnly)) {
1935 QGlyphLayout g = glyphs->mid(position: glyph_pos, n: 1);
1936 engine->recalcAdvances(&g, flags);
1937 }
1938
1939 lastFallback = x;
1940
1941 // set the high byte to indicate which engine the glyph came from
1942 glyphs->glyphs[glyph_pos] |= (x << 24);
1943 break;
1944 }
1945 }
1946
1947 // For variant-selectors, they are modifiers to the previous character. If we
1948 // end up with different font selections for the selector and the character it
1949 // modifies, we try applying the selector font to the preceding character as well
1950 const int variantSelectorBlock = 0xFE00;
1951 if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
1952 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
1953 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
1954
1955 if (selectorFontEngine != precedingCharacterFontEngine) {
1956 // Emoji variant selectors are specially handled and should affect font
1957 // selection. If VS-16 is used, then this means we want to select a color
1958 // font. If the selected font is already a color font, we do not need search
1959 // again. If the VS-15 is used, then this means we want to select a non-color
1960 // font. If the selected font is not a color font, we don't do anything.
1961 const QFontEngine *selectedEngine = m_engines.at(i: precedingCharacterFontEngine);
1962 const bool colorFont = selectedEngine->isColorFont();
1963 const char32_t vs15 = 0xFE0E;
1964 const char32_t vs16 = 0xFE0F;
1965 bool adaptVariantSelector = ucs4 < vs15
1966 || (ucs4 == vs15 && colorFont)
1967 || (ucs4 == vs16 && !colorFont);
1968
1969 if (adaptVariantSelector) {
1970 QFontEngine *engine = m_engines.at(i: selectorFontEngine);
1971 glyph_t glyph = engine->glyphIndex(ucs4: previousUcs4);
1972 if (glyph != 0) {
1973 glyphs->glyphs[glyph_pos - 1] = glyph;
1974 if (!(flags & GlyphIndicesOnly)) {
1975 QGlyphLayout g = glyphs->mid(position: glyph_pos - 1, n: 1);
1976 engine->recalcAdvances(&g, flags);
1977 }
1978
1979 // set the high byte to indicate which engine the glyph came from
1980 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
1981 }
1982 }
1983 }
1984 }
1985 }
1986
1987 it.advance();
1988 ++glyph_pos;
1989 previousUcs4 = ucs4;
1990 }
1991
1992 *nglyphs = glyph_pos;
1993 glyphs->numGlyphs = glyph_pos;
1994 return mappedGlyphCount;
1995}
1996
1997bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
1998{
1999 Q_UNUSED(at);
2000 Q_UNUSED(ucs4);
2001 return true;
2002}
2003
2004glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2005{
2006 if (glyphs.numGlyphs <= 0)
2007 return glyph_metrics_t();
2008
2009 glyph_metrics_t overall;
2010
2011 int which = highByte(glyph: glyphs.glyphs[0]);
2012 int start = 0;
2013 int end, i;
2014 for (end = 0; end < glyphs.numGlyphs; ++end) {
2015 const int e = highByte(glyph: glyphs.glyphs[end]);
2016 if (e == which)
2017 continue;
2018
2019 // set the high byte to zero
2020 for (i = start; i < end; ++i)
2021 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2022
2023 // merge the bounding box for this run
2024 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
2025
2026 overall.x = qMin(a: overall.x, b: gm.x);
2027 overall.y = qMin(a: overall.y, b: gm.y);
2028 overall.width = overall.xoff + gm.width;
2029 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
2030 qMin(a: overall.y, b: gm.y);
2031 overall.xoff += gm.xoff;
2032 overall.yoff += gm.yoff;
2033
2034 // reset the high byte for all glyphs
2035 const int hi = which << 24;
2036 for (i = start; i < end; ++i)
2037 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2038
2039 // change engine
2040 start = end;
2041 which = e;
2042 }
2043
2044 // set the high byte to zero
2045 for (i = start; i < end; ++i)
2046 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2047
2048 // merge the bounding box for this run
2049 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
2050
2051 overall.x = qMin(a: overall.x, b: gm.x);
2052 overall.y = qMin(a: overall.y, b: gm.y);
2053 overall.width = overall.xoff + gm.width;
2054 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
2055 qMin(a: overall.y, b: gm.y);
2056 overall.xoff += gm.xoff;
2057 overall.yoff += gm.yoff;
2058
2059 // reset the high byte for all glyphs
2060 const int hi = which << 24;
2061 for (i = start; i < end; ++i)
2062 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2063
2064 return overall;
2065}
2066
2067void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2068{
2069 int which = highByte(glyph);
2070 ensureEngineAt(at: which);
2071 engine(at: which)->getGlyphBearings(glyph: stripped(glyph), leftBearing, rightBearing);
2072}
2073
2074void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2075 QPainterPath *path, QTextItem::RenderFlags flags)
2076{
2077 if (glyphs.numGlyphs <= 0)
2078 return;
2079
2080 int which = highByte(glyph: glyphs.glyphs[0]);
2081 int start = 0;
2082 int end, i;
2083 if (flags & QTextItem::RightToLeft) {
2084 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2085 x += glyphs.advances[gl].toReal();
2086 }
2087 for (end = 0; end < glyphs.numGlyphs; ++end) {
2088 const int e = highByte(glyph: glyphs.glyphs[end]);
2089 if (e == which)
2090 continue;
2091
2092 if (flags & QTextItem::RightToLeft) {
2093 for (i = start; i < end; ++i)
2094 x -= glyphs.advances[i].toReal();
2095 }
2096
2097 // set the high byte to zero
2098 for (i = start; i < end; ++i)
2099 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2100 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2101 // reset the high byte for all glyphs and update x and y
2102 const int hi = which << 24;
2103 for (i = start; i < end; ++i)
2104 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2105
2106 if (!(flags & QTextItem::RightToLeft)) {
2107 for (i = start; i < end; ++i)
2108 x += glyphs.advances[i].toReal();
2109 }
2110
2111 // change engine
2112 start = end;
2113 which = e;
2114 }
2115
2116 if (flags & QTextItem::RightToLeft) {
2117 for (i = start; i < end; ++i)
2118 x -= glyphs.advances[i].toReal();
2119 }
2120
2121 // set the high byte to zero
2122 for (i = start; i < end; ++i)
2123 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2124
2125 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2126
2127 // reset the high byte for all glyphs
2128 const int hi = which << 24;
2129 for (i = start; i < end; ++i)
2130 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2131}
2132
2133void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2134{
2135 if (glyphs->numGlyphs <= 0)
2136 return;
2137
2138 int which = highByte(glyph: glyphs->glyphs[0]);
2139 int start = 0;
2140 int end, i;
2141 for (end = 0; end < glyphs->numGlyphs; ++end) {
2142 const int e = highByte(glyph: glyphs->glyphs[end]);
2143 if (e == which)
2144 continue;
2145
2146 // set the high byte to zero
2147 for (i = start; i < end; ++i)
2148 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2149
2150 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2151 engine(at: which)->recalcAdvances(&offs, flags);
2152
2153 // reset the high byte for all glyphs and update x and y
2154 const int hi = which << 24;
2155 for (i = start; i < end; ++i)
2156 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2157
2158 // change engine
2159 start = end;
2160 which = e;
2161 }
2162
2163 // set the high byte to zero
2164 for (i = start; i < end; ++i)
2165 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2166
2167 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2168 engine(at: which)->recalcAdvances(&offs, flags);
2169
2170 // reset the high byte for all glyphs
2171 const int hi = which << 24;
2172 for (i = start; i < end; ++i)
2173 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2174}
2175
2176void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2177{
2178 if (glyphs->numGlyphs <= 0)
2179 return;
2180
2181 int which = highByte(glyph: glyphs->glyphs[0]);
2182 int start = 0;
2183 int end, i;
2184 for (end = 0; end < glyphs->numGlyphs; ++end) {
2185 const int e = highByte(glyph: glyphs->glyphs[end]);
2186 if (e == which)
2187 continue;
2188
2189 // set the high byte to zero
2190 for (i = start; i < end; ++i)
2191 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2192
2193 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2194 engine(at: which)->doKerning(glyphs: &offs, flags);
2195
2196 // reset the high byte for all glyphs and update x and y
2197 const int hi = which << 24;
2198 for (i = start; i < end; ++i)
2199 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2200
2201 // change engine
2202 start = end;
2203 which = e;
2204 }
2205
2206 // set the high byte to zero
2207 for (i = start; i < end; ++i)
2208 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2209
2210 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2211 engine(at: which)->doKerning(glyphs: &offs, flags);
2212
2213 // reset the high byte for all glyphs
2214 const int hi = which << 24;
2215 for (i = start; i < end; ++i)
2216 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2217}
2218
2219glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2220{
2221 const int which = highByte(glyph);
2222 return engine(at: which)->boundingBox(glyph: stripped(glyph));
2223}
2224
2225QFixed QFontEngineMulti::ascent() const
2226{ return engine(at: 0)->ascent(); }
2227
2228QFixed QFontEngineMulti::capHeight() const
2229{ return engine(at: 0)->capHeight(); }
2230
2231QFixed QFontEngineMulti::descent() const
2232{ return engine(at: 0)->descent(); }
2233
2234QFixed QFontEngineMulti::leading() const
2235{
2236 return engine(at: 0)->leading();
2237}
2238
2239QFixed QFontEngineMulti::xHeight() const
2240{
2241 return engine(at: 0)->xHeight();
2242}
2243
2244QFixed QFontEngineMulti::averageCharWidth() const
2245{
2246 return engine(at: 0)->averageCharWidth();
2247}
2248
2249QFixed QFontEngineMulti::lineThickness() const
2250{
2251 return engine(at: 0)->lineThickness();
2252}
2253
2254QFixed QFontEngineMulti::underlinePosition() const
2255{
2256 return engine(at: 0)->underlinePosition();
2257}
2258
2259qreal QFontEngineMulti::maxCharWidth() const
2260{
2261 return engine(at: 0)->maxCharWidth();
2262}
2263
2264qreal QFontEngineMulti::minLeftBearing() const
2265{
2266 return engine(at: 0)->minLeftBearing();
2267}
2268
2269qreal QFontEngineMulti::minRightBearing() const
2270{
2271 return engine(at: 0)->minRightBearing();
2272}
2273
2274bool QFontEngineMulti::canRender(const QChar *string, int len) const
2275{
2276 if (engine(at: 0)->canRender(str: string, len))
2277 return true;
2278
2279 int nglyphs = len;
2280
2281 QVarLengthArray<glyph_t> glyphs(nglyphs);
2282
2283 QGlyphLayout g;
2284 g.numGlyphs = nglyphs;
2285 g.glyphs = glyphs.data();
2286 if (stringToCMap(str: string, len, glyphs: &g, nglyphs: &nglyphs, flags: GlyphIndicesOnly) < 0)
2287 Q_UNREACHABLE();
2288
2289 for (int i = 0; i < nglyphs; i++) {
2290 if (glyphs[i] == 0)
2291 return false;
2292 }
2293
2294 return true;
2295}
2296
2297/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2298 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2299
2300QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2301{
2302 const int which = highByte(glyph);
2303 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph));
2304}
2305
2306QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2307{
2308 const int which = highByte(glyph);
2309 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition);
2310}
2311
2312QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2313{
2314 const int which = highByte(glyph);
2315 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), t);
2316}
2317
2318QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2319 const QFixedPoint &subPixelPosition,
2320 const QTransform &t)
2321{
2322 const int which = highByte(glyph);
2323 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2324}
2325
2326QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2327 const QFixedPoint &subPixelPosition,
2328 const QTransform &t)
2329{
2330 const int which = highByte(glyph);
2331 return engine(at: which)->alphaRGBMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2332}
2333
2334/*
2335 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2336
2337 The purpose of this is to provide the necessary font fallbacks when drawing complex
2338 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2339 the same raw font over and over again, we want to cache the corresponding multi font engine
2340 as it may contain fallback font engines already.
2341*/
2342QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2343{
2344 QFontEngine *engine = nullptr;
2345 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2346 QFontCache *fc = QFontCache::instance();
2347 // We can't rely on the fontDef (and hence the cache Key)
2348 // alone to distinguish webfonts, since these should not be
2349 // accidentally shared, even if the resulting fontcache key
2350 // is strictly identical. See:
2351 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2352 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2353 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2354 end = fc->engineCache.end();
2355 while (it != end && it.key() == key) {
2356 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2357 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2358 if (fe == cachedEngine->engine(at: 0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(at: 0)->faceId().filename)) {
2359 engine = cachedEngine;
2360 fc->updateHitCountAndTimeStamp(value&: it.value());
2361 break;
2362 }
2363 ++it;
2364 }
2365 if (!engine) {
2366 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fontEngine: fe, script: QChar::Script(script));
2367 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2368 }
2369 Q_ASSERT(engine);
2370 return engine;
2371}
2372
2373QTestFontEngine::QTestFontEngine(int size)
2374 : QFontEngineBox(TestFontEngine, size)
2375{}
2376
2377QT_END_NAMESPACE
2378

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/gui/text/qfontengine.cpp