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