1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "qpf2.h"
30 
31 #include <math.h>
32 #include <private/qfontengine_p.h>
33 #include <QFile>
34 #include <qendian.h>
35 
36 QT_BEGIN_NAMESPACE
37 
38 int QPF::debugVerbosity = 0;
39 
40 // ### copied from qfontdatabase.cpp
41 
42 // see the Unicode subset bitfields in the MSDN docs
43 static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = {
44         // Any,
45     { 127, 127 },
46         // Latin,
47     { 0, 127 },
48         // Greek,
49     { 7, 127 },
50         // Cyrillic,
51     { 9, 127 },
52         // Armenian,
53     { 10, 127 },
54         // Hebrew,
55     { 11, 127 },
56         // Arabic,
57     { 13, 127 },
58         // Syriac,
59     { 71, 127 },
60     //Thaana,
61     { 72, 127 },
62     //Devanagari,
63     { 15, 127 },
64     //Bengali,
65     { 16, 127 },
66     //Gurmukhi,
67     { 17, 127 },
68     //Gujarati,
69     { 18, 127 },
70     //Oriya,
71     { 19, 127 },
72     //Tamil,
73     { 20, 127 },
74     //Telugu,
75     { 21, 127 },
76     //Kannada,
77     { 22, 127 },
78     //Malayalam,
79     { 23, 127 },
80     //Sinhala,
81     { 73, 127 },
82     //Thai,
83     { 24, 127 },
84     //Lao,
85     { 25, 127 },
86     //Tibetan,
87     { 70, 127 },
88     //Myanmar,
89     { 74, 127 },
90         // Georgian,
91     { 26, 127 },
92         // Khmer,
93     { 80, 127 },
94         // SimplifiedChinese,
95     { 126, 127 },
96         // TraditionalChinese,
97     { 126, 127 },
98         // Japanese,
99     { 126, 127 },
100         // Korean,
101     { 56, 127 },
102         // Vietnamese,
103     { 0, 127 }, // same as latin1
104         // Other,
105     { 126, 127 }
106 };
107 
108 #define SimplifiedChineseCsbBit 18
109 #define TraditionalChineseCsbBit 20
110 #define JapaneseCsbBit 17
111 #define KoreanCsbBit 21
112 
113 static const QFontEngineQPF2::TagType tagTypes[QFontEngineQPF2::NumTags] = {
114     QFontEngineQPF2::StringType, // FontName
115     QFontEngineQPF2::StringType, // FileName
116     QFontEngineQPF2::UInt32Type, // FileIndex
117     QFontEngineQPF2::UInt32Type, // FontRevision
118     QFontEngineQPF2::StringType, // FreeText
119     QFontEngineQPF2::FixedType,  // Ascent
120     QFontEngineQPF2::FixedType,  // Descent
121     QFontEngineQPF2::FixedType,  // Leading
122     QFontEngineQPF2::FixedType,  // XHeight
123     QFontEngineQPF2::FixedType,  // AverageCharWidth
124     QFontEngineQPF2::FixedType,  // MaxCharWidth
125     QFontEngineQPF2::FixedType,  // LineThickness
126     QFontEngineQPF2::FixedType,  // MinLeftBearing
127     QFontEngineQPF2::FixedType,  // MinRightBearing
128     QFontEngineQPF2::FixedType,  // UnderlinePosition
129     QFontEngineQPF2::UInt8Type,  // GlyphFormat
130     QFontEngineQPF2::UInt8Type,  // PixelSize
131     QFontEngineQPF2::UInt8Type,  // Weight
132     QFontEngineQPF2::UInt8Type,  // Style
133     QFontEngineQPF2::StringType, // EndOfHeader
134     QFontEngineQPF2::BitFieldType// WritingSystems
135 };
136 
determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4],quint32 codePageRange[2])137 static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2])
138 {
139     QList<QFontDatabase::WritingSystem> writingSystems;
140     bool hasScript = false;
141 
142     int i;
143     for(i = 0; i < QFontDatabase::WritingSystemsCount; i++) {
144         int bit = requiredUnicodeBits[i][0];
145         int index = bit/32;
146         int flag =  1 << (bit&31);
147         if (bit != 126 && unicodeRange[index] & flag) {
148             bit = requiredUnicodeBits[i][1];
149             index = bit/32;
150 
151             flag =  1 << (bit&31);
152             if (bit == 127 || unicodeRange[index] & flag) {
153                 writingSystems.append(QFontDatabase::WritingSystem(i));
154                 hasScript = true;
155                 // qDebug("font %s: index=%d, flag=%8x supports script %d", familyName.latin1(), index, flag, i);
156             }
157         }
158     }
159     if(codePageRange[0] & (1 << SimplifiedChineseCsbBit)) {
160         writingSystems.append(QFontDatabase::SimplifiedChinese);
161         hasScript = true;
162         //qDebug("font %s supports Simplified Chinese", familyName.latin1());
163     }
164     if(codePageRange[0] & (1 << TraditionalChineseCsbBit)) {
165         writingSystems.append(QFontDatabase::TraditionalChinese);
166         hasScript = true;
167         //qDebug("font %s supports Traditional Chinese", familyName.latin1());
168     }
169     if(codePageRange[0] & (1 << JapaneseCsbBit)) {
170         writingSystems.append(QFontDatabase::Japanese);
171         hasScript = true;
172         //qDebug("font %s supports Japanese", familyName.latin1());
173     }
174     if(codePageRange[0] & (1 << KoreanCsbBit)) {
175         writingSystems.append(QFontDatabase::Korean);
176         hasScript = true;
177         //qDebug("font %s supports Korean", familyName.latin1());
178     }
179     if (!hasScript)
180         writingSystems.append(QFontDatabase::Symbol);
181 
182     return writingSystems;
183 }
184 
getWritingSystems(QFontEngine * fontEngine)185 static QByteArray getWritingSystems(QFontEngine *fontEngine)
186 {
187     QByteArray os2Table = fontEngine->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
188     if (os2Table.isEmpty())
189         return QByteArray();
190 
191     const uchar *data = reinterpret_cast<const uchar *>(os2Table.constData());
192 
193     quint32 unicodeRange[4] = {
194         qFromBigEndian<quint32>(data + 42),
195         qFromBigEndian<quint32>(data + 46),
196         qFromBigEndian<quint32>(data + 50),
197         qFromBigEndian<quint32>(data + 54)
198     };
199     quint32 codePageRange[2] = { qFromBigEndian<quint32>(data + 78), qFromBigEndian<quint32>(data + 82) };
200     QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
201 
202     QByteArray bitField((QFontDatabase::WritingSystemsCount + 7) / 8, 0);
203 
204     for (int i = 0; i < systems.count(); ++i) {
205         int bitPos = systems.at(i);
206         bitField[bitPos / 8] = bitField.at(bitPos / 8) | (1 << (bitPos % 8));
207     }
208 
209     return bitField;
210 }
211 
stringify(const QByteArray & bits)212 static QString stringify(const QByteArray &bits)
213 {
214     QString result;
215     for (int i = 0; i < bits.count(); ++i) {
216         uchar currentByte = bits.at(i);
217         for (int j = 0; j < 8; ++j) {
218             if (currentByte & 1)
219                 result += '1';
220             else
221                 result += '0';
222             currentByte >>= 1;
223         }
224     }
225     return result;
226 }
227 
dumpWritingSystems(const QByteArray & bits)228 static void dumpWritingSystems(const QByteArray &bits)
229 {
230     QStringList writingSystems;
231 
232     QString bitString = stringify(bits);
233     for (int i = 0; i < qMin(int(QFontDatabase::WritingSystemsCount), bitString.length()); ++i) {
234         if (bitString.at(i) == QLatin1Char('1'))
235             writingSystems << QFontDatabase::writingSystemName(QFontDatabase::WritingSystem(i));
236     }
237 
238     qDebug() << "Supported writing systems:" << writingSystems;
239 }
240 
241 static const char *headerTagNames[QFontEngineQPF2::NumTags] = {
242     "FontName",
243     "FileName",
244     "FileIndex",
245     "FontRevision",
246     "FreeText",
247     "Ascent",
248     "Descent",
249     "Leading",
250     "XHeight",
251     "AverageCharWidth",
252     "MaxCharWidth",
253     "LineThickness",
254     "MinLeftBearing",
255     "MinRightBearing",
256     "UnderlinePosition",
257     "GlyphFormat",
258     "PixelSize",
259     "Weight",
260     "Style",
261     "EndOfHeader",
262     "WritingSystems"
263 };
264 
fileNameForFont(const QFont & f)265 QString QPF::fileNameForFont(const QFont &f)
266 {
267     QString fileName = f.family().toLower() + "_" + QString::number(f.pixelSize())
268                        + "_" + QString::number(f.weight())
269                        + (f.italic() ? "_italic" : "")
270                        + ".qpf2";
271     fileName.replace(QLatin1Char(' '), QLatin1Char('_'));
272     return fileName;
273 }
274 
generate(const QFont & font,int options,const QList<CharacterRange> & ranges,QString * originalFontFile)275 QByteArray QPF::generate(const QFont &font, int options, const QList<CharacterRange> &ranges, QString *originalFontFile)
276 {
277     QTextEngine engine("Test", font);
278     engine.itemize();
279     engine.shape(0);
280     QFontEngine *fontEngine = engine.fontEngine(engine.layoutData->items[0]);
281     if (fontEngine->type() == QFontEngine::Multi)
282         fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(0);
283 
284     if (originalFontFile)
285         *originalFontFile = QFile::decodeName(fontEngine->faceId().filename);
286 
287     return generate(fontEngine, options, ranges);
288 }
289 
generate(QFontEngine * fontEngine,int options,const QList<CharacterRange> & ranges)290 QByteArray QPF::generate(QFontEngine *fontEngine, int options, const QList<CharacterRange> &ranges)
291 {
292     QPF font;
293 
294     font.options = options;
295     font.addHeader(fontEngine);
296     if (options & IncludeCMap)
297         font.addCMap(fontEngine);
298     font.addGlyphs(fontEngine, ranges);
299 
300     return font.qpf;
301 }
302 
addHeader(QFontEngine * fontEngine)303 void QPF::addHeader(QFontEngine *fontEngine)
304 {
305     QFontEngineQPF2::Header *header = reinterpret_cast<QFontEngineQPF2::Header *>(addBytes(sizeof(QFontEngineQPF2::Header)));
306 
307     header->magic[0] = 'Q';
308     header->magic[1] = 'P';
309     header->magic[2] = 'F';
310     header->magic[3] = '2';
311     if (options & RenderGlyphs)
312         header->lock = 0xffffffff;
313     else
314         header->lock = 0;
315     header->majorVersion = QFontEngineQPF2::CurrentMajorVersion;
316     header->minorVersion = QFontEngineQPF2::CurrentMinorVersion;
317     header->dataSize = 0;
318     int oldSize = qpf.size();
319 
320     addTaggedString(QFontEngineQPF2::Tag_FontName, fontEngine->fontDef.family.toUtf8());
321 
322     QFontEngine::FaceId face = fontEngine->faceId();
323     addTaggedString(QFontEngineQPF2::Tag_FileName, face.filename);
324     addTaggedUInt32(QFontEngineQPF2::Tag_FileIndex, face.index);
325 
326     {
327         const QByteArray head = fontEngine->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd'));
328         const quint32 revision = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(head.constData()) + 4);
329         addTaggedUInt32(QFontEngineQPF2::Tag_FontRevision, revision);
330     }
331 
332     addTaggedQFixed(QFontEngineQPF2::Tag_Ascent, fontEngine->ascent());
333     addTaggedQFixed(QFontEngineQPF2::Tag_Descent, fontEngine->descent());
334     addTaggedQFixed(QFontEngineQPF2::Tag_Leading, fontEngine->leading());
335     addTaggedQFixed(QFontEngineQPF2::Tag_XHeight, fontEngine->xHeight());
336     addTaggedQFixed(QFontEngineQPF2::Tag_AverageCharWidth, fontEngine->averageCharWidth());
337     addTaggedQFixed(QFontEngineQPF2::Tag_MaxCharWidth, QFixed::fromReal(fontEngine->maxCharWidth()));
338     addTaggedQFixed(QFontEngineQPF2::Tag_LineThickness, fontEngine->lineThickness());
339     addTaggedQFixed(QFontEngineQPF2::Tag_MinLeftBearing, QFixed::fromReal(fontEngine->minLeftBearing()));
340     addTaggedQFixed(QFontEngineQPF2::Tag_MinRightBearing, QFixed::fromReal(fontEngine->minRightBearing()));
341     addTaggedQFixed(QFontEngineQPF2::Tag_UnderlinePosition, fontEngine->underlinePosition());
342     addTaggedUInt8(QFontEngineQPF2::Tag_PixelSize, fontEngine->fontDef.pixelSize);
343     addTaggedUInt8(QFontEngineQPF2::Tag_Weight, fontEngine->fontDef.weight);
344     addTaggedUInt8(QFontEngineQPF2::Tag_Style, fontEngine->fontDef.style);
345 
346     QByteArray writingSystemBitField = getWritingSystems(fontEngine);
347     if (!writingSystemBitField.isEmpty())
348         addTaggedString(QFontEngineQPF2::Tag_WritingSystems, writingSystemBitField);
349 
350     addTaggedUInt8(QFontEngineQPF2::Tag_GlyphFormat, QFontEngineQPF2::AlphamapGlyphs);
351 
352     addTaggedString(QFontEngineQPF2::Tag_EndOfHeader, QByteArray());
353     align4();
354     header = reinterpret_cast<QFontEngineQPF2::Header *>(qpf.data());
355     header->dataSize = qToBigEndian<quint16>(qpf.size() - oldSize);
356 }
357 
appendBytes(QByteArray & array,int size)358 static uchar *appendBytes(QByteArray &array, int size)
359 {
360     int oldSize = array.size();
361     array.resize(array.size() + size);
362     return reinterpret_cast<uchar *>(array.data() + oldSize);
363 }
364 
365 #define APPEND(type, value) \
366     qToBigEndian<type>(value, appendBytes(cmap, sizeof(type)))
367 
368 struct CMapSegment
369 {
370     int start; // codepoints
371     int end;
372     int startGlyphIndex;
373 };
374 
generateTrueTypeCMap(QFontEngine * fe)375 static QByteArray generateTrueTypeCMap(QFontEngine *fe)
376 {
377     QByteArray cmap;
378     const int glyphCount = fe->glyphCount();
379     if (!glyphCount)
380         return cmap;
381 
382     // cmap header
383     APPEND(quint16, 0); // table version number
384     APPEND(quint16, 1); // number of tables
385 
386     // encoding record
387     APPEND(quint16, 3); // platform-id
388     APPEND(quint16, 10); // encoding-id (ucs-4)
389     const int cmapOffset = cmap.size() + sizeof(quint32);
390     APPEND(quint32, cmapOffset); // offset to sub-table
391 
392     APPEND(quint16, 4); // subtable format
393     const int cmapTableLengthOffset = cmap.size();
394     APPEND(quint16, 0); // length in bytes, will fill in later
395     APPEND(quint16, 0); // language field
396 
397     QList<CMapSegment> segments;
398     CMapSegment currentSegment;
399     currentSegment.start = 0xffff;
400     currentSegment.end = 0;
401     currentSegment.startGlyphIndex = 0;
402     quint32 previousGlyphIndex = 0xfffffffe;
403     bool inSegment = false;
404 
405     QGlyphLayoutArray<1> layout;
406     for (uint uc = 0; uc < 0x10000; ++uc) {
407         QChar ch(uc);
408         int nglyphs = 1;
409 
410         bool validGlyph = fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0)
411                           && nglyphs == 1 && layout.glyphs[0];
412 
413         // leaving a segment?
414         if (inSegment && (!validGlyph || layout.glyphs[0] != previousGlyphIndex + 1)) {
415             Q_ASSERT(currentSegment.start != 0xffff);
416             // store the current segment
417             currentSegment.end = uc - 1;
418             segments.append(currentSegment);
419             currentSegment.start = 0xffff;
420             inSegment = false;
421         }
422         // entering a new segment?
423         if (validGlyph && (!inSegment || layout.glyphs[0] != previousGlyphIndex + 1)) {
424             currentSegment.start = uc;
425             currentSegment.startGlyphIndex = layout.glyphs[0];
426             inSegment = true;
427         }
428 
429         if (validGlyph)
430             previousGlyphIndex = layout.glyphs[0];
431         else
432             previousGlyphIndex = 0xfffffffe;
433     }
434 
435     currentSegment.start = 0xffff;
436     currentSegment.end = 0xffff;
437     currentSegment.startGlyphIndex = 0;
438     segments.append(currentSegment);
439 
440     if (QPF::debugVerbosity > 3)
441         qDebug() << "segments:" << segments.count();
442 
443     Q_ASSERT(!inSegment);
444 
445     const quint16 entrySelector = int(log2(segments.count()));
446     const quint16 searchRange = 2 * (1 << entrySelector);
447     const quint16 rangeShift = segments.count() * 2 - searchRange;
448 
449     if (QPF::debugVerbosity > 3)
450         qDebug() << "entrySelector" << entrySelector << "searchRange" << searchRange
451                  << "rangeShift" << rangeShift;
452 
453     APPEND(quint16, segments.count() * 2); // segCountX2
454     APPEND(quint16, searchRange);
455     APPEND(quint16, entrySelector);
456     APPEND(quint16, rangeShift);
457 
458     // end character codes
459     for (int i = 0; i < segments.count(); ++i)
460         APPEND(quint16, segments.at(i).end);
461 
462     APPEND(quint16, 0); // pad
463 
464     // start character codes
465     for (int i = 0; i < segments.count(); ++i)
466         APPEND(quint16, segments.at(i).start);
467 
468     // id deltas
469     for (int i = 0; i < segments.count(); ++i)
470         APPEND(quint16, segments.at(i).startGlyphIndex - segments.at(i).start);
471 
472     // id range offsets
473     for (int i = 0; i < segments.count(); ++i)
474         APPEND(quint16, 0);
475 
476     uchar *lengthPtr = reinterpret_cast<uchar *>(cmap.data()) + cmapTableLengthOffset;
477     qToBigEndian<quint16>(cmap.size() - cmapOffset, lengthPtr);
478 
479     return cmap;
480 }
481 
addCMap(QFontEngine * fontEngine)482 void QPF::addCMap(QFontEngine *fontEngine)
483 {
484     QByteArray cmapTable = fontEngine->getSfntTable(MAKE_TAG('c', 'm', 'a', 'p'));
485     if (cmapTable.isEmpty())
486         cmapTable = generateTrueTypeCMap(fontEngine);
487     addBlock(QFontEngineQPF2::CMapBlock, cmapTable);
488 }
489 
addGlyphs(QFontEngine * fe,const QList<CharacterRange> & ranges)490 void QPF::addGlyphs(QFontEngine *fe, const QList<CharacterRange> &ranges)
491 {
492     const quint16 glyphCount = fe->glyphCount();
493 
494     QByteArray gmap;
495     gmap.resize(glyphCount * sizeof(quint32));
496     gmap.fill(char(0xff));
497     //qDebug() << "glyphCount" << glyphCount;
498 
499     QByteArray glyphs;
500     if (options & RenderGlyphs) {
501         // this is only a rough estimation
502         glyphs.reserve(glyphCount
503                 * (sizeof(QFontEngineQPF2::Glyph)
504                     + qRound(fe->maxCharWidth() * (fe->ascent() + fe->descent()).toReal())));
505 
506         QGlyphLayoutArray<1> layout;
507 
508         for (CharacterRange range : ranges) {
509             if (debugVerbosity > 2)
510                 qDebug() << "rendering range from" << range.start << "to" << range.end;
511             for (uint uc = range.start; uc < range.end; ++uc) {
512                 QChar ch(uc);
513                 int nglyphs = 1;
514                 if (!fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0))
515                     continue;
516 
517                 if (nglyphs != 1)
518                     continue;
519 
520                 const quint32 glyphIndex = layout.glyphs[0];
521 
522                 if (!glyphIndex)
523                     continue;
524 
525                 Q_ASSERT(glyphIndex < glyphCount);
526 
527                 glyph_metrics_t metrics = fe->boundingBox(glyphIndex);
528                 const bool valid = metrics.width.value() != 0 && metrics.height.value() != 0;
529 
530                 QImage img = valid
531                     ? fe->alphaMapForGlyph(glyphIndex).convertToFormat(QImage::Format_Indexed8)
532                     : QPixmap(0,0).toImage();
533 
534                 const quint32 oldSize = glyphs.size();
535                 glyphs.resize(glyphs.size() + sizeof(QFontEngineQPF2::Glyph) + img.byteCount());
536                 uchar *data = reinterpret_cast<uchar *>(glyphs.data() + oldSize);
537 
538                 uchar *gmapPtr = reinterpret_cast<uchar *>(gmap.data() + glyphIndex * sizeof(quint32));
539                 qToBigEndian(oldSize, gmapPtr);
540 
541                 QFontEngineQPF2::Glyph *glyph = reinterpret_cast<QFontEngineQPF2::Glyph *>(data);
542                 glyph->width = img.width();
543                 glyph->height = img.height();
544                 glyph->bytesPerLine = img.bytesPerLine();
545                 glyph->x = qRound(metrics.x);
546                 glyph->y = qRound(metrics.y);
547                 glyph->advance = qRound(metrics.xoff);
548                 data += sizeof(QFontEngineQPF2::Glyph);
549 
550                 if ((debugVerbosity && (uc >= 'A') && (uc <= 'z')) || debugVerbosity > 1) {
551                     qDebug() << "adding glyph with index" << glyphIndex << " uc =" << char(uc) << ":\n"
552                         << "    glyph->x =" << glyph->x << "rounded from" << metrics.x << "\n"
553                         << "    glyph->y =" << glyph->y << "rounded from" << metrics.y << "\n"
554                         << "    width =" << glyph->width << "height =" << glyph->height
555                         << "    advance =" << glyph->advance << "rounded from" << metrics.xoff
556                         ;
557                 }
558 
559                 memcpy(data, img.bits(), img.byteCount());
560             }
561         }
562     }
563 
564     addBlock(QFontEngineQPF2::GMapBlock, gmap);
565     addBlock(QFontEngineQPF2::GlyphBlock, glyphs);
566 }
567 
addBlock(QFontEngineQPF2::BlockTag tag,const QByteArray & blockData)568 void QPF::addBlock(QFontEngineQPF2::BlockTag tag, const QByteArray &blockData)
569 {
570     addUInt16(tag);
571     addUInt16(0); // padding
572     const int padSize = ((blockData.size() + 3) / 4) * 4 - blockData.size();
573     addUInt32(blockData.size() + padSize);
574     addByteArray(blockData);
575     for (int i = 0; i < padSize; ++i)
576         addUInt8(0);
577 }
578 
579 #define ADD_TAGGED_DATA(tag, qtype, type, value) \
580     addUInt16(tag); \
581     addUInt16(sizeof(qtype)); \
582     add##type(value)
583 
addTaggedString(QFontEngineQPF2::HeaderTag tag,const QByteArray & string)584 void QPF::addTaggedString(QFontEngineQPF2::HeaderTag tag, const QByteArray &string)
585 {
586     addUInt16(tag);
587     addUInt16(string.length());
588     addByteArray(string);
589 }
590 
addTaggedQFixed(QFontEngineQPF2::HeaderTag tag,QFixed value)591 void QPF::addTaggedQFixed(QFontEngineQPF2::HeaderTag tag, QFixed value)
592 {
593     ADD_TAGGED_DATA(tag, quint32, UInt32, value.value());
594 }
595 
addTaggedUInt8(QFontEngineQPF2::HeaderTag tag,quint8 value)596 void QPF::addTaggedUInt8(QFontEngineQPF2::HeaderTag tag, quint8 value)
597 {
598     ADD_TAGGED_DATA(tag, quint8, UInt8, value);
599 }
600 
addTaggedInt8(QFontEngineQPF2::HeaderTag tag,qint8 value)601 void QPF::addTaggedInt8(QFontEngineQPF2::HeaderTag tag, qint8 value)
602 {
603     ADD_TAGGED_DATA(tag, qint8, Int8, value);
604 }
605 
addTaggedUInt16(QFontEngineQPF2::HeaderTag tag,quint16 value)606 void QPF::addTaggedUInt16(QFontEngineQPF2::HeaderTag tag, quint16 value)
607 {
608     ADD_TAGGED_DATA(tag, quint16, UInt16, value);
609 }
610 
addTaggedUInt32(QFontEngineQPF2::HeaderTag tag,quint32 value)611 void QPF::addTaggedUInt32(QFontEngineQPF2::HeaderTag tag, quint32 value)
612 {
613     ADD_TAGGED_DATA(tag, quint32, UInt32, value);
614 }
615 
dump(const QByteArray & qpf)616 void QPF::dump(const QByteArray &qpf)
617 {
618     QPF font;
619     font.qpf = qpf;
620 
621     const uchar *data = reinterpret_cast<const uchar *>(qpf.constData());
622     const uchar *endPtr = reinterpret_cast<const uchar *>(qpf.constData() + qpf.size());
623     data = font.dumpHeader(data);
624 
625     const quint32 *gmap = 0;
626     quint32 glyphCount = 0;
627 
628     while (data < endPtr) {
629         const QFontEngineQPF2::Block *block = reinterpret_cast<const QFontEngineQPF2::Block *>(data);
630         quint32 tag = qFromBigEndian(block->tag);
631         quint32 blockSize = qFromBigEndian(block->dataSize);
632         qDebug() << "Block: Tag =" << qFromBigEndian(block->tag) << "; Size =" << blockSize << "; Offset =" << Qt::hex << data - reinterpret_cast<const uchar *>(qpf.constData());
633         data += sizeof(QFontEngineQPF2::Block);
634 
635         if (debugVerbosity) {
636             if (tag == QFontEngineQPF2::GMapBlock) {
637                 gmap = reinterpret_cast<const quint32 *>(data);
638                 glyphCount = blockSize / 4;
639                 font.dumpGMapBlock(gmap, glyphCount);
640             } else if (tag == QFontEngineQPF2::GlyphBlock
641                        && gmap && debugVerbosity > 1) {
642                 font.dumpGlyphBlock(gmap, glyphCount, data, data + blockSize);
643             }
644         }
645 
646         data += blockSize;
647     }
648 }
649 
dumpHeader(const uchar * data)650 const uchar *QPF::dumpHeader(const uchar *data)
651 {
652     const QFontEngineQPF2::Header *header = reinterpret_cast<const QFontEngineQPF2::Header *>(data);
653     qDebug() << "Header:";
654     qDebug() << "magic ="
655              << header->magic[0]
656              << header->magic[1]
657              << header->magic[2]
658              << header->magic[3];
659     qDebug() << "lock =" << qFromBigEndian(header->lock);
660     qDebug() << "majorVersion =" << header->majorVersion;
661     qDebug() << "minorVersion =" << header->minorVersion;
662     qDebug() << "dataSize =" << qFromBigEndian(header->dataSize);
663 
664     data += sizeof(QFontEngineQPF2::Header);
665 
666     const uchar *endPtr = data + qFromBigEndian(header->dataSize);
667 
668     while (data && data < endPtr) {
669         data = dumpHeaderTag(data);
670     }
671 
672     return endPtr;
673 }
674 
dumpHeaderTag(const uchar * data)675 const uchar *QPF::dumpHeaderTag(const uchar *data)
676 {
677     const QFontEngineQPF2::Tag *tagPtr = reinterpret_cast<const QFontEngineQPF2::Tag *>(data);
678     quint16 tag = qFromBigEndian(tagPtr->tag);
679     quint16 size = qFromBigEndian(tagPtr->size);
680 
681     qDebug() << "Tag =" << tag << headerTagNames[tag];
682     qDebug() << "Size =" << size;
683 
684     if (tag == QFontEngineQPF2::Tag_EndOfHeader)
685         return 0;
686 
687     data += sizeof(QFontEngineQPF2::Tag);
688 
689     Q_ASSERT(tag < QFontEngineQPF2::NumTags);
690 
691     switch (tagTypes[tag]) {
692         case QFontEngineQPF2::StringType:
693             qDebug() << "Payload =" << QString::fromUtf8(QByteArray(reinterpret_cast<const char *>(data), size));
694             break;
695         case QFontEngineQPF2::FixedType:
696             Q_ASSERT(size == sizeof(quint32));
697             qDebug() << "Payload =" << QFixed::fromFixed(qFromBigEndian<quint32>(data)).toReal();
698             break;
699         case QFontEngineQPF2::UInt8Type:
700             Q_ASSERT(size == sizeof(quint8));
701             qDebug() << "Payload =" << *data;
702             break;
703         case QFontEngineQPF2::UInt32Type:
704             Q_ASSERT(size == sizeof(quint32));
705             qDebug() << "Payload =" << qFromBigEndian<quint32>(data);
706             break;
707         case QFontEngineQPF2::BitFieldType: {
708             QByteArray bits(reinterpret_cast<const char *>(data), size);
709             qDebug() << "Payload =" << stringify(bits);
710             if (QPF::debugVerbosity > 2 && tag == QFontEngineQPF2::Tag_WritingSystems)
711                 dumpWritingSystems(bits);
712             } break;
713     }
714 
715     data += size;
716     return data;
717 }
718 
dumpGMapBlock(const quint32 * gmap,int glyphCount)719 void QPF::dumpGMapBlock(const quint32 *gmap, int glyphCount)
720 {
721     qDebug() << "glyphCount =" << glyphCount;
722     int renderedGlyphs = 0;
723     for (int i = 0; i < glyphCount; ++i) {
724         if (gmap[i] != 0xffffffff) {
725             const quint32 glyphPos = qFromBigEndian(gmap[i]);
726             qDebug("gmap[%d] = 0x%x / %u", i, glyphPos, glyphPos);
727             ++renderedGlyphs;
728         }
729     }
730     qDebug() << "Glyphs rendered:" << renderedGlyphs << "; Glyphs missing from the font:" << glyphCount - renderedGlyphs;
731 }
732 
dumpGlyphBlock(const quint32 * gmap,int glyphCount,const uchar * data,const uchar * endPtr)733 void QPF::dumpGlyphBlock(const quint32 *gmap, int glyphCount, const uchar *data, const uchar *endPtr)
734 {
735     // glyphPos -> glyphIndex
736     QMap<quint32, quint32> reverseGlyphMap;
737     for (int i = 0; i < glyphCount; ++i) {
738         if (gmap[i] == 0xffffffff)
739             continue;
740         const quint32 glyphPos = qFromBigEndian(gmap[i]);
741         reverseGlyphMap[glyphPos] = i;
742     }
743 
744     const uchar *glyphBlockBegin = data;
745     while (data < endPtr) {
746         const QFontEngineQPF2::Glyph *g = reinterpret_cast<const QFontEngineQPF2::Glyph *>(data);
747 
748         const quint64 glyphOffset = data - glyphBlockBegin;
749         const quint32 glyphIndex = reverseGlyphMap.value(glyphOffset, 0xffffffff);
750 
751         if (glyphIndex == 0xffffffff)
752             qDebug() << "############: Glyph present in glyph block is not listed in glyph map!";
753         qDebug("glyph at offset 0x%x glyphIndex = %u", quint32(glyphOffset), glyphIndex);
754         qDebug() << "    width =" << g->width << "height =" << g->height << "x =" << g->x << "y =" << g->y;
755         qDebug() << "    advance =" << g->advance << "bytesPerLine =" << g->bytesPerLine;
756 
757         data += sizeof(*g);
758         if (glyphIndex == 0xffffffff || debugVerbosity > 4) {
759             dumpGlyph(data, g);
760         }
761 
762         data += g->height * g->bytesPerLine;
763     }
764 }
765 
dumpGlyph(const uchar * data,const QFontEngineQPF2::Glyph * glyph)766 void QPF::dumpGlyph(const uchar *data, const QFontEngineQPF2::Glyph *glyph)
767 {
768     fprintf(stderr, "---- glyph data:\n");
769     const char *alphas = " .o#";
770     for (int y = 0; y < glyph->height; ++y) {
771         for (int x = 0; x < glyph->width; ++x) {
772             const uchar value = data[y * glyph->bytesPerLine + x];
773             fprintf(stderr, "%c", alphas[value >> 6]);
774         }
775         fprintf(stderr, "\n");
776     }
777     fprintf(stderr, "----\n");
778 }
779 
780 QT_END_NAMESPACE
781