1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qfontsubset_p.h"
5#include <qdebug.h>
6#include <qendian.h>
7#include <qpainterpath.h>
8#include "private/qpdf_p.h"
9
10#include "qfontsubset_agl.cpp"
11
12#include <algorithm>
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt::StringLiterals;
17
18#ifndef QT_NO_PDF
19
20// This map is used for symbol fonts to get the correct glyph names for the latin range
21static const unsigned short symbol_map[0x100] = {
22 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
23 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
24 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
25 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
26 0x0020, 0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220b,
27 0x0028, 0x0029, 0x2217, 0x002b, 0x002c, 0x2212, 0x002e, 0x002f,
28 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
29 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
30
31 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393,
32 0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f,
33 0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9,
34 0x039e, 0x03a8, 0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f,
35 0xf8e5, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3,
36 0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf,
37 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9,
38 0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d, 0x223c, 0x007f,
39
40 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
41 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
42 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
43 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
44 0x20ac, 0x03d2, 0x2023, 0x2264, 0x2044, 0x221e, 0x0192, 0x2263,
45 0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193,
46 0x00b0, 0x00b1, 0x2033, 0x2265, 0x00d7, 0x221d, 0x2202, 0x2022,
47 0x00f7, 0x2260, 0x2261, 0x2248, 0x2026, 0xf8e6, 0xf8e7, 0x21b5,
48
49 0x2135, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229,
50 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209,
51 0x2220, 0x2207, 0xf6da, 0xf6d9, 0xf6db, 0x220f, 0x221a, 0x22c5,
52 0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3,
53 0x25ca, 0x2329, 0xf8e8, 0xf8e9, 0xf8ea, 0x2211, 0xf8eb, 0xf8ec,
54 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0, 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4,
55 0x0000, 0x232a, 0x222b, 0x2320, 0xf8f5, 0x2321, 0xf8f6, 0xf8f7,
56 0xf8f8, 0xf8f9, 0xf8fa, 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0x0000
57};
58
59// ---------------------------- PS/PDF helper methods -----------------------------------
60
61
62
63QByteArray QFontSubset::glyphName(unsigned short unicode, bool symbol)
64{
65 if (symbol && unicode < 0x100)
66 // map from latin1 to symbol
67 unicode = symbol_map[unicode];
68
69 const AGLEntry *r = std::lower_bound(first: unicode_to_agl_map, last: unicode_to_agl_map + unicode_to_agl_map_size, val: unicode);
70 if ((r != unicode_to_agl_map + unicode_to_agl_map_size) && !(unicode < *r))
71 return glyph_names + r->index;
72
73 char buffer[8];
74 buffer[0] = 'u';
75 buffer[1] = 'n';
76 buffer[2] = 'i';
77 QPdf::toHex(u: unicode, buffer: buffer+3);
78 return buffer;
79}
80
81QByteArray QFontSubset::widthArray() const
82{
83 Q_ASSERT(!widths.isEmpty());
84
85 QFontEngine::Properties properties = fontEngine->properties();
86
87 QByteArray width;
88 QPdf::ByteStream s(&width);
89 const qreal scale = 1000.0/emSquare.toInt();
90
91 QFixed defWidth = widths[0];
92 //qDebug("defWidth=%d, scale=%f", defWidth.toInt(), scale.toReal());
93 for (qsizetype i = 0; i < nGlyphs(); ++i) {
94 if (defWidth != widths[i])
95 defWidth = 0;
96 }
97 if (defWidth > 0) {
98 s << "/DW " << qRound(d: defWidth.toInt() * scale);
99 } else {
100 s << "/W [";
101 for (qsizetype g = 0; g < nGlyphs();) {
102 QFixed w = widths[g];
103 qsizetype start = g;
104 qsizetype startLinear = 0;
105 ++g;
106 while (g < nGlyphs()) {
107 QFixed nw = widths[g];
108 if (nw == w) {
109 if (!startLinear)
110 startLinear = g - 1;
111 } else {
112 if (startLinear > 0 && g - startLinear >= 10)
113 break;
114 startLinear = 0;
115 }
116 w = nw;
117 ++g;
118 }
119 // qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
120 if (g - startLinear < 10)
121 startLinear = 0;
122 qsizetype endnonlinear = startLinear ? startLinear : g;
123 // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
124 if (endnonlinear > start) {
125 s << start << '[';
126 for (qsizetype i = start; i < endnonlinear; ++i)
127 s << qRound(d: widths[i].toInt() * scale);
128 s << "]\n";
129 }
130 if (startLinear)
131 s << startLinear << g - 1 << qRound(d: widths[startLinear].toInt() * scale) << '\n';
132 }
133 s << "]\n";
134 }
135 return width;
136}
137
138static void checkRanges(QPdf::ByteStream &ts, QByteArray &ranges, int &nranges)
139{
140 if (++nranges > 100) {
141 ts << nranges << "beginbfrange\n"
142 << ranges << "endbfrange\n";
143 ranges = QByteArray();
144 nranges = 0;
145 }
146}
147
148QList<int> QFontSubset::getReverseMap() const
149{
150 QList<int> reverseMap(0x10000, 0);
151 for (uint uc = 0; uc < 0x10000; ++uc) {
152 auto idx = glyph_indices.indexOf(t: fontEngine->glyphIndex(ucs4: uc));
153 if (idx >= 0 && !reverseMap.at(i: idx))
154 reverseMap[idx] = uc;
155 }
156 return reverseMap;
157}
158
159QByteArray QFontSubset::createToUnicodeMap() const
160{
161 QList<int> reverseMap = getReverseMap();
162
163 QByteArray touc;
164 QPdf::ByteStream ts(&touc);
165 ts << "/CIDInit /ProcSet findresource begin\n"
166 "12 dict begin\n"
167 "begincmap\n"
168 "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n"
169 "/CMapName /Adobe-Identity-UCS def\n"
170 "/CMapType 2 def\n"
171 "1 begincodespacerange\n"
172 "<0000> <FFFF>\n"
173 "endcodespacerange\n";
174
175 int nranges = 1;
176 QByteArray ranges = "<0000> <0000> <0000>\n";
177 QPdf::ByteStream s(&ranges);
178
179 char buf[5];
180 for (qsizetype g = 1; g < nGlyphs(); ) {
181 int uc0 = reverseMap.at(i: g);
182 if (!uc0) {
183 ++g;
184 continue;
185 }
186 qsizetype start = g;
187 qsizetype startLinear = 0;
188 ++g;
189 while (g < nGlyphs()) {
190 int uc = reverseMap[g];
191 // cmaps can't have the high byte changing within one range, so we need to break on that as well
192 if (!uc || (g>>8) != (start >> 8))
193 break;
194 if (uc == uc0 + 1) {
195 if (!startLinear)
196 startLinear = g - 1;
197 } else {
198 if (startLinear > 0 && g - startLinear >= 10)
199 break;
200 startLinear = 0;
201 }
202 uc0 = uc;
203 ++g;
204 }
205 // qDebug("start=%x startLinear=%x g-1=%x",start,startLinear,g-1);
206 if (g - startLinear < 10)
207 startLinear = 0;
208 qsizetype endnonlinear = startLinear ? startLinear : g;
209 // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear);
210 if (endnonlinear > start) {
211 s << '<' << QPdf::toHex(u: (ushort)start, buffer: buf) << "> <";
212 s << QPdf::toHex(u: (ushort)(endnonlinear - 1), buffer: buf) << "> ";
213 if (endnonlinear == start + 1) {
214 s << '<' << QPdf::toHex(u: (ushort)reverseMap[start], buffer: buf) << ">\n";
215 } else {
216 s << '[';
217 for (qsizetype i = start; i < endnonlinear; ++i) {
218 s << '<' << QPdf::toHex(u: (ushort)reverseMap[i], buffer: buf) << "> ";
219 }
220 s << "]\n";
221 }
222 checkRanges(ts, ranges, nranges);
223 }
224 if (startLinear) {
225 while (startLinear < g) {
226 qsizetype len = g - startLinear;
227 qsizetype uc_start = reverseMap[startLinear];
228 qsizetype uc_end = uc_start + len - 1;
229 if ((uc_end >> 8) != (uc_start >> 8))
230 len = 256 - (uc_start & 0xff);
231 s << '<' << QPdf::toHex(u: (ushort)startLinear, buffer: buf) << "> <";
232 s << QPdf::toHex(u: (ushort)(startLinear + len - 1), buffer: buf) << "> ";
233 s << '<' << QPdf::toHex(u: (ushort)reverseMap[startLinear], buffer: buf) << ">\n";
234 checkRanges(ts, ranges, nranges);
235 startLinear += len;
236 }
237 }
238 }
239 if (nranges) {
240 ts << nranges << "beginbfrange\n"
241 << ranges << "endbfrange\n";
242 }
243 ts << "endcmap\n"
244 "CMapName currentdict /CMap defineresource pop\n"
245 "end\n"
246 "end\n";
247
248 return touc;
249}
250
251qsizetype QFontSubset::addGlyph(uint index)
252{
253 qsizetype idx = glyph_indices.indexOf(t: index);
254 if (idx < 0) {
255 idx = glyph_indices.size();
256 glyph_indices.append(t: index);
257 }
258 return idx;
259}
260
261#endif // QT_NO_PDF
262
263// ------------------------------ Truetype generation ----------------------------------------------
264
265typedef qint16 F2DOT14;
266typedef quint32 Tag;
267typedef quint16 GlyphID;
268typedef quint16 Offset;
269
270
271class QTtfStream {
272public:
273 QTtfStream(QByteArray &ba) : data((uchar *)ba.data()) { start = data; }
274 QTtfStream &operator <<(quint8 v) { *data = v; ++data; return *this; }
275 QTtfStream &operator <<(quint16 v) { qToBigEndian(src: v, dest: data); data += sizeof(v); return *this; }
276 QTtfStream &operator <<(quint32 v) { qToBigEndian(src: v, dest: data); data += sizeof(v); return *this; }
277 QTtfStream &operator <<(qint8 v) { *data = quint8(v); ++data; return *this; }
278 QTtfStream &operator <<(qint16 v) { qToBigEndian(src: v, dest: data); data += sizeof(v); return *this; }
279 QTtfStream &operator <<(qint32 v) { qToBigEndian(src: v, dest: data); data += sizeof(v); return *this; }
280 QTtfStream &operator <<(qint64 v) { qToBigEndian(src: v, dest: data); data += sizeof(v); return *this; }
281
282 int offset() const { return data - start; }
283 void setOffset(int o) { data = start + o; }
284 void align4() { while (offset() & 3) { *data = '\0'; ++data; } }
285private:
286 uchar *data;
287 uchar *start;
288};
289
290struct QTtfTable {
291 Tag tag;
292 QByteArray data;
293};
294Q_DECLARE_TYPEINFO(QTtfTable, Q_RELOCATABLE_TYPE);
295
296
297struct qttf_head_table {
298 qint32 font_revision;
299 quint16 flags;
300 qint64 created;
301 qint64 modified;
302 qint16 xMin;
303 qint16 yMin;
304 qint16 xMax;
305 qint16 yMax;
306 quint16 macStyle;
307 qint16 indexToLocFormat;
308};
309Q_DECLARE_TYPEINFO(qttf_head_table, Q_PRIMITIVE_TYPE);
310
311
312struct qttf_hhea_table {
313 qint16 ascender;
314 qint16 descender;
315 qint16 lineGap;
316 quint16 maxAdvanceWidth;
317 qint16 minLeftSideBearing;
318 qint16 minRightSideBearing;
319 qint16 xMaxExtent;
320 quint16 numberOfHMetrics;
321};
322Q_DECLARE_TYPEINFO(qttf_hhea_table, Q_PRIMITIVE_TYPE);
323
324
325struct qttf_maxp_table {
326 quint16 numGlyphs;
327 quint16 maxPoints;
328 quint16 maxContours;
329 quint16 maxCompositePoints;
330 quint16 maxCompositeContours;
331 quint16 maxComponentElements;
332 quint16 maxComponentDepth;
333};
334Q_DECLARE_TYPEINFO(qttf_maxp_table, Q_PRIMITIVE_TYPE);
335
336struct qttf_name_table {
337 QString copyright;
338 QString family;
339 QString subfamily;
340 QString postscript_name;
341};
342Q_DECLARE_TYPEINFO(qttf_name_table, Q_RELOCATABLE_TYPE);
343
344
345static QTtfTable generateHead(const qttf_head_table &head);
346static QTtfTable generateHhea(const qttf_hhea_table &hhea);
347static QTtfTable generateMaxp(const qttf_maxp_table &maxp);
348static QTtfTable generateName(const qttf_name_table &name);
349
350struct qttf_font_tables
351{
352 qttf_head_table head;
353 qttf_hhea_table hhea;
354 qttf_maxp_table maxp;
355};
356
357
358struct QTtfGlyph {
359 quint16 index;
360 qint16 xMin;
361 qint16 xMax;
362 qint16 yMin;
363 qint16 yMax;
364 quint16 advanceWidth;
365 qint16 lsb;
366 quint16 numContours;
367 quint16 numPoints;
368 QByteArray data;
369};
370Q_DECLARE_TYPEINFO(QTtfGlyph, Q_RELOCATABLE_TYPE);
371
372static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advance, qreal lsb, qreal ppem);
373// generates glyf, loca and hmtx
374static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QList<QTtfGlyph> &_glyphs);
375
376static QByteArray bindFont(const QList<QTtfTable>& _tables);
377
378
379static quint32 checksum(const QByteArray &table)
380{
381 quint32 sum = 0;
382 int offset = 0;
383 const uchar *d = (const uchar *)table.constData();
384 while (offset <= table.size()-3) {
385 sum += qFromBigEndian<quint32>(src: d + offset);
386 offset += 4;
387 }
388 int shift = 24;
389 quint32 x = 0;
390 while (offset < table.size()) {
391 x |= ((quint32)d[offset]) << shift;
392 ++offset;
393 shift -= 8;
394 }
395 sum += x;
396
397 return sum;
398}
399
400static QTtfTable generateHead(const qttf_head_table &head)
401{
402 const int head_size = 54;
403 QTtfTable t;
404 t.tag = MAKE_TAG('h', 'e', 'a', 'd');
405 t.data.resize(size: head_size);
406
407 QTtfStream s(t.data);
408
409// qint32 Table version number 0x00010000 for version 1.0.
410// qint32 fontRevision Set by font manufacturer.
411 s << qint32(0x00010000)
412 << head.font_revision
413// quint32 checkSumAdjustment To compute: set it to 0, sum the entire font as quint32, then store 0xB1B0AFBA - sum.
414 << quint32(0)
415// quint32 magicNumber Set to 0x5F0F3CF5.
416 << quint32(0x5F0F3CF5)
417// quint16 flags Bit 0: Baseline for font at y=0;
418// Bit 1: Left sidebearing point at x=0;
419// Bit 2: Instructions may depend on point size;
420// Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear;
421// Bit 4: Instructions may alter advance width (the advance widths might not scale linearly);
422// Bits 5-10: These should be set according to Apple's specification . However, they are not implemented in OpenType.
423// Bit 11: Font data is 'lossless,' as a result of having been compressed and decompressed with the Agfa MicroType Express engine.
424// Bit 12: Font converted (produce compatible metrics)
425// Bit 13: Font optimized for ClearType
426// Bit 14: Reserved, set to 0
427// Bit 15: Reserved, set to 0
428 << quint16(0)
429
430// quint16 unitsPerEm Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
431 << quint16(2048)
432// qint64 created Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
433 << head.created
434// qint64 modified Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
435 << head.modified
436// qint16 xMin For all glyph bounding boxes.
437// qint16 yMin For all glyph bounding boxes.
438// qint16 xMax For all glyph bounding boxes.
439// qint16 yMax For all glyph bounding boxes.
440 << head.xMin
441 << head.yMin
442 << head.xMax
443 << head.yMax
444// quint16 macStyle Bit 0: Bold (if set to 1);
445// Bit 1: Italic (if set to 1)
446// Bit 2: Underline (if set to 1)
447// Bit 3: Outline (if set to 1)
448// Bit 4: Shadow (if set to 1)
449// Bit 5: Condensed (if set to 1)
450// Bit 6: Extended (if set to 1)
451// Bits 7-15: Reserved (set to 0).
452 << head.macStyle
453// quint16 lowestRecPPEM Smallest readable size in pixels.
454 << quint16(6) // just a wild guess
455// qint16 fontDirectionHint 0: Fully mixed directional glyphs;
456 << qint16(0)
457// 1: Only strongly left to right;
458// 2: Like 1 but also contains neutrals;
459// -1: Only strongly right to left;
460// -2: Like -1 but also contains neutrals. 1
461// qint16 indexToLocFormat 0 for short offsets, 1 for long.
462 << head.indexToLocFormat
463// qint16 glyphDataFormat 0 for current format.
464 << qint16(0);
465
466 Q_ASSERT(s.offset() == head_size);
467 return t;
468}
469
470
471static QTtfTable generateHhea(const qttf_hhea_table &hhea)
472{
473 const int hhea_size = 36;
474 QTtfTable t;
475 t.tag = MAKE_TAG('h', 'h', 'e', 'a');
476 t.data.resize(size: hhea_size);
477
478 QTtfStream s(t.data);
479// qint32 Table version number 0x00010000 for version 1.0.
480 s << qint32(0x00010000)
481// qint16 Ascender Typographic ascent. (Distance from baseline of highest ascender)
482 << hhea.ascender
483// qint16 Descender Typographic descent. (Distance from baseline of lowest descender)
484 << hhea.descender
485// qint16 LineGap Typographic line gap.
486// Negative LineGap values are treated as zero
487// in Windows 3.1, System 6, and
488// System 7.
489 << hhea.lineGap
490// quint16 advanceWidthMax Maximum advance width value in 'hmtx' table.
491 << hhea.maxAdvanceWidth
492// qint16 minLeftSideBearing Minimum left sidebearing value in 'hmtx' table.
493 << hhea.minLeftSideBearing
494// qint16 minRightSideBearing Minimum right sidebearing value; calculated as Min(aw - lsb - (xMax - xMin)).
495 << hhea.minRightSideBearing
496// qint16 xMaxExtent Max(lsb + (xMax - xMin)).
497 << hhea.xMaxExtent
498// qint16 caretSlopeRise Used to calculate the slope of the cursor (rise/run); 1 for vertical.
499 << qint16(1)
500// qint16 caretSlopeRun 0 for vertical.
501 << qint16(0)
502// qint16 caretOffset The amount by which a slanted highlight on a glyph needs to be shifted to produce the best appearance. Set to 0 for non-slanted fonts
503 << qint16(0)
504// qint16 (reserved) set to 0
505 << qint16(0)
506// qint16 (reserved) set to 0
507 << qint16(0)
508// qint16 (reserved) set to 0
509 << qint16(0)
510// qint16 (reserved) set to 0
511 << qint16(0)
512// qint16 metricDataFormat 0 for current format.
513 << qint16(0)
514// quint16 numberOfHMetrics Number of hMetric entries in 'hmtx' table
515 << hhea.numberOfHMetrics;
516
517 Q_ASSERT(s.offset() == hhea_size);
518 return t;
519}
520
521
522static QTtfTable generateMaxp(const qttf_maxp_table &maxp)
523{
524 const int maxp_size = 32;
525 QTtfTable t;
526 t.tag = MAKE_TAG('m', 'a', 'x', 'p');
527 t.data.resize(size: maxp_size);
528
529 QTtfStream s(t.data);
530
531// qint32 Table version number 0x00010000 for version 1.0.
532 s << qint32(0x00010000)
533// quint16 numGlyphs The number of glyphs in the font.
534 << maxp.numGlyphs
535// quint16 maxPoints Maximum points in a non-composite glyph.
536 << maxp.maxPoints
537// quint16 maxContours Maximum contours in a non-composite glyph.
538 << maxp.maxContours
539// quint16 maxCompositePoints Maximum points in a composite glyph.
540 << maxp.maxCompositePoints
541// quint16 maxCompositeContours Maximum contours in a composite glyph.
542 << maxp.maxCompositeContours
543// quint16 maxZones 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should be set to 2 in most cases.
544 << quint16(1) // we do not embed instructions
545// quint16 maxTwilightPoints Maximum points used in Z0.
546 << quint16(0)
547// quint16 maxStorage Number of Storage Area locations.
548 << quint16(0)
549// quint16 maxFunctionDefs Number of FDEFs.
550 << quint16(0)
551// quint16 maxInstructionDefs Number of IDEFs.
552 << quint16(0)
553// quint16 maxStackElements Maximum stack depth2.
554 << quint16(0)
555// quint16 maxSizeOfInstructions Maximum byte count for glyph instructions.
556 << quint16(0)
557// quint16 maxComponentElements Maximum number of components referenced at "top level" for any composite glyph.
558 << maxp.maxComponentElements
559// quint16 maxComponentDepth Maximum levels of recursion; 1 for simple components.
560 << maxp.maxComponentDepth;
561
562 Q_ASSERT(s.offset() == maxp_size);
563 return t;
564}
565
566struct QTtfNameRecord {
567 quint16 nameId;
568 QString value;
569};
570Q_DECLARE_TYPEINFO(QTtfNameRecord, Q_RELOCATABLE_TYPE);
571
572static QTtfTable generateName(const QList<QTtfNameRecord> &name);
573
574static QTtfTable generateName(const qttf_name_table &name)
575{
576 QList<QTtfNameRecord> list;
577 list.reserve(asize: 5);
578 QTtfNameRecord rec;
579 rec.nameId = 0;
580 rec.value = name.copyright;
581 list.append(t: rec);
582 rec.nameId = 1;
583 rec.value = name.family;
584 list.append(t: rec);
585 rec.nameId = 2;
586 rec.value = name.subfamily;
587 list.append(t: rec);
588 rec.nameId = 4;
589 rec.value = name.family;
590 if (name.subfamily != "Regular"_L1)
591 rec.value += u' ' + name.subfamily;
592 list.append(t: rec);
593 rec.nameId = 6;
594 rec.value = name.postscript_name;
595 list.append(t: rec);
596
597 return generateName(name: list);
598}
599
600// ####### should probably generate Macintosh/Roman name entries as well
601static QTtfTable generateName(const QList<QTtfNameRecord> &name)
602{
603 const int char_size = 2;
604
605 QTtfTable t;
606 t.tag = MAKE_TAG('n', 'a', 'm', 'e');
607
608 const int name_size = 6 + 12*name.size();
609 int string_size = 0;
610 for (int i = 0; i < name.size(); ++i) {
611 string_size += name.at(i).value.size()*char_size;
612 }
613 t.data.resize(size: name_size + string_size);
614
615 QTtfStream s(t.data);
616// quint16 format Format selector (=0).
617 s << quint16(0)
618// quint16 count Number of name records.
619 << quint16(name.size())
620// quint16 stringOffset Offset to start of string storage (from start of table).
621 << quint16(name_size);
622// NameRecord nameRecord[count] The name records where count is the number of records.
623// (Variable)
624
625 int off = 0;
626 for (int i = 0; i < name.size(); ++i) {
627 int len = name.at(i).value.size()*char_size;
628// quint16 platformID Platform ID.
629// quint16 encodingID Platform-specific encoding ID.
630// quint16 languageID Language ID.
631 s << quint16(3)
632 << quint16(1)
633 << quint16(0x0409) // en_US
634// quint16 nameId Name ID.
635 << name.at(i).nameId
636// quint16 length String length (in bytes).
637 << quint16(len)
638// quint16 offset String offset from start of storage area (in bytes).
639 << quint16(off);
640 off += len;
641 }
642 for (int i = 0; i < name.size(); ++i) {
643 for (QChar ch : name.at(i).value)
644 s << quint16(ch.unicode());
645 }
646 return t;
647}
648
649
650enum Flags {
651 OffCurve = 0,
652 OnCurve = (1 << 0),
653 XShortVector = (1 << 1),
654 YShortVector = (1 << 2),
655 Repeat = (1 << 3),
656 XSame = (1 << 4),
657 XShortPositive = (1 << 4),
658 YSame = (1 << 5),
659 YShortPositive = (1 << 5)
660};
661struct TTF_POINT {
662 qint16 x;
663 qint16 y;
664 quint8 flags;
665};
666Q_DECLARE_TYPEINFO(TTF_POINT, Q_PRIMITIVE_TYPE);
667
668static void convertPath(const QPainterPath &path, QList<TTF_POINT> *points, QList<int> *endPoints, qreal ppem)
669{
670 int numElements = path.elementCount();
671 for (int i = 0; i < numElements - 1; ++i) {
672 const QPainterPath::Element &e = path.elementAt(i);
673 TTF_POINT p;
674 p.x = qRound(d: e.x * 2048. / ppem);
675 p.y = qRound(d: -e.y * 2048. / ppem);
676 p.flags = 0;
677
678 switch(e.type) {
679 case QPainterPath::MoveToElement:
680 if (i != 0) {
681 // see if start and end points of the last contour agree
682 int start = endPoints->size() ? endPoints->at(i: endPoints->size()-1) - 1 : 0;
683 int end = points->size() - 1;
684 if (points->at(i: end).x == points->at(i: start).x
685 && points->at(i: end).y == points->at(i: start).y)
686 points->takeLast();
687 endPoints->append(t: points->size() - 1);
688 }
689 Q_FALLTHROUGH();
690 case QPainterPath::LineToElement:
691 p.flags = OnCurve;
692 break;
693 case QPainterPath::CurveToElement: {
694 // cubic bezier curve, we need to reduce to a list of quadratic curves
695 TTF_POINT list[3*16 + 4]; // we need max 16 subdivisions
696 list[3] = points->at(i: points->size() - 1);
697 list[2] = p;
698 const QPainterPath::Element &e2 = path.elementAt(i: ++i);
699 list[1].x = qRound(d: e2.x * 2048. / ppem);
700 list[1].y = qRound(d: -e2.y * 2048. / ppem);
701 const QPainterPath::Element &e3 = path.elementAt(i: ++i);
702 list[0].x = qRound(d: e3.x * 2048. / ppem);
703 list[0].y = qRound(d: -e3.y * 2048. / ppem);
704
705 TTF_POINT *base = list;
706
707 bool try_reduce = points->size() > 1
708 && points->at(i: points->size() - 1).flags == OnCurve
709 && points->at(i: points->size() - 2).flags == OffCurve;
710// qDebug("generating beziers:");
711 while (base >= list) {
712 const int split_limit = 3;
713// {
714// qDebug("iteration:");
715// TTF_POINT *x = list;
716// while (x <= base + 3) {
717// qDebug() << " " << QPoint(x->x, x->y);
718// ++x;
719// }
720// }
721 Q_ASSERT(base - list < 3*16 + 1);
722 // first see if we can easily reduce the cubic to a quadratic bezier curve
723 int i1_x = base[1].x + ((base[1].x - base[0].x) >> 1);
724 int i1_y = base[1].y + ((base[1].y - base[0].y) >> 1);
725 int i2_x = base[2].x + ((base[2].x - base[3].x) >> 1);
726 int i2_y = base[2].y + ((base[2].y - base[3].y) >> 1);
727// qDebug() << "checking: i1=" << QPoint(i1_x, i1_y) << " i2=" << QPoint(i2_x, i2_y);
728 if (qAbs(t: i1_x - i2_x) <= split_limit && qAbs(t: i1_y - i2_y) <= split_limit) {
729 // got a quadratic bezier curve
730 TTF_POINT np;
731 np.x = (i1_x + i2_x) >> 1;
732 np.y = (i1_y + i2_y) >> 1;
733 if (try_reduce) {
734 // see if we can optimize out the last onCurve point
735 int mx = (points->at(i: points->size() - 2).x + base[2].x) >> 1;
736 int my = (points->at(i: points->size() - 2).y + base[2].y) >> 1;
737 if (qAbs(t: mx - base[3].x) <= split_limit && qAbs(t: my - base[3].y) <= split_limit)
738 points->takeLast();
739 try_reduce = false;
740 }
741 np.flags = OffCurve;
742 points->append(t: np);
743// qDebug() << " appending offcurve point " << QPoint(np.x, np.y);
744 base -= 3;
745 } else {
746 // need to split
747// qDebug(" -> splitting");
748 qint16 a, b, c, d;
749 base[6].x = base[3].x;
750 c = base[1].x;
751 d = base[2].x;
752 base[1].x = a = ( base[0].x + c ) >> 1;
753 base[5].x = b = ( base[3].x + d ) >> 1;
754 c = ( c + d ) >> 1;
755 base[2].x = a = ( a + c ) >> 1;
756 base[4].x = b = ( b + c ) >> 1;
757 base[3].x = ( a + b ) >> 1;
758
759 base[6].y = base[3].y;
760 c = base[1].y;
761 d = base[2].y;
762 base[1].y = a = ( base[0].y + c ) >> 1;
763 base[5].y = b = ( base[3].y + d ) >> 1;
764 c = ( c + d ) >> 1;
765 base[2].y = a = ( a + c ) >> 1;
766 base[4].y = b = ( b + c ) >> 1;
767 base[3].y = ( a + b ) >> 1;
768 base += 3;
769 }
770 }
771 p = list[0];
772 p.flags = OnCurve;
773 break;
774 }
775 case QPainterPath::CurveToDataElement:
776 Q_ASSERT(false);
777 break;
778 }
779// qDebug() << " appending oncurve point " << QPoint(p.x, p.y);
780 points->append(t: p);
781 }
782 int start = endPoints->size() ? endPoints->at(i: endPoints->size()-1) + 1 : 0;
783 int end = points->size() - 1;
784 if (points->at(i: end).x == points->at(i: start).x
785 && points->at(i: end).y == points->at(i: start).y)
786 points->takeLast();
787 endPoints->append(t: points->size() - 1);
788}
789
790static void getBounds(const QList<TTF_POINT> &points, qint16 *xmin, qint16 *xmax, qint16 *ymin, qint16 *ymax)
791{
792 *xmin = points.at(i: 0).x;
793 *xmax = *xmin;
794 *ymin = points.at(i: 0).y;
795 *ymax = *ymin;
796
797 for (int i = 1; i < points.size(); ++i) {
798 *xmin = qMin(a: *xmin, b: points.at(i).x);
799 *xmax = qMax(a: *xmax, b: points.at(i).x);
800 *ymin = qMin(a: *ymin, b: points.at(i).y);
801 *ymax = qMax(a: *ymax, b: points.at(i).y);
802 }
803}
804
805static int convertToRelative(QList<TTF_POINT> *points)
806{
807 // convert points to relative and setup flags
808// qDebug("relative points:");
809 qint16 prev_x = 0;
810 qint16 prev_y = 0;
811 int point_array_size = 0;
812 for (int i = 0; i < points->size(); ++i) {
813 const int x = points->at(i).x;
814 const int y = points->at(i).y;
815 TTF_POINT rel;
816 rel.x = x - prev_x;
817 rel.y = y - prev_y;
818 rel.flags = points->at(i).flags;
819 Q_ASSERT(rel.flags < 2);
820 if (!rel.x) {
821 rel.flags |= XSame;
822 } else if (rel.x > 0 && rel.x < 256) {
823 rel.flags |= XShortVector|XShortPositive;
824 point_array_size++;
825 } else if (rel.x < 0 && rel.x > -256) {
826 rel.flags |= XShortVector;
827 rel.x = -rel.x;
828 point_array_size++;
829 } else {
830 point_array_size += 2;
831 }
832 if (!rel.y) {
833 rel.flags |= YSame;
834 } else if (rel.y > 0 && rel.y < 256) {
835 rel.flags |= YShortVector|YShortPositive;
836 point_array_size++;
837 } else if (rel.y < 0 && rel.y > -256) {
838 rel.flags |= YShortVector;
839 rel.y = -rel.y;
840 point_array_size++;
841 } else {
842 point_array_size += 2;
843 }
844 (*points)[i] = rel;
845// #define toString(x) ((rel.flags & x) ? #x : "")
846// qDebug() << " " << QPoint(rel.x, rel.y) << "flags="
847// << toString(OnCurve) << toString(XShortVector)
848// << (rel.flags & XShortVector ? toString(XShortPositive) : toString(XSame))
849// << toString(YShortVector)
850// << (rel.flags & YShortVector ? toString(YShortPositive) : toString(YSame));
851
852 prev_x = x;
853 prev_y = y;
854 }
855 return point_array_size;
856}
857
858static void getGlyphData(QTtfGlyph *glyph, const QList<TTF_POINT> &points, const QList<int> &endPoints, int point_array_size)
859{
860 const int max_size = int(5 * sizeof(qint16) // header
861 + endPoints.size() * sizeof(quint16) // end points of contours
862 + sizeof(quint16) // instruction length == 0
863 + points.size()*(1) // flags
864 + point_array_size); // coordinates
865
866 glyph->data.resize(size: max_size);
867
868 QTtfStream s(glyph->data);
869 s << qint16(endPoints.size())
870 << glyph->xMin << glyph->yMin << glyph->xMax << glyph->yMax;
871
872 for (int i = 0; i < endPoints.size(); ++i)
873 s << quint16(endPoints.at(i));
874 s << quint16(0); // instruction length
875
876 // emit flags
877 for (int i = 0; i < points.size(); ++i)
878 s << quint8(points.at(i).flags);
879 // emit points
880 for (int i = 0; i < points.size(); ++i) {
881 quint8 flags = points.at(i).flags;
882 qint16 x = points.at(i).x;
883
884 if (flags & XShortVector)
885 s << quint8(x);
886 else if (!(flags & XSame))
887 s << qint16(x);
888 }
889 for (int i = 0; i < points.size(); ++i) {
890 quint8 flags = points.at(i).flags;
891 qint16 y = points.at(i).y;
892
893 if (flags & YShortVector)
894 s << quint8(y);
895 else if (!(flags & YSame))
896 s << qint16(y);
897 }
898
899// qDebug() << "offset=" << s.offset() << "max_size=" << max_size << "point_array_size=" << point_array_size;
900 Q_ASSERT(s.offset() == max_size);
901
902 glyph->numContours = endPoints.size();
903 glyph->numPoints = points.size();
904}
905
906static QTtfGlyph generateGlyph(int index, const QPainterPath &path, qreal advance, qreal lsb, qreal ppem)
907{
908 QList<TTF_POINT> points;
909 QList<int> endPoints;
910 QTtfGlyph glyph;
911 glyph.index = index;
912 glyph.advanceWidth = qRound(d: advance * 2048. / ppem);
913 glyph.lsb = qRound(d: lsb * 2048. / ppem);
914
915 if (path.isEmpty()) {
916 //qDebug("glyph %d is empty", index);
917 lsb = 0;
918 glyph.xMin = glyph.xMax = glyph.yMin = glyph.yMax = 0;
919 glyph.numContours = 0;
920 glyph.numPoints = 0;
921 return glyph;
922 }
923
924 convertPath(path, points: &points, endPoints: &endPoints, ppem);
925
926// qDebug() << "number of contours=" << endPoints.size();
927// for (int i = 0; i < points.size(); ++i)
928// qDebug() << " point[" << i << "] = " << QPoint(points.at(i).x, points.at(i).y) << " flags=" << points.at(i).flags;
929// qDebug("endPoints:");
930// for (int i = 0; i < endPoints.size(); ++i)
931// qDebug() << endPoints.at(i);
932
933 getBounds(points, xmin: &glyph.xMin, xmax: &glyph.xMax, ymin: &glyph.yMin, ymax: &glyph.yMax);
934 int point_array_size = convertToRelative(points: &points);
935 getGlyphData(glyph: &glyph, points, endPoints, point_array_size);
936 return glyph;
937}
938
939static bool operator <(const QTtfGlyph &g1, const QTtfGlyph &g2)
940{
941 return g1.index < g2.index;
942}
943
944static QList<QTtfTable> generateGlyphTables(qttf_font_tables &tables, const QList<QTtfGlyph> &_glyphs)
945{
946 const int max_size_small = 65536*2;
947 QList<QTtfGlyph> glyphs = _glyphs;
948 std::sort(first: glyphs.begin(), last: glyphs.end());
949
950 Q_ASSERT(tables.maxp.numGlyphs == glyphs.at(glyphs.size()-1).index + 1);
951 int nGlyphs = tables.maxp.numGlyphs;
952
953 int glyf_size = 0;
954 for (int i = 0; i < glyphs.size(); ++i)
955 glyf_size += (glyphs.at(i).data.size() + 3) & ~3;
956
957 tables.head.indexToLocFormat = glyf_size < max_size_small ? 0 : 1;
958 tables.hhea.numberOfHMetrics = nGlyphs;
959
960 QTtfTable glyf;
961 glyf.tag = MAKE_TAG('g', 'l', 'y', 'f');
962
963 QTtfTable loca;
964 loca.tag = MAKE_TAG('l', 'o', 'c', 'a');
965 loca.data.resize(size: glyf_size < max_size_small ? (nGlyphs+1)*sizeof(quint16) : (nGlyphs+1)*sizeof(quint32));
966 QTtfStream ls(loca.data);
967
968 QTtfTable hmtx;
969 hmtx.tag = MAKE_TAG('h', 'm', 't', 'x');
970 hmtx.data.resize(size: nGlyphs*4);
971 QTtfStream hs(hmtx.data);
972
973 int pos = 0;
974 for (int i = 0; i < nGlyphs; ++i) {
975 int gpos = glyf.data.size();
976 quint16 advance = 0;
977 qint16 lsb = 0;
978
979 if (glyphs[pos].index == i) {
980 // emit glyph
981// qDebug("emitting glyph %d: size=%d", i, glyphs.at(i).data.size());
982 glyf.data += glyphs.at(i: pos).data;
983 while (glyf.data.size() & 1)
984 glyf.data.append(c: '\0');
985 advance = glyphs.at(i: pos).advanceWidth;
986 lsb = glyphs.at(i: pos).lsb;
987 ++pos;
988 }
989 if (glyf_size < max_size_small) {
990 // use short loca format
991 ls << quint16(gpos>>1);
992 } else {
993 // use long loca format
994 ls << quint32(gpos);
995 }
996 hs << advance
997 << lsb;
998 }
999 if (glyf_size < max_size_small) {
1000 // use short loca format
1001 ls << quint16(glyf.data.size()>>1);
1002 } else {
1003 // use long loca format
1004 ls << quint32(glyf.data.size());
1005 }
1006
1007 Q_ASSERT(loca.data.size() == ls.offset());
1008 Q_ASSERT(hmtx.data.size() == hs.offset());
1009
1010 QList<QTtfTable> list;
1011 list.reserve(asize: 3);
1012 list.append(t: glyf);
1013 list.append(t: loca);
1014 list.append(t: hmtx);
1015 return list;
1016}
1017
1018static bool operator <(const QTtfTable &t1, const QTtfTable &t2)
1019{
1020 return t1.tag < t2.tag;
1021}
1022
1023static QByteArray bindFont(const QList<QTtfTable>& _tables)
1024{
1025 QList<QTtfTable> tables = _tables;
1026
1027 std::sort(first: tables.begin(), last: tables.end());
1028
1029 QByteArray font;
1030 const int header_size = sizeof(qint32) + 4*sizeof(quint16);
1031 const int directory_size = 4*sizeof(quint32)*tables.size();
1032 font.resize(size: header_size + directory_size);
1033
1034 int log2 = 0;
1035 int pow = 1;
1036 int n = tables.size() >> 1;
1037 while (n) {
1038 ++log2;
1039 pow <<= 1;
1040 n >>= 1;
1041 }
1042
1043 quint32 head_offset = 0;
1044 {
1045 QTtfStream f(font);
1046// Offset Table
1047// Type Name Description
1048// qint32 sfnt version 0x00010000 for version 1.0.
1049// quint16 numTables Number of tables.
1050// quint16 searchRange (Maximum power of 2 <= numTables) x 16.
1051// quint16 entrySelector Log2(maximum power of 2 <= numTables).
1052// quint16 rangeShift NumTables x 16-searchRange.
1053 f << qint32(0x00010000)
1054 << quint16(tables.size())
1055 << quint16(16*pow)
1056 << quint16(log2)
1057 << quint16(16*(tables.size() - pow));
1058
1059// Table Directory
1060// Type Name Description
1061// quint32 tag 4 -byte identifier.
1062// quint32 checkSum CheckSum for this table.
1063// quint32 offset Offset from beginning of TrueType font file.
1064// quint32 length Length of this table.
1065 quint32 table_offset = header_size + directory_size;
1066 for (int i = 0; i < tables.size(); ++i) {
1067 const QTtfTable &t = tables.at(i);
1068 const quint32 size = (t.data.size() + 3) & ~3;
1069 if (t.tag == MAKE_TAG('h', 'e', 'a', 'd'))
1070 head_offset = table_offset;
1071 f << t.tag
1072 << checksum(table: t.data)
1073 << table_offset
1074 << quint32(t.data.size());
1075 table_offset += size;
1076#define TAG(x) char(t.tag >> 24) << char((t.tag >> 16) & 0xff) << char((t.tag >> 8) & 0xff) << char(t.tag & 0xff)
1077 //qDebug() << "table " << TAG(t.tag) << "has size " << t.data.size() << "stream at " << f.offset();
1078 }
1079 }
1080 for (int i = 0; i < tables.size(); ++i) {
1081 const QByteArray &t = tables.at(i).data;
1082 font += t;
1083 int s = t.size();
1084 while (s & 3) { font += '\0'; ++s; }
1085 }
1086
1087 if (!head_offset) {
1088 qWarning(msg: "QFontSubset: Font misses 'head' table");
1089 return QByteArray();
1090 }
1091
1092 // calculate the fonts checksum and qToBigEndian into 'head's checksum_adjust
1093 quint32 checksum_adjust = 0xB1B0AFBA - checksum(table: font);
1094 qToBigEndian(src: checksum_adjust, dest: font.data() + head_offset + 8);
1095
1096 return font;
1097}
1098
1099
1100/*
1101 PDF requires the following tables:
1102
1103 head, hhea, loca, maxp, cvt , prep, glyf, hmtx, fpgm
1104
1105 This means we don't have to add a os/2, post or name table. cvt , prep and fpgm could be empty
1106 if really required.
1107*/
1108
1109QByteArray QFontSubset::toTruetype() const
1110{
1111 qttf_font_tables font;
1112 memset(s: &font, c: 0, n: sizeof(qttf_font_tables));
1113
1114 qreal ppem = fontEngine->fontDef.pixelSize;
1115#define TO_TTF(x) qRound(x * 2048. / ppem)
1116
1117 QFontEngine::Properties properties = fontEngine->properties();
1118 // initialize some stuff needed in createWidthArray
1119 emSquare = 2048;
1120 widths.resize(size: nGlyphs());
1121
1122 // head table
1123 font.head.font_revision = 0x00010000;
1124 font.head.flags = (1 << 2) | (1 << 4);
1125 font.head.created = 0; // ###
1126 font.head.modified = 0; // ###
1127 font.head.xMin = SHRT_MAX;
1128 font.head.xMax = SHRT_MIN;
1129 font.head.yMin = SHRT_MAX;
1130 font.head.yMax = SHRT_MIN;
1131 font.head.macStyle = (fontEngine->fontDef.weight > QFont::Normal) ? 1 : 0;
1132 font.head.macStyle |= (fontEngine->fontDef.styleHint != QFont::StyleNormal) ? 1 : 0;
1133
1134 // hhea table
1135 font.hhea.ascender = qRound(f: properties.ascent);
1136 font.hhea.descender = -qRound(f: properties.descent);
1137 font.hhea.lineGap = qRound(f: properties.leading);
1138 font.hhea.maxAdvanceWidth = TO_TTF(fontEngine->maxCharWidth());
1139 font.hhea.minLeftSideBearing = TO_TTF(fontEngine->minLeftBearing());
1140 font.hhea.minRightSideBearing = TO_TTF(fontEngine->minRightBearing());
1141 font.hhea.xMaxExtent = SHRT_MIN;
1142
1143 font.maxp.numGlyphs = 0;
1144 font.maxp.maxPoints = 0;
1145 font.maxp.maxContours = 0;
1146 font.maxp.maxCompositePoints = 0;
1147 font.maxp.maxCompositeContours = 0;
1148 font.maxp.maxComponentElements = 0;
1149 font.maxp.maxComponentDepth = 0;
1150 const qsizetype numGlyphs = nGlyphs();
1151 font.maxp.numGlyphs = quint16(numGlyphs);
1152 QList<QTtfGlyph> glyphs;
1153 glyphs.reserve(asize: numGlyphs);
1154
1155 for (qsizetype i = 0; i < numGlyphs; ++i) {
1156 glyph_t g = glyph_indices.at(i);
1157 QPainterPath path;
1158 glyph_metrics_t metric;
1159 fontEngine->getUnscaledGlyph(glyph: g, path: &path, metrics: &metric);
1160 if (noEmbed) {
1161 path = QPainterPath();
1162 if (g == 0)
1163 path.addRect(rect: QRectF(0, 0, 1000, 1000));
1164 }
1165 QTtfGlyph glyph = generateGlyph(index: i, path, advance: metric.xoff.toReal(), lsb: metric.x.toReal(), ppem: properties.emSquare.toReal());
1166
1167 font.head.xMin = qMin(a: font.head.xMin, b: glyph.xMin);
1168 font.head.xMax = qMax(a: font.head.xMax, b: glyph.xMax);
1169 font.head.yMin = qMin(a: font.head.yMin, b: glyph.yMin);
1170 font.head.yMax = qMax(a: font.head.yMax, b: glyph.yMax);
1171
1172 font.hhea.xMaxExtent = qMax(a: font.hhea.xMaxExtent, b: (qint16)(glyph.lsb + glyph.xMax - glyph.xMin));
1173
1174 font.maxp.maxPoints = qMax(a: font.maxp.maxPoints, b: glyph.numPoints);
1175 font.maxp.maxContours = qMax(a: font.maxp.maxContours, b: glyph.numContours);
1176
1177// qDebug("adding glyph %d size=%d", glyph.index, glyph.data.size());
1178 glyphs.append(t: glyph);
1179 widths[i] = glyph.advanceWidth;
1180 }
1181
1182
1183 QList<QTtfTable> tables = generateGlyphTables(tables&: font, glyphs: glyphs);
1184 tables.append(t: generateHead(head: font.head));
1185 tables.append(t: generateHhea(hhea: font.hhea));
1186 tables.append(t: generateMaxp(maxp: font.maxp));
1187 // name
1188 QTtfTable name_table;
1189 name_table.tag = MAKE_TAG('n', 'a', 'm', 'e');
1190 if (!noEmbed)
1191 name_table.data = fontEngine->getSfntTable(tag: name_table.tag);
1192 if (name_table.data.isEmpty()) {
1193 qttf_name_table name;
1194 if (noEmbed)
1195 name.copyright = "Fake font"_L1;
1196 else
1197 name.copyright = QLatin1StringView(properties.copyright);
1198 name.family = fontEngine->fontDef.families.first();
1199 name.subfamily = "Regular"_L1; // ######
1200 name.postscript_name = QLatin1StringView(properties.postscriptName);
1201 name_table = generateName(name);
1202 }
1203 tables.append(t: name_table);
1204
1205 if (!noEmbed) {
1206 QTtfTable os2;
1207 os2.tag = MAKE_TAG('O', 'S', '/', '2');
1208 os2.data = fontEngine->getSfntTable(tag: os2.tag);
1209 if (!os2.data.isEmpty())
1210 tables.append(t: os2);
1211 }
1212
1213 return bindFont(tables: tables);
1214}
1215
1216QT_END_NAMESPACE
1217

source code of qtbase/src/gui/text/qfontsubset.cpp