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