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