1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qfontengine_qpa_p.h"
44 #include <QtCore/QFile>
45 #include <QtCore/QFileInfo>
46 #include <QtCore/QDir>
47 #include <QtCore/QBuffer>
49 #include <QtGui/private/qpaintengine_raster_p.h>
50 #include <QtGui/private/qguiapplication_p.h>
51 #include <qpa/qplatformfontdatabase.h>
52 #include <qpa/qplatformintegration.h>
56 //#define DEBUG_HEADER
57 //#define DEBUG_FONTENGINE
59 static QFontEngineQPA::TagType tagTypes[QFontEngineQPA::NumTags] = {
60 QFontEngineQPA::StringType, // FontName
61 QFontEngineQPA::StringType, // FileName
62 QFontEngineQPA::UInt32Type, // FileIndex
63 QFontEngineQPA::UInt32Type, // FontRevision
64 QFontEngineQPA::StringType, // FreeText
65 QFontEngineQPA::FixedType, // Ascent
66 QFontEngineQPA::FixedType, // Descent
67 QFontEngineQPA::FixedType, // Leading
68 QFontEngineQPA::FixedType, // XHeight
69 QFontEngineQPA::FixedType, // AverageCharWidth
70 QFontEngineQPA::FixedType, // MaxCharWidth
71 QFontEngineQPA::FixedType, // LineThickness
72 QFontEngineQPA::FixedType, // MinLeftBearing
73 QFontEngineQPA::FixedType, // MinRightBearing
74 QFontEngineQPA::FixedType, // UnderlinePosition
75 QFontEngineQPA::UInt8Type, // GlyphFormat
76 QFontEngineQPA::UInt8Type, // PixelSize
77 QFontEngineQPA::UInt8Type, // Weight
78 QFontEngineQPA::UInt8Type, // Style
79 QFontEngineQPA::StringType, // EndOfHeader
80 QFontEngineQPA::BitFieldType// WritingSystems
84 #if defined(DEBUG_HEADER)
85 # define DEBUG_VERIFY qDebug
87 # define DEBUG_VERIFY if (0) qDebug
90 #define READ_VERIFY(type, variable) \
91 if (tagPtr + sizeof(type) > endPtr) { \
92 DEBUG_VERIFY() << "read verify failed in line" << __LINE__; \
95 variable = qFromBigEndian<type>(tagPtr); \
96 DEBUG_VERIFY() << "read value" << variable << "of type " #type; \
97 tagPtr += sizeof(type)
100 T readValue(const uchar *&data)
102 T value = qFromBigEndian<T>(data);
107 #define VERIFY(condition) \
108 if (!(condition)) { \
109 DEBUG_VERIFY() << "condition " #condition " failed in line" << __LINE__; \
113 #define VERIFY_TAG(condition) \
114 if (!(condition)) { \
115 DEBUG_VERIFY() << "verifying tag condition " #condition " failed in line" << __LINE__ << "with tag" << tag; \
119 static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr)
122 READ_VERIFY(quint16, tag);
123 READ_VERIFY(quint16, length);
124 if (tag == QFontEngineQPA::Tag_EndOfHeader)
126 if (tag < QFontEngineQPA::NumTags) {
127 switch (tagTypes[tag]) {
128 case QFontEngineQPA::BitFieldType:
129 case QFontEngineQPA::StringType:
130 // can't do anything...
132 case QFontEngineQPA::UInt32Type:
133 VERIFY_TAG(length == sizeof(quint32));
135 case QFontEngineQPA::FixedType:
136 VERIFY_TAG(length == sizeof(quint32));
138 case QFontEngineQPA::UInt8Type:
139 VERIFY_TAG(length == sizeof(quint8));
142 #if defined(DEBUG_HEADER)
144 qDebug() << "tag data" << hex << *tagPtr;
145 else if (length == 4)
146 qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
149 return tagPtr + length;
152 const QFontEngineQPA::Glyph *QFontEngineQPA::findGlyph(glyph_t g) const
154 if (!g || g >= glyphMapEntries)
156 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
157 quint32 glyphPos = qFromBigEndian<quint32>(gmapPtr[g]);
158 if (glyphPos > glyphDataSize) {
159 if (glyphPos == 0xffffffff)
161 #if defined(DEBUG_FONTENGINE)
162 qDebug() << "glyph" << g << "outside of glyphData, remapping font file";
164 if (glyphPos > glyphDataSize)
167 return reinterpret_cast<const Glyph *>(fontData + glyphDataOffset + glyphPos);
170 bool QFontEngineQPA::verifyHeader(const uchar *data, int size)
172 VERIFY(size >= int(sizeof(Header)));
173 const Header *header = reinterpret_cast<const Header *>(data);
174 if (header->magic[0] != 'Q'
175 || header->magic[1] != 'P'
176 || header->magic[2] != 'F'
177 || header->magic[3] != '2')
180 VERIFY(header->majorVersion <= CurrentMajorVersion);
181 const quint16 dataSize = qFromBigEndian<quint16>(header->dataSize);
182 VERIFY(size >= int(sizeof(Header)) + dataSize);
184 const uchar *tagPtr = data + sizeof(Header);
185 const uchar *tagEndPtr = tagPtr + dataSize;
186 while (tagPtr < tagEndPtr - 3) {
187 tagPtr = verifyTag(tagPtr, tagEndPtr);
191 VERIFY(tagPtr <= tagEndPtr);
195 QVariant QFontEngineQPA::extractHeaderField(const uchar *data, HeaderTag requestedTag)
197 const Header *header = reinterpret_cast<const Header *>(data);
198 const uchar *tagPtr = data + sizeof(Header);
199 const uchar *endPtr = tagPtr + qFromBigEndian<quint16>(header->dataSize);
200 while (tagPtr < endPtr - 3) {
201 quint16 tag = readValue<quint16>(tagPtr);
202 quint16 length = readValue<quint16>(tagPtr);
203 if (tag == requestedTag) {
204 switch (tagTypes[requestedTag]) {
206 return QVariant(QString::fromUtf8(reinterpret_cast<const char *>(tagPtr), length));
208 return QVariant(readValue<quint32>(tagPtr));
210 return QVariant(uint(*tagPtr));
212 return QVariant(QFixed::fromFixed(readValue<quint32>(tagPtr)).toReal());
214 return QVariant(QByteArray(reinterpret_cast<const char *>(tagPtr), length));
217 } else if (tag == Tag_EndOfHeader) {
228 static inline unsigned int getChar(const QChar *str, int &i, const int len)
230 uint ucs4 = str[i].unicode();
231 if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
233 ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
238 QFontEngineQPA::QFontEngineQPA(const QFontDef &def, const QByteArray &data)
239 : fontData(reinterpret_cast<const uchar *>(data.constData())), dataSize(data.size())
250 kerning_pairs_loaded = false;
253 #if defined(DEBUG_FONTENGINE)
254 qDebug() << "QFontEngineQPA::QFontEngineQPA( fd =" << fd << ", renderingFontEngine =" << renderingFontEngine << ')';
257 if (!verifyHeader(fontData, dataSize)) {
258 #if defined(DEBUG_FONTENGINE)
259 qDebug() << "verifyHeader failed!";
264 const Header *header = reinterpret_cast<const Header *>(fontData);
266 readOnly = (header->lock == 0xffffffff);
268 const uchar *imgData = fontData + sizeof(Header) + qFromBigEndian<quint16>(header->dataSize);
269 const uchar *endPtr = fontData + dataSize;
270 while (imgData <= endPtr - 8) {
271 quint16 blockTag = readValue<quint16>(imgData);
272 imgData += 2; // skip padding
273 quint32 blockSize = readValue<quint32>(imgData);
275 if (blockTag == CMapBlock) {
276 cmapOffset = imgData - fontData;
277 cmapSize = blockSize;
278 } else if (blockTag == GMapBlock) {
279 glyphMapOffset = imgData - fontData;
280 glyphMapEntries = blockSize / 4;
281 } else if (blockTag == GlyphBlock) {
282 glyphDataOffset = imgData - fontData;
283 glyphDataSize = blockSize;
286 imgData += blockSize;
289 face_id.filename = QFile::encodeName(extractHeaderField(fontData, Tag_FileName).toString());
290 face_id.index = extractHeaderField(fontData, Tag_FileIndex).toInt();
294 int tableSize = cmapSize;
295 const uchar *cmapPtr = getCMap(fontData + cmapOffset, tableSize, &symbol, &cmapSize);
297 cmapOffset = cmapPtr - fontData;
300 } else if (externalCMap) {
301 int tableSize = cmapSize;
302 externalCMap = getCMap(externalCMap, tableSize, &symbol, &cmapSize);
305 // verify all the positions in the glyphMap
306 if (glyphMapOffset) {
307 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
308 for (uint i = 0; i < glyphMapEntries; ++i) {
309 quint32 glyphDataPos = qFromBigEndian<quint32>(gmapPtr[i]);
310 if (glyphDataPos == 0xffffffff)
312 if (glyphDataPos >= glyphDataSize) {
321 #if defined(DEBUG_FONTENGINE)
323 qDebug() << "fontData" << fontData << "dataSize" << dataSize
324 << "externalCMap" << externalCMap << "cmapOffset" << cmapOffset
325 << "glyphMapOffset" << glyphMapOffset << "glyphDataOffset" << glyphDataOffset
326 << "fd" << fd << "glyphDataSize" << glyphDataSize;
330 QFontEngineQPA::~QFontEngineQPA()
334 bool QFontEngineQPA::getSfntTableData(uint tag, uchar *buffer, uint *length) const
342 bool QFontEngineQPA::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
344 if (*nglyphs < len) {
349 #if defined(DEBUG_FONTENGINE)
350 QSet<QChar> seenGlyphs;
353 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
355 bool mirrored = flags & QTextEngine::RightToLeft;
358 for (int i = 0; i < len; ++i) {
359 unsigned int uc = getChar(str, i, len);
361 uc = QChar::mirroredChar(uc);
362 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
363 if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
364 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
368 for (int i = 0; i < len; ++i) {
369 unsigned int uc = getChar(str, i, len);
371 uc = QChar::mirroredChar(uc);
372 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
373 #if 0 && defined(DEBUG_FONTENGINE)
375 if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c))
376 qDebug() << "glyph for character" << c << '/' << hex << uc << "is" << dec << glyphs[glyph_pos].glyph;
378 seenGlyphs.insert(c);
384 *nglyphs = glyph_pos;
385 glyphs->numGlyphs = glyph_pos;
386 recalcAdvances(glyphs, flags);
390 void QFontEngineQPA::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
392 for (int i = 0; i < glyphs->numGlyphs; ++i) {
393 const Glyph *g = findGlyph(glyphs->glyphs[i]);
395 glyphs->glyphs[i] = 0;
398 glyphs->advances_x[i] = g->advance;
399 glyphs->advances_y[i] = 0;
403 QImage QFontEngineQPA::alphaMapForGlyph(glyph_t g)
405 const Glyph *glyph = findGlyph(g);
409 const uchar *bits = ((const uchar *) glyph) + sizeof(Glyph);
411 QImage image(bits,glyph->width, glyph->height, glyph->bytesPerLine, QImage::Format_Indexed8);
416 void QFontEngineQPA::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
418 addBitmapFontToPath(x, y, glyphs, path, flags);
421 glyph_metrics_t QFontEngineQPA::boundingBox(const QGlyphLayout &glyphs)
423 glyph_metrics_t overall;
424 // initialize with line height, we get the same behaviour on all platforms
425 overall.y = -ascent();
426 overall.height = ascent() + descent() + 1;
430 for (int i = 0; i < glyphs.numGlyphs; i++) {
431 const Glyph *g = findGlyph(glyphs.glyphs[i]);
435 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
436 QFixed y = overall.yoff + glyphs.offsets[i].y + g->y;
437 overall.x = qMin(overall.x, x);
438 overall.y = qMin(overall.y, y);
439 xmax = qMax(xmax, x + g->width);
440 ymax = qMax(ymax, y + g->height);
441 overall.xoff += g->advance;
443 overall.height = qMax(overall.height, ymax - overall.y);
444 overall.width = xmax - overall.x;
449 glyph_metrics_t QFontEngineQPA::boundingBox(glyph_t glyph)
451 glyph_metrics_t overall;
452 const Glyph *g = findGlyph(glyph);
457 overall.width = g->width;
458 overall.height = g->height;
459 overall.xoff = g->advance;
463 QFixed QFontEngineQPA::ascent() const
465 return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>());
468 QFixed QFontEngineQPA::descent() const
470 return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>());
473 QFixed QFontEngineQPA::leading() const
475 return QFixed::fromReal(extractHeaderField(fontData, Tag_Leading).value<qreal>());
478 qreal QFontEngineQPA::maxCharWidth() const
480 return extractHeaderField(fontData, Tag_MaxCharWidth).value<qreal>();
483 qreal QFontEngineQPA::minLeftBearing() const
485 return extractHeaderField(fontData, Tag_MinLeftBearing).value<qreal>();
488 qreal QFontEngineQPA::minRightBearing() const
490 return extractHeaderField(fontData, Tag_MinRightBearing).value<qreal>();
493 QFixed QFontEngineQPA::underlinePosition() const
495 return QFixed::fromReal(extractHeaderField(fontData, Tag_UnderlinePosition).value<qreal>());
498 QFixed QFontEngineQPA::lineThickness() const
500 return QFixed::fromReal(extractHeaderField(fontData, Tag_LineThickness).value<qreal>());
503 QFontEngine::Type QFontEngineQPA::type() const
505 return QFontEngine::QPF2;
508 bool QFontEngineQPA::canRender(const QChar *string, int len)
510 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
513 for (int i = 0; i < len; ++i) {
514 unsigned int uc = getChar(string, i, len);
515 glyph_t g = getTrueTypeGlyphIndex(cmap, uc);
517 g = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
522 for (int i = 0; i < len; ++i) {
523 unsigned int uc = getChar(string, i, len);
524 if (!getTrueTypeGlyphIndex(cmap, uc))
531 bool QFontEngineQPA::isValid() const
533 return fontData && dataSize && (cmapOffset || externalCMap)
534 && glyphMapOffset && glyphDataOffset && glyphDataSize > 0;
537 void QPAGenerator::generate()
541 writeBlock(QFontEngineQPA::GlyphBlock, QByteArray());
543 dev->seek(4); // position of header.lock
547 void QPAGenerator::writeHeader()
549 QFontEngineQPA::Header header;
551 header.magic[0] = 'Q';
552 header.magic[1] = 'P';
553 header.magic[2] = 'F';
554 header.magic[3] = '2';
556 header.majorVersion = QFontEngineQPA::CurrentMajorVersion;
557 header.minorVersion = QFontEngineQPA::CurrentMinorVersion;
559 dev->write((const char *)&header, sizeof(header));
561 writeTaggedString(QFontEngineQPA::Tag_FontName, fe->fontDef.family.toUtf8());
563 QFontEngine::FaceId face = fe->faceId();
564 writeTaggedString(QFontEngineQPA::Tag_FileName, face.filename);
565 writeTaggedUInt32(QFontEngineQPA::Tag_FileIndex, face.index);
570 bool ok = fe->getSfntTableData(MAKE_TAG('h', 'e', 'a', 'd'), data, &len);
572 const quint32 revision = qFromBigEndian<quint32>(data);
573 writeTaggedUInt32(QFontEngineQPA::Tag_FontRevision, revision);
577 writeTaggedQFixed(QFontEngineQPA::Tag_Ascent, fe->ascent());
578 writeTaggedQFixed(QFontEngineQPA::Tag_Descent, fe->descent());
579 writeTaggedQFixed(QFontEngineQPA::Tag_Leading, fe->leading());
580 writeTaggedQFixed(QFontEngineQPA::Tag_XHeight, fe->xHeight());
581 writeTaggedQFixed(QFontEngineQPA::Tag_AverageCharWidth, fe->averageCharWidth());
582 writeTaggedQFixed(QFontEngineQPA::Tag_MaxCharWidth, QFixed::fromReal(fe->maxCharWidth()));
583 writeTaggedQFixed(QFontEngineQPA::Tag_LineThickness, fe->lineThickness());
584 writeTaggedQFixed(QFontEngineQPA::Tag_MinLeftBearing, QFixed::fromReal(fe->minLeftBearing()));
585 writeTaggedQFixed(QFontEngineQPA::Tag_MinRightBearing, QFixed::fromReal(fe->minRightBearing()));
586 writeTaggedQFixed(QFontEngineQPA::Tag_UnderlinePosition, fe->underlinePosition());
587 writeTaggedUInt8(QFontEngineQPA::Tag_PixelSize, fe->fontDef.pixelSize);
588 writeTaggedUInt8(QFontEngineQPA::Tag_Weight, fe->fontDef.weight);
589 writeTaggedUInt8(QFontEngineQPA::Tag_Style, fe->fontDef.style);
591 writeTaggedUInt8(QFontEngineQPA::Tag_GlyphFormat, QFontEngineQPA::AlphamapGlyphs);
593 writeTaggedString(QFontEngineQPA::Tag_EndOfHeader, QByteArray());
596 const quint64 size = dev->pos();
597 header.dataSize = qToBigEndian<quint16>(size - sizeof(header));
599 dev->write((const char *)&header, sizeof(header));
603 void QPAGenerator::writeGMap()
605 const quint16 glyphCount = fe->glyphCount();
607 writeUInt16(QFontEngineQPA::GMapBlock);
608 writeUInt16(0); // padding
609 writeUInt32(glyphCount * 4);
611 QByteArray &buffer = dev->buffer();
612 const int numBytes = glyphCount * sizeof(quint32);
613 qint64 pos = buffer.size();
614 buffer.resize(pos + numBytes);
615 memset(buffer.data() + pos, 0xff, numBytes);
616 dev->seek(pos + numBytes);
619 void QPAGenerator::writeBlock(QFontEngineQPA::BlockTag tag, const QByteArray &data)
622 writeUInt16(0); // padding
623 const int padSize = ((data.size() + 3) / 4) * 4 - data.size();
624 writeUInt32(data.size() + padSize);
626 for (int i = 0; i < padSize; ++i)
630 void QPAGenerator::writeTaggedString(QFontEngineQPA::HeaderTag tag, const QByteArray &string)
633 writeUInt16(string.length());
637 void QPAGenerator::writeTaggedUInt32(QFontEngineQPA::HeaderTag tag, quint32 value)
640 writeUInt16(sizeof(value));
644 void QPAGenerator::writeTaggedUInt8(QFontEngineQPA::HeaderTag tag, quint8 value)
647 writeUInt16(sizeof(value));
651 void QPAGenerator::writeTaggedQFixed(QFontEngineQPA::HeaderTag tag, QFixed value)
654 writeUInt16(sizeof(quint32));
655 writeUInt32(value.value());
660 Creates a new multi QPA engine.
662 This function takes ownership of the QFontEngine, increasing it's refcount.
664 QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QStringList &fallbacks)
665 : QFontEngineMulti(fallbacks.size() + 1),
666 fallbackFamilies(fallbacks), script(_script)
667 , fallbacksQueried(true)
672 QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script)
673 : QFontEngineMulti(2)
675 , fallbacksQueried(false)
677 fallbackFamilies << QString();
681 void QFontEngineMultiQPA::init(QFontEngine *fe)
683 Q_ASSERT(fe && fe->type() != QFontEngine::Multi);
686 fontDef = engines[0]->fontDef;
687 setObjectName(QStringLiteral("QFontEngineMultiQPA"));
690 void QFontEngineMultiQPA::loadEngine(int at)
692 ensureFallbackFamiliesQueried();
693 Q_ASSERT(at < engines.size());
694 Q_ASSERT(engines.at(at) == 0);
695 QFontDef request = fontDef;
696 request.styleStrategy |= QFont::NoFontMerging;
697 request.family = fallbackFamilies.at(at-1);
698 engines[at] = QFontDatabase::findFont(script,
700 request, /*multi = */false);
701 Q_ASSERT(engines[at]);
702 engines[at]->ref.ref();
703 engines[at]->fontDef = request;
705 void QFontEngineMultiQPA::ensureFallbackFamiliesQueried()
707 if (fallbacksQueried)
709 QStringList fallbacks = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fallbacksForFamily(engine(0)->fontDef.family, QFont::Style(engine(0)->fontDef.style)
710 , QFont::AnyStyle, QUnicodeTables::Script(script));
711 setFallbackFamiliesList(fallbacks);
714 void QFontEngineMultiQPA::setFallbackFamiliesList(const QStringList &fallbacks)
716 // Original FontEngine to restore after the fill.
717 QFontEngine *fe = engines[0];
718 fallbackFamilies = fallbacks;
719 if (!fallbackFamilies.isEmpty()) {
720 engines.fill(0, fallbackFamilies.size() + 1);
723 // Turns out we lied about having any fallback at all.
724 fallbackFamilies << fe->fontDef.family;
727 fallbacksQueried = true;
731 This is used indirectly by QtWebKit when using QTextLayout::setRawFont
733 The purpose of this is to provide the necessary font fallbacks when drawing complex
734 text. Since QtWebKit ends up repeatedly creating QTextLayout instances and passing them
735 the same raw font over and over again, we want to cache the corresponding multi font engine
736 as it may contain fallback font engines already.
738 QFontEngine* QFontEngineMultiQPA::createMultiFontEngine(QFontEngine *fe, int script)
740 QFontEngine *engine = 0;
741 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
742 QFontCache *fc = QFontCache::instance();
743 // We can't rely on the fontDef (and hence the cache Key)
744 // alone to distinguish webfonts, since these should not be
745 // accidentally shared, even if the resulting fontcache key
746 // is strictly identical. See:
747 // http://www.w3.org/TR/css3-fonts/#font-face-rule
748 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
749 QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
750 end = fc->engineCache.end();
751 while (it != end && it.key() == key) {
752 QFontEngineMulti *cachedEngine = qobject_cast<QFontEngineMulti *>(it.value().data);
753 if (faceIsLocal || (cachedEngine && fe == cachedEngine->engine(0))) {
754 engine = cachedEngine;
755 fc->updateHitCountAndTimeStamp(it.value());
761 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fe, QUnicodeTables::Script(script));
762 QFontCache::instance()->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);