• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "embed.h"
2 #include "embed_pdf_int.h"
3 #include "embed_sfnt_int.h"
4 #include "sfnt.h"
5 #include "sfnt_int.h"
6 #include <assert.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
emb_otf_get_rights(OTF_FILE * otf)11 EMB_RIGHT_TYPE emb_otf_get_rights(OTF_FILE *otf) // {{{
12 {
13   EMB_RIGHT_TYPE ret=EMB_RIGHT_FULL;
14 
15   int len;
16   char *os2=otf_get_table(otf,OTF_TAG('O','S','/','2'),&len);
17   if (os2) {
18     const unsigned short os2_version=get_USHORT(os2);
19     // check len
20     assert( (os2_version!=0x0000)||(len==78) );
21     assert( (os2_version!=0x0001)||(len==86) );
22     assert( (os2_version<0x0002)||(os2_version>0x0004)||(len==96) );
23     if (os2_version<=0x0004) {
24       // get rights
25       unsigned short fsType=get_USHORT(os2+8);
26       // from Adobe's Fontpolicies_v9.pdf, pg 13:
27       if (fsType==0x0002) {
28         ret=EMB_RIGHT_NONE;
29       } else {
30         ret=fsType&0x0300; // EMB_RIGHT_BITMAPONLY, EMB_RIGHT_NO_SUBSET
31         if ((fsType&0x000c)==0x0004) {
32           ret|=EMB_RIGHT_READONLY;
33         }
34       }
35     }
36     free(os2);
37   }
38   return ret;
39 }
40 // }}}
41 
42 // NOTE: statically allocated buffer
emb_otf_get_fontname(OTF_FILE * otf)43 const char *emb_otf_get_fontname(OTF_FILE *otf) // {{{
44 {
45   static char fontname[64];
46 
47   int len;
48   const char *fname=otf_get_name(otf,3,1,0x409,6,&len); // microsoft
49   if (fname) {
50     int iA,iB=0;
51     for (iA=0;(iA<63)&&(iA*2<len);iA++) {
52       if ( (fname[2*iA]==0)&&
53            (fname[2*iA+1]>=33)&&(fname[2*iA+1]<=126)&&
54            (!strchr("[](){}<>/%",fname[iA*2+1])) ) {
55         fontname[iB++]=fname[iA*2+1];
56       }
57     }
58     fontname[iB]=0;
59   } else if ((fname=otf_get_name(otf,1,0,0,6,&len))) { // mac
60     int iA,iB=0;
61     for (iA=0;(iA<63)&&(iA<len);iA++) {
62       if ( (fname[iA]>=33)&&(fname[iA]<=126)&&
63            (!strchr("[](){}<>/%",fname[iA])) ) {
64         fontname[iB++]=fname[iA];
65       }
66     }
67     fontname[iB]=0;
68   } else {
69     fontname[0]=0;
70   }
71   if (!*fontname) {
72     // TODO construct a fontname, eg from */*/*/4
73     fprintf(stderr,"WARNING: no fontName\n");
74   }
75   return fontname;
76 }
77 // }}}
78 
79 // TODO? monospaced by actual glyph width?
80 // TODO? use PCLT table? (esp. CFF, as table dircouraged for glyf fonts)
emb_otf_get_pdf_fontdescr(OTF_FILE * otf,EMB_PDF_FONTDESCR * ret)81 void emb_otf_get_pdf_fontdescr(OTF_FILE *otf,EMB_PDF_FONTDESCR *ret) // {{{
82 {
83   int len;
84 
85 //  TODO
86 //  ... fill in struct
87   char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&len);
88   assert(head); // version is 1.0 from otf_load
89   ret->bbxmin=get_SHORT(head+36)*1000/otf->unitsPerEm;
90   ret->bbymin=get_SHORT(head+38)*1000/otf->unitsPerEm;
91   ret->bbxmax=get_SHORT(head+40)*1000/otf->unitsPerEm;
92   ret->bbymax=get_SHORT(head+42)*1000/otf->unitsPerEm;
93   const int macStyle=get_USHORT(head+44);
94   free(head);
95 
96   char *post=otf_get_table(otf,OTF_TAG('p','o','s','t'),&len);
97   assert(post);
98   const unsigned int post_version=get_ULONG(post);
99   // check length
100   assert( (post_version!=0x00010000)||(len==32) );
101   assert( (post_version!=0x00020000)||(len>=34+2*otf->numGlyphs) );
102   assert( (post_version!=0x00025000)||(len==35+otf->numGlyphs) );
103   assert( (post_version!=0x00030000)||(len==32) );
104   assert( (post_version!=0x00020000)||(get_USHORT(post+32)==otf->numGlyphs) ); // v4?
105 //  assert( (post_version==0x00030000)==(!!(otf->flags&OTF_F_FMT_CFF)) ); // ghostscript embedding does this..
106   // TODO: v4 (apple) :  uint16 reencoding[numGlyphs]
107   if ( (post_version==0x00010000)||
108        (post_version==0x00020000)||
109        (post_version==0x00025000)||
110        (post_version==0x00030000)||
111        (post_version==0x00040000) ) {
112     ret->italicAngle=get_LONG(post+4)>>16;
113     if (get_ULONG(post+12)>0) { // monospaced
114       ret->flags|=1;
115     }
116   } else {
117     fprintf(stderr,"WARNING: no italicAngle, no monospaced flag\n");
118   }
119   free(post);
120 
121   char *os2=otf_get_table(otf,OTF_TAG('O','S','/','2'),&len);
122   if (os2) {
123     const unsigned short os2_version=get_USHORT(os2);
124     // check len
125     assert( (os2_version!=0x0000)||(len==78) );
126     assert( (os2_version!=0x0001)||(len==86) );
127     assert( (os2_version<0x0002)||(os2_version>0x0004)||(len==96) );
128     if (os2_version<=0x0004) {
129 
130       // from PDF14Deltas.pdf, pg 113
131       const int weightClass=get_USHORT(os2+4);
132       ret->stemV=50+weightClass*weightClass/(65*65); // TODO, really bad
133 //printf("a %d\n",weightClass);
134 
135       if (ret->supplement>=0) { // cid
136         ret->panose=ret->data;
137         memcpy(ret->panose,os2+30,12); // sFamilyClass + panose
138       }
139       const unsigned short fsSelection=get_USHORT(os2+62);
140       if (fsSelection&0x01) { // italic
141         ret->flags|=0x0040;
142       }
143       if ( (fsSelection&0x10)&&(weightClass>600) ) { // force bold
144         ret->flags|=0x0400;
145       }
146       const unsigned char family_class=get_USHORT(os2+30)>>8;
147       if (family_class==10) { // script
148         ret->flags|=0x0008;
149       }
150       if (family_class!=8) { // not sans-serif
151         ret->flags|=0x0002;
152       }
153 
154       ret->avgWidth=get_SHORT(os2+2)*1000/otf->unitsPerEm;
155       ret->ascent=get_SHORT(os2+68)*1000/otf->unitsPerEm;
156       ret->descent=get_SHORT(os2+70)*1000/otf->unitsPerEm;
157       if (os2_version>=0x0002) {
158         ret->xHeight=get_SHORT(os2+86)*1000/otf->unitsPerEm;
159         ret->capHeight=get_SHORT(os2+88)*1000/otf->unitsPerEm;
160       } // else capHeight fixed later
161     } else {
162       free(os2);
163       os2=NULL;
164     }
165   } else {
166     fprintf(stderr,"WARNING: no OS/2 table\n");
167     // e.g. subsetted font from ghostscript // e.g. CFF
168   }
169   if (os2) {
170     free(os2);
171   } else { // TODO (if(CFF))
172     fprintf(stderr,"WARNING: no ascent/descent, capHeight, stemV, flags\n");
173     if (macStyle&0x01) { // force bold - just do it on bold
174       ret->flags|=0x0400;
175     }
176     if (macStyle&0x02) { // italic
177       ret->flags|=0x0004;
178     }
179     //  ... flags TODO? (Serif, Script, Italic, AllCap,SmallCap, ForceBold)
180   }
181 
182 // ? maybe get ascent,descent,capHeight,xHeight,stemV directly from cff
183   // Fallbacks
184   if ( (!ret->ascent)||(!ret->descent) ) {
185     char *hhea=otf_get_table(otf,OTF_TAG('h','h','e','a'),&len);
186     if (hhea) {
187       ret->ascent=get_SHORT(hhea+4)*1000/otf->unitsPerEm;
188       ret->descent=get_SHORT(hhea+6)*1000/otf->unitsPerEm;
189     }
190     free(hhea);
191   }
192   if (!ret->stemV) { // TODO? use name
193     const unsigned short d_gid=otf_from_unicode(otf,'.');
194     if (d_gid) { // stemV=bbox['.'].width;
195       len=otf_get_glyph(otf,d_gid);
196       assert(len>=10);
197       ret->stemV=(get_SHORT(otf->gly+6)-get_SHORT(otf->gly+2))*1000/otf->unitsPerEm;
198     } else {
199       if (macStyle&1) { // bold
200         ret->stemV=165;
201       } else {
202         ret->stemV=109; // TODO... unserious values...
203       }
204     }
205   }
206   if (!ret->capHeight) { // TODO? only reqd. for fonts with latin...
207     ret->capHeight=ret->ascent;
208     // TODO: OTF spec says:  use metrics of 'H' (0 if not available)
209   }
210   if (0) { // TODO? uses only adobe latin standard? ?? e.g. Type1
211     ret->flags|=0x0020;
212   } else {
213     ret->flags|=0x0004;
214   }
215   // TODO SmallCap by font name(?)
216 
217 // TODO ;   ? cid ?
218 }
219 // }}}
220 
221 // TODO: split generic part and otf part
222 // TODO: FIXME: gid vs. char   ... NOTE: not called in multi_byte mode...
223 // Adobe does: char --MacRoman/WinAnsi--> name --AGL--> unicode --cmap(3,1) --> gid   only avoidable by setting 'symbol'+custom(1,0)/(3,0)
224 // HINT: caller sets len == otf->numGlyphs   (only when not using encoding...)
emb_otf_get_pdf_widths(OTF_FILE * otf,const unsigned short * encoding,int len,const BITSET glyphs)225 EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_widths(OTF_FILE *otf,const unsigned short *encoding,int len,const BITSET glyphs) // {{{ glyphs==NULL -> all from 0 to len
226 {
227   assert(otf);
228 
229   int first=len,last=0;
230   int iA;
231 
232   if (glyphs) {
233     for (iA=0;iA<len;iA++) { // iA is a "gid" when in multi_byte mode...
234       const int gid=(encoding)?encoding[iA]:otf_from_unicode(otf,iA); // TODO
235       if (bit_check(glyphs,gid)) {
236         if (first>iA) { // first is a character index
237           first=iA;
238         }
239         if (last<iA) {
240           last=iA;
241         }
242       }
243     }
244   } else {
245     first=0;
246     last=len;
247   }
248   if (last<first) {
249     // empty
250     fprintf(stderr,"WARNING: empty embedding range\n");
251     return NULL;
252   }
253 
254   // ensure hmtx is there
255   if (!otf->hmtx) {
256     if (otf_load_more(otf)!=0) {
257       fprintf(stderr,"Unsupported OTF font / cmap table \n");
258       return NULL;
259     }
260   }
261 
262   // now create the array
263   EMB_PDF_FONTWIDTHS *ret=emb_pdf_fw_new(last-first+1);
264   if (!ret) {
265     return NULL;
266   }
267   ret->first=first;
268   ret->last=last;
269   ret->widths=ret->data;
270   for (iA=0;first<=last;iA++,first++) {
271     const int gid=(encoding)?encoding[first]:otf_from_unicode(otf,first); // TODO
272     if (gid>=otf->numGlyphs) {
273       fprintf(stderr,"Bad glyphid\n");
274       assert(0);
275       free(ret);
276       return NULL;
277     }
278     if ( (!glyphs)||(bit_check(glyphs,gid)) ) {
279       ret->widths[iA]=get_width_fast(otf,gid)*1000/otf->unitsPerEm;
280     } // else 0 from calloc
281   }
282 
283   return ret;
284 }
285 // }}}
286 
287 // otf->hmtx must be there
emb_otf_pdf_glyphwidth(void * context,int gid)288 static int emb_otf_pdf_glyphwidth(void *context,int gid) // {{{
289 {
290   OTF_FILE *otf=(OTF_FILE *)context;
291   return get_width_fast(otf,gid)*1000/otf->unitsPerEm;
292 }
293 // }}}
294 
emb_otf_get_pdf_cidwidths(OTF_FILE * otf,const BITSET glyphs)295 EMB_PDF_FONTWIDTHS *emb_otf_get_pdf_cidwidths(OTF_FILE *otf,const BITSET glyphs) // {{{ // glyphs==NULL -> output all
296 {
297   assert(otf);
298 
299   // ensure hmtx is there
300   if (!otf->hmtx) {
301     if (otf_load_more(otf)!=0) {
302       fprintf(stderr,"Unsupported OTF font / cmap table \n");
303       return NULL;
304     }
305   }
306 //  int dw=emb_otf_pdf_glyphwidth(otf,0); // e.g.
307   int dw=-1; // let them estimate
308 
309   return emb_pdf_fw_cidwidths(glyphs,otf->numGlyphs,dw,emb_otf_pdf_glyphwidth,otf);
310 }
311 // }}}
312 
313 /*** PS stuff ***/
314 
315 #include "dynstring.h"
316 
317 const char *aglfn13(unsigned short uni); // aglfn13.c
318 #include "macroman.h"
319 
320 // TODO? optimize pascal string skipping? (create index)
321 // NOTE: might return a statically allocated string
emb_otf_get_post_name(const char * post,unsigned short gid)322 static const char *emb_otf_get_post_name(const char *post,unsigned short gid) // {{{
323 {
324   if (!post) {
325     return NULL;
326   }
327   const unsigned int post_version=get_ULONG(post);
328   if (post_version==0x00010000) { // font has only 258 chars... font cannot be used on windows
329     if (gid<sizeof(macRoman)/sizeof(macRoman[0])) {
330       return macRoman[gid];
331     }
332   } else if (post_version==0x00020000) {
333     const unsigned short num_glyphs=get_USHORT(post+32);
334     // assert(num_glyphs==otf->numGlyphs);
335     if (gid<num_glyphs) {
336       unsigned short idx=get_USHORT(post+34+2*gid);
337       if (idx<258) {
338         if (idx<sizeof(macRoman)/sizeof(macRoman[0])) {
339           return macRoman[idx];
340         }
341       } else if (idx<32768) {
342         const unsigned char *pos=(unsigned char *)post+34+2*num_glyphs;
343         for (idx-=258;idx>0;idx--) { // this sucks...
344           pos+=*pos+1; // skip this string
345         }
346         // convert pascal string to asciiz
347         static char ret[256];
348         const unsigned char len=*pos;
349         memcpy(ret,(const char *)pos+1,len);
350         ret[len]=0;
351         return ret;
352       }
353     }
354   } else if (post_version==0x00025000) { // similiar to 0x00010000, deprecated
355     const unsigned short num_glyphs=get_USHORT(post+32);
356     if (gid<num_glyphs) {
357       const unsigned short idx=post[34+gid]+gid; // post is signed char *
358       if (idx<sizeof(macRoman)/sizeof(macRoman[0])) {
359         return macRoman[idx];
360       }
361     }
362   } else if (post_version==0x00030000) {
363     // no glyph names, sorry
364 //  } else if (post_version==0x00040000) { // apple AAT ?!
365   }
366   return NULL;
367 }
368 // }}}
369 
370 // TODO!? to_unicode should be able to represent more than one unicode character?
371 // NOTE: statically allocated string
get_glyphname(const char * post,unsigned short * to_unicode,int charcode,unsigned short gid)372 static const char *get_glyphname(const char *post,unsigned short *to_unicode,int charcode,unsigned short gid) // {{{ if charcode==0 -> force gid to be used
373 {
374   if (gid==0) {
375     return ".notdef";
376   }
377   const char *postName=emb_otf_get_post_name(post,gid);
378   if (postName) {
379     return postName;
380   }
381   static char ret[255];
382   if (charcode) {
383     if (to_unicode) { // i.e. encoding was there
384       charcode=to_unicode[charcode];
385       // TODO!? to_unicode should be able to represent more than one unicode character?
386       // TODO for additional credit: for ligatures, etc  create /f_f /uni12341234  or the like
387     }
388     const char *aglname=aglfn13(charcode); // TODO? special case ZapfDingbats?
389     if (aglname) {
390       return aglname;
391     }
392     snprintf(ret,250,"uni%04X",charcode); // allows extraction
393   } else {
394     snprintf(ret,250,"c%d",gid);  // last resort: only by gid
395   }
396   return ret;
397 }
398 // }}}
399 
400 struct OUTFILTER_PS {
401   OUTPUT_FN out;
402   void *ctx;
403   int len;
404 };
405 
406 // TODO: for maximum compatiblity (PS<2013 interpreter)  split only on table or glyph boundary (needs lookup in loca table!)
407 // Note: table boundaries are at each call!
outfilter_ascii_ps(const char * buf,int len,void * context)408 static void outfilter_ascii_ps(const char *buf,int len,void *context)  // {{{
409 {
410   struct OUTFILTER_PS *of=context;
411   OUTPUT_FN out=of->out;
412   int iA;
413 
414   (*out)("<",1,of->ctx);
415   of->len++;
416 
417   const char *last=buf;
418   char tmp[256];
419   while (len>0) {
420     for (iA=0;(iA<76)&&(len>0);iA+=2,len--) {
421       const unsigned char ch=buf[iA>>1];
422       tmp[iA]="0123456789abcdef"[ch>>4];
423       tmp[iA+1]="0123456789abcdef"[ch&0x0f];
424     }
425     buf+=iA>>1;
426     if (buf<last+64000) {
427       if (len>0) {
428         tmp[iA++]='\n';
429       }
430       (*out)(tmp,iA,of->ctx);
431     } else {
432       last=buf;
433       strcpy(tmp+iA,"00>\n<");
434       iA+=5;
435       (*out)(tmp,iA,of->ctx);
436     }
437     of->len+=iA;
438   }
439 
440   (*out)("00>\n",4,of->ctx);
441   of->len+=4;
442 }
443 // }}}
444 
outfilter_binary_ps(const char * buf,int len,void * context)445 static void outfilter_binary_ps(const char *buf,int len,void *context)  // {{{
446 {
447   struct OUTFILTER_PS *of=context;
448   OUTPUT_FN out=of->out;
449 
450   char tmp[100];
451   while (len>0) {
452     const int maxlen=(len>64000)?64000:len;
453     const int l=sprintf(tmp,"%d RD ",maxlen);
454     (*out)(tmp,l,of->ctx);
455     of->len+=l;
456 
457     (*out)(buf,maxlen,of->ctx);
458     (*out)("\n",1,of->ctx);
459     of->len+=maxlen+1;
460     len-=maxlen;
461     buf+=maxlen;
462   }
463 }
464 // }}}
465 
466 /*
467   encoding:  character-code -> glyph id  ["required", NULL: identity, i.e. from_unicode()] // TODO: respect subsetting
468   to_unicode:  character-code -> unicode  [NULL: no char names]  // kind-of "reverse" of encoding (to_unicode does not make sense without >encoding)
469 
470 Status:
471   - we need a 0..255 encoding to be used in the PS file
472   - we want to allow the use of encoding[];  this should map from your desired PS-stream output character (0..255) directly to the gid
473   - if encoding[] is not used, MacRoman/WinAnsi/latin1 is expected (easiest: latin1, as it is a subset of unicode)
474     i.e. your want to output latin1 to the PS-stream
475   - len is the length of >encoding, or the "last used latin1 character"
476   - oh. in multibyte-mode no >encoding probably should mean identity(gid->gid) not (latin1->gid)
477   - non-multibyte PDF -> only 255 chars  ... not recommended (we can't just map to gids, but only to names, which acro will then cmap(3,1) to gids)
478 
479   => problem with subsetting BITSET (keyed by gid); we want BITSET keyed by 0..255 (via encoding)
480 
481   // TODO: a) multi font encoding
482   // TODO: b) cid/big font encoding (PS>=2015) [/CIDFontType 2]     : CMap does Charcode->CID, /CIDMap does CID->GID [e.g. Identity/delta value]
483   //          (also needed [or a)] for loca>64000 if split, etc)      e.g. /CIDMap 0  [requires PS>=3011?]
484   //          [Danger: do not split composites]
485   // TODO? incremental download [/GlyphDirectory array or dict]     : /GlyphDirectory does GID-><glyf entry> mapping
486   //       need 'fake' gdir table (size,offset=0) in sfnt; loca, glyf can be ommited; hmtx can be omitted for PS>=3011 [/MetricsCount 2]
487   //       idea is to fill initial null entries in the array/dict   [Beware of save/restore!]
488   // NOTE: even when subsetting the font has to come first in the PS file
489 
490 
491 ... special information: when multi-byte PDF encoding is used <gid> is output.
492     therefore /Encoding /Identity-H + /CIDSystemInfo Adobe-Identity-0 will yield 1-1 mapping for font.
493     problem is that text is not selectable. therefore there is the /ToUnicode CMap option
494 */
emb_otf_ps(OTF_FILE * otf,unsigned short * encoding,int len,unsigned short * to_unicode,OUTPUT_FN output,void * context)495 int emb_otf_ps(OTF_FILE *otf,unsigned short *encoding,int len,unsigned short *to_unicode,OUTPUT_FN output,void *context) // {{{
496 {
497   const int binary=0; // binary format? // TODO
498   if (len>256) {
499     fprintf(stderr,"Encoding too big(%d) for Type42\n",len);
500     return -1;
501   }
502   if (len<1) {
503     fprintf(stderr,"At least .notdef required in Type42\n");
504     return -1;
505   }
506   if (!encoding) {
507     to_unicode=NULL; // does not make sense
508   }
509   int iA,ret=0;
510 
511   DYN_STRING ds;
512   if (dyn_init(&ds,1024)==-1) {
513     return -1;
514   }
515 
516   int rlen=0;
517   char *head=otf_get_table(otf,OTF_TAG('h','e','a','d'),&rlen);
518   if (!head) {
519     free(ds.buf);
520     return -1;
521   }
522   dyn_printf(&ds,"%%!PS-TrueTypeFont-%d-%d\n",
523                  otf->version,get_ULONG(head+4));
524   const int bbxmin=get_SHORT(head+36)*1000/otf->unitsPerEm,
525             bbymin=get_SHORT(head+38)*1000/otf->unitsPerEm,
526             bbxmax=get_SHORT(head+40)*1000/otf->unitsPerEm,
527             bbymax=get_SHORT(head+42)*1000/otf->unitsPerEm;
528   free(head);
529 
530   char *post=otf_get_table(otf,OTF_TAG('p','o','s','t'),&rlen);
531   if ( (!post)&&(rlen!=-1) ) { // other error than "not found"
532     free(ds.buf);
533     return -1;
534   }
535   if (post) {
536     const unsigned int minMem=get_ULONG(post+16),maxMem=get_ULONG(post+20);
537     if (minMem) {
538       dyn_printf(&ds,"%%VMusage: %d %d\n",minMem,maxMem);
539     }
540   }
541 
542   // don't forget the coordinate scaling...
543   dyn_printf(&ds,"11 dict begin\n"
544                  "/FontName /%s def\n"
545                  "/FontType 42 def\n"
546                  "/FontMatrix [1 0 0 1 0 0] def\n"
547                  "/FontBBox [%f %f %f %f] def\n"
548                  "/PaintType 0 def\n",
549 //                 "/XUID [42 16#%X 16#%X 16#%X 16#%X] def\n"  // TODO?!? (md5 of font data)  (16# means base16)
550                  emb_otf_get_fontname(otf),
551                  bbxmin/1000.0,bbymin/1000.0,bbxmax/1000.0,bbymax/1000.0);
552   if (post) {
553     dyn_printf(&ds,"/FontInfo 4 dict dup begin\n"
554 // TODO? [even non-post]: /version|/Notice|/Copyright|/FullName|/FamilyName|/Weight  () readonly def\n   from name table: 5 7 0 4 1 2
555 // using: otf_get_name(otf,3,1,0x409,?,&len) / otf_get_name(otf,1,0,0,?,&len)   + encoding
556                    "  /ItalicAngle %d def\n"
557                    "  /isFixedPitch %s def\n"
558                    "  /UnderlinePosition %f def\n"
559                    "  /UnderlineThickness %f def\n"
560                    "end readonly def\n",
561                    get_LONG(post+4)>>16,
562                    (get_ULONG(post+12)?"true":"false"),
563                    (get_SHORT(post+8)-get_SHORT(post+10)/2)/(float)otf->unitsPerEm,
564                    get_SHORT(post+10)/(float)otf->unitsPerEm);
565   }
566   dyn_printf(&ds,"/Encoding 256 array\n"
567                  "0 1 255 { 1 index exch /.notdef put } for\n");
568   for (iA=0;iA<len;iA++) { // encoding data: 0...255 -> /glyphname
569     const int gid=(encoding)?encoding[iA]:otf_from_unicode(otf,iA);
570     if (gid!=0) {
571       dyn_printf(&ds,"dup %d /%s put\n",
572                      iA,get_glyphname(post,to_unicode,iA,gid));
573     }
574   }
575   dyn_printf(&ds,"readonly def\n");
576 
577   if (binary) {
578     dyn_printf(&ds,"/RD { string currentfile exch readstring pop } executeonly def\n");
579   }
580   dyn_printf(&ds,"/sfnts[\n");
581 
582   if (ds.len<0) {
583     free(post);
584     free(ds.buf);
585     return -1;
586   }
587   (*output)(ds.buf,ds.len,context);
588   ret+=ds.len;
589   ds.len=0;
590 
591 // TODO: only tables as in otf_subset
592 // TODO:  somehow communicate table boundaries:
593   //   otf_action_copy  does exactly one output call (per table)
594   //   only otf_action_replace might do two (padding)
595   // {{{ copy tables verbatim (does not affect ds .len)
596   struct _OTF_WRITE *otfree=NULL;
597 #if 0
598   struct _OTF_WRITE *otw;
599   otwfree=otw=malloc(sizeof(struct _OTF_WRITE)*otf->numTables);
600   if (!otw) {
601     fprintf(stderr,"Bad alloc: %m\n");
602     free(post);
603     free(ds.buf);
604     return -1;
605   }
606   // just copy everything
607   for (iA=0;iA<otf->numTables;iA++) {
608     otw[iA].tag=otf->tables[iA].tag;
609     otw[iA].action=otf_action_copy;
610     otw[iA].param=otf;
611     otw[iA].length=iA;
612   }
613   int numTables=otf->numTables;
614 #else
615   struct _OTF_WRITE otw[]={ // sorted
616       {OTF_TAG('c','m','a','p'),otf_action_copy,otf,},
617       {OTF_TAG('c','v','t',' '),otf_action_copy,otf,},
618       {OTF_TAG('f','p','g','m'),otf_action_copy,otf,},
619       {OTF_TAG('g','l','y','f'),otf_action_copy,otf,},
620       {OTF_TAG('h','e','a','d'),otf_action_copy,otf,},
621       {OTF_TAG('h','h','e','a'),otf_action_copy,otf,},
622       {OTF_TAG('h','m','t','x'),otf_action_copy,otf,},
623       {OTF_TAG('l','o','c','a'),otf_action_copy,otf,},
624       {OTF_TAG('m','a','x','p'),otf_action_copy,otf,},
625       {OTF_TAG('n','a','m','e'),otf_action_copy,otf,},
626       {OTF_TAG('p','r','e','p'),otf_action_copy,otf,},
627       // vhea vmtx (never used in PDF, but possible in PS>=3011)
628       {0,0,0,0}};
629   int numTables=otf_intersect_tables(otf,otw);
630 #endif
631 
632   struct OUTFILTER_PS of;
633   of.out=output;
634   of.ctx=context;
635   of.len=0;
636   if (binary) {
637     iA=otf_write_sfnt(otw,otf->version,numTables,outfilter_binary_ps,&of);
638   } else {
639     iA=otf_write_sfnt(otw,otf->version,numTables,outfilter_ascii_ps,&of);
640   }
641   free(otfree);
642   if (iA==-1) {
643     free(post);
644     free(ds.buf);
645     return -1;
646   }
647   ret+=of.len;
648   // }}} done copying
649 
650   dyn_printf(&ds,"] def\n");
651 
652   dyn_printf(&ds,"/CharStrings %d dict dup begin\n"
653                  "/.notdef 0 def\n",len);
654   for (iA=0;iA<len;iA++) { // charstrings data: /glyphname -> gid
655     const int gid=(encoding)?encoding[iA]:otf_from_unicode(otf,iA);
656     if (gid) {
657       dyn_printf(&ds,"/%s %d def\n",get_glyphname(post,to_unicode,iA,gid),gid);
658     }
659     // (respecting subsetting...)
660   }
661   dyn_printf(&ds,"end readonly def\n");
662   dyn_printf(&ds,"FontName currentdict end definefont pop\n");
663   free(post);
664 
665   if (ds.len<0) {
666     free(ds.buf);
667     return -1;
668   }
669   (*output)(ds.buf,ds.len,context);
670   ret+=ds.len;
671   ds.len=0;
672 
673   free(ds.buf);
674   return ret;
675 }
676 // }}}
677 
678