1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
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/QPlatformFontDatabase>
50 #include <QtGui/private/qpaintengine_raster_p.h>
54 //#define DEBUG_HEADER
55 //#define DEBUG_FONTENGINE
57 static QFontEngineQPA::TagType tagTypes[QFontEngineQPA::NumTags] = {
58 QFontEngineQPA::StringType, // FontName
59 QFontEngineQPA::StringType, // FileName
60 QFontEngineQPA::UInt32Type, // FileIndex
61 QFontEngineQPA::UInt32Type, // FontRevision
62 QFontEngineQPA::StringType, // FreeText
63 QFontEngineQPA::FixedType, // Ascent
64 QFontEngineQPA::FixedType, // Descent
65 QFontEngineQPA::FixedType, // Leading
66 QFontEngineQPA::FixedType, // XHeight
67 QFontEngineQPA::FixedType, // AverageCharWidth
68 QFontEngineQPA::FixedType, // MaxCharWidth
69 QFontEngineQPA::FixedType, // LineThickness
70 QFontEngineQPA::FixedType, // MinLeftBearing
71 QFontEngineQPA::FixedType, // MinRightBearing
72 QFontEngineQPA::FixedType, // UnderlinePosition
73 QFontEngineQPA::UInt8Type, // GlyphFormat
74 QFontEngineQPA::UInt8Type, // PixelSize
75 QFontEngineQPA::UInt8Type, // Weight
76 QFontEngineQPA::UInt8Type, // Style
77 QFontEngineQPA::StringType, // EndOfHeader
78 QFontEngineQPA::BitFieldType// WritingSystems
82 #if defined(DEBUG_HEADER)
83 # define DEBUG_VERIFY qDebug
85 # define DEBUG_VERIFY if (0) qDebug
88 #define READ_VERIFY(type, variable) \
89 if (tagPtr + sizeof(type) > endPtr) { \
90 DEBUG_VERIFY() << "read verify failed in line" << __LINE__; \
93 variable = qFromBigEndian<type>(tagPtr); \
94 DEBUG_VERIFY() << "read value" << variable << "of type " #type; \
95 tagPtr += sizeof(type)
98 T readValue(const uchar *&data)
100 T value = qFromBigEndian<T>(data);
105 #define VERIFY(condition) \
106 if (!(condition)) { \
107 DEBUG_VERIFY() << "condition " #condition " failed in line" << __LINE__; \
111 #define VERIFY_TAG(condition) \
112 if (!(condition)) { \
113 DEBUG_VERIFY() << "verifying tag condition " #condition " failed in line" << __LINE__ << "with tag" << tag; \
117 static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr)
120 READ_VERIFY(quint16, tag);
121 READ_VERIFY(quint16, length);
122 if (tag == QFontEngineQPA::Tag_EndOfHeader)
124 if (tag < QFontEngineQPA::NumTags) {
125 switch (tagTypes[tag]) {
126 case QFontEngineQPA::BitFieldType:
127 case QFontEngineQPA::StringType:
128 // can't do anything...
130 case QFontEngineQPA::UInt32Type:
131 VERIFY_TAG(length == sizeof(quint32));
133 case QFontEngineQPA::FixedType:
134 VERIFY_TAG(length == sizeof(quint32));
136 case QFontEngineQPA::UInt8Type:
137 VERIFY_TAG(length == sizeof(quint8));
140 #if defined(DEBUG_HEADER)
142 qDebug() << "tag data" << hex << *tagPtr;
143 else if (length == 4)
144 qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
147 return tagPtr + length;
150 const QFontEngineQPA::Glyph *QFontEngineQPA::findGlyph(glyph_t g) const
152 if (!g || g >= glyphMapEntries)
154 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
155 quint32 glyphPos = qFromBigEndian<quint32>(gmapPtr[g]);
156 if (glyphPos > glyphDataSize) {
157 if (glyphPos == 0xffffffff)
159 #if defined(DEBUG_FONTENGINE)
160 qDebug() << "glyph" << g << "outside of glyphData, remapping font file";
162 if (glyphPos > glyphDataSize)
165 return reinterpret_cast<const Glyph *>(fontData + glyphDataOffset + glyphPos);
168 bool QFontEngineQPA::verifyHeader(const uchar *data, int size)
170 VERIFY(size >= int(sizeof(Header)));
171 const Header *header = reinterpret_cast<const Header *>(data);
172 if (header->magic[0] != 'Q'
173 || header->magic[1] != 'P'
174 || header->magic[2] != 'F'
175 || header->magic[3] != '2')
178 VERIFY(header->majorVersion <= CurrentMajorVersion);
179 const quint16 dataSize = qFromBigEndian<quint16>(header->dataSize);
180 VERIFY(size >= int(sizeof(Header)) + dataSize);
182 const uchar *tagPtr = data + sizeof(Header);
183 const uchar *tagEndPtr = tagPtr + dataSize;
184 while (tagPtr < tagEndPtr - 3) {
185 tagPtr = verifyTag(tagPtr, tagEndPtr);
189 VERIFY(tagPtr <= tagEndPtr);
193 QVariant QFontEngineQPA::extractHeaderField(const uchar *data, HeaderTag requestedTag)
195 const Header *header = reinterpret_cast<const Header *>(data);
196 const uchar *tagPtr = data + sizeof(Header);
197 const uchar *endPtr = tagPtr + qFromBigEndian<quint16>(header->dataSize);
198 while (tagPtr < endPtr - 3) {
199 quint16 tag = readValue<quint16>(tagPtr);
200 quint16 length = readValue<quint16>(tagPtr);
201 if (tag == requestedTag) {
202 switch (tagTypes[requestedTag]) {
204 return QVariant(QString::fromUtf8(reinterpret_cast<const char *>(tagPtr), length));
206 return QVariant(readValue<quint32>(tagPtr));
208 return QVariant(uint(*tagPtr));
210 return QVariant(QFixed::fromFixed(readValue<quint32>(tagPtr)).toReal());
212 return QVariant(QByteArray(reinterpret_cast<const char *>(tagPtr), length));
215 } else if (tag == Tag_EndOfHeader) {
226 static inline unsigned int getChar(const QChar *str, int &i, const int len)
228 uint ucs4 = str[i].unicode();
229 if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
231 ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
236 QFontEngineQPA::QFontEngineQPA(const QFontDef &def, const QByteArray &data)
237 : fontData(reinterpret_cast<const uchar *>(data.constData())), dataSize(data.size())
248 kerning_pairs_loaded = false;
251 #if defined(DEBUG_FONTENGINE)
252 qDebug() << "QFontEngineQPA::QFontEngineQPA( fd =" << fd << ", renderingFontEngine =" << renderingFontEngine << ')';
255 if (!verifyHeader(fontData, dataSize)) {
256 #if defined(DEBUG_FONTENGINE)
257 qDebug() << "verifyHeader failed!";
262 const Header *header = reinterpret_cast<const Header *>(fontData);
264 readOnly = (header->lock == 0xffffffff);
266 const uchar *imgData = fontData + sizeof(Header) + qFromBigEndian<quint16>(header->dataSize);
267 const uchar *endPtr = fontData + dataSize;
268 while (imgData <= endPtr - 8) {
269 quint16 blockTag = readValue<quint16>(imgData);
270 imgData += 2; // skip padding
271 quint32 blockSize = readValue<quint32>(imgData);
273 if (blockTag == CMapBlock) {
274 cmapOffset = imgData - fontData;
275 cmapSize = blockSize;
276 } else if (blockTag == GMapBlock) {
277 glyphMapOffset = imgData - fontData;
278 glyphMapEntries = blockSize / 4;
279 } else if (blockTag == GlyphBlock) {
280 glyphDataOffset = imgData - fontData;
281 glyphDataSize = blockSize;
284 imgData += blockSize;
287 face_id.filename = QFile::encodeName(extractHeaderField(fontData, Tag_FileName).toString());
288 face_id.index = extractHeaderField(fontData, Tag_FileIndex).toInt();
292 int tableSize = cmapSize;
293 const uchar *cmapPtr = getCMap(fontData + cmapOffset, tableSize, &symbol, &cmapSize);
295 cmapOffset = cmapPtr - fontData;
298 } else if (externalCMap) {
299 int tableSize = cmapSize;
300 externalCMap = getCMap(externalCMap, tableSize, &symbol, &cmapSize);
303 // verify all the positions in the glyphMap
304 if (glyphMapOffset) {
305 const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset);
306 for (uint i = 0; i < glyphMapEntries; ++i) {
307 quint32 glyphDataPos = qFromBigEndian<quint32>(gmapPtr[i]);
308 if (glyphDataPos == 0xffffffff)
310 if (glyphDataPos >= glyphDataSize) {
319 #if defined(DEBUG_FONTENGINE)
321 qDebug() << "fontData" << fontData << "dataSize" << dataSize
322 << "externalCMap" << externalCMap << "cmapOffset" << cmapOffset
323 << "glyphMapOffset" << glyphMapOffset << "glyphDataOffset" << glyphDataOffset
324 << "fd" << fd << "glyphDataSize" << glyphDataSize;
328 QFontEngineQPA::~QFontEngineQPA()
332 bool QFontEngineQPA::getSfntTableData(uint tag, uchar *buffer, uint *length) const
340 bool QFontEngineQPA::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
342 if (*nglyphs < len) {
347 #if defined(DEBUG_FONTENGINE)
348 QSet<QChar> seenGlyphs;
351 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
353 bool mirrored = flags & QTextEngine::RightToLeft;
356 for (int i = 0; i < len; ++i) {
357 unsigned int uc = getChar(str, i, len);
359 uc = QChar::mirroredChar(uc);
360 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
361 if(!glyphs->glyphs[glyph_pos] && uc < 0x100)
362 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
366 for (int i = 0; i < len; ++i) {
367 unsigned int uc = getChar(str, i, len);
369 uc = QChar::mirroredChar(uc);
370 glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc);
371 #if 0 && defined(DEBUG_FONTENGINE)
373 if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c))
374 qDebug() << "glyph for character" << c << '/' << hex << uc << "is" << dec << glyphs[glyph_pos].glyph;
376 seenGlyphs.insert(c);
382 *nglyphs = glyph_pos;
383 glyphs->numGlyphs = glyph_pos;
384 recalcAdvances(glyphs, flags);
388 void QFontEngineQPA::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
390 for (int i = 0; i < glyphs->numGlyphs; ++i) {
391 const Glyph *g = findGlyph(glyphs->glyphs[i]);
393 glyphs->glyphs[i] = 0;
396 glyphs->advances_x[i] = g->advance;
397 glyphs->advances_y[i] = 0;
401 QImage QFontEngineQPA::alphaMapForGlyph(glyph_t g)
403 const Glyph *glyph = findGlyph(g);
407 const uchar *bits = ((const uchar *) glyph) + sizeof(Glyph);
409 QImage image(bits,glyph->width, glyph->height, glyph->bytesPerLine, QImage::Format_Indexed8);
414 void QFontEngineQPA::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
416 addBitmapFontToPath(x, y, glyphs, path, flags);
419 glyph_metrics_t QFontEngineQPA::boundingBox(const QGlyphLayout &glyphs)
421 glyph_metrics_t overall;
422 // initialize with line height, we get the same behaviour on all platforms
423 overall.y = -ascent();
424 overall.height = ascent() + descent() + 1;
428 for (int i = 0; i < glyphs.numGlyphs; i++) {
429 const Glyph *g = findGlyph(glyphs.glyphs[i]);
433 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
434 QFixed y = overall.yoff + glyphs.offsets[i].y + g->y;
435 overall.x = qMin(overall.x, x);
436 overall.y = qMin(overall.y, y);
437 xmax = qMax(xmax, x + g->width);
438 ymax = qMax(ymax, y + g->height);
439 overall.xoff += g->advance;
441 overall.height = qMax(overall.height, ymax - overall.y);
442 overall.width = xmax - overall.x;
447 glyph_metrics_t QFontEngineQPA::boundingBox(glyph_t glyph)
449 glyph_metrics_t overall;
450 const Glyph *g = findGlyph(glyph);
455 overall.width = g->width;
456 overall.height = g->height;
457 overall.xoff = g->advance;
461 QFixed QFontEngineQPA::ascent() const
463 return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>());
466 QFixed QFontEngineQPA::descent() const
468 return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>());
471 QFixed QFontEngineQPA::leading() const
473 return QFixed::fromReal(extractHeaderField(fontData, Tag_Leading).value<qreal>());
476 qreal QFontEngineQPA::maxCharWidth() const
478 return extractHeaderField(fontData, Tag_MaxCharWidth).value<qreal>();
481 qreal QFontEngineQPA::minLeftBearing() const
483 return extractHeaderField(fontData, Tag_MinLeftBearing).value<qreal>();
486 qreal QFontEngineQPA::minRightBearing() const
488 return extractHeaderField(fontData, Tag_MinRightBearing).value<qreal>();
491 QFixed QFontEngineQPA::underlinePosition() const
493 return QFixed::fromReal(extractHeaderField(fontData, Tag_UnderlinePosition).value<qreal>());
496 QFixed QFontEngineQPA::lineThickness() const
498 return QFixed::fromReal(extractHeaderField(fontData, Tag_LineThickness).value<qreal>());
501 QFontEngine::Type QFontEngineQPA::type() const
503 return QFontEngine::QPF2;
506 bool QFontEngineQPA::canRender(const QChar *string, int len)
508 const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset);
511 for (int i = 0; i < len; ++i) {
512 unsigned int uc = getChar(string, i, len);
513 glyph_t g = getTrueTypeGlyphIndex(cmap, uc);
515 g = getTrueTypeGlyphIndex(cmap, uc + 0xf000);
520 for (int i = 0; i < len; ++i) {
521 unsigned int uc = getChar(string, i, len);
522 if (!getTrueTypeGlyphIndex(cmap, uc))
529 bool QFontEngineQPA::isValid() const
531 return fontData && dataSize && (cmapOffset || externalCMap)
532 && glyphMapOffset && glyphDataOffset && glyphDataSize > 0;
535 void QPAGenerator::generate()
539 writeBlock(QFontEngineQPA::GlyphBlock, QByteArray());
541 dev->seek(4); // position of header.lock
545 void QPAGenerator::writeHeader()
547 QFontEngineQPA::Header header;
549 header.magic[0] = 'Q';
550 header.magic[1] = 'P';
551 header.magic[2] = 'F';
552 header.magic[3] = '2';
554 header.majorVersion = QFontEngineQPA::CurrentMajorVersion;
555 header.minorVersion = QFontEngineQPA::CurrentMinorVersion;
557 dev->write((const char *)&header, sizeof(header));
559 writeTaggedString(QFontEngineQPA::Tag_FontName, fe->fontDef.family.toUtf8());
561 QFontEngine::FaceId face = fe->faceId();
562 writeTaggedString(QFontEngineQPA::Tag_FileName, face.filename);
563 writeTaggedUInt32(QFontEngineQPA::Tag_FileIndex, face.index);
568 bool ok = fe->getSfntTableData(MAKE_TAG('h', 'e', 'a', 'd'), data, &len);
570 const quint32 revision = qFromBigEndian<quint32>(data);
571 writeTaggedUInt32(QFontEngineQPA::Tag_FontRevision, revision);
575 writeTaggedQFixed(QFontEngineQPA::Tag_Ascent, fe->ascent());
576 writeTaggedQFixed(QFontEngineQPA::Tag_Descent, fe->descent());
577 writeTaggedQFixed(QFontEngineQPA::Tag_Leading, fe->leading());
578 writeTaggedQFixed(QFontEngineQPA::Tag_XHeight, fe->xHeight());
579 writeTaggedQFixed(QFontEngineQPA::Tag_AverageCharWidth, fe->averageCharWidth());
580 writeTaggedQFixed(QFontEngineQPA::Tag_MaxCharWidth, QFixed::fromReal(fe->maxCharWidth()));
581 writeTaggedQFixed(QFontEngineQPA::Tag_LineThickness, fe->lineThickness());
582 writeTaggedQFixed(QFontEngineQPA::Tag_MinLeftBearing, QFixed::fromReal(fe->minLeftBearing()));
583 writeTaggedQFixed(QFontEngineQPA::Tag_MinRightBearing, QFixed::fromReal(fe->minRightBearing()));
584 writeTaggedQFixed(QFontEngineQPA::Tag_UnderlinePosition, fe->underlinePosition());
585 writeTaggedUInt8(QFontEngineQPA::Tag_PixelSize, fe->fontDef.pixelSize);
586 writeTaggedUInt8(QFontEngineQPA::Tag_Weight, fe->fontDef.weight);
587 writeTaggedUInt8(QFontEngineQPA::Tag_Style, fe->fontDef.style);
589 writeTaggedUInt8(QFontEngineQPA::Tag_GlyphFormat, QFontEngineQPA::AlphamapGlyphs);
591 writeTaggedString(QFontEngineQPA::Tag_EndOfHeader, QByteArray());
594 const quint64 size = dev->pos();
595 header.dataSize = qToBigEndian<quint16>(size - sizeof(header));
597 dev->write((const char *)&header, sizeof(header));
601 void QPAGenerator::writeGMap()
603 const quint16 glyphCount = fe->glyphCount();
605 writeUInt16(QFontEngineQPA::GMapBlock);
606 writeUInt16(0); // padding
607 writeUInt32(glyphCount * 4);
609 QByteArray &buffer = dev->buffer();
610 const int numBytes = glyphCount * sizeof(quint32);
611 qint64 pos = buffer.size();
612 buffer.resize(pos + numBytes);
613 qMemSet(buffer.data() + pos, 0xff, numBytes);
614 dev->seek(pos + numBytes);
617 void QPAGenerator::writeBlock(QFontEngineQPA::BlockTag tag, const QByteArray &data)
620 writeUInt16(0); // padding
621 const int padSize = ((data.size() + 3) / 4) * 4 - data.size();
622 writeUInt32(data.size() + padSize);
624 for (int i = 0; i < padSize; ++i)
628 void QPAGenerator::writeTaggedString(QFontEngineQPA::HeaderTag tag, const QByteArray &string)
631 writeUInt16(string.length());
635 void QPAGenerator::writeTaggedUInt32(QFontEngineQPA::HeaderTag tag, quint32 value)
638 writeUInt16(sizeof(value));
642 void QPAGenerator::writeTaggedUInt8(QFontEngineQPA::HeaderTag tag, quint8 value)
645 writeUInt16(sizeof(value));
649 void QPAGenerator::writeTaggedQFixed(QFontEngineQPA::HeaderTag tag, QFixed value)
652 writeUInt16(sizeof(quint32));
653 writeUInt32(value.value());
658 Creates a new multi QPA engine.
660 This function takes ownership of the QFontEngine, increasing it's refcount.
662 QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QStringList &fallbacks)
663 : QFontEngineMulti(fallbacks.size() + 1),
664 fallbackFamilies(fallbacks), script(_script)
666 Q_ASSERT(fe && fe->type() != QFontEngine::Multi);
669 fontDef = engines[0]->fontDef;
670 setObjectName(QStringLiteral("QFontEngineMultiQPA"));
673 void QFontEngineMultiQPA::loadEngine(int at)
675 Q_ASSERT(at < engines.size());
676 Q_ASSERT(engines.at(at) == 0);
678 QFontDef request = fontDef;
679 request.styleStrategy |= QFont::NoFontMerging;
680 request.family = fallbackFamilies.at(at-1);
681 engines[at] = QFontDatabase::findFont(script,
684 Q_ASSERT(engines[at]);
685 engines[at]->ref.ref();
686 engines[at]->fontDef = request;