Coverage Report

Created: 2026-11-10 07:41

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