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
28QT_BEGIN_NAMESPACE
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...
37 Q_ASSERT(a.type() < QTransform::TxProject);
38 Q_ASSERT(b.type() < QTransform::TxProject);
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);
60 Q_UNUSED(flags);
61 Q_UNUSED(point);
62 Q_UNUSED(xpos);
63 Q_UNUSED(ypos);
64 Q_UNUSED(nPoints);
65 return Err_Not_Covered;
66}
67
68static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
69{
70 QFontEngine *fe = (QFontEngine *)user_data;
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
100QFontEngine::QFontEngine(Type type)
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;
109 faceData.get_font_table = qt_get_font_table_default;
110
111 cache_cost = 0;
112 fsType = 0;
113 symbol = false;
114 isSmoothlyScalable = false;
115
116 glyphFormat = Format_None;
117 m_subPixelPositionCount = 0;
118
119#ifdef QT_BUILD_INTERNAL
120 if (enginesCollector)
121 enginesCollector->append(t: this);
122#endif
123}
124
125QFontEngine::~QFontEngine()
126{
127#ifdef QT_BUILD_INTERNAL
128 if (enginesCollector)
129 enginesCollector->removeOne(t: this);
130#endif
131}
132
133QFixed QFontEngine::lineThickness() const
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
146QFixed QFontEngine::underlinePosition() const
147{
148 return ((lineThickness() * 2) + 3) / 6;
149}
150
151void *QFontEngine::harfbuzzFont() const
152{
153 Q_ASSERT(type() != QFontEngine::Multi);
154#if QT_CONFIG(harfbuzz)
155 return hb_qt_font_get_for_engine(fe: const_cast<QFontEngine *>(this));
156#else
157 return nullptr;
158#endif
159}
160
161void *QFontEngine::harfbuzzFace() const
162{
163 Q_ASSERT(type() != QFontEngine::Multi);
164#if QT_CONFIG(harfbuzz)
165 return hb_qt_face_get_for_engine(fe: const_cast<QFontEngine *>(this));
166#else
167 return nullptr;
168#endif
169}
170
171bool QFontEngine::supportsScript(QChar::Script script) const
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'), buffer: nullptr, length: &lenMort) || getSfntTableData(MAKE_TAG('m','o','r','x'), buffer: nullptr, length: &lenMorx))
186 return true;
187
188 if (hb_face_t *face = hb_qt_face_get_for_engine(fe: 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(script: hb_qt_script_to_script(script), HB_LANGUAGE_INVALID,
193 script_count: &script_count, script_tags,
194 language_count: nullptr, language_tags: nullptr);
195
196 if (hb_ot_layout_table_select_script(face, HB_OT_TAG_GSUB, script_count, script_tags, script_index: nullptr, chosen_script: nullptr))
197 return true;
198 }
199#endif
200 return false;
201}
202
203bool QFontEngine::canRender(const QChar *str, int len) const
204{
205 QStringIterator it(str, str + len);
206 while (it.hasNext()) {
207 if (glyphIndex(ucs4: it.next()) == 0)
208 return false;
209 }
210
211 return true;
212}
213
214glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
215{
216 glyph_metrics_t metrics = boundingBox(glyph);
217
218 if (matrix.type() > QTransform::TxTranslate) {
219 return metrics.transformed(xform: matrix);
220 }
221 return metrics;
222}
223
224QFixed QFontEngine::calculatedCapHeight() const
225{
226 const glyph_t glyph = glyphIndex(ucs4: 'H');
227 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
228 return bb.height;
229}
230
231QFixed QFontEngine::xHeight() const
232{
233 const glyph_t glyph = glyphIndex(ucs4: 'x');
234 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
235 return bb.height;
236}
237
238QFixed QFontEngine::averageCharWidth() const
239{
240 const glyph_t glyph = glyphIndex(ucs4: 'x');
241 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
242 return bb.xoff;
243}
244
245bool QFontEngine::supportsTransformation(const QTransform &transform) const
246{
247 return transform.type() < QTransform::TxProject;
248}
249
250bool QFontEngine::expectsGammaCorrectedBlending() const
251{
252 return true;
253}
254
255void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
256 QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
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(r: matrix.dx());
267 ypos = QFixed::fromReal(r: matrix.dy());
268 }
269
270 int current = 0;
271 if (flags & QTextItem::RightToLeft) {
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(fixed: glyphs.justifications[i].space_18d6);
278 totalKashidas += glyphs.justifications[i].nKashidas;
279 }
280 positions.resize(sz: glyphs.numGlyphs+totalKashidas);
281 glyphs_out.resize(sz: 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(r: gpos.x());
297 gpos_y = QFixed::fromReal(r: 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(ucs4: ch.unicode());
307 QFixed kashidaWidth;
308
309 QGlyphLayout g;
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(r: gpos.x());
324 gpos_y = QFixed::fromReal(r: 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 {
332 xpos -= QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
333 }
334 ++i;
335 }
336 } else {
337 positions.resize(sz: glyphs.numGlyphs);
338 glyphs_out.resize(sz: 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(fixed: 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(r: gpos.x());
359 positions[current].y = QFixed::fromReal(r: gpos.y());
360 glyphs_out[current] = glyphs.glyphs[i];
361 xpos += glyphs.advances[i] + QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
362 ++current;
363 }
364 ++i;
365 }
366 }
367 }
368 positions.resize(sz: current);
369 glyphs_out.resize(sz: 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
382bool QFontEngine::processHheaTable() const
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>(src: ptr + 4);
388 qint16 descent = qFromBigEndian<qint16>(src: ptr + 6);
389 qint16 leading = qFromBigEndian<qint16>(src: 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();
396 m_ascent = QFixed::fromReal(r: ascent * fontDef.pixelSize) / unitsPerEm;
397 m_descent = -QFixed::fromReal(r: descent * fontDef.pixelSize) / unitsPerEm;
398
399 m_leading = QFixed::fromReal(r: leading * fontDef.pixelSize) / unitsPerEm;
400
401 return true;
402 }
403
404 return false;
405}
406
407void QFontEngine::initializeHeightMetrics() const
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
415 processHheaTable();
416
417 // Allow OS/2 metrics to override if present
418 processOS2Table();
419
420 if (!supportsSubPixelPositions()) {
421 m_ascent = m_ascent.round();
422 m_descent = m_descent.round();
423 m_leading = m_leading.round();
424 }
425 }
426
427 m_heightMetricsQueried = true;
428}
429
430bool QFontEngine::processOS2Table() const
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>(src: ptr + 62);
436 qint16 typoAscent = qFromBigEndian<qint16>(src: ptr + 68);
437 qint16 typoDescent = qFromBigEndian<qint16>(src: ptr + 70);
438 qint16 typoLineGap = qFromBigEndian<qint16>(src: ptr + 72);
439 quint16 winAscent = qFromBigEndian<quint16>(src: ptr + 74);
440 quint16 winDescent = qFromBigEndian<quint16>(src: 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(r: typoAscent * fontDef.pixelSize) / unitsPerEm;
449 m_descent = -QFixed::fromReal(r: typoDescent * fontDef.pixelSize) / unitsPerEm;
450 m_leading = QFixed::fromReal(r: 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(r: winAscent * fontDef.pixelSize) / unitsPerEm;
456 m_descent = QFixed::fromReal(r: winDescent * fontDef.pixelSize) / unitsPerEm;
457 m_leading = QFixed{};
458 }
459
460 return true;
461 }
462
463 return false;
464}
465
466QFixed QFontEngine::leading() const
467{
468 if (!m_heightMetricsQueried)
469 initializeHeightMetrics();
470
471 return m_leading;
472}
473
474QFixed QFontEngine::ascent() const
475{
476 if (!m_heightMetricsQueried)
477 initializeHeightMetrics();
478
479 return m_ascent;
480}
481
482QFixed QFontEngine::descent() const
483{
484 if (!m_heightMetricsQueried)
485 initializeHeightMetrics();
486
487 return m_descent;
488}
489
490qreal QFontEngine::minLeftBearing() const
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
503qreal QFontEngine::minRightBearing() const
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>(src: tableData + kMinLeftSideBearingOffset);
514 qint16 minRightSideBearing = qFromBigEndian<qint16>(src: 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(t: minLeftSideBearing) < largestValidBearing)
529 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
530 if (qAbs(t: 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(ucs4: 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(a: m_minLeftBearing, b: glyphMetrics.leftBearing().toReal());
560 m_minRightBearing = qMin(a: m_minRightBearing, b: 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
572glyph_metrics_t QFontEngine::boundingBox(const QGlyphLayout &glyphs)
573{
574 QFixed w;
575 for (int i = 0; i < glyphs.numGlyphs; ++i)
576 w += glyphs.effectiveAdvance(item: 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
582glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
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(glyph: 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(a: overall.x, b: x);
596 overall.y = qMin(a: overall.y, b: y);
597 xmax = qMax(a: xmax, b: x.ceil() + bb.width);
598 ymax = qMax(a: ymax, b: y.ceil() + bb.height);
599 overall.xoff += glyphs.effectiveAdvance(item: i);
600 overall.yoff += bb.yoff;
601 }
602 overall.height = qMax(a: overall.height, b: ymax - overall.y);
603 overall.width = xmax - overall.x;
604
605 return overall;
606}
607
608
609void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
610 QTextItem::RenderFlags flags)
611{
612 if (!glyphs.numGlyphs)
613 return;
614
615 QVarLengthArray<QFixedPoint> positions;
616 QVarLengthArray<glyph_t> positioned_glyphs;
617 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y);
618 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
619 addGlyphsToPath(glyphs: positioned_glyphs.data(), positions: positions.data(), nglyphs: 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
629};
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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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: x + x0, y: 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
716void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
717 QPainterPath *path, QTextItem::RenderFlags flags)
718{
719// TODO what to do with 'flags' ??
720 Q_UNUSED(flags);
721 QFixed advanceX = QFixed::fromReal(r: x);
722 QFixed advanceY = QFixed::fromReal(r: y);
723 for (int i=0; i < glyphs.numGlyphs; ++i) {
724 glyph_metrics_t metrics = boundingBox(glyph: glyphs.glyphs[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();
734 QImage bitmap;
735 if (alphaMask.depth() == 1) {
736 bitmap = alphaMask;
737 } else {
738 bitmap = QImage(w, h, QImage::Format_Mono);
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(x0: (advanceX + metrics.x).toReal(), y0: (advanceY + metrics.y).toReal(), image_data: bitmap_data, bpl: bitmap.bytesPerLine(), w, h, path);
761 advanceX += glyphs.advances[i];
762 }
763}
764
765void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
766 QPainterPath *path, QTextItem::RenderFlags flags)
767{
768 qreal x = positions[0].x.toReal();
769 qreal y = positions[0].y.toReal();
770 QVarLengthGlyphLayoutArray g(nGlyphs);
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(r: maxCharWidth());
778
779 addBitmapFontToPath(x, y, glyphs: g, path, flags);
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
788QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
789{
790 QImage i = alphaMapForGlyph(glyph);
791 if (t.type() > QTransform::TxTranslate)
792 i = i.transformed(matrix: t).convertToFormat(f: QImage::Format_Alpha8);
793 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
794
795 return i;
796}
797
798QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &t)
799{
800 if (!supportsHorizontalSubPixelPositions() && !supportsVerticalSubPixelPositions())
801 return alphaMapForGlyph(glyph, t);
802
803 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
804 if (t.type() > QTransform::TxTranslate)
805 i = i.transformed(matrix: t).convertToFormat(f: 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(r: val, g: val, b: 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
835QFixedPoint QFontEngine::subPixelPositionFor(const QFixedPoint &position) const
836{
837 if (m_subPixelPositionCount <= 1
838 || (!supportsHorizontalSubPixelPositions()
839 && !supportsVerticalSubPixelPositions())) {
840 return QFixedPoint();
841 }
842
843 auto f = [&](QFixed v) {
844 if (v != 0) {
845 v = v - v.floor() + QFixed::fromFixed(fixed: 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
855QFontEngine::Glyph *QFontEngine::glyphData(glyph_t,
856 const QFixedPoint &,
857 QFontEngine::GlyphFormat,
858 const QTransform &)
859{
860 return nullptr;
861}
862
863QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
864{
865 glyph_metrics_t gm = boundingBox(glyph);
866 int glyph_x = qFloor(v: gm.x.toReal());
867 int glyph_y = qFloor(v: gm.y.toReal());
868 int glyph_width = qCeil(v: (gm.x + gm.width).toReal()) - glyph_x;
869 int glyph_height = qCeil(v: (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
876 QPainterPath path;
877 path.setFillRule(Qt::WindingFill);
878 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
879 im.fill(color: Qt::transparent);
880 QPainter p(&im);
881 p.setRenderHint(hint: QPainter::Antialiasing);
882 addGlyphsToPath(glyphs: &glyph, positions: &pt, nGlyphs: 1, path: &path, flags: { });
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(rgb: src[x]);
895 }
896
897 return alphaMap;
898}
899
900void QFontEngine::removeGlyphFromCache(glyph_t)
901{
902}
903
904QFontEngine::Properties QFontEngine::properties() const
905{
906 Properties p;
907 p.postscriptName =
908 QFontEngine::convertToPostscriptFontFamilyName(fontFamily: fontDef.families.first().toUtf8()) + '-'
909 + QByteArray::number(fontDef.style) + '-' + QByteArray::number(fontDef.weight);
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
921void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
922{
923 *metrics = boundingBox(glyph);
924 QFixedPoint p;
925 p.x = 0;
926 p.y = 0;
927 addGlyphsToPath(glyphs: &glyph, positions: &p, nGlyphs: 1, path, flags: QFlag(0));
928}
929
930/*!
931 Returns \c true if the font table idetified by \a tag exists in the font;
932 returns \c false otherwise.
933
934 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
935 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
936 of the buffer, passed in \a length, is sufficient to store the font table data,
937 also copies the font table data to \a buffer.
938
939 Note: returning \c false when the font table exists could lead to an undefined behavior.
940*/
941bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
942{
943 Q_UNUSED(tag);
944 Q_UNUSED(buffer);
945 Q_UNUSED(length);
946 return false;
947}
948
949QByteArray QFontEngine::getSfntTable(uint tag) const
950{
951 QByteArray table;
952 uint len = 0;
953 if (!getSfntTableData(tag, buffer: nullptr, length: &len))
954 return table;
955 table.resize(size: len);
956 if (!getSfntTableData(tag, buffer: reinterpret_cast<uchar *>(table.data()), length: &len))
957 return QByteArray();
958 return table;
959}
960
961void QFontEngine::clearGlyphCache(const void *context)
962{
963 m_glyphCaches.remove(key: context);
964}
965
966void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
967{
968 Q_ASSERT(cache);
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(x: entry);
984
985}
986
987QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
988 GlyphFormat format,
989 const QTransform &transform,
990 const QColor &color) const
991{
992 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(key: context);
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())
1000 && qtransform_equals_no_translate(a: cache->m_transform, b: transform)) {
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(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs);
1038 } else {
1039 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1040 glyphs->advances[i] += qRound(f: kerning(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs));
1041 }
1042}
1043
1044void QFontEngine::loadKerningPairs(QFixed scalingFactor)
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(source: table, end, output: &version))
1057 return;
1058
1059 if (version != 0) {
1060// qDebug("wrong version");
1061 return;
1062 }
1063
1064 quint16 numTables;
1065 if (!qSafeFromBigEndian(source: table + 2, end, output: &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(source: header, end, output: &version))
1075 goto end;
1076
1077 quint16 length;
1078 if (!qSafeFromBigEndian(source: header + 2, end, output: &length))
1079 goto end;
1080
1081 quint16 coverage;
1082 if (!qSafeFromBigEndian(source: header + 4, end, output: &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(source: data, end, output: &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) {
1105 QFontEngine::KernPair p;
1106
1107 quint16 tmp;
1108 if (!qSafeFromBigEndian(source: data + off, end, output: &tmp))
1109 goto end;
1110
1111 p.left_right = uint(tmp) << 16;
1112 if (!qSafeFromBigEndian(source: data + off + 2, end, output: &tmp))
1113 goto end;
1114
1115 p.left_right |= tmp;
1116
1117 if (!qSafeFromBigEndian(source: data + off + 4, end, output: &tmp))
1118 goto end;
1119
1120 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1121 kerning_pairs.append(t: p);
1122 off += 6;
1123 }
1124 }
1125 offset += length;
1126 }
1127 }
1128end:
1129 std::sort(first: kerning_pairs.begin(), last: 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
1135int QFontEngine::glyphCount() const
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;
1145 qSafeFromBigEndian(source, end, output: &count);
1146 return count;
1147}
1148
1149Qt::HANDLE QFontEngine::handle() const
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(source: header, end: endPtr, output: &version) || version != 0)
1162 return nullptr;
1163
1164 quint16 numTables;
1165 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &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(source: maps + 8 * n, end: endPtr, output: &platformId))
1186 return nullptr;
1187
1188 quint16 platformSpecificId = 0;
1189 if (!qSafeFromBigEndian(source: maps + 8 * n + 2, end: endPtr, output: &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(source: maps + 8 * tableToUse + 4, end: endPtr, output: &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
1255 quint16 format;
1256 if (!qSafeFromBigEndian(source: header, end: endPtr, output: &format))
1257 return nullptr;
1258
1259 quint32 length;
1260 if (format < 8) {
1261 quint16 tmp;
1262 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &tmp))
1263 return nullptr;
1264 length = tmp;
1265 } else {
1266 if (!qSafeFromBigEndian(source: header + 4, end: endPtr, output: &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(cmap: selectedTable, cmapSize: length, unicode: 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(cmap: selectedTable, cmapSize: length, unicode: 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;
1314 quint16 format;
1315 if (!qSafeFromBigEndian(source: cmap, end, output: &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(source: cmap + 6, end, output: &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(source: ends + 2 * i, end, output: &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(source: idx, end, output: &startIndex))
1350 return 0;
1351 if (startIndex > unicode)
1352 return 0;
1353
1354 idx += segCountX2;
1355
1356 quint16 tmp;
1357 if (!qSafeFromBigEndian(source: idx, end, output: &tmp))
1358 return 0;
1359 qint16 idDelta = qint16(tmp);
1360
1361 idx += segCountX2;
1362
1363 quint16 idRangeoffset_t;
1364 if (!qSafeFromBigEndian(source: idx, end, output: &idRangeoffset_t))
1365 return 0;
1366
1367 quint16 glyphIndex;
1368 if (idRangeoffset_t) {
1369 quint16 id;
1370 if (!qSafeFromBigEndian(source: idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, output: &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(source: cmap + 2, end, output: &tableSize))
1384 return 0;
1385
1386 quint16 firstCode6;
1387 if (!qSafeFromBigEndian(source: cmap + 6, end, output: &firstCode6))
1388 return 0;
1389 if (unicode < firstCode6)
1390 return 0;
1391
1392 quint16 entryCount6;
1393 if (!qSafeFromBigEndian(source: cmap + 8, end, output: &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(source: cmap + 10 + (entryIndex6 * 2), end, output: &index);
1406 return index;
1407 } else if (format == 12) {
1408 quint32 nGroups;
1409 if (!qSafeFromBigEndian(source: cmap + 12, end, output: &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(source: cmap + 12 * middle, end, output: &startCharCode))
1420 return 0;
1421
1422 if (unicode < startCharCode)
1423 right = middle - 1;
1424 else {
1425 quint32 endCharCode;
1426 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 4, end, output: &endCharCode))
1427 return 0;
1428
1429 if (unicode <= endCharCode) {
1430 quint32 index;
1431 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 8, end, output: &index))
1432 return 0;
1433
1434 return index + unicode - startCharCode;
1435 }
1436 left = middle + 1;
1437 }
1438 }
1439 } else {
1440 qDebug(msg: "cmap table of format %d not implemented", format);
1441 }
1442
1443 return 0;
1444}
1445
1446QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1447{
1448 QByteArray f = family;
1449 f.replace(before: ' ', after: "");
1450 f.replace(before: '(', after: "");
1451 f.replace(before: ')', after: "");
1452 f.replace(before: '<', after: "");
1453 f.replace(before: '>', after: "");
1454 f.replace(before: '[', after: "");
1455 f.replace(before: ']', after: "");
1456 f.replace(before: '{', after: "");
1457 f.replace(before: '}', after: "");
1458 f.replace(before: '/', after: "");
1459 f.replace(before: '%', after: "");
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.
1466bool QFontEngine::hasUnreliableGlyphOutline() const
1467{
1468 // Color glyphs (Emoji) are generally not suited for outlining
1469 return glyphFormat == QFontEngine::Format_ARGB;
1470}
1471
1472QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
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
1483QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
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
1518QFontEngineBox::QFontEngineBox(int size)
1519 : QFontEngine(Box),
1520 _size(size)
1521{
1522 cache_cost = sizeof(QFontEngineBox);
1523}
1524
1525QFontEngineBox::QFontEngineBox(Type type, int size)
1526 : QFontEngine(type),
1527 _size(size)
1528{
1529 cache_cost = sizeof(QFontEngineBox);
1530}
1531
1532QFontEngineBox::~QFontEngineBox()
1533{
1534}
1535
1536glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
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;
1551 QStringIterator it(str, str + len);
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
1577 QVarLengthArray<QFixedPoint> positions;
1578 QVarLengthArray<glyph_t> positioned_glyphs;
1579 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1580 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
1581
1582 QSize s(_size - 3, _size - 3);
1583 for (int k = 0; k < positions.size(); k++)
1584 path->addRect(rect: QRectF(positions[k].toPointF(), s));
1585}
1586
1587glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
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
1596void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
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
1604 QVarLengthArray<QFixedPoint> positions;
1605 QVarLengthArray<glyph_t> glyphs;
1606 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1607 ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions);
1608 if (glyphs.size() == 0)
1609 return;
1610
1611
1612 QPainter *painter = p->painter();
1613 painter->save();
1614 painter->setBrush(Qt::NoBrush);
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(rect: QRectF(positions[k].toPointF(), s));
1620 painter->restore();
1621}
1622
1623glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1624{
1625 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1626}
1627
1628QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1629{
1630 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1631 return fe;
1632}
1633
1634QFixed QFontEngineBox::ascent() const
1635{
1636 return _size;
1637}
1638
1639QFixed QFontEngineBox::capHeight() const
1640{
1641 return _size;
1642}
1643
1644QFixed QFontEngineBox::descent() const
1645{
1646 return 0;
1647}
1648
1649QFixed QFontEngineBox::leading() const
1650{
1651 QFixed l = _size * QFixed::fromReal(r: qreal(0.15));
1652 return l.ceil();
1653}
1654
1655qreal QFontEngineBox::maxCharWidth() const
1656{
1657 return _size;
1658}
1659
1660bool QFontEngineBox::canRender(const QChar *, int) const
1661{
1662 return true;
1663}
1664
1665QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1666{
1667 QImage image(_size, _size, QImage::Format_Alpha8);
1668 image.fill(pixel: 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
1684uchar QFontEngineMulti::highByte(glyph_t glyph)
1685{ return glyph >> 24; }
1686
1687// strip high byte from glyph
1688static inline glyph_t stripped(glyph_t glyph)
1689{ return glyph & 0x00ffffff; }
1690
1691QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1692 : QFontEngine(Multi),
1693 m_fallbackFamilies(fallbackFamilies),
1694 m_script(script),
1695 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1696{
1697 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
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(size: 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
1713QFontEngineMulti::~QFontEngineMulti()
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
1722QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script);
1723
1724void QFontEngineMulti::ensureFallbackFamiliesQueried()
1725{
1726 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
1727 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1728 styleHint = QFont::TypeWriter;
1729
1730 setFallbackFamiliesList(qt_fallbacksForFamily(family: fontDef.families.first(),
1731 style: QFont::Style(fontDef.style), styleHint,
1732 script: QChar::Script(m_script)));
1733}
1734
1735void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
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(i: 0);
1744 engine->ref.ref();
1745 m_engines[1] = engine;
1746 m_fallbackFamilies << fontDef.families.first();
1747 } else {
1748 m_engines.resize(size: m_fallbackFamilies.size() + 1);
1749 }
1750
1751 m_fallbackFamiliesQueried = true;
1752}
1753
1754void QFontEngineMulti::ensureEngineAt(int at)
1755{
1756 if (!m_fallbackFamiliesQueried && at > 0)
1757 ensureFallbackFamiliesQueried();
1758 Q_ASSERT(at < m_engines.size());
1759 if (!m_engines.at(i: at)) {
1760 QFontEngine *engine = loadEngine(at);
1761 if (!engine)
1762 engine = new QFontEngineBox(fontDef.pixelSize);
1763 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1764 engine->ref.ref();
1765 m_engines[at] = engine;
1766 }
1767}
1768
1769QFontEngine *QFontEngineMulti::loadEngine(int at)
1770{
1771 QFontDef request(fontDef);
1772 request.styleStrategy |= QFont::NoFontMerging;
1773 request.families = QStringList(fallbackFamilyAt(at: 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.
1780 if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, script: QChar::Script_Common)) {
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
1790glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
1791{
1792 glyph_t glyph = engine(at: 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(a: m_engines.size(), b: 256); x < n; ++x) {
1801 QFontEngine *engine = m_engines.at(i: x);
1802 if (!engine) {
1803 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1804 continue;
1805 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1806 engine = m_engines.at(i: 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
1824bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
1825 QGlyphLayout *glyphs, int *nglyphs,
1826 QFontEngine::ShaperFlags flags) const
1827{
1828 if (!engine(at: 0)->stringToCMap(str, len, glyphs, nglyphs, flags))
1829 return false;
1830
1831 int glyph_pos = 0;
1832 QStringIterator it(str, str + len);
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(i: 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(position: glyph_pos, n: 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
1866 && QChar::category(ucs4) != QChar::Other_PrivateUse) {
1867 if (!m_fallbackFamiliesQueried)
1868 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1869 for (int x = 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1870 QFontEngine *engine = m_engines.at(i: x);
1871 if (!engine) {
1872 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1873 continue;
1874 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1875 engine = m_engines.at(i: x);
1876 if (!engine)
1877 continue;
1878 }
1879 Q_ASSERT(engine != nullptr);
1880 if (engine->type() == Box)
1881 continue;
1882
1883 glyph_t glyph = engine->glyphIndex(ucs4);
1884 if (glyph != 0) {
1885 glyphs->glyphs[glyph_pos] = glyph;
1886 if (!(flags & GlyphIndicesOnly)) {
1887 QGlyphLayout g = glyphs->mid(position: glyph_pos, n: 1);
1888 engine->recalcAdvances(&g, flags);
1889 }
1890
1891 lastFallback = x;
1892
1893 // set the high byte to indicate which engine the glyph came from
1894 glyphs->glyphs[glyph_pos] |= (x << 24);
1895 break;
1896 }
1897 }
1898
1899 // For variant-selectors, they are modifiers to the previous character. If we
1900 // end up with different font selections for the selector and the character it
1901 // modifies, we try applying the selector font to the preceding character as well
1902 const int variantSelectorBlock = 0xFE00;
1903 if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
1904 int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
1905 int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
1906
1907 if (selectorFontEngine != precedingCharacterFontEngine) {
1908 QFontEngine *engine = m_engines.at(i: selectorFontEngine);
1909 glyph_t glyph = engine->glyphIndex(ucs4: previousUcs4);
1910 if (glyph != 0) {
1911 glyphs->glyphs[glyph_pos - 1] = glyph;
1912 if (!(flags & GlyphIndicesOnly)) {
1913 QGlyphLayout g = glyphs->mid(position: glyph_pos - 1, n: 1);
1914 engine->recalcAdvances(&g, flags);
1915 }
1916
1917 // set the high byte to indicate which engine the glyph came from
1918 glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
1919 }
1920 }
1921 }
1922 }
1923
1924 it.advance();
1925 ++glyph_pos;
1926 previousUcs4 = ucs4;
1927 }
1928
1929 *nglyphs = glyph_pos;
1930 glyphs->numGlyphs = glyph_pos;
1931
1932 return true;
1933}
1934
1935bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
1936{
1937 Q_UNUSED(at);
1938 Q_UNUSED(ucs4);
1939 return true;
1940}
1941
1942glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
1943{
1944 if (glyphs.numGlyphs <= 0)
1945 return glyph_metrics_t();
1946
1947 glyph_metrics_t overall;
1948
1949 int which = highByte(glyph: glyphs.glyphs[0]);
1950 int start = 0;
1951 int end, i;
1952 for (end = 0; end < glyphs.numGlyphs; ++end) {
1953 const int e = highByte(glyph: glyphs.glyphs[end]);
1954 if (e == which)
1955 continue;
1956
1957 // set the high byte to zero
1958 for (i = start; i < end; ++i)
1959 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
1960
1961 // merge the bounding box for this run
1962 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
1963
1964 overall.x = qMin(a: overall.x, b: gm.x);
1965 overall.y = qMin(a: overall.y, b: gm.y);
1966 overall.width = overall.xoff + gm.width;
1967 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
1968 qMin(a: overall.y, b: gm.y);
1969 overall.xoff += gm.xoff;
1970 overall.yoff += gm.yoff;
1971
1972 // reset the high byte for all glyphs
1973 const int hi = which << 24;
1974 for (i = start; i < end; ++i)
1975 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
1976
1977 // change engine
1978 start = end;
1979 which = e;
1980 }
1981
1982 // set the high byte to zero
1983 for (i = start; i < end; ++i)
1984 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
1985
1986 // merge the bounding box for this run
1987 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
1988
1989 overall.x = qMin(a: overall.x, b: gm.x);
1990 overall.y = qMin(a: overall.y, b: gm.y);
1991 overall.width = overall.xoff + gm.width;
1992 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
1993 qMin(a: overall.y, b: gm.y);
1994 overall.xoff += gm.xoff;
1995 overall.yoff += gm.yoff;
1996
1997 // reset the high byte for all glyphs
1998 const int hi = which << 24;
1999 for (i = start; i < end; ++i)
2000 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2001
2002 return overall;
2003}
2004
2005void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2006{
2007 int which = highByte(glyph);
2008 ensureEngineAt(at: which);
2009 engine(at: which)->getGlyphBearings(glyph: stripped(glyph), leftBearing, rightBearing);
2010}
2011
2012void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2013 QPainterPath *path, QTextItem::RenderFlags flags)
2014{
2015 if (glyphs.numGlyphs <= 0)
2016 return;
2017
2018 int which = highByte(glyph: glyphs.glyphs[0]);
2019 int start = 0;
2020 int end, i;
2021 if (flags & QTextItem::RightToLeft) {
2022 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2023 x += glyphs.advances[gl].toReal();
2024 }
2025 for (end = 0; end < glyphs.numGlyphs; ++end) {
2026 const int e = highByte(glyph: glyphs.glyphs[end]);
2027 if (e == which)
2028 continue;
2029
2030 if (flags & QTextItem::RightToLeft) {
2031 for (i = start; i < end; ++i)
2032 x -= glyphs.advances[i].toReal();
2033 }
2034
2035 // set the high byte to zero
2036 for (i = start; i < end; ++i)
2037 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2038 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2039 // reset the high byte for all glyphs and update x and y
2040 const int hi = which << 24;
2041 for (i = start; i < end; ++i)
2042 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2043
2044 if (!(flags & QTextItem::RightToLeft)) {
2045 for (i = start; i < end; ++i)
2046 x += glyphs.advances[i].toReal();
2047 }
2048
2049 // change engine
2050 start = end;
2051 which = e;
2052 }
2053
2054 if (flags & QTextItem::RightToLeft) {
2055 for (i = start; i < end; ++i)
2056 x -= glyphs.advances[i].toReal();
2057 }
2058
2059 // set the high byte to zero
2060 for (i = start; i < end; ++i)
2061 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2062
2063 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2064
2065 // reset the high byte for all glyphs
2066 const int hi = which << 24;
2067 for (i = start; i < end; ++i)
2068 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2069}
2070
2071void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2072{
2073 if (glyphs->numGlyphs <= 0)
2074 return;
2075
2076 int which = highByte(glyph: glyphs->glyphs[0]);
2077 int start = 0;
2078 int end, i;
2079 for (end = 0; end < glyphs->numGlyphs; ++end) {
2080 const int e = highByte(glyph: glyphs->glyphs[end]);
2081 if (e == which)
2082 continue;
2083
2084 // set the high byte to zero
2085 for (i = start; i < end; ++i)
2086 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2087
2088 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2089 engine(at: which)->recalcAdvances(&offs, flags);
2090
2091 // reset the high byte for all glyphs and update x and y
2092 const int hi = which << 24;
2093 for (i = start; i < end; ++i)
2094 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2095
2096 // change engine
2097 start = end;
2098 which = e;
2099 }
2100
2101 // set the high byte to zero
2102 for (i = start; i < end; ++i)
2103 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2104
2105 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2106 engine(at: which)->recalcAdvances(&offs, flags);
2107
2108 // reset the high byte for all glyphs
2109 const int hi = which << 24;
2110 for (i = start; i < end; ++i)
2111 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2112}
2113
2114void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2115{
2116 if (glyphs->numGlyphs <= 0)
2117 return;
2118
2119 int which = highByte(glyph: glyphs->glyphs[0]);
2120 int start = 0;
2121 int end, i;
2122 for (end = 0; end < glyphs->numGlyphs; ++end) {
2123 const int e = highByte(glyph: glyphs->glyphs[end]);
2124 if (e == which)
2125 continue;
2126
2127 // set the high byte to zero
2128 for (i = start; i < end; ++i)
2129 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2130
2131 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2132 engine(at: which)->doKerning(glyphs: &offs, flags);
2133
2134 // reset the high byte for all glyphs and update x and y
2135 const int hi = which << 24;
2136 for (i = start; i < end; ++i)
2137 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2138
2139 // change engine
2140 start = end;
2141 which = e;
2142 }
2143
2144 // set the high byte to zero
2145 for (i = start; i < end; ++i)
2146 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2147
2148 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2149 engine(at: which)->doKerning(glyphs: &offs, flags);
2150
2151 // reset the high byte for all glyphs
2152 const int hi = which << 24;
2153 for (i = start; i < end; ++i)
2154 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2155}
2156
2157glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2158{
2159 const int which = highByte(glyph);
2160 return engine(at: which)->boundingBox(glyph: stripped(glyph));
2161}
2162
2163QFixed QFontEngineMulti::ascent() const
2164{ return engine(at: 0)->ascent(); }
2165
2166QFixed QFontEngineMulti::capHeight() const
2167{ return engine(at: 0)->capHeight(); }
2168
2169QFixed QFontEngineMulti::descent() const
2170{ return engine(at: 0)->descent(); }
2171
2172QFixed QFontEngineMulti::leading() const
2173{
2174 return engine(at: 0)->leading();
2175}
2176
2177QFixed QFontEngineMulti::xHeight() const
2178{
2179 return engine(at: 0)->xHeight();
2180}
2181
2182QFixed QFontEngineMulti::averageCharWidth() const
2183{
2184 return engine(at: 0)->averageCharWidth();
2185}
2186
2187QFixed QFontEngineMulti::lineThickness() const
2188{
2189 return engine(at: 0)->lineThickness();
2190}
2191
2192QFixed QFontEngineMulti::underlinePosition() const
2193{
2194 return engine(at: 0)->underlinePosition();
2195}
2196
2197qreal QFontEngineMulti::maxCharWidth() const
2198{
2199 return engine(at: 0)->maxCharWidth();
2200}
2201
2202qreal QFontEngineMulti::minLeftBearing() const
2203{
2204 return engine(at: 0)->minLeftBearing();
2205}
2206
2207qreal QFontEngineMulti::minRightBearing() const
2208{
2209 return engine(at: 0)->minRightBearing();
2210}
2211
2212bool QFontEngineMulti::canRender(const QChar *string, int len) const
2213{
2214 if (engine(at: 0)->canRender(str: string, len))
2215 return true;
2216
2217 int nglyphs = len;
2218
2219 QVarLengthArray<glyph_t> glyphs(nglyphs);
2220
2221 QGlyphLayout g;
2222 g.numGlyphs = nglyphs;
2223 g.glyphs = glyphs.data();
2224 if (!stringToCMap(str: string, len, glyphs: &g, nglyphs: &nglyphs, flags: GlyphIndicesOnly))
2225 Q_UNREACHABLE();
2226
2227 for (int i = 0; i < nglyphs; i++) {
2228 if (glyphs[i] == 0)
2229 return false;
2230 }
2231
2232 return true;
2233}
2234
2235/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2236 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2237
2238QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2239{
2240 const int which = highByte(glyph);
2241 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph));
2242}
2243
2244QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2245{
2246 const int which = highByte(glyph);
2247 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition);
2248}
2249
2250QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2251{
2252 const int which = highByte(glyph);
2253 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), t);
2254}
2255
2256QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2257 const QFixedPoint &subPixelPosition,
2258 const QTransform &t)
2259{
2260 const int which = highByte(glyph);
2261 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2262}
2263
2264QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2265 const QFixedPoint &subPixelPosition,
2266 const QTransform &t)
2267{
2268 const int which = highByte(glyph);
2269 return engine(at: which)->alphaRGBMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2270}
2271
2272/*
2273 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2274
2275 The purpose of this is to provide the necessary font fallbacks when drawing complex
2276 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2277 the same raw font over and over again, we want to cache the corresponding multi font engine
2278 as it may contain fallback font engines already.
2279*/
2280QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2281{
2282 QFontEngine *engine = nullptr;
2283 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2284 QFontCache *fc = QFontCache::instance();
2285 // We can't rely on the fontDef (and hence the cache Key)
2286 // alone to distinguish webfonts, since these should not be
2287 // accidentally shared, even if the resulting fontcache key
2288 // is strictly identical. See:
2289 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2290 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2291 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2292 end = fc->engineCache.end();
2293 while (it != end && it.key() == key) {
2294 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2295 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2296 if (fe == cachedEngine->engine(at: 0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(at: 0)->faceId().filename)) {
2297 engine = cachedEngine;
2298 fc->updateHitCountAndTimeStamp(value&: it.value());
2299 break;
2300 }
2301 ++it;
2302 }
2303 if (!engine) {
2304 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fontEngine: fe, script: QChar::Script(script));
2305 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2306 }
2307 Q_ASSERT(engine);
2308 return engine;
2309}
2310
2311QTestFontEngine::QTestFontEngine(int size)
2312 : QFontEngineBox(TestFontEngine, size)
2313{}
2314
2315QT_END_NAMESPACE
2316

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