1 #include "sfnt.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include "sfnt_int.h"
8
9 // TODO?
10 // get_SHORT(head+48) // fontDirectionHint
11 /* reqd. Tables: cmap, head, hhea, hmtx, maxp, name, OS/2, post
12 OTF: glyf,loca [cvt,fpgm,prep]
13 */
14
otf_bsearch_params(int num,int recordSize,int * searchRange,int * entrySelector,int * rangeShift)15 static void otf_bsearch_params(int num, // {{{
16 int recordSize,
17 int *searchRange,
18 int *entrySelector,
19 int *rangeShift)
20 {
21 assert(num>0);
22 assert(searchRange);
23 assert(entrySelector);
24 assert(rangeShift);
25
26 int iA,iB;
27 for (iA=1,iB=0;iA<=num;iA<<=1,iB++) {}
28
29 *searchRange=iA*recordSize/2;
30 *entrySelector=iB-1;
31 *rangeShift=num*recordSize-*searchRange;
32 }
33 // }}}
34
otf_bsearch(char * table,const char * target,int len,int searchRange,int entrySelector,int rangeShift,int lower_bound)35 static char *otf_bsearch(char *table, // {{{
36 const char *target,int len,
37 int searchRange,
38 int entrySelector,
39 int rangeShift,
40 int lower_bound) // return lower_bound, if !=0
41 {
42 char *ret=table+rangeShift;
43 if (memcmp(target,ret,len)<0) {
44 ret=table;
45 }
46
47 for (;entrySelector>0;entrySelector--) {
48 searchRange>>=1;
49 ret+=searchRange;
50 if (memcmp(target,ret,len)<0) {
51 ret-=searchRange;
52 }
53 }
54 const int result=memcmp(target,ret,len);
55 if (result==0) {
56 return ret;
57 } else if (lower_bound) {
58 if (result>0) {
59 return ret+searchRange;
60 }
61 return ret;
62 }
63 return NULL; // not found;
64 }
65 // }}}
66
otf_new(FILE * f)67 static OTF_FILE *otf_new(FILE *f) // {{{
68 {
69 assert(f);
70
71 OTF_FILE *ret;
72 ret=calloc(1,sizeof(OTF_FILE));
73 if (ret) {
74 ret->f=f;
75 ret->version=0x00010000;
76 }
77
78 return ret;
79 }
80 // }}}
81
82 // will alloc, if >buf ==NULL, returns >buf, or NULL on error
83 // NOTE: you probably want otf_get_table()
otf_read(OTF_FILE * otf,char * buf,long pos,int length)84 static char *otf_read(OTF_FILE *otf,char *buf,long pos,int length) // {{{
85 {
86 char *ours=NULL;
87
88 if (length==0) {
89 return buf;
90 } else if (length<0) {
91 assert(0);
92 return NULL;
93 }
94
95 int res=fseek(otf->f,pos,SEEK_SET);
96 if (res==-1) {
97 fprintf(stderr,"Seek failed: %s\n", strerror(errno));
98 return NULL;
99 }
100
101 // (+3)&~3 for checksum...
102 const int pad_len=(length+3)&~3;
103 if (!buf) {
104 ours=buf=malloc(sizeof(char)*pad_len);
105 if (!buf) {
106 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
107 return NULL;
108 }
109 }
110
111 res=fread(buf,1,pad_len,otf->f);
112 if (res!=pad_len) {
113 if (res==length) { // file size not multiple of 4, pad with zero
114 memset(buf+res,0,pad_len-length);
115 } else {
116 fprintf(stderr,"Short read\n");
117 free(ours);
118 return NULL;
119 }
120 }
121
122 return buf;
123 }
124 // }}}
125
126
otf_get_ttc_start(OTF_FILE * otf,int use_ttc)127 static int otf_get_ttc_start(OTF_FILE *otf,int use_ttc) // {{{
128 {
129 char buf[4];
130
131 if (!otf->numTTC) { // >0 if TTC...
132 return 0;
133 }
134
135 int pos=0;
136 if ( (use_ttc<0)||(use_ttc>=otf->numTTC)||
137 (!otf_read(otf,buf,pos+12+4*use_ttc,4)) ) {
138 fprintf(stderr,"Bad TTC subfont number\n");
139 return -1;
140 }
141 return get_ULONG(buf);
142 }
143 // }}}
144
otf_do_load(OTF_FILE * otf,int pos)145 OTF_FILE *otf_do_load(OTF_FILE *otf,int pos) // {{{
146 {
147 int iA;
148 char buf[16];
149
150 // {{{ read offset table
151 if (otf_read(otf,buf,pos,12)) {
152 otf->version=get_ULONG(buf);
153 if (otf->version==0x00010000) { // 1.0 truetype
154 } else if (otf->version==OTF_TAG('O','T','T','O')) { // OTF(CFF)
155 otf->flags|=OTF_F_FMT_CFF;
156 } else if (otf->version==OTF_TAG('t','r','u','e')) { // (old mac)
157 } else if (otf->version==OTF_TAG('t','y','p','1')) { // sfnt wrapped type1
158 // TODO: unsupported
159 } else {
160 otf_close(otf);
161 otf=NULL;
162 }
163 pos+=12;
164 } else {
165 otf_close(otf);
166 otf=NULL;
167 }
168 if (!otf) {
169 fprintf(stderr,"Not a ttf font\n");
170 return NULL;
171 }
172 otf->numTables=get_USHORT(buf+4);
173 // }}}
174
175 // {{{ read directory
176 otf->tables=malloc(sizeof(OTF_DIRENT)*otf->numTables);
177 if (!otf->tables) {
178 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
179 otf_close(otf);
180 return NULL;
181 }
182 for (iA=0;iA<otf->numTables;iA++) {
183 if (!otf_read(otf,buf,pos,16)) {
184 otf_close(otf);
185 return NULL;
186 }
187 otf->tables[iA].tag=get_ULONG(buf);
188 otf->tables[iA].checkSum=get_ULONG(buf+4);
189 otf->tables[iA].offset=get_ULONG(buf+8);
190 otf->tables[iA].length=get_ULONG(buf+12);
191 if ( (otf->tables[iA].tag==OTF_TAG('C','F','F',' '))&&
192 ((otf->flags&OTF_F_FMT_CFF)==0) ) {
193 fprintf(stderr,"Wrong magic\n");
194 otf_close(otf);
195 return NULL;
196 } else if ( (otf->tables[iA].tag==OTF_TAG('g','l','y','p'))&&
197 (otf->flags&OTF_F_FMT_CFF) ) {
198 fprintf(stderr,"Wrong magic\n");
199 otf_close(otf);
200 return NULL;
201 }
202 pos+=16;
203 }
204 // }}}
205
206 // otf->flags|=OTF_F_DO_CHECKSUM;
207 // {{{ check head table
208 int len=0;
209 char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&len);
210 if ( (!head)||
211 (get_ULONG(head+0)!=0x00010000)|| // version
212 (len!=54)||
213 (get_ULONG(head+12)!=0x5F0F3CF5)|| // magic
214 (get_SHORT(head+52)!=0x0000) ) { // glyphDataFormat
215 fprintf(stderr,"Unsupported OTF font / head table \n");
216 free(head);
217 otf_close(otf);
218 return NULL;
219 }
220 // }}}
221 otf->unitsPerEm=get_USHORT(head+18);
222 otf->indexToLocFormat=get_SHORT(head+50);
223
224 // {{{ checksum whole file
225 if (otf->flags&OTF_F_DO_CHECKSUM) {
226 unsigned int csum=0;
227 char tmp[1024];
228 rewind(otf->f);
229 while (!feof(otf->f)) {
230 len=fread(tmp,1,1024,otf->f);
231 if (len&3) { // zero padding reqd.
232 memset(tmp+len,0,4-(len&3));
233 }
234 csum+=otf_checksum(tmp,len);
235 }
236 if (csum!=0xb1b0afba) {
237 fprintf(stderr,"Wrong global checksum\n");
238 free(head);
239 otf_close(otf);
240 return NULL;
241 }
242 }
243 // }}}
244 free(head);
245
246 // {{{ read maxp table / numGlyphs
247 char *maxp=otf_get_table(otf,OTF_TAG('m','a','x','p'),&len);
248 if (maxp) {
249 const unsigned int maxp_version=get_ULONG(maxp);
250 if ( (maxp_version==0x00005000)&&(len==6) ) { // version 0.5
251 otf->numGlyphs=get_USHORT(maxp+4);
252 if ( (otf->flags&OTF_F_FMT_CFF)==0) { // only CFF
253 free(maxp);
254 maxp=NULL;
255 }
256 } else if ( (maxp_version==0x00010000)&&(len==32) ) { // version 1.0
257 otf->numGlyphs=get_USHORT(maxp+4);
258 if (otf->flags&OTF_F_FMT_CFF) { // only TTF
259 free(maxp);
260 maxp=NULL;
261 }
262 } else {
263 free(maxp);
264 maxp=NULL;
265 }
266 }
267 if (!maxp) {
268 fprintf(stderr,"Unsupported OTF font / maxp table \n");
269 free(maxp);
270 otf_close(otf);
271 return NULL;
272 }
273 free(maxp);
274 // }}}
275
276 return otf;
277 }
278 // }}}
279
otf_load(const char * file)280 OTF_FILE *otf_load(const char *file) // {{{
281 {
282 FILE *f;
283 OTF_FILE *otf;
284
285 int use_ttc=-1;
286 if ((f=fopen(file,"rb"))==NULL) {
287 // check for TTC
288 char *tmp=strrchr(file,'/'),*end;
289 if (tmp) {
290 use_ttc=strtoul(tmp+1,&end,10);
291 if (!*end) {
292 end=malloc((tmp-file+1)*sizeof(char));
293 if (!end) {
294 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
295 return NULL;
296 }
297 strncpy(end,file,tmp-file);
298 end[tmp-file]=0;
299 f=fopen(end,"rb");
300 free(end);
301 }
302 }
303 if (!f) {
304 fprintf(stderr,"Could not open \"%s\": %s\n", file, strerror(errno));
305 return NULL;
306 }
307 }
308 otf=otf_new(f);
309 if (!otf) {
310 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
311 fclose(f);
312 return NULL;
313 }
314
315 char buf[12];
316 int pos=0;
317 // {{{ check for TTC
318 if (otf_read(otf,buf,pos,12)) {
319 const unsigned int version=get_ULONG(buf);
320 if (version==OTF_TAG('t','t','c','f')) {
321 const unsigned int ttc_version=get_ULONG(buf+4);
322 if ( (ttc_version!=0x00010000)&&(ttc_version!=0x00020000) ) {
323 fprintf(stderr,"Unsupported TTC version\n");
324 otf_close(otf);
325 return NULL;
326 }
327 otf->numTTC=get_ULONG(buf+8);
328 otf->useTTC=use_ttc;
329 pos=otf_get_ttc_start(otf,use_ttc);
330 if (pos==-1) {
331 otf_close(otf);
332 return NULL;
333 }
334 }
335 } else {
336 fprintf(stderr,"Not a ttf font\n");
337 otf_close(otf);
338 return NULL;
339 }
340 // }}}
341
342 return otf_do_load(otf,pos);
343 }
344 // }}}
345
otf_close(OTF_FILE * otf)346 void otf_close(OTF_FILE *otf) // {{{
347 {
348 assert(otf);
349 if (otf) {
350 free(otf->gly);
351 free(otf->cmap);
352 free(otf->name);
353 free(otf->hmtx);
354 free(otf->glyphOffsets);
355 fclose(otf->f);
356 free(otf->tables);
357 free(otf);
358 }
359 }
360 // }}}
361
otf_dirent_compare(const void * a,const void * b)362 static int otf_dirent_compare(const void *a,const void *b) // {{{
363 {
364 const unsigned int aa=((const OTF_DIRENT *)a)->tag;
365 const unsigned int bb=((const OTF_DIRENT *)b)->tag;
366 if (aa<bb) {
367 return -1;
368 } else if (aa>bb) {
369 return 1;
370 }
371 return 0;
372 }
373 // }}}
374
otf_find_table(OTF_FILE * otf,unsigned int tag)375 int otf_find_table(OTF_FILE *otf,unsigned int tag) // {{{ - table_index or -1 on error
376 {
377 #if 0
378 // binary search would require raw table
379 int pos=0;
380 char buf[12];
381 if (!otf_read(otf,buf,pos,12)) {
382 return -1;
383 }
384 pos=12;
385 const unsigned int numTables=get_USHORT(buf+4);
386 char *tables=malloc(16*numTables);
387 if (!tables) {
388 return -1;
389 }
390 if (!otf_read(otf,tables,pos,16*numTables)) {
391 free(tables);
392 return -1;
393 }
394 char target[]={(tag>>24),(tag>>16),(tag>>8),tag};
395 // assert(get_USHORT(buf+6)+get_USHORT(buf+10)==16*numTables);
396 char *result=otf_bsearch(tables,target,4,
397 get_USHORT(buf+6),
398 get_USHORT(buf+8),
399 get_USHORT(buf+10),0);
400 free(tables);
401 if (result) {
402 return (result-tables)/16;
403 }
404 #elif 1
405 OTF_DIRENT key={.tag=tag},*res;
406 res=bsearch(&key,otf->tables,otf->numTables,sizeof(otf->tables[0]),otf_dirent_compare);
407 if (res) {
408 return (res-otf->tables);
409 }
410 #else
411 int iA;
412 for (iA=0;iA<otf->numTables;iA++) {
413 if (otf->tables[iA].tag==tag) {
414 return iA;
415 }
416 }
417 #endif
418 return -1;
419 }
420 // }}}
421
otf_get_table(OTF_FILE * otf,unsigned int tag,int * ret_len)422 char *otf_get_table(OTF_FILE *otf,unsigned int tag,int *ret_len) // {{{
423 {
424 assert(otf);
425 assert(ret_len);
426
427 const int idx=otf_find_table(otf,tag);
428 if (idx==-1) {
429 *ret_len=-1;
430 return NULL;
431 }
432 const OTF_DIRENT *table=otf->tables+idx;
433
434 char *ret=otf_read(otf,NULL,table->offset,table->length);
435 if (!ret) {
436 return NULL;
437 }
438 if (otf->flags&OTF_F_DO_CHECKSUM) {
439 unsigned int csum=otf_checksum(ret,table->length);
440 if (tag==OTF_TAG('h','e','a','d')) { // special case
441 csum-=get_ULONG(ret+8);
442 }
443 if (csum!=table->checkSum) {
444 fprintf(stderr,"Wrong checksum for %c%c%c%c\n",OTF_UNTAG(tag));
445 free(ret);
446 return NULL;
447 }
448 }
449 *ret_len=table->length;
450 return ret;
451 }
452 // }}}
453
otf_load_glyf(OTF_FILE * otf)454 int otf_load_glyf(OTF_FILE *otf) // {{{ - 0 on success
455 {
456 assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF
457 int iA,len;
458 // {{{ find glyf table
459 iA=otf_find_table(otf,OTF_TAG('g','l','y','f'));
460 if (iA==-1) {
461 fprintf(stderr,"Unsupported OTF font / glyf table \n");
462 return -1;
463 }
464 otf->glyfTable=otf->tables+iA;
465 // }}}
466
467 // {{{ read loca table
468 char *loca=otf_get_table(otf,OTF_TAG('l','o','c','a'),&len);
469 if ( (!loca)||
470 (otf->indexToLocFormat>=2)||
471 (((len+3)&~3)!=((((otf->numGlyphs+1)*(otf->indexToLocFormat+1)*2)+3)&~3)) ) {
472 fprintf(stderr,"Unsupported OTF font / loca table \n");
473 return -1;
474 }
475 if (otf->glyphOffsets) {
476 free(otf->glyphOffsets);
477 assert(0);
478 }
479 otf->glyphOffsets=malloc((otf->numGlyphs+1)*sizeof(unsigned int));
480 if (!otf->glyphOffsets) {
481 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
482 return -1;
483 }
484 if (otf->indexToLocFormat==0) {
485 for (iA=0;iA<=otf->numGlyphs;iA++) {
486 otf->glyphOffsets[iA]=get_USHORT(loca+iA*2)*2;
487 }
488 } else { // indexToLocFormat==1
489 for (iA=0;iA<=otf->numGlyphs;iA++) {
490 otf->glyphOffsets[iA]=get_ULONG(loca+iA*4);
491 }
492 }
493 free(loca);
494 if (otf->glyphOffsets[otf->numGlyphs]>otf->glyfTable->length) {
495 fprintf(stderr,"Bad loca table \n");
496 return -1;
497 }
498 // }}}
499
500 // {{{ allocate otf->gly slot
501 int maxGlyfLen=0; // no single glyf takes more space
502 for (iA=1;iA<=otf->numGlyphs;iA++) {
503 const int glyfLen=otf->glyphOffsets[iA]-otf->glyphOffsets[iA-1];
504 if (glyfLen<0) {
505 fprintf(stderr,"Bad loca table: glyph len %d\n",glyfLen);
506 return -1;
507 }
508 if (maxGlyfLen<glyfLen) {
509 maxGlyfLen=glyfLen;
510 }
511 }
512 if (otf->gly) {
513 free(otf->gly);
514 assert(0);
515 }
516 otf->gly=malloc(maxGlyfLen*sizeof(char));
517 if (!otf->gly) {
518 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
519 return -1;
520 }
521 // }}}
522
523 return 0;
524 }
525 // }}}
526
otf_load_more(OTF_FILE * otf)527 int otf_load_more(OTF_FILE *otf) // {{{ - 0 on success => hhea,hmtx,name,[glyf]
528 {
529 int iA;
530
531 int len;
532 if ((otf->flags&OTF_F_FMT_CFF)==0) { // not for CFF
533 if (otf_load_glyf(otf)==-1) {
534 return -1;
535 }
536 }
537
538 // {{{ read hhea table
539 char *hhea=otf_get_table(otf,OTF_TAG('h','h','e','a'),&len);
540 if ( (!hhea)||
541 (get_ULONG(hhea)!=0x00010000)|| // version
542 (len!=36)||
543 (get_SHORT(hhea+32)!=0) ) { // metric format
544 fprintf(stderr,"Unsupported OTF font / hhea table \n");
545 return -1;
546 }
547 otf->numberOfHMetrics=get_USHORT(hhea+34);
548 free(hhea);
549 // }}}
550
551 // {{{ read hmtx table
552 char *hmtx=otf_get_table(otf,OTF_TAG('h','m','t','x'),&len);
553 if ( (!hmtx)||
554 (len!=otf->numberOfHMetrics*2+otf->numGlyphs*2) ) {
555 fprintf(stderr,"Unsupported OTF font / hmtx table \n");
556 return -1;
557 }
558 if (otf->hmtx) {
559 free(otf->hmtx);
560 assert(0);
561 }
562 otf->hmtx=hmtx;
563 // }}}
564
565 // {{{ read name table
566 char *name=otf_get_table(otf,OTF_TAG('n','a','m','e'),&len);
567 if ( (!name)||
568 (get_USHORT(name)!=0x0000)|| // version
569 (len<get_USHORT(name+2)*12+6)||
570 (len<=get_USHORT(name+4)) ) {
571 fprintf(stderr,"Unsupported OTF font / name table \n");
572 return -1;
573 }
574 // check bounds
575 int name_count=get_USHORT(name+2);
576 const char *nstore=name+get_USHORT(name+4);
577 for (iA=0;iA<name_count;iA++) {
578 const char *nrec=name+6+12*iA;
579 if (nstore-name+get_USHORT(nrec+10)+get_USHORT(nrec+8)>len) {
580 fprintf(stderr,"Bad name table \n");
581 free(name);
582 return -1;
583 }
584 }
585 if (otf->name) {
586 free(otf->name);
587 assert(0);
588 }
589 otf->name=name;
590 // }}}
591
592 return 0;
593 }
594 // }}}
595
otf_load_cmap(OTF_FILE * otf)596 int otf_load_cmap(OTF_FILE *otf) // {{{ - 0 on success
597 {
598 int iA;
599 int len;
600
601 char *cmap=otf_get_table(otf,OTF_TAG('c','m','a','p'),&len);
602 if ( (!cmap)||
603 (get_USHORT(cmap)!=0x0000)|| // version
604 (len<get_USHORT(cmap+2)*8+4) ) {
605 fprintf(stderr,"Unsupported OTF font / cmap table \n");
606 assert(0);
607 return -1;
608 }
609 // check bounds, find (3,0) or (3,1) [TODO?]
610 const int numTables=get_USHORT(cmap+2);
611 for (iA=0;iA<numTables;iA++) {
612 const char *nrec=cmap+4+8*iA;
613 const unsigned int offset=get_ULONG(nrec+4);
614 const char *ndata=cmap+offset;
615 if ( (ndata<cmap+4+8*numTables)||
616 (offset>=len)||
617 (offset+get_USHORT(ndata+2)>len) ) {
618 fprintf(stderr,"Bad cmap table \n");
619 free(cmap);
620 assert(0);
621 return -1;
622 }
623 if ( (get_USHORT(nrec)==3)&&
624 (get_USHORT(nrec+2)<=1)&&
625 (get_USHORT(ndata)==4)&&
626 (get_USHORT(ndata+4)==0) ) {
627 otf->unimap=ndata;
628 }
629 }
630 if (otf->cmap) {
631 free(otf->cmap);
632 assert(0);
633 }
634 otf->cmap=cmap;
635
636 return 0;
637 }
638 // }}}
639
otf_get_width(OTF_FILE * otf,unsigned short gid)640 int otf_get_width(OTF_FILE *otf,unsigned short gid) // {{{ -1 on error
641 {
642 assert(otf);
643
644 if (gid>=otf->numGlyphs) {
645 return -1;
646 }
647
648 // ensure hmtx is there
649 if (!otf->hmtx) {
650 if (otf_load_more(otf)!=0) {
651 fprintf(stderr,"Unsupported OTF font / cmap table \n");
652 return -1;
653 }
654 }
655
656 return get_width_fast(otf,gid);
657 #if 0
658 if (gid>=otf->numberOfHMetrics) {
659 return get_USHORT(otf->hmtx+(otf->numberOfHMetrics-1)*2);
660 // TODO? lsb=get_SHORT(otf->hmtx+otf->numberOfHMetrics*2+gid*2); // lsb: left_side_bearing (also in table)
661 }
662 return get_USHORT(otf->hmtx+gid*4);
663 // TODO? lsb=get_SHORT(otf->hmtx+gid*4+2);
664 #endif
665 }
666 // }}}
667
otf_name_compare(const void * a,const void * b)668 static int otf_name_compare(const void *a,const void *b) // {{{
669 {
670 return memcmp(a,b,8);
671 }
672 // }}}
673
otf_get_name(OTF_FILE * otf,int platformID,int encodingID,int languageID,int nameID,int * ret_len)674 const char *otf_get_name(OTF_FILE *otf,int platformID,int encodingID,int languageID,int nameID,int *ret_len) // {{{
675 {
676 assert(otf);
677 assert(ret_len);
678
679 // ensure name is there
680 if (!otf->name) {
681 if (otf_load_more(otf)!=0) {
682 *ret_len=-1;
683 assert(0);
684 return NULL;
685 }
686 }
687
688 char key[8];
689 set_USHORT(key,platformID);
690 set_USHORT(key+2,encodingID);
691 set_USHORT(key+4,languageID);
692 set_USHORT(key+6,nameID);
693
694 char *res=bsearch(key,otf->name+6,get_USHORT(otf->name+2),12,otf_name_compare);
695 if (res) {
696 *ret_len=get_USHORT(res+8);
697 int npos=get_USHORT(res+10);
698 const char *nstore=otf->name+get_USHORT(otf->name+4);
699 return nstore+npos;
700 }
701 *ret_len=0;
702 return NULL;
703 }
704 // }}}
705
otf_get_glyph(OTF_FILE * otf,unsigned short gid)706 int otf_get_glyph(OTF_FILE *otf,unsigned short gid) // {{{ result in >otf->gly, returns length, -1 on error
707 {
708 assert(otf);
709 assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF
710
711 if (gid>=otf->numGlyphs) {
712 return -1;
713 }
714
715 // ensure >glyphOffsets and >gly is there
716 if ( (!otf->gly)||(!otf->glyphOffsets) ) {
717 if (otf_load_more(otf)!=0) {
718 assert(0);
719 return -1;
720 }
721 }
722
723 const int len=otf->glyphOffsets[gid+1]-otf->glyphOffsets[gid];
724 if (len==0) {
725 return 0;
726 }
727
728 assert(otf->glyfTable->length>=otf->glyphOffsets[gid+1]);
729 if (!otf_read(otf,otf->gly,
730 otf->glyfTable->offset+otf->glyphOffsets[gid],len)) {
731 return -1;
732 }
733
734 return len;
735 }
736 // }}}
737
otf_from_unicode(OTF_FILE * otf,int unicode)738 unsigned short otf_from_unicode(OTF_FILE *otf,int unicode) // {{{ 0 = missing
739 {
740 assert(otf);
741 assert( (unicode>=0)&&(unicode<65536) );
742 // assert((otf->flags&OTF_F_FMT_CFF)==0); // not for CFF, other method!
743
744 // ensure >cmap and >unimap is there
745 if (!otf->cmap) {
746 if (otf_load_cmap(otf)!=0) {
747 assert(0);
748 return 0; // TODO?
749 }
750 }
751 if (!otf->unimap) {
752 fprintf(stderr,"Unicode (3,1) cmap in format 4 not found\n");
753 return 0;
754 }
755
756 #if 0
757 // linear search is cache friendly and should be quite fast
758 #else
759 const unsigned short segCountX2=get_USHORT(otf->unimap+6);
760 char target[]={unicode>>8,unicode}; // set_USHORT(target,unicode);
761 char *result=otf_bsearch((char *)otf->unimap+14,target,2,
762 get_USHORT(otf->unimap+8),
763 get_USHORT(otf->unimap+10),
764 get_USHORT(otf->unimap+12),1);
765 if (result>=otf->unimap+14+segCountX2) { // outside of endCode[segCount]
766 assert(0); // bad font, no 0xffff sentinel
767 return 0;
768 }
769
770 result+=2+segCountX2; // jump over padding into startCode[segCount]
771 const unsigned short startCode=get_USHORT(result);
772 if (startCode>unicode) {
773 return 0;
774 }
775 result+=2*segCountX2;
776 const unsigned short rangeOffset=get_USHORT(result);
777 if (rangeOffset) {
778 return get_USHORT(result+rangeOffset+2*(unicode-startCode)); // the so called "obscure indexing trick" into glyphIdArray[]
779 // NOTE: this is according to apple spec; microsoft says we must add delta (probably incorrect; fonts probably have delta==0)
780 } else {
781 const short delta=get_SHORT(result-segCountX2);
782 return (delta+unicode)&0xffff;
783 }
784 #endif
785 }
786 // }}}
787
788 /** output stuff **/
otf_action_copy(void * param,int table_no,OUTPUT_FN output,void * context)789 int otf_action_copy(void *param,int table_no,OUTPUT_FN output,void *context) // {{{
790 {
791 OTF_FILE *otf=param;
792 const OTF_DIRENT *table=otf->tables+table_no;
793
794 if (!output) { // get checksum and unpadded length
795 *(unsigned int *)context=table->checkSum;
796 return table->length;
797 }
798
799 // TODO? copy_block(otf->f,table->offset,(table->length+3)&~3,output,context);
800 // problem: PS currently depends on single-output. also checksum not possible
801 char *data=otf_read(otf,NULL,table->offset,table->length);
802 if (!data) {
803 return -1;
804 }
805 int ret=(table->length+3)&~3;
806 (*output)(data,ret,context);
807 free(data);
808 return ret; // padded length
809 }
810 // }}}
811
812 // TODO? >modified time-stamp?
813 // Note: don't use this directly. otf_write_sfnt will internally replace otf_action_copy for head with this
otf_action_copy_head(void * param,int csum,OUTPUT_FN output,void * context)814 int otf_action_copy_head(void *param,int csum,OUTPUT_FN output,void *context) // {{{
815 {
816 OTF_FILE *otf=param;
817 const int table_no=otf_find_table(otf,OTF_TAG('h','e','a','d')); // we can't have csum AND table_no ... never mind!
818 assert(table_no!=-1);
819 const OTF_DIRENT *table=otf->tables+table_no;
820
821 if (!output) { // get checksum and unpadded length
822 *(unsigned int *)context=table->checkSum;
823 return table->length;
824 }
825
826 char *data=otf_read(otf,NULL,table->offset,table->length);
827 if (!data) {
828 return -1;
829 }
830 set_ULONG(data+8,0xb1b0afba-csum); // head. fix global checksum
831 int ret=(table->length+3)&~3;
832 (*output)(data,ret,context);
833 free(data);
834 return ret; // padded length
835 }
836 // }}}
837
otf_action_replace(void * param,int length,OUTPUT_FN output,void * context)838 int otf_action_replace(void *param,int length,OUTPUT_FN output,void *context) // {{{
839 {
840 char *data=param;
841 char pad[4]={0,0,0,0};
842
843 int ret=(length+3)&~3;
844 if (!output) { // get checksum and unpadded length
845 if (ret!=length) {
846 unsigned int csum=otf_checksum(data,ret-4);
847 memcpy(pad,data+ret-4,ret-length);
848 csum+=get_ULONG(pad);
849 *(unsigned int *)context=csum;
850 } else {
851 *(unsigned int *)context=otf_checksum(data,length);
852 }
853 return length;
854 }
855
856 (*output)(data,length,context);
857 if (ret!=length) {
858 (*output)(pad,ret-length,context);
859 }
860
861 return ret; // padded length
862 }
863 // }}}
864
865 /* windows "works best" with the following ordering:
866 head, hhea, maxp, OS/2, hmtx, LTSH, VDMX, hdmx, cmap, fpgm, prep, cvt, loca, glyf, kern, name, post, gasp, PCLT, DSIG
867 or for CFF:
868 head, hhea, maxp, OS/2, name, cmap, post, CFF, (other tables, as convenient)
869 */
870 #define NUM_PRIO 20
871 static const struct { int prio; unsigned int tag; } otf_tagorder_win[]={ // {{{
872 {19,OTF_TAG('D','S','I','G')},
873 { 5,OTF_TAG('L','T','S','H')},
874 { 3,OTF_TAG('O','S','/','2')},
875 {18,OTF_TAG('P','C','L','T')},
876 { 6,OTF_TAG('V','D','M','X')},
877 { 8,OTF_TAG('c','m','a','p')},
878 {11,OTF_TAG('c','v','t',' ')},
879 { 9,OTF_TAG('f','p','g','m')},
880 {17,OTF_TAG('g','a','s','p')},
881 {13,OTF_TAG('g','l','y','f')},
882 { 7,OTF_TAG('h','d','m','x')},
883 { 0,OTF_TAG('h','e','a','d')},
884 { 1,OTF_TAG('h','h','e','a')},
885 { 4,OTF_TAG('h','m','t','x')},
886 {14,OTF_TAG('k','e','r','n')},
887 {12,OTF_TAG('l','o','c','a')},
888 { 2,OTF_TAG('m','a','x','p')},
889 {15,OTF_TAG('n','a','m','e')},
890 {16,OTF_TAG('p','o','s','t')},
891 {10,OTF_TAG('p','r','e','p')}};
892 // }}}
893
otf_write_sfnt(struct _OTF_WRITE * otw,unsigned int version,int numTables,OUTPUT_FN output,void * context)894 int otf_write_sfnt(struct _OTF_WRITE *otw,unsigned int version,int numTables,OUTPUT_FN output,void *context) // {{{
895 {
896 int iA;
897 int ret;
898
899 int *order=malloc(sizeof(int)*numTables); // temporary
900 char *start=malloc(12+16*numTables);
901 if ( (!order)||(!start) ) {
902 fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
903 free(order);
904 free(start);
905 return -1;
906 }
907
908 if (1) { // sort tables
909 int priolist[NUM_PRIO]={0,};
910
911 // reverse intersection of both sorted arrays
912 int iA=numTables-1,iB=sizeof(otf_tagorder_win)/sizeof(otf_tagorder_win[0])-1;
913 int ret=numTables-1;
914 while ( (iA>=0)&&(iB>=0) ) {
915 if (otw[iA].tag==otf_tagorder_win[iB].tag) {
916 priolist[otf_tagorder_win[iB--].prio]=1+iA--;
917 } else if (otw[iA].tag>otf_tagorder_win[iB].tag) { // no order known: put unchanged at end of result
918 order[ret--]=iA--;
919 } else { // <
920 iB--;
921 }
922 }
923 for (iA=NUM_PRIO-1;iA>=0;iA--) { // pick the matched tables up in sorted order (bucketsort principle)
924 if (priolist[iA]) {
925 order[ret--]=priolist[iA]-1;
926 }
927 }
928 } else {
929 for (iA=0;iA<numTables;iA++) {
930 order[iA]=iA;
931 }
932 }
933
934 // the header
935 set_ULONG(start,version);
936 set_USHORT(start+4,numTables);
937 int a,b,c;
938 otf_bsearch_params(numTables,16,&a,&b,&c);
939 set_USHORT(start+6,a);
940 set_USHORT(start+8,b);
941 set_USHORT(start+10,c);
942
943 // first pass: calculate table directory / offsets and checksums
944 unsigned int globalSum=0,csum;
945 int offset=12+16*numTables;
946 int headAt=-1;
947 for (iA=0;iA<numTables;iA++) {
948 char *entry=start+12+16*order[iA];
949 const int res=(*otw[order[iA]].action)(otw[order[iA]].param,otw[order[iA]].length,NULL,&csum);
950 assert(res>=0);
951 if (otw[order[iA]].tag==OTF_TAG('h','e','a','d')) {
952 headAt=order[iA];
953 }
954 set_ULONG(entry,otw[order[iA]].tag);
955 set_ULONG(entry+4,csum);
956 set_ULONG(entry+8,offset);
957 set_ULONG(entry+12,res);
958 offset+=(res+3)&~3; // padding
959 globalSum+=csum;
960 }
961
962 // second pass: write actual data
963 // write header + directory
964 ret=12+16*numTables;
965 (*output)(start,ret,context);
966 globalSum+=otf_checksum(start,ret);
967
968 // change head
969 if ( (headAt!=-1)&&(otw[headAt].action==otf_action_copy) ) { // more needed?
970 otw[headAt].action=otf_action_copy_head;
971 otw[headAt].length=globalSum;
972 }
973
974 // write tables
975 for (iA=0;iA<numTables;iA++) {
976 const int res=(*otw[order[iA]].action)(otw[order[iA]].param,otw[order[iA]].length,output,context);
977 if (res<0) {
978 free(order);
979 free(start);
980 return -1;
981 }
982 assert(((res+3)&~3)==res); // correctly padded? (i.e. next line is just ret+=res;)
983 ret+=(res+3)&~3;
984 }
985 assert(offset==ret);
986 free(order);
987 free(start);
988
989 return ret;
990 }
991 // }}}
992
993