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

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