/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the tools applications of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qpf2.h" | |
#include <math.h> | |
#include <private/qfontengine_p.h> | |
#include <QFile> | |
#include <qendian.h> | |
QT_BEGIN_NAMESPACE | |
#include "../../src/gui/text/qpfutil.cpp" | |
int QPF::debugVerbosity = 0; | |
// ### copied from qfontdatabase.cpp | |
// see the Unicode subset bitfields in the MSDN docs | |
static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = { | |
// Any, | |
{ 127, 127 }, | |
// Latin, | |
{ 0, 127 }, | |
// Greek, | |
{ 7, 127 }, | |
// Cyrillic, | |
{ 9, 127 }, | |
// Armenian, | |
{ 10, 127 }, | |
// Hebrew, | |
{ 11, 127 }, | |
// Arabic, | |
{ 13, 127 }, | |
// Syriac, | |
{ 71, 127 }, | |
//Thaana, | |
{ 72, 127 }, | |
//Devanagari, | |
{ 15, 127 }, | |
//Bengali, | |
{ 16, 127 }, | |
//Gurmukhi, | |
{ 17, 127 }, | |
//Gujarati, | |
{ 18, 127 }, | |
//Oriya, | |
{ 19, 127 }, | |
//Tamil, | |
{ 20, 127 }, | |
//Telugu, | |
{ 21, 127 }, | |
//Kannada, | |
{ 22, 127 }, | |
//Malayalam, | |
{ 23, 127 }, | |
//Sinhala, | |
{ 73, 127 }, | |
//Thai, | |
{ 24, 127 }, | |
//Lao, | |
{ 25, 127 }, | |
//Tibetan, | |
{ 70, 127 }, | |
//Myanmar, | |
{ 74, 127 }, | |
// Georgian, | |
{ 26, 127 }, | |
// Khmer, | |
{ 80, 127 }, | |
// SimplifiedChinese, | |
{ 126, 127 }, | |
// TraditionalChinese, | |
{ 126, 127 }, | |
// Japanese, | |
{ 126, 127 }, | |
// Korean, | |
{ 56, 127 }, | |
// Vietnamese, | |
{ 0, 127 }, // same as latin1 | |
// Other, | |
{ 126, 127 } | |
}; | |
#define SimplifiedChineseCsbBit 18 | |
#define TraditionalChineseCsbBit 20 | |
#define JapaneseCsbBit 17 | |
#define KoreanCsbBit 21 | |
static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]) | |
{ | |
QList<QFontDatabase::WritingSystem> writingSystems; | |
bool hasScript = false; | |
int i; | |
for(i = 0; i < QFontDatabase::WritingSystemsCount; i++) { | |
int bit = requiredUnicodeBits[i][0]; | |
int index = bit/32; | |
int flag = 1 << (bit&31); | |
if (bit != 126 && unicodeRange[index] & flag) { | |
bit = requiredUnicodeBits[i][1]; | |
index = bit/32; | |
flag = 1 << (bit&31); | |
if (bit == 127 || unicodeRange[index] & flag) { | |
writingSystems.append(QFontDatabase::WritingSystem(i)); | |
hasScript = true; | |
// qDebug("font %s: index=%d, flag=%8x supports script %d", familyName.latin1(), index, flag, i); | |
} | |
} | |
} | |
if(codePageRange[0] & (1 << SimplifiedChineseCsbBit)) { | |
writingSystems.append(QFontDatabase::SimplifiedChinese); | |
hasScript = true; | |
//qDebug("font %s supports Simplified Chinese", familyName.latin1()); | |
} | |
if(codePageRange[0] & (1 << TraditionalChineseCsbBit)) { | |
writingSystems.append(QFontDatabase::TraditionalChinese); | |
hasScript = true; | |
//qDebug("font %s supports Traditional Chinese", familyName.latin1()); | |
} | |
if(codePageRange[0] & (1 << JapaneseCsbBit)) { | |
writingSystems.append(QFontDatabase::Japanese); | |
hasScript = true; | |
//qDebug("font %s supports Japanese", familyName.latin1()); | |
} | |
if(codePageRange[0] & (1 << KoreanCsbBit)) { | |
writingSystems.append(QFontDatabase::Korean); | |
hasScript = true; | |
//qDebug("font %s supports Korean", familyName.latin1()); | |
} | |
if (!hasScript) | |
writingSystems.append(QFontDatabase::Symbol); | |
return writingSystems; | |
} | |
static QByteArray getWritingSystems(QFontEngine *fontEngine) | |
{ | |
QByteArray os2Table = fontEngine->getSfntTable(MAKE_TAG('O', 'S', '/', '2')); | |
if (os2Table.isEmpty()) | |
return QByteArray(); | |
const uchar *data = reinterpret_cast<const uchar *>(os2Table.constData()); | |
quint32 unicodeRange[4] = { | |
qFromBigEndian<quint32>(data + 42), | |
qFromBigEndian<quint32>(data + 46), | |
qFromBigEndian<quint32>(data + 50), | |
qFromBigEndian<quint32>(data + 54) | |
}; | |
quint32 codePageRange[2] = { qFromBigEndian<quint32>(data + 78), qFromBigEndian<quint32>(data + 82) }; | |
QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); | |
QByteArray bitField((QFontDatabase::WritingSystemsCount + 7) / 8, 0); | |
for (int i = 0; i < systems.count(); ++i) { | |
int bitPos = systems.at(i); | |
bitField[bitPos / 8] = bitField.at(bitPos / 8) | (1 << (bitPos % 8)); | |
} | |
return bitField; | |
} | |
static QString stringify(const QByteArray &bits) | |
{ | |
QString result; | |
for (int i = 0; i < bits.count(); ++i) { | |
uchar currentByte = bits.at(i); | |
for (int j = 0; j < 8; ++j) { | |
if (currentByte & 1) | |
result += '1'; | |
else | |
result += '0'; | |
currentByte >>= 1; | |
} | |
} | |
return result; | |
} | |
static void dumpWritingSystems(const QByteArray &bits) | |
{ | |
QStringList writingSystems; | |
QString bitString = stringify(bits); | |
for (int i = 0; i < qMin(int(QFontDatabase::WritingSystemsCount), bitString.length()); ++i) { | |
if (bitString.at(i) == QLatin1Char('1')) | |
writingSystems << QFontDatabase::writingSystemName(QFontDatabase::WritingSystem(i)); | |
} | |
qDebug() << "Supported writing systems:" << writingSystems; | |
} | |
static const char *headerTagNames[QFontEngineQPF::NumTags] = { | |
"FontName", | |
"FileName", | |
"FileIndex", | |
"FontRevision", | |
"FreeText", | |
"Ascent", | |
"Descent", | |
"Leading", | |
"XHeight", | |
"AverageCharWidth", | |
"MaxCharWidth", | |
"LineThickness", | |
"MinLeftBearing", | |
"MinRightBearing", | |
"UnderlinePosition", | |
"GlyphFormat", | |
"PixelSize", | |
"Weight", | |
"Style", | |
"EndOfHeader", | |
"WritingSystems" | |
}; | |
QString QPF::fileNameForFont(const QFont &f) | |
{ | |
QString fileName = f.family().toLower() + "_" + QString::number(f.pixelSize()) | |
+ "_" + QString::number(f.weight()) | |
+ (f.italic() ? "_italic" : "") | |
+ ".qpf2"; | |
fileName.replace(QLatin1Char(' '), QLatin1Char('_')); | |
return fileName; | |
} | |
QByteArray QPF::generate(const QFont &font, int options, const QList<CharacterRange> &ranges, QString *originalFontFile) | |
{ | |
QTextEngine engine("Test", font); | |
engine.itemize(); | |
engine.shape(0); | |
QFontEngine *fontEngine = engine.fontEngine(engine.layoutData->items[0]); | |
if (fontEngine->type() == QFontEngine::Multi) | |
fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(0); | |
if (originalFontFile) | |
*originalFontFile = QFile::decodeName(fontEngine->faceId().filename); | |
return generate(fontEngine, options, ranges); | |
} | |
QByteArray QPF::generate(QFontEngine *fontEngine, int options, const QList<CharacterRange> &ranges) | |
{ | |
QPF font; | |
font.options = options; | |
font.addHeader(fontEngine); | |
if (options & IncludeCMap) | |
font.addCMap(fontEngine); | |
font.addGlyphs(fontEngine, ranges); | |
return font.qpf; | |
} | |
void QPF::addHeader(QFontEngine *fontEngine) | |
{ | |
QFontEngineQPF::Header *header = reinterpret_cast<QFontEngineQPF::Header *>(addBytes(sizeof(QFontEngineQPF::Header))); | |
header->magic[0] = 'Q'; | |
header->magic[1] = 'P'; | |
header->magic[2] = 'F'; | |
header->magic[3] = '2'; | |
if (options & RenderGlyphs) | |
header->lock = 0xffffffff; | |
else | |
header->lock = 0; | |
header->majorVersion = QFontEngineQPF::CurrentMajorVersion; | |
header->minorVersion = QFontEngineQPF::CurrentMinorVersion; | |
header->dataSize = 0; | |
int oldSize = qpf.size(); | |
addTaggedString(QFontEngineQPF::Tag_FontName, fontEngine->fontDef.family.toUtf8()); | |
QFontEngine::FaceId face = fontEngine->faceId(); | |
addTaggedString(QFontEngineQPF::Tag_FileName, face.filename); | |
addTaggedUInt32(QFontEngineQPF::Tag_FileIndex, face.index); | |
{ | |
const QByteArray head = fontEngine->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')); | |
const quint32 revision = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(head.constData()) + 4); | |
addTaggedUInt32(QFontEngineQPF::Tag_FontRevision, revision); | |
} | |
addTaggedQFixed(QFontEngineQPF::Tag_Ascent, fontEngine->ascent()); | |
addTaggedQFixed(QFontEngineQPF::Tag_Descent, fontEngine->descent()); | |
addTaggedQFixed(QFontEngineQPF::Tag_Leading, fontEngine->leading()); | |
addTaggedQFixed(QFontEngineQPF::Tag_XHeight, fontEngine->xHeight()); | |
addTaggedQFixed(QFontEngineQPF::Tag_AverageCharWidth, fontEngine->averageCharWidth()); | |
addTaggedQFixed(QFontEngineQPF::Tag_MaxCharWidth, QFixed::fromReal(fontEngine->maxCharWidth())); | |
addTaggedQFixed(QFontEngineQPF::Tag_LineThickness, fontEngine->lineThickness()); | |
addTaggedQFixed(QFontEngineQPF::Tag_MinLeftBearing, QFixed::fromReal(fontEngine->minLeftBearing())); | |
addTaggedQFixed(QFontEngineQPF::Tag_MinRightBearing, QFixed::fromReal(fontEngine->minRightBearing())); | |
addTaggedQFixed(QFontEngineQPF::Tag_UnderlinePosition, fontEngine->underlinePosition()); | |
addTaggedUInt8(QFontEngineQPF::Tag_PixelSize, fontEngine->fontDef.pixelSize); | |
addTaggedUInt8(QFontEngineQPF::Tag_Weight, fontEngine->fontDef.weight); | |
addTaggedUInt8(QFontEngineQPF::Tag_Style, fontEngine->fontDef.style); | |
QByteArray writingSystemBitField = getWritingSystems(fontEngine); | |
if (!writingSystemBitField.isEmpty()) | |
addTaggedString(QFontEngineQPF::Tag_WritingSystems, writingSystemBitField); | |
addTaggedUInt8(QFontEngineQPF::Tag_GlyphFormat, QFontEngineQPF::AlphamapGlyphs); | |
addTaggedString(QFontEngineQPF::Tag_EndOfHeader, QByteArray()); | |
align4(); | |
header = reinterpret_cast<QFontEngineQPF::Header *>(qpf.data()); | |
header->dataSize = qToBigEndian<quint16>(qpf.size() - oldSize); | |
} | |
static uchar *appendBytes(QByteArray &array, int size) | |
{ | |
int oldSize = array.size(); | |
array.resize(array.size() + size); | |
return reinterpret_cast<uchar *>(array.data() + oldSize); | |
} | |
#define APPEND(type, value) \ | |
qToBigEndian<type>(value, appendBytes(cmap, sizeof(type))) | |
struct CMapSegment | |
{ | |
int start; // codepoints | |
int end; | |
int startGlyphIndex; | |
}; | |
static QByteArray generateTrueTypeCMap(QFontEngine *fe) | |
{ | |
QByteArray cmap; | |
const int glyphCount = fe->glyphCount(); | |
if (!glyphCount) | |
return cmap; | |
// cmap header | |
APPEND(quint16, 0); // table version number | |
APPEND(quint16, 1); // number of tables | |
// encoding record | |
APPEND(quint16, 3); // platform-id | |
APPEND(quint16, 10); // encoding-id (ucs-4) | |
const int cmapOffset = cmap.size() + sizeof(quint32); | |
APPEND(quint32, cmapOffset); // offset to sub-table | |
APPEND(quint16, 4); // subtable format | |
const int cmapTableLengthOffset = cmap.size(); | |
APPEND(quint16, 0); // length in bytes, will fill in later | |
APPEND(quint16, 0); // language field | |
QList<CMapSegment> segments; | |
CMapSegment currentSegment; | |
currentSegment.start = 0xffff; | |
currentSegment.end = 0; | |
currentSegment.startGlyphIndex = 0; | |
quint32 previousGlyphIndex = 0xfffffffe; | |
bool inSegment = false; | |
QGlyphLayoutArray<10> layout; | |
for (uint uc = 0; uc < 0x10000; ++uc) { | |
QChar ch(uc); | |
int nglyphs = 10; | |
bool validGlyph = fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0) | |
&& nglyphs == 1 && layout.glyphs[0]; | |
// leaving a segment? | |
if (inSegment && (!validGlyph || layout.glyphs[0] != previousGlyphIndex + 1)) { | |
Q_ASSERT(currentSegment.start != 0xffff); | |
// store the current segment | |
currentSegment.end = uc - 1; | |
segments.append(currentSegment); | |
currentSegment.start = 0xffff; | |
inSegment = false; | |
} | |
// entering a new segment? | |
if (validGlyph && (!inSegment || layout.glyphs[0] != previousGlyphIndex + 1)) { | |
currentSegment.start = uc; | |
currentSegment.startGlyphIndex = layout.glyphs[0]; | |
inSegment = true; | |
} | |
if (validGlyph) | |
previousGlyphIndex = layout.glyphs[0]; | |
else | |
previousGlyphIndex = 0xfffffffe; | |
} | |
currentSegment.start = 0xffff; | |
currentSegment.end = 0xffff; | |
currentSegment.startGlyphIndex = 0; | |
segments.append(currentSegment); | |
if (QPF::debugVerbosity > 3) | |
qDebug() << "segments:" << segments.count(); | |
Q_ASSERT(!inSegment); | |
const quint16 entrySelector = int(log2(segments.count())); | |
const quint16 searchRange = 2 * (1 << entrySelector); | |
const quint16 rangeShift = segments.count() * 2 - searchRange; | |
if (QPF::debugVerbosity > 3) | |
qDebug() << "entrySelector" << entrySelector << "searchRange" << searchRange | |
<< "rangeShift" << rangeShift; | |
APPEND(quint16, segments.count() * 2); // segCountX2 | |
APPEND(quint16, searchRange); | |
APPEND(quint16, entrySelector); | |
APPEND(quint16, rangeShift); | |
// end character codes | |
for (int i = 0; i < segments.count(); ++i) | |
APPEND(quint16, segments.at(i).end); | |
APPEND(quint16, 0); // pad | |
// start character codes | |
for (int i = 0; i < segments.count(); ++i) | |
APPEND(quint16, segments.at(i).start); | |
// id deltas | |
for (int i = 0; i < segments.count(); ++i) | |
APPEND(quint16, segments.at(i).startGlyphIndex - segments.at(i).start); | |
// id range offsets | |
for (int i = 0; i < segments.count(); ++i) | |
APPEND(quint16, 0); | |
uchar *lengthPtr = reinterpret_cast<uchar *>(cmap.data()) + cmapTableLengthOffset; | |
qToBigEndian<quint16>(cmap.size() - cmapOffset, lengthPtr); | |
return cmap; | |
} | |
void QPF::addCMap(QFontEngine *fontEngine) | |
{ | |
QByteArray cmapTable = fontEngine->getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); | |
if (cmapTable.isEmpty()) | |
cmapTable = generateTrueTypeCMap(fontEngine); | |
addBlock(QFontEngineQPF::CMapBlock, cmapTable); | |
} | |
void QPF::addGlyphs(QFontEngine *fe, const QList<CharacterRange> &ranges) | |
{ | |
const quint16 glyphCount = fe->glyphCount(); | |
QByteArray gmap; | |
gmap.resize(glyphCount * sizeof(quint32)); | |
gmap.fill(char(0xff)); | |
//qDebug() << "glyphCount" << glyphCount; | |
QByteArray glyphs; | |
if (options & RenderGlyphs) { | |
// this is only a rough estimation | |
glyphs.reserve(glyphCount | |
* (sizeof(QFontEngineQPF::Glyph) | |
+ qRound(fe->maxCharWidth() * (fe->ascent() + fe->descent()).toReal()))); | |
QGlyphLayoutArray<10> layout; | |
foreach (CharacterRange range, ranges) { | |
if (debugVerbosity > 2) | |
qDebug() << "rendering range from" << range.start << "to" << range.end; | |
for (uint uc = range.start; uc < range.end; ++uc) { | |
QChar ch(uc); | |
int nglyphs = 10; | |
if (!fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0)) | |
continue; | |
if (nglyphs != 1) | |
continue; | |
const quint32 glyphIndex = layout.glyphs[0]; | |
if (!glyphIndex) | |
continue; | |
Q_ASSERT(glyphIndex < glyphCount); | |
QImage img = fe->alphaMapForGlyph(glyphIndex).convertToFormat(QImage::Format_Indexed8); | |
glyph_metrics_t metrics = fe->boundingBox(glyphIndex); | |
const quint32 oldSize = glyphs.size(); | |
glyphs.resize(glyphs.size() + sizeof(QFontEngineQPF::Glyph) + img.byteCount()); | |
uchar *data = reinterpret_cast<uchar *>(glyphs.data() + oldSize); | |
uchar *gmapPtr = reinterpret_cast<uchar *>(gmap.data() + glyphIndex * sizeof(quint32)); | |
qToBigEndian(oldSize, gmapPtr); | |
QFontEngineQPF::Glyph *glyph = reinterpret_cast<QFontEngineQPF::Glyph *>(data); | |
glyph->width = img.width(); | |
glyph->height = img.height(); | |
glyph->bytesPerLine = img.bytesPerLine(); | |
glyph->x = qRound(metrics.x); | |
glyph->y = qRound(metrics.y); | |
glyph->advance = qRound(metrics.xoff); | |
data += sizeof(QFontEngineQPF::Glyph); | |
if (debugVerbosity && uc >= 'A' && uc <= 'z' || debugVerbosity > 1) { | |
qDebug() << "adding glyph with index" << glyphIndex << " uc =" << char(uc) << ":\n" | |
<< " glyph->x =" << glyph->x << "rounded from" << metrics.x << "\n" | |
<< " glyph->y =" << glyph->y << "rounded from" << metrics.y << "\n" | |
<< " width =" << glyph->width << "height =" << glyph->height | |
<< " advance =" << glyph->advance << "rounded from" << metrics.xoff | |
; | |
} | |
memcpy(data, img.bits(), img.byteCount()); | |
} | |
} | |
} | |
addBlock(QFontEngineQPF::GMapBlock, gmap); | |
addBlock(QFontEngineQPF::GlyphBlock, glyphs); | |
} | |
void QPF::addBlock(QFontEngineQPF::BlockTag tag, const QByteArray &blockData) | |
{ | |
addUInt16(tag); | |
addUInt16(0); // padding | |
const int padSize = ((blockData.size() + 3) / 4) * 4 - blockData.size(); | |
addUInt32(blockData.size() + padSize); | |
addByteArray(blockData); | |
for (int i = 0; i < padSize; ++i) | |
addUInt8(0); | |
} | |
#define ADD_TAGGED_DATA(tag, qtype, type, value) \ | |
addUInt16(tag); \ | |
addUInt16(sizeof(qtype)); \ | |
add##type(value) | |
void QPF::addTaggedString(QFontEngineQPF::HeaderTag tag, const QByteArray &string) | |
{ | |
addUInt16(tag); | |
addUInt16(string.length()); | |
addByteArray(string); | |
} | |
void QPF::addTaggedQFixed(QFontEngineQPF::HeaderTag tag, QFixed value) | |
{ | |
ADD_TAGGED_DATA(tag, quint32, UInt32, value.value()); | |
} | |
void QPF::addTaggedUInt8(QFontEngineQPF::HeaderTag tag, quint8 value) | |
{ | |
ADD_TAGGED_DATA(tag, quint8, UInt8, value); | |
} | |
void QPF::addTaggedInt8(QFontEngineQPF::HeaderTag tag, qint8 value) | |
{ | |
ADD_TAGGED_DATA(tag, qint8, Int8, value); | |
} | |
void QPF::addTaggedUInt16(QFontEngineQPF::HeaderTag tag, quint16 value) | |
{ | |
ADD_TAGGED_DATA(tag, quint16, UInt16, value); | |
} | |
void QPF::addTaggedUInt32(QFontEngineQPF::HeaderTag tag, quint32 value) | |
{ | |
ADD_TAGGED_DATA(tag, quint32, UInt32, value); | |
} | |
void QPF::dump(const QByteArray &qpf) | |
{ | |
QPF font; | |
font.qpf = qpf; | |
const uchar *data = reinterpret_cast<const uchar *>(qpf.constData()); | |
const uchar *endPtr = reinterpret_cast<const uchar *>(qpf.constData() + qpf.size()); | |
data = font.dumpHeader(data); | |
const quint32 *gmap = 0; | |
quint32 glyphCount = 0; | |
while (data < endPtr) { | |
const QFontEngineQPF::Block *block = reinterpret_cast<const QFontEngineQPF::Block *>(data); | |
quint32 tag = qFromBigEndian(block->tag); | |
quint32 blockSize = qFromBigEndian(block->dataSize); | |
qDebug() << "Block: Tag =" << qFromBigEndian(block->tag) << "; Size =" << blockSize << "; Offset =" << hex << data - reinterpret_cast<const uchar *>(qpf.constData()); | |
data += sizeof(QFontEngineQPF::Block); | |
if (debugVerbosity) { | |
if (tag == QFontEngineQPF::GMapBlock) { | |
gmap = reinterpret_cast<const quint32 *>(data); | |
glyphCount = blockSize / 4; | |
font.dumpGMapBlock(gmap, glyphCount); | |
} else if (tag == QFontEngineQPF::GlyphBlock | |
&& gmap && debugVerbosity > 1) { | |
font.dumpGlyphBlock(gmap, glyphCount, data, data + blockSize); | |
} | |
} | |
data += blockSize; | |
} | |
} | |
const uchar *QPF::dumpHeader(const uchar *data) | |
{ | |
const QFontEngineQPF::Header *header = reinterpret_cast<const QFontEngineQPF::Header *>(data); | |
qDebug() << "Header:"; | |
qDebug() << "magic =" | |
<< header->magic[0] | |
<< header->magic[1] | |
<< header->magic[2] | |
<< header->magic[3]; | |
qDebug() << "lock =" << qFromBigEndian(header->lock); | |
qDebug() << "majorVersion =" << header->majorVersion; | |
qDebug() << "minorVersion =" << header->minorVersion; | |
qDebug() << "dataSize =" << qFromBigEndian(header->dataSize); | |
data += sizeof(QFontEngineQPF::Header); | |
const uchar *endPtr = data + qFromBigEndian(header->dataSize); | |
while (data && data < endPtr) { | |
data = dumpHeaderTag(data); | |
} | |
return endPtr; | |
} | |
const uchar *QPF::dumpHeaderTag(const uchar *data) | |
{ | |
const QFontEngineQPF::Tag *tagPtr = reinterpret_cast<const QFontEngineQPF::Tag *>(data); | |
quint16 tag = qFromBigEndian(tagPtr->tag); | |
quint16 size = qFromBigEndian(tagPtr->size); | |
qDebug() << "Tag =" << tag << headerTagNames[tag]; | |
qDebug() << "Size =" << size; | |
if (tag == QFontEngineQPF::Tag_EndOfHeader) | |
return 0; | |
data += sizeof(QFontEngineQPF::Tag); | |
Q_ASSERT(tag < QFontEngineQPF::NumTags); | |
switch (tagTypes[tag]) { | |
case QFontEngineQPF::StringType: | |
qDebug() << "Payload =" << QString::fromUtf8(QByteArray(reinterpret_cast<const char *>(data), size)); | |
break; | |
case QFontEngineQPF::FixedType: | |
Q_ASSERT(size == sizeof(quint32)); | |
qDebug() << "Payload =" << QFixed::fromFixed(qFromBigEndian<quint32>(data)).toReal(); | |
break; | |
case QFontEngineQPF::UInt8Type: | |
Q_ASSERT(size == sizeof(quint8)); | |
qDebug() << "Payload =" << *data; | |
break; | |
case QFontEngineQPF::UInt32Type: | |
Q_ASSERT(size == sizeof(quint32)); | |
qDebug() << "Payload =" << qFromBigEndian<quint32>(data); | |
break; | |
case QFontEngineQPF::BitFieldType: { | |
QByteArray bits(reinterpret_cast<const char *>(data), size); | |
qDebug() << "Payload =" << stringify(bits); | |
if (QPF::debugVerbosity > 2 && tag == QFontEngineQPF::Tag_WritingSystems) | |
dumpWritingSystems(bits); | |
} break; | |
} | |
data += size; | |
return data; | |
} | |
void QPF::dumpGMapBlock(const quint32 *gmap, int glyphCount) | |
{ | |
qDebug() << "glyphCount =" << glyphCount; | |
int renderedGlyphs = 0; | |
for (int i = 0; i < glyphCount; ++i) { | |
if (gmap[i] != 0xffffffff) { | |
const quint32 glyphPos = qFromBigEndian(gmap[i]); | |
qDebug("gmap[%d] = 0x%x / %u", i, glyphPos, glyphPos); | |
++renderedGlyphs; | |
} | |
} | |
qDebug() << "Glyphs rendered:" << renderedGlyphs << "; Glyphs missing from the font:" << glyphCount - renderedGlyphs; | |
} | |
void QPF::dumpGlyphBlock(const quint32 *gmap, int glyphCount, const uchar *data, const uchar *endPtr) | |
{ | |
// glyphPos -> glyphIndex | |
QMap<quint32, quint32> reverseGlyphMap; | |
for (int i = 0; i < glyphCount; ++i) { | |
if (gmap[i] == 0xffffffff) | |
continue; | |
const quint32 glyphPos = qFromBigEndian(gmap[i]); | |
reverseGlyphMap[glyphPos] = i; | |
} | |
const uchar *glyphBlockBegin = data; | |
while (data < endPtr) { | |
const QFontEngineQPF::Glyph *g = reinterpret_cast<const QFontEngineQPF::Glyph *>(data); | |
const quint64 glyphOffset = data - glyphBlockBegin; | |
const quint32 glyphIndex = reverseGlyphMap.value(glyphOffset, 0xffffffff); | |
if (glyphIndex == 0xffffffff) | |
qDebug() << "############: Glyph present in glyph block is not listed in glyph map!"; | |
qDebug("glyph at offset 0x%x glyphIndex = %u", quint32(glyphOffset), glyphIndex); | |
qDebug() << " width =" << g->width << "height =" << g->height << "x =" << g->x << "y =" << g->y; | |
qDebug() << " advance =" << g->advance << "bytesPerLine =" << g->bytesPerLine; | |
data += sizeof(*g); | |
if (glyphIndex == 0xffffffff || debugVerbosity > 4) { | |
dumpGlyph(data, g); | |
} | |
data += g->height * g->bytesPerLine; | |
} | |
} | |
void QPF::dumpGlyph(const uchar *data, const QFontEngineQPF::Glyph *glyph) | |
{ | |
fprintf(stderr, "---- glyph data:\n"); | |
const char *alphas = " .o#"; | |
for (int y = 0; y < glyph->height; ++y) { | |
for (int x = 0; x < glyph->width; ++x) { | |
const uchar value = data[y * glyph->bytesPerLine + x]; | |
fprintf(stderr, "%c", alphas[value >> 6]); | |
} | |
fprintf(stderr, "\n"); | |
} | |
fprintf(stderr, "----\n"); | |
} | |
QT_END_NAMESPACE |