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/> |
1a61f569 |
15 | * |
16 | * Microsoft typography specifications: |
17 | * <http://www.microsoft.com/typography/SpecificationsOverview.mspx> |
2729eafc |
18 | */ |
19 | |
20 | #include <assert.h> |
21 | #include <stddef.h> |
22 | #include <stdio.h> |
23 | #include <stdlib.h> |
24 | #include "halibut.h" |
25 | #include "paper.h" |
26 | |
27 | typedef struct sfnt_decode_Tag sfnt_decode; |
28 | struct sfnt_decode_Tag { |
29 | void (*decoder)(void *src, void *dest); |
30 | size_t src_len; |
31 | size_t dest_offset; |
32 | }; |
33 | |
18e7663a |
34 | #if 0 /* unused */ |
2729eafc |
35 | static void decode_uint8(void *src, void *dest) { |
36 | *(unsigned int *)dest = *(unsigned char *)src; |
37 | } |
38 | #define d_uint8 decode_uint8, 1 |
18e7663a |
39 | #endif |
2729eafc |
40 | |
18e7663a |
41 | #if 0 /* unused */ |
2729eafc |
42 | static void decode_int8(void *src, void *dest) { |
43 | *(int *)dest = *(signed char *)src; |
44 | } |
45 | #define d_int8 decode_int8, 1 |
18e7663a |
46 | #endif |
2729eafc |
47 | |
48 | static void decode_uint16(void *src, void *dest) { |
49 | unsigned char *cp = src; |
50 | *(unsigned int *)dest = (cp[0] << 8) + cp[1]; |
51 | } |
52 | #define d_uint16 decode_uint16, 2 |
53 | |
54 | static void decode_int16(void *src, void *dest) { |
55 | signed char *cp = src; |
56 | unsigned char *ucp = src; |
57 | *(int *)dest = (cp[0] << 8) + ucp[1]; |
58 | } |
59 | #define d_int16 decode_int16, 2 |
60 | |
61 | static void decode_uint32(void *src, void *dest) { |
62 | unsigned char *cp = src; |
63 | *(unsigned int *)dest = |
64 | (cp[0] << 24) + (cp[1] << 16) + (cp[2] << 8) + cp[3]; |
65 | } |
66 | #define d_uint32 decode_uint32, 4 |
67 | |
68 | static void decode_int32(void *src, void *dest) { |
69 | signed char *cp = src; |
70 | unsigned char *ucp = src; |
71 | *(int *)dest = (cp[0] << 24) + (ucp[1] << 16) + (ucp[2] << 8) + ucp[3]; |
72 | } |
73 | #define d_int32 decode_int32, 4 |
74 | |
75 | static void decode_skip(void *src, void *dest) { |
18e7663a |
76 | IGNORE(src); |
77 | IGNORE(dest); |
2729eafc |
78 | /* do nothing */ |
79 | } |
80 | #define d_skip(n) decode_skip, (n), 0 |
81 | |
82 | static void decode_end(void *src, void *dest) { |
18e7663a |
83 | IGNORE(src); |
84 | IGNORE(dest); |
2729eafc |
85 | /* never called */ |
86 | } |
87 | #define d_end decode_end, 0, 0 |
88 | |
89 | static void *decode(sfnt_decode *dec, void *src, void *end, void *dest) { |
90 | while (dec->decoder != decode_end) { |
91 | if ((char *)src + dec->src_len > (char *)end) return NULL; |
92 | dec->decoder(src, (char *)dest + dec->dest_offset); |
93 | src = (char *)src + dec->src_len; |
94 | dec++; |
95 | } |
96 | return src; |
97 | } |
98 | |
99 | static void *decoden(sfnt_decode *dec, void *src, void *end, void *dest, |
100 | size_t size, size_t n) { |
101 | while (n-- && src) { |
102 | src = decode(dec, src, end, dest); |
103 | dest = (char *)dest + size; |
104 | } |
105 | return src; |
106 | } |
107 | |
108 | /* Decoding specs for simple data types */ |
109 | sfnt_decode uint16_decode[] = { { d_uint16, 0 }, { d_end } }; |
110 | sfnt_decode int16_decode[] = { { d_int16, 0 }, { d_end } }; |
111 | sfnt_decode uint32_decode[] = { { d_uint32, 0 }, { d_end } }; |
112 | |
113 | /* Offset subdirectory -- the start of the file */ |
114 | typedef struct offsubdir_Tag offsubdir; |
115 | struct offsubdir_Tag { |
116 | unsigned scaler_type; |
117 | unsigned numTables; |
118 | }; |
119 | sfnt_decode offsubdir_decode[] = { |
120 | { d_uint32, offsetof(offsubdir, scaler_type) }, |
121 | { d_uint16, offsetof(offsubdir, numTables) }, |
122 | { d_skip(6) }, |
123 | { d_end } |
124 | }; |
125 | |
126 | #define sfnt_00010000 0x00010000 |
39d3d900 |
127 | #define TAG_OS_2 0x4f532f32 |
2729eafc |
128 | #define TAG_cmap 0x636d6170 |
129 | #define TAG_glyf 0x676c7966 |
130 | #define TAG_head 0x68656164 |
131 | #define TAG_hhea 0x68686561 |
132 | #define TAG_hmtx 0x686d7478 |
1a61f569 |
133 | #define TAG_kern 0x6b65726e |
2729eafc |
134 | #define TAG_loca 0x6c6f6361 |
cb93ccb7 |
135 | #define TAG_maxp 0x6d617870 |
2729eafc |
136 | #define TAG_name 0x6e616d65 |
137 | #define TAG_post 0x706f7374 |
138 | #define sfnt_true 0x74727565 |
139 | |
140 | /* Table directory */ |
141 | typedef struct tabledir_Tag tabledir; |
142 | struct tabledir_Tag { |
143 | unsigned tag; |
144 | unsigned checkSum; |
145 | unsigned offset; |
146 | unsigned length; |
147 | }; |
148 | sfnt_decode tabledir_decode[] = { |
149 | { d_uint32, offsetof(tabledir, tag) }, |
150 | { d_uint32, offsetof(tabledir, checkSum) }, |
151 | { d_uint32, offsetof(tabledir, offset) }, |
152 | { d_uint32, offsetof(tabledir, length) }, |
153 | { d_end } |
154 | }; |
155 | |
39d3d900 |
156 | /* OS/2 and Windows compatibility table */ |
157 | typedef struct t_OS_2_Tag t_OS_2; |
158 | struct t_OS_2_Tag { |
159 | unsigned version; |
160 | int sTypoAscender, sTypoDescender; |
161 | int sxHeight, sCapHeight; |
162 | }; |
163 | sfnt_decode t_OS_2_v0_decode[] = { |
164 | { d_uint16, offsetof(t_OS_2, version) }, |
165 | { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */ |
166 | /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */ |
167 | /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */ |
168 | /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */ |
169 | /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */ |
170 | /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */ |
171 | { d_end } |
172 | }; |
173 | sfnt_decode t_OS_2_v1_decode[] = { |
174 | { d_uint16, offsetof(t_OS_2, version) }, |
175 | { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */ |
176 | /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */ |
177 | /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */ |
178 | /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */ |
179 | /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */ |
180 | /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */ |
181 | { d_int16, offsetof(t_OS_2, sTypoAscender) }, |
182 | { d_int16, offsetof(t_OS_2, sTypoDescender) }, |
183 | { d_skip(14) }, /* sTypoLineGap, usWinAscent, usWinDescent, */ |
184 | /* ulCodePageRange1, ulCodePageRange2 */ |
185 | { d_end } |
186 | }; |
187 | sfnt_decode t_OS_2_v2_decode[] = { |
188 | { d_uint16, offsetof(t_OS_2, version) }, |
189 | { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */ |
190 | /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */ |
191 | /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */ |
192 | /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */ |
193 | /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */ |
194 | /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */ |
195 | { d_int16, offsetof(t_OS_2, sTypoAscender) }, |
196 | { d_int16, offsetof(t_OS_2, sTypoDescender) }, |
197 | { d_skip(14) }, /* sTypoLineGap, usWinAscent, usWinDescent, */ |
198 | /* ulCodePageRange1, ulCodePageRange2 */ |
199 | { d_int16, offsetof(t_OS_2, sxHeight) }, |
200 | { d_int16, offsetof(t_OS_2, sCapHeight) }, |
201 | { d_skip(6) }, /* usDefaultChar, usBreakChar, usMaxContext */ |
202 | { d_end } |
203 | }; |
204 | |
2729eafc |
205 | /* Character to Glyph ('cmap') table */ |
206 | typedef struct t_cmap_Tag t_cmap; |
207 | struct t_cmap_Tag { |
208 | unsigned numTables; |
209 | }; |
210 | sfnt_decode t_cmap_decode[] = { |
211 | { d_skip(2) }, |
212 | { d_uint16, offsetof(t_cmap, numTables) }, |
213 | { d_end } |
214 | }; |
215 | typedef struct encodingrec_Tag encodingrec; |
216 | struct encodingrec_Tag { |
217 | unsigned platformID; |
218 | unsigned encodingID; |
219 | unsigned offset; |
220 | }; |
221 | sfnt_decode encodingrec_decode[] = { |
222 | { d_uint16, offsetof(encodingrec, platformID) }, |
223 | { d_uint16, offsetof(encodingrec, encodingID) }, |
224 | { d_uint32, offsetof(encodingrec, offset) }, |
225 | { d_end } |
226 | }; |
227 | typedef struct cmap4_Tag cmap4; |
228 | struct cmap4_Tag { |
229 | unsigned length; |
230 | unsigned segCountX2; |
231 | }; |
232 | sfnt_decode cmap4_decode[] = { |
233 | { d_skip(2) }, /* format */ |
234 | { d_uint16, offsetof(cmap4, length) }, |
235 | { d_skip(2) }, /* language */ |
236 | { d_uint16, offsetof(cmap4, segCountX2) }, |
237 | { d_skip(6) }, /* searchRange, entrySelector, rangeShift */ |
238 | { d_end } |
239 | }; |
240 | |
241 | /* Font Header ('head') table */ |
242 | typedef struct t_head_Tag t_head; |
243 | struct t_head_Tag { |
244 | unsigned version; |
245 | unsigned fontRevision; |
246 | unsigned flags; |
247 | unsigned unitsPerEm; |
248 | int xMin, yMin, xMax, yMax; |
249 | int indexToLocFormat; |
250 | }; |
251 | sfnt_decode t_head_decode[] = { |
252 | { d_uint32, offsetof(t_head, version) }, |
253 | { d_uint32, offsetof(t_head, fontRevision) }, |
254 | { d_skip(8) }, /* checkSumAdjustment, magicNumber, flags */ |
255 | { d_uint16, offsetof(t_head, flags) }, |
256 | { d_uint16, offsetof(t_head, unitsPerEm) }, |
257 | { d_skip(16) }, /* created, modified */ |
258 | { d_int16, offsetof(t_head, xMin) }, |
259 | { d_int16, offsetof(t_head, yMin) }, |
260 | { d_int16, offsetof(t_head, xMax) }, |
261 | { d_int16, offsetof(t_head, yMax) }, |
262 | { d_skip(6) }, /* macStyle, lowestRecPPEM, fontDirectionHint */ |
263 | { d_int16, offsetof(t_head, indexToLocFormat) }, |
264 | { d_skip(2) }, |
265 | { d_end } |
266 | }; |
267 | |
268 | /* Horizontal Header ('hhea') table */ |
269 | typedef struct t_hhea_Tag t_hhea; |
270 | struct t_hhea_Tag { |
271 | unsigned version; |
272 | int ascent; |
273 | int descent; |
274 | int lineGap; |
275 | int metricDataFormat; |
276 | unsigned numOfLongHorMetrics; |
277 | }; |
278 | sfnt_decode t_hhea_decode[] = { |
279 | { d_uint32, offsetof(t_hhea, version) }, |
280 | { d_int16, offsetof(t_hhea, ascent) }, |
281 | { d_int16, offsetof(t_hhea, descent) }, |
282 | { d_int16, offsetof(t_hhea, lineGap) }, |
283 | { d_skip(22) }, |
284 | { d_int16, offsetof(t_hhea, metricDataFormat) }, |
285 | { d_uint16, offsetof(t_hhea, numOfLongHorMetrics) }, |
286 | { d_end } |
287 | }; |
288 | |
289 | /* Horizontal Metrics ('hmtx') table */ |
290 | sfnt_decode longhormetric_decode[] = { |
291 | { d_uint16, 0 }, |
292 | { d_skip(2) }, |
293 | { d_end } |
294 | }; |
295 | |
1a61f569 |
296 | /* Kerning ('kern') table */ |
297 | typedef struct t_kern_Tag t_kern; |
298 | struct t_kern_Tag { |
299 | unsigned version; |
300 | unsigned nTables; |
301 | }; |
302 | sfnt_decode t_kern_v0_decode[] = { |
303 | { d_uint16, offsetof(t_kern, version) }, |
304 | { d_uint16, offsetof(t_kern, nTables) }, |
305 | { d_end } |
306 | }; |
307 | typedef struct kern_v0_subhdr_Tag kern_v0_subhdr; |
308 | struct kern_v0_subhdr_Tag { |
309 | unsigned version; |
310 | unsigned length; |
311 | unsigned coverage; |
312 | }; |
313 | sfnt_decode kern_v0_subhdr_decode[] = { |
314 | { d_uint16, offsetof(kern_v0_subhdr, version) }, |
315 | { d_uint16, offsetof(kern_v0_subhdr, length) }, |
316 | { d_uint16, offsetof(kern_v0_subhdr, coverage) }, |
317 | { d_end } |
318 | }; |
319 | #define KERN_V0_HORIZ 0x0001 |
320 | #define KERN_V0_MINIMUM 0x0002 |
321 | #define KERN_V0_CROSSSTREAM 0x0004 |
322 | #define KERN_V0_OVERRIDE 0x0008 |
323 | #define KERN_V0_FORMAT 0xff00 |
324 | #define KERN_V0_FORMAT_0 0x0000 |
325 | sfnt_decode t_kern_v1_decode[] = { |
326 | { d_uint32, offsetof(t_kern, version) }, |
327 | { d_uint32, offsetof(t_kern, nTables) }, |
328 | { d_end } |
329 | }; |
330 | typedef struct kern_v1_subhdr_Tag kern_v1_subhdr; |
331 | struct kern_v1_subhdr_Tag { |
332 | unsigned length; |
333 | unsigned coverage; |
334 | }; |
335 | sfnt_decode kern_v1_subhdr_decode[] = { |
336 | { d_uint32, offsetof(kern_v1_subhdr, length) }, |
337 | { d_uint16, offsetof(kern_v1_subhdr, coverage) }, |
338 | { d_skip(2) }, /* tupleIndex */ |
339 | { d_end } |
340 | }; |
341 | #define KERN_V1_VERTICAL 0x8000 |
342 | #define KERN_V1_CROSSSTREAM 0x4000 |
343 | #define KERN_V1_VARIATION 0x2000 |
344 | #define KERN_V1_FORMAT 0x00ff |
345 | #define KERN_V1_FORMAT_0 0x0000 |
346 | typedef struct kern_f0_Tag kern_f0; |
347 | struct kern_f0_Tag { |
348 | unsigned nPairs; |
349 | }; |
350 | sfnt_decode kern_f0_decode[] = { |
351 | { d_uint16, offsetof(kern_f0, nPairs) }, |
352 | { d_skip(6) }, /* searchRange, entrySelector, rangeShift */ |
353 | { d_end } |
354 | }; |
355 | typedef struct kern_f0_pair_Tag kern_f0_pair; |
356 | struct kern_f0_pair_Tag { |
357 | unsigned left; |
358 | unsigned right; |
359 | int value; |
360 | }; |
361 | sfnt_decode kern_f0_pair_decode[] = { |
362 | { d_uint16, offsetof(kern_f0_pair, left) }, |
363 | { d_uint16, offsetof(kern_f0_pair, right) }, |
364 | { d_int16, offsetof(kern_f0_pair, value) }, |
365 | { d_end } |
366 | }; |
367 | |
cb93ccb7 |
368 | /* Maximum profile ('maxp') table */ |
369 | typedef struct t_maxp_Tag t_maxp; |
370 | struct t_maxp_Tag { |
371 | unsigned version; |
372 | unsigned numGlyphs; |
373 | }; |
374 | sfnt_decode t_maxp_decode[] = { |
375 | { d_uint32, offsetof(t_maxp, version) }, |
376 | { d_uint16, offsetof(t_maxp, numGlyphs) }, |
377 | { d_end } |
378 | }; |
379 | |
2729eafc |
380 | /* Naming ('name') table */ |
381 | typedef struct t_name_Tag t_name; |
382 | typedef struct namerecord_Tag namerecord; |
383 | struct t_name_Tag { |
384 | unsigned format; |
385 | unsigned count; |
386 | unsigned stringOffset; |
387 | namerecord *nameRecord; |
388 | }; |
389 | sfnt_decode t_name_decode[] = { |
390 | { d_uint16, offsetof(t_name, format) }, |
391 | { d_uint16, offsetof(t_name, count) }, |
392 | { d_uint16, offsetof(t_name, stringOffset) }, |
393 | { d_end } |
394 | }; |
395 | struct namerecord_Tag { |
396 | unsigned platformID; |
397 | unsigned encodingID; |
398 | unsigned languageID; |
399 | unsigned nameID; |
400 | unsigned length; |
401 | unsigned offset; |
402 | }; |
403 | sfnt_decode namerecord_decode[] = { |
404 | { d_uint16, offsetof(namerecord, platformID) }, |
405 | { d_uint16, offsetof(namerecord, encodingID) }, |
406 | { d_uint16, offsetof(namerecord, languageID) }, |
407 | { d_uint16, offsetof(namerecord, nameID) }, |
408 | { d_uint16, offsetof(namerecord, length) }, |
409 | { d_uint16, offsetof(namerecord, offset) }, |
410 | { d_end } |
411 | }; |
412 | |
413 | /* PostScript compatibility ('post') table */ |
414 | typedef struct t_post_Tag t_post; |
415 | struct t_post_Tag { |
416 | unsigned format; |
47d88706 |
417 | int italicAngle; |
2729eafc |
418 | int underlinePosition; |
419 | int underlineThickness; |
420 | unsigned isFixedPitch; |
421 | unsigned minMemType42; |
422 | unsigned maxMemType42; |
423 | }; |
424 | sfnt_decode t_post_decode[] = { |
425 | { d_uint32, offsetof(t_post, format) }, |
47d88706 |
426 | { d_int32, offsetof(t_post, italicAngle) }, |
2729eafc |
427 | { d_int16, offsetof(t_post, underlinePosition) }, |
428 | { d_int16, offsetof(t_post, underlineThickness) }, |
429 | { d_uint32, offsetof(t_post, isFixedPitch) }, |
430 | { d_uint32, offsetof(t_post, minMemType42) }, |
431 | { d_uint32, offsetof(t_post, maxMemType42) }, |
432 | { d_skip(8) }, /* minMemType1, maxMemType1 */ |
433 | { d_end } |
434 | }; |
435 | |
436 | typedef struct { |
437 | glyph name; |
438 | unsigned short index; |
439 | } glyphmap; |
440 | |
2729eafc |
441 | struct sfnt_Tag { |
442 | void *data; |
443 | size_t len; |
444 | void *end; |
d41ae9ad |
445 | filepos pos; |
2729eafc |
446 | offsubdir osd; |
447 | tabledir *td; |
448 | t_head head; |
449 | unsigned nglyphs; |
450 | glyph *glyphsbyindex; |
451 | unsigned short *glyphsbyname; |
452 | unsigned minmem, maxmem; |
453 | }; |
454 | |
455 | static int sfnt_findtable(sfnt *sf, unsigned tag, |
456 | void **startp, void **endp) { |
457 | size_t i; |
458 | |
459 | for (i = 0; i < sf->osd.numTables; i++) { |
460 | if (sf->td[i].tag == tag) { |
461 | *startp = (char *)sf->data + sf->td[i].offset; |
462 | *endp = (char *)*startp + sf->td[i].length; |
463 | return TRUE; |
464 | } |
465 | } |
466 | return FALSE; |
467 | } |
468 | |
469 | static char *sfnt_psname(font_info *fi) { |
470 | sfnt *sf = fi->fontfile; |
471 | t_name name; |
472 | void *ptr, *end; |
473 | size_t i; |
474 | char *psname; |
475 | namerecord *nr; |
476 | |
d41ae9ad |
477 | if (!sfnt_findtable(sf, TAG_name, &ptr, &end)) { |
478 | error(err_sfntnotable, &sf->pos, "name"); |
479 | return NULL; |
480 | } |
2729eafc |
481 | ptr = decode(t_name_decode, ptr, end, &name); |
482 | name.nameRecord = snewn(name.count, namerecord); |
483 | ptr = decoden(namerecord_decode, ptr, sf->end, name.nameRecord, |
484 | sizeof(*name.nameRecord), name.count); |
485 | for (i = 0; i < name.count; i++) { |
486 | nr = name.nameRecord + i; |
487 | if (nr->nameID == 6) { |
488 | /* PostScript name, but can we make sense of it? */ |
489 | if (nr->platformID == 1 && nr->encodingID == 0) { |
490 | /* Mac Roman, which is ASCII for our purposes */ |
491 | psname = snewn(nr->length + 1, char); |
492 | memcpy(psname, (char *)ptr + nr->offset, nr->length); |
493 | psname[nr->length] = 0; |
494 | sfree(name.nameRecord); |
495 | return psname; |
496 | } |
497 | } |
498 | } |
d41ae9ad |
499 | error(err_sfntnopsname, &sf->pos); |
2729eafc |
500 | return NULL; |
501 | } |
502 | |
47d88706 |
503 | static unsigned short *cmp_glyphsbyindex; |
504 | static int glyphsbyname_cmp(void const *a, void const *b) { |
505 | glyph ga = cmp_glyphsbyindex[*(unsigned short *)a]; |
506 | glyph gb = cmp_glyphsbyindex[*(unsigned short *)b]; |
507 | if (ga < gb) return -1; |
508 | if (ga > gb) return 1; |
f4833a3c |
509 | /* For de-duping, we'd prefer to have the first glyph stay first */ |
510 | if (*(unsigned short *)a < *(unsigned short *)b) return -1; |
511 | if (*(unsigned short *)a > *(unsigned short *)b) return 1; |
47d88706 |
512 | return 0; |
513 | } |
514 | static int glyphsbyname_cmp_search(void const *a, void const *b) { |
515 | glyph ga = *(glyph *)a; |
516 | glyph gb = cmp_glyphsbyindex[*(unsigned short *)b]; |
517 | if (ga < gb) return -1; |
518 | if (ga > gb) return 1; |
519 | return 0; |
520 | } |
521 | |
cb93ccb7 |
522 | /* Generate an name for a glyph that doesn't have one. */ |
523 | static glyph genglyph(unsigned idx) { |
524 | char buf[11]; |
525 | if (idx == 0) return glyph_intern(".notdef"); |
526 | sprintf(buf, "glyph%u", idx); |
527 | return glyph_intern(buf); |
528 | } |
529 | |
2729eafc |
530 | /* |
531 | * Extract data from the 'post' table (mostly glyph mappings) |
532 | * |
533 | * TODO: cope better with duplicated glyph names (usually .notdef) |
534 | * TODO: when presented with format 3.0, try to use 'CFF' if present. |
535 | */ |
47d88706 |
536 | static void sfnt_mapglyphs(font_info *fi) { |
537 | sfnt *sf = fi->fontfile; |
2729eafc |
538 | t_post post; |
539 | void *ptr, *end; |
18e7663a |
540 | unsigned char *sptr; |
541 | char tmp[256]; |
f4833a3c |
542 | glyph *extraglyphs, prev, this; |
543 | unsigned nextras, i, g, suflen; |
2729eafc |
544 | |
545 | sf->glyphsbyname = sf->glyphsbyindex = NULL; |
cb93ccb7 |
546 | if (sfnt_findtable(sf, TAG_post, &ptr, &end)) { |
547 | ptr = decode(t_post_decode, ptr, end, &post); |
548 | if (ptr == NULL) { |
549 | error(err_sfntbadtable, &sf->pos, "post"); |
550 | goto noglyphs; |
551 | } |
47d88706 |
552 | |
cb93ccb7 |
553 | sf->minmem = post.minMemType42; |
554 | sf->maxmem = post.maxMemType42; |
555 | fi->italicangle = post.italicAngle / 65536.0; |
556 | switch (post.format) { |
557 | case 0x00010000: |
558 | if (sf->nglyphs != 258) { |
559 | error(err_sfntbadtable, &sf->pos, "post"); |
560 | break; |
561 | } |
562 | sf->glyphsbyindex = (glyph *)tt_std_glyphs; |
563 | break; |
564 | case 0x00020000: |
565 | if ((char *)ptr + 2 > (char *)end) { |
566 | error(err_sfntbadtable, &sf->pos, "post"); |
567 | break; |
568 | } |
569 | ptr = (char *)ptr + 2; |
570 | if ((char *)ptr + 2*sf->nglyphs > (char *)end) { |
571 | error(err_sfntbadtable, &sf->pos, "post"); |
572 | break; |
573 | } |
574 | nextras = 0; |
575 | for (sptr = (unsigned char *)ptr + 2*sf->nglyphs; |
576 | sptr < (unsigned char *)end; |
577 | sptr += *sptr+1) |
578 | nextras++; |
579 | extraglyphs = snewn(nextras, glyph); |
580 | i = 0; |
581 | for (sptr = (unsigned char *)ptr + 2*sf->nglyphs; |
582 | sptr < (unsigned char *)end; |
583 | sptr += *sptr+1) { |
584 | memcpy(tmp, sptr + 1, *sptr); |
585 | tmp[*sptr] = 0; |
586 | assert(i < nextras); |
587 | extraglyphs[i++] = glyph_intern(tmp); |
588 | } |
589 | sf->glyphsbyindex = snewn(sf->nglyphs, glyph); |
590 | for (i = 0; i < sf->nglyphs; i++) { |
591 | decode_uint16((char *)ptr + 2*i, &g); |
592 | if (g <= 257) |
593 | sf->glyphsbyindex[i] = tt_std_glyphs[g]; |
594 | else if (g < 258 + nextras) |
595 | sf->glyphsbyindex[i] = extraglyphs[g - 258]; |
596 | else { |
597 | error(err_sfntbadtable, &sf->pos, "post"); |
598 | sf->glyphsbyindex[i] = genglyph(i); |
599 | } |
600 | } |
601 | sfree(extraglyphs); |
602 | break; |
603 | case 0x00030000: |
604 | break; |
605 | default: |
606 | error(err_sfnttablevers, &sf->pos, "post"); |
607 | break; |
2729eafc |
608 | } |
cb93ccb7 |
609 | } |
610 | noglyphs: |
611 | if (!sf->glyphsbyindex) { |
2729eafc |
612 | sf->glyphsbyindex = snewn(sf->nglyphs, glyph); |
cb93ccb7 |
613 | for (i = 0; i < sf->nglyphs; i++) |
614 | sf->glyphsbyindex[i] = genglyph(i); |
2729eafc |
615 | } |
47d88706 |
616 | /* Construct glyphsbyname */ |
617 | sf->glyphsbyname = snewn(sf->nglyphs, unsigned short); |
618 | for (i = 0; i < sf->nglyphs; i++) |
619 | sf->glyphsbyname[i] = i; |
620 | cmp_glyphsbyindex = sf->glyphsbyindex; |
621 | qsort(sf->glyphsbyname, sf->nglyphs, sizeof(*sf->glyphsbyname), |
622 | glyphsbyname_cmp); |
f4833a3c |
623 | /* |
624 | * It's possible for fonts to specify the same name for multiple |
625 | * glyphs, which would make one of them inaccessible. Check for |
626 | * that, and rename all but one of each set. |
627 | * |
628 | * To ensure that we don't clash with any existing glyph names, |
629 | * our renaming involves appending the glyph number formatted with |
630 | * enough leading zeroes to make it longer than any all-digit |
631 | * suffix that already exists in the font. |
632 | */ |
633 | suflen = 4; |
634 | for (i = 0; i < sf->nglyphs; i++) { |
635 | char const *p; |
636 | p = strrchr(glyph_extern(sfnt_indextoglyph(sf, i)), '.'); |
637 | if (p && !(p+1)[strspn(p+1, "0123456789")] && strlen(p+1) > suflen) |
638 | suflen = strlen(p+1); |
639 | } |
640 | suflen++; |
641 | prev = sfnt_indextoglyph(sf, sf->glyphsbyname[0]); |
642 | for (i = 1; i < sf->nglyphs; i++) { |
643 | if (prev == (this = sfnt_indextoglyph(sf, sf->glyphsbyname[i]))) { |
644 | char const *basename; |
645 | char *buf; |
646 | basename = glyph_extern(this); |
647 | buf = snewn(strlen(basename) + 2 + suflen, char); |
648 | strcpy(buf, basename); |
649 | sprintf(buf + strlen(basename), ".%0*hu", suflen, |
650 | sf->glyphsbyname[i]); |
651 | sf->glyphsbyindex[sf->glyphsbyname[i]] = glyph_intern(buf); |
652 | sfree(buf); |
653 | } |
654 | prev = this; |
655 | } |
656 | /* We may have renamed some glyphs, so re-sort the array. */ |
657 | qsort(sf->glyphsbyname, sf->nglyphs, sizeof(*sf->glyphsbyname), |
658 | glyphsbyname_cmp); |
47d88706 |
659 | } |
660 | |
e7d4c88f |
661 | glyph sfnt_indextoglyph(sfnt *sf, unsigned idx) { |
47d88706 |
662 | return sf->glyphsbyindex[idx]; |
663 | } |
664 | |
e7d4c88f |
665 | unsigned sfnt_nglyphs(sfnt *sf) { |
666 | return sf->nglyphs; |
667 | } |
668 | |
669 | unsigned sfnt_glyphtoindex(sfnt *sf, glyph g) { |
47d88706 |
670 | cmp_glyphsbyindex = sf->glyphsbyindex; |
671 | return *(unsigned short *)bsearch(&g, sf->glyphsbyname, sf->nglyphs, |
672 | sizeof(*sf->glyphsbyname), |
673 | glyphsbyname_cmp_search); |
2729eafc |
674 | } |
675 | |
676 | /* |
39d3d900 |
677 | * Get data from 'hhea', 'hmtx', and 'OS/2' tables |
2729eafc |
678 | */ |
679 | void sfnt_getmetrics(font_info *fi) { |
680 | sfnt *sf = fi->fontfile; |
681 | t_hhea hhea; |
39d3d900 |
682 | t_OS_2 OS_2; |
2729eafc |
683 | void *ptr, *end; |
684 | unsigned i, j; |
685 | unsigned *hmtx; |
686 | |
39d3d900 |
687 | /* First, the bounding box from the 'head' table. */ |
688 | fi->fontbbox[0] = sf->head.xMin * FUNITS_PER_PT / sf->head.unitsPerEm; |
689 | fi->fontbbox[1] = sf->head.yMin * FUNITS_PER_PT / sf->head.unitsPerEm; |
690 | fi->fontbbox[2] = sf->head.xMax * FUNITS_PER_PT / sf->head.unitsPerEm; |
691 | fi->fontbbox[3] = sf->head.yMax * FUNITS_PER_PT / sf->head.unitsPerEm; |
d41ae9ad |
692 | if (!sfnt_findtable(sf, TAG_hhea, &ptr, &end)) { |
693 | error(err_sfntnotable, &sf->pos, "hhea"); |
694 | return; |
695 | } |
696 | if (decode(t_hhea_decode, ptr, end, &hhea) == NULL) { |
697 | error(err_sfntbadtable, &sf->pos, "hhea"); |
698 | return; |
699 | } |
700 | if ((hhea.version & 0xffff0000) != 0x00010000) { |
701 | error(err_sfnttablevers, &sf->pos, "hhea"); |
702 | return; |
703 | } |
2729eafc |
704 | fi->ascent = hhea.ascent; |
705 | fi->descent = hhea.descent; |
d41ae9ad |
706 | if (hhea.metricDataFormat != 0) { |
707 | error(err_sfnttablevers, &sf->pos, "hmtx"); |
708 | return; |
709 | } |
710 | if (!sfnt_findtable(sf, TAG_hmtx, &ptr, &end)) { |
711 | error(err_sfntnotable, &sf->pos, "hmtx"); |
712 | return; |
713 | } |
2729eafc |
714 | hmtx = snewn(hhea.numOfLongHorMetrics, unsigned); |
715 | if (decoden(longhormetric_decode, ptr, end, hmtx, sizeof(*hmtx), |
d41ae9ad |
716 | hhea.numOfLongHorMetrics) == NULL) { |
717 | error(err_sfntbadtable, &sf->pos, "hmtx"); |
718 | return; |
719 | } |
2729eafc |
720 | for (i = 0; i < sf->nglyphs; i++) { |
721 | glyph_width *w = snew(glyph_width); |
47d88706 |
722 | w->glyph = sfnt_indextoglyph(sf, i); |
2729eafc |
723 | j = i < hhea.numOfLongHorMetrics ? i : hhea.numOfLongHorMetrics - 1; |
724 | w->width = hmtx[j] * UNITS_PER_PT / sf->head.unitsPerEm; |
725 | add234(fi->widths, w); |
726 | } |
39d3d900 |
727 | /* Now see if the 'OS/2' table has any useful metrics */ |
728 | if (!sfnt_findtable(sf, TAG_OS_2, &ptr, &end)) |
729 | return; |
730 | if (decode(uint16_decode, ptr, end, &OS_2.version) == NULL) |
d41ae9ad |
731 | goto bados2; |
39d3d900 |
732 | if (OS_2.version >= 2) { |
733 | if (decode(t_OS_2_v2_decode, ptr, end, &OS_2) == NULL) |
d41ae9ad |
734 | goto bados2; |
39d3d900 |
735 | fi->xheight = OS_2.sxHeight * FUNITS_PER_PT / sf->head.unitsPerEm; |
736 | fi->capheight = OS_2.sCapHeight * FUNITS_PER_PT / sf->head.unitsPerEm; |
737 | } else if (OS_2.version == 1) { |
738 | if (decode(t_OS_2_v1_decode, ptr, end, &OS_2) == NULL) |
d41ae9ad |
739 | goto bados2; |
39d3d900 |
740 | } else |
741 | return; |
742 | fi->ascent = OS_2.sTypoAscender * FUNITS_PER_PT / sf->head.unitsPerEm; |
743 | fi->descent = OS_2.sTypoDescender * FUNITS_PER_PT / sf->head.unitsPerEm; |
d41ae9ad |
744 | return; |
745 | bados2: |
746 | error(err_sfntbadtable, &sf->pos, "OS/2"); |
2729eafc |
747 | } |
748 | |
749 | /* |
1a61f569 |
750 | * Get kerning data from a 'kern' table |
751 | * |
752 | * 'kern' tables have two gratuitously different header formats, one |
753 | * used by Apple and one by Microsoft. Happily, the kerning tables |
754 | * themselves use the same formats. Halibut only supports simple kern |
755 | * pairs for horizontal kerning of horizontal text, and ignores |
756 | * everything else. |
757 | */ |
758 | static void sfnt_getkern(font_info *fi) { |
759 | sfnt *sf = fi->fontfile; |
760 | t_kern kern; |
761 | unsigned version, i, j; |
762 | void *ptr, *end; |
763 | |
764 | if (!sfnt_findtable(sf, TAG_kern, &ptr, &end)) |
765 | return; |
766 | if (!decode(uint16_decode, ptr, end, &version)) |
d41ae9ad |
767 | goto bad; |
1a61f569 |
768 | if (version == 0) |
769 | ptr = decode(t_kern_v0_decode, ptr, end, &kern); |
770 | else if (version == 1) |
771 | ptr = decode(t_kern_v1_decode, ptr, end, &kern); |
772 | else return; |
d41ae9ad |
773 | if (ptr == NULL) goto bad; |
1a61f569 |
774 | for (i = 0; i < kern.nTables; i++) { |
775 | kern_f0 f0; |
776 | kern_pair *kerns; |
777 | if (version == 0) { |
778 | kern_v0_subhdr sub; |
779 | ptr = decode(kern_v0_subhdr_decode, ptr, end, &sub); |
d41ae9ad |
780 | if (ptr == NULL) goto bad; |
1a61f569 |
781 | if (sub.version != 0 || |
782 | (sub.coverage & (KERN_V0_HORIZ | KERN_V0_MINIMUM | |
783 | KERN_V0_CROSSSTREAM | KERN_V0_FORMAT)) != |
784 | (KERN_V0_HORIZ | KERN_V0_FORMAT_0)) { |
785 | ptr = (char *)ptr + sub.length - 6; |
786 | continue; |
787 | } |
788 | } else { |
789 | kern_v1_subhdr sub; |
790 | ptr = decode(kern_v1_subhdr_decode, ptr, end, &sub); |
d41ae9ad |
791 | if (ptr == NULL) goto bad; |
1a61f569 |
792 | if ((sub.coverage & (KERN_V1_VERTICAL | KERN_V1_CROSSSTREAM | |
793 | KERN_V1_VARIATION | KERN_V1_FORMAT)) != |
794 | KERN_V0_FORMAT_0) { |
795 | ptr = (char *)ptr + sub.length - 8; |
796 | continue; |
797 | } |
798 | } |
799 | ptr = decode(kern_f0_decode, ptr, end, &f0); |
d41ae9ad |
800 | if (ptr == NULL) goto bad; |
1a61f569 |
801 | kerns = snewn(f0.nPairs, kern_pair); |
802 | for (j = 0; j < f0.nPairs; j++) { |
803 | kern_f0_pair p; |
804 | kern_pair *kp = kerns + j; |
805 | ptr = decode(kern_f0_pair_decode, ptr, end, &p); |
d41ae9ad |
806 | if (ptr == NULL) goto bad; |
807 | if (p.left >= sf->nglyphs || p.right >= sf->nglyphs) goto bad; |
1a61f569 |
808 | kp->left = sfnt_indextoglyph(sf, p.left); |
809 | kp->right = sfnt_indextoglyph(sf, p.right); |
810 | kp->kern = p.value * UNITS_PER_PT / (int)sf->head.unitsPerEm; |
811 | add234(fi->kerns, kp); |
812 | } |
813 | } |
d41ae9ad |
814 | return; |
815 | bad: |
816 | error(err_sfntbadtable, &sf->pos, "kern"); |
817 | return; |
1a61f569 |
818 | } |
819 | |
820 | /* |
2729eafc |
821 | * Get mapping data from 'cmap' table |
822 | * |
f322d7ec |
823 | * We look for either a (0, 0), (0, 2), (0, 3), or (3, 1) table, all |
824 | * of these being versions of UCS-2. We ignore (0, 1), since it's |
825 | * Unicode 1.1 with precomposed Hangul syllables. We only handle |
826 | * format 4 of this table, since that seems to be the only one in use. |
2729eafc |
827 | */ |
828 | void sfnt_getmap(font_info *fi) { |
829 | sfnt *sf = fi->fontfile; |
830 | t_cmap cmap; |
831 | encodingrec *esd; |
832 | void *base, *ptr, *end; |
833 | unsigned i; |
834 | unsigned format; |
835 | |
836 | |
837 | for (i = 0; i < lenof(fi->bmp); i++) |
838 | fi->bmp[i] = 0xFFFF; |
d41ae9ad |
839 | if (!sfnt_findtable(sf, TAG_cmap, &ptr, &end)) { |
840 | error(err_sfntnotable, &sf->pos, "cmap"); |
841 | } |
2729eafc |
842 | base = ptr; |
843 | ptr = decode(t_cmap_decode, ptr, end, &cmap); |
d41ae9ad |
844 | if (ptr == NULL) goto bad; |
2729eafc |
845 | esd = snewn(cmap.numTables, encodingrec); |
846 | ptr = decoden(encodingrec_decode, ptr, end, esd, sizeof(*esd), |
847 | cmap.numTables); |
d41ae9ad |
848 | if (ptr == NULL) goto bad; |
2729eafc |
849 | for (i = 0; i < cmap.numTables; i++) { |
850 | if (!decode(uint16_decode, (char *)base + esd[i].offset, end, &format)) |
d41ae9ad |
851 | goto bad; |
f322d7ec |
852 | if ((esd[i].platformID == 0 && esd[i].encodingID == 0) || |
853 | (esd[i].platformID == 0 && esd[i].encodingID == 2) || |
854 | (esd[i].platformID == 0 && esd[i].encodingID == 3) || |
2729eafc |
855 | (esd[i].platformID == 3 && esd[i].encodingID == 1)) { |
856 | /* UCS-2 encoding */ |
857 | if (!decode(uint16_decode, (char *)base + esd[i].offset, end, |
858 | &format)) |
d41ae9ad |
859 | goto bad; |
2729eafc |
860 | if (format == 4) { |
861 | unsigned *data, *endCode, *startCode, *idDelta, *idRangeOffset; |
862 | unsigned *glyphIndexArray; |
863 | unsigned segcount, nword, nglyphindex, j; |
864 | cmap4 cmap4; |
865 | |
866 | ptr = decode(cmap4_decode, (char *)base + esd[i].offset, end, |
867 | &cmap4); |
d41ae9ad |
868 | if (!ptr) goto bad; |
2729eafc |
869 | segcount = cmap4.segCountX2 / 2; |
870 | nword = cmap4.length / 2 - 7; |
871 | data = snewn(nword, unsigned); |
872 | if (!decoden(uint16_decode, ptr, (char *)ptr + nword * 2, |
d41ae9ad |
873 | data, sizeof(*data), nword)) goto bad; |
2729eafc |
874 | endCode = data; |
875 | startCode = data + segcount + 1; |
876 | idDelta = startCode + segcount; |
877 | idRangeOffset = idDelta + segcount; |
878 | glyphIndexArray = idRangeOffset + segcount; |
879 | nglyphindex = nword - segcount * 4 - 1; |
880 | |
881 | for (j = 0; j < segcount; j++) { |
882 | unsigned k, idx; |
883 | |
884 | if (idRangeOffset[j] == 0) { |
885 | for (k = startCode[j]; k <= endCode[j]; k++) { |
886 | idx = (k + idDelta[j]) & 0xffff; |
887 | if (idx != 0) { |
e997256c |
888 | if (idx > sf->nglyphs) { |
9e0fd4a7 |
889 | error(err_sfntbadglyph, &sf->pos, k); |
e997256c |
890 | continue; |
891 | } |
47d88706 |
892 | fi->bmp[k] = sfnt_indextoglyph(sf, idx); |
2729eafc |
893 | } |
894 | } |
895 | } else { |
896 | unsigned startidx = idRangeOffset[j]/2 - segcount + j; |
897 | for (k = startCode[j]; k <= endCode[j]; k++) { |
e997256c |
898 | if (startidx + k - startCode[j] >= |
899 | nglyphindex) { |
9e0fd4a7 |
900 | error(err_sfntbadglyph, &sf->pos, k); |
e997256c |
901 | continue; |
902 | } |
2729eafc |
903 | idx = glyphIndexArray[startidx + k - startCode[j]]; |
904 | if (idx != 0) { |
905 | idx = (idx + idDelta[j]) & 0xffff; |
e997256c |
906 | if (idx > sf->nglyphs) { |
9e0fd4a7 |
907 | error(err_sfntbadglyph, &sf->pos, k); |
e997256c |
908 | continue; |
909 | } |
47d88706 |
910 | fi->bmp[k] = sfnt_indextoglyph(sf, idx); |
2729eafc |
911 | } |
912 | } |
913 | } |
914 | } |
2729eafc |
915 | sfree(data); |
d41ae9ad |
916 | return; |
2729eafc |
917 | } |
918 | } |
919 | } |
d41ae9ad |
920 | error(err_sfntnounicmap, &sf->pos); |
921 | return; |
922 | bad: |
923 | error(err_sfntbadtable, &sf->pos, "cmap"); |
2729eafc |
924 | } |
925 | |
926 | void read_sfnt_file(input *in) { |
927 | sfnt *sf = snew(sfnt); |
928 | size_t off = 0, got; |
929 | FILE *fp = in->currfp; |
930 | font_info *fi = snew(font_info); |
2729eafc |
931 | void *ptr, *end; |
cb93ccb7 |
932 | t_maxp maxp; |
2729eafc |
933 | |
934 | fi->name = NULL; |
935 | fi->widths = newtree234(width_cmp); |
936 | fi->kerns = newtree234(kern_cmp); |
937 | fi->ligs = newtree234(lig_cmp); |
938 | fi->fontbbox[0] = fi->fontbbox[1] = fi->fontbbox[2] = fi->fontbbox[3] = 0; |
939 | fi->capheight = fi->xheight = fi->ascent = fi->descent = 0; |
940 | fi->stemh = fi->stemv = fi->italicangle = 0; |
941 | fi->fontfile = sf; |
942 | fi->filetype = TRUETYPE; |
943 | |
944 | sf->len = 32768; |
945 | sf->data = snewn(sf->len, unsigned char); |
946 | for (;;) { |
947 | got = fread((char *)sf->data + off, 1, sf->len - off, fp); |
948 | off += got; |
949 | if (off != sf->len) break; |
950 | sf->len *= 2; |
951 | sf->data = sresize(sf->data, sf->len, unsigned char); |
952 | } |
d41ae9ad |
953 | fclose(in->currfp); |
2729eafc |
954 | sf->len = off; |
955 | sf->data = sresize(sf->data, sf->len, unsigned char); |
956 | sf->end = (char *)sf->data + sf->len; |
d41ae9ad |
957 | sf->pos = in->pos; |
958 | sf->pos.line = 0; |
959 | sf->nglyphs = 0; |
960 | ptr = decode(offsubdir_decode, sf->data, sf->end, &sf->osd); |
961 | if (ptr == NULL) { |
962 | error(err_sfntbadhdr, &sf->pos); |
963 | return; |
964 | } |
2729eafc |
965 | sf->td = snewn(sf->osd.numTables, tabledir); |
d41ae9ad |
966 | ptr = decoden(tabledir_decode, ptr, sf->end, sf->td, sizeof(*sf->td), |
967 | sf->osd.numTables); |
968 | if (ptr == NULL) { |
969 | error(err_sfntbadhdr, &sf->pos); |
970 | return; |
971 | } |
972 | if (!sfnt_findtable(sf, TAG_head, &ptr, &end)) { |
973 | error(err_sfntnotable, &sf->pos, "head"); |
974 | return; |
975 | } |
976 | if (decode(t_head_decode, ptr, end, &sf->head) == NULL) { |
977 | error(err_sfntbadtable, &sf->pos, "head"); |
978 | return; |
979 | } |
980 | if ((sf->head.version & 0xffff0000) != 0x00010000) { |
981 | error(err_sfnttablevers, &sf->pos, "head"); |
982 | return; |
983 | } |
cb93ccb7 |
984 | if (!sfnt_findtable(sf, TAG_maxp, &ptr, &end)) { |
985 | error(err_sfntnotable, &sf->pos, "maxp"); |
986 | return; |
987 | } |
988 | if (decode(t_maxp_decode, ptr, end, &maxp) == NULL) { |
989 | error(err_sfntbadtable, &sf->pos, "maxp"); |
990 | return; |
991 | } |
992 | if (maxp.version < 0x00005000 || maxp.version > 0x0001ffff) { |
993 | error(err_sfnttablevers, &sf->pos, "maxp"); |
994 | return; |
995 | } |
996 | sf->nglyphs = maxp.numGlyphs; |
2729eafc |
997 | fi->name = sfnt_psname(fi); |
d41ae9ad |
998 | if (fi->name == NULL) return; |
47d88706 |
999 | sfnt_mapglyphs(fi); |
2729eafc |
1000 | sfnt_getmetrics(fi); |
1a61f569 |
1001 | sfnt_getkern(fi); |
2729eafc |
1002 | sfnt_getmap(fi); |
1003 | fi->next = all_fonts; |
1004 | all_fonts = fi; |
2729eafc |
1005 | } |
1006 | |
1007 | static int sizecmp(const void *a, const void *b) { |
1008 | if (*(size_t *)a < *(size_t *)b) return -1; |
1009 | if (*(size_t *)a > *(size_t *)b) return 1; |
1010 | return 0; |
1011 | } |
1012 | |
1013 | /* |
1014 | * The format for embedding TrueType fonts in Postscript is defined in |
1015 | * Adobe Technical Note #5012: The Type 42 Font Format Specification. |
1016 | * <http://partners.adobe.com/public/developer/en/font/5012.Type42_Spec.pdf> |
1017 | */ |
1018 | |
1019 | void sfnt_writeps(font_info const *fi, FILE *ofp) { |
1020 | unsigned i, j, lastbreak; |
1021 | sfnt *sf = fi->fontfile; |
1022 | size_t *breaks, glyfoff, glyflen; |
1023 | void *glyfptr, *glyfend, *locaptr, *locaend; |
1024 | unsigned *loca; |
c358bf5b |
1025 | int cc = 0; |
2729eafc |
1026 | |
1027 | /* XXX Unclear that this is the correct format. */ |
1028 | fprintf(ofp, "%%!PS-TrueTypeFont-%u-%u\n", sf->osd.scaler_type, |
1029 | sf->head.fontRevision); |
1030 | if (sf->minmem) |
1031 | fprintf(ofp, "%%%%VMUsage: %u %u\n", sf->minmem, sf->maxmem); |
1032 | fprintf(ofp, "9 dict dup begin\n"); |
1033 | fprintf(ofp, "/FontType 42 def\n"); |
1034 | fprintf(ofp, "/FontMatrix [1 0 0 1 0 0] def\n"); |
1035 | fprintf(ofp, "/FontName /%s def\n", fi->name); |
1036 | fprintf(ofp, "/Encoding StandardEncoding def\n"); |
1037 | if ((sf->head.flags & 0x0003) == 0x0003) { |
1038 | /* |
1039 | * Sensible font with the origin in the right place, such that |
1040 | * the bounding box is meaningful. |
1041 | */ |
1042 | fprintf(ofp, "/FontBBox [%g %g %g %g] readonly def\n", |
1043 | (double)sf->head.xMin / sf->head.unitsPerEm, |
1044 | (double)sf->head.yMin / sf->head.unitsPerEm, |
1045 | (double)sf->head.xMax / sf->head.unitsPerEm, |
1046 | (double)sf->head.yMax / sf->head.unitsPerEm); |
1047 | } else { |
1048 | /* Non-sensible font. */ |
b554f0ec |
1049 | fprintf(ofp, "/FontBBox [0 0 0 0] readonly def\n"); |
2729eafc |
1050 | } |
1051 | fprintf(ofp, "/PaintType 0 def\n"); |
1052 | fprintf(ofp, "/CharStrings %u dict dup begin\n", sf->nglyphs); |
a2bd2219 |
1053 | fprintf(ofp, "0 1 %u{currentfile token pop exch def}bind for\n", |
1054 | sf->nglyphs - 1); |
2729eafc |
1055 | for (i = 0; i < sf->nglyphs; i++) |
c358bf5b |
1056 | ps_token(ofp, &cc, "/%s", glyph_extern(sfnt_indextoglyph(sf, i))); |
1057 | fprintf(ofp, "\nend readonly def\n"); |
2729eafc |
1058 | fprintf(ofp, "/sfnts [<"); |
1059 | breaks = snewn(sf->osd.numTables + sf->nglyphs, size_t); |
1060 | for (i = 0; i < sf->osd.numTables; i++) { |
1061 | breaks[i] = sf->td[i].offset; |
1062 | } |
d41ae9ad |
1063 | if (!sfnt_findtable(sf, TAG_glyf, &glyfptr, &glyfend)) { |
4691e240 |
1064 | error(err_sfntnotable, &sf->pos, "glyf"); |
d41ae9ad |
1065 | return; |
1066 | } |
2729eafc |
1067 | glyfoff = (char *)glyfptr - (char *)sf->data; |
1068 | glyflen = (char *)glyfend - (char *)glyfptr; |
d41ae9ad |
1069 | if (!sfnt_findtable(sf, TAG_loca, &locaptr, &locaend)) { |
4691e240 |
1070 | error(err_sfntnotable, &sf->pos, "loca"); |
d41ae9ad |
1071 | return; |
1072 | } |
2729eafc |
1073 | loca = snewn(sf->nglyphs, unsigned); |
1074 | if (sf->head.indexToLocFormat == 0) { |
1075 | if (!decoden(uint16_decode, locaptr, locaend, loca, sizeof(*loca), |
d41ae9ad |
1076 | sf->nglyphs)) goto badloca; |
2729eafc |
1077 | for (i = 0; i < sf->nglyphs; i++) loca[i] *= 2; |
1078 | } else { |
1079 | if (!decoden(uint32_decode, locaptr, locaend, loca, sizeof(*loca), |
d41ae9ad |
1080 | sf->nglyphs)) goto badloca; |
2729eafc |
1081 | } |
1082 | for (i = 1; i < sf->nglyphs; i++) { |
d41ae9ad |
1083 | if (loca[i] > glyflen) goto badloca; |
2729eafc |
1084 | breaks[sf->osd.numTables + i - 1] = loca[i] + glyfoff; |
1085 | } |
1086 | breaks[sf->osd.numTables + sf->nglyphs - 1] = sf->len; |
1087 | qsort(breaks, sf->osd.numTables + sf->nglyphs, sizeof(*breaks), sizecmp); |
1088 | j = lastbreak = 0; |
1089 | for (i = 0; i < sf->len; i++) { |
1090 | if ((i - lastbreak) % 38 == 0) fprintf(ofp, "\n"); |
1091 | if (i == breaks[j]) { |
1092 | while (i == breaks[j]) j++; |
1093 | lastbreak = i; |
1094 | fprintf(ofp, "00><\n"); |
1095 | } |
1096 | fprintf(ofp, "%02x", *((unsigned char *)sf->data + i)); |
1097 | } |
1098 | fprintf(ofp, "00>] readonly def\n"); |
1099 | sfree(breaks); |
1100 | fprintf(ofp, "end /%s exch definefont\n", fi->name); |
d41ae9ad |
1101 | return; |
1102 | badloca: |
1103 | error(err_sfntbadtable, &sf->pos, "loca"); |
2729eafc |
1104 | } |
dde3b989 |
1105 | |
dde3b989 |
1106 | void sfnt_data(font_info *fi, char **bufp, size_t *lenp) { |
1107 | sfnt *sf = fi->fontfile; |
1108 | *bufp = sf->data; |
1109 | *lenp = sf->len; |
1110 | } |