Subversion Repositories Scribus

Rev

Rev 24696 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
24469 jghali 1
//
2
//  sfnt.cpp
3
//  Scribus
4
//
5
//  Created by Andreas Vox on 18.04.15.
6
//
7
//
8
 
9
#include "fonts/sfnt.h"
10
#include "fonts/sfnt_format.h"
11
 
12
#include FT_TRUETYPE_TABLES_H
13
#include FT_TRUETYPE_TAGS_H
14
#include FT_TRUETYPE_IDS_H
15
 
16
#include <QDebug>
17
#include <QScopedPointer>
18
 
19
#include "scconfig.h"
20
 
24464 jghali 21
#include <harfbuzz/hb.h>
24469 jghali 22
#ifdef HAVE_HARFBUZZ_SUBSET
23
#include <harfbuzz/hb-subset.h>
24
#endif
25
 
24464 jghali 26
struct HbBlobDeleter
27
{
28
        static void cleanup(void *pointer)
29
        {
30
                if (pointer)
31
                        hb_blob_destroy((hb_blob_t*) pointer);
32
        }
33
};
34
 
35
struct HbFaceDeleter
36
{
37
        static void cleanup(void *pointer)
38
        {
39
                if (pointer)
40
                        hb_face_destroy((hb_face_t*) pointer);
41
        }
42
};
43
 
24469 jghali 44
#ifdef HAVE_HARFBUZZ_SUBSET
24464 jghali 45
struct HbSubsetInputDeleter
46
{
47
        static void cleanup(void *pointer)
48
        {
49
                if (pointer)
50
                        hb_subset_input_destroy((hb_subset_input_t*) pointer);
51
        }
24469 jghali 52
};
53
#endif
54
 
55
namespace sfnt {
56
 
57
        uchar byte(const QByteArray & bb, uint pos)
58
        {
59
                const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
60
                return pp[0];
61
        }
62
 
63
        quint32 word(const QByteArray & bb, uint pos)
64
        {
65
                const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
66
                return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3];
67
        }
68
 
69
        void putWord(QByteArray & bb, uint pos, quint32 val)
70
        {
71
                unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
72
                *pp++ = (val >> 24) & 0xFF;
73
                *pp++ = (val >> 16) & 0xFF;
74
                *pp++ = (val >> 8) & 0xFF;
75
                *pp++ = (val) & 0xFF;
76
        }
77
 
78
        void appendWord(QByteArray & bb, quint32 val)
79
        {
80
                uint pos = bb.size();
81
                bb.resize(pos + 4);
82
                putWord(bb, pos, val);
83
        }
84
 
85
        quint16 word16(const QByteArray & bb, uint pos)
86
        {
87
                const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
88
                return pp[0] << 8 | pp[1];
89
        }
90
 
91
        void putWord16(QByteArray & bb, uint pos, quint16 val)
92
        {
93
                unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
94
                *pp++ = (val >> 8) & 0xFF;
95
                *pp++ = (val) & 0xFF;
96
        }
97
 
98
        void appendWord16(QByteArray & bb, quint16 val)
99
        {
100
                uint pos = bb.size();
101
                bb.resize(pos + 2);
102
                putWord16(bb, pos, val);
103
        }
104
 
105
        QByteArray tag(const QByteArray& bb, uint pos)
106
        {
107
                return QByteArray::fromRawData(bb.constData() + pos, 4);
108
        }
109
 
110
        QByteArray tag(uint word)
111
        {
112
                QByteArray result;
113
                result.resize(4);
114
                result[0] = (word >> 24) & 0xFF;
115
                result[1] = (word >> 16) & 0xFF;
116
                result[2] = (word >> 8) & 0xFF;
117
                result[3] = (word) & 0xFF;
118
                return result;
119
        }
120
 
121
        bool copy(QByteArray & dst, uint to, const QByteArray & src, uint from, uint len)
122
        {
123
                if (!dst.data())
124
                        return false;
125
                if (!src.data())
126
                        return false;
127
                if (to + len > static_cast<uint>(dst.size()))
128
                        return false;
129
                if (from + len > static_cast<uint>(src.size()))
130
                        return false;
131
 
132
                memcpy(dst.data() + to, src.data() + from, len);
133
                return true;
134
        }
135
 
136
        const uint post_format10_names_count = 258;
137
 
138
        static const char* post_format10_names[] = {
139
                ".notdef",
140
                ".null",
141
                "nonmarkingreturn",
142
                "space",
143
                "exclam",
144
                "quotedbl",
145
                "numbersign",
146
                "dollar",
147
                "percent",
148
                "ampersand",
149
                "quotesingle",
150
                "parenleft",
151
                "parenright",
152
                "asterisk",
153
                "plus",
154
                "comma",
155
                "hyphen",
156
                "period",
157
                "slash",
158
                "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
159
                "colon",
160
                "semicolon",
161
                "less",
162
                "equal",
163
                "greater",
164
                "question",
165
                "at",
166
                "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
167
                "bracketleft",
168
                "backslash",
169
                "bracketright",
170
                "asciicircum",
171
                "underscore",
172
                "grave",
173
                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
174
                "braceleft",
175
                "bar",
176
                "braceright",
177
                "asciitilde",
178
                "Adieresis",
179
                "Aring",
180
                "Ccedilla",
181
                "Eacute",
182
                "Ntilde",
183
                "Odieresis",
184
                "Udieresis",
185
                "aacute",
186
                "agrave",
187
                "acircumflex",
188
                "adieresis",
189
                "atilde",
190
                "aring",
191
                "ccedilla",
192
                "eacute",
193
                "egrave",
194
                "ecircumflex",
195
                "edieresis",
196
                "iacute",
197
                "igrave",
198
                "icircumflex",
199
                "idieresis",
200
                "ntilde",
201
                "oacute",
202
                "ograve",
203
                "ocircumflex",
204
                "odieresis",
205
                "otilde",
206
                "uacute",
207
                "ugrave",
208
                "ucircumflex",
209
                "udieresis",
210
                "dagger",
211
                "degree",
212
                "cent",
213
                "sterling",
214
                "section",
215
                "bullet",
216
                "paragraph",
217
                "germandbls",
218
                "registered",
219
                "copyright",
220
                "trademark",
221
                "acute",
222
                "dieresis",
223
                "notequal",
224
                "AE",
225
                "Oslash",
226
                "infinity",
227
                "plusminus",
228
                "lessequal",
229
                "greaterequal",
230
                "yen",
231
                "mu",
232
                "partialdiff",
233
                "summation",
234
                "product",
235
                "pi",
236
                "integral",
237
                "ordfeminine",
238
                "ordmasculine",
239
                "Omega",
240
                "ae",
241
                "oslash",
242
                "questiondown",
243
                "exclamdown",
244
                "logicalnot",
245
                "radical",
246
                "florin",
247
                "approxequal",
248
                "Delta",
249
                "guillemotleft",
250
                "guillemotright",
251
                "ellipsis",
252
                "nonbreakingspace",
253
                "Agrave",
254
                "Atilde",
255
                "Otilde",
256
                "OE",
257
                "oe",
258
                "endash",
259
                "emdash",
260
                "quotedblleft",
261
                "quotedblright",
262
                "quoteleft",
263
                "quoteright",
264
                "divide",
265
                "lozenge",
266
                "ydieresis",
267
                "Ydieresis",
268
                "fraction",
269
                "currency",
270
                "guilsinglleft",
271
                "guilsinglright",
272
                "fi",
273
                "fl",
274
                "daggerdbl",
275
                "periodcentered",
276
                "quotesinglbase",
277
                "quotedblbase",
278
                "perthousand",
279
                "Acircumflex",
280
                "Ecircumflex",
281
                "Aacute",
282
                "Edieresis",
283
                "Egrave",
284
                "Iacute",
285
                "Icircumflex",
286
                "Idieresis",
287
                "Igrave",
288
                "Oacute",
289
                "Ocircumflex",
290
                "apple",
291
                "Ograve",
292
                "Uacute",
293
                "Ucircumflex",
294
                "Ugrave",
295
                "dotlessi",
296
                "circumflex",
297
                "tilde",
298
                "macron",
299
                "breve",
300
                "dotaccent",
301
                "ring",
302
                "cedilla",
303
                "hungarumlaut",
304
                "ogonek",
305
                "caron",
306
                "Lslash",
307
                "lslash",
308
                "Scaron",
309
                "scaron",
310
                "Zcaron",
311
                "zcaron",
312
                "brokenbar",
313
                "Eth",
314
                "eth",
315
                "Yacute",
316
                "yacute",
317
                "Thorn",
318
                "thorn",
319
                "minus",
320
                "multiply",
321
                "onesuperior",
322
                "twosuperior",
323
                "threesuperior",
324
                "onehalf",
325
                "onequarter",
326
                "threequarters",
327
                "franc",
328
                "Gbreve",
329
                "gbreve",
330
                "Idotaccent",
331
                "Scedilla",
332
                "scedilla",
333
                "Cacute",
334
                "cacute",
335
                "Ccaron",
336
                "ccaron",
337
                "dcroat"
338
        };
339
 
340
        bool PostTable::usable() const
341
        {
342
                return m_usable;
343
        }
344
 
345
        void PostTable::setUsable(bool usable)
346
        {
347
                m_usable = usable;
348
        }
349
 
350
        QString PostTable::errorMsg() const
351
        {
352
                return m_errorMsg;
353
        }
354
 
355
        void PostTable::setErrorMsg(const QString& errorMsg)
356
        {
357
                m_errorMsg = errorMsg;
358
        }
359
 
360
        uint PostTable::numberOfGlyphs() const
361
        {
362
                if (m_names.length() > 0)
363
                        return m_names.length();
364
                return post_format10_names_count;
365
        }
366
 
367
        QString PostTable::nameFor(uint glyph) const
368
        {
369
                if (glyph < (uint) m_names.length())
370
                        return m_names[glyph];
371
                if (glyph < sfnt::post_format10_names_count)
372
                        return post_format10_names[glyph];
373
                return ".notdef";
374
        }
375
 
376
        void PostTable::readFrom(FT_Face face)
377
        {
378
                QByteArray postData;
379
                FT_ULong size = 0;
380
                int error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, nullptr, &size );
381
                //qDebug() << "load post" << error << size;
382
                if (error || size == 0)
383
                {
384
                        m_errorMsg = "no post table";
385
                        m_usable = false;
386
                        return;
387
                }
388
                postData.resize(size);
389
                error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, reinterpret_cast<FT_Byte*>(postData.data()), &size );
390
                if (error)
391
                {
392
                        m_errorMsg = "can't load post table";
393
                        m_usable = false;
394
                        return;
395
                }
396
 
397
                switch (sfnt::word(postData, ttf_post_format))
398
                {
399
                        case sfnt::post_format10:
400
                                m_usable = true;
401
                                m_names.clear();
402
                                return;
403
                        case sfnt::post_format20:
404
                                break;
405
                        case sfnt::post_format30:
406
                                m_errorMsg = QString("post table has no glyph names");
407
                                m_usable = false;
408
                                return;
409
                        case sfnt::post_format25:
410
                        case sfnt::post_format40:
411
                        default:
412
                                m_errorMsg = QString("unsupported post format %1").arg(sfnt::word(postData,0));
413
                                m_usable = false;
414
                                return;
415
 
416
                }
417
                QMap<QString,uint> usedNames;
418
                QList<QByteArray> pascalStrings;
419
 
420
                uint nrOfGlyphs = sfnt::word16(postData, ttf_post_header_length);
421
                uint stringPos = ttf_post_header_length + 2 + 2 * nrOfGlyphs;
422
                while (stringPos < (uint) postData.length())
423
                {
424
                        int strLen = byte(postData, stringPos);
425
                        ++stringPos;
426
                        pascalStrings.append(postData.mid(stringPos, strLen));
427
                        stringPos += strLen;
428
                }
429
                uint pos = ttf_post_header_length + 2;
430
                for (uint gid = 0; gid < nrOfGlyphs; ++gid)
431
                {
432
                        uint nameIndex = sfnt::word16(postData, pos);
433
                        pos += 2;
434
                        QString name;
435
                        if (nameIndex < sfnt::post_format10_names_count)
436
                                name = sfnt::post_format10_names[nameIndex];
437
                        else if (nameIndex < pascalStrings.length() + sfnt::post_format10_names_count)
438
                                name = pascalStrings[nameIndex - sfnt::post_format10_names_count];
439
                        else {
440
                                m_usable = false;
441
                                m_errorMsg = QString("missing name %1 for glyph %2").arg(nameIndex).arg(gid);
442
                                return;
443
                        }
444
                        if (name != ".notdef" && name[0] != QChar(0) && usedNames.contains(name))
445
                        {
446
                                m_usable = false;
447
                                m_errorMsg = QString("duplicate name %1 used for glyphs %2 and %3").arg(name).arg(gid).arg(usedNames[name]);
448
                                return;
449
                        }
450
                        usedNames[name] = gid;
451
                        m_names.append(name);
452
                }
453
                m_errorMsg = "";
454
                m_usable = true;
455
        }
456
 
457
        int copyTable(QByteArray& ttf, uint destDirEntry, uint pos, const QByteArray& source, uint dirEntry)
458
        {
459
                FT_ULong tag = word(source, dirEntry + ttf_TableRecord_tag);
460
                uint checksum = word(source, dirEntry + ttf_TableRecord_checkSum);
461
                uint tableStart = word(source, dirEntry + ttf_TableRecord_offset);
462
                uint tableSize  = word(source, dirEntry + ttf_TableRecord_length);
463
 
464
                if (!copy(ttf, pos, source, tableStart, tableSize))
465
                        return -1;
466
 
467
                putWord(ttf, destDirEntry + ttf_TableRecord_tag, tag);
468
                putWord(ttf, destDirEntry + ttf_TableRecord_checkSum, checksum);
469
                putWord(ttf, destDirEntry + ttf_TableRecord_offset, pos);
470
                putWord(ttf, destDirEntry + ttf_TableRecord_length, tableSize);
471
 
472
                return tableSize;
473
        }
474
 
475
        QByteArray extractFace(const QByteArray& coll, int faceIndex)
476
        {
477
                QByteArray result;
478
 
479
                const int numFonts = word(coll, ttc_numFonts);
480
                if (faceIndex >= static_cast<int>(numFonts))
481
                {
482
                        return result;
483
                }
484
 
485
                uint faceOffset = sfnt::word(coll, ttc_OffsetTables + 4 * faceIndex);
486
                uint nTables    = sfnt::word16(coll, faceOffset + ttf_numtables);
487
 
488
                //qDebug() << QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg("collection").arg(faceOffset).arg(nTables);
489
 
490
                uint headerLength = ttf_TableRecords + ttf_TableRecord_Size * nTables;
491
 
492
                uint tableLengths = 0;
493
                // sum table lengths incl padding
494
                for (uint i=0; i < nTables; ++i)
495
                {
496
                        tableLengths += sfnt::word(coll, faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i + ttf_TableRecord_length);
497
                        tableLengths = (tableLengths+3) & ~3;
498
                }
499
                result.resize(headerLength + tableLengths);
500
                if (!result.data())
501
                {
502
                        result.resize(0);
503
                        return result;
504
                }
505
 
506
                // write header
507
                //              sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength));
508
                if (!copy(result, 0, coll, faceOffset, headerLength))
509
                {
510
                        result.resize(0);
511
                        return result;
512
                }
513
                uint pos = headerLength;
514
                for (uint i=0; i < nTables; ++i)
515
                {
516
                        uint sourceDirEntry = faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i;
517
                        uint destDirEntry = ttf_TableRecords + ttf_TableRecord_Size * i;
518
 
519
                        int tableSize = copyTable(result, destDirEntry, pos, coll, sourceDirEntry);
520
                        if (tableSize < 0)
521
                        {
522
                                result.resize(0);
523
                                return result;
524
                        }
525
                        pos += tableSize;
526
 
527
                        // pad
528
                        while ((pos & 3) != 0)
529
                                result.data()[pos++] = '\0';
530
                }
531
                return result;
532
        }
533
 
534
        uint getTableDirEntry(const QByteArray& ttf, const QByteArray& ttfTag)
535
        {
536
                uint nTables = word16(ttf, ttf_numtables);
537
                uint pos = ttf_TableRecords;
538
                for (uint i=0; i < nTables; ++i)
539
                {
540
                        if (ttfTag == tag(word(ttf, pos + ttf_TableRecord_tag)))
541
                        {
542
                                return pos;
543
                        }
544
                        pos += ttf_TableRecord_Size;
545
                }
546
                return 0;
547
        }
548
 
549
        QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag)
550
        {
551
                uint pos = getTableDirEntry(ttf, ttfTag);
552
                if (pos <= 0)
553
                        return QByteArray();
554
 
555
                uint offset = word(ttf, pos + ttf_TableRecord_offset);
556
                uint length = word(ttf, pos + ttf_TableRecord_length);
557
                return QByteArray::fromRawData(ttf.constData() + offset, length);
558
        }
559
 
560
        QByteArray createTableDir(const QList<QByteArray>& tags)
561
        {
562
                QByteArray result;
563
                uint numTables = tags.length();
564
                uint tableRecordsSize = numTables * ttf_TableRecord_Size;
565
                result.resize(ttf_TableRecords +  tableRecordsSize);
566
                uint entrySelector = 0;
567
                uint searchRange = ttf_TableRecord_Size;
568
                while (2*searchRange < tableRecordsSize)
569
                {
570
                        searchRange *= 2;
571
                        ++entrySelector;
572
                }
573
                uint rangeShift = tableRecordsSize - searchRange;
574
 
575
                putWord(result, ttf_sfnt_version, 0x00010000);
576
                putWord16(result, ttf_numtables, numTables);
577
                putWord16(result, ttf_searchRange, searchRange);
578
                putWord16(result, ttf_entrySelector, entrySelector);
579
                putWord16(result, ttf_rangeShift, rangeShift);
580
                for (uint i = 0; i < numTables; ++i)
581
                {
582
                        copy(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_tag, tags[i], 0, 4);
583
                        putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_checkSum, 0);
584
                        putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_offset, 0);
585
                        putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size +ttf_TableRecord_length, 0);
586
                }
587
                return result;
588
        }
589
 
590
        quint32 calcTableChecksum(QByteArray& table)
591
        {
592
                quint32 Sum = 0L;
593
                for (int pos = 0; pos < table.length(); pos += 4)
594
                        Sum += word(table, pos);
595
                return Sum;
596
        }
597
 
598
        void writeTable(QByteArray& ttf, const QByteArray& tag, QByteArray& table)
599
        {
600
                //qDebug() << "writing table" << tag << table.size() << "@" << ttf.size();
601
                uint length = table.size();
602
                while (table.size() & 0x3)
603
                        table.append('\0');
604
                uint offset = ttf.size();
605
                uint checksum = calcTableChecksum(table);
606
                uint pos = getTableDirEntry(ttf, tag);
607
                putWord(ttf, pos + ttf_TableRecord_checkSum, checksum);
608
                putWord(ttf, pos + ttf_TableRecord_offset, offset);
609
                putWord(ttf, pos + ttf_TableRecord_length, length);
610
                ttf.append(table);
611
        }
612
 
613
        bool hasLongLocaFormat(const QByteArray& ttf)
614
        {
615
                const QByteArray head = getTable(ttf, "head");
616
                uint idxToLocFormat = word16(head, ttf_head_indexToLocFormat);
617
                //              qDebug() << "loca format:" << (void*)idxToLocFormat;
618
                return idxToLocFormat == 1;
619
        }
620
 
621
        QList<quint32> readLoca(const QByteArray& ttf)
622
        {
623
                QList<quint32> result;
624
                const QByteArray loca = getTable(ttf, "loca");
625
                result.reserve(loca.length());
626
                if (hasLongLocaFormat(ttf))
627
                {
628
                        for (int i = 0; i < loca.length(); i+=4)
629
                                result.append(word(loca, i));
630
                }
631
                else
632
                {
633
                        for (int i = 0; i < loca.length(); i+=2)
634
                                result.append(word16(loca, i) * 2);
635
                }
636
                return result;
637
        }
638
 
639
        QByteArray writeLoca(const QList<uint>& loca, bool longFormat)
640
        {
641
                QByteArray result;
642
                if (longFormat)
643
                {
644
                        for (int i=0; i < loca.length(); ++i)
645
                                appendWord(result, loca[i]);
646
                }
647
                else
648
                {
649
                        for (int i=0; i < loca.length(); ++i)
650
                                appendWord16(result, loca[i] / 2);
651
                }
652
                return result;
653
        }
654
 
655
        QList<std::pair<qint16,quint16> > readHmtx(const QByteArray& ttf)
656
        {
657
                QList<std::pair<qint16,quint16> > result;
658
                const QByteArray hhea = getTable(ttf, "hhea");
659
                const QByteArray hmtx = getTable(ttf, "hmtx");
660
                uint endOfLongHorMetrics = 4 * word16(hhea, ttf_hhea_numOfLongHorMetrics);
661
                qint16 advance;
662
                quint16 leftSideBearing;
663
                uint pos = 0;
664
                while (pos < endOfLongHorMetrics)
665
                {
666
                        advance = word16(hmtx, pos);
667
                        leftSideBearing = word16(hmtx, pos+2);
668
                        //qDebug() << pos << "hmtx" << advance << leftSideBearing;
669
                        result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
670
                        pos += 4;
671
                }
672
                while (pos < (uint) hmtx.length())
673
                {
674
                        leftSideBearing = word16(hmtx, pos);
675
                        //qDebug() << pos << "hmtx =" << advance << leftSideBearing;
676
                        result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
677
                        pos += 2;
678
                }
679
                return result;
680
        }
681
 
682
        QByteArray writeHmtx(const QList<std::pair<qint16,quint16> >& longHorMetrics)
683
        {
684
                QByteArray result;
685
                QList<std::pair<qint16,quint16> >::const_iterator it;
686
                //int i = 0;
687
                for (it = longHorMetrics.cbegin(); it < longHorMetrics.cend(); ++it)
688
                {
689
                        //qDebug() << "hmtx" << i++ << it->first << it->second;
690
                        appendWord16(result, it->first);
691
                        appendWord16(result, it->second);
692
                }
693
                return result;
694
        }
695
 
696
        QMap<uint, uint> readCMap(const QByteArray& ttf)
697
        {
698
                QMap<uint,uint> result;
699
                const QByteArray cmaps = getTable(ttf, "cmap");
700
 
701
                uint numSubtables = word16(cmaps, ttf_cmap_numberSubtables);
702
                uint startOfUnicodeTable = 0;
703
                uint startOfSymbolTable = 0;
704
                uint unicodeFormat = 0, symbolFormat = 0;
705
                uint pos = ttf_cmap_encodings;
706
                for (uint i = 0; i < numSubtables; ++i)
707
                {
708
                        uint platform = word16(cmaps, pos + ttf_cmap_encoding_platformID);
709
                        uint encoding = word16(cmaps, pos + ttf_cmap_encoding_platformSpecificID);
710
                        uint offset = word(cmaps, pos + ttf_cmap_encoding_offset);
711
                        uint format = word16(cmaps, offset + ttf_cmapx_format);
712
                        pos += ttf_cmap_encoding_Size;
713
 
714
                        if (format < 4 || format > 12)
715
                                continue;
716
                        if (platform == 0 || (platform == 3 && encoding == 1))
717
                        {
718
                                startOfUnicodeTable = offset;
719
                                unicodeFormat = format;
720
                                break;
721
                        }
722
                        if (platform == 3 && encoding == 0) // MS Symbol cmap
723
                        {
724
                                startOfSymbolTable = offset;
725
                                symbolFormat = format;
726
                                continue;
727
                        }
728
                        format = 1; // no such format
729
                }
730
                // If no unicode cmap was found, fallback on ms symbol cmap
731
                uint format = unicodeFormat;
732
                if ((unicodeFormat <= 0) && (symbolFormat > 0))
733
                {
734
                        startOfUnicodeTable = startOfSymbolTable;
735
                        format = symbolFormat;
736
                }
737
                //qDebug() << "reading cmap format" << format;
738
                switch (format)
739
                {
740
                        case 4:
741
                                {
742
                                        uint segCount2 = word16(cmaps, startOfUnicodeTable + ttf_cmap4_segCountX2);
743
                                        uint endCodes = startOfUnicodeTable + ttf_cmap4_EndCodes;
744
                                        uint startCodes = endCodes + segCount2 + ttf_cmap4_StartCodes_EndCodes;
745
                                        uint idDeltas = startCodes + segCount2 + ttf_cmap4_IdDeltas_StartCodes;
746
                                        uint idRangeOffsets = idDeltas + segCount2 + ttf_cmap4_IdRangeOffsets_IdDeltas;
747
                                        //uint glyphIndexArray = idRangeOffsets + segCount2 + ttf_cmap4_GlyphIds_IdRangeOffsets;
748
 
749
                                        for (uint seg = 0; seg < segCount2; seg+=2)
750
                                        {
751
                                                uint start = word16(cmaps, startCodes + seg);
752
                                                uint end = word16(cmaps, endCodes + seg);
753
                                                uint idDelta = word16(cmaps, idDeltas + seg);
754
                                                uint idRangeOffset = word16(cmaps, idRangeOffsets + seg);
755
                                                for (uint c = start; c <= end; ++c)
756
                                                {
757
                                                        quint16 glyph;
758
                                                        if (idRangeOffset > 0)
759
                                                        {
760
                                                                uint glyphIndexAdress = idRangeOffset + 2*(c-start) + (idRangeOffsets + seg);
761
                                                                glyph = word16(cmaps, glyphIndexAdress);
762
                                                                if (glyph != 0)
763
                                                                        glyph += idDelta;
764
                                                        }
765
                                                        else
766
                                                        {
767
                                                                glyph = c + idDelta;
768
                                                        }
769
                                                        if (!result.contains(c))
770
                                                        {
771
                                                                // search would always find the one in the segment with the lower endcode, i.e. earlier segment
772
                                                                if (c < 256 || glyph == 0)
773
                                                                        //qDebug() << "(" << QChar(c) << "," << glyph << ")";
774
                                                                        result[c] = glyph;
775
                                                        }
776
                                                        else
777
                                                        {
778
                                                                // nothing to do. No idea if fonts with overlapping cmap4 segments exist, though.
779
                                                        }
780
                                                }
781
                                        }
782
                                        break;
783
                                }
784
                        case 6:
785
                                {
786
                                        uint firstCode = word16(cmaps, startOfUnicodeTable + ttf_cmap6_firstCode);
787
                                        uint count = word16(cmaps, startOfUnicodeTable + ttf_cmap6_entryCount);
788
                                        pos = word16(cmaps, startOfUnicodeTable + ttf_cmap6_glyphIndexArray);
789
                                        for (uint i = 0; i < count; ++i)
790
                                        {
791
                                                result[firstCode + i] = word16(cmaps, pos);
792
                                                pos += 2;
793
                                        }
794
                                        break;
795
                                }
796
                        case 12:
797
                                {
798
                                        uint nGroups = word(cmaps, startOfUnicodeTable + ttf_cmap12_nGroups);
799
                                        pos = startOfUnicodeTable + ttf_cmap12_Groups;
800
                                        for (uint grp = 0; grp < nGroups; ++grp)
801
                                        {
802
                                                uint start = word(cmaps, pos + ttf_cmap12_Group_startCharCode);
803
                                                uint end = word(cmaps, pos + ttf_cmap12_Group_endCharCode);
804
                                                uint gid = word(cmaps, pos + ttf_cmap12_Group_startGlyphCode);
805
                                                for (uint c = start; c <= end; ++c)
806
                                                {
807
                                                        result[c] = gid;
808
                                                        ++gid;
809
                                                }
810
                                                pos += ttf_cmap12_Group_Size;
811
                                        }
812
                                        break;
813
                                }
814
                        default:
815
                                {
816
                                        qDebug() << "unsupported cmap format" << format;
817
                                        break;
818
                                }
819
                }
820
                return result;
821
        }
822
 
823
        QByteArray writeCMap(const QMap<uint, uint>& cmap)
824
        {
825
                // we always write only one table: platform=3(MS), encoding=1(Unicode 16bit)
826
                QByteArray result;
827
                appendWord16(result, 0); // version
828
                appendWord16(result, 1); // number of subtables
829
                appendWord16(result, 3); // platformID Microsoft
830
                appendWord16(result, 1); // encodingID UnicodeBMP
831
                appendWord(result, result.size() + 4); // offset
832
 
833
                // find the segments
834
 
835
                QList<uint> chars;
836
                QMap<uint, uint>::ConstIterator cit;
837
                //qDebug() << "writing cmap";
838
                bool cmapHasData = false;
839
                for (cit = cmap.cbegin(); cit != cmap.cend(); ++cit)
840
                {
841
                        uint ch = cit.key();
842
                        if (!QChar::requiresSurrogates(ch) && cit.value() != 0)
843
                        {
844
                                //qDebug() << "(" << QChar(cit.key()) << "," << cit.value() << ")";
845
                                chars.append(ch);
846
                                cmapHasData = true;
847
                        }
848
                        //                      qDebug() << QChar(ch) << QChar::requiresSurrogates(ch) << cit.value();
849
                }
850
                std::sort(chars.begin(), chars.end());
851
 
852
                QList<quint16> startCodes;
853
                QList<quint16> endCodes;
854
                QList<quint16> idDeltas;
855
                QList<quint16> rangeOffsets;
856
                if (cmapHasData)
857
                {
858
                        uint pos = 0;
859
                        do {
860
                                quint16 start = chars[pos];
861
                                quint16 delta = cmap[start] - start;
862
                                quint16 rangeOffset = 0;
863
                                quint16 end = start;
864
                                quint16 next;
865
                                ++pos;
866
                                while (pos < (uint) chars.length() && (next = chars[pos]) == end+1)
867
                                {
868
                                        end = next;
869
                                        if (delta != (quint16)(cmap[chars[pos]] - next))
870
                                        {
871
                                                rangeOffset = 1; // will be changed later
872
                                        }
873
                                        ++pos;
874
                                }
875
                                startCodes.append(start);
876
                                endCodes.append(end);
877
                                idDeltas.append(delta);
878
                                rangeOffsets.append(rangeOffset);
879
                        }
880
                        while (pos < (uint) chars.length());
881
                }
882
 
883
                startCodes.append(0xFFFF);
884
                endCodes.append(0xFFFF);
885
                idDeltas.append(1); // makes gid 0
886
                rangeOffsets.append(0);
887
 
888
                // write the tables
889
 
890
                uint startOfTable = result.size();
891
                result.resize(startOfTable + ttf_cmap4_EndCodes);
892
 
893
                uint segCount = endCodes.length();
894
                uint segCountX2 = 2 * segCount;
895
                uint entrySelector = 0;
896
                uint searchRange = 2;
897
                while (searchRange <= segCount)
898
                {
899
                        ++entrySelector;
900
                        searchRange *= 2;
901
                }
902
                putWord16(result, startOfTable + ttf_cmapx_format, 4);
903
                /* ttf_cmap4_length is set later */
904
                putWord16(result, startOfTable + ttf_cmap4_language, 0);
905
                putWord16(result, startOfTable + ttf_cmap4_segCountX2, segCountX2);
906
                putWord16(result, startOfTable + ttf_cmap4_searchRange, searchRange);
907
                putWord16(result, startOfTable + ttf_cmap4_entrySelector, entrySelector);
908
                putWord16(result, startOfTable + ttf_cmap4_rangeShift, segCountX2 - searchRange);
909
 
910
                for (uint i = 0; i < segCount; ++i)
911
                {
912
                        appendWord16(result, endCodes[i]);
913
                };
914
                appendWord16(result, 0); // reservedPad
915
 
916
                for (uint i = 0; i < segCount; ++i)
917
                {
918
                        appendWord16(result, startCodes[i]);
919
                };
920
                for (uint i = 0; i < segCount; ++i)
921
                {
922
                        appendWord16(result, idDeltas[i]);
923
                };
924
 
925
 
926
                uint startOfIdRangeOffsetTable = result.size();
927
                uint startOfGlyphIndexArray = startOfIdRangeOffsetTable + segCountX2;
928
                result.resize(startOfGlyphIndexArray);
929
 
930
                for (uint i = 0; i < segCount; ++i)
931
                {
932
                        uint idRangeOffsetAddress = startOfIdRangeOffsetTable + 2*i;
933
                        if (rangeOffsets[i] == 0)
934
                        {
935
                                //quint16 dbg = startCodes[i] + idDeltas[i];
936
                                //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "+" << idDeltas[i] << "-->" << dbg;
937
                                putWord16(result, idRangeOffsetAddress, 0);
938
                        }
939
                        else
940
                        {
941
                                quint16 idRangeOffset = result.size() - idRangeOffsetAddress;
942
                                putWord16(result, idRangeOffsetAddress, idRangeOffset);
943
 
944
                                //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "@" << idRangeOffset << "+" << idDeltas[i];
945
 
946
                                uint startCode = startCodes[i];
947
                                uint segLength = (endCodes[i]-startCode+1);
948
                                for (uint offset = 0; offset < segLength; ++offset)
949
                                {
950
                                        quint16 glyph = cmap[startCode + offset];
951
                                        if (glyph != 0)
952
                                        {
953
                                                glyph -= idDeltas[i];
954
                                        }
955
                                        appendWord16(result, glyph);
956
                                }
957
                        }
958
                };
959
 
960
                putWord16(result, startOfTable + ttf_cmap4_length, result.size() - startOfTable);
961
                return result;
962
        }
963
 
964
        QList<uint> copyGlyphComponents(QByteArray& destGlyf, const QByteArray& srcGlyf, uint srcOffset,
965
                                                                        QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
966
        {
967
                QList<uint> result;
968
 
969
                uint destStart = destGlyf.size();
970
                destGlyf.resize(destStart + ttf_glyf_headerSize);
971
                copy(destGlyf, destStart, srcGlyf, srcOffset, ttf_glyf_headerSize);
972
 
973
                uint pos = srcOffset + ttf_glyf_headerSize;
974
                bool haveInstructions = false;
975
                uint flags = 0;
976
                do {
977
                        /* flags */
978
                        flags = word16(srcGlyf, pos);
979
                        pos += 2;
980
                        haveInstructions |= ((flags & ttf_glyf_ComponentFlag_WE_HAVE_INSTRUCTIONS) != 0);
981
                        appendWord16(destGlyf, flags);
982
 
983
                        /* glyphindex */
984
                        uint glyphIndex = word16(srcGlyf, pos);
985
                        pos += 2;
986
                        if (!newForOldGid.contains(glyphIndex))
987
                        {
988
                                result.append(glyphIndex);
989
                                newForOldGid[glyphIndex] = nextFreeGid++;
990
                        }
991
                        glyphIndex = newForOldGid[glyphIndex];
992
                        appendWord16(destGlyf, glyphIndex);
993
 
994
                        /* args */
995
                        if ( flags & ttf_glyf_ComponentFlag_ARG_1_AND_2_ARE_WORDS) {
996
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1
997
                                pos += 2;
998
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // arg2
999
                                pos += 2;
1000
                        }
1001
                        else {
1002
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1and2
1003
                                pos += 2;
1004
                        }
1005
                        if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_SCALE ) {
1006
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // scale
1007
                                pos += 2;
1008
                        } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_AN_X_AND_Y_SCALE ) {
1009
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
1010
                                pos += 2;
1011
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
1012
                                pos += 2;
1013
                        } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_TWO_BY_TWO ) {
1014
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
1015
                                pos += 2;
1016
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // scale01
1017
                                pos += 2;
1018
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // scale10
1019
                                pos += 2;
1020
                                appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
1021
                                pos += 2;
1022
                        }
1023
                } while ( flags & ttf_glyf_ComponentFlag_MORE_COMPONENTS );
1024
 
1025
                if (haveInstructions)
1026
                {
1027
                        uint numInstr = word16(srcGlyf, pos);
1028
                        appendWord16(destGlyf, numInstr);
1029
                        pos += 2;
1030
                        uint destPos = destGlyf.size();
1031
                        destGlyf.resize(destPos + numInstr);
1032
                        copy(destGlyf, destPos, srcGlyf, pos, numInstr);
1033
                }
1034
 
1035
                return result;
1036
        }
1037
 
1038
        QList<uint> copyGlyph(QList<uint>& destLoca, QByteArray& destGlyf, uint destGid,
1039
                                                  const QList<uint>& srcLoca, const QByteArray& srcGlyf, uint srcGid,
1040
                                                  QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
1041
        {
1042
                QList<uint> compositeElements;
1043
                uint glyphStart = srcLoca[srcGid];
1044
                uint glyphLength = srcLoca[srcGid+1] - glyphStart;
1045
                destLoca.append(destGlyf.size());
1046
                //int i = 0;
1047
                if (glyphLength > 0)
1048
                {
1049
                        quint16 nrOfContours = word16(srcGlyf, glyphStart);
1050
                        if (nrOfContours <= ttf_glyf_Max_numberOfContours)
1051
                        {
1052
                                // simple glyph
1053
                                uint destStart = destGlyf.size();
1054
                                //qDebug() << i++ << ":" << nrOfContours << "contours" << glyphStart << "-->" << destStart << "/" << glyphLength;
1055
                                destGlyf.resize(destStart + glyphLength);
1056
                                copy(destGlyf, destStart, srcGlyf, glyphStart, glyphLength);
1057
                        }
1058
                        else
1059
                        {
1060
                                compositeElements.append(copyGlyphComponents(destGlyf, srcGlyf, glyphStart, newForOldGid, nextFreeGid));
1061
                                //qDebug() << i++ << ":" << srcGid << "composite glyph brought" << compositeElements.size() << "more glyphs";
1062
                        }
1063
                        // Data must be aligned at least on 2 byte boundary, however
1064
                        // a 4-byte alignment is recommended by TTF specs for reasons
1065
                        // related to CPU and efficiency
1066
                        int targetSize = (destGlyf.size() + 3) & ~3;
1067
                        if (destGlyf.size() < targetSize)
1068
                        {
1069
                                destGlyf.reserve(targetSize);
1070
                                while (destGlyf.size() < targetSize)
1071
                                        destGlyf.append('\0');
1072
                        }
1073
                }
1074
 
1075
                return compositeElements;
1076
        }
1077
 
1078
        QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs, QMap<uint, uint>& glyphMap)
1079
        {
1080
                QMap<QByteArray,QByteArray> tables;
1081
 
1082
                //              qDebug() << "loca table:" << (void*) oldLoca[0] << (void*) oldLoca[1] << (void*) oldLoca[2] << (void*) oldLoca[3] << (void*) oldLoca[4] << (void*) oldLoca[5] << (void*) oldLoca[6] << (void*) oldLoca[7];
1083
 
1084
                QMap<uint, uint> newForOldGid;
1085
                if (glyphs.length() == 0)
1086
                {
1087
                        tables["loca"] = getTable(ttf, "loca");
1088
                        tables["glyf"] = getTable(ttf, "glyf");
1089
                }
1090
                else
1091
                {
1092
                        QList<uint> oldLoca = readLoca(ttf);
1093
                        const QByteArray oldGlyf = getTable(ttf, "glyf");
1094
 
1095
                        QList<quint32> newLoca;
1096
                        QByteArray newGlyf;
1097
                        glyphs.removeAll(0);
1098
                        glyphs.prepend(0);
1099
 
1100
                        for (int i = 0; i < glyphs.length(); ++i)
1101
                        {
1102
                                uint oldGid = glyphs[i];
1103
                                newForOldGid[oldGid] = i;
1104
                        }
1105
 
1106
                        uint nextFreeGid = glyphs.length();
1107
                        for (int i = 0; i < glyphs.length(); ++i)
1108
                        {
1109
                                uint oldGid = glyphs[i];
1110
                                glyphs.append(copyGlyph(newLoca, newGlyf, i,
1111
                                                                                oldLoca, oldGlyf, oldGid,
1112
                                                                                newForOldGid, nextFreeGid));
1113
                        }
1114
                        newLoca.append(newGlyf.length());
1115
 
1116
                        tables["loca"] = writeLoca(newLoca, hasLongLocaFormat(ttf));
1117
                        tables["glyf"] = newGlyf;
1118
                }
1119
 
1120
                QMap<uint,uint> cmap = readCMap(ttf);
1121
                QMap<uint,uint>::iterator it;
1122
                uint firstChar = 0xFFFFFFFF;
1123
                uint lastChar = 0;
1124
                for (it = cmap.begin(); it != cmap.end(); ++it)
1125
                {
1126
                        if (glyphs.length() > 0 && !glyphs.contains(it.value()))
1127
                        {
1128
                                it.value() = 0;
1129
                        }
1130
                        else if (it.value() != 0)
1131
                        {
1132
                                if (glyphs.length() > 0)
1133
                                {
1134
                                        //qDebug() << "MAP" << QChar(it.key()) << it.value() << "-->" << newForOldGid[it.value()];
1135
                                        it.value() = newForOldGid[it.value()];
1136
                                }
1137
                                if (it.key() < firstChar)
1138
                                        firstChar = it.key();
1139
                                else if (it.key() > lastChar)
1140
                                        lastChar = it.key();
1141
                        }
1142
                }
1143
                tables["cmap"] = writeCMap(cmap);
1144
 
1145
                QByteArray os2 = getTable(ttf, "OS/2");
1146
                if (os2.length() > ttf_os2_usLastCharIndex)
1147
                {
1148
                        // TODO: adapt unicode ranges
1149
                        putWord16(os2, ttf_os2_usFirstCharIndex, firstChar < 0xFFFF ? firstChar : 0xFFFF);
1150
                        putWord16(os2, ttf_os2_usLastCharIndex, lastChar < 0xFFFF ? lastChar : 0xFFFF);
1151
                        tables["OS/2"] = os2;
1152
                }
1153
 
1154
                if (glyphs.length() > 0)
1155
                {
1156
                        QList<std::pair<qint16, quint16> > oldHmtx = readHmtx(ttf);
1157
                        QList<std::pair<qint16, quint16> > newHmtx;
1158
                        newHmtx.reserve(glyphs.length() + 1);
1159
                        newHmtx.append(std::pair<qint16, quint16>(1234, 123));
1160
                        for (int i = 1; i < glyphs.length(); ++i)
1161
                                newHmtx.append(newHmtx[0]);
1162
                        QMap<uint,uint>::const_iterator iter;
1163
                        for (iter = newForOldGid.cbegin(); iter != newForOldGid.cend(); ++iter)
1164
                        {
1165
                                //qDebug() << "hmtx" << iter.key() << " -> " << iter.value() << "=" << oldHmtx[iter.key()].first;
1166
                                newHmtx[iter.value()] = oldHmtx[iter.key()];
1167
                        }
1168
                        tables["hmtx"] = writeHmtx(newHmtx);
1169
                }
1170
                else
1171
                {
1172
                        tables["hmtx"] = getTable(ttf, "hmtx");
1173
                }
1174
 
1175
                QByteArray maxp = getTable(ttf, "maxp");
1176
                if (glyphs.length() > 0)
1177
                {
1178
                        putWord16(maxp, ttf_maxp_numGlyphs, glyphs.length());
1179
                }
1180
                tables["maxp"] = maxp;
1181
 
1182
                QByteArray hhea = getTable(ttf, "hhea");
1183
                if (glyphs.length() > 0)
1184
                {
1185
                        putWord16(hhea, ttf_hhea_numOfLongHorMetrics, glyphs.length());
1186
                }
1187
                tables["hhea"] = hhea;
1188
 
1189
                QByteArray post = getTable(ttf, "post");
1190
                if (word(post, ttf_post_format) != post_format30)
1191
                {
1192
                        putWord(post, ttf_post_format, post_format30);
1193
                        post.truncate(ttf_post_header_length);
1194
                }
1195
                tables["post"] = post;
1196
 
1197
                // TODO: kern table
1198
 
1199
                QByteArray name = getTable(ttf, "name");
1200
                if (name.length() > 0)
1201
                        tables["name"] = name;
1202
 
1203
                QByteArray prep = getTable(ttf, "prep");
1204
                if (prep.length() > 0)
1205
                        tables["prep"] = prep;
1206
 
1207
                QByteArray cvt = getTable(ttf, "cvt ");
1208
                if (cvt.length() > 0)
1209
                        tables["cvt "] = cvt;
1210
 
1211
                QByteArray fpgm = getTable(ttf, "fpgm");
1212
                if (fpgm.length() > 0)
1213
                        tables["fpgm"] = fpgm;
1214
 
1215
                QByteArray head = getTable(ttf, "head");
1216
                putWord(head, ttf_head_checkSumAdjustment, 0);
1217
                tables["head"] = head;
1218
 
1219
                QByteArray font = createTableDir(tables.keys());
1220
                QMap<QByteArray,QByteArray>::iterator tableP;
1221
                for (tableP = tables.begin(); tableP != tables.end(); ++tableP)
1222
                {
1223
                        writeTable(font, tableP.key(), tableP.value());
1224
                }
1225
 
1226
                uint checkSumAdjustment = 0xB1B0AFBA - calcTableChecksum(font);
1227
                uint headTable = getTableDirEntry(font, "head");
1228
                headTable = word(font, headTable + ttf_TableRecord_offset);
1229
                putWord(font, headTable + ttf_head_checkSumAdjustment, checkSumAdjustment);
1230
 
24464 jghali 1231
                glyphMap.clear();
1232
                for (int i = 0; i < glyphs.length(); ++i)
24469 jghali 1233
                        glyphMap[glyphs[i]] = i;
1234
                // done!
1235
 
1236
                return font;
1237
        }
1238
 
24470 jghali 1239
        QByteArray subsetFaceWithHB(const QByteArray& fontData, QList<uint> cids, int faceIndex, QMap<uint, uint>& glyphMap)
24469 jghali 1240
        {
24464 jghali 1241
                glyphMap.clear();
1242
 
24469 jghali 1243
#ifdef HAVE_HARFBUZZ_SUBSET
24470 jghali 1244
                QScopedPointer<hb_blob_t, HbBlobDeleter> hbBlob(hb_blob_create(fontData.data(), fontData.length(), HB_MEMORY_MODE_READONLY, nullptr, nullptr));
24464 jghali 1245
                if (hbBlob.isNull())
1246
                        return QByteArray();
1247
 
1248
                QScopedPointer<hb_face_t, HbFaceDeleter> hbFullFace(hb_face_create(hbBlob.get(), faceIndex));
1249
                if (hbFullFace.isNull())
1250
                        return QByteArray();
1251
 
1252
                QScopedPointer<hb_subset_input_t, HbSubsetInputDeleter> hbSubsetInput(hb_subset_input_create_or_fail());
1253
                hb_set_t* glyphSet = hb_subset_input_glyph_set(hbSubsetInput.get());
1254
                if (glyphSet == nullptr)
1255
                        return QByteArray();
1256
 
1257
                for (int i = 0; i < cids.count(); ++i)
1258
                        hb_set_add(glyphSet, cids.at(i));
24696 jghali 1259
 
1260
#if HB_VERSION_ATLEAST(2, 9, 0)
1261
                uint32_t subsetFlags = (uint32_t) hb_subset_input_get_flags(hbSubsetInput.get());
1262
                subsetFlags |= HB_SUBSET_FLAGS_RETAIN_GIDS;
1263
                subsetFlags &= ~HB_SUBSET_FLAGS_NO_HINTING;
1264
                subsetFlags |= HB_SUBSET_FLAGS_NAME_LEGACY;
1265
                hb_subset_input_set_flags(hbSubsetInput.get(), subsetFlags);
1266
#else
24464 jghali 1267
                hb_subset_input_set_retain_gids(hbSubsetInput.get(), true);
1268
                hb_subset_input_set_drop_hints(hbSubsetInput.get(), false);
24535 jghali 1269
#if HB_VERSION_ATLEAST(2, 6, 5)
24464 jghali 1270
                hb_subset_input_set_name_legacy(hbSubsetInput.get(), true);
24535 jghali 1271
#endif
24696 jghali 1272
#endif
24464 jghali 1273
 
24707 jghali 1274
#if HB_VERSION_ATLEAST(2, 9, 0)
1275
                QScopedPointer<hb_face_t, HbFaceDeleter> hbSubsetFace(hb_subset_or_fail(hbFullFace.get(), hbSubsetInput.get()));
1276
                if (hbSubsetFace.isNull())
1277
                        return QByteArray();
1278
#else
24464 jghali 1279
                QScopedPointer<hb_face_t, HbFaceDeleter> hbSubsetFace(hb_subset(hbFullFace.get(), hbSubsetInput.get()));
1280
                if (hbSubsetFace.isNull())
1281
                        return QByteArray();
24707 jghali 1282
#endif
24464 jghali 1283
 
1284
                QScopedPointer<hb_blob_t, HbBlobDeleter> hbSubsetBlob(hb_face_reference_blob(hbSubsetFace.get()));
1285
                if (hbSubsetBlob.isNull())
1286
                        return QByteArray();
1287
 
1288
                unsigned int length;
1289
                const char* subsetData = hb_blob_get_data(hbSubsetBlob.get(), &length);
1290
 
1291
                QByteArray subset(subsetData, length);
1292
                if (!subset.isEmpty())
1293
                {
1294
                        for (int i = 0; i < cids.length(); ++i)
1295
                                glyphMap[cids[i]] = cids[i];
1296
                }
24469 jghali 1297
                return subset;
1298
#else
1299
                return QByteArray();
1300
#endif
1301
        }
1302
 
1303
        bool canSubsetOpenTypeFonts()
1304
        {
1305
#ifdef HAVE_HARFBUZZ_SUBSET
1306
                return true;
1307
#else
1308
                return false;
1309
#endif
1310
        }
1311
 
1312
} // namespace sfnt