1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtGui module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include <qdebug.h>
43
#include <private/qfontengine_p.h>
44
45
#include "qbitmap.h"
46
#include "qpainter.h"
47
#include "qpainterpath.h"
48
#include "qvarlengtharray.h"
49
#include <qmath.h>
50
#include <qendian.h>
51
#include <private/qharfbuzz_p.h>
52
53
QT_BEGIN_NAMESPACE
54
55
static inline bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
56
{
57
    if (a.type() <= QTransform::TxTranslate && b.type() <= QTransform::TxTranslate) {
58
        return true;
59
    } else {
60
        // We always use paths for perspective text anyway, so no
61
        // point in checking the full matrix...
62
        Q_ASSERT(a.type() < QTransform::TxProject);
63
        Q_ASSERT(b.type() < QTransform::TxProject);
64
65
        return a.m11() == b.m11()
66
            && a.m12() == b.m12()
67
            && a.m21() == b.m21()
68
            && a.m22() == b.m22();
69
    }
70
}
71
72
// Harfbuzz helper functions
73
74
static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft)
75
{
76
    QFontEngine *fe = (QFontEngine *)font->userData;
77
78
    QVarLengthGlyphLayoutArray qglyphs(*numGlyphs);
79
80
    QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
81
    if (rightToLeft)
82
        shaperFlags |= QTextEngine::RightToLeft;
83
84
    int nGlyphs = *numGlyphs;
85
    bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(string), length, &qglyphs, &nGlyphs, shaperFlags);
86
    *numGlyphs = nGlyphs;
87
    if (!result)
88
        return false;
89
90
    for (hb_uint32 i = 0; i < *numGlyphs; ++i)
91
        glyphs[i] = qglyphs.glyphs[i];
92
93
    return true;
94
}
95
96
static void hb_getAdvances(HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags)
97
{
98
    QFontEngine *fe = (QFontEngine *)font->userData;
99
100
    QVarLengthGlyphLayoutArray qglyphs(numGlyphs);
101
102
    for (hb_uint32 i = 0; i < numGlyphs; ++i)
103
        qglyphs.glyphs[i] = glyphs[i];
104
105
    fe->recalcAdvances(&qglyphs, flags & HB_ShaperFlag_UseDesignMetrics ? QFlags<QTextEngine::ShaperFlag>(QTextEngine::DesignMetrics) : QFlags<QTextEngine::ShaperFlag>(0));
106
107
    for (hb_uint32 i = 0; i < numGlyphs; ++i)
108
        advances[i] = qglyphs.advances_x[i].value();
109
}
110
111
static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
112
{
113
    QFontEngine *fe = (QFontEngine *)font->userData;
114
    return fe->canRender(reinterpret_cast<const QChar *>(string), length);
115
}
116
117
static void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
118
{
119
    QFontEngine *fe = (QFontEngine *)font->userData;
120
    glyph_metrics_t m = fe->boundingBox(glyph);
121
    metrics->x = m.x.value();
122
    metrics->y = m.y.value();
123
    metrics->width = m.width.value();
124
    metrics->height = m.height.value();
125
    metrics->xOffset = m.xoff.value();
126
    metrics->yOffset = m.yoff.value();
127
}
128
129
static HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
130
{
131
    QFontEngine *fe = (QFontEngine *)font->userData;
132
    switch (metric) {
133
    case HB_FontAscent:
134
        return fe->ascent().value();
135
        break;
136
    case HB_FontDescent:
137
        return fe->descent().value();
138
        break;
139
    default:
140
        return 0;
141
    }
142
}
143
144
HB_Error QFontEngine::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
145
{
146
    Q_UNUSED(glyph)
147
    Q_UNUSED(flags)
148
    Q_UNUSED(point)
149
    Q_UNUSED(xpos)
150
    Q_UNUSED(ypos)
151
    Q_UNUSED(nPoints)
152
    return HB_Err_Not_Covered;
153
}
154
155
static HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
156
{
157
    QFontEngine *fe = (QFontEngine *)font->userData;
158
    return fe->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
159
}
160
161
static const HB_FontClass hb_fontClass = {
162
    hb_stringToGlyphs, hb_getAdvances, hb_canRender, hb_getPointInOutline,
163
    hb_getGlyphMetrics, hb_getFontMetric
164
};
165
166
static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
167
{
168
    QFontEngine *fe = (QFontEngine *)font;
169
    if (!fe->getSfntTableData(tableTag, buffer, length))
170
        return HB_Err_Invalid_Argument;
171
    return HB_Err_Ok;
172
}
173
174
// QFontEngine
175
176
QFontEngine::QFontEngine()
177
    : QObject()
178
{
179
    ref = 0;
180
    cache_count = 0;
181
    fsType = 0;
182
    symbol = false;
183
    memset(&hbFont, 0, sizeof(hbFont));
184
    hbFont.klass = &hb_fontClass;
185
    hbFont.userData = this;
186
187
    hbFace = 0;
188
    glyphFormat = -1;
189
}
190
191
QFontEngine::~QFontEngine()
192
{
193
    m_glyphCaches.clear();
194
    qHBFreeFace(hbFace);
195
}
196
197
QFixed QFontEngine::lineThickness() const
198
{
199
    // ad hoc algorithm
200
    int score = fontDef.weight * fontDef.pixelSize;
201
    int lw = score / 700;
202
203
    // looks better with thicker line for small pointsizes
204
    if (lw < 2 && score >= 1050) lw = 2;
205
    if (lw == 0) lw = 1;
206
207
    return lw;
208
}
209
210
QFixed QFontEngine::underlinePosition() const
211
{
212
    return ((lineThickness() * 2) + 3) / 6;
213
}
214
215
HB_Font QFontEngine::harfbuzzFont() const
216
{
217
    if (!hbFont.x_ppem) {
218
        QFixed emSquare = emSquareSize();
219
        hbFont.x_ppem = fontDef.pixelSize;
220
        hbFont.y_ppem = fontDef.pixelSize * fontDef.stretch / 100;
221
        hbFont.x_scale = (QFixed(hbFont.x_ppem * (1 << 16)) / emSquare).value();
222
        hbFont.y_scale = (QFixed(hbFont.y_ppem * (1 << 16)) / emSquare).value();
223
    }
224
    return &hbFont;
225
}
226
227
HB_Face QFontEngine::harfbuzzFace() const
228
{
229
    if (!hbFace) {
230
        hbFace = qHBNewFace(const_cast<QFontEngine *>(this), hb_getSFntTable);
231
        Q_CHECK_PTR(hbFace);
232
    }
233
    return hbFace;
234
}
235
236
glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
237
{
238
    glyph_metrics_t metrics = boundingBox(glyph);
239
240
    if (matrix.type() > QTransform::TxTranslate) {
241
        return metrics.transformed(matrix);
242
    }
243
    return metrics;
244
}
245
246
QFixed QFontEngine::xHeight() const
247
{
248
    QGlyphLayoutArray<8> glyphs;
249
    int nglyphs = 7;
250
    QChar x((ushort)'x');
251
    stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
252
253
    glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]);
254
    return bb.height;
255
}
256
257
QFixed QFontEngine::averageCharWidth() const
258
{
259
    QGlyphLayoutArray<8> glyphs;
260
    int nglyphs = 7;
261
    QChar x((ushort)'x');
262
    stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
263
264
    glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]);
265
    return bb.xoff;
266
}
267
268
269
void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
270
                                    QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
271
{
272
    QFixed xpos;
273
    QFixed ypos;
274
275
    const bool transform = matrix.m11() != 1.
276
                           || matrix.m12() != 0.
277
                           || matrix.m21() != 0.
278
                           || matrix.m22() != 1.;
279
    if (!transform) {
280
        xpos = QFixed::fromReal(matrix.dx());
281
        ypos = QFixed::fromReal(matrix.dy());
282
    }
283
284
    int current = 0;
285
    if (flags & QTextItem::RightToLeft) {
286
        int i = glyphs.numGlyphs;
287
        int totalKashidas = 0;
288
        while(i--) {
289
            if (glyphs.attributes[i].dontPrint)
290
                continue;
291
            xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
292
            ypos += glyphs.advances_y[i];
293
            totalKashidas += glyphs.justifications[i].nKashidas;
294
        }
295
        positions.resize(glyphs.numGlyphs+totalKashidas);
296
        glyphs_out.resize(glyphs.numGlyphs+totalKashidas);
297
298
        i = 0;
299
        while(i < glyphs.numGlyphs) {
300
            if (glyphs.attributes[i].dontPrint) {
301
                ++i;
302
                continue;
303
            }
304
            xpos -= glyphs.advances_x[i];
305
            ypos -= glyphs.advances_y[i];
306
307
            QFixed gpos_x = xpos + glyphs.offsets[i].x;
308
            QFixed gpos_y = ypos + glyphs.offsets[i].y;
309
            if (transform) {
310
                QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
311
                gpos = gpos * matrix;
312
                gpos_x = QFixed::fromReal(gpos.x());
313
                gpos_y = QFixed::fromReal(gpos.y());
314
            }
315
            positions[current].x = gpos_x;
316
            positions[current].y = gpos_y;
317
            glyphs_out[current] = glyphs.glyphs[i];
318
            ++current;
319
            if (glyphs.justifications[i].nKashidas) {
320
                QChar ch(0x640); // Kashida character
321
                QGlyphLayoutArray<8> g;
322
                int nglyphs = 7;
323
                stringToCMap(&ch, 1, &g, &nglyphs, 0);
324
                for (uint k = 0; k < glyphs.justifications[i].nKashidas; ++k) {
325
                    xpos -= g.advances_x[0];
326
                    ypos -= g.advances_y[0];
327
328
                    QFixed gpos_x = xpos + glyphs.offsets[i].x;
329
                    QFixed gpos_y = ypos + glyphs.offsets[i].y;
330
                    if (transform) {
331
                        QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
332
                        gpos = gpos * matrix;
333
                        gpos_x = QFixed::fromReal(gpos.x());
334
                        gpos_y = QFixed::fromReal(gpos.y());
335
                    }
336
                    positions[current].x = gpos_x;
337
                    positions[current].y = gpos_y;
338
                    glyphs_out[current] = g.glyphs[0];
339
                    ++current;
340
                }
341
            } else {
342
                xpos -= QFixed::fromFixed(glyphs.justifications[i].space_18d6);
343
            }
344
            ++i;
345
        }
346
    } else {
347
        positions.resize(glyphs.numGlyphs);
348
        glyphs_out.resize(glyphs.numGlyphs);
349
        int i = 0;
350
        if (!transform) {
351
            while (i < glyphs.numGlyphs) {
352
                if (!glyphs.attributes[i].dontPrint) {
353
                    positions[current].x = xpos + glyphs.offsets[i].x;
354
                    positions[current].y = ypos + glyphs.offsets[i].y;
355
                    glyphs_out[current] = glyphs.glyphs[i];
356
                    xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
357
                    ypos += glyphs.advances_y[i];
358
                    ++current;
359
                }
360
                ++i;
361
            }
362
        } else {
363
            while (i < glyphs.numGlyphs) {
364
                if (!glyphs.attributes[i].dontPrint) {
365
                    QFixed gpos_x = xpos + glyphs.offsets[i].x;
366
                    QFixed gpos_y = ypos + glyphs.offsets[i].y;
367
                    QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
368
                    gpos = gpos * matrix;
369
                    positions[current].x = QFixed::fromReal(gpos.x());
370
                    positions[current].y = QFixed::fromReal(gpos.y());
371
                    glyphs_out[current] = glyphs.glyphs[i];
372
                    xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6);
373
                    ypos += glyphs.advances_y[i];
374
                    ++current;
375
                }
376
                ++i;
377
            }
378
        }
379
    }
380
    positions.resize(current);
381
    glyphs_out.resize(current);
382
    Q_ASSERT(positions.size() == glyphs_out.size());
383
}
384
385
void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
386
{
387
    glyph_metrics_t gi = boundingBox(glyph);
388
    bool isValid = gi.isValid();
389
    if (leftBearing != 0)
390
        *leftBearing = isValid ? gi.x.toReal() : qreal(0.0);
391
    if (rightBearing != 0)
392
        *rightBearing = isValid ? (gi.xoff - gi.x - gi.width).toReal() : qreal(0.0);
393
}
394
395
glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
396
{
397
    glyph_metrics_t overall;
398
399
    QFixed ymax = 0;
400
    QFixed xmax = 0;
401
    for (int i = 0; i < glyphs.numGlyphs; i++) {
402
        glyph_metrics_t bb = boundingBox(glyphs.glyphs[i]);
403
        QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x;
404
        QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
405
        overall.x = qMin(overall.x, x);
406
        overall.y = qMin(overall.y, y);
407
        xmax = qMax(xmax, x + bb.width);
408
        ymax = qMax(ymax, y + bb.height);
409
        overall.xoff += bb.xoff;
410
        overall.yoff += bb.yoff;
411
    }
412
    overall.height = qMax(overall.height, ymax - overall.y);
413
    overall.width = xmax - overall.x;
414
415
    return overall;
416
}
417
418
419
void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
420
                                   QTextItem::RenderFlags flags)
421
{
422
    if (!glyphs.numGlyphs)
423
        return;
424
425
    QVarLengthArray<QFixedPoint> positions;
426
    QVarLengthArray<glyph_t> positioned_glyphs;
427
    QTransform matrix = QTransform::fromTranslate(x, y);
428
    getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
429
    addGlyphsToPath(positioned_glyphs.data(), positions.data(), positioned_glyphs.size(), path, flags);
430
}
431
432
#define GRID(x, y) grid[(y)*(w+1) + (x)]
433
#define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7)))
434
435
enum { EdgeRight = 0x1,
436
       EdgeDown = 0x2,
437
       EdgeLeft = 0x4,
438
       EdgeUp = 0x8
439
};
440
441
static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
442
{
443
    Q_UNUSED(h);
444
445
    path->moveTo(x + x0, y + y0);
446
    while (GRID(x, y)) {
447
        if (GRID(x, y) & EdgeRight) {
448
            while (GRID(x, y) & EdgeRight) {
449
                GRID(x, y) &= ~EdgeRight;
450
                ++x;
451
            }
452
            Q_ASSERT(x <= w);
453
            path->lineTo(x + x0, y + y0);
454
            continue;
455
        }
456
        if (GRID(x, y) & EdgeDown) {
457
            while (GRID(x, y) & EdgeDown) {
458
                GRID(x, y) &= ~EdgeDown;
459
                ++y;
460
            }
461
            Q_ASSERT(y <= h);
462
            path->lineTo(x + x0, y + y0);
463
            continue;
464
        }
465
        if (GRID(x, y) & EdgeLeft) {
466
            while (GRID(x, y) & EdgeLeft) {
467
                GRID(x, y) &= ~EdgeLeft;
468
                --x;
469
            }
470
            Q_ASSERT(x >= 0);
471
            path->lineTo(x + x0, y + y0);
472
            continue;
473
        }
474
        if (GRID(x, y) & EdgeUp) {
475
            while (GRID(x, y) & EdgeUp) {
476
                GRID(x, y) &= ~EdgeUp;
477
                --y;
478
            }
479
            Q_ASSERT(y >= 0);
480
            path->lineTo(x + x0, y + y0);
481
            continue;
482
        }
483
    }
484
    path->closeSubpath();
485
}
486
487
Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
488
{
489
    uint *grid = new uint[(w+1)*(h+1)];
490
    // set up edges
491
    for (int y = 0; y <= h; ++y) {
492
        for (int x = 0; x <= w; ++x) {
493
            bool topLeft = (x == 0)|(y == 0) ? false : SET(x - 1, y - 1);
494
            bool topRight = (x == w)|(y == 0) ? false : SET(x, y - 1);
495
            bool bottomLeft = (x == 0)|(y == h) ? false : SET(x - 1, y);
496
            bool bottomRight = (x == w)|(y == h) ? false : SET(x, y);
497
498
            GRID(x, y) = 0;
499
            if ((!topRight) & bottomRight)
500
                GRID(x, y) |= EdgeRight;
501
            if ((!bottomRight) & bottomLeft)
502
                GRID(x, y) |= EdgeDown;
503
            if ((!bottomLeft) & topLeft)
504
                GRID(x, y) |= EdgeLeft;
505
            if ((!topLeft) & topRight)
506
                GRID(x, y) |= EdgeUp;
507
        }
508
    }
509
510
    // collect edges
511
    for (int y = 0; y < h; ++y) {
512
        for (int x = 0; x < w; ++x) {
513
            if (!GRID(x, y))
514
                continue;
515
            // found start of a contour, follow it
516
            collectSingleContour(x0, y0, grid, x, y, w, h, path);
517
        }
518
    }
519
    delete [] grid;
520
}
521
522
#undef GRID
523
#undef SET
524
525
526
void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
527
                                      QPainterPath *path, QTextItem::RenderFlags flags)
528
{
529
// TODO what to do with 'flags' ??
530
    Q_UNUSED(flags);
531
    QFixed advanceX = QFixed::fromReal(x);
532
    QFixed advanceY = QFixed::fromReal(y);
533
    for (int i=0; i < glyphs.numGlyphs; ++i) {
534
        glyph_metrics_t metrics = boundingBox(glyphs.glyphs[i]);
535
        if (metrics.width.value() == 0 || metrics.height.value() == 0) {
536
            advanceX += glyphs.advances_x[i];
537
            advanceY += glyphs.advances_y[i];
538
            continue;
539
        }
540
        const QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]);
541
542
        const int w = alphaMask.width();
543
        const int h = alphaMask.height();
544
        const int srcBpl = alphaMask.bytesPerLine();
545
        QImage bitmap;
546
        if (alphaMask.depth() == 1) {
547
            bitmap = alphaMask;
548
        } else {
549
            bitmap = QImage(w, h, QImage::Format_Mono);
550
            const uchar *imageData = alphaMask.bits();
551
            const int destBpl = bitmap.bytesPerLine();
552
            uchar *bitmapData = bitmap.bits();
553
554
            for (int yi = 0; yi < h; ++yi) {
555
                const uchar *src = imageData + yi*srcBpl;
556
                uchar *dst = bitmapData + yi*destBpl;
557
                for (int xi = 0; xi < w; ++xi) {
558
                    const int byte = xi / 8;
559
                    const int bit = xi % 8;
560
                    if (bit == 0)
561
                        dst[byte] = 0;
562
                    if (src[xi])
563
                        dst[byte] |= 128 >> bit;
564
                }
565
            }
566
        }
567
        const uchar *bitmap_data = bitmap.bits();
568
        QFixedPoint offset = glyphs.offsets[i];
569
        advanceX += offset.x;
570
        advanceY += offset.y;
571
        qt_addBitmapToPath((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, bitmap.bytesPerLine(), w, h, path);
572
        advanceX += glyphs.advances_x[i];
573
        advanceY += glyphs.advances_y[i];
574
    }
575
}
576
577
void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
578
                                  QPainterPath *path, QTextItem::RenderFlags flags)
579
{
580
    qreal x = positions[0].x.toReal();
581
    qreal y = positions[0].y.toReal();
582
    QVarLengthGlyphLayoutArray g(nGlyphs);
583
584
    for (int i = 0; i < nGlyphs; ++i) {
585
        g.glyphs[i] = glyphs[i];
586
        if (i < nGlyphs - 1) {
587
            g.advances_x[i] = positions[i+1].x - positions[i].x;
588
            g.advances_y[i] = positions[i+1].y - positions[i].y;
589
        } else {
590
            g.advances_x[i] = QFixed::fromReal(maxCharWidth());
591
            g.advances_y[i] = 0;
592
        }
593
    }
594
595
    addBitmapFontToPath(x, y, g, path, flags);
596
}
597
598
QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/)
599
{
600
    // For font engines don't support subpixel positioning
601
    return alphaMapForGlyph(glyph);
602
}
603
604
QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
605
{
606
    QImage i = alphaMapForGlyph(glyph);
607
    if (t.type() > QTransform::TxTranslate)
608
        i = i.transformed(t).convertToFormat(QImage::Format_Indexed8);
609
    Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
610
611
    return i;
612
}
613
614
QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
615
{
616
    if (! supportsSubPixelPositions())
617
        return alphaMapForGlyph(glyph, t);
618
619
    QImage i = alphaMapForGlyph(glyph, subPixelPosition);
620
    if (t.type() > QTransform::TxTranslate)
621
        i = i.transformed(t).convertToFormat(QImage::Format_Indexed8);
622
    Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
623
624
    return i;
625
}
626
627
QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, int /* margin */, const QTransform &t)
628
{
629
    QImage alphaMask = alphaMapForGlyph(glyph, t);
630
    QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
631
632
    QVector<QRgb> colorTable = alphaMask.colorTable();
633
    for (int y=0; y<alphaMask.height(); ++y) {
634
        uint *dst = (uint *) rgbMask.scanLine(y);
635
        uchar *src = (uchar *) alphaMask.scanLine(y);
636
        for (int x=0; x<alphaMask.width(); ++x) {
637
            int val = qAlpha(colorTable.at(src[x]));
638
            dst[x] = qRgb(val, val, val);
639
        }
640
    }
641
642
    return rgbMask;
643
}
644
645
QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
646
{
647
    glyph_metrics_t gm = boundingBox(glyph);
648
    int glyph_x = qFloor(gm.x.toReal());
649
    int glyph_y = qFloor(gm.y.toReal());
650
    int glyph_width = qCeil((gm.x + gm.width).toReal()) -  glyph_x;
651
    int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y;
652
653
    if (glyph_width <= 0 || glyph_height <= 0)
654
        return QImage();
655
    QFixedPoint pt;
656
    pt.x = -glyph_x;
657
    pt.y = -glyph_y; // the baseline
658
    QPainterPath path;
659
    QImage im(glyph_width + 4, glyph_height, QImage::Format_ARGB32_Premultiplied);
660
    im.fill(Qt::transparent);
661
    QPainter p(&im);
662
    p.setRenderHint(QPainter::Antialiasing);
663
    addGlyphsToPath(&glyph, &pt, 1, &path, 0);
664
    p.setPen(Qt::NoPen);
665
    p.setBrush(Qt::black);
666
    p.drawPath(path);
667
    p.end();
668
669
    QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
670
    QVector<QRgb> colors(256);
671
    for (int i=0; i<256; ++i)
672
        colors[i] = qRgba(0, 0, 0, i);
673
    indexed.setColorTable(colors);
674
675
    for (int y=0; y<im.height(); ++y) {
676
        uchar *dst = (uchar *) indexed.scanLine(y);
677
        uint *src = (uint *) im.scanLine(y);
678
        for (int x=0; x<im.width(); ++x)
679
            dst[x] = qAlpha(src[x]);
680
    }
681
682
    return indexed;
683
}
684
685
void QFontEngine::removeGlyphFromCache(glyph_t)
686
{
687
}
688
689
QFontEngine::Properties QFontEngine::properties() const
690
{
691
    Properties p;
692
    QByteArray psname = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8());
693
    psname += '-';
694
    psname += QByteArray::number(fontDef.style);
695
    psname += '-';
696
    psname += QByteArray::number(fontDef.weight);
697
698
    p.postscriptName = psname;
699
    p.ascent = ascent();
700
    p.descent = descent();
701
    p.leading = leading();
702
    p.emSquare = p.ascent;
703
    p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
704
    p.italicAngle = 0;
705
    p.capHeight = p.ascent;
706
    p.lineWidth = lineThickness();
707
    return p;
708
}
709
710
void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
711
{
712
    *metrics = boundingBox(glyph);
713
    QFixedPoint p;
714
    p.x = 0;
715
    p.y = 0;
716
    addGlyphsToPath(&glyph, &p, 1, path, QFlag(0));
717
}
718
719
QByteArray QFontEngine::getSfntTable(uint tag) const
720
{
721
    QByteArray table;
722
    uint len = 0;
723
    if (!getSfntTableData(tag, 0, &len))
724
        return table;
725
    if (!len)
726
        return table;
727
    table.resize(len);
728
    if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len))
729
        return QByteArray();
730
    return table;
731
}
732
733
void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data)
734
{
735
    Q_ASSERT(data);
736
737
    GlyphCacheEntry entry;
738
    entry.context = key;
739
    entry.cache = data;
740
    if (m_glyphCaches.contains(entry))
741
        return;
742
743
    // Limit the glyph caches to 4. This covers all 90 degree rotations and limits
744
    // memory use when there is continuous or random rotation
745
    if (m_glyphCaches.size() == 4)
746
        m_glyphCaches.removeLast();
747
748
    m_glyphCaches.push_front(entry);
749
750
}
751
752
QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, QFontEngineGlyphCache::Type type, const QTransform &transform) const
753
{
754
    for (QLinkedList<GlyphCacheEntry>::const_iterator it = m_glyphCaches.constBegin(), end = m_glyphCaches.constEnd(); it != end; ++it) {
755
        QFontEngineGlyphCache *c = it->cache.data();
756
        if (key == it->context
757
            && type == c->cacheType()
758
            && qtransform_equals_no_translate(c->m_transform, transform)) {
759
            return c;
760
        }
761
    }
762
    return 0;
763
}
764
765
#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN)
766
static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
767
{
768
    uint left_right = (left << 16) + right;
769
770
    left = 0, right = numPairs - 1;
771
    while (left <= right) {
772
        int middle = left + ( ( right - left ) >> 1 );
773
774
        if(pairs[middle].left_right == left_right)
775
            return pairs[middle].adjust;
776
777
        if (pairs[middle].left_right < left_right)
778
            left = middle + 1;
779
        else
780
            right = middle - 1;
781
    }
782
    return 0;
783
}
784
785
void QFontEngine::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
786
{
787
    int numPairs = kerning_pairs.size();
788
    if(!numPairs)
789
        return;
790
791
    const KernPair *pairs = kerning_pairs.constData();
792
793
    if(flags & QTextEngine::DesignMetrics) {
794
        for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
795
            glyphs->advances_x[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs);
796
    } else {
797
        for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
798
            glyphs->advances_x[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs));
799
    }
800
}
801
802
void QFontEngine::loadKerningPairs(QFixed scalingFactor)
803
{
804
    kerning_pairs.clear();
805
806
    QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n'));
807
    if (tab.isEmpty())
808
        return;
809
810
    const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
811
812
    unsigned short version = qFromBigEndian<quint16>(table);
813
    if (version != 0) {
814
//        qDebug("wrong version");
815
       return;
816
    }
817
818
    unsigned short numTables = qFromBigEndian<quint16>(table + 2);
819
    {
820
        int offset = 4;
821
        for(int i = 0; i < numTables; ++i) {
822
            if (offset + 6 > tab.size()) {
823
//                qDebug("offset out of bounds");
824
                goto end;
825
            }
826
            const uchar *header = table + offset;
827
828
            ushort version = qFromBigEndian<quint16>(header);
829
            ushort length = qFromBigEndian<quint16>(header+2);
830
            ushort coverage = qFromBigEndian<quint16>(header+4);
831
//            qDebug("subtable: version=%d, coverage=%x",version, coverage);
832
            if(version == 0 && coverage == 0x0001) {
833
                if (offset + length > tab.size()) {
834
//                    qDebug("length ouf ot bounds");
835
                    goto end;
836
                }
837
                const uchar *data = table + offset + 6;
838
839
                ushort nPairs = qFromBigEndian<quint16>(data);
840
                if(nPairs * 6 + 8 > length - 6) {
841
//                    qDebug("corrupt table!");
842
                    // corrupt table
843
                    goto end;
844
                }
845
846
                int off = 8;
847
                for(int i = 0; i < nPairs; ++i) {
848
                    QFontEngine::KernPair p;
849
                    p.left_right = (((uint)qFromBigEndian<quint16>(data+off)) << 16) + qFromBigEndian<quint16>(data+off+2);
850
                    p.adjust = QFixed(((int)(short)qFromBigEndian<quint16>(data+off+4))) / scalingFactor;
851
                    kerning_pairs.append(p);
852
                    off += 6;
853
                }
854
            }
855
            offset += length;
856
        }
857
    }
858
end:
859
    qSort(kerning_pairs);
860
//    for (int i = 0; i < kerning_pairs.count(); ++i)
861
//        qDebug() << 'i' << i << "left_right" << hex << kerning_pairs.at(i).left_right;
862
}
863
864
#else
865
void QFontEngine::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const
866
{
867
}
868
#endif
869
870
int QFontEngine::glyphCount() const
871
{
872
    QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
873
    if (maxpTable.size() < 6)
874
        return 0;
875
    return qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(maxpTable.constData() + 4));
876
}
877
878
const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
879
{
880
    const uchar *header = table;
881
    if (tableSize < 4)
882
        return 0;
883
884
    const uchar *endPtr = table + tableSize;
885
886
    // version check
887
    if (qFromBigEndian<quint16>(header) != 0)
888
        return 0;
889
890
    unsigned short numTables = qFromBigEndian<quint16>(header + 2);
891
    const uchar *maps = table + 4;
892
    if (maps + 8 * numTables > endPtr)
893
        return 0;
894
895
    enum {
896
        Invalid,
897
        AppleRoman,
898
        Symbol,
899
        Unicode11,
900
        Unicode,
901
        MicrosoftUnicode,
902
        MicrosoftUnicodeExtended
903
    };
904
905
    int symbolTable = -1;
906
    int tableToUse = -1;
907
    int score = Invalid;
908
    for (int n = 0; n < numTables; ++n) {
909
        const quint16 platformId = qFromBigEndian<quint16>(maps + 8 * n);
910
        const quint16 platformSpecificId = qFromBigEndian<quint16>(maps + 8 * n + 2);
911
        switch (platformId) {
912
        case 0: // Unicode
913
            if (score < Unicode &&
914
                (platformSpecificId == 0 ||
915
                 platformSpecificId == 2 ||
916
                 platformSpecificId == 3)) {
917
                tableToUse = n;
918
                score = Unicode;
919
            } else if (score < Unicode11 && platformSpecificId == 1) {
920
                tableToUse = n;
921
                score = Unicode11;
922
            }
923
            break;
924
        case 1: // Apple
925
            if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
926
                tableToUse = n;
927
                score = AppleRoman;
928
            }
929
            break;
930
        case 3: // Microsoft
931
            switch (platformSpecificId) {
932
            case 0:
933
                symbolTable = n;
934
                if (score < Symbol) {
935
                    tableToUse = n;
936
                    score = Symbol;
937
                }
938
                break;
939
            case 1:
940
                if (score < MicrosoftUnicode) {
941
                    tableToUse = n;
942
                    score = MicrosoftUnicode;
943
                }
944
                break;
945
            case 0xa:
946
                if (score < MicrosoftUnicodeExtended) {
947
                    tableToUse = n;
948
                    score = MicrosoftUnicodeExtended;
949
                }
950
                break;
951
            default:
952
                break;
953
            }
954
        default:
955
            break;
956
        }
957
    }
958
    if(tableToUse < 0)
959
        return 0;
960
961
resolveTable:
962
    *isSymbolFont = (symbolTable > -1);
963
964
    unsigned int unicode_table = qFromBigEndian<quint32>(maps + 8*tableToUse + 4);
965
966
    if (!unicode_table || unicode_table + 8 > tableSize)
967
        return 0;
968
969
    // get the header of the unicode table
970
    header = table + unicode_table;
971
972
    unsigned short format = qFromBigEndian<quint16>(header);
973
    unsigned int length;
974
    if(format < 8)
975
        length = qFromBigEndian<quint16>(header + 2);
976
    else
977
        length = qFromBigEndian<quint32>(header + 4);
978
979
    if (table + unicode_table + length > endPtr)
980
        return 0;
981
    *cmapSize = length;
982
983
    // To support symbol fonts that contain a unicode table for the symbol area
984
    // we check the cmap tables and fall back to symbol font unless that would
985
    // involve losing information from the unicode table
986
    if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
987
        const uchar *selectedTable = table + unicode_table;
988
989
        // Check that none of the latin1 range are in the unicode table
990
        bool unicodeTableHasLatin1 = false;
991
        for (int uc=0x00; uc<0x100; ++uc) {
992
            if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) {
993
                unicodeTableHasLatin1 = true;
994
                break;
995
            }
996
        }
997
998
        // Check that at least one symbol char is in the unicode table
999
        bool unicodeTableHasSymbols = false;
1000
        if (!unicodeTableHasLatin1) {
1001
            for (int uc=0xf000; uc<0xf100; ++uc) {
1002
                if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) {
1003
                    unicodeTableHasSymbols = true;
1004
                    break;
1005
                }
1006
            }
1007
        }
1008
1009
        // Fall back to symbol table
1010
        if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1011
            tableToUse = symbolTable;
1012
            score = Symbol;
1013
            goto resolveTable;
1014
        }
1015
    }
1016
1017
    return table + unicode_table;
1018
}
1019
1020
quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode)
1021
{
1022
    unsigned short format = qFromBigEndian<quint16>(cmap);
1023
    if (format == 0) {
1024
        if (unicode < 256)
1025
            return (int) *(cmap+6+unicode);
1026
    } else if (format == 4) {
1027
        /* some fonts come with invalid cmap tables, where the last segment
1028
           specified end = start = rangeoffset = 0xffff, delta = 0x0001
1029
           Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1030
           by returning 0 for 0xffff
1031
        */
1032
        if(unicode >= 0xffff)
1033
            return 0;
1034
        quint16 segCountX2 = qFromBigEndian<quint16>(cmap + 6);
1035
        const unsigned char *ends = cmap + 14;
1036
        int i = 0;
1037
        for (; i < segCountX2/2 && qFromBigEndian<quint16>(ends + 2*i) < unicode; i++) {}
1038
1039
        const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1040
        quint16 startIndex = qFromBigEndian<quint16>(idx);
1041
1042
        if (startIndex > unicode)
1043
            return 0;
1044
1045
        idx += segCountX2;
1046
        qint16 idDelta = (qint16)qFromBigEndian<quint16>(idx);
1047
        idx += segCountX2;
1048
        quint16 idRangeoffset_t = (quint16)qFromBigEndian<quint16>(idx);
1049
1050
        quint16 glyphIndex;
1051
        if (idRangeoffset_t) {
1052
            quint16 id = qFromBigEndian<quint16>(idRangeoffset_t + 2*(unicode - startIndex) + idx);
1053
            if (id)
1054
                glyphIndex = (idDelta + id) % 0x10000;
1055
            else
1056
                glyphIndex = 0;
1057
        } else {
1058
            glyphIndex = (idDelta + unicode) % 0x10000;
1059
        }
1060
        return glyphIndex;
1061
    } else if (format == 6) {
1062
        quint16 tableSize = qFromBigEndian<quint16>(cmap + 2);
1063
1064
        quint16 firstCode6 = qFromBigEndian<quint16>(cmap + 6);
1065
        if (unicode < firstCode6)
1066
            return 0;
1067
1068
        quint16 entryCount6 = qFromBigEndian<quint16>(cmap + 8);
1069
        if (entryCount6 * 2 + 10 > tableSize)
1070
            return 0;
1071
1072
        quint16 sentinel6 = firstCode6 + entryCount6;
1073
        if (unicode >= sentinel6)
1074
            return 0;
1075
1076
        quint16 entryIndex6 = unicode - firstCode6;
1077
        return qFromBigEndian<quint16>(cmap + 10 + (entryIndex6 * 2));
1078
    } else if (format == 12) {
1079
        quint32 nGroups = qFromBigEndian<quint32>(cmap + 12);
1080
1081
        cmap += 16; // move to start of groups
1082
1083
        int left = 0, right = nGroups - 1;
1084
        while (left <= right) {
1085
            int middle = left + ( ( right - left ) >> 1 );
1086
1087
            quint32 startCharCode = qFromBigEndian<quint32>(cmap + 12*middle);
1088
            if(unicode < startCharCode)
1089
                right = middle - 1;
1090
            else {
1091
                quint32 endCharCode = qFromBigEndian<quint32>(cmap + 12*middle + 4);
1092
                if(unicode <= endCharCode)
1093
                    return qFromBigEndian<quint32>(cmap + 12*middle + 8) + unicode - startCharCode;
1094
                left = middle + 1;
1095
            }
1096
        }
1097
    } else {
1098
        qDebug("cmap table of format %d not implemented", format);
1099
    }
1100
1101
    return 0;
1102
}
1103
1104
QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1105
{
1106
    QByteArray f = family;
1107
    f.replace(' ', "");
1108
    f.replace('(', "");
1109
    f.replace(')', "");
1110
    f.replace('<', "");
1111
    f.replace('>', "");
1112
    f.replace('[', "");
1113
    f.replace(']', "");
1114
    f.replace('{', "");
1115
    f.replace('}', "");
1116
    f.replace('/', "");
1117
    f.replace('%', "");
1118
    return f;
1119
}
1120
1121
Q_GLOBAL_STATIC_WITH_INITIALIZER(QVector<QRgb>, qt_grayPalette, {
1122
    x->resize(256);
1123
    QRgb *it = x->data();
1124
    for (int i = 0; i < x->size(); ++i, ++it)
1125
        *it = 0xff000000 | i | (i<<8) | (i<<16);
1126
})
1127
1128
const QVector<QRgb> &QFontEngine::grayPalette()
1129
{
1130
    return *qt_grayPalette();
1131
}
1132
1133
QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs, bool round)
1134
{
1135
    if (glyphs.numGlyphs >= 1) {
1136
        glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1137
        glyph_metrics_t gi = boundingBox(glyph);
1138
        if (gi.isValid())
1139
            return round ? QFixed(qRound(gi.xoff - gi.x - gi.width))
1140
                         : QFixed(gi.xoff - gi.x - gi.width);
1141
    }
1142
    return 0;
1143
}
1144
1145
// ------------------------------------------------------------------
1146
// The box font engine
1147
// ------------------------------------------------------------------
1148
1149
QFontEngineBox::QFontEngineBox(int size)
1150
    : _size(size)
1151
{
1152
    cache_cost = sizeof(QFontEngineBox);
1153
}
1154
1155
QFontEngineBox::~QFontEngineBox()
1156
{
1157
}
1158
1159
bool QFontEngineBox::stringToCMap(const QChar *, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags) const
1160
{
1161
    if (*nglyphs < len) {
1162
        *nglyphs = len;
1163
        return false;
1164
    }
1165
1166
    for (int i = 0; i < len; i++) {
1167
        glyphs->glyphs[i] = 0;
1168
        glyphs->advances_x[i] = _size;
1169
        glyphs->advances_y[i] = 0;
1170
    }
1171
1172
    *nglyphs = len;
1173
    glyphs->numGlyphs = len;
1174
    return true;
1175
}
1176
1177
void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
1178
{
1179
    for (int i = 0; i < glyphs->numGlyphs; i++) {
1180
        glyphs->advances_x[i] = _size;
1181
        glyphs->advances_y[i] = 0;
1182
    }
1183
}
1184
1185
void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1186
{
1187
    if (!glyphs.numGlyphs)
1188
        return;
1189
1190
    QVarLengthArray<QFixedPoint> positions;
1191
    QVarLengthArray<glyph_t> positioned_glyphs;
1192
    QTransform matrix = QTransform::fromTranslate(x, y - _size);
1193
    getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1194
1195
    QSize s(_size - 3, _size - 3);
1196
    for (int k = 0; k < positions.size(); k++)
1197
        path->addRect(QRectF(positions[k].toPointF(), s));
1198
}
1199
1200
glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1201
{
1202
    glyph_metrics_t overall;
1203
    overall.width = _size*glyphs.numGlyphs;
1204
    overall.height = _size;
1205
    overall.xoff = overall.width;
1206
    return overall;
1207
}
1208
1209
#if defined(Q_WS_QWS) || defined(Q_WS_QPA)
1210
void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1211
{
1212
    if (!ti.glyphs.numGlyphs)
1213
        return;
1214
1215
    // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1216
    QSize s(_size - 3, _size - 3);
1217
1218
    QVarLengthArray<QFixedPoint> positions;
1219
    QVarLengthArray<glyph_t> glyphs;
1220
    QTransform matrix = QTransform::fromTranslate(x, y - _size);
1221
    ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
1222
    if (glyphs.size() == 0)
1223
        return;
1224
1225
1226
    QPainter *painter = p->painter();
1227
    painter->save();
1228
    painter->setBrush(Qt::NoBrush);
1229
    QPen pen = painter->pen();
1230
    pen.setWidthF(lineThickness().toReal());
1231
    painter->setPen(pen);
1232
    for (int k = 0; k < positions.size(); k++)
1233
        painter->drawRect(QRectF(positions[k].toPointF(), s));
1234
    painter->restore();
1235
}
1236
#endif
1237
1238
glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1239
{
1240
    return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1241
}
1242
1243
1244
1245
QFixed QFontEngineBox::ascent() const
1246
{
1247
    return _size;
1248
}
1249
1250
QFixed QFontEngineBox::descent() const
1251
{
1252
    return 0;
1253
}
1254
1255
QFixed QFontEngineBox::leading() const
1256
{
1257
    QFixed l = _size * QFixed::fromReal(qreal(0.15));
1258
    return l.ceil();
1259
}
1260
1261
qreal QFontEngineBox::maxCharWidth() const
1262
{
1263
    return _size;
1264
}
1265
1266
#ifdef Q_WS_X11
1267
int QFontEngineBox::cmap() const
1268
{
1269
    return -1;
1270
}
1271
#endif
1272
1273
const char *QFontEngineBox::name() const
1274
{
1275
    return "null";
1276
}
1277
1278
bool QFontEngineBox::canRender(const QChar *, int)
1279
{
1280
    return true;
1281
}
1282
1283
QFontEngine::Type QFontEngineBox::type() const
1284
{
1285
    return Box;
1286
}
1287
1288
QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1289
{
1290
    QImage image(_size, _size, QImage::Format_Indexed8);
1291
    QVector<QRgb> colors(256);
1292
    for (int i=0; i<256; ++i)
1293
        colors[i] = qRgba(0, 0, 0, i);
1294
    image.setColorTable(colors);
1295
    image.fill(0);
1296
1297
    // can't use qpainter for index8; so use setPixel to draw our rectangle.
1298
    for (int i=2; i <= _size-3; ++i) {
1299
        image.setPixel(i, 2, 255);
1300
        image.setPixel(i, _size-3, 255);
1301
        image.setPixel(2, i, 255);
1302
        image.setPixel(_size-3, i, 255);
1303
    }
1304
    return image;
1305
}
1306
1307
// ------------------------------------------------------------------
1308
// Multi engine
1309
// ------------------------------------------------------------------
1310
1311
static inline uchar highByte(glyph_t glyph)
1312
{ return glyph >> 24; }
1313
1314
// strip high byte from glyph
1315
static inline glyph_t stripped(glyph_t glyph)
1316
{ return glyph & 0x00ffffff; }
1317
1318
QFontEngineMulti::QFontEngineMulti(int engineCount)
1319
{
1320
    engines.fill(0, engineCount);
1321
    cache_cost = 0;
1322
}
1323
1324
QFontEngineMulti::~QFontEngineMulti()
1325
{
1326
    for (int i = 0; i < engines.size(); ++i) {
1327
        QFontEngine *fontEngine = engines.at(i);
1328
        if (fontEngine) {
1329
            fontEngine->ref.deref();
1330
            if (fontEngine->cache_count == 0 && fontEngine->ref == 0)
1331
                delete fontEngine;
1332
        }
1333
    }
1334
}
1335
1336
bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
1337
                                    QGlyphLayout *glyphs, int *nglyphs,
1338
                                    QTextEngine::ShaperFlags flags) const
1339
{
1340
    int ng = *nglyphs;
1341
    if (!engine(0)->stringToCMap(str, len, glyphs, &ng, flags))
1342
        return false;
1343
1344
    int glyph_pos = 0;
1345
    for (int i = 0; i < len; ++i) {
1346
        bool surrogate = (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate());
1347
1348
        if (glyphs->glyphs[glyph_pos] == 0 && str[i].category() != QChar::Separator_Line) {
1349
            QGlyphLayoutInstance tmp = glyphs->instance(glyph_pos);
1350
            for (int x = 1; x < engines.size(); ++x) {
1351
                QFontEngine *engine = engines.at(x);
1352
                if (!engine) {
1353
                    const_cast<QFontEngineMulti *>(this)->loadEngine(x);
1354
                    engine = engines.at(x);
1355
                }
1356
                Q_ASSERT(engine != 0);
1357
                if (engine->type() == Box)
1358
                    continue;
1359
                glyphs->advances_x[glyph_pos] = glyphs->advances_y[glyph_pos] = 0;
1360
                glyphs->offsets[glyph_pos] = QFixedPoint();
1361
                int num = 2;
1362
                QGlyphLayout offs = glyphs->mid(glyph_pos, num);
1363
                engine->stringToCMap(str + i, surrogate ? 2 : 1, &offs, &num, flags);
1364
                Q_ASSERT(num == 1); // surrogates only give 1 glyph
1365
                if (glyphs->glyphs[glyph_pos]) {
1366
                    // set the high byte to indicate which engine the glyph came from
1367
                    glyphs->glyphs[glyph_pos] |= (x << 24);
1368
                    break;
1369
                }
1370
            }
1371
            // ensure we use metrics from the 1st font when we use the fallback image.
1372
            if (!glyphs->glyphs[glyph_pos]) {
1373
                glyphs->setInstance(glyph_pos, tmp);
1374
            }
1375
        }
1376
        if (surrogate)
1377
            ++i;
1378
        ++glyph_pos;
1379
    }
1380
1381
    *nglyphs = ng;
1382
    glyphs->numGlyphs = ng;
1383
    return true;
1384
}
1385
1386
glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
1387
{
1388
    if (glyphs.numGlyphs <= 0)
1389
        return glyph_metrics_t();
1390
1391
    glyph_metrics_t overall;
1392
1393
    int which = highByte(glyphs.glyphs[0]);
1394
    int start = 0;
1395
    int end, i;
1396
    for (end = 0; end < glyphs.numGlyphs; ++end) {
1397
        const int e = highByte(glyphs.glyphs[end]);
1398
        if (e == which)
1399
            continue;
1400
1401
        // set the high byte to zero
1402
        for (i = start; i < end; ++i)
1403
            glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
1404
1405
        // merge the bounding box for this run
1406
        const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
1407
1408
        overall.x = qMin(overall.x, gm.x);
1409
        overall.y = qMin(overall.y, gm.y);
1410
        overall.width = overall.xoff + gm.width;
1411
        overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
1412
                         qMin(overall.y, gm.y);
1413
        overall.xoff += gm.xoff;
1414
        overall.yoff += gm.yoff;
1415
1416
        // reset the high byte for all glyphs
1417
        const int hi = which << 24;
1418
        for (i = start; i < end; ++i)
1419
            glyphs.glyphs[i] = hi | glyphs.glyphs[i];
1420
1421
        // change engine
1422
        start = end;
1423
        which = e;
1424
    }
1425
1426
    // set the high byte to zero
1427
    for (i = start; i < end; ++i)
1428
        glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
1429
1430
    // merge the bounding box for this run
1431
    const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start));
1432
1433
    overall.x = qMin(overall.x, gm.x);
1434
    overall.y = qMin(overall.y, gm.y);
1435
    overall.width = overall.xoff + gm.width;
1436
    overall.height = qMax(overall.height + overall.y, gm.height + gm.y) -
1437
                     qMin(overall.y, gm.y);
1438
    overall.xoff += gm.xoff;
1439
    overall.yoff += gm.yoff;
1440
1441
    // reset the high byte for all glyphs
1442
    const int hi = which << 24;
1443
    for (i = start; i < end; ++i)
1444
        glyphs.glyphs[i] = hi | glyphs.glyphs[i];
1445
1446
    return overall;
1447
}
1448
1449
void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
1450
{
1451
    int which = highByte(glyph);
1452
    engine(which)->getGlyphBearings(stripped(glyph), leftBearing, rightBearing);
1453
}
1454
1455
void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
1456
                                        QPainterPath *path, QTextItem::RenderFlags flags)
1457
{
1458
    if (glyphs.numGlyphs <= 0)
1459
        return;
1460
1461
    int which = highByte(glyphs.glyphs[0]);
1462
    int start = 0;
1463
    int end, i;
1464
    if (flags & QTextItem::RightToLeft) {
1465
        for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1466
            x += glyphs.advances_x[gl].toReal();
1467
            y += glyphs.advances_y[gl].toReal();
1468
        }
1469
    }
1470
    for (end = 0; end < glyphs.numGlyphs; ++end) {
1471
        const int e = highByte(glyphs.glyphs[end]);
1472
        if (e == which)
1473
            continue;
1474
1475
        if (flags & QTextItem::RightToLeft) {
1476
            for (i = start; i < end; ++i) {
1477
                x -= glyphs.advances_x[i].toReal();
1478
                y -= glyphs.advances_y[i].toReal();
1479
            }
1480
        }
1481
1482
        // set the high byte to zero
1483
        for (i = start; i < end; ++i)
1484
            glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
1485
        engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
1486
        // reset the high byte for all glyphs and update x and y
1487
        const int hi = which << 24;
1488
        for (i = start; i < end; ++i)
1489
            glyphs.glyphs[i] = hi | glyphs.glyphs[i];
1490
1491
        if (!(flags & QTextItem::RightToLeft)) {
1492
            for (i = start; i < end; ++i) {
1493
                x += glyphs.advances_x[i].toReal();
1494
                y += glyphs.advances_y[i].toReal();
1495
            }
1496
        }
1497
1498
        // change engine
1499
        start = end;
1500
        which = e;
1501
    }
1502
1503
    if (flags & QTextItem::RightToLeft) {
1504
        for (i = start; i < end; ++i) {
1505
            x -= glyphs.advances_x[i].toReal();
1506
            y -= glyphs.advances_y[i].toReal();
1507
        }
1508
    }
1509
1510
    // set the high byte to zero
1511
    for (i = start; i < end; ++i)
1512
        glyphs.glyphs[i] = stripped(glyphs.glyphs[i]);
1513
1514
    engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags);
1515
1516
    // reset the high byte for all glyphs
1517
    const int hi = which << 24;
1518
    for (i = start; i < end; ++i)
1519
        glyphs.glyphs[i] = hi | glyphs.glyphs[i];
1520
}
1521
1522
void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1523
{
1524
    if (glyphs->numGlyphs <= 0)
1525
        return;
1526
1527
    int which = highByte(glyphs->glyphs[0]);
1528
    int start = 0;
1529
    int end, i;
1530
    for (end = 0; end < glyphs->numGlyphs; ++end) {
1531
        const int e = highByte(glyphs->glyphs[end]);
1532
        if (e == which)
1533
            continue;
1534
1535
        // set the high byte to zero
1536
        for (i = start; i < end; ++i)
1537
            glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
1538
1539
        QGlyphLayout offs = glyphs->mid(start, end - start);
1540
        engine(which)->recalcAdvances(&offs, flags);
1541
1542
        // reset the high byte for all glyphs and update x and y
1543
        const int hi = which << 24;
1544
        for (i = start; i < end; ++i)
1545
            glyphs->glyphs[i] = hi | glyphs->glyphs[i];
1546
1547
        // change engine
1548
        start = end;
1549
        which = e;
1550
    }
1551
1552
    // set the high byte to zero
1553
    for (i = start; i < end; ++i)
1554
        glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
1555
1556
    QGlyphLayout offs = glyphs->mid(start, end - start);
1557
    engine(which)->recalcAdvances(&offs, flags);
1558
1559
    // reset the high byte for all glyphs
1560
    const int hi = which << 24;
1561
    for (i = start; i < end; ++i)
1562
        glyphs->glyphs[i] = hi | glyphs->glyphs[i];
1563
}
1564
1565
void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
1566
{
1567
    if (glyphs->numGlyphs <= 0)
1568
        return;
1569
1570
    int which = highByte(glyphs->glyphs[0]);
1571
    int start = 0;
1572
    int end, i;
1573
    for (end = 0; end < glyphs->numGlyphs; ++end) {
1574
        const int e = highByte(glyphs->glyphs[end]);
1575
        if (e == which)
1576
            continue;
1577
1578
        // set the high byte to zero
1579
        for (i = start; i < end; ++i)
1580
            glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
1581
1582
        QGlyphLayout offs = glyphs->mid(start, end - start);
1583
        engine(which)->doKerning(&offs, flags);
1584
1585
        // reset the high byte for all glyphs and update x and y
1586
        const int hi = which << 24;
1587
        for (i = start; i < end; ++i)
1588
            glyphs->glyphs[i] = hi | glyphs->glyphs[i];
1589
1590
        // change engine
1591
        start = end;
1592
        which = e;
1593
    }
1594
1595
    // set the high byte to zero
1596
    for (i = start; i < end; ++i)
1597
        glyphs->glyphs[i] = stripped(glyphs->glyphs[i]);
1598
1599
    QGlyphLayout offs = glyphs->mid(start, end - start);
1600
    engine(which)->doKerning(&offs, flags);
1601
1602
    // reset the high byte for all glyphs
1603
    const int hi = which << 24;
1604
    for (i = start; i < end; ++i)
1605
        glyphs->glyphs[i] = hi | glyphs->glyphs[i];
1606
}
1607
1608
glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
1609
{
1610
    const int which = highByte(glyph);
1611
    Q_ASSERT(which < engines.size());
1612
    return engine(which)->boundingBox(stripped(glyph));
1613
}
1614
1615
QFixed QFontEngineMulti::ascent() const
1616
{ return engine(0)->ascent(); }
1617
1618
QFixed QFontEngineMulti::descent() const
1619
{ return engine(0)->descent(); }
1620
1621
QFixed QFontEngineMulti::leading() const
1622
{
1623
    return engine(0)->leading();
1624
}
1625
1626
QFixed QFontEngineMulti::xHeight() const
1627
{
1628
    return engine(0)->xHeight();
1629
}
1630
1631
QFixed QFontEngineMulti::averageCharWidth() const
1632
{
1633
    return engine(0)->averageCharWidth();
1634
}
1635
1636
QFixed QFontEngineMulti::lineThickness() const
1637
{
1638
    return engine(0)->lineThickness();
1639
}
1640
1641
QFixed QFontEngineMulti::underlinePosition() const
1642
{
1643
    return engine(0)->underlinePosition();
1644
}
1645
1646
qreal QFontEngineMulti::maxCharWidth() const
1647
{
1648
    return engine(0)->maxCharWidth();
1649
}
1650
1651
qreal QFontEngineMulti::minLeftBearing() const
1652
{
1653
    return engine(0)->minLeftBearing();
1654
}
1655
1656
qreal QFontEngineMulti::minRightBearing() const
1657
{
1658
    return engine(0)->minRightBearing();
1659
}
1660
1661
bool QFontEngineMulti::canRender(const QChar *string, int len)
1662
{
1663
    if (engine(0)->canRender(string, len))
1664
        return true;
1665
1666
    QVarLengthGlyphLayoutArray glyphs(len);
1667
    int nglyphs = len;
1668
    if (stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly) == false) {
1669
        glyphs.resize(nglyphs);
1670
        stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
1671
    }
1672
1673
    bool allExist = true;
1674
    for (int i = 0; i < nglyphs; i++) {
1675
        if (!glyphs.glyphs[i]) {
1676
            allExist = false;
1677
            break;
1678
        }
1679
    }
1680
1681
    return allExist;
1682
}
1683
1684
QImage QFontEngineMulti::alphaMapForGlyph(glyph_t)
1685
{
1686
    Q_ASSERT(false);
1687
    return QImage();
1688
}
1689
1690
1691
QT_END_NAMESPACE