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