Coverage Report

Created: 2026-10-14 06:45

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 *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1402
0
    const uchar *end = source + maxpTable.size();
1403
1404
0
    quint16 count = 0;
1405
0
    qSafeFromBigEndian(source, end, &count);
1406
0
    return count;
1407
0
}
1408
1409
Qt::HANDLE QFontEngine::handle() const
1410
0
{
1411
0
    return nullptr;
1412
0
}
1413
1414
const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1415
0
{
1416
0
    const uchar *header = table;
1417
0
    const uchar *endPtr = table + tableSize;
1418
1419
    // version check
1420
0
    quint16 version;
1421
0
    if (!qSafeFromBigEndian(header, endPtr, &version) || version != 0)
1422
0
        return nullptr;
1423
1424
0
    quint16 numTables;
1425
0
    if (!qSafeFromBigEndian(header + 2, endPtr, &numTables))
1426
0
        return nullptr;
1427
1428
0
    const uchar *maps = table + 4;
1429
1430
0
    enum {
1431
0
        Invalid,
1432
0
        AppleRoman,
1433
0
        Symbol,
1434
0
        Unicode11,
1435
0
        Unicode,
1436
0
        MicrosoftUnicode,
1437
0
        MicrosoftUnicodeExtended
1438
0
    };
1439
1440
0
    int symbolTable = -1;
1441
0
    int tableToUse = -1;
1442
0
    int score = Invalid;
1443
0
    for (int n = 0; n < numTables; ++n) {
1444
0
        quint16 platformId = 0;
1445
0
        if (!qSafeFromBigEndian(maps + 8 * n, endPtr, &platformId))
1446
0
            return nullptr;
1447
1448
0
        quint16 platformSpecificId = 0;
1449
0
        if (!qSafeFromBigEndian(maps + 8 * n + 2, endPtr, &platformSpecificId))
1450
0
            return nullptr;
1451
1452
0
        switch (platformId) {
1453
0
        case 0: // Unicode
1454
0
            if (score < Unicode &&
1455
0
                (platformSpecificId == 0 ||
1456
0
                 platformSpecificId == 2 ||
1457
0
                 platformSpecificId == 3)) {
1458
0
                tableToUse = n;
1459
0
                score = Unicode;
1460
0
            } else if (score < Unicode11 && platformSpecificId == 1) {
1461
0
                tableToUse = n;
1462
0
                score = Unicode11;
1463
0
            }
1464
0
            break;
1465
0
        case 1: // Apple
1466
0
            if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1467
0
                tableToUse = n;
1468
0
                score = AppleRoman;
1469
0
            }
1470
0
            break;
1471
0
        case 3: // Microsoft
1472
0
            switch (platformSpecificId) {
1473
0
            case 0:
1474
0
                symbolTable = n;
1475
0
                if (score < Symbol) {
1476
0
                    tableToUse = n;
1477
0
                    score = Symbol;
1478
0
                }
1479
0
                break;
1480
0
            case 1:
1481
0
                if (score < MicrosoftUnicode) {
1482
0
                    tableToUse = n;
1483
0
                    score = MicrosoftUnicode;
1484
0
                }
1485
0
                break;
1486
0
            case 0xa:
1487
0
                if (score < MicrosoftUnicodeExtended) {
1488
0
                    tableToUse = n;
1489
0
                    score = MicrosoftUnicodeExtended;
1490
0
                }
1491
0
                break;
1492
0
            default:
1493
0
                break;
1494
0
            }
1495
0
            break;
1496
0
        default:
1497
0
            break;
1498
0
        }
1499
0
    }
1500
0
    if (tableToUse < 0)
1501
0
        return nullptr;
1502
1503
0
resolveTable:
1504
0
    *isSymbolFont = (symbolTable > -1);
1505
1506
0
    quint32 unicode_table = 0;
1507
0
    if (!qSafeFromBigEndian(maps + 8 * tableToUse + 4, endPtr, &unicode_table))
1508
0
        return nullptr;
1509
1510
0
    if (!unicode_table)
1511
0
        return nullptr;
1512
1513
    // get the header of the unicode table
1514
0
    header = table + unicode_table;
1515
1516
0
    quint16 format;
1517
0
    if (!qSafeFromBigEndian(header, endPtr, &format))
1518
0
        return nullptr;
1519
1520
0
    quint32 length;
1521
0
    if (format < 8) {
1522
0
        quint16 tmp;
1523
0
        if (!qSafeFromBigEndian(header + 2, endPtr, &tmp))
1524
0
            return nullptr;
1525
0
        length = tmp;
1526
0
    } else {
1527
0
        if (!qSafeFromBigEndian(header + 4, endPtr, &length))
1528
0
            return nullptr;
1529
0
    }
1530
1531
0
    if (table + unicode_table + length > endPtr)
1532
0
        return nullptr;
1533
0
    *cmapSize = length;
1534
1535
    // To support symbol fonts that contain a unicode table for the symbol area
1536
    // we check the cmap tables and fall back to symbol font unless that would
1537
    // involve losing information from the unicode table
1538
0
    if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1539
0
        const uchar *selectedTable = table + unicode_table;
1540
1541
        // Check that none of the latin1 range are in the unicode table
1542
0
        bool unicodeTableHasLatin1 = false;
1543
0
        for (int uc=0x00; uc<0x100; ++uc) {
1544
0
            if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1545
0
                unicodeTableHasLatin1 = true;
1546
0
                break;
1547
0
            }
1548
0
        }
1549
1550
        // Check that at least one symbol char is in the unicode table
1551
0
        bool unicodeTableHasSymbols = false;
1552
0
        if (!unicodeTableHasLatin1) {
1553
0
            for (int uc=0xf000; uc<0xf100; ++uc) {
1554
0
                if (getTrueTypeGlyphIndex(selectedTable, length, uc) != 0) {
1555
0
                    unicodeTableHasSymbols = true;
1556
0
                    break;
1557
0
                }
1558
0
            }
1559
0
        }
1560
1561
        // Fall back to symbol table
1562
0
        if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1563
0
            tableToUse = symbolTable;
1564
0
            score = Symbol;
1565
0
            goto resolveTable;
1566
0
        }
1567
0
    }
1568
1569
0
    return table + unicode_table;
1570
0
}
1571
1572
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1573
0
{
1574
0
    const uchar *end = cmap + cmapSize;
1575
0
    quint16 format = 0;
1576
0
    if (!qSafeFromBigEndian(cmap, end, &format))
1577
0
        return 0;
1578
1579
0
    if (format == 0) {
1580
0
        const uchar *ptr = cmap + 6 + unicode;
1581
0
        if (unicode < 256 && ptr < end)
1582
0
            return quint32(*ptr);
1583
0
    } else if (format == 4) {
1584
        /* some fonts come with invalid cmap tables, where the last segment
1585
           specified end = start = rangeoffset = 0xffff, delta = 0x0001
1586
           Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1587
           by returning 0 for 0xffff
1588
        */
1589
0
        if (unicode >= 0xffff)
1590
0
            return 0;
1591
1592
0
        quint16 segCountX2 = 0;
1593
0
        if (!qSafeFromBigEndian(cmap + 6, end, &segCountX2))
1594
0
            return 0;
1595
1596
0
        const unsigned char *ends = cmap + 14;
1597
1598
0
        int i = 0;
1599
0
        for (; i < segCountX2/2; ++i) {
1600
0
            quint16 codePoint = 0;
1601
0
            if (!qSafeFromBigEndian(ends + 2 * i, end, &codePoint))
1602
0
                return 0;
1603
0
            if (codePoint >= unicode)
1604
0
                break;
1605
0
        }
1606
1607
0
        const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1608
1609
0
        quint16 startIndex = 0;
1610
0
        if (!qSafeFromBigEndian(idx, end, &startIndex))
1611
0
            return 0;
1612
0
        if (startIndex > unicode)
1613
0
            return 0;
1614
1615
0
        idx += segCountX2;
1616
1617
0
        quint16 tmp = 0;
1618
0
        if (!qSafeFromBigEndian(idx, end, &tmp))
1619
0
            return 0;
1620
0
        qint16 idDelta = qint16(tmp);
1621
1622
0
        idx += segCountX2;
1623
1624
0
        quint16 idRangeoffset_t = 0;
1625
0
        if (!qSafeFromBigEndian(idx, end, &idRangeoffset_t))
1626
0
            return 0;
1627
1628
0
        quint16 glyphIndex = 0;
1629
0
        if (idRangeoffset_t) {
1630
0
            quint16 id = 0;
1631
0
            if (!qSafeFromBigEndian(idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, &id))
1632
0
                return 0;
1633
1634
0
            if (id)
1635
0
                glyphIndex = (idDelta + id) % 0x10000;
1636
0
            else
1637
0
                glyphIndex = 0;
1638
0
        } else {
1639
0
            glyphIndex = (idDelta + unicode) % 0x10000;
1640
0
        }
1641
0
        return glyphIndex;
1642
0
    } else if (format == 6) {
1643
0
        quint16 tableSize = 0;
1644
0
        if (!qSafeFromBigEndian(cmap + 2, end, &tableSize))
1645
0
            return 0;
1646
1647
0
        quint16 firstCode6 = 0;
1648
0
        if (!qSafeFromBigEndian(cmap + 6, end, &firstCode6))
1649
0
            return 0;
1650
0
        if (unicode < firstCode6)
1651
0
            return 0;
1652
1653
0
        quint16 entryCount6 = 0;
1654
0
        if (!qSafeFromBigEndian(cmap + 8, end, &entryCount6))
1655
0
            return 0;
1656
0
        if (entryCount6 * 2 + 10 > tableSize)
1657
0
            return 0;
1658
1659
0
        quint16 sentinel6 = firstCode6 + entryCount6;
1660
0
        if (unicode >= sentinel6)
1661
0
            return 0;
1662
1663
0
        quint16 entryIndex6 = unicode - firstCode6;
1664
1665
0
        quint16 index = 0;
1666
0
        qSafeFromBigEndian(cmap + 10 + (entryIndex6 * 2), end, &index);
1667
0
        return index;
1668
0
    } else if (format == 12) {
1669
0
        quint32 nGroups = 0;
1670
0
        if (!qSafeFromBigEndian(cmap + 12, end, &nGroups))
1671
0
            return 0;
1672
1673
0
        cmap += 16; // move to start of groups
1674
1675
0
        int left = 0, right = nGroups - 1;
1676
0
        while (left <= right) {
1677
0
            int middle = left + ( ( right - left ) >> 1 );
1678
1679
0
            quint32 startCharCode = 0;
1680
0
            if (!qSafeFromBigEndian(cmap + 12 * middle, end, &startCharCode))
1681
0
                return 0;
1682
1683
0
            if (unicode < startCharCode)
1684
0
                right = middle - 1;
1685
0
            else {
1686
0
                quint32 endCharCode = 0;
1687
0
                if (!qSafeFromBigEndian(cmap + 12 * middle + 4, end, &endCharCode))
1688
0
                    return 0;
1689
1690
0
                if (unicode <= endCharCode) {
1691
0
                    quint32 index = 0;
1692
0
                    if (!qSafeFromBigEndian(cmap + 12 * middle + 8, end, &index))
1693
0
                        return 0;
1694
1695
0
                    return index + unicode - startCharCode;
1696
0
                }
1697
0
                left = middle + 1;
1698
0
            }
1699
0
        }
1700
0
    } else {
1701
0
        qDebug("cmap table of format %d not implemented", format);
1702
0
    }
1703
1704
0
    return 0;
1705
0
}
1706
1707
QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1708
0
{
1709
0
    QByteArray f = family;
1710
0
    f.replace(' ', "");
1711
0
    f.replace('(', "");
1712
0
    f.replace(')', "");
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
    return f;
1722
0
}
1723
1724
// Allow font engines (e.g. Windows) that can not reliably create
1725
// outline paths for distance-field rendering to switch the scene
1726
// graph over to native text rendering.
1727
bool QFontEngine::hasUnreliableGlyphOutline() const
1728
0
{
1729
    // Color glyphs (Emoji) are generally not suited for outlining
1730
0
    return glyphFormat == QFontEngine::Format_ARGB;
1731
0
}
1732
1733
QFixed QFontEngine::firstLeftBearing(const QGlyphLayout &glyphs)
1734
0
{
1735
0
    for (int i = 0; i < glyphs.numGlyphs; ++i) {
1736
0
        glyph_t glyph = glyphs.glyphs[i];
1737
0
        glyph_metrics_t gi = boundingBox(glyph);
1738
0
        if (gi.isValid() && gi.width > 0)
1739
0
            return gi.leftBearing();
1740
0
    }
1741
0
    return 0;
1742
0
}
1743
1744
QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs)
1745
0
{
1746
0
    if (glyphs.numGlyphs >= 1) {
1747
0
        glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1748
0
        glyph_metrics_t gi = boundingBox(glyph);
1749
0
        if (gi.isValid())
1750
0
            return gi.rightBearing();
1751
0
    }
1752
0
    return 0;
1753
0
}
1754
1755
QList<QFontVariableAxis> QFontEngine::variableAxes() const
1756
0
{
1757
0
    return QList<QFontVariableAxis>{};
1758
0
}
1759
1760
QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1761
0
{
1762
0
}
1763
1764
QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1765
0
    : cache(o.cache)
1766
0
{
1767
0
}
1768
1769
QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1770
0
{
1771
0
}
1772
1773
QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1774
0
{
1775
0
    cache = o.cache;
1776
0
    return *this;
1777
0
}
1778
1779
bool QFontEngine::disableEmojiSegmenter()
1780
0
{
1781
#if defined(QT_NO_EMOJISEGMENTER)
1782
    return true;
1783
#else
1784
0
    static const bool sDisableEmojiSegmenter = qEnvironmentVariableIntValue("QT_DISABLE_EMOJI_SEGMENTER") > 0;
1785
0
    return sDisableEmojiSegmenter;
1786
0
#endif
1787
0
}
1788
1789
int QFontEngine::maxCachedGlyphSize()
1790
0
{
1791
0
    static const int maxCachedSize = []{
1792
0
        if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE"))
1793
0
            return env;
1794
0
        return QT_MAX_CACHED_GLYPH_SIZE;
1795
0
    }();
1796
1797
0
    return maxCachedSize;
1798
0
}
1799
1800
// ------------------------------------------------------------------
1801
// The box font engine
1802
// ------------------------------------------------------------------
1803
1804
QFontEngineBox::QFontEngineBox(int size)
1805
0
    : QFontEngine(Box),
1806
0
      _size(size)
1807
0
{
1808
0
    cache_cost = sizeof(QFontEngineBox);
1809
0
}
1810
1811
QFontEngineBox::QFontEngineBox(Type type, int size)
1812
0
    : QFontEngine(type),
1813
0
      _size(size)
1814
0
{
1815
0
    cache_cost = sizeof(QFontEngineBox);
1816
0
}
1817
1818
QFontEngineBox::~QFontEngineBox()
1819
{
1820
}
1821
1822
glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1823
0
{
1824
0
    Q_UNUSED(ucs4);
1825
0
    return 1;
1826
0
}
1827
1828
int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1829
0
{
1830
0
    Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1831
0
    if (*nglyphs < len) {
1832
0
        *nglyphs = len;
1833
0
        return -1;
1834
0
    }
1835
1836
0
    int ucs4Length = 0;
1837
0
    QStringIterator it(str, str + len);
1838
0
    while (it.hasNext()) {
1839
0
        it.advance();
1840
0
        glyphs->glyphs[ucs4Length++] = 1;
1841
0
    }
1842
1843
0
    *nglyphs = ucs4Length;
1844
0
    glyphs->numGlyphs = ucs4Length;
1845
1846
0
    if (!(flags & GlyphIndicesOnly))
1847
0
        recalcAdvances(glyphs, flags);
1848
1849
0
    return *nglyphs;
1850
0
}
1851
1852
void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1853
0
{
1854
0
    for (int i = 0; i < glyphs->numGlyphs; i++)
1855
0
        glyphs->advances[i] = _size;
1856
0
}
1857
1858
void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1859
0
{
1860
0
    if (!glyphs.numGlyphs)
1861
0
        return;
1862
1863
0
    QVarLengthArray<QFixedPoint> positions;
1864
0
    QVarLengthArray<glyph_t> positioned_glyphs;
1865
0
    QTransform matrix = QTransform::fromTranslate(x, y - _size);
1866
0
    getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1867
1868
0
    QSize s(_size - 3, _size - 3);
1869
0
    for (int k = 0; k < positions.size(); k++)
1870
0
        path->addRect(QRectF(positions[k].toPointF(), s));
1871
0
}
1872
1873
glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1874
0
{
1875
0
    glyph_metrics_t overall;
1876
0
    overall.width = _size*glyphs.numGlyphs;
1877
0
    overall.height = _size;
1878
0
    overall.xoff = overall.width;
1879
0
    return overall;
1880
0
}
1881
1882
void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1883
0
{
1884
0
    if (!ti.glyphs.numGlyphs)
1885
0
        return;
1886
1887
    // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1888
0
    QSize s(_size - 3, _size - 3);
1889
1890
0
    QVarLengthArray<QFixedPoint> positions;
1891
0
    QVarLengthArray<glyph_t> glyphs;
1892
0
    QTransform matrix = QTransform::fromTranslate(x, y - _size);
1893
0
    ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1894
0
    if (glyphs.size() == 0)
1895
0
        return;
1896
1897
1898
0
    QPainter *painter = p->painter();
1899
0
    painter->save();
1900
0
    painter->setBrush(Qt::NoBrush);
1901
0
    QPen pen = painter->pen();
1902
0
    pen.setWidthF(lineThickness().toReal());
1903
0
    painter->setPen(pen);
1904
0
    for (int k = 0; k < positions.size(); k++)
1905
0
        painter->drawRect(QRectF(positions[k].toPointF(), s));
1906
0
    painter->restore();
1907
0
}
1908
1909
glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1910
0
{
1911
0
    return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1912
0
}
1913
1914
QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1915
0
{
1916
0
    QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1917
0
    return fe;
1918
0
}
1919
1920
QFixed QFontEngineBox::ascent() const
1921
0
{
1922
0
    return _size;
1923
0
}
1924
1925
QFixed QFontEngineBox::capHeight() const
1926
0
{
1927
0
    return _size;
1928
0
}
1929
1930
QFixed QFontEngineBox::descent() const
1931
0
{
1932
0
    return 0;
1933
0
}
1934
1935
QFixed QFontEngineBox::leading() const
1936
0
{
1937
0
    QFixed l = _size * QFixed::fromReal(qreal(0.15));
1938
0
    return l.ceil();
1939
0
}
1940
1941
qreal QFontEngineBox::maxCharWidth() const
1942
0
{
1943
0
    return _size;
1944
0
}
1945
1946
bool QFontEngineBox::canRender(const QChar *, int) const
1947
0
{
1948
0
    return true;
1949
0
}
1950
1951
QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1952
0
{
1953
0
    QImage image(_size, _size, QImage::Format_Alpha8);
1954
0
    image.fill(0);
1955
1956
0
    uchar *bits = image.bits();
1957
0
    for (int i=2; i <= _size-3; ++i) {
1958
0
        bits[i + 2 * image.bytesPerLine()] = 255;
1959
0
        bits[i + (_size - 3) * image.bytesPerLine()] = 255;
1960
0
        bits[2 + i * image.bytesPerLine()] = 255;
1961
0
        bits[_size - 3 + i * image.bytesPerLine()] = 255;
1962
0
    }
1963
0
    return image;
1964
0
}
1965
1966
// ------------------------------------------------------------------
1967
// Multi engine
1968
// ------------------------------------------------------------------
1969
1970
uchar QFontEngineMulti::highByte(glyph_t glyph)
1971
0
{ return glyph >> 24; }
1972
1973
// strip high byte from glyph
1974
static inline glyph_t stripped(glyph_t glyph)
1975
0
{ return glyph & 0x00ffffff; }
1976
1977
QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1978
0
    : QFontEngine(Multi),
1979
0
      m_fallbackFamilies(fallbackFamilies),
1980
0
      m_script(script),
1981
0
      m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1982
0
{
1983
0
    Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1984
1985
0
    if (m_fallbackFamilies.isEmpty()) {
1986
        // defer obtaining the fallback families until loadEngine(1)
1987
0
        m_fallbackFamilies << QString();
1988
0
    }
1989
1990
0
    m_engines.resize(m_fallbackFamilies.size() + 1);
1991
1992
0
    engine->ref.ref();
1993
0
    m_engines[0] = engine;
1994
1995
0
    fontDef = engine->fontDef;
1996
0
    cache_cost = engine->cache_cost;
1997
0
}
1998
1999
QFontEngineMulti::~QFontEngineMulti()
2000
0
{
2001
0
    for (int i = 0; i < m_engines.size(); ++i) {
2002
0
        QFontEngine *fontEngine = m_engines.at(i);
2003
0
        if (fontEngine && !fontEngine->ref.deref())
2004
0
            delete fontEngine;
2005
0
    }
2006
0
}
2007
2008
QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script);
2009
2010
void QFontEngineMulti::ensureFallbackFamiliesQueried()
2011
0
{
2012
0
    QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
2013
0
    if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
2014
0
        styleHint = QFont::TypeWriter;
2015
2016
0
    setFallbackFamiliesList(qt_fallbacksForFamily(fontDef.family(),
2017
0
                                                  QFont::Style(fontDef.style), styleHint,
2018
0
                                                  QFontDatabasePrivate::ExtendedScript(m_script)));
2019
0
}
2020
2021
void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
2022
0
{
2023
0
    Q_ASSERT(!m_fallbackFamiliesQueried);
2024
2025
0
    m_fallbackFamilies = fallbackFamilies;
2026
0
    if (m_fallbackFamilies.isEmpty()) {
2027
        // turns out we lied about having any fallback at all
2028
0
        Q_ASSERT(m_engines.size() == 2); // see c-tor for details
2029
0
        QFontEngine *engine = m_engines.at(0);
2030
0
        engine->ref.ref();
2031
0
        m_engines[1] = engine;
2032
0
        m_fallbackFamilies << fontDef.family();
2033
0
    } else {
2034
0
        m_engines.resize(m_fallbackFamilies.size() + 1);
2035
0
    }
2036
2037
0
    m_fallbackFamiliesQueried = true;
2038
0
}
2039
2040
void QFontEngineMulti::ensureEngineAt(int at)
2041
0
{
2042
0
    if (!m_fallbackFamiliesQueried && at > 0)
2043
0
        ensureFallbackFamiliesQueried();
2044
0
    Q_ASSERT(at < m_engines.size());
2045
0
    if (!m_engines.at(at)) {
2046
0
        QFontEngine *engine = loadEngine(at);
2047
0
        if (!engine)
2048
0
            engine = new QFontEngineBox(fontDef.pixelSize);
2049
0
        Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
2050
0
        engine->ref.ref();
2051
0
        m_engines[at] = engine;
2052
0
    }
2053
0
}
2054
2055
QFontEngine *QFontEngineMulti::loadEngine(int at)
2056
0
{
2057
0
    QFontDef request(fontDef);
2058
0
    request.styleStrategy |= QFont::NoFontMerging;
2059
0
    request.families = QStringList(fallbackFamilyAt(at - 1));
2060
2061
    // At this point, the main script of the text has already been considered
2062
    // when fetching the list of fallback families from the database, and the
2063
    // info about the actual script of the characters may have been discarded,
2064
    // so we do not check for writing system support, but instead just load
2065
    // the family indiscriminately.
2066
0
    if (QFontEngine *engine = QFontDatabasePrivate::findFont(request, QFontDatabasePrivate::Script_Common)) {
2067
0
        engine->fontDef.weight = request.weight;
2068
0
        if (request.style > QFont::StyleNormal)
2069
0
            engine->fontDef.style = request.style;
2070
0
        return engine;
2071
0
    }
2072
2073
0
    return nullptr;
2074
0
}
2075
2076
int QFontEngineMulti::glyphCount() const
2077
0
{
2078
0
    return engine(0)->glyphCount();
2079
0
}
2080
2081
glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
2082
0
{
2083
0
    glyph_t glyph = engine(0)->glyphIndex(ucs4);
2084
0
    if (glyph == 0 && !isIgnorableChar(ucs4)) {
2085
0
        if (!m_fallbackFamiliesQueried)
2086
0
            const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2087
0
        for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2088
0
            QFontEngine *engine = m_engines.at(x);
2089
0
            if (!engine) {
2090
0
                if (!shouldLoadFontEngineForCharacter(x, ucs4))
2091
0
                    continue;
2092
0
                const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2093
0
                engine = m_engines.at(x);
2094
0
            }
2095
0
            Q_ASSERT(engine != nullptr);
2096
0
            if (engine->type() == Box)
2097
0
                continue;
2098
2099
0
            glyph = engine->glyphIndex(ucs4);
2100
0
            if (glyph != 0) {
2101
                // set the high byte to indicate which engine the glyph came from
2102
0
                glyph |= (x << 24);
2103
0
                break;
2104
0
            }
2105
0
        }
2106
0
    }
2107
2108
0
    return glyph;
2109
0
}
2110
2111
QString QFontEngineMulti::glyphName(glyph_t glyph) const
2112
0
{
2113
0
    const int which = highByte(glyph);
2114
0
    const_cast<QFontEngineMulti *>(this)->ensureEngineAt(which);
2115
0
    return engine(which)->glyphName(stripped(glyph));
2116
0
}
2117
2118
glyph_t QFontEngineMulti::findGlyph(QLatin1StringView name) const
2119
0
{
2120
0
    return engine(0)->findGlyph(name);
2121
0
}
2122
2123
int QFontEngineMulti::stringToCMap(const QChar *str, int len,
2124
                                   QGlyphLayout *glyphs, int *nglyphs,
2125
                                   QFontEngine::ShaperFlags flags) const
2126
0
{
2127
0
    qCDebug(lcTextFontCmap) << "Mapping characters in"
2128
0
                            << QString(str, len)
2129
0
                            << "; Shaper flags:" << flags;
2130
2131
0
    const int originalNumGlyphs = glyphs->numGlyphs;
2132
0
    int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags);
2133
0
    qCDebug(lcTextFontCmap) << "    Main font mapped"
2134
0
                            << mappedGlyphCount
2135
0
                            << "of"
2136
0
                            << (nglyphs != nullptr ? *nglyphs : 0)
2137
0
                            << "characters";
2138
2139
0
    if (mappedGlyphCount < 0)
2140
0
        return -1;
2141
2142
    // If ContextFontMerging is set and the match for the string was incomplete, we try all
2143
    // fallbacks on the full string until we find the best match.
2144
0
    bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging);
2145
0
    if (contextFontMerging) {
2146
0
        qCDebug(lcTextFontCmap) << "    Context font merging, first pass";
2147
2148
0
        QVarLengthGlyphLayoutArray tempLayout(len);
2149
0
        if (!m_fallbackFamiliesQueried)
2150
0
            const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2151
2152
0
        int maxGlyphCount = 0;
2153
0
        uchar engineIndex = 0;
2154
0
        for (int x = 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2155
0
            int numGlyphs = len;
2156
0
            const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2157
0
            maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags);
2158
0
            qCDebug(lcTextFontCmap) << "    Fallback" << x << "mapped" << maxGlyphCount;
2159
2160
            // If we found a better match, we copy data into the main QGlyphLayout
2161
0
            if (maxGlyphCount > mappedGlyphCount) {
2162
0
                qCDebug(lcTextFontCmap) << "        Better match!";
2163
0
                *nglyphs = numGlyphs;
2164
0
                glyphs->numGlyphs = originalNumGlyphs;
2165
0
                glyphs->copy(&tempLayout);
2166
0
                engineIndex = x;
2167
0
                if (maxGlyphCount == numGlyphs)
2168
0
                    break;
2169
0
            } else {
2170
0
                qCDebug(lcTextFontCmap) << "        No improvement";
2171
0
            }
2172
0
        }
2173
2174
0
        if (engineIndex > 0) {
2175
0
            for (int y = 0; y < glyphs->numGlyphs; ++y) {
2176
0
                if (glyphs->glyphs[y] != 0)
2177
0
                    glyphs->glyphs[y] |= (engineIndex << 24);
2178
0
            }
2179
0
        } else {
2180
0
            contextFontMerging = false;
2181
0
        }
2182
2183
0
        mappedGlyphCount = maxGlyphCount;
2184
0
    }
2185
2186
    // Fill in missing glyphs by going through string one character at the time and finding
2187
    // the first viable fallback.
2188
0
    int glyph_pos = 0;
2189
0
    QStringIterator it(str, str + len);
2190
2191
0
    const bool enableVariationSelectorHack = disableEmojiSegmenter();
2192
0
    char32_t previousUcs4 = 0;
2193
2194
0
    int lastFallback = -1;
2195
0
    while (it.hasNext()) {
2196
0
        const char32_t ucs4 = it.peekNext();
2197
2198
        // If we applied a fallback font to previous glyph, and the current is either
2199
        // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
2200
        // to get the correct shaping rules applied.
2201
0
        if (lastFallback >= 0 && (ucs4 == 0x200d || ucs4 == 0x200c)) {
2202
0
            QFontEngine *engine = m_engines.at(lastFallback);
2203
0
            glyph_t glyph = engine->glyphIndex(ucs4);
2204
0
            if (glyph != 0) {
2205
0
                qCDebug(lcTextFontCmap) << "    Applying ZWJ/ZWNJ special case for"
2206
0
                                        << QString::number(ucs4, 16)
2207
0
                                        << ", lastFallback:" << lastFallback;
2208
2209
0
                glyphs->glyphs[glyph_pos] = glyph;
2210
0
                if (!(flags & GlyphIndicesOnly)) {
2211
0
                    QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2212
0
                    engine->recalcAdvances(&g, flags);
2213
0
                }
2214
2215
                // set the high byte to indicate which engine the glyph came from
2216
0
                glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
2217
0
            } else {
2218
0
                lastFallback = -1;
2219
0
            }
2220
0
        } else {
2221
0
            lastFallback = -1;
2222
0
        }
2223
2224
0
        if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) {
2225
0
            qCDebug(lcTextFontCmap) << "    Unmapped character"
2226
0
                                    << QString::number(ucs4, 16);
2227
2228
0
            if (!m_fallbackFamiliesQueried)
2229
0
                const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
2230
0
            for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), maximumEngineCount()); x < n; ++x) {
2231
0
                QFontEngine *engine = m_engines.at(x);
2232
0
                if (!engine) {
2233
0
                    if (!shouldLoadFontEngineForCharacter(x, ucs4)) {
2234
0
                        qCDebug(lcTextFontCmap) << "        Skipping fallback #" << x;
2235
0
                        continue;
2236
0
                    }
2237
0
                    const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x);
2238
0
                    engine = m_engines.at(x);
2239
0
                    if (!engine)
2240
0
                        continue;
2241
0
                }
2242
0
                Q_ASSERT(engine != nullptr);
2243
0
                if (engine->type() == Box)
2244
0
                    continue;
2245
2246
0
                glyph_t glyph = engine->glyphIndex(ucs4);
2247
0
                qCDebug(lcTextFontCmap) << "        Fallback #" << x
2248
0
                                        << ", family:" << engine->fontDef.families
2249
0
                                        << ", styleName:" << engine->fontDef.styleName
2250
0
                                        << ", style:" << engine->fontDef.style
2251
0
                                        << ", weight:" << engine->fontDef.weight
2252
0
                                        << ", glyph mapping:" << glyph;
2253
0
                if (glyph != 0) {
2254
0
                    glyphs->glyphs[glyph_pos] = glyph;
2255
0
                    if (!(flags & GlyphIndicesOnly)) {
2256
0
                        QGlyphLayout g = glyphs->mid(glyph_pos, 1);
2257
0
                        engine->recalcAdvances(&g, flags);
2258
0
                    }
2259
2260
0
                    lastFallback = x;
2261
2262
                    // set the high byte to indicate which engine the glyph came from
2263
0
                    glyphs->glyphs[glyph_pos] |= (x << 24);
2264
0
                    break;
2265
0
                }
2266
0
            }
2267
2268
            // For variant-selectors, they are modifiers to the previous character. If we
2269
            // end up with different font selections for the selector and the character it
2270
            // modifies, we try applying the selector font to the preceding character as well
2271
0
            const int variantSelectorBlock = 0xFE00;
2272
0
            if (enableVariationSelectorHack && (ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) {
2273
0
                int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24;
2274
0
                int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24;
2275
2276
0
                if (selectorFontEngine != precedingCharacterFontEngine) {
2277
                    // Emoji variant selectors are specially handled and should affect font
2278
                    // selection. If VS-16 is used, then this means we want to select a color
2279
                    // font. If the selected font is already a color font, we do not need search
2280
                    // again. If the VS-15 is used, then this means we want to select a non-color
2281
                    // font. If the selected font is not a color font, we don't do anything.
2282
0
                    const QFontEngine *selectedEngine = m_engines.at(precedingCharacterFontEngine);
2283
0
                    const bool colorFont = selectedEngine->isColorFont();
2284
0
                    const char32_t vs15 = 0xFE0E;
2285
0
                    const char32_t vs16 = 0xFE0F;
2286
0
                    bool adaptVariantSelector = ucs4 < vs15
2287
0
                                                || (ucs4 == vs15 && colorFont)
2288
0
                                                || (ucs4 == vs16 && !colorFont);
2289
2290
0
                    if (adaptVariantSelector) {
2291
0
                        QFontEngine *engine = m_engines.at(selectorFontEngine);
2292
0
                        glyph_t glyph = engine->glyphIndex(previousUcs4);
2293
0
                        if (glyph != 0) {
2294
0
                            glyphs->glyphs[glyph_pos - 1] = glyph;
2295
0
                            if (!(flags & GlyphIndicesOnly)) {
2296
0
                                QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1);
2297
0
                                engine->recalcAdvances(&g, flags);
2298
0
                            }
2299
2300
                            // set the high byte to indicate which engine the glyph came from
2301
0
                            glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24);
2302
0
                        }
2303
0
                    }
2304
0
                }
2305
0
            }
2306
0
        }
2307
2308
0
        it.advance();
2309
0
        ++glyph_pos;
2310
2311
0
        previousUcs4 = ucs4;
2312
0
    }
2313
2314
0
    *nglyphs = glyph_pos;
2315
0
    glyphs->numGlyphs = glyph_pos;
2316
0
    return mappedGlyphCount;
2317
0
}
2318
2319
bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
2320
0
{
2321
0
    Q_UNUSED(at);
2322
0
    Q_UNUSED(ucs4);
2323
0
    return true;
2324
0
}
2325
2326
glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
2327
0
{
2328
0
    if (glyphs.numGlyphs <= 0)
2329
0
        return glyph_metrics_t();
2330
2331
0
    glyph_metrics_t overall;
2332
2333
0
    int which = highByte(glyphs.glyphs[0]);
2334
0
    int start = 0;
2335
0
    int end, i;
2336
0
    for (end = 0; end < glyphs.numGlyphs; ++end) {
2337
0
        const int e = highByte(glyphs.glyphs[end]);
2338
0
        if (e == which)
2339
0
            continue;
2340
2341
        // set the high byte to zero
2342
0
        for (i = start; i < end; ++i)
2343
0
            glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2344
2345
        // merge the bounding box for this run
2346
0
        const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2347
2348
0
        overall.x = qMin(overall.x, gm.x);
2349
0
        overall.y = qMin(overall.y, gm.y);
2350
0
        overall.width = overall.xoff + gm.width;
2351
0
        overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2352
0
                         qMin(overall.y, gm.y);
2353
0
        overall.xoff += gm.xoff;
2354
0
        overall.yoff += gm.yoff;
2355
2356
        // reset the high byte for all glyphs
2357
0
        const int hi = which << 24;
2358
0
        for (i = start; i < end; ++i)
2359
0
            glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2360
2361
        // change engine
2362
0
        start = end;
2363
0
        which = e;
2364
0
    }
2365
2366
    // set the high byte to zero
2367
0
    for (i = start; i < end; ++i)
2368
0
        glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2369
2370
    // merge the bounding box for this run
2371
0
    const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
2372
2373
0
    overall.x = qMin(overall.x, gm.x);
2374
0
    overall.y = qMin(overall.y, gm.y);
2375
0
    overall.width = overall.xoff + gm.width;
2376
0
    overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
2377
0
                     qMin(overall.y, gm.y);
2378
0
    overall.xoff += gm.xoff;
2379
0
    overall.yoff += gm.yoff;
2380
2381
    // reset the high byte for all glyphs
2382
0
    const int hi = which << 24;
2383
0
    for (i = start; i < end; ++i)
2384
0
        glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2385
2386
0
    return overall;
2387
0
}
2388
2389
void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2390
0
{
2391
0
    int which = highByte(glyph);
2392
0
    ensureEngineAt(which);
2393
0
    engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
2394
0
}
2395
2396
void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2397
                                        QPainterPath *path, QTextItem::RenderFlags flags)
2398
0
{
2399
0
    if (glyphs.numGlyphs <= 0)
2400
0
        return;
2401
2402
0
    int which = highByte(glyphs.glyphs[0]);
2403
0
    int start = 0;
2404
0
    int end, i;
2405
0
    if (flags & QTextItem::RightToLeft) {
2406
0
        for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2407
0
            x += glyphs.advances[gl].toReal();
2408
0
    }
2409
0
    for (end = 0; end < glyphs.numGlyphs; ++end) {
2410
0
        const int e = highByte(glyphs.glyphs[end]);
2411
0
        if (e == which)
2412
0
            continue;
2413
2414
0
        if (flags & QTextItem::RightToLeft) {
2415
0
            for (i = start; i < end; ++i)
2416
0
                x -= glyphs.advances[i].toReal();
2417
0
        }
2418
2419
        // set the high byte to zero
2420
0
        for (i = start; i < end; ++i)
2421
0
            glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2422
0
        engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2423
        // reset the high byte for all glyphs and update x and y
2424
0
        const int hi = which << 24;
2425
0
        for (i = start; i < end; ++i)
2426
0
            glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2427
2428
0
        if (!(flags & QTextItem::RightToLeft)) {
2429
0
            for (i = start; i < end; ++i)
2430
0
                x += glyphs.advances[i].toReal();
2431
0
        }
2432
2433
        // change engine
2434
0
        start = end;
2435
0
        which = e;
2436
0
    }
2437
2438
0
    if (flags & QTextItem::RightToLeft) {
2439
0
        for (i = start; i < end; ++i)
2440
0
            x -= glyphs.advances[i].toReal();
2441
0
    }
2442
2443
    // set the high byte to zero
2444
0
    for (i = start; i < end; ++i)
2445
0
        glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
2446
2447
0
    engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
2448
2449
    // reset the high byte for all glyphs
2450
0
    const int hi = which << 24;
2451
0
    for (i = start; i < end; ++i)
2452
0
        glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2453
0
}
2454
2455
void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2456
0
{
2457
0
    if (glyphs->numGlyphs <= 0)
2458
0
        return;
2459
2460
0
    int which = highByte(glyphs->glyphs[0]);
2461
0
    int start = 0;
2462
0
    int end, i;
2463
0
    for (end = 0; end < glyphs->numGlyphs; ++end) {
2464
0
        const int e = highByte(glyphs->glyphs[end]);
2465
0
        if (e == which)
2466
0
            continue;
2467
2468
        // set the high byte to zero
2469
0
        for (i = start; i < end; ++i)
2470
0
            glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2471
2472
0
        QGlyphLayout offs = glyphs->mid(start, end - start);
2473
0
        engine(which)->recalcAdvances(&offs, flags);
2474
2475
        // reset the high byte for all glyphs and update x and y
2476
0
        const int hi = which << 24;
2477
0
        for (i = start; i < end; ++i)
2478
0
            glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2479
2480
        // change engine
2481
0
        start = end;
2482
0
        which = e;
2483
0
    }
2484
2485
    // set the high byte to zero
2486
0
    for (i = start; i < end; ++i)
2487
0
        glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2488
2489
0
    QGlyphLayout offs = glyphs->mid(start, end - start);
2490
0
    engine(which)->recalcAdvances(&offs, flags);
2491
2492
    // reset the high byte for all glyphs
2493
0
    const int hi = which << 24;
2494
0
    for (i = start; i < end; ++i)
2495
0
        glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2496
0
}
2497
2498
void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2499
0
{
2500
0
    if (glyphs->numGlyphs <= 0)
2501
0
        return;
2502
2503
0
    int which = highByte(glyphs->glyphs[0]);
2504
0
    int start = 0;
2505
0
    int end, i;
2506
0
    for (end = 0; end < glyphs->numGlyphs; ++end) {
2507
0
        const int e = highByte(glyphs->glyphs[end]);
2508
0
        if (e == which)
2509
0
            continue;
2510
2511
        // set the high byte to zero
2512
0
        for (i = start; i < end; ++i)
2513
0
            glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2514
2515
0
        QGlyphLayout offs = glyphs->mid(start, end - start);
2516
0
        engine(which)->doKerning(&offs, flags);
2517
2518
        // reset the high byte for all glyphs and update x and y
2519
0
        const int hi = which << 24;
2520
0
        for (i = start; i < end; ++i)
2521
0
            glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2522
2523
        // change engine
2524
0
        start = end;
2525
0
        which = e;
2526
0
    }
2527
2528
    // set the high byte to zero
2529
0
    for (i = start; i < end; ++i)
2530
0
        glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
2531
2532
0
    QGlyphLayout offs = glyphs->mid(start, end - start);
2533
0
    engine(which)->doKerning(&offs, flags);
2534
2535
    // reset the high byte for all glyphs
2536
0
    const int hi = which << 24;
2537
0
    for (i = start; i < end; ++i)
2538
0
        glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2539
0
}
2540
2541
glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2542
0
{
2543
0
    const int which = highByte(glyph);
2544
0
    return engine(which)->boundingBox(stripped(glyph));
2545
0
}
2546
2547
QFixed QFontEngineMulti::emSquareSize() const
2548
0
{ return engine(0)->emSquareSize(); }
2549
2550
QFixed QFontEngineMulti::ascent() const
2551
0
{ return engine(0)->ascent(); }
2552
2553
QFixed QFontEngineMulti::capHeight() const
2554
0
{ return engine(0)->capHeight(); }
2555
2556
QFixed QFontEngineMulti::descent() const
2557
0
{ return engine(0)->descent(); }
2558
2559
QFixed QFontEngineMulti::leading() const
2560
0
{
2561
0
    return engine(0)->leading();
2562
0
}
2563
2564
QFixed QFontEngineMulti::xHeight() const
2565
0
{
2566
0
    return engine(0)->xHeight();
2567
0
}
2568
2569
QFixed QFontEngineMulti::averageCharWidth() const
2570
0
{
2571
0
    return engine(0)->averageCharWidth();
2572
0
}
2573
2574
QFixed QFontEngineMulti::lineThickness() const
2575
0
{
2576
0
    return engine(0)->lineThickness();
2577
0
}
2578
2579
QFixed QFontEngineMulti::underlinePosition() const
2580
0
{
2581
0
    return engine(0)->underlinePosition();
2582
0
}
2583
2584
qreal QFontEngineMulti::maxCharWidth() const
2585
0
{
2586
0
    return engine(0)->maxCharWidth();
2587
0
}
2588
2589
qreal QFontEngineMulti::minLeftBearing() const
2590
0
{
2591
0
    return engine(0)->minLeftBearing();
2592
0
}
2593
2594
qreal QFontEngineMulti::minRightBearing() const
2595
0
{
2596
0
    return engine(0)->minRightBearing();
2597
0
}
2598
2599
bool QFontEngineMulti::canRender(const QChar *string, int len) const
2600
0
{
2601
0
    if (engine(0)->canRender(string, len))
2602
0
        return true;
2603
2604
0
    int nglyphs = len;
2605
2606
0
    QVarLengthArray<glyph_t> glyphs(nglyphs);
2607
2608
0
    QGlyphLayout g;
2609
0
    g.numGlyphs = nglyphs;
2610
0
    g.glyphs = glyphs.data();
2611
0
    if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0)
2612
0
        Q_UNREACHABLE();
2613
2614
0
    for (int i = 0; i < nglyphs; i++) {
2615
0
        if (glyphs[i] == 0)
2616
0
            return false;
2617
0
    }
2618
2619
0
    return true;
2620
0
}
2621
2622
/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2623
 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2624
2625
QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2626
0
{
2627
0
    const int which = highByte(glyph);
2628
0
    return engine(which)->alphaMapForGlyph(stripped(glyph));
2629
0
}
2630
2631
QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QFixedPoint &subPixelPosition)
2632
0
{
2633
0
    const int which = highByte(glyph);
2634
0
    return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition);
2635
0
}
2636
2637
QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2638
0
{
2639
0
    const int which = highByte(glyph);
2640
0
    return engine(which)->alphaMapForGlyph(stripped(glyph), t);
2641
0
}
2642
2643
QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph,
2644
                                          const QFixedPoint &subPixelPosition,
2645
                                          const QTransform &t)
2646
0
{
2647
0
    const int which = highByte(glyph);
2648
0
    return engine(which)->alphaMapForGlyph(stripped(glyph), subPixelPosition, t);
2649
0
}
2650
2651
QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph,
2652
                                             const QFixedPoint &subPixelPosition,
2653
                                             const QTransform &t)
2654
0
{
2655
0
    const int which = highByte(glyph);
2656
0
    return engine(which)->alphaRGBMapForGlyph(stripped(glyph), subPixelPosition, t);
2657
0
}
2658
2659
QList<QFontVariableAxis> QFontEngineMulti::variableAxes() const
2660
0
{
2661
0
    return engine(0)->variableAxes();
2662
0
}
2663
2664
/*
2665
  This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2666
2667
  The purpose of this is to provide the necessary font fallbacks when drawing complex
2668
  text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2669
  the same raw font over and over again, we want to cache the corresponding multi font engine
2670
  as it may contain fallback font engines already.
2671
*/
2672
QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2673
0
{
2674
0
    QFontEngine *engine = nullptr;
2675
0
    QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2676
0
    QFontCache *fc = QFontCache::instance();
2677
    //  We can't rely on the fontDef (and hence the cache Key)
2678
    //  alone to distinguish webfonts, since these should not be
2679
    //  accidentally shared, even if the resulting fontcache key
2680
    //  is strictly identical. See:
2681
    //   http://www.w3.org/TR/css3-fonts/#font-face-rule
2682
0
    const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2683
0
    QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
2684
0
            end = fc->engineCache.end();
2685
0
    while (it != end && it.key() == key) {
2686
0
        Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2687
0
        QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2688
0
        if (fe == cachedEngine->engine(0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(0)->faceId().filename)) {
2689
0
            engine = cachedEngine;
2690
0
            fc->updateHitCountAndTimeStamp(it.value());
2691
0
            break;
2692
0
        }
2693
0
        ++it;
2694
0
    }
2695
0
    if (!engine) {
2696
0
        engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QFontDatabasePrivate::ExtendedScript(script));
2697
0
        fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2698
0
    }
2699
0
    Q_ASSERT(engine);
2700
0
    return engine;
2701
0
}
2702
2703
QTestFontEngine::QTestFontEngine(int size)
2704
0
    : QFontEngineBox(TestFontEngine, size)
2705
0
{}
2706
2707
QT_END_NAMESPACE