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