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