Correct a comment.
[sgt/halibut] / in_sfnt.c
CommitLineData
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
24typedef struct sfnt_decode_Tag sfnt_decode;
25struct 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 32static 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 39static 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
45static 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
51static 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
58static 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 66static 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
74static 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
81static 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
88static 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
98static 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 */
108sfnt_decode uint16_decode[] = { { d_uint16, 0 }, { d_end } };
109sfnt_decode int16_decode[] = { { d_int16, 0 }, { d_end } };
110sfnt_decode uint32_decode[] = { { d_uint32, 0 }, { d_end } };
111
112/* Offset subdirectory -- the start of the file */
113typedef struct offsubdir_Tag offsubdir;
114struct offsubdir_Tag {
115 unsigned scaler_type;
116 unsigned numTables;
117};
118sfnt_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 */
137typedef struct tabledir_Tag tabledir;
138struct tabledir_Tag {
139 unsigned tag;
140 unsigned checkSum;
141 unsigned offset;
142 unsigned length;
143};
144sfnt_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 */
153typedef struct t_cmap_Tag t_cmap;
154struct t_cmap_Tag {
155 unsigned numTables;
156};
157sfnt_decode t_cmap_decode[] = {
158 { d_skip(2) },
159 { d_uint16, offsetof(t_cmap, numTables) },
160 { d_end }
161};
162typedef struct encodingrec_Tag encodingrec;
163struct encodingrec_Tag {
164 unsigned platformID;
165 unsigned encodingID;
166 unsigned offset;
167};
168sfnt_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};
174typedef struct cmap4_Tag cmap4;
175struct cmap4_Tag {
176 unsigned length;
177 unsigned segCountX2;
178};
179sfnt_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 */
189typedef struct t_head_Tag t_head;
190struct 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};
198sfnt_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 */
216typedef struct t_hhea_Tag t_hhea;
217struct t_hhea_Tag {
218 unsigned version;
219 int ascent;
220 int descent;
221 int lineGap;
222 int metricDataFormat;
223 unsigned numOfLongHorMetrics;
224};
225sfnt_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 */
237sfnt_decode longhormetric_decode[] = {
238 { d_uint16, 0 },
239 { d_skip(2) },
240 { d_end }
241};
242
243/* Naming ('name') table */
244typedef struct t_name_Tag t_name;
245typedef struct namerecord_Tag namerecord;
246struct t_name_Tag {
247 unsigned format;
248 unsigned count;
249 unsigned stringOffset;
250 namerecord *nameRecord;
251};
252sfnt_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};
258struct namerecord_Tag {
259 unsigned platformID;
260 unsigned encodingID;
261 unsigned languageID;
262 unsigned nameID;
263 unsigned length;
264 unsigned offset;
265};
266sfnt_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 */
277typedef struct t_post_Tag t_post;
278struct 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};
287sfnt_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
299typedef struct {
300 glyph name;
301 unsigned short index;
302} glyphmap;
303
304typedef struct sfnt_Tag sfnt;
305struct 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
318static 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
332static 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 */
369static 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 */
429void 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 */
468void 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
548void 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
606static 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
618void 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}