Coverage Report

Created: 2025-12-03 06:32

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