/* Copyright (C) 2000-2006 by George Williams */
/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.

 * The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.

 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "pfaedit.h"
#include <chardata.h>
#include <utype.h>
#include <ustring.h>
#include <math.h>
#include <locale.h>
#include <gwidget.h>
#include "ttf.h"

/* True Type is a really icky format. Nothing is together. It's badly described */
/*  much of the description is misleading */
/* Apple's version: */
/*  http://fonts.apple.com/TTRefMan/index.html */
/* MS's version: */
/*  http://www.microsoft.com/typography/tt/tt.htm */
/* An helpful but incomplete description is given at */
/*  http://www.truetype.demon.co.uk/ttoutln.htm */
/* For some things I looked at freetype's code to see how they did it */
/*  (I think only for what happens if !ARGS_ARE_XY) */
/*  http://freetype.sourceforge.net/ */
/* It grows on you though... now that I understand it better it seems better designed */
/*  but the docs remain in conflict. Sometimes badly so */

/* !!!I don't currently parse instructions to get hints, but I do save them */

int prefer_cjk_encodings=false;

int getushort(FILE *ttf) {
    int ch1 = getc(ttf);
    int ch2 = getc(ttf);
    if ( ch2==EOF )
return( EOF );
return( (ch1<<8)|ch2 );
}

int get3byte(FILE *ttf) {
    int ch1 = getc(ttf);
    int ch2 = getc(ttf);
    int ch3 = getc(ttf);
    if ( ch3==EOF )
return( EOF );
return( (ch1<<16)|(ch2<<8)|ch3 );
}

int32 getlong(FILE *ttf) {
    int ch1 = getc(ttf);
    int ch2 = getc(ttf);
    int ch3 = getc(ttf);
    int ch4 = getc(ttf);
    if ( ch4==EOF )
return( EOF );
return( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 );
}

static int32 getoffset(FILE *ttf, int offsize) {
    if ( offsize==1 )
return( getc(ttf));
    else if ( offsize==2 )
return( getushort(ttf));
    else if ( offsize==3 )
return( get3byte(ttf));
    else
return( getlong(ttf));
}

real getfixed(FILE *ttf) {
    int32 val = getlong(ttf);
    int mant = val&0xffff;
    /* This oddity may be needed to deal with the first 16 bits being signed */
    /*  and the low-order bits unsigned */
return( (real) (val>>16) + (mant/65536.0) );
}

real get2dot14(FILE *ttf) {
    int32 val = getushort(ttf);
    int mant = val&0x3fff;
    /* This oddity may be needed to deal with the first 2 bits being signed */
    /*  and the low-order bits unsigned */
return( (real) ((val<<16)>>(16+14)) + (mant/16384.0) );
}

static Encoding *enc_from_platspec(int platform,int specific) {
    char *enc;
    Encoding *e;

    enc = "Custom";
    if ( platform==0 )
	enc = "Unicode";
    else if ( platform==1 ) {
	if ( specific==0 )
	    enc = "Mac";
	else if ( specific==1 )
	    enc = "Sjis";
	else if ( specific==2 )
	    enc = "Big5hkscs";		/* Or should we just guess big5? Both are wrong sometimes */
	else if ( specific==3 )
	    enc = "EUC-KR";
	else if ( specific==25 )
	    enc = "EUC-CN";
    } else if ( platform==2 ) {		/* obselete */
	if ( specific==0 )
	    enc = "ASCII";
	else if ( specific==1 )
	    enc = "Unicode";
	else if ( specific==2 )
	    enc = "ISO8859-1";
    } else if ( platform==3 ) {
	if ( specific==1 || specific==0 )	/* symbol (sp=0) is just unicode (PUA) */
	    enc = "Unicode";
	else if ( specific==2 )
	    enc = "Sjis";
	else if ( specific==3 )
	    enc = "EUC-CN";
	else if ( specific==4 )
	    enc = "Big5hkscs";
	else if ( specific==5 )
	    enc = "EUC-KR";
	else if ( specific==6 )
	    enc = "Johab";
	else if ( specific==10 )
	    enc = "UnicodeFull";
    } else if ( platform==7 ) {		/* Used internally in freetype, but */
	if ( specific==0 )		/*  there's no harm in looking for it */
	    enc = "AdobeStandard";	/*  even if it never happens */
	else if ( specific==1 )
	    /* adobe_expert */;
	else if ( specific==2 )
	    /* adobe_custom */;
    }
    e = FindOrMakeEncoding(enc);
    if ( e==NULL ) {
	static int p = -1,s = -1;
	if ( p!=platform || s!=specific ) {
	    LogError( _("The truetype encoding specified by platform=%d specific=%d (which we map to %s) is not supported by your version of iconv(3).\n"),
		    platform, specific, enc );
	    p = platform; s = specific;
	}
    }
return( e );
}

static char *_readencstring(FILE *ttf,int offset,int len,
	int platform,int specific,int language) {
    long pos = ftell(ttf);
    unichar_t *str, *pt;
    char *ret;
    int i, ch;
    Encoding *enc;

    fseek(ttf,offset,SEEK_SET);

    if ( platform==1 ) {
	/* Mac is screwy, there are several different varients of MacRoman */
	/*  depending on the language, they didn't get it right when they  */
	/*  invented their script system */
	char *cstr, *cpt;
	cstr = cpt = galloc(len+1);
	for ( i=0; i<len; ++i )
	    *cpt++ = getc(ttf);
	*cpt = '\0';
	ret = MacStrToUtf8(cstr,specific,language);
	free(cstr);
    } else {
	enc = enc_from_platspec(platform,specific);
	if ( enc==NULL )
return( NULL );
	if ( enc->is_unicodebmp ) {
	    str = pt = galloc(len+2);
	    for ( i=0; i<len/2; ++i ) {
		ch = getc(ttf)<<8;
		*pt++ = ch | getc(ttf);
	    }
	    *pt = 0;
	} else if ( enc->unicode!=NULL ) {
	    str = pt = galloc(2*len+2);
	    for ( i=0; i<len; ++i )
		*pt++ = enc->unicode[getc(ttf)];
	    *pt = 0;
	} else if ( enc->tounicode!=NULL ) {
	    size_t inlen = len+1, outlen = 2*len+2;
	    char *cstr = galloc(inlen);
	    ICONV_CONST char *in = cstr;
	    char *out;
	    str = galloc(outlen+2);
	    out = (char *) str;
	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
	    out[0] = '\0'; out[1] = '\0';
	    free(cstr);
	} else {
	    str = uc_copy("");
	}
	ret = u2utf8_copy(str);
	free(str);
    }
    fseek(ttf,pos,SEEK_SET);
return( ret );
}

char *TTFGetFontName(FILE *ttf,int32 offset,int32 off2) {
    int i,num;
    int32 tag, nameoffset, length, stringoffset;
    int plat, spec, lang, name, len, off, val;
    int fullval, fullstr, fulllen, famval, famstr, famlen;
    Encoding *enc;
    int fullplat, fullspec, fulllang, famplat, famspec, famlang;

    fseek(ttf,offset,SEEK_SET);
    /* version = */ getlong(ttf);
    num = getushort(ttf);
    /* srange = */ getushort(ttf);
    /* esel = */ getushort(ttf);
    /* rshift = */ getushort(ttf);
    for ( i=0; i<num; ++i ) {
	tag = getlong(ttf);
	/* checksum = */ getlong(ttf);
	nameoffset = off2+getlong(ttf);
	length = getlong(ttf);
	if ( tag==CHR('n','a','m','e'))
    break;
    }
    if ( i==num )
return( NULL );

    fseek(ttf,nameoffset,SEEK_SET);
    /* format = */ getushort(ttf);
    num = getushort(ttf);
    stringoffset = nameoffset+getushort(ttf);
    fullval = famval = 0;
    for ( i=0; i<num; ++i ) {
	plat = getushort(ttf);
	spec = getushort(ttf);
	lang = getushort(ttf);
	name = getushort(ttf);
	len = getushort(ttf);
	off = getushort(ttf);
	enc = enc_from_platspec(plat,spec);
	if ( enc==NULL )
    continue;
	val = 0;
    /* I really want an english name */
	if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
	    val = 11;
	else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
	    val = 12;
    /* failing that I'll take what I can get */
	else if ( !enc->is_custom )
	    val = 1;
	if ( name==4 && val>fullval ) {
	    fullval = val;
	    fullstr = off;
	    fulllen = len;
	    fullplat = plat;
	    fullspec = spec;
	    fulllang = lang;
	    if ( val==12 )
    break;
	} else if ( name==1 && val>famval ) {
	    famval = val;
	    famstr = off;
	    famlen = len;
	    famplat = plat;
	    famspec = spec;
	    famlang = lang;
	}
    }
    if ( fullval==0 ) {
	if ( famval==0 )
return( NULL );
	fullstr = famstr;
	fulllen = famlen;
	fullplat = famplat;
	fullspec = famspec;
	fulllang = famlang;
    }
return( _readencstring(ttf,stringoffset+fullstr,fulllen,fullplat,fullspec,fulllang));
}

static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
    int32 *offsets, cnt, i, choice, j;
    char **names;
    char *pt, *lparen;

    /* TTCF version = */ getlong(ttf);
    cnt = getlong(ttf);
    if ( cnt==1 ) {
	/* This is easy, don't bother to ask the user, there's no choice */
	int32 offset = getlong(ttf);
	fseek(ttf,offset,SEEK_SET);
return( true );
    }
    offsets = galloc(cnt*sizeof(int32));
    for ( i=0; i<cnt; ++i )
	offsets[i] = getlong(ttf);
    names = galloc(cnt*sizeof(char *));
    for ( i=j=0; i<cnt; ++i ) {
	names[j] = TTFGetFontName(ttf,offsets[i],0);
	if ( names[j]!=NULL ) ++j;
    }
    pt = strrchr(filename,'/');
    if ( pt==NULL ) pt = filename;
    if ( (lparen = strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) {
	char *find = copy(lparen+1);
	pt = strchr(find,')');
	if ( pt!=NULL ) *pt='\0';
	for ( choice=cnt-1; choice>=0; --choice )
	    if ( strcmp(names[choice],find)==0 )
	break;
	if ( choice==-1 ) {
	    char *fn = copy(filename);
	    fn[lparen-filename] = '\0';
	    gwwv_post_error(_("Not in Collection"),
/* GT: The user is trying to open a font file which contains multiple fonts and */
/* GT: has asked for a font which is not in that file. */
/* GT: The string will look like: <fontname> is not in <filename> */
		    _("%1$s is not in %2$.100s"),find,fn);
	    free(fn);
	}
	free(find);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    } else if ( no_windowing_ui )
	choice = 0;
    else
	choice = gwwv_choose(_("Pick a font, any font..."),(const char **) names,j,0,_("There are multiple fonts in this file, pick one"));
#else
    } else
	choice = 0;
#endif		/* FONTFORGE_CONFIG_NO_WINDOWING_UI */
    if ( choice!=-1 ) {
	fseek(ttf,offsets[choice],SEEK_SET);
	*chosenname = copy(names[choice]);
    }
    for ( i=0; i<j; ++i )
	free(names[i]);
    free(names);
    free(offsets);
return( choice!=-1);
}

static int PickCFFFont(char **fontnames) {
    unichar_t **names;
    int cnt, i, choice;

    for ( cnt=0; fontnames[cnt]!=NULL; ++cnt);
    names = gcalloc(cnt+1,sizeof(unichar_t *));
    for ( i=0; i<cnt; ++i )
	names[i] = uc_copy(fontnames[i]);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    if ( no_windowing_ui )
	choice = 0;
    else
	choice = gwwv_choose(_("Pick a font, any font..."),
	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
#elif defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    choice = 0;
#endif		/* FONTFORGE_CONFIG_NO_WINDOWING_UI */
    for ( i=0; i<cnt; ++i )
	free(names[i]);
    free(names);
return( choice );
}

static void ParseSaveTablesPref(struct ttfinfo *info) {
    extern char *SaveTablesPref;
    char *pt, *spt;
    int cnt;

    info->savecnt = 0;
    info->savetab = NULL;
    if ( SaveTablesPref==NULL || *SaveTablesPref=='\0' )
return;
    for ( pt=SaveTablesPref, cnt=0; *pt; ++pt )
	if ( *pt==',' )
	    ++cnt;
    info->savecnt = cnt+1;
    info->savetab = gcalloc(cnt+1,sizeof(struct savetab));
    for ( pt=spt=SaveTablesPref, cnt=0; ; ++pt ) {
	if ( *pt==',' || *pt=='\0' ) {
	    uint32 tag;
	    tag  = ( ( spt  <pt )? spt[0] : ' ' )<<24;
	    tag |= ( ( spt+1<pt )? spt[1] : ' ' )<<16;
	    tag |= ( ( spt+2<pt )? spt[2] : ' ' )<<8 ;
	    tag |= ( ( spt+3<pt )? spt[3] : ' ' )    ;
	    info->savetab[cnt++].tag = tag;
	    if ( *pt )
		spt = pt+1;
	    else
    break;
	}
    }
}

static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
	char **choosenname) {
    int i, j;
    int tag, checksum, offset, length, version;

    version=getlong(ttf);
    if ( version==CHR('t','t','c','f')) {
	/* TrueType font collection */
	info->is_ttc = true;
	if ( !PickTTFFont(ttf,filename,choosenname))
return( 0 );
	/* If they picked a font, then we should be left pointing at the */
	/*  start of the Table Directory for that font */
	info->one_of_many = true;
	version = getlong(ttf);
    }

    /* Apple says that 'typ1' is a valid code for a type1 font wrapped up in */
    /*  a truetype table structure, but gives no docs on what tables get used */
    /*  or how */
    if ( version==CHR('t','y','p','1')) {
	if ( filename==NULL ) filename = "it";
	LogError( _("Interesting, I've never seen an example of this type of font, could you\nsend me a copy of %s?\nThanks\n  gww@silcom.com\n"), filename );
    }
    if ( version!=0x00010000 && version!=CHR('t','r','u','e') &&
	    version!=0x00020000 &&	/* Windows 3.1 Chinese version used this version for some arphic fonts */
					/* See discussion on freetype list, july 2004 */
	    version!=CHR('O','T','T','O'))
return( 0 );			/* Not version 1 of true type, nor Open Type */
    info->numtables = getushort(ttf);
    /* searchRange = */ getushort(ttf);
    /* entrySelector = */ getushort(ttf);
    /* rangeshift = */ getushort(ttf);

    ParseSaveTablesPref(info);

    for ( i=0; i<info->numtables; ++i ) {
	tag = getlong(ttf);
	checksum = getlong(ttf);
	offset = getlong(ttf);
	length = getlong(ttf);
#ifdef DEBUG
 printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
#endif
	switch ( tag ) {
	  case CHR('C','F','F',' '):
	    info->cff_start = offset;
	    info->cff_length = length;
	  break;
	  case CHR('c','m','a','p'):
	    info->encoding_start = offset;
	  break;
	  case CHR('g','l','y','f'):
	    info->glyph_start = offset;
	    info->glyph_length = length;
	  break;
	  case CHR('G','D','E','F'):
	    info->gdef_start = offset;
	    info->gdef_length = length;
	  break;
	  case CHR('G','P','O','S'):
	    info->gpos_start = offset;
	    info->gpos_length = length;
	  break;
	  case CHR('G','S','U','B'):
	    info->gsub_start = offset;
	    info->gsub_length = length;
	  break;
	  case CHR('b','d','a','t'):		/* Apple/MS use a different tag, but the same format. Great. */
	  case CHR('E','B','D','T'):
	    info->bitmapdata_start = offset;
	    info->bitmapdata_length = length;
	  break;
	  case CHR('b','l','o','c'):		/* Apple/MS use a different tag. Great. */
	  case CHR('E','B','L','C'):
	    info->bitmaploc_start = offset;
	    info->bitmaploc_length = length;
	  break;
	  case CHR('b','h','e','d'):		/* Apple uses bhed for fonts with only bitmaps */
	  case CHR('h','e','a','d'):
	    info->head_start = offset;
	  break;
	  case CHR('h','h','e','a'):
	    info->hhea_start = offset;
	  break;
	  case CHR('h','m','t','x'):
	    info->hmetrics_start = offset;
	  break;
	  case CHR('k','e','r','n'):
	    info->kern_start = offset;
	  break;
	  case CHR('l','o','c','a'):
	    info->glyphlocations_start = offset;
	    info->loca_length = length;
	    info->glyph_cnt = length/2-1;	/* the minus one is because there is one extra entry to give the length of the last glyph */
	    if ( info->glyph_cnt<0 ) info->glyph_cnt = 0;
	  break;
	  case CHR('m','a','x','p'):
	    info->maxp_start = offset;
	    info->maxp_len = length;
	  break;
	  case CHR('n','a','m','e'):
	    info->copyright_start = offset;
	  break;
	  case CHR('p','o','s','t'):
	    info->postscript_start = offset;
	  break;
	  case CHR('O','S','/','2'):
	    info->os2_start = offset;
	  break;
	  case CHR('v','h','e','a'):
	    info->vhea_start = offset;
	  break;
	  case CHR('v','m','t','x'):
	    info->vmetrics_start = offset;
	  break;
	  case CHR('V','O','R','G'):
	    info->vorg_start = offset;
	  break;
	      /* Apple stuff */
	  case CHR('a','c','n','t'):
	    info->acnt_start = offset;
	  break;
	  case CHR('f','e','a','t'):
	    info->feat_start = offset;
	  break;
	  case CHR('l','c','a','r'):
	    info->lcar_start = offset;
	  break;
	  case CHR('m','o','r','t'):
	    info->mort_start = offset;
	  break;
	  case CHR('m','o','r','x'):
	    info->morx_start = offset;
	  break;
	  case CHR('o','p','b','d'):
	    info->opbd_start = offset;
	  break;
	  case CHR('p','r','o','p'):
	    info->prop_start = offset;
	  break;
	      /* to make sense of instrs */
	  case CHR('c','v','t',' '):
	    info->cvt_start = offset;
	    info->cvt_len = length;
	  break;
	  case CHR('p','r','e','p'):
	    info->prep_start = offset;
	    info->prep_len = length;
	  break;
	  case CHR('f','p','g','m'):
	    info->fpgm_start = offset;
	    info->fpgm_len = length;
	  break;

	    /* non-standard tables I've added */
	  case CHR('P','f','E','d'):
	    info->pfed_start = offset;
	  break;
	  case CHR('F','F','T','M'):
	    info->fftm_start = offset;
	  break;
	  case CHR('T','e','X',' '):
	    info->tex_start = offset;
	  break;
	  case CHR('B','D','F',' '):
	    info->bdf_start = offset;
	  break;

	    /* Apple's mm fonts */
	  case CHR('g','v','a','r'):
	    info->gvar_start = offset;
	    info->gvar_len = length;
	  break;
	  case CHR('f','v','a','r'):
	    info->fvar_start = offset;
	    info->fvar_len = length;
	  break;
	  case CHR('a','v','a','r'):
	    info->avar_start = offset;
	    info->avar_len = length;
	  break;
	  case CHR('c','v','a','r'):
	    info->cvar_start = offset;
	    info->cvar_len = length;
	  break;

	  default:
	    for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
		info->savetab[j].offset = offset;
		info->savetab[j].len = length;
	    break;
	    }
	}
    }
return( true );
}

static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
    int date[4], date1970[4], year[2];
    int i;
    /* Dates in sfnt files are seconds since 1904. I adjust to unix time */
    /*  seconds since 1970 by figuring out how many seconds were in between */

    date[3] = getushort(ttf);
    date[2] = getushort(ttf);
    date[1] = getushort(ttf);
    date[0] = getushort(ttf);
    memset(date1970,0,sizeof(date1970));
    year[0] = (60*60*24*365L)&0xffff;
    year[1] = (60*60*24*365L)>>16;
    for ( i=1904; i<1970; ++i ) {
	date1970[0] += year[0];
	date1970[1] += year[1];
	if ( (i&3)==0 && (i%100!=0 || i%400==0))
	    date1970[0] += 24*60*60L;		/* Leap year */
	date1970[1] += (date1970[0]>>16);
	date1970[0] &= 0xffff;
	date1970[2] += date1970[1]>>16;
	date1970[1] &= 0xffff;
	date1970[3] += date1970[2]>>16;
	date1970[2] &= 0xffff;
    }

    for ( i=0; i<3; ++i ) {
	date[i] -= date1970[i];
	date[i+1] += date[i]>>16;
	date[i] &= 0xffff;
    }
    date[3] -= date1970[3];

    *(ismod ? &info->modificationtime : &info->creationtime) =
#ifdef _HAS_LONGLONG
	    (((long long) date[3])<<48) |
	    (((long long) date[2])<<32) |
#endif
	    (             date[1] <<16) |
			  date[0];
}

static void readttfhead(FILE *ttf,struct ttfinfo *info) {
    /* Here I want units per em, and size of loca entries */
    /* oh... also creation/modification times */
    int i;
    fseek(ttf,info->head_start+4*4+2,SEEK_SET);		/* skip over the version number and a bunch of junk */
    info->emsize = getushort(ttf);
    for ( i=0; i<12; ++i )
	getushort(ttf);
    info->macstyle = getushort(ttf);
    for ( i=0; i<2; ++i )
	getushort(ttf);
    info->index_to_loc_is_long = getushort(ttf);
    if ( info->index_to_loc_is_long )
	info->glyph_cnt = info->loca_length/4-1;
    if ( info->glyph_cnt<0 ) info->glyph_cnt = 0;

    if ( info->fftm_start!=0 ) {
	fseek(ttf,info->fftm_start+3*4,SEEK_SET);
    } else {
	fseek(ttf,info->head_start+4*4+2+2,SEEK_SET);
    }
    readdate(ttf,info,0);
    readdate(ttf,info,1);
}

static void readttfhhea(FILE *ttf,struct ttfinfo *info) {
    /* Here I want ascent, descent and the number of horizontal metrics */
    int i;

    fseek(ttf,info->hhea_start+4,SEEK_SET);		/* skip over the version number */
    info->pfminfo.hhead_ascent = getushort(ttf);
    info->pfminfo.hhead_descent = (short) getushort(ttf);
    info->pfminfo.hheadascent_add = info->pfminfo.hheaddescent_add = false;
    info->pfminfo.linegap = getushort(ttf);
    info->pfminfo.hheadset = true;
    /*info->ascent = info->pfminfo.hhead_ascent;*/

    info->ascent = .8*info->emsize;
    info->descent = info->emsize-info->ascent;

    for ( i=0; i<12; ++i )
	getushort(ttf);
    info->width_cnt = getushort(ttf);
}

static void readttfmaxp(FILE *ttf,struct ttfinfo *info) {
    /* All I want here is the number of glyphs */
    int cnt;
    fseek(ttf,info->maxp_start+4,SEEK_SET);		/* skip over the version number */
    cnt = getushort(ttf);
    if ( info->glyph_cnt==0 && info->glyph_length==0 && info->loca_length<=4 &&
	    info->cff_length==0 ) {
	/* X11 OpenType bitmap format */;
	info->onlystrikes = true;
    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
#endif
	if ( cnt>info->glyph_cnt )
	    cnt = info->glyph_cnt;		/* Use the smaller of the two values */
    }
    /* Open Type fonts have no loca table, so we can't calculate the glyph */
    /*  count from it */
    info->glyph_cnt = cnt;
    if ( cnt<0 ) info->glyph_cnt = 0;
}

static char *stripspaces(char *str) {
    char *str2 = str, *base = str;

    if ( str==NULL )
return( NULL );

    while ( *str ) {
	if ( *str==' ' )
	    ++str;
	else
	    *str2++ = *str++;
    }
    *str2 = '\0';
return( base );
}

static struct macname *AddMacName(FILE *ttf,
	int strlen, int stroff,int spec,int language, struct macname *last) {
    struct macname *new = chunkalloc(sizeof(struct macname));
    long pos = ftell(ttf);
    char *pt;
    int i;

    new->next = last;
    new->enc = spec;
    new->lang = language;
    new->name = pt = galloc(strlen+1);

    fseek(ttf,stroff,SEEK_SET);

    for ( i=0; i<strlen; ++i )
	*pt++ = getc(ttf);
    *pt = '\0';

    fseek(ttf,pos,SEEK_SET);
return( new );
}

static void MacFeatureAdd(FILE *ttf, struct ttfinfo *info, int id,
	int strlen, int stroff,int spec,int language) {
    MacFeat *f;
    struct macsetting *s;

    for ( f=info->features; f!=NULL; f=f->next ) {
	if ( f->strid==id ) {
	    f->featname = AddMacName(ttf,strlen,stroff,spec,language,f->featname);
return;
	} else {
	    for ( s=f->settings; s!=NULL; s=s->next ) {
		if ( s->strid==id ) {
		    s->setname = AddMacName(ttf,strlen,stroff,spec,language,s->setname);
return;
		}
	    }
	}
    }
    /* Well, there are some things in the name table other than feature/setting*/
    /*  names. Let's keep track of everything just in case.... */
    if ( info->fvar_start!=0 ) {
	struct macidname *mi, *p;
	for ( p=NULL, mi=info->macstrids; mi!=NULL && mi->id!=id; p = mi, mi=mi->next );
	if ( mi==NULL ) {
	    mi = chunkalloc(sizeof(struct macidname));
	    mi->id = id;
	    mi->last = mi->head = AddMacName(ttf,strlen,stroff,spec,language,NULL);
	    if ( p==NULL )
		info->macstrids = mi;
	    else
		p->next = mi;
	} else {
	    mi->last->next = AddMacName(ttf,strlen,stroff,spec,language,NULL);
	    mi->last = mi->last->next;
	}
    }
}

static void ValidatePostScriptFontName(char *str) {
    char *end, *pt, *npt;
    int complained = false;

    /* someone gave me a font where the fontname started with the utf8 byte */
    /*  order mark.  PLRM says only ASCII encoding is supported. CFF says */
    /*  only printable ASCII should be used */
    if ( ((uint8 *) str)[0] == 0xef && ((uint8 *) str)[1]==0xbb && ((uint8 *) str)[2] == 0xbf ) {
	LogError(_("The fontname begins with the utf8 byte order sequence. This is illegal. %s"), str+3 );
	for ( pt=str+3; *pt; ++pt )
	    pt[-3] = *pt;		/* ANSI says we can't strcpy overlapping strings */
    }
    strtod(str,&end);
    if ( (*end=='\0' || (isdigit(str[0]) && strchr(str,'#')!=NULL)) &&
	    *str!='\0' ) {
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_post_error(_("Bad Font Name"),_("A Postscript name may not be a number"));
#endif
	*str = 'a';
	complained = true;
    }
    for ( pt=str; *pt; ++pt ) {
	if ( *pt<=' ' || *pt>=0x7f ||
		*pt=='(' || *pt=='[' || *pt=='{' || *pt=='<' ||
		*pt==')' || *pt==']' || *pt=='}' || *pt=='>' ||
		*pt=='%' || *pt=='/' ) {
	    if ( !complained ) {
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
		gwwv_post_error(_("Bad Font Name"),_("The Postscript font name \"%.63s\" is invalid.\nIt should be printable ASCII,\nmust not contain (){}[]<>%%/ or space\nand must be shorter than 63 characters"),str);
#endif
	    }
	    complained = true;
	    for ( npt=pt; npt[1]; ++npt )
		*npt = npt[1];
	    *npt = '\0';
	    --pt;
	}
    }
    if ( strlen(str)>63 ) {
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_post_error(_("Bad Font Name"),_("The Postscript font name \"%.63s\" is invalid.\nIt should be printable ASCII,\nmust not contain (){}[]<>%%/ or space\nand must be shorter than 63 characters"),str);
#endif
	str[63] = '\0';
    }
}

char *EnforcePostScriptName(char *old) {
    char *end, *pt, *npt, *str = copy(old);

    if ( old==NULL )
return( old );

    strtod(str,&end);
    if ( (*end=='\0' || (isdigit(str[0]) && strchr(str,'#')!=NULL)) &&
	    *str!='\0' ) {
	free(str);
	str=galloc(strlen(old)+2);
	*str = 'a';
	strcpy(str+1,old);
    }
    for ( pt=str; *pt; ++pt ) {
	if ( *pt<=' ' || *pt>=0x7f ||
		*pt=='(' || *pt=='[' || *pt=='{' || *pt=='<' ||
		*pt==')' || *pt==']' || *pt=='}' || *pt=='>' ||
		*pt=='%' || *pt=='/' ) {
	    for ( npt=pt; npt[1]; ++npt )
		*npt = npt[1];
	    *npt = '\0';
	}
    }
    if ( strlen(str)>63 )
	str[63] = '\0';
return( str );
}

static int IsSubSetOf(const char *substr,const char *fullstr ) {
    /* The mac string is often a subset of the unicode string. Certain */
    /*  characters can't be expressed in the mac encoding and are omitted */
    /*  or turned to question marks or some such */
    const char *pt1, *pt2;
    uint32 ch1, ch2;

    for ( pt1=substr, pt2=fullstr, ch1=utf8_ildb(&pt1); ch1!=0 ; ) {
	if ( *pt2=='\0' )
    break;
	ch2 = utf8_ildb(&pt2);
	if ( ch1==ch2 )
	    ch1 = utf8_ildb(&pt1);
    }
    if ( ch1=='\0' )
return( true );

    for ( pt1=substr, pt2=fullstr, ch1=utf8_ildb(&pt1); ch1!=0 ; ) {
	if ( *pt2=='\0' )
    break;
	ch2 = utf8_ildb(&pt2);
	if ( ch1==ch2 || ch1=='?' )
	    ch1 = utf8_ildb(&pt1);
    }
return( ch1=='\0' );
}

static void TTFAddLangStr(FILE *ttf, struct ttfinfo *info, int id,
	int strlen, int stroff,int plat,int spec,int language) {
    struct ttflangname *cur, *prev;
    char *str;

    if ( plat==1 && id>=256 && (info->features!=NULL || info->fvar_start!=0)) {
	MacFeatureAdd(ttf,info,id,strlen,stroff,spec,language);
return;
    } else if ( id<0 || id>=ttf_namemax )
return;

    str = _readencstring(ttf,stroff,strlen,plat,spec,language);
    if ( str==NULL )		/* we didn't understand the encoding */
return;
    if ( id==ttf_postscriptname )
	ValidatePostScriptFontName(str);
    if ( *str=='\0' ) {
	free(str);
return;
    }

    if ( plat==1 || plat==0 )
	language = WinLangFromMac(language);
    if ( (language&0xff00)==0 ) language |= 0x400;

    for ( prev=NULL, cur=info->names; cur!=NULL && cur->lang!=language; prev = cur, cur=cur->next );
    if ( cur==NULL ) {
	cur = chunkalloc(sizeof(struct ttflangname));
	cur->lang = language;
	if ( prev==NULL )
	    info->names = cur;
	else
	    prev->next = cur;
    }
    if ( cur->names[id]==NULL ) {
	cur->names[id] = str;
	if ( plat==1 || plat==0 )
	    cur->frommac[id/32] |= (1<<(id&0x1f));
/* There's some wacky bug in gcc. If the optimizer is on then strcmp gets turned */
/*  into some inline call. Which would be fine, except the following line goes */
/*  bluey. "Called object is not a function", etc. Compiles fine if we turn off */
/*  the inlining magic */
#ifdef strcmp
# undef strcmp
#endif
    } else if ( strcmp(str,cur->names[id])==0 ) {
	free(str);
	if ( plat==3 )
	    cur->frommac[id/32] &= ~(1<<(id&0x1f));
    } else if ( plat==1 ) {
	/* Mac string doesn't match mac unicode string */
	if ( !IsSubSetOf(str,cur->names[id]) )
	    LogError( _("Warning: Mac and Unicode entries in the 'name' table differ for the\n %s string in the language %s\n Mac String: %s\nMac Unicode String: %s\n"),
		    TTFNameIds(id),MSLangString(language),
		    str,cur->names[id]);
	else
	    LogError( _("Warning: Mac string is a subset of the Unicode string in the 'name' table\n for the %s string in the %s language.\n"),
		    TTFNameIds(id),MSLangString(language));
	free(str);
    } else if ( plat==3 && (cur->frommac[id/32] & (1<<(id&0x1f))) ) {
	if ( !IsSubSetOf(cur->names[id],str) )
	    LogError( _("Warning: Mac and Windows entries in the 'name' table differ for the\n %s string in the language %s\n Mac String: %s\nWindows String: %s\n"),
		    TTFNameIds(id),MSLangString(language),
		    cur->names[id],str);
	else
	    LogError( _("Warning: Mac string is a subset of the Windows string in the 'name' table\n for the %s string in the %s language.\n"),
		    TTFNameIds(id),MSLangString(language));
	free(cur->names[id]);
	cur->names[id] = str;
	cur->frommac[id/32] &= ~(1<<(id&0x1f));
    } else {
	int ret;
	extern int running_script;
	if ( info->dupnamestate!=0 )
	    ret = info->dupnamestate;
	else if ( running_script )
	    ret = 3;
	else {
	    char *buts[5];
	    buts[0] = _("Use _First");
	    buts[1] = _("First to _All");
	    buts[2] = _("Second _to All");
	    buts[3] = _("Use _Second");
	    buts[4] = NULL;
#ifndef FONTFORGE_CONFIG_NO_WINDOWING_UI
	    ret = gwwv_ask(_("Multiple names for language"),(const char **)buts,0,3,
		    _("The 'name' table contains (at least) two strings for the %s in language %s, the first '%.12s...' the second '%.12s...'.\nWhich do you prefer?"),
		    TTFNameIds(id),MSLangString(language),
		    cur->names[id],str);
#else
	    ret = 3;
#endif
	    if ( ret==1 || ret==2 )
		info->dupnamestate = ret;
	}
	if ( ret==0 || ret==1 )
	    free(str);
	else {
	    free(cur->names[id]);
	    cur->names[id] = str;
	}
    }
}

static int is_ascii(char *str) {	/* isascii is in ctype */
    if ( str==NULL )
return( false );
    while ( *str && *str<127 && *str>=' ' )
	++str;
return( *str=='\0' );
}

static char *FindLangEntry(struct ttfinfo *info, int id ) {
    /* Look for an entry with string id */
    /* we prefer english, if we can't find english look for something in ascii */
    struct ttflangname *cur;
    char *ret;

    for ( cur=info->names; cur!=NULL && cur->lang!=0x409; cur=cur->next );
    if ( cur!=NULL && cur->names[id]==NULL ) cur = NULL;
    if ( cur==NULL )
	for ( cur=info->names; cur!=NULL && (cur->lang&0xf)!=0x09; cur=cur->next );
    if ( cur!=NULL && cur->names[id]==NULL ) cur = NULL;
    if ( cur==NULL )
	for ( cur=info->names; cur!=NULL && !is_ascii(cur->names[id]); cur=cur->next );
    if ( cur==NULL )
	for ( cur=info->names; cur!=NULL && cur->names[id]==NULL; cur=cur->next );
    if ( cur==NULL )
return( NULL );
    ret = copy(cur->names[id]);	
return( ret );
}

struct otfname *FindAllLangEntries(FILE *ttf, struct ttfinfo *info, int id ) {
    /* Look for all entries with string id under windows platform */
    int32 here = ftell(ttf);
    int i, cnt, tableoff;
    int platform, specific, language, name, str_len, stroff;
    struct otfname *head=NULL, *cur;

    if ( info->copyright_start!=0 && id!=0 ) {
	fseek(ttf,info->copyright_start,SEEK_SET);
	/* format selector = */ getushort(ttf);
	cnt = getushort(ttf);
	tableoff = info->copyright_start+getushort(ttf);
	for ( i=0; i<cnt; ++i ) {
	    platform = getushort(ttf);
	    specific = getushort(ttf);
	    language = getushort(ttf);
	    name = getushort(ttf);
	    str_len = getushort(ttf);
	    stroff = getushort(ttf);

	    if ( platform==3 && name==id ) {
		char *temp = _readencstring(ttf,tableoff+stroff,str_len,platform,specific,language);
		if ( temp!=NULL ) {
		    cur = chunkalloc(sizeof(struct otfname));
		    cur->next = head;
		    head = cur;
		    cur->lang = language;
		    cur->name = temp;
		}
	    }
	}
	fseek(ttf,here,SEEK_SET);
    }
return( head );
}

static struct macname *reversemacnames(struct macname *mn) {
    struct macname *next, *prev=NULL;

    if ( mn==NULL )
return( NULL );

    next = mn->next;
    while ( next!=NULL ) {
	mn->next = prev;
	prev = mn;
	mn = next;
	next = mn->next;
    }
    mn->next = prev;
return( mn );
}

static void readttfcopyrights(FILE *ttf,struct ttfinfo *info) {
    int i, cnt, tableoff;
    int platform, specific, language, name, str_len, stroff;

    if ( info->feat_start!=0 )
	readmacfeaturemap(ttf,info);
    if ( info->copyright_start!=0 ) {
	fseek(ttf,info->copyright_start,SEEK_SET);
	/* format selector = */ getushort(ttf);
	cnt = getushort(ttf);
	tableoff = info->copyright_start+getushort(ttf);
	for ( i=0; i<cnt; ++i ) {
	    platform = getushort(ttf);
	    specific = getushort(ttf);
	    language = getushort(ttf);
	    name = getushort(ttf);
	    str_len = getushort(ttf);
	    stroff = getushort(ttf);
    
	    TTFAddLangStr(ttf,info,name,str_len,tableoff+stroff,
		    platform,specific,language);
	}
    }

    if ( info->copyright==NULL )
	info->copyright = FindLangEntry(info,ttf_copyright);
    if ( info->familyname==NULL )
	info->familyname = FindLangEntry(info,ttf_family);
    if ( info->fullname==NULL )
	info->fullname = FindLangEntry(info,ttf_fullname);
    if ( info->version==NULL )
	info->version = FindLangEntry(info,ttf_version);
    if ( info->fontname==NULL )
	info->fontname = FindLangEntry(info,ttf_postscriptname);

    if ( info->fontname != NULL && *info->fontname=='\0' ) {
	free(info->fontname);
	info->fontname = NULL;
    }
    if ( info->familyname != NULL && *info->familyname=='\0' ) {
	free(info->familyname);
	info->familyname = NULL;
    }
    if ( info->fullname != NULL && *info->fullname=='\0' ) {
	free(info->fullname);
	info->fullname = NULL;
    }

    /* OpenType spec says the version string should begin with "Version " and */
    /*  end with a space and have a number in between */
    if ( info->version==NULL ) info->version = copy("1.0");
    else if ( strnmatch(info->version,"Version ",8)==0 ) {
	char *temp = copy(info->version+8);
	if ( temp[strlen(temp)-1]==' ' )
	    temp[strlen(temp)-1] = '\0';
	free(info->version);
	info->version = temp;
    }
    if ( info->fontname==NULL ) {
	if ( info->fullname!=NULL )
	    info->fontname = stripspaces(copy(info->fullname));
	if ( info->fontname==NULL && info->familyname!=NULL )
	    info->fontname = stripspaces(copy(info->familyname));
	if ( info->fontname!=NULL )
	    ValidatePostScriptFontName(info->fontname);
    }

    if ( info->features ) {
	MacFeat *mf;
	struct macsetting *ms;
	for ( mf=info->features; mf!=NULL; mf = mf->next ) {
	    mf->featname = reversemacnames(mf->featname);
	    for ( ms=mf->settings; ms!=NULL; ms=ms->next )
		ms->setname = reversemacnames(ms->setname);
	}
    }
}

static void readttfpreglyph(FILE *ttf,struct ttfinfo *info) {
    readttfhead(ttf,info);
    readttfhhea(ttf,info);
    readttfmaxp(ttf,info);
    readttfcopyrights(ttf,info);
}

#define _On_Curve	1
#define _X_Short	2
#define _Y_Short	4
#define _Repeat		8
#define _X_Same		0x10
#define _Y_Same		0x20

static void FigureControls(SplinePoint *from, SplinePoint *to, BasePoint *cp,
	int is_order2) {
    /* What are the control points for 2 cp bezier which will provide the same*/
    /*  curve as that for the 1 cp bezier specified above */
    real b, c, d;

    if ( is_order2 ) {
	from->nextcp = to->prevcp = *cp;
	if ( cp->x==to->me.x && cp->y==to->me.y ) {
	    /* I would lose track of the proper location of this cp if I left */
	    /* it here (would end up with from->nonextcp, which would mean I'd*/
	    /* use from->me rather than to->me in tottf.c:SSAddPoints. So we  */
	    /* distort it a little */
	    BasePoint off;
	    double len;
	    off.x = from->me.x-to->me.x; off.y = from->me.y-to->me.y;
	    len = sqrt(off.x*off.x+off.y*off.y);
	    if ( len>3 ) {
		/* move the cp slightly toward from, but on the line between the two */
		from->nextcp.x = (to->prevcp.x += rint(off.x/len));
		from->nextcp.y = (to->prevcp.y += rint(off.y/len));
	    }
	}
    } else {
	d = from->me.x;
	c = 2*cp->x - 2*from->me.x;
	b = to->me.x+from->me.x-2*cp->x;
	from->nextcp.x = d+c/3;
	to->prevcp.x = from->nextcp.x + (c+b)/3;

	d = from->me.y;
	c = 2*cp->y - 2*from->me.y;
	b = to->me.y+from->me.y-2*cp->y;
	from->nextcp.y = d+c/3;
	to->prevcp.y = from->nextcp.y + (c+b)/3;
    }

    if ( from->me.x!=from->nextcp.x || from->me.y!=from->nextcp.y )
	from->nonextcp = false;
    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y )
	to->noprevcp = false;
    if ( is_order2 && (to->noprevcp || from->nonextcp)) {
	to->noprevcp = from->nonextcp = true;
	from->nextcp = from->me;
	to->prevcp = to->me;
    }
}

static SplineSet *ttfbuildcontours(int path_cnt,uint16 *endpt, char *flags,
	BasePoint *pts, int is_order2) {
    SplineSet *head=NULL, *last=NULL, *cur;
    int i, path, start, last_off;
    SplinePoint *sp;

    for ( path=i=0; path<path_cnt; ++path ) {
	if ( endpt[path]<i )	/* Sigh. Yes there are fonts with bad endpt info */
    continue;
	cur = chunkalloc(sizeof(SplineSet));
	if ( head==NULL )
	    head = cur;
	else
	    last->next = cur;
	last = cur;
	last_off = false;
	start = i;
	sp = NULL;
	while ( i<=endpt[path] ) {
	    if ( flags[i]&_On_Curve ) {
		sp = chunkalloc(sizeof(SplinePoint));
		sp->me = sp->nextcp = sp->prevcp = pts[i];
		sp->nonextcp = sp->noprevcp = true;
		sp->ttfindex = i;
		sp->nextcpindex = 0xffff;
		if ( last_off && cur->last!=NULL )
		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
		last_off = false;
	    } else if ( last_off ) {
		/* two off curve points get a third on curve point created */
		/* half-way between them. Now isn't that special */
		sp = chunkalloc(sizeof(SplinePoint));
		sp->me.x = (pts[i].x+pts[i-1].x)/2;
		sp->me.y = (pts[i].y+pts[i-1].y)/2;
		sp->nextcp = sp->prevcp = sp->me;
		sp->nonextcp = true;
		sp->ttfindex = 0xffff;
		sp->nextcpindex = i;
		if ( last_off && cur->last!=NULL )
		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
		/* last_off continues to be true */
	    } else {
		if ( cur->first!=NULL )
		    cur->last->nextcpindex = i;
		last_off = true;
		sp = NULL;
	    }
	    if ( sp!=NULL ) {
		if ( cur->first==NULL )
		    cur->first = sp;
		else
		    SplineMake(cur->last,sp,is_order2);
		cur->last = sp;
	    }
	    ++i;
	}
	if ( start==i-1 ) {
	    /* MS chinese fonts have contours consisting of a single off curve*/
	    /*  point. What on earth do they think that means? */
	    /* Oh. I see. It's used to possition marks and such */
	    if ( cur->first==NULL ) {
		sp = chunkalloc(sizeof(SplinePoint));
		sp->me.x = pts[start].x;
		sp->me.y = pts[start].y;
		sp->nextcp = sp->prevcp = sp->me;
		sp->nonextcp = sp->noprevcp = true;
		sp->ttfindex = i-1;
		sp->nextcpindex = 0xffff;
		cur->first = cur->last = sp;
	    }
	} else if ( !(flags[start]&_On_Curve) && !(flags[i-1]&_On_Curve) ) {
	    sp = chunkalloc(sizeof(SplinePoint));
	    sp->me.x = (pts[start].x+pts[i-1].x)/2;
	    sp->me.y = (pts[start].y+pts[i-1].y)/2;
	    sp->nextcp = sp->prevcp = sp->me;
	    sp->nonextcp = true;
	    sp->ttfindex = 0xffff;
	    sp->nextcpindex = start;
	    FigureControls(cur->last,sp,&pts[i-1],is_order2);
	    SplineMake(cur->last,sp,is_order2);
	    cur->last = sp;
	    FigureControls(sp,cur->first,&pts[start],is_order2);
	} else if ( !(flags[i-1]&_On_Curve)) {
	    FigureControls(cur->last,cur->first,&pts[i-1],is_order2);
	    cur->last->nextcpindex = i-1;
	} else if ( !(flags[start]&_On_Curve) ) {
	    FigureControls(cur->last,cur->first,&pts[start],is_order2);
	    sp->nextcpindex = start;
	}
	if ( cur->last!=cur->first ) {
	    SplineMake(cur->last,cur->first,is_order2);
	    cur->last = cur->first;
	}
	for ( sp=cur->first; ; ) {
	    if ( sp->ttfindex!=0xffff && SPInterpolate(sp) )
		sp->dontinterpolate = true;
	    if ( sp->next==NULL )
	break;
	    sp=sp->next->to;
	    if ( sp==cur->first )
	break;
	}
    }
return( head );
}

static void readttfsimpleglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int path_cnt) {
    uint16 *endpt = galloc((path_cnt+1)*sizeof(uint16));
    uint8 *instructions;
    char *flags;
    BasePoint *pts;
    int i, j, tot, len;
    int last_pos;

    for ( i=0; i<path_cnt; ++i ) {
	endpt[i] = getushort(ttf);
	if ( i!=0 && endpt[i]<endpt[i-1] ) {
	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
		    sc->orig_pos );
return;
	}
    }
    if ( path_cnt==0 ) {
	tot = 0;
	pts = galloc(sizeof(BasePoint));
    } else {
	tot = endpt[path_cnt-1]+1;
	pts = galloc(tot*sizeof(BasePoint));
    }

    len = getushort(ttf);
    instructions = galloc(len);
    for ( i=0; i<len; ++i )
	instructions[i] = getc(ttf);

    flags = galloc(tot);
    for ( i=0; i<tot; ++i ) {
	flags[i] = getc(ttf);
	if ( flags[i]&_Repeat ) {
	    int cnt = getc(ttf);
	    if ( i+cnt>=tot ) {
		IError("Flag count is wrong (or total is): %d %d", i+cnt, tot );
		cnt = tot-i-1;
	    }
	    for ( j=0; j<cnt; ++j )
		flags[i+j+1] = flags[i];
	    i += cnt;
	}
	if ( feof(ttf))
    break;
    }
    if ( i!=tot )
	IError("Flag count is wrong (or total is): %d %d in glyph %d", i, tot, sc->orig_pos );

    last_pos = 0;
    for ( i=0; i<tot; ++i ) {
	if ( flags[i]&_X_Short ) {
	    int off = getc(ttf);
	    if ( !(flags[i]&_X_Same ) )
		off = -off;
	    pts[i].x = last_pos + off;
	} else if ( flags[i]&_X_Same )
	    pts[i].x = last_pos;
	else
	    pts[i].x = last_pos + (short) getushort(ttf);
	last_pos = pts[i].x;
    }

    last_pos = 0;
    for ( i=0; i<tot; ++i ) {
	if ( flags[i]&_Y_Short ) {
	    int off = getc(ttf);
	    if ( !(flags[i]&_Y_Same ) )
		off = -off;
	    pts[i].y = last_pos + off;
	} else if ( flags[i]&_Y_Same )
	    pts[i].y = last_pos;
	else
	    pts[i].y = last_pos + (short) getushort(ttf);
	last_pos = pts[i].y;
    }

    sc->layers[ly_fore].splines = ttfbuildcontours(path_cnt,endpt,flags,pts,info->to_order2);
    if ( info->to_order2 && len!=0 ) {
	sc->ttf_instrs_len = len;
	sc->ttf_instrs = instructions;
    } else
	free(instructions);
    SCCatagorizePoints(sc);
    free(endpt);
    free(flags);
    free(pts);
    if ( feof(ttf))
	LogError( _("Reached end of file when reading simple glyph\n") );
}

static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
    RefChar *head=NULL, *last=NULL, *cur;
    int flags=0, arg1, arg2;

    if ( ftell(ttf)>=end ) {
	LogError( _("Empty composite %d\n"), sc->orig_pos );
return;
    }

    do {
	if ( ftell(ttf)>=end ) {
	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
    break;
	}
	cur = RefCharCreate();
	flags = getushort(ttf);
	cur->orig_pos = getushort(ttf);
	if ( feof(ttf) || cur->orig_pos>=info->glyph_cnt ) {
	    LogError(_("Reference to glyph %d out of bounds when parsing 'glyf' table.\n"), cur->orig_pos );
	    cur->orig_pos = 0;
	}
	if ( info->inuse!=NULL )
	    info->inuse[cur->orig_pos] = true;
	if ( flags&_ARGS_ARE_WORDS ) {
	    arg1 = (short) getushort(ttf);
	    arg2 = (short) getushort(ttf);
	} else {
	    arg1 = (signed char) getc(ttf);
	    arg2 = (signed char) getc(ttf);
	}
	cur->use_my_metrics =		 (flags & _USE_MY_METRICS) ? 1 : 0;
	cur->round_translation_to_grid = (flags & _ROUND) ? 1 : 0;
	if ( flags & _ARGS_ARE_XY ) {
	    /* There is some very strange stuff (half-)documented on the apple*/
	    /*  site about how these should be interpretted when there are */
	    /*  scale factors, or rotations */
	    /* It isn't well enough described to be comprehensible */
	    /*  http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html */
	    /* Microsoft says nothing about this */
	    /* Adobe implies this is a difference between MS and Apple */
	    /*  MS doesn't do this, Apple does (GRRRGH!!!!) */
	    /* Adobe says that setting bit 12 means that this will not happen */
	    /* Adobe says that setting bit 11 means that this will happen */
	    /*  So if either bit is set we know when this happens, if neither */
	    /*  we guess... But I still don't know how to interpret the */
	    /*  apple mode under rotation... */
	    /* I notice that FreeType does nothing about rotation nor does it */
	    /*  interpret bits 11&12 */
	    /* Ah. It turns out that even Apple does not do what Apple's docs */
	    /*  claim it does. I think I've worked it out (see below), but... */
	    /*  Bleah! */
	    cur->transform[4] = arg1;
	    cur->transform[5] = arg2;
	} else {
	    /* Somehow we can get offsets by looking at the points in the */
	    /*  points so far generated and comparing them to the points in */
	    /*  the current componant */
	    /* How exactly is not described on any of the Apple, MS, Adobe */
	    /* freetype looks up arg1 in the set of points we've got so far */
	    /*  looks up arg2 in the new component (before renumbering) */
	    /*  offset.x = arg1.x - arg2.x; offset.y = arg1.y - arg2.y; */
	    /* This fixup needs to be done later though (after all glyphs */
	    /*  have been loaded) */
	    cur->match_pt_base = arg1;
	    cur->match_pt_ref = arg2;
	    cur->point_match = true;
	}
	cur->transform[0] = cur->transform[3] = 1.0;
	if ( flags & _SCALE )
	    cur->transform[0] = cur->transform[3] = get2dot14(ttf);
	else if ( flags & _XY_SCALE ) {
	    cur->transform[0] = get2dot14(ttf);
	    cur->transform[3] = get2dot14(ttf);
	} else if ( flags & _MATRIX ) {
	    cur->transform[0] = get2dot14(ttf);
	    cur->transform[1] = get2dot14(ttf);
	    cur->transform[2] = get2dot14(ttf);
	    cur->transform[3] = get2dot14(ttf);
	}
	if ( flags & _ARGS_ARE_XY ) {	/* Only muck with these guys if they are real offsets and not point matching */
#ifdef __Mac
	/* On mac assume scaled offsets unless told unscaled explicitly */
	if ( !(flags&_UNSCALED_OFFSETS) &&
#else
	/* everywhere else assume unscaled offsets unless told scaled explicitly */
	if ( (flags & _SCALED_OFFSETS) &&
#endif
		(flags & _ARGS_ARE_XY) && (flags&(_SCALE|_XY_SCALE|_MATRIX))) {
	    /*static int asked = 0;*/
	    /* This is not what Apple documents on their website. But it is */
	    /*  what appears to match the behavior of their rasterizer */
	    /* Apple has changed their documentation (without updating their */
	    /*  changelog), but I believe they are still incorrect */
	    cur->transform[4] *= sqrt(cur->transform[0]*cur->transform[0]+
		    cur->transform[1]*cur->transform[1]);
	    cur->transform[5] *= sqrt(cur->transform[2]*cur->transform[2]+
		    cur->transform[3]*cur->transform[3]);
#if 0
	    /* Apple's Chicago is an example */
	    if ( info->fontname!=NULL && strcmp(info->fontname,"CompositeMac")!=0 && !asked ) {
		/* Not interested in the test font I generated myself */
		asked = true;
		fprintf( stderr, "Neat! You've got a font that actually uses Apple's scaled composite offsets.\n" );
		fprintf( stderr, "  I've never seen one, could you send me a copy of %s?\n", info->fontname );
		fprintf( stderr, "  Thanks.  gww@silcom.com\n" );
	    }
#endif
	}
	}
	if ( cur->orig_pos>=info->glyph_cnt ) {
	    LogError(_("Glyph %d attempts to reference glyph %d which is outside the font\n"), sc->orig_pos, cur->orig_pos );
	    chunkfree(cur,sizeof(*cur));
	} else {
	    if ( head==NULL )
		head = cur;
	    else
		last->next = cur;
	    last = cur;
	}
	if ( feof(ttf)) {
	    LogError(_("Reached end of file when reading composit glyph\n") );
    break;
	}
    } while ( flags&_MORE );
    if ( (flags & _INSTR ) && info->to_order2 && ftell(ttf)<end ) {
	sc->ttf_instrs_len = getushort(ttf);
	if ( sc->ttf_instrs_len > 0 && ftell(ttf)+sc->ttf_instrs_len<=end ) {
	    uint8 *instructions = galloc(sc->ttf_instrs_len);
	    int i;
	    for ( i=0; i<sc->ttf_instrs_len; ++i )
		instructions[i] = getc(ttf);
	    sc->ttf_instrs = instructions;
	} else
	    sc->ttf_instrs_len = 0;
    }
    /* I'm ignoring many things that I don't understand here */
    /* what happens if ARGS AREN'T XY */
    /* ROUND means that offsets should rounded to the grid before being added */
    /*  (it's irrelevant for my purposes) */
    sc->layers[ly_fore].refs = head;
}

static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,int start, int end,int gid) {
    int path_cnt;
    SplineChar *sc = SplineCharCreate();

    sc->unicodeenc = -1;
    sc->vwidth = info->emsize;
    /* sc->manualhints = 1; */ /* But only when I know how to read them in!!!! */

    if ( end>info->glyph_length ) {
	if ( !info->complainedbeyondglyfend )
	    LogError(_("Bad glyph (%d), its definition extends beyond the end of the glyf table\n"), gid );
	info->complainedbeyondglyfend = true;
	SplineCharFree(sc);
return( NULL );
    }
    if ( start==end ) {
	/* This isn't mentioned, but we seem to get some glyphs with no size,*/
	/*  not even a path cnt. They appear to be empty glyphs */
return( sc );
    }
    fseek(ttf,info->glyph_start+start,SEEK_SET);
    path_cnt = (short) getushort(ttf);
    /* xmin = */ getushort(ttf);
    /* ymin = */ getushort(ttf);
    /* xmax = */ getushort(ttf);
    /* ymax = */ sc->lsidebearing = getushort(ttf);
    sc->orig_pos = gid;
    if ( path_cnt>=0 )
	readttfsimpleglyph(ttf,info,sc,path_cnt);
    else
	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
    if ( start>end )
	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
    else if ( ftell(ttf)>info->glyph_start+end )
	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
return( sc );
}

static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse);

static void readttfglyphs(FILE *ttf,struct ttfinfo *info) {
    int i, anyread;
    uint32 *goffsets = galloc((info->glyph_cnt+1)*sizeof(uint32));

    /* First we read all the locations. This might not be needed, they may */
    /*  just follow one another, but nothing I've noticed says that so let's */
    /*  be careful */
    fseek(ttf,info->glyphlocations_start,SEEK_SET);
    if ( info->index_to_loc_is_long ) {
	for ( i=0; i<=info->glyph_cnt ; ++i )
	    goffsets[i] = getlong(ttf);
    } else {
	for ( i=0; i<=info->glyph_cnt ; ++i )
	    goffsets[i] = 2*getushort(ttf);
    }

    info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *));
    if ( !info->is_ttc ) {
	/* read all the glyphs */
	for ( i=0; i<info->glyph_cnt ; ++i ) {
	    info->chars[i] = readttfglyph(ttf,info,goffsets[i],goffsets[i+1],i);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	    gwwv_progress_next();
#endif
	}
    } else {
	/* only read the glyphs we actually use in this font */
	/* this is complicated by references (and substitutions), */
	/* we can't just rely on the encoding to tell us what is used */
	info->inuse = gcalloc(info->glyph_cnt,sizeof(char));
	readttfencodings(ttf,info,true);
	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
	    readttfgsubUsed(ttf,info);
	    /* I don't bother to read the morx table because mac doesn't */
	    /*  support ttc files */
	anyread = true;
	while ( anyread ) {
	    anyread = false;
	    for ( i=0; i<info->glyph_cnt ; ++i ) {
		if ( info->inuse[i] && info->chars[i]==NULL ) {
		    info->chars[i] = readttfglyph(ttf,info,goffsets[i],goffsets[i+1],i);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
		    gwwv_progress_next();
#endif
		    anyread = info->chars[i]!=NULL;
		}
	    }
	}
	free(info->inuse); info->inuse = NULL;
    }
    free(goffsets);
    for ( i=0; i<info->glyph_cnt ; ++i )
	if ( info->chars[i]!=NULL )
	    info->chars[i]->orig_pos = i;
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_next_stage();
#endif
}

/* Standard names for cff */
const char *cffnames[] = {
 ".notdef",
 "space",
 "exclam",
 "quotedbl",
 "numbersign",
 "dollar",
 "percent",
 "ampersand",
 "quoteright",
 "parenleft",
 "parenright",
 "asterisk",
 "plus",
 "comma",
 "hyphen",
 "period",
 "slash",
 "zero",
 "one",
 "two",
 "three",
 "four",
 "five",
 "six",
 "seven",
 "eight",
 "nine",
 "colon",
 "semicolon",
 "less",
 "equal",
 "greater",
 "question",
 "at",
 "A",
 "B",
 "C",
 "D",
 "E",
 "F",
 "G",
 "H",
 "I",
 "J",
 "K",
 "L",
 "M",
 "N",
 "O",
 "P",
 "Q",
 "R",
 "S",
 "T",
 "U",
 "V",
 "W",
 "X",
 "Y",
 "Z",
 "bracketleft",
 "backslash",
 "bracketright",
 "asciicircum",
 "underscore",
 "quoteleft",
 "a",
 "b",
 "c",
 "d",
 "e",
 "f",
 "g",
 "h",
 "i",
 "j",
 "k",
 "l",
 "m",
 "n",
 "o",
 "p",
 "q",
 "r",
 "s",
 "t",
 "u",
 "v",
 "w",
 "x",
 "y",
 "z",
 "braceleft",
 "bar",
 "braceright",
 "asciitilde",
 "exclamdown",
 "cent",
 "sterling",
 "fraction",
 "yen",
 "florin",
 "section",
 "currency",
 "quotesingle",
 "quotedblleft",
 "guillemotleft",
 "guilsinglleft",
 "guilsinglright",
 "fi",
 "fl",
 "endash",
 "dagger",
 "daggerdbl",
 "periodcentered",
 "paragraph",
 "bullet",
 "quotesinglbase",
 "quotedblbase",
 "quotedblright",
 "guillemotright",
 "ellipsis",
 "perthousand",
 "questiondown",
 "grave",
 "acute",
 "circumflex",
 "tilde",
 "macron",
 "breve",
 "dotaccent",
 "dieresis",
 "ring",
 "cedilla",
 "hungarumlaut",
 "ogonek",
 "caron",
 "emdash",
 "AE",
 "ordfeminine",
 "Lslash",
 "Oslash",
 "OE",
 "ordmasculine",
 "ae",
 "dotlessi",
 "lslash",
 "oslash",
 "oe",
 "germandbls",
 "onesuperior",
 "logicalnot",
 "mu",
 "trademark",
 "Eth",
 "onehalf",
 "plusminus",
 "Thorn",
 "onequarter",
 "divide",
 "brokenbar",
 "degree",
 "thorn",
 "threequarters",
 "twosuperior",
 "registered",
 "minus",
 "eth",
 "multiply",
 "threesuperior",
 "copyright",
 "Aacute",
 "Acircumflex",
 "Adieresis",
 "Agrave",
 "Aring",
 "Atilde",
 "Ccedilla",
 "Eacute",
 "Ecircumflex",
 "Edieresis",
 "Egrave",
 "Iacute",
 "Icircumflex",
 "Idieresis",
 "Igrave",
 "Ntilde",
 "Oacute",
 "Ocircumflex",
 "Odieresis",
 "Ograve",
 "Otilde",
 "Scaron",
 "Uacute",
 "Ucircumflex",
 "Udieresis",
 "Ugrave",
 "Yacute",
 "Ydieresis",
 "Zcaron",
 "aacute",
 "acircumflex",
 "adieresis",
 "agrave",
 "aring",
 "atilde",
 "ccedilla",
 "eacute",
 "ecircumflex",
 "edieresis",
 "egrave",
 "iacute",
 "icircumflex",
 "idieresis",
 "igrave",
 "ntilde",
 "oacute",
 "ocircumflex",
 "odieresis",
 "ograve",
 "otilde",
 "scaron",
 "uacute",
 "ucircumflex",
 "udieresis",
 "ugrave",
 "yacute",
 "ydieresis",
 "zcaron",
 "exclamsmall",
 "Hungarumlautsmall",
 "dollaroldstyle",
 "dollarsuperior",
 "ampersandsmall",
 "Acutesmall",
 "parenleftsuperior",
 "parenrightsuperior",
 "twodotenleader",
 "onedotenleader",
 "zerooldstyle",
 "oneoldstyle",
 "twooldstyle",
 "threeoldstyle",
 "fouroldstyle",
 "fiveoldstyle",
 "sixoldstyle",
 "sevenoldstyle",
 "eightoldstyle",
 "nineoldstyle",
 "commasuperior",
 "threequartersemdash",
 "periodsuperior",
 "questionsmall",
 "asuperior",
 "bsuperior",
 "centsuperior",
 "dsuperior",
 "esuperior",
 "isuperior",
 "lsuperior",
 "msuperior",
 "nsuperior",
 "osuperior",
 "rsuperior",
 "ssuperior",
 "tsuperior",
 "ff",
 "ffi",
 "ffl",
 "parenleftinferior",
 "parenrightinferior",
 "Circumflexsmall",
 "hyphensuperior",
 "Gravesmall",
 "Asmall",
 "Bsmall",
 "Csmall",
 "Dsmall",
 "Esmall",
 "Fsmall",
 "Gsmall",
 "Hsmall",
 "Ismall",
 "Jsmall",
 "Ksmall",
 "Lsmall",
 "Msmall",
 "Nsmall",
 "Osmall",
 "Psmall",
 "Qsmall",
 "Rsmall",
 "Ssmall",
 "Tsmall",
 "Usmall",
 "Vsmall",
 "Wsmall",
 "Xsmall",
 "Ysmall",
 "Zsmall",
 "colonmonetary",
 "onefitted",
 "rupiah",
 "Tildesmall",
 "exclamdownsmall",
 "centoldstyle",
 "Lslashsmall",
 "Scaronsmall",
 "Zcaronsmall",
 "Dieresissmall",
 "Brevesmall",
 "Caronsmall",
 "Dotaccentsmall",
 "Macronsmall",
 "figuredash",
 "hypheninferior",
 "Ogoneksmall",
 "Ringsmall",
 "Cedillasmall",
 "questiondownsmall",
 "oneeighth",
 "threeeighths",
 "fiveeighths",
 "seveneighths",
 "onethird",
 "twothirds",
 "zerosuperior",
 "foursuperior",
 "fivesuperior",
 "sixsuperior",
 "sevensuperior",
 "eightsuperior",
 "ninesuperior",
 "zeroinferior",
 "oneinferior",
 "twoinferior",
 "threeinferior",
 "fourinferior",
 "fiveinferior",
 "sixinferior",
 "seveninferior",
 "eightinferior",
 "nineinferior",
 "centinferior",
 "dollarinferior",
 "periodinferior",
 "commainferior",
 "Agravesmall",
 "Aacutesmall",
 "Acircumflexsmall",
 "Atildesmall",
 "Adieresissmall",
 "Aringsmall",
 "AEsmall",
 "Ccedillasmall",
 "Egravesmall",
 "Eacutesmall",
 "Ecircumflexsmall",
 "Edieresissmall",
 "Igravesmall",
 "Iacutesmall",
 "Icircumflexsmall",
 "Idieresissmall",
 "Ethsmall",
 "Ntildesmall",
 "Ogravesmall",
 "Oacutesmall",
 "Ocircumflexsmall",
 "Otildesmall",
 "Odieresissmall",
 "OEsmall",
 "Oslashsmall",
 "Ugravesmall",
 "Uacutesmall",
 "Ucircumflexsmall",
 "Udieresissmall",
 "Yacutesmall",
 "Thornsmall",
 "Ydieresissmall",
 "001.000",
 "001.001",
 "001.002",
 "001.003",
 "Black",
 "Bold",
 "Book",
 "Light",
 "Medium",
 "Regular",
 "Roman",
 "Semibold",
 NULL
};
const int nStdStrings = sizeof(cffnames)/sizeof(cffnames[0])-1;

static char **readcfffontnames(FILE *ttf,int *cnt) {
    uint16 count = getushort(ttf);
    int offsize;
    uint32 *offsets;
    char **names;
    int i,j;

    if ( cnt!=NULL ) *cnt = count;

    if ( count==0 )
return( NULL );
    offsets = galloc((count+1)*sizeof(uint32));
    offsize = getc(ttf);
    for ( i=0; i<=count; ++i )
	offsets[i] = getoffset(ttf,offsize);
    names = galloc((count+1)*sizeof(char *));
    for ( i=0; i<count; ++i ) {
	if ( offsets[i+1]<offsets[i] ) {
/* GT: The CFF font type contains a thing called a name INDEX, and that INDEX */
/* GT: is bad. It is an index of many of the names used in the CFF font. */
/* GT: We hope the user will never see this. */
	    LogError( _("Bad CFF name INDEX\n") );
	    while ( i<count ) {
		names[i] = copy("");
		++i;
	    }
	    --i;
	} else {
	    names[i] = galloc(offsets[i+1]-offsets[i]+1);
	    for ( j=0; j<offsets[i+1]-offsets[i]; ++j )
		names[i][j] = getc(ttf);
	    names[i][j] = '\0';
	}
    }
    names[i] = NULL;
    free(offsets);
return( names );
}

static char *addnibble(char *pt, int nib) {
    if ( nib<=9 )
	*pt++ = nib+'0';
    else if ( nib==10 )
	*pt++ = '.';
    else if ( nib==11 )
	*pt++ = 'E';
    else if ( nib==12 ) {
	*pt++ = 'E';
	*pt++ = '-';
    } else if ( nib==14 )
	*pt++ = '-';
    else if ( nib==15 )
	*pt++ = '\0';
return( pt );
}

static int readcffthing(FILE *ttf,int *_ival,real *dval,int *operand) {
    char buffer[50], *pt;
    int ch, ival;

    ch = getc(ttf);
    if ( ch==12 ) {
	*operand = (12<<8) | getc(ttf);
return( 3 );
    } else if ( ch<=21 ) {
	*operand = ch;
return( 3 );
    } else if ( ch==30 ) {
	/* fixed format doesn't exist in dict data but does in type2 strings */
	pt = buffer;
	do {
	    ch = getc(ttf);
	    if ( pt<buffer+44 || (ch&0xf)==0xf || (ch&0xf0)==0xf0 ) {
		pt = addnibble(pt,ch>>4);
		pt = addnibble(pt,ch&0xf);
	    }
	} while ( pt[-1]!='\0' );
	*dval = strtod(buffer,NULL);
return( 2 );
    } else if ( ch>=32 && ch<=246 ) {
	*_ival = ch-139;
return( 1 );
    } else if ( ch>=247 && ch<=250 ) {
	*_ival = ((ch-247)<<8) + getc(ttf)+108;
return( 1 );
    } else if ( ch>=251 && ch<=254 ) {
	*_ival = -((ch-251)<<8) - getc(ttf)-108;
return( 1 );
    } else if ( ch==28 ) {
	ival = getc(ttf)<<8;
	*_ival = (short) (ival | getc(ttf));
return( 1 );
    } else if ( ch==29 ) {
	/* 4 byte integers exist in dict data but not in type2 strings */
	ival = getc(ttf)<<24;
	ival = ival | getc(ttf)<<16;
	ival = ival | getc(ttf)<<8;
	*_ival = (int) (ival | getc(ttf));
return( 1 );
    }
    LogError(_("Unexpected value in dictionary %d\n"), ch );
    *_ival = 0;
return( 0 );
}

static void skipcfft2thing(FILE *ttf) {
    /* The old CFF spec allows little type2 programs to live in the CFF dict */
    /*  indices. These are designed to allow interpolation of values for mm */
    /*  fonts. */
    /* The Type2 program is terminated by an "endchar" operator */
    /* I don't support this, but I shall try to skip over them properly */
    /* There's no discussion about how values move from the t2 stack to the */
    /*  cff stack, as there are no examples of this, it's hard to guess */
    int ch;

/* GT: DICT is a magic term inside CFF fonts, as is INDEX, and I guess CFF and type2 */
    LogError( _("FontForge does not support type2 programs embedded in CFF DICT INDICES.\n") );
    forever {
	ch = getc(ttf);
	if ( ch>=247 && ch<=254 )
	    getc(ttf);		/* Two byte number */
	else if ( ch==255 ) {
	    getc(ttf); getc(ttf); getc(ttf); getc(ttf);
	    /* 16.16 number */
	} else if ( ch==28 ) {
	    getc(ttf);
	    getc(ttf);
	} else if ( ch==12 ) {
	    getc(ttf);		/* Two byte operator */
	} else if ( ch==14 ) {
return;
	}
    }
}

struct topdicts {
    int32 cff_start;

    char *fontname;	/* From Name Index */

    int version;	/* SID */
    int notice;		/* SID */
    int copyright;	/* SID */
    int fullname;	/* SID */
    int familyname;	/* SID */
    int weight;		/* SID */
    int isfixedpitch;
    real italicangle;
    real underlinepos;
    real underlinewidth;
    int painttype;
    int charstringtype;
    real fontmatrix[6];
    int uniqueid;
    real fontbb[4];
    real strokewidth;
    int xuid[20];
    int charsetoff;	/* from start of file */
    int encodingoff;	/* from start of file */
    int charstringsoff;	/* from start of file */
    int private_size;
    int private_offset;	/* from start of file */
    int synthetic_base;	/* font index */
    int postscript_code;	/* SID */
 /* synthetic fonts only (whatever they are) */
    int basefontname;		/* SID */
 /* Multiple master/synthetic fonts */
    real basefontblend[16];	/* delta */	/* No description of why this is relevant for mm fonts */
 /* Multiple master fonts only */
    int blendaxistypes[17];	/* SID */
    int nMasters;
    int nAxes;
    real weightvector[17];
    int lenBuildCharArray;	/* No description of what this means */
    int NormalizeDesignVector;	/* SID */	/* No description of what this does */
    int ConvertDesignVector;	/* SID */	/* No description of what this does */
 /* CID fonts only */
    int ros_registry;		/* SID */
    int ros_ordering;		/* SID */
    int ros_supplement;
    real cidfontversion;
    int cidfontrevision;
    int cidfonttype;
    int cidcount;
    int uidbase;
    int fdarrayoff;	/* from start of file */
    int fdselectoff;	/* from start of file */
    int sid_fontname;	/* SID */
/* Private stuff */
    real bluevalues[14];
    real otherblues[10];
    real familyblues[14];
    real familyotherblues[10];
    real bluescale;
    real blueshift;
    real bluefuzz;
    int stdhw;
    int stdvw;
    real stemsnaph[10];
    real stemsnapv[10];
    int forcebold;
    real forceboldthreshold;
    int languagegroup;
    real expansionfactor;
    int initialRandomSeed;
    int subrsoff;	/* from start of this private table */
    int defaultwidthx;
    int nominalwidthx;

    struct pschars glyphs;
    struct pschars local_subrs;
    uint16 *charset;
};

static void TopDictFree(struct topdicts *dict) {
    int i;

    free(dict->charset);
    for ( i=0; i<dict->glyphs.cnt; ++i )
	free(dict->glyphs.values[i]);
    free(dict->glyphs.values);
    free(dict->glyphs.lens);
    for ( i=0; i<dict->local_subrs.cnt; ++i )
	free(dict->local_subrs.values[i]);
    free(dict->local_subrs.values);
    free(dict->local_subrs.lens);
    free(dict);
}

static void readcffsubrs(FILE *ttf, struct pschars *subs) {
    uint16 count = getushort(ttf);
    int offsize;
    uint32 *offsets;
    int i,j, base;
    int err = false;

    memset(subs,'\0',sizeof(struct pschars));
    if ( count==0 )
return;
    subs->cnt = count;
    subs->lens = galloc(count*sizeof(int));
    subs->values = galloc(count*sizeof(uint8 *));
    offsets = galloc((count+1)*sizeof(uint32));
    offsize = getc(ttf);
    for ( i=0; i<=count; ++i )
	offsets[i] = getoffset(ttf,offsize);
    base = ftell(ttf)-1;
    for ( i=0; i<count; ++i ) {
	if ( offsets[i+1]>offsets[i] && offsets[i+1]-offsets[i]<0x10000 ) {
	    subs->lens[i] = offsets[i+1]-offsets[i];
	    subs->values[i] = galloc(offsets[i+1]-offsets[i]+1);
	    for ( j=0; j<offsets[i+1]-offsets[i]; ++j )
		subs->values[i][j] = getc(ttf);
	    subs->values[i][j] = '\0';
	} else {
	    if ( !err )
		LogError( _("Bad subroutine INDEX in cff font.\n" ));
	    err = true;
	    subs->lens[i] = 1;
	    subs->values[i] = galloc(2);
	    subs->values[i][0] = 11;		/* return */
	    subs->values[i][1] = '\0';
	    fseek(ttf,base+offsets[i+1],SEEK_SET);
	}
    }
    free(offsets);
}

static struct topdicts *readcfftopdict(FILE *ttf, char *fontname, int len) {
    struct topdicts *td = gcalloc(1,sizeof(struct topdicts));
    long base = ftell(ttf);
    int ival, oval, sp, ret, i;
    real stack[50];

    if ( fontname!=NULL )
	ValidatePostScriptFontName(fontname);

    td->fontname = fontname;
    td->underlinepos = -100;
    td->underlinewidth = 50;
    td->charstringtype = 2;
    td->fontmatrix[0] = td->fontmatrix[3] = .001;

    td->notice = td->copyright = td->fullname = td->familyname = td->weight = td->version = -1;
    td->postscript_code = td->basefontname = -1;
    td->synthetic_base = td->ros_registry = -1;
    td->fdarrayoff = td->fdselectoff = td->sid_fontname = -1;
    td->blendaxistypes[0] = -1;

    /* Multiple master fonts can have Type2 operators here, particularly */
    /*  blend operators. We're ignoring that */
    while ( ftell(ttf)<base+len ) {
	sp = 0;
	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval))!=3 && ftell(ttf)<base+len ) {
	    if ( ret==1 )
		stack[sp]=ival;
	    if ( ret!=0 && sp<45 )
		++sp;
	}
	if ( ret==3 && oval==31 /* "T2" operator, can have 0 arguments */ ) {
	    skipcfft2thing(ttf);
	} else if ( sp==0 )
	    LogError( _("No argument to operator\n") );
	else if ( ret==3 ) switch( oval ) {
	  case 0:
	    td->version = stack[sp-1];
	  break;
	  case 1:
	    td->notice = stack[sp-1];
	  break;
	  case (12<<8)+0:
	    td->copyright = stack[sp-1];
	  break;
	  case 2:
	    td->fullname = stack[sp-1];
	  break;
	  case 3:
	    td->familyname = stack[sp-1];
	  break;
	  case 4:
	    td->weight = stack[sp-1];
	  break;
	  case (12<<8)+1:
	    td->isfixedpitch = stack[sp-1];
	  break;
	  case (12<<8)+2:
	    td->italicangle = stack[sp-1];
	  break;
	  case (12<<8)+3:
	    td->underlinepos = stack[sp-1];
	  break;
	  case (12<<8)+4:
	    td->underlinewidth = stack[sp-1];
	  break;
	  case (12<<8)+5:
	    td->painttype = stack[sp-1];
	  break;
	  case (12<<8)+6:
	    td->charstringtype = stack[sp-1];
	  break;
	  case (12<<8)+7:
	    memcpy(td->fontmatrix,stack,(sp>=6?6:sp)*sizeof(real));
	  break;
	  case 13:
	    td->uniqueid = stack[sp-1];
	  break;
	  case 5:
	    memcpy(td->fontbb,stack,(sp>=4?4:sp)*sizeof(real));
	  break;
	  case (12<<8)+8:
	    td->strokewidth = stack[sp-1];
	  break;
	  case 14:
	    for ( i=0; i<sp && i<20; ++i )
		td->xuid[i] = stack[i];
	  break;
	  case 15:
	    td->charsetoff = stack[sp-1];
	  break;
	  case 16:
	    td->encodingoff = stack[sp-1];
	  break;
	  case 17:
	    td->charstringsoff = stack[sp-1];
	  break;
	  case 18:
	    td->private_size = stack[0];
	    td->private_offset = stack[1];
	  break;
	  case (12<<8)+20:
	    LogError( _("FontForge does not support synthetic fonts\n") );
	    td->synthetic_base = stack[sp-1];
	  break;
	  case (12<<8)+21:
	    td->postscript_code = stack[sp-1];
	  break;
	  case (12<<8)+22:
	    td->basefontname = stack[sp-1];
	  break;
	  case (12<<8)+23:
	    for ( i=0; i<sp && i<16; ++i )
		td->basefontblend[i] = stack[i];
	  break;
	  case (12<<8)+24:
	    LogError( _("FontForge does not support type2 multiple master fonts\n") );
	    td->nMasters = stack[0];
	    td->nAxes = sp-4;
	    memcpy(td->weightvector,stack+1,(sp-4)*sizeof(real));
	    td->lenBuildCharArray = stack[sp-3];
	    td->NormalizeDesignVector = stack[sp-2];	/* These are type2 charstrings, even in type1 fonts */
	    td->ConvertDesignVector = stack[sp-1];
	  break;
	  case (12<<8)+26:
	    for ( i=0; i<sp && i<16; ++i )
		td->blendaxistypes[i] = stack[i];
	    td->blendaxistypes[i] = -1;
	  break;
	  case (12<<8)+30:
	    td->ros_registry = stack[0];
	    td->ros_ordering = stack[1];
	    td->ros_supplement = stack[2];
	  break;
	  case (12<<8)+31:
	    td->cidfontversion = stack[sp-1];
	  break;
	  case (12<<8)+32:
	    td->cidfontrevision = stack[sp-1];
	  break;
	  case (12<<8)+33:
	    td->cidfonttype = stack[sp-1];
	  break;
	  case (12<<8)+34:
	    td->cidcount = stack[sp-1];
	  break;
	  case (12<<8)+35:
	    td->uidbase = stack[sp-1];
	  break;
	  case (12<<8)+36:
	    td->fdarrayoff = stack[sp-1];
	  break;
	  case (12<<8)+37:
	    td->fdselectoff = stack[sp-1];
	  break;
	  case (12<<8)+38:
	    td->sid_fontname = stack[sp-1];
	  break;
	  case (12<<8)+39:
	    LogError(_("FontForge does not support Chameleon fonts\n"));;
	  break;
	  default:
	    LogError(_("Unknown operator in %s: %x\n"), fontname, oval );
	  break;
	}
    }
return( td );
}

static void readcffprivate(FILE *ttf, struct topdicts *td) {
    int ival, oval, sp, ret, i;
    real stack[50];
    int32 end = td->cff_start+td->private_offset+td->private_size;

    fseek(ttf,td->cff_start+td->private_offset,SEEK_SET);

    td->subrsoff = -1;
    td->expansionfactor = .06;
    td->bluefuzz = 1;
    td->blueshift = 7;
    td->bluescale = .039625;

    while ( ftell(ttf)<end ) {
	sp = 0;
	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval))!=3 && ftell(ttf)<end ) {
	    if ( ret==1 )
		stack[sp]=ival;
	    if ( ret!=0 && sp<45 )
		++sp;
	}
	if ( ret==3 && oval==31 /* "T2" operator, can have 0 arguments */ ) {
	    skipcfft2thing(ttf);
	} else if ( sp==0 && oval!=6 && oval!=7 && oval!=8 && oval!=9 && oval !=(12<<8)+12 && oval !=(12<<8)+13)
	    LogError( _("No argument to operator %d in private dict\n"), oval );
	else if ( ret==3 ) switch( oval ) {
	  case 6:
	    for ( i=0; i<sp && i<14; ++i ) {
		td->bluevalues[i] = stack[i];
		if ( i!=0 )
		    td->bluevalues[i] += td->bluevalues[i-1];
	    }
	  break;
	  case 7:
	    for ( i=0; i<sp && i<10; ++i ) {
		td->otherblues[i] = stack[i];
		if ( i!=0 )
		    td->otherblues[i] += td->otherblues[i-1];
	    }
	  break;
	  case 8:
	    for ( i=0; i<sp && i<14; ++i ) {
		td->familyblues[i] = stack[i];
		if ( i!=0 )
		    td->familyblues[i] += td->familyblues[i-1];
	    }
	  break;
	  case 9:
	    for ( i=0; i<sp && i<10; ++i ) {
		td->familyotherblues[i] = stack[i];
		if ( i!=0 )
		    td->familyotherblues[i] += td->familyotherblues[i-1];
	    }
	  break;
	  case (12<<8)+9:
	    td->bluescale = stack[sp-1];
	  break;
	  case (12<<8)+10:
	    td->blueshift = stack[sp-1];
	  break;
	  case (12<<8)+11:
	    td->bluefuzz = stack[sp-1];
	  break;
	  case 10:
	    td->stdhw = stack[sp-1];
	  break;
	  case 11:
	    td->stdvw = stack[sp-1];
	  break;
	  case (12<<8)+12:
	    for ( i=0; i<sp && i<10; ++i ) {
		td->stemsnaph[i] = stack[i];
		if ( i!=0 )
		    td->stemsnaph[i] += td->stemsnaph[i-1];
	    }
	  break;
	  case (12<<8)+13:
	    for ( i=0; i<sp && i<10; ++i ) {
		td->stemsnapv[i] = stack[i];
		if ( i!=0 )
		    td->stemsnapv[i] += td->stemsnapv[i-1];
	    }
	  break;
	  case (12<<8)+14:
	    td->forcebold = stack[sp-1];
	  break;
	  case (12<<8)+15:
	    td->forceboldthreshold = stack[sp-1];
	  break;
	  case (12<<8)+16:
	    /* lenIV. -1 => unencrypted charstrings */
	    /* obsolete */
	  break;
	  case (12<<8)+17:
	    td->languagegroup = stack[sp-1];
	  break;
	  case (12<<8)+18:
	    td->expansionfactor = stack[sp-1];
	  break;
	  case (12<<8)+19:
	    td->initialRandomSeed = stack[sp-1];
	  break;
	  case 19:
	    td->subrsoff = stack[sp-1];
	  break;
	  case 20:
	    td->defaultwidthx = stack[sp-1];
	  break;
	  case 21:
	    td->nominalwidthx = stack[sp-1];
	  break;
	  default:
	    LogError(_("Unknown operator in %s: %x\n"), td->fontname, oval );
	  break;
	}
    }

    if ( td->subrsoff!=-1 ) {
	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
	readcffsubrs(ttf,&td->local_subrs);
    }
}

static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start) {
    uint16 count = getushort(ttf);
    int offsize;
    uint32 *offsets;
    struct topdicts **dicts;
    int i;

    if ( count==0 )
return( NULL );
    offsets = galloc((count+1)*sizeof(uint32));
    offsize = getc(ttf);
    for ( i=0; i<=count; ++i )
	offsets[i] = getoffset(ttf,offsize);
    dicts = galloc((count+1)*sizeof(struct topdicts *));
    for ( i=0; i<count; ++i ) {
	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
		offsets[i+1]-offsets[i]);
	dicts[i]->cff_start = cff_start;
    }
    dicts[i] = NULL;
    free(offsets);
return( dicts );
}

static const char *getsid(int sid,char **strings,int scnt) {
    if ( sid==-1 )
return( NULL );
    else if ( sid<nStdStrings )
return( cffnames[sid] );
    else if ( sid-nStdStrings>scnt ) {
	LogError( _("Bad sid %d (must be less than %d)\n"), sid, scnt+nStdStrings );
return( NULL );
    } else
return( strings[sid-nStdStrings]);
}

/* I really expect to deal with encodings in ttf cmap, but ocasionally we */
/*  get a bare cff */
static void readcffenc(FILE *ttf,struct topdicts *dict,struct ttfinfo *info,
	char **strings, int scnt) {
    int format, cnt, i, j, pos, first, last, dupenc, sid;
    extern char *AdobeStandardEncoding[], *AdobeExpertEncoding[];
    const char *name;
    EncMap *map;

    if ( info->encoding_start!=0 )		/* Use the cmap instead */
return;
    if ( info->subfontcnt!=0 )
return;						/* Use cids instead */

    for ( i=0; i<info->glyph_cnt; ++i ) {
	if ( info->chars[i]->unicodeenc==-1 )
	    info->chars[i]->unicodeenc = UniFromName(info->chars[i]->name,ui_none,&custom);
    }

    map = EncMapNew(256,256,&custom);
    if ( dict->encodingoff==0 || dict->encodingoff==1 ) {
	/* Standard Encodings */
	char **enc = dict->encodingoff==0 ? AdobeStandardEncoding : AdobeExpertEncoding;
	map->enc = FindOrMakeEncoding( dict->encodingoff==0 ?
		"AdobeStandard" : "Custom" );
	if ( map->enc==NULL )
	    map->enc = &custom;
	for ( i=0; i<info->glyph_cnt; ++i ) {
	    for ( pos=0; pos<256; ++pos )
		if ( strcmp(info->chars[i]->name,enc[pos])==0 )
	    break;
	    if ( pos<256 )
		map->map[pos] = i;
	}
    } else {
	fseek(ttf,dict->cff_start+dict->encodingoff,SEEK_SET);
	format = getc(ttf);
	if ( (format&0x7f)==0 ) {
	    cnt = getc(ttf);
	    for ( i=1; i<=cnt && i<info->glyph_cnt; ++i )
		map->map[getc(ttf)] = i;
	} else if ( (format&0x7f)==1 ) {
	    cnt = getc(ttf);
	    pos = 0;
	    for ( i=0; i<cnt ; ++i ) {
		first = getc(ttf);
		last = first + getc(ttf)-1;
		while ( first<=last && first<256 ) {
		    if ( pos<info->glyph_cnt )
			map->map[first] = pos;
		    ++pos;
		    ++first;
		}
	    }
	} else
	    LogError( _("Unexpected encoding format in cff: %d\n"), format );
	if ( format&0x80 ) {
	    cnt = getc(ttf);
	    for ( i=0; i<cnt; ++i ) {
		dupenc = getc(ttf);
		sid = getushort(ttf);
		name = getsid(sid,strings,scnt);
		if ( name==NULL )	/* Table is erroneous */
	    break;
		for ( j=0; j<info->glyph_cnt; ++j )
		    if ( strcmp(name,info->chars[j]->name)==0 )
		break;
		if ( j!=info->glyph_cnt )
		    map->map[dupenc] = j;
	    }
	}
    }
    info->map = map;
}

static void readcffset(FILE *ttf,struct topdicts *dict) {
    int len = dict->glyphs.cnt;
    int i;
    int format, cnt, j, first;

    i = 0;
    if ( dict->charsetoff==0 ) {
	/* ISO Adobe charset */
	dict->charset = galloc(len*sizeof(uint16));
	for ( i=0; i<len && i<=228; ++i )
	    dict->charset[i] = i;
    } else if ( dict->charsetoff==1 ) {
	/* Expert charset */
	dict->charset = galloc((len<162?162:len)*sizeof(uint16));
	dict->charset[0] = 0;		/* .notdef */
	dict->charset[1] = 1;
	for ( i=2; i<len && i<=238-227; ++i )
	    dict->charset[i] = i+227;
	dict->charset[12] = 13;
	dict->charset[13] = 14;
	dict->charset[14] = 15;
	dict->charset[15] = 99;
	for ( i=16; i<len && i<=248-223; ++i )
	    dict->charset[i] = i+223;
	dict->charset[25] = 27;
	dict->charset[26] = 28;
	for ( i=27; i<len && i<=266-222; ++i )
	    dict->charset[i] = i+222;
	dict->charset[44] = 109;
	dict->charset[45] = 110;
	for ( i=46; i<len && i<=318-221; ++i )
	    dict->charset[i] = i+221;
	dict->charset[96] = 158;
	dict->charset[97] = 155;
	dict->charset[98] = 163;
	for ( i=99; i<len && i<=326-220; ++i )
	    dict->charset[i] = i+220;
	dict->charset[107] = 150;
	dict->charset[108] = 164;
	dict->charset[109] = 169;
	for ( i=110; i<len && i<=378-217; ++i )
	    dict->charset[i] = i+217;
    } else if ( dict->charsetoff==2 ) {
	/* Expert subset charset */
	dict->charset = galloc((len<130?130:len)*sizeof(uint16));
	dict->charset[0] = 0;		/* .notdef */
	dict->charset[1] = 1;
	for ( i=2; i<len && i<=238-227; ++i )
	    dict->charset[i] = i+227;
	dict->charset[12] = 13;
	dict->charset[13] = 14;
	dict->charset[14] = 15;
	dict->charset[15] = 99;
	for ( i=16; i<len && i<=248-223; ++i )
	    dict->charset[i] = i+223;
	dict->charset[25] = 27;
	dict->charset[26] = 28;
	for ( i=27; i<len && i<=266-222; ++i )
	    dict->charset[i] = i+222;
	dict->charset[44] = 109;
	dict->charset[45] = 110;
	for ( i=46; i<len && i<=272-221; ++i )
	    dict->charset[i] = i+221;
	dict->charset[51] = 300;
	dict->charset[52] = 301;
	dict->charset[53] = 302;
	dict->charset[54] = 305;
	dict->charset[55] = 314;
	dict->charset[56] = 315;
	dict->charset[57] = 158;
	dict->charset[58] = 155;
	dict->charset[59] = 163;
	for ( i=60; i<len && i<=326-260; ++i )
	    dict->charset[i] = i+260;
	dict->charset[67] = 150;
	dict->charset[68] = 164;
	dict->charset[69] = 169;
	for ( i=110; i<len && i<=346-217; ++i )
	    dict->charset[i] = i+217;
    } else {
	dict->charset = galloc(len*sizeof(uint16));
	dict->charset[0] = 0;		/* .notdef */
	fseek(ttf,dict->cff_start+dict->charsetoff,SEEK_SET);
	format = getc(ttf);
	if ( format==0 ) {
	    for ( i=1; i<len; ++i )
		dict->charset[i] = getushort(ttf);
	} else if ( format==1 ) {
	    for ( i = 1; i<len; ) {
		first = dict->charset[i++] = getushort(ttf);
		cnt = getc(ttf);
		for ( j=0; j<cnt; ++j )
		    dict->charset[i++] = ++first;
	    }
	} else if ( format==2 ) {
	    for ( i = 1; i<len; ) {
		first = dict->charset[i++] = getushort(ttf);
		cnt = getushort(ttf);
		for ( j=0; j<cnt; ++j )
		    dict->charset[i++] = ++first;
	    }
	} else
	    LogError( _("Unexpected charset format in cff: %d\n"), format );
    }
    while ( i<len ) dict->charset[i++] = 0;
}

static uint8 *readfdselect(FILE *ttf,int numglyphs) {
    uint8 *fdselect = gcalloc(numglyphs,sizeof(uint8));
    int i, j, format, nr, first, end, fd;

    format = getc(ttf);
    if ( format==0 ) {
	for ( i=0; i<numglyphs; ++i )
	    fdselect[i] = getc(ttf);
    } else if ( format==3 ) {
	nr = getushort(ttf);
	first = getushort(ttf);
	for ( i=0; i<nr; ++i ) {
	    fd = getc(ttf);
	    end = getushort(ttf);
	    for ( j=first; j<end; ++j ) {
		if ( j>=numglyphs )
		    LogError( _("Bad fdselect\n") );
		else
		    fdselect[j] = fd;
	    }
	    first = end;
	}
    } else {
	LogError( _("Didn't understand format for fdselect %d\n"), format );
    }
return( fdselect );
}


static char *intarray2str(int *array, int size) {
    int i,j;
    char *pt, *ret;

    for ( i=size-1; i>=0 && array[i]==0; --i );
    if ( i==-1 )
return( NULL );
    ret = pt = galloc((i+1)*12+12);
    *pt++ = '[';
    for ( j=0; j<=i; ++j ) {
	sprintf( pt, "%d ", array[j]);
	pt += strlen(pt);
    }
    pt[-1]=']';
return( ret );
}

static char *realarray2str(real *array, int size) {
    int i,j;
    char *pt, *ret;

    for ( i=size-1; i>=0 && array[i]==0; --i );
    if ( i==-1 )
return( NULL );
    ret = pt = galloc((i+1)*20+12);
    *pt++ = '[';
    for ( j=0; j<=i; ++j ) {
	sprintf( pt, "%g ", (double) array[j]);
	pt += strlen(pt);
    }
    pt[-1]=']';
return( ret );
}

static void privateadd(struct psdict *private,char *key,char *value) {
    if ( value==NULL )
return;
    private->keys[private->next] = copy(key);
    private->values[private->next++] = value;
}

static void privateaddint(struct psdict *private,char *key,int val) {
    char buf[10];
    if ( val==0 )
return;
    sprintf( buf,"%d", val );
    privateadd(private,key,copy(buf));
}

static void privateaddintarray(struct psdict *private,char *key,int val) {
    char buf[10];
    if ( val==0 )
return;
    sprintf( buf,"[%d]", val );
    privateadd(private,key,copy(buf));
}

static void privateaddreal(struct psdict *private,char *key,double val) {
    char buf[10];
    if ( val==0 )
return;
    sprintf( buf,"%g", val );
    privateadd(private,key,copy(buf));
}

static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
    private->cnt = 14;
    private->keys = galloc(14*sizeof(char *));
    private->values = galloc(14*sizeof(char *));
    privateadd(private,"BlueValues",
	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0])));
    privateadd(private,"OtherBlues",
	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0])));
    privateadd(private,"FamilyBlues",
	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0])));
    privateadd(private,"FamilyOtherBlues",
	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0])));
    privateaddreal(private,"BlueScale",dict->bluescale);
    privateaddreal(private,"BlueShift",dict->blueshift);
    privateaddreal(private,"BlueFuzz",dict->bluefuzz);
    privateaddintarray(private,"StdHW",dict->stdhw);
    privateaddintarray(private,"StdVW",dict->stdvw);
    privateadd(private,"SnapStemH",
	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0])));
    privateadd(private,"SnapStemV",
	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0])));
    if ( dict->forcebold )
	privateadd(private,"ForceBold",copy("true"));
    if ( dict->forceboldthreshold!=0 )
	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold);
    privateaddint(private,"LanguageGroup",dict->languagegroup);
    privateaddreal(private,"ExpansionFactor",dict->expansionfactor);
}

static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
	int scnt) {
    SplineFont *sf = SplineFontEmpty();
    int emsize;
    static int nameless;

    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt));
    if ( sf->fontname==NULL ) {
	char buffer[40];
	sprintf(buffer,"UntitledSubFont_%d", ++nameless );
	sf->fontname = copy(buffer);
    }

    if ( subdict->fontmatrix[0]==0 )
	emsize = 1000;
    else
	emsize = rint( 1/subdict->fontmatrix[0] );
    sf->ascent = .8*emsize;
    sf->descent = emsize - sf->ascent;
    if ( subdict->copyright!=-1 )
	sf->copyright = utf8_verify_copy(getsid(subdict->copyright,strings,scnt));
    else
	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt));
    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt));
    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt));
    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt));
    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt));
    sf->italicangle = subdict->italicangle;
    sf->upos = subdict->underlinepos;
    sf->uwidth = subdict->underlinewidth;
    sf->xuid = intarray2str(subdict->xuid,sizeof(subdict->xuid)/sizeof(subdict->xuid[0]));
    sf->uniqueid = subdict->uniqueid;
    sf->strokewidth = subdict->strokewidth;
    sf->strokedfont = subdict->painttype==2;

    if ( subdict->private_size>0 ) {
	sf->private = gcalloc(1,sizeof(struct psdict));
	cffprivatefillup(sf->private,subdict);
    }
return( sf );
}

static void cffinfofillup(struct ttfinfo *info, struct topdicts *dict,
	char **strings, int scnt ) {

    info->glyph_cnt = dict->glyphs.cnt;
    if ( info->glyph_cnt<0 ) info->glyph_cnt = 0;

    if ( dict->fontmatrix[0]==0 )
	info->emsize = 1000;
    else
	info->emsize = rint( 1/dict->fontmatrix[0] );
#if 1
    info->ascent = .8*info->emsize;
#else
    info->ascent = dict->fontbb[3]*info->emsize/(dict->fontbb[3]-dict->fontbb[1]);
#endif
    info->descent = info->emsize - info->ascent;
    if ( dict->copyright!=-1 || dict->notice!=-1 )
	free( info->copyright );
    if ( dict->copyright!=-1 )
	info->copyright = utf8_verify_copy(getsid(dict->copyright,strings,scnt));
    else if ( dict->notice!=-1 )
	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt));
    if ( dict->familyname!=-1 ) {
	free(info->familyname);
	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt));
    }
    if ( dict->fullname!=-1 ) {
	free(info->fullname);
	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt));
    }
    if ( dict->weight!=-1 ) {
	free(info->weight);
	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt));
    }
    if ( dict->version!=-1 ) {
	free(info->version);
	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt));
    }
    if ( dict->fontname!=NULL ) {
	free(info->fontname);
	info->fontname = utf8_verify_copy(dict->fontname);
    }
    info->italicAngle = dict->italicangle;
    info->upos = dict->underlinepos;
    info->uwidth = dict->underlinewidth;
    info->xuid = intarray2str(dict->xuid,sizeof(dict->xuid)/sizeof(dict->xuid[0]));
    info->uniqueid = dict->uniqueid;
    info->strokewidth = dict->strokewidth;
    info->strokedfont = dict->painttype==2;

    if ( dict->private_size>0 ) {
	info->private = gcalloc(1,sizeof(struct psdict));
	cffprivatefillup(info->private,dict);
    }
    if ( dict->ros_registry!=-1 ) {
	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt));
	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt));
	info->supplement = dict->ros_supplement;
	info->cidfontversion = dict->cidfontversion;
    }
}

static void cfffigure(struct ttfinfo *info, struct topdicts *dict,
	char **strings, int scnt, struct pschars *gsubrs) {
    int i, cstype;
    struct pschars *subrs;
    struct pscontext pscontext;

    memset(&pscontext,0,sizeof(pscontext));

    cffinfofillup(info, dict, strings, scnt );

/* The format allows for some dicts that are type1 strings and others that */
/*  are type2s. Which means that the global subrs will have a different bias */
/*  as we flip from font to font. So we can't set the bias when we read in */
/*  the subrs but must wait until we know which font we're working on. */
    cstype = dict->charstringtype;
    pscontext.is_type2 = cstype-1;
    pscontext.painttype = dict->painttype;
    gsubrs->bias = cstype==1 ? 0 :
	    gsubrs->cnt < 1240 ? 107 :
	    gsubrs->cnt <33900 ? 1131 : 32768;
    subrs = &dict->local_subrs;
    subrs->bias = cstype==1 ? 0 :
	    subrs->cnt < 1240 ? 107 :
	    subrs->cnt <33900 ? 1131 : 32768;

    info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *));
    for ( i=0; i<info->glyph_cnt; ++i ) {
	info->chars[i] = PSCharStringToSplines(
		dict->glyphs.values[i], dict->glyphs.lens[i],&pscontext,
		subrs,gsubrs,getsid(dict->charset[i],strings,scnt));
	info->chars[i]->vwidth = info->emsize;
	if ( cstype==2 ) {
	    if ( info->chars[i]->width == (int16) 0x8000 )
		info->chars[i]->width = dict->defaultwidthx;
	    else
		info->chars[i]->width += dict->nominalwidthx;
	}
    }
    /* Need to do a reference fixup here !!!!! just in case some idiot */
    /*  used type1 char strings -- or used the depreciated meaning of */
    /*  endchar (==seac) */
}

static void cidfigure(struct ttfinfo *info, struct topdicts *dict,
	char **strings, int scnt, struct pschars *gsubrs, struct topdicts **subdicts,
	uint8 *fdselect) {
    int i, j, cstype, uni, cid;
    struct pschars *subrs;
    SplineFont *sf;
    struct cidmap *map;
    char buffer[100];
    struct pscontext pscontext;
    EncMap *encmap = NULL;

    memset(&pscontext,0,sizeof(pscontext));

    cffinfofillup(info, dict, strings, scnt );

    /* We'll set the encmap later */
    /*info->map = encmap = EncMapNew(info->glyph_cnt,info->glyph_cnt,&custom);*/

    for ( j=0; subdicts[j]!=NULL; ++j );
    info->subfontcnt = j;
    info->subfonts = gcalloc(j+1,sizeof(SplineFont *));
    for ( j=0; subdicts[j]!=NULL; ++j )  {
	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt);
	info->subfonts[j]->map = encmap;
    }
    for ( i=0; i<info->glyph_cnt; ++i ) {
	sf = info->subfonts[ fdselect[i] ];
	cid = dict->charset[i];
	if ( cid>=sf->glyphcnt ) sf->glyphcnt = sf->glyphmax = cid+1;
	/*if ( cid>=encmap->enccount ) encmap->enccount = cid+1;*/
    }
    for ( j=0; subdicts[j]!=NULL; ++j )
	info->subfonts[j]->glyphs = gcalloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
    /*encmap->encmax = encmap->enccount;*/
    /*encmap->map = galloc(encmap->enccount*sizeof(int));*/
    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/

    info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *));

    /* info->chars provides access to the chars ordered by glyph, which the */
    /*  ttf routines care about */
    /* sf->glyphs provides access to the chars ordered by CID. Not sure what */
    /*  would happen to a kern from one font to another... */

    map = FindCidMap(info->cidregistry,info->ordering,info->supplement,NULL);

    for ( i=0; i<info->glyph_cnt; ++i ) {
	j = fdselect[i];
	sf = info->subfonts[ j ];
/* The format allows for some dicts that are type1 strings and others that */
/*  are type2s. Which means that the global subrs will have a different bias */
/*  as we flip from font to font. So we can't set the bias when we read in */
/*  the subrs but must wait until we know which font we're working on. */
	cstype = subdicts[j]->charstringtype;
	pscontext.is_type2 = cstype-1;
	pscontext.painttype = subdicts[j]->painttype;
	gsubrs->bias = cstype==1 ? 0 :
		gsubrs->cnt < 1240 ? 107 :
		gsubrs->cnt <33900 ? 1131 : 32768;
	subrs = &subdicts[j]->local_subrs;
	subrs->bias = cstype==1 ? 0 :
		subrs->cnt < 1240 ? 107 :
		subrs->cnt <33900 ? 1131 : 32768;

	cid = dict->charset[i];
	/*encmap->map[cid] = cid;*/
	uni = CID2NameUni(map,cid,buffer,sizeof(buffer));
	info->chars[i] = PSCharStringToSplines(
		dict->glyphs.values[i], dict->glyphs.lens[i],&pscontext,
		subrs,gsubrs,buffer);
	info->chars[i]->vwidth = sf->ascent+sf->descent;
	info->chars[i]->unicodeenc = uni;
	sf->glyphs[cid] = info->chars[i];
	sf->glyphs[cid]->parent = sf;
	sf->glyphs[cid]->orig_pos = cid;		/* Bug! should be i, but I assume sf->chars[orig_pos]->orig_pos==orig_pos */
	if ( sf->glyphs[cid]->layers[ly_fore].refs!=NULL )
	    IError( "Reference found in CID font. Can't fix it up");
	if ( cstype==2 ) {
	    if ( sf->glyphs[cid]->width == (int16) 0x8000 )
		sf->glyphs[cid]->width = subdicts[j]->defaultwidthx;
	    else
		sf->glyphs[cid]->width += subdicts[j]->nominalwidthx;
	}
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_progress_next();
#endif
    }
    /* No need to do a reference fixup here-- the chars aren't associated */
    /*  with any encoding as is required for seac */
}

static int readcffglyphs(FILE *ttf,struct ttfinfo *info) {
    int offsize;
    int hdrsize;
    char **fontnames, **strings;
    struct topdicts **dicts, **subdicts;
    int i, j, which;
    struct pschars gsubs;
    uint8 *fdselect;
    int scnt;

    fseek(ttf,info->cff_start,SEEK_SET);
    if ( getc(ttf)!='\1' ) {		/* Major version */
	LogError( _("CFF version mismatch\n" ));
return( 0 );
    }
    getc(ttf);				/* Minor version */
    hdrsize = getc(ttf);
    offsize = getc(ttf);
    if ( hdrsize!=4 )
	fseek(ttf,info->cff_start+hdrsize,SEEK_SET);
    fontnames = readcfffontnames(ttf,NULL);
    which = 0;
    if ( fontnames[1]!=NULL ) {		/* More than one? Can that even happen in OpenType? */
	which = PickCFFFont(fontnames);
	if ( which==-1 ) {
	    for ( i=0; fontnames[i]!=NULL; ++i )
		free(fontnames[i]);
	    free(fontnames);
return( 0 );
	}
    }
    dicts = readcfftopdicts(ttf,fontnames,info->cff_start);
	/* String index is just the same as fontname index */
    strings = readcfffontnames(ttf,&scnt);
    readcffsubrs(ttf,&gsubs );
    /* Can be many fonts here. Only decompose the one */
    if ( dicts[which]->charstringsoff!=-1 ) {
	fseek(ttf,info->cff_start+dicts[which]->charstringsoff,SEEK_SET);
	readcffsubrs(ttf,&dicts[which]->glyphs);
    }
    if ( dicts[which]->private_offset!=-1 )
	readcffprivate(ttf,dicts[which]);
    if ( dicts[which]->charsetoff!=-1 )
	readcffset(ttf,dicts[which]);
    if ( dicts[which]->fdarrayoff==-1 )
	cfffigure(info,dicts[which],strings,scnt,&gsubs);
    else {
	fseek(ttf,info->cff_start+dicts[which]->fdarrayoff,SEEK_SET);
	subdicts = readcfftopdicts(ttf,NULL,info->cff_start);
	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt);
	for ( j=0; subdicts[j]!=NULL; ++j ) {
	    if ( subdicts[j]->private_offset!=-1 )
		readcffprivate(ttf,subdicts[j]);
	    if ( subdicts[j]->charsetoff!=-1 )
		readcffset(ttf,subdicts[j]);
	}
	cidfigure(info,dicts[which],strings,scnt,&gsubs,subdicts,fdselect);
	for ( j=0; subdicts[j]!=NULL; ++j )
	    TopDictFree(subdicts[j]);
	free(subdicts); free(fdselect);
    }
    if ( dicts[which]->encodingoff!=-1 )
	readcffenc(ttf,dicts[which],info,strings,scnt);

    if ( dicts[which]->fdarrayoff==-1 ) {
	for ( i=0; i<info->glyph_cnt ; ++i )
	    if ( info->chars[i]!=NULL )
		info->chars[i]->orig_pos = i;
    }

    if ( info->to_order2 ) {
	for ( i=0; i<info->glyph_cnt; ++i )
	    SCConvertToOrder2(info->chars[i]);
    }

    for ( i=0; fontnames[i]!=NULL && i<1; ++i ) {
	free(fontnames[i]);
	TopDictFree(dicts[i]);
    }
    free(fontnames); free(dicts);
    if ( strings!=NULL ) {
	for ( i=0; strings[i]!=NULL; ++i )
	    free(strings[i]);
	free(strings);
    }
    for ( i=0; i<gsubs.cnt; ++i )
	free(gsubs.values[i]);
    free(gsubs.values); free(gsubs.lens);

return( 1 );
}

static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
    int i,j;
    int lastwidth = info->emsize;
    /* I'm not interested in the lsb, I'm not sure what it means if it differs*/
    /*  from that is specified in the outline. Do we move the outline? */
    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
    SplineChar *sc;

    fseek(ttf,info->hmetrics_start,SEEK_SET);
    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
	lastwidth = getushort(ttf);
	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
	    if ( check_width_consistency && sc->width!=lastwidth ) {
		if ( info->fontname!=NULL && sc->name!=NULL )
		    LogError("In %s, in glyph %s, 'CFF ' advance width (%d) and\n  'hmtx' width (%d) do not match. (Subsequent mismatches will not be reported)\n",
			    info->fontname, sc->name, sc->width, lastwidth );
		else
		    LogError("In GID %d, 'CFF ' advance width (%d) and 'hmtx' width (%d) do not match.\n  (Subsequent mismatches will not be reported)\n",
			    i, sc->width, lastwidth );
		check_width_consistency = false;
	    }
	    sc->width = lastwidth;
	    sc->widthset = true;
	}
	/* lsb = */ getushort(ttf);
    }
    if ( i==0 )
	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
	
    for ( j=i; j<info->glyph_cnt; ++j ) {
	if ( info->chars[j]!=NULL ) {		/* In a ttc file we may skip some */
	    info->chars[j]->width = lastwidth;
	    info->chars[j]->widthset = true;
	}
    }
}

static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
    BDFFont *bdf;
    int i, cnt;
    double scaled_sum;

    if ( info->bitmaps==NULL )
return;
#if 0
    for ( bdf=info->bitmaps; bdf->next!=NULL; bdf=bdf->next );
    for ( i=0; i<bdf->glyphcnt; ++i ) if ( bdf->glyphs[i]!=NULL ) {
	bdf->glyphs[i]->sc->width = info->emsize*bdf->glyphs[i]->width/bdf->pixelsize;
	bdf->glyphs[i]->sc->widthset = true;
    }
#else
    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) {
	cnt = 0; scaled_sum = 0;
	for ( bdf=info->bitmaps; bdf->next!=NULL; bdf=bdf->next ) {
	    if ( i<bdf->glyphcnt && bdf->glyphs[i]!=NULL ) {
		scaled_sum += ((double) (info->emsize*bdf->glyphs[i]->width))/bdf->pixelsize;
		++cnt;
	    }
	}
	if ( cnt!=0 ) {
	    info->chars[i]->width = scaled_sum/cnt;
	    info->chars[i]->widthset = true;
	}
    }
#endif
}

static void readttfvwidths(FILE *ttf,struct ttfinfo *info) {
    int i,j;
    int lastvwidth = info->emsize, vwidth_cnt, tsb, cnt=0;
    int32 voff=0;

    fseek(ttf,info->vhea_start+4+4,SEEK_SET);		/* skip over the version number & typo right/left */
    info->pfminfo.vlinegap = getushort(ttf);
    info->pfminfo.vheadset = true;

    for ( i=0; i<12; ++i )
	getushort(ttf);
    vwidth_cnt = getushort(ttf);

    fseek(ttf,info->vmetrics_start,SEEK_SET);
    for ( i=0; i<vwidth_cnt && i<info->glyph_cnt; ++i ) {
	lastvwidth = getushort(ttf);
	tsb = getushort(ttf);
	if ( info->chars[i]!=NULL ) {		/* can happen in ttc files */
	    info->chars[i]->vwidth = lastvwidth;
	    if ( info->cff_start==0 ) {
		voff += tsb + info->chars[i]->lsidebearing /* actually maxy */;
		++cnt;
	    }
	}
    }
    if ( i==0 )
	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
	
    for ( j=i; j<info->glyph_cnt; ++j ) {
	if ( info->chars[j]!=NULL )		/* In a ttc file we may skip some */
	    info->chars[j]->vwidth = lastvwidth;
    }

    /* for truetype fonts the vertical offset is found by adding the ymax of a */
    /*  character to the top side bearing. I set the font wide value to the */
    /*  average of them all */
    /* But opentype doesn't give us the ymax easily, and rather than compute */
    /*  the bounding box I'll just punt and pick a reasonable value */
    /* Of course I hope it will be over riden by the VORG table */
    if ( cnt!=0 )
	info->vertical_origin = (voff+cnt/2)/cnt;
    if ( info->vertical_origin==0 )
	info->vertical_origin = info->ascent;
    if ( info->vorg_start!=0 ) {
	fseek(ttf,info->vorg_start+4,SEEK_SET);
	info->vertical_origin = (short) getushort(ttf);
    }
}

static int modenc(int enc,int modtype) {
return( enc );
}

static int umodenc(int enc,int modtype) {
    if ( modtype==-1 )
return( -1 );
    if ( modtype<=1 /* Unicode */ ) {
	/* No conversion */;
    } else if ( modtype==2 /* SJIS */ ) {
	if ( enc<=127 ) {
	    /* Latin */
	    if ( enc=='\\' ) enc = 0xa5;	/* Yen */
	} else if ( enc>=161 && enc<=223 ) {
	    /* Katakana */
	    enc = unicode_from_jis201[enc];
	} else {
	    int ch1 = enc>>8, ch2 = enc&0xff;
	    if ( ch1 >= 129 && ch1<= 159 )
		ch1 -= 112;
	    else
		ch1 -= 176;
	    ch1 <<= 1;
	    if ( ch2>=159 )
		ch2-= 126;
	    else if ( ch2>127 ) {
		--ch1;
		ch2 -= 32;
	    } else {
		--ch1;
		ch2 -= 31;
	    }
	    enc = unicode_from_jis208[(ch1-0x21)*94+(ch2-0x21)];
	}
    } else if ( modtype==3 /* GB2312 offset by 0x8080, parse just like wansung */ ) {
	if ( enc>0xa1a1 ) {
	    enc -= 0xa1a1;
	    enc = (enc>>8)*94 + (enc&0xff);
	    enc = unicode_from_gb2312[enc];
	    if ( enc==0 ) enc = -1;
	} else if ( enc>0x100 )
	    enc = -1;
    } else if ( modtype==4 /* BIG5 */ ) {	/* old ms docs say big5 is modtype==3, but new ones say 4 */
	if ( enc>0x8100 )
	    enc = unicode_from_big5hkscs[enc-0x8100];
	else if ( enc>0x100 )
	    enc = -1;
    } else if ( modtype==5 /* Wansung == KSC 5601-1987, I hope */ ) {
	if ( enc>0xa1a1 ) {
	    enc -= 0xa1a1;
	    enc = (enc>>8)*94 + (enc&0xff);
	    enc = unicode_from_ksc5601[enc];
	    if ( enc==0 ) enc = -1;
	} else if ( enc>0x100 )
	    enc = -1;
    } else if ( modtype==6 /* Johab */ ) {
	if ( enc>0x8400 )
	    enc = unicode_from_johab[enc-0x8400];
	else if ( enc>0x100 )
	    enc = -1;
    }
    if ( enc==0 )
	enc = -1;
return( enc );
}

static int SubtableIsntSupported(FILE *ttf,uint32 offset,int platform,int specific) {
    uint32 here = ftell(ttf);
    int format, len, ret=false;

    fseek(ttf,offset,SEEK_SET);

    format = getushort(ttf);
    if ( format<0 || (format&1) || format>12 ) {
	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
		platform, specific, format );
	ret = true;
    }

    if ( format!=12 && format!=10 && format!=8 ) {
	len = getushort(ttf);
    } else {
	/* padding */ getushort(ttf);
	len = getlong(ttf);
    }
    if ( len==0 ) {
	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
		platform, specific );
	ret = true;
    }
    fseek(ttf,here,SEEK_SET);
return( ret );
}

static enum uni_interp amscheck(struct ttfinfo *info, EncMap *map) {
    int cnt = 0;
    /* Try to guess if the font uses the AMS math PUA assignments */

    if ( map==NULL )
return( ui_none );

    if ( 0xe668<map->enccount && map->map[0xe668]!=-1 &&
	    info->chars[map->map[0xe668]]->unicodeenc=='b' )
	++cnt;
    if ( 0xe3c8<map->enccount && map->map[0xe626]!=-1 &&
	    info->chars[map->map[0xe626]]->unicodeenc==0xe626 )
	++cnt;
    if ( 0xe3c8<map->enccount && map->map[0xe3c8]!=-1 &&
	    info->chars[map->map[0xe3c8]]->unicodeenc==0x29e1 )
	++cnt;
    if ( 0x2A7C<map->enccount && map->map[0x2A7C]!=-1 &&
	    info->chars[map->map[0x2A7C]]->unicodeenc==0xE32A )
	++cnt;
    if ( 0x2920<map->enccount && map->map[0x2920]!=-1 &&
	    info->chars[map->map[0x2920]]->unicodeenc==0xE221 )
	++cnt;
return( cnt>=2 ? ui_ams : ui_none );
}

static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
    int i,j;
    int nencs, version;
    Encoding *enc = &custom;
    const int32 *trans=NULL;
    enum uni_interp interp = ui_none;
    int platform, specific;
    int offset, encoff=0;
    int format, len;
    uint16 table[256];
    int segCount;
    uint16 *endchars, *startchars, *delta, *rangeOffset, *glyphs;
    int index, last;
    int mod = 0;
    SplineChar *sc;
    uint8 *used;
    int badencwarned=false;
    int glyph_tot;
    Encoding *temp;
    EncMap *map;

    fseek(ttf,info->encoding_start,SEEK_SET);
    version = getushort(ttf);
    nencs = getushort(ttf);
    if ( version!=0 && nencs==0 )
	nencs = version;		/* Sometimes they are backwards */
    for ( i=0; i<nencs; ++i ) {
	platform = getushort(ttf);
	specific = getushort(ttf);
	offset = getlong(ttf);
	if ( SubtableIsntSupported(ttf,info->encoding_start+offset,platform,specific))
    continue;
	temp = enc_from_platspec(platform,specific);
	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
    continue;
	interp = interp_from_encoding(temp,interp);
	if ( platform==3 && specific==10 ) { /* MS Unicode 4 byte */
	    enc = temp;
	    encoff = offset;
	    mod = 0;
	    info->platform = platform; info->specific = specific;
	} else if ( (enc->is_unicodefull || (!prefer_cjk_encodings ||
		(!enc->is_japanese && !enc->is_korean && !enc->is_tradchinese &&
		    !enc->is_simplechinese))) &&
		(( platform==3 && specific==1 ) || /* MS Unicode */
/* Well I should only deal with apple unicode specific==0 (default) and 3 (U2.0 semantics) */
/*  but apple ships dfonts with specific==1 (Unicode 1.1 semantics) */
/*  which is stupid of them */
		( platform==0 /*&& (specific==0 || specific==3)*/ ))) {	/* Apple Unicode */
	    enc = temp;
	    encoff = offset;
	    info->platform = platform; info->specific = specific;
	    mod = 0;
	} else if ( platform==3 && specific==0 && enc->is_custom ) {
	    /* Only select symbol if we don't have something better */
	    enc = temp;
	    encoff = offset;
	    info->platform = platform; info->specific = specific;
	    /* Now I had assumed this would be a 1 byte encoding, but it turns*/
	    /*  out to map into the unicode private use area at U+f000-U+F0FF */
	    /*  so it's a 2 byte enc */
/* Mac platform specific encodings are script numbers. 0=>roman, 1=>jap, 2=>big5, 3=>korean, 4=>arab, 5=>hebrew, 6=>greek, 7=>cyrillic, ... 25=>simplified chinese */
	} else if ( platform==1 && specific==0 && enc->is_custom ) {
	    info->platform = platform; info->specific = specific;
	    enc = temp;
	    encoff = offset;
	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
		!enc->is_unicodefull &&
		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
	    enc = temp;
	    mod = specific==1?2:specific==2?4:specific==3?5:3;		/* convert to ms specific */
	    info->platform = platform; info->specific = specific;
	    encoff = offset;
	} else if ( platform==3 && (specific>=2 && specific<=6 ) &&
		!enc->is_unicodefull &&
		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
	    /* Old ms docs say that specific==3 => big 5, new docs say specific==4 => big5 */
	    /*  Ain't that jus' great? */
	    enc = temp;
	    info->platform = platform; info->specific = specific;
	    mod = specific;
	    encoff = offset;
	}
	if ( enc==NULL )
	    enc = &custom;
    }

    if ( !enc->is_custom ) {
	fseek(ttf,info->encoding_start+encoff,SEEK_SET);
	format = getushort(ttf);
	if ( format!=12 && format!=10 && format!=8 ) {
	    len = getushort(ttf);
	    /* version/language = */ getushort(ttf);
	} else {
	    /* padding */ getushort(ttf);
	    len = getlong(ttf);
	    /* language = */ getlong(ttf);
	}
	if ( enc->is_unicodebmp && (format==8 || format==10 || format==12))
	    enc = FindOrMakeEncoding("UnicodeFull");
    }
    if ( justinuse )
	map = NULL;
    else {
	info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
	info->uni_interp = interp;
    }
    if ( !enc->is_custom ) {
	if ( format==0 ) {
	    if ( !justinuse && map->enccount<256 ) {
		map->map = grealloc(map->map,256*sizeof(int));
		memset(map->map,-1,(256-map->enccount)*sizeof(int));
		map->enccount = map->encmax = 256;
	    }
	    for ( i=0; i<len-6; ++i )
		table[i] = getc(ttf);
	    trans = enc->unicode;
	    for ( i=0; i<256 && i<len-6; ++i )
		if ( !justinuse ) {
		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
			map->map[i] = table[i];
			if ( trans!=NULL )
			    info->chars[table[i]]->unicodeenc = trans[i];
		    }
		} else if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL )
		    info->inuse[table[i]] = 1;
	} else if ( format==4 ) {
	    if ( strmatch(enc->enc_name,"symbol")==0 ) {
		enc=FindOrMakeEncoding("UnicodeBmp");
		info->twobytesymbol=true;
	    }
	    segCount = getushort(ttf)/2;
	    /* searchRange = */ getushort(ttf);
	    /* entrySelector = */ getushort(ttf);
	    /* rangeShift = */ getushort(ttf);
	    endchars = galloc(segCount*sizeof(uint16));
	    used = gcalloc(65536,sizeof(uint8));
	    for ( i=0; i<segCount; ++i )
		endchars[i] = getushort(ttf);
	    if ( getushort(ttf)!=0 )
		IError("Expected 0 in 'cmap' format 4 subtable");
	    startchars = galloc(segCount*sizeof(uint16));
	    for ( i=0; i<segCount; ++i )
		startchars[i] = getushort(ttf);
	    delta = galloc(segCount*sizeof(uint16));
	    for ( i=0; i<segCount; ++i )
		delta[i] = getushort(ttf);
	    rangeOffset = galloc(segCount*sizeof(uint16));
	    for ( i=0; i<segCount; ++i )
		rangeOffset[i] = getushort(ttf);
	    len -= 8*sizeof(uint16) +
		    4*segCount*sizeof(uint16);
	    /* that's the amount of space left in the subtable and it must */
	    /*  be filled with glyphIDs */
	    if ( len<0 ) {
		IError("This font has an illegal format 4 subtable with too little space for all the segments.\nThis error is not recoverable.\nBye" );
		exit(1);
	    }
	    glyphs = galloc(len);
	    glyph_tot = len/2;
	    for ( i=0; i<glyph_tot; ++i )
		glyphs[i] = getushort(ttf);
	    for ( i=0; i<segCount; ++i ) {
		if ( rangeOffset[i]==0 && startchars[i]==0xffff )
		    /* Done */;
		else if ( rangeOffset[i]==0 ) {
		    for ( j=startchars[i]; j<=endchars[i]; ++j ) {
			if ( justinuse && (uint16) (j+delta[i])<info->glyph_cnt )
			    info->inuse[(uint16) (j+delta[i])] = true;
			else if ( (uint16) (j+delta[i])>=info->glyph_cnt || info->chars[(uint16) (j+delta[i])]==NULL )
			    LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"),
				    (uint16) (j+delta[i]), modenc(j,mod), modenc(j,mod));
			else {
			    int uenc = umodenc(j,mod);
			    int lenc = modenc(j,mod);
			    if ( uenc!=-1 && used[uenc] ) {
				if ( !badencwarned ) {
				    LogError( _("Multiple glyphs map to the same unicode encoding U+%04X, only one will be used\n"), uenc );
			            badencwarned = true;
				}
			    } else {
				if ( uenc!=-1 ) used[uenc] = true;
				if ( info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
			        if ( lenc<map->enccount )
				    map->map[lenc] = (uint16) (j+delta[i]);
			    }
			}
		    }
		} else if ( rangeOffset[i]!=0xffff ) {
		    /* Apple says a rangeOffset of 0xffff means no glyph */
		    /*  OpenType doesn't mention this */
		    for ( j=startchars[i]; j<=endchars[i]; ++j ) {
			int temp = (i-segCount+rangeOffset[i]/2) + j-startchars[i];
			if ( temp<glyph_tot )
			    index = glyphs[ temp ];
			else {
			    /* This happened in mingliu.ttc(PMingLiU) */
			    if ( !justinuse )
				LogError( _("Glyph index out of bounds. Was %d, must be less than %d.\n In attempt to associate a glyph with encoding %x in segment %d\n with platform=%d, specific=%d (in 'cmap')\n"),
					temp, glyph_tot, j, i, info->platform, info->specific );
			    index = 0;
			}
			if ( index!=0 ) {
			    index = (unsigned short) (index+delta[i]);
			    if ( index>=info->glyph_cnt )
				/* This isn't mentioned either, but in some */
			        /*  MS Chinese fonts (kaiu.ttf) the index */
			        /*  goes out of bounds. and MS's ttf dump */
			        /*  program says it is treated as 0 */
				LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"),
					index, modenc(j,mod), modenc(j,mod));
			    else if ( justinuse )
				info->inuse[index] = 1;
			    else if ( info->chars[index]==NULL )
				LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"),
					index, modenc(j,mod), modenc(j,mod));
			    else {
				int uenc = umodenc(j,mod);
				int lenc = modenc(j,mod);
				if ( uenc!=-1 && used[uenc] ) {
				    if ( !badencwarned ) {
					LogError( _("Multiple glyphs map to the same unicode encoding U+%04X, only one will be used\n"), uenc );
					badencwarned = true;
				    }
				} else {
				    if ( uenc!=-1 ) used[uenc] = true;
				    if ( info->chars[index]->unicodeenc==-1 )
					info->chars[index]->unicodeenc = uenc;
				    if ( lenc<map->enccount )
					map->map[lenc] = index;
				}
			    }
			}
		    }
		} else
		    LogError( _("Use of a range offset of 0xffff to mean a missing glyph in cmap table\n") );
	    }
	    free(glyphs);
	    free(rangeOffset);
	    free(delta);
	    free(startchars);
	    free(endchars);
	    free(used);
	} else if ( format==6 ) {
	    /* array unicode format */
	    /* Well, the docs say it's for 2byte encodings, but Apple actually*/
	    /*  uses it for 1 byte encodings which don't fit into the require-*/
	    /*  ments for a format 0 sub-table. See Zapfino.dfont */
	    int first, count;
	    if ( !enc->is_unicodebmp && !enc->is_unicodefull ) {
		IError("I don't support truncated array encoding (format=6) except for unicode" );
		enc = FindOrMakeEncoding("UnicodeBmp");
	    }
	    first = getushort(ttf);
	    count = getushort(ttf);
	    if ( justinuse )
		for ( i=0; i<count; ++i )
		    info->inuse[getushort(ttf)]= 1;
	    else {
		for ( i=0; i<count; ++i ) {
		    int gid = getushort(ttf);
		    info->chars[gid]->unicodeenc = first+i;
		    if ( first+i < map->enccount )
			map->map[first+i] = gid;
		}
	    }
	} else if ( format==2 ) {
	    int max_sub_head_key = 0, cnt, max_pos= -1;
	    struct subhead *subheads;
	    
	    for ( i=0; i<256; ++i ) {
		table[i] = getushort(ttf)/8;	/* Sub-header keys */
		if ( table[i]>max_sub_head_key ) {
		    max_sub_head_key = table[i];	/* The entry is a byte pointer, I want a pointer in units of struct subheader */
		    max_pos = i;
		}
	    }
	    subheads = galloc((max_sub_head_key+1)*sizeof(struct subhead));
	    for ( i=0; i<=max_sub_head_key; ++i ) {
		subheads[i].first = getushort(ttf);
		subheads[i].cnt = getushort(ttf);
		subheads[i].delta = getushort(ttf);
		subheads[i].rangeoff = (getushort(ttf)-
				(max_sub_head_key-i)*sizeof(struct subhead)-
				sizeof(short))/sizeof(short);
	    }
	    cnt = (len-(ftell(ttf)-(info->encoding_start+encoff)))/sizeof(short);
	    /* The count is the number of glyph indexes to read. it is the */
	    /*  length of the entire subtable minus that bit we've read so far */
	    glyphs = galloc(cnt*sizeof(short));
	    for ( i=0; i<cnt; ++i )
		glyphs[i] = getushort(ttf);
	    last = -1;
	    for ( i=0; i<256; ++i ) {
		if ( table[i]==0 ) {
		    /* Special case, single byte encoding entry, look it up in */
		    /*  subhead */
		    /* In the one example I've got of this encoding (wcl-02.ttf) the chars */
		    /* 0xfd, 0xfe, 0xff are said to exist but there is no mapping */
		    /* for them. */
		    if ( i>=max_pos )
			index = 0;	/* the subhead says there are 256 entries, but in fact there are only 193, so attempting to find these guys should give an error */
		    else if ( i<subheads[0].first || i>=subheads[0].first+subheads[0].cnt ||
			    subheads[0].rangeoff+(i-subheads[0].first)>=cnt )
			index = 0;
		    else if ( (index = glyphs[subheads[0].rangeoff+(i-subheads[0].first)])!= 0 )
			index = (uint32) (index+subheads[0].delta);
		    /* I assume the single byte codes are just ascii or latin1*/
		    if ( index!=0 && index<info->glyph_cnt ) {
			if ( justinuse )
			    info->inuse[index] = 1;
			else if ( info->chars[index]==NULL )
			    /* Do Nothing */;
			else if ( info->chars[index]->unicodeenc==-1 ) {
			    int lenc = modenc(i,mod);
			    if ( info->chars[index]->unicodeenc==-1 )
				info->chars[index]->unicodeenc = i;
			    if ( lenc<map->enccount )
				map->map[lenc] = index;
			}
		    }
		} else {
		    int k = table[i];
		    for ( j=0; j<subheads[k].cnt; ++j ) {
			int enc, lenc;
			if ( subheads[k].rangeoff+j>=cnt )
			    index = 0;
			else if ( (index = glyphs[subheads[k].rangeoff+j])!= 0 )
			    index = (uint16) (index+subheads[k].delta);
			if ( index!=0 && index<info->glyph_cnt ) {
			    enc = (i<<8)|(j+subheads[k].first);
			    lenc = modenc(enc,mod);
			    if ( justinuse )
				info->inuse[index] = 1;
			    else if ( info->chars[index]==NULL )
				/* Do Nothing */;
			    else {
				if ( info->chars[index]->unicodeenc==-1 )
				    info->chars[index]->unicodeenc = umodenc(enc,mod);
				if ( lenc<map->enccount )
				    map->map[lenc] = index;
			    }
			}
		    }
		    /*if ( last==-1 ) last = i;*/
		}
	    }
	    free(subheads);
	    free(glyphs);
	} else if ( format==8 ) {
	    uint32 ngroups, start, end, startglyph;
	    if ( !enc->is_unicodefull ) {
		IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" );
		enc = FindOrMakeEncoding("UnicodeFull");
	    }
	    /* I'm now assuming unicode surrogate encoding, so I just ignore */
	    /*  the is32 table (it will be set for the surrogates and not for */
	    /*  anything else */
	    fseek(ttf,8192,SEEK_CUR);
	    ngroups = getlong(ttf);
	    for ( j=0; j<ngroups; ++j ) {
		start = getlong(ttf);
		end = getlong(ttf);
		startglyph = getlong(ttf);
		if ( justinuse )
		    for ( i=start; i<=end; ++i )
			info->inuse[startglyph+i-start]= 1;
		else
		    for ( i=start; i<=end; ++i ) {
			(sc = info->chars[startglyph+i-start])->unicodeenc =
				((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
			if ( sc->unicodeenc < map->enccount )
			    map->map[sc->unicodeenc] = startglyph+i-start;
		    }
	    }
	} else if ( format==10 ) {
	    /* same as format 6, except for 4byte chars */
	    int first, count;
	    if ( !enc->is_unicodefull ) {
		IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" );
		enc = FindOrMakeEncoding("UnicodeFull");
	    }
	    first = getlong(ttf);
	    count = getlong(ttf);
	    if ( justinuse )
		for ( i=0; i<count; ++i )
		    info->inuse[getushort(ttf)]= 1;
	    else
		for ( i=0; i<count; ++i ) {
		    int gid = getushort(ttf);
		    info->chars[gid]->unicodeenc = first+i;
		    if ( first+i < map->enccount )
			map->map[first+i] = gid;
		}
	} else if ( format==12 ) {
	    uint32 ngroups, start, end, startglyph;
	    if ( !enc->is_unicodefull ) {
		IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" );
		enc = FindOrMakeEncoding("UnicodeFull");
	    }
	    ngroups = getlong(ttf);
	    for ( j=0; j<ngroups; ++j ) {
		start = getlong(ttf);
		end = getlong(ttf);
		startglyph = getlong(ttf);
		if ( justinuse ) {
		    for ( i=start; i<=end; ++i )
			if ( startglyph+i-start < info->glyph_cnt )
			    info->inuse[startglyph+i-start]= 1;
			else
		    break;
		} else
		    for ( i=start; i<=end; ++i ) {
			if ( startglyph+i-start >= info->glyph_cnt ||
				info->chars[startglyph+i-start]==NULL ) {
			    LogError( _("Bad font: Encoding data out of range.\n") );
		    break;
			} else {
			    info->chars[startglyph+i-start]->unicodeenc = i;
			    if ( i < map->enccount )
				map->map[i] = startglyph+i-start;
			}
		    }
	    }
	}
    }
    if ( info->chars!=NULL )
	for ( i=0; i<info->glyph_cnt; ++i )
	    if ( info->chars[i]!=NULL && info->chars[i]->unicodeenc==0xffff )
		info->chars[i]->unicodeenc = -1;
    if ( !justinuse ) {
	if ( interp==ui_none )
	    info->uni_interp = amscheck(info,map);
	map->enc = enc;		/* This can be changed from the initial value */
    }
    info->map = map;
}

static void readttfos2metrics(FILE *ttf,struct ttfinfo *info) {
    int i;

    fseek(ttf,info->os2_start,SEEK_SET);
    /* version */ getushort(ttf);
    /* avgWidth */ getushort(ttf);
    info->pfminfo.weight = getushort(ttf);
    info->pfminfo.width = getushort(ttf);
    info->pfminfo.fstype = getushort(ttf);
    info->pfminfo.os2_subxsize = getushort(ttf);
    info->pfminfo.os2_subysize = getushort(ttf);
    info->pfminfo.os2_subxoff = getushort(ttf);
    info->pfminfo.os2_subyoff = getushort(ttf);
    info->pfminfo.os2_supxsize = getushort(ttf);
    info->pfminfo.os2_supysize = getushort(ttf);
    info->pfminfo.os2_supxoff = getushort(ttf);
    info->pfminfo.os2_supyoff = getushort(ttf);
    info->pfminfo.os2_strikeysize = getushort(ttf);
    info->pfminfo.os2_strikeypos = getushort(ttf);
    info->pfminfo.os2_family_class = getushort(ttf);
    for ( i=0; i<10; ++i )
	info->pfminfo.panose[i] = getc(ttf);
    info->pfminfo.pfmfamily = info->pfminfo.panose[0]==2 ? 0x11 :	/* might be 0x21 */ /* Text & Display maps to either serif 0x11 or sans 0x21 or monospace 0x31 */
		      info->pfminfo.panose[0]==3 ? 0x41 :	/* Script */
		      info->pfminfo.panose[0]==4 ? 0x51 :	/* Decorative */
		      0x51;					/* And pictorial doesn't fit into pfm */
    /* unicoderange[] */ getlong(ttf);
    /* unicoderange[] */ getlong(ttf);
    /* unicoderange[] */ getlong(ttf);
    /* unicoderange[] */ getlong(ttf);
    info->pfminfo.os2_vendor[0] = getc(ttf);
    info->pfminfo.os2_vendor[1] = getc(ttf);
    info->pfminfo.os2_vendor[2] = getc(ttf);
    info->pfminfo.os2_vendor[3] = getc(ttf);
    /* fsselection */ getushort(ttf);
    /* firstchar */ getushort(ttf);
    /* lastchar */ getushort(ttf);
    info->pfminfo.os2_typoascent = getushort(ttf);
    info->pfminfo.os2_typodescent = (short) getushort(ttf);
    if ( info->pfminfo.os2_typoascent-info->pfminfo.os2_typodescent == info->emsize ) {
	info->ascent = info->pfminfo.os2_typoascent;
	info->descent = -info->pfminfo.os2_typodescent;
    }
    if ( info->pfminfo.linegap==0 ) {
	info->pfminfo.linegap = getushort(ttf);
    } else
	/* typographic linegap = */ getushort(ttf);
    info->pfminfo.os2_winascent = getushort(ttf);
    info->pfminfo.os2_windescent = getushort(ttf);
    info->pfminfo.winascent_add = info->pfminfo.windescent_add = false;
    info->pfminfo.typoascent_add = info->pfminfo.typodescent_add = false;
    info->pfminfo.pfmset = true;
    info->pfminfo.panose_set = true;
    info->pfminfo.subsuper_set = true;
}

static int cmapEncFromName(struct ttfinfo *info,const char *nm, int glyphid) {
    int uni;
    int i;

    if ( info->map!=NULL )
	uni = EncFromName(nm,info->uni_interp,info->map->enc);
    else
	uni = EncFromName(nm,ui_none,&custom);
    if ( uni==-1 )
return( -1 );

    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) {
	if ( info->chars[i]->unicodeenc==uni ) {
	    if ( info->complainedmultname )
		/* Don't do it again */;
	    else if ( info->chars[i]->name!=NULL && strcmp(info->chars[i]->name,nm)==0 )
		LogError( _("Warning: Glyph %d has the same name (%s) as Glyph %d\n"),
			i, nm, glyphid );
	    else
		LogError( _("Warning: Glyph %d is named %s which should mean it is mapped to\n Unicode U+%04X, but Glyph %d already has that encoding.\n"),
			glyphid, nm, uni, i);
	    info->complainedmultname = true;
return( -1 );
	}
    }
return( uni );
}

static void readttfpostnames(FILE *ttf,struct ttfinfo *info) {
    int i,j;
    int format, len, gc, gcbig, val;
    const char *name;
    char buffer[30];
    uint16 *indexes;
    extern const char *ttfstandardnames[];
    int notdefwarned = false;

#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_change_line2(_("Reading Names"));
#endif

    /* Give ourselves an xuid, just in case they want to convert to PostScript*/
    /*  (even type42)							      */
    if ( xuid!=NULL && info->fd==NULL && info->xuid==NULL ) {
	info->xuid = galloc(strlen(xuid)+20);
	sprintf(info->xuid,"[%s %d]", xuid, (rand()&0xffffff));
    }

    if ( info->postscript_start!=0 ) {
	fseek(ttf,info->postscript_start,SEEK_SET);
	format = getlong(ttf);
	info->italicAngle = getfixed(ttf);
	info->upos = (short) getushort(ttf);
	info->uwidth = (short) getushort(ttf);
	/* fixedpitch = */ getlong(ttf);
	/* mem1 = */ getlong(ttf);
	/* mem2 = */ getlong(ttf);
	/* mem3 = */ getlong(ttf);
	/* mem4 = */ getlong(ttf);
	if ( format==0x00020000 ) {
	    gc = getushort(ttf);
	    indexes = gcalloc(65536,sizeof(uint16));
	    /* the index table is backwards from the way I want to use it */
	    gcbig = 0;
	    for ( i=0; i<gc; ++i ) {
		indexes[val = getushort(ttf)] = i;
		if ( val>=258 ) ++gcbig;
	    }

	    /* if we are only loading bitmaps, we can get holes in our data */
	    for ( i=0; i<258; ++i ) if ( indexes[i]!=0 || i==0 ) if ( indexes[i]<info->glyph_cnt && info->chars[indexes[i]]!=NULL ) {
		info->chars[indexes[i]]->name = copy(ttfstandardnames[i]);
		if ( info->chars[indexes[i]]->unicodeenc==-1 )
		    info->chars[indexes[i]]->unicodeenc = cmapEncFromName(info,ttfstandardnames[i],indexes[i]);
	    }
	    gcbig += 258;
	    for ( i=258; i<gcbig; ++i ) {
		char *nm;
		len = getc(ttf);
		if ( len<0 )		/* Don't crash on EOF */
	    break;
		nm = galloc(len+1);
		for ( j=0; j<len; ++j )
		    nm[j] = getc(ttf);
		nm[j] = '\0';
		if ( indexes[i]<info->glyph_cnt && info->chars[indexes[i]]!=NULL ) {
		    info->chars[indexes[i]]->name = nm;
		    if ( info->chars[indexes[i]]->unicodeenc==-1 )
			info->chars[indexes[i]]->unicodeenc = cmapEncFromName(info,nm,indexes[i]);
		}
	    }
	    free(indexes);
	}
    }

    if ( info->fd!=NULL && info->fd->chars!=NULL) {
	EncMap *map = NULL;
	struct pschars *chars = info->fd->chars;
	if ( info->map==NULL )
	    info->map = map = EncMapNew(65536,65536,FindOrMakeEncoding("UnicodeBmp"));
	/* In type42 fonts the names are stored in a postscript /CharStrings dictionary */
	for ( i=0; i<chars->next; ++i ) {
	    int gid = (intpt) (chars->values[i]);
	    if ( gid>=0 && gid<info->glyph_cnt && chars->keys[i]!=NULL ) {
		free(info->chars[gid]->name);
		info->chars[gid]->name = chars->keys[i];
		info->chars[gid]->unicodeenc = UniFromName(chars->keys[i],info->uni_interp,info->map->enc);
		if ( map!=NULL && info->chars[gid]->unicodeenc!=-1 &&
			info->chars[gid]->unicodeenc<map->enccount)
		    map->map[ info->chars[gid]->unicodeenc ] = gid;
		chars->keys[i] = NULL;
		chars->values[i] = NULL;
	    } else
		chars->values[i] = NULL;
	}
    }

    /* where no names are given, but we've got a unicode encoding use */
    /*  that to guess at them */
    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) {
	/* info->chars[i] can be null in some TTC files */
	if ( i!=0 && info->chars[i]->name!=NULL &&
		strcmp(info->chars[i]->name,".notdef")==0 ) {
	    /* for some reason MS puts out fonts where several characters */
	    /* are called .notdef (and only one is a real notdef). So if we */
	    /* find a glyph other than 0 called ".notdef" then pretend it had */
	    /* no name */
	    if ( !notdefwarned ) {
		notdefwarned = true;
		LogError( _("Glyph %d is called \".notdef\", a singularly inept choice of name (only glyph 0\n may be called .notdef)\nFontForge will rename it.\n"), i );
	    }
	    free(info->chars[i]->name);
	    info->chars[i]->name = NULL;
	}
	/* And some volt files actually assign nul strings to the name */
	if ( (info->chars[i]->name!=NULL && *info->chars[i]->name!='\0' ))
    continue;
	free(info->chars[i]->name);	/* If it's a null string get rid of it */
	if ( i==0 )
	    name = ".notdef";
	else if ( info->chars[i]->unicodeenc==-1 ) {
	    /* Do this later */;
	    name = NULL;
	} else
	    name = StdGlyphName(buffer,info->chars[i]->unicodeenc,info->uni_interp,NULL);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_progress_next();
#endif
	info->chars[i]->name = copy(name);
    }

    /* If we have a GSUB table we can give some unencoded glyphs names */
    /*  for example if we have a vrt2 substitution of A to <unencoded> */
    /*  we could name the unencoded "A.vrt2" (though in this case we might */
    /*  try A.vert instead */ /* Werner suggested this */
    /* We could try this from morx too, except that apple features don't */
    /*  use meaningful ids. That is A.15,3 isn't very readable */
    for ( i=info->glyph_cnt-1; i>=0 ; --i )
	if ( info->chars[i]!=NULL && info->chars[i]->name==NULL )
    break;
    if ( i>=0 && info->gsub_start!=0 )
	GuessNamesFromGSUB(ttf,info);

    for ( i=0; i<info->glyph_cnt; ++i ) {
	/* info->chars[i] can be null in some TTC files */
	if ( info->chars[i]==NULL )
    continue;
	if ( info->chars[i]->name!=NULL )
    continue;
	if ( info->ordering!=NULL )
	    sprintf(buffer, "%.20s-%d", info->ordering, i );
	else if ( info->map!=NULL && info->map->map[i]!=-1 )
	    sprintf(buffer, "nounicode-%d-%d-%x", info->platform, info->specific,
		    (int) info->map->map[i] );
	else
	    sprintf( buffer, "glyph%d", i );
	info->chars[i]->name = copy(buffer);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_progress_next();
#endif
    }
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_next_stage();
#endif
}

static void UnfigureControls(Spline *spline,BasePoint *pos) {
    pos->x = rint( (spline->splines[0].c+2*spline->splines[0].d)/2 );
    pos->y = rint( (spline->splines[1].c+2*spline->splines[1].d)/2 );
}

int ttfFindPointInSC(SplineChar *sc,int pnum,BasePoint *pos,
	RefChar *bound) {
    SplineSet *ss;
    SplinePoint *sp;
    int last=0, ret;
    RefChar *refs;

    for ( ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
	for ( sp=ss->first; ; ) {
	    if ( sp->ttfindex==pnum ) {
		*pos = sp->me;
return(-1);
	    } else if ( sp->nextcpindex==pnum ) {
		if ( sp->next!=NULL && sp->next->order2 )
		    *pos = sp->nextcp;
		else {
		    /* fix this up to be 2 degree bezier control point */
		    UnfigureControls(sp->next,pos);
		}
return( -1 );
	    }
	    if ( !sp->nonextcp && last<sp->nextcpindex )
		last = sp->nextcpindex;
	    else if ( sp->ttfindex!=0xffff )
		last = sp->ttfindex;
	    if ( sp->next==NULL )
	break;
	    sp = sp->next->to;
	    if ( sp==ss->first )
	break;
	}
    }
    for ( refs=sc->layers[ly_fore].refs; refs!=NULL; refs=refs->next ) {
	if ( refs==bound ) {
	    LogError( _("Invalid point match. Point would be after this reference.\n") );
return( 0x800000 );
	}
	ret = ttfFindPointInSC(refs->sc,pnum-last,pos,NULL);
	if ( ret==-1 ) {
	    BasePoint p;
	    p.x = refs->transform[0]*pos->x + refs->transform[2]*pos->y + refs->transform[4];
	    p.y = refs->transform[1]*pos->x + refs->transform[3]*pos->y + refs->transform[5];
	    *pos = p;
return( -1 );
	}
	last += ret;
    }
return( last );		/* Count of number of points in the character */
}

static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
    BasePoint sofar, inref;

    if ( ttfFindPointInSC(sc,rf->match_pt_base,&sofar,rf)!=-1 ||
	    ttfFindPointInSC(rf->sc,rf->match_pt_ref,&inref,NULL)!=-1 ) {
	LogError( _("Could not match points in composite glyph (%d to %d) when adding %s to %s\n"),
		rf->match_pt_base, rf->match_pt_ref, rf->sc->name, sc->name);
return;
    }
    rf->transform[4] = sofar.x-inref.x;
    rf->transform[5] = sofar.y-inref.y;
}

int ttfFixupRef(SplineChar **chars,int i) {
    RefChar *ref, *prev, *next;

    if ( chars[i]==NULL )		/* Can happen in ttc files */
return( false );
    if ( chars[i]->ticked )
return( false );
    chars[i]->ticked = true;
    prev = NULL;
    for ( ref=chars[i]->layers[ly_fore].refs; ref!=NULL; ref=next ) {
	if ( ref->sc!=NULL )
    break;				/* Already done */
	next = ref->next;
	if ( !ttfFixupRef(chars,ref->orig_pos)) {
	    if ( prev==NULL )
		chars[i]->layers[ly_fore].refs = next;
	    else
		prev->next = next;
	    chunkfree(ref,sizeof(RefChar));
	} else {
	    ref->sc = chars[ref->orig_pos];
	    ref->adobe_enc = getAdobeEnc(ref->sc->name);
	    if ( ref->point_match )
		ttfPointMatch(chars[i],ref);
	    SCReinstanciateRefChar(chars[i],ref);
	    SCMakeDependent(chars[i],ref->sc);
	    prev = ref;
	}
    }
    chars[i]->ticked = false;
return( true );
}

static void ttfFixupReferences(struct ttfinfo *info) {
    int i;

#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_change_line2(_("Fixing up References"));
#endif
    for ( i=0; i<info->glyph_cnt; ++i ) {
	ttfFixupRef(info->chars,i);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_progress_next();
#endif
    }
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_next_stage();
#endif
}

static void TtfCopyTableBlindly(struct ttfinfo *info,FILE *ttf,
	uint32 start,uint32 len,uint32 tag) {
    struct ttf_table *tab;

    if ( start==0 || len==0 )
return;
    if ( len>0x1000000 ) {
	LogError( _("Unlikely length for table, so I'm ignoring it. %u\n"), len );
return;
    }

    tab = chunkalloc(sizeof(struct ttf_table));
    tab->tag = tag;
    tab->len = len;
    tab->data = galloc(len);
    fseek(ttf,start,SEEK_SET);
    fread(tab->data,1,len,ttf);
    tab->next = info->tabs;
    info->tabs = tab;
}

static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
    char *oldloc;
    int i;

#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_change_stages(3);
#endif
    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
return( 0 );
    }
    oldloc = setlocale(LC_NUMERIC,"C");		/* TrueType doesn't need this but opentype dictionaries do */
    readttfpreglyph(ttf,info);
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
    gwwv_progress_change_total(info->glyph_cnt);
#endif

    /* If font only contains bitmaps, then only read bitmaps */
    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
	    info->cff_start==0 &&
	    info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
	info->onlystrikes = true;

    if ( !info->onlystrikes &&
	    info->glyphlocations_start!=0 && info->glyph_start!=0 &&
	    info->cff_start!=0 ) {
	char *buts[4];
	int choice;
	buts[0] = _("TTF 'glyf'");
	buts[1] = _("OTF 'CFF '");
	buts[3] = NULL;
#if defined(FONTFORGE_CONFIG_GDRAW)
	buts[2] = _("_Cancel");
	choice = gwwv_ask(_("Pick a font, any font..."),(const char **) buts,0,2,_("This font contains both a TrueType 'glyf' table and an OpenType 'CFF ' table. FontForge can only deal with one at a time, please pick which one you want to use"));
#elif defined(FONTFORGE_CONFIG_GTK)
	buts[2] = GTK_STOCK_CANCEL;
	choice = gwwv_ask(_("Pick a font, any font..."),(const char **) buts,0,2,_("This font contains both a TrueType 'glyf' table and an OpenType 'CFF ' table. FontForge can only deal with one at a time, please pick which one you want to use"));
#else
	choice = 0;
#endif
	if ( choice==2 ) {
	    setlocale(LC_NUMERIC,oldloc);
return( 0 );
	} else if ( choice==0 )
	    info->cff_start=0;
	else
	    info->glyph_start = info->glyphlocations_start = 0;
    }

    if ( info->onlystrikes ) {
	info->chars = gcalloc(info->glyph_cnt+1,sizeof(SplineChar *));
	info->to_order2 = new_fonts_are_order2;
    } else if ( info->glyphlocations_start!=0 && info->glyph_start!=0 ) {
	info->to_order2 = (!loaded_fonts_same_as_new ||
		(loaded_fonts_same_as_new && new_fonts_are_order2));
	/* If it's an apple mm font, then we don't want to change the order */
	/*  This messes up the point count */
	if ( info->gvar_start!=0 && info->fvar_start!=0 )
	    info->to_order2 = true;
	readttfglyphs(ttf,info);
    } else if ( info->cff_start!=0 ) {
	info->to_order2 = (loaded_fonts_same_as_new && new_fonts_are_order2);
	if ( !readcffglyphs(ttf,info) ) {
	    setlocale(LC_NUMERIC,oldloc);
return( 0 );
	}
    } else {
	setlocale(LC_NUMERIC,oldloc);
return( 0 );
    }
    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
    else if ( info->onlystrikes )
#if !defined(FONTFORGE_CONFIG_NO_WINDOWING_UI)
	gwwv_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
#else
	;
#endif
    if ( info->onlystrikes && info->bitmaps==NULL ) {
	free(info->chars);
	setlocale(LC_NUMERIC,oldloc);
return( 0 );
    }
    if ( info->hmetrics_start!=0 )
	readttfwidths(ttf,info);
    else if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
	dummywidthsfromstrike(ttf,info);
    if ( info->vmetrics_start!=0 && info->vhea_start!=0 )
	readttfvwidths(ttf,info);
    /* 'cmap' is not meaningful for cid keyed fonts, and not supplied for */
    /*  type42 fonts */
    if ( info->cidregistry==NULL && info->encoding_start!=0 )
	readttfencodings(ttf,info,false);
    if ( info->os2_start!=0 )
	readttfos2metrics(ttf,info);
    readttfpostnames(ttf,info);		/* If no postscript table we'll guess at names */
    if ( info->gdef_start!=0 )		/* ligature caret positioning info */
	readttfgdef(ttf,info);
    else {
	if ( info->prop_start!=0 )
	    readttfprop(ttf,info);
	if ( info->lcar_start!=0 )
	    readttflcar(ttf,info);
    }
    /* read the cvt table before reading variation data */
    if ( info->to_order2 ) {
	    /* Yes, even though we've looked at maxp already, let's make a blind */
	    /*  copy too for those fields we can't compute on our own */
	    /* Like size of twilight zone, etc. */
	TtfCopyTableBlindly(info,ttf,info->maxp_start,info->maxp_len,CHR('m','a','x','p'));
	TtfCopyTableBlindly(info,ttf,info->cvt_start,info->cvt_len,CHR('c','v','t',' '));
	TtfCopyTableBlindly(info,ttf,info->fpgm_start,info->fpgm_len,CHR('f','p','g','m'));
	TtfCopyTableBlindly(info,ttf,info->prep_start,info->prep_len,CHR('p','r','e','p'));
    }
    for ( i=0; i<info->savecnt; ++i ) if ( info->savetab[i].offset!=0 )
	TtfCopyTableBlindly(info,ttf,info->savetab[i].offset,info->savetab[i].len,info->savetab[i].tag);
    /* Do this before reading kerning info */
    if ( info->to_order2 && info->gvar_start!=0 && info->fvar_start!=0 )
	readttfvariations(info,ttf);
    if ( info->gpos_start!=0 )		/* kerning info may live in the gpos table too */
	readttfgpossub(ttf,info,true);
    else {
	if ( info->kern_start!=0 )
	    readttfkerns(ttf,info);
	if ( info->opbd_start!=0 )
	    readttfopbd(ttf,info);
    }
    if ( info->gsub_start!=0 )
	readttfgpossub(ttf,info,false);
    else {
	/* We will default the gsub table later... */;
	if ( info->morx_start!=0 || info->mort_start!=0 )
	    readttfmort(ttf,info);
    }
    if ( info->pfed_start!=0 )
	pfed_read(ttf,info);
    if ( info->tex_start!=0 )
	tex_read(ttf,info);
    setlocale(LC_NUMERIC,oldloc);
    if ( !info->onlystrikes && info->glyphlocations_start!=0 && info->glyph_start!=0 )
	ttfFixupReferences(info);
    /* Can't fix up any postscript references until we create a SplineFont */
    /*  so the check for cff is delayed. Generally there aren't any cff refs */
    /*  anyway */
return( true );
}

static void SymbolFixup(struct ttfinfo *info) {
    /* convert a two-byte symbol encoding (one using PUA) into expected */
    /*  one-byte encoding. */
    int i, max;
    EncMap *map = info->map;

    max = -1;
    for ( i=map->enccount-1; i>=0; --i ) {
	if ( map->map[i]==-1 )
    continue;
	if ( i>=0xf000 && i<=0xf0ff ) {
	    map->map[i-0xf000] = map->map[i];
	    map->map[i] = -1;
    continue;
	}
	if ( i>max ) max = i;
    }
    map->enccount = max;
}

void AltUniFigure(SplineFont *sf,EncMap *map) {
    int i,gid;

    if ( map->enc!=&custom ) {
	for ( i=0; i<map->enccount; ++i ) if ( (gid = map->map[i])!=-1 ) {
	    int uni = UniFromEnc(i,map->enc);
	    AltUniAdd(sf->glyphs[gid],uni);
	}
    }
}

static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
    int i;
    RefChar *rf, *prev, *next;

    sf->glyphs = info->chars;
    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
    for ( i=0; i<sf->glyphcnt; ++i )
	if ( sf->glyphs[i]!=NULL )
	    sf->glyphs[i]->parent = sf;

    /* A CFF font could contain type1 charstrings, or a type2 font could use */
    /*  the depreciated convention that endchar =~ seac */
    if ( info->cff_length!=0 )
	SFInstanciateRefs(sf);

    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
	for ( rf = sf->glyphs[i]->layers[ly_fore].refs, prev=NULL; rf!=NULL; rf = next ) {
	    next = rf->next;
	    if ( rf->sc==NULL ) {
		if ( prev==NULL ) sf->glyphs[i]->layers[ly_fore].refs = next;
		else prev->next = next;
		RefCharFree(rf);
	    } else {
		rf->orig_pos = rf->sc->orig_pos;
		rf->unicode_enc = rf->sc->unicodeenc;
		prev = rf;
	    }
	}
    }
    sf->map = info->map;
    sf->uni_interp = info->uni_interp;
    AltUniFigure(sf,sf->map);
}

static char *AxisNameConvert(uint32 tag) {
    char buffer[8];

    if ( tag==CHR('w','g','h','t'))
return( copy("Weight"));
    if ( tag==CHR('w','d','t','h'))
return( copy("Width"));
    if ( tag==CHR('o','p','s','z'))
return( copy("OpticalSize"));
    if ( tag==CHR('s','l','n','t'))
return( copy("Slant"));

    buffer[0] = tag>>24;
    buffer[1] = tag>>16;
    buffer[2] = tag>>8;
    buffer[3] = tag&0xff;
    buffer[4] = 0;
return( copy(buffer ));
}

static struct macname *FindMacName(struct ttfinfo *info, int strid) {
    struct macidname *sid;

    for ( sid=info->macstrids; sid!=NULL; sid=sid->next ) {
	if ( sid->id == strid )
return( sid->head );
    }
return( NULL );
}

static SplineFont *SFFromTuple(SplineFont *basesf,struct variations *v,int tuple,
	MMSet *mm, struct ttfinfo *info) {
    SplineFont *sf;
    int i;
    RefChar *r;

    sf = SplineFontEmpty();
    sf->display_size = basesf->display_size;
    sf->display_antialias = basesf->display_antialias;

    sf->fontname = MMMakeMasterFontname(mm,tuple,&sf->fullname);
    sf->familyname = copy(basesf->familyname);
    sf->weight = copy("All");
    sf->italicangle = basesf->italicangle;
    sf->strokewidth = basesf->strokewidth;
    sf->strokedfont = basesf->strokedfont;
    sf->upos = basesf->upos;
    sf->uwidth = basesf->uwidth;
    sf->ascent = basesf->ascent;
    sf->vertical_origin = basesf->vertical_origin;
    sf->hasvmetrics = basesf->hasvmetrics;
    sf->descent = basesf->descent;
    sf->kerns = v->tuples[tuple].khead;
    sf->vkerns = v->tuples[tuple].vkhead;
    sf->map = basesf->map;
    sf->mm = mm;
    sf->glyphmax = sf->glyphcnt = basesf->glyphcnt;
    sf->glyphs = v->tuples[tuple].chars;
    sf->order2 = true;
    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
	sf->glyphs[i]->orig_pos = i;
	sf->glyphs[i]->parent = sf;
    }
    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
	for ( r=sf->glyphs[i]->layers[ly_fore].refs; r!=NULL; r=r->next )
	    SCReinstanciateRefChar(sf->glyphs[i],r);
    }

    sf->ttf_tables = v->tuples[tuple].cvt;

    v->tuples[tuple].chars = NULL;
    v->tuples[tuple].khead = NULL;
    v->tuples[tuple].vkhead = NULL;
    v->tuples[tuple].cvt = NULL;
return( sf );
}

static void MMFillFromVAR(SplineFont *sf, struct ttfinfo *info) {
    MMSet *mm = chunkalloc(sizeof(MMSet));
    struct variations *v = info->variations;
    int i,j;

    sf->mm = mm;
    mm->normal = sf;
    mm->apple = true;
    mm->axis_count = v->axis_count;
    mm->instance_count = v->tuple_count;
    mm->instances = galloc(v->tuple_count*sizeof(SplineFont *));
    mm->positions = galloc(v->tuple_count*v->axis_count*sizeof(real));
    for ( i=0; i<v->tuple_count; ++i ) for ( j=0; j<v->axis_count; ++j )
	mm->positions[i*v->axis_count+j] = v->tuples[i].coords[j];
    mm->defweights = gcalloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
    mm->axismaps = gcalloc(v->axis_count,sizeof(struct axismap));
    for ( i=0; i<v->axis_count; ++i ) {
	mm->axes[i] = AxisNameConvert(v->axes[i].tag);
	mm->axismaps[i].min = v->axes[i].min;
	mm->axismaps[i].def = v->axes[i].def;
	mm->axismaps[i].max = v->axes[i].max;
	if ( v->axes[i].paircount==0 ) {
	    mm->axismaps[i].points = 3;
	    mm->axismaps[i].blends = galloc(3*sizeof(real));
	    mm->axismaps[i].designs = galloc(3*sizeof(real));
	    mm->axismaps[i].blends[0] = -1; mm->axismaps[i].designs[0] = mm->axismaps[i].min;
	    mm->axismaps[i].blends[1] =  0; mm->axismaps[i].designs[1] = mm->axismaps[i].def;
	    mm->axismaps[i].blends[2] =  1; mm->axismaps[i].designs[2] = mm->axismaps[i].max;
	} else {
	    mm->axismaps[i].points = v->axes[i].paircount;
	    mm->axismaps[i].blends = galloc(v->axes[i].paircount*sizeof(real));
	    mm->axismaps[i].designs = galloc(v->axes[i].paircount*sizeof(real));
	    for ( j=0; j<v->axes[i].paircount; ++j ) {
		if ( v->axes[i].mapfrom[j]<=0 ) {
		    mm->axismaps[i].designs[j] = mm->axismaps[i].def +
			    v->axes[i].mapfrom[j]*(mm->axismaps[i].def-mm->axismaps[i].min);
		} else {
		    mm->axismaps[i].designs[j] = mm->axismaps[i].def +
			    v->axes[i].mapfrom[j]*(mm->axismaps[i].max-mm->axismaps[i].def);
		}
		mm->axismaps[i].blends[j] = v->axes[i].mapto[j];
	    }
	}
	mm->axismaps[i].axisnames = MacNameCopy(FindMacName(info, v->axes[i].nameid));
    }
    mm->named_instance_count = v->instance_count;
    mm->named_instances = galloc(v->instance_count*sizeof(struct named_instance));
    for ( i=0; i<v->instance_count; ++i ) {
	mm->named_instances[i].coords = v->instances[i].coords;
	v->instances[i].coords = NULL;
	mm->named_instances[i].names = MacNameCopy(FindMacName(info, v->instances[i].nameid));
    }
    for ( i=0; i<mm->instance_count; ++i )
	mm->instances[i] = SFFromTuple(sf,v,i,mm,info);
    VariationFree(info);
}

static void SFRelativeWinAsDs(SplineFont *sf) {
    if ( !sf->pfminfo.winascent_add || !sf->pfminfo.windescent_add ||
	    !sf->pfminfo.hheadascent_add || !sf->pfminfo.hheaddescent_add ) {
	DBounds b;
	CIDFindBounds(sf,&b);
	if ( !sf->pfminfo.winascent_add ) {
	    sf->pfminfo.winascent_add = true;
	    if ( sf->pfminfo.os2_winascent < sf->ascent/8 ) {
		/* There was a bug for a while which gave us really bad values */
		sf->pfminfo.os2_winascent = 0;
		sf->pfminfo.windescent_add = true;
		sf->pfminfo.os2_windescent = 0;
	    } else
		sf->pfminfo.os2_winascent -= b.maxy;
	}
	if ( !sf->pfminfo.windescent_add ) {
	    sf->pfminfo.windescent_add = true;
	    sf->pfminfo.os2_windescent += b.miny;
	}
	if ( !sf->pfminfo.hheadascent_add ) {
	    sf->pfminfo.hheadascent_add = true;
	    sf->pfminfo.hhead_ascent -= b.maxy;
	}
	if ( !sf->pfminfo.hheaddescent_add ) {
	    sf->pfminfo.hheaddescent_add = true;
	    sf->pfminfo.hhead_descent -= b.miny;
	}
    }
    if ( !sf->pfminfo.typoascent_add ) {
	sf->pfminfo.typoascent_add = true;
	sf->pfminfo.os2_typoascent -= sf->ascent;
    }
    if ( !sf->pfminfo.typodescent_add  ) {
	sf->pfminfo.typodescent_add = true;
	sf->pfminfo.os2_typodescent -= -sf->descent;
    }
}

static void PsuedoEncodeUnencoded(EncMap *map,struct ttfinfo *info) {
    int extras, base;
    int i;

    for ( i=0; i<info->glyph_cnt; ++i )
	if ( info->chars[i]!=NULL )
	    info->chars[i]->ticked = false;
    for ( i=0; i<map->enccount; ++i )
	if ( map->map[i]!=-1 )
	    info->chars[map->map[i]]->ticked = true;
    extras = 0;
    for ( i=0; i<info->glyph_cnt; ++i )
	if ( info->chars[i]!=NULL && !info->chars[i]->ticked )
	    ++extras;
    if ( extras!=0 ) {
	if ( map->enccount<=256 )
	    base = 256;
	else if ( map->enccount<=65536 )
	    base = 65536;
	else if ( map->enccount<=17*65536 )
	    base = 17*65536;
	else
	    base = map->enccount;
	if ( base+extras>map->encmax ) {
	    map->map = grealloc(map->map,(base+extras)*sizeof(int));
	    memset(map->map+map->enccount,-1,(base+extras-map->enccount)*sizeof(int));
	    map->encmax = base+extras;
	}
	map->enccount = base+extras;
	extras = 0;
	for ( i=0; i<info->glyph_cnt; ++i )
	    if ( info->chars[i]!=NULL && !info->chars[i]->ticked )
		map->map[base+extras++] = i;
    }
}

static void MapDoBack(EncMap *map,struct ttfinfo *info) {
    int i;

    if ( map==NULL )		/* CID fonts */
return;
    free(map->backmap);		/* CFF files have this */
    map->backmax = info->glyph_cnt;
    map->backmap = galloc(info->glyph_cnt*sizeof(int));
    memset(map->backmap,-1,info->glyph_cnt*sizeof(int));
    for ( i = map->enccount-1; i>=0; --i )
	if ( map->map[i]>=0 && map->map[i]<info->glyph_cnt )
	    if ( map->backmap[map->map[i]]==-1 )
		map->backmap[map->map[i]] = i;
}

static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
    SplineFont *sf, *_sf;
    int i,k;
    BDFFont *bdf;
    struct table_ordering *ord;
    SplineChar *sc;
    struct ttf_table *last[2], *tab, *next;

    
    sf = SplineFontEmpty();
    sf->display_size = -default_fv_font_size;
    sf->display_antialias = default_fv_antialias;
    sf->fontname = info->fontname;
    sf->fullname = info->fullname;
    sf->familyname = info->familyname;
    sf->chosenname = info->chosenname;
    sf->onlybitmaps = info->onlystrikes;
    sf->order2 = info->to_order2;
    sf->comments = info->fontcomments;

    sf->creationtime = info->creationtime;
    sf->modificationtime = info->modificationtime;

    sf->design_size = info->design_size;
    sf->design_range_bottom = info->design_range_bottom;
    sf->design_range_top = info->design_range_top;
    sf->fontstyle_id = info->fontstyle_id;
    sf->fontstyle_name = info->fontstyle_name;

    sf->texdata = info->texdata;

    sf->mark_class_cnt = info->mark_class_cnt;
    sf->mark_classes = info->mark_classes;
    sf->mark_class_names = info->mark_class_names;

    if ( info->fd!=NULL ) {		/* Special hack for type42 fonts */
	sf->fontname = copy(info->fd->fontname);
	sf->uniqueid = info->fd->uniqueid;
	sf->xuid = XUIDFromFD(info->fd->xuid);
	if ( info->fd->fontinfo!=NULL ) {
	    sf->familyname = utf8_verify_copy(info->fd->fontinfo->familyname);
	    sf->fullname = utf8_verify_copy(info->fd->fontinfo->fullname);
	    sf->copyright = utf8_verify_copy(info->fd->fontinfo->notice);
	    sf->weight = utf8_verify_copy(info->fd->fontinfo->weight);
	    sf->version = utf8_verify_copy(info->fd->fontinfo->version);
	    sf->italicangle = info->fd->fontinfo->italicangle;
	    sf->upos = info->fd->fontinfo->underlineposition*(sf->ascent+sf->descent);
	    sf->uwidth = info->fd->fontinfo->underlinethickness*(sf->ascent+sf->descent);
	}
    }

    if ( sf->fontname==NULL ) {
	sf->fontname = EnforcePostScriptName(sf->fullname);
	if ( sf->fontname==NULL )
	    sf->fontname = EnforcePostScriptName(sf->familyname);
	if ( sf->fontname==NULL ) sf->fontname = EnforcePostScriptName("UntitledTTF");
    }
    if ( sf->fullname==NULL ) sf->fullname = copy( sf->fontname );
    if ( sf->familyname==NULL ) sf->familyname = copy( sf->fontname );
    if ( sf->weight==NULL ) {
	if ( info->weight != NULL )
	    sf->weight = info->weight;
	else if ( info->pfminfo.pfmset )
	    sf->weight = copy( info->pfminfo.weight <= 100 ? "Thin" :
				info->pfminfo.weight <= 200 ? "Extra-Light" :
				info->pfminfo.weight <= 300 ? "Light" :
				info->pfminfo.weight <= 400 ? "Book" :
				info->pfminfo.weight <= 500 ? "Medium" :
				info->pfminfo.weight <= 600 ? "Demi" :
				info->pfminfo.weight <= 700 ? "Bold" :
				info->pfminfo.weight <= 800 ? "Heavy" :
				    "Black" );
	else
	    sf->weight = copy("");
    } else
	free( info->weight );
    if ( sf->copyright==NULL )
	sf->copyright = info->copyright;
    else
	free( info->copyright );
    sf->version = info->version;
    sf->italicangle = info->italicAngle;
    sf->strokewidth = info->strokewidth;
    sf->strokedfont = info->strokedfont;
    sf->upos = info->upos;
    sf->uwidth = info->uwidth;
    sf->ascent = info->ascent;
    sf->vertical_origin = info->vertical_origin;
    if ( info->vhea_start!=0 && info->vmetrics_start!=0 )
	sf->hasvmetrics = true;
    sf->descent = info->descent;
    sf->private = info->private;
    sf->xuid = info->xuid;
    sf->uniqueid = info->uniqueid;
    sf->pfminfo = info->pfminfo;
    sf->names = info->names;
    sf->anchor = info->ahead;
    sf->kerns = info->khead;
    sf->vkerns = info->vkhead;
    sf->possub = info->possub;
    sf->sm = info->sm;
    sf->features = info->features;
    sf->gentags = info->gentags;
    sf->script_lang = info->script_lang;
    sf->sli_cnt = info->sli_cnt;

    last[0] = sf->ttf_tables;
    last[1] = NULL;
    for ( tab=info->tabs; tab!=NULL; tab = next ) {
	next = tab->next;
	if ( tab->tag==CHR('f','p','g','m') || tab->tag==CHR('p','r','e','p') ||
		tab->tag==CHR('c','v','t',' ') || tab->tag==CHR('m','a','x','p')) {
	    if ( last[0]==NULL )
		sf->ttf_tables = tab;
	    else
		last[0]->next = tab;
	    last[0] = tab;
	} else {
	    if ( last[1]==NULL )
		sf->ttf_tab_saved = tab;
	    else
		last[1]->next = tab;
	    last[1] = tab;
	}
	tab->next = NULL;
    }

    if ( info->twobytesymbol )
	/* rework ms symbol encodings */
	SymbolFixup(info);
    if ( info->map==NULL && info->subfonts==NULL )		/* Can happen when reading a ttf from a pdf */
	info->map = EncMapFromEncoding(sf,FindOrMakeEncoding("original"));
    if ( info->subfontcnt==0 )
	PsuedoEncodeUnencoded(info->map,info);
    MapDoBack(info->map,info);
    sf->map = info->map;
    sf->cidregistry = info->cidregistry;
    sf->ordering = info->ordering;
    sf->supplement = info->supplement;
    sf->cidversion = info->cidfontversion;
    sf->bitmaps = info->bitmaps;
    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
	bdf->sf = sf;
    }
    SFDefaultAscent(sf);

    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) {
	SCOrderAP(info->chars[i]);
    }

    if ( info->subfontcnt == 0 ) {
	UseGivenEncoding(sf,info);
    } else {
	sf->subfontcnt = info->subfontcnt;
	sf->subfonts = info->subfonts;
	free(info->chars);		/* This is the GID->char index, don't need it now */
	for ( i=0; i<sf->subfontcnt; ++i ) {
	    sf->subfonts[i]->cidmaster = sf;
	    sf->subfonts[i]->vertical_origin = sf->vertical_origin;
	    sf->subfonts[i]->hasvmetrics = sf->hasvmetrics;
	}
    }
    TTF_PSDupsDefault(sf);
    if ( info->gsub_start==0 && info->mort_start==0 && info->morx_start==0 ) {
	/* Get default ligature values, etc. */
	k=0;
	do {
	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
	    for ( i=0; i<sf->glyphcnt; ++i ) {
		if ( _sf->glyphs[i]!=NULL )		/* Might be null in ttc files */
		    SCLigDefault(_sf->glyphs[i]);
	    }
	    ++k;
	} while ( k<sf->subfontcnt );
    }

    if ( info->feats[0]!=NULL ) {
	ord = chunkalloc(sizeof(struct table_ordering));
	ord->table_tag = CHR('G','S','U','B');		/* or mort/morx */
	ord->ordered_features = info->feats[0];
	sf->orders = ord;
    }
    if ( info->feats[1]!=NULL ) {
	ord = chunkalloc(sizeof(struct table_ordering));
	ord->table_tag = CHR('G','P','O','S');
	ord->ordered_features = info->feats[1];
	ord->next = sf->orders;
	sf->orders = ord;
    }

    otf_orderlangs(NULL,sf);		/* I thought these had to be ordered, but it seems I was wrong. But I depend on the order, so I enforce it here */

    if ( info->variations!=NULL )
	MMFillFromVAR(sf,info);

    if ( info->cff_length!=0 && !sf->order2 ) {
	/* Clean up the hint masks, We create an initial hintmask whether we */
	/*  need it or not */
	k=0;
	do {
	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
	    for ( i=0; i<sf->glyphcnt; ++i ) {
		if ( (sc = _sf->glyphs[i])!=NULL && !sc->hconflicts && !sc->vconflicts &&
			sc->layers[ly_fore].splines!=NULL ) {
		    chunkfree( sc->layers[ly_fore].splines->first->hintmask,sizeof(HintMask) );
		    sc->layers[ly_fore].splines->first->hintmask = NULL;
		}
	    }
	    ++k;
	} while ( k<sf->subfontcnt );
    }
    SFRelativeWinAsDs(sf);
    free(info->savetab);
return( sf );
}

SplineFont *_SFReadTTF(FILE *ttf, int flags,char *filename,struct fontdict *fd) {
    struct ttfinfo info;
    int ret;

    memset(&info,'\0',sizeof(struct ttfinfo));
    info.onlystrikes = (flags&ttf_onlystrikes)?1:0;
    info.onlyonestrike = (flags&ttf_onlyonestrike)?1:0;
    info.fd = fd;
    ret = readttf(ttf,&info,filename);
    if ( !ret )
return( NULL );
return( SFFillFromTTF(&info));
}

SplineFont *SFReadTTF(char *filename, int flags) {
    FILE *ttf;
    SplineFont *sf;
    char *temp=filename, *pt, *lparen;

    pt = strrchr(filename,'/');
    if ( pt==NULL ) pt = filename;
    if ( (lparen=strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) {
	temp = copy(filename);
	pt = temp + (lparen-filename);
	*pt = '\0';
    }
    ttf = fopen(temp,"rb");
    if ( temp!=filename ) free(temp);
    if ( ttf==NULL )
return( NULL );

    sf = _SFReadTTF(ttf,flags,filename,NULL);
    fclose(ttf);
return( sf );
}

SplineFont *_CFFParse(FILE *temp,int len, char *fontsetname) {
    struct ttfinfo info;

    memset(&info,'\0',sizeof(info));
    info.cff_start = 0;
    info.cff_length = len;
    info.barecff = true;
    if ( !readcffglyphs(temp,&info) )
return( NULL );
return( SFFillFromTTF(&info));
}

SplineFont *CFFParse(char *filename) {
    FILE *cff = fopen(filename,"r");
    SplineFont *sf;
    long len;

    if ( cff == NULL )
return( NULL );
    fseek(cff,0,SEEK_END);
    len = ftell(cff);
    fseek(cff,0,SEEK_SET);
    sf = _CFFParse(cff,len,NULL);
    fclose(cff);
return( sf );
}

char **NamesReadCFF(char *filename) {
    FILE *cff = fopen(filename,"rb");
    int32 hdrsize, offsize;
    char **fontnames;

    if ( cff==NULL )
return( NULL );
    if ( getc(cff)!='\1' ) {		/* Major version */
	LogError( _("CFF version mismatch\n") );
	fclose(cff);
return( NULL );
    }
    getc(cff);				/* Minor version */
    hdrsize = getc(cff);
    offsize = getc(cff);
    if ( hdrsize!=4 )
	fseek(cff,hdrsize,SEEK_SET);
    fontnames = readcfffontnames(cff,NULL);
    fclose(cff);
return( fontnames );
}

char **NamesReadTTF(char *filename) {
    FILE *ttf = fopen(filename,"rb");
    int32 version, cnt, *offsets;
    int i,j;
    char **ret = NULL;
    char *temp;

    if ( ttf==NULL )
return( NULL );
    version=getlong(ttf);
    if ( version==CHR('t','t','c','f')) {
	/* TTCF version = */ getlong(ttf);
	cnt = getlong(ttf);
	offsets = galloc(cnt*sizeof(int32));
	for ( i=0; i<cnt; ++i )
	    offsets[i] = getlong(ttf);
	ret = galloc((cnt+1)*sizeof(char *));
	for ( i=j=0; i<cnt; ++i ) {
	    temp = TTFGetFontName(ttf,offsets[i],0);
	    if ( temp!=NULL )
		ret[j++] = temp;
	}
	ret[j] = NULL;
	free(offsets);
    } else {
	temp = TTFGetFontName(ttf,0,0);
	if ( temp!=NULL ) {
	    ret = galloc(2*sizeof(char *));
	    ret[0] = temp;
	    ret[1] = NULL;
	}
    }
    fclose(ttf);
return(ret);
}
