1 /*
2 * General handling of *ttf files
3 *
4 * Copyright © 1997-1998 Herbert Duerr
5 * Copyright © 2003-2009, 2016, 2018-2020 Guillem Jover
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free Softaware
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23 #include "ttf.h"
24 #include <cstring>
25 #include <cstdlib>
26
TTFont(const char * fileName,int infoOnly)27 TTFont::TTFont(const char *fileName, int infoOnly):
28 RandomAccessFile(fileName),
29 endPoints(nullptr), points(nullptr),
30 nameTable(nullptr), headTable(nullptr), maxpTable(nullptr),
31 cmapTable(nullptr), locaTable(nullptr), glyphTable(nullptr),
32 fpgmTable(nullptr), prepTable(nullptr), cvtTable(nullptr),
33 hheaTable(nullptr), hmtxTable(nullptr), os2Table(nullptr),
34 ltshTable(nullptr), hdmxTable(nullptr), vdmxTable(nullptr),
35 gaspTable(nullptr), kernTable(nullptr), //postTable(nullptr),
36 ebdtTable(nullptr), eblcTable(nullptr),
37 mortTable(nullptr), vheaTable(nullptr)
38 {
39 debug("TTFont(\"%s\");\n", fileName);
40
41 if (openError())
42 return;
43
44 /* uint32_t version = */ readUInt();
45 short nTables = readSShort();
46 /* uint16_t searchRange = */ readUShort();
47 /* uint16_t entrySelector = */ readUShort();
48 /* uint16_t rangeShift = */ readUShort();
49
50 for (int i = nTables; --i >= 0;) {
51 int name = readUInt();
52 /* int checksum = */ readUInt();
53 uint32_t offset = readUInt();
54 uint32_t length = readUInt();
55 if (length == 0)
56 continue;
57 if (offset >= getLength() || offset + length > getLength()) {
58 headTable = nullptr;
59 return;
60 }
61 if (infoOnly && name != NAME_MAGIC && name != OS2_MAGIC
62 && name != HEAD_MAGIC)
63 continue;
64
65 switch (name) {
66 case NAME_MAGIC:
67 nameTable = new NameTable(*this, offset, length);
68 break;
69 case HEAD_MAGIC:
70 headTable = new HeadTable(*this, offset, length);
71 break;
72 case MAXP_MAGIC:
73 maxpTable = new MaxpTable(*this, offset, length);
74 break;
75 case CMAP_MAGIC:
76 cmapTable = new CmapTable(*this, offset, length);
77 break;
78 case LOCA_MAGIC:
79 locaTable = new LocaTable(*this, offset, length);
80 break;
81 case GLYF_MAGIC:
82 glyphTable = new GlyphTable(*this, offset, length);
83 break;
84 case LTSH_MAGIC:
85 ltshTable = new LtshTable(*this, offset, length);
86 break;
87 case OS2_MAGIC:
88 os2Table = new OS2Table(*this, offset, length);
89 break;
90 case VDMX_MAGIC:
91 vdmxTable = new VdmxTable(*this, offset, length);
92 break;
93 case CVT_MAGIC:
94 cvtTable = new CvtTable(*this, offset, length);
95 break;
96 case FPGM_MAGIC:
97 fpgmTable = new FpgmTable(*this, offset, length);
98 break;
99 case PREP_MAGIC:
100 prepTable = new PrepTable(*this, offset, length);
101 break;
102 case HDMX_MAGIC:
103 hdmxTable = new HdmxTable(*this, offset, length);
104 break;
105 case HHEA_MAGIC:
106 hheaTable = new HheaTable(*this, offset, length);
107 break;
108 case HMTX_MAGIC:
109 hmtxTable = new HmtxTable(*this, offset, length);
110 break;
111 case KERN_MAGIC:
112 kernTable = new KernTable(*this, offset, length);
113 break;
114 case POST_MAGIC:
115 //postTable = new PostTable(*this, offset, length);
116 break;
117 case GASP_MAGIC:
118 gaspTable = new GaspTable(*this, offset, length);
119 break;
120 case EBDT_MAGIC:
121 case BDAT_MAGIC:
122 // XXX: ebdtTable = new EbdtTable(*this, offset, length);
123 break;
124 case EBLC_MAGIC:
125 case BLOC_MAGIC:
126 // XXX: eblcTable = new EblcTable(*this, offset, length);
127 break;
128 case VHEA_MAGIC:
129 //vheaTable = new VheaTable(*this, offset, length);
130 break;
131 case MORT_MAGIC:
132 //mortTable = new MortTable(*this, offset, length);
133 break;
134 default:
135 // System.out.println("???");
136 break;
137 }
138 }
139
140 if (!(nameTable && headTable)) {
141 if (headTable)
142 delete headTable;
143 headTable = nullptr;
144 }
145
146 if (infoOnly)
147 return;
148
149 if (!(maxpTable && cmapTable && locaTable && glyphTable)) {
150 if (headTable)
151 delete headTable;
152 headTable = nullptr;
153 }
154
155 if (headTable == nullptr) {
156 debug("Incomplete TrueType file\n");
157 return;
158 }
159
160 locaTable->setupLoca(headTable->shortLoca(), maxpTable->numGlyphs);
161 hmtxTable->setupHmtx(hheaTable->nLongHMetrics);
162 }
163
~TTFont()164 TTFont::~TTFont()
165 {
166 closeRAFile();
167
168 if (nameTable)
169 delete nameTable;
170 if (headTable)
171 delete headTable;
172 if (maxpTable)
173 delete maxpTable;
174 if (cmapTable)
175 delete cmapTable;
176 if (locaTable)
177 delete locaTable;
178 if (glyphTable)
179 delete glyphTable;
180
181 if (os2Table)
182 delete os2Table;
183 if (cvtTable)
184 delete cvtTable;
185 if (fpgmTable)
186 delete fpgmTable;
187 if (prepTable)
188 delete prepTable;
189
190 if (hheaTable)
191 delete hheaTable;
192 if (hmtxTable)
193 delete hmtxTable;
194
195 if (ltshTable)
196 delete ltshTable;
197 if (hdmxTable)
198 delete hdmxTable;
199 if (vdmxTable)
200 delete vdmxTable;
201
202 if (gaspTable)
203 delete gaspTable;
204 if (kernTable)
205 delete kernTable;
206 }
207
208 int
badFont()209 TTFont::badFont()
210 {
211 return (openError() || !headTable || headTable->badHeadMagic());
212 }
213
214 int
getEmUnits()215 TTFont::getEmUnits()
216 {
217 return headTable->emUnits;
218 }
219
220 void
getFontInfo(FontInfo * fi)221 TTFont::getFontInfo(FontInfo *fi)
222 {
223 if (os2Table) {
224 fi->firstChar = os2Table->firstCharNo;
225 fi->lastChar = os2Table->lastCharNo;
226
227 for (int i = 0; i < 10; ++i)
228 fi->panose[i] = os2Table->panose[i];
229 } else {
230 fi->firstChar = 0x0020; // space
231 fi->lastChar = 0x007f; // end of Latin 1 in Unicode
232
233 for (int i = 0; i < 10; ++i)
234 fi->panose[i] = 0; // any
235 }
236
237 string faceName = nameTable->getString(1, 4);
238
239 if (faceName.empty())
240 faceName = "Unknown";
241
242 if (faceName.size() > sizeof(fi->faceName))
243 fi->faceLength = sizeof(fi->faceName);
244 else
245 fi->faceLength = faceName.size();
246
247 faceName.copy(fi->faceName, fi->faceLength);
248 }
249
250 int
getGlyphNo8(char char8)251 TTFont::getGlyphNo8(char char8)
252 {
253 return cmapTable->char2glyphNo(char8);
254 }
255
256 int
getGlyphNo16(int unicode)257 TTFont::getGlyphNo16(int unicode)
258 {
259 return cmapTable->unicode2glyphNo(unicode);
260 }
261
262 #if 0 // currently not used
263 int
264 TTFont::doCharNo8(GlyphTable *g, int char8)
265 {
266 return doGlyphNo(g, getGlyphNo8(char8));
267 }
268
269 int
270 TTFont::doCharNo16(GlyphTable *g, int unicode)
271 {
272 return doGlyphNo(g, getGlyphNo16(unicode));
273 }
274 #endif
275
276 int
getMaxWidth(int mppemx)277 TTFont::getMaxWidth(int mppemx)
278 {
279 int maxWidth = 0;
280
281 if (hdmxTable)
282 maxWidth = hdmxTable->getMaxWidth(mppemx);
283
284 if (maxWidth == 0) {
285 maxWidth = headTable->xmax - headTable->xmin;
286 maxWidth += headTable->emUnits >> 5; // +3%
287 maxWidth = maxWidth * mppemx / headTable->emUnits;
288 debug("using maxWidth %d instead\n", maxWidth);
289 }
290
291 return maxWidth;
292 }
293
294 int
getGlyphWidth(int mppemx,int glyphNo)295 TTFont::getGlyphWidth(int mppemx, int glyphNo)
296 {
297 int width = 0;
298
299 if (hdmxTable)
300 width = hdmxTable->getGlyphWidth(mppemx, glyphNo);
301
302 if (width == 0) {
303 int dummy;
304 hmtxTable->getHMetrics(glyphNo, &width, &dummy);
305 // XXX: if (width == 0)
306 // XXX: width = getMaxWidth(mppemx):
307 width += headTable->emUnits >> 5; // +3%
308 width = width * mppemx / headTable->emUnits;
309 debug("using width %d instead\n", width);
310 }
311
312 return width;
313 }
314
315 // verify on reference implementation
316 int
patchGlyphCode(GlyphTable * g XFSTT_ATTR_UNUSED,int glyphNo XFSTT_ATTR_UNUSED)317 TTFont::patchGlyphCode(GlyphTable *g XFSTT_ATTR_UNUSED,
318 int glyphNo XFSTT_ATTR_UNUSED)
319 {
320 return 0;
321 }
322
323 int
checksum(uint8_t * buf,int len)324 TTFont::checksum(uint8_t *buf, int len)
325 {
326 len = (len + 3) >> 2;
327 int sum = 0;
328
329 for (uint8_t *p = buf; --len >= 0; p += 4) {
330 int val = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
331 sum += val;
332 }
333
334 return sum;
335 }
336
337 void
updateChecksums()338 TTFont::updateChecksums()
339 {
340 uint8_t *buf = base;
341 uint8_t *headTable = nullptr;
342 int nTables = (buf[4] << 8) + buf[5];
343
344 debug("nTables = %d\n", nTables);
345
346 for (int i = 0; i < nTables; ++i) {
347 uint8_t *b = &buf[12 + i * 16];
348 int name = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
349 int offset = (b[8] << 24) + (b[9] << 16) + (b[10] << 8) + b[11];
350 int length = (b[12] << 24) + (b[13] << 16) + (b[14] << 8) + b[15];
351 int check = checksum(buf + offset, length);
352
353 debug("offset = %08X, length = %08X\n", offset, length);
354
355 b[4] = (uint8_t)(check >> 24);
356 b[5] = (uint8_t)(check >> 16);
357 b[6] = (uint8_t)(check >> 8);
358 b[7] = (uint8_t)check;
359
360 debug("checksum[%d] = %08X\n", i, check);
361
362 if (name == 0x68656164) {
363 headTable = buf + offset;
364 debug("headOffset = %08X\n", offset);
365 }
366 }
367
368 if (headTable) {
369 int check = checksum(buf, getLength()) - 0xB1B0AFBA;
370
371 debug("csAdjust = %08X\n", check);
372
373 headTable[8] = (uint8_t)(check >> 24);
374 headTable[9] = (uint8_t)(check >> 16);
375 headTable[10] = (uint8_t)(check >> 8);
376 headTable[11] = (uint8_t)check;
377 }
378 }
379
380 int
write2File(const char * filename)381 TTFont::write2File(const char *filename)
382 {
383 FILE *fd = fopen(filename, "wb");
384
385 if (fd) {
386 int len = fwrite(base, 1, getLength(), fd);
387 fclose(fd);
388 return len;
389 }
390
391 return -1;
392 }
393
394 #include <cctype>
395 #include <algorithm>
396
397 static char
char_tolower(const char c)398 char_tolower(const char c)
399 {
400 return std::tolower(c);
401 }
402
403 // result has to be preset with the category name "-category-",
404 // returns "-category-family-weight-slant-setwidth-TT-"
405 // XXX: "pixelsize-pointsize-xres-yres-spacing-avgwidth-charset-encoding"
406 string
getXLFDbase(string xlfd_templ)407 TTFont::getXLFDbase(string xlfd_templ)
408 {
409 //#define XLFDEXT "-normal-tt-0-0-0-0-p-0-iso8859-1"
410 //#define XLFDEXT "-normal-tt-"
411
412 string strFamily = nameTable->getString(1, 1);
413 string strSubFamily = nameTable->getString(1, 2);
414
415 if (strFamily.empty())
416 strFamily = "unknown";
417
418 if (strSubFamily.empty())
419 strSubFamily = "tt";
420
421 std::replace(strFamily.begin(), strFamily.end(), '-', ' ');
422 std::replace(strSubFamily.begin(), strSubFamily.end(), '-', ' ');
423
424 string xlfd = xlfd_templ + '-' + strFamily;
425
426 if (os2Table) {
427 xlfd += (os2Table->selection & 32) ? "-bold" : "-medium";
428 xlfd += (os2Table->selection & 1) ? "-i" : "-r";
429 } else {
430 xlfd += (headTable->macStyle & 1) ? "-bold" : "-medium";
431 xlfd += (headTable->macStyle & 2) ? "-i" : "-r";
432 }
433
434 xlfd += "-normal-" + strSubFamily + '-';
435
436 std::transform(xlfd.begin(), xlfd.end(), xlfd.begin(), char_tolower);
437
438 debug("xlfd = \"%s\"\n", xlfd.c_str());
439
440 return xlfd;
441 }
442