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 
21 #include <harfbuzz/hb.h>
22 #ifdef HAVE_HARFBUZZ_SUBSET
23 #include <harfbuzz/hb-subset.h>
24 #endif
25 
26 struct HbBlobDeleter
27 {
cleanupHbBlobDeleter28 	static void cleanup(void *pointer)
29 	{
30 		if (pointer)
31 			hb_blob_destroy((hb_blob_t*) pointer);
32 	}
33 };
34 
35 struct HbFaceDeleter
36 {
cleanupHbFaceDeleter37 	static void cleanup(void *pointer)
38 	{
39 		if (pointer)
40 			hb_face_destroy((hb_face_t*) pointer);
41 	}
42 };
43 
44 #ifdef HAVE_HARFBUZZ_SUBSET
45 struct HbSubsetInputDeleter
46 {
cleanupHbSubsetInputDeleter47 	static void cleanup(void *pointer)
48 	{
49 		if (pointer)
50 			hb_subset_input_destroy((hb_subset_input_t*) pointer);
51 	}
52 };
53 #endif
54 
55 namespace sfnt {
56 
byte(const QByteArray & bb,uint pos)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 
word(const QByteArray & bb,uint pos)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 
putWord(QByteArray & bb,uint pos,quint32 val)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 
appendWord(QByteArray & bb,quint32 val)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 
word16(const QByteArray & bb,uint pos)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 
putWord16(QByteArray & bb,uint pos,quint16 val)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 
appendWord16(QByteArray & bb,quint16 val)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 
tag(const QByteArray & bb,uint pos)105 	QByteArray tag(const QByteArray& bb, uint pos)
106 	{
107 		return QByteArray::fromRawData(bb.constData() + pos, 4);
108 	}
109 
tag(uint word)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 
copy(QByteArray & dst,uint to,const QByteArray & src,uint from,uint len)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 
usable() const340 	bool PostTable::usable() const
341 	{
342 		return m_usable;
343 	}
344 
setUsable(bool usable)345 	void PostTable::setUsable(bool usable)
346 	{
347 		m_usable = usable;
348 	}
349 
errorMsg() const350 	QString PostTable::errorMsg() const
351 	{
352 		return m_errorMsg;
353 	}
354 
setErrorMsg(const QString & errorMsg)355 	void PostTable::setErrorMsg(const QString& errorMsg)
356 	{
357 		m_errorMsg = errorMsg;
358 	}
359 
numberOfGlyphs() const360 	uint PostTable::numberOfGlyphs() const
361 	{
362 		if (m_names.length() > 0)
363 			return m_names.length();
364 		return post_format10_names_count;
365 	}
366 
nameFor(uint glyph) const367 	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 
readFrom(FT_Face face)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 
copyTable(QByteArray & ttf,uint destDirEntry,uint pos,const QByteArray & source,uint dirEntry)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 
extractFace(const QByteArray & coll,int faceIndex)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 
getTableDirEntry(const QByteArray & ttf,const QByteArray & ttfTag)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 
getTable(const QByteArray & ttf,const QByteArray & ttfTag)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 
createTableDir(const QList<QByteArray> & tags)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 
calcTableChecksum(QByteArray & table)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 
writeTable(QByteArray & ttf,const QByteArray & tag,QByteArray & table)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 
hasLongLocaFormat(const QByteArray & ttf)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 
readLoca(const QByteArray & ttf)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 
writeLoca(const QList<uint> & loca,bool longFormat)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 
readHmtx(const QByteArray & ttf)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 
writeHmtx(const QList<std::pair<qint16,quint16>> & longHorMetrics)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 
readCMap(const QByteArray & ttf)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 
writeCMap(const QMap<uint,uint> & cmap)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 
copyGlyphComponents(QByteArray & destGlyf,const QByteArray & srcGlyf,uint srcOffset,QMap<uint,uint> & newForOldGid,uint & nextFreeGid)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 
copyGlyph(QList<uint> & destLoca,QByteArray & destGlyf,uint destGid,const QList<uint> & srcLoca,const QByteArray & srcGlyf,uint srcGid,QMap<uint,uint> & newForOldGid,uint & nextFreeGid)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 
subsetFace(const QByteArray & ttf,QList<uint> & glyphs,QMap<uint,uint> & glyphMap)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 
1231 		glyphMap.clear();
1232 		for (int i = 0; i < glyphs.length(); ++i)
1233 			glyphMap[glyphs[i]] = i;
1234 		// done!
1235 
1236 		return font;
1237 	}
1238 
subsetFaceWithHB(const QByteArray & fontData,QList<uint> cids,int faceIndex,QMap<uint,uint> & glyphMap)1239 	QByteArray subsetFaceWithHB(const QByteArray& fontData, QList<uint> cids, int faceIndex, QMap<uint, uint>& glyphMap)
1240 	{
1241 		glyphMap.clear();
1242 
1243 #ifdef HAVE_HARFBUZZ_SUBSET
1244 		QScopedPointer<hb_blob_t, HbBlobDeleter> hbBlob(hb_blob_create(fontData.data(), fontData.length(), HB_MEMORY_MODE_READONLY, nullptr, nullptr));
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));
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
1267 		hb_subset_input_set_retain_gids(hbSubsetInput.get(), true);
1268 		hb_subset_input_set_drop_hints(hbSubsetInput.get(), false);
1269 #if HB_VERSION_ATLEAST(2, 6, 5)
1270 		hb_subset_input_set_name_legacy(hbSubsetInput.get(), true);
1271 #endif
1272 #endif
1273 
1274 		QScopedPointer<hb_face_t, HbFaceDeleter> hbSubsetFace(hb_subset_or_fail(hbFullFace.get(), hbSubsetInput.get()));
1275 		if (hbSubsetFace.isNull())
1276 			return QByteArray();
1277 
1278 		QScopedPointer<hb_blob_t, HbBlobDeleter> hbSubsetBlob(hb_face_reference_blob(hbSubsetFace.get()));
1279 		if (hbSubsetBlob.isNull())
1280 			return QByteArray();
1281 
1282 		unsigned int length;
1283 		const char* subsetData = hb_blob_get_data(hbSubsetBlob.get(), &length);
1284 
1285 		QByteArray subset(subsetData, length);
1286 		if (!subset.isEmpty())
1287 		{
1288 			for (int i = 0; i < cids.length(); ++i)
1289 				glyphMap[cids[i]] = cids[i];
1290 		}
1291 		return subset;
1292 #else
1293 		return QByteArray();
1294 #endif
1295 	}
1296 
canSubsetOpenTypeFonts()1297 	bool canSubsetOpenTypeFonts()
1298 	{
1299 #ifdef HAVE_HARFBUZZ_SUBSET
1300 		return true;
1301 #else
1302 		return false;
1303 #endif
1304 	}
1305 
1306 } // namespace sfnt
1307