2729eafc |
1 | /* |
2 | * Support for sfnt-housed fonts for Halibut |
3 | * |
4 | * sfnt-housed fonts include TrueType, OpenType, sfnt-housed Type 1 |
5 | * fonts and a couple of bitmap formats. |
6 | * |
7 | * The various tables that can appear in sfnt-housed fonts are defined |
8 | * in several places. These include: |
9 | * |
10 | * The OpenType Specification: |
11 | * <http://partners.adobe.com/public/developer/opentype/index_spec.html> |
12 | * |
13 | * The TrueType Reference Manual: |
14 | * <http://developer.apple.com/textfonts/TTRefMan/> |
15 | */ |
16 | |
17 | #include <assert.h> |
18 | #include <stddef.h> |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include "halibut.h" |
22 | #include "paper.h" |
23 | |
24 | typedef struct sfnt_decode_Tag sfnt_decode; |
25 | struct sfnt_decode_Tag { |
26 | void (*decoder)(void *src, void *dest); |
27 | size_t src_len; |
28 | size_t dest_offset; |
29 | }; |
30 | |
18e7663a |
31 | #if 0 /* unused */ |
2729eafc |
32 | static void decode_uint8(void *src, void *dest) { |
33 | *(unsigned int *)dest = *(unsigned char *)src; |
34 | } |
35 | #define d_uint8 decode_uint8, 1 |
18e7663a |
36 | #endif |
2729eafc |
37 | |
18e7663a |
38 | #if 0 /* unused */ |
2729eafc |
39 | static void decode_int8(void *src, void *dest) { |
40 | *(int *)dest = *(signed char *)src; |
41 | } |
42 | #define d_int8 decode_int8, 1 |
18e7663a |
43 | #endif |
2729eafc |
44 | |
45 | static void decode_uint16(void *src, void *dest) { |
46 | unsigned char *cp = src; |
47 | *(unsigned int *)dest = (cp[0] << 8) + cp[1]; |
48 | } |
49 | #define d_uint16 decode_uint16, 2 |
50 | |
51 | static void decode_int16(void *src, void *dest) { |
52 | signed char *cp = src; |
53 | unsigned char *ucp = src; |
54 | *(int *)dest = (cp[0] << 8) + ucp[1]; |
55 | } |
56 | #define d_int16 decode_int16, 2 |
57 | |
58 | static void decode_uint32(void *src, void *dest) { |
59 | unsigned char *cp = src; |
60 | *(unsigned int *)dest = |
61 | (cp[0] << 24) + (cp[1] << 16) + (cp[2] << 8) + cp[3]; |
62 | } |
63 | #define d_uint32 decode_uint32, 4 |
64 | |
18e7663a |
65 | #if 0 /* unused */ |
2729eafc |
66 | static void decode_int32(void *src, void *dest) { |
67 | signed char *cp = src; |
68 | unsigned char *ucp = src; |
69 | *(int *)dest = (cp[0] << 24) + (ucp[1] << 16) + (ucp[2] << 8) + ucp[3]; |
70 | } |
71 | #define d_int32 decode_int32, 4 |
18e7663a |
72 | #endif |
2729eafc |
73 | |
74 | static void decode_skip(void *src, void *dest) { |
18e7663a |
75 | IGNORE(src); |
76 | IGNORE(dest); |
2729eafc |
77 | /* do nothing */ |
78 | } |
79 | #define d_skip(n) decode_skip, (n), 0 |
80 | |
81 | static void decode_end(void *src, void *dest) { |
18e7663a |
82 | IGNORE(src); |
83 | IGNORE(dest); |
2729eafc |
84 | /* never called */ |
85 | } |
86 | #define d_end decode_end, 0, 0 |
87 | |
88 | static void *decode(sfnt_decode *dec, void *src, void *end, void *dest) { |
89 | while (dec->decoder != decode_end) { |
90 | if ((char *)src + dec->src_len > (char *)end) return NULL; |
91 | dec->decoder(src, (char *)dest + dec->dest_offset); |
92 | src = (char *)src + dec->src_len; |
93 | dec++; |
94 | } |
95 | return src; |
96 | } |
97 | |
98 | static void *decoden(sfnt_decode *dec, void *src, void *end, void *dest, |
99 | size_t size, size_t n) { |
100 | while (n-- && src) { |
101 | src = decode(dec, src, end, dest); |
102 | dest = (char *)dest + size; |
103 | } |
104 | return src; |
105 | } |
106 | |
107 | /* Decoding specs for simple data types */ |
108 | sfnt_decode uint16_decode[] = { { d_uint16, 0 }, { d_end } }; |
109 | sfnt_decode int16_decode[] = { { d_int16, 0 }, { d_end } }; |
110 | sfnt_decode uint32_decode[] = { { d_uint32, 0 }, { d_end } }; |
111 | |
112 | /* Offset subdirectory -- the start of the file */ |
113 | typedef struct offsubdir_Tag offsubdir; |
114 | struct offsubdir_Tag { |
115 | unsigned scaler_type; |
116 | unsigned numTables; |
117 | }; |
118 | sfnt_decode offsubdir_decode[] = { |
119 | { d_uint32, offsetof(offsubdir, scaler_type) }, |
120 | { d_uint16, offsetof(offsubdir, numTables) }, |
121 | { d_skip(6) }, |
122 | { d_end } |
123 | }; |
124 | |
125 | #define sfnt_00010000 0x00010000 |
126 | #define TAG_cmap 0x636d6170 |
127 | #define TAG_glyf 0x676c7966 |
128 | #define TAG_head 0x68656164 |
129 | #define TAG_hhea 0x68686561 |
130 | #define TAG_hmtx 0x686d7478 |
131 | #define TAG_loca 0x6c6f6361 |
132 | #define TAG_name 0x6e616d65 |
133 | #define TAG_post 0x706f7374 |
134 | #define sfnt_true 0x74727565 |
135 | |
136 | /* Table directory */ |
137 | typedef struct tabledir_Tag tabledir; |
138 | struct tabledir_Tag { |
139 | unsigned tag; |
140 | unsigned checkSum; |
141 | unsigned offset; |
142 | unsigned length; |
143 | }; |
144 | sfnt_decode tabledir_decode[] = { |
145 | { d_uint32, offsetof(tabledir, tag) }, |
146 | { d_uint32, offsetof(tabledir, checkSum) }, |
147 | { d_uint32, offsetof(tabledir, offset) }, |
148 | { d_uint32, offsetof(tabledir, length) }, |
149 | { d_end } |
150 | }; |
151 | |
152 | /* Character to Glyph ('cmap') table */ |
153 | typedef struct t_cmap_Tag t_cmap; |
154 | struct t_cmap_Tag { |
155 | unsigned numTables; |
156 | }; |
157 | sfnt_decode t_cmap_decode[] = { |
158 | { d_skip(2) }, |
159 | { d_uint16, offsetof(t_cmap, numTables) }, |
160 | { d_end } |
161 | }; |
162 | typedef struct encodingrec_Tag encodingrec; |
163 | struct encodingrec_Tag { |
164 | unsigned platformID; |
165 | unsigned encodingID; |
166 | unsigned offset; |
167 | }; |
168 | sfnt_decode encodingrec_decode[] = { |
169 | { d_uint16, offsetof(encodingrec, platformID) }, |
170 | { d_uint16, offsetof(encodingrec, encodingID) }, |
171 | { d_uint32, offsetof(encodingrec, offset) }, |
172 | { d_end } |
173 | }; |
174 | typedef struct cmap4_Tag cmap4; |
175 | struct cmap4_Tag { |
176 | unsigned length; |
177 | unsigned segCountX2; |
178 | }; |
179 | sfnt_decode cmap4_decode[] = { |
180 | { d_skip(2) }, /* format */ |
181 | { d_uint16, offsetof(cmap4, length) }, |
182 | { d_skip(2) }, /* language */ |
183 | { d_uint16, offsetof(cmap4, segCountX2) }, |
184 | { d_skip(6) }, /* searchRange, entrySelector, rangeShift */ |
185 | { d_end } |
186 | }; |
187 | |
188 | /* Font Header ('head') table */ |
189 | typedef struct t_head_Tag t_head; |
190 | struct t_head_Tag { |
191 | unsigned version; |
192 | unsigned fontRevision; |
193 | unsigned flags; |
194 | unsigned unitsPerEm; |
195 | int xMin, yMin, xMax, yMax; |
196 | int indexToLocFormat; |
197 | }; |
198 | sfnt_decode t_head_decode[] = { |
199 | { d_uint32, offsetof(t_head, version) }, |
200 | { d_uint32, offsetof(t_head, fontRevision) }, |
201 | { d_skip(8) }, /* checkSumAdjustment, magicNumber, flags */ |
202 | { d_uint16, offsetof(t_head, flags) }, |
203 | { d_uint16, offsetof(t_head, unitsPerEm) }, |
204 | { d_skip(16) }, /* created, modified */ |
205 | { d_int16, offsetof(t_head, xMin) }, |
206 | { d_int16, offsetof(t_head, yMin) }, |
207 | { d_int16, offsetof(t_head, xMax) }, |
208 | { d_int16, offsetof(t_head, yMax) }, |
209 | { d_skip(6) }, /* macStyle, lowestRecPPEM, fontDirectionHint */ |
210 | { d_int16, offsetof(t_head, indexToLocFormat) }, |
211 | { d_skip(2) }, |
212 | { d_end } |
213 | }; |
214 | |
215 | /* Horizontal Header ('hhea') table */ |
216 | typedef struct t_hhea_Tag t_hhea; |
217 | struct t_hhea_Tag { |
218 | unsigned version; |
219 | int ascent; |
220 | int descent; |
221 | int lineGap; |
222 | int metricDataFormat; |
223 | unsigned numOfLongHorMetrics; |
224 | }; |
225 | sfnt_decode t_hhea_decode[] = { |
226 | { d_uint32, offsetof(t_hhea, version) }, |
227 | { d_int16, offsetof(t_hhea, ascent) }, |
228 | { d_int16, offsetof(t_hhea, descent) }, |
229 | { d_int16, offsetof(t_hhea, lineGap) }, |
230 | { d_skip(22) }, |
231 | { d_int16, offsetof(t_hhea, metricDataFormat) }, |
232 | { d_uint16, offsetof(t_hhea, numOfLongHorMetrics) }, |
233 | { d_end } |
234 | }; |
235 | |
236 | /* Horizontal Metrics ('hmtx') table */ |
237 | sfnt_decode longhormetric_decode[] = { |
238 | { d_uint16, 0 }, |
239 | { d_skip(2) }, |
240 | { d_end } |
241 | }; |
242 | |
243 | /* Naming ('name') table */ |
244 | typedef struct t_name_Tag t_name; |
245 | typedef struct namerecord_Tag namerecord; |
246 | struct t_name_Tag { |
247 | unsigned format; |
248 | unsigned count; |
249 | unsigned stringOffset; |
250 | namerecord *nameRecord; |
251 | }; |
252 | sfnt_decode t_name_decode[] = { |
253 | { d_uint16, offsetof(t_name, format) }, |
254 | { d_uint16, offsetof(t_name, count) }, |
255 | { d_uint16, offsetof(t_name, stringOffset) }, |
256 | { d_end } |
257 | }; |
258 | struct namerecord_Tag { |
259 | unsigned platformID; |
260 | unsigned encodingID; |
261 | unsigned languageID; |
262 | unsigned nameID; |
263 | unsigned length; |
264 | unsigned offset; |
265 | }; |
266 | sfnt_decode namerecord_decode[] = { |
267 | { d_uint16, offsetof(namerecord, platformID) }, |
268 | { d_uint16, offsetof(namerecord, encodingID) }, |
269 | { d_uint16, offsetof(namerecord, languageID) }, |
270 | { d_uint16, offsetof(namerecord, nameID) }, |
271 | { d_uint16, offsetof(namerecord, length) }, |
272 | { d_uint16, offsetof(namerecord, offset) }, |
273 | { d_end } |
274 | }; |
275 | |
276 | /* PostScript compatibility ('post') table */ |
277 | typedef struct t_post_Tag t_post; |
278 | struct t_post_Tag { |
279 | unsigned format; |
280 | unsigned italicAngle; |
281 | int underlinePosition; |
282 | int underlineThickness; |
283 | unsigned isFixedPitch; |
284 | unsigned minMemType42; |
285 | unsigned maxMemType42; |
286 | }; |
287 | sfnt_decode t_post_decode[] = { |
288 | { d_uint32, offsetof(t_post, format) }, |
289 | { d_uint32, offsetof(t_post, italicAngle) }, |
290 | { d_int16, offsetof(t_post, underlinePosition) }, |
291 | { d_int16, offsetof(t_post, underlineThickness) }, |
292 | { d_uint32, offsetof(t_post, isFixedPitch) }, |
293 | { d_uint32, offsetof(t_post, minMemType42) }, |
294 | { d_uint32, offsetof(t_post, maxMemType42) }, |
295 | { d_skip(8) }, /* minMemType1, maxMemType1 */ |
296 | { d_end } |
297 | }; |
298 | |
299 | typedef struct { |
300 | glyph name; |
301 | unsigned short index; |
302 | } glyphmap; |
303 | |
304 | typedef struct sfnt_Tag sfnt; |
305 | struct sfnt_Tag { |
306 | void *data; |
307 | size_t len; |
308 | void *end; |
309 | offsubdir osd; |
310 | tabledir *td; |
311 | t_head head; |
312 | unsigned nglyphs; |
313 | glyph *glyphsbyindex; |
314 | unsigned short *glyphsbyname; |
315 | unsigned minmem, maxmem; |
316 | }; |
317 | |
318 | static int sfnt_findtable(sfnt *sf, unsigned tag, |
319 | void **startp, void **endp) { |
320 | size_t i; |
321 | |
322 | for (i = 0; i < sf->osd.numTables; i++) { |
323 | if (sf->td[i].tag == tag) { |
324 | *startp = (char *)sf->data + sf->td[i].offset; |
325 | *endp = (char *)*startp + sf->td[i].length; |
326 | return TRUE; |
327 | } |
328 | } |
329 | return FALSE; |
330 | } |
331 | |
332 | static char *sfnt_psname(font_info *fi) { |
333 | sfnt *sf = fi->fontfile; |
334 | t_name name; |
335 | void *ptr, *end; |
336 | size_t i; |
337 | char *psname; |
338 | namerecord *nr; |
339 | |
340 | if (!sfnt_findtable(sf, TAG_name, &ptr, &end)) |
341 | abort(); |
342 | ptr = decode(t_name_decode, ptr, end, &name); |
343 | name.nameRecord = snewn(name.count, namerecord); |
344 | ptr = decoden(namerecord_decode, ptr, sf->end, name.nameRecord, |
345 | sizeof(*name.nameRecord), name.count); |
346 | for (i = 0; i < name.count; i++) { |
347 | nr = name.nameRecord + i; |
348 | if (nr->nameID == 6) { |
349 | /* PostScript name, but can we make sense of it? */ |
350 | if (nr->platformID == 1 && nr->encodingID == 0) { |
351 | /* Mac Roman, which is ASCII for our purposes */ |
352 | psname = snewn(nr->length + 1, char); |
353 | memcpy(psname, (char *)ptr + nr->offset, nr->length); |
354 | psname[nr->length] = 0; |
355 | sfree(name.nameRecord); |
356 | return psname; |
357 | } |
358 | } |
359 | } |
360 | return NULL; |
361 | } |
362 | |
363 | /* |
364 | * Extract data from the 'post' table (mostly glyph mappings) |
365 | * |
366 | * TODO: cope better with duplicated glyph names (usually .notdef) |
367 | * TODO: when presented with format 3.0, try to use 'CFF' if present. |
368 | */ |
369 | static void sfnt_mapglyphs(sfnt *sf) { |
370 | t_post post; |
371 | void *ptr, *end; |
18e7663a |
372 | unsigned char *sptr; |
373 | char tmp[256]; |
2729eafc |
374 | glyph *extraglyphs; |
375 | unsigned nextras, i, g; |
376 | |
377 | sf->glyphsbyname = sf->glyphsbyindex = NULL; |
378 | if (!sfnt_findtable(sf, TAG_post, &ptr, &end)) |
379 | abort(); |
380 | ptr = decode(t_post_decode, ptr, end, &post); |
381 | sf->minmem = post.minMemType42; |
382 | sf->maxmem = post.maxMemType42; |
383 | if (ptr == NULL) abort(); |
384 | switch (post.format) { |
385 | case 0x00010000: |
386 | sf->nglyphs = 258; |
387 | sf->glyphsbyindex = (glyph *)tt_std_glyphs; |
388 | break; |
389 | case 0x00020000: |
18e7663a |
390 | if ((char *)ptr + 2 > (char *)end) return; |
2729eafc |
391 | decode_uint16(ptr, &sf->nglyphs); |
392 | ptr = (char *)ptr + 2; |
18e7663a |
393 | if ((char *)ptr + 2*sf->nglyphs > (char *)end) return; |
2729eafc |
394 | nextras = 0; |
18e7663a |
395 | for (sptr = (unsigned char *)ptr + 2*sf->nglyphs; |
396 | sptr < (unsigned char *)end; |
397 | sptr += *sptr+1) |
2729eafc |
398 | nextras++; |
399 | extraglyphs = snewn(nextras, glyph); |
400 | i = 0; |
18e7663a |
401 | for (sptr = (unsigned char *)ptr + 2*sf->nglyphs; |
402 | sptr < (unsigned char *)end; |
403 | sptr += *sptr+1) { |
2729eafc |
404 | memcpy(tmp, sptr + 1, *sptr); |
405 | tmp[*sptr] = 0; |
406 | assert(i < nextras); |
407 | extraglyphs[i++] = glyph_intern(tmp); |
408 | } |
409 | sf->glyphsbyindex = snewn(sf->nglyphs, glyph); |
410 | for (i = 0; i < sf->nglyphs; i++) { |
411 | decode_uint16((char *)ptr + 2*i, &g); |
412 | if (g <= 257) |
413 | sf->glyphsbyindex[i] = tt_std_glyphs[g]; |
414 | else if (g < 258 + nextras) |
415 | sf->glyphsbyindex[i] = extraglyphs[g - 258]; |
416 | else |
417 | sf->glyphsbyindex[i] = NOGLYPH; |
418 | } |
419 | sfree(extraglyphs); |
420 | break; |
421 | default: |
422 | abort(); |
423 | } |
424 | } |
425 | |
426 | /* |
427 | * Get data from 'hhea' and 'hmtx' tables |
428 | */ |
429 | void sfnt_getmetrics(font_info *fi) { |
430 | sfnt *sf = fi->fontfile; |
431 | t_hhea hhea; |
432 | void *ptr, *end; |
433 | unsigned i, j; |
434 | unsigned *hmtx; |
435 | |
436 | if (!sfnt_findtable(sf, TAG_hhea, &ptr, &end)) |
437 | abort(); |
438 | if (decode(t_hhea_decode, ptr, end, &hhea) == NULL) |
439 | abort(); |
440 | if ((hhea.version & 0xffff0000) != 0x00010000) |
441 | abort(); |
442 | fi->ascent = hhea.ascent; |
443 | fi->descent = hhea.descent; |
444 | if (hhea.metricDataFormat != 0) |
445 | abort(); |
446 | if (!sfnt_findtable(sf, TAG_hmtx, &ptr, &end)) |
447 | abort(); |
448 | hmtx = snewn(hhea.numOfLongHorMetrics, unsigned); |
449 | if (decoden(longhormetric_decode, ptr, end, hmtx, sizeof(*hmtx), |
450 | hhea.numOfLongHorMetrics) == NULL) |
451 | abort(); |
452 | for (i = 0; i < sf->nglyphs; i++) { |
453 | glyph_width *w = snew(glyph_width); |
454 | w->glyph = sf->glyphsbyindex[i]; |
455 | j = i < hhea.numOfLongHorMetrics ? i : hhea.numOfLongHorMetrics - 1; |
456 | w->width = hmtx[j] * UNITS_PER_PT / sf->head.unitsPerEm; |
457 | add234(fi->widths, w); |
458 | } |
459 | } |
460 | |
461 | /* |
462 | * Get mapping data from 'cmap' table |
463 | * |
d274495f |
464 | * We look for either a (0, 3), or (3, 1) table, both of these being |
465 | * versions of UCS-2. We only handle format 4 of this table, since |
466 | * that seems to be the only one in use. |
2729eafc |
467 | */ |
468 | void sfnt_getmap(font_info *fi) { |
469 | sfnt *sf = fi->fontfile; |
470 | t_cmap cmap; |
471 | encodingrec *esd; |
472 | void *base, *ptr, *end; |
473 | unsigned i; |
474 | unsigned format; |
475 | |
476 | |
477 | for (i = 0; i < lenof(fi->bmp); i++) |
478 | fi->bmp[i] = 0xFFFF; |
479 | if (!sfnt_findtable(sf, TAG_cmap, &ptr, &end)) |
480 | abort(); |
481 | base = ptr; |
482 | ptr = decode(t_cmap_decode, ptr, end, &cmap); |
483 | if (ptr == NULL) abort(); |
484 | esd = snewn(cmap.numTables, encodingrec); |
485 | ptr = decoden(encodingrec_decode, ptr, end, esd, sizeof(*esd), |
486 | cmap.numTables); |
487 | if (ptr == NULL) abort(); |
488 | for (i = 0; i < cmap.numTables; i++) { |
489 | if (!decode(uint16_decode, (char *)base + esd[i].offset, end, &format)) |
490 | abort(); |
491 | if ((esd[i].platformID == 0 && esd[i].encodingID == 3) || |
492 | (esd[i].platformID == 3 && esd[i].encodingID == 1)) { |
493 | /* UCS-2 encoding */ |
494 | if (!decode(uint16_decode, (char *)base + esd[i].offset, end, |
495 | &format)) |
496 | abort(); |
497 | if (format == 4) { |
498 | unsigned *data, *endCode, *startCode, *idDelta, *idRangeOffset; |
499 | unsigned *glyphIndexArray; |
500 | unsigned segcount, nword, nglyphindex, j; |
501 | cmap4 cmap4; |
502 | |
503 | ptr = decode(cmap4_decode, (char *)base + esd[i].offset, end, |
504 | &cmap4); |
505 | if (!ptr) abort(); |
506 | segcount = cmap4.segCountX2 / 2; |
507 | nword = cmap4.length / 2 - 7; |
508 | data = snewn(nword, unsigned); |
509 | if (!decoden(uint16_decode, ptr, (char *)ptr + nword * 2, |
510 | data, sizeof(*data), nword)) abort(); |
511 | endCode = data; |
512 | startCode = data + segcount + 1; |
513 | idDelta = startCode + segcount; |
514 | idRangeOffset = idDelta + segcount; |
515 | glyphIndexArray = idRangeOffset + segcount; |
516 | nglyphindex = nword - segcount * 4 - 1; |
517 | |
518 | for (j = 0; j < segcount; j++) { |
519 | unsigned k, idx; |
520 | |
521 | if (idRangeOffset[j] == 0) { |
522 | for (k = startCode[j]; k <= endCode[j]; k++) { |
523 | idx = (k + idDelta[j]) & 0xffff; |
524 | if (idx != 0) { |
525 | if (idx > sf->nglyphs) abort(); |
526 | fi->bmp[k] = sf->glyphsbyindex[idx]; |
527 | } |
528 | } |
529 | } else { |
530 | unsigned startidx = idRangeOffset[j]/2 - segcount + j; |
531 | for (k = startCode[j]; k <= endCode[j]; k++) { |
532 | idx = glyphIndexArray[startidx + k - startCode[j]]; |
533 | if (idx != 0) { |
534 | idx = (idx + idDelta[j]) & 0xffff; |
535 | if (idx > sf->nglyphs) abort(); |
536 | fi->bmp[k] = sf->glyphsbyindex[idx]; |
537 | } |
538 | } |
539 | } |
540 | } |
541 | |
542 | sfree(data); |
543 | } |
544 | } |
545 | } |
546 | } |
547 | |
548 | void read_sfnt_file(input *in) { |
549 | sfnt *sf = snew(sfnt); |
550 | size_t off = 0, got; |
551 | FILE *fp = in->currfp; |
552 | font_info *fi = snew(font_info); |
18e7663a |
553 | /* size_t i; */ |
2729eafc |
554 | void *ptr, *end; |
555 | |
556 | fi->name = NULL; |
557 | fi->widths = newtree234(width_cmp); |
558 | fi->kerns = newtree234(kern_cmp); |
559 | fi->ligs = newtree234(lig_cmp); |
560 | fi->fontbbox[0] = fi->fontbbox[1] = fi->fontbbox[2] = fi->fontbbox[3] = 0; |
561 | fi->capheight = fi->xheight = fi->ascent = fi->descent = 0; |
562 | fi->stemh = fi->stemv = fi->italicangle = 0; |
563 | fi->fontfile = sf; |
564 | fi->filetype = TRUETYPE; |
565 | |
566 | sf->len = 32768; |
567 | sf->data = snewn(sf->len, unsigned char); |
568 | for (;;) { |
569 | got = fread((char *)sf->data + off, 1, sf->len - off, fp); |
570 | off += got; |
571 | if (off != sf->len) break; |
572 | sf->len *= 2; |
573 | sf->data = sresize(sf->data, sf->len, unsigned char); |
574 | } |
575 | sf->len = off; |
576 | sf->data = sresize(sf->data, sf->len, unsigned char); |
577 | sf->end = (char *)sf->data + sf->len; |
578 | decode(offsubdir_decode, sf->data, sf->end, &sf->osd); |
579 | /* |
580 | fprintf(stderr, "scaler type = 0x%x; numTables = %u\n", |
581 | sf->osd.scaler_type, sf->osd.numTables); |
582 | */ |
583 | sf->td = snewn(sf->osd.numTables, tabledir); |
584 | decoden(tabledir_decode, (char *)sf->data + 12, sf->end, |
585 | sf->td, sizeof(*sf->td), sf->osd.numTables); |
586 | /* |
587 | for (i = 0; i < sf->osd.numTables; i++) |
588 | fprintf(stderr, "table tag = '%c%c%c%c'; offset = %#10x; length = %#10x\n", |
589 | sf->td[i].tag >> 24, sf->td[i].tag >> 16, sf->td[i].tag >> 8, sf->td[i].tag, sf->td[i].offset, sf->td[i].length); |
590 | */ |
591 | if (!sfnt_findtable(sf, TAG_head, &ptr, &end)) |
592 | abort(); |
593 | if (decode(t_head_decode, ptr, end, &sf->head) == NULL) |
594 | abort(); |
595 | if ((sf->head.version & 0xffff0000) != 0x00010000) |
596 | abort(); |
597 | fi->name = sfnt_psname(fi); |
598 | sfnt_mapglyphs(sf); |
599 | sfnt_getmetrics(fi); |
600 | sfnt_getmap(fi); |
601 | fi->next = all_fonts; |
602 | all_fonts = fi; |
603 | fclose(in->currfp); |
604 | } |
605 | |
606 | static int sizecmp(const void *a, const void *b) { |
607 | if (*(size_t *)a < *(size_t *)b) return -1; |
608 | if (*(size_t *)a > *(size_t *)b) return 1; |
609 | return 0; |
610 | } |
611 | |
612 | /* |
613 | * The format for embedding TrueType fonts in Postscript is defined in |
614 | * Adobe Technical Note #5012: The Type 42 Font Format Specification. |
615 | * <http://partners.adobe.com/public/developer/en/font/5012.Type42_Spec.pdf> |
616 | */ |
617 | |
618 | void sfnt_writeps(font_info const *fi, FILE *ofp) { |
619 | unsigned i, j, lastbreak; |
620 | sfnt *sf = fi->fontfile; |
621 | size_t *breaks, glyfoff, glyflen; |
622 | void *glyfptr, *glyfend, *locaptr, *locaend; |
623 | unsigned *loca; |
624 | |
625 | /* XXX Unclear that this is the correct format. */ |
626 | fprintf(ofp, "%%!PS-TrueTypeFont-%u-%u\n", sf->osd.scaler_type, |
627 | sf->head.fontRevision); |
628 | if (sf->minmem) |
629 | fprintf(ofp, "%%%%VMUsage: %u %u\n", sf->minmem, sf->maxmem); |
630 | fprintf(ofp, "9 dict dup begin\n"); |
631 | fprintf(ofp, "/FontType 42 def\n"); |
632 | fprintf(ofp, "/FontMatrix [1 0 0 1 0 0] def\n"); |
633 | fprintf(ofp, "/FontName /%s def\n", fi->name); |
634 | fprintf(ofp, "/Encoding StandardEncoding def\n"); |
635 | if ((sf->head.flags & 0x0003) == 0x0003) { |
636 | /* |
637 | * Sensible font with the origin in the right place, such that |
638 | * the bounding box is meaningful. |
639 | */ |
640 | fprintf(ofp, "/FontBBox [%g %g %g %g] readonly def\n", |
641 | (double)sf->head.xMin / sf->head.unitsPerEm, |
642 | (double)sf->head.yMin / sf->head.unitsPerEm, |
643 | (double)sf->head.xMax / sf->head.unitsPerEm, |
644 | (double)sf->head.yMax / sf->head.unitsPerEm); |
645 | } else { |
646 | /* Non-sensible font. */ |
647 | fprintf(ofp, "/FontBBox [0 0 0 0] readonly def"); |
648 | } |
649 | fprintf(ofp, "/PaintType 0 def\n"); |
650 | fprintf(ofp, "/CharStrings %u dict dup begin\n", sf->nglyphs); |
651 | for (i = 0; i < sf->nglyphs; i++) |
652 | fprintf(ofp, "/%s %u def\n", glyph_extern(sf->glyphsbyindex[i]), i); |
653 | fprintf(ofp, "end readonly def\n"); |
654 | fprintf(ofp, "/sfnts [<"); |
655 | breaks = snewn(sf->osd.numTables + sf->nglyphs, size_t); |
656 | for (i = 0; i < sf->osd.numTables; i++) { |
657 | breaks[i] = sf->td[i].offset; |
658 | } |
659 | if (!sfnt_findtable(sf, TAG_glyf, &glyfptr, &glyfend)) |
660 | abort(); |
661 | glyfoff = (char *)glyfptr - (char *)sf->data; |
662 | glyflen = (char *)glyfend - (char *)glyfptr; |
663 | if (!sfnt_findtable(sf, TAG_loca, &locaptr, &locaend)) |
664 | abort(); |
665 | loca = snewn(sf->nglyphs, unsigned); |
666 | if (sf->head.indexToLocFormat == 0) { |
667 | if (!decoden(uint16_decode, locaptr, locaend, loca, sizeof(*loca), |
668 | sf->nglyphs)) abort(); |
669 | for (i = 0; i < sf->nglyphs; i++) loca[i] *= 2; |
670 | } else { |
671 | if (!decoden(uint32_decode, locaptr, locaend, loca, sizeof(*loca), |
672 | sf->nglyphs)) abort(); |
673 | } |
674 | for (i = 1; i < sf->nglyphs; i++) { |
675 | if (loca[i] > glyflen) abort(); |
676 | breaks[sf->osd.numTables + i - 1] = loca[i] + glyfoff; |
677 | } |
678 | breaks[sf->osd.numTables + sf->nglyphs - 1] = sf->len; |
679 | qsort(breaks, sf->osd.numTables + sf->nglyphs, sizeof(*breaks), sizecmp); |
680 | j = lastbreak = 0; |
681 | for (i = 0; i < sf->len; i++) { |
682 | if ((i - lastbreak) % 38 == 0) fprintf(ofp, "\n"); |
683 | if (i == breaks[j]) { |
684 | while (i == breaks[j]) j++; |
685 | lastbreak = i; |
686 | fprintf(ofp, "00><\n"); |
687 | } |
688 | fprintf(ofp, "%02x", *((unsigned char *)sf->data + i)); |
689 | } |
690 | fprintf(ofp, "00>] readonly def\n"); |
691 | sfree(breaks); |
692 | fprintf(ofp, "end /%s exch definefont\n", fi->name); |
693 | } |