[Pkg-fonts-bugs] Ubuntu (new upstream) fontforge 1:20161005~dfsg-4+deb9u1ubuntu1

Ubuntu Merge-o-Matic mom at ubuntu.com
Thu Sep 21 06:52:04 UTC 2017


This e-mail has been sent due to an upload to Ubuntu of a new upstream
version which still contains Ubuntu changes.  It contains the difference
between the Ubuntu version and the equivalent base version in Debian, note
that this difference may include the upstream changes.
-------------- next part --------------
Format: 1.8
Date: Wed, 20 Sep 2017 21:55:44 -0500
Source: fontforge
Binary: fontforge fontforge-nox fontforge-common libfontforge-dev libfontforge2 libgdraw5 python-fontforge fontforge-dbg
Architecture: source
Version: 1:20161005~dfsg-4+deb9u1ubuntu1
Distribution: artful
Urgency: high
Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
Changed-By: Simon Quigley <tsimonq2 at ubuntu.com>
Description: 
 fontforge  - font editor
 fontforge-common - font editor (common files)
 fontforge-dbg - debugging symbols for fontforge
 fontforge-nox - font editor - non-X version
 libfontforge-dev - font editor - runtime library (development files)
 libfontforge2 - font editor - runtime library
 libgdraw5  - font editor - runtime graphics and widget library
 python-fontforge - font editor - Python bindings
Changes: 
 fontforge (1:20161005~dfsg-4+deb9u1ubuntu1) artful; urgency=high
 .
   * Merge from Debian Stable. Remaining changes:
     - d/rules: Make sure [cairo/pango]-dev are added to runtime dependencies,
       to avoid build failures on pdf2htmlex.
       (as-needed strips libraries, and dh fails to add the corresponding -dev
       packages)
 .
 fontforge (1:20161005~dfsg-4+deb9u1) stretch-security; urgency=high
 .
   * Import upstream patches fixing following CVE's
     CVE-2017-11577, CVE-2017-11576, CVE-2017-11575, CVE-2017-11574,
     CVE-2017-11572, CVE-2017-11571, CVE-2017-11569, CVE-2017-11568.
Checksums-Sha1: 
 a24192754c7a92e9d24836275d55724a51d46121 3221 fontforge_20161005~dfsg-4+deb9u1ubuntu1.dsc
 01808bbf98140d74020fa1d609536bac5e44beb5 122144 fontforge_20161005~dfsg-4+deb9u1ubuntu1.debian.tar.xz
Checksums-Sha256: 
 12067670b544b51ce073125328cea58975155d3f2ea51615fd101956bbf185de 3221 fontforge_20161005~dfsg-4+deb9u1ubuntu1.dsc
 9cd2dbbd2e0855b126b64886ce958cd4ba91a04f95d04cae3f9be710a584e94f 122144 fontforge_20161005~dfsg-4+deb9u1ubuntu1.debian.tar.xz
Files: 
 9b1da52cbd10de7b926a0d40c31d7e43 3221 fonts optional fontforge_20161005~dfsg-4+deb9u1ubuntu1.dsc
 1d703457ae93db86732f38737ad7a7a4 122144 fonts optional fontforge_20161005~dfsg-4+deb9u1ubuntu1.debian.tar.xz
Original-Maintainer: Debian Fonts Task Force <pkg-fonts-devel at lists.alioth.debian.org>
-------------- next part --------------
diff -pruN 1:20161005~dfsg-4/debian/changelog 1:20161005~dfsg-4+deb9u1ubuntu1/debian/changelog
--- 1:20161005~dfsg-4/debian/changelog	2016-11-12 15:07:51.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/changelog	2017-09-21 02:55:44.000000000 +0000
@@ -1,3 +1,36 @@
+fontforge (1:20161005~dfsg-4+deb9u1ubuntu1) artful; urgency=high
+
+  * Merge from Debian Stable. Remaining changes:
+    - d/rules: Make sure [cairo/pango]-dev are added to runtime dependencies,
+      to avoid build failures on pdf2htmlex.
+      (as-needed strips libraries, and dh fails to add the corresponding -dev
+      packages)
+
+ -- Simon Quigley <tsimonq2 at ubuntu.com>  Wed, 20 Sep 2017 21:55:44 -0500
+
+fontforge (1:20161005~dfsg-4+deb9u1) stretch-security; urgency=high
+
+  * Import upstream patches fixing following CVE's
+    CVE-2017-11577, CVE-2017-11576, CVE-2017-11575, CVE-2017-11574,
+    CVE-2017-11572, CVE-2017-11571, CVE-2017-11569, CVE-2017-11568.
+
+ -- Vasudev Kamath <vasudev at copyninja.info>  Sat, 12 Aug 2017 09:51:12 +0530
+
+fontforge (1:20161005~dfsg-4ubuntu2) zesty; urgency=medium
+
+  * d/rules: fix syntax.
+
+ -- Mattia Rizzolo <mapreri at ubuntu.com>  Fri, 06 Jan 2017 17:44:40 +0100
+
+fontforge (1:20161005~dfsg-4ubuntu1) zesty; urgency=medium
+
+  * d/rules: Make sure [cairo/pango]-dev are added to runtime dependencies,
+    to avoid build failures on pdf2htmlex.
+    (as-needed strips libraries, and dh fails to add the corresponding -dev
+    packages)
+
+ -- Gianfranco Costamagna <locutusofborg at debian.org>  Fri, 06 Jan 2017 13:23:29 +0100
+
 fontforge (1:20161005~dfsg-4) unstable; urgency=medium
 
   * Upload to unstable.
diff -pruN 1:20161005~dfsg-4/debian/control 1:20161005~dfsg-4+deb9u1ubuntu1/debian/control
--- 1:20161005~dfsg-4/debian/control	2016-11-12 12:52:15.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/control	2017-09-21 02:53:41.000000000 +0000
@@ -1,7 +1,8 @@
 Source: fontforge
 Section: fonts
 Priority: optional
-Maintainer: Debian Fonts Task Force <pkg-fonts-devel at lists.alioth.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian Fonts Task Force <pkg-fonts-devel at lists.alioth.debian.org>
 Uploaders:
  Christian Perrier <bubulle at debian.org>,
  Daniel Kahn Gillmor <dkg at fifthhorseman.net>,
diff -pruN 1:20161005~dfsg-4/debian/control.in 1:20161005~dfsg-4+deb9u1ubuntu1/debian/control.in
--- 1:20161005~dfsg-4/debian/control.in	2016-11-12 12:52:15.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/control.in	2017-09-21 02:53:41.000000000 +0000
@@ -1,7 +1,8 @@
 Source: fontforge
 Section: fonts
 Priority: optional
-Maintainer: Debian Fonts Task Force <pkg-fonts-devel at lists.alioth.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian Fonts Task Force <pkg-fonts-devel at lists.alioth.debian.org>
 Uploaders:
  Christian Perrier <bubulle at debian.org>,
  Daniel Kahn Gillmor <dkg at fifthhorseman.net>,
diff -pruN 1:20161005~dfsg-4/debian/patches/0001-Fix-out-of-bounds-read-in-getsid.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0001-Fix-out-of-bounds-read-in-getsid.patch
--- 1:20161005~dfsg-4/debian/patches/0001-Fix-out-of-bounds-read-in-getsid.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0001-Fix-out-of-bounds-read-in-getsid.patch	2017-08-12 04:03:16.000000000 +0000
@@ -0,0 +1,54 @@
+From 3245d354865def9d712bdffe61fa211ad6aa4081 Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 09:17:40 +0800
+Subject: [PATCH 1/6] Fix out of bounds read in getsid
+
+Closes #3088
+---
+ fontforge/parsettf.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index da33a492c..341088154 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -3333,8 +3333,14 @@ return( dicts );
+ }
+ 
+ static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+-    if ( sid==-1 )
++    if ( sid==-1 ) // Default value, indicating it's not present
+ return( NULL );
++    else if (sid < 0) {
++        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
++        if (info != NULL)
++            info->bad_cff = true;
++        return NULL;
++    }
+     else if ( sid<nStdStrings )
+ return( cffnames[sid] );
+     else if ( sid-nStdStrings>scnt ) {
+@@ -5938,17 +5944,17 @@ void TTF_PSDupsDefault(SplineFont *sf) {
+     for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+     if ( english==NULL )
+ return;
+-    if ( english->names[ttf_family]!=NULL &&
++    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+ 	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+ 	free(english->names[ttf_family]);
+ 	english->names[ttf_family]=NULL;
+     }
+-    if ( english->names[ttf_copyright]!=NULL &&
++    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+ 	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+ 	free(english->names[ttf_copyright]);
+ 	english->names[ttf_copyright]=NULL;
+     }
+-    if ( english->names[ttf_fullname]!=NULL &&
++    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+ 	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+ 	free(english->names[ttf_fullname]);
+ 	english->names[ttf_fullname]=NULL;
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/0002-readcfftopdict-Prevent-stack-underflow-condition.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0002-readcfftopdict-Prevent-stack-underflow-condition.patch
--- 1:20161005~dfsg-4/debian/patches/0002-readcfftopdict-Prevent-stack-underflow-condition.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0002-readcfftopdict-Prevent-stack-underflow-condition.patch	2017-08-12 04:03:16.000000000 +0000
@@ -0,0 +1,28 @@
+From df349365630344ef3004a3c7934c7e7496692fb1 Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 09:38:56 +0800
+Subject: [PATCH 2/6] readcfftopdict: Prevent stack underflow condition
+
+Closes #3091
+---
+ fontforge/parsettf.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index 341088154..982f86a51 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -3116,6 +3116,10 @@ static struct topdicts *readcfftopdict(FILE *ttf, char *fontname, int len,
+ 	  case (12<<8)+24:
+ 	    LogError( _("FontForge does not support type2 multiple master fonts\n") );
+ 	    info->bad_cff = true;
++	    if (sp < 4) {
++	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
++	        break;
++	    }
+ 	    td->nMasters = stack[0];
+ 	    td->nAxes = sp-4;
+ 	    memcpy(td->weightvector,stack+1,(sp-4)*sizeof(real));
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch
--- 1:20161005~dfsg-4/debian/patches/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch	2017-08-12 04:03:16.000000000 +0000
@@ -0,0 +1,64 @@
+From 4de0c58a01e5e30610c200e9aea98bc7db12c7ac Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 10:20:48 +0800
+Subject: [PATCH 3/6] Fix out of bounds read condition and buffer overflow
+ condition
+
+* parsettf.c: Reading past the end of the fontnames array
+* psread.c: Reading more data than is available in type1
+* tottf.c: Use snprintf instead of sprintf
+
+Closes #3096
+---
+ fontforge/parsettf.c | 4 ++++
+ fontforge/psread.c   | 5 +++++
+ fontforge/tottf.c    | 2 +-
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index 982f86a51..90ac6eb62 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -3324,6 +3324,10 @@ return( NULL );
+ 	offsets[i] = getoffset(ttf,offsize);
+     dicts = malloc((count+1)*sizeof(struct topdicts *));
+     for ( i=0; i<count; ++i ) {
++	if (fontnames != NULL && fontnames[i] == NULL) {
++		LogError(_("Number of CFF font names is less than dict size: %d < %d"), i, count);
++		break;
++	}
+ 	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
+ 		offsets[i+1]-offsets[i], info);
+ 	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+diff --git a/fontforge/psread.c b/fontforge/psread.c
+index 9e19f25b2..4e0aa2130 100644
+--- a/fontforge/psread.c
++++ b/fontforge/psread.c
+@@ -3665,6 +3665,11 @@ SplineChar *PSCharStringToSplines(uint8 *type1, int len, struct pscontext *conte
+ 		stack[sp++] = -(v-251)*256 - *type1++ - 108;
+ 		--len;
+ 	    } else {
++		if (len < 4) {
++			LogError(_("Not enough data: %d < 4"), len);
++			len = 0;
++			break;
++		}
+ 		int val = (*type1<<24) | (type1[1]<<16) | (type1[2]<<8) | type1[3];
+ 		stack[sp++] = val;
+ 		type1 += 4;
+diff --git a/fontforge/tottf.c b/fontforge/tottf.c
+index a832a79ef..5f0ecaaad 100644
+--- a/fontforge/tottf.c
++++ b/fontforge/tottf.c
+@@ -3826,7 +3826,7 @@ void DefaultTTFEnglishNames(struct ttflangname *dummy, SplineFont *sf) {
+     if ( dummy->names[ttf_uniqueid]==NULL || *dummy->names[ttf_uniqueid]=='\0' ) {
+ 	time(&now);
+ 	tm = localtime(&now);
+-	sprintf( buffer, "%s : %s : %d-%d-%d",
++	snprintf( buffer, sizeof(buffer), "%s : %s : %d-%d-%d",
+ 		BDFFoundry?BDFFoundry:TTFFoundry?TTFFoundry:"FontForge 2.0",
+ 		sf->fullname!=NULL?sf->fullname:sf->fontname,
+ 		tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900 );
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/0004-parsettf.c-Fix-buffer-overrun-condition.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0004-parsettf.c-Fix-buffer-overrun-condition.patch
--- 1:20161005~dfsg-4/debian/patches/0004-parsettf.c-Fix-buffer-overrun-condition.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0004-parsettf.c-Fix-buffer-overrun-condition.patch	2017-08-12 04:03:16.000000000 +0000
@@ -0,0 +1,26 @@
+From 62b6433a81ee7ed6e0ac2d6b09ac85b885046ac3 Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 10:27:17 +0800
+Subject: [PATCH 4/6] parsettf.c: Fix buffer overrun condition
+
+Closes #3090
+---
+ fontforge/parsettf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index 90ac6eb62..04c038607 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -3533,7 +3533,7 @@ static void readcffset(FILE *ttf,struct topdicts *dict,struct ttfinfo *info) {
+ 	    for ( i = 1; i<len; ) {
+ 		first = dict->charset[i++] = getushort(ttf);
+ 		cnt = getc(ttf);
+-		for ( j=0; j<cnt; ++j )
++		for ( j=0; j<cnt && i<len; ++j )
+ 		    dict->charset[i++] = ++first;
+ 	    }
+ 	} else if ( format==2 ) {
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch
--- 1:20161005~dfsg-4/debian/patches/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch	2017-08-12 04:03:16.000000000 +0000
@@ -0,0 +1,43 @@
+From 5a0c6522682b0788fc478dd159dd6168cb5fa38b Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 11:42:26 +0800
+Subject: [PATCH 5/6] parsettf.c: Fix buffer overflow condition when reading
+ CFF top dictionary
+
+Closes #3087
+---
+ fontforge/parsettf.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index 04c038607..992db970c 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -2796,6 +2796,15 @@ return( 3 );
+ 	pt = buffer;
+ 	do {
+ 	    ch = getc(ttf);
++		// Space for at least 2 bytes is required
++		if ((pt-buffer) > (sizeof(buffer) - 2)) {
++			// The buffer is completely full; null-terminate truncate it
++			if ((pt-buffer) == sizeof(buffer)) {
++				pt--;
++			}
++			*pt++ = '\0';
++			break;
++		}
+ 	    if ( pt<buffer+44 || (ch&0xf)==0xf || (ch&0xf0)==0xf0 ) {
+ 		pt = addnibble(pt,ch>>4);
+ 		pt = addnibble(pt,ch&0xf);
+@@ -3019,7 +3028,7 @@ static struct topdicts *readcfftopdict(FILE *ttf, char *fontname, int len,
+ 
+     /* Multiple master fonts can have Type2 operators here, particularly */
+     /*  blend operators. We're ignoring that */
+-    while ( ftell(ttf)<base+len ) {
++    while ( !feof(ttf) && ftell(ttf)<base+len ) {
+ 	sp = 0;
+ 	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=3 && ftell(ttf)<base+len ) {
+ 	    if ( ret==1 )
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch
--- 1:20161005~dfsg-4/debian/patches/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch	2017-08-12 04:03:17.000000000 +0000
@@ -0,0 +1,26 @@
+From 7bfec47910293bf149b8debe44c6f3f788506092 Mon Sep 17 00:00:00 2001
+From: Jeremy Tan <jtanx at outlook.com>
+Date: Sun, 30 Jul 2017 11:56:43 +0800
+Subject: [PATCH 6/6] parsettf.c: Fix out of bounds read condition on buffer
+
+Closes #3093
+---
+ fontforge/parsettf.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fontforge/parsettf.c b/fontforge/parsettf.c
+index 992db970c..192ab6ac7 100644
+--- a/fontforge/parsettf.c
++++ b/fontforge/parsettf.c
+@@ -1769,7 +1769,7 @@ static void readttfcopyrights(FILE *ttf,struct ttfinfo *info) {
+     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]==' ' )
++	if ( temp[0] != '\0' && temp[strlen(temp)-1]==' ' )
+ 	    temp[strlen(temp)-1] = '\0';
+ 	free(info->version);
+ 	info->version = temp;
+-- 
+2.13.3
+
diff -pruN 1:20161005~dfsg-4/debian/patches/series 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/series
--- 1:20161005~dfsg-4/debian/patches/series	2016-11-12 12:52:15.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/patches/series	2017-08-12 04:03:17.000000000 +0000
@@ -5,6 +5,12 @@
 #900_debian_HelpDir_path.diff
 #901_ld_as_needed.diff
 #671971.diff
+0001-Fix-out-of-bounds-read-in-getsid.patch -p1
+0002-readcfftopdict-Prevent-stack-underflow-condition.patch -p1
+0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch -p1
+0004-parsettf.c-Fix-buffer-overrun-condition.patch -p1
+0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch -p1
+0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch -p1
 2001_repackaging_fixup.patch
 2002_avoid_upstream_git_or_pkg.patch
 1001_reproducibe_buildtimestamps.patch
diff -pruN 1:20161005~dfsg-4/debian/rules 1:20161005~dfsg-4+deb9u1ubuntu1/debian/rules
--- 1:20161005~dfsg-4/debian/rules	2016-11-12 15:06:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/debian/rules	2017-09-21 02:53:41.000000000 +0000
@@ -2,6 +2,8 @@
 # -*- makefile -*-
 #export DH_VERBOSE=1
 
+CC := $(CC) -Wl,--no-as-needed
+
 DEB_MAKE_FLAVORS = x nox
 DEB_BUILDDIR = build
 include /usr/share/cdbs/1/rules/debhelper.mk
diff -pruN 1:20161005~dfsg-4/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/parsettf.c
--- 1:20161005~dfsg-4/fontforge/parsettf.c	2016-10-04 11:36:10.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -1793,7 +1793,7 @@ static void readttfcopyrights(FILE *ttf,
     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]==' ' )
+	if ( temp[0] != '\0' && temp[strlen(temp)-1]==' ' )
 	    temp[strlen(temp)-1] = '\0';
 	free(info->version);
 	info->version = temp;
@@ -2820,6 +2820,15 @@ return( 3 );
 	pt = buffer;
 	do {
 	    ch = getc(ttf);
+		// Space for at least 2 bytes is required
+		if ((pt-buffer) > (sizeof(buffer) - 2)) {
+			// The buffer is completely full; null-terminate truncate it
+			if ((pt-buffer) == sizeof(buffer)) {
+				pt--;
+			}
+			*pt++ = '\0';
+			break;
+		}
 	    if ( pt<buffer+44 || (ch&0xf)==0xf || (ch&0xf0)==0xf0 ) {
 		pt = addnibble(pt,ch>>4);
 		pt = addnibble(pt,ch&0xf);
@@ -3043,7 +3052,7 @@ static struct topdicts *readcfftopdict(F
 
     /* Multiple master fonts can have Type2 operators here, particularly */
     /*  blend operators. We're ignoring that */
-    while ( ftell(ttf)<base+len ) {
+    while ( !feof(ttf) && ftell(ttf)<base+len ) {
 	sp = 0;
 	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=3 && ftell(ttf)<base+len ) {
 	    if ( ret==1 )
@@ -3140,6 +3149,10 @@ static struct topdicts *readcfftopdict(F
 	  case (12<<8)+24:
 	    LogError( _("FontForge does not support type2 multiple master fonts\n") );
 	    info->bad_cff = true;
+	    if (sp < 4) {
+	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
+	        break;
+	    }
 	    td->nMasters = stack[0];
 	    td->nAxes = sp-4;
 	    memcpy(td->weightvector,stack+1,(sp-4)*sizeof(real));
@@ -3344,6 +3357,10 @@ return( NULL );
 	offsets[i] = getoffset(ttf,offsize);
     dicts = malloc((count+1)*sizeof(struct topdicts *));
     for ( i=0; i<count; ++i ) {
+	if (fontnames != NULL && fontnames[i] == NULL) {
+		LogError(_("Number of CFF font names is less than dict size: %d < %d"), i, count);
+		break;
+	}
 	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
 		offsets[i+1]-offsets[i], info);
 	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
@@ -3357,8 +3374,14 @@ return( dicts );
 }
 
 static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
-    if ( sid==-1 )
+    if ( sid==-1 ) // Default value, indicating it's not present
 return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        return NULL;
+    }
     else if ( sid<nStdStrings )
 return( cffnames[sid] );
     else if ( sid-nStdStrings>scnt ) {
@@ -3543,7 +3566,7 @@ static void readcffset(FILE *ttf,struct
 	    for ( i = 1; i<len; ) {
 		first = dict->charset[i++] = getushort(ttf);
 		cnt = getc(ttf);
-		for ( j=0; j<cnt; ++j )
+		for ( j=0; j<cnt && i<len; ++j )
 		    dict->charset[i++] = ++first;
 	    }
 	} else if ( format==2 ) {
@@ -5962,17 +5985,17 @@ void TTF_PSDupsDefault(SplineFont *sf) {
     for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
     if ( english==NULL )
 return;
-    if ( english->names[ttf_family]!=NULL &&
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
 	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
 	free(english->names[ttf_family]);
 	english->names[ttf_family]=NULL;
     }
-    if ( english->names[ttf_copyright]!=NULL &&
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
 	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
 	free(english->names[ttf_copyright]);
 	english->names[ttf_copyright]=NULL;
     }
-    if ( english->names[ttf_fullname]!=NULL &&
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
 	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
 	free(english->names[ttf_fullname]);
 	english->names[ttf_fullname]=NULL;
diff -pruN 1:20161005~dfsg-4/fontforge/psread.c 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/psread.c
--- 1:20161005~dfsg-4/fontforge/psread.c	2016-10-04 11:36:10.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/psread.c	2017-09-21 06:45:20.000000000 +0000
@@ -3658,6 +3658,11 @@ SplineChar *PSCharStringToSplines(uint8
 		stack[sp++] = -(v-251)*256 - *type1++ - 108;
 		--len;
 	    } else {
+		if (len < 4) {
+			LogError(_("Not enough data: %d < 4"), len);
+			len = 0;
+			break;
+		}
 		int val = (*type1<<24) | (type1[1]<<16) | (type1[2]<<8) | type1[3];
 		stack[sp++] = val;
 		type1 += 4;
diff -pruN 1:20161005~dfsg-4/fontforge/tottf.c 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/tottf.c
--- 1:20161005~dfsg-4/fontforge/tottf.c	2016-10-04 11:36:10.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/fontforge/tottf.c	2017-09-21 06:45:20.000000000 +0000
@@ -3808,7 +3808,7 @@ void DefaultTTFEnglishNames(struct ttfla
     if ( dummy->names[ttf_uniqueid]==NULL || *dummy->names[ttf_uniqueid]=='\0' ) {
 	time(&now);
 	tm = localtime(&now);
-	sprintf( buffer, "%s : %s : %d-%d-%d",
+	snprintf( buffer, sizeof(buffer), "%s : %s : %d-%d-%d",
 		BDFFoundry?BDFFoundry:TTFFoundry?TTFFoundry:"FontForge 2.0",
 		sf->fullname!=NULL?sf->fullname:sf->fontname,
 		tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900 );
diff -pruN 1:20161005~dfsg-4/.pc/0001-Fix-out-of-bounds-read-in-getsid.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0001-Fix-out-of-bounds-read-in-getsid.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0001-Fix-out-of-bounds-read-in-getsid.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0001-Fix-out-of-bounds-read-in-getsid.patch/fontforge/parsettf.c	2016-10-04 11:36:10.000000000 +0000
@@ -0,0 +1,6407 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((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], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/0002-readcfftopdict-Prevent-stack-underflow-condition.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0002-readcfftopdict-Prevent-stack-underflow-condition.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0002-readcfftopdict-Prevent-stack-underflow-condition.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0002-readcfftopdict-Prevent-stack-underflow-condition.patch/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -0,0 +1,6413 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((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], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    if ( sid==-1 ) // Default value, indicating it's not present
+return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -0,0 +1,6417 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    if (sp < 4) {
+	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
+	        break;
+	    }
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((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], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    if ( sid==-1 ) // Default value, indicating it's not present
+return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/psread.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/psread.c
--- 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/psread.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/psread.c	2016-10-04 11:36:10.000000000 +0000
@@ -0,0 +1,4667 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include <math.h>
+#include <locale.h>
+#include <ustring.h>
+#include <utype.h>
+#include "psfont.h"
+#include "sd.h"
+#include "views.h"		/* For CharViewBase */
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>		/* Solaris defines isnan in ieeefp rather than math.h */
+#endif
+
+typedef struct _io {
+    const char *macro;
+    char *start;
+    FILE *ps, *fog;
+    char fogbuf[60];
+    int backedup, cnt, isloop, isstopped, fogns;
+    struct _io *prev;
+} _IO;
+
+typedef struct io {
+    struct _io *top;
+    int endedstopped;
+    int advance_width;		/* Can be set from a PS comment by MF2PT1 */
+} IO;
+
+#define GARBAGE_MAX	64
+struct garbage {
+    int cnt;
+    struct garbage *next;
+    struct pskeyval *entries[GARBAGE_MAX];
+    int16 cnts[GARBAGE_MAX];
+};
+
+static void AddTok(GrowBuf *gb,char *buf,int islit) {
+
+    if ( islit )
+	GrowBufferAdd(gb,'/');
+    GrowBufferAddStr(gb,buf);
+    GrowBufferAdd(gb,' ');
+}
+
+static struct pskeyval *lookup(struct pskeydict *dict,char *tokbuf) {
+    int i;
+
+    for ( i=0; i<dict->cnt; ++i )
+	if ( strcmp(dict->entries[i].key,tokbuf)==0 )
+return( &dict->entries[i] );
+
+return( NULL );
+}
+
+static void dictfree(struct pskeydict *dict) {
+    int i;
+
+    for ( i=0; i<dict->cnt; ++i ) {
+	if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr ||
+		dict->entries[i].type==ps_lit )
+	    free(dict->entries[i].u.str);
+	else if ( dict->entries[i].type==ps_array || dict->entries[i].type==ps_dict )
+	    dictfree(&dict->entries[i].u.dict);
+    }
+}
+
+static void garbagefree(struct garbage *all) {
+    struct garbage *junk, *next;
+    int i,j;
+
+    for ( junk = all; junk!=NULL; junk = next ) {
+	next = junk->next;
+	for ( j=0; j<junk->cnt; ++j ) {
+	    for ( i=0; i<junk->cnts[j]; ++i ) {
+		if ( junk->entries[j][i].type==ps_string || junk->entries[j][i].type==ps_instr ||
+			junk->entries[j][i].type==ps_lit )
+		    free(junk->entries[j][i].u.str);
+	    }
+	    free(junk->entries[j]);
+	}
+	if ( junk!=all )
+	    chunkfree(junk,sizeof(struct garbage));
+    }
+}
+/**************************** PostScript Importer *****************************/
+/* It's really dumb. It ignores almost everything except linetos and curvetos */
+/*  anything else, function calls, ... is thrown out, if this breaks a lineto */
+/*  or curveto or moveto (if there aren't enough things on the stack) then we */
+/*  ignore that too */
+
+enum pstoks { pt_eof=-1, pt_moveto, pt_rmoveto, pt_curveto, pt_rcurveto,
+    pt_lineto, pt_rlineto, pt_arc, pt_arcn, pt_arct, pt_arcto,
+    pt_newpath, pt_closepath, pt_dup, pt_pop, pt_index,
+    pt_exch, pt_roll, pt_clear, pt_copy, pt_count,
+    pt_setcachedevice, pt_setcharwidth,
+    pt_translate, pt_scale, pt_rotate, pt_concat, pt_end, pt_exec,
+    pt_add, pt_sub, pt_mul, pt_div, pt_idiv, pt_mod, pt_neg,
+    pt_abs, pt_round, pt_ceiling, pt_floor, pt_truncate, pt_max, pt_min,
+    pt_ne, pt_eq, pt_gt, pt_ge, pt_lt, pt_le, pt_and, pt_or, pt_xor, pt_not,
+    pt_exp, pt_sqrt, pt_ln, pt_log, pt_atan, pt_sin, pt_cos,
+    pt_true, pt_false,
+    pt_if, pt_ifelse, pt_for, pt_loop, pt_repeat, pt_exit,
+    pt_stopped, pt_stop,
+    pt_def, pt_bind, pt_load,
+    pt_setlinecap, pt_setlinejoin, pt_setlinewidth, pt_setdash,
+    pt_currentlinecap, pt_currentlinejoin, pt_currentlinewidth, pt_currentdash,
+    pt_setgray, pt_currentgray, pt_sethsbcolor, pt_currenthsbcolor,
+    pt_setrgbcolor, pt_currentrgbcolor, pt_setcmykcolor, pt_currentcmykcolor,
+    pt_currentpoint,
+    pt_fill, pt_stroke, pt_clip,
+
+    pt_imagemask,
+
+    pt_transform, pt_itransform, pt_dtransform, pt_idtransform,
+
+    /* things we sort of pretend to do, but actually do something wrong */
+    pt_gsave, pt_grestore, pt_save, pt_restore, pt_currentmatrix, pt_setmatrix,
+    pt_null,
+
+    pt_currentflat, pt_setflat,
+    pt_currentglobal, pt_setglobal,
+    pt_currentmiterlimit, pt_setmiterlimit,
+    pt_currentobjectformat, pt_setobjectformat,
+    pt_currentoverprint, pt_setoverprint,
+    pt_currentpacking, pt_setpacking,
+    pt_currentshared,
+    pt_currentsmoothness, pt_setsmoothness,
+    pt_currentstrokeadjust, pt_setstrokeadjust,
+
+    pt_mark, pt_counttomark, pt_cleartomark, pt_array, pt_aload, pt_astore,
+    pt_print, pt_cvi, pt_cvlit, pt_cvn, pt_cvr, pt_cvrs, pt_cvs, pt_cvx, pt_stringop,
+
+    pt_opencurly, pt_closecurly, pt_openarray, pt_closearray, pt_string,
+    pt_number, pt_unknown, pt_namelit, pt_output, pt_outputd,
+
+    pt_showpage };
+
+static const char (*toknames[]) = { "moveto", "rmoveto", "curveto", "rcurveto",
+	"lineto", "rlineto", "arc", "arcn", "arct", "arcto",
+	"newpath", "closepath", "dup", "pop", "index",
+	"exch", "roll", "clear", "copy", "count",
+	"setcachedevice", "setcharwidth",
+	"translate", "scale", "rotate", "concat", "end", "exec",
+	"add", "sub", "mul", "div", "idiv", "mod", "neg",
+	"abs", "round", "ceiling", "floor", "truncate", "max", "min",
+	"ne", "eq", "gt", "ge", "lt", "le", "and", "or", "xor", "not",
+	"exp", "sqrt", "ln", "log", "atan", "sin", "cos",
+	"true", "false",
+	"if", "ifelse", "for", "loop", "repeat", "exit",
+	"stopped", "stop",
+	"def", "bind", "load",
+	"setlinecap", "setlinejoin", "setlinewidth", "setdash",
+	"currentlinecap", "currentlinejoin", "currentlinewidth", "currentdash",
+	"setgray", "currentgray", "sethsbcolor", "currenthsbcolor",
+	"setrgbcolor", "currentrgbcolor", "setcmykcolor", "currentcmykcolor",
+	"currentpoint",
+	"fill", "stroke", "clip",
+
+	"imagemask",
+
+	"transform", "itransform", "dtransform", "idtransform",
+
+	"gsave", "grestore", "save", "restore", "currentmatrix", "setmatrix",
+	"null",
+
+	"currentflat", "setflat",
+	"currentglobal", "setglobal",
+	"currentmiterlimit", "setmiterlimit",
+	"currentobjectformat", "setobjectformat",
+	"currentoverprint", "setoverprint",
+	"currentpacking", "setpacking",
+	"currentshared",
+	"currentsmoothness", "setsmoothness",
+	"currentstrokeadjust", "setstrokeadjust",
+
+	"mark", "counttomark", "cleartomark", "array", "aload", "astore",
+	"print", "cvi", "cvlit", "cvn", "cvr", "cvrs", "cvs", "cvx", "string",
+
+	"opencurly", "closecurly", "openarray", "closearray", "string",
+	"number", "unknown", "namelit", "=", "==",
+
+	"showpage",
+
+	NULL };
+
+/* length (of string)
+    fill eofill stroke
+    gsave grestore
+*/
+
+static int getfoghex(_IO *io) {
+    int ch,val;
+
+    while ( isspace( ch = getc(io->fog)));
+    if ( isdigit(ch))
+	val = ch-'0';
+    else if ( ch >= 'A' && ch <= 'F' )
+	val = ch-'A'+10;
+    else if ( ch >= 'a' && ch <= 'f' )
+	val = ch-'a'+10;
+    else
+return(EOF);
+
+    val <<= 4;
+    while ( isspace( ch = getc(io->fog)));
+    if ( isdigit(ch))
+	val |= ch-'0';
+    else if ( ch >= 'A' && ch <= 'F' )
+	val |= ch-'A'+10;
+    else if ( ch >= 'a' && ch <= 'f' )
+	val |= ch-'a'+10;
+    else
+return(EOF);
+
+return( val );
+}
+
+static int nextch(IO *wrapper) {
+    int ch;
+    _IO *io = wrapper->top;
+/* This works for fog 4.1. Fonts generated by 2.4 seem to use a different */
+/*  vector, and a different number parsing scheme */
+    static const char *foguvec[]= { "moveto ", "rlineto ", "rrcurveto ", " ", " ",
+	"Cache ", "10 div setlinewidth ", "ShowInt ", " ", " ", " ", " ",
+	"FillStroke ", " ", " ", "SetWid ", "100 mul add ", "togNS_ ",
+	" ", "closepath ", " ", "SG " };
+
+    while ( io!=NULL ) {
+	if ( io->backedup!=EOF ) {
+	    ch = io->backedup;
+	    io->backedup = EOF;
+return( ch );
+	} else if ( io->ps!=NULL ) {
+	    if ( (ch = getc(io->ps))!=EOF )
+return( ch );
+	} else if ( io->fog!=NULL ) {
+	    if ( io->macro!=NULL && *io->macro!='\0' )
+return( *(io->macro++) );
+	    ch = getfoghex(io);
+	    if ( ch>=233 ) {
+		io->macro = foguvec[ch-233];
+return( *(io->macro++) );
+	    } else if ( ch!=EOF && ch<200 ) {
+		sprintf( io->fogbuf, "%d ", ch-100);
+		io->macro=io->fogbuf;
+return( *(io->macro++) );
+	    } else if (ch!=EOF) {
+		sprintf( io->fogbuf, "%d %s ", ch-233+17, io->fogns
+			? "2 exch exp 3 1 roll 100 mul add mul"
+			: "100 mul add" );
+		io->macro=io->fogbuf;
+return( *(io->macro++) );
+	    }
+	} else {
+	    if ( (ch = *(io->macro++))!='\0' )
+return( ch );
+	    if ( --io->cnt>0 ) {
+		io->macro = io->start;
+return( nextch(wrapper));
+	    }
+	}
+	wrapper->top = io->prev;
+	if ( io->isstopped )
+	    wrapper->endedstopped = true;
+	if (io->start != NULL) free(io->start); io->start = NULL;
+	free(io);
+	io = wrapper->top;
+    }
+return( EOF );
+}
+
+static void unnextch(int ch,IO *wrapper) {
+    if ( ch==EOF )
+return;
+    if ( wrapper->top==NULL )
+	LogError( _("Can't back up with nothing on stack\n") );
+    else if ( wrapper->top->backedup!=EOF )
+	LogError( _("Attempt to back up twice\n") );
+    else if ( wrapper->top->ps!=NULL )
+	ungetc(ch,wrapper->top->ps);
+    else
+	wrapper->top->backedup = ch;
+}
+
+static void pushio(IO *wrapper, FILE *ps, char *macro, int cnt) {
+    _IO *io = calloc(1,sizeof(_IO));
+
+    io->prev = wrapper->top;
+    io->ps = ps;
+    io->macro = io->start = copy(macro);
+    io->backedup = EOF;
+    if ( cnt==-1 ) {
+	io->cnt = 1;
+	io->isstopped = true;
+    } else if ( cnt==0 ) {
+	io->cnt = 1;
+	io->isloop = false;
+    } else {
+	io->cnt = cnt;
+	io->isloop = true;
+    }
+    wrapper->top = io;
+}
+
+static void pushfogio(IO *wrapper, FILE *fog) {
+    _IO *io = calloc(1,sizeof(_IO));
+
+    io->prev = wrapper->top;
+    io->fog = fog;
+    io->backedup = EOF;
+    io->cnt = 1;
+    io->isloop = false;
+    wrapper->top = io;
+}
+
+static void ioescapeloop(IO *wrapper) {
+    _IO *io = wrapper->top, *iop;
+    int wasloop;
+
+    while ( io->prev!=NULL && !io->isstopped ) {
+	iop = io->prev;
+	wasloop = io->isloop;
+	if (io->start != NULL) free(io->start); io->start = NULL;
+	free(io);
+	if ( wasloop ) {
+	    wrapper->top = iop;
+return;
+	}
+	io = iop;
+    }
+
+/* GT: This is part of the PostScript language. "exit" should not be translated */
+/* GT: as it is a PostScript keyword. (FF contains a small PostScript interpreter */
+/* GT: so it can understand some PostScript fonts, and can generate errors when */
+/* GT: handed bad PostScript). */
+    LogError( _("Use of \"exit\" when not in a loop\n") );
+    wrapper->top = io;
+}
+
+static int ioescapestopped(IO *wrapper, struct psstack *stack, int sp, const size_t bsize) {
+    _IO *io = wrapper->top, *iop;
+    int wasstopped;
+
+    while ( io->prev!=NULL ) {
+	iop = io->prev;
+	wasstopped = io->isstopped;
+	if (io->start != NULL) free(io->start); io->start = NULL;
+	free(io);
+	if ( wasstopped ) {
+	    wrapper->top = iop;
+	    if ( sp<(int)bsize ) {
+		stack[sp].type = ps_bool;
+		stack[sp++].u.tf = true;
+	    }
+return(sp);
+	}
+	io = iop;
+    }
+
+/* GT: This is part of the PostScript language. Neither "stop" nor "stopped" */
+/* GT: should be translated as both are PostScript keywords. */
+    LogError( _("Use of \"stop\" when not in a stopped\n") );
+    wrapper->top = io;
+return( sp );
+}
+
+static int endedstopped(IO *wrapper) {
+    if ( wrapper->endedstopped ) {
+	wrapper->endedstopped = false;
+return( true );
+    }
+return( false );
+}
+
+static int CheckCodePointsComment(IO *wrapper) {
+    /* Check to see if this encoding includes the special comment we use */
+    /*  to indicate that the encoding is based on unicode code points rather */
+    /*  than glyph names */
+    char commentbuffer[128], *pt;
+    int ch;
+
+    /* Eat whitespace and comments. Comments last to eol (or formfeed) */
+    while ( isspace(ch = nextch(wrapper)) );
+    if ( ch!='%' ) {
+	unnextch(ch,wrapper);
+return( false );
+    }
+
+    pt = commentbuffer;
+    while ( (ch=nextch(wrapper))!=EOF && ch!='\r' && ch!='\n' && ch!='\f' ) {
+	if ( pt-commentbuffer < (ptrdiff_t)sizeof(commentbuffer)-1 )
+	    *pt++ = ch;
+    }
+    *pt = '\0';
+    if ( strcmp(commentbuffer," Use codepoints.")== 0 )
+return( true );
+
+return( false );
+}
+
+static int nextpstoken(IO *wrapper, real *val, char *tokbuf, int tbsize) {
+    int ch, r, i;
+    char *pt, *end;
+    float mf2pt_advance_width;
+
+    pt = tokbuf;
+    end = pt+tbsize-1;
+
+    /* Eat whitespace and comments. Comments last to eol (or formfeed) */
+    while ( 1 ) {
+	while ( isspace(ch = nextch(wrapper)) );
+	if ( ch!='%' )
+    break;
+	while ( (ch=nextch(wrapper))!=EOF && ch!='\r' && ch!='\n' && ch!='\f' )
+	    if ( pt<end )
+		*pt++ = ch;
+	*pt='\0';
+	/* Some comments have meanings (that we care about) */
+	if ( sscanf( tokbuf, " MF2PT1: bbox %*g %*g %g %*g", &mf2pt_advance_width )==1 )
+	    wrapper->advance_width = mf2pt_advance_width;
+	else if ( sscanf( tokbuf, " MF2PT1: glyph_dimensions %*g %*g %g %*g", &mf2pt_advance_width )==1 )
+	    wrapper->advance_width = mf2pt_advance_width;
+	pt = tokbuf;
+    }
+
+    if ( ch==EOF )
+return( pt_eof );
+
+    pt = tokbuf;
+    end = pt+tbsize-1;
+    *pt++ = ch; *pt='\0';
+
+    if ( ch=='(' ) {
+	int nest=1, quote=0;
+	while ( (ch=nextch(wrapper))!=EOF ) {
+	    if ( pt<end ) *pt++ = ch;
+	    if ( quote )
+		quote=0;
+	    else if ( ch=='(' )
+		++nest;
+	    else if ( ch==')' ) {
+		if ( --nest==0 )
+	break;
+	    } else if ( ch=='\\' )
+		quote = 1;
+	}
+	*pt='\0';
+return( pt_string );
+    } else if ( ch=='<' ) {
+	ch = nextch(wrapper);
+	if ( pt<end ) *pt++ = ch;
+	if ( ch=='>' )
+	    /* Done */;
+	else if ( ch!='~' ) {
+	    while ( (ch=nextch(wrapper))!=EOF && ch!='>' )
+		if ( pt<end ) *pt++ = ch;
+	} else {
+	    int twiddle=0;
+	    while ( (ch=nextch(wrapper))!=EOF ) {
+		if ( pt<end ) *pt++ = ch;
+		if ( ch=='~' ) twiddle = 1;
+		else if ( twiddle && ch=='>' )
+	    break;
+		else twiddle = 0;
+	    }
+	}
+	*pt='\0';
+return( pt_string );
+    } else if ( ch==')' || ch=='>' || ch=='[' || ch==']' || ch=='{' || ch=='}' ) {
+	if ( ch=='{' )
+return( pt_opencurly );
+	else if ( ch=='}' )
+return( pt_closecurly );
+	if ( ch=='[' )
+return( pt_openarray );
+	else if ( ch==']' )
+return( pt_closearray );
+
+return( pt_unknown );	/* single character token */
+    } else if ( ch=='/' ) {
+	pt = tokbuf;
+	while ( (ch=nextch(wrapper))!=EOF && !isspace(ch) && ch!='%' &&
+		ch!='(' && ch!=')' && ch!='<' && ch!='>' && ch!='[' && ch!=']' &&
+		ch!='{' && ch!='}' && ch!='/' )
+	    if ( pt<tokbuf+tbsize-2 )
+		*pt++ = ch;
+	*pt = '\0';
+	unnextch(ch,wrapper);
+return( pt_namelit );	/* name literal */
+    } else {
+	while ( (ch=nextch(wrapper))!=EOF && !isspace(ch) && ch!='%' &&
+		ch!='(' && ch!=')' && ch!='<' && ch!='>' && ch!='[' && ch!=']' &&
+		ch!='{' && ch!='}' && ch!='/' ) {
+	    if ( pt<tokbuf+tbsize-2 )
+		*pt++ = ch;
+	}
+	*pt = '\0';
+	unnextch(ch,wrapper);
+	r = strtol(tokbuf,&end,10);
+	pt = end;
+	if ( *pt=='\0' ) {		/* It's a normal integer */
+	    *val = r;
+return( pt_number );
+	} else if ( *pt=='#' ) {
+	    r = strtol(pt+1,&end,r);
+	    if ( *end=='\0' ) {		/* It's a radix integer */
+		*val = r;
+return( pt_number );
+	    }
+	} else {
+	    *val = strtod(tokbuf,&end);
+	    if ( !isfinite(*val) ) {
+/* GT: NaN is a concept in IEEE floating point which means "Not a Number" */
+/* GT: it is used to represent errors like 0/0 or sqrt(-1). */
+		LogError( _("Bad number, infinity or nan: %s\n"), tokbuf );
+		*val = 0;
+	    }
+	    if ( *end=='\0' )		/* It's a real */
+return( pt_number );
+	}
+	/* It's not a number */
+	for ( i=0; toknames[i]!=NULL; ++i )
+	    if ( strcmp(tokbuf,toknames[i])==0 )
+return( i );
+
+return( pt_unknown );
+    }
+}
+
+static void Transform(BasePoint *to, DBasePoint *from, real trans[6]) {
+    to->x = trans[0]*from->x+trans[2]*from->y+trans[4];
+    to->y = trans[1]*from->x+trans[3]*from->y+trans[5];
+}
+
+void MatMultiply(real m1[6], real m2[6], real to[6]) {
+    real trans[6];
+
+    trans[0] = m1[0]*m2[0] +
+		m1[1]*m2[2];
+    trans[1] = m1[0]*m2[1] +
+		m1[1]*m2[3];
+    trans[2] = m1[2]*m2[0] +
+		m1[3]*m2[2];
+    trans[3] = m1[2]*m2[1] +
+		m1[3]*m2[3];
+    trans[4] = m1[4]*m2[0] +
+		m1[5]*m2[2] +
+		m2[4];
+    trans[5] = m1[4]*m2[1] +
+		m1[5]*m2[3] +
+		m2[5];
+    memcpy(to,trans,sizeof(trans));
+}
+
+void MatInverse(real into[6], real orig[6]) {
+    real det = orig[0]*orig[3] - orig[1]*orig[2];
+
+    if ( det==0 ) {
+	LogError( _("Attempt to invert a singular matrix\n") );
+	memset(into,0,sizeof(*into));
+    } else {
+	into[0] =  orig[3]/det;
+	into[1] = -orig[1]/det;
+	into[2] = -orig[2]/det;
+	into[3] =  orig[0]/det;
+	into[4] = -orig[4]*into[0] - orig[5]*into[2];
+	into[5] = -orig[4]*into[1] - orig[5]*into[3];
+    }
+}
+
+int MatIsIdentity(real transform[6]) {
+return( transform[0]==1 && transform[3]==1 && transform[1]==0 && transform[2]==0 &&
+	transform[4]==0 && transform[5]==0 );
+}
+
+static void ECCategorizePoints( EntityChar *ec ) {
+    Entity *ent;
+
+    for ( ent=ec->splines; ent!=NULL; ent=ent->next ) if ( ent->type == et_splines ) {
+	SPLCategorizePoints( ent->u.splines.splines );
+	SPLCategorizePoints( ent->clippath );
+    }
+}
+
+static int AddEntry(struct pskeydict *dict,struct psstack *stack, int sp) {
+    int i;
+
+    if ( dict->cnt>=dict->max ) {
+	if ( dict->cnt==0 ) {
+	    dict->max = 30;
+	    dict->entries = malloc(dict->max*sizeof(struct pskeyval));
+	} else {
+	    dict->max += 30;
+	    dict->entries = realloc(dict->entries,dict->max*sizeof(struct pskeyval));
+	}
+    }
+    if ( sp<2 )
+return(sp);
+    if ( stack[sp-2].type!=ps_string && stack[sp-2].type!=ps_lit ) {
+/* GT: Here "def" is a PostScript keyword, (meaning define). */
+/* GT: This "def" should not be translated as it is part of the PostScript language. */
+	LogError( _("Key for a def must be a string or name literal\n") );
+return(sp-2);
+    }
+    for ( i=0; i<dict->cnt; ++i )
+	if ( strcmp(dict->entries[i].key,stack[sp-2].u.str)==0 )
+    break;
+    if ( i!=dict->cnt ) {
+	free(stack[sp-2].u.str);
+	if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr ||
+		dict->entries[i].type==ps_lit )
+	    free(dict->entries[i].u.str);
+    } else {
+	memset(&dict->entries[i],'\0',sizeof(struct pskeyval));
+	dict->entries[i].key = stack[sp-2].u.str;
+	++dict->cnt;
+    }
+    dict->entries[i].type = stack[sp-1].type;
+    dict->entries[i].u = stack[sp-1].u;
+return(sp-2);
+}
+
+static int forgetstack(struct psstack *stack, int forgets, int sp) {
+    /* forget the bottom most "forgets" entries on the stack */
+    /* we presume they are garbage that has accumulated because we */
+    /*  don't understand all of PS */
+    int i;
+    for ( i=0; i<forgets; ++i ) {
+	if ( stack[i].type==ps_string || stack[i].type==ps_instr ||
+		stack[i].type==ps_lit )
+	    free(stack[i].u.str);
+	else if ( stack[i].type==ps_array || stack[i].type==ps_dict )
+	    dictfree(&stack[i].u.dict);
+    }
+    for ( i=forgets; i<sp; ++i )
+	stack[i-forgets] = stack[i];
+return( sp-forgets );
+}
+
+static int rollstack(struct psstack *stack, int sp) {
+    int n,j,i;
+    struct psstack *temp;
+
+    if ( sp>1 ) {
+	n = stack[sp-2].u.val;
+	j = stack[sp-1].u.val;
+	sp-=2;
+	if ( sp>=n && n>0 ) {
+	    j %= n;
+	    if ( j<0 ) j += n;
+	    temp = malloc(n*sizeof(struct psstack));
+	    for ( i=0; i<n; ++i )
+		temp[i] = stack[sp-n+i];
+	    for ( i=0; i<n; ++i )
+		stack[sp-n+(i+j)%n] = temp[i];
+	    free(temp);
+	}
+    }
+return( sp );
+}
+
+static void CheckMakeB(BasePoint *test, BasePoint *good) {
+    if ( !isfinite(test->x) || test->x>100000 || test->x<-100000 ) {
+	LogError( _("Value out of bounds in spline.\n") );
+	if ( good!=NULL )
+	    test->x = good->x;
+	else
+	    test->x = 0;
+    }
+    if ( !isfinite(test->y) || test->y>100000 || test->y<-100000 ) {
+	LogError( _("Value out of bounds in spline.\n") );
+	if ( good!=NULL )
+	    test->y = good->y;
+	else
+	    test->y = 0;
+    }
+}
+
+static void CheckMake(SplinePoint *from, SplinePoint *to) {
+    CheckMakeB(&from->me,NULL);
+    CheckMakeB(&from->nextcp,&from->me);
+    CheckMakeB(&to->prevcp,&from->nextcp);
+    CheckMakeB(&to->me,&to->prevcp);
+}
+
+static void circlearcto(real a1, real a2, real cx, real cy, real r,
+	SplineSet *cur, real *transform ) {
+    SplinePoint *pt;
+    DBasePoint temp, base, cp;
+    real cplen;
+    int sign=1;
+    real s1, s2, c1, c2;
+
+    if ( a1==a2 )
+return;
+
+    cplen = (a2-a1)/90 * r * .552;
+    a1 *= 3.1415926535897932/180; a2 *= 3.1415926535897932/180;
+    s1 = sin(a1); s2 = sin(a2); c1 = cos(a1); c2 = cos(a2);
+    temp.x = cx+r*c2; temp.y = cy+r*s2;
+    base.x = cx+r*c1; base.y = cy+r*s1;
+    pt = chunkalloc(sizeof(SplinePoint));
+    Transform(&pt->me,&temp,transform);
+    cp.x = temp.x-cplen*s2; cp.y = temp.y + cplen*c2;
+    if ( (cp.x-base.x)*(cp.x-base.x)+(cp.y-base.y)*(cp.y-base.y) >
+	     (temp.x-base.x)*(temp.x-base.x)+(temp.y-base.y)*(temp.y-base.y) ) {
+	sign = -1;
+	cp.x = temp.x+cplen*s2; cp.y = temp.y - cplen*c2;
+    }
+    Transform(&pt->prevcp,&cp,transform);
+    pt->nonextcp = true;
+    cp.x = base.x + sign*cplen*s1; cp.y = base.y - sign*cplen*c1;
+    Transform(&cur->last->nextcp,&cp,transform);
+    cur->last->nonextcp = false;
+    CheckMake(cur->last,pt);
+    SplineMake3(cur->last,pt);
+    cur->last = pt;
+}
+
+static void circlearcsto(real a1, real a2, real cx, real cy, real r,
+	SplineSet *cur, real *transform, int clockwise ) {
+    int a;
+    real last;
+
+    while ( a1<0 ) { a1 += 360; a2 +=360;} while ( a2-a1<=-360 ) a2 += 360;
+    while ( a1>360 ) { a1 -= 360; a2 -= 360; } while ( a2-a1>360 ) a2 -= 360;
+    if ( !clockwise ) {
+	if ( a1>a2 )
+	    a2 += 360;
+	last = a1;
+	for ( a=((int) (a1+90)/90)*90; a<a2; a += 90 ) {
+	    circlearcto(last,a,cx,cy,r,cur,transform);
+	    last = a;
+	}
+	circlearcto(last,a2,cx,cy,r,cur,transform);
+    } else {
+	if ( a2>a1 )
+	    a1 += 360;
+	last = a1;
+	for ( a=((int) (a1-90)/90)*90+90; a>a2; a -= 90 ) {
+	    circlearcto(last,a,cx,cy,r,cur,transform);
+	    last = a;
+	}
+	circlearcto(last,a2,cx,cy,r,cur,transform);
+    }
+}
+
+static void collectgarbage(struct garbage *tofrees,struct pskeydict *to) {
+    struct garbage *into;
+
+    /* Garbage collection pointers */
+    into = tofrees;
+    if ( tofrees->cnt>=GARBAGE_MAX && tofrees->next!=NULL )
+	into = tofrees->next;
+    if ( into->cnt>=GARBAGE_MAX ) {
+	into = chunkalloc(sizeof(struct garbage));
+	into->next = tofrees->next;
+	tofrees->next = into;
+    }
+    into->cnts[    into->cnt   ] = to->cnt;
+    into->entries[ into->cnt++ ] = to->entries;
+}
+
+static void copyarray(struct pskeydict *to,struct pskeydict *from, struct garbage *tofrees) {
+    int i;
+    struct pskeyval *oldent = from->entries;
+
+    *to = *from;
+    to->entries = calloc(to->cnt,sizeof(struct pskeyval));
+    for ( i=0; i<to->cnt; ++i ) {
+	to->entries[i] = oldent[i];
+	if ( to->entries[i].type==ps_string || to->entries[i].type==ps_instr ||
+		to->entries[i].type==ps_lit )
+	    to->entries[i].u.str = copy(to->entries[i].u.str);
+	else if ( to->entries[i].type==ps_array || to->entries[i].type==ps_dict )
+	    copyarray(&to->entries[i].u.dict,&oldent[i].u.dict,tofrees);
+    }
+    collectgarbage(tofrees,to);
+}
+
+static int aload(unsigned sp, struct psstack *stack,size_t stacktop, struct garbage *tofrees) {
+    int i;
+
+    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+	struct pskeydict dict;
+	--sp;
+	dict = stack[sp].u.dict;
+	for ( i=0; i<dict.cnt; ++i ) {
+	    if ( sp<stacktop ) {
+		stack[sp].type = dict.entries[i].type;
+		stack[sp].u = dict.entries[i].u;
+		if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			stack[sp].type==ps_lit )
+		    stack[sp].u.str = copy(stack[sp].u.str);
+/* The following is incorrect behavior, but as I don't do garbage collection */
+/*  and I'm not going to implement reference counts, this will work in most cases */
+		else if ( stack[sp].type==ps_array )
+		    copyarray(&stack[sp].u.dict,&stack[sp].u.dict,tofrees);
+		++sp;
+	    }
+	}
+    }
+return( sp );
+}
+
+static void printarray(struct pskeydict *dict) {
+    int i;
+
+    printf("[" );
+    for ( i=0; i<dict->cnt; ++i ) {
+	switch ( dict->entries[i].type ) {
+	  case ps_num:
+	    printf( "%g", (double) dict->entries[i].u.val );
+	  break;
+	  case ps_bool:
+	    printf( "%s", dict->entries[i].u.tf ? "true" : "false" );
+	  break;
+	  case ps_string: case ps_instr: case ps_lit:
+	    printf( dict->entries[i].type==ps_lit ? "/" :
+		    dict->entries[i].type==ps_string ? "(" : "{" );
+	    printf( "%s", dict->entries[i].u.str );
+	    printf( dict->entries[i].type==ps_lit ? "" :
+		    dict->entries[i].type==ps_string ? ")" : "}" );
+	  break;
+	  case ps_array:
+	    printarray(&dict->entries[i].u.dict);
+	  break;
+	  case ps_void:
+	    printf( "-- void --" );
+	  break;
+	  default:
+	    printf( "-- nostringval --" );
+	  break;
+	}
+	printf(" ");
+    }
+    printf( "]" );
+}
+
+static void freestuff(struct psstack *stack, int sp, struct pskeydict *dict,
+	GrowBuf *gb, struct garbage *tofrees) {
+    int i;
+
+    free(gb->base);
+    for ( i=0; i<dict->cnt; ++i ) {
+	if ( dict->entries[i].type==ps_string || dict->entries[i].type==ps_instr ||
+		dict->entries[i].type==ps_lit )
+	    free(dict->entries[i].u.str);
+	free(dict->entries[i].key);
+    }
+    free( dict->entries );
+    for ( i=0; i<sp; ++i ) {
+	if ( stack[i].type==ps_string || stack[i].type==ps_instr ||
+		stack[i].type==ps_lit )
+	    free(stack[i].u.str);
+    }
+    garbagefree(tofrees);
+}
+
+static void DoMatTransform(int tok,int sp,struct psstack *stack) {
+    real invt[6], t[6];
+
+    if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) {
+	double x = stack[sp-3].u.val, y = stack[sp-2].u.val;
+	--sp;
+	t[5] = stack[sp].u.dict.entries[5].u.val;
+	t[4] = stack[sp].u.dict.entries[4].u.val;
+	t[3] = stack[sp].u.dict.entries[3].u.val;
+	t[2] = stack[sp].u.dict.entries[2].u.val;
+	t[1] = stack[sp].u.dict.entries[1].u.val;
+	t[0] = stack[sp].u.dict.entries[0].u.val;
+	dictfree(&stack[sp].u.dict);
+	if ( tok==pt_itransform || tok==pt_idtransform ) {
+	    MatInverse(invt,t);
+	    memcpy(t,invt,sizeof(t));
+	}
+	stack[sp-2].u.val = t[0]*x + t[1]*y;
+	stack[sp-1].u.val = t[2]*x + t[3]*y;
+	if ( tok==pt_transform || tok==pt_itransform ) {
+	    stack[sp-2].u.val += t[4];
+	    stack[sp-1].u.val += t[5];
+	}
+    }
+}
+
+static int DoMatOp(int tok,int sp,struct psstack *stack) {
+    real temp[6], t[6];
+    int nsp=sp;
+
+    if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) {
+	t[5] = stack[sp-1].u.dict.entries[5].u.val;
+	t[4] = stack[sp-1].u.dict.entries[4].u.val;
+	t[3] = stack[sp-1].u.dict.entries[3].u.val;
+	t[2] = stack[sp-1].u.dict.entries[2].u.val;
+	t[1] = stack[sp-1].u.dict.entries[1].u.val;
+	t[0] = stack[sp-1].u.dict.entries[0].u.val;
+	switch ( tok ) {
+	  case pt_translate:
+	    if ( sp>=3 ) {
+		stack[sp-1].u.dict.entries[5].u.val += stack[sp-3].u.val*t[0]+stack[sp-2].u.val*t[2];
+		stack[sp-1].u.dict.entries[4].u.val += stack[sp-3].u.val*t[1]+stack[sp-2].u.val*t[3];
+		nsp = sp-2;
+	    }
+	  break;
+	  case pt_scale:
+	    if ( sp>=2 ) {
+		stack[sp-1].u.dict.entries[0].u.val *= stack[sp-3].u.val;
+		stack[sp-1].u.dict.entries[1].u.val *= stack[sp-3].u.val;
+		stack[sp-1].u.dict.entries[2].u.val *= stack[sp-2].u.val;
+		stack[sp-1].u.dict.entries[3].u.val *= stack[sp-2].u.val;
+		/* transform[4,5] are unchanged */
+		nsp = sp-2;
+	    }
+	  break;
+	  case pt_rotate:
+	    if ( sp>=1 ) {
+		--sp;
+		temp[0] = temp[3] = cos(stack[sp].u.val);
+		temp[1] = sin(stack[sp].u.val);
+		temp[2] = -temp[1];
+		temp[4] = temp[5] = 0;
+		MatMultiply(temp,t,t);
+		stack[sp-1].u.dict.entries[5].u.val = t[5];
+		stack[sp-1].u.dict.entries[4].u.val = t[4];
+		stack[sp-1].u.dict.entries[3].u.val = t[3];
+		stack[sp-1].u.dict.entries[2].u.val = t[2];
+		stack[sp-1].u.dict.entries[1].u.val = t[1];
+		stack[sp-1].u.dict.entries[0].u.val = t[0];
+		nsp = sp-1;
+	    }
+	  break;
+	  default:
+	  break;
+	}
+	stack[nsp-1] = stack[sp-1];
+    }
+return(nsp);
+}
+
+static Entity *EntityCreate(SplinePointList *head,int linecap,int linejoin,
+	real linewidth, real *transform, SplineSet *clippath) {
+    Entity *ent = calloc(1,sizeof(Entity));
+    ent->type = et_splines;
+    ent->u.splines.splines = head;
+    ent->u.splines.cap = linecap;
+    ent->u.splines.join = linejoin;
+    ent->u.splines.stroke_width = linewidth;
+    ent->u.splines.fill.col = 0xffffffff;
+    ent->u.splines.stroke.col = 0xffffffff;
+    ent->u.splines.fill.opacity = 1.0;
+    ent->u.splines.stroke.opacity = 1.0;
+    ent->clippath = SplinePointListCopy(clippath);
+    memcpy(ent->u.splines.transform,transform,6*sizeof(real));
+return( ent );
+}
+
+static uint8 *StringToBytes(struct psstack *stackel,int *len) {
+    char *pt;
+    uint8 *upt, *base, *ret;
+    int half, sofar, val, nesting;
+    int i,j;
+
+    pt = stackel->u.str;
+    if ( stackel->type==ps_instr ) {
+	/* imagemask operators take strings or procedures or files */
+	/* we support strings, or procedures containing strings */
+	while ( isspace(*pt)) ++pt;
+	if ( *pt=='{' || *pt=='[' ) ++pt;
+	while ( isspace(*pt)) ++pt;
+    } else if ( stackel->type!=ps_string )
+return( NULL );
+
+    upt = base = malloc(65536+1);	/* Maximum size of ps string */
+
+    if ( *pt=='(' ) {
+	/* A conventional string */
+	++pt;
+	nesting = 0;
+	while ( *pt!='\0' && (nesting!=0 || *pt!=')') ) {
+	    if ( *pt=='(' ) {
+		++nesting;
+		*upt++ = *pt++;
+	    } else if ( *pt==')' ) {
+		--nesting;
+		*upt++ = *pt++;
+	    } else if ( *pt=='\r' || *pt=='\n' ) {
+		/* any of lf, cr, crlf gets translated to \n */
+		if ( *pt=='\r' && pt[1]=='\n' ) ++pt;
+		*upt++ = '\n';
+		++pt;
+	    } else if ( *pt!='\\' ) {
+		*upt++ = *pt++;
+	    } else {
+		++pt;
+		if ( *pt=='\r' || *pt=='\n' ) {
+		    /* any of \lf, \cr, \crlf gets ignored */
+		    if ( *pt=='\r' && pt[1]=='\n' ) ++pt;
+		    ++pt;
+		} else if ( *pt=='n' ) {
+		    *upt++ = '\n';
+		    ++pt;
+		} else if ( *pt=='r' ) {
+		    *upt++ = '\r';
+		    ++pt;
+		} else if ( *pt=='t' ) {
+		    *upt++ = '\t';
+		    ++pt;
+		} else if ( *pt=='b' ) {
+		    *upt++ = '\b';
+		    ++pt;
+		} else if ( *pt=='f' ) {
+		    *upt++ = '\f';
+		    ++pt;
+		} else if ( *pt>='0' && *pt<='7' ) {
+		    if ( pt[1]<'0' || pt[1]>'7' )	/* This isn't really legal postscript */
+			*upt++ = *pt++ - '0';
+		    else if ( pt[2]<'0' || pt[2]>'7' ) {/* 3 octal digits are required */
+			*upt++ = ((*pt - '0')<<3) + (pt[1]-'0');
+			pt += 2;
+		    } else {
+			*upt++ = ((*pt - '0')<<6) + ((pt[1]-'0')<<3) + (pt[2]-'0');
+			pt += 3;
+		    }
+		} else if ( *pt=='(' || *pt==')' || *pt=='\\' )
+		    *upt++ = *pt++;
+		else {
+		    LogError( _("Unknown character after backslash in literal string.\n"));
+		    *upt++ = *pt++;
+		}
+	    }
+	}
+    } else if ( *pt!='<' ) {
+	LogError( _("Unknown string type\n" ));
+	free(base);
+return( NULL );
+    } else if ( pt[1]!='~' ) {
+	/* A hex string. Ignore any characters which aren't hex */
+	half = sofar = 0;
+	++pt;
+	while ( *pt!='>' && *pt!='\0' ) {
+	    if ( *pt>='a' && *pt<='f' )
+		val = *pt++-'a'+10;
+	    else if ( *pt>='A' && *pt<='F' )
+		val = *pt++-'A'+10;
+	    else if ( isdigit(*pt))
+		val = *pt++-'0';
+	    else {
+		++pt;		/* Not hex */
+	continue;
+	    }
+	    if ( !half ) {
+		half = true;
+		sofar = val<<4;
+	    } else {
+		*upt++ = sofar|val;
+		half = false;
+	    }
+	}
+	if ( half )
+	    *upt++ = sofar;
+    } else {
+	/* An ASCII-85 string */
+	/* c1 c2 c3 c4 c5 (c>='!' && c<='u') => ((c1-'!')*85+(c2-'!'))*85... */
+	/* z => 32bits of 0 */
+	pt += 2;
+	while ( *pt!='\0' && *pt!='~' ) {
+	    if ( upt-base+4 > 65536 )
+	break;
+	    if ( *pt=='z' ) {
+		*upt++ = 0;
+		*upt++ = 0;
+		*upt++ = 0;
+		*upt++ = 0;
+		++pt;
+	    } else if ( *pt>='!' && *pt<='u' ) {
+		val = 0;
+		for ( i=0; i<5 && *pt>='!' && *pt<='u'; ++i )
+		    val = (val*85) + *pt++ - '!';
+		for ( j=i; j<5; ++j )
+		    val *= 85;
+		*upt++ =  val>>24      ;
+		if ( i>2 )
+		    *upt++ = (val>>16)&0xff;
+		if ( i>3 )
+		    *upt++ = (val>>8 )&0xff;
+		if ( i>4 )
+		    *upt++ =  val     &0xff;
+		if ( i<5 )
+	break;
+	    } else if ( isspace( *pt ) ) {
+		++pt;
+	    } else
+	break;
+	}
+    }
+    *len = upt-base;
+    ret = malloc(upt-base);
+    memcpy(ret,base,upt-base);
+    free(base);
+return(ret);
+}
+
+static int PSAddImagemask(EntityChar *ec,struct psstack *stack,int sp,
+	real transform[6],Color fillcol) {
+    uint8 *data;
+    int datalen, width, height, polarity;
+    real trans[6];
+    struct _GImage *base;
+    GImage *gi;
+    Entity *ent;
+    int i,j;
+
+    if ( sp<5 || (stack[sp-1].type!=ps_instr && stack[sp-1].type!=ps_string)) {
+	LogError( _("FontForge does not support dictionary based imagemask operators.\n" ));
+return( sp-1 );
+    }
+
+    if ( stack[sp-2].type!=ps_array || stack[sp-2].u.dict.cnt!=6 ) {
+	LogError( _("Fourth argument of imagemask must be a 6-element transformation matrix.\n" ));
+return( sp-5 );
+    }
+
+    if ( stack[sp-3].type!=ps_bool ) {
+	LogError( _("Third argument of imagemask must be a boolean.\n" ));
+return( sp-5 );
+    }
+    polarity = stack[sp-3].u.tf;
+
+    if ( stack[sp-4].type!=ps_num || stack[sp-5].type!=ps_num ) {
+	LogError( _("First and second arguments of imagemask must be integers.\n" ));
+return( sp-5 );
+    }
+    height = stack[sp-4].u.val;
+    width = stack[sp-5].u.val;
+
+    data = StringToBytes(&stack[sp-1],&datalen);
+
+    if ( width<=0 || height<=0 || ((width+7)/8)*height>datalen ) {
+	LogError( _("Width or height arguments to imagemask contain invalid values\n(either negative or they require more data than provided).\n" ));
+	free(data);
+return( sp-5 );
+    }
+    trans[0] = stack[sp-2].u.dict.entries[0].u.val;
+    trans[1] = stack[sp-2].u.dict.entries[1].u.val;
+    trans[2] = stack[sp-2].u.dict.entries[2].u.val;
+    trans[3] = stack[sp-2].u.dict.entries[3].u.val;
+    trans[4] = stack[sp-2].u.dict.entries[4].u.val;
+    trans[5] = stack[sp-2].u.dict.entries[5].u.val;
+
+    gi = GImageCreate(it_mono,width,height);
+    base = gi->u.image;
+    base->trans = 1;
+    if ( polarity ) {
+	for ( i=0; i<datalen; ++i )
+	    data[i] ^= 0xff;
+    }
+    if ( trans[0]>0 && trans[3]<0 )
+	memcpy(base->data,data,datalen);
+    else if ( trans[0]>0 && trans[3]>0 ) {
+	for ( i=0; i<height; ++i )
+	    memcpy(base->data+i*base->bytes_per_line,data+(height-i)*base->bytes_per_line,
+		    base->bytes_per_line);
+    } else if ( trans[0]<0 && trans[3]<0 ) {
+	for ( i=0; i<height; ++i ) for ( j=0; j<width; ++j ) {
+	    if ( data[i*base->bytes_per_line+ (j>>3)]&(0x80>>(j&7)) )
+		base->data[i*base->bytes_per_line + ((width-j-1)>>3)] |=
+			(0x80>>((width-j-1)&7));
+	}
+    } else {
+	for ( i=0; i<height; ++i ) for ( j=0; j<width; ++j ) {
+	    if ( data[i*base->bytes_per_line+ (j>>3)]&(0x80>>(j&7)) )
+		base->data[(height-i-1)*base->bytes_per_line + ((width-j-1)>>3)] |=
+			(0x80>>((width-j-1)&7));
+	}
+    }
+    free(data);
+
+    ent = calloc(1,sizeof(Entity));
+    ent->type = et_image;
+    ent->u.image.image = gi;
+    memcpy(ent->u.image.transform,transform,sizeof(real[6]));
+    ent->u.image.transform[0] /= width;
+    ent->u.image.transform[3] /= height;
+    ent->u.image.transform[5] += height;
+    ent->u.image.col = fillcol;
+
+    ent->next = ec->splines;
+    ec->splines = ent;
+return( sp-5 );
+}
+
+static void HandleType3Reference(IO *wrapper,EntityChar *ec,real transform[6],
+	char *tokbuf, int toksize) {
+    int tok;
+    real dval;
+    char *glyphname;
+    RefChar *ref;
+
+   tok = nextpstoken(wrapper,&dval,tokbuf,toksize);
+   if ( strcmp(tokbuf,"get")!=0 )
+return;		/* Hunh. I don't understand it. I give up */
+   tok = nextpstoken(wrapper,&dval,tokbuf,toksize);
+   if ( tok!=pt_namelit )
+return;		/* Hunh. I don't understand it. I give up */
+    glyphname = copy(tokbuf);
+   tok = nextpstoken(wrapper,&dval,tokbuf,toksize);
+   if ( strcmp(tokbuf,"get")!=0 ) {
+	free(glyphname);
+	return;	/* Hunh. I don't understand it. I give up */
+   }
+   tok = nextpstoken(wrapper,&dval,tokbuf,toksize);
+   if ( strcmp(tokbuf,"exec")!=0 ) {
+	free(glyphname);
+	return;	/* Hunh. I don't understand it. I give up */
+    }
+
+    /* Ok, it looks very much like a reference to glyphname */
+    ref = RefCharCreate();
+    memcpy(ref->transform,transform,sizeof(ref->transform));
+    ref->sc = (SplineChar *) glyphname;
+    ref->next = ec->refs;
+    ec->refs = ref;
+}
+
+static void _InterpretPS(IO *wrapper, EntityChar *ec, RetStack *rs) {
+    SplinePointList *cur=NULL, *head=NULL;
+    DBasePoint current, temp;
+    int tok, i, j;
+    struct psstack stack[100];
+    real dval;
+    unsigned sp=0;
+    SplinePoint *pt;
+    RefChar *ref, *lastref=NULL;
+    real transform[6], t[6];
+    struct graphicsstate {
+	real transform[6];
+	DBasePoint current;
+	real linewidth;
+	int linecap, linejoin;
+	Color fore;
+	DashType dashes[DASH_MAX];
+	SplineSet *clippath;
+    } gsaves[30];
+    int gsp = 0;
+    int ccnt=0;
+    GrowBuf gb;
+    struct pskeydict dict;
+    struct pskeyval *kv;
+    Color fore=COLOR_INHERITED;
+    int linecap=lc_inherited, linejoin=lj_inherited; real linewidth=WIDTH_INHERITED;
+    DashType dashes[DASH_MAX];
+    int dash_offset = 0;
+    Entity *ent;
+    int warned = 0;
+    struct garbage tofrees;
+    SplineSet *clippath = NULL;
+    char *tokbuf;
+    const int tokbufsize = 2*65536+10;
+
+    tokbuf = malloc(tokbufsize);
+
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+
+    memset(&gb,'\0',sizeof(GrowBuf));
+    memset(&dict,'\0',sizeof(dict));
+    tofrees.cnt = 0; tofrees.next = NULL;
+
+    transform[0] = transform[3] = 1.0;
+    transform[1] = transform[2] = transform[4] = transform[5] = 0;
+    current.x = current.y = 0;
+    dashes[0] = 0; dashes[1] = DASH_INHERITED;
+
+    if ( ec->fromtype3 ) {
+	/* My type3 fonts have two things pushed on the stack when they */
+	/*  start. One is a dictionary, the other a flag (number). If the */
+	/*  flag is non-zero then we are a nested call (a reference char) */
+	/*  if 0, we're normal. We don't want to do setcachedevice for */
+	/*  reference chars.  We can't represent a dictionary on the stack */
+	/*  so just push two 0s */
+	stack[0].type = stack[1].type = ps_num;
+	stack[0].u.val = stack[1].u.val = 0;
+	sp = 2;
+    }
+
+    while ( (tok = nextpstoken(wrapper,&dval,tokbuf,tokbufsize))!=pt_eof ) {
+	if ( endedstopped(wrapper)) {
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_bool;
+		stack[sp++].u.tf = false;
+	    }
+	}
+	if ( sp>sizeof(stack)/sizeof(stack[0])*4/5 ) {
+	    /* We don't interpret all of postscript */
+	    /* Sometimes we leave garbage on the stack that a real PS interp */
+	    /*  would have handled. If the stack gets too deep, clean out the */
+	    /*  oldest entries */
+	    sp = forgetstack(stack,sizeof(stack)/sizeof(stack[0])/3,sp );
+	}
+	if ( ccnt>0 ) {
+	    if ( tok==pt_closecurly )
+		--ccnt;
+	    else if ( tok==pt_opencurly )
+		++ccnt;
+	    if ( ccnt>0 )
+		AddTok(&gb,tokbuf,tok==pt_namelit);
+	    else {
+		if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		    stack[sp].type = ps_instr;
+		    if ( gb.pt==NULL )
+			stack[sp++].u.str = copy("");
+		    else {
+			*gb.pt = '\0'; gb.pt = gb.base;
+			stack[sp++].u.str = copy((char *)gb.base);
+		    }
+		}
+	    }
+	} else if ( tok==pt_unknown && (kv=lookup(&dict,tokbuf))!=NULL ) {
+	    if ( kv->type == ps_instr )
+		pushio(wrapper,NULL,copy(kv->u.str),0);
+	    else if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = kv->type;
+		stack[sp++].u = kv->u;
+		if ( kv->type==ps_instr || kv->type==ps_lit || kv->type==ps_string )
+		    stack[sp-1].u.str = copy(stack[sp-1].u.str);
+		else if ( kv->type==ps_array || kv->type==ps_dict ) {
+		    copyarray(&stack[sp-1].u.dict,&stack[sp-1].u.dict,&tofrees);
+		    if ( stack[sp-1].u.dict.is_executable )
+			sp = aload(sp,stack,sizeof(stack)/sizeof(stack[0]),&tofrees);
+		}
+	    }
+	} else {
+	if ( tok==pt_unknown ) {
+	    if ( strcmp(tokbuf,"Cache")==0 )	/* Fontographer type3s */
+		tok = pt_setcachedevice;
+	    else if ( strcmp(tokbuf,"SetWid")==0 ) {
+		tok = pt_setcharwidth;
+		if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		    stack[sp].type = ps_num;
+		    stack[sp++].u.val = 0;
+		}
+	    } else if ( strcmp(tokbuf,"rrcurveto")==0 ) {
+		if ( sp>=6 ) {
+		    stack[sp-4].u.val += stack[sp-6].u.val;
+		    stack[sp-3].u.val += stack[sp-5].u.val;
+		    stack[sp-2].u.val += stack[sp-4].u.val;
+		    stack[sp-1].u.val += stack[sp-3].u.val;
+		    tok = pt_rcurveto;
+		}
+	    } else if ( strcmp(tokbuf,"FillStroke")==0 ) {
+		if ( sp>0 )
+		    --sp;
+		tok = linewidth!=WIDTH_INHERITED ? pt_stroke : pt_fill;
+		if ( wrapper->top!=NULL && wrapper->top->ps!=NULL &&
+			linewidth!=WIDTH_INHERITED )
+		    linewidth /= 10.0;	/* bug in Fontographer's unencrypted type3 fonts */
+	    } else if ( strcmp(tokbuf,"SG")==0 ) {
+		if ( linewidth!=WIDTH_INHERITED && sp>1 )
+		    stack[sp-2].u.val = stack[sp-1].u.val;
+		if ( sp>0 )
+		    --sp;
+		if ( sp>0 )
+		    stack[sp-1].u.val = (stack[sp-1].u.val+99)/198.0;
+		tok = pt_setgray;
+	    } else if ( strcmp(tokbuf,"ShowInt")==0 ) {
+	    	/* Fontographer reference */
+		if ( (!wrapper->top->fogns && sp>0 && stack[sp-1].type == ps_num &&
+			 stack[sp-1].u.val>=0 && stack[sp-1].u.val<=255 ) ||
+			(wrapper->top->fogns && sp>6 && stack[sp-7].type == ps_num &&
+			 stack[sp-7].u.val>=0 && stack[sp-7].u.val<=255 )) {
+		    ref = RefCharCreate();
+		    memcpy(ref->transform,transform,sizeof(ref->transform));
+		    if ( wrapper->top->fogns ) {
+			sp -= 6;
+			t[0] = stack[sp+0].u.val;
+			t[1] = stack[sp+1].u.val;
+			t[2] = stack[sp+2].u.val;
+			t[3] = stack[sp+3].u.val;
+			t[4] = stack[sp+4].u.val;
+			t[5] = stack[sp+5].u.val;
+			MatMultiply(t,ref->transform,ref->transform);
+			wrapper->top->fogns = false;
+		    }
+		    ref->orig_pos = stack[--sp].u.val;
+		    ref->next = ec->refs;
+		    ec->refs = ref;
+    continue;
+		}
+	    } else if ( strcmp(tokbuf,"togNS_")==0 ) {
+		wrapper->top->fogns = !wrapper->top->fogns;
+    continue;
+	    }
+	}
+	switch ( tok ) {
+	  case pt_number:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = dval;
+	    }
+	  break;
+	  case pt_string:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_string;
+		stack[sp++].u.str = copyn(tokbuf+1,strlen(tokbuf)-2);
+	    }
+	  break;
+	  case pt_true: case pt_false:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_bool;
+		stack[sp++].u.tf = tok==pt_true;
+	    }
+	  break;
+	  case pt_opencurly:
+	    ++ccnt;
+	  break;
+	  case pt_closecurly:
+	    --ccnt;
+	    if ( ccnt<0 ) {
+ goto done;
+	    }
+	  break;
+	  case pt_count:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp].u.val = sp;
+		++sp;
+	    }
+	  break;
+	  case pt_pop:
+	    if ( sp>0 ) {
+		--sp;
+		if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			stack[sp].type==ps_lit )
+		    free(stack[sp].u.str);
+		else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict )
+		    dictfree(&stack[sp].u.dict);
+	    }
+	  break;
+	  case pt_clear:
+	    while ( sp>0 ) {
+		--sp;
+		if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			stack[sp].type==ps_lit )
+		    free(stack[sp].u.str);
+		else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict )
+		    dictfree(&stack[sp].u.dict);
+	    }
+	  break;
+	  case pt_dup:
+	    if ( sp>0 && sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp] = stack[sp-1];
+		if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			stack[sp].type==ps_lit )
+		    stack[sp].u.str = copy(stack[sp].u.str);
+    /* The following is incorrect behavior, but as I don't do garbage collection */
+    /*  and I'm not going to implement reference counts, this will work in most cases */
+		else if ( stack[sp].type==ps_array )
+		    copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees);
+		++sp;
+	    }
+	  break;
+	  case pt_copy:
+	    if ( sp>0 ) {
+		int n = stack[--sp].u.val;
+		if ( n+sp<sizeof(stack)/sizeof(stack[0]) ) {
+		    int i;
+		    for ( i=0; i<n; ++i ) {
+			stack[sp] = stack[sp-n];
+			if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+				stack[sp].type==ps_lit )
+			    stack[sp].u.str = copy(stack[sp].u.str);
+    /* The following is incorrect behavior, but as I don't do garbage collection */
+    /*  and I'm not going to implement reference counts, this will work in most cases */
+			else if ( stack[sp].type==ps_array )
+			    copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees);
+			++sp;
+		    }
+		}
+	    }
+	  break;
+	  case pt_exch:
+	    if ( sp>1 ) {
+		struct psstack temp;
+		temp = stack[sp-1];
+		stack[sp-1] = stack[sp-2];
+		stack[sp-2] = temp;
+	    }
+	  break;
+	  case pt_roll:
+	    sp = rollstack(stack,sp);
+	  break;
+	  case pt_index:
+	    if ( sp>0 ) {
+		i = stack[--sp].u.val;
+		if ( i>=0 && sp>(unsigned)i ) {
+		    stack[sp] = stack[sp-i-1];
+		    if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			    stack[sp].type==ps_lit )
+			stack[sp].u.str = copy(stack[sp].u.str);
+    /* The following is incorrect behavior, but as I don't do garbage collection */
+    /*  and I'm not going to implement reference counts, this will work in most cases */
+		    else if ( stack[sp].type==ps_array )
+			copyarray(&stack[sp].u.dict,&stack[sp].u.dict,&tofrees);
+		    ++sp;
+		}
+	    }
+	  break;
+	  case pt_add:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		stack[sp-2].u.val += stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_sub:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		stack[sp-2].u.val -= stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_mul:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		stack[sp-2].u.val *= stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_div:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		if ( stack[sp-1].u.val == 0 )
+		    LogError( _("Divide by zero in postscript code.\n" ));
+		else
+		    stack[sp-2].u.val /= stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_idiv:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		if ( stack[sp-1].u.val == 0 )
+		    LogError( _("Divide by zero in postscript code.\n" ));
+		else
+		    stack[sp-2].u.val = ((int) stack[sp-2].u.val) / ((int) stack[sp-1].u.val);
+		--sp;
+	    }
+	  break;
+	  case pt_mod:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		if ( stack[sp-1].u.val == 0 )
+		    LogError( _("Divide by zero in postscript code.\n" ));
+		else
+		    stack[sp-2].u.val = ((int) stack[sp-2].u.val) % ((int) stack[sp-1].u.val);
+		--sp;
+	    }
+	  break;
+	  case pt_max:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		if ( stack[sp-2].u.val < stack[sp-1].u.val )
+		    stack[sp-2].u.val = stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_min:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		if ( stack[sp-2].u.val > stack[sp-1].u.val )
+		    stack[sp-2].u.val = stack[sp-1].u.val;
+		--sp;
+	    }
+	  break;
+	  case pt_neg:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num )
+		    stack[sp-1].u.val = -stack[sp-1].u.val;
+	    }
+	  break;
+	  case pt_abs:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num )
+		    if ( stack[sp-1].u.val < 0 )
+			stack[sp-1].u.val = -stack[sp-1].u.val;
+	    }
+	  break;
+	  case pt_round:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num )
+		    stack[sp-1].u.val = rint(stack[sp-1].u.val);
+		    /* rint isn't quite right, round will take 6.5 to 7, 5.5 to 6, etc. while rint() will take both to 6 */
+	    }
+	  break;
+	  case pt_floor:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num )
+		    stack[sp-1].u.val = floor(stack[sp-1].u.val);
+	    }
+	  break;
+	  case pt_ceiling:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num )
+		    stack[sp-1].u.val = ceil(stack[sp-1].u.val);
+	    }
+	  break;
+	  case pt_truncate:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_num ) {
+		    if ( stack[sp-1].u.val<0 )
+			stack[sp-1].u.val = ceil(stack[sp-1].u.val);
+		    else
+			stack[sp-1].u.val = floor(stack[sp-1].u.val);
+		}
+	    }
+	  break;
+	  case pt_ne: case pt_eq:
+	    if ( sp>=2 ) {
+		if ( stack[sp-2].type!=stack[sp-1].type )
+		    stack[sp-2].u.tf = false;
+		else if ( stack[sp-2].type==ps_num )
+		    stack[sp-2].u.tf = (stack[sp-2].u.val == stack[sp-1].u.val);
+		else if ( stack[sp-2].type==ps_bool )
+		    stack[sp-2].u.tf = (stack[sp-2].u.tf == stack[sp-1].u.tf);
+		else
+		    stack[sp-2].u.tf = strcmp(stack[sp-2].u.str,stack[sp-1].u.str)==0 ;
+		stack[sp-2].type = ps_bool;
+		if ( tok==pt_ne ) stack[sp-2].u.tf = !stack[sp-2].u.tf;
+		--sp;
+	    }
+	  break;
+	  case pt_gt: case pt_le: case pt_lt: case pt_ge:
+	    if ( sp>=2 ) {
+		if ( stack[sp-2].type!=stack[sp-1].type )
+		    stack[sp-2].u.tf = false;
+		else if ( stack[sp-2].type==ps_array )
+		    LogError( _("Can't compare arrays\n" ));
+		else {
+		    int cmp;
+		    if ( stack[sp-2].type==ps_num )
+			cmp = (stack[sp-2].u.val > stack[sp-1].u.val)?1:
+				(stack[sp-2].u.val == stack[sp-1].u.val)?0:-1;
+		    else if ( stack[sp-2].type==ps_bool )
+			cmp = (stack[sp-2].u.tf - stack[sp-1].u.tf);
+		    else
+			cmp = strcmp(stack[sp-2].u.str,stack[sp-1].u.str);
+		    if ( tok==pt_gt )
+			stack[sp-2].u.tf = cmp>0;
+		    else if ( tok==pt_lt )
+			stack[sp-2].u.tf = cmp<0;
+		    else if ( tok==pt_le )
+			stack[sp-2].u.tf = cmp<=0;
+		    else
+			stack[sp-2].u.tf = cmp>=0;
+		}
+		stack[sp-2].type = ps_bool;
+		--sp;
+	    }
+	  break;
+	  case pt_not:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type == ps_bool )
+		    stack[sp-1].u.tf = !stack[sp-1].u.tf;
+	    }
+	  break;
+	  case pt_and:
+	    if ( sp>=2 ) {
+		if ( stack[sp-2].type == ps_num )
+		    stack[sp-2].u.val = ((int) stack[sp-2].u.val) & (int) stack[sp-1].u.val;
+		else if ( stack[sp-2].type == ps_bool )
+		    stack[sp-2].u.tf &= stack[sp-1].u.tf;
+		--sp;
+	    }
+	  break;
+	  case pt_or:
+	    if ( sp>=2 ) {
+		if ( stack[sp-2].type == ps_num )
+		    stack[sp-2].u.val = ((int) stack[sp-2].u.val) | (int) stack[sp-1].u.val;
+		else if ( stack[sp-2].type == ps_bool )
+		    stack[sp-2].u.tf |= stack[sp-1].u.tf;
+		--sp;
+	    }
+	  break;
+	  case pt_xor:
+	    if ( sp>=2 ) {
+		if ( stack[sp-2].type == ps_num )
+		    stack[sp-2].u.val = ((int) stack[sp-2].u.val) ^ (int) stack[sp-1].u.val;
+		else if ( stack[sp-2].type == ps_bool )
+		    stack[sp-2].u.tf ^= stack[sp-1].u.tf;
+		--sp;
+	    }
+	  break;
+	  case pt_exp:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		stack[sp-2].u.val = pow(stack[sp-2].u.val,stack[sp-1].u.val);
+		--sp;
+	    }
+	  break;
+	  case pt_sqrt:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].u.val = sqrt(stack[sp-1].u.val);
+	    }
+	  break;
+	  case pt_ln:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].u.val = log(stack[sp-1].u.val);
+	    }
+	  break;
+	  case pt_log:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].u.val = log10(stack[sp-1].u.val);
+	    }
+	  break;
+	  case pt_atan:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_num ) {
+		stack[sp-2].u.val = atan2(stack[sp-2].u.val,stack[sp-1].u.val)*
+			180/3.1415926535897932;
+		--sp;
+	    }
+	  break;
+	  case pt_sin:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].u.val = sin(stack[sp-1].u.val*3.1415926535897932/180);
+	    }
+	  break;
+	  case pt_cos:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].u.val = cos(stack[sp-1].u.val*3.1415926535897932/180);
+	    }
+	  break;
+	  case pt_if:
+	    if ( sp>=2 ) {
+		if ( ((stack[sp-2].type == ps_bool && stack[sp-2].u.tf) ||
+			(stack[sp-2].type == ps_num && strstr(stack[sp-1].u.str,"setcachedevice")!=NULL)) &&
+			stack[sp-1].type==ps_instr )
+		    pushio(wrapper,NULL,stack[sp-1].u.str,0);
+		if ( stack[sp-1].type==ps_string || stack[sp-1].type==ps_instr || stack[sp-1].type==ps_lit )
+		    free(stack[sp-1].u.str);
+		sp -= 2;
+	    } else if ( sp==1 && stack[sp-1].type==ps_instr ) {
+		/*This can happen when reading our type3 fonts, we get passed */
+		/* values on the stack which the interpreter knows nothing */
+		/* about, but the interp needs to learn the width of the char */
+		if ( strstr(stack[sp-1].u.str,"setcachedevice")!=NULL ||
+			strstr(stack[sp-1].u.str,"setcharwidth")!=NULL )
+		    pushio(wrapper,NULL,stack[sp-1].u.str,0);
+		free(stack[sp-1].u.str);
+		sp = 0;
+	    }
+	  break;
+	  case pt_ifelse:
+	    if ( sp>=3 ) {
+		if ( stack[sp-3].type == ps_bool && stack[sp-3].u.tf ) {
+		    if ( stack[sp-2].type==ps_instr )
+			pushio(wrapper,NULL,stack[sp-2].u.str,0);
+		} else {
+		    if ( stack[sp-1].type==ps_instr )
+			pushio(wrapper,NULL,stack[sp-1].u.str,0);
+		}
+		if ( stack[sp-1].type==ps_string || stack[sp-1].type==ps_instr || stack[sp-1].type==ps_lit )
+		    free(stack[sp-1].u.str);
+		if ( stack[sp-2].type==ps_string || stack[sp-2].type==ps_instr || stack[sp-2].type==ps_lit )
+		    free(stack[sp-2].u.str);
+		sp -= 3;
+	    }
+	  break;
+	  case pt_for:
+	    if ( sp>=4 ) {
+		real init, incr, limit;
+		char *func;
+		int cnt;
+
+		if ( stack[sp-4].type == ps_num && stack[sp-3].type==ps_num &&
+			stack[sp-2].type==ps_num && stack[sp-1].type==ps_instr ) {
+		    init = stack[sp-4].u.val;
+		    incr = stack[sp-3].u.val;
+		    limit = stack[sp-2].u.val;
+		    func = stack[sp-1].u.str;
+		    sp -= 4;
+		    cnt = 0;
+		    if ( incr>0 ) {
+			while ( init<=limit ) { ++cnt; init += incr; }
+		    } else if ( incr<0 ) {
+			while ( init>=limit ) { ++cnt; init += incr; }
+		    }
+		    pushio(wrapper,NULL,func,cnt);
+		    free(func);
+		}
+	    }
+	  break;
+	  case pt_loop:
+	    if ( sp>=1 ) {
+		char *func;
+		int cnt;
+
+		if ( stack[sp-1].type==ps_instr ) {
+		    cnt = 0x7fffffff;		/* Loop for ever */
+		    func = stack[sp-1].u.str;
+		    --sp;
+		    pushio(wrapper,NULL,func,cnt);
+		    free(func);
+		}
+	    }
+	  break;
+	  case pt_repeat:
+	    if ( sp>=2 ) {
+		char *func;
+		int cnt;
+
+		if ( stack[sp-2].type==ps_num && stack[sp-1].type==ps_instr ) {
+		    cnt = stack[sp-2].u.val;
+		    func = stack[sp-1].u.str;
+		    sp -= 2;
+		    pushio(wrapper,NULL,func,cnt);
+		    free(func);
+		}
+	    }
+	  break;
+	  case pt_exit:
+	    ioescapeloop(wrapper);
+	  break;
+	  case pt_stopped:
+	    if ( sp>=1 ) {
+		char *func;
+
+		if ( stack[sp-1].type==ps_instr ) {
+		    func = stack[sp-1].u.str;
+		    --sp;
+		    pushio(wrapper,NULL,func,-1);
+		    free(func);
+		}
+	    }
+	  break;
+	  case pt_stop:
+	    sp = ioescapestopped(wrapper,stack,sp,sizeof(stack)/sizeof(stack[0]));
+	  break;
+	  case pt_load:
+	    if ( sp>=1 && stack[sp-1].type==ps_lit ) {
+		kv = lookup(&dict,stack[sp-1].u.str);
+		if ( kv!=NULL ) {
+		    free( stack[sp-1].u.str );
+		    stack[sp-1].type = kv->type;
+		    stack[sp-1].u = kv->u;
+		    if ( kv->type==ps_instr || kv->type==ps_lit )
+			stack[sp-1].u.str = copy(stack[sp-1].u.str);
+		} else
+		    stack[sp-1].type = ps_instr;
+	    }
+	  break;
+	  case pt_def:
+	    sp = AddEntry(&dict,stack,sp);
+	  break;
+	  case pt_bind:
+	    /* a noop in this context */
+	  break;
+	  case pt_setcachedevice:
+	    if ( sp>=6 ) {
+		ec->width = stack[sp-6].u.val;
+		ec->vwidth = stack[sp-5].u.val;
+		/* I don't care about the bounding box */
+		sp-=6;
+	    }
+	  break;
+	  case pt_setcharwidth:
+	    if ( sp>=2 )
+		ec->width = stack[sp-=2].u.val;
+	  break;
+	  case pt_translate:
+	    if ( sp>=1 && stack[sp-1].type==ps_array )
+		sp = DoMatOp(tok,sp,stack);
+	    else if ( sp>=2 ) {
+		transform[4] += stack[sp-2].u.val*transform[0]+stack[sp-1].u.val*transform[2];
+		transform[5] += stack[sp-2].u.val*transform[1]+stack[sp-1].u.val*transform[3];
+		sp -= 2;
+	    }
+	  break;
+	  case pt_scale:
+	    if ( sp>=1 && stack[sp-1].type==ps_array )
+		sp = DoMatOp(tok,sp,stack);
+	    else if ( sp>=2 ) {
+		transform[0] *= stack[sp-2].u.val;
+		transform[1] *= stack[sp-2].u.val;
+		transform[2] *= stack[sp-1].u.val;
+		transform[3] *= stack[sp-1].u.val;
+		/* transform[4,5] are unchanged */
+		sp -= 2;
+	    }
+	  break;
+	  case pt_rotate:
+	    if ( sp>=1 && stack[sp-1].type==ps_array )
+		sp = DoMatOp(tok,sp,stack);
+	    else if ( sp>=1 ) {
+		--sp;
+		t[0] = t[3] = cos(stack[sp].u.val);
+		t[1] = sin(stack[sp].u.val);
+		t[2] = -t[1];
+		t[4] = t[5] = 0;
+		MatMultiply(t,transform,transform);
+	    }
+	  break;
+	  case pt_concat:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type==ps_array ) {
+		    if ( stack[sp-1].u.dict.cnt==6 && stack[sp-1].u.dict.entries[0].type==ps_num ) {
+			--sp;
+			t[5] = stack[sp].u.dict.entries[5].u.val;
+			t[4] = stack[sp].u.dict.entries[4].u.val;
+			t[3] = stack[sp].u.dict.entries[3].u.val;
+			t[2] = stack[sp].u.dict.entries[2].u.val;
+			t[1] = stack[sp].u.dict.entries[1].u.val;
+			t[0] = stack[sp].u.dict.entries[0].u.val;
+			dictfree(&stack[sp].u.dict);
+			MatMultiply(t,transform,transform);
+		    }
+		}
+	    }
+	  break;
+	  case pt_transform:
+	    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+		if ( sp>=3 ) {
+		    DoMatTransform(tok,sp,stack);
+		    --sp;
+		}
+	    } else if ( sp>=2 ) {
+		double x = stack[sp-2].u.val, y = stack[sp-1].u.val;
+		stack[sp-2].u.val = transform[0]*x + transform[1]*y + transform[4];
+		stack[sp-1].u.val = transform[2]*x + transform[3]*y + transform[5];
+	    }
+	  break;
+	  case pt_itransform:
+	    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+		if ( sp>=3 ) {
+		    DoMatTransform(tok,sp,stack);
+		    --sp;
+		}
+	    } else if ( sp>=2 ) {
+		double x = stack[sp-2].u.val, y = stack[sp-1].u.val;
+		MatInverse(t,transform);
+		stack[sp-2].u.val = t[0]*x + t[1]*y + t[4];
+		stack[sp-1].u.val = t[2]*x + t[3]*y + t[5];
+	    }
+	  break;
+	  case pt_dtransform:
+	    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+		if ( sp>=3 ) {
+		    DoMatTransform(tok,sp,stack);
+		    --sp;
+		}
+	    } else if ( sp>=2 ) {
+		double x = stack[sp-2].u.val, y = stack[sp-1].u.val;
+		stack[sp-2].u.val = transform[0]*x + transform[1]*y;
+		stack[sp-1].u.val = transform[2]*x + transform[3]*y;
+	    }
+	  break;
+	  case pt_idtransform:
+	    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+		if ( sp>=3 ) {
+		    DoMatTransform(tok,sp,stack);
+		    --sp;
+		}
+	    } else if ( sp>=2 ) {
+		double x = stack[sp-2].u.val, y = stack[sp-1].u.val;
+		MatInverse(t,transform);
+		stack[sp-2].u.val = t[0]*x + t[1]*y;
+		stack[sp-1].u.val = t[2]*x + t[3]*y;
+	    }
+	  break;
+	  case pt_namelit:
+	    if ( strcmp(tokbuf,"CharProcs")==0 && ec!=NULL ) {
+		HandleType3Reference(wrapper,ec,transform,tokbuf,tokbufsize);
+	    } else if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_lit;
+		stack[sp++].u.str = copy(tokbuf);
+	    }
+	  break;
+	  case pt_exec:
+	    if ( sp>0 && stack[sp-1].type == ps_lit ) {
+		ref = RefCharCreate();
+		ref->sc = (SplineChar *) stack[--sp].u.str;
+		memcpy(ref->transform,transform,sizeof(transform));
+		if ( ec->refs==NULL )
+		    ec->refs = ref;
+		else
+		    lastref->next = ref;
+		lastref = ref;
+	    }
+	  break;
+	  case pt_newpath:
+	    SplinePointListsFree(head);
+	    head = NULL;
+	    cur = NULL;
+	  break;
+	  case pt_lineto: case pt_rlineto:
+	  case pt_moveto: case pt_rmoveto:
+	    if ( sp>=2 || tok==pt_newpath ) {
+		if ( tok==pt_rlineto || tok==pt_rmoveto ) {
+		    current.x += stack[sp-2].u.val;
+		    current.y += stack[sp-1].u.val;
+		    sp -= 2;
+		} else if ( tok==pt_lineto || tok == pt_moveto ) {
+		    current.x = stack[sp-2].u.val;
+		    current.y = stack[sp-1].u.val;
+		    sp -= 2;
+		}
+		pt = chunkalloc(sizeof(SplinePoint));
+		Transform(&pt->me,&current,transform);
+		pt->noprevcp = true; pt->nonextcp = true;
+		if ( tok==pt_moveto || tok==pt_rmoveto ) {
+		    SplinePointList *spl = chunkalloc(sizeof(SplinePointList));
+		    spl->first = spl->last = pt;
+		    if ( cur!=NULL )
+			cur->next = spl;
+		    else
+			head = spl;
+		    cur = spl;
+		} else {
+		    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+			CheckMake(cur->last,pt);
+			SplineMake3(cur->last,pt);
+			cur->last = pt;
+		    }
+		}
+	    } else
+		sp = 0;
+	  break;
+	  case pt_curveto: case pt_rcurveto:
+	    if ( sp>=6 ) {
+		if ( tok==pt_rcurveto ) {
+		    stack[sp-1].u.val += current.y;
+		    stack[sp-3].u.val += current.y;
+		    stack[sp-5].u.val += current.y;
+		    stack[sp-2].u.val += current.x;
+		    stack[sp-4].u.val += current.x;
+		    stack[sp-6].u.val += current.x;
+		}
+		current.x = stack[sp-2].u.val;
+		current.y = stack[sp-1].u.val;
+		if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+		    temp.x = stack[sp-6].u.val; temp.y = stack[sp-5].u.val;
+		    Transform(&cur->last->nextcp,&temp,transform);
+		    cur->last->nonextcp = false;
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    temp.x = stack[sp-4].u.val; temp.y = stack[sp-3].u.val;
+		    Transform(&pt->prevcp,&temp,transform);
+		    Transform(&pt->me,&current,transform);
+		    pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		}
+		sp -= 6;
+	    } else
+		sp = 0;
+	  break;
+	  case pt_arc: case pt_arcn:
+	    if ( sp>=5 ) {
+		real cx, cy, r, a1, a2;
+		cx = stack[sp-5].u.val;
+		cy = stack[sp-4].u.val;
+		r = stack[sp-3].u.val;
+		a1 = stack[sp-2].u.val;
+		a2 = stack[sp-1].u.val;
+		sp -= 5;
+		temp.x = cx+r*cos(a1/180 * 3.1415926535897932);
+		temp.y = cy+r*sin(a1/180 * 3.1415926535897932);
+		if ( temp.x!=current.x || temp.y!=current.y ||
+			!( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) )) {
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    Transform(&pt->me,&temp,transform);
+		    pt->noprevcp = true; pt->nonextcp = true;
+		    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+			CheckMake(cur->last,pt);
+			SplineMake3(cur->last,pt);
+			cur->last = pt;
+		    } else {	/* if no current point, then start here */
+			SplinePointList *spl = chunkalloc(sizeof(SplinePointList));
+			spl->first = spl->last = pt;
+			if ( cur!=NULL )
+			    cur->next = spl;
+			else
+			    head = spl;
+			cur = spl;
+		    }
+		}
+		circlearcsto(a1,a2,cx,cy,r,cur,transform,tok==pt_arcn);
+		current.x = cx+r*cos(a2/180 * 3.1415926535897932);
+		current.y = cy+r*sin(a2/180 * 3.1415926535897932);
+	    } else
+		sp = 0;
+	  break;
+	  case pt_arct: case pt_arcto:
+	    if ( sp>=5 ) {
+		real x1, y1, x2, y2, r;
+		real xt1, xt2, yt1, yt2;
+		x1 = stack[sp-5].u.val;
+		y1 = stack[sp-4].u.val;
+		x2 = stack[sp-3].u.val;
+		y2 = stack[sp-2].u.val;
+		r = stack[sp-1].u.val;
+		sp -= 5;
+
+		xt1 = xt2 = x1; yt1 = yt2 = y1;
+		if ( cur==NULL || cur->first==NULL || (cur->first==cur->last && cur->first->next!=NULL) )
+		    /* Error */;
+		else if ( current.x==x1 && current.y==y1 )
+		    /* Error */;
+		else if (( x1==x2 && y1==y2 ) ||
+			(current.x-x1)*(y2-y1) == (x2-x1)*(current.y-y1) ) {
+		    /* Degenerate case */
+		    current.x = x1; current.y = y1;
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    Transform(&pt->me,&current,transform);
+		    pt->noprevcp = true; pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		} else {
+		    real l1 = sqrt((current.x-x1)*(current.x-x1)+(current.y-y1)*(current.y-y1));
+		    real l2 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
+		    real dx = ((current.x-x1)/l1 + (x2-x1)/l2);
+		    real dy = ((current.y-y1)/l1 + (y2-y1)/l2);
+		    /* the line from (x1,y1) to (x1+dx,y1+dy) contains the center*/
+		    real l3 = sqrt(dx*dx+dy*dy);
+		    real cx, cy, t, tmid;
+		    real a1, amid, a2;
+		    int clockwise = true;
+		    dx /= l3; dy /= l3;
+		    a1 = atan2(current.y-y1,current.x-x1);
+		    a2 = atan2(y2-y1,x2-x1);
+		    amid = atan2(dy,dx) - a1;
+		    tmid = r/sin(amid);
+		    t = r/tan(amid);
+		    if ( t<0 ) {
+			clockwise = false;
+			t = -t;
+			tmid = -tmid;
+		    }
+		    cx = x1+ tmid*dx; cy = y1 + tmid*dy;
+		    xt1 = x1 + t*(current.x-x1)/l1; yt1 = y1 + t*(current.y-y1)/l1;
+		    xt2 = x1 + t*(x2-x1)/l2; yt2 = y1 + t*(y2-y1)/l2;
+		    if ( xt1!=current.x || yt1!=current.y ) {
+			DBasePoint temp;
+			temp.x = xt1; temp.y = yt1;
+			pt = chunkalloc(sizeof(SplinePoint));
+			Transform(&pt->me,&temp,transform);
+			pt->noprevcp = true; pt->nonextcp = true;
+			CheckMake(cur->last,pt);
+			SplineMake3(cur->last,pt);
+			cur->last = pt;
+		    }
+		    a1 = 3*3.1415926535897932/2+a1;
+		    a2 = 3.1415926535897932/2+a2;
+		    if ( !clockwise ) {
+			a1 += 3.1415926535897932;
+			a2 += 3.1415926535897932;
+		    }
+		    circlearcsto(a1*180/3.1415926535897932,a2*180/3.1415926535897932,
+			    cx,cy,r,cur,transform,clockwise);
+		}
+		if ( tok==pt_arcto ) {
+		    stack[sp].type = stack[sp+1].type = stack[sp+2].type = stack[sp+3].type = ps_num;
+		    stack[sp++].u.val = xt1;
+		    stack[sp++].u.val = yt1;
+		    stack[sp++].u.val = xt2;
+		    stack[sp++].u.val = yt2;
+		}
+		current.x = xt2; current.y = yt2;
+	    }
+	  break;
+	  case pt_closepath:
+	    if ( cur!=NULL && cur->first!=NULL && cur->first!=cur->last ) {
+		if ( RealNear(cur->first->me.x,cur->last->me.x) && RealNear(cur->first->me.y,cur->last->me.y) ) {
+		    SplinePoint *oldlast = cur->last;
+		    cur->first->prevcp = oldlast->prevcp;
+		    cur->first->prevcp.x += (cur->first->me.x-oldlast->me.x);
+		    cur->first->prevcp.y += (cur->first->me.y-oldlast->me.y);
+		    cur->first->noprevcp = oldlast->noprevcp;
+		    oldlast->prev->from->next = NULL;
+		    cur->last = oldlast->prev->from;
+		    SplineFree(oldlast->prev);
+		    SplinePointFree(oldlast);
+		}
+		CheckMake(cur->last,cur->first);
+		SplineMake3(cur->last,cur->first);
+		cur->last = cur->first;
+	    }
+	  break;
+	  case pt_setlinecap:
+	    if ( sp>=1 )
+		linecap = stack[--sp].u.val;
+	  break;
+	  case pt_setlinejoin:
+	    if ( sp>=1 )
+		linejoin = stack[--sp].u.val;
+	  break;
+	  case pt_setlinewidth:
+	    if ( sp>=1 )
+		linewidth = stack[--sp].u.val;
+	  break;
+	  case pt_setdash:
+	    if ( sp>=2 && stack[sp-1].type==ps_num && stack[sp-2].type==ps_array ) {
+		sp -= 2;
+		dash_offset = stack[sp+1].u.val;
+		for ( i=0; i<DASH_MAX && i<stack[sp].u.dict.cnt; ++i )
+		    dashes[i] = stack[sp].u.dict.entries[i].u.val;
+		dictfree(&stack[sp].u.dict);
+	    }
+	  break;
+	  case pt_currentlinecap: case pt_currentlinejoin:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = tok==pt_currentlinecap?linecap:linejoin;
+	    }
+	  break;
+	  case pt_currentlinewidth:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = linewidth;
+	    }
+	  break;
+	  case pt_currentdash:
+	    if ( sp+1<sizeof(stack)/sizeof(stack[0]) ) {
+		struct pskeydict dict;
+		for ( i=0; i<DASH_MAX && dashes[i]!=0; ++i );
+		dict.cnt = dict.max = i;
+		dict.entries = calloc(i,sizeof(struct pskeyval));
+                dict.is_executable = false;
+		for ( j=0; j<i; ++j ) {
+		    dict.entries[j].type = ps_num;
+		    dict.entries[j].u.val = dashes[j];
+		}
+		stack[sp].type = ps_array;
+		stack[sp++].u.dict = dict;
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = dash_offset;
+	    }
+	  break;
+	  case pt_currentgray:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = (3*((fore>>16)&0xff) + 6*((fore>>8)&0xff) + (fore&0xff))/2550.;
+	    }
+	  break;
+	  case pt_setgray:
+	    if ( sp>=1 ) {
+		fore = stack[--sp].u.val*255;
+		fore *= 0x010101;
+	    }
+	  break;
+	  case pt_setrgbcolor:
+	    if ( sp>=3 ) {
+		fore = (((int) (stack[sp-3].u.val*255))<<16) +
+			(((int) (stack[sp-2].u.val*255))<<8) +
+			(int) (stack[sp-1].u.val*255);
+		sp -= 3;
+	    }
+	  break;
+	  case pt_currenthsbcolor: case pt_currentrgbcolor:
+	    if ( sp+2<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = stack[sp+1].type = stack[sp+2].type = ps_num;
+		if ( tok==pt_currentrgbcolor ) {
+		    stack[sp++].u.val = ((fore>>16)&0xff)/255.;
+		    stack[sp++].u.val = ((fore>>8)&0xff)/255.;
+		    stack[sp++].u.val = (fore&0xff)/255.;
+		} else {
+		    int r=fore>>16, g=(fore>>8)&0xff, bl=fore&0xff;
+		    int mx, mn;
+		    real h, s, b;
+		    mx = mn = r;
+		    if ( mx>g ) mn=g; else mx=g;
+		    if ( mx<bl ) mx = bl; if ( mn>bl ) mn = bl;
+		    b = mx/255.;
+		    s = h = 0;
+		    if ( mx>0 )
+			s = ((real) (mx-mn))/mx;
+		    if ( s!=0 ) {
+			real rdiff = ((real) (mx-r))/(mx-mn);
+			real gdiff = ((real) (mx-g))/(mx-mn);
+			real bdiff = ((real) (mx-bl))/(mx-mn);
+			if ( rdiff==0 )
+			    h = bdiff-gdiff;
+			else if ( gdiff==0 )
+			    h = 2 + rdiff-bdiff;
+			else
+			    h = 4 + gdiff-rdiff;
+			h /= 6;
+			if ( h<0 ) h += 1;
+		    }
+		    stack[sp++].u.val = h;
+		    stack[sp++].u.val = s;
+		    stack[sp++].u.val = b;
+		}
+	    }
+	  break;
+	  case pt_sethsbcolor:
+	    if ( sp>=3 ) {
+		real h = stack[sp-3].u.val, s = stack[sp-2].u.val, b = stack[sp-1].u.val;
+		int r,g,bl;
+		if ( s==0 )	/* it's grey */
+		    fore = ((int) (b*255)) * 0x010101;
+		else {
+		    real sextant = (h-floor(h))*6;
+		    real mod = sextant-floor(sextant);
+		    real p = b*(1-s), q = b*(1-s*mod), t = b*(1-s*(1-mod));
+		    switch( (int) sextant) {
+		      case 0:
+			r = b*255.; g = t*255.; bl = p*255.;
+		      break;
+		      case 1:
+			r = q*255.; g = b*255.; bl = p*255.;
+		      break;
+		      case 2:
+			r = p*255.; g = b*255.; bl = t*255.;
+		      break;
+		      case 3:
+			r = p*255.; g = q*255.; bl = b*255.;
+		      break;
+		      case 4:
+			r = t*255.; g = p*255.; bl = b*255.;
+		      break;
+		      case 5:
+			r = b*255.; g = p*255.; bl = q*255.;
+		      break;
+		      default:
+		      break;
+		    }
+		    fore = COLOR_CREATE(r,g,bl);
+		}
+		sp -= 3;
+	    }
+	  break;
+	  case pt_currentcmykcolor:
+	    if ( sp+3<sizeof(stack)/sizeof(stack[0]) ) {
+		real c,m,y,k;
+		stack[sp].type = stack[sp+1].type = stack[sp+2].type = stack[sp+3].type = ps_num;
+		y = 1.-(fore&0xff)/255.;
+		m = 1.-((fore>>8)&0xff)/255.;
+		c = 1.-((fore>>16)&0xff)/255.;
+		k = y; if ( k>m ) k=m; if ( k>c ) k=c;
+		if ( k!=1 ) {
+		    y = (y-k)/(1-k);
+		    m = (m-k)/(1-k);
+		    c = (c-k)/(1-k);
+		} else
+		    y = m = c = 0;
+		stack[sp++].u.val = c;
+		stack[sp++].u.val = m;
+		stack[sp++].u.val = y;
+		stack[sp++].u.val = k;
+	    }
+	  break;
+	  case pt_setcmykcolor:
+	    if ( sp>=4 ) {
+		real c=stack[sp-4].u.val,m=stack[sp-3].u.val,y=stack[sp-2].u.val,k=stack[sp-1].u.val;
+		sp -= 4;
+		if ( k==1 )
+		    fore = 0x000000;
+		else {
+		    if (( y = (1-k)*y+k )<0 ) y=0; else if ( y>1 ) y=1;
+		    if (( m = (1-k)*m+k )<0 ) m=0; else if ( m>1 ) m=1;
+		    if (( c = (1-k)*c+k )<0 ) c=0; else if ( c>1 ) c=1;
+		    fore = ((int) ((1-c)*255.)<<16) |
+			    ((int) ((1-m)*255.)<<8) |
+			    ((int) ((1-y)*255.));
+		}
+	    }
+	  break;
+	  case pt_currentpoint:
+	    if ( sp+1<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = current.x;
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = current.y;
+	    }
+	  break;
+	  case pt_fill: case pt_stroke:
+	    if ( head==NULL && ec->splines!=NULL ) {
+		/* assume they did a "gsave fill grestore stroke" (or reverse)*/
+		ent = ec->splines;
+		if ( tok==pt_stroke ) {
+		    ent->u.splines.cap = linecap; ent->u.splines.join = linejoin;
+		    ent->u.splines.stroke_width = linewidth;
+		    memcpy(ent->u.splines.transform,transform,sizeof(transform));
+		}
+	    } else {
+		ent = EntityCreate(head,linecap,linejoin,linewidth,transform,clippath);
+		ent->next = ec->splines;
+		ec->splines = ent;
+	    }
+	    if ( tok==pt_fill )
+		ent->u.splines.fill.col = fore;
+	    else
+		ent->u.splines.stroke.col = fore;
+	    head = NULL; cur = NULL;
+	  break;
+	  case pt_clip:
+	    /* I really should intersect the old clip path with the new, but */
+	    /*  I don't trust my intersect routine, crashes too often */
+	    SplinePointListsFree(clippath);
+	    clippath = SplinePointListCopy(head);
+	    if ( clippath!=NULL && clippath->first!=clippath->last ) {
+		SplineMake3(clippath->last,clippath->first);
+		clippath->last = clippath->first;
+	    }
+	  break;
+	  case pt_imagemask:
+	    i = PSAddImagemask(ec,stack,sp,transform,fore);
+	    while ( sp>i ) {
+		--sp;
+		if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			stack[sp].type==ps_lit )
+		    free(stack[sp].u.str);
+		else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict )
+		    dictfree(&stack[sp].u.dict);
+	    }
+	  break;
+
+	  /* We don't do these right, but at least we'll avoid some errors with this hack */
+	  case pt_save: case pt_currentmatrix:
+	    /* push some junk on the stack */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = 0;
+	    }
+	  /* Fall through into gsave */;
+	  case pt_gsave:
+	    if ( gsp<30 ) {
+		memcpy(gsaves[gsp].transform,transform,sizeof(transform));
+		gsaves[gsp].current = current;
+		gsaves[gsp].linewidth = linewidth;
+		gsaves[gsp].linecap = linecap;
+		gsaves[gsp].linejoin = linejoin;
+		gsaves[gsp].fore = fore;
+		gsaves[gsp].clippath = SplinePointListCopy(clippath);
+		++gsp;
+		/* I should be saving the "current path" too, but that's too hard */
+	    }
+	  break;
+	  case pt_restore: case pt_setmatrix:
+	    /* pop some junk off the stack */
+	    if ( sp>=1 )
+		--sp;
+	  /* Fall through into grestore */;
+	  case pt_grestore:
+	    if ( gsp>0 ) {
+		--gsp;
+		memcpy(transform,gsaves[gsp].transform,sizeof(transform));
+		current = gsaves[gsp].current;
+		linewidth = gsaves[gsp].linewidth;
+		linecap = gsaves[gsp].linecap;
+		linejoin = gsaves[gsp].linejoin;
+		fore = gsaves[gsp].fore;
+		SplinePointListsFree(clippath);
+		clippath = gsaves[gsp].clippath;
+	    }
+	  break;
+	  case pt_null:
+	    /* push a 0. I don't handle pointers properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0;
+		stack[sp++].type = ps_num;
+	    }
+	  break;
+	  case pt_currentoverprint:
+	    /* push false. I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0;
+		stack[sp++].type = ps_bool;
+	    }
+	  break;
+	  case pt_setoverprint:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentflat:
+	    /* push 1.0 (default value). I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 1.0;
+		stack[sp++].type = ps_num;
+	    }
+	  break;
+	  case pt_setflat:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentmiterlimit:
+	    /* push 10.0 (default value). I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 10.0;
+		stack[sp++].type = ps_num;
+	    }
+	  break;
+	  case pt_setmiterlimit:
+	    /* pop one item off stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentpacking:
+	    /* push false (default value). I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0;
+		stack[sp++].type = ps_bool;
+	    }
+	  break;
+	  case pt_setpacking:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentstrokeadjust:
+	    /* push false (default value). I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0;
+		stack[sp++].type = ps_bool;
+	    }
+	  break;
+	  case pt_setstrokeadjust:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentsmoothness:
+	    /* default value is installation dependant. I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 1.0;
+		stack[sp++].type = ps_num;
+	    }
+	  break;
+	  case pt_setsmoothness:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentobjectformat:
+	    /* default value is installation dependant. I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0.0;
+		stack[sp++].type = ps_num;
+	    }
+	  break;
+	  case pt_setobjectformat:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+	  case pt_currentglobal: case pt_currentshared:
+	    /* push false (default value). I don't handle this properly */
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].u.val = 0;
+		stack[sp++].type = ps_bool;
+	    }
+	  break;
+	  case pt_setglobal:
+	    /* pop one item on stack */
+	    if ( sp>=1 )
+		--sp;
+	  break;
+
+	  case pt_openarray: case pt_mark:
+	    if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp++].type = ps_mark;
+	    }
+	  break;
+	  case pt_counttomark:
+	    for ( i=0; (unsigned)i<sp; ++i )
+		if ( stack[sp-1-i].type==ps_mark )
+	    break;
+	    if ( (unsigned)i==sp )
+		LogError( _("No mark in counttomark\n") );
+	    else if ( sp<sizeof(stack)/sizeof(stack[0]) ) {
+		stack[sp].type = ps_num;
+		stack[sp++].u.val = i;
+	    }
+	  break;
+	  case pt_cleartomark:
+	    for ( i=0; (unsigned)i<sp; ++i )
+		if ( stack[sp-1-i].type==ps_mark )
+	    break;
+	    if ( (unsigned)i==sp )
+		LogError( _("No mark in cleartomark\n") );
+	    else {
+		while ( sp>=i ) {
+		    --sp;
+		    if ( stack[sp].type==ps_string || stack[sp].type==ps_instr ||
+			    stack[sp].type==ps_lit )
+			free(stack[sp].u.str);
+		    else if ( stack[sp].type==ps_array || stack[sp].type==ps_dict )
+			dictfree(&stack[sp].u.dict);
+		}
+	    }
+	  break;
+	  case pt_closearray:
+	    for ( i=0; (unsigned)i<sp; ++i )
+		if ( stack[sp-1-i].type==ps_mark )
+	    break;
+	    if ( (unsigned)i==sp )
+		LogError( _("No mark in ] (close array)\n") );
+	    else {
+		struct pskeydict dict;
+		dict.cnt = dict.max = i;
+		dict.entries = calloc(i,sizeof(struct pskeyval));
+                dict.is_executable = false;
+		for ( j=0; j<i; ++j ) {
+		    dict.entries[j].type = stack[sp-i+j].type;
+		    dict.entries[j].u = stack[sp-i+j].u;
+		    /* don't need to copy because the things on the stack */
+		    /*  are being popped (don't need to free either) */
+		}
+		collectgarbage(&tofrees,&dict);
+		sp = sp-i;
+		stack[sp-1].type = ps_array;
+		stack[sp-1].u.dict = dict;
+	    }
+	  break;
+	  case pt_array:
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		struct pskeydict dict;
+		dict.cnt = dict.max = stack[sp-1].u.val;
+		dict.entries = calloc(dict.cnt,sizeof(struct pskeyval));
+                dict.is_executable = false;
+		/* all entries are inited to void */
+		stack[sp-1].type = ps_array;
+		stack[sp-1].u.dict = dict;
+	    }
+	  break;
+	  case pt_aload:
+	    sp = aload(sp,stack,sizeof(stack)/sizeof(stack[0]),&tofrees);
+	  break;
+	  case pt_astore:
+	    if ( sp>=1 && stack[sp-1].type==ps_array ) {
+		struct pskeydict dict;
+		--sp;
+		dict = stack[sp].u.dict;
+		if ( sp>=dict.cnt ) {
+		    for ( i=dict.cnt-1; i>=0 ; --i ) {
+			--sp;
+			dict.entries[i].type = stack[sp].type;
+			dict.entries[i].u = stack[sp].u;
+		    }
+		}
+		stack[sp].type = ps_array;
+		stack[sp].u.dict = dict;
+		++sp;
+	    }
+	  break;
+
+	  case pt_output: case pt_outputd: case pt_print:
+	    if ( sp>=1 ) {
+		--sp;
+		switch ( stack[sp].type ) {
+		  case ps_num:
+		    printf( "%g", (double) stack[sp].u.val );
+		  break;
+		  case ps_bool:
+		    printf( "%s", stack[sp].u.tf ? "true" : "false" );
+		  break;
+		  case ps_string: case ps_instr: case ps_lit:
+		    if ( tok==pt_outputd )
+			printf( stack[sp].type==ps_lit ? "/" :
+				stack[sp].type==ps_string ? "(" : "{" );
+		    printf( "%s", stack[sp].u.str );
+		    if ( tok==pt_outputd )
+			printf( stack[sp].type==ps_lit ? "" :
+				stack[sp].type==ps_string ? ")" : "}" );
+		    free(stack[sp].u.str);
+		  break;
+		  case ps_void:
+		    printf( "-- void --" );
+		  break;
+		  case ps_array:
+		    if ( tok==pt_outputd ) {
+			printarray(&stack[sp].u.dict);
+			dictfree(&stack[sp].u.dict);
+		  break;
+		    } /* else fall through */
+		    dictfree(&stack[sp].u.dict);
+		  default:
+		    printf( "-- nostringval --" );
+		  break;
+		}
+		if ( tok==pt_output || tok==pt_outputd )
+		    printf( "\n" );
+	    } else
+		LogError( _("Nothing on stack to print\n") );
+	  break;
+
+	  case pt_cvi: case pt_cvr:
+	    /* I shan't distinguish between integers and reals */
+	    if ( sp>=1 && stack[sp-1].type==ps_string ) {
+		double val = strtod(stack[sp-1].u.str,NULL);
+		free(stack[sp-1].u.str);
+		stack[sp-1].u.val = val;
+		stack[sp-1].type = ps_num;
+	    }
+	  break;
+	  case pt_cvlit:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type==ps_array )
+		    stack[sp-1].u.dict.is_executable = false;
+	    }
+	  case pt_cvn:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type==ps_string )
+		    stack[sp-1].type = ps_lit;
+	    }
+	  case pt_cvx:
+	    if ( sp>=1 ) {
+		if ( stack[sp-1].type==ps_array )
+		    stack[sp-1].u.dict.is_executable = true;
+	    }
+	  break;
+	  case pt_cvrs:
+	    if ( sp>=3 && stack[sp-1].type==ps_string &&
+		    stack[sp-2].type==ps_num &&
+		    stack[sp-3].type==ps_num ) {
+		if ( stack[sp-2].u.val==8 )
+		    sprintf( stack[sp-1].u.str, "%o", (int) stack[sp-3].u.val );
+		else if ( stack[sp-2].u.val==16 )
+		    sprintf( stack[sp-1].u.str, "%X", (int) stack[sp-3].u.val );
+		else /* default to radix 10 no matter what they asked for */
+		    sprintf( stack[sp-1].u.str, "%g", (double) stack[sp-3].u.val );
+		stack[sp-3] = stack[sp-1];
+		sp-=2;
+	    }
+	  break;
+	  case pt_cvs:
+	    if ( sp>=2 && stack[sp-1].type==ps_string ) {
+		switch ( stack[sp].type ) {
+		  case ps_num:
+		    sprintf( stack[sp-1].u.str, "%g", (double) stack[sp-2].u.val );
+		  break;
+		  case ps_bool:
+		    sprintf( stack[sp-1].u.str, "%s", stack[sp-2].u.tf ? "true" : "false" );
+		  break;
+		  case ps_string: case ps_instr: case ps_lit:
+		    sprintf( stack[sp-1].u.str, "%s", stack[sp-2].u.str );
+		    free(stack[sp].u.str);
+		  break;
+		  case ps_void:
+		    printf( "-- void --" );
+		  break;
+		  case ps_array:
+		    dictfree(&stack[sp].u.dict);
+		  default:
+		    sprintf( stack[sp-1].u.str, "-- nostringval --" );
+		  break;
+		}
+		stack[sp-2] = stack[sp-1];
+		--sp;
+	    }
+	  break;
+	  case pt_stringop:	/* the string keyword, not the () thingy */
+	    if ( sp>=1 && stack[sp-1].type==ps_num ) {
+		stack[sp-1].type = ps_string;
+		stack[sp-1].u.str = calloc(stack[sp-1].u.val+1,1);
+	    }
+	  break;
+
+	  case pt_unknown:
+	    if ( !warned ) {
+		LogError( _("Warning: Unable to parse token %s, some features may be lost\n"), tokbuf );
+		warned = true;
+	    }
+	  break;
+
+	  default:
+	  break;
+	}}
+    }
+ done:
+    if ( rs!=NULL ) {
+	int i, cnt, j;
+	for ( i=sp-1; i>=0; --i )
+	    if ( stack[i].type!=ps_num )
+	break;
+	cnt = sp-1-i;
+	if ( cnt>rs->max ) cnt = rs->max;
+	rs->cnt = cnt;
+	for ( j=i+1; (unsigned)j<sp; ++j )
+	    rs->stack[j-i-1] = stack[j].u.val;
+    }
+    freestuff(stack,sp,&dict,&gb,&tofrees);
+    if ( head!=NULL ) {
+	ent = EntityCreate(head,linecap,linejoin,linewidth,transform,clippath);
+	ent->next = ec->splines;
+	ec->splines = ent;
+    }
+    while ( gsp>0 ) {
+	--gsp;
+	SplinePointListsFree(gsaves[gsp].clippath);
+    }
+    SplinePointListsFree(clippath);
+    ECCategorizePoints(ec);
+    if ( ec->width == UNDEFINED_WIDTH )
+	ec->width = wrapper->advance_width;
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    free(tokbuf);
+}
+
+static void InterpretPS(FILE *ps, char *psstr, EntityChar *ec, RetStack *rs) {
+    IO wrapper;
+
+    memset(&wrapper,0,sizeof(wrapper));
+    wrapper.advance_width = UNDEFINED_WIDTH;
+    pushio(&wrapper,ps,psstr,0);
+    _InterpretPS(&wrapper,ec,rs);
+}
+
+static SplinePointList *EraseStroke(SplineChar *sc,SplinePointList *head,SplinePointList *erase) {
+    SplineSet *spl, *last;
+    SplinePoint *sp;
+
+    if ( head==NULL ) {
+	/* Pointless, but legal */
+	SplinePointListsFree(erase);
+return( NULL );
+    }
+
+    last = NULL;
+    for ( spl=head; spl!=NULL; spl=spl->next ) {
+	for ( sp=spl->first; sp!=NULL; ) {
+	    sp->selected = false;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==spl->first )
+	break;
+	}
+	last = spl;
+    }
+    for ( spl=erase; spl!=NULL; spl=spl->next ) {
+	for ( sp=spl->first; sp!=NULL; ) {
+	    sp->selected = true;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==spl->first )
+	break;
+	}
+    }
+    last->next = erase;
+return( SplineSetRemoveOverlap(sc,head,over_exclude) );
+}
+
+static Entity *EntityReverse(Entity *ent) {
+    Entity *next, *last = NULL;
+
+    while ( ent!=NULL ) {
+	next = ent->next;
+	ent->next = last;
+	last = ent;
+	ent = next;
+    }
+return( last );
+}
+
+static SplinePointList *SplinesFromLayers(SplineChar *sc,int *flags, int tostroke) {
+    int layer;
+    SplinePointList *head=NULL, *last, *new, *nlast, *temp, *each, *transed;
+    StrokeInfo si;
+    /*SplineSet *spl;*/
+    int handle_eraser;
+    real inversetrans[6], transform[6];
+    int changed;
+
+    if ( tostroke ) {
+	for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
+	    if ( sc->layers[layer].splines==NULL )
+	continue;
+	    else if ( head==NULL )
+		head = sc->layers[layer].splines;
+	    else
+		last->next = sc->layers[layer].splines;
+	    for ( last = sc->layers[layer].splines; last->next!=NULL; last=last->next );
+	    sc->layers[layer].splines = NULL;
+	}
+return( head );
+    }
+
+    if ( *flags==-1 )
+	*flags = PsStrokeFlagsDlg();
+
+    if ( *flags & sf_correctdir ) {
+	for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) if ( sc->layers[layer].dofill )
+	    SplineSetsCorrect(sc->layers[layer].splines,&changed);
+    }
+
+    handle_eraser = *flags & sf_handle_eraser;
+
+    for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
+	if ( sc->layers[layer].dostroke ) {
+	    memset(&si,'\0',sizeof(si));
+	    si.join = sc->layers[layer].stroke_pen.linejoin;
+	    si.cap = sc->layers[layer].stroke_pen.linecap;
+	    si.radius = sc->layers[layer].stroke_pen.width/2.0f;
+	    if ( sc->layers[layer].stroke_pen.width==WIDTH_INHERITED )
+		si.radius = .5;
+	    if ( si.cap == lc_inherited ) si.cap = lc_butt;
+	    if ( si.join == lj_inherited ) si.join = lj_miter;
+	    new = NULL;
+	    memcpy(transform,sc->layers[layer].stroke_pen.trans,4*sizeof(real));
+	    transform[4] = transform[5] = 0;
+	    MatInverse(inversetrans,transform);
+	    transed = SplinePointListTransform(SplinePointListCopy(
+		    sc->layers[layer].splines),inversetrans,tpt_AllPoints);
+	    for ( each = transed; each!=NULL; each=each->next ) {
+		temp = SplineSetStroke(each,&si,sc->layers[layer].order2);
+		if ( new==NULL )
+		    new=temp;
+		else
+		    nlast->next = temp;
+		if ( temp!=NULL )
+		    for ( nlast=temp; nlast->next!=NULL; nlast=nlast->next );
+	    }
+	    new = SplinePointListTransform(new,transform,tpt_AllPoints);
+	    SplinePointListsFree(transed);
+	    if ( handle_eraser && sc->layers[layer].stroke_pen.brush.col==0xffffff ) {
+		head = EraseStroke(sc,head,new);
+		last = head;
+		if ( last!=NULL )
+		    for ( ; last->next!=NULL; last=last->next );
+	    } else {
+		if ( head==NULL )
+		    head = new;
+		else
+		    last->next = new;
+		if ( new!=NULL )
+		    for ( last = new; last->next!=NULL; last=last->next );
+	    }
+	}
+	if ( sc->layers[layer].dofill ) {
+	    if ( handle_eraser && sc->layers[layer].fill_brush.col==0xffffff ) {
+		head = EraseStroke(sc,head,sc->layers[layer].splines);
+		last = head;
+		if ( last!=NULL )
+		    for ( ; last->next!=NULL; last=last->next );
+	    } else {
+		new = SplinePointListCopy(sc->layers[layer].splines);
+		if ( head==NULL )
+		    head = new;
+		else
+		    last->next = new;
+		if ( new!=NULL )
+		    for ( last = new; last->next!=NULL; last=last->next );
+	    }
+	}
+    }
+return( head );
+}
+
+void SFSplinesFromLayers(SplineFont *sf,int tostroke) {
+    /* User has turned off multi-layer, flatten the font */
+    int i, layer;
+    int flags= -1;
+    Layer *new;
+    CharViewBase *cv;
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	SplineSet *splines = SplinesFromLayers(sc,&flags,tostroke);
+	RefChar *head=NULL, *last=NULL;
+	for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
+	    if ( head==NULL )
+		head = last = sc->layers[layer].refs;
+	    else
+		last->next = sc->layers[layer].refs;
+	    if ( last!=NULL )
+		while ( last->next!=NULL ) last = last->next;
+	    sc->layers[layer].refs = NULL;
+	}
+	new = calloc(2,sizeof(Layer));
+	new[ly_back] = sc->layers[ly_back];
+	memset(&sc->layers[ly_back],0,sizeof(Layer));
+	LayerDefault(&new[ly_fore]);
+	new[ly_fore].splines = splines;
+	new[ly_fore].refs = head;
+	for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
+	    SplinePointListsMDFree(sc,sc->layers[layer].splines);
+	    RefCharsFree(sc->layers[layer].refs);
+	    ImageListsFree(sc->layers[layer].images);
+	}
+	free(sc->layers);
+	sc->layers = new;
+	sc->layer_cnt = 2;
+	for ( cv=sc->views; cv!=NULL; cv=cv->next ) {
+	    cv->layerheads[dm_back] = &sc->layers[ly_back];
+	    cv->layerheads[dm_fore] = &sc->layers[ly_fore];
+	}
+    }
+    SFReinstanciateRefs(sf);
+}
+
+void SFSetLayerWidthsStroked(SplineFont *sf, real strokewidth) {
+    int i;
+    /* We changed from a stroked font to a multilayered font */
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->layers[ly_fore].dofill = false;
+	sc->layers[ly_fore].dostroke = true;
+	sc->layers[ly_fore].stroke_pen.width = strokewidth;
+    }
+}
+
+static void EntityCharCorrectDir(EntityChar *ec) {
+    SplineSet *ss;
+    Entity *ent;
+    int changed;
+
+    for ( ent=ec->splines; ent!=NULL; ent = ent->next ) {
+	/* ignore splines which are only stoked, but not filled */
+	if ( ent->type == et_splines && ent->u.splines.fill.col!=0xffffffff ) {
+	    /* Correct the direction of each stroke or fill with respect to */
+	    /*  the splines in it */
+	    SplineSetsCorrect(ent->u.splines.splines,&changed);
+	    if ( ent->u.splines.fill.col==0xffffff ) {
+		/* If they are filling with white, then assume they mean */
+		/*  an internal area that should be drawn backwards */
+		for ( ss=ent->u.splines.splines; ss!=NULL; ss=ss->next )
+		    SplineSetReverse(ss);
+	    }
+	    SplineSetsCorrect(ent->clippath,&changed);
+	}
+    }
+}
+
+void EntityDefaultStrokeFill(Entity *ent) {
+    while ( ent!=NULL ) {
+	if ( ent->type == et_splines &&
+		ent->u.splines.stroke.col==0xffffffff &&
+		ent->u.splines.fill.col==0xffffffff ) {
+	    SplineSet *spl;
+	    int all=1;
+	    for ( spl=ent->u.splines.splines; spl!=NULL; spl=spl->next )
+		if ( spl->first->prev!=NULL ) {
+		    all = false;
+	    break;
+		}
+	    if ( all && ent->u.splines.splines!=NULL &&
+		    (ent->u.splines.stroke_width==0 || ent->u.splines.stroke_width==WIDTH_INHERITED))
+		ent->u.splines.stroke_width=40;		/* random guess */
+	    if (ent->u.splines.stroke_width==0 || ent->u.splines.stroke_width==WIDTH_INHERITED)
+		ent->u.splines.fill.col = COLOR_INHERITED;
+	    else
+		ent->u.splines.stroke.col = COLOR_INHERITED;
+	}
+	ent = ent->next;
+    }
+}
+
+SplinePointList *SplinesFromEntityChar(EntityChar *ec,int *flags,int is_stroked) {
+    Entity *ent, *next;
+    SplinePointList *head=NULL, *last, *new, *nlast, *temp, *each, *transed;
+    StrokeInfo si;
+    real inversetrans[6];
+    /*SplineSet *spl;*/
+    int handle_eraser = false;
+    int ask = false;
+
+    EntityDefaultStrokeFill(ec->splines);
+
+    if ( !is_stroked ) {
+
+	if ( *flags==-1 ) {
+	    for ( ent=ec->splines; ent!=NULL; ent = ent->next ) {
+		if ( ent->type == et_splines &&
+			(ent->u.splines.fill.col==0xffffff ||
+			 /*ent->u.splines.clippath!=NULL ||*/
+			 (ent->u.splines.stroke_width!=0 && ent->u.splines.stroke.col!=0xffffffff))) {
+		    ask = true;
+	    break;
+		}
+	    }
+	    if ( ask )
+		*flags = PsStrokeFlagsDlg();
+	}
+
+	if ( *flags & sf_correctdir )		/* Will happen if flags still unset (-1) */
+	    EntityCharCorrectDir(ec);
+
+	handle_eraser = *flags!=-1 && (*flags & sf_handle_eraser);
+	if ( handle_eraser )
+	    ec->splines = EntityReverse(ec->splines);
+    }
+
+    for ( ent=ec->splines; ent!=NULL; ent = next ) {
+	next = ent->next;
+	if ( ent->type == et_splines && is_stroked ) {
+	    if ( head==NULL )
+		head = ent->u.splines.splines;
+	    else
+		last->next = ent->u.splines.splines;
+	    if ( ent->u.splines.splines!=NULL )
+		for ( last = ent->u.splines.splines; last->next!=NULL; last=last->next );
+	    ent->u.splines.splines = NULL;
+	} else if ( ent->type == et_splines ) {
+	    if ( ent->u.splines.stroke.col!=0xffffffff &&
+		    (ent->u.splines.fill.col==0xffffffff || ent->u.splines.stroke_width!=0)) {
+		/* What does a stroke width of 0 mean? PS Says minimal width line */
+		/* How do we implement that? Special case: If filled and stroked 0, then */
+		/*  ignore the stroke. This idiom is used by MetaPost sometimes and means */
+		/*  no stroke */
+		memset(&si,'\0',sizeof(si));
+		si.join = ent->u.splines.join;
+		si.cap = ent->u.splines.cap;
+		si.radius = ent->u.splines.stroke_width/2;
+		if ( ent->u.splines.stroke_width==WIDTH_INHERITED )
+		    si.radius = .5;
+		if ( si.cap == lc_inherited ) si.cap = lc_butt;
+		if ( si.join == lj_inherited ) si.join = lj_miter;
+		new = NULL;
+		MatInverse(inversetrans,ent->u.splines.transform);
+		transed = SplinePointListTransform(SplinePointListCopy(
+			ent->u.splines.splines),inversetrans,tpt_AllPoints);
+		for ( each = transed; each!=NULL; each=each->next ) {
+		    temp = SplineSetStroke(each,&si,false);
+		    if ( new==NULL )
+			new=temp;
+		    else
+			nlast->next = temp;
+		    if ( temp!=NULL )
+			for ( nlast=temp; nlast->next!=NULL; nlast=nlast->next );
+		}
+		new = SplinePointListTransform(new,ent->u.splines.transform,tpt_AllPoints);
+		SplinePointListsFree(transed);
+		if ( handle_eraser && ent->u.splines.stroke.col==0xffffff ) {
+		    head = EraseStroke(ec->sc,head,new);
+		    last = head;
+		    if ( last!=NULL )
+			for ( ; last->next!=NULL; last=last->next );
+		} else {
+		    if ( head==NULL )
+			head = new;
+		    else
+			last->next = new;
+		    if ( new!=NULL )
+			for ( last = new; last->next!=NULL; last=last->next );
+		}
+	    }
+	    /* If they have neither a stroke nor a fill, pretend they said fill */
+	    if ( ent->u.splines.fill.col==0xffffffff && ent->u.splines.stroke.col!=0xffffffff )
+		SplinePointListsFree(ent->u.splines.splines);
+	    else if ( handle_eraser && ent->u.splines.fill.col==0xffffff ) {
+		head = EraseStroke(ec->sc,head,ent->u.splines.splines);
+		last = head;
+		if ( last!=NULL )
+		    for ( ; last->next!=NULL; last=last->next );
+	    } else {
+		new = ent->u.splines.splines;
+		if ( head==NULL )
+		    head = new;
+		else
+		    last->next = new;
+		if ( new!=NULL )
+		    for ( last = new; last->next!=NULL; last=last->next );
+	    }
+	}
+	SplinePointListsFree(ent->clippath);
+	free(ent);
+    }
+return( head );
+}
+
+SplinePointList *SplinesFromEntities(Entity *ent,int *flags,int is_stroked) {
+    EntityChar ec;
+
+    memset(&ec,'\0',sizeof(ec));
+    ec.splines = ent;
+return( SplinesFromEntityChar(&ec,flags,is_stroked));
+}
+
+SplinePointList *SplinePointListInterpretPS(FILE *ps,int flags,int is_stroked, int *width) {
+    EntityChar ec;
+    SplineChar sc;
+
+    memset(&ec,'\0',sizeof(ec));
+    ec.width = ec.vwidth = UNDEFINED_WIDTH;
+    memset(&sc,0,sizeof(sc)); sc.name = "<No particular character>";
+    ec.sc = ≻
+    InterpretPS(ps,NULL,&ec,NULL);
+    if ( width!=NULL )
+	*width = ec.width;
+return( SplinesFromEntityChar(&ec,&flags,is_stroked));
+}
+
+Entity *EntityInterpretPS(FILE *ps,int *width) {
+    EntityChar ec;
+
+    memset(&ec,'\0',sizeof(ec));
+    ec.width = ec.vwidth = UNDEFINED_WIDTH;
+    InterpretPS(ps,NULL,&ec,NULL);
+    if ( width!=NULL )
+	*width = ec.width;
+return( ec.splines );
+}
+
+static RefChar *revrefs(RefChar *cur) {
+    RefChar *p, *n;
+
+    if ( cur==NULL )
+return( NULL );
+
+    p = NULL;
+    for ( ; (n=cur->next)!=NULL; cur = n ) {
+	cur->next = p;
+	p = cur;
+    }
+    cur->next = p;
+return( cur );
+}
+
+static void SCInterpretPS(FILE *ps,SplineChar *sc) {
+    EntityChar ec;
+    real dval;
+    char tokbuf[10];
+    IO wrapper;
+    int ch;
+
+    while ( isspace(ch = getc(ps)) );
+    ungetc(ch,ps);
+
+    memset(&wrapper,0,sizeof(wrapper));
+    wrapper.advance_width = UNDEFINED_WIDTH;
+    if ( ch!='<' ) {
+	pushio(&wrapper,ps,NULL,0);
+
+	if ( nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf))!=pt_opencurly )
+	    LogError( _("We don't understand this font\n") );
+    } else {
+	(void) getc(ps);
+	pushfogio(&wrapper,ps);
+    }
+    memset(&ec,'\0',sizeof(ec));
+    ec.fromtype3 = true;
+    ec.sc = sc;
+    _InterpretPS(&wrapper,&ec,NULL);
+    sc->width = ec.width;
+    sc->layer_cnt = 1;
+    SCAppendEntityLayers(sc,ec.splines);
+    if ( sc->layer_cnt==1 ) ++sc->layer_cnt;
+    sc->layers[ly_fore].refs = revrefs(ec.refs);
+    free(wrapper.top);
+}
+
+void PSFontInterpretPS(FILE *ps,struct charprocs *cp,char **encoding) {
+    char tokbuf[100];
+    int tok,i, j;
+    real dval;
+    SplineChar *sc; EntityChar dummy;
+    RefChar *p, *ref, *next;
+    IO wrapper;
+
+    wrapper.top = NULL;
+    wrapper.advance_width = UNDEFINED_WIDTH;
+    pushio(&wrapper,ps,NULL,0);
+
+    while ( (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof && tok!=pt_end ) {
+	if ( tok==pt_namelit ) {
+	    if ( cp->next>=cp->cnt ) {
+		++cp->cnt;
+		cp->keys = realloc(cp->keys,cp->cnt*sizeof(char *));
+		cp->values = realloc(cp->values,cp->cnt*sizeof(char *));
+	    }
+	    if ( cp->next<cp->cnt ) {
+		sc = SplineCharCreate(2);
+		cp->keys[cp->next] = copy(tokbuf);
+		cp->values[cp->next++] = sc;
+		sc->name = copy(tokbuf);
+		SCInterpretPS(ps,sc);
+       		ff_progress_next();
+	    } else {
+		memset(&dummy,0,sizeof(dummy));
+		dummy.fromtype3 = true;
+		InterpretPS(ps,NULL,&dummy,NULL);
+	    }
+	}
+    }
+    free(wrapper.top);
+
+    /* References were done by name in the postscript. we stored the names in */
+    /*  ref->sc (which is a hack). Now look up all those names and replace */
+    /*  with the appropriate splinechar. If we can't find anything then throw */
+    /*  out the reference */
+    /* Further fixups come later, where all ps refs are fixedup */
+    for ( i=0; i<cp->next; ++i ) {
+	for ( p=NULL, ref=cp->values[i]->layers[ly_fore].refs; ref!=NULL; ref=next ) {
+	    char *refname = (char *) (ref->sc);
+	    next = ref->next;
+	    if ( ref->sc==NULL )
+		refname=encoding[ref->orig_pos];
+	    for ( j=0; j<cp->next; ++j )
+		if ( strcmp(cp->keys[j],refname)==0 )
+	    break;
+	    free(ref->sc);	/* a string, not a splinechar */
+	    if ( j!=cp->next ) {
+		ref->sc = cp->values[j];
+		SCMakeDependent(cp->values[i],ref->sc);
+		ref->adobe_enc = getAdobeEnc(ref->sc->name);
+		ref->checked = true;
+		p = ref;
+	    } else {
+		if ( p==NULL )
+		    cp->values[i]->layers[ly_fore].refs = next;
+		else
+		    p->next = next;
+		ref->next = NULL;
+		RefCharFree(ref);
+	    }
+	}
+    }
+}
+
+/* Slurp up an encoding in the form:
+ /Enc-name [
+    /charname
+    ...
+ ] def
+We're not smart here no: 0 1 255 {1 index exch /.notdef put} for */
+Encoding *PSSlurpEncodings(FILE *file) {
+    char *names[1024];
+    int32 encs[1024];
+    Encoding *item, *head = NULL, *last;
+    char *encname;
+    char tokbuf[200];
+    IO wrapper;
+    real dval;
+    size_t i, any;
+    int max, enc, codepointsonly, tok;
+
+    wrapper.top = NULL;
+    wrapper.advance_width = UNDEFINED_WIDTH;
+    pushio(&wrapper,file,NULL,0);
+
+    while ( (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof ) {
+	encname = NULL;
+	if ( tok==pt_namelit ) {
+	    encname = copy(tokbuf);
+	    tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf));
+	}
+	if ( tok!=pt_openarray && tok!=pt_opencurly )
+return( head );
+	for ( i=0; i<sizeof(names)/sizeof(names[0]); ++i ) {
+	    encs[i] = -1;
+	    names[i]=NULL;
+	}
+	codepointsonly = CheckCodePointsComment(&wrapper);
+
+	max = -1; any = 0;
+	for (i = 0; (tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf)))!=pt_eof &&
+                 tok!=pt_closearray && tok!=pt_closecurly;
+             i++) {
+	    if ( tok==pt_namelit && i<sizeof(names)/sizeof(names[0]) ) {
+		max = i;
+		if ( strcmp(tokbuf,".notdef")==0 ) {
+		    encs[i] = -1;
+		} else if ( (enc=UniFromName(tokbuf,ui_none,&custom))!=-1 ) {
+		    encs[i] = enc;
+		    /* Used not to do this, but there are several legal names */
+		    /*  for some slots and people get unhappy (rightly) if we */
+		    /*  use the wrong one */
+		    names[i] = copy(tokbuf);
+		    any = 1;
+		} else {
+		    names[i] = copy(tokbuf);
+		    any = 1;
+		}
+	    }
+	}
+	if ( encname!=NULL ) {
+	    tok = nextpstoken(&wrapper,&dval,tokbuf,sizeof(tokbuf));
+	    if ( tok==pt_def ) {
+		/* Good */
+	    } else {
+        	/* TODO! */
+        	/* I guess it's not good... */
+	    }
+	}
+	if ( max!=-1 ) {
+	    if ( ++max<256 ) max = 256;
+	    item = calloc(1,sizeof(Encoding));
+	    item->enc_name = encname;
+	    item->char_cnt = max;
+	    item->unicode = malloc(max*sizeof(int32));
+	    memcpy(item->unicode,encs,max*sizeof(int32));
+	    if ( any && !codepointsonly ) {
+		item->psnames = calloc(max,sizeof(char *));
+		memcpy(item->psnames,names,max*sizeof(char *));
+	    } else {
+		for ( i=0; i<max; ++i )
+		    free(names[i]);
+	    }
+	    if ( head==NULL )
+		head = item;
+	    else
+		last->next = item;
+	    last = item;
+	}
+    }
+
+return( head );
+}
+
+int EvaluatePS(char *str,real *stack,int size) {
+    EntityChar ec;
+    RetStack rs;
+
+    memset(&ec,'\0',sizeof(ec));
+    memset(&rs,'\0',sizeof(rs));
+    rs.max = size;
+    rs.stack = stack;
+    InterpretPS(NULL,str,&ec,&rs);
+return( rs.cnt );
+}
+
+static void closepath(SplinePointList *cur, int is_type2) {
+    if ( cur!=NULL && cur->first==cur->last && cur->first->prev==NULL && is_type2 )
+return;		/* The "path" is just a single point created by a moveto */
+		/* Probably we're just doing another moveto */
+    if ( cur!=NULL && cur->first!=NULL && cur->first!=cur->last ) {
+/* I allow for greater errors here than I do in the straight postscript code */
+/*  because: 1) the rel-rel operators will accumulate more rounding errors   */
+/*  2) I only output 2 decimal digits after the decimal in type1 output */
+	if ( RealWithin(cur->first->me.x,cur->last->me.x,.05) && RealWithin(cur->first->me.y,cur->last->me.y,.05) ) {
+	    SplinePoint *oldlast = cur->last;
+	    cur->first->prevcp = oldlast->prevcp;
+	    cur->first->prevcp.x += (cur->first->me.x-oldlast->me.x);
+	    cur->first->prevcp.y += (cur->first->me.y-oldlast->me.y);
+	    cur->first->noprevcp = oldlast->noprevcp;
+	    oldlast->prev->from->next = NULL;
+	    cur->last = oldlast->prev->from;
+	    chunkfree(oldlast->prev,sizeof(*oldlast));
+	    chunkfree(oldlast->hintmask,sizeof(HintMask));
+	    chunkfree(oldlast,sizeof(*oldlast));
+	}
+	CheckMake(cur->last,cur->first);
+	SplineMake3(cur->last,cur->first);
+	cur->last = cur->first;
+    }
+}
+
+static void UnblendFree(StemInfo *h ) {
+    while ( h!=NULL ) {
+	chunkfree(h->u.unblended,sizeof(real [2][MmMax]));
+	h->u.unblended = NULL;
+	h = h->next;
+    }
+}
+
+static StemInfo *HintsAppend(StemInfo *to,StemInfo *extra) {
+    StemInfo *h;
+
+    if ( to==NULL )
+return( extra );
+    if ( extra==NULL )
+return( to );
+    for ( h=to; h->next!=NULL; h=h->next );
+    h->next = extra;
+return( to );
+}
+
+static StemInfo *HintNew(double start,double width) {
+    StemInfo *h;
+
+    h = chunkalloc(sizeof(StemInfo));
+    h->start = start;
+    h->width = width;
+return( h );
+}
+
+static void RemapHintMask(HintMask *hm,int mapping[96],int max) {
+    HintMask rpl;
+    int i, mb;
+
+    if ( hm==NULL )
+return;
+
+    if ( max>96 ) max = 96;
+    mb = (max+7)>>3;
+
+    memset(&rpl,0,mb);
+    for ( i=0; i<max; ++i ) if ( (*hm)[i>>3]&(0x80>>(i&0x7)) )
+	rpl[mapping[i]>>3] |= (0x80>>(mapping[i]&0x7));
+    memcpy(hm,&rpl,mb);
+}
+
+static void HintsRenumber(SplineChar *sc) {
+    /* In a type1 font the hints may get added to our hint list in a semi- */
+    /*  random order. In an incorrect type2 font the same thing could happen. */
+    /*  Force the order to be correct, and then update all masks */
+    int mapping[96];
+    int i, max;
+    StemInfo *h;
+    SplineSet *spl;
+    SplinePoint *sp;
+
+    for ( i=0; i<96; ++i ) mapping[i] = i;
+
+    i = 0;
+    for ( h=sc->hstem; h!=NULL; h=h->next ) {
+	if ( h->hintnumber<96 && i<96 ) {
+	    mapping[h->hintnumber] = i;
+	    h->hintnumber = i++;
+	}
+	chunkfree(h->u.unblended,sizeof(real [2][MmMax]));
+	h->u.unblended = NULL;
+    }
+    for ( h=sc->vstem; h!=NULL; h=h->next ) {
+	if ( h->hintnumber<96 && i<96 ) {
+	    mapping[h->hintnumber] = i;
+	    h->hintnumber = i++;
+	}
+	chunkfree(h->u.unblended,sizeof(real [2][MmMax]));
+	h->u.unblended = NULL;
+    }
+    max = i;
+    for ( i=0; i<max; ++i )
+	if ( mapping[i]!=i )
+    break;
+    if ( i==max )
+return;				/* Didn't change the order */
+
+    for ( i=0; i<sc->countermask_cnt; ++i )
+	RemapHintMask(&sc->countermasks[i],mapping,max);
+    for ( spl = sc->layers[ly_fore].splines; spl!=NULL; spl=spl->next ) {
+	for ( sp = spl->first; ; ) {
+	    RemapHintMask(sp->hintmask,mapping,max);
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==spl->first )
+	break;
+	}
+    }
+}
+
+int UnblendedCompare(real u1[MmMax], real u2[MmMax], int cnt) {
+    int i;
+
+    for ( i=0; i<cnt; ++i ) {
+	if ( u1[i]!=u2[i] )
+return( u1[i]>u2[i]?1:-1 );
+    }
+return( 0 );
+}
+
+static StemInfo *SameH(StemInfo *old,real start, real width,
+	real unblended[2][MmMax], int instance_count) {
+    StemInfo *sameh;
+
+    if ( instance_count==0 ) {
+	for ( sameh=old; sameh!=NULL; sameh=sameh->next )
+	    if ( sameh->start==start && sameh->width==width)
+	break;
+    } else { int j;
+	for ( j=1; j<instance_count; ++j ) {
+	    unblended[0][j] += unblended[0][j-1];
+	    unblended[1][j] += unblended[1][j-1];
+	}
+	for ( sameh=old; sameh!=NULL; sameh=sameh->next ) {
+	    if ( (*sameh->u.unblended)[0] == NULL || (*sameh->u.unblended)[1]==NULL )
+	continue;
+	    if ( UnblendedCompare((*sameh->u.unblended)[0],unblended[0],instance_count)==0 &&
+		    UnblendedCompare((*sameh->u.unblended)[1],unblended[1],instance_count)==0)
+	break;
+	}
+    }
+return( sameh );
+}
+
+static real Blend(real u[MmMax],struct pscontext *context) {
+    real sum = u[0];
+    int i;
+
+    for ( i=1; i<context->instance_count; ++i )
+	sum += context->blend_values[i]*u[i];
+return( sum );
+}
+
+/* this handles either Type1 or Type2 charstrings. Type2 charstrings have */
+/*  more operators than Type1s and the old operators have extended meanings */
+/*  (ie. the rlineto operator can produce more than one line). But pretty */
+/*  much it's a superset and if we parse for type2 (with a few additions) */
+/*  we'll get it right */
+/* Char width is done differently. Moveto starts a newpath. 0xff starts a 16.16*/
+/*  number rather than a 32 bit number */
+SplineChar *PSCharStringToSplines(uint8 *type1, int len, struct pscontext *context,
+	struct pschars *subrs, struct pschars *gsubrs, const char *name) {
+    int is_type2 = context->is_type2;
+    real stack[50]; int sp=0, v;		/* Type1 stack is about 25 long, Type2 stack is 48 */
+    real transient[32];
+    SplineChar *ret = SplineCharCreate(2);
+    SplinePointList *cur=NULL, *oldcur=NULL;
+    RefChar *r1, *r2, *rlast=NULL;
+    DBasePoint current;
+    real dx, dy, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6;
+    SplinePoint *pt;
+    /* subroutines may be nested to a depth of 10 */
+    struct substate { unsigned char *type1; int len; int subnum; } pcstack[11];
+    int pcsp=0;
+    StemInfo *hint, *hp;
+    real pops[30];
+    int popsp=0;
+    int base, polarity;
+    real coord;
+    struct pschars *s;
+    int hint_cnt = 0;
+    StemInfo *activeh=NULL, *activev=NULL, *sameh;
+    HintMask *pending_hm = NULL;
+    HintMask *counters[96];
+    int cp=0;
+    real unblended[2][MmMax];
+    int last_was_b1=false, old_last_was_b1;
+
+    if ( !is_type2 && context->instance_count>1 )
+	memset(unblended,0,sizeof(unblended));
+
+    ret->name = copy( name );
+    ret->unicodeenc = -1;
+    ret->width = (int16) 0x8000;
+    if ( name==NULL ) name = "unnamed";
+    ret->manualhints = true;
+
+    current.x = current.y = 0;
+    while ( len>0 ) {
+	if ( sp>48 ) {
+	    LogError( _("Stack got too big in %s\n"), name );
+	    sp = 48;
+	}
+	base = 0;
+	--len;
+	if ( (v = *type1++)>=32 ) {
+	    if ( v<=246) {
+		stack[sp++] = v - 139;
+	    } else if ( v<=250 ) {
+		stack[sp++] = (v-247)*256 + *type1++ + 108;
+		--len;
+	    } else if ( v<=254 ) {
+		stack[sp++] = -(v-251)*256 - *type1++ - 108;
+		--len;
+	    } else {
+		int val = (*type1<<24) | (type1[1]<<16) | (type1[2]<<8) | type1[3];
+		stack[sp++] = val;
+		type1 += 4;
+		len -= 4;
+		if ( is_type2 ) {
+#ifndef PSFixed_Is_TTF	/* The type2 spec is contradictory. It says this is a */
+			/*  two's complement number, but it also says it is a */
+			/*  Fixed, which in truetype is not two's complement */
+			/*  (mantisa is always unsigned) */
+		    stack[sp-1] /= 65536.;
+#else
+		    int mant = val&0xffff;
+		    stack[sp-1] = (val>>16) + mant/65536.;
+#endif
+		}
+	    }
+	} else if ( v==28 ) {
+	    stack[sp++] = (short) ((type1[0]<<8) | type1[1]);
+	    type1 += 2;
+	    len -= 2;
+	/* In the Dict tables of CFF, a 5byte fixed value is prefixed by a */
+	/*  29 code. In Type2 strings the prefix is 255. */
+	} else if ( v==12 ) {
+	    old_last_was_b1 = last_was_b1; last_was_b1 = false;
+	    v = *type1++;
+	    --len;
+	    switch ( v ) {
+	      case 0: /* dotsection */
+		if ( is_type2 )
+		    LogError( _("%s\'s dotsection operator is deprecated for Type2\n"), name );
+		sp = 0;
+	      break;
+	      case 1: /* vstem3 */	/* specifies three v hints zones at once */
+		if ( sp<6 ) LogError( _("Stack underflow on vstem3 in %s\n"), name );
+		/* according to the standard, if there is a vstem3 there can't */
+		/*  be any vstems, so there can't be any confusion about hint order */
+		/*  so we don't need to worry about unblended stuff */
+		if ( is_type2 )
+		    LogError( _("%s\'s vstem3 operator is not supported for Type2\n"), name );
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->vstem,stack[0] + ret->lsidebearing,stack[1],
+				unblended,0);
+		hint = HintNew(stack[0] + ret->lsidebearing,stack[1]);
+		hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( activev==NULL )
+		    activev = hp = hint;
+		else {
+		    for ( hp=activev; hp->next!=NULL; hp = hp->next );
+		    hp->next = hint;
+		    hp = hint;
+		}
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->vstem,stack[2] + ret->lsidebearing,stack[3],
+				unblended,0);
+		hp->next = HintNew(stack[2] + ret->lsidebearing,stack[3]);
+		hp->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( !is_type2 )
+		    sameh = SameH(ret->vstem,stack[4] + ret->lsidebearing,stack[5],
+				unblended,0);
+		hp->next->next = HintNew(stack[4] + ret->lsidebearing,stack[5]);
+		hp->next->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( !is_type2 && hp->next->next->hintnumber<96 ) {
+		    if ( pending_hm==NULL )
+			pending_hm = chunkalloc(sizeof(HintMask));
+		    (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7);
+		    (*pending_hm)[hint->next->hintnumber>>3] |= 0x80>>(hint->next->hintnumber&0x7);
+		    (*pending_hm)[hint->next->next->hintnumber>>3] |= 0x80>>(hint->next->next->hintnumber&0x7);
+		}
+		hp = hp->next->next;
+		sp = 0;
+	      break;
+	      case 2: /* hstem3 */	/* specifies three h hints zones at once */
+		if ( sp<6 ) LogError( _("Stack underflow on hstem3 in %s\n"), name );
+		if ( is_type2 )
+		    LogError( _("%s\'s vstem3 operator is not supported for Type2\n"), name );
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->hstem,stack[0],stack[1], unblended,0);
+		hint = HintNew(stack[0],stack[1]);
+		hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( activeh==NULL )
+		    activeh = hp = hint;
+		else {
+		    for ( hp=activeh; hp->next!=NULL; hp = hp->next );
+		    hp->next = hint;
+		    hp = hint;
+		}
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->hstem,stack[2],stack[3], unblended,0);
+		hp->next = HintNew(stack[2],stack[3]);
+		hp->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->hstem,stack[4],stack[5], unblended,0);
+		hp->next->next = HintNew(stack[4],stack[5]);
+		hp->next->next->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( !is_type2 && hp->next->next->hintnumber<96 ) {
+		    if ( pending_hm==NULL )
+			pending_hm = chunkalloc(sizeof(HintMask));
+		    (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7);
+		    (*pending_hm)[hint->next->hintnumber>>3] |= 0x80>>(hint->next->hintnumber&0x7);
+		    (*pending_hm)[hint->next->next->hintnumber>>3] |= 0x80>>(hint->next->next->hintnumber&0x7);
+		}
+		hp = hp->next->next;
+		sp = 0;
+	      break;
+	      case 6: /* seac */	/* build accented characters */
+ seac:
+		if ( sp<5 ) LogError( _("Stack underflow on seac in %s\n"), name );
+		if ( is_type2 ) {
+			if ( v==6 ) LogError( _("%s\'s SEAC operator is invalid for Type2\n"), name );
+			else LogError( _("%s\'s SEAC-like endchar operator is deprecated for Type2\n"), name );
+		}
+		/* stack[0] must be the lsidebearing of the accent. I'm not sure why */
+		r1 = RefCharCreate();
+		r2 = RefCharCreate();
+		r2->transform[0] = 1; r2->transform[3]=1;
+		r2->transform[4] = stack[1] - (stack[0]-ret->lsidebearing);
+		r2->transform[5] = stack[2];
+		/* the translation of the accent here is said to be relative */
+		/*  to the origins of the base character. I think they place */
+		/*  the origin at the left bearing. And they don't mean the  */
+		/*  base char at all, they mean the current char's lbearing  */
+		/*  (which is normally the same as the base char's, except   */
+		/*  when I has a big accent (like diaerisis) */
+		r1->transform[0] = 1; r1->transform[3]=1;
+		r1->adobe_enc = stack[3];
+		r2->adobe_enc = stack[4];
+		if ( stack[3]<0 || stack[3]>=256 || stack[4]<0 || stack[4]>=256 ) {
+		    LogError( _("Reference encoding out of bounds in %s\n"), name );
+		    r1->adobe_enc = 0;
+		    r2->adobe_enc = 0;
+		}
+		r1->next = r2;
+		if ( rlast!=NULL ) rlast->next = r1;
+		else ret->layers[ly_fore].refs = r1;
+		ret->changedsincelasthinted = true;	/* seac glyphs contain no hints */
+		rlast = r2;
+		sp = 0;
+	      break;
+	      case 7: /* sbw */		/* generalized width/sidebearing command */
+		if ( sp<4 ) LogError( _("Stack underflow on sbw in %s\n"), name );
+		if ( is_type2 )
+		    LogError( _("%s\'s sbw operator is not supported for Type2\n"), name );
+		ret->lsidebearing = stack[0];
+		/* stack[1] is lsidebearing y (only for vertical writing styles, CJK) */
+		ret->width = stack[2];
+		/* stack[3] is height (for vertical writing styles, CJK) */
+		sp = 0;
+	      break;
+	      case 5: case 9: case 14: case 26:
+		if ( sp<1 ) LogError( _("Stack underflow on unary operator in %s\n"), name );
+		switch ( v ) {
+		  case 5: stack[sp-1] = (stack[sp-1]==0); break;	/* not */
+		  case 9: if ( stack[sp-1]<0 ) stack[sp-1]= -stack[sp-1]; break;	/* abs */
+		  case 14: stack[sp-1] = -stack[sp-1]; break;		/* neg */
+		  case 26: stack[sp-1] = sqrt(stack[sp-1]); break;	/* sqrt */
+		  default: break;
+		}
+	      break;
+	      case 3: case 4: case 10: case 11: case 12: case 15: case 24:
+		if ( sp<2 ) LogError( _("Stack underflow on binary operator in %s\n"), name );
+		else switch ( v ) {
+		  case 3: /* and */
+		    stack[sp-2] = (stack[sp-1]!=0 && stack[sp-2]!=0);
+		  break;
+		  case 4: /* and */
+		    stack[sp-2] = (stack[sp-1]!=0 || stack[sp-2]!=0);
+		  break;
+		  case 10: /* add */
+		    stack[sp-2] += stack[sp-1];
+		  break;
+		  case 11: /* sub */
+		    stack[sp-2] -= stack[sp-1];
+		  break;
+		  case 12: /* div */
+		    stack[sp-2] /= stack[sp-1];
+		  break;
+		  case 24: /* mul */
+		    stack[sp-2] *= stack[sp-1];
+		  break;
+		  case 15: /* eq */
+		    stack[sp-2] = (stack[sp-1]==stack[sp-2]);
+		  break;
+		  default:
+		  break;
+		}
+		--sp;
+	      break;
+	      case 22: /* ifelse */
+		if ( sp<4 ) LogError( _("Stack underflow on ifelse in %s\n"), name );
+		else {
+		    if ( stack[sp-2]>stack[sp-1] )
+			stack[sp-4] = stack[sp-3];
+		    sp -= 3;
+		}
+	      break;
+	      case 23: /* random */
+		/* This function returns something (0,1]. It's not clear to me*/
+		/*  if rand includes 0 and RAND_MAX or not, but this approach */
+		/*  should work no matter what */
+		do {
+		    stack[sp] = (rand()/(RAND_MAX-1));
+		} while ( stack[sp]==0 || stack[sp]>1 );
+		++sp;
+	      break;
+	      case 16: /* callothersubr */
+		/* stack[sp-1] is the number of the thing to call in the othersubr array */
+		/* stack[sp-2] is the number of args to grab off our stack and put on the */
+		/*  real postscript stack */
+		if ( is_type2 )
+		    LogError( _("Type2 fonts do not support the Type1 callothersubrs operator") );
+		if ( sp<2 || sp < 2+stack[sp-2] ) {
+		    LogError( _("Stack underflow on callothersubr in %s\n"), name );
+		    sp = 0;
+		} else {
+		    int tot = stack[sp-2], i, k, j;
+		    popsp = 0;
+		    for ( k=sp-3; k>=sp-2-tot; --k )
+			pops[popsp++] = stack[k];
+		    /* othersubrs 0-3 must be interpretted. 0-2 are Flex, 3 is Hint Replacement */
+		    /* othersubrs 12,13 are for counter hints. We don't need to */
+		    /*  do anything to ignore them */
+		    /* Subroutines 14-18 are multiple master blenders. We need */
+		    /*  to pay attention to them too */
+		    switch ( (int) stack[sp-1] ) {
+		      case 3: {
+			/* when we weren't capabable of hint replacement we */
+			/*  punted by putting 3 on the stack (T1 spec page 70) */
+			/*  subroutine 3 is a noop */
+			/*pops[popsp-1] = 3;*/
+			ret->manualhints = false;
+			/* We can manage hint substitution from hintmask though*/
+			/*  well enough that we needn't clear the manualhints bit */
+			ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL;
+			ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL;
+		      } break;
+		      case 1: {
+			/* Essentially what we want to do is draw a line from */
+			/*  where we are at the beginning to where we are at */
+			/*  the end. So we save the beginning here (this starts*/
+			/*  the flex sequence), we ignore all calls to othersub*/
+			/*  2, and when we get to othersub 0 we put everything*/
+			/*  back to where it should be and free up whatever */
+			/*  extranious junk we created along the way and draw */
+			/*  our line. */
+			/* Let's punt a little less, and actually figure out */
+			/*  the appropriate rrcurveto commands and put in a */
+			/*  dished serif */
+			/* We should never get here in a type2 font. But we did*/
+			/*  this code won't work if we follow type2 conventions*/
+			/*  so turn off type2 until we get 0 callothersubrs */
+			/*  which marks the end of the flex sequence */
+			is_type2 = false;
+			if ( cur!=NULL ) {
+			    oldcur = cur;
+			    cur->next = NULL;
+			} else
+			    LogError( _("Bad flex subroutine in %s\n"), name );
+		      } break;
+		      case 2: {
+			/* No op */;
+		      } break;
+		      case 0: if ( oldcur!=NULL ) {
+			SplinePointList *spl = oldcur->next;
+			if ( spl!=NULL && spl->next!=NULL &&
+				spl->next->next!=NULL &&
+				spl->next->next->next!=NULL &&
+				spl->next->next->next->next!=NULL &&
+				spl->next->next->next->next->next!=NULL &&
+				spl->next->next->next->next->next->next!=NULL ) {
+			    BasePoint old_nextcp, mid_prevcp, mid, mid_nextcp,
+				    end_prevcp, end;
+			    old_nextcp	= spl->next->first->me;
+			    mid_prevcp	= spl->next->next->first->me;
+			    mid		= spl->next->next->next->first->me;
+			    mid_nextcp	= spl->next->next->next->next->first->me;
+			    end_prevcp	= spl->next->next->next->next->next->first->me;
+			    end		= spl->next->next->next->next->next->next->first->me;
+			    cur = oldcur;
+			    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+				cur->last->nextcp = old_nextcp;
+				cur->last->nonextcp = false;
+				pt = chunkalloc(sizeof(SplinePoint));
+			        pt->hintmask = pending_hm; pending_hm = NULL;
+				pt->prevcp = mid_prevcp;
+				pt->me = mid;
+				pt->nextcp = mid_nextcp;
+				/*pt->flex = pops[2];*/
+			        CheckMake(cur->last,pt);
+				SplineMake3(cur->last,pt);
+				cur->last = pt;
+				pt = chunkalloc(sizeof(SplinePoint));
+				pt->prevcp = end_prevcp;
+				pt->me = end;
+				pt->nonextcp = true;
+			        CheckMake(cur->last,pt);
+				SplineMake3(cur->last,pt);
+				cur->last = pt;
+			    } else
+				LogError( _("No previous point on path in curveto from flex 0 in %s\n"), name );
+			} else {
+			    /* Um, something's wrong. Let's just draw a line */
+			    /* do the simple method, which consists of creating */
+			    /*  the appropriate line */
+			    pt = chunkalloc(sizeof(SplinePoint));
+			    pt->me.x = pops[1]; pt->me.y = pops[0];
+			    pt->noprevcp = true; pt->nonextcp = true;
+			    SplinePointListFree(oldcur->next); oldcur->next = NULL; spl = NULL;
+			    cur = oldcur;
+			    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+				CheckMake(cur->last,pt);
+				SplineMake3(cur->last,pt);
+				cur->last = pt;
+			    } else
+				LogError( _("No previous point on path in lineto from flex 0 in %s\n"), name );
+			}
+			--popsp;
+			cur->next = NULL;
+			SplinePointListsFree(spl);
+			oldcur = NULL;
+		      } else
+			LogError( _("Bad flex subroutine in %s\n"), name );
+
+			is_type2 = context->is_type2;
+			/* If we found a type2 font with a type1 flex sequence */
+			/*  (an illegal idea, but never mind, someone gave us one)*/
+			/*  then we had to turn off type2 untill the end of the */
+			/*  flex sequence. Which is here */
+		      break;
+		      case 14: 		/* results in 1 blended value */
+		      case 15:		/* results in 2 blended values */
+		      case 16:		/* results in 3 blended values */
+		      case 17:		/* results in 4 blended values */
+		      case 18: {	/* results in 6 blended values */
+			int cnt = stack[sp-1]-13;
+			if ( cnt==5 ) cnt=6;
+			if ( context->instance_count==0 )
+			    LogError( _("Attempt to use a multiple master subroutine in a non-mm font in %s.\n"), name );
+			else if ( tot!=cnt*context->instance_count )
+			    LogError( _("Multiple master subroutine called with the wrong number of arguments in %s.\n"), name );
+			else {
+			    /* Hints need to keep track of the original blends */
+			    if ( cnt==1 && !is_type2 ) {
+				if ( sp-2-tot>=1 && (!old_last_was_b1 || stack[0]!=Blend(unblended[1],context))) {
+				    unblended[0][0] = stack[0];
+				    for ( i=1; i<context->instance_count; ++i )
+					unblended[0][i] = 0;
+			        } else
+				    memcpy(unblended,unblended+1,context->instance_count*sizeof(real));
+			        for ( j=0; j<context->instance_count; ++j )
+				    unblended[1][j] = stack[sp-2-tot+j];
+			    } else if ( cnt==2 && !is_type2 ) {
+				unblended[0][0] = stack[sp-2-tot];
+				unblended[1][0] = stack[sp-2-tot+1];
+				for ( i=0; i<2; ++i )
+				    for ( j=1; j<context->instance_count; ++j )
+					unblended[i][j] = stack[sp-2-tot+2+i*(context->instance_count-1)+(j-1)];
+			    }
+			    popsp = 0;
+			    for ( i=0; i<cnt; ++i ) {
+				double sum = stack[sp-2-tot+ i];
+				for ( j=1; j<context->instance_count; ++j )
+				    sum += context->blend_values[j]*
+					    stack[sp-2-tot+ cnt +i*(context->instance_count-1)+ j-1];
+				pops[cnt-1-popsp++] = sum;
+			    }
+			}
+		      } break;
+		    }
+		    sp = k+1;
+		}
+	      break;
+	      case 20: /* put */
+		if ( sp<2 ) LogError( _("Too few items on stack for put in %s\n"), name );
+		else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) LogError( _("Reference to transient memory out of bounds in put in %s\n"), name );
+		else {
+		    transient[(int)stack[sp-1]] = stack[sp-2];
+		    sp -= 2;
+		}
+	      break;
+	      case 21: /* get */
+		if ( sp<1 ) LogError( _("Too few items on stack for get in %s\n"), name );
+		else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) LogError( _("Reference to transient memory out of bounds in put in %s\n"), name );
+		else
+		    stack[sp-1] = transient[(int)stack[sp-1]];
+	      break;
+	      case 17: /* pop */
+		/* pops something from the postscript stack and pushes it on ours */
+		/* used to get a return value from an othersubr call */
+		/* Bleah. Adobe wants the pops to return the arguments if we */
+		/*  don't understand the call. What use is the subroutine then?*/
+		if ( popsp<=0 )
+		    LogError( _("Pop stack underflow on pop in %s\n"), name );
+		else
+		    stack[sp++] = pops[--popsp];
+	      break;
+	      case 18: /* drop */
+		if ( sp>0 ) --sp;
+	      break;
+	      case 27: /* dup */
+		if ( sp>=1 ) {
+		    stack[sp] = stack[sp-1];
+		    ++sp;
+		}
+	      break;
+	      case 28: /* exch */
+		if ( sp>=2 ) {
+		    real temp = stack[sp-1];
+		    stack[sp-1] = stack[sp-2]; stack[sp-2] = temp;
+		}
+	      break;
+	      case 29: /* index */
+		if ( sp>=1 ) {
+		    int index = stack[--sp];
+		    if ( index<0 || sp<index+1 )
+			LogError( _("Index out of range in %s\n"), name );
+		    else {
+			stack[sp] = stack[sp-index-1];
+			++sp;
+		    }
+		}
+	      break;
+	      case 30: /* roll */
+		if ( sp>=2 ) {
+		    int j = stack[sp-1], N=stack[sp-2];
+		    if ( N>sp || j>=N || j<0 || N<0 )
+			LogError( _("roll out of range in %s\n"), name );
+		    else if ( j==0 || N==0 )
+			/* No op */;
+		    else {
+			real *temp = malloc(N*sizeof(real));
+			int i;
+			for ( i=0; i<N; ++i )
+			    temp[i] = stack[sp-N+i];
+			for ( i=0; i<N; ++i )
+			    stack[sp-N+i] = temp[(i+j)%N];
+			free(temp);
+		    }
+		}
+	      break;
+	      case 33: /* setcurrentpoint */
+		if ( is_type2 )
+		    LogError( _("Type2 fonts do not support the Type1 setcurrentpoint operator") );
+		if ( sp<2 ) LogError( _("Stack underflow on setcurrentpoint in %s\n"), name );
+		else {
+		    current.x = stack[0];
+		    current.y = stack[1];
+		}
+		sp = 0;
+	      break;
+	      case 34:	/* hflex */
+	      case 35:	/* flex */
+	      case 36:	/* hflex1 */
+	      case 37:	/* flex1 */
+		dy = dy3 = dy4 = dy5 = dy6 = 0;
+		dx = stack[base++];
+		if ( v!=34 )
+		    dy = stack[base++];
+		dx2 = stack[base++];
+		dy2 = stack[base++];
+		dx3 = stack[base++];
+		if ( v!=34 && v!=36 )
+		    dy3 = stack[base++];
+		dx4 = stack[base++];
+		if ( v!=34 && v!=36 )
+		    dy4 = stack[base++];
+		dx5 = stack[base++];
+		if ( v==34 )
+		    dy5 = -dy2;
+		else
+		    dy5 = stack[base++];
+		switch ( v ) {
+		    real xt, yt;
+		    case 35:    /* flex */
+			dx6 = stack[base++];
+			dy6 = stack[base++];
+			break;
+		    case 34:    /* hflex */
+			dx6 = stack[base++];
+			break;
+		    case 36:    /* hflex1 */
+			dx6 = stack[base++];
+			dy6 = -dy-dy2-dy5;
+			break;
+		    case 37:    /* flex1 */
+			xt = dx+dx2+dx3+dx4+dx5;
+			yt = dy+dy2+dy3+dy4+dy5;
+			if ( xt<0 ) xt= -xt;
+			if ( yt<0 ) yt= -yt;
+			if ( xt>yt ) {
+			    dx6 = stack[base++];
+			    dy6 = -dy-dy2-dy3-dy4-dy5;
+			} else {
+			    dy6 = stack[base++];
+			    dx6 = -dx-dx2-dx3-dx4-dx5;
+			}
+			break;
+		}
+		if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+		    current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024;
+		    cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y;
+		    cur->last->nonextcp = false;
+		    current.x = rint((current.x+dx2)*1024)/1024; current.y = rint((current.y+dy2)*1024)/1024;
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    pt->hintmask = pending_hm; pending_hm = NULL;
+		    pt->prevcp.x = current.x; pt->prevcp.y = current.y;
+		    current.x = rint((current.x+dx3)*1024)/1024; current.y = rint((current.y+dy3)*1024)/1024;
+		    pt->me.x = current.x; pt->me.y = current.y;
+		    pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+
+		    current.x = rint((current.x+dx4)*1024)/1024; current.y = rint((current.y+dy4)*1024)/1024;
+		    cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y;
+		    cur->last->nonextcp = false;
+		    current.x = rint((current.x+dx5)*1024)/1024; current.y = rint((current.y+dy5)*1024)/1024;
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    pt->prevcp.x = current.x; pt->prevcp.y = current.y;
+		    current.x = rint((current.x+dx6)*1024)/1024; current.y = rint((current.y+dy6)*1024)/1024;
+		    pt->me.x = current.x; pt->me.y = current.y;
+		    pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		} else
+		    LogError( _("No previous point on path in flex operator in %s\n"), name );
+		sp = 0;
+	      break;
+	      default:
+		LogError( _("Uninterpreted opcode 12,%d in %s\n"), v, name );
+	      break;
+	    }
+	} else { last_was_b1 = false; switch ( v ) {
+	  case 1: /* hstem */
+	  case 18: /* hstemhm */
+	    base = 0;
+	    if ( (sp&1) && ret->width == (int16) 0x8000 )
+		ret->width = stack[0];
+	    if ( sp&1 )
+		base=1;
+	    if ( sp-base<2 )
+		LogError( _("Stack underflow on hstem in %s\n"), name );
+	    /* stack[0] is absolute y for start of horizontal hint */
+	    /*	(actually relative to the y specified as lsidebearing y in sbw*/
+	    /* stack[1] is relative y for height of hint zone */
+	    coord = 0;
+	    hp = NULL;
+	    if ( activeh!=NULL )
+		for ( hp=activeh; hp->next!=NULL; hp = hp->next );
+	    while ( sp-base>=2 ) {
+		sameh = NULL;
+		if ( !is_type2 )
+		    sameh = SameH(ret->hstem,stack[base]+coord,stack[base+1],
+				unblended,context->instance_count);
+		hint = HintNew(stack[base]+coord,stack[base+1]);
+		hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		if ( !is_type2 && context->instance_count!=0 ) {
+		    hint->u.unblended = chunkalloc(sizeof(real [2][MmMax]));
+		    memcpy(hint->u.unblended,unblended,sizeof(real [2][MmMax]));
+		}
+		if ( activeh==NULL )
+		    activeh = hint;
+		else
+		    hp->next = hint;
+		hp = hint;
+		if ( !is_type2 && hint->hintnumber<96 ) {
+		    if ( pending_hm==NULL )
+			pending_hm = chunkalloc(sizeof(HintMask));
+		    (*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7);
+		}
+		base+=2;
+		coord = hint->start+hint->width;
+	    }
+	    sp = 0;
+	    break;
+	  case 19: /* hintmask */
+	  case 20: /* cntrmask */
+	    /* If there's anything on the stack treat it as a vstem hint */
+	  case 3: /* vstem */
+	  case 23: /* vstemhm */
+	    base = 0;
+	    if ( cur==NULL || v==3 || v==23 ) {
+		if ( (sp&1) && is_type2 && ret->width == (int16) 0x8000 ) {
+		    ret->width = stack[0];
+		}
+		if ( sp&1 )
+		    base=1;
+		/* I've seen a vstemhm with no arguments. I've no idea what that */
+		/*  means. It came right after a hintmask */
+		/* I'm confused about v/hstemhm because the manual says it needs */
+		/*  to be used if one uses a hintmask, but that's not what the */
+		/*  examples show.  Or I'm not understanding. */
+		if ( sp-base<2 && v!=19 && v!=20 )
+		    LogError( _("Stack underflow on vstem in %s\n"), name );
+		/* stack[0] is absolute x for start of vertical hint */
+		/*	(actually relative to the x specified as lsidebearing in h/sbw*/
+		/* stack[1] is relative x for height of hint zone */
+		coord = ret->lsidebearing;
+		hp = NULL;
+		if ( activev!=NULL )
+		    for ( hp=activev; hp->next!=NULL; hp = hp->next );
+		while ( sp-base>=2 ) {
+		    sameh = NULL;
+		    if ( !is_type2 )
+			sameh = SameH(ret->vstem,stack[base]+coord,stack[base+1],
+				    unblended,context->instance_count);
+		    hint = HintNew(stack[base]+coord,stack[base+1]);
+		    hint->hintnumber = sameh!=NULL ? sameh->hintnumber : hint_cnt++;
+		    if ( !is_type2 && context->instance_count!=0 ) {
+			hint->u.unblended = chunkalloc(sizeof(real [2][MmMax]));
+			memcpy(hint->u.unblended,unblended,sizeof(real [2][MmMax]));
+		    }
+		    if ( !is_type2 && hint->hintnumber<96 ) {
+			if ( pending_hm==NULL )
+			    pending_hm = chunkalloc(sizeof(HintMask));
+			(*pending_hm)[hint->hintnumber>>3] |= 0x80>>(hint->hintnumber&0x7);
+		    }
+		    if ( activev==NULL )
+			activev = hint;
+		    else
+			hp->next = hint;
+		    hp = hint;
+		    base+=2;
+		    coord = hint->start+hint->width;
+		}
+		sp = 0;
+	    }
+	    if ( v==19 || v==20 ) {		/* hintmask, cntrmask */
+		int bytes = (hint_cnt+7)/8;
+		if ( bytes>sizeof(HintMask) ) bytes = sizeof(HintMask);
+		if ( v==19 ) {
+		    ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL;
+		    ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL;
+		    if ( pending_hm==NULL )
+			pending_hm = chunkalloc(sizeof(HintMask));
+		    memcpy(pending_hm,type1,bytes);
+		} else if ( cp<sizeof(counters)/sizeof(counters[0]) ) {
+		    counters[cp] = chunkalloc(sizeof(HintMask));
+		    memcpy(counters[cp],type1,bytes);
+		    ++cp;
+		}
+		if ( bytes!=hint_cnt/8 ) {
+		    int mask = 0xff>>(hint_cnt&7);
+		    if ( type1[bytes-1]&mask )
+			LogError( _("Hint mask (or counter mask) with too many hints in %s\n"), name );
+		}
+		type1 += bytes;
+		len -= bytes;
+	    }
+	  break;
+	  case 14: /* endchar */
+	    /* endchar is allowed to terminate processing even within a subroutine */
+	    if ( (sp&1) && is_type2 && ret->width == (int16) 0x8000 )
+		ret->width = stack[0];
+	    if ( context->painttype!=2 )
+		closepath(cur,is_type2);
+	    pcsp = 0;
+	    if ( sp==4 ) {
+		/* In Type2 strings endchar has a deprecated function of doing */
+		/*  a seac (which doesn't exist at all). Except enchar takes */
+		/*  4 args and seac takes 5. Bleah */
+		stack[4] = stack[3]; stack[3] = stack[2]; stack[2] = stack[1]; stack[1] = stack[0];
+		stack[0] = 0;
+		sp = 5;
+  goto seac;
+	    } else if ( sp==5 ) {
+		/* same as above except also specified a width */
+		stack[0] = 0;
+  goto seac;
+	    }
+	    /* the docs say that endchar must be the last command in a char */
+	    /*  (or the last command in a subroutine which is the last in the */
+	    /*  char) So in theory if there's anything left we should complain*/
+	    /*  In practice though, the EuroFont has a return statement after */
+	    /*  the endchar in a subroutine. So we won't try to catch that err*/
+	    /*  and just stop. */
+	    /* Adobe says it's not an error, but I can't understand their */
+	    /*  logic */
+  goto done;
+	  break;
+	  case 13: /* hsbw (set left sidebearing and width) */
+	    if ( sp<2 ) LogError( _("Stack underflow on hsbw in %s\n"), name );
+	    ret->lsidebearing = stack[0];
+	    current.x = stack[0];		/* sets the current point too */
+	    ret->width = stack[1];
+	    sp = 0;
+	  break;
+	  case 9: /* closepath */
+	    sp = 0;
+	    closepath(cur,is_type2);
+	  break;
+	  case 21: /* rmoveto */
+	  case 22: /* hmoveto */
+	  case 4: /* vmoveto */
+	    if ( is_type2 ) {
+		if (( (v==21 && sp==3) || (v!=21 && sp==2))  && ret->width == (int16) 0x8000 )
+		    /* Character's width may be specified on the first moveto */
+		    ret->width = stack[0];
+		if ( v==21 && sp>2 ) {
+		    stack[0] = stack[sp-2]; stack[1] = stack[sp-1];
+		    sp = 2;
+		} else if ( v!=21 && sp>1 ) {
+		    stack[0] = stack[sp-1];
+		    sp = 1;
+		}
+		if ( context->painttype!=2 )
+		    closepath(cur,true);
+	    }
+	  case 5: /* rlineto */
+	  case 6: /* hlineto */
+	  case 7: /* vlineto */
+	    polarity = 0;
+	    base = 0;
+	    while ( base<sp ) {
+		dx = dy = 0;
+		if ( v==5 || v==21 ) {
+		    if ( sp<base+2 ) {
+			LogError( _("Stack underflow on rlineto/rmoveto in %s\n"), name );
+	    break;
+		    }
+		    dx = stack[base++];
+		    dy = stack[base++];
+		} else if ( (v==6 && !(polarity&1)) || (v==7 && (polarity&1)) || v==22 ) {
+		    if ( sp<=base ) {
+			LogError( _("Stack underflow on hlineto/hmoveto in %s\n"), name );
+	    break;
+		    }
+		    dx = stack[base++];
+		} else /*if ( (v==7 && !(parity&1)) || (v==6 && (parity&1) || v==4 )*/ {
+		    if ( sp<=base ) {
+			LogError( _("Stack underflow on vlineto/vmoveto in %s\n"), name );
+	    break;
+		    }
+		    dy = stack[base++];
+		}
+		++polarity;
+		current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024;
+		pt = chunkalloc(sizeof(SplinePoint));
+		pt->hintmask = pending_hm; pending_hm = NULL;
+		pt->me.x = current.x; pt->me.y = current.y;
+		pt->noprevcp = true; pt->nonextcp = true;
+		if ( v==4 || v==21 || v==22 ) {
+		    if ( cur!=NULL && cur->first==cur->last && cur->first->prev==NULL && is_type2 ) {
+			/* Two adjacent movetos should not create single point paths */
+			cur->first->me.x = current.x; cur->first->me.y = current.y;
+			SplinePointFree(pt);
+		    } else {
+			SplinePointList *spl = chunkalloc(sizeof(SplinePointList));
+			spl->first = spl->last = pt;
+			if ( cur!=NULL )
+			    cur->next = spl;
+			else
+			    ret->layers[ly_fore].splines = spl;
+			cur = spl;
+		    }
+	    break;
+		} else {
+		    if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+			CheckMake(cur->last,pt);
+			SplineMake3(cur->last,pt);
+			cur->last = pt;
+		    } else
+			LogError( _("No previous point on path in lineto in %s\n"), name );
+		    if ( !is_type2 )
+	    break;
+		}
+	    }
+	    sp = 0;
+	  break;
+	  case 25: /* rlinecurve */
+	    base = 0;
+	    while ( sp>base+6 ) {
+		current.x = rint((current.x+stack[base++])*1024)/1024; current.y = rint((current.y+stack[base++])*1024)/1024;
+		if ( cur!=NULL ) {
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    pt->hintmask = pending_hm; pending_hm = NULL;
+		    pt->me.x = current.x; pt->me.y = current.y;
+		    pt->noprevcp = true; pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		}
+	    }
+	  case 24: /* rcurveline */
+	  case 8:  /* rrcurveto */
+	  case 31: /* hvcurveto */
+	  case 30: /* vhcurveto */
+	  case 27: /* hhcurveto */
+	  case 26: /* vvcurveto */
+	    polarity = 0;
+	    while ( sp>base+2 ) {
+		dx = dy = dx2 = dy2 = dx3 = dy3 = 0;
+		if ( v==8 || v==25 || v==24 ) {
+		    if ( sp<6+base ) {
+			LogError( _("Stack underflow on rrcurveto in %s\n"), name );
+			base = sp;
+		    } else {
+			dx = stack[base++];
+			dy = stack[base++];
+			dx2 = stack[base++];
+			dy2 = stack[base++];
+			dx3 = stack[base++];
+			dy3 = stack[base++];
+		    }
+		} else if ( v==27 ) {		/* hhcurveto */
+		    if ( sp<4+base ) {
+			LogError( _("Stack underflow on hhcurveto in %s\n"), name );
+			base = sp;
+		    } else {
+			if ( (sp-base)&1 ) dy = stack[base++];
+			dx = stack[base++];
+			dx2 = stack[base++];
+			dy2 = stack[base++];
+			dx3 = stack[base++];
+		    }
+		} else if ( v==26 ) {		/* vvcurveto */
+		    if ( sp<4+base ) {
+			LogError( _("Stack underflow on hhcurveto in %s\n"), name );
+			base = sp;
+		    } else {
+			if ( (sp-base)&1 ) dx = stack[base++];
+			dy = stack[base++];
+			dx2 = stack[base++];
+			dy2 = stack[base++];
+			dy3 = stack[base++];
+		    }
+		} else if ( (v==31 && !(polarity&1)) || (v==30 && (polarity&1)) ) {
+		    if ( sp<4+base ) {
+			LogError( _("Stack underflow on hvcurveto in %s\n"), name );
+			base = sp;
+		    } else {
+			dx = stack[base++];
+			dx2 = stack[base++];
+			dy2 = stack[base++];
+			dy3 = stack[base++];
+			if ( sp==base+1 )
+			    dx3 = stack[base++];
+		    }
+		} else /*if ( (v==30 && !(polarity&1)) || (v==31 && (polarity&1)) )*/ {
+		    if ( sp<4+base ) {
+			LogError( _("Stack underflow on vhcurveto in %s\n"), name );
+			base = sp;
+		    } else {
+			dy = stack[base++];
+			dx2 = stack[base++];
+			dy2 = stack[base++];
+			dx3 = stack[base++];
+			if ( sp==base+1 )
+			    dy3 = stack[base++];
+		    }
+		}
+		++polarity;
+		if ( cur!=NULL && cur->first!=NULL && (cur->first!=cur->last || cur->first->next==NULL) ) {
+		    current.x = rint((current.x+dx)*1024)/1024; current.y = rint((current.y+dy)*1024)/1024;
+		    cur->last->nextcp.x = current.x; cur->last->nextcp.y = current.y;
+		    cur->last->nonextcp = false;
+		    current.x = rint((current.x+dx2)*1024)/1024; current.y = rint((current.y+dy2)*1024)/1024;
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    pt->hintmask = pending_hm; pending_hm = NULL;
+		    pt->prevcp.x = current.x; pt->prevcp.y = current.y;
+		    current.x = rint((current.x+dx3)*1024)/1024; current.y = rint((current.y+dy3)*1024)/1024;
+		    pt->me.x = current.x; pt->me.y = current.y;
+		    pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		} else
+		    LogError( _("No previous point on path in curveto in %s\n"), name );
+	    }
+	    if ( v==24 ) {
+		current.x = rint((current.x+stack[base++])*1024)/1024; current.y = rint((current.y+stack[base++])*1024)/1024;
+		if ( cur!=NULL ) {	/* In legal code, cur can't be null here, but I got something illegal... */
+		    pt = chunkalloc(sizeof(SplinePoint));
+		    pt->hintmask = pending_hm; pending_hm = NULL;
+		    pt->me.x = current.x; pt->me.y = current.y;
+		    pt->noprevcp = true; pt->nonextcp = true;
+		    CheckMake(cur->last,pt);
+		    SplineMake3(cur->last,pt);
+		    cur->last = pt;
+		}
+	    }
+	    sp = 0;
+	  break;
+	  case 29: /* callgsubr */
+	  case 10: /* callsubr */
+	    /* stack[sp-1] contains the number of the subroutine to call */
+	    if ( sp<1 ) {
+		LogError( _("Stack underflow on callsubr in %s\n"), name );
+	  break;
+	    } else if ( pcsp>10 ) {
+		LogError( _("Too many subroutine calls in %s\n"), name );
+	  break;
+	    }
+	    s=subrs; if ( v==29 ) s = gsubrs;
+	    if ( s!=NULL ) stack[sp-1] += s->bias;
+	    /* Type2 subrs have a bias that must be added to the subr-number */
+	    /* Type1 subrs do not. We set the bias on them to 0 */
+	    if ( s==NULL || stack[sp-1]>=s->cnt || stack[sp-1]<0 ||
+		    s->values[(int) stack[sp-1]]==NULL )
+		LogError( _("Subroutine number out of bounds in %s\n"), name );
+	    else {
+		pcstack[pcsp].type1 = type1;
+		pcstack[pcsp].len = len;
+		pcstack[pcsp].subnum = stack[sp-1];
+		++pcsp;
+		type1 = s->values[(int) stack[sp-1]];
+		len = s->lens[(int) stack[sp-1]];
+	    }
+	    if ( --sp<0 ) sp = 0;
+	  break;
+	  case 11: /* return */
+	    /* return from a subr outine */
+	    if ( pcsp<1 ) LogError( _("return when not in subroutine in %s\n"), name );
+	    else {
+		--pcsp;
+		type1 = pcstack[pcsp].type1;
+		len = pcstack[pcsp].len;
+	    }
+	  break;
+	  case 16: { /* blend -- obsolete type 2 multiple master operator */
+	    int cnt,i,j;
+	    if ( context->instance_count==0 )
+		LogError( _("Attempt to use a multiple master subroutine in a non-mm font.\n") );
+	    else if ( sp<1 || sp<context->instance_count*stack[sp-1]+1 )
+		LogError( _("Too few items on stack for blend in %s\n"), name );
+	    else {
+		if ( !context->blend_warn ) {
+		    LogError( _("Use of obsolete blend operator.\n") );
+		    context->blend_warn = true;
+		}
+		cnt = stack[sp-1];
+		sp -= context->instance_count*stack[sp-1]+1;
+		for ( i=0; i<cnt; ++i ) {
+		    for ( j=1; j<context->instance_count; ++j )
+			stack[sp+i] += context->blend_values[j]*stack[sp+
+				cnt+ i*(context->instance_count-1)+ j-1];
+		}
+		/* there will always be fewer pushes than there were pops */
+		/*  so I don't bother to check the stack */
+		sp += cnt;
+	    }
+	  }
+	  break;
+	  default:
+	    LogError( _("Uninterpreted opcode %d in %s\n"), v, name );
+	  break;
+	}}
+    }
+  done:
+    if ( pcsp!=0 )
+	LogError( _("end of subroutine reached with no return in %s\n"), name );
+    SCCategorizePoints(ret);
+
+    ret->hstem = HintsAppend(ret->hstem,activeh); activeh=NULL;
+    ret->vstem = HintsAppend(ret->vstem,activev); activev=NULL;
+
+    if ( cp!=0 ) { int i;
+	ret->countermasks = malloc(cp*sizeof(HintMask));
+	ret->countermask_cnt = cp;
+	for ( i=0; i<cp; ++i ) {
+	    memcpy(&ret->countermasks[i],counters[i],sizeof(HintMask));
+	    chunkfree(counters[i],sizeof(HintMask));
+	}
+    }
+
+    /* Even in type1 fonts all paths should be closed. But if we close them at*/
+    /*  the obvious moveto, that breaks flex hints. So we have a hack here at */
+    /*  the end which closes any open paths. */
+    /* If we do have a PaintType 2 font, then presumably the difference between*/
+    /*  open and closed paths matters */
+    if ( !is_type2 && !context->painttype )
+	for ( cur = ret->layers[ly_fore].splines; cur!=NULL; cur = cur->next ) if ( cur->first->prev==NULL ) {
+	    CheckMake(cur->last,cur->first);
+	    SplineMake3(cur->last,cur->first);
+	    cur->last = cur->first;
+	}
+
+    /* Oh, I see. PS and TT disagree on which direction to use, so Fontographer*/
+    /*  chose the TT direction and we must reverse postscript */
+    for ( cur = ret->layers[ly_fore].splines; cur!=NULL; cur = cur->next )
+	SplineSetReverse(cur);
+    if ( ret->hstem==NULL && ret->vstem==NULL )
+	ret->manualhints = false;
+    if ( !is_type2 && context->instance_count!=0 ) {
+	UnblendFree(ret->hstem);
+	UnblendFree(ret->vstem);
+    }
+    ret->hstem = HintCleanup(ret->hstem,true,context->instance_count);
+    ret->vstem = HintCleanup(ret->vstem,true,context->instance_count);
+
+    SCGuessHHintInstancesList(ret,ly_fore);
+    SCGuessVHintInstancesList(ret,ly_fore);
+
+    ret->hconflicts = StemListAnyConflicts(ret->hstem);
+    ret->vconflicts = StemListAnyConflicts(ret->vstem);
+    if ( context->instance_count==1 && !ret->hconflicts && !ret->vconflicts )
+	SCClearHintMasks(ret,ly_fore,false);
+    HintsRenumber(ret);
+    if ( name!=NULL && strcmp(name,".notdef")!=0 )
+	ret->widthset = true;
+return( ret );
+}
diff -pruN 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/tottf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/tottf.c
--- 1:20161005~dfsg-4/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/tottf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch/fontforge/tottf.c	2016-10-04 11:36:10.000000000 +0000
@@ -0,0 +1,7012 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+#include <locale.h>
+#include <utype.h>
+#include <ustring.h>
+#include <chardata.h>
+#include <gwidget.h>
+
+#ifdef __CygWin
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+#endif
+
+#include "ttf.h"
+
+char *TTFFoundry=NULL;
+
+/* This file produces a ttf file given a splinefont. */
+
+/* ************************************************************************** */
+
+/* Required tables:
+	cmap		encoding
+	head		header data
+	hhea		horizontal metrics header data
+	hmtx		horizontal metrics (widths, lsidebearing)
+	maxp		various maxima in the font
+	name		various names associated with the font
+	post		postscript names and other stuff
+Required by windows but not mac
+	OS/2		bleah.
+Required for TrueType
+	loca		pointers to the glyphs
+	glyf		character shapes
+Required for OpenType (PostScript)
+	CFF 		A complete postscript CFF font here with all its internal tables
+Required for bitmaps
+	bdat/EBDT	bitmap data
+	bloc/EBLC	pointers to bitmaps
+	bhed		for apple bitmap only fonts, replaces head
+Optional for bitmaps
+	EBSC		bitmap scaling table (used in windows "bitmap-only" fonts)
+"Advanced Typograpy"
+  Apple
+	feat		(mapping between morx features and 'name' names)
+	kern		(if data are present)
+	lcar		(ligature caret, if data present)
+	morx		(substitutions, if data present)
+	prop		(glyph properties, if data present)
+	opbd		(optical bounds, if data present)
+  OpenType
+	GPOS		(opentype, if kern,anchor data are present)
+	GSUB		(opentype, if ligature (other subs) data are present)
+	GDEF		(opentype, if anchor data are present)
+MATH
+	MATH		(MS proposal, if math data present)
+Apple variation tables (for distortable (multiple master type) fonts)
+	fvar		(font variations)
+	gvar		(glyph variations)
+	cvar		(cvt variations)
+	avar		(axis variations)
+additional tables
+	cvt		for hinting
+	gasp		to control when things should be hinted
+	fpgm		for hinting (currently only copied and dumped verbatim)
+	prep		for hinting (currently only copied and dumped verbatim)
+FontForge
+	PfEd		My own table
+TeX
+	TeX		TeX specific info (stuff that used to live in tfm files)
+*/
+
+const char *ttfstandardnames[258] = {
+".notdef",
+".null",
+"nonmarkingreturn",
+"space",
+"exclam",
+"quotedbl",
+"numbersign",
+"dollar",
+"percent",
+"ampersand",
+"quotesingle",
+"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",
+"grave",
+"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",
+"Adieresis",
+"Aring",
+"Ccedilla",
+"Eacute",
+"Ntilde",
+"Odieresis",
+"Udieresis",
+"aacute",
+"agrave",
+"acircumflex",
+"adieresis",
+"atilde",
+"aring",
+"ccedilla",
+"eacute",
+"egrave",
+"ecircumflex",
+"edieresis",
+"iacute",
+"igrave",
+"icircumflex",
+"idieresis",
+"ntilde",
+"oacute",
+"ograve",
+"ocircumflex",
+"odieresis",
+"otilde",
+"uacute",
+"ugrave",
+"ucircumflex",
+"udieresis",
+"dagger",
+"degree",
+"cent",
+"sterling",
+"section",
+"bullet",
+"paragraph",
+"germandbls",
+"registered",
+"copyright",
+"trademark",
+"acute",
+"dieresis",
+"notequal",
+"AE",
+"Oslash",
+"infinity",
+"plusminus",
+"lessequal",
+"greaterequal",
+"yen",
+"mu",
+"partialdiff",
+"summation",
+"product",
+"pi",
+"integral",
+"ordfeminine",
+"ordmasculine",
+"Omega",
+"ae",
+"oslash",
+"questiondown",
+"exclamdown",
+"logicalnot",
+"radical",
+"florin",
+"approxequal",
+"Delta",
+"guillemotleft",
+"guillemotright",
+"ellipsis",
+"nonbreakingspace",
+"Agrave",
+"Atilde",
+"Otilde",
+"OE",
+"oe",
+"endash",
+"emdash",
+"quotedblleft",
+"quotedblright",
+"quoteleft",
+"quoteright",
+"divide",
+"lozenge",
+"ydieresis",
+"Ydieresis",
+"fraction",
+"currency",
+"guilsinglleft",
+"guilsinglright",
+"fi",
+"fl",
+"daggerdbl",
+"periodcentered",
+"quotesinglbase",
+"quotedblbase",
+"perthousand",
+"Acircumflex",
+"Ecircumflex",
+"Aacute",
+"Edieresis",
+"Egrave",
+"Iacute",
+"Icircumflex",
+"Idieresis",
+"Igrave",
+"Oacute",
+"Ocircumflex",
+"apple",
+"Ograve",
+"Uacute",
+"Ucircumflex",
+"Ugrave",
+"dotlessi",
+"circumflex",
+"tilde",
+"macron",
+"breve",
+"dotaccent",
+"ring",
+"cedilla",
+"hungarumlaut",
+"ogonek",
+"caron",
+"Lslash",
+"lslash",
+"Scaron",
+"scaron",
+"Zcaron",
+"zcaron",
+"brokenbar",
+"Eth",
+"eth",
+"Yacute",
+"yacute",
+"Thorn",
+"thorn",
+"minus",
+"multiply",
+"onesuperior",
+"twosuperior",
+"threesuperior",
+"onehalf",
+"onequarter",
+"threequarters",
+"franc",
+"Gbreve",
+"gbreve",
+"Idotaccent",
+"Scedilla",
+"scedilla",
+"Cacute",
+"cacute",
+"Ccaron",
+"ccaron",
+"dcroat"
+};
+/* Relates Unicode blocks as in
+ 	http://unicode.org/Public/UNIDATA/Blocks.txt
+   to bit positions in the OpenType standard Unicode Character Range
+   field 'ulUnicodeRange'.
+   Note that the OpenType standard specifies bits for a subset
+   of the Unicode blocks.
+ */
+static int uniranges[][3] = {
+    { 0x20, 0x7e, 0 },		/* Basic Latin */
+    { 0xa0, 0xff, 1 },		/* Latin-1 Supplement */
+    { 0x100, 0x17f, 2 },	/* Latin Extended-A */
+    { 0x180, 0x24f, 3 },	/* Latin Extended-B */
+    { 0x250, 0x2af, 4 },	/* IPA Extensions */
+    { 0x2b0, 0x2ff, 5 },	/* Spacing Modifier Letters */
+    { 0x300, 0x36f, 6 },	/* Combining Diacritical Marks */
+    { 0x370, 0x3ff, 7 },	/* Greek and Coptic */
+    { 0x400, 0x52f, 9 },	/* Cyrillic / Cyrillic Supplement */
+    { 0x530, 0x58f, 10 },	/* Armenian */
+    { 0x590, 0x5ff, 11 },	/* Hebrew */
+    { 0x600, 0x6ff, 13 },	/* Arabic */
+    { 0x700, 0x74f, 71 },	/* Syriac */
+    { 0x750, 0x77f, 13 },	/* Arabic Supplement */
+    { 0x780, 0x7bf, 72 },	/* Thaana */
+    { 0x7c0, 0x7ff, 14 },	/* N'Ko */
+    /* { 0x800, 0x83f, ? },	 Samaritan */
+    /* { 0x840, 0x85f, ? },	 Mandaic */
+    { 0x900, 0x97f, 15 },	/* Devanagari */
+    { 0x980, 0x9ff, 16 },	/* Bengali */
+    { 0xa00, 0xa7f, 17 },	/* Gurmukhi */
+    { 0xa80, 0xaff, 18 },	/* Gujarati */
+    { 0xb00, 0xb7f, 19 },	/* Oriya */
+    { 0xb80, 0xbff, 20 },	/* Tamil */
+    { 0xc00, 0xc7f, 21 },	/* Telugu */
+    { 0xc80, 0xcff, 22 },	/* Kannada */
+    { 0xd00, 0xd7f, 23 },	/* Malayalam */
+    { 0xd80, 0xdff, 73 },	/* Sinhala */
+    { 0xe00, 0xe7f, 24 },	/* Thai */
+    { 0xe80, 0xeff, 25 },	/* Lao */
+    { 0xf00, 0xfbf, 70 },	/* Tibetan */
+    { 0x1000, 0x109f, 74 },	/* Myanmar */
+    { 0x10a0, 0x10ff, 26 },	/* Georgian */
+    { 0x1100, 0x11ff, 28 },	/* Hangul Jamo */
+    { 0x1200, 0x137f, 75 },	/* Ethiopic */
+    { 0x1380, 0x139f, 75 },	/* Ethiopic Supplement */
+    { 0x13a0, 0x13ff, 76 },	/* Cherokee */
+    { 0x1400, 0x167f, 77 },	/* Unified Canadian Aboriginal Symbols */
+    { 0x1680, 0x169f, 78 },	/* Ogham */
+    { 0x16a0, 0x16ff, 79 },	/* Runic */
+    { 0x1700, 0x1714, 84 },	/* Tagalog */
+    { 0x1720, 0x1736, 84 },	/* Harunoo */
+    { 0x1740, 0x1753, 84 },	/* Buhid */
+    { 0x1750, 0x1773, 84 },	/* Tagbanwa */
+    { 0x1780, 0x17ff, 80 },	/* Khmer */
+    { 0x1800, 0x18af, 81 },	/* Mongolian */
+    { 0x18B0, 0x18f5, 77 },	/* Unified Canadian Aboriginal Symbols Extended */
+    { 0x1900, 0x194f, 93 },	/* Limbu */
+    { 0x1950, 0x197f, 94 },	/* Tai Le */
+    { 0x1980, 0x19DF, 95 },	/* New Tai Lue */
+    { 0x19e0, 0x19ff, 80 },	/* Khmer Symbols */
+    { 0x1A00, 0x1A1F, 96 },	/* Buginese */
+    { 0x1B00, 0x1B7f, 27 },	/* Balinese */
+    { 0x1B80, 0x1BB9, 112 },	/* Sudanese */
+    /*{ 0x1bc0, 0x1bff, ? },	 Batak */
+    { 0x1C00, 0x1C4F, 113 },	/* Lepcha */
+    { 0x1C50, 0x1C7F, 114 },	/* Ol Chiki */
+    /*{ 0x1cd0, 0x1cff, ? },	 Vedic Extensions */
+    { 0x1d00, 0x1dbf, 4 },	/* Phonetic Extensions & Supplement */
+    { 0x1d80, 0x1dff, 6 },	/* Combining Diacritical Marks Supplement */
+    { 0x1e00, 0x1eff, 29 },	/* Latin Extended Additional */
+    { 0x1f00, 0x1fff, 30 },	/* Greek Extended */
+    { 0x2000, 0x206f, 31 },	/* General Punctuation */
+    { 0x2070, 0x209f, 32 },	/* Superscripts and Subscripts */
+    { 0x20a0, 0x20cf, 33 },	/* Currency Symbols */
+    { 0x20d0, 0x20ff, 34 },	/* Combining Marks for Symbols */
+    { 0x2100, 0x214f, 35 },	/* Letterlike Symbols */
+    { 0x2150, 0x218f, 36 },	/* Number Forms */
+    { 0x2190, 0x21ff, 37 },	/* Arrows */
+    { 0x2200, 0x22ff, 38 },	/* Mathematical Operators */
+    { 0x2300, 0x237f, 39 },	/* Miscellaneous Technical */
+    { 0x2400, 0x243f, 40 },	/* Control Pictures */
+    { 0x2440, 0x245f, 41 },	/* Optical Character Recognition */
+    { 0x2460, 0x24ff, 42 },	/* Enclosed Alphanumerics */
+    { 0x2500, 0x257f, 43 },	/* Box Drawing */
+    { 0x2580, 0x259f, 44 },	/* Block Elements */
+    { 0x25a0, 0x25ff, 45 },	/* Geometric Shapes */
+    { 0x2600, 0x267f, 46 },	/* Miscellaneous Symbols */
+    { 0x2700, 0x27bf, 47 },	/* Dingbats */
+    { 0x27c0, 0x27ef, 38 },	/* Miscellaneous Mathematical Symbols-A */
+    { 0x27f0, 0x27ff, 37 },	/* Supplementary Arrows-A */
+    { 0x2800, 0x28ff, 82 },	/* Braille Patterns */
+    { 0x2900, 0x297f, 37 },	/* Supplementary Arrows-B */
+    { 0x2980, 0x2aff, 38 },	/* Miscellaneous Mathematical Symbols-B /
+				   Supplemental Mathematical Operators */
+    { 0x2b00, 0x2bff, 37 },	/* Miscellaneous Symbols and Arrows */
+    { 0x2C00, 0x2C5E, 97 },	/* Glagolitic */
+    { 0x2c60, 0x2c7f, 29 },	/* Latin Extended-C */
+    { 0x2c80, 0x2cff, 8 },	/* Coptic */
+    { 0x2D00, 0x2D25, 26 },	/* Georgian Supplement */
+    { 0x2D30, 0x2D6F, 98 },	/* Tifinagh */
+    { 0x2d80, 0x2ddf, 75 },	/* Ethiopic Extended */
+    { 0x2de0, 0x2dff, 9 },	/* Cyrillic Extended-A */
+    { 0x2e00, 0x2e7f, 31 },	/* Supplemental Punctuation */
+    { 0x2e80, 0x2fff, 59 },	/* CJK Radicals Supplement / Kangxi Radicals /
+				   Ideographic Description Characters */
+    { 0x3000, 0x303f, 48 },	/* CJK Symbols and Punctuation */
+    { 0x3040, 0x309f, 49 },	/* Hiragana */
+    { 0x30a0, 0x30ff, 50 },	/* Katakana */
+    { 0x3100, 0x312f, 51 },	/* Bopomofo */
+    { 0x3130, 0x318f, 52 },	/* Hangul Compatibility Jamo */
+    { 0x3190, 0x319f, 59 },	/* Kanbun */
+    { 0x31a0, 0x31bf, 51 },	/* Bopomofo Extended */
+    { 0x31f0, 0x31ff, 50 },	/* Katakana Phonetic Extensions */
+    { 0x3200, 0x32ff, 54 },	/* Enclosed CJK Letters and Months */
+    { 0x3300, 0x33ff, 55 },	/* CJK compatability */
+    { 0x3400, 0x4dbf, 59 },	/* CJK Unified Ideographs Extension A */
+    { 0x4dc0, 0x4dff, 99 },	/* Yijing Hexagram Symbols */
+    { 0x4e00, 0x9fff, 59 },	/* CJK Unified Ideographs */
+    { 0xa000, 0xa4cf, 81 },	/* Yi Syllables / Yi Radicals */
+    /*{ 0xA4d0, 0xA4ff, ? },	 Lisu */
+    { 0xA500, 0xA62b, 12 },	/* Vai */
+    { 0xa640, 0xa69f, 9 },	/* Cyrillic Extended-B */
+    /*{ 0xa6a0, 0xa6ff, ? },	 Bamum */
+    { 0xa700, 0xa71f, 5 },	/* Modifier Tone Letters */
+    { 0xa720, 0xa7ff, 29 },	/* Latin Extended-D */
+    { 0xA800, 0xA82F, 100 },	/* Syloti Nagri */
+    /*{ 0xa830, 0xa83f, ? },	 Common Indic Number Forms */
+    { 0xa840, 0xa87f, 53 },	/* Phags-pa */
+    { 0xA880, 0xA8D9, 115 },	/* Saurashtra */
+    /*{ 0xA8E0, 0xA8FF, ? },	 Devanagari Extended */
+    { 0xA900, 0xA92F, 116 },	/* Kayah Li */
+    { 0xA930, 0xA95F, 117 },	/* Rejang */
+    /*{ 0xA960, 0xA97F, 28? },	 Hangul Jamo Extended-A */
+    /*{ 0xA980, 0xA9DF, ? },	 Javanese */
+    { 0xAA00, 0xAA5F, 118 },	/* Cham */
+    /*{ 0xAA60, 0xAA7F, 74? },	 Myanmar Extended-A */
+    /*{ 0xAA80, 0xAADF, ? },	 Tai Viet */
+    /*{ 0xab00, 0xab2f, 75? },	 Ethiopic Extended-A */
+    /*{ 0xabc0, 0xabff, ? },	 Meetei Mayek */
+    { 0xac00, 0xd7af, 56 },	/* Hangul Syllables */
+    { 0xd800, 0xdfff, 57 },	/* Non-Plane 0 */
+    { 0xe000, 0xf8ff, 60 },	/* Private Use Area */
+
+    { 0xf900, 0xfaff, 61 },	/* CJK Compatibility Ideographs */
+    /* 12 ideographs in The IBM 32 Compatibility Additions are CJK unified
+       ideographs despite their names: see The Unicode Standard 4.0, p.475 */
+    { 0xfa0e, 0xfa0f, 59 },
+    { 0xfa10, 0xfa10, 61 },
+    { 0xfa11, 0xfa11, 59 },
+    { 0xfa12, 0xfa12, 61 },
+    { 0xfa13, 0xfa14, 59 },
+    { 0xfa15, 0xfa1e, 61 },
+    { 0xfa1f, 0xfa1f, 59 },
+    { 0xfa20, 0xfa20, 61 },
+    { 0xfa21, 0xfa21, 59 },
+    { 0xfa22, 0xfa22, 61 },
+    { 0xfa23, 0xfa24, 59 },
+    { 0xfa25, 0xfa26, 61 },
+    { 0xfa27, 0xfa29, 59 },
+    { 0xfa2a, 0xfaff, 61 },	/* CJK Compatibility Ideographs */
+
+    { 0xfb00, 0xfb4f, 62 },	/* Alphabetic Presentation Forms */
+    { 0xfb50, 0xfdff, 63 },	/* Arabic Presentation Forms-A */
+    { 0xfe00, 0xfe0f, 91 },	/* Variation Selectors */
+    { 0xfe20, 0xfe2f, 64 },	/* Combining Half Marks */
+    { 0xfe30, 0xfe4f, 65 },	/* CJK Compatibility Forms */
+    { 0xfe50, 0xfe6f, 66 },	/* Small Form Variants */
+    { 0xfe70, 0xfeef, 67 },	/* Arabic Presentation Forms-B */
+    { 0xff00, 0xffef, 68 },	/* Halfwidth and Fullwidth Forms */
+    { 0xfff0, 0xffff, 69 },	/* Specials */
+
+    { 0x10000, 0x1007f, 101 },	/* Linear B Syllabary */
+    { 0x10080, 0x100ff, 101 },	/* Linear B Ideograms */
+    { 0x10100, 0x1013f, 101 },	/* Aegean Numbers */
+    { 0x10140, 0x1018F, 102 },	/* Ancient Greek Numbers */
+    { 0x10190, 0x101CF, 119 },	/* Ancient Symbols */
+    { 0x101D0, 0x101FF, 120 },	/* Phaistos Disc */
+    { 0x102A0, 0x102D0, 121 },	/* Carian */
+    { 0x10280, 0x1029C, 121 },	/* Lycian */
+    { 0x10300, 0x1032f, 85 },	/* Old Italic */
+    { 0x10330, 0x1034f, 86 },	/* Gothic */
+    { 0x10380, 0x1039F, 103 },	/* Ugaritic */
+    { 0x103A0, 0x103D6, 104 },	/* Old Persian */
+    { 0x10400, 0x1044f, 87 },	/* Deseret */
+    { 0x10450, 0x1047f, 105 },	/* Shavian */
+    { 0x10480, 0x104af, 106 },	/* Osmanya */
+    { 0x10800, 0x1083f, 107 },	/* Cypriot Syllabary */
+    /*{ 0x10840, 0x1085f, ? },	 Imperial Aramaic */
+    { 0x10900, 0x1091f, 58 },	/* Phoenician */
+    { 0x10920, 0x10939, 121 },	/* Lydian */
+    { 0x10A00, 0x10A5F, 108 },	/* Kharoshthi */
+    /*{ 0x10A60, 0x10A7F, ? },	 Old South Arabian */
+    /*{ 0x10B00, 0x10B3F, ? },	 Avestan */
+    /*{ 0x10B40, 0x10B5F, ? },	 Inscriptional Parthian */
+    /*{ 0x10B60, 0x10B7F, ? },	 Inscriptional Pahlavi */
+    /*{ 0x10C00, 0x10C4F, ? },	 Old Turkic */
+    /*{ 0x10E60, 0x10E7F, ? },	 Rumi Numeral Symbols */
+    /*{ 0x11000, 0x1107F, ? },	 Brahmi */
+    /*{ 0x11000, 0x1107F, ? },	 Kaithi */
+    { 0x12000, 0x1247F, 110 },	/* Cuneiform; Numbers & Punctuation */
+    /*{ 0x13000, 0x1342F, ? },	 Egyptian Hieroglyphs */
+    /*{ 0x16800, 0x16A3F, ? },	 Bamum Supplement */
+    /*{ 0x1B000, 0x1B0FF, ? },	 Kana Supplement */
+    { 0x1d000, 0x1d1ff, 88 },	/* Byzantine Musical Symbols / Musical Symbols */
+    /*{ 0x1D200, 0x1D24F, ? },	 Ancient Greek Musical Notation */
+    { 0x1d300, 0x1d35f, 109 },	/* Tai Xuan Jing Symbols */
+    { 0x1D360, 0x1D37F, 111 },	/* Counting Rod Numerals */
+    { 0x1d400, 0x1d7ff, 89 },	/* Mathematical Alphanumeric Symbols */
+    { 0x1F000, 0x1F02B, 122 },	/* Mahjong Tiles */
+    { 0x1F030, 0x1F093, 122 },	/* Dominos */
+    /*{ 0x1F0A0, 0x1F0FF, ? },	 Playing Cards */
+    /*{ 0x1F100, 0x1F1FF, ? },	 Enclosed Alphanumeric Supplement */
+    /*{ 0x1F200, 0x1F2FF, ? },	 Enclosed Ideographic Supplement */
+    /*{ 0x1F300, 0x1F5FF, ? },	 Miscellaneous Symbols And Pictographs */
+    /*{ 0x1F600, 0x1F64F, ? },	 Emoticons */
+    /*{ 0x1F680, 0x1F6FF, ? },	 Transport And Map Symbols */
+    /*{ 0x1F700, 0x1F77F, ? },	 Alchemical Symbols */
+    { 0x20000, 0x2a6df, 59 },	/* CJK Unified Ideographs Extension B */
+    /*{ 0x2A700, 0x2B73F, 59? },	CJK Unified Ideographs Extension C */
+    /*{ 0x2B740, 0x2B81F, 59? },	CJK Unified Ideographs Extension D */
+    { 0x2f800, 0x2fa1f, 61 },	/* CJK Compatibility Ideographs Supplement */
+    { 0xe0000, 0xe007f, 92 },	/* Tags */
+    { 0xe0100, 0xe01ef, 91 },	/* Variation Selectors Supplement */
+    { 0xf0000, 0xffffd, 90 },	/* Supplementary Private Use Area-A */
+    { 0x100000, 0x10fffd, 90 },	/* Supplementary Private Use Area-B */
+};
+
+static int32 getuint32(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 int short_too_long_warned = 0;
+
+void putshort(FILE *file,int sval) {
+    if ( sval<-32768 || sval>65535 )
+	if (!short_too_long_warned) {
+	  IError(_("Attempt to output %d into a 16-bit field. It will be truncated and the file may not be useful."), sval );
+	  short_too_long_warned = 1;
+	}
+    putc((sval>>8)&0xff,file);
+    putc(sval&0xff,file);
+}
+
+static void putu24(FILE *file,int val) {
+    putc((val>>16)&0xff,file);
+    putc((val>>8)&0xff,file);
+    putc(val&0xff,file);
+}
+
+void putlong(FILE *file,int val) {
+    putc((val>>24)&0xff,file);
+    putc((val>>16)&0xff,file);
+    putc((val>>8)&0xff,file);
+    putc(val&0xff,file);
+}
+#define dumpabsoffset	putlong
+
+static void dumpoffset(FILE *file,int offsize,int val) {
+    if ( offsize==1 )
+	putc(val,file);
+    else if ( offsize==2 )
+	putshort(file,val);
+    else if ( offsize==3 ) {
+	putc((val>>16)&0xff,file);
+	putc((val>>8)&0xff,file);
+	putc(val&0xff,file);
+    } else
+	putlong(file,val);
+}
+
+static void put2d14(FILE *file,real dval) {
+    int val;
+    int mant;
+
+    val = floor(dval);
+    mant = floor(16384.*(dval-val));
+    val = (val<<14) | mant;
+    putshort(file,val);
+}
+
+void putfixed(FILE *file,real dval) {
+    int val;
+    int mant;
+
+    val = floor(dval);
+    mant = floor(65536.*(dval-val));
+    val = (val<<16) | mant;
+    putlong(file,val);
+}
+
+int ttfcopyfile(FILE *ttf, FILE *other, int pos, const char *tab_name) {
+    int ch;
+    int ret = 1;
+
+    if ( ferror(ttf) || ferror(other)) {
+	IError("Disk error of some nature. Perhaps no space on device?\nGenerated font will be unusable" );
+    } else if ( pos!=ftell(ttf)) {
+	IError("File Offset wrong for ttf table (%s), %d expected %d", tab_name, ftell(ttf), pos );
+    }
+    rewind(other);
+    while (( ch = getc(other))!=EOF )
+	putc(ch,ttf);
+    if ( ferror(other)) ret = 0;
+    if ( fclose(other)) ret = 0;
+return( ret );
+}
+
+static void FigureFullMetricsEnd(SplineFont *sf,struct glyphinfo *gi, int istt ) {
+    /* We can reduce the size of the width array by removing a run at the end */
+    /*  of the same width. So start at the end, find the width of the last */
+    /*  character we'll output, then run backwards as long as we've got the */
+    /*  same width */
+    /* (do same thing for vertical metrics too */
+    int i, lasti, lastv, lastdefault = istt ? 3 : 1;
+    int width, vwidth;
+
+    lasti = lastv = gi->gcnt-1;
+    for ( i=gi->gcnt-1; i>lastdefault && gi->bygid[i]==-1; --i );
+    if ( i>=lastdefault ) {
+	width = sf->glyphs[gi->bygid[i]]->width;
+	vwidth = sf->glyphs[gi->bygid[i]]->vwidth;
+	lasti = lastv = i;
+	for ( i=lasti-1; i>=lastdefault; --i ) {
+	    if ( SCWorthOutputting(sf->glyphs[gi->bygid[i]]) ) {
+		if ( sf->glyphs[gi->bygid[i]]->width!=width )
+	break;
+		else
+		    lasti = i;
+	    }
+	}
+	gi->lasthwidth = lasti;
+	if ( sf->hasvmetrics ) {
+	    for ( i=lastv-1; i>=lastdefault; --i ) {
+		if ( SCWorthOutputting(sf->glyphs[gi->bygid[i]]) ) {
+		    if ( sf->glyphs[gi->bygid[i]]->vwidth!=vwidth )
+	    break;
+		    else
+			lastv = i;
+		}
+	    }
+	    gi->lastvwidth = lastv;
+	}
+    } else {
+	gi->lasthwidth = 0;
+	gi->lastvwidth = 0;
+    }
+}
+
+static void dumpghstruct(struct glyphinfo *gi,struct glyphhead *gh) {
+
+    putshort(gi->glyphs,gh->numContours);
+    putshort(gi->glyphs,gh->xmin);
+    putshort(gi->glyphs,gh->ymin);
+    putshort(gi->glyphs,gh->xmax);
+    putshort(gi->glyphs,gh->ymax);
+    if ( gh->xmin<gi->xmin ) gi->xmin = gh->xmin;
+    if ( gh->ymin<gi->ymin ) gi->ymin = gh->ymin;
+    if ( gh->xmax>gi->xmax ) gi->xmax = gh->xmax;
+    if ( gh->ymax>gi->ymax ) gi->ymax = gh->ymax;
+}
+
+static void ttfdumpmetrics(SplineChar *sc,struct glyphinfo *gi,DBounds *b) {
+    int width=sc->width;
+    int vwidth=sc->vwidth;
+
+    if ( width<0 ) width=0;
+    if ( vwidth<0 ) vwidth=0;
+
+    if ( sc->ttf_glyph<=gi->lasthwidth )
+	putshort(gi->hmtx,width);
+    putshort(gi->hmtx,b->minx);
+    if ( sc->parent->hasvmetrics ) {
+	if ( sc->ttf_glyph<=gi->lastvwidth )
+	    putshort(gi->vmtx,vwidth);
+	putshort(gi->vmtx, sc->parent->ascent - b->maxy);
+    }
+    if ( sc->ttf_glyph==gi->lasthwidth )
+	gi->hfullcnt = sc->ttf_glyph+1;
+    if ( sc->ttf_glyph==gi->lastvwidth )
+	gi->vfullcnt = sc->ttf_glyph+1;
+}
+
+static SplineSet *SCttfApprox(SplineChar *sc,int layer) {
+    SplineSet *head=NULL, *last, *ss, *tss;
+    RefChar *ref;
+
+    for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
+	tss = sc->layers[layer].order2 ? SplinePointListCopy1(ss) : SSttfApprox(ss);
+	if ( head==NULL ) head = tss;
+	else last->next = tss;
+	last = tss;
+    }
+    for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
+	for ( ss=ref->layers[0].splines; ss!=NULL; ss=ss->next ) {
+	    tss = sc->layers[layer].order2 ? SplinePointListCopy1(ss) : SSttfApprox(ss);
+	    if ( head==NULL ) head = tss;
+	    else last->next = tss;
+	    last = tss;
+	}
+    }
+return( head );
+}
+
+#define _On_Curve	1
+#define _X_Short	2
+#define _Y_Short	4
+#define _Repeat		8
+#define _X_Same		0x10
+#define _Y_Same		0x20
+
+int SSAddPoints(SplineSet *ss,int ptcnt,BasePoint *bp, char *flags) {
+    SplinePoint *sp, *first, *nextsp;
+    int startcnt = ptcnt;
+
+    if ( ss->first->prev!=NULL &&
+	    ss->first->prev->from->nextcpindex==startcnt ) {
+	if ( flags!=NULL ) flags[ptcnt] = 0;
+	bp[ptcnt].x = rint(ss->first->prevcp.x);
+	bp[ptcnt++].y = rint(ss->first->prevcp.y);
+    } else if ( ss->first->ttfindex!=ptcnt && ss->first->ttfindex!=0xfffe )
+	IError("Unexpected point count in SSAddPoints" );
+
+    first = NULL;
+    for ( sp=ss->first; sp!=first ; ) {
+	if ( sp->ttfindex!=0xffff ) {
+	    if ( flags!=NULL ) flags[ptcnt] = _On_Curve;
+	    bp[ptcnt].x = rint(sp->me.x);
+	    bp[ptcnt].y = rint(sp->me.y);
+	    sp->ttfindex = ptcnt++;
+	} else if ( !SPInterpolate( sp ) ) {
+	    /* If an on curve point is midway between two off curve points*/
+	    /*  it may be omitted and will be interpolated on read in */
+	    if ( flags!=NULL ) flags[ptcnt] = _On_Curve;
+	    bp[ptcnt].x = rint(sp->me.x);
+	    bp[ptcnt].y = rint(sp->me.y);
+	    sp->ttfindex = ptcnt++;
+	}
+	nextsp = sp->next!=NULL ? sp->next->to : NULL;
+	if ( sp->nextcpindex == startcnt )
+	    /* This control point is actually our first point, not our last */
+    break;
+	if ( (sp->nextcpindex !=0xffff && sp->nextcpindex!=0xfffe ) ||
+		!sp->nonextcp ) {
+	    if ( flags!=NULL ) flags[ptcnt] = 0;
+	    bp[ptcnt].x = rint(sp->nextcp.x);
+	    bp[ptcnt++].y = rint(sp->nextcp.y);
+	}
+	if ( nextsp==NULL )
+    break;
+	if ( first==NULL ) first = sp;
+	sp = nextsp;
+    }
+return( ptcnt );
+}
+
+static void dumppointarrays(struct glyphinfo *gi,BasePoint *bp, char *fs, int pc) {
+    BasePoint last;
+    int i,flags;
+    int lastflag, flagcnt;
+
+    if ( gi->maxp->maxPoints<pc )
+	gi->maxp->maxPoints = pc;
+
+	/* flags */
+    last.x = last.y = 0;
+    lastflag = -1; flagcnt = 0;
+    for ( i=0; i<pc; ++i ) {
+	flags = 0;
+	if ( fs==NULL || fs[i] )
+	    flags = _On_Curve;		/* points are on curve */
+	if ( last.x==bp[i].x )
+	    flags |= _X_Same;
+	else if ( bp[i].x-last.x>-256 && bp[i].x-last.x<255 ) {
+	    flags |= _X_Short;
+	    if ( bp[i].x>=last.x )
+		flags |= _X_Same;		/* In this context it means positive */
+	}
+	if ( last.y==bp[i].y )
+	    flags |= _Y_Same;
+	else if ( bp[i].y-last.y>-256 && bp[i].y-last.y<255 ) {
+	    flags |= _Y_Short;
+	    if ( bp[i].y>=last.y )
+		flags |= _Y_Same;		/* In this context it means positive */
+	}
+	last = bp[i];
+	if ( lastflag==-1 ) {
+	    lastflag = flags;
+	    flagcnt = 0;
+	} else if ( flags!=lastflag ) {
+	    if ( flagcnt!=0 )
+		lastflag |= _Repeat;
+	    putc(lastflag,gi->glyphs);
+	    if ( flagcnt!=0 )
+		putc(flagcnt,gi->glyphs);
+	    lastflag = flags;
+	    flagcnt = 0;
+	} else {
+	    if ( ++flagcnt == 255 ) {
+		putc(lastflag|_Repeat,gi->glyphs);
+		putc(255,gi->glyphs);
+		lastflag = -1;
+		flagcnt = 0;
+	    }
+	}
+    }
+    if ( lastflag!=-1 ) {
+	if ( flagcnt!=0 )
+	    lastflag |= _Repeat;
+	putc(lastflag,gi->glyphs);
+	if ( flagcnt!=0 )
+	    putc(flagcnt,gi->glyphs);
+    }
+
+	/* xcoords */
+    last.x = 0;
+    for ( i=0; i<pc; ++i ) {
+	if ( last.x==bp[i].x )
+	    /* Do Nothing */;
+	else if ( bp[i].x-last.x>-256 && bp[i].x-last.x<255 ) {
+	    if ( bp[i].x>=last.x )
+		putc(bp[i].x-last.x,gi->glyphs);
+	    else
+		putc(last.x-bp[i].x,gi->glyphs);
+	} else
+	    putshort(gi->glyphs,bp[i].x-last.x);
+	last.x = bp[i].x;
+    }
+	/* ycoords */
+    last.y = 0;
+    for ( i=0; i<pc; ++i ) {
+	if ( last.y==bp[i].y )
+	    /* Do Nothing */;
+	else if ( bp[i].y-last.y>-256 && bp[i].y-last.y<255 ) {
+	    if ( bp[i].y>=last.y )
+		putc(bp[i].y-last.y,gi->glyphs);
+	    else
+		putc(last.y-bp[i].y,gi->glyphs);
+	} else
+	    putshort(gi->glyphs,bp[i].y-last.y);
+	last.y = bp[i].y;
+    }
+    if ( ftell(gi->glyphs)&1 )		/* Pad the file so that the next glyph */
+	putc('\0',gi->glyphs);		/* on a word boundary */
+}
+
+static void dumpinstrs(struct glyphinfo *gi,uint8 *instrs,int cnt) {
+    int i;
+
+    if ( (gi->flags&ttf_flag_nohints) ) {
+	putshort(gi->glyphs,0);
+return;
+    }
+    /* Do we ever want to call AutoHint and AutoInst here? I think not. */
+
+    if ( gi->maxp->maxglyphInstr<cnt ) gi->maxp->maxglyphInstr=cnt;
+    putshort(gi->glyphs,cnt);
+    for ( i=0; i<cnt; ++i )
+	putc( instrs[i],gi->glyphs );
+}
+
+static void dumpmissingglyph(SplineFont *sf,struct glyphinfo *gi,int fixedwidth) {
+    /* Or .notdef */
+    struct glyphhead gh;
+    BasePoint bp[10];
+    uint8 instrs[50];
+    int stemcvt, stem;
+    char *stempt;
+
+    stem = 0;
+    if ( sf->private!=NULL && (stempt=PSDictHasEntry(sf->private,"StdVW"))!=NULL )
+	stem = strtod(stempt,NULL);
+    else if ( sf->private!=NULL && (stempt=PSDictHasEntry(sf->private,"StdHW"))!=NULL )
+	stem = strtod(stempt,NULL);
+    if ( stem<=0 )
+	stem = (sf->ascent+sf->descent)/30;
+
+    gi->pointcounts[gi->next_glyph] = 8;
+    gi->loca[gi->next_glyph++] = ftell(gi->glyphs);
+    gi->maxp->maxContours = 2;
+
+    gh.numContours = 2;
+    gh.ymin = 0;
+    gh.ymax = 2*(sf->ascent+sf->descent)/3;
+    gh.xmax = 5*stem+(sf->ascent+sf->descent)/10;
+    gh.xmin = stem;
+    gh.xmax += stem;
+    if ( gh.ymax>sf->ascent ) gh.ymax = sf->ascent;
+    dumpghstruct(gi,&gh);
+
+    bp[0].x = stem;		bp[0].y = 0;
+    bp[1].x = stem;		bp[1].y = gh.ymax;
+    bp[2].x = gh.xmax;		bp[2].y = gh.ymax;
+    bp[3].x = gh.xmax;		bp[3].y = 0;
+
+    bp[4].x = 2*stem;		bp[4].y = stem;
+    bp[5].x = gh.xmax-stem;	bp[5].y = stem;
+    bp[6].x = gh.xmax-stem;	bp[6].y = gh.ymax-stem;
+    bp[7].x = 2*stem;		bp[7].y = gh.ymax-stem;
+
+    if ( !gi->ttc_composite_font ) {
+	stemcvt = TTF_getcvtval(gi->sf,stem);
+
+	instrs[0] = 0xb1;		/* Pushb, 2byte */
+	instrs[1] = 1;			/* Point 1 */
+	instrs[2] = 0;			/* Point 0 */
+	instrs[3] = 0x2f;		/* MDAP, rounded (pt0) */
+	instrs[4] = 0x3c;		/* ALIGNRP, (pt1 same pos as pt0)*/
+	instrs[5] = 0xb2;		/* Pushb, 3byte */
+	instrs[6] = 7;			/* Point 7 */
+	instrs[7] = 4;			/* Point 4 */
+	instrs[8] = stemcvt;		/* CVT entry for our stem width */
+	instrs[9] = 0xe0+0x0d;		/* MIRP, don't set rp0, minimum, rounded, black */
+	instrs[10] = 0x32;		/* SHP[rp2] (pt7 same pos as pt4) */
+	instrs[11] = 0xb1;		/* Pushb, 2byte */
+	instrs[12] = 6;			/* Point 6 */
+	instrs[13] = 5;			/* Point 5 */
+	instrs[14] = 0xc0+0x1c;		/* MDRP, set rp0, minimum, rounded, grey */
+	instrs[15] = 0x3c;		/* ALIGNRP, (pt6 same pos as pt5)*/
+	instrs[16] = 0xb2;		/* Pushb, 3byte */
+	instrs[17] = 3;			/* Point 3 */
+	instrs[18] = 2;			/* Point 2 */
+	instrs[19] = stemcvt;		/* CVT entry for our stem width */
+	instrs[20] = 0xe0+0x0d;		/* MIRP, dont set rp0, minimum, rounded, black */
+	instrs[21] = 0x32;		/* SHP[rp2] (pt3 same pos as pt2) */
+
+	instrs[22] = 0x00;		/* SVTCA, y axis */
+
+	instrs[23] = 0xb1;		/* Pushb, 2byte */
+	instrs[24] = 3;			/* Point 3 */
+	instrs[25] = 0;			/* Point 0 */
+	instrs[26] = 0x2f;		/* MDAP, rounded */
+	instrs[27] = 0x3c;		/* ALIGNRP, (pt3 same height as pt0)*/
+	instrs[28] = 0xb2;		/* Pushb, 3byte */
+	instrs[29] = 5;			/* Point 5 */
+	instrs[30] = 4;			/* Point 4 */
+	instrs[31] = stemcvt;		/* CVT entry for our stem width */
+	instrs[32] = 0xe0+0x0d;		/* MIRP, don't set rp0, minimum, rounded, black */
+	instrs[33] = 0x32;		/* SHP[rp2] (pt5 same height as pt4) */
+	instrs[34] = 0xb2;		/* Pushb, 3byte */
+	instrs[35] = 7;			/* Point 7 */
+	instrs[36] = 6;			/* Point 6 */
+	instrs[37] = TTF_getcvtval(gi->sf,bp[6].y);	/* CVT entry for top height */
+	instrs[38] = 0xe0+0x1c;		/* MIRP, set rp0, minimum, rounded, grey */
+	instrs[39] = 0x3c;		/* ALIGNRP (pt7 same height as pt6) */
+	instrs[40] = 0xb2;		/* Pushb, 3byte */
+	instrs[41] = 1;			/* Point 1 */
+	instrs[42] = 2;			/* Point 2 */
+	instrs[43] = stemcvt;		/* CVT entry for our stem width */
+	instrs[44] = 0xe0+0x0d;		/* MIRP, dont set rp0, minimum, rounded, black */
+	instrs[45] = 0x32;		/* SHP[rp2] (pt1 same height as pt2) */
+
+	/* We've touched all points in all dimensions */
+	/* Don't need any IUP */
+    }
+
+	/* end contours array */
+    putshort(gi->glyphs,4-1);
+    putshort(gi->glyphs,8-1);
+	/* instruction length&instructions */
+    if ( !gi->ttc_composite_font )
+	dumpinstrs(gi,instrs,46);
+    else
+	dumpinstrs(gi,NULL,0);
+
+    dumppointarrays(gi,bp,NULL,8);
+
+    if ( fixedwidth<=0 )
+	putshort(gi->hmtx,gh.xmax + 2*stem);
+    else
+	putshort(gi->hmtx,fixedwidth);
+    putshort(gi->hmtx,stem);
+    if ( sf->hasvmetrics ) {
+	putshort(gi->vmtx,sf->ascent+sf->descent);
+	putshort(gi->vmtx,/*sf->vertical_origin-*/gh.ymax);
+    }
+}
+
+static void dumpblankglyph(struct glyphinfo *gi,SplineFont *sf,int fixedwidth) {
+    int advance = gi->next_glyph==1?0:fixedwidth<=0?(sf->ascent+sf->descent)/3:
+	    fixedwidth;
+    /* For reasons quite obscure to me, glyph 1 has an advance width of 0 */
+    /* even in a mono-spaced font like CourierNew.ttf */
+
+    /* These don't get a glyph header, because there are no contours */
+    gi->pointcounts[gi->next_glyph] = 0;
+    gi->loca[gi->next_glyph++] = ftell(gi->glyphs);
+    putshort(gi->hmtx,advance);
+    putshort(gi->hmtx,0);
+    if ( sf->hasvmetrics ) {
+	putshort(gi->vmtx,gi->next_glyph==2?0:(sf->ascent+sf->descent));
+	putshort(gi->vmtx,0);
+    }
+}
+
+static void dumpspace(SplineChar *sc, struct glyphinfo *gi) {
+    /* These don't get a glyph header, because there are no contours */
+    DBounds b;
+    gi->pointcounts[gi->next_glyph] = 0;
+    gi->loca[gi->next_glyph++] = ftell(gi->glyphs);
+    memset(&b,0,sizeof(b));
+    ttfdumpmetrics(sc,gi,&b);
+}
+
+static int IsTTFRefable(SplineChar *sc,int layer) {
+    RefChar *ref;
+
+    if ( sc->layers[layer].refs==NULL || sc->layers[layer].splines!=NULL )
+return( false );
+
+    for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
+	if ( ref->transform[0]<-2 || ref->transform[0]>1.999939 ||
+		ref->transform[1]<-2 || ref->transform[1]>1.999939 ||
+		ref->transform[2]<-2 || ref->transform[2]>1.999939 ||
+		ref->transform[3]<-2 || ref->transform[3]>1.999939 )
+return( false );
+    }
+return( true );
+}
+
+int RefDepth(RefChar *ref,int layer) {
+    int rd, temp;
+    SplineChar *sc = ref->sc;
+
+    if ( sc->layers[layer].refs==NULL || sc->layers[layer].splines!=NULL )
+return( 1 );
+    rd = 0;
+    for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
+	if ( ref->transform[0]>=-2 || ref->transform[0]<=1.999939 ||
+		ref->transform[1]>=-2 || ref->transform[1]<=1.999939 ||
+		ref->transform[2]>=-2 || ref->transform[2]<=1.999939 ||
+		ref->transform[3]>=-2 || ref->transform[3]<=1.999939 ) {
+	    temp = RefDepth(ref,layer);
+	    if ( temp>rd ) rd = temp;
+	}
+    }
+return( rd+1 );
+}
+
+static void CountCompositeMaxPts(SplineChar *sc,struct glyphinfo *gi) {
+    RefChar *ref;
+    int ptcnt = 0, index;
+
+    for ( ref=sc->layers[gi->layer].refs; ref!=NULL; ref=ref->next ) {
+	if ( ref->sc->ttf_glyph==-1 )
+    continue;
+	index = ref->sc->ttf_glyph;
+	if ( gi->pointcounts[index]==-1 )
+	    CountCompositeMaxPts(ref->sc,gi);
+	ptcnt += gi->pointcounts[index];
+    }
+    gi->pointcounts[sc->ttf_glyph] = ptcnt;
+    if ( gi->maxp->maxCompositPts<ptcnt ) gi->maxp->maxCompositPts=ptcnt;
+}
+
+/* In order3 fonts we figure out the composite point counts at the end */
+/*  when we know how many points are in each sub-glyph */
+static void RefigureCompositeMaxPts(SplineFont *sf,struct glyphinfo *gi) {
+    int i;
+
+    for ( i=0; i<gi->gcnt; ++i ) if ( gi->bygid[i]!=-1 && sf->glyphs[gi->bygid[i]]->ttf_glyph!=-1 ) {
+	if ( sf->glyphs[gi->bygid[i]]->layers[gi->layer].splines==NULL &&
+		sf->glyphs[gi->bygid[i]]->layers[gi->layer].refs!=NULL &&
+		gi->pointcounts[i]== -1 )
+	    CountCompositeMaxPts(sf->glyphs[gi->bygid[i]],gi);
+    }
+}
+
+static void dumpcomposite(SplineChar *sc, struct glyphinfo *gi) {
+    struct glyphhead gh;
+    DBounds bb;
+    int i, ptcnt, ctcnt, flags, sptcnt, rd;
+    SplineSet *ss;
+    RefChar *ref;
+    SplineChar *isc = sc->ttf_instrs==NULL && sc->parent->mm!=NULL && sc->parent->mm->apple ?
+		sc->parent->mm->normal->glyphs[sc->orig_pos] : sc;
+    int arg1, arg2;
+
+    if ( gi->next_glyph!=sc->ttf_glyph )
+	IError("Glyph count wrong in ttf output");
+    if ( gi->next_glyph>=gi->maxp->numGlyphs )
+	IError("max glyph count wrong in ttf output");
+    gi->loca[gi->next_glyph] = ftell(gi->glyphs);
+
+    SplineCharLayerQuickBounds(sc,gi->layer,&bb);
+    gh.numContours = -1;
+    gh.xmin = floor(bb.minx); gh.ymin = floor(bb.miny);
+    gh.xmax = ceil(bb.maxx); gh.ymax = ceil(bb.maxy);
+    dumpghstruct(gi,&gh);
+
+    i=ptcnt=ctcnt=0;
+    for ( ref=sc->layers[gi->layer].refs; ref!=NULL; ref=ref->next, ++i ) {
+	if ( ref->sc->ttf_glyph==-1 ) {
+	    /*if ( sc->layers[gi->layer].refs->next==NULL || any )*/
+    continue;
+	}
+	flags = 0;
+	if ( ref->round_translation_to_grid )
+	    flags |= _ROUND;
+	if ( ref->use_my_metrics )
+	    flags |= _USE_MY_METRICS;
+	if ( ref->next!=NULL )
+	    flags |= _MORE;		/* More components */
+	else if ( isc->ttf_instrs_len!=0 )	/* Composits also inherit instructions */
+	    flags |= _INSTR;		/* Instructions appear after last ref */
+	if ( ref->transform[1]!=0 || ref->transform[2]!=0 )
+	    flags |= _MATRIX;		/* Need a full matrix */
+	else if ( ref->transform[0]!=ref->transform[3] )
+	    flags |= _XY_SCALE;		/* different xy scales */
+	else if ( ref->transform[0]!=1. )
+	    flags |= _SCALE;		/* xy scale is same */
+	if ( ref->point_match ) {
+	    arg1 = ref->match_pt_base;
+	    arg2 = ref->match_pt_ref;
+	} else {
+	    arg1 = rint(ref->transform[4]);
+	    arg2 = rint(ref->transform[5]);
+	    flags |= _ARGS_ARE_XY|_UNSCALED_OFFSETS;
+	    /* The values I output are the values I want to see */
+	    /* There is some very strange stuff wrongly-documented on the apple*/
+	    /*  site about how these should be interpretted when there are */
+	    /*  scale factors, or rotations */
+	    /* That description does not match the behavior of their rasterizer*/
+	    /*  I've reverse engineered something else (see parsettf.c) */
+	    /*  http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html */
+	    /* Adobe says that setting bit 12 means that this will not happen */
+	    /*  Apple doesn't mention bit 12 though...(but they do support it) */
+	}
+	if ( arg1<-128 || arg1>127 ||
+		arg2<-128 || arg2>127 )
+	    flags |= _ARGS_ARE_WORDS;
+	putshort(gi->glyphs,flags);
+	putshort(gi->glyphs,ref->sc->ttf_glyph==-1?0:ref->sc->ttf_glyph);
+	if ( flags&_ARGS_ARE_WORDS ) {
+	    putshort(gi->glyphs,(short)arg1);
+	    putshort(gi->glyphs,(short)arg2);
+	} else {
+	    putc((char) arg1,gi->glyphs);
+	    putc((char) arg2,gi->glyphs);
+	}
+	if ( flags&_MATRIX ) {
+	    put2d14(gi->glyphs,ref->transform[0]);
+	    put2d14(gi->glyphs,ref->transform[1]);
+	    put2d14(gi->glyphs,ref->transform[2]);
+	    put2d14(gi->glyphs,ref->transform[3]);
+	} else if ( flags&_XY_SCALE ) {
+	    put2d14(gi->glyphs,ref->transform[0]);
+	    put2d14(gi->glyphs,ref->transform[3]);
+	} else if ( flags&_SCALE ) {
+	    put2d14(gi->glyphs,ref->transform[0]);
+	}
+	sptcnt = SSTtfNumberPoints(ref->layers[0].splines);
+	for ( ss=ref->layers[0].splines; ss!=NULL ; ss=ss->next ) {
+	    ++ctcnt;
+	}
+	if ( sc->layers[gi->layer].order2 )
+	    ptcnt += sptcnt;
+	else if ( ptcnt>=0 && gi->pointcounts[ref->sc->ttf_glyph==-1?0:ref->sc->ttf_glyph]>=0 )
+	    ptcnt += gi->pointcounts[ref->sc->ttf_glyph==-1?0:ref->sc->ttf_glyph];
+	else
+	    ptcnt = -1;
+	rd = RefDepth(ref,gi->layer);
+	if ( rd>gi->maxp->maxcomponentdepth )
+	    gi->maxp->maxcomponentdepth = rd;
+    }
+
+    if ( isc->ttf_instrs_len!=0 )
+	dumpinstrs(gi,isc->ttf_instrs,isc->ttf_instrs_len);
+
+    gi->pointcounts[gi->next_glyph++] = ptcnt;
+    if ( gi->maxp->maxnumcomponents<i ) gi->maxp->maxnumcomponents = i;
+    if ( gi->maxp->maxCompositPts<ptcnt ) gi->maxp->maxCompositPts=ptcnt;
+    if ( gi->maxp->maxCompositCtrs<ctcnt ) gi->maxp->maxCompositCtrs=ctcnt;
+
+    ttfdumpmetrics(sc,gi,&bb);
+    if ( ftell(gi->glyphs)&1 )		/* Pad the file so that the next glyph */
+	putc('\0',gi->glyphs);		/* on a word boundary, can only happen if odd number of instrs */
+}
+
+static void dumpglyph(SplineChar *sc, struct glyphinfo *gi) {
+    struct glyphhead gh;
+    DBounds bb;
+    SplineSet *ss, *ttfss;
+    int contourcnt, ptcnt, origptcnt;
+    BasePoint *bp;
+    char *fs;
+    SplineChar *isc = sc->ttf_instrs==NULL && sc->parent->mm!=NULL && sc->parent->mm->apple ?
+		sc->parent->mm->normal->glyphs[sc->orig_pos] : sc;
+
+    /* This must have been an error on my part, can't just remove points */
+    /* they might be matched to anchors or something */
+/* I haven't seen this documented, but ttf rasterizers are unhappy with a */
+/*  glyph that consists of a single point. Glyphs containing two single points*/
+/*  are ok, glyphs with a single point and anything else are ok, glyphs with */
+/*  a line are ok. But a single point is not ok. Dunno why */
+    if ( sc->layers[gi->layer].splines==NULL && sc->layers[gi->layer].refs==NULL ) {
+	dumpspace(sc,gi);
+return;
+    }
+
+    if ( gi->next_glyph!=sc->ttf_glyph )
+	IError("Glyph count wrong in ttf output");
+    if ( gi->next_glyph>=gi->maxp->numGlyphs )
+	IError("max glyph count wrong in ttf output");
+    gi->loca[gi->next_glyph] = ftell(gi->glyphs);
+
+    ttfss = SCttfApprox(sc,gi->layer);
+    ptcnt = SSTtfNumberPoints(ttfss);
+    for ( ss=ttfss, contourcnt=0; ss!=NULL; ss=ss->next ) {
+	++contourcnt;
+    }
+    origptcnt = ptcnt;
+
+    SplineSetQuickBounds(ttfss,&bb);
+    gh.numContours = contourcnt;
+    gh.xmin = floor(bb.minx); gh.ymin = floor(bb.miny);
+    gh.xmax = ceil(bb.maxx); gh.ymax = ceil(bb.maxy);
+    dumpghstruct(gi,&gh);
+    if ( contourcnt>gi->maxp->maxContours ) gi->maxp->maxContours = contourcnt;
+    if ( ptcnt>gi->maxp->maxPoints ) gi->maxp->maxPoints = ptcnt;
+
+    bp = malloc(ptcnt*sizeof(BasePoint));
+    fs = malloc(ptcnt);
+    ptcnt = contourcnt = 0;
+    for ( ss=ttfss; ss!=NULL; ss=ss->next ) {
+	ptcnt = SSAddPoints(ss,ptcnt,bp,fs);
+	putshort(gi->glyphs,ptcnt-1);
+    }
+    if ( ptcnt!=origptcnt )
+	IError( "Point count wrong calculated=%d, actual=%d in %.20s", origptcnt, ptcnt, sc->name );
+    gi->pointcounts[gi->next_glyph++] = ptcnt;
+
+    dumpinstrs(gi,isc->ttf_instrs,isc->ttf_instrs_len);
+
+    dumppointarrays(gi,bp,fs,ptcnt);
+    SplinePointListsFree(ttfss);
+    free(bp);
+    free(fs);
+
+    ttfdumpmetrics(sc,gi,&bb);
+}
+
+void SFDummyUpCIDs(struct glyphinfo *gi,SplineFont *sf) {
+    int i,j,k,max;
+    int *bygid;
+
+    max = 0;
+    for ( k=0; k<sf->subfontcnt; ++k )
+	if ( sf->subfonts[k]->glyphcnt>max ) max = sf->subfonts[k]->glyphcnt;
+    if ( max == 0 )
+return;
+
+    sf->glyphs = calloc(max,sizeof(SplineChar *));
+    sf->glyphcnt = sf->glyphmax = max;
+    for ( k=0; k<sf->subfontcnt; ++k )
+	for ( i=0; i<sf->subfonts[k]->glyphcnt; ++i ) if ( sf->subfonts[k]->glyphs[i]!=NULL )
+	    sf->glyphs[i] = sf->subfonts[k]->glyphs[i];
+
+    if ( gi==NULL )
+return;
+
+    bygid = malloc((sf->glyphcnt+3)*sizeof(int));
+    memset(bygid,0xff, (sf->glyphcnt+3)*sizeof(int));
+
+    j=1;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	if ( bygid[0]== -1 && strcmp(sf->glyphs[i]->name,".notdef")==0 ) {
+	    sf->glyphs[i]->ttf_glyph = 0;
+	    bygid[0] = i;
+	} else if ( SCWorthOutputting(sf->glyphs[i])) {
+	    sf->glyphs[i]->ttf_glyph = j;
+	    bygid[j++] = i;
+	}
+    }
+    gi->bygid = bygid;
+    gi->gcnt = j;
+}
+
+static void AssignNotdefNull(SplineFont *sf,int *bygid, int iscff) {
+    int i;
+
+    /* The first three glyphs are magic, glyph 0 is .notdef */
+    /*  glyph 1 is .null and glyph 2 is nonmarking return */
+    /*  We may generate them automagically */
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	if ( bygid[0]== -1 && strcmp(sf->glyphs[i]->name,".notdef")==0 ) {
+	    sf->glyphs[i]->ttf_glyph = 0;
+	    bygid[0] = i;
+	} else if ( !iscff && bygid[1]== -1 &&
+		(strcmp(sf->glyphs[i]->name,".null")==0 ||
+		 strcmp(sf->glyphs[i]->name,"uni0000")==0 ||
+		 (i==1 && strcmp(sf->glyphs[1]->name,"glyph1")==0)) ) {
+	    sf->glyphs[i]->ttf_glyph = 1;
+	    bygid[1] = i;
+	} else if ( !iscff && bygid[2]== -1 &&
+		(strcmp(sf->glyphs[i]->name,"nonmarkingreturn")==0 ||
+		 strcmp(sf->glyphs[i]->name,"uni000D")==0 ||
+		 (i==2 && strcmp(sf->glyphs[2]->name,"glyph2")==0)) ) {
+	    sf->glyphs[i]->ttf_glyph = 2;
+	    bygid[2] = i;
+	}
+    }
+}
+
+static int AssignTTFGlyph(struct glyphinfo *gi,SplineFont *sf,EncMap *map,int iscff) {
+    int *bygid = malloc((sf->glyphcnt+3)*sizeof(int));
+    int i,j;
+
+    memset(bygid,0xff, (sf->glyphcnt+3)*sizeof(int));
+
+    AssignNotdefNull(sf,bygid,iscff);
+
+    j = iscff ? 1 : 3;
+    for ( i=0; i<map->enccount; ++i ) if ( map->map[i]!=-1 ) {
+	SplineChar *sc = sf->glyphs[map->map[i]];
+	if ( SCWorthOutputting(sc) && sc->ttf_glyph==-1
+#if HANYANG
+		&& (!iscff || !sc->compositionunit)
+#endif
+	) {
+	    sc->ttf_glyph = j;
+	    bygid[j++] = sc->orig_pos;
+	}
+    }
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	if ( SCWorthOutputting(sc) && sc->ttf_glyph==-1
+#if HANYANG
+		&& (!iscff || !sc->compositionunit)
+#endif
+	) {
+	    sc->ttf_glyph = j;
+	    bygid[j++] = i;
+	}
+    }
+    gi->bygid = bygid;
+    gi->gcnt = j;
+return j;
+}
+
+static int AssignTTFBitGlyph(struct glyphinfo *gi,SplineFont *sf,EncMap *map,int32 *bsizes) {
+    int i, j;
+    BDFFont *bdf;
+    int *bygid = malloc((sf->glyphcnt+3)*sizeof(int));
+
+    memset(bygid,0xff, (sf->glyphcnt+3)*sizeof(int));
+
+    AssignNotdefNull(sf,bygid,false);
+
+    for ( bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
+	for ( j=0; bsizes[j]!=0 && ((bsizes[j]&0xffff)!=bdf->pixelsize || (bsizes[j]>>16)!=BDFDepth(bdf)); ++j );
+	if ( bsizes[j]==0 )
+    continue;
+	for ( i=0; i<bdf->glyphcnt; ++i ) if ( !IsntBDFChar(bdf->glyphs[i]) )
+	    sf->glyphs[i]->ttf_glyph = -2;
+    }
+
+    j = 3;
+    for ( i=0; i<map->enccount; ++i ) if ( map->map[i]!=-1 ) {
+	SplineChar *sc = sf->glyphs[map->map[i]];
+	if ( sc->ttf_glyph==-2 ) {
+	    sc->ttf_glyph = j;
+	    bygid[j++] = sc->orig_pos;
+	}
+    }
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	if ( sc->ttf_glyph==-2 ) {
+	    sc->ttf_glyph = j;
+	    bygid[j++] = i;
+	}
+    }
+
+    gi->bygid = bygid;
+    gi->gcnt = j;
+return j;
+}
+
+static int dumpglyphs(SplineFont *sf,struct glyphinfo *gi) {
+    int i;
+    int fixed = gi->fixed_width;
+    int answer, answered=-1;
+
+    ff_progress_change_stages(2+gi->strikecnt);
+    QuickBlues(sf,gi->layer,&gi->bd);
+    /*FindBlues(sf,gi->blues,NULL);*/
+    ff_progress_next_stage();
+
+    if ( !gi->onlybitmaps ) {
+	if ( sf->layers[gi->layer].order2 )
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		SplineChar *sc = sf->glyphs[i];
+		if ( SCWorthOutputting(sc) )
+		    if ( !SCPointsNumberedProperly(sc,gi->layer)) {
+			if ( answered==-1 && sc->ttf_instrs_len!=0 ) {
+			    char *buts[5];
+			    buts[0] = _("_Yes");
+			    buts[1] = _("Yes to _All");
+			    buts[2] = _("No _to All");
+			    buts[3] = _("_No");
+			    buts[4] = NULL;
+			    ff_progress_pause_timer();
+			    answer = ff_ask(_("Bad Point Numbering"),(const char **) buts,0,3,_("The points in %s are not numbered properly. This means that any instructions will probably move the wrong points and do the wrong thing.\nWould you like me to remove the instructions?"),sc->name);
+			    if ( answer==1 || answer==2 )
+				answered = answer;
+			} else
+			    answer=answered;
+			if ( answer==0 ) {
+			    free(sc->ttf_instrs); sc->ttf_instrs = NULL;
+			    sc->ttf_instrs_len = 0;
+			    SCMarkInstrDlgAsChanged(sc);
+			}
+			SCNumberPoints(sc,gi->layer);
+		    }
+	    }
+    }
+
+    gi->maxp->numGlyphs = gi->gcnt;
+    gi->loca = malloc((gi->maxp->numGlyphs+1)*sizeof(uint32));
+    gi->pointcounts = malloc((gi->maxp->numGlyphs+1)*sizeof(int32));
+    memset(gi->pointcounts,-1,(gi->maxp->numGlyphs+1)*sizeof(int32));
+    gi->next_glyph = 0;
+    gi->glyphs = tmpfile();
+    gi->hmtx = tmpfile();
+    if ( sf->hasvmetrics )
+	gi->vmtx = tmpfile();
+    FigureFullMetricsEnd(sf,gi,true);
+
+    if ( fixed>0 ) {
+	gi->lasthwidth = 3;
+	gi->hfullcnt = 3;
+    }
+    for ( i=0; i<gi->gcnt; ++i ) {
+	if ( i==0 ) {
+	    if ( gi->bygid[0]!=-1 && (fixed<=0 || sf->glyphs[gi->bygid[0]]->width==fixed))
+		dumpglyph(sf->glyphs[gi->bygid[0]],gi);
+	    else
+		dumpmissingglyph(sf,gi,fixed);
+	} else if ( i<=2 && gi->bygid[i]==-1 )
+	    dumpblankglyph(gi,sf,fixed);
+	else if ( gi->onlybitmaps ) {
+	    if ( gi->bygid[i]!=-1 && sf->glyphs[gi->bygid[i]]->ttf_glyph>0 )
+		dumpspace(sf->glyphs[gi->bygid[i]],gi);
+	} else {
+	    if ( gi->bygid[i]!=-1 && sf->glyphs[gi->bygid[i]]->ttf_glyph>0 ) {
+		if ( IsTTFRefable(sf->glyphs[gi->bygid[i]],gi->layer) )
+		    dumpcomposite(sf->glyphs[gi->bygid[i]],gi);
+		else
+		    dumpglyph(sf->glyphs[gi->bygid[i]],gi);
+	    }
+	}
+	if ( (ftell(gi->glyphs)&3) != 0 ) {
+	    /* Apple says glyphs must be 16bit aligned */
+	    if ( ftell(gi->glyphs)&1 )
+		putc('\0',gi->glyphs);
+	    /* MS says glyphs should be 32bit aligned */
+	    if ( ftell(gi->glyphs)&2 )
+		putshort(gi->glyphs,0);
+	}
+	if ( !ff_progress_next())
+return( false );
+    }
+
+    /* extra location entry points to end of last glyph */
+    gi->loca[gi->next_glyph] = ftell(gi->glyphs);
+    /* Microsoft's Font Validator wants the last loca entry to point into the */
+    /*  glyph table. I think that's an error on their part, but it's so easy */
+    /*  to fix, I might as well (instead of pointing to right after the table)*/
+    /* Sigh. But if I do that, it complains that there's extra stuff in the */
+    /*  glyph table. There's just no pleasing them */
+    /* putlong(gi->glyphs,0);*/
+    gi->glyph_len = ftell(gi->glyphs);
+    gi->hmtxlen = ftell(gi->hmtx);
+    /* pad out to four bytes */
+    if ( gi->hmtxlen&2 ) putshort(gi->hmtx,0);
+    if ( gi->loca[gi->next_glyph]&3 ) {
+	for ( i=4-(gi->loca[gi->next_glyph]&3); i>0; --i )
+	    putc('\0',gi->glyphs);
+    }
+    if ( sf->hasvmetrics ) {
+	gi->vmtxlen = ftell(gi->vmtx);
+	if ( gi->vmtxlen&2 ) putshort(gi->vmtx,0);
+    }
+    if ( !sf->layers[gi->layer].order2 )
+	RefigureCompositeMaxPts(sf,gi);
+    free(gi->pointcounts);
+
+return( true );
+}
+
+/* Generate a null glyph and loca table for X opentype bitmaps */
+static int dumpnoglyphs(struct glyphinfo *gi) {
+    gi->glyphs = tmpfile();
+    gi->glyph_len = 0;
+    /* loca gets built in dummyloca */
+return( true );
+}
+
+static int storesid(struct alltabs *at,const char *str) {
+    int i;
+    FILE *news;
+    const char *pt;
+    long pos;
+
+    if ( str!=NULL ) {			/* NULL is the magic string at end of array */
+	for ( i=0; cffnames[i]!=NULL; ++i ) {
+	    if ( strcmp(cffnames[i],str)==0 )
+return( i );
+	}
+    }
+
+    pos = ftell(at->sidf)+1;
+    if ( pos>=65536 && !at->sidlongoffset ) {
+	at->sidlongoffset = true;
+	news = tmpfile();
+	rewind(at->sidh);
+	for ( i=0; i<at->sidcnt; ++i )
+	    putlong(news,getushort(at->sidh));
+	fclose(at->sidh);
+	at->sidh = news;
+    }
+    if ( at->sidlongoffset )
+	putlong(at->sidh,pos);
+    else
+	putshort(at->sidh,pos);
+
+    if ( str!=NULL ) {
+	for ( pt=str; *pt; ++pt )
+	    putc(*pt,at->sidf);
+    }
+return( at->sidcnt++ + nStdStrings );
+}
+
+static void dumpint(FILE *cfff,int num) {
+
+    if ( num>=-107 && num<=107 )
+	putc(num+139,cfff);
+    else if ( num>=108 && num<=1131 ) {
+	num -= 108;
+	putc((num>>8)+247,cfff);
+	putc(num&0xff,cfff);
+    } else if ( num>=-1131 && num<=-108 ) {
+	num = -num;
+	num -= 108;
+	putc((num>>8)+251,cfff);
+	putc(num&0xff,cfff);
+    } else if ( num>=-32768 && num<32768 ) {
+	putc(28,cfff);
+	putc(num>>8,cfff);
+	putc(num&0xff,cfff);
+    } else {		/* In dict data we have 4 byte ints, in type2 strings we don't */
+	putc(29,cfff);
+	putc((num>>24)&0xff,cfff);
+	putc((num>>16)&0xff,cfff);
+	putc((num>>8)&0xff,cfff);
+	putc(num&0xff,cfff);
+    }
+}
+
+static void dumpdbl(FILE *cfff,double d) {
+    if ( d-rint(d)>-.00001 && d-rint(d)<.00001 )
+	dumpint(cfff,(int) d);
+    else {
+	/* The type2 strings have a fixed format, but the dict data does not */
+	char buffer[20], *pt;
+	int sofar,n,odd;
+	sprintf( buffer, "%g", d);
+	sofar = 0; odd=true;
+	putc(30,cfff);		/* Start a double */
+	for ( pt=buffer; *pt; ++pt ) {
+	    if ( isdigit(*pt) )
+		n = *pt-'0';
+	    else if ( *pt=='.' )
+		n = 0xa;
+	    else if ( *pt=='-' )
+		n = 0xe;
+	    else if (( *pt=='E' || *pt=='e') && pt[1]=='-' ) {
+		n = 0xc;
+		++pt;
+	    } else if ( *pt=='E' || *pt=='e')
+		n = 0xb;
+	    else
+		n = 0;		/* Should never happen */
+	    if ( odd ) {
+		sofar = n<<4;
+		odd = false;
+	    } else {
+		putc(sofar|n,cfff);
+		sofar=0;
+		odd = true;
+	    }
+	}
+	if ( sofar==0 )
+	    putc(0xff,cfff);
+	else
+	    putc(sofar|0xf,cfff);
+    }
+}
+
+static void dumpoper(FILE *cfff,int oper ) {
+    if ( oper!=-1 ) {
+	if ( oper>=256 )
+	    putc(oper>>8,cfff);
+	putc(oper&0xff,cfff);
+    }
+}
+
+static void dumpdbloper(FILE *cfff,double d, int oper ) {
+    dumpdbl(cfff,d);
+    dumpoper(cfff,oper);
+}
+
+static void dumpintoper(FILE *cfff,int v, int oper ) {
+    dumpint(cfff,v);
+    dumpoper(cfff,oper);
+}
+
+static void dumpsizedint(FILE *cfff,int big,int num, int oper ) {
+    if ( big ) {
+	putc(29,cfff);
+	putc((num>>24)&0xff,cfff);
+	putc((num>>16)&0xff,cfff);
+	putc((num>>8)&0xff,cfff);
+	putc(num&0xff,cfff);
+    } else {
+	putc(28,cfff);
+	putc(num>>8,cfff);
+	putc(num&0xff,cfff);
+    }
+    dumpoper(cfff,oper);
+}
+
+static void dumpsid(FILE *cfff,struct alltabs *at,char *str,int oper) {
+    if ( str==NULL )
+return;
+    dumpint(cfff,storesid(at,str));
+    dumpoper(cfff,oper);
+}
+
+static void DumpStrDouble(char *pt,FILE *cfff,int oper) {
+    real d;
+    if ( *pt=='[' ) ++pt;		/* For StdHW, StdVW */
+    d = strtod(pt,NULL);
+    dumpdbloper(cfff,d,oper);
+}
+
+static void DumpDblArray(real *arr,int n,FILE *cfff, int oper) {
+    int mi,i;
+
+    for ( mi=n-1; mi>=0 && arr[mi]==0; --mi );
+    if ( mi<0 )
+return;
+    dumpdbl(cfff,arr[0]);
+    for ( i=1; i<=mi; ++i )
+	dumpdbl(cfff,arr[i]-arr[i-1]);
+    dumpoper(cfff,oper);
+}
+
+static void DumpStrArray(char *pt,FILE *cfff,int oper) {
+    real d, last=0;
+    char *end;
+
+    while ( *pt==' ' ) ++pt;
+    if ( *pt=='\0' )
+return;
+    if ( *pt=='[' ) ++pt;
+    while ( *pt==' ' ) ++pt;
+    while ( *pt!=']' && *pt!='\0' ) {
+	d = strtod(pt,&end);
+	if ( pt==end )		/* User screwed up. Should be a number */
+    break;
+	dumpdbl(cfff,d-last);
+	last = d;
+	pt = end;
+	while ( *pt==' ' ) ++pt;
+    }
+    dumpoper(cfff,oper);
+}
+
+static void dumpcffheader(FILE *cfff) {
+    putc('\1',cfff);		/* Major version: 1 */
+    putc('\0',cfff);		/* Minor version: 0 */
+    putc('\4',cfff);		/* Header size in bytes */
+    putc('\4',cfff);		/* Absolute Offset size. */
+	/* I don't think there are any absolute offsets that aren't encoded */
+	/*  in a dict as numbers (ie. inherently variable sized items) */
+}
+
+static void dumpcffnames(SplineFont *sf,FILE *cfff) {
+    char *pt;
+
+    putshort(cfff,1);		/* One font name */
+    putc('\1',cfff);		/* Offset size */
+    putc('\1',cfff);		/* Offset to first name */
+    putc('\1'+strlen(sf->fontname),cfff);
+    for ( pt=sf->fontname; *pt; ++pt )
+	putc(*pt,cfff);
+}
+
+static void dumpcffcharset(SplineFont *sf,struct alltabs *at) {
+    int i;
+
+    at->gn_sid = calloc(at->gi.gcnt,sizeof(uint32));
+    putc(0,at->charset);
+    /* I always use a format 0 charset. ie. an array of SIDs in random order */
+
+    /* First element must be ".notdef" and is omitted */
+
+    for ( i=1; i<at->gi.gcnt; ++i )
+	if ( at->gi.bygid[i]!=-1 && SCWorthOutputting(sf->glyphs[at->gi.bygid[i]])) {
+	    at->gn_sid[i] = storesid(at,sf->glyphs[at->gi.bygid[i]]->name);
+	    putshort(at->charset,at->gn_sid[i]);
+	}
+}
+
+static void dumpcffcidset(struct alltabs *at) {
+    int gid, start;
+
+    putc(2,at->charset);
+
+    start = -1;			/* Glyph 0 always maps to CID 0, and is omitted */
+    for ( gid = 1; gid<at->gi.gcnt; ++gid ) {
+	if ( start==-1 )
+	    start = gid;
+	else if ( at->gi.bygid[gid]-at->gi.bygid[start]!=gid-start ) {
+	    putshort(at->charset,at->gi.bygid[start]);
+	    putshort(at->charset,at->gi.bygid[gid-1]-at->gi.bygid[start]);
+	    start = gid;
+	}
+    }
+    if ( start!=-1 ) {
+	putshort(at->charset,at->gi.bygid[start]);
+	putshort(at->charset,at->gi.bygid[gid-1]-at->gi.bygid[start]);
+    }
+}
+
+static void dumpcfffdselect(SplineFont *sf,struct alltabs *at) {
+    int cid, k, lastfd, cnt;
+    int gid;
+
+    putc(3,at->fdselect);
+    putshort(at->fdselect,0);		/* number of ranges, fill in later */
+
+    for ( k=0; k<sf->subfontcnt; ++k )
+	if ( SCWorthOutputting(sf->subfonts[k]->glyphs[0]))
+    break;
+    if ( k==sf->subfontcnt ) --k;	/* If CID 0 not defined, put it in last font */
+    putshort(at->fdselect,0);
+    putc(k,at->fdselect);
+    lastfd = k;
+    cnt = 1;
+    for ( gid = 1; gid<at->gi.gcnt; ++gid ) {
+	cid = at->gi.bygid[gid];
+	for ( k=0; k<sf->subfontcnt; ++k ) {
+	    if ( cid<sf->subfonts[k]->glyphcnt &&
+		SCWorthOutputting(sf->subfonts[k]->glyphs[cid]) )
+	break;
+	}
+	if ( k==sf->subfontcnt )
+	    /* Doesn't map to a glyph, irrelevant */;
+	else {
+	    if ( k!=lastfd ) {
+		putshort(at->fdselect,gid);
+		putc(k,at->fdselect);
+		lastfd = k;
+		++cnt;
+	    }
+	}
+    }
+    putshort(at->fdselect,gid);
+    fseek(at->fdselect,1,SEEK_SET);
+    putshort(at->fdselect,cnt);
+    fseek(at->fdselect,0,SEEK_END);
+}
+
+static void dumpcffencoding(SplineFont *sf,struct alltabs *at) {
+    int i, cnt, anydups;
+    uint32 start_pos = ftell(at->encoding);
+    SplineChar *sc;
+    EncMap *map = at->map;
+
+    putc(0,at->encoding);
+    /* I always use a format 0 encoding. ie. an array of glyph indexes */
+    putc(0xff,at->encoding);		/* fixup later */
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
+	sf->glyphs[i]->ticked = false;
+
+    cnt = 0;
+    anydups = 0;
+    for ( i=0; i<256 && i<map->enccount; ++i ) if ( map->map[i]!=-1 && (sc=sf->glyphs[map->map[i]])!=NULL ) {
+	if ( sc->ttf_glyph>255 )
+    continue;
+	if ( sc->ticked ) {
+	    ++anydups;
+	} else if ( sc->ttf_glyph>0 ) {
+	    if ( cnt>=255 )
+    break;
+	    putc(i,at->encoding);
+	    ++cnt;
+	    sc->ticked = true;
+	}
+    }
+    if ( anydups ) {
+	fseek(at->encoding,start_pos,SEEK_SET);
+	putc(0x80,at->encoding);
+	putc(cnt,at->encoding);
+	fseek(at->encoding,0,SEEK_END);
+	putc(anydups,at->encoding);
+
+	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
+	    sf->glyphs[i]->ticked = false;
+	for ( i=0; i<256 && i<map->enccount; ++i ) if ( map->map[i]!=-1 && (sc=sf->glyphs[map->map[i]])!=NULL ) {
+	    if ( sc->ttf_glyph>255 )
+    continue;
+	    if ( sc->ticked ) {
+		putc(i,at->encoding);
+		putshort(at->encoding,at->gn_sid[sc->ttf_glyph]);
+	    }
+	    sc->ticked = true;
+	}
+    } else {
+	fseek(at->encoding,start_pos+1,SEEK_SET);
+	putc(cnt,at->encoding);
+	fseek(at->encoding,0,SEEK_END);
+    }
+    free( at->gn_sid );
+    at->gn_sid = NULL;
+}
+
+static void _dumpcffstrings(FILE *file, struct pschars *strs) {
+    int i, len, offsize;
+    
+    /* First figure out the offset size */
+    len = 1;
+    for ( i=0; i<strs->next; ++i )
+	len += strs->lens[i];
+
+    /* Then output the index size and offsets */
+    putshort( file, strs->next );
+    if ( strs->next!=0 ) {
+	/* presumably offsets are unsigned. But the docs don't state this in the obvious place */
+	offsize = len<=255?1:len<=65535?2:len<=0xffffff?3:4;
+	putc(offsize,file);
+	len = 1;
+	for ( i=0; i<strs->next; ++i ) {
+	    dumpoffset(file,offsize,len);
+	    len += strs->lens[i];
+	}
+	dumpoffset(file,offsize,len);
+
+	/* last of all the strings */
+	for ( i=0; i<strs->next; ++i ) {
+	    uint8 *pt = strs->values[i], *end = pt+strs->lens[i];
+	    while ( pt<end ) {
+		putc( *pt++, file );
+	    }
+	}
+    }
+}
+
+static FILE *dumpcffstrings(struct pschars *strs) {
+    FILE *file = tmpfile();
+    _dumpcffstrings(file,strs);
+    PSCharsFree(strs);
+return( file );
+}
+
+int SFFigureDefWidth(SplineFont *sf, int *_nomwid) {
+    uint16 *widths; uint32 *cumwid;
+    int nomwid, defwid, i, j, sameval=(int) 0x80000000, maxw=0, allsame=true;
+    unsigned cnt;
+
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( SCWorthOutputting(sf->glyphs[i]) ) {
+	    if ( maxw<sf->glyphs[i]->width ) maxw = sf->glyphs[i]->width;
+	    if ( sameval == 0x8000000 )
+		sameval = sf->glyphs[i]->width;
+	    else if ( sameval!=sf->glyphs[i]->width )
+		allsame = false;
+	}
+    if ( allsame ) {
+	nomwid = defwid = sameval;
+    } else {
+	++maxw;
+	if ( maxw>65535 ) maxw = 3*(sf->ascent+sf->descent);
+	widths = calloc(maxw,sizeof(uint16));
+	cumwid = calloc(maxw,sizeof(uint32));
+	defwid = 0; cnt=0;
+	for ( i=0; i<sf->glyphcnt; ++i )
+	    if ( SCWorthOutputting(sf->glyphs[i]) &&
+		    sf->glyphs[i]->width>=0 &&
+		    sf->glyphs[i]->width<maxw )
+		if ( ++widths[sf->glyphs[i]->width] > cnt ) {
+		    defwid = sf->glyphs[i]->width;
+		    cnt = widths[defwid];
+		}
+	widths[defwid] = 0;
+	for ( i=0; i<maxw; ++i )
+		for ( j=-107; j<=107; ++j )
+		    if ( i+j>=0 && i+j<maxw )
+			cumwid[i] += widths[i+j];
+	cnt = 0; nomwid = 0;
+	for ( i=0; i<maxw; ++i )
+	    if ( cnt<cumwid[i] ) {
+		cnt = cumwid[i];
+		nomwid = i;
+	    }
+	free(widths); free(cumwid);
+    }
+    if ( _nomwid!=NULL )
+	*_nomwid = nomwid;
+return( defwid );
+}
+
+static void ATFigureDefWidth(SplineFont *sf, struct alltabs *at, int subfont) {
+    int nomwid, defwid;
+
+    defwid = SFFigureDefWidth(sf,&nomwid);
+    if ( subfont==-1 )
+	at->defwid = defwid;
+    else
+	at->fds[subfont].defwid = defwid;
+    if ( subfont==-1 )
+	at->nomwid = nomwid;
+    else
+	at->fds[subfont].nomwid = nomwid;
+}
+
+static void dumpcffprivate(SplineFont *sf,struct alltabs *at,int subfont,
+	int subrcnt) {
+    char *pt;
+    FILE *private = subfont==-1?at->private:at->fds[subfont].private;
+    int mi,i;
+    real bluevalues[14], otherblues[10];
+    real snapcnt[12];
+    real stemsnaph[12], stemsnapv[12];
+    real stdhw[1], stdvw[1];
+    int hasblue=0, hash=0, hasv=0, bs;
+    int nomwid, defwid;
+    EncMap *map = at->map;
+    double bluescale;
+
+    /* The private dict is not in an index, so no index header. Just the data */
+
+    if ( subfont==-1 )
+	defwid = at->defwid;
+    else
+	defwid = at->fds[subfont].defwid;
+    dumpintoper(private,defwid,20);		/* Default Width */
+    if ( subfont==-1 )
+	nomwid = at->nomwid;
+    else
+	nomwid = at->fds[subfont].nomwid;
+    dumpintoper(private,nomwid,21);		/* Nominative Width */
+
+    bs = SplineFontIsFlexible(sf,at->gi.layer,at->gi.flags);
+    hasblue = PSDictHasEntry(sf->private,"BlueValues")!=NULL;
+    hash = PSDictHasEntry(sf->private,"StdHW")!=NULL;
+    hasv = PSDictHasEntry(sf->private,"StdVW")!=NULL;
+    ff_progress_change_stages(2+autohint_before_generate+!hasblue);
+    if ( autohint_before_generate ) {
+	ff_progress_change_line1(_("Auto Hinting Font..."));
+	SplineFontAutoHint(sf,at->gi.layer);
+	ff_progress_next_stage();
+    }
+
+    otherblues[0] = otherblues[1] = bluevalues[0] = bluevalues[1] = 0;
+    if ( !hasblue ) {
+	FindBlues(sf,at->gi.layer,bluevalues,otherblues);
+	ff_progress_next_stage();
+    }
+
+    stdhw[0] = stdvw[0] = 0;
+    if ( !hash ) {
+	FindHStems(sf,stemsnaph,snapcnt);
+	mi = -1;
+	for ( i=0; i<12 && stemsnaph[i]!=0; ++i )
+	    if ( mi==-1 ) mi = i;
+	    else if ( snapcnt[i]>snapcnt[mi] ) mi = i;
+	if ( mi!=-1 ) stdhw[0] = stemsnaph[mi];
+    }
+
+    if ( !hasv ) {
+	FindVStems(sf,stemsnapv,snapcnt);
+	mi = -1;
+	for ( i=0; i<12 && stemsnapv[i]!=0; ++i )
+	    if ( mi==-1 ) mi = i;
+	    else if ( snapcnt[i]>snapcnt[mi] ) mi = i;
+	if ( mi!=-1 ) stdvw[0] = stemsnapv[mi];
+    }
+    ff_progress_change_line1(_("Saving OpenType Font"));
+
+    if ( hasblue )
+	DumpStrArray(PSDictHasEntry(sf->private,"BlueValues"),private,6);
+    else
+	DumpDblArray(bluevalues,sizeof(bluevalues)/sizeof(bluevalues[0]),private,6);
+    if ( (pt=PSDictHasEntry(sf->private,"OtherBlues"))!=NULL )
+	DumpStrArray(pt,private,7);
+    else if ( !hasblue )
+	DumpDblArray(otherblues,sizeof(otherblues)/sizeof(otherblues[0]),private,7);
+    if ( (pt=PSDictHasEntry(sf->private,"FamilyBlues"))!=NULL )
+	DumpStrArray(pt,private,8);
+    bluescale = BlueScaleFigure(sf->private,bluevalues,otherblues);
+    if ( (pt=PSDictHasEntry(sf->private,"FamilyOtherBlues"))!=NULL )
+	DumpStrArray(pt,private,9);
+    if ( (pt=PSDictHasEntry(sf->private,"BlueScale"))!=NULL )
+	DumpStrDouble(pt,private,(12<<8)+9);
+    else if ( bluescale!=-1 )
+	dumpdbloper(private,bluescale,(12<<8)+9);
+    if ( (pt=PSDictHasEntry(sf->private,"BlueShift"))!=NULL )
+	DumpStrDouble(pt,private,(12<<8)+10);
+    else
+	dumpintoper(private,bs,(12<<8)+10);
+    if ( (pt=PSDictHasEntry(sf->private,"BlueFuzz"))!=NULL )
+	DumpStrDouble(pt,private,(12<<8)+11);
+    if ( hash ) {
+	DumpStrDouble(PSDictHasEntry(sf->private,"StdHW"),private,10);
+	if ( (pt=PSDictHasEntry(sf->private,"StemSnapH"))!=NULL )
+	    DumpStrArray(pt,private,(12<<8)|12);
+    } else {
+	if ( stdhw[0]!=0 )
+	    dumpdbloper(private,stdhw[0],10);
+	DumpDblArray(stemsnaph,sizeof(stemsnaph)/sizeof(stemsnaph[0]),private,(12<<8)|12);
+    }
+    if ( hasv ) {
+	DumpStrDouble(PSDictHasEntry(sf->private,"StdVW"),private,11);
+	if ( (pt=PSDictHasEntry(sf->private,"StemSnapV"))!=NULL )
+	    DumpStrArray(pt,private,(12<<8)|13);
+    } else {
+	if ( stdvw[0]!=0 )
+	    dumpdbloper(private,stdvw[0],11);
+	DumpDblArray(stemsnapv,sizeof(stemsnapv)/sizeof(stemsnapv[0]),private,(12<<8)|13);
+    }
+    if ( (pt=PSDictHasEntry(sf->private,"ForceBold"))!=NULL ) {
+	dumpintoper(private,*pt=='t'||*pt=='T',(12<<8)|14);
+    } else if ( sf->weight!=NULL &&
+	    (strstrmatch(sf->weight,"Bold")!=NULL ||
+	     strstrmatch(sf->weight,"Demi")!=NULL ||
+	     strstrmatch(sf->weight,"Fett")!=NULL ||
+	     strstrmatch(sf->weight,"Gras")!=NULL ||
+	     strstrmatch(sf->weight,"Heavy")!=NULL ||
+	     strstrmatch(sf->weight,"Black")!=NULL))
+	dumpintoper(private,1,(12<<8)|14);
+    if ( (pt=PSDictHasEntry(sf->private,"LanguageGroup"))!=NULL )
+	DumpStrDouble(pt,private,(12<<8)+17);
+    else if ( map==NULL )
+	/* Do Nothing */;
+    else if ( map->enc->is_japanese ||
+	      map->enc->is_korean ||
+	      map->enc->is_tradchinese ||
+	      map->enc->is_simplechinese )
+	dumpintoper(private,1,(12<<8)|17);
+    if ( (pt=PSDictHasEntry(sf->private,"ExpansionFactor"))!=NULL )
+	DumpStrDouble(pt,private,(12<<8)+18);
+    if ( subrcnt!=0 )
+	dumpsizedint(private,false,ftell(private)+3+1,19);	/* Subrs */
+
+    if ( subfont==-1 )
+	at->privatelen = ftell(private);
+    else
+	at->fds[subfont].privatelen = ftell(private);
+}
+
+/* When we exit this the topdict is not complete, we still need to fill in */
+/*  values for charset,encoding,charstrings and private. Then we need to go */
+/*  back and fill in the table length (at lenpos) */
+static void dumpcfftopdict(SplineFont *sf,struct alltabs *at) {
+    char *pt, *end;
+    FILE *cfff = at->cfff;
+    DBounds b;
+
+    putshort(cfff,1);		/* One top dict */
+    putc('\2',cfff);		/* Offset size */
+    putshort(cfff,1);		/* Offset to topdict */
+    at->lenpos = ftell(cfff);
+    putshort(cfff,0);		/* placeholder for final position (final offset in index points beyond last element) */
+    dumpsid(cfff,at,sf->version,0);
+    dumpsid(cfff,at,sf->copyright,1);
+    dumpsid(cfff,at,sf->fullname?sf->fullname:sf->fontname,2);
+    dumpsid(cfff,at,sf->familyname,3);
+    dumpsid(cfff,at,sf->weight,4);
+    if ( at->gi.fixed_width>0 ) dumpintoper(cfff,1,(12<<8)|1);
+    if ( sf->italicangle!=0 ) dumpdbloper(cfff,sf->italicangle,(12<<8)|2);
+    if ( sf->upos!=-100 ) dumpdbloper(cfff,sf->upos,(12<<8)|3);
+    if ( sf->uwidth!=50 ) dumpdbloper(cfff,sf->uwidth,(12<<8)|4);
+    if ( sf->strokedfont ) {
+	dumpintoper(cfff,2,(12<<8)|5);
+	dumpdbloper(cfff,sf->strokewidth,(12<<8)|8);
+    }
+    /* We'll never set CharstringType */
+    if ( sf->ascent+sf->descent!=1000 ) {
+	dumpdbl(cfff,1.0/(sf->ascent+sf->descent));
+	dumpint(cfff,0);
+	dumpint(cfff,0);
+	dumpdbl(cfff,1.0/(sf->ascent+sf->descent));
+	dumpint(cfff,0);
+	dumpintoper(cfff,0,(12<<8)|7);
+    }
+    if ( sf->uniqueid!=-1 && sf->use_uniqueid )
+	dumpintoper(cfff, sf->uniqueid?sf->uniqueid:4000000 + (rand()&0x3ffff), 13 );
+    SplineFontLayerFindBounds(sf,at->gi.layer,&b);
+    at->gi.xmin = b.minx;
+    at->gi.ymin = b.miny;
+    at->gi.xmax = b.maxx;
+    at->gi.ymax = b.maxy;
+    dumpdbl(cfff,floor(b.minx));
+    dumpdbl(cfff,floor(b.miny));
+    dumpdbl(cfff,ceil(b.maxx));
+    dumpdbloper(cfff,ceil(b.maxy),5);
+    /* We'll never set StrokeWidth */
+    if ( sf->xuid!=NULL && sf->use_xuid ) {
+	pt = sf->xuid; if ( *pt=='[' ) ++pt;
+	while ( *pt && *pt!=']' ) {
+	    dumpint(cfff,strtol(pt,&end,10));
+	    if ( pt==end )	/* garbage in XUID */
+	break;
+	    for ( pt = end; *pt==' '; ++pt );
+	}
+	putc(14,cfff);
+	if ( sf->changed_since_xuidchanged )
+	    SFIncrementXUID(sf);
+    }
+    /* Offset to charset (oper=15) needed here */
+    /* Offset to encoding (oper=16) needed here (not for CID )*/
+    /* Offset to charstrings (oper=17) needed here */
+    /* Length of, and Offset to private (oper=18) needed here (not for CID )*/
+}
+
+static int dumpcffdict(SplineFont *sf,struct alltabs *at) {
+    FILE *fdarray = at->fdarray;
+    int pstart;
+    /* according to the PSRef Man v3, only fontname, fontmatrix and private */
+    /*  appear in this dictionary */
+
+    dumpsid(fdarray,at,sf->fontname,(12<<8)|38);
+    if ( sf->ascent+sf->descent!=1000 ) {
+	dumpdbl(fdarray,1.0/(sf->ascent+sf->descent));
+	dumpint(fdarray,0);
+	dumpint(fdarray,0);
+	dumpdbl(fdarray,1.0/(sf->ascent+sf->descent));
+	dumpint(fdarray,0);
+	dumpintoper(fdarray,0,(12<<8)|7);
+    }
+    pstart = ftell(fdarray);
+    dumpsizedint(fdarray,false,0,-1);	/* private length */
+    dumpsizedint(fdarray,true,0,18);	/* private offset */
+return( pstart );
+}
+
+static void dumpcffdictindex(SplineFont *sf,struct alltabs *at) {
+    int i;
+    int pos;
+
+    putshort(at->fdarray,sf->subfontcnt);
+    putc('\2',at->fdarray);		/* DICTs aren't very big, and there are at most 255 */
+    putshort(at->fdarray,1);		/* Offset to first dict */
+    for ( i=0; i<sf->subfontcnt; ++i )
+	putshort(at->fdarray,0);	/* Dump offset placeholders (note there's one extra to mark the end) */
+    pos = ftell(at->fdarray)-1;
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	at->fds[i].fillindictmark = dumpcffdict(sf->subfonts[i],at);
+	at->fds[i].eodictmark = ftell(at->fdarray);
+	if ( at->fds[i].eodictmark>65536 )
+	    IError("The DICT INDEX got too big, result won't work");
+    }
+    fseek(at->fdarray,2*sizeof(short)+sizeof(char),SEEK_SET);
+    for ( i=0; i<sf->subfontcnt; ++i )
+	putshort(at->fdarray,at->fds[i].eodictmark-pos);
+    fseek(at->fdarray,0,SEEK_END);
+}
+
+static void dumpcffcidtopdict(SplineFont *sf,struct alltabs *at) {
+    char *pt, *end;
+    FILE *cfff = at->cfff;
+    DBounds b;
+    int cidcnt=0, k;
+
+    for ( k=0; k<sf->subfontcnt; ++k )
+	if ( sf->subfonts[k]->glyphcnt>cidcnt ) cidcnt = sf->subfonts[k]->glyphcnt;
+
+    putshort(cfff,1);		/* One top dict */
+    putc('\2',cfff);		/* Offset size */
+    putshort(cfff,1);		/* Offset to topdict */
+    at->lenpos = ftell(cfff);
+    putshort(cfff,0);		/* placeholder for final position */
+    dumpsid(cfff,at,sf->cidregistry,-1);
+    dumpsid(cfff,at,sf->ordering,-1);
+    dumpintoper(cfff,sf->supplement,(12<<8)|30);		/* ROS operator must be first */
+    dumpdbloper(cfff,sf->cidversion,(12<<8)|31);
+    dumpintoper(cfff,cidcnt,(12<<8)|34);
+    if ( sf->use_uniqueid )
+	dumpintoper(cfff, sf->uniqueid?sf->uniqueid:4000000 + (rand()&0x3ffff), (12<<8)|35 );
+
+    dumpsid(cfff,at,sf->copyright,1);
+    dumpsid(cfff,at,sf->fullname?sf->fullname:sf->fontname,2);
+    dumpsid(cfff,at,sf->familyname,3);
+    dumpsid(cfff,at,sf->weight,4);
+    /* FontMatrix  (identity here, real ones in sub fonts)*/
+    /* Actually there is no fontmatrix in the adobe cid font I'm looking at */
+    /*  which means it should default to [.001...] but it doesn't so the */
+    /*  docs aren't completely accurate */
+    /* I now see I've no idea what the FontMatrix means in a CID keyed font */
+    /*  it seems to be ignored everywhere, so we omit it */
+
+    CIDLayerFindBounds(sf,at->gi.layer,&b);
+    at->gi.xmin = b.minx;
+    at->gi.ymin = b.miny;
+    at->gi.xmax = b.maxx;
+    at->gi.ymax = b.maxy;
+    dumpdbl(cfff,floor(b.minx));
+    dumpdbl(cfff,floor(b.miny));
+    dumpdbl(cfff,ceil(b.maxx));
+    dumpdbloper(cfff,ceil(b.maxy),5);
+    /* We'll never set StrokeWidth */
+    if ( sf->xuid!=NULL && sf->use_xuid ) {
+	pt = sf->xuid; if ( *pt=='[' ) ++pt;
+	while ( *pt && *pt!=']' ) {
+	    dumpint(cfff,strtol(pt,&end,10));
+	    for ( pt = end; *pt==' '; ++pt );
+	}
+	putc(14,cfff);
+	if ( sf->changed_since_xuidchanged )
+	    SFIncrementXUID(sf);
+    }
+    /* Acrobat doesn't seem to care about a private dict here. Ghostscript */
+    /*  dies.  Tech Note: 5176.CFF.PDF, top of page 23 says:		   */
+    /*		A Private DICT is required, but may be specified as having */
+    /*		a length of 0 if there are no non-default values to be stored*/
+    /* No indication >where< it is required. I assumed everywhere. Perhaps */
+    /*  just in basefonts?                                                 */
+    /* Omit it.		                                                   */
+
+    /* Offset to charset (oper=15) needed here */
+    /* Offset to charstrings (oper=17) needed here */
+    /* Offset to FDArray (oper=12,36) needed here */
+    /* Offset to FDSelect (oper=12,37) needed here */
+}
+
+static int isStdEncoding(SplineFont *sf,EncMap *map) {
+    int i;
+
+    for ( i=0; i<256 && i<map->enccount; ++i ) if ( map->map[i]!=-1 && sf->glyphs[map->map[i]]!=NULL )
+	if ( sf->glyphs[map->map[i]]->unicodeenc!=-1 )
+	    if ( sf->glyphs[map->map[i]]->unicodeenc!=unicode_from_adobestd[i] )
+return( 0 );
+
+return( 1 );
+}
+
+static void finishup(SplineFont *sf,struct alltabs *at) {
+    int strlen, shlen, glen,enclen,csetlen,cstrlen,prvlen;
+    int base, eotop, strhead;
+    int output_enc = ( at->format==ff_cff && !isStdEncoding(sf,at->map));
+
+    storesid(at,NULL);		/* end the strings index */
+    strlen = ftell(at->sidf) + (shlen = ftell(at->sidh));
+    glen = sizeof(short);	/* Single entry: 0, no globals */
+    enclen = ftell(at->encoding);
+    csetlen = ftell(at->charset);
+    cstrlen = ftell(at->charstrings);
+    prvlen = ftell(at->private);
+    base = ftell(at->cfff);
+    if ( base+6*3+strlen+glen+enclen+csetlen+cstrlen+prvlen > 32767 ) {
+	at->cfflongoffset = true;
+	base += 5*5+4;
+    } else
+	base += 5*3+4;
+    strhead = 2+(at->sidcnt>1);
+    base += strhead;
+
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen,15);		/* Charset */
+    if ( output_enc )					/* encoding offset */
+	dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen,16);	/* encoding offset */
+    else {
+	dumpsizedint(at->cfff,at->cfflongoffset,0,16);
+	enclen = 0;
+    }
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen+enclen,17);/* charstrings */
+    dumpsizedint(at->cfff,at->cfflongoffset,at->privatelen,-1);
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen+enclen+cstrlen,18); /* private size */
+    eotop = base-strhead-at->lenpos-1;
+    if ( at->cfflongoffset ) {
+	fseek(at->cfff,3,SEEK_SET);
+	putc(4,at->cfff);
+    }
+    fseek(at->cfff,at->lenpos,SEEK_SET);
+    putshort(at->cfff,eotop);
+    fseek(at->cfff,0,SEEK_END);
+
+    /* String Index */
+    putshort(at->cfff,at->sidcnt-1);
+    if ( at->sidcnt!=1 ) {		/* Everybody gets an added NULL */
+	putc(at->sidlongoffset?4:2,at->cfff);
+	if ( !ttfcopyfile(at->cfff,at->sidh,base,"CFF-StringBase")) at->error = true;
+	if ( !ttfcopyfile(at->cfff,at->sidf,base+shlen,"CFF-StringData")) at->error = true;
+    }
+
+    /* Global Subrs */
+    putshort(at->cfff,0);
+
+    /* Charset */
+    if ( !ttfcopyfile(at->cfff,at->charset,base+strlen+glen,"CFF-Charset")) at->error = true;
+
+    /* Encoding */
+    if ( !ttfcopyfile(at->cfff,at->encoding,base+strlen+glen+csetlen,"CFF-Encoding")) at->error = true;
+
+    /* Char Strings */
+    if ( !ttfcopyfile(at->cfff,at->charstrings,base+strlen+glen+csetlen+enclen,"CFF-CharStrings")) at->error = true;
+
+    /* Private & Subrs */
+    if ( !ttfcopyfile(at->cfff,at->private,base+strlen+glen+csetlen+enclen+cstrlen,"CFF-Private")) at->error = true;
+}
+
+static void finishupcid(SplineFont *sf,struct alltabs *at) {
+    int strlen, shlen, glen,csetlen,cstrlen,fdsellen,fdarrlen,prvlen;
+    int base, eotop, strhead;
+    int i;
+
+    storesid(at,NULL);		/* end the strings index */
+    strlen = ftell(at->sidf) + (shlen = ftell(at->sidh));
+    glen = ftell(at->globalsubrs);
+    /* No encodings */
+    csetlen = ftell(at->charset);
+    fdsellen = ftell(at->fdselect);
+    cstrlen = ftell(at->charstrings);
+    fdarrlen = ftell(at->fdarray);
+    base = ftell(at->cfff);
+
+    at->cfflongoffset = true;
+    base += 5*4+4+2;		/* two of the opers below are two byte opers */
+    strhead = 2+(at->sidcnt>1);
+    base += strhead;
+
+    prvlen = 0;
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	fseek(at->fdarray,at->fds[i].fillindictmark,SEEK_SET);
+	dumpsizedint(at->fdarray,false,at->fds[i].privatelen,-1);	/* Private len */
+	dumpsizedint(at->fdarray,true,base+strlen+glen+csetlen+fdsellen+cstrlen+fdarrlen+prvlen,18);	/* Private offset */
+	prvlen += ftell(at->fds[i].private);	/* private & subrs */
+    }
+
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen,15);	/* charset */
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen,(12<<8)|37);	/* fdselect */
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen+fdsellen,17);	/* charstrings */
+    dumpsizedint(at->cfff,at->cfflongoffset,base+strlen+glen+csetlen+fdsellen+cstrlen,(12<<8)|36);	/* fdarray */
+    eotop = base-strhead-at->lenpos-1;
+    fseek(at->cfff,at->lenpos,SEEK_SET);
+    putshort(at->cfff,eotop);
+    fseek(at->cfff,0,SEEK_END);
+
+    /* String Index */
+    putshort(at->cfff,at->sidcnt-1);
+    if ( at->sidcnt!=1 ) {		/* Everybody gets an added NULL */
+	putc(at->sidlongoffset?4:2,at->cfff);
+	if ( !ttfcopyfile(at->cfff,at->sidh,base,"CFF-StringBase")) at->error = true;
+	if ( !ttfcopyfile(at->cfff,at->sidf,base+shlen,"CFF-StringData")) at->error = true;
+    }
+
+    /* Global Subrs */
+    if ( !ttfcopyfile(at->cfff,at->globalsubrs,base+strlen,"CFF-GlobalSubrs")) at->error = true;
+
+    /* Charset */
+    if ( !ttfcopyfile(at->cfff,at->charset,base+strlen+glen,"CFF-Charset")) at->error = true;
+
+    /* FDSelect */
+    if ( !ttfcopyfile(at->cfff,at->fdselect,base+strlen+glen+csetlen,"CFF-FDSelect")) at->error = true;
+
+    /* Char Strings */
+    if ( !ttfcopyfile(at->cfff,at->charstrings,base+strlen+glen+csetlen+fdsellen,"CFF-CharStrings")) at->error = true;
+
+    /* FDArray (DICT Index) */
+    if ( !ttfcopyfile(at->cfff,at->fdarray,base+strlen+glen+csetlen+fdsellen+cstrlen,"CFF-FDArray")) at->error = true;
+
+    /* Private & Subrs */
+    prvlen = 0;
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	int temp = ftell(at->fds[i].private);
+	if ( !ttfcopyfile(at->cfff,at->fds[i].private,
+		base+strlen+glen+csetlen+fdsellen+cstrlen+fdarrlen+prvlen,"CFF-PrivateSubrs")) at->error = true;
+	prvlen += temp;
+    }
+
+    free(at->fds);
+}
+
+static int dumpcffhmtx(struct alltabs *at,SplineFont *sf,int bitmaps) {
+    DBounds b;
+    SplineChar *sc;
+    int i,cnt;
+    int dovmetrics = sf->hasvmetrics;
+    int width = at->gi.fixed_width;
+
+    at->gi.hmtx = tmpfile();
+    if ( dovmetrics )
+	at->gi.vmtx = tmpfile();
+    FigureFullMetricsEnd(sf,&at->gi,bitmaps);	/* Bitmap fonts use ttf convention of 3 magic glyphs */
+    if ( at->gi.bygid[0]!=-1 && (sf->glyphs[at->gi.bygid[0]]->width==width || width<=0 )) {
+	putshort(at->gi.hmtx,sf->glyphs[at->gi.bygid[0]]->width);
+	SplineCharLayerFindBounds(sf->glyphs[at->gi.bygid[0]],at->gi.layer,&b);
+	putshort(at->gi.hmtx,b.minx);
+	if ( dovmetrics ) {
+	    putshort(at->gi.vmtx,sf->glyphs[at->gi.bygid[0]]->vwidth);
+	    putshort(at->gi.vmtx,/*sf->vertical_origin-*/b.miny);
+	}
+    } else {
+	putshort(at->gi.hmtx,width<=0?(sf->ascent+sf->descent)/2:width);
+	putshort(at->gi.hmtx,0);
+	if ( dovmetrics ) {
+	    putshort(at->gi.vmtx,sf->ascent+sf->descent);
+	    putshort(at->gi.vmtx,0);
+	}
+    }
+    cnt = 1;
+    if ( bitmaps ) {
+	if ( width<=0 ) width = (sf->ascent+sf->descent)/3;
+	putshort(at->gi.hmtx,width);
+	putshort(at->gi.hmtx,0);
+	if ( dovmetrics ) {
+	    putshort(at->gi.vmtx,sf->ascent+sf->descent);
+	    putshort(at->gi.vmtx,0);
+	}
+	putshort(at->gi.hmtx,width);
+	putshort(at->gi.hmtx,0);
+	if ( dovmetrics ) {
+	    putshort(at->gi.vmtx,sf->ascent+sf->descent);
+	    putshort(at->gi.vmtx,0);
+	}
+	cnt = 3;
+    }
+
+    for ( i=cnt; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
+	sc = sf->glyphs[at->gi.bygid[i]];
+	if ( SCWorthOutputting(sc) ) {
+	    if ( i<=at->gi.lasthwidth )
+		putshort(at->gi.hmtx,sc->width);
+	    SplineCharLayerFindBounds(sc,at->gi.layer,&b);
+	    putshort(at->gi.hmtx,b.minx);
+	    if ( dovmetrics ) {
+		if ( i<=at->gi.lastvwidth )
+		    putshort(at->gi.vmtx,sc->vwidth);
+		putshort(at->gi.vmtx,/*sf->vertical_origin-*/b.maxy);
+	    }
+	    ++cnt;
+	    if ( i==at->gi.lasthwidth )
+		at->gi.hfullcnt = cnt;
+	    if ( i==at->gi.lastvwidth )
+		at->gi.vfullcnt = cnt;
+	}
+    }
+    at->gi.hmtxlen = ftell(at->gi.hmtx);
+    if ( at->gi.hmtxlen&2 ) putshort(at->gi.hmtx,0);
+    if ( dovmetrics ) {
+	at->gi.vmtxlen = ftell(at->gi.vmtx);
+	if ( at->gi.vmtxlen&2 ) putshort(at->gi.vmtx,0);
+    }
+
+    at->gi.maxp->numGlyphs = cnt;
+return( true );
+}
+
+static void dumpcffcidhmtx(struct alltabs *at,SplineFont *_sf) {
+    DBounds b;
+    SplineChar *sc;
+    int cid,i,cnt=0,max;
+    SplineFont *sf;
+    int dovmetrics = _sf->hasvmetrics;
+
+    at->gi.hmtx = tmpfile();
+    if ( dovmetrics )
+	at->gi.vmtx = tmpfile();
+    FigureFullMetricsEnd(_sf,&at->gi,false);
+
+    max = 0;
+    for ( i=0; i<_sf->subfontcnt; ++i )
+	if ( max<_sf->subfonts[i]->glyphcnt )
+	    max = _sf->subfonts[i]->glyphcnt;
+    for ( cid = 0; cid<max; ++cid ) {
+	for ( i=0; i<_sf->subfontcnt; ++i ) {
+	    sf = _sf->subfonts[i];
+	    if ( cid<sf->glyphcnt && SCWorthOutputting(sf->glyphs[cid]))
+	break;
+	}
+	if ( i!=_sf->subfontcnt ) {
+	    sc = sf->glyphs[cid];
+	    if ( sc->ttf_glyph<=at->gi.lasthwidth )
+		putshort(at->gi.hmtx,sc->width);
+	    SplineCharLayerFindBounds(sc,at->gi.layer,&b);
+	    putshort(at->gi.hmtx,b.minx);
+	    if ( dovmetrics ) {
+		if ( sc->ttf_glyph<=at->gi.lastvwidth )
+		    putshort(at->gi.vmtx,sc->vwidth);
+		putshort(at->gi.vmtx,/*sf->vertical_origin-*/b.maxy);
+	    }
+	    ++cnt;
+	    if ( sc->ttf_glyph==at->gi.lasthwidth )
+		at->gi.hfullcnt = cnt;
+	    if ( sc->ttf_glyph==at->gi.lastvwidth )
+		at->gi.vfullcnt = cnt;
+	} else if ( cid==0 ) {
+	    /* Create a dummy entry for .notdef */
+	    sf = _sf->subfonts[0];
+	    putshort(at->gi.hmtx,sf->ascent+sf->descent);
+	    putshort(at->gi.hmtx,0);
+	    ++cnt;
+	    if ( dovmetrics ) {
+		putshort(at->gi.vmtx,sf->ascent+sf->descent);
+		putshort(at->gi.vmtx,0);
+	    }
+	}
+    }
+    at->gi.hmtxlen = ftell(at->gi.hmtx);
+    if ( at->gi.hmtxlen&2 ) putshort(at->gi.hmtx,0);
+    if ( dovmetrics ) {
+	at->gi.vmtxlen = ftell(at->gi.vmtx);
+	if ( at->gi.vmtxlen&2 ) putshort(at->gi.vmtx,0);
+    }
+
+    at->gi.maxp->numGlyphs = cnt;
+}
+
+static int dumptype2glyphs(SplineFont *sf,struct alltabs *at) {
+    int i;
+    struct pschars *subrs, *chrs;
+
+    at->cfff = tmpfile();
+    at->sidf = tmpfile();
+    at->sidh = tmpfile();
+    at->charset = tmpfile();
+    at->encoding = tmpfile();
+    at->private = tmpfile();
+
+    dumpcffheader(at->cfff);
+    dumpcffnames(sf,at->cfff);
+    dumpcffcharset(sf,at);
+    ff_progress_change_stages(2+at->gi.strikecnt);
+
+    ATFigureDefWidth(sf,at,-1);
+    if ((chrs =SplineFont2ChrsSubrs2(sf,at->nomwid,at->defwid,at->gi.bygid,at->gi.gcnt,at->gi.flags,&subrs,at->gi.layer))==NULL )
+return( false );
+    dumpcffprivate(sf,at,-1,subrs->next);
+    if ( subrs->next!=0 )
+	_dumpcffstrings(at->private,subrs);
+    ff_progress_next_stage();
+    at->charstrings = dumpcffstrings(chrs);
+    PSCharsFree(subrs);
+    if ( at->charstrings == NULL )
+return( false );
+    if ( at->format==ff_cff && !isStdEncoding(sf,at->map))
+	dumpcffencoding(sf,at);		/* Do this after we've assigned glyph ids */
+    dumpcfftopdict(sf,at);
+    finishup(sf,at);
+
+    at->cfflen = ftell(at->cfff);
+    if ( at->cfflen&3 ) {
+	for ( i=4-(at->cfflen&3); i>0; --i )
+	    putc('\0',at->cfff);
+    }
+
+    if ( at->format!=ff_cff )
+	dumpcffhmtx(at,sf,false);
+    free(at->gn_sid); at->gn_sid=NULL;
+return( true );
+}
+
+static int dumpcidglyphs(SplineFont *sf,struct alltabs *at) {
+    int i;
+    struct pschars *glbls = NULL, *chrs;
+
+    at->cfff = tmpfile();
+    at->sidf = tmpfile();
+    at->sidh = tmpfile();
+    at->charset = tmpfile();
+    at->fdselect = tmpfile();
+    at->fdarray = tmpfile();
+    at->globalsubrs = tmpfile();
+
+    at->fds = calloc(sf->subfontcnt,sizeof(struct fd2data));
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	at->fds[i].private = tmpfile();
+	ATFigureDefWidth(sf->subfonts[i],at,i);
+    }
+    if ( (chrs = CID2ChrsSubrs2(sf,at->fds,at->gi.flags,&glbls,at->gi.layer))==NULL )
+return( false );
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	dumpcffprivate(sf->subfonts[i],at,i,at->fds[i].subrs->next);
+	if ( at->fds[i].subrs->next!=0 )
+	    _dumpcffstrings(at->fds[i].private,at->fds[i].subrs);
+	PSCharsFree(at->fds[i].subrs);
+    }
+    _dumpcffstrings(at->globalsubrs,glbls);
+    PSCharsFree(glbls);
+
+    dumpcffheader(at->cfff);
+    dumpcffnames(sf,at->cfff);
+    dumpcffcidset(at);
+    dumpcfffdselect(sf,at);
+    dumpcffdictindex(sf,at);
+    if ( (at->charstrings = dumpcffstrings(chrs))==NULL )
+return( false );
+    dumpcffcidtopdict(sf,at);
+    finishupcid(sf,at);
+
+    at->cfflen = ftell(at->cfff);
+    if ( at->cfflen&3 ) {
+	for ( i=4-(at->cfflen&3); i>0; --i )
+	    putc('\0',at->cfff);
+    }
+
+    if ( at->format!=ff_cffcid )
+	dumpcffcidhmtx(at,sf);
+return( true );
+}
+
+static int AnyInstructions(SplineFont *sf) {
+    int i;
+
+    if ( sf->subfontcnt!=0 ) {
+	for ( i=0; i<sf->subfontcnt; ++i )
+	    if ( AnyInstructions(sf->subfonts[i]))
+return( true );
+    } else {
+	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	    if ( sf->glyphs[i]->ttf_instrs_len!=0 )
+return( true );
+	}
+    }
+return( false );
+}
+
+static int AnyMisleadingBitmapAdvances(SplineFont *sf, int32 *bsizes) {
+    int strike, gid;
+    double em = sf->ascent+sf->descent;
+    BDFFont *bdf;
+    /* Are there any bitmap glyphs whose advance width differs from the */
+    /*  expected value from scaling the outline's advance width? */
+
+    if ( bsizes==NULL )
+return( false );
+    for ( strike = 0; bsizes[strike]!=0; ++strike ) {
+	for ( bdf=sf->bitmaps; bdf!=NULL && (bdf->pixelsize!=(bsizes[strike]&0xffff) || BDFDepth(bdf)!=(bsizes[strike]>>16)); bdf=bdf->next );
+	if ( bdf==NULL )
+    continue;
+	for ( gid=0; gid<sf->glyphcnt && gid<bdf->glyphcnt; ++gid ) {
+	    SplineChar *sc = sf->glyphs[gid];
+	    BDFChar *bc = bdf->glyphs[gid];
+	    if ( sc==NULL || bc==NULL )
+	continue;
+	    if ( (int) rint( (sc->width*bdf->pixelsize)/em ) != bc->width )
+return( true );
+	}
+    }
+return( false );
+}
+
+void cvt_unix_to_1904( long long time, int32 result[2]) {
+    uint32 date1970[4], tm[4];
+    uint32 year[2];
+    int i;
+
+    tm[0] =  time     &0xffff;
+    tm[1] = (time>>16)&0xffff;
+    tm[2] = (time>>32)&0xffff;
+    tm[3] = (time>>48)&0xffff;
+    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 ) {
+	tm[i] += date1970[i];
+	tm[i+1] += tm[i]>>16;
+	tm[i] &= 0xffff;
+    }
+    tm[3] -= date1970[3];
+
+    result[0] = (tm[1]<<16) | tm[0];
+    result[1] = (tm[3]<<16) | tm[2];
+}
+
+static void sethead(struct head *head,SplineFont *sf,struct alltabs *at,
+	enum fontformat format, int32 *bsizes) {
+    int i, lr, rl, indic_rearrange, arabic;
+    ASM *sm;
+    struct ttflangname *useng;
+    float vn;
+
+    if ( at->gi.xmin==15000 ) at->gi.xmin = 0;
+    if ( at->gi.ymin==15000 ) at->gi.ymin = 0;
+    if ( bsizes!=NULL && format==ff_none ) {
+	if (  sf->ascent >at->gi.ymax ) at->gi.ymax =  sf->ascent;
+	if ( -sf->descent<at->gi.ymin ) at->gi.ymin = -sf->descent;
+    }
+    head->xmin = at->gi.xmin;
+    head->ymin = at->gi.ymin;
+    head->xmax = at->gi.xmax;
+    head->ymax = at->gi.ymax;
+
+    lr = rl = arabic = 0;
+    for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
+	SplineChar *sc = sf->glyphs[at->gi.bygid[i]];
+	int uni = sc->unicodeenc ;
+	if ( SCRightToLeft(sc) )
+	    rl = 1;
+	else if (( uni!=-1 && uni<0x10000 && islefttoright(uni)) ||
+		 (uni>=0x10300 && uni<0x107ff))
+	    lr = 1;
+	if ( SCScriptFromUnicode(sc)==CHR('a','r','a','b') )
+	    arabic = 1;
+    }
+
+    head->version = 0x00010000;
+    head->revision = sf->sfntRevision;
+    if ( sf->sfntRevision==sfntRevisionUnset ) {
+	head->revision = 0x00010000;
+	for ( useng=sf->names; useng!=NULL; useng=useng->next )
+	    if ( useng->lang==0x409 )
+	break;
+	if ( useng!=NULL && useng->names[ttf_version]!=NULL &&
+		sscanf(useng->names[ttf_version], "Version %f", &vn)==1 ) {
+	    head->revision = vn*65536;
+	} else if ( sf->subfontcnt!=0 ) {
+	    int val, mant;
+	    val = floor(sf->cidversion);
+	    mant = floor(65536.*((double)sf->cidversion-val));
+	    head->revision = (val<<16) | mant;
+	} else if ( sf->version!=NULL ) {
+	    char *pt=sf->version;
+	    double dval;
+	    int val, mant;
+	    while ( *pt && !isdigit(*pt) && *pt!='.' ) ++pt;
+	    if ( *pt ) {
+		dval = strtod(pt,NULL);
+		val = floor(dval);
+		mant = floor(65536.*(dval-val));
+		head->revision = (val<<16) | mant;
+	    }
+	}
+    }
+    head->checksumAdj = 0;
+    head->magicNum = 0x5f0f3cf5;
+    head->flags = 8|2|1;		/* baseline at 0, lsbline at 0, round ppem */
+    if ( format>=ff_ttf && format<=ff_ttfdfont ) {
+	if ( AnyInstructions(sf) )
+	    head->flags = 0x10|8|4|2|1;	/* baseline at 0, lsbline at 0, round ppem, instructions may depend on point size, instructions change metrics */
+	else if ( AnyMisleadingBitmapAdvances(sf,bsizes))
+	    head->flags = 0x10|8|2|1;	/* baseline at 0, lsbline at 0, round ppem, instructions change metrics */
+    }
+    /* If a font contains embedded bitmaps, and if some of those bitmaps have */
+    /*  a different advance width from that expected by scaling, then windows */
+    /*  will only notice the fact if the 0x10 bit is set (even though this has*/
+    /*  nothing to do with instructions) */
+/* Apple flags */
+    if ( sf->hasvmetrics )
+	head->flags |= (1<<5);		/* designed to be layed out vertically */
+    /* Bit 6 must be zero */
+    if ( arabic )
+	head->flags |= (1<<7);
+    if ( sf->sm )
+	head->flags |= (1<<8);		/* has metamorphesis effects */
+    if ( rl )
+	head->flags |= (1<<9);
+    indic_rearrange = 0;
+    for ( sm = sf->sm; sm!=NULL; sm=sm->next )
+	if ( sm->type == asm_indic )
+	    indic_rearrange = true;
+    if ( indic_rearrange )
+	head->flags |= (1<<10);
+/* End apple flags */
+    if ( sf->head_optimized_for_cleartype )
+	head->flags |= (1<<13);
+    head->emunits = sf->ascent+sf->descent;
+    head->macstyle = MacStyleCode(sf,NULL);
+    head->lowestreadable = 8;
+    head->locais32 = 1;
+    if ( at->gi.glyph_len<0x20000 )
+	head->locais32 = 0;
+
+    /* I assume we've always got some neutrals (spaces, punctuation) */
+    if ( lr && rl )
+	head->dirhint = 0;
+    else if ( rl )
+	head->dirhint = -2;
+    else
+	head->dirhint = 2;
+    if ( rl )
+	head->flags |= (1<<9);		/* Apple documents this */
+    /* if there are any indic characters, set bit 10 */
+
+    cvt_unix_to_1904(sf->creationtime,head->createtime);
+    cvt_unix_to_1904(sf->modificationtime,head->modtime);
+}
+
+static void sethhead(struct hhead *hhead,struct hhead *vhead,struct alltabs *at, SplineFont *sf) {
+    int i, width, rbearing, height, bbearing;
+    int ymax, ymin, xmax, xmin, off;
+    DBounds bb;
+    /* Might as well fill in the vhead even if we don't use it */
+    /*  we just won't dump it out if we don't want it */
+
+    width = 0x80000000; rbearing = 0x7fffffff; height = 0x80000000; bbearing=0x7fffffff;
+    xmax = ymax = 0x80000000; xmin = ymin = 0x7fffffff;
+    for ( i=0; i<at->gi.gcnt; ++i ) if ( at->gi.bygid[i]!=-1 ) {
+	SplineChar *sc = sf->glyphs[at->gi.bygid[i]];
+	SplineCharLayerFindBounds(sc,at->gi.layer,&bb);
+	if ( sc->width>width ) width = sc->width;
+	if ( sc->vwidth>height ) height = sc->vwidth;
+	if ( sc->width-bb.maxx < rbearing ) rbearing = sc->width-bb.maxx;
+	if ( sc->vwidth-bb.maxy < bbearing ) bbearing = sc->vwidth-bb.maxy;
+	if ( bb.maxy > ymax ) ymax = bb.maxy;
+	if ( bb.miny < ymin ) ymin = bb.miny;
+	if ( bb.maxx > xmax ) xmax = bb.maxx;
+	if ( bb.minx < xmin ) xmin = bb.minx;
+    }
+
+    if ( at->head.ymax>ymax ) ymax = at->head.ymax;	/* If generated .notdef glyph is bigger than real glyphs */
+    if ( at->head.ymin<ymin ) ymin = at->head.ymin;
+
+    if ( ymax==0 && ymin==0 ) {
+	/* this can happen in a bitmap only font */
+	ymax = sf->ascent;
+	ymin = -sf->descent;
+    }
+    hhead->version = 0x00010000;
+    if ( sf->pfminfo.hheadascent_add )
+	hhead->ascender = ymax + sf->pfminfo.hhead_ascent;
+    else
+	hhead->ascender = sf->pfminfo.hhead_ascent;
+    if ( sf->pfminfo.hheaddescent_add )
+	hhead->descender = ymin + sf->pfminfo.hhead_descent;
+    else
+	hhead->descender = sf->pfminfo.hhead_descent;
+    hhead->linegap = sf->pfminfo.linegap;
+
+    vhead->version = 0x00011000;
+    off = (sf->ascent+sf->descent)/2;
+    vhead->ascender = xmax-off;
+    vhead->descender = xmin-off;
+    vhead->linegap = sf->pfminfo.linegap;
+
+    at->isfixed = at->gi.fixed_width>0;
+    hhead->maxwidth = width;
+    hhead->minlsb = at->head.xmin;
+    hhead->minrsb = rbearing;
+    /* Apple's ftxvalidator says the min sidebearing should be 0 even if it isn't */
+    if ( hhead->minlsb>0 ) hhead->minlsb = 0;
+    if ( hhead->minrsb>0 ) hhead->minrsb = 0;
+    hhead->maxextent = at->head.xmax;
+    if ( sf->italicangle==0 )
+	hhead->caretSlopeRise = 1;
+    else {
+	hhead->caretSlopeRise = 100;
+	hhead->caretSlopeRun = (int) rint(100*tan(-sf->italicangle*3.1415926535897/180.));
+    }
+
+    vhead->maxwidth = height;
+    vhead->minlsb = at->head.ymin;
+    vhead->minrsb = bbearing;
+    vhead->maxextent = at->head.ymax;
+    vhead->caretSlopeRise = 0;
+    vhead->caretSlopeRun = 1;
+	/* Are there vertical oblique fonts? */
+
+    hhead->numMetrics = at->gi.hfullcnt;
+    vhead->numMetrics = at->gi.vfullcnt;
+}
+
+static void OS2WeightCheck(struct pfminfo *pfminfo,char *weight) {
+    if ( weight==NULL ) {
+	/* default it */
+    } else if ( strstrmatch(weight,"medi")!=NULL ) {
+	pfminfo->weight = 500;
+	pfminfo->panose[2] = 6;
+    } else if ( strstrmatch(weight,"demi")!=NULL ||
+		strstrmatch(weight,"halb")!=NULL ||
+		(strstrmatch(weight,"semi")!=NULL &&
+		    strstrmatch(weight,"bold")!=NULL) ) {
+	pfminfo->weight = 600;
+	pfminfo->panose[2] = 7;
+    } else if ( strstrmatch(weight,"bold")!=NULL ||
+		strstrmatch(weight,"fett")!=NULL ||
+		strstrmatch(weight,"gras")!=NULL ) {
+	pfminfo->weight = 700;
+	pfminfo->panose[2] = 8;
+    } else if ( strstrmatch(weight,"heavy")!=NULL ) {
+	pfminfo->weight = 800;
+	pfminfo->panose[2] = 9;
+    } else if ( strstrmatch(weight,"black")!=NULL ) {
+	pfminfo->weight = 900;
+	pfminfo->panose[2] = 10;
+    } else if ( strstrmatch(weight,"nord")!=NULL ) {
+	pfminfo->weight = 950;
+	pfminfo->panose[2] = 11;
+    } else if ( strstrmatch(weight,"thin")!=NULL ) {
+	pfminfo->weight = 100;
+	pfminfo->panose[2] = 2;
+    } else if ( strstrmatch(weight,"extra")!=NULL ||
+	    strstrmatch(weight,"light")!=NULL ) {
+	pfminfo->weight = 200;
+	pfminfo->panose[2] = 3;
+    } else if ( strstrmatch(weight,"light")!=NULL ) {
+	pfminfo->weight = 300;
+	pfminfo->panose[2] = 4;
+    }
+}
+
+void SFDefaultOS2Simple(struct pfminfo *pfminfo,SplineFont *sf) {
+    pfminfo->pfmfamily = 0x11;
+    pfminfo->panose[0] = 2;
+    pfminfo->weight = 400;
+    pfminfo->panose[2] = 5;
+    pfminfo->width = 5;
+    pfminfo->panose[3] = 3;
+    pfminfo->winascent_add = pfminfo->windescent_add = true;
+    pfminfo->hheadascent_add = pfminfo->hheaddescent_add = true;
+    pfminfo->typoascent_add = pfminfo->typodescent_add = true;
+    pfminfo->os2_winascent = pfminfo->os2_windescent = 0;
+
+    if ( sf->subfonts!=NULL ) sf = sf->subfonts[0];
+    pfminfo->linegap = pfminfo->vlinegap = pfminfo->os2_typolinegap =
+	    rint(.09*(sf->ascent+sf->descent));
+}
+
+void SFDefaultOS2SubSuper(struct pfminfo *pfminfo,int emsize,double italic_angle) {
+    double s = sin(italic_angle*3.1415926535897932/180.0);
+    pfminfo->os2_supysize = pfminfo->os2_subysize = .7*emsize;
+    pfminfo->os2_supxsize = pfminfo->os2_subxsize = .65*emsize;
+    pfminfo->os2_subyoff = .14*emsize;
+    pfminfo->os2_supyoff = .48*emsize;
+    pfminfo->os2_supxoff =  s*pfminfo->os2_supyoff;
+    pfminfo->os2_subxoff = -s*pfminfo->os2_subyoff;
+    pfminfo->os2_strikeysize = 102*emsize/2048;
+    pfminfo->os2_strikeypos = 530*emsize/2048;
+}
+
+void SFDefaultOS2Info(struct pfminfo *pfminfo,SplineFont *sf,char *fontname) {
+    int samewid= -1;
+    char *weight = sf->cidmaster==NULL ? sf->weight : sf->cidmaster->weight;
+
+    if ( sf->pfminfo.pfmset ) {
+	if ( pfminfo!=&sf->pfminfo )
+	    *pfminfo = sf->pfminfo;
+	if ( !pfminfo->panose_set ) {
+	    struct pfminfo info;
+	    memset(&info,0,sizeof(info));
+	    sf->pfminfo.pfmset = false;
+	    SFDefaultOS2Info(&info,sf,fontname);
+	    sf->pfminfo.pfmset = true;
+	    memcpy(pfminfo->panose,info.panose,sizeof(info.panose));
+	}
+    } else {
+	struct pfminfo hold;
+	if ( pfminfo->hheadset || pfminfo->vheadset )
+	    hold = *pfminfo;
+	else
+	    hold.hheadset = hold.vheadset = false;
+	memset(pfminfo,'\0',sizeof(*pfminfo));
+	SFDefaultOS2Simple(pfminfo,sf);
+	samewid = CIDOneWidth(sf);
+
+	pfminfo->pfmfamily = 0x10;
+	if ( samewid>0 ) {
+	    pfminfo->pfmfamily = 0x30;
+	    /* pfminfo->panose[3] = 9; */ /* This is done later */
+	} else if ( strstrmatch(fontname,"sans")!=NULL )
+	    pfminfo->pfmfamily = 0x20;
+	else if ( strstrmatch(fontname,"script")!=NULL ) {
+	    pfminfo->pfmfamily = 0x40;
+	    pfminfo->panose[0] = 3;
+	}
+	if ( samewid==-1 )
+	    pfminfo->pfmfamily |= 0x1;	/* Else it assumes monospace */
+
+/* urw uses 4 character abreviations */
+	if ( weight!=NULL )
+	    OS2WeightCheck(pfminfo,weight);
+	OS2WeightCheck(pfminfo,fontname);
+
+	if ( strstrmatch(fontname,"ultra")!=NULL &&
+		strstrmatch(fontname,"condensed")!=NULL ) {
+	    pfminfo->width = 1;
+	    pfminfo->panose[3] = 8;
+	} else if ( strstrmatch(fontname,"extra")!=NULL &&
+		strstrmatch(fontname,"condensed")!=NULL ) {
+	    pfminfo->width = 2;
+	    pfminfo->panose[3] = 8;
+	} else if ( strstrmatch(fontname,"semi")!=NULL &&
+		strstrmatch(fontname,"condensed")!=NULL ) {
+	    pfminfo->width = 4;
+	    pfminfo->panose[3] = 6;
+	} else if ( strstrmatch(fontname,"condensed")!=NULL ||
+		strstrmatch(fontname,"narrow")!=NULL ) {
+	    pfminfo->width = 3;
+	    pfminfo->panose[3] = 6;
+	} else if ( strstrmatch(fontname,"ultra")!=NULL &&
+		strstrmatch(fontname,"expanded")!=NULL ) {
+	    pfminfo->width = 9;
+	    pfminfo->panose[3] = 7;
+	} else if ( strstrmatch(fontname,"extra")!=NULL &&
+		strstrmatch(fontname,"expanded")!=NULL ) {
+	    pfminfo->width = 8;
+	    pfminfo->panose[3] = 7;
+	} else if ( strstrmatch(fontname,"semi")!=NULL &&
+		strstrmatch(fontname,"expanded")!=NULL ) {
+	    pfminfo->width = 6;
+	    pfminfo->panose[3] = 5;
+	} else if ( strstrmatch(fontname,"expanded")!=NULL ) {
+	    pfminfo->width = 7;
+	    pfminfo->panose[3] = 5;
+	}
+	if ( samewid>0 )
+	    pfminfo->panose[3] = 9;
+	if ( hold.hheadset ) {
+	    pfminfo->hheadset = true;
+	    pfminfo->hheadascent_add = hold.hheadascent_add;
+	    pfminfo->hheaddescent_add = hold.hheaddescent_add;
+	    pfminfo->hhead_ascent = hold.hhead_ascent;
+	    pfminfo->hhead_descent = hold.hhead_descent;
+	    pfminfo->linegap = hold.linegap;
+	}
+	if ( hold.vheadset ) {
+	    pfminfo->vheadset = true;
+	    pfminfo->vlinegap = hold.vlinegap;
+	}
+    }
+    if ( !pfminfo->subsuper_set )
+	SFDefaultOS2SubSuper(pfminfo,sf->ascent+sf->descent,sf->italicangle);
+}
+
+int AlreadyMSSymbolArea(SplineFont *sf,EncMap *map) {
+    int i;
+    int acnt=0, pcnt=0;
+
+    for ( i=0; i<map->enccount && i<0xffff; ++i ) {
+	if ( map->map[i]!=-1 && sf->glyphs[map->map[i]]!=NULL &&
+		sf->glyphs[map->map[i]]->ttf_glyph!=-1 ) {
+	    if ( i>=0xf000 && i<=0xf0ff )
+		++pcnt;
+	    else if ( i>=0x20 && i<=0xff )
+		++acnt;
+	}
+    }
+return( pcnt>acnt );
+}
+
+void OS2FigureCodePages(SplineFont *sf, uint32 CodePage[2]) {
+    int i;
+    uint32 latin1[8];
+    int has_ascii, has_lineart=0, has_radical=0, has_summation=0;
+    int cp852=0, cp775=0, cp861=0, cp860=0, cp857=0, cp855=0, cp862=0, cp863=0;
+    int cp864=0, cp865=0, cp866=0, cp869=0, cp737=0, cp708=0, mac=0;
+    int k;
+    SplineChar *sc;
+    SplineFont *sub;
+
+    memset(latin1,0,sizeof(latin1));
+    k=0;
+    do {
+	sub = k<sf->subfontcnt? sf->subfonts[k] : sf;
+	for ( i=0; i<sub->glyphcnt; ++i ) if ( (sc = sub->glyphs[i])!=NULL ) {
+	    if ( sc->unicodeenc<256 && sc->unicodeenc>=0 )
+		latin1[(sc->unicodeenc>>5)] |= 1<<(sc->unicodeenc&31);
+	}
+	++k;
+    } while ( k<sf->subfontcnt );
+
+    has_ascii = latin1[1]==0xffffffff && latin1[2]==0xffffffff &&
+	    (latin1[3]&0x7fffffff)==0x7fffffff;		/* DEL is not a char */
+    CodePage[0] = CodePage[1] = 0;
+
+    k=0;
+    do {
+	sub = k<sf->subfontcnt? sf->subfonts[k] : sf;
+	for ( i=0; i<sub->glyphcnt; ++i ) if ( (sc = sub->glyphs[i])!=NULL ) {
+	    int uni = sc->unicodeenc;
+	    if ( uni==0xde && has_ascii )
+		CodePage[0] |= 1<<0;		/* (ANSI) Latin1 */
+	    else if ( uni==0x255a && has_ascii ) {
+		CodePage[1] |= 1U<<30;		/* WE/latin1 */ /* Not latin1 at all */
+		CodePage[1] |= 1U<<31;		/* US */
+	    } else if ( uni==0x13d && has_ascii ) {
+		CodePage[0] |= 1<<1;		/* latin2 */
+		++cp852;
+	    } else if ( uni==0x411 ) {
+		CodePage[0] |= 1<<2;		/* cyrillic */
+		++cp866;
+		++cp855;
+	    } else if ( uni==0x405 ) {
+		++cp855;
+	    } else if ( uni==0x386 ) {
+		CodePage[0] |= 1<<3;		/* greek */
+		++cp869;
+		++cp737;
+	    } else if ( uni==0x130 && has_ascii ) {
+		CodePage[0] |= 1<<4;		/* turkish */
+		++cp857;
+	    } else if ( uni==0x5d0 ) {
+		CodePage[0] |= 1<<5;		/* hebrew */
+		++cp862;
+	    } else if ( uni==0x631 ) {
+		CodePage[0] |= 1<<6;		/* arabic */
+		++cp864;
+		++cp708;
+	    } else if ( uni==0x157 && has_ascii ) {
+		CodePage[0] |= 1<<7;		/* baltic */
+		++cp775;
+	    } else if ( uni==0x20AB && has_ascii ) {
+		CodePage[0] |= 1<<8;		/* vietnamese */
+	    } else if ( uni==0xe45 )
+		CodePage[0] |= 1<<16;		/* thai */
+	    else if ( uni==0x30a8 )
+		CodePage[0] |= 1<<17;		/* japanese */
+	    else if ( uni==0x3105 )
+		CodePage[0] |= 1<<18;		/* simplified chinese */
+	    else if ( uni==0x3131 )
+		CodePage[0] |= 1<<19;		/* korean wansung */
+	    else if ( uni==0x592E )
+		CodePage[0] |= 1<<20;		/* traditional chinese */
+	    else if ( uni==0xacf4 )
+		CodePage[0] |= 1<<21;		/* korean Johab */
+	    else if ( uni==0x2030 && has_ascii )
+		++mac;
+	    else if ( uni==0x2665 && has_ascii )
+		CodePage[0] |= 1U<<30;		/* OEM */
+	/* the symbol bit doesn't mean it contains the glyphs in symbol */
+	    /* rather that one is using a symbol encoding. Or that there are */
+	    /* glyphs with unicode encoding between 0xf000 and 0xf0ff, in which */
+	    /* case those guys should be given a symbol encoding */
+	    /* There's a bug in the way otf fonts handle this (but not ttf) and */
+	    /*  they only seem to list the symbol glyphs. */
+            /* Hence we don't test uni==0x21d4 */
+	    /* This doesn't work well either. In ttf fonts the bit is ignored */
+	    /*  in otf fonts the bit means "ignore all other bits" */
+	    else if ( uni>=0xf000 && uni<=0xf0ff )
+		CodePage[0] |= 1U<<31;		/* symbol */
+	    else if ( uni==0xc5 && has_ascii )
+		++cp865;
+	    else if ( uni==0xe9 && has_ascii )
+		++cp863;
+	    else if ( uni==0xf5 && has_ascii )
+		++cp860;
+	    else if ( uni==0xfe && has_ascii )
+		++cp861;
+	    else if ( uni==0x2524 )
+		++has_lineart;
+	    else if ( uni==0x255c )
+		++cp866;
+	    else if ( uni==0xbd )
+		++cp869;
+	    else if ( uni==0x221A )
+		has_radical=true;
+	    else if ( uni==0x2211 )
+		has_summation=true;
+	}
+	++k;
+    } while ( k<sf->subfontcnt );
+    if ( cp852 && has_lineart )
+	CodePage[1] |= 1<<26;		/* latin2 */
+    if ( cp775 && has_lineart )
+	CodePage[1] |= 1<<27;		/* baltic */
+    if ( cp861 && has_lineart )
+	CodePage[1] |= 1<<22;		/* MS-DOS Icelandic */
+    if ( cp866==2 && has_lineart )
+	CodePage[1] |= 1<<17;		/* MS DOS Russian */
+    if ( cp855==2 && has_lineart )
+	CodePage[1] |= 1<<25;		/* IBM Cyrillic */
+    if ( cp869==2 && has_lineart )
+	CodePage[1] |= 1<<16;		/* IBM Greek */
+    if ( cp737 && has_lineart && has_radical )
+	CodePage[1] |= 1<<28;		/* Greek, former 437 G */
+    if ( cp857 && has_lineart )
+	CodePage[1] |= 1<<24;		/* IBM turkish */
+    if ( cp862 && has_lineart && has_radical )
+	CodePage[1] |= 1<<21;		/* hebrew */
+    if ( cp864 && has_radical )
+	CodePage[1] |= 1<<19;		/* arabic */
+    if ( cp708 && has_lineart)
+	CodePage[1] |= 1<<29;		/* arabic; ASMO 708 */
+    if ( cp863 && has_lineart && has_radical )
+	CodePage[1] |= 1<<20;		/* MS-DOS Canadian French */
+    if ( cp865 && has_lineart && has_radical )
+	CodePage[1] |= 1<<18;		/* MS-DOS Nordic */
+    if ( cp860 && has_lineart && has_radical )
+	CodePage[1] |= 1<<23;		/* MS-DOS Portuguese */
+    if ( mac && has_summation )
+	CodePage[0] |= 1U<<29;		/* mac roman */
+}
+
+void OS2FigureUnicodeRanges(SplineFont *sf, uint32 Ranges[4]) {
+    int i, k;
+    unsigned j;
+    SplineChar *sc;
+    SplineFont *sub;
+
+    memset(Ranges,0,4*sizeof(uint32));
+    k=0;
+    do {
+	sub = k<sf->subfontcnt? sf->subfonts[k] : sf;
+	for ( i=0; i<sub->glyphcnt; ++i ) if ( (sc = sub->glyphs[i])!=NULL ) {
+	    if ( SCWorthOutputting(sc) && sc->unicodeenc!=-1 ) {
+		if ( sc->unicodeenc > 0xffff )
+		    Ranges[57>>5] |= (1<<(57&31));
+		for ( j=0; j<sizeof(uniranges)/sizeof(uniranges[0]); ++j )
+		    if ( sc->unicodeenc>=uniranges[j][0] &&
+			    sc->unicodeenc<=uniranges[j][1] ) {
+			int bit = uniranges[j][2];
+			Ranges[bit>>5] |= (1<<(bit&31));
+		break;
+		    }
+	    }
+	}
+	++k;
+    } while ( k<sf->subfontcnt );
+}
+
+static void WinBB(SplineFont *sf,uint16 *winascent,uint16 *windescent,struct alltabs *at) {
+    /* The windows ascent/descent is calculated on the ymin/max of the */
+    /*  glyphs in the so called ANSI character set. I'm going to pretend */
+    /*  that's Latin1 with a few additions */
+    /* Well, that's what is documented, but the documentation says contradictory */
+    /*  things. I believe that winAscent should be the same as hhea.ascent */
+
+    *winascent = at->head.ymax;
+    *windescent = -at->head.ymin;		/* Should be positive */
+    if ( sf->cidmaster!=NULL )
+	sf = sf->cidmaster;
+
+    if ( sf->pfminfo.winascent_add )
+	*winascent += sf->pfminfo.os2_winascent;
+    else
+	*winascent  = sf->pfminfo.os2_winascent;
+    if ( sf->pfminfo.windescent_add )
+	*windescent += sf->pfminfo.os2_windescent;
+    else
+	*windescent  = sf->pfminfo.os2_windescent;
+}
+
+static void redohead(struct alltabs *at);
+
+static void setos2(struct os2 *os2,struct alltabs *at, SplineFont *sf,
+	enum fontformat format) {
+    int i,cnt1,cnt2,first,last,avg1,avg2,gid;
+    char *pt;
+    static int const weightFactors[26] = { 64, 14, 27, 35, 100, 20, 14, 42, 63,
+	3, 6, 35, 20, 56, 56, 17, 4, 49, 56, 71, 31, 10, 18, 3, 18, 2 };
+    EncMap *map;
+    SplineChar *sc;
+    int modformat = format;
+
+    os2->version = 1;
+    if ( format==ff_otf || format==ff_otfcid )
+	os2->version = 3;
+    if ( sf->use_typo_metrics || sf->weight_width_slope_only )
+	os2->version = 4;
+    if ( sf->os2_version > os2->version )
+	os2->version = sf->os2_version;
+    if (( format>=ff_ttf && format<=ff_otfdfont) && (at->gi.flags&ttf_flag_symbol))
+	modformat = ff_ttfsym;
+
+    os2->weightClass = sf->pfminfo.weight;
+    os2->widthClass = sf->pfminfo.width;
+    os2->fstype = 0x8;
+    if ( sf->pfminfo.fstype!=-1 )
+	os2->fstype = sf->pfminfo.fstype;
+    if ( !sf->pfminfo.subsuper_set )
+	SFDefaultOS2SubSuper(&sf->pfminfo,sf->ascent+sf->descent,sf->italicangle);
+    os2->ysupYSize = sf->pfminfo.os2_supysize;
+    os2->ysubXSize = sf->pfminfo.os2_subxsize;
+    os2->ysubYSize = sf->pfminfo.os2_subysize;
+    os2->ysupXSize = sf->pfminfo.os2_supxsize;
+    os2->ysubYOff = sf->pfminfo.os2_subyoff;
+    os2->ysubXOff = sf->pfminfo.os2_subxoff;
+    os2->ysupXOff = sf->pfminfo.os2_supxoff;
+    os2->ysupYOff = sf->pfminfo.os2_supyoff;
+    os2->yStrikeoutSize = sf->pfminfo.os2_strikeysize;
+    os2->yStrikeoutPos = sf->pfminfo.os2_strikeypos;
+    if ( sf->pfminfo.stylemap!=-1 ) {
+        int changed = 0;
+        os2->fsSel = sf->pfminfo.stylemap;
+        /* Make sure fsSel and macStyle don't contradict */
+        if (at->head.macstyle&1 && !(os2->fsSel&32)) {at->head.macstyle &= 0x7E; changed=1;}
+        if (at->head.macstyle&2 && !(os2->fsSel&1)) {at->head.macstyle &= 0x7D; changed=1;}
+        if (changed) redohead(at);
+    } else {
+        os2->fsSel = (at->head.macstyle&1?32:0)|(at->head.macstyle&2?1:0);
+        if ( os2->fsSel==0 && sf->pfminfo.weight==400 )
+	        os2->fsSel = 64;		/* Regular */
+    }
+    if ( sf->fullname!=NULL && strstrmatch(sf->fullname,"outline")!=NULL )
+	os2->fsSel |= 8;
+    if ( os2->version>=4 ) {
+	if ( strstrmatch(sf->fontname,"Obli")!=NULL ) {
+	    os2->fsSel &= ~1;		/* Turn off Italic */
+	    os2->fsSel |= 512;		/* Turn on Oblique */
+	}
+	if ( sf->use_typo_metrics )
+	    os2->fsSel |= 128;		/* Don't use win ascent/descent for line spacing */
+	if ( sf->weight_width_slope_only )
+	    os2->fsSel |= 256;
+    }
+/* David Lemon @Adobe.COM
+1)  The sTypoAscender and sTypoDescender values should sum to 2048 in
+a 2048-unit font. They indicate the position of the em square
+relative to the baseline.
+GWW: Almost, sTypoAscender-sTypoDescender == EmSize
+
+2)  The usWinAscent and usWinDescent values represent the maximum
+height and depth of specific glyphs within the font, and some
+applications will treat them as the top and bottom of the font
+bounding box. (the "ANSI" glyphs)
+GWW: That's what's documented. But it means non-ANSI glyphs get clipped. So the
+docs are wrong.
+*/
+    if ( sf->pfminfo.typoascent_add )
+	os2->ascender = sf->ascent + sf->pfminfo.os2_typoascent;
+    else
+	os2->ascender = sf->pfminfo.os2_typoascent;
+    if ( sf->pfminfo.typodescent_add )
+	os2->descender = -sf->descent + sf->pfminfo.os2_typodescent;	/* Should be neg */
+    else
+	os2->descender = sf->pfminfo.os2_typodescent;
+    WinBB(sf,&os2->winascent,&os2->windescent,at);
+    os2->linegap = sf->pfminfo.os2_typolinegap;
+    os2->sFamilyClass = sf->pfminfo.os2_family_class;
+
+    avg1 = avg2 = last = 0; first = 0xffff;
+    cnt1 = cnt2 = 0;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
+	if ( SCWorthOutputting(sc) && sc->unicodeenc!=-1 ) {
+	    /* Don't include the dummy glyphs (.notdef, .null, etc.) they aren't */
+	    /*  really encoded. Don't include glyphs out of BMP, OS/2 uses shorts */
+	    /*  for the first/last char and can't represent them. */
+	    /* If no BMP glyphs, then first should be 0xffff. If any outside */
+	    /*  BMP then last is 0xffff */
+	    /* sc->ttf_glyph>2 is to skip the first few truetype glyphs but */
+	    /*  that doesn't work for cff files which only have .notdef to ignore */
+	    if ( ( format>=ff_ttf && format<=ff_otfdfont && sc->ttf_glyph>2) ||
+		    ( format>=ff_ttf && format<=ff_otfdfont && sc->ttf_glyph>0) ) {
+		if ( sc->unicodeenc<=0xffff ) {
+		    if ( sc->unicodeenc<first ) first = sc->unicodeenc;
+		    if ( sc->unicodeenc>last ) last = sc->unicodeenc;
+		} else {
+		    last = 0xffff;
+		}
+	    }
+	    if ( sc->width!=0 ) {
+		avg2 += sc->width; ++cnt2;
+	    }
+	    if ( sc->unicodeenc==' ') {
+		avg1 += sc->width * 166; ++cnt1;
+	    } else if (sc->unicodeenc>='a' && sc->unicodeenc<='z') {
+		avg1 += sc->width * weightFactors[sc->unicodeenc-'a']; ++cnt1;
+	    }
+	}
+    }
+    if ( sf->pfminfo.hasunicoderanges )
+	memcpy(os2->unicoderange,sf->pfminfo.unicoderanges,sizeof(os2->unicoderange));
+    else
+	OS2FigureUnicodeRanges(sf,os2->unicoderange);
+    if ( modformat==ff_ttfsym )	/* MS Symbol font has this set to zero. Does it matter? */
+	memset(os2->unicoderange,0,sizeof(os2->unicoderange));
+
+    if ( sf->pfminfo.pfmset )
+	strncpy(os2->achVendID,sf->pfminfo.os2_vendor,4);
+    else if ( TTFFoundry!=NULL )
+	strncpy(os2->achVendID,TTFFoundry,4);
+    else
+	memcpy(os2->achVendID,"PfEd",4);
+    for ( pt=os2->achVendID; pt<os2->achVendID && *pt!='\0'; ++pt );
+    while ( pt<os2->achVendID ) *pt++ = ' ';	/* Pad with spaces not NUL */
+
+    /* v1,2 & v3,4 have different ways of calculating avgCharWid. */
+    /* but I'm told that using the v3 way breaks display of CJK fonts in windows */
+    os2->avgCharWid = 500;
+    os2->v1_avgCharWid = os2->v3_avgCharWid = 0;
+    if ( cnt1==27 )
+	os2->v1_avgCharWid = avg1/1000;
+    if ( cnt2!=0 )
+	os2->v3_avgCharWid = avg2/cnt2;
+    memcpy(os2->panose,sf->pfminfo.panose,sizeof(os2->panose));
+    map = at->map;
+    if ( modformat==ff_ttfsym ) {
+	if ( sf->pfminfo.hascodepages )
+	    memcpy(os2->ulCodePage,sf->pfminfo.codepages,sizeof(os2->ulCodePage));
+	else {
+	    os2->ulCodePage[0] = 0x80000000;
+	    os2->ulCodePage[1] = 0;
+	}
+	if ( AlreadyMSSymbolArea(sf,map)) {
+	    first = 0xf0ff; last = 0;
+	    for ( i=0xf020; i<map->enccount && i<=0xf0ff; ++i )
+		if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
+			sf->glyphs[gid]->ttf_glyph!=-1 ) {
+		    if ( i<first ) first = i;
+		    if ( i>last ) last = i;
+		}
+	    for ( i=0; i<map->enccount && i<=255; ++i )
+		if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
+			sf->glyphs[gid]->ttf_glyph!=-1 ) {
+		    if ( i+0xf000<first ) first = i+0xf000;
+		    if ( i+0xf000>last ) last = i+0xf000;
+		}
+	    os2->firstcharindex = first;	/* This gets mapped to space */
+	    os2->lastcharindex  = last;
+	} else {
+	    first = 255; last = 0;
+	    for ( i=0; i<map->enccount && i<=255; ++i )
+		if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
+			sf->glyphs[gid]->ttf_glyph!=-1 ) {
+		    if ( i<first ) first = i;
+		    if ( i>last ) last = i;
+		}
+	    for ( i=0xf020; i<map->enccount && i<=0xf0ff; ++i )
+		if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
+			sf->glyphs[gid]->ttf_glyph!=-1 ) {
+		    if ( i-0xf000<first ) first = i-0xf000;
+		    if ( i-0xf000>last ) last = i-0xf000;
+		}
+	    if ( first<' ' ) first = ' ';
+	    os2->firstcharindex = 0xf000 + first;	/* This gets mapped to space */
+	    os2->lastcharindex  = 0xf000 + last;
+	}
+    } else {
+	os2->firstcharindex = first;
+	os2->lastcharindex = last;
+	if ( sf->pfminfo.hascodepages )
+	    memcpy(os2->ulCodePage,sf->pfminfo.codepages,sizeof(os2->ulCodePage));
+	else
+	    OS2FigureCodePages(sf, os2->ulCodePage);
+	/* Herbert Duerr: */
+	/* Some old versions of Windows do not provide access to all    */
+	/* glyphs in a font if the fonts contains non-PUA symbols       */
+	/* and thus only has sets the codepage flag for symbol          */
+	/* => a workaround for this problem on Windows legacy versions  */
+	/* is to use an OS2-table version without codepage flags        */
+	/* GWW: */
+	/* This sounds to me like a windows bug rather than one in ff	*/
+	/*  and this is a work-around for windows. As far as I can tell	*/
+	/*  ff is setting the codepage field properly, it's just that	*/
+	/*  windows doesn't interpret that bit correctly		*/
+	/* GWW: Things get worse. Windows no longer accepts a version 0 */
+	/*  for OS/2. FontLab simply lies and says we have a latin1     */
+	/*  code page when we don't.					*/
+	if( !sf->pfminfo.hascodepages )
+	    if( (os2->ulCodePage[0]&~(1U<<31))==0 && os2->ulCodePage[1]==0 )
+                os2->ulCodePage[0] |= 1;
+    }
+
+    if ( os2->version>=2 ) {
+    if ( sf->pfminfo.os2_xheight!=0 )
+        os2->xHeight = sf->pfminfo.os2_xheight;
+    else {
+	    double xh = SFXHeight(sf,at->gi.layer,true);
+        os2->xHeight = (xh >= 0.0 ? xh : 0);
+    }
+    if ( sf->pfminfo.os2_capheight!=0 )
+        os2->capHeight = sf->pfminfo.os2_capheight;
+    else {
+	    double caph = SFCapHeight(sf,at->gi.layer,true);
+        os2->capHeight = (caph >= 0.0 ? caph : 0);
+    }
+	os2->defChar = 0;
+	if ( format==ff_otf || format==ff_otfcid )
+	    os2->defChar = ' ';
+	os2->breakChar = ' ';
+	os2->maxContext = 1;	/* Kerning will set this to 2, ligature to whatever */
+    }
+
+    if ( os2->version>=3 && os2->v3_avgCharWid!=0 )
+	os2->avgCharWid = os2->v3_avgCharWid;
+    else if ( os2->v1_avgCharWid!=0 )
+	os2->avgCharWid = os2->v1_avgCharWid;
+    else if ( os2->v3_avgCharWid!=0 )
+	os2->avgCharWid = os2->v3_avgCharWid;
+}
+
+static void redoloca(struct alltabs *at) {
+    int i;
+
+    at->loca = tmpfile();
+    if ( at->head.locais32 ) {
+	for ( i=0; i<=at->maxp.numGlyphs; ++i )
+	    putlong(at->loca,at->gi.loca[i]);
+	at->localen = sizeof(int32)*(at->maxp.numGlyphs+1);
+    } else {
+	for ( i=0; i<=at->maxp.numGlyphs; ++i )
+	    putshort(at->loca,at->gi.loca[i]/2);
+	at->localen = sizeof(int16)*(at->maxp.numGlyphs+1);
+	if ( ftell(at->loca)&2 )
+	    putshort(at->loca,0);
+    }
+    if ( at->format!=ff_type42 && at->format!=ff_type42cid ) {
+	free(at->gi.loca);
+	at->gi.loca = NULL;
+    }
+}
+
+static void dummyloca(struct alltabs *at) {
+
+    at->loca = tmpfile();
+    if ( at->head.locais32 ) {
+	putlong(at->loca,0);
+	at->localen = sizeof(int32);
+    } else {
+	putshort(at->loca,0);
+	at->localen = sizeof(int16);
+	putshort(at->loca,0);	/* pad it */
+    }
+}
+
+static void redohead(struct alltabs *at) {
+    at->headf = tmpfile();
+
+    putlong(at->headf,at->head.version);
+    putlong(at->headf,at->head.revision);
+    putlong(at->headf,at->head.checksumAdj);
+    putlong(at->headf,at->head.magicNum);
+    putshort(at->headf,at->head.flags);
+    putshort(at->headf,at->head.emunits);
+    putlong(at->headf,at->head.createtime[1]);
+    putlong(at->headf,at->head.createtime[0]);
+    putlong(at->headf,at->head.modtime[1]);
+    putlong(at->headf,at->head.modtime[0]);
+    putshort(at->headf,at->head.xmin);
+    putshort(at->headf,at->head.ymin);
+    putshort(at->headf,at->head.xmax);
+    putshort(at->headf,at->head.ymax);
+    putshort(at->headf,at->head.macstyle);
+    putshort(at->headf,at->head.lowestreadable);
+    putshort(at->headf,at->head.dirhint);
+    putshort(at->headf,at->head.locais32);
+    putshort(at->headf,at->head.glyphformat);
+
+    at->headlen = ftell(at->headf);
+    if ( (at->headlen&2)!=0 )
+	putshort(at->headf,0);
+}
+
+static void redohhead(struct alltabs *at,int isv) {
+    int i;
+    struct hhead *head;
+    FILE *f;
+
+    if ( !isv ) {
+	f = at->hheadf = tmpfile();
+	head = &at->hhead;
+    } else {
+	f = at->vheadf = tmpfile();
+	head = &at->vhead;
+    }
+
+    putlong(f,head->version);
+    putshort(f,head->ascender);
+    putshort(f,head->descender);
+    putshort(f,head->linegap);
+    putshort(f,head->maxwidth);
+    putshort(f,head->minlsb);
+    putshort(f,head->minrsb);
+    putshort(f,head->maxextent);
+    putshort(f,head->caretSlopeRise);
+    putshort(f,head->caretSlopeRun);
+    for ( i=0; i<5; ++i )
+	putshort(f,head->mbz[i]);
+    putshort(f,head->metricformat);
+    putshort(f,head->numMetrics);
+
+    if ( !isv ) {
+	at->hheadlen = ftell(f);
+	if ( (at->hheadlen&2)!=0 )
+	    putshort(f,0);
+    } else {
+	at->vheadlen = ftell(f);
+	if ( (at->vheadlen&2)!=0 )
+	    putshort(f,0);
+    }
+}
+
+static void redomaxp(struct alltabs *at,enum fontformat format) {
+    at->maxpf = tmpfile();
+
+    putlong(at->maxpf,at->maxp.version);
+    putshort(at->maxpf,at->maxp.numGlyphs);
+    if ( format!=ff_otf && format!=ff_otfcid ) {
+	putshort(at->maxpf,at->maxp.maxPoints);
+	putshort(at->maxpf,at->maxp.maxContours);
+	putshort(at->maxpf,at->maxp.maxCompositPts);
+	putshort(at->maxpf,at->maxp.maxCompositCtrs);
+	putshort(at->maxpf,at->maxp.maxZones);
+	putshort(at->maxpf,at->maxp.maxTwilightPts);
+	putshort(at->maxpf,at->maxp.maxStorage);
+	putshort(at->maxpf,at->maxp.maxFDEFs);
+	putshort(at->maxpf,at->maxp.maxIDEFs);
+	putshort(at->maxpf,at->maxp.maxStack);
+	putshort(at->maxpf,at->maxp.maxglyphInstr);
+	putshort(at->maxpf,at->maxp.maxnumcomponents);
+	putshort(at->maxpf,at->maxp.maxcomponentdepth);
+    }
+
+    at->maxplen = ftell(at->maxpf);
+    if ( (at->maxplen&2)!=0 )
+	putshort(at->maxpf,0);
+}
+
+static void redoos2(struct alltabs *at) {
+    int i;
+    at->os2f = tmpfile();
+
+    putshort(at->os2f,at->os2.version);
+    putshort(at->os2f,at->os2.avgCharWid);
+    putshort(at->os2f,at->os2.weightClass);
+    putshort(at->os2f,at->os2.widthClass);
+    putshort(at->os2f,at->os2.fstype);
+    putshort(at->os2f,at->os2.ysubXSize);
+    putshort(at->os2f,at->os2.ysubYSize);
+    putshort(at->os2f,at->os2.ysubXOff);
+    putshort(at->os2f,at->os2.ysubYOff);
+    putshort(at->os2f,at->os2.ysupXSize);
+    putshort(at->os2f,at->os2.ysupYSize);
+    putshort(at->os2f,at->os2.ysupXOff);
+    putshort(at->os2f,at->os2.ysupYOff);
+    putshort(at->os2f,at->os2.yStrikeoutSize);
+    putshort(at->os2f,at->os2.yStrikeoutPos);
+    putshort(at->os2f,at->os2.sFamilyClass);
+    for ( i=0; i<10; ++i )
+	putc(at->os2.panose[i],at->os2f);
+    for ( i=0; i<4; ++i )
+	putlong(at->os2f,at->os2.unicoderange[i]);
+    for ( i=0; i<4; ++i )
+	putc(at->os2.achVendID[i],at->os2f);
+    putshort(at->os2f,at->os2.fsSel);
+    putshort(at->os2f,at->os2.firstcharindex);
+    putshort(at->os2f,at->os2.lastcharindex);
+    putshort(at->os2f,at->os2.ascender);
+    putshort(at->os2f,at->os2.descender);
+    putshort(at->os2f,at->os2.linegap);
+    putshort(at->os2f,at->os2.winascent);
+    putshort(at->os2f,at->os2.windescent);
+    if ( at->os2.version>=1 ) {
+	putlong(at->os2f,at->os2.ulCodePage[0]);
+	putlong(at->os2f,at->os2.ulCodePage[1]);
+    }
+
+    if ( at->os2.version>=2 ) {
+	putshort(at->os2f,at->os2.xHeight);
+	putshort(at->os2f,at->os2.capHeight);
+	putshort(at->os2f,at->os2.defChar);
+	putshort(at->os2f,at->os2.breakChar);
+	putshort(at->os2f,at->os2.maxContext);
+    }
+
+    at->os2len = ftell(at->os2f);
+    if ( (at->os2len&2)!=0 )
+	putshort(at->os2f,0);
+}
+
+static void dumpgasp(struct alltabs *at, SplineFont *sf) {
+    int i;
+
+    at->gaspf = tmpfile();
+    if ( sf->gasp_cnt==0 ) {
+	putshort(at->gaspf,0);	/* Old version number */
+	/* For fonts with no instructions always dump a gasp table which */
+	/*  asks for grey and no grid fit */
+	putshort(at->gaspf,1);
+	putshort(at->gaspf,0xffff);	/* Upper bound on pixels/em for this range */
+	putshort(at->gaspf,0x2);	/* Grey scale, no gridfitting */
+					/* No hints, so no grids to fit */
+    } else {
+	putshort(at->gaspf,sf->gasp_version);	/* New version number, with clear type info */
+	putshort(at->gaspf,sf->gasp_cnt);
+	for ( i=0; i<sf->gasp_cnt; ++i ) {
+	    putshort(at->gaspf,sf->gasp[i].ppem);
+	    putshort(at->gaspf,sf->gasp[i].flags);
+	}
+    }
+    at->gasplen = ftell(at->gaspf);
+	/* This table is always 32 bit aligned */
+}
+
+static void dumpstr(FILE *file,char *str) {
+    do {
+	putc(*str,file);
+    } while ( *str++!='\0' );
+}
+
+static void dumpustr(FILE *file,char *utf8_str) {
+    unichar_t *ustr = utf82u_copy(utf8_str), *pt=ustr;
+    do {
+	putc(*pt>>8,file);
+	putc(*pt&0xff,file);
+    } while ( *pt++!='\0' );
+    free(ustr);
+}
+
+static void dumppstr(FILE *file,const char *str) {
+    putc(strlen(str),file);
+    fwrite(str,sizeof(char),strlen(str),file);
+}
+
+char *utf8_verify_copy(const char *str) {
+    /* When given a postscript string it SHOULD be in ASCII. But it will often*/
+    /* contain a copyright symbol (sometimes in latin1, sometimes in macroman)*/
+    /* unfortunately both encodings use 0xa9 for copyright so we can't distinguish */
+    /* guess that it's latin1 (or that copyright is the only odd char which */
+    /* means a latin1 conversion will work for macs too). */
+
+    if ( str==NULL )
+return( NULL );
+
+    if ( utf8_valid(str))
+return( copy(str));		/* Either in ASCII (good) or appears to be utf8*/
+return( latin1_2_utf8_copy(str));
+}
+
+/* Oh. If the encoding is symbol (platform=3, specific=0) then Windows won't */
+/*  accept the font unless the name table also has entries for (3,0). I'm not */
+/*  sure if this is the case for the CJK encodings (docs don't mention that) */
+/*  but let's do it just in case */
+void DefaultTTFEnglishNames(struct ttflangname *dummy, SplineFont *sf) {
+    time_t now;
+    struct tm *tm;
+    char buffer[200];
+
+    if ( dummy->names[ttf_copyright]==NULL || *dummy->names[ttf_copyright]=='\0' )
+	dummy->names[ttf_copyright] = utf8_verify_copy(sf->copyright);
+    if ( dummy->names[ttf_family]==NULL || *dummy->names[ttf_family]=='\0' )
+	dummy->names[ttf_family] = utf8_verify_copy(sf->familyname);
+    if ( dummy->names[ttf_subfamily]==NULL || *dummy->names[ttf_subfamily]=='\0' )
+	dummy->names[ttf_subfamily] = utf8_verify_copy(SFGetModifiers(sf));
+    if ( dummy->names[ttf_uniqueid]==NULL || *dummy->names[ttf_uniqueid]=='\0' ) {
+	time(&now);
+	tm = localtime(&now);
+	sprintf( buffer, "%s : %s : %d-%d-%d",
+		BDFFoundry?BDFFoundry:TTFFoundry?TTFFoundry:"FontForge 2.0",
+		sf->fullname!=NULL?sf->fullname:sf->fontname,
+		tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900 );
+	dummy->names[ttf_uniqueid] = copy(buffer);
+    }
+    if ( dummy->names[ttf_fullname]==NULL || *dummy->names[ttf_fullname]=='\0' )
+	dummy->names[ttf_fullname] = utf8_verify_copy(sf->fullname);
+    if ( dummy->names[ttf_version]==NULL || *dummy->names[ttf_version]=='\0' ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( buffer, "Version %f ", (double)sf->cidversion );
+	else if ( sf->version!=NULL )
+	    sprintf(buffer,"Version %.20s ", sf->version);
+	else
+	    strcpy(buffer,"Version 1.0" );
+	dummy->names[ttf_version] = copy(buffer);
+    }
+    if ( dummy->names[ttf_postscriptname]==NULL || *dummy->names[ttf_postscriptname]=='\0' )
+	dummy->names[ttf_postscriptname] = utf8_verify_copy(sf->fontname);
+}
+
+typedef struct {
+    uint16 platform;
+    uint16 specific;
+    uint16 lang;
+    uint16 strid;
+    uint16 len;
+    uint16 offset;
+} NameEntry;
+
+typedef struct {
+    FILE *strings;
+    int cur, max;
+    enum fontformat format;
+    Encoding *encoding_name;
+    NameEntry *entries;
+    int applemode;
+} NamTab;
+
+static int compare_entry(const void *_mn1, const void *_mn2) {
+    const NameEntry *mn1 = _mn1, *mn2 = _mn2;
+
+    if ( mn1->platform!=mn2->platform )
+return( mn1->platform - mn2->platform );
+    if ( mn1->specific!=mn2->specific )
+return( mn1->specific - mn2->specific );
+    if ( mn1->lang!=mn2->lang )
+return( mn1->lang - mn2->lang );
+
+return( mn1->strid-mn2->strid );
+}
+
+static void AddEncodedName(NamTab *nt,char *utf8name,uint16 lang,uint16 strid) {
+    NameEntry *ne;
+    int maclang, macenc= -1, specific;
+    char *macname = NULL;
+
+    if ( strid==ttf_postscriptname && lang!=0x409 )
+return;		/* Should not happen, but it did */
+
+    if ( nt->cur+6>=nt->max ) {
+	if ( nt->cur==0 )
+	    nt->entries = malloc((nt->max=100)*sizeof(NameEntry));
+	else
+	    nt->entries = realloc(nt->entries,(nt->max+=100)*sizeof(NameEntry));
+    }
+
+    ne = nt->entries + nt->cur;
+
+    ne->platform = 3;		/* Windows */
+    ne->specific = 1;		/* unicode */
+    ne->lang     = lang;
+    ne->strid    = strid;
+    ne->offset   = ftell(nt->strings);
+    ne->len      = 2*utf82u_strlen(utf8name);
+    dumpustr(nt->strings,utf8name);
+    ++ne;
+
+    if ( nt->format==ff_ttfsym ) {
+	*ne = ne[-1];
+	ne->specific = 0;	/* Windows "symbol" */
+	++ne;
+    }
+
+    maclang = WinLangToMac(lang);
+    if ( !nt->applemode && lang!=0x409 )
+	maclang = 0xffff;
+    if ( maclang!=0xffff ) {
+#ifdef FONTFORGE_CONFIG_APPLE_UNICODE_NAMES
+	if ( strid!=ttf_postscriptname ) {
+	    *ne = ne[-1];
+	    ne->platform = 0;	/* Mac unicode */
+	    ne->specific = 0;	/* 3 => Unicode 2.0 semantics */ /* 0 ("default") is also a reasonable value */
+	    ne->lang     = maclang;
+	    ++ne;
+	}
+#endif
+
+	macenc = MacEncFromMacLang(maclang);
+	macname = Utf8ToMacStr(utf8name,macenc,maclang);
+	if ( macname!=NULL ) {
+	    ne->platform = 1;		/* apple non-unicode encoding */
+	    ne->specific = macenc;	/* whatever */
+	    ne->lang     = maclang;
+	    ne->strid    = strid;
+	    ne->offset   = ftell(nt->strings);
+	    ne->len      = strlen(macname);
+	    dumpstr(nt->strings,macname);
+	    ++ne;
+	    free(macname);
+	}
+    }
+
+    specific =  nt->encoding_name->is_korean ? 5 :	/* Wansung, korean */
+		nt->encoding_name->is_japanese ? 2 :	/* SJIS */
+		nt->encoding_name->is_simplechinese ? 3 :/* packed gb2312, don't know the real name */
+		strmatch(nt->encoding_name->enc_name,"EUC-GB12345")==0 ? 3 :/* Lie */
+		nt->encoding_name->is_tradchinese ? 4 :	/* Big5, traditional Chinese */
+			-1;
+    if ( specific != -1 ) {
+	ne->platform = 3;		/* windows */
+	ne->specific = specific;	/* whatever */
+	ne->lang     = lang;
+	ne->strid    = strid;
+	if ( macname!=NULL &&
+		(( specific== 2 && macenc==1 ) ||	/* Japanese */
+		 ( specific== 3 && macenc==25 ) ||	/* simplified chinese */
+		 ( specific== 4 && macenc==2 ) ||	/* traditional chinese */
+		 ( specific== 5 && macenc==3 )) ) {	/* wansung korean */
+	    ne->offset = ne[-1].offset;
+	    ne->len    = ne[-1].len;
+	} else {
+	    char *space, *out;
+            const char *encname;
+	    ICONV_CONST char *in;
+	    Encoding *enc;
+	    size_t inlen, outlen;
+	    ne->offset  = ftell(nt->strings);
+	    encname     = nt->encoding_name->is_japanese ? "SJIS" :
+			strmatch(nt->encoding_name->enc_name,"JOHAB")==0 ? "JOHAB" :
+			nt->encoding_name->is_korean ? "EUC-KR" :
+			nt->encoding_name->is_simplechinese ? "EUC-CN" :
+			    nt->encoding_name->enc_name;
+	    enc = FindOrMakeEncoding(encname);
+	    if ( enc==NULL )
+		--ne;
+	    else {
+		unichar_t *uin = utf82u_copy(utf8name);
+		outlen = 3*strlen(utf8name)+10;
+		out = space = malloc(outlen+2);
+		in = (char *) uin; inlen = 2*u_strlen(uin);
+		iconv(enc->fromunicode,NULL,NULL,NULL,NULL);	/* should not be needed, but just in case */
+		iconv(enc->fromunicode,&in,&inlen,&out,&outlen);
+		out[0] = '\0'; out[1] = '\0';
+		ne->offset = ftell(nt->strings);
+		ne->len    = strlen(space);
+		dumpstr(nt->strings,space);
+		free(space); free(uin);
+	    }
+	}
+	++ne;
+    }
+    nt->cur = ne - nt->entries;
+}
+
+static void AddMacName(NamTab *nt,struct macname *mn, int strid) {
+    NameEntry *ne;
+
+    if ( nt->cur+1>=nt->max ) {
+	if ( nt->cur==0 )
+	    nt->entries = malloc((nt->max=100)*sizeof(NameEntry));
+	else
+	    nt->entries = realloc(nt->entries,(nt->max+=100)*sizeof(NameEntry));
+    }
+
+    ne = nt->entries + nt->cur;
+
+    ne->platform = 1;		/* apple non-unicode encoding */
+    ne->specific = mn->enc;	/* whatever */
+    ne->lang     = mn->lang;
+    ne->strid    = strid;
+    ne->offset   = ftell(nt->strings);
+    ne->len      = strlen(mn->name);
+    dumpstr(nt->strings,mn->name);
+
+    ++nt->cur;
+}
+
+/* There's an inconsistancy here. Apple's docs say there most be only one */
+/*  nameid==6 and that name must be ascii (presumably plat=1, spec=0, lang=0) */
+/* The opentype docs say there must be two (psl=1,0,0 & psl=3,1,0x409) any */
+/*  others are to be ignored */
+/* A representative from Apple says they will change their spec to accept */
+/*  the opentype version, and tells me that they don't currently care */
+/* So ignore this */
+/* Undocumented fact: Windows insists on having a UniqueID string 3,1 */
+static void dumpnames(struct alltabs *at, SplineFont *sf,enum fontformat format) {
+    int i,j;
+    struct ttflangname dummy, *cur, *useng = NULL;
+    struct macname *mn;
+    struct other_names *on, *onn;
+    NamTab nt;
+    struct otfname *otfn;
+    struct otffeatname *fn;
+
+    memset(&nt,0,sizeof(nt));
+    nt.encoding_name = at->map->enc;
+    nt.format	     = format;
+    nt.applemode     = at->applemode;
+    nt.strings	     = tmpfile();
+    if (( format>=ff_ttf && format<=ff_otfdfont) && (at->gi.flags&ttf_flag_symbol))
+	nt.format    = ff_ttfsym;
+
+    memset(&dummy,0,sizeof(dummy));
+    for ( cur=sf->names; cur!=NULL; cur=cur->next ) {
+	if ( cur->lang==0x409 ) {
+	    dummy = *cur;
+	    useng = cur;
+    break;
+	}
+    }
+    DefaultTTFEnglishNames(&dummy, sf);
+
+    for ( i=0; i<ttf_namemax; ++i ) if ( dummy.names[i]!=NULL )
+	AddEncodedName(&nt,dummy.names[i],0x409,i);
+    for ( cur=sf->names; cur!=NULL; cur=cur->next ) {
+	if ( cur->lang!=0x409 )
+	    for ( i=0; i<ttf_namemax; ++i )
+		if ( cur->names[i]!=NULL )
+		    AddEncodedName(&nt,cur->names[i],cur->lang,i);
+    }
+
+    /* The examples I've seen of the feature table only contain platform==mac */
+    /*  so I'm not including apple unicode */
+    if ( at->feat_name!=NULL ) {
+	for ( i=0; at->feat_name[i].strid!=0; ++i ) {
+	    for ( mn=at->feat_name[i].mn; mn!=NULL; mn=mn->next )
+		AddMacName(&nt,mn,at->feat_name[i].strid);
+	/* I'm not sure why I keep track of these alternates (feat_name[i].smn) */
+	/*  Dumping them out is a bad idea. It might be worth */
+	/*  something if we searched through the alternate sets for languages */
+	/*  not found in the main set, but at the moment I don't think so */
+	/*  What happens now if I do it is that I get duplicate names output. */
+	}
+    }
+    /* And the names used by the fvar table aren't mac unicode either */
+    for ( on = at->other_names; on!=NULL; on=onn ) {
+	for ( mn = on->mn; mn!=NULL ; mn = mn->next )
+	    AddMacName(&nt,mn,on->strid);
+	onn = on->next;
+	chunkfree(on,sizeof(*on));
+    }
+    /* Wow, the GPOS 'size' feature uses the name table in a very mac-like way*/
+    if ( at->fontstyle_name_strid!=0 && sf->fontstyle_name!=NULL ) {
+	for ( otfn = sf->fontstyle_name; otfn!=NULL; otfn = otfn->next )
+	    AddEncodedName(&nt,otfn->name,otfn->lang,at->fontstyle_name_strid);
+    }
+    /* As do some other features now */
+    for ( fn = sf->feat_names; fn!=NULL; fn=fn->next ) {
+	for ( otfn = fn->names; otfn!=NULL; otfn = otfn->next )
+	    AddEncodedName(&nt,otfn->name,otfn->lang,fn->nid);
+    }
+
+    qsort(nt.entries,nt.cur,sizeof(NameEntry),compare_entry);
+
+    at->name = tmpfile();
+    putshort(at->name,0);				/* format */
+    putshort(at->name,nt.cur);				/* numrec */
+    putshort(at->name,(3+nt.cur*6)*sizeof(int16));	/* offset to strings */
+
+    for ( i=0; i<nt.cur; ++i ) {
+	putshort(at->name,nt.entries[i].platform);
+	putshort(at->name,nt.entries[i].specific);
+	putshort(at->name,nt.entries[i].lang);
+	putshort(at->name,nt.entries[i].strid);
+	putshort(at->name,nt.entries[i].len);
+	putshort(at->name,nt.entries[i].offset);
+    }
+    if ( !ttfcopyfile(at->name,nt.strings,(3+nt.cur*6)*sizeof(int16),"name-data"))
+	at->error = true;
+
+    at->namelen = ftell(at->name);
+    if ( (at->namelen&3)!=0 )
+	for ( j= 4-(at->namelen&3); j>0; --j )
+	    putc('\0',at->name);
+
+    for ( i=0; i<ttf_namemax; ++i )
+	if ( useng==NULL || dummy.names[i]!=useng->names[i] )
+	    free( dummy.names[i]);
+    free( nt.entries );
+    free( at->feat_name );
+
+    /* Windows at one point refused to load fonts with 'name' tables bigger than 5K (decided they were insecure). */
+}
+
+static void dumppost(struct alltabs *at, SplineFont *sf, enum fontformat format) {
+    int pos, i,j, shouldbe;
+    int shorttable = (format==ff_otf || format==ff_otfcid ||
+	    (at->gi.flags&ttf_flag_shortps));
+    uint32 here;
+
+    at->post = tmpfile();
+
+    putlong(at->post,shorttable?0x00030000:0x00020000);	/* formattype */
+    putfixed(at->post,sf->italicangle);
+    putshort(at->post,sf->upos-sf->uwidth/2);		/* 'post' defn says top of rect, while FontInfo def says center of rect */
+    putshort(at->post,sf->uwidth);
+    putlong(at->post,at->isfixed);
+    putlong(at->post,0);		/* no idea about memory */
+    putlong(at->post,0);		/* no idea about memory */
+    putlong(at->post,0);		/* no idea about memory */
+    putlong(at->post,0);		/* no idea about memory */
+    if ( !shorttable ) {
+	here = ftell(at->post);
+	putshort(at->post,at->maxp.numGlyphs);
+
+	shouldbe = 0;
+	for ( i=0, pos=0; i<at->maxp.numGlyphs; ++i ) {
+	    if ( at->gi.bygid[i]!=-1 && sf->glyphs[at->gi.bygid[i]]!=NULL ) {
+		SplineChar *sc = sf->glyphs[at->gi.bygid[i]];
+		while ( i>shouldbe ) {
+		    if ( shouldbe==0 )
+			putshort(at->post,0);		/* glyph 0 is named .notdef */
+		    else if ( shouldbe==1 )
+			putshort(at->post,1);		/* glyphs 1&2 are .null and cr */
+		    else if ( shouldbe==2 )
+			putshort(at->post,2);		/* or something */
+		    else
+			putshort(at->post,0);
+		    ++shouldbe;
+		}
+		if ( strcmp(sc->name,".notdef")==0 )
+		    putshort(at->post,0);
+		else {
+		    for ( j=0; j<258; ++j )
+			if ( strcmp(sc->name,ttfstandardnames[j])==0 )
+		    break;
+		    if ( j!=258 )
+			putshort(at->post,j);
+		    else {
+			putshort(at->post,pos+258);
+			++pos;
+		    }
+		}
+		++shouldbe;
+	    }
+	}
+
+	if ( shouldbe!=at->maxp.numGlyphs ) {
+	    fseek(at->post,here,SEEK_SET);
+	    putshort(at->post,shouldbe);
+	    fseek(at->post,0,SEEK_END);
+	}
+	if ( pos!=0 ) {
+	    for ( i=0; i<at->maxp.numGlyphs; ++i ) if ( at->gi.bygid[i]!=-1 ) {
+		SplineChar *sc = sf->glyphs[at->gi.bygid[i]];
+		if ( strcmp(sc->name,".notdef")==0 )
+		    /* Do Nothing */;
+		else {
+		    for ( j=0; j<258; ++j )
+			if ( strcmp(sc->name,ttfstandardnames[j])==0 )
+		    break;
+		    if ( j!=258 )
+			/* Do Nothing */;
+		    else
+			dumppstr(at->post,sc->name);
+		}
+	    }
+	}
+    }
+    at->postlen = ftell(at->post);
+    if ( (at->postlen&3)!=0 )
+	for ( j= 4-(at->postlen&3); j>0; --j )
+	    putc('\0',at->post);
+}
+
+static FILE *_Gen816Enc(SplineFont *sf,int *tlen,EncMap *map) {
+    int i, j, complained, pos, k, subheadindex, jj, isbig5=false;
+    uint16 table[256];
+    struct subhead subheads[128];
+    uint16 *glyphs;
+    uint16 tempglyphs[256];
+    int base, lbase, basebound, subheadcnt, planesize, plane0size;
+    int base2, base2bound;
+    FILE *sub;
+    const char *encname = map->enc->iconv_name!=NULL ? map->enc->iconv_name : map->enc->enc_name;
+
+    *tlen = 0;
+
+    base2 = -1; base2bound = -2;
+    if ( map->enc->is_tradchinese && strstrmatch(encname,"hkscs")!=NULL ) {
+	base = 0x81;
+	basebound = 0xfe;
+	subheadcnt = basebound-base+1;
+	lbase = 0x40;
+	planesize = 191;
+    } else if ( map->enc->is_tradchinese || sf->uni_interp==ui_trad_chinese) {
+	base = 0xa1;
+	basebound = 0xf9;	/* wcl-02.ttf's cmap claims to go up to fc, but everything after f9 is invalid (according to what I know of big5, f9 should be the end) */
+	subheadcnt = basebound-base+1;
+	lbase = 0x40;
+	planesize = 191;
+	isbig5 = true;
+    } else if ( strstrmatch(encname,"euc")!=NULL ) {
+	base = 0xa1;
+	basebound = 0xfd;
+	lbase = 0xa1;
+	subheadcnt = basebound-base+1;
+	planesize = 0xfe - lbase +1;
+    } else if ( strstrmatch(encname,"johab")!=NULL ) {
+	base = 0x84;
+	basebound = 0xf9;
+	lbase = 0x31;
+	subheadcnt = basebound-base+1;
+	planesize = 0xfe -0x31+1;	/* Stupid gcc bug, thinks 0xfe- is ambiguous (exponant) */
+    } else if ( strstrmatch(encname,"sjis")!=NULL  || strstrmatch(encname,"cp932")!=NULL ) {
+	base = 129;
+	basebound = 159;
+	lbase = 64;
+	planesize = 252 - lbase +1;
+	base2 = 0xe0;
+	/* SJIS supports "user defined characters" between 0xf040 and 0xfcfc */
+	/*  there probably won't be any, but allow space for them if there are*/
+	for ( base2bound=0xfc00; base2bound>0xefff; --base2bound )
+	    if ( base2bound<map->enccount && map->map[base2bound]!=-1 &&
+		    SCWorthOutputting(sf->glyphs[map->map[base2bound]]))
+	break;
+	base2bound >>= 8;
+	subheadcnt = basebound-base + 1 + base2bound-base2 + 1;
+    } else {
+	IError( "Unsupported 8/16 encoding %s\n", map->enc->enc_name );
+return( NULL );
+    }
+    plane0size = base2==-1? base : base2;
+    i=0;
+    if ( base2!=-1 ) {
+	for ( i=basebound; i<base2 && i<map->enccount; ++i )
+	    if ( map->map[i]==-1 )
+	continue;
+	    else if ( SCWorthOutputting(sf->glyphs[map->map[i]]))
+	break;
+	if ( i==base2 || i==map->enccount )
+	    i = 0;
+    }
+    if ( i==0 ) {
+	for ( i=0; i<base && i<map->enccount; ++i )
+	    if ( map->map[i]==-1 )
+	continue;
+	    else if ( SCWorthOutputting(sf->glyphs[map->map[i]]))
+	break;
+    }
+    if ( base2!=-1 ) {
+	for ( i=base; i<=basebound && i<map->enccount; ++i )
+	    if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]])) {
+		ff_post_error(_("Bad Encoding"),_("There is a single byte character (%d) using one of the slots needed for double byte characters"),i);
+	break;
+	    }
+	if ( i==basebound+1 )
+	    for ( i=base2; i<256 && i<map->enccount; ++i )
+		if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]])) {
+		    ff_post_error(_("Bad Encoding"),_("There is a single byte character (%d) using one of the slots needed for double byte characters"),i);
+	    break;
+		}
+    } else {
+	for ( i=base; i<=256 && i<map->enccount; ++i )
+	    if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]])) {
+		ff_post_error(_("Bad Encoding"),_("There is a single byte character (%d) using one of the slots needed for double byte characters"),i);
+	break;
+	    }
+    }
+    for ( i=256; i<(base<<8) && i<map->enccount; ++i )
+	if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]])) {
+	    ff_post_error(_("Bad Encoding"),_("There is a character (%d) which cannot be encoded"),i);
+    break;
+	}
+    if ( i==(base<<8) && base2==-1 )
+	for ( i=((basebound+1)<<8); i<0x10000 && i<map->enccount; ++i )
+	    if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]])) {
+		ff_post_error(_("Bad Encoding"),_("There is a character (%d) which cannot be encoded"),i);
+	break;
+	    }
+
+    memset(table,'\0',sizeof(table));
+    for ( i=base; i<=basebound; ++i )
+	table[i] = 8*(i-base+1);
+    for ( i=base2; i<=base2bound; ++i )
+	table[i] = 8*(i-base2+basebound-base+1+1);
+    memset(subheads,'\0',sizeof(subheads));
+    subheads[0].first = 0; subheads[0].cnt = plane0size;
+    for ( i=1; i<subheadcnt+1; ++i ) {
+	subheads[i].first = lbase;
+	subheads[i].cnt = planesize;
+    }
+    glyphs = calloc(subheadcnt*planesize+plane0size,sizeof(uint16));
+    subheads[0].rangeoff = 0;
+    for ( i=0; i<plane0size && i<map->enccount; ++i )
+	if ( map->map[i]!=-1 && sf->glyphs[map->map[i]]!=NULL &&
+		sf->glyphs[map->map[i]]->ttf_glyph!=-1 )
+	    glyphs[i] = sf->glyphs[map->map[i]]->ttf_glyph;
+
+    pos = 1;
+
+    complained = false;
+    subheadindex = 1;
+    for ( jj=0; jj<2 || (base2==-1 && jj<1); ++jj )
+	    for ( j=((jj==0?base:base2)<<8); j<=((jj==0?basebound:base2bound)<<8); j+= 0x100 ) {
+	for ( i=0; i<lbase; ++i )
+	    if ( !complained && map->map[i+j]!=-1 &&
+		    SCWorthOutputting(sf->glyphs[map->map[i+j]])) {
+		ff_post_error(_("Bad Encoding"),_("There is a character (%d) which is not normally in the encoding"),i+j);
+		complained = true;
+	    }
+	if ( isbig5 ) {
+	    /* big5 has a gap here. Does johab? */
+	    for ( i=0x7f; i<0xa1; ++i )
+		if ( !complained && map->map[i+j]!=-1 &&
+			SCWorthOutputting(sf->glyphs[map->map[i+j]])) {
+		    ff_post_error(_("Bad Encoding"),_("There is a character (%d) which is not normally in the encoding"),i+j);
+		    complained = true;
+		}
+	}
+	memset(tempglyphs,0,sizeof(tempglyphs));
+	for ( i=0; i<planesize; ++i )
+	    if ( map->map[j+lbase+i]!=-1 && sf->glyphs[map->map[j+lbase+i]]!=NULL &&
+		    sf->glyphs[map->map[j+lbase+i]]->ttf_glyph!=-1 )
+		tempglyphs[i] = sf->glyphs[map->map[j+lbase+i]]->ttf_glyph;
+	for ( i=1; i<pos; ++i ) {
+	    int delta = 0;
+	    for ( k=0; k<planesize; ++k )
+		if ( tempglyphs[k]==0 && glyphs[plane0size+(i-1)*planesize+k]==0 )
+		    /* Still matches */;
+		else if ( tempglyphs[k]==0 || glyphs[plane0size+(i-1)*planesize+k]==0 )
+	    break;  /* Doesn't match */
+		else if ( delta==0 )
+		    delta = (uint16) (tempglyphs[k]-glyphs[plane0size+(i-1)*planesize+k]);
+		else if ( tempglyphs[k]==(uint16) (glyphs[plane0size+(i-1)*planesize+k]+delta) )
+		    /* Still matches */;
+		else
+	    break;
+	    if ( k==planesize ) {
+		subheads[subheadindex].delta = delta;
+		subheads[subheadindex].rangeoff = plane0size+(i-1)*planesize;
+	break;
+	    }
+	}
+	if ( subheads[subheadindex].rangeoff==0 ) {
+	    memcpy(glyphs+(pos-1)*planesize+plane0size,tempglyphs,planesize*sizeof(uint16));
+	    subheads[subheadindex].rangeoff = plane0size+(pos++-1)*planesize ;
+	}
+	++subheadindex;
+    }
+
+    /* fixup offsets */
+    /* my rangeoffsets are indexes into the glyph array. That's nice and */
+    /*  simple. Unfortunately ttf says they are offsets from the current */
+    /*  location in the file (sort of) so we now fix them up. */
+    for ( i=0; i<subheadcnt+1; ++i )
+	subheads[i].rangeoff = subheads[i].rangeoff*sizeof(uint16) +
+		(subheadcnt-i)*sizeof(struct subhead) + sizeof(uint16);
+
+    sub = tmpfile();
+    if ( sub==NULL )
+return( NULL );
+
+    putshort(sub,2);		/* 8/16 format */
+    putshort(sub,0);		/* Subtable length, we'll come back and fix this */
+    putshort(sub,0);		/* version/language, not meaningful in ms systems */
+    for ( i=0; i<256; ++i )
+	putshort(sub,table[i]);
+    for ( i=0; i<subheadcnt+1; ++i ) {
+	putshort(sub,subheads[i].first);
+	putshort(sub,subheads[i].cnt);
+	putshort(sub,subheads[i].delta);
+	putshort(sub,subheads[i].rangeoff);
+    }
+    for ( i=0; i<(pos-1)*planesize+plane0size; ++i )
+	putshort(sub,glyphs[i]);
+    free(glyphs);
+
+    *tlen = ftell(sub);
+    fseek(sub,2,SEEK_SET);
+    putshort(sub,*tlen);	/* Length, I said we'd come back to it */
+    rewind( sub );
+return( sub );
+}
+
+static FILE *Needs816Enc(SplineFont *sf,int *tlen,EncMap *map, FILE **apple, int *appletlen) {
+    FILE *sub;
+    const char *encname = map->enc->iconv_name!=NULL ? map->enc->iconv_name : map->enc->enc_name;
+    EncMap *oldmap = map;
+    EncMap *applemap = NULL;
+    Encoding *enc;
+
+    *tlen = 0;
+    if ( apple!=NULL ) {
+	*apple = NULL;
+	*appletlen = 0;
+    }
+    if ( sf->cidmaster!=NULL || sf->subfontcnt!=0 )
+return( NULL );
+    if ( (strstrmatch(encname,"big")!=NULL && strchr(encname,'5')!=NULL) ||
+	    strstrmatch(encname,"johab")!=NULL ||
+	    strstrmatch(encname,"sjis")!=NULL ||
+	    strstrmatch(encname,"cp932")!=NULL ||
+	    strstrmatch(encname,"euc-kr")!=NULL ||
+	    strstrmatch(encname,"euc-cn")!=NULL )
+	/* Already properly encoded */;
+    else if ( strstrmatch(encname,"2022")!=NULL &&
+	    (strstrmatch(encname,"JP2")!=NULL ||
+	     strstrmatch(encname,"JP-2")!=NULL ||
+	     strstrmatch(encname,"JP-3")!=NULL ))
+return( NULL );		/* No 8/16 encoding for JP2 nor JP3 */
+    else if ( sf->uni_interp>=ui_japanese && sf->uni_interp<=ui_korean ) {
+	enc = FindOrMakeEncoding(
+		sf->uni_interp==ui_japanese ? "sjis" :
+		sf->uni_interp==ui_trad_chinese ? "big5" :
+		sf->uni_interp==ui_simp_chinese ? "euc-cn" :
+		    "euc-kr");
+	if ( map->enc!=enc ) {
+	    map = EncMapFromEncoding(sf,enc);
+	    encname = map->enc->iconv_name!=NULL ? map->enc->iconv_name : map->enc->enc_name;
+	}
+    } else
+return( NULL );
+
+    /* Both MS and Apple extend sjis. I don't know how to get iconv to give me*/
+    /*  apple's encoding though. So I generate one 8/16 table for MS based on */
+    /*  their extension (cp932), and another table based on plain sjis for Apple*/
+    /* Don't know if this is true of other cjk encodings... for the moment I */
+    /*  will just use standard encodings for them */
+    if ( strstrmatch(encname,"sjis")!=NULL ) {
+	enc = FindOrMakeEncoding("cp932");
+	if ( enc!=NULL ) {
+	    applemap = map;
+	    map = EncMapFromEncoding(sf,enc);
+	}
+    } else if ( strstrmatch(encname,"cp932")!=NULL )
+	applemap = EncMapFromEncoding(sf,FindOrMakeEncoding("sjis"));
+
+    if ( applemap!=NULL )
+	*apple = _Gen816Enc(sf,appletlen,applemap);
+    sub = _Gen816Enc(sf,tlen,map);
+
+    if ( applemap!=NULL && applemap!=oldmap )
+	EncMapFree(applemap);
+    if ( map!=oldmap )
+	EncMapFree(map);
+return( sub );
+}
+
+static FILE *NeedsUCS4Table(SplineFont *sf,int *ucs4len,EncMap *map) {
+    int i=0,j,group;
+    FILE *format12;
+    SplineChar *sc;
+    EncMap *freeme = NULL;
+    struct altuni *altuni;
+
+    if ( map->enc->is_unicodefull )
+	i=0x10000;
+    else if ( map->enc->is_custom )
+	i = 0;
+    else
+	i = map->enc->char_cnt;
+    for ( ; i<map->enccount; ++i ) {
+	if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]]) ) {
+	    if ( sf->glyphs[map->map[i]]->unicodeenc>=0x10000 )
+    break;
+	    for ( altuni=sf->glyphs[map->map[i]]->altuni; altuni!=NULL && (altuni->unienc<0x10000 || altuni->vs!=-1 || altuni->fid!=0);
+		    altuni=altuni->next );
+	    if ( altuni!=NULL )
+    break;
+	}
+    }
+
+    if ( i>=map->enccount )
+return(NULL);
+
+    if ( !map->enc->is_unicodefull )
+	map = freeme = EncMapFromEncoding(sf,FindOrMakeEncoding("ucs4"));
+
+    format12 = tmpfile();
+    if ( format12==NULL )
+return( NULL );
+
+    putshort(format12,12);		/* Subtable format */
+    putshort(format12,0);		/* padding */
+    putlong(format12,0);		/* Length, we'll come back to this */
+    putlong(format12,0);		/* language */
+    putlong(format12,0);		/* Number of groups, we'll come back to this */
+
+    group = 0;
+    for ( i=0; i<map->enccount; ++i ) if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]]) && sf->glyphs[map->map[i]]->unicodeenc!=-1 ) {
+	sc = sf->glyphs[map->map[i]];
+	for ( j=i+1; j<map->enccount && map->map[j]!=-1 &&
+		SCWorthOutputting(sf->glyphs[map->map[j]]) &&
+		sf->glyphs[map->map[j]]->unicodeenc!=-1 &&
+		sf->glyphs[map->map[j]]->ttf_glyph==sc->ttf_glyph+j-i; ++j );
+	--j;
+	putlong(format12,i);		/* start char code */
+	putlong(format12,j);		/* end char code */
+	putlong(format12,sc->ttf_glyph);
+	++group;
+	i=j;				/* move to the start of the next group */
+    }
+    *ucs4len = ftell(format12);
+    fseek(format12,4,SEEK_SET);
+    putlong(format12,*ucs4len);		/* Length, I said we'd come back to it */
+    putlong(format12,0);		/* language */
+    putlong(format12,group);		/* Number of groups */
+    rewind( format12 );
+
+    if ( freeme!=NULL )
+	EncMapFree(freeme);
+return( format12 );
+}
+
+static FILE *NeedsUCS2Table(SplineFont *sf,int *ucs2len,EncMap *map,int issymbol) {
+    /* We always want a format 4 2byte unicode encoding map */
+    /* But if it's symbol, only include encodings 0xff20 - 0xffff */
+    uint32 *avail = malloc(65536*sizeof(uint32));
+    int i,j,l;
+    int segcnt, cnt=0, delta, rpos;
+    struct cmapseg { uint16 start, end; uint16 delta; uint16 rangeoff; } *cmapseg;
+    uint16 *ranges;
+    SplineChar *sc;
+    FILE *format4 = tmpfile();
+
+    memset(avail,0xff,65536*sizeof(uint32));
+    if ( map->enc->is_unicodebmp || map->enc->is_unicodefull ) { int gid;
+	for ( i=0; i<65536 && i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL && sf->glyphs[gid]->ttf_glyph!=-1 ) {
+	    avail[i] = gid;
+	    ++cnt;
+	}
+    } else {
+	struct altuni *altuni;
+	for ( i=0; i<sf->glyphcnt; ++i ) {
+	    if ( (sc=sf->glyphs[i])!=NULL && sc->ttf_glyph!=-1 ) {
+		if ( sc->unicodeenc>=0 && sc->unicodeenc<=0xffff ) {
+		    avail[sc->unicodeenc] = i;
+		    ++cnt;
+		}
+		for ( altuni=sc->altuni; altuni!=NULL; altuni = altuni->next ) {
+		    if ( altuni->unienc<=0xffff && altuni->vs==-1 && altuni->fid==0 ) {
+			avail[altuni->unienc] = i;
+			++cnt;
+		    }
+		}
+	    }
+	}
+    }
+    if ( issymbol ) {
+	/* Clear out all entries we don't want */
+	memset(avail       ,0xff,0xf020*sizeof(uint32));
+	memset(avail+0xf100,0xff,0x0eff*sizeof(uint32));
+    }
+
+    j = -1;
+    for ( i=segcnt=0; i<65536; ++i ) {
+	if ( avail[i]!=0xffffffff && j==-1 ) {
+	    j=i;
+	    ++segcnt;
+	} else if ( j!=-1 && avail[i]==0xffffffff )
+	    j = -1;
+    }
+    cmapseg = calloc(segcnt+1,sizeof(struct cmapseg));
+    ranges = malloc(cnt*sizeof(int16));
+    j = -1;
+    for ( i=segcnt=0; i<65536; ++i ) {
+	if ( avail[i]!=0xffffffff && j==-1 ) {
+	    j=i;
+	    cmapseg[segcnt].start = j;
+	    ++segcnt;
+	} else if ( j!=-1 && avail[i]==0xffffffff ) {
+	    cmapseg[segcnt-1].end = i-1;
+	    j = -1;
+	}
+    }
+    if ( j!=-1 )
+	cmapseg[segcnt-1].end = i-1;
+    /* create a dummy segment to mark the end of the table */
+    cmapseg[segcnt].start = cmapseg[segcnt].end = 0xffff;
+    cmapseg[segcnt++].delta = 1;
+    rpos = 0;
+    for ( i=0; i<segcnt-1; ++i ) {
+	l = avail[cmapseg[i].start];
+	sc = sf->glyphs[l];
+	delta = sc->ttf_glyph-cmapseg[i].start;
+	for ( j=cmapseg[i].start; j<=cmapseg[i].end; ++j ) {
+	    l = avail[j];
+	    sc = sf->glyphs[l];
+	    if ( delta != sc->ttf_glyph-j )
+	break;
+	}
+	if ( j>cmapseg[i].end )
+	    cmapseg[i].delta = delta;
+	else {
+	    cmapseg[i].rangeoff = (rpos + (segcnt-i)) * sizeof(int16);
+	    for ( j=cmapseg[i].start; j<=cmapseg[i].end; ++j ) {
+		l = avail[j];
+		sc = sf->glyphs[l];
+		ranges[rpos++] = sc->ttf_glyph;
+	    }
+	}
+    }
+    free(avail);
+
+
+    putshort(format4,4);		/* format */
+    putshort(format4,(8+4*segcnt+rpos)*sizeof(int16));
+    putshort(format4,0);		/* language/version */
+    putshort(format4,2*segcnt);	/* segcnt */
+    for ( j=0,i=1; i<=segcnt; i<<=1, ++j );
+    putshort(format4,i);		/* 2*2^floor(log2(segcnt)) */
+    putshort(format4,j-1);
+    putshort(format4,2*segcnt-i);
+    for ( i=0; i<segcnt; ++i )
+	putshort(format4,cmapseg[i].end);
+    putshort(format4,0);
+    for ( i=0; i<segcnt; ++i )
+	putshort(format4,cmapseg[i].start);
+    for ( i=0; i<segcnt; ++i )
+	putshort(format4,cmapseg[i].delta);
+    for ( i=0; i<segcnt; ++i )
+	putshort(format4,cmapseg[i].rangeoff);
+    for ( i=0; i<rpos; ++i )
+	putshort(format4,ranges[i]);
+    free(ranges);
+    free(cmapseg);
+    *ucs2len = ftell(format4);
+return( format4 );
+}
+
+static FILE *NeedsVariationSequenceTable(SplineFont *sf,int *vslen) {
+    /* Do we need a format 14 (unicode variation sequence) subtable? */
+    int gid, vs_cnt=0, vs_max=512, i, j, k, cnt, mingid, maxgid;
+    struct altuni *altuni, *au;
+    int32 vsbuf[512], *vses = vsbuf;
+    FILE *format14;
+    uint32 *avail = NULL;
+    enum vs_type {vs_default=(1<<24), vs_nondefault=(2<<24) };
+    SplineChar *sc;
+    uint32 here;
+    int any;
+
+    mingid = maxgid = -1;
+    for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
+	for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next ) {
+	    if ( altuni->unienc!=-1 && (uint32)altuni->unienc<unicode4_size &&
+		    altuni->vs!=-1 && altuni->fid==0 ) {
+		for ( i=0; i<vs_cnt; ++i )
+		    if ( vses[i]==altuni->vs )
+		break;
+		if ( i>=vs_cnt ) {
+		    if ( i>=vs_max ) {
+			if ( vses==vsbuf ) {
+			    vses = malloc((vs_max*=2)*sizeof(uint32));
+			    memcpy(vses,vsbuf,sizeof(vsbuf));
+			} else
+			    vses = realloc(vses,(vs_max+=512)*sizeof(uint32));
+		    }
+		    vses[vs_cnt++] = altuni->vs;
+		}
+		if ( mingid==-1 )
+		    mingid = maxgid = gid;
+		else
+		    maxgid = gid;
+	    }
+	}
+    }
+    if ( vs_cnt==0 ) {
+	*vslen = 0;
+return( NULL );			/* No variation selectors */
+    }
+
+    /* Sort the variation selectors */
+    for ( i=0; i<vs_cnt; ++i ) for ( j=i+1; j<vs_cnt; ++j ) {
+	if ( vses[i]>vses[j] ) {
+	    int temp = vses[i];
+	    vses[i] = vses[j];
+	    vses[j] = temp;
+	}
+    }
+
+    avail = malloc(unicode4_size*sizeof(uint32));
+
+    format14 = tmpfile();
+    putshort(format14,14);
+    putlong(format14,0);		/* Length, fixup later */
+    putlong(format14,vs_cnt);		/* number of selectors */
+
+    /* Variation selector records */
+    for ( i=0; i<vs_cnt; ++i ) {
+	putu24(format14,vses[i]);
+	putlong(format14,0);
+	putlong(format14,0);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	memset(avail,0,unicode4_size*sizeof(uint32));
+	any = 0;
+	for ( gid=mingid; gid<=maxgid; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next ) {
+		if ( altuni->unienc!=-1 && altuni->unienc < (int)unicode4_size &&
+			altuni->vs==vses[i] && altuni->fid==0 ) {
+		    for ( au=sc->altuni; au!=NULL; au=au->next )
+			if ( au->unienc==altuni->unienc && au->vs==-1 && au->fid==0 )
+		    break;
+		    if ( altuni->unienc==sc->unicodeenc || au!=NULL ) {
+			avail[altuni->unienc] = gid | vs_default;
+			any |= vs_default;
+		    } else {
+			avail[altuni->unienc] = gid | vs_nondefault;
+			any |= vs_nondefault;
+		    }
+		}
+	    }
+	}
+	if ( any&vs_default ) {
+	    here = ftell(format14);
+	    fseek(format14,10+ i*11 + 3, SEEK_SET);	/* Seek to defaultUVSOffset */
+	    putlong(format14,here);
+	    fseek(format14,0,SEEK_END);
+	    cnt = 0;
+	    for ( j=0; (unsigned)j<unicode4_size; ++j ) if ( avail[j]&vs_default ) {
+                for ( k=j+1; (unsigned)k<unicode4_size && (avail[k]&vs_default); ++k );
+		if ( k-j>256 ) k=j+256;	/* Each range is limited to 255 code points, as the count is a byte */
+		++cnt;
+		j = k-1;
+	    }
+	    putlong(format14,cnt);
+	    for ( j=0; (unsigned)j<unicode4_size; ++j ) if ( avail[j]&vs_default ) {
+                for ( k=j+1; (unsigned)k<unicode4_size && (avail[k]&vs_default); ++k );
+		if ( k-j>256 ) k=j+256;
+		putu24(format14,j);
+		putc(k-j-1,format14);
+		j = k-1;
+	    }
+	}
+	if ( any&vs_nondefault ) {
+	    here = ftell(format14);
+	    fseek(format14,10+ i*11 + 7, SEEK_SET);	/* Seek to nonDefaultUVSOffset */
+	    putlong(format14,here);
+	    fseek(format14,0,SEEK_END);
+	    cnt = 0;
+	    for ( j=0; (unsigned)j<unicode4_size; ++j ) if ( avail[j]&vs_nondefault )
+		++cnt;
+	    putlong(format14,cnt);
+	    for ( j=0; (unsigned)j<unicode4_size; ++j ) if ( avail[j]&vs_nondefault ) {
+		putu24(format14,j);
+		putshort(format14,sf->glyphs[avail[j]&0xffff]->ttf_glyph);
+	    }
+	}
+    }
+
+    here = ftell(format14);
+    fseek(format14,2,SEEK_SET);
+    putlong(format14,here);
+    fseek(format14,0,SEEK_END);
+    if ( here&1 ) {
+	putc('\0',format14);
+	++here;
+    }
+    if ( here&2 ) {
+	putshort(format14,0);
+	here += 2;
+    }
+    *vslen = here;
+
+    free(avail);
+    if ( vses!=vsbuf )
+	free(vses);
+
+return( format14 );
+}
+
+extern unichar_t MacRomanEnc[];
+static void dumpcmap(struct alltabs *at, SplineFont *sf,enum fontformat format) {
+    int i,enccnt, issmall, hasmac;
+    uint16 table[256];
+    SplineChar *sc;
+    int alreadyprivate = false;
+    int wasotf = format==ff_otf || format==ff_otfcid;
+    EncMap *map = at->map;
+    int ucs4len=0, ucs2len=0, cjklen=0, applecjklen=0, vslen=0;
+    FILE *format12, *format4, *format2, *apple2, *format14;
+    int mspos, ucs4pos, cjkpos, applecjkpos, vspos, start_of_macroman;
+    int modformat = format;
+
+    if (( format>=ff_ttf && format<=ff_otfdfont) && (at->gi.flags&ttf_flag_symbol))
+	modformat = ff_ttfsym;
+
+    at->cmap = tmpfile();
+
+    /* MacRoman encoding table */ /* Not going to bother with making this work for cid fonts */
+    /* I now see that Apple doesn't restrict us to format 0 sub-tables (as */
+    /*  the docs imply) but instead also uses format 6 tables. Wildly in- */
+    /*  appropriate as they are for 2byte encodings, but Apple uses them */
+    /*  for one byte ones too */
+    memset(table,'\0',sizeof(table));
+    if ( !wasotf ) {
+	table[29] = table[8] = table[0] = 1;
+	table[9] = table[13] = 2;
+    }
+    for ( i=0; i<256 ; ++i ) {
+	sc = SFGetChar(sf,MacRomanEnc[i],NULL);
+	if ( sc!=NULL && sc->ttf_glyph!=-1 )
+	    table[i] = sc->ttf_glyph;
+    }
+    if ( table[0]==0 ) table[0] = 1;
+
+    if ( modformat==ff_ttfsym ) {
+	alreadyprivate = AlreadyMSSymbolArea(sf,map);
+	memset(table,'\0',sizeof(table));
+	if ( !wasotf ) {
+	    table[29] = table[8] = table[0] = 1;
+	    table[9] = table[13] = 2;
+	}
+	if ( !alreadyprivate ) {
+	    for ( i=0; i<map->enccount && i<256; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL &&
+			sc->ttf_glyph!=-1 )
+		    table[i] = sc->ttf_glyph;
+	    }
+	    for ( i=0xf020; i<=0xf0ff && i<sf->glyphcnt; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL &&
+			sc->ttf_glyph!=-1 && table[i-0xf000]==0 )
+		    table[i-0xf000] = sc->ttf_glyph;
+	    }
+	} else {
+	    for ( i=0xf020; i<=0xf0ff && i<sf->glyphcnt; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL &&
+			sc->ttf_glyph!=-1 )
+		    table[i-0xf000] = sc->ttf_glyph;
+	    }
+	    for ( i=0; i<map->enccount && i<256; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL &&
+			sc->ttf_glyph!=-1 && table[i]==0 )
+		    table[i] = sc->ttf_glyph;
+	    }
+	}
+	/* if the user has read in a ttf symbol file then it will already have */
+	/*  the right private use encoding, and we don't want to mess it up. */
+	/*  The alreadyprivate flag should detect this case */
+	if ( !alreadyprivate ) {
+	    for ( i=0; i<map->enccount && i<256; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL ) {
+		    sc->orig_pos = sc->unicodeenc;
+		    sc->unicodeenc = 0xf000 + i;
+		}
+	    }
+	    for ( ; i<map->enccount; ++i ) {
+		if ( map->map[i]!=-1 && (sc = sf->glyphs[map->map[i]])!=NULL ) {
+		    sc->orig_pos = sc->unicodeenc;
+		    sc->unicodeenc = -1;
+		}
+	    }
+	}
+    }
+
+    format4  = NeedsUCS2Table(sf,&ucs2len,map,modformat==ff_ttfsym);
+    apple2 = NULL;
+    if ( modformat!=ff_ttfsym ) {
+	format12 = NeedsUCS4Table(sf,&ucs4len,map);
+	format2  = Needs816Enc(sf,&cjklen,map,&apple2,&applecjklen);
+	format14 = NeedsVariationSequenceTable(sf,&vslen);
+    } else
+	format12 = format2 = format14 = apple2 = NULL;
+
+    /* Two/Three/Four encoding table pointers, one for ms, one for mac */
+    /*  usually one for mac big, just a copy of ms */
+    /* plus we may have a format12 encoding for ucs4, mac doesn't support */
+    /* plus we may have a format2 encoding for cjk, sometimes I know the codes for the mac... */
+    /* sometimes the mac will have a slightly different cjk table */
+    /* Sometimes we want a variation sequence subtable (format=14) for */
+    /*  unicode platform */
+    if ( modformat==ff_ttfsym ) {
+	enccnt = 2;
+	hasmac = 0;
+    } else {
+	hasmac = 1;
+	enccnt = 3;
+	if ( format12!=NULL )
+	    enccnt = 5;
+	if ( format2!=NULL ) {
+	    if ( strstrmatch(map->enc->enc_name,"johab")!=NULL ) {
+		++enccnt;
+	    } else {
+		enccnt+=2;
+		hasmac=3;
+	    }
+	}
+	if ( format14!=NULL )
+	    ++enccnt;
+    }
+
+    putshort(at->cmap,0);		/* version */
+    putshort(at->cmap,enccnt);		/* num tables */
+
+    mspos = 2*sizeof(uint16)+enccnt*(2*sizeof(uint16)+sizeof(uint32));
+    ucs4pos = mspos+ucs2len;
+    cjkpos = ucs4pos+ucs4len;
+    if ( apple2==NULL ) {
+	applecjkpos = cjkpos;
+	applecjklen = cjklen;
+    } else
+	applecjkpos = cjkpos + cjklen;
+	/* applecjklen set above */
+    vspos = applecjkpos + applecjklen;
+    start_of_macroman = vspos + vslen;
+
+    if ( hasmac&1 ) {
+	/* big mac table, just a copy of the ms table */
+	putshort(at->cmap,0);	/* mac unicode platform */
+	putshort(at->cmap,3);	/* Unicode 2.0 */
+	putlong(at->cmap,mspos);
+    }
+    if ( format12!=NULL ) {
+	/* full unicode mac table, just a copy of the ms table */
+	putshort(at->cmap,0);	/* mac unicode platform */
+        if( map->enc->is_unicodefull ) {
+	    putshort(at->cmap,10);	/* Unicode 2.0, unicode beyond BMP */
+	} else {
+	    putshort(at->cmap,4);	/* Unicode 2.0, unicode BMP */
+	}
+	putlong(at->cmap,ucs4pos);
+    }
+    if ( format14!=NULL ) {
+	/* variation sequence subtable. Only for platform 0. */
+	putshort(at->cmap,0);	/* mac unicode platform */
+	putshort(at->cmap,5);	/* Variation sequence table */
+	putlong(at->cmap,vspos);
+    }
+    putshort(at->cmap,1);		/* mac platform */
+    putshort(at->cmap,0);		/* plat specific enc, script=roman */
+	/* Even the symbol font on the mac claims a mac roman encoding */
+	/* although it actually contains a symbol encoding. There is an*/
+	/* "RSymbol" language listed for Mac (specific=8) but it isn't used*/
+    putlong(at->cmap,start_of_macroman);	/* offset from tab start to sub tab start */
+    if ( format2!=NULL && (hasmac&2) ) {
+	/* mac cjk table, often a copy of the ms table */
+	putshort(at->cmap,1);		/* mac platform */
+	putshort(at->cmap,
+	    map->enc->is_japanese || sf->uni_interp==ui_japanese ? 1 :	/* SJIS */
+	    map->enc->is_korean || sf->uni_interp==ui_korean ? 3 :	/* Korean */
+	    map->enc->is_simplechinese || sf->uni_interp==ui_simp_chinese ? 25 :/* Simplified Chinese */
+	    2 );			/* Big5 */
+	putlong(at->cmap,applecjkpos);
+    }
+
+    putshort(at->cmap,3);	/* ms platform */
+    putshort(at->cmap,		/* plat specific enc */
+	    modformat==ff_ttfsym ? 0 :	/* Symbol */
+	     1 );			/* Unicode */
+    putlong(at->cmap,mspos);		/* offset from tab start to sub tab start */
+
+    if ( format2!=NULL ) {
+	putshort(at->cmap,3);		/* ms platform */
+	putshort(at->cmap,		/* plat specific enc */
+		strstrmatch(map->enc->enc_name,"johab")!=NULL ? 6 :
+		map->enc->is_korean || sf->uni_interp==ui_korean ?		5 :
+		map->enc->is_japanese || sf->uni_interp==ui_japanese ?		2 :
+		map->enc->is_simplechinese || sf->uni_interp==ui_simp_chinese ?	3 :
+			    4);		/* Big5 */
+	putlong(at->cmap,cjkpos);	/* offset from tab start to sub tab start */
+    }
+
+    if ( format12!=NULL ) {
+	putshort(at->cmap,3);		/* ms platform */
+	putshort(at->cmap,10);		/* plat specific enc, ucs4 */
+	putlong(at->cmap,ucs4pos);	/* offset from tab start to sub tab start */
+    }
+    if ( format4!=NULL ) {
+	if ( !ttfcopyfile(at->cmap,format4,mspos,"cmap-Unicode16")) at->error = true;
+    }
+    if ( format12!=NULL ) {
+	if ( !ttfcopyfile(at->cmap,format12,ucs4pos,"cmap-Unicode32")) at->error = true;
+    }
+    if ( format2!=NULL ) {
+	if ( !ttfcopyfile(at->cmap,format2,cjkpos,"cmap-cjk")) at->error = true;
+    }
+    if ( apple2!=NULL ) {
+	if ( !ttfcopyfile(at->cmap,apple2,applecjkpos,"cmap-applecjk")) at->error = true;
+    }
+    if ( format14!=NULL ) {
+	if ( !ttfcopyfile(at->cmap,format14,vspos,"cmap-uniVariations")) at->error = true;
+    }
+
+    /* Mac table */
+    issmall = true;
+    for ( i=0; i<256; ++i )
+	if ( table[i]>=256 ) {
+	    issmall = false;
+    break;
+	}
+    if ( issmall ) {
+	putshort(at->cmap,0);		/* format */
+	putshort(at->cmap,262);		/* length = 256bytes + 6 header bytes */
+	putshort(at->cmap,0);		/* language = english */
+	for ( i=0; i<256; ++i )
+	    putc(table[i],at->cmap);
+    } else {
+	putshort(at->cmap,6);		/* format 6 */
+	putshort(at->cmap,522);		/* length = 256short +10 header bytes */
+	putshort(at->cmap,0);		/* language = english */
+	putshort(at->cmap,0);		/* first code */
+	putshort(at->cmap,256);		/* entry count */
+	for ( i=0; i<256; ++i )
+	    putshort(at->cmap,table[i]);
+    }
+
+    at->cmaplen = ftell(at->cmap);
+    if ( (at->cmaplen&2)!=0 )
+	putshort(at->cmap,0);
+
+    if ( modformat==ff_ttfsym ) {
+	if ( !alreadyprivate ) {
+	    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+		sf->glyphs[i]->unicodeenc = sf->glyphs[i]->orig_pos;
+		sf->glyphs[i]->orig_pos = i;
+	    }
+	}
+    }
+}
+
+int32 filechecksum(FILE *file) {
+    uint32 sum = 0, chunk;
+
+    rewind(file);
+    while ( 1 ) {
+	chunk = getuint32(file);
+	if ( feof(file) || ferror(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void AbortTTF(struct alltabs *at, SplineFont *sf) {
+    int i;
+
+    if ( at->loca!=NULL )
+	fclose(at->loca);
+    if ( at->name!=NULL )
+	fclose(at->name);
+    if ( at->post!=NULL )
+	fclose(at->post);
+    if ( at->gpos!=NULL )
+	fclose(at->gpos);
+    if ( at->gsub!=NULL )
+	fclose(at->gsub);
+    if ( at->gdef!=NULL )
+	fclose(at->gdef);
+    if ( at->kern!=NULL )
+	fclose(at->kern);
+    if ( at->cmap!=NULL )
+	fclose(at->cmap);
+    if ( at->headf!=NULL )
+	fclose(at->headf);
+    if ( at->hheadf!=NULL )
+	fclose(at->hheadf);
+    if ( at->maxpf!=NULL )
+	fclose(at->maxpf);
+    if ( at->os2f!=NULL )
+	fclose(at->os2f);
+    if ( at->cvtf!=NULL )
+	fclose(at->cvtf);
+    if ( at->vheadf!=NULL )
+	fclose(at->vheadf);
+    if ( at->vorgf!=NULL )
+	fclose(at->vorgf);
+    if ( at->cfff!=NULL )
+	fclose(at->cfff);
+
+    if ( at->gi.glyphs!=NULL )
+	fclose(at->gi.glyphs);
+    if ( at->gi.hmtx!=NULL )
+	fclose(at->gi.hmtx);
+    if ( at->gi.vmtx!=NULL )
+	fclose(at->gi.vmtx);
+    if ( at->fpgmf!=NULL )
+	fclose(at->fpgmf);
+    if ( at->prepf!=NULL )
+	fclose(at->prepf);
+    if ( at->gaspf!=NULL )
+	fclose(at->gaspf);
+
+    if ( at->sidf!=NULL )
+	fclose(at->sidf);
+    if ( at->sidh!=NULL )
+	fclose(at->sidh);
+    if ( at->charset!=NULL )
+	fclose(at->charset);
+    if ( at->encoding!=NULL )
+	fclose(at->encoding);
+    if ( at->private!=NULL )
+	fclose(at->private);
+    if ( at->charstrings!=NULL )
+	fclose(at->charstrings);
+    if ( at->fdselect!=NULL )
+	fclose(at->fdselect);
+    if ( at->fdarray!=NULL )
+	fclose(at->fdarray);
+    if ( at->bdat!=NULL )
+	fclose(at->bdat);
+    if ( at->bloc!=NULL )
+	fclose(at->bloc);
+    if ( at->ebsc!=NULL )
+	fclose(at->ebsc);
+
+    if ( at->prop!=NULL )
+	fclose(at->prop);
+    if ( at->opbd!=NULL )
+	fclose(at->opbd);
+    if ( at->acnt!=NULL )
+	fclose(at->acnt);
+    if ( at->lcar!=NULL )
+	fclose(at->lcar);
+    if ( at->feat!=NULL )
+	fclose(at->feat);
+    if ( at->morx!=NULL )
+	fclose(at->morx);
+
+    if ( at->pfed!=NULL )
+	fclose(at->pfed);
+    if ( at->tex!=NULL )
+	fclose(at->tex);
+
+    if ( at->gvar!=NULL )
+	fclose(at->gvar);
+    if ( at->fvar!=NULL )
+	fclose(at->fvar);
+    if ( at->cvar!=NULL )
+	fclose(at->cvar);
+    if ( at->avar!=NULL )
+	fclose(at->avar);
+
+    for ( i=0; i<sf->subfontcnt; ++i ) {
+	if ( at->fds[i].private!=NULL )
+	    fclose(at->fds[i].private);
+    }
+    if ( sf->subfontcnt!=0 ) {
+	free(sf->glyphs);
+	sf->glyphs = NULL;
+	sf->glyphcnt = sf->glyphmax = 0;
+    }
+    free( at->fds );
+    free( at->gi.bygid );
+}
+
+int SFHasInstructions(SplineFont *sf) {
+    int i;
+
+    if ( sf->mm!=NULL && sf->mm->apple )
+	sf = sf->mm->normal;
+
+    if ( sf->subfontcnt!=0 )
+return( false );		/* Truetype doesn't support cid keyed fonts */
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
+	if ( strcmp(sf->glyphs[i]->name,".notdef")==0 )
+    continue;		/* ff produces fonts with instructions in .notdef & not elsewhere. Ignore these */
+	if ( sf->glyphs[i]->ttf_instrs!=NULL )
+return( true );
+    }
+return( false );
+}
+
+static void MaxpFromTable(struct alltabs *at,SplineFont *sf) {
+    struct ttf_table *maxp;
+
+    maxp = SFFindTable(sf,CHR('m','a','x','p'));
+    if ( maxp==NULL && sf->mm!=NULL && sf->mm->apple )
+	maxp = SFFindTable(sf->mm->normal,CHR('m','a','x','p'));
+    if ( maxp==NULL || maxp->len<13*sizeof(uint16) )
+return;
+    /* We can figure out the others ourselves, but these depend on the contents */
+    /*  of uninterpretted tables */
+    at->maxp.maxZones = memushort(maxp->data,maxp->len, 7*sizeof(uint16));
+    at->maxp.maxTwilightPts = memushort(maxp->data,maxp->len, 8*sizeof(uint16));
+    at->maxp.maxStorage = memushort(maxp->data,maxp->len, 9*sizeof(uint16));
+    at->maxp.maxFDEFs = memushort(maxp->data,maxp->len, 10*sizeof(uint16));
+    at->maxp.maxIDEFs = memushort(maxp->data,maxp->len, 11*sizeof(uint16));
+    at->maxp.maxStack = memushort(maxp->data,maxp->len, 12*sizeof(uint16));
+}
+
+static FILE *dumpstoredtable(SplineFont *sf,uint32 tag,int *len) {
+    struct ttf_table *tab = SFFindTable(sf,tag);
+    FILE *out;
+
+    if ( tab==NULL && sf->mm!=NULL && sf->mm->apple )
+	tab = SFFindTable(sf->mm->normal,tag);
+    if ( tab==NULL ) {
+	*len = 0;
+return( NULL );
+    }
+
+    out = tmpfile();
+    fwrite(tab->data,1,tab->len,out);
+    if ( (tab->len&1))
+	putc('\0',out);
+    if ( (tab->len+1)&2 )
+	putshort(out,0);
+    *len = tab->len;
+return( out );
+}
+
+static FILE *dumpsavedtable(struct ttf_table *tab) {
+    FILE *out;
+
+    if ( tab==NULL )
+return( NULL );
+
+    out = tmpfile();
+    fwrite(tab->data,1,tab->len,out);
+    if ( (tab->len&1))
+	putc('\0',out);
+    if ( (tab->len+1)&2 )
+	putshort(out,0);
+return( out );
+}
+
+static int tagcomp(const void *_t1, const void *_t2) {
+    struct taboff *t1 = *((struct taboff **) _t1), *t2 = *((struct taboff **) _t2);
+return( (int) (t1->tag - t2->tag) );
+}
+
+static int tcomp(const void *_t1, const void *_t2) {
+    struct taboff *t1 = *((struct taboff **) _t1), *t2 = *((struct taboff **) _t2);
+return( t1->orderingval - t2->orderingval );
+}
+
+static int tcomp2(const void *_t1, const void *_t2) {
+    struct taboff *t1 = *((struct taboff **) _t1), *t2 = *((struct taboff **) _t2);
+    if ( t1->offset>t2->offset )
+return( 1 );
+    else if ( t1->offset < t2->offset )
+return( -1 );
+return( 0 );
+}
+
+static void ATmaxpInit(struct alltabs *at,SplineFont *sf, enum fontformat format) {
+
+    at->maxp.version = 0x00010000;
+    if ( format==ff_otf || format==ff_otfcid || (format==ff_none && at->applemode) )
+	at->maxp.version = 0x00005000;
+    at->maxp.maxnumcomponents = 0;
+    at->maxp.maxcomponentdepth = 0;
+    at->maxp.maxZones = 2;		/* 1 would probably do, don't use twilight */
+    at->maxp.maxFDEFs = 1;		/* Not even 1 */
+    at->maxp.maxStorage = 1;		/* Not even 1 */
+    at->maxp.maxStack = 64;		/* A guess, it's probably more like 8 */
+    if ( format==ff_otf || format==ff_otfcid || (format==ff_none && at->applemode) )
+	at->maxp.version = 0x00005000;
+    else
+	MaxpFromTable(at,sf);
+    at->gi.maxp = &at->maxp;
+}
+
+static void initATTables(struct alltabs *at, SplineFont *sf, enum fontformat format) {
+    setos2(&at->os2,at,sf,format);	/* should precede kern/ligature output */
+    if ( at->opentypemode ) {
+	SFFindUnusedLookups(sf);
+	otf_dumpgpos(at,sf);
+	otf_dumpgsub(at,sf);
+	otf_dumpgdef(at,sf);
+	otf_dumpjstf(at,sf);
+	otf_dumpbase(at,sf);
+	otf_dump_math(at,sf);	/* Not strictly OpenType yet */
+	if ( at->gi.flags & ttf_flag_dummyDSIG )
+	    otf_dump_dummydsig(at,sf);
+    }
+    if ( at->dovariations )
+	ttf_dumpvariations(at,sf);
+    if ( at->applemode ) {
+	if ( !at->opentypemode )
+	    SFFindUnusedLookups(sf);
+	ttf_dumpkerns(at,sf);
+	aat_dumplcar(at,sf);
+	aat_dumpmorx(at,sf);		/* Sets the feat table too */
+	aat_dumpopbd(at,sf);
+	aat_dumpprop(at,sf);
+	aat_dumpbsln(at,sf);
+    }
+    if ( !at->applemode && (!at->opentypemode || (at->gi.flags&ttf_flag_oldkern)) )
+	ttf_dumpkerns(at,sf);		/* everybody supports a mimimal kern table */
+
+    dumpnames(at,sf,format);		/* Must be after dumpmorx which may create extra names */
+					    /* GPOS 'size' can also create names (so must be after that too) */
+    redoos2(at);
+}
+
+static struct taboff *findtabindir(struct tabdir *td, uint32 tag ) {
+    int i;
+
+    for ( i=0; i<td->numtab; ++i )
+	if ( td->tabs[i].tag == tag )
+return( &td->tabs[i] );
+
+return( NULL );
+}
+
+static void buildtablestructures(struct alltabs *at, SplineFont *sf,
+	enum fontformat format) {
+    int i;
+    int ebdtpos, eblcpos;
+    struct ttf_table *tab;
+
+    if ( format==ff_otf || format==ff_otfcid ) {
+	at->tabdir.version = CHR('O','T','T','O');
+#ifdef FONTFORGE_CONFIG_APPLE_ONLY_TTF		/* This means that Windows will reject the font. In general not a good idea */
+    } else if ( at->applemode && !at->opentypemode ) {
+	at->tabdir.version = CHR('t','r','u','e');
+#endif
+    } else {
+	at->tabdir.version = 0x00010000;
+    }
+
+    i = 0;
+
+    if ( at->base!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('B','A','S','E');
+	at->tabdir.tabs[i].data = at->base;
+	at->tabdir.tabs[i++].length = at->baselen;
+    }
+
+    if ( at->bsln!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('b','s','l','n');
+	at->tabdir.tabs[i].data = at->bsln;
+	at->tabdir.tabs[i++].length = at->bslnlen;
+    }
+
+    if ( at->bdf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('B','D','F',' ');
+	at->tabdir.tabs[i].data = at->bdf;
+	at->tabdir.tabs[i++].length = at->bdflen;
+    }
+
+    if ( format==ff_otf || format==ff_otfcid ) {
+	at->tabdir.tabs[i].tag = CHR('C','F','F',' ');
+	at->tabdir.tabs[i].length = at->cfflen;
+	at->tabdir.tabs[i++].data = at->cfff;
+    }
+
+    if ( at->dsigf!=NULL ) {
+	ebdtpos = i;
+	at->tabdir.tabs[i].tag = CHR('D','S','I','G');
+	at->tabdir.tabs[i].length = at->dsiglen;
+	at->tabdir.tabs[i++].data = at->dsigf;
+    }
+
+    if ( at->bdat!=NULL && (at->msbitmaps || at->otbbitmaps)) {
+	ebdtpos = i;
+	at->tabdir.tabs[i].tag = CHR('E','B','D','T');
+	at->tabdir.tabs[i].length = at->bdatlen;
+	at->tabdir.tabs[i++].data = at->bdat;
+    }
+
+    if ( at->bloc!=NULL && (at->msbitmaps || at->otbbitmaps)) {
+	eblcpos = i;
+	at->tabdir.tabs[i].tag = CHR('E','B','L','C');
+	at->tabdir.tabs[i].data = at->bloc;
+	at->tabdir.tabs[i++].length = at->bloclen;
+    }
+
+    if ( at->ebsc!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('E','B','S','C');
+	at->tabdir.tabs[i].data = at->ebsc;
+	at->tabdir.tabs[i++].length = at->ebsclen;
+    }
+
+    if ( at->fftmf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('F','F','T','M');
+	at->tabdir.tabs[i].data = at->fftmf;
+	at->tabdir.tabs[i++].length = at->fftmlen;
+    }
+
+    if ( at->gdef!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('G','D','E','F');
+	at->tabdir.tabs[i].data = at->gdef;
+	at->tabdir.tabs[i++].length = at->gdeflen;
+    }
+
+    if ( at->gpos!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('G','P','O','S');
+	at->tabdir.tabs[i].data = at->gpos;
+	at->tabdir.tabs[i++].length = at->gposlen;
+    }
+
+    if ( at->gsub!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('G','S','U','B');
+	at->tabdir.tabs[i].data = at->gsub;
+	at->tabdir.tabs[i++].length = at->gsublen;
+    }
+
+    if ( at->jstf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('J','S','T','F');
+	at->tabdir.tabs[i].data = at->jstf;
+	at->tabdir.tabs[i++].length = at->jstflen;
+    }
+
+    if ( at->os2f!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('O','S','/','2');
+	at->tabdir.tabs[i].data = at->os2f;
+	at->tabdir.tabs[i++].length = at->os2len;
+    }
+
+    if ( at->pfed!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('P','f','E','d');
+	at->tabdir.tabs[i].data = at->pfed;
+	at->tabdir.tabs[i++].length = at->pfedlen;
+    }
+
+    if ( at->tex!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('T','e','X',' ');
+	at->tabdir.tabs[i].data = at->tex;
+	at->tabdir.tabs[i++].length = at->texlen;
+    }
+
+    if ( at->math!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('M','A','T','H');
+	at->tabdir.tabs[i].data = at->math;
+	at->tabdir.tabs[i++].length = at->mathlen;
+    }
+
+    if ( at->vorgf!=NULL ) {		/* No longer generated */
+	at->tabdir.tabs[i].tag = CHR('V','O','R','G');
+	at->tabdir.tabs[i].data = at->vorgf;
+	at->tabdir.tabs[i++].length = at->vorglen;
+    }
+
+    if ( at->acnt!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('a','c','n','t');
+	at->tabdir.tabs[i].data = at->acnt;
+	at->tabdir.tabs[i++].length = at->acntlen;
+    }
+
+    if ( at->bdat!=NULL && at->applebitmaps ) {
+	at->tabdir.tabs[i].tag = CHR('b','d','a','t');
+	if ( !at->msbitmaps ) {
+	    at->tabdir.tabs[i].data = at->bdat;
+	    at->tabdir.tabs[i++].length = at->bdatlen;
+	} else {
+	    at->tabdir.tabs[i].data = NULL;
+	    at->tabdir.tabs[i].dup_of = ebdtpos;
+	    at->tabdir.tabs[i++].length = at->tabdir.tabs[ebdtpos].length;
+	}
+    }
+
+    if ( format==ff_none && at->applebitmaps ) {
+	/* Bitmap only fonts get a bhed table rather than a head */
+	at->tabdir.tabs[i].tag = CHR('b','h','e','d');
+	at->tabdir.tabs[i].data = at->headf;
+	at->tabdir.tabs[i++].length = at->headlen;
+    }
+
+    if ( at->bloc!=NULL && at->applebitmaps ) {
+	at->tabdir.tabs[i].tag = CHR('b','l','o','c');
+	if ( !at->msbitmaps ) {
+	    at->tabdir.tabs[i].data = at->bloc;
+	    at->tabdir.tabs[i++].length = at->bloclen;
+	} else {
+	    at->tabdir.tabs[i].data = NULL;
+	    at->tabdir.tabs[i].dup_of = eblcpos;
+	    at->tabdir.tabs[i++].length = at->tabdir.tabs[eblcpos].length;
+	}
+    }
+
+    if ( at->cmap!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('c','m','a','p');
+	at->tabdir.tabs[i].data = at->cmap;
+	at->tabdir.tabs[i++].length = at->cmaplen;
+    }
+
+    if ( format!=ff_otf && format!=ff_otfcid && format!=ff_none ) {
+	if ( at->cvtf!=NULL ) {
+	    at->tabdir.tabs[i].tag = CHR('c','v','t',' ');
+	    at->tabdir.tabs[i].data = at->cvtf;
+	    at->tabdir.tabs[i++].length = at->cvtlen;
+	}
+    }
+
+    if ( at->feat!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('f','e','a','t');
+	at->tabdir.tabs[i].data = at->feat;
+	at->tabdir.tabs[i++].length = at->featlen;
+    }
+
+    if ( at->fpgmf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('f','p','g','m');
+	at->tabdir.tabs[i].data = at->fpgmf;
+	at->tabdir.tabs[i++].length = at->fpgmlen;
+    }
+
+    if ( at->gaspf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('g','a','s','p');
+	at->tabdir.tabs[i].data = at->gaspf;
+	at->tabdir.tabs[i++].length = at->gasplen;
+    }
+
+    if ( at->gi.glyphs!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('g','l','y','f');
+	at->tabdir.tabs[i].data = at->gi.glyphs;
+	at->tabdir.tabs[i++].length = at->gi.glyph_len;
+    }
+
+    if ( at->hdmxf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('h','d','m','x');
+	at->tabdir.tabs[i].data = at->hdmxf;
+	at->tabdir.tabs[i++].length = at->hdmxlen;
+    }
+
+    if ( format!=ff_none || !at->applebitmaps ) {
+	at->tabdir.tabs[i].tag = CHR('h','e','a','d');
+	at->tabdir.tabs[i].data = at->headf;
+	at->tabdir.tabs[i++].length = at->headlen;
+    }
+
+    if ( format!=ff_none || !at->applemode ) {
+	at->tabdir.tabs[i].tag = CHR('h','h','e','a');
+	at->tabdir.tabs[i].data = at->hheadf;
+	at->tabdir.tabs[i++].length = at->hheadlen;
+    } else if ( at->hheadf!=NULL ) {
+	fclose(at->hheadf);
+	at->hheadf = NULL;
+    }
+
+    if ( format!=ff_none || !at->applemode ) {
+	at->tabdir.tabs[i].tag = CHR('h','m','t','x');
+	at->tabdir.tabs[i].data = at->gi.hmtx;
+	at->tabdir.tabs[i++].length = at->gi.hmtxlen;
+    }
+
+    if ( at->kern!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('k','e','r','n');
+	at->tabdir.tabs[i].data = at->kern;
+	at->tabdir.tabs[i++].length = at->kernlen;
+    }
+
+    if ( at->lcar!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('l','c','a','r');
+	at->tabdir.tabs[i].data = at->lcar;
+	at->tabdir.tabs[i++].length = at->lcarlen;
+    }
+
+    if ( at->loca!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('l','o','c','a');
+	at->tabdir.tabs[i].data = at->loca;
+	at->tabdir.tabs[i++].length = at->localen;
+    }
+
+    at->tabdir.tabs[i].tag = CHR('m','a','x','p');
+    at->tabdir.tabs[i].data = at->maxpf;
+    at->tabdir.tabs[i++].length = at->maxplen;
+
+    if ( at->morx!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('m','o','r','x');
+	at->tabdir.tabs[i].data = at->morx;
+	at->tabdir.tabs[i++].length = at->morxlen;
+    }
+
+    if ( at->name!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('n','a','m','e');
+	at->tabdir.tabs[i].data = at->name;
+	at->tabdir.tabs[i++].length = at->namelen;
+    }
+
+    if ( at->opbd!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('o','p','b','d');
+	at->tabdir.tabs[i].data = at->opbd;
+	at->tabdir.tabs[i++].length = at->opbdlen;
+    }
+
+    if ( at->post!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('p','o','s','t');
+	at->tabdir.tabs[i].data = at->post;
+	at->tabdir.tabs[i++].length = at->postlen;
+    }
+
+    if ( format!=ff_otf && format!=ff_otfcid && format!=ff_none ) {
+	if ( at->prepf!=NULL ) {
+	    at->tabdir.tabs[i].tag = CHR('p','r','e','p');
+	    at->tabdir.tabs[i].data = at->prepf;
+	    at->tabdir.tabs[i++].length = at->preplen;
+	}
+    }
+
+    if ( at->prop!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('p','r','o','p');
+	at->tabdir.tabs[i].data = at->prop;
+	at->tabdir.tabs[i++].length = at->proplen;
+    }
+
+    if ( at->vheadf!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('v','h','e','a');
+	at->tabdir.tabs[i].data = at->vheadf;
+	at->tabdir.tabs[i++].length = at->vheadlen;
+
+	at->tabdir.tabs[i].tag = CHR('v','m','t','x');
+	at->tabdir.tabs[i].data = at->gi.vmtx;
+	at->tabdir.tabs[i++].length = at->gi.vmtxlen;
+    }
+
+    if ( at->fvar!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('f','v','a','r');
+	at->tabdir.tabs[i].data = at->fvar;
+	at->tabdir.tabs[i++].length = at->fvarlen;
+    }
+    if ( at->gvar!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('g','v','a','r');
+	at->tabdir.tabs[i].data = at->gvar;
+	at->tabdir.tabs[i++].length = at->gvarlen;
+    }
+    if ( at->cvar!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('c','v','a','r');
+	at->tabdir.tabs[i].data = at->cvar;
+	at->tabdir.tabs[i++].length = at->cvarlen;
+    }
+    if ( at->avar!=NULL ) {
+	at->tabdir.tabs[i].tag = CHR('a','v','a','r');
+	at->tabdir.tabs[i].data = at->avar;
+	at->tabdir.tabs[i++].length = at->avarlen;
+    }
+
+    if ( i>=MAX_TAB )
+	IError("Miscalculation of number of tables needed. Up sizeof tabs array in struct tabdir in ttf.h" );
+
+    for ( tab=sf->ttf_tab_saved; tab!=NULL && i<MAX_TAB; tab=tab->next ) {
+	at->tabdir.tabs[i].tag = tab->tag;
+	at->tabdir.tabs[i].data = tab->temp;
+	at->tabdir.tabs[i++].length = tab->len;
+    }
+    if ( tab!=NULL )
+	IError("Some user supplied tables omitted. Up sizeof tabs array in struct tabdir in ttf.h" );
+
+    at->tabdir.numtab = i;
+    at->tabdir.searchRange = (i<16?8:i<32?16:i<64?32:64)*16;
+    at->tabdir.entrySel = (i<16?3:i<32?4:i<64?5:6);
+    at->tabdir.rangeShift = at->tabdir.numtab*16-at->tabdir.searchRange;
+}
+
+static int initTables(struct alltabs *at, SplineFont *sf,enum fontformat format,
+	int32 *bsizes, enum bitmapformat bf) {
+    int i, j, aborted, offset;
+    BDFFont *bdf;
+    struct ttf_table *tab;
+
+   if ( strmatch(at->map->enc->enc_name,"symbol")==0 && format==ff_ttf )
+  	format = ff_ttfsym;
+
+    tab = SFFindTable(sf,CHR('c','v','t',' '));
+    if ( tab!=NULL ) {
+	at->oldcvt = tab;
+	at->oldcvtlen = tab->len;
+    }
+
+    SFDefaultOS2Info(&sf->pfminfo,sf,sf->fontname);
+
+    at->gi.xmin = at->gi.ymin = 15000;
+    at->gi.sf = sf;
+    if ( bf!=bf_ttf && bf!=bf_sfnt_dfont && bf!=bf_otb && bf!=bf_sfnt_ms )
+	bsizes = NULL;
+    if ( bsizes!=NULL ) {
+	for ( i=j=0; bsizes[i]!=0; ++i ) {
+	    for ( bdf=sf->bitmaps; bdf!=NULL && (bdf->pixelsize!=(bsizes[i]&0xffff) || BDFDepth(bdf)!=(bsizes[i]>>16)); bdf=bdf->next );
+	    if ( bdf!=NULL )
+		bsizes[j++] = bsizes[i];
+	    else
+		ff_post_error(_("Missing bitmap strike"), _("The font database does not contain a bitmap of size %d and depth %d"), bsizes[i]&0xffff, bsizes[i]>>16 );
+	}
+	bsizes[j] = 0;
+	for ( i=0; bsizes[i]!=0; ++i );
+	at->gi.strikecnt = i;
+	if ( i==0 ) bsizes=NULL;
+    }
+
+    if ( sf->subfonts!=NULL ) {
+	SFDummyUpCIDs(&at->gi,sf);	/* life is easier if we ignore the separate fonts of a cid keyed fonts and treat it as flat */
+    } else if ( format!=ff_none )
+	AssignTTFGlyph(&at->gi,sf,at->map,format==ff_otf);
+    else {
+	if ( bsizes==NULL ) {
+	    ff_post_error(_("No bitmap strikes"), _("No bitmap strikes"));
+	    AbortTTF(at,sf);
+return( false );
+	}
+	AssignTTFBitGlyph(&at->gi,sf,at->map,bsizes);
+    }
+    if ( at->gi.gcnt>65535 ) {
+	ff_post_error(_("Too many glyphs"), _("The 'sfnt' format is currently limited to 65535 glyphs, and your font has %d of them."),
+		at->gi.gcnt );
+	AbortTTF(at,sf);
+return( false );
+    } else if ( at->gi.gcnt==65535 ) {
+        /* GID 65535 is used as a "No Glyph" mark in many places (cmap tables, mac substitutions to delete a glyph */
+        LogError(_("Your font has exactly 65535 glyphs. Encoding 65535 is the limit and is often used as a magic \
+            value, so it may cause quirks.\n"));
+    }
+
+
+    ATmaxpInit(at,sf,format);
+    if ( format==ff_otf )
+	aborted = !dumptype2glyphs(sf,at);
+    else if ( format==ff_otfcid )
+	aborted = !dumpcidglyphs(sf,at);
+    else if ( format==ff_none && at->applebitmaps ) {
+	aborted = !dumpcffhmtx(at,sf,true);	/* There is no 'hmtx' table for apple bitmap only fonts */
+		/* But we need some of the side effects of this */
+    } else if ( format==ff_none && at->otbbitmaps ) {
+	aborted = !dumpcffhmtx(at,sf,true);
+	dumpnoglyphs(&at->gi);
+    } else if ( format==ff_none && at->msbitmaps ) {
+	aborted = !dumpglyphs(sf,&at->gi);
+    } else {
+	/* if format==ff_none the following will put out lots of space glyphs */
+	aborted = !dumpglyphs(sf,&at->gi);
+    }
+    if ( format!=ff_type42 && format!=ff_type42cid ) {
+	if ( bsizes!=NULL && !aborted )
+	    ttfdumpbitmap(sf,at,bsizes);
+	if ( bsizes!=NULL && format==ff_none && at->msbitmaps )
+	    ttfdumpbitmapscaling(sf,at,bsizes);
+    }
+    if ( aborted ) {
+	AbortTTF(at,sf);
+return( false );
+    }
+
+    sethead(&at->head,sf,at,format,bsizes);
+    sethhead(&at->hhead,&at->vhead,at,sf);
+    if ( format==ff_none && at->otbbitmaps )
+	dummyloca(at);
+    else if ( format!=ff_otf && format!=ff_otfcid && bf!=bf_sfnt_dfont &&
+	    (format!=ff_none || (bsizes!=NULL && !at->applemode && at->opentypemode)) )
+	redoloca(at);
+    redohead(at);
+    if ( format!=ff_none || !at->applemode )	/* No 'hhea' table for apple bitmap-only fonts */
+	redohhead(at,false);
+    if ( sf->hasvmetrics ) {
+	redohhead(at,true);
+    }
+    ttf_fftm_dump(sf,at);
+
+    if ( format!=ff_type42 && format!=ff_type42cid && !sf->internal_temp ) {
+	initATTables(at, sf, format);
+    }
+    redomaxp(at,format);
+    if ( format!=ff_otf && format!=ff_otfcid && format!=ff_none ) {
+	if (( sf->gasp_cnt!=0 || !SFHasInstructions(sf))
+		&& format!=ff_type42 && format!=ff_type42cid )
+	    dumpgasp(at, sf);
+	at->fpgmf = dumpstoredtable(sf,CHR('f','p','g','m'),&at->fpgmlen);
+	at->prepf = dumpstoredtable(sf,CHR('p','r','e','p'),&at->preplen);
+	at->cvtf = dumpstoredtable(sf,CHR('c','v','t',' '),&at->cvtlen);
+    }
+    for ( tab=sf->ttf_tab_saved; tab!=NULL; tab=tab->next )
+	tab->temp = dumpsavedtable(tab);
+    if ( format!=ff_type42 && format!=ff_type42cid ) {
+	dumppost(at,sf,format);
+	dumpcmap(at,sf,format);
+
+	pfed_dump(at,sf);
+	tex_dump(at,sf);
+    }
+    if ( sf->subfonts!=NULL ) {
+	free(sf->glyphs); sf->glyphs = NULL;
+	sf->glyphcnt = sf->glyphmax = 0;
+    }
+    free( at->gi.bygid );
+    at->gi.gcnt = 0;
+
+    buildtablestructures(at,sf,format);
+    for ( i=0; i<at->tabdir.numtab; ++i ) {
+	struct taboff *tab = &at->tabdir.tabs[i];
+	at->tabdir.ordered[i] = tab;
+	at->tabdir.alpha[i] = tab;
+/* This is the ordering of tables in ARIAL. I've no idea why it makes a */
+/*  difference to order them, time to do a seek seems likely to be small, but */
+/*  other people make a big thing about ordering them so I'll do it. */
+/* I got bored after glyph. Adobe follows the same scheme for their otf fonts */
+/*  so at least the world is consistant */
+/* On the other hand, MS Font validator has a different idea. Oh well */
+/* From: http://partners.adobe.com/asn/tech/type/opentype/recom.jsp	      */
+/* TrueType Ordering							      */
+/*  head, hhea, maxp, OS/2, hmtx, LTSH, VDMX, hdmx, cmap, fpgm, prep, cvt,    */
+/*  loca, glyf, kern, name, post, gasp, PCLT, DSIG			      */
+/* CFF in OpenType Ordering						      */
+/*  head, hhea, maxp, OS/2, name, cmap, post, CFF, (other tables, as convenient) */
+	if ( format==ff_otf || format==ff_otfcid ) {
+	    tab->orderingval = tab->tag==CHR('h','e','a','d')? 1 :
+			       tab->tag==CHR('h','h','e','a')? 2 :
+			       tab->tag==CHR('m','a','x','p')? 3 :
+			       tab->tag==CHR('O','S','/','2')? 4 :
+			       tab->tag==CHR('n','a','m','e')? 5 :
+			       tab->tag==CHR('c','m','a','p')? 6 :
+			       tab->tag==CHR('p','o','s','t')? 7 :
+			       tab->tag==CHR('C','F','F',' ')? 8 :
+			       tab->tag==CHR('G','D','E','F')? 17 :
+			       tab->tag==CHR('G','S','U','B')? 18 :
+			       tab->tag==CHR('G','P','O','S')? 19 :
+			       20;
+	} else {
+	    tab->orderingval = tab->tag==CHR('h','e','a','d')? 1 :
+			       tab->tag==CHR('h','h','e','a')? 2 :
+			       tab->tag==CHR('m','a','x','p')? 3 :
+			       tab->tag==CHR('O','S','/','2')? 4 :
+			       tab->tag==CHR('h','m','t','x')? 5 :
+			       tab->tag==CHR('L','T','S','H')? 6 :
+			       tab->tag==CHR('V','D','M','X')? 7 :
+			       tab->tag==CHR('h','d','m','x')? 8 :
+			       tab->tag==CHR('c','m','a','p')? 9 :
+			       tab->tag==CHR('f','p','g','m')? 10 :
+			       tab->tag==CHR('p','r','e','p')? 11 :
+			       tab->tag==CHR('c','v','t',' ')? 12 :
+			       tab->tag==CHR('l','o','c','a')? 13 :
+			       tab->tag==CHR('g','l','y','f')? 14 :
+			       tab->tag==CHR('k','e','r','n')? 15 :
+			       tab->tag==CHR('n','a','m','e')? 16 :
+			       tab->tag==CHR('p','o','s','t')? 17 :
+			       tab->tag==CHR('g','a','s','p')? 18 :
+			       tab->tag==CHR('P','C','L','T')? 19 :
+			       tab->tag==CHR('D','S','I','G')? 20 :
+			       tab->tag==CHR('G','D','E','F')? 21 :
+			       tab->tag==CHR('G','S','U','B')? 22 :
+			       tab->tag==CHR('G','P','O','S')? 23 :
+			       24;
+	    }
+       }
+
+    qsort(at->tabdir.ordered,at->tabdir.numtab,sizeof(struct taboff *),tcomp);
+    qsort(at->tabdir.alpha,i,sizeof(struct taboff *),tagcomp);
+
+    offset = sizeof(int32)+4*sizeof(int16) + at->tabdir.numtab*4*sizeof(int32);
+    for ( i=0; i<at->tabdir.numtab; ++i ) if ( at->tabdir.ordered[i]->data!=NULL ) {
+	at->tabdir.ordered[i]->offset = offset;
+	offset += ((at->tabdir.ordered[i]->length+3)>>2)<<2;
+	at->tabdir.ordered[i]->checksum = filechecksum(at->tabdir.ordered[i]->data);
+    }
+    for ( i=0; i<at->tabdir.numtab; ++i ) if ( at->tabdir.ordered[i]->data==NULL ) {
+	struct taboff *tab = &at->tabdir.tabs[at->tabdir.ordered[i]->dup_of];
+	at->tabdir.ordered[i]->offset = tab->offset;
+	at->tabdir.ordered[i]->checksum = tab->checksum;
+    }
+
+    tab = SFFindTable(sf,CHR('c','v','t',' '));
+    if ( tab!=NULL ) {
+	if ( at->oldcvt!=NULL && at->oldcvtlen<tab->len )
+	    tab->len = at->oldcvtlen;
+	else if ( at->oldcvt==NULL ) {
+	    /* We created a cvt table when we output the .notdef glyph */
+	    /*  now that means AutoInstr thinks it no longer has a blank */
+	    /*  slate to work with, and will complain, much to the user's */
+	    /*  surprise.  So get rid of it */
+	    struct ttf_table *prev = NULL;
+	    for ( tab = sf->ttf_tables; tab!=NULL ; prev = tab, tab=tab->next )
+		if ( tab->tag==CHR('c','v','t',' ') )
+	    break;
+	    if ( tab!=NULL ) {
+		if ( prev==NULL )
+		    sf->ttf_tables = tab->next;
+		else
+		    prev->next = tab->next;
+		tab->next = NULL;
+		TtfTablesFree(tab);
+	    }
+	}
+    }
+return( true );
+}
+
+static char *Tag2String(uint32 tag) {
+    static char buffer[8];
+
+    buffer[0] = tag>>24;
+    buffer[1] = tag>>16;
+    buffer[2] = tag>>8;
+    buffer[3] = tag;
+    buffer[4] = 0;
+return( buffer );
+}
+
+static void dumpttf(FILE *ttf,struct alltabs *at) {
+    int32 checksum;
+    int i, head_index=-1;
+    /* I can't use fwrite because I (may) have to byte swap everything */
+
+    putlong(ttf,at->tabdir.version);
+    putshort(ttf,at->tabdir.numtab);
+    putshort(ttf,at->tabdir.searchRange);
+    putshort(ttf,at->tabdir.entrySel);
+    putshort(ttf,at->tabdir.rangeShift);
+    for ( i=0; i<at->tabdir.numtab; ++i ) {
+	if ( at->tabdir.alpha[i]->tag==CHR('h','e','a','d') || at->tabdir.alpha[i]->tag==CHR('b','h','e','d') )
+	    head_index = i;
+	putlong(ttf,at->tabdir.alpha[i]->tag);
+	putlong(ttf,at->tabdir.alpha[i]->checksum);
+	putlong(ttf,at->tabdir.alpha[i]->offset);
+	putlong(ttf,at->tabdir.alpha[i]->length);
+    }
+
+    for ( i=0; i<at->tabdir.numtab; ++i ) if ( at->tabdir.ordered[i]->data!=NULL ) {
+	if ( !ttfcopyfile(ttf,at->tabdir.ordered[i]->data,
+		at->tabdir.ordered[i]->offset,Tag2String(at->tabdir.ordered[i]->tag)))
+	    at->error = true;
+    }
+
+    if ( head_index!=-1 ) {
+	checksum = filechecksum(ttf);
+	checksum = 0xb1b0afba-checksum;
+	fseek(ttf,at->tabdir.alpha[head_index]->offset+2*sizeof(int32),SEEK_SET);
+	putlong(ttf,checksum);
+    }
+
+    /* ttfcopyfile closed all the files (except ttf) */
+}
+
+static void DumpGlyphToNameMap(char *fontname,SplineFont *sf) {
+    char *d, *e;
+    char *newname = malloc(strlen(fontname)+10);
+    FILE *file;
+    int i,k,max;
+    SplineChar *sc;
+
+    strcpy(newname,fontname);
+    d = strrchr(newname,'/');
+    if ( d==NULL ) d=newname;
+    e = strrchr(d,'.');
+    if ( e==NULL ) e = newname+strlen(newname);
+    strcpy(e,".g2n");
+
+    file = fopen(newname,"wb");
+    if ( file==NULL ) {
+	LogError( _("Failed to open glyph to name map file for writing: %s\n"), newname );
+	free(newname);
+return;
+    }
+
+    if ( sf->subfontcnt==0 )
+	max = sf->glyphcnt;
+    else {
+	for ( k=max=0; k<sf->subfontcnt; ++k )
+	    if ( sf->subfonts[k]->glyphcnt > max )
+		max = sf->subfonts[k]->glyphcnt;
+    }
+    for ( i=0; i<max; ++i ) {
+	sc = NULL;
+	if ( sf->subfontcnt==0 )
+	    sc = sf->glyphs[i];
+	else {
+	    for ( k=0; k<sf->subfontcnt; ++k ) if ( i<sf->subfonts[k]->glyphcnt )
+		if ( (sc=sf->subfonts[k]->glyphs[i])!=NULL )
+	    break;
+	}
+	if ( sc!=NULL && sc->ttf_glyph!=-1 ) {
+	    fprintf( file, "GLYPHID %d\tPSNAME %s", sc->ttf_glyph, sc->name );
+	    if ( sc->unicodeenc!=-1 )
+		fprintf( file, "\tUNICODE %04X", sc->unicodeenc );
+	    putc('\n',file);
+	}
+    }
+    fclose(file);
+    free(newname);
+}
+
+static int dumpcff(struct alltabs *at,SplineFont *sf,enum fontformat format,
+	FILE *cff) {
+    int ret;
+
+    if ( format==ff_cff ) {
+	AssignTTFGlyph(&at->gi,sf,at->map,true);
+	ret = dumptype2glyphs(sf,at);
+    } else {
+	SFDummyUpCIDs(&at->gi,sf);	/* life is easier if we ignore the separate fonts of a cid keyed fonts and treat it as flat */
+	ret = dumpcidglyphs(sf,at);
+	free(sf->glyphs); sf->glyphs = NULL;
+	sf->glyphcnt = sf->glyphmax = 0;
+    }
+    free( at->gi.bygid );
+
+    if ( !ret )
+	at->error = true;
+    else if ( at->gi.flags & ps_flag_nocffsugar ) {
+	if ( !ttfcopyfile(cff,at->cfff,0,"CFF"))
+	    at->error = true;
+    } else {
+	long len;
+	char buffer[80];
+	fprintf(cff,"%%!PS-Adobe-3.0 Resource-FontSet\n");
+	fprintf(cff,"%%%%DocumentNeedResources:ProcSet (FontSetInit)\n");
+	fprintf(cff,"%%%%Title: (FontSet/%s)\n", sf->fontname);
+	fprintf(cff,"%%%%EndComments\n" );
+	fprintf(cff,"%%%%IncludeResource: ProcSet(FontSetInit)\n" );
+	fprintf(cff,"%%%%BeginResource: FontSet(%s)\n", sf->fontname );
+	fprintf(cff,"/FontSetInit /ProcSet findresource begin\n" );
+	fseek(at->cfff,0,SEEK_END);
+	len = ftell(at->cfff);
+	rewind(at->cfff);
+	sprintf( buffer, "/%s %ld StartData\n", sf->fontname, len );
+	fprintf(cff,"%%%%BeginData: %ld Binary Bytes\n", (long) (len+strlen(buffer)) );
+	fputs(buffer,cff);
+	if ( !ttfcopyfile(cff,at->cfff,ftell(cff),"CFF"))
+	    at->error = true;
+	fprintf(cff,"\n%%%%EndData\n" );
+	fprintf(cff,"%%%%EndResource\n" );
+	fprintf(cff,"%%%%EOF\n" );
+    }
+return( !at->error );
+}
+
+static void ATinit(struct alltabs *at,SplineFont *sf,EncMap *map,int flags, int layer,
+	enum fontformat format, enum bitmapformat bf,int *bsizes) {
+
+    at->gi.flags = flags;
+    at->gi.layer = layer;
+    at->gi.is_ttf = format == ff_ttf || format==ff_ttfsym || format==ff_ttfmacbin || format==ff_ttfdfont;
+    at->gi.sf = sf;
+    at->applemode = (flags&ttf_flag_applemode)?1:0;
+    at->opentypemode = (flags&ttf_flag_otmode)?1:0;
+    at->msbitmaps = bsizes!=NULL && at->opentypemode;
+    at->applebitmaps = bsizes!=NULL && at->applemode;
+    at->gi.onlybitmaps = format==ff_none;
+
+    if ( bf==bf_sfnt_dfont ) { at->msbitmaps = false; at->applebitmaps=true; at->opentypemode=false; at->gi.onlybitmaps=true;}
+    if ( bf==bf_sfnt_ms ) { at->msbitmaps = true; at->applebitmaps=false; at->applemode=false; at->gi.onlybitmaps=true;}
+    if ( bf==bf_otb ) { at->otbbitmaps = true; at->applebitmaps=at->msbitmaps=false; at->applemode=false; at->gi.onlybitmaps=true;}
+
+    if ( bsizes!=NULL && !at->applebitmaps && !at->otbbitmaps && !at->msbitmaps )
+	at->msbitmaps = true;		/* They asked for bitmaps, but no bitmap type selected */
+    at->gi.bsizes = bsizes;
+    at->gi.fixed_width = CIDOneWidth(sf);
+    at->isotf = format==ff_otf || format==ff_otfcid;
+    at->format = format;
+    at->next_strid = 256;
+    if ( at->applemode && sf->mm!=NULL && sf->mm->apple &&
+	    (format==ff_ttf || format==ff_ttfsym ||  format==ff_ttfmacbin ||
+			format==ff_ttfdfont) &&
+	    MMValid(sf->mm,false)) {
+	at->dovariations = true;
+	at->gi.dovariations = true;
+	sf = sf->mm->normal;
+    }
+    at->sf = sf;
+    at->map = map;
+}
+
+int _WriteTTFFont(FILE *ttf,SplineFont *sf,enum fontformat format,
+	int32 *bsizes, enum bitmapformat bf,int flags,EncMap *map, int layer) {
+    struct alltabs at;
+    int i, anyglyphs;
+
+    short_too_long_warned = 0; // This is a static variable defined for putshort.
+    /* TrueType probably doesn't need this, but OpenType does for floats in dictionaries */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+
+    if ( format==ff_otfcid || format== ff_cffcid ) {
+	if ( sf->cidmaster ) sf = sf->cidmaster;
+    } else {
+	if ( sf->subfontcnt!=0 ) sf = sf->subfonts[0];
+    }
+
+    if ( sf->subfontcnt==0 ) {
+	anyglyphs = false;
+	for ( i=sf->glyphcnt-1; i>=0 ; --i ) {
+	    if ( SCWorthOutputting(sf->glyphs[i])) {
+		anyglyphs = true;
+		if ( sf->glyphs[i]->unicodeenc!=-1 )
+	break;
+	    }
+	}
+	if ( !anyglyphs && !sf->internal_temp ) {
+	    ff_post_error(_("No Encoded Glyphs"),_("Warning: Font contained no glyphs"));
+	}
+	if ( format!=ff_ttfsym && !(flags&ttf_flag_symbol) && !sf->internal_temp ) {
+	    if ( i<0 && anyglyphs ) {
+		if ( map->enccount<=256 ) {
+		    char *buts[3];
+		    buts[0] = _("_Yes"); buts[1] = _("_No"); buts[2] = NULL;
+		    if ( ff_ask(_("No Encoded Glyphs"),(const char **) buts,0,1,_("This font contains no glyphs with unicode encodings.\nWould you like to use a \"Symbol\" encoding instead of Unicode?"))==0 )
+			flags |= ttf_flag_symbol;
+		} else
+		    ff_post_error(_("No Encoded Glyphs"),_("This font contains no glyphs with unicode encodings.\nYou will probably not be able to use the output."));
+	    }
+	}
+    }
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
+	sf->glyphs[i]->ttf_glyph = -1;
+
+    memset(&at,'\0',sizeof(struct alltabs));
+    ATinit(&at,sf,map,flags,layer,format,bf,bsizes);
+
+    if ( format==ff_cff || format==ff_cffcid ) {
+	dumpcff(&at,sf,format,ttf);
+    } else {
+	if ( initTables(&at,sf,format,bsizes,bf))
+	    dumpttf(ttf,&at);
+    }
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    if ( at.error || ferror(ttf))
+return( 0 );
+
+#ifdef __CygWin
+    /* Modern versions of windows want the execute bit set on a ttf file */
+    /* I've no idea what this corresponds to in windows, nor any idea on */
+    /*  how to set it from the windows UI, but this seems to work */
+    {
+	struct stat buf;
+	fstat(fileno(ttf),&buf);
+	fchmod(fileno(ttf),S_IXUSR | buf.st_mode );
+    }
+#endif
+
+return( 1 );
+}
+
+int WriteTTFFont(char *fontname,SplineFont *sf,enum fontformat format,
+	int32 *bsizes, enum bitmapformat bf,int flags,EncMap *map, int layer) {
+    FILE *ttf;
+    int ret;
+
+    if ( strstr(fontname,"://")!=NULL ) {
+	if (( ttf = tmpfile())==NULL )
+return( 0 );
+    } else {
+	if (( ttf=fopen(fontname,"wb+"))==NULL )
+return( 0 );
+    }
+    ret = _WriteTTFFont(ttf,sf,format,bsizes,bf,flags,map,layer);
+    if ( strstr(fontname,"://")!=NULL && ret )
+	ret = URLFromFile(fontname,ttf);
+    if ( ret && (flags&ttf_flag_glyphmap) )
+	DumpGlyphToNameMap(fontname,sf);
+    if ( fclose(ttf)==-1 )
+return( 0 );
+return( ret );
+}
+
+/* ************************************************************************** */
+/* ****************************** Type42 stuff ****************************** */
+/* ************************************************************************** */
+
+struct hexout {
+    FILE *type42;
+    int bytesout;
+};
+
+static void dumphex(struct hexout *hexout,FILE *temp,int length) {
+    int i, ch, ch1;
+
+    if ( length&1 )
+	LogError( _("Table length should not be odd\n") );
+
+    while ( length>65534 ) {
+	dumphex(hexout,temp,65534);
+	length -= 65534;
+    }
+
+    fprintf( hexout->type42, " <\n  " );
+    hexout->bytesout = 0;
+    for ( i=0; i<length; ++i ) {
+	ch = getc(temp);
+	if ( ch==EOF )
+    break;
+	if ( hexout->bytesout>=31 ) {
+	    fprintf( hexout->type42, "\n  " );
+	    hexout->bytesout = 0;
+	}
+	ch1 = ch>>4;
+	if ( ch1>=10 )
+	    ch1 += 'A'-10;
+	else
+	    ch1 += '0';
+	putc(ch1,hexout->type42);
+	ch1 = ch&0xf;
+	if ( ch1>=10 )
+	    ch1 += 'A'-10;
+	else
+	    ch1 += '0';
+	putc(ch1,hexout->type42);
+	++hexout->bytesout;
+    }
+    fprintf( hexout->type42, "\n  00\n >\n" );
+}
+
+static void dumptype42(FILE *type42,struct alltabs *at, enum fontformat format) {
+    FILE *temp = tmpfile();
+    struct hexout hexout;
+    int i, length;
+
+    dumpttf(temp,at);
+    rewind(temp);
+
+    hexout.type42 = type42;
+    hexout.bytesout = 0;
+
+    /* Resort the tables into file order */
+    qsort(at->tabdir.ordered,at->tabdir.numtab,sizeof(struct taboff *),tcomp2);
+
+    dumphex(&hexout,temp,at->tabdir.ordered[0]->offset);
+
+    for ( i=0; i<at->tabdir.numtab; ++i ) {
+	if ( at->tabdir.ordered[i]->length>65534 && at->tabdir.ordered[i]->tag==CHR('g','l','y','f')) {
+	    uint32 last = 0;
+	    int j;
+	    fseek(temp,at->tabdir.ordered[i]->offset,SEEK_SET);
+	    for ( j=0; j<at->maxp.numGlyphs; ++j ) {
+		if ( at->gi.loca[j+1]-last > 65534 ) {
+		    dumphex(&hexout,temp,at->gi.loca[j]-last);
+		    last = at->gi.loca[j];
+		}
+	    }
+	    dumphex(&hexout,temp,at->gi.loca[j]-last);
+	} else {
+	    if ( i<at->tabdir.numtab-1 )
+		length = at->tabdir.ordered[i+1]->offset-at->tabdir.ordered[i]->offset;
+	    else {
+		fseek(temp,0,SEEK_END);
+		length = ftell(temp)-at->tabdir.ordered[i]->offset;
+	    }
+	    fseek(temp,at->tabdir.ordered[i]->offset,SEEK_SET);
+	    dumphex(&hexout,temp,length);
+	}
+    }
+
+    fclose( temp );
+}
+
+int _WriteType42SFNTS(FILE *type42,SplineFont *sf,enum fontformat format,
+	int flags,EncMap *map,int layer) {
+    struct alltabs at;
+    int i;
+
+    /* TrueType probably doesn't need this, but OpenType does for floats in dictionaries */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+
+    if ( sf->subfontcnt!=0 ) sf = sf->subfonts[0];
+
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
+	sf->glyphs[i]->ttf_glyph = -1;
+
+    memset(&at,'\0',sizeof(struct alltabs));
+    ATinit(&at,sf,map,flags,layer,format,bf_none,NULL);
+    at.applemode = false;
+    at.opentypemode = false;
+
+    if ( initTables(&at,sf,format,NULL,bf_none))
+	dumptype42(type42,&at,format);
+    free(at.gi.loca);
+
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    if ( at.error || ferror(type42))
+return( 0 );
+
+return( 1 );
+}
+
+/* ************************************************************************** */
+/* ******************************* TTC stuff ******************************** */
+/* ************************************************************************** */
+
+typedef struct splinecharlist *UHash[65536];
+typedef struct splinecharlist *NHash[257];
+
+static unsigned int hashname(const char *name) {
+    unsigned int hash = 0;
+
+    while ( *name ) {
+	unsigned int extra = (hash>>27);
+	hash<<=5;
+	hash += *name&0x1f;
+	hash ^= extra;
+	++name;
+    }
+return( hash % 257 );
+}
+
+static int glyphmatches(SplineChar *sc,SplineChar *sc2,int layer) {
+    RefChar *r, *r2;
+    SplineSet *ss, *ss2;
+    SplinePoint *sp, *sp2;
+
+    if ( sc->width!=sc2->width )
+return( false );
+    if ( sc->ttf_instrs_len != sc2->ttf_instrs_len )
+return( false );
+    if ( sc->ttf_instrs_len!=0 && memcmp(sc->ttf_instrs,sc2->ttf_instrs,sc->ttf_instrs_len)!=0 )
+return( false );
+
+    for ( r=sc->layers[layer].refs, r2=sc2->layers[layer].refs;
+	    r!=NULL && r2!=NULL;
+	    r = r->next, r2 = r2->next ) {
+	if ( r->transform[0] != r2->transform[0] ||
+		r->transform[1] != r2->transform[1] ||
+		r->transform[2] != r2->transform[2] ||
+		r->transform[3] != r2->transform[3] ||
+		r->transform[4] != r2->transform[4] ||
+		r->transform[5] != r2->transform[5] )
+return( false );
+	if ( r->sc->unicodeenc!=r2->sc->unicodeenc )
+return( false );
+	if ( r->sc->unicodeenc==-1 && strcmp(r->sc->name,r2->sc->name)!=0 )
+return( false );
+    }
+    if ( r!=NULL || r2!=NULL )
+return( false );
+
+    for ( ss=sc->layers[layer].splines, ss2=sc2->layers[layer].splines;
+	    ss!=NULL && ss2!=NULL;
+	    ss = ss->next, ss2 = ss2->next ) {
+	for ( sp=ss->first, sp2=ss2->first; sp!=NULL && sp2!=NULL; ) {
+	    if ( sp->me.x != sp2->me.x ||
+		    sp->me.y != sp2->me.y ||
+		    sp->nextcp.x != sp2->nextcp.x ||
+		    sp->nextcp.y != sp2->nextcp.y ||
+		    sp->prevcp.x != sp2->prevcp.x ||
+		    sp->prevcp.y != sp2->prevcp.y )
+return( false );
+	    sp = (sp->next ? sp->next->to : NULL);
+	    sp2 = (sp2->next ? sp2->next->to : NULL);
+	    if ( sp==ss->first ) {
+		if ( sp2==ss2->first )
+	break;
+return( false );
+	    } else if ( sp2==ss2->first )
+return( false );
+	}
+	if (( sp==NULL && sp2!=NULL ) || ( sp!=NULL && sp2==NULL ))
+return( false );
+    }
+    if ( ss==NULL && ss2==NULL )
+return( true );
+
+return( false );
+}
+
+static SplineChar *hashglyphfound(SplineChar *sc,UHash *uhash,NHash *nhash,int layer) {
+    int hash;
+    struct splinecharlist *test;
+    struct altuni *alt;
+
+    /* the unicodeenc might be unset and we might still have altunis if the */
+    /*  glyph is controlled by a variant selector */
+    if ( sc->unicodeenc==-1 && sc->altuni==NULL ) {
+	hash = hashname(sc->name);
+	for ( test=(*nhash)[hash]; test!=NULL; test=test->next )
+	    if ( strcmp(test->sc->name,sc->name)==0 && glyphmatches(sc,test->sc,layer))
+return( test->sc );
+    } else if ( sc->unicodeenc!=-1 ) {
+	hash = sc->unicodeenc&0xffff;
+	for ( test=(*uhash)[hash]; test!=NULL; test=test->next )
+	    if ( glyphmatches(sc,test->sc,layer))
+return( test->sc );
+    }
+    for ( alt=sc->altuni; alt!=NULL; alt=alt->next ) {
+	hash = alt->unienc&0xffff;
+	for ( test=(*uhash)[hash]; test!=NULL; test=test->next )
+	    if ( glyphmatches(sc,test->sc,layer))
+return( test->sc );
+    }
+return( NULL );
+}
+
+static void hashglyphadd(SplineChar *sc,UHash *uhash,NHash *nhash) {
+    int hash;
+    struct splinecharlist *test;
+    struct altuni *alt;
+
+    if ( sc->unicodeenc==-1 && sc->altuni==NULL ) {
+	hash = hashname(sc->name);
+	test = chunkalloc(sizeof(struct splinecharlist));
+	test->sc = sc;
+	test->next = (*nhash)[hash];
+	(*nhash)[hash] = test;
+    } else if ( sc->unicodeenc!=-1 ) {
+	hash = sc->unicodeenc&0xffff;
+	test = chunkalloc(sizeof(struct splinecharlist));
+	test->sc = sc;
+	test->next = (*uhash)[hash];
+	(*uhash)[hash] = test;
+    }
+    for ( alt=sc->altuni; alt!=NULL; alt=alt->next ) {
+	hash = alt->unienc&0xffff;
+	test = chunkalloc(sizeof(struct splinecharlist));
+	test->sc = sc;
+	test->next = (*uhash)[hash];
+	(*uhash)[hash] = test;
+    }
+}
+
+static struct alltabs *ttc_prep(struct sflist *sfs, enum fontformat format,
+				enum bitmapformat bf,int flags, int layer,SplineFont *dummysf) {
+    struct alltabs *ret;
+    int fcnt, cnt, gcnt=3;
+    struct sflist *sfitem;
+    int emsize = -1;
+    UHash *uhash;		/* hash glyphs by unicode */
+    NHash *nhash;		/* hash glyphs by name if no unicode */
+    int anyvmetrics = false;
+    int *bygid;
+    SplineFont *sf;
+    SplineChar *sc, *test;
+    int i, aborted;
+
+    for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt ) {
+	sf = sfitem->sf;
+	/* to share the same 'head' table all must be the same emsize */
+	if ( emsize==-1 )
+	    emsize = sf->ascent + sf->descent;
+	else if ( emsize != sf->ascent + sf->descent )
+return( NULL );
+	if ( format==ff_otf && !PSDictSame(sf->private,sfs->sf->private))
+return( NULL );
+	if ( sf->hasvmetrics ) anyvmetrics = true;
+	for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
+	    sc->ttf_glyph = -1;
+	gcnt += sf->glyphcnt;
+    }
+    if ( cnt==0 )
+return( NULL );
+    fcnt = cnt;
+
+    uhash = calloc(1,sizeof(UHash));
+    nhash = calloc(1,sizeof(NHash));
+
+    *dummysf = *sfs->sf;
+    dummysf->glyphmax = gcnt;
+    dummysf->glyphs = calloc(gcnt,sizeof(SplineChar *));
+    dummysf->glyphcnt = 0;
+    dummysf->hasvmetrics = anyvmetrics;
+
+    bygid = malloc((gcnt+3)*sizeof(int));
+    memset(bygid,0xff, (gcnt+3)*sizeof(int));
+    for ( sfitem= sfs; sfitem!=NULL; sfitem=sfitem->next ) {
+	AssignNotdefNull(sfitem->sf,bygid,false);
+	if ( bygid[0]!=-1 && dummysf->glyphs[0]==NULL ) {
+	    dummysf->glyphs[0] = sfitem->sf->glyphs[bygid[0]];
+	    bygid[0]=0;
+	}
+	if ( format==ff_ttf ) {
+	    if ( bygid[1]!=-1 && dummysf->glyphs[1]==NULL ) {
+		dummysf->glyphs[1] = sfitem->sf->glyphs[bygid[1]];
+		bygid[1]=1;
+	    }
+	    if ( bygid[2]!=-1 && dummysf->glyphs[2]==NULL ) {
+		dummysf->glyphs[2] = sfitem->sf->glyphs[bygid[2]];
+		bygid[2]=2;
+	    }
+	    if ( bygid[0]!=-1 && bygid[1]!=-1 && bygid[2]!=-1 )
+    break;
+	} else {
+	    if ( bygid[0]!=-1 )
+    break;
+	}
+    }
+    dummysf->glyphcnt = format==ff_ttf ? 3 : 1;
+
+    ret = calloc(fcnt+2,sizeof(struct alltabs));
+    ATinit(&ret[fcnt],dummysf,sfs->map,flags&~ttf_flag_dummyDSIG,
+	    layer,format,bf,NULL);
+    ret[fcnt].gi.ttc_composite_font = true;
+    ATmaxpInit(&ret[fcnt],dummysf,format);
+
+    for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt ) {
+	sf = sfitem->sf;
+	ATinit(&ret[cnt],sf,sfitem->map,flags&~ttf_flag_dummyDSIG,
+		layer,format,bf,sfitem->sizes);
+	ret[cnt].gi.bygid = malloc((gcnt+3)*sizeof(int));
+	memset(ret[cnt].gi.bygid,-1,(gcnt+3)*sizeof(int));
+	for ( i=0; i<sf->glyphcnt; ++i ) {
+	    if ( SCWorthOutputting(sc = sf->glyphs[i])) {
+		if ( sc->ttf_glyph==-1 ) {
+		    if ( strcmp(sc->name,".notdef")==0 )
+			sc->ttf_glyph = bygid[0];
+		    else if ( format==ff_ttf &&
+			     (strcmp(sf->glyphs[i]->name,".null")==0 ||
+			      strcmp(sf->glyphs[i]->name,"uni0000")==0 ||
+			      (i==1 && strcmp(sf->glyphs[1]->name,"glyph1")==0)) )
+			sc->ttf_glyph = bygid[1];
+		    else if ( format==ff_ttf &&
+			     (strcmp(sf->glyphs[i]->name,"nonmarkingreturn")==0 ||
+			      strcmp(sf->glyphs[i]->name,"uni000D")==0 ||
+			      (i==2 && strcmp(sf->glyphs[2]->name,"glyph2")==0)))
+			sc->ttf_glyph = bygid[2];
+		    else {
+			test = hashglyphfound(sc,uhash,nhash,layer);
+			if ( test!=NULL )
+			    sc->ttf_glyph = test->ttf_glyph;
+			else {
+			    sc->ttf_glyph = dummysf->glyphcnt++;
+			    bygid[sc->ttf_glyph] = sc->ttf_glyph;
+			    dummysf->glyphs[sc->ttf_glyph] = sc;
+			    hashglyphadd(sc,uhash,nhash);
+			}
+		    }
+		}
+		if ( sc->ttf_glyph!=-1 ) {
+		    ret[cnt].gi.bygid[sc->ttf_glyph] = i;
+		    if ( sc->ttf_glyph>=ret[cnt].gi.gcnt )
+			ret[cnt].gi.gcnt = sc->ttf_glyph+1;
+		}
+	    }
+	    if ( sc!=NULL )
+		sc->lsidebearing = 0x7fff;
+	}
+
+	MaxpFromTable(&ret[cnt],sf);
+
+	if ( ret[cnt].maxp.maxZones > ret[fcnt].maxp.maxZones )
+	    ret[fcnt].maxp.maxZones = ret[cnt].maxp.maxZones;
+	if ( ret[cnt].maxp.maxTwilightPts > ret[fcnt].maxp.maxTwilightPts )
+	    ret[fcnt].maxp.maxTwilightPts = ret[cnt].maxp.maxTwilightPts;
+	if ( ret[cnt].maxp.maxStorage > ret[fcnt].maxp.maxStorage )
+	    ret[fcnt].maxp.maxStorage = ret[cnt].maxp.maxStorage;
+	if ( ret[cnt].maxp.maxFDEFs > ret[fcnt].maxp.maxFDEFs )
+	    ret[fcnt].maxp.maxFDEFs = ret[cnt].maxp.maxFDEFs;
+	if ( ret[cnt].maxp.maxIDEFs > ret[fcnt].maxp.maxIDEFs )
+	    ret[fcnt].maxp.maxIDEFs = ret[cnt].maxp.maxIDEFs;
+	if ( ret[cnt].maxp.maxStack > ret[fcnt].maxp.maxStack )
+	    ret[fcnt].maxp.maxStack = ret[cnt].maxp.maxStack;
+    }
+    free(uhash);
+    free(nhash);
+
+    if ( dummysf->glyphcnt>0xffff ) {
+	free(dummysf->glyphs);
+	free(bygid);
+	for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt )
+	    free(ret[cnt].gi.bygid);
+	free(ret);
+return( NULL );
+    }
+
+    ret[fcnt].gi.fixed_width = CIDOneWidth(sf);
+    ret[fcnt].gi.bygid = bygid;
+    ret[fcnt].gi.gcnt = ret[fcnt].maxp.numGlyphs = dummysf->glyphcnt;
+    if ( format==ff_ttf )
+	aborted = !dumpglyphs(dummysf,&ret[cnt].gi);
+    else
+	aborted = !dumptype2glyphs(dummysf,&ret[cnt]);
+    if ( aborted ) {
+	free(dummysf->glyphs);
+	free(bygid);
+	for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt )
+	    free(ret[cnt].gi.bygid);
+	free(ret);
+return( NULL );
+    }
+    sethhead(&ret[fcnt].hhead,&ret[fcnt].vhead,&ret[fcnt],dummysf);
+    for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt )
+	ret[cnt].maxp = ret[fcnt].maxp;
+
+    /* Just to get a timestamp for all other heads */
+    /*  and to figure out whether 'loca' is 4byte or 2 */
+    sethead(&ret[fcnt].head,dummysf,&ret[fcnt],format,NULL);
+    if ( format==ff_ttf )
+	redoloca(&ret[fcnt]);
+    redohhead(&ret[fcnt],false);
+    if ( dummysf->hasvmetrics )
+	redohhead(&ret[fcnt],true);
+    ttf_fftm_dump(dummysf,&ret[fcnt]);
+
+return( ret );
+}
+
+static FILE *checkdupstoredtable(SplineFont *sf,uint32 tag,int *len,
+	struct alltabs *all, int me) {
+    int i;
+    struct ttf_table *tab = SFFindTable(sf,tag), *test;
+
+    if ( tab==NULL ) {
+	*len = 0;
+return( NULL );
+    }
+    for ( i=0; i<me; ++i ) {
+	test = SFFindTable(all[i].sf,tag);
+	if ( test!=NULL && test->len==tab->len &&
+		memcmp(test->data,tab->data,tab->len)==0 ) {
+	    *len = i;
+return( (FILE *) (intpt) -1 );
+	}
+    }
+return( dumpstoredtable(sf,tag,len));
+}
+
+static void ttc_perfonttables(struct alltabs *all, int me, int mainpos,
+	enum fontformat format ) {
+    struct alltabs *at = &all[me];
+    struct alltabs *main = &all[mainpos];
+    SplineFont *sf = at->sf;
+    struct ttf_table *tab;
+
+    at->gi.xmin = main->gi.xmin; at->gi.xmax = main->gi.xmax;
+    at->gi.ymin = main->gi.ymin; at->gi.ymax = main->gi.ymax;
+    at->gi.glyph_len = main->gi.glyph_len;
+    at->gi.gcnt = main->maxp.numGlyphs;
+    sethead(&at->head,sf,at,format,NULL);
+    memcpy(at->head.modtime,main->head.modtime,sizeof(at->head.modtime));
+    memcpy(at->head.createtime,at->head.modtime,sizeof(at->head.modtime));
+    initATTables(at, sf, format);	/* also name and OS/2 */
+
+    if ( format==ff_ttf ) {
+	if ( sf->gasp_cnt!=0 || !SFHasInstructions(sf) )
+	    dumpgasp(at, sf);
+	at->fpgmf = checkdupstoredtable(sf,CHR('f','p','g','m'),&at->fpgmlen, all, me);
+	at->prepf = checkdupstoredtable(sf,CHR('p','r','e','p'),&at->preplen, all, me);
+	at->cvtf = checkdupstoredtable(sf,CHR('c','v','t',' '),&at->cvtlen, all, me);
+    }
+
+    for ( tab=sf->ttf_tab_saved; tab!=NULL; tab=tab->next )
+	tab->temp = dumpsavedtable(tab);
+    {
+	/* post table is expected to have names for every glyph (or none) even*/
+	/*  those not used in this font. Now it might seem we could just share*/
+	/*  the post table, but it also contains a few font specific things   */
+	/*  (italic angle, etc.) so dump one for each font and hope that we'll*/
+	/*  be able to coalesce them later when we check if any tables are the*/
+	/*  same across fonts */
+	int cnt = sf->glyphcnt;
+	SplineChar **g = sf->glyphs;
+	int *bygid = at->gi.bygid;
+	sf->glyphcnt = main->sf->glyphcnt;
+	sf->glyphs = main->sf->glyphs;
+	at->gi.bygid = main->gi.bygid;
+	dumppost(at,sf,format);
+	sf->glyphcnt = cnt;
+	sf->glyphs = g;
+	at->gi.bygid = bygid;
+    }
+    dumpcmap(at,sf,format);
+    redohead(at);
+
+    pfed_dump(at,sf);
+    tex_dump(at,sf);
+
+    /* These tables are always to be shared and are found in the extra structure */
+    /*  called main */
+    if ( format==ff_ttf ) {
+	at->loca = (void *) (intpt) -1; at->localen = mainpos;
+	at->gi.glyphs = (void *) (intpt) -1; at->gi.glyph_len = mainpos;
+    } else {
+	at->cfff = (void *) (intpt) -1; at->cfflen = mainpos;
+    }
+    at->fftmf = (void *) (intpt) -1; at->fftmlen = mainpos;
+    at->hheadf = (void *) (intpt) -1; at->hheadlen = mainpos;
+    at->gi.hmtx = (void *) (intpt) -1; at->gi.hmtxlen = mainpos;
+    at->maxpf = (void *) (intpt) -1; at->maxplen = mainpos;
+    if ( all[mainpos].vheadf!=NULL ) {
+	at->vheadf = (void *) (intpt) -1; at->vheadlen = mainpos;
+	at->gi.vmtx = (void *) (intpt) -1; at->gi.vmtxlen = mainpos;
+    }
+
+    free(at->gi.bygid);
+}
+
+static int tablefilematch(struct taboff *tab,FILE *ttc,struct alltabs *all,int pos) {
+    int i, ch1, ch2;
+    struct taboff *test;
+    unsigned len;
+
+    /* See if this table (which lives in its own file) matches any tables */
+    /*  with the same tag in an earlier font */
+
+    for ( i=0; i<pos; ++i ) {
+	test = findtabindir(&all[i].tabdir,tab->tag);
+	if ( test==NULL || test->data==(void *) (intpt) -1 ||
+		test->length!=tab->length )
+    continue;
+	rewind(tab->data);
+	fseek(ttc,test->offset,SEEK_SET);
+	for ( len=0; len<tab->length && (ch1=getc(tab->data))!=EOF && (ch2=getc(ttc))!=EOF; ++len ) {
+	    if ( ch1!=ch2 )
+	break;
+	}
+	if ( len==tab->length ) {
+	    rewind(tab->data);
+	    fseek(ttc,0,SEEK_END);
+return( i );
+	}
+    }
+    rewind(tab->data);
+    fseek(ttc,0,SEEK_END);
+return( -1 );
+}
+
+static void ttc_dump(FILE *ttc,struct alltabs *all, enum fontformat format,
+	int flags, enum ttc_flags ttc_flags ) {
+    int i,j,cnt,tot,ch,dup;
+    int offset, startoffset;
+    struct taboff *tab;
+
+    for ( cnt=0; all[cnt].sf!=NULL; ++cnt );
+    --cnt;			/* Last one is dummysf */
+
+    putlong(ttc,CHR('t','t','c','f'));
+    if ( flags&ttf_flag_dummyDSIG ) {
+	putlong(ttc,0x00020000);
+	startoffset = 4*(3+cnt+4);
+    } else {
+	putlong(ttc,0x00010000);
+	startoffset = 4*(3+cnt);
+    }
+    putlong(ttc,cnt);
+    offset = startoffset;
+    for ( i=0; i<cnt; ++i ) {
+	putlong(ttc,offset);			/* Pointer to font header */
+	offset += (8+16*MAX_TAB);		/* Explained below */
+    }
+    if ( flags&ttf_flag_dummyDSIG ) {
+	putlong(ttc,CHR('D','S','I','G'));
+	putlong(ttc,8);				/* Length of dummy DSIG table */
+	putlong(ttc,0x00000001);		/* Standard DSIG version */
+	putlong(ttc,0);				/* No Signatures, no flags */
+    }
+
+    /* Now a font header contains 8 bytes of header and 16 bytes/table */
+    /* Reserve space for cnt fonts. Don't know how many tables they'll */
+    /*  have, so reserve MAX_TAB */
+    tot = cnt*(8+16*MAX_TAB);
+    for ( i=0; i<tot; ++i )
+	putc('\0', ttc);
+
+    /* Build, but don't output. This is so we can lookup tables by tag later */
+    buildtablestructures(&all[cnt],all[cnt].sf,format);
+
+    /* Output some of the smaller tables now, near the head of the file */
+    /* I have my doubts about this being a significant savings... but */
+    /* it doesn't hurt */
+    tab = findtabindir(&all[cnt].tabdir,CHR('h','h','e','a'));
+    tab->offset = ftell(ttc);
+    tab->checksum = filechecksum(tab->data);
+    if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	all[cnt].error = true;
+    tab = findtabindir(&all[cnt].tabdir,CHR('v','h','e','a'));
+    if ( tab!=NULL ) {
+	tab->offset = ftell(ttc);
+	tab->checksum = filechecksum(tab->data);
+	if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	    all[cnt].error = true;
+    }
+    tab = findtabindir(&all[cnt].tabdir,CHR('m','a','x','p'));
+    tab->offset = ftell(ttc);
+    for ( i=0; i<64; ++i )		/* maxp table is 64 bytes, fill in later */
+	putc('\0', ttc);
+    tab = findtabindir(&all[cnt].tabdir,CHR('F','F','T','M'));
+    tab->offset = ftell(ttc);
+    tab->checksum = filechecksum(tab->data);
+    if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	all[cnt].error = true;
+
+    for ( i=0; i<cnt; ++i ) {
+	/* Now generate all tables unique to this font */
+	ttc_perfonttables(all, i, cnt, format );
+	buildtablestructures(&all[i],all[i].sf,format);
+	/* Check for any tables which match those of a previous font */
+	for ( j=0 ; j<all[i].tabdir.numtab; ++j ) {
+	    if ( all[i].tabdir.tabs[j].data!=(void *) (intpt) -1 &&
+		    (dup = tablefilematch(&all[i].tabdir.tabs[j],ttc,all,i))!=-1 ) {
+		fclose(all[i].tabdir.tabs[j].data);
+		all[i].tabdir.tabs[j].data = (void *) (intpt) -1;
+		all[i].tabdir.tabs[j].length = dup;
+	    }
+	}
+
+	/* And now dump those tables into the file. I don't see how I could */
+	/*  order them meaningfully */
+	for ( j=0 ; j<all[i].tabdir.numtab; ++j ) {
+	    if ( all[i].tabdir.tabs[j].data!=(void *) (intpt) -1 ) {
+		all[i].tabdir.tabs[j].offset = ftell(ttc);
+		all[i].tabdir.tabs[j].checksum = filechecksum(all[i].tabdir.tabs[j].data);
+		if ( !ttfcopyfile(ttc,all[i].tabdir.tabs[j].data, all[i].tabdir.tabs[j].offset,Tag2String(all[i].tabdir.tabs[j].tag)))
+		    all[cnt].error = true;
+	    }
+	}
+    }
+
+    free(all[cnt].sf->glyphs);
+    free(all[cnt].gi.bygid);
+
+    /* Now dump the big shared tables */
+    tab = findtabindir(&all[cnt].tabdir,CHR('h','m','t','x'));
+    tab->offset = ftell(ttc);
+    tab->checksum = filechecksum(tab->data);
+    if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	all[cnt].error = true;
+    tab = findtabindir(&all[cnt].tabdir,CHR('v','m','t','x'));
+    if ( tab!=NULL ) {
+	tab->offset = ftell(ttc);
+	tab->checksum = filechecksum(tab->data);
+	if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	    all[cnt].error = true;
+    }
+    if ( format==ff_ttf ) {
+	tab = findtabindir(&all[cnt].tabdir,CHR('l','o','c','a'));
+	tab->offset = ftell(ttc);
+	tab->checksum = filechecksum(tab->data);
+	if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	    all[cnt].error = true;
+	tab = findtabindir(&all[cnt].tabdir,CHR('g','l','y','f'));
+	tab->offset = ftell(ttc);
+	tab->checksum = filechecksum(tab->data);
+	if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	    all[cnt].error = true;
+    } else {
+	tab = findtabindir(&all[cnt].tabdir,CHR('C','F','F',' '));
+	tab->offset = ftell(ttc);
+	tab->checksum = filechecksum(tab->data);
+	if ( !ttfcopyfile(ttc,tab->data, tab->offset,Tag2String(tab->tag)))
+	    all[cnt].error = true;
+    }
+
+    /* Do maxp last, in case generating other tables changed it */
+    redomaxp(&all[cnt],format);
+    tab = findtabindir(&all[cnt].tabdir,CHR('m','a','x','p'));
+    fseek(ttc,tab->offset,SEEK_SET);
+    tab->checksum = filechecksum(all[cnt].maxpf);
+    tab->length = all[cnt].maxplen;
+    rewind(all[cnt].maxpf);
+    while ( (ch=getc(all[cnt].maxpf))!=EOF )
+	putc(ch,ttc);
+
+    /* Now output the font headers */
+    for ( offset=startoffset, i=0; i<cnt; ++i, offset += (8+16*MAX_TAB)) {
+	struct alltabs *at = &all[i];
+	/* Find the location of any shared tables */
+	for ( j=0; j<at->tabdir.numtab; ++j ) {
+	    struct taboff *curtab = &at->tabdir.tabs[j];
+	    if ( curtab->data == (void *) (intpt) -1 ) {
+		tab = findtabindir(&all[curtab->length].tabdir,curtab->tag);
+		if ( tab==NULL ) {
+		    IError("Failed to find tab");
+		    curtab->length = 0;
+		    curtab->offset = 0;
+		    curtab->checksum = 0;
+		} else {
+		    curtab->offset = tab->offset;
+		    curtab->length = tab->length;
+		    curtab->checksum = tab->checksum;
+		}
+	    }
+	}
+	fseek(ttc,offset,SEEK_SET);
+
+	/* Put in alphabetic order */
+	for ( j=0; j<at->tabdir.numtab; ++j )
+	    at->tabdir.alpha[j] = &at->tabdir.tabs[j];
+	qsort(at->tabdir.alpha,at->tabdir.numtab,sizeof(struct taboff *),tagcomp);
+
+	putlong(ttc,at->tabdir.version);
+	putshort(ttc,at->tabdir.numtab);
+	putshort(ttc,at->tabdir.searchRange);
+	putshort(ttc,at->tabdir.entrySel);
+	putshort(ttc,at->tabdir.rangeShift);
+	for ( j=0; j<at->tabdir.numtab; ++j ) {
+	    putlong(ttc,at->tabdir.alpha[j]->tag);
+	    putlong(ttc,at->tabdir.alpha[j]->checksum);
+	    putlong(ttc,at->tabdir.alpha[j]->offset);
+	    putlong(ttc,at->tabdir.alpha[j]->length);
+	}
+    }
+
+    tab = findtabindir(&all[0].tabdir,CHR('h','e','a','d'));
+    if ( tab!=NULL ) {
+	/* As far as I can tell the file checksum is ignored */
+	int checksum;
+	checksum = filechecksum(ttc);
+	checksum = 0xb1b0afba-checksum;
+	fseek(ttc,tab->offset+2*sizeof(int32),SEEK_SET);
+	putlong(ttc,checksum);
+    }
+}
+
+static void CopySFNTAndFixup(FILE *ttc,FILE *ttf) {
+    /* ttf contains a truetype file which we want to copy into ttc */
+    /* Mostly this is just a dump copy, but the offset table at the */
+    /* start of the file must be adjusted to reflect the absolute */
+    /* locations of the tables in the ttc */
+    int offset = ftell(ttc);
+    int val, table_cnt, i;
+
+    fseek(ttf,0,SEEK_SET);
+    val = getlong(ttf);
+    putlong(ttc,val);			/* sfnt version */
+    table_cnt = getushort(ttf);
+    putshort(ttc,table_cnt);
+    val = getushort(ttf);
+    putshort(ttc,val);
+    val = getushort(ttf);
+    putshort(ttc,val);
+    val = getushort(ttf);
+    putshort(ttc,val);
+
+    for ( i=0; i<table_cnt; ++i ) {
+	val = getlong(ttf);
+	putlong(ttc,val);			/* tag */
+	val = getlong(ttf);
+	putlong(ttc,val);			/* checkSum */
+	val = getlong(ttf);
+	putlong(ttc,val+offset);		/* offset */
+	val = getlong(ttf);
+	putlong(ttc,val);			/* length */
+    }
+
+    while ( (val=getc(ttf))!=EOF )
+	putc(val,ttc);
+
+    fclose(ttf);
+
+    if ( ftell(ttc)&1 )
+	putc('\0',ttc);
+    if ( ftell(ttc)&2 )
+	putshort(ttc,0);
+}
+
+int WriteTTC(const char *filename,struct sflist *sfs,enum fontformat format,
+	enum bitmapformat bf,int flags, int layer, enum ttc_flags ttcflags) {
+    struct sflist *sfitem, *sfi2;
+    int ok=1;
+    FILE *ttc;
+    int cnt, offset;
+    int dobruteforce;
+    struct alltabs *ret;
+    SplineFont dummysf;
+
+    if ( strstr(filename,"://")!=NULL ) {
+	if (( ttc = tmpfile())==NULL )
+return( 0 );
+    } else {
+	if (( ttc=fopen(filename,"wb+"))==NULL )
+return( 0 );
+    }
+
+    format = (ttcflags & ttc_flag_cff) ? ff_otf : ff_ttf;
+
+    dobruteforce = true;
+    if ( (ttcflags & ttc_flag_trymerge) && bf==bf_none ) {
+	dobruteforce = false;
+	ret = ttc_prep(sfs,format,bf,flags,layer,&dummysf);
+	if ( ret==NULL )
+	    dobruteforce = true;
+	else
+	    ttc_dump(ttc,ret,format,flags,ttcflags);
+	free(ret);
+    }
+    if ( dobruteforce ) {
+	/* Create a trivial ttc where each font is its own entity and there */
+	/*  are no common tables */
+	/* Generate all the fonts (don't generate DSIGs, there's one DSIG for */
+	/*  the ttc as a whole) */
+	for ( sfitem= sfs, cnt=0; sfitem!=NULL; sfitem=sfitem->next, ++cnt ) {
+	    sfitem->tempttf = tmpfile();
+	    if ( sfitem->tempttf==NULL )
+		ok=0;
+	    else
+		ok = _WriteTTFFont(sfitem->tempttf,sfitem->sf,format,sfitem->sizes,
+			bf,flags&~ttf_flag_dummyDSIG,sfitem->map,layer);
+	    if ( !ok ) {
+		for ( sfi2=sfs; sfi2!=NULL; sfi2 = sfi2->next )
+		    if ( sfi2->tempttf!=NULL )
+			fclose(sfi2->tempttf );
+		fclose(ttc);
+return( true );
+	    }
+	    fseek(sfitem->tempttf,0,SEEK_END);
+	    sfitem->len = ftell(sfitem->tempttf);
+	}
+
+	putlong(ttc,CHR('t','t','c','f'));
+	if ( flags&ttf_flag_dummyDSIG ) {
+	    putlong(ttc,0x00020000);
+	    offset = 4*(3+cnt+4);
+	} else {
+	    putlong(ttc,0x00010000);
+	    offset = 4*(3+cnt);
+	}
+	putlong(ttc,cnt);
+	for ( sfitem= sfs; sfitem!=NULL; sfitem=sfitem->next ) {
+	    putlong(ttc,offset);
+	    offset += ((sfitem->len+3)>>2)<<2;		/* Align on 4 byte boundary */
+	}
+	if ( flags&ttf_flag_dummyDSIG ) {
+	    putlong(ttc,CHR('D','S','I','G'));
+	    putlong(ttc,8);			/* Length of dummy DSIG table */
+	    putlong(ttc,0x00000001);		/* Standard DSIG version */
+	    putlong(ttc,0);			/* No Signatures, no flags */
+	}
+	for ( sfitem= sfs; sfitem!=NULL; sfitem=sfitem->next )
+	    CopySFNTAndFixup(ttc,sfitem->tempttf);
+	if ( ftell(ttc)!=offset )
+	    IError("Miscalculated offsets in ttc");
+    } else
+
+    if ( strstr(filename,"://")!=NULL && ok )
+	ok = URLFromFile(filename,ttc);
+    if ( ferror(ttc))
+	ok = false;
+    if ( fclose(ttc)==-1 )
+	ok = false;
+    if ( !ok )
+	LogError(_("Something went wrong"));
+return( ok );
+}
diff -pruN 1:20161005~dfsg-4/.pc/0004-parsettf.c-Fix-buffer-overrun-condition.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0004-parsettf.c-Fix-buffer-overrun-condition.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0004-parsettf.c-Fix-buffer-overrun-condition.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0004-parsettf.c-Fix-buffer-overrun-condition.patch/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -0,0 +1,6421 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    if (sp < 4) {
+	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
+	        break;
+	    }
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((count+1)*sizeof(struct topdicts *));
+    for ( i=0; i<count; ++i ) {
+	if (fontnames != NULL && fontnames[i] == NULL) {
+		LogError(_("Number of CFF font names is less than dict size: %d < %d"), i, count);
+		break;
+	}
+	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
+		offsets[i+1]-offsets[i], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    if ( sid==-1 ) // Default value, indicating it's not present
+return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -0,0 +1,6421 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    if (sp < 4) {
+	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
+	        break;
+	    }
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((count+1)*sizeof(struct topdicts *));
+    for ( i=0; i<count; ++i ) {
+	if (fontnames != NULL && fontnames[i] == NULL) {
+		LogError(_("Number of CFF font names is less than dict size: %d < %d"), i, count);
+		break;
+	}
+	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
+		offsets[i+1]-offsets[i], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    if ( sid==-1 ) // Default value, indicating it's not present
+return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 && i<len; ++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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch/fontforge/parsettf.c 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch/fontforge/parsettf.c
--- 1:20161005~dfsg-4/.pc/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch/fontforge/parsettf.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch/fontforge/parsettf.c	2017-09-21 06:45:20.000000000 +0000
@@ -0,0 +1,6430 @@
+/* Copyright (C) 2000-2012 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 "fontforge.h"
+#include "cvundoes.h"
+#include "splinefont.h"
+#include <chardata.h>
+#include <utype.h>
+#include <ustring.h>
+#include <math.h>
+#include <locale.h>
+#include <gwidget.h>
+#include "ttf.h"
+#include "scripting.h"
+
+char *SaveTablesPref;
+int ask_user_for_cmap = false;
+
+/* 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 */
+
+int prefer_cjk_encodings=false;
+
+/* ************************************************************************** */
+static struct ms_2_locales { const char *loc_name; int local_id; } ms_2_locals[] = {
+    { "af", 0x436 },
+    { "sq_AL", 0x41c },
+    { "am", 0x45e },
+    { "ar_SA", 0x401 },
+    { "ar_IQ", 0x801 },
+    { "ar_EG", 0xc01 },
+    { "ar_LY", 0x1001 },
+    { "ar_DZ", 0x1401 },
+    { "ar_MA", 0x1801 },
+    { "ar_TN", 0x1C01 },
+    { "ar_OM", 0x2001 },
+    { "ar_YE", 0x2401 },
+    { "ar_SY", 0x2801 },
+    { "ar_JO", 0x2c01 },
+    { "ar_LB", 0x3001 },
+    { "ar_KW", 0x3401 },
+    { "ar_AE", 0x3801 },
+    { "ar_BH", 0x3c01 },
+    { "ar_QA", 0x4001 },
+    { "hy", 0x42b },
+    { "as", 0x44d },
+    { "az", 0x42c },
+    { "az", 0x82c },
+    { "eu", 0x42d },
+    { "be_BY", 0x423 },
+    { "bn_IN", 0x445 },
+    { "bn_BD", 0x845 },
+    { "bg_BG", 0x402 },
+    { "my", 0x455 },
+    { "ca", 0x403 },
+    { "km", 0x453 },
+    { "zh_TW", 0x404 },		/* Trad */
+    { "zh_CN", 0x804 },		/* Simp */
+    { "zh_HK", 0xc04 },		/* Trad */
+    { "zh_SG", 0x1004 },	/* Simp */
+    { "zh_MO", 0x1404 },	/* Trad */
+    { "hr", 0x41a },
+    { "hr_BA", 0x101a },
+    { "cs_CZ", 0x405 },
+    { "da_DK", 0x406 },
+    { "div", 0x465 },
+    { "nl_NL", 0x413 },
+    { "nl_BE", 0x813 },
+    { "en_UK", 0x809 },
+    { "en_US", 0x409 },
+    { "en_CA", 0x1009 },
+    { "en_AU", 0xc09 },
+    { "en_NZ", 0x1409 },
+    { "en_IE", 0x1809 },
+    { "en_ZA", 0x1c09 },
+    { "en_JM", 0x2009 },
+    { "en", 0x2409 },
+    { "en_BZ", 0x2809 },
+    { "en_TT", 0x2c09 },
+    { "en_ZW", 0x3009 },
+    { "en_PH", 0x3409 },
+    { "en_ID", 0x3809 },
+    { "en_HK", 0x3c09 },
+    { "en_IN", 0x4009 },
+    { "en_MY", 0x4409 },
+    { "et_EE", 0x425 },
+    { "fo", 0x438 },
+/* No language code for filipino */
+    { "fa", 0x429 },
+    { "fi_FI", 0x40b },
+    { "fr_FR", 0x40c },
+    { "fr_BE", 0x80c },
+    { "fr_CA", 0xc0c },
+    { "fr_CH", 0x100c },
+    { "fr_LU", 0x140c },
+    { "fr_MC", 0x180c },
+    { "fr", 0x1c0c },		/* West Indes */
+    { "fr_RE", 0x200c },
+    { "fr_CD", 0x240c },
+    { "fr_SN", 0x280c },
+    { "fr_CM", 0x2c0c },
+    { "fr_CI", 0x300c },
+    { "fr_ML", 0x340c },
+    { "fr_MA", 0x380c },
+    { "fr_HT", 0x3c0c },
+    { "fr_DZ", 0xe40c },	/* North African is most likely to be Algeria, possibly Tunisia */
+    { "fy", 0x462 },
+    { "gl", 0x456 },
+    { "ka", 0x437 },
+    { "de_DE", 0x407 },
+    { "de_CH", 0x807 },
+    { "de_AT", 0xc07 },
+    { "de_LU", 0x1007 },
+    { "de_LI", 0x1407 },
+    { "el_GR", 0x408 },
+    { "ga", 0x83c },
+    { "gd", 0x43c },
+    { "gn", 0x474 },
+    { "gu", 0x447 },
+    { "ha", 0x468 },
+    { "he_IL", 0x40d },
+    { "iw", 0x40d },		/* Obsolete name for Hebrew */
+    { "hi", 0x439 },
+    { "hu_HU", 0x40e },
+    { "is_IS", 0x40f },
+    { "id", 0x421 },
+    { "in", 0x421 },		/* Obsolete name for Indonesean */
+    { "iu", 0x45d },
+    { "it_IT", 0x410 },
+    { "it_CH", 0x810 },
+    { "ja_JP", 0x411 },
+    { "kn", 0x44b },
+    { "ks_IN", 0x860 },
+    { "kk", 0x43f },
+    { "ky", 0x440 },
+    { "km", 0x453 },
+    { "kok", 0x457 },
+    { "ko", 0x412 },
+    { "ko", 0x812 },	/*Johab */
+    { "lo", 0x454 },
+    { "la", 0x476 },
+    { "lv_LV", 0x426 },
+    { "lt_LT", 0x427 },
+    { "lt", 0x827 },	/* Classic */
+    { "mk", 0x42f },
+    { "ms", 0x43e },
+    { "ms", 0x83e },
+    { "ml", 0x44c },
+    { "mt", 0x43a },
+    { "mr", 0x44e },
+    { "mn", 0x450 },
+    { "ne_NP", 0x461 },
+    { "ne_IN", 0x861 },
+    { "no_NO", 0x414 },	/* Bokmal */
+    { "no_NO", 0x814 },	/* Nynorsk */
+    { "or", 0x448 },
+    { "om", 0x472 },
+    { "ps", 0x463 },
+    { "pl_PL", 0x415 },
+    { "pt_PT", 0x416 },
+    { "pt_BR", 0x816 },
+    { "pa_IN", 0x446 },
+    { "pa_PK", 0x846 },
+    { "qu_BO", 0x46b },
+    { "qu_EC", 0x86b },
+    { "qu_PE", 0xc6b },
+    { "rm", 0x417 },
+    { "ro_RO", 0x418 },
+    { "ro_MD", 0x818 },
+    { "ru_RU", 0x419 },
+    { "ru_MD", 0x819 },
+    { "smi", 0x43b },
+    { "sa", 0x43b },
+/* No language code for Sepedi */
+    { "sr", 0xc1a },	/* Cyrillic */
+    { "sr", 0x81a },	/* Latin */
+    { "sd_IN", 0x459 },
+    { "sd_PK", 0x859 },
+    { "si", 0x45b },
+    { "sk_SK", 0x41b },
+    { "sl_SI", 0x424 },
+    { "wen", 0x42e },
+    { "es_ES", 0x40a },	/* traditional spanish */
+    { "es_MX", 0x80a },
+    { "es_ES", 0xc0a },	/* Modern spanish */
+    { "es_GT", 0x100a },
+    { "es_CR", 0x140a },
+    { "es_PA", 0x180a },
+    { "es_DO", 0x1c0a },
+    { "es_VE", 0x200a },
+    { "es_CO", 0x240a },
+    { "es_PE", 0x280a },
+    { "es_AR", 0x2c0a },
+    { "es_EC", 0x300a },
+    { "es_CL", 0x340a },
+    { "es_UY", 0x380a },
+    { "es_PY", 0x3c0a },
+    { "es_BO", 0x400a },
+    { "es_SV", 0x440a },
+    { "es_HN", 0x480a },
+    { "es_NI", 0x4c0a },
+    { "es_PR", 0x500a },
+    { "es_US", 0x540a },
+    { "sutu", 0x430 },
+    { "sw_KE", 0x441 },
+    { "sv_SE", 0x41d },
+    { "sv_FI", 0x81d },
+    { "tl", 0x464 },
+    { "tg", 0x464 },
+    { "ta", 0x449 },
+    { "tt", 0x444 },
+    { "te", 0x44a },
+    { "th", 0x41e },
+    { "bo_CN", 0x451 },
+    { "bo_BT", 0x451 },
+    { "ti_ET", 0x473 },
+    { "ti_ER", 0x873 },
+    { "ts", 0x431 },
+    { "tn", 0x432 },
+    { "tr_TR", 0x41f },
+    { "tk", 0x442 },
+    { "uk_UA", 0x422 },
+    { "ug", 0x480 },
+    { "ur_PK", 0x420 },
+    { "ur_IN", 0x820 },
+    { "uz", 0x443 },	/* Latin */
+    { "uz", 0x843 },	/* Cyrillic */
+    { "ven", 0x433 },
+    { "vi", 0x42a },
+    { "cy", 0x452 },
+    { "xh", 0x434 },
+    { "yi", 0x43d },
+    { "ji", 0x43d },	/* Obsolete Yiddish */
+    { "yo", 0x46a },
+    { "zu", 0x435 },
+    { NULL, 0 }
+};
+
+int MSLanguageFromLocale(void) {
+    const char *lang=NULL;
+    int i, langlen;
+    static const char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+    char langcountry[8], language[4];
+    int langcode, langlocalecode;
+
+    for ( i=0; envs[i]!=NULL; ++i ) {
+	lang = getenv(envs[i]);
+	if ( lang!=NULL ) {
+	    langlen = strlen(lang);
+	    if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) ||
+		    (langlen==5 && lang[2]=='_' ) ||
+		    (langlen==2) ||
+		    (langlen==3))	/* Some obscure languages have a 3 letter code */
+		/* I understand this language */
+    break;
+	}
+    }
+    if ( lang==NULL )
+	lang = "en_US";
+    strncpy(langcountry,lang,5); langcountry[5] = '\0';
+    strncpy(language,lang,3); language[3] = '\0';
+    if ( language[2]=='_' ) language[2] = '\0';
+    langlen = strlen(language);
+
+    langcode = langlocalecode = -1;
+    for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) {
+	if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) {
+	    langlocalecode = ms_2_locals[i].local_id;
+	    langcode = langlocalecode&0x3ff;
+    break;
+	} else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 )
+	    langcode = ms_2_locals[i].local_id&0x3ff;
+    }
+    if ( langcode==-1 )		/* Default to English */
+	langcode = 0x9;
+return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode );
+}
+/* ************************************************************************** */
+
+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) {
+    const char *enc;
+    Encoding *e;
+
+    enc = "Custom";
+    if ( platform==0 ) {
+	enc = "Unicode";
+	if ( specific==4 )
+	    enc = "UnicodeFull";
+    } 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 = malloc(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 ) {
+	  fseek(ttf, pos, SEEK_SET);
+	  return( NULL );
+	}
+	if ( enc->is_unicodebmp ) {
+	    str = pt = malloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t));
+	    for ( i=0; i<len/2; ++i ) {
+		ch = getc(ttf)<<8;
+		*pt++ = ch | getc(ttf);
+	    }
+	    *pt = 0;
+	} else if ( enc->unicode!=NULL ) {
+	    str = pt = malloc(sizeof(unichar_t)*len+sizeof(unichar_t));
+	    for ( i=0; i<len; ++i )
+		*pt++ = enc->unicode[getc(ttf)];
+	    *pt = 0;
+	} else if ( enc->tounicode!=NULL ) {
+	    size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1);
+	    char *cstr = malloc(inlen), *cpt;
+	    ICONV_CONST char *in = cstr;
+	    char *out;
+	    for ( cpt=cstr, i=0; i<len; ++i )
+		*cpt++ = getc(ttf);
+	    str = malloc(outlen+sizeof(unichar_t));
+	    out = (char *) str;
+	    iconv(enc->tounicode,&in,&inlen,&out,&outlen);
+	    out[0] = '\0'; out[1] = '\0';
+	    out[2] = '\0'; out[3] = '\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, namelength, 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;
+    int locale = MSLanguageFromLocale();
+    int maclang = WinLangToMac(locale);
+    long ttfFileSize;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    ttfFileSize = ftell(ttf);
+
+    fseek(ttf,offset,SEEK_SET);
+    /* version = */ getlong(ttf);
+    num = getushort(ttf);
+    /* srange = */ getushort(ttf);
+    /* esel = */ getushort(ttf);
+    /* rshift = */ getushort(ttf);
+    if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
+        return( NULL );
+    for ( i=0; i<num; ++i ) {
+        tag = getlong(ttf);
+        /* checksum = */ getlong(ttf);
+        nameoffset = off2+getlong(ttf);
+        namelength = getlong(ttf);
+        if ( feof(ttf) )
+            return( NULL );
+        if ( tag==CHR('n','a','m','e'))
+            break;
+    }
+    if ( i==num )
+        return( NULL );
+    if ( nameoffset+namelength > ttfFileSize )
+        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;
+        if ( plat==3 && !enc->is_custom && lang==locale )
+            val = 15;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) )
+            val = 14;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang )
+            val = 13;
+        /* Ok, that didn't work, how about an english name? */
+        else if ( plat==3 && !enc->is_custom && lang==0x409 )
+            val = 12;
+        else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 )
+            val = 11;
+        else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 )
+            val = 10;
+        /* 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));
+}
+
+/* Chooses which font to open from a TTC TrueType Collection font file.      */
+/*                                                                           */
+/* There are five ways that one enclosed font is selected:                   */
+/*   1)  there is only one font enclosed, so we force defaulting to that one.*/
+/*   2a) the filename has a font index appended, we choose that N'th font.   */
+/*   2b) the filename has a font name appended, we try to match that name    */
+/*           in list of discovered font names and select that named font.    */
+/*   3)  the user is prompted with a list of all discovered font names, and  */
+/*           asked to select one, and then that N'th font is chosen.         */
+/*   4)  when there is no UI, then font index zero is used.                  */
+/*                                                                           */
+/* On failure and no font is chosen, returns false.                          */
+/*                                                                           */
+/* On success, true is returned.  The chosen font name (allocated) pointer   */
+/*   is returned via 'chosenname'. Additionally, the file position is set    */
+/*   pointing to the chosen TTF font offset table, ready for reading the     */
+/*   TTF header.                                                             */
+/*                                                                           */
+/* Example filename strings with appended font selector:                     */
+/*     ./tests/fonts/mingliu.windows.ttc(PMingLiU)                           */
+/*     ./tests/fonts/mingliu.windows.ttc(1)                                  */
+/*                                                                           */
+/* 'offsets' is a list of file offsets to each enclosed TTF offset table.    */
+/* 'names' is a list of font names as found in each enclosed name table.     */
+/* 'names' is used to search for a matching font name, or to present as a    */
+/*    list to the user via ff_choose() to select from.                       */
+/*  Once the chosen font index is determined, offsets[choice] is used to     */
+/*    call fseek() to position to the chosen TTF header offset table. Then   */
+/*    the chosen font name is copied into 'chosenname'.                      */
+
+static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
+    int32 *offsets, cnt, i, choice;
+    char **names;
+    char *pt, *lparen, *rparen;
+
+    /* 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 = malloc(cnt*sizeof(int32));
+    for ( i=0; i<cnt; ++i )
+	offsets[i] = getlong(ttf);
+    names = malloc(cnt*sizeof(char *));
+    for ( i=0; i<cnt; ++i ) {
+	names[i] = TTFGetFontName(ttf,offsets[i],0);
+        if ( names[i]==NULL ) 
+            asprintf(&names[i], "<Unknown font name %d>", i+1);
+    }
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
+    /*  that ff wouldn't open it */
+    /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+        char *find = copyn(lparen+1, rparen-lparen-1);
+	for ( choice=cnt-1; choice>=0; --choice )
+            if ( names[choice]!=NULL )
+	        if ( strcmp(names[choice],find)==0 )
+	            break;
+	if ( choice==-1 ) {
+	    char *end;
+	    choice = strtol(find,&end,10);
+	    if ( *end!='\0' )
+		choice = -1;
+            else if ( choice < 0 || choice >= cnt )
+		choice = -1;
+	}
+	if ( choice==-1 ) {
+	    char *fn = copy(filename);
+	    fn[lparen-filename] = '\0';
+	    ff_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);
+    } else if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    if ( choice < -1 || choice >= cnt )
+        choice = -1;
+    if ( choice!=-1 ) {
+        /* position file to start of the chosen TTF font header */
+	fseek(ttf,offsets[choice],SEEK_SET);
+	*chosenname = names[choice];
+	names[choice] = NULL;
+    }
+    for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
+    for ( i=0; i<cnt; ++i )
+	names[i] = uc_copy(fontnames[i]);
+    if ( no_windowing_ui )
+	choice = 0;
+    else
+	choice = ff_choose(_("Pick a font, any font..."),
+	    (const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
+    for ( i=0; i<cnt; ++i )
+	free(names[i]);
+    free(names);
+return( choice );
+}
+
+static void ParseSaveTablesPref(struct ttfinfo *info) {
+    char *pt, *spt;
+    int cnt;
+
+    if (info->openflags & of_all_tables) {
+        info->savecnt = info->numtables;
+        info->savetab = calloc(info->savecnt,sizeof(struct savetab));
+    } else {
+        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 = calloc(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 uint32 regionchecksum(FILE *file, int start, int len) {
+    uint32 sum = 0, chunk;
+
+    fseek(file,start,SEEK_SET);
+    if ( len!=-1 ) len=(len+3)>>2;
+    while ( len==-1 || --len>=0 ) {
+	chunk = getlong(file);
+	if ( feof(file))
+    break;
+	sum += chunk;
+    }
+return( sum );
+}
+
+static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) {
+    /* When doing font lint we want to check the ttf header and make */
+    /*  sure all the offsets and lengths are valid, and the checksums */
+    /*  match. Most of the time this is just extra work and we don't */
+    /*  bather */
+    uint32 restore_this_pos = ftell(ttf);
+    struct tt_tables {
+	uint32 tag;
+	uint32 checksum;
+	uint32 offset;
+	uint32 length;
+    } *tabs, temp;
+    int i,j;
+    uint32 file_len;
+    int sr, es, rs, e_sr, e_es, e_rs;
+    int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2;
+    int hasloca, hascff, hasglyf;
+
+    info->numtables = getushort(ttf);
+    sr = getushort(ttf);
+    es = getushort(ttf);
+    rs = getushort(ttf);
+    e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16;
+    e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6);
+    e_rs = info->numtables*16-e_sr;
+    if ( e_sr!=sr || e_es!=es || e_rs!=rs ) {
+	LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"),
+		e_sr, sr, e_es, es, e_rs, rs );
+	info->bad_sfnt_header = true;
+    }
+
+    if ( info->numtables<=0 ) {
+	LogError(_("An sfnt file must contain SOME tables, but this one does not."));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    } else if ( info->numtables>1000 ) {
+	LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n"));
+	info->bad_sfnt_header = true;
+	fseek(ttf,restore_this_pos,SEEK_SET);
+return;
+    }
+
+    tabs = malloc(info->numtables*sizeof(struct tt_tables));
+
+    for ( i=0; i<info->numtables; ++i ) {
+	tabs[i].tag = getlong(ttf);
+	tabs[i].checksum = getlong(ttf);
+	tabs[i].offset = getlong(ttf);
+	tabs[i].length = getlong(ttf);
+	if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) {
+	    LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."),
+		    tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag,
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+    fseek(ttf,0,SEEK_END);
+    file_len = ftell(ttf);
+
+    for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) {
+	if ( tabs[i].offset>tabs[j].offset ) {
+	    temp = tabs[i];
+	    tabs[i] = tabs[j];
+	    tabs[j] = temp;
+	}
+    }
+    for ( i=0; i<info->numtables-1; ++i ) {
+	for ( j=i+1; j<info->numtables; ++j ) {
+	    if ( tabs[i].tag==tabs[j].tag ) {
+		LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+		info->bad_sfnt_header = true;
+	    }
+	}
+	if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) {
+	    LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+		    tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag );
+	}
+    }
+    if ( tabs[i].offset+tabs[i].length > file_len ) {
+	LogError(_("Table '%c%c%c%c' extends beyond end of file."),
+		tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	info->bad_sfnt_header = true;
+    }
+
+    /* Checksums. First file as a whole, then each table */
+    if ( regionchecksum(ttf,0,-1)!=0xb1b0afba ) {
+	LogError(_("File checksum is incorrect."));
+	info->bad_sfnt_header = true;
+    }
+    for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) {
+	if ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=tabs[i].checksum ) {
+	    LogError(_("Table '%c%c%c%c' has a bad checksum."),
+		    tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	    info->bad_sfnt_header = true;
+	}
+    }
+
+    hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false;
+    hasloca = hascff = hasglyf = false;
+    for ( i=0; i<info->numtables-1; ++i ) {
+	switch ( tabs[i].tag ) {
+	  case CHR('c','v','t',' '):
+	    if ( tabs[i].length&1 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be even."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag );
+	  break;
+	  case CHR('b','h','e','d'):	/* Fonts with bitmaps but no outlines get bhea */
+	  case CHR('h','e','a','d'):
+	    if ( tabs[i].length!=54 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	    hashead = true;
+	  break;
+	  case CHR('h','h','e','a'):
+	    hashhea = true;
+	  case CHR('v','h','e','a'):
+	    if ( tabs[i].length!=36 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('m','a','x','p'):
+	    hasmaxp = true;
+	    if ( tabs[i].length!=32 && tabs[i].length!=6 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('O','S','/','2'):
+	    hasos2 = true;
+	    if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 )
+		LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."),
+			tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag,
+			tabs[i].length );
+	  break;
+	  case CHR('p','o','s','t'):
+	    haspost = true;
+	  break;
+	  case CHR('n','a','m','e'):
+	    hasname = true;
+	  break;
+	  case CHR('l','o','c','a'):
+	    hasloca = true;
+	  break;
+	  case CHR('g','l','y','f'):
+	    hasglyf = true;
+	  break;
+	  case CHR('C','F','F',' '):
+	    hascff = true;
+	  break;
+          default:
+          break;
+	}
+    }
+    if ( !hashead )
+	LogError(_("Missing required table: \"head\""));
+    if ( !hashhea )
+	LogError(_("Missing required table: \"hhea\""));
+    if ( !hasmaxp )
+	LogError(_("Missing required table: \"maxp\""));
+    if ( !haspost )
+	LogError(_("Missing required table: \"post\""));
+    if ( !hasname )
+	LogError(_("Missing required table: \"name\""));
+    if ( hasglyf && !hasloca )
+	LogError(_("Missing required table: \"loca\""));
+    if ( !hasos2 )
+	LogError(_("Missing \"OS/2\" table"));
+    if ( !hasglyf && hasloca )
+	LogError(_("Missing required table: \"glyf\""));
+    if ( !hasglyf && !hascff )
+	LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables"));
+
+    free(tabs);
+    fseek(ttf,restore_this_pos,SEEK_SET);
+}
+	    
+static struct tablenames { uint32 tag; const char *name; } stdtables[] = {
+    { CHR('a','c','n','t'), N_("accent attachment table") },
+    { CHR('a','v','a','r'), N_("axis variation table") },
+    { CHR('B','A','S','E'), N_("Baseline table (OT version)") },
+    { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") },
+    { CHR('B','D','F',' '), N_("BDF bitmap properties table") },
+    { CHR('b','h','e','d'), N_("bitmap font header table") },
+    { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") },
+    { CHR('b','s','l','n'), N_("baseline table (AAT version)") },
+    { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") },
+    { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") },
+    { CHR('c','m','a','p'), N_("character code mapping table") },
+    { CHR('c','v','a','r'), N_("CVT variation table") },
+    { CHR('c','v','t',' '), N_("control value table") },
+    { CHR('D','S','I','G'), N_("digital signature table") },
+    { CHR('E','B','D','T'), N_("bitmap data table (OT version)") },
+    { CHR('E','B','L','C'), N_("bitmap location table (OT version)") },
+    { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") },
+    { CHR('E','L','U','A'), N_("electronic end user license table") },
+    { CHR('f','d','s','c'), N_("font descriptor table") },
+    { CHR('f','e','a','t'), N_("layout feature table") },
+    { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") },
+    { CHR('F','F','T','M'), N_("FontForge time stamp table") },
+    { CHR('f','m','t','x'), N_("font metrics table") },
+    { CHR('f','p','g','m'), N_("font program table") },
+    { CHR('f','v','a','r'), N_("font variation table") },
+    { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") },
+    { CHR('G','D','E','F'), N_("glyph definition table") },
+    { CHR('G','l','a','t'), N_("Graphite glyph attribute table") },
+    { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") },
+    { CHR('g','l','y','f'), N_("glyph outline table") },
+    { CHR('G','P','O','S'), N_("glyph positioning table") },
+    { CHR('g','v','a','r'), N_("glyph variation table") },
+    { CHR('G','S','U','B'), N_("glyph substitution table") },
+    { CHR('h','d','m','x'), N_("horizontal device metrics table") },
+    { CHR('h','e','a','d'), N_("font header table") },
+    { CHR('h','h','e','a'), N_("horizontal header table") },
+    { CHR('h','m','t','x'), N_("horizontal metrics table") },
+    { CHR('h','s','t','y'), N_("horizontal style table") },
+    { CHR('j','u','s','t'), N_("justification table (AAT version)") },
+    { CHR('J','S','T','F'), N_("justification table (OT version)") },
+    { CHR('k','e','r','n'), N_("kerning table") },
+    { CHR('l','c','a','r'), N_("ligature caret table") },
+    { CHR('l','o','c','a'), N_("glyph location table") },
+    { CHR('L','T','S','H'), N_("linear threshold table") },
+    { CHR('M','A','T','H'), N_("math table") },
+    { CHR('m','a','x','p'), N_("maximum profile table") },
+    { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") },
+    { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") },
+    { CHR('m','o','r','t'), N_("metamorphosis table") },
+    { CHR('m','o','r','x'), N_("extended metamorphosis table") },
+    { CHR('n','a','m','e'), N_("name table") },
+    { CHR('o','p','b','d'), N_("optical bounds table") },
+    { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") },
+    { CHR('P','C','L','T'), N_("PCL 5 data table") },
+    { CHR('P','f','E','d'), N_("FontForge font debugging table") },
+    { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") },
+    { CHR('p','r','e','p'), N_("control value program table") },
+    { CHR('p','r','o','p'), N_("properties table") },
+    { CHR('S','i','l','f'), N_("SIL Graphite rule table") },
+    { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") },
+    { CHR('S','i','l','t'), N_("unknown SIL table") },
+    { CHR('T','e','X',' '), N_("TeX table") },
+    { CHR('t','r','a','k'), N_("tracking table") },
+    { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") },
+    { CHR('V','D','M','X'), N_("vertical device metrics table") },
+    { CHR('v','h','e','a'), N_("vertical header table") },
+    { CHR('v','m','t','x'), N_("vertical metrics table") },
+    { CHR('V','O','R','G'), N_("vertical origin table") },
+    { CHR('Z','a','p','f'), N_("glyph reference table") },
+    { 0, NULL }
+};
+
+static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename,
+	char **choosenname) {
+    int i, j, k, offset, length, version;
+    uint32 tag;
+    int first = true;
+
+    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 */ /* Turns out to be pretty simple */
+    /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */
+    if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) {
+	LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") );
+    } else 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 */
+
+    if ( info->openflags & of_fontlint )
+	ValidateTTFHead(ttf,info);
+
+    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);
+        if ( offset+length > info->ttfFileSize ) {
+	    LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
+	    	            tag>>24, tag>>16, tag>>8, tag );
+	    info->bad_sfnt_header = true;
+            continue;
+        }
+#ifdef DEBUG
+ printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );
+#endif
+	switch ( tag ) {
+	  case CHR('B','A','S','E'):
+	    info->base_start = offset;
+	  break;
+	  case CHR('b','s','l','n'):
+	    info->bsln_start = offset;
+	  break;
+	  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','a','s','p'):
+	    info->gasp_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('J','S','T','F'):
+	    info->jstf_start = offset;
+	    info->jstf_length = length;
+	  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('C','I','D',' '):
+	  case CHR('T','Y','P','1'):
+	    info->typ1_start = offset;
+	    info->typ1_length = length;
+	  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('M','A','T','H'):
+	    info->math_start = offset;
+	    info->math_length = length;
+	  break;
+	      /* Apple stuff */
+	  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:
+            if (info->openflags & of_all_tables) {
+                info->savetab[i].offset = offset;
+                info->savetab[i].tag = tag;
+                info->savetab[i].len = length;
+            }
+            else {
+                for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) {
+                    info->savetab[j].offset = offset;
+                    info->savetab[j].len = length;
+                break;
+                }
+                if ( j==info->savecnt ) {
+                    if ( first ) {
+                        LogError( _("The following table(s) in the font have been ignored by FontForge\n") );
+                        first = false;
+                    }
+                    for ( k=0; stdtables[k].tag!=0; ++k )
+                        if ( stdtables[k].tag == tag )
+                    break;
+                    if ( stdtables[k].tag==0 ) {
+                        LogError( _("  Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag);
+                    } else {
+                        LogError( _("  Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag,
+                                _(stdtables[k].name));
+                    }
+                }
+            }
+	}
+    }
+    if ( info->glyphlocations_start!=0 && info->cff_start!=0 )
+	LogError( _("This font contains both truetype and PostScript glyph descriptions\n  only one will be used.\n"));
+    else if ( (info->glyphlocations_start!=0) +
+		(info->cff_start!=0) +
+		(info->typ1_start!=0)>1 )
+	LogError( _("This font contains multiple glyph descriptions\n  only one will be used.\n"));
+    if ( info->gpos_start!=0 && info->kern_start!=0 )
+	LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n  The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n"));
+    if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 )
+	LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n  FF will only read feature/settings in 'morx' which do not match features\n  found in 'GSUB'.\n"));
+    if ( info->base_start!=0 && info->bsln_start!=0 )
+	LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n  FontForge will only read one of them ('BASE').\n"));
+return( true );
+}
+
+static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) {
+    int i, date[4];
+    /* TTFs have creation and modification timestamps in the 'head' table.  */
+    /* These are 64 bit values in network order / big-endian and denoted    */
+    /* as 'LONGDATETIME'.                                                   */
+    /* These timestamps are in "number of seconds since 00:00 1904-01-01",  */
+    /* noted some places as a Mac OS epoch time value.  We use Unix epoch   */
+    /* timestamps which are "number of seconds since 00:00 1970-01-01".     */
+    /* The difference between these two epoch values is a constant number   */ 
+    /* of seconds, and so we convert from Mac to Unix time by simple        */
+    /* subtraction of that constant difference.                             */
+
+    /*      (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
+    int date1970[4] = {45184, 31781, 0, 0}; 
+
+    /* As there was not (nor still is?) a portable way to do 64-bit math aka*/
+    /* "long long" the code below works on 16-bit slices of the full value. */
+    /* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
+
+    date[3] = getushort(ttf);
+    date[2] = getushort(ttf);
+    date[1] = getushort(ttf);
+    date[0] = getushort(ttf);
+
+    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) =
+	    (((long long) date[3])<<48) |
+	    (((long long) date[2])<<32) |
+	    (             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, flags;
+
+    fseek(ttf,info->head_start+4,SEEK_SET);		/* skip over the version number */
+    info->sfntRevision = getlong(ttf);
+    (void) getlong(ttf);
+    (void) getlong(ttf);
+    flags = getushort(ttf);
+    info->optimized_for_cleartype = (flags&(1<<13))?1:0;
+    info->apply_lsb = !(flags&(1<<1));
+    info->emsize = getushort(ttf);
+
+    info->ascent = .8*info->emsize;
+    info->descent = info->emsize-info->ascent;
+
+    for ( i=0; i<8; ++i )
+	getushort(ttf);
+    for ( i=0; i<4; ++i )
+	info->fbb[i] = (short) 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 = (short) getushort(ttf);
+    info->advanceWidthMax = getushort(ttf);
+    info->pfminfo.hheadset = true;
+    /*info->ascent = info->pfminfo.hhead_ascent;*/
+
+    for ( i=0; i<11; ++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 && info->typ1_start==0 ) {
+	/* X11 OpenType bitmap format */;
+	info->onlystrikes = true;
+    } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) {
+	ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt);
+	info->bad_glyph_data = true;
+	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 = malloc(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(struct ttfinfo *info, 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 );
+	info->bad_ps_fontname = true;
+	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' ) {
+	ff_post_error(_("Bad Font Name"),_("A PostScript name may not be a number"));
+	info->bad_ps_fontname = true;
+	*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 ) {
+		ff_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);
+		info->bad_ps_fontname = true;
+	    }
+	    complained = true;
+	    for ( npt=pt; npt[1]; ++npt )
+		*npt = npt[1];
+	    *npt = '\0';
+	    --pt;
+	}
+    }
+    if ( strlen(str)>63 ) {
+	ff_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);
+	info->bad_ps_fontname = true;
+	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=malloc(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 strlength, 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,strlength,stroff,spec,language);
+return;
+    } else if ( id<0 || id>=ttf_namemax )
+return;
+
+    str = _readencstring(ttf,stroff,strlength,plat,spec,language);
+    if ( str==NULL )		/* we didn't understand the encoding */
+return;
+    if ( id==ttf_postscriptname )
+	ValidatePostScriptFontName(info,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));
+    } 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;
+	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;
+	    ret = ff_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);
+	    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,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) {
+    if ( info->head_start!=0 )
+	readttfhead(ttf,info);
+    if ( info->hhea_start!=0 )
+	readttfhhea(ttf,info);
+    if ( info->maxp_start!=0 )
+	readttfmaxp(ttf,info);
+    readttfcopyrights(ttf,info);	/* This one has internal checks */
+}
+
+#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;
+    } 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->nextcpindex<0xfffe )
+	from->nonextcp = false;
+    if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
+	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 ) {
+		  sp->noprevcp = false;
+		  if ( cur->last!=NULL ) {
+		    cur->last->nonextcp = false;
+		    FigureControls(cur->last,sp,&pts[i-1],is_order2);
+		    cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
+		  }
+		}
+		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, int gbb[4]) {
+    uint16 *endpt = malloc((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] ) {
+	    info->bad_glyph_data = true;
+	    LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"),
+		    sc->orig_pos );
+return;
+	}
+    }
+    if ( path_cnt==0 ) {
+	tot = 0;
+	pts = malloc(sizeof(BasePoint));
+    } else {
+	tot = endpt[path_cnt-1]+1;
+	pts = malloc(tot*sizeof(BasePoint));
+    }
+
+    len = getushort(ttf);
+    instructions = malloc(len);
+    for ( i=0; i<len; ++i )
+	instructions[i] = getc(ttf);
+
+    flags = malloc(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;
+	if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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;
+	if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
+	    if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
+		LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+		if ( !(info->openflags&of_fontlint) )
+		    LogError(_("  Subsequent errors will not be reported.\n") );
+		info->gbbcomplain = true;
+	    }
+	}
+    }
+
+    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);
+    SCCategorizePoints(sc);
+    free(endpt);
+    free(flags);
+    free(pts);
+    if ( feof(ttf)) {
+	LogError( _("Reached end of file when reading simple glyph\n") );
+	info->bad_glyph_data = true;
+    }
+}
+
+static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) {
+    RefChar *head=NULL, *last=NULL, *cur;
+    int flags=0, arg1, arg2;
+    int use_my_metrics=0;
+
+    if ( ftell(ttf)>=end ) {
+	LogError( _("Empty composite %d\n"), sc->orig_pos );
+	info->bad_glyph_data = true;
+return;
+    }
+
+    do {
+	if ( ftell(ttf)>=end ) {
+	    LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos );
+	    info->bad_glyph_data = true;
+    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 );
+	    info->bad_glyph_data = true;
+	    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;
+	if ( cur->use_my_metrics ) {
+	    if ( use_my_metrics ) {
+		LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos );
+		info->bad_glyph_data = true;
+	    } else
+		use_my_metrics = true;
+	}
+	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 ( 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 composite glyph\n") );
+	    info->bad_glyph_data = true;
+    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 = malloc(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;
+    }
+    sc->layers[ly_fore].refs = head;
+}
+
+static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,uint32 start, uint32 end,int gid) {
+    int path_cnt;
+    SplineChar *sc = SplineCharCreate(2);
+    int gbb[4];
+
+    sc->layers[ly_fore].background = 0;
+    sc->layers[ly_back].background = 1;
+    sc->unicodeenc = -1;
+    sc->vwidth = info->emsize;
+    sc->orig_pos = gid;
+
+    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->bad_glyph_data = true;
+	info->complainedbeyondglyfend = true;
+	SplineCharFree(sc);
+return( NULL );
+    } else if ( end<start ) {
+	LogError(_("Bad glyph (%d), its data length is negative\n"), gid );
+	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);
+    gbb[0] = sc->lsidebearing = (short) getushort(ttf);
+    gbb[1] = (short) getushort(ttf);
+    gbb[2] = (short) getushort(ttf);
+    gbb[3] = (short) getushort(ttf);
+    if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
+				  gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
+	if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
+	    LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
+	    info->bad_glyph_data = true;
+	    if ( !(info->openflags&of_fontlint) )
+		LogError(_("  Subsequent errors will not be reported.\n") );
+	    info->bbcomplain = true;
+	}
+    }
+    if ( path_cnt>=0 )
+	readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
+    else
+	readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
+	/* I don't check that composite glyphs fit in the bounding box */
+	/* because the components may not have been read in yet */
+	/* I'll check against the font bb later, if validation mode */
+    if ( start>end ) {
+	LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid );
+	info->bad_glyph_data = true;
+    } else if ( ftell(ttf)>info->glyph_start+end ) {
+	LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid );
+	info->bad_glyph_data = true;
+    }
+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 = malloc((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 = calloc(info->glyph_cnt,sizeof(SplineChar *));
+    if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_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);
+	    ff_progress_next();
+	}
+    } 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 = calloc(info->glyph_cnt,sizeof(char));
+	readttfencodings(ttf,info,git_justinuse);
+	if ( info->gsub_start!=0 )		/* Some glyphs may appear in substitutions and not in the encoding... */
+	    readttfgsubUsed(ttf,info);
+	if ( info->math_start!=0 )
+	    otf_read_math_used(ttf,info);
+	if ( info->morx_start!=0 || info->mort_start!=0 )
+	    readttfmort_glyphsused(ttf,info);
+	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);
+		    ff_progress_next();
+		    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;
+    ff_progress_next_stage();
+}
+
+/* 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,struct ttfinfo *info) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    char **names;
+    uint32 i,j;
+
+    if ( cnt!=NULL ) *cnt = count;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    names = malloc((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") );
+	    if ( info!=NULL ) info->bad_cff = true;
+	    while ( i<count ) {
+		names[i] = copy("");
+		++i;
+	    }
+	    --i;
+	} else {
+	    names[i] = malloc(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,struct ttfinfo *info) {
+    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);
+		// Space for at least 2 bytes is required
+		if ((pt-buffer) > (sizeof(buffer) - 2)) {
+			// The buffer is completely full; null-terminate truncate it
+			if ((pt-buffer) == sizeof(buffer)) {
+				pt--;
+			}
+			*pt++ = '\0';
+			break;
+		}
+	    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 );
+    info->bad_cff = true;
+    *_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") );
+    for (;;) {
+	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 fontmatrix_set;
+    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, struct ttfinfo *info) {
+    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 = malloc(count*sizeof(int));
+    subs->values = malloc(count*sizeof(uint8 *));
+    offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
+	    for ( j=0; j+offsets[i]<offsets[i+1]; ++j )
+		subs->values[i][j] = getc(ttf);
+	    subs->values[i][j] = '\0';
+	} else {
+	    if ( !err )
+		LogError( _("Bad subroutine INDEX in cff font.\n" ));
+	    info->bad_cff = true;
+	    err = true;
+	    subs->lens[i] = 1;
+	    subs->values[i] = malloc(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 ttfinfo *info) {
+    struct topdicts *td = calloc(1,sizeof(struct topdicts));
+    long base = ftell(ttf);
+    int ival, oval, sp, ret, i;
+    real stack[50];
+
+    if ( fontname!=NULL )
+	ValidatePostScriptFontName(info,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 ( !feof(ttf) && ftell(ttf)<base+len ) {
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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") );
+	    info->bad_cff = true;
+	} 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));
+	    td->fontmatrix_set = 1;
+	  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") );
+	    info->bad_cff = true;
+	    if (sp < 4) {
+	        LogError(_("CFF dict stack underflow detected: %d < 4\n"), sp);
+	        break;
+	    }
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+return( td );
+}
+
+static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) {
+    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 ) {
+	if ( feof(ttf) ) {
+	    LogError(_("End of file found when reading private dictionary.\n") );
+    break;
+	}
+	sp = 0;
+	while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=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 );
+	    info->bad_cff = true;
+	} 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];
+	    }
+	    if ( i==0 ) td->bluevalues[0] = 1234567;	/* Marker for an empty arry, which is legal, and different from no array */
+	  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];
+	    }
+	    if ( i==0 ) td->otherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->familyotherblues[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnaph[0] = 1234567;
+	  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];
+	    }
+	    if ( i==0 ) td->stemsnapv[0] = 1234567;
+	  break;
+	  case (12<<8)+14:
+	    td->forcebold = stack[sp-1];
+	  break;
+	  case (12<<8)+15:		/* obsolete */
+	    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 );
+	    info->bad_cff = true;
+	  break;
+	}
+    }
+
+    if ( td->subrsoff!=-1 ) {
+	fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET);
+	readcffsubrs(ttf,&td->local_subrs,info);
+    }
+}
+
+static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start,
+	struct ttfinfo *info, struct topdicts *parent_dict) {
+    uint16 count = getushort(ttf);
+    int offsize;
+    uint32 *offsets;
+    struct topdicts **dicts;
+    int i;
+
+    if ( count==0 )
+return( NULL );
+    offsets = malloc((count+1)*sizeof(uint32));
+    offsize = getc(ttf);
+    for ( i=0; i<=count; ++i )
+	offsets[i] = getoffset(ttf,offsize);
+    dicts = malloc((count+1)*sizeof(struct topdicts *));
+    for ( i=0; i<count; ++i ) {
+	if (fontnames != NULL && fontnames[i] == NULL) {
+		LogError(_("Number of CFF font names is less than dict size: %d < %d"), i, count);
+		break;
+	}
+	dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL,
+		offsets[i+1]-offsets[i], info);
+	if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) {
+	    MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix);
+	}
+	dicts[i]->cff_start = cff_start;
+    }
+    dicts[i] = NULL;
+    free(offsets);
+return( dicts );
+}
+
+static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) {
+    if ( sid==-1 ) // Default value, indicating it's not present
+return( NULL );
+    else if (sid < 0) {
+        LogError(_("Bad sid %d (0 <= sid < %d)\n"), sid, scnt+nStdStrings);
+        if (info != NULL)
+            info->bad_cff = true;
+        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 );
+	if ( info!=NULL ) info->bad_cff = true;
+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;
+    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 ? (char **)AdobeStandardEncoding : (char **)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);
+        /* Mask off high (additional encoding bit) and check format type */
+	if ( (format&0x7f)==0 ) {
+            /* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
+	    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);
+            /* CFF encodings start with glyph_id 1 since 0 is always .notdef */
+	    pos = 1;
+            /* Parse format 1 code ranges */
+	    for ( i=0; i<cnt ; ++i ) {
+                /* next byte is code of first character in range */
+		first = getc(ttf);
+                /* next byte is the number of additional characters in range */
+		last = first + getc(ttf);
+		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 ( info!=NULL ) info->bad_cff = true;
+	}
+        /* if additional encoding bit set, add all additional encodings */
+	if ( format&0x80 ) {
+	    cnt = getc(ttf);
+	    for ( i=0; i<cnt; ++i ) {
+		dupenc = getc(ttf);
+		sid = getushort(ttf);
+		name = getsid(sid,strings,scnt,info);
+		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,struct ttfinfo *info) {
+    int len = dict->glyphs.cnt;
+    int i;
+    int format, cnt, j, first;
+
+    i = 0;
+    if ( dict->charsetoff==0 ) {
+	/* ISO Adobe charset */
+	dict->charset = malloc(len*sizeof(uint16));
+	for ( i=0; i<len && i<=228; ++i )
+	    dict->charset[i] = i;
+    } else if ( dict->charsetoff==1 ) {
+	/* Expert charset */
+	dict->charset = malloc((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 = malloc((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 = malloc(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 && i<len; ++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 );
+	    if ( info!=NULL ) info->bad_cff = true;
+	}
+    }
+    while ( i<len ) dict->charset[i++] = 0;
+}
+
+static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) {
+    uint8 *fdselect = calloc(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") );
+		    if ( info!=NULL ) info->bad_cff = true;
+		} else
+		    fdselect[j] = fd;
+	    }
+	    first = end;
+	}
+    } else {
+	LogError( _("Didn't understand format for fdselect %d\n"), format );
+	if ( info!=NULL ) info->bad_cff = true;
+    }
+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 = malloc((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 must_be_even) {
+    int i,j;
+    char *pt, *ret;
+
+    for ( i=size-1; i>=0 && array[i]==0; --i );
+    if ( i==-1 )
+return( NULL );
+    if ( i==0 && array[0]==1234567 ) /* Special marker for a null array */
+return( copy( "[]" ));
+    if ( must_be_even && !(i&1) && array[i]<0 )
+	++i;			/* Someone gave us a bluevalues of [-20 0] and we reported [-20] */
+    ret = pt = malloc((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[20];
+    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[20];
+    if ( val==0 )
+return;
+    sprintf( buf,"[%d]", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void privateaddreal(struct psdict *private,char *key,double val,double def) {
+    char buf[40];
+    if ( val==def )
+return;
+    sprintf( buf,"%g", val );
+    privateadd(private,key,copy(buf));
+}
+
+static void cffprivatefillup(struct psdict *private, struct topdicts *dict) {
+    private->cnt = 14;
+    private->keys = malloc(14*sizeof(char *));
+    private->values = malloc(14*sizeof(char *));
+    privateadd(private,"BlueValues",
+	    realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true));
+    privateadd(private,"OtherBlues",
+	    realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true));
+    privateadd(private,"FamilyBlues",
+	    realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true));
+    privateadd(private,"FamilyOtherBlues",
+	    realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true));
+    privateaddreal(private,"BlueScale",dict->bluescale,0.039625);
+    privateaddreal(private,"BlueShift",dict->blueshift,7);
+    privateaddreal(private,"BlueFuzz",dict->bluefuzz,1);
+    privateaddintarray(private,"StdHW",dict->stdhw);
+    privateaddintarray(private,"StdVW",dict->stdvw);
+    privateadd(private,"StemSnapH",
+	    realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false));
+    privateadd(private,"StemSnapV",
+	    realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false));
+    if ( dict->forcebold )
+	privateadd(private,"ForceBold",copy("true"));
+    if ( dict->forceboldthreshold!=0 )
+	privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0);
+    privateaddint(private,"LanguageGroup",dict->languagegroup);
+    privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06);
+}
+
+static SplineFont *cffsffillup(struct topdicts *subdict, char **strings,
+	int scnt, struct ttfinfo *info) {
+    SplineFont *sf = SplineFontEmpty();
+    int emsize;
+    static int nameless;
+
+    sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info));
+    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,info));
+    else
+	sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info));
+    sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info));
+    sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info));
+    sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info));
+    sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info));
+    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 = calloc(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] );
+    info->ascent = .8*info->emsize;
+    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,info));
+    else if ( dict->notice!=-1 )
+	info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info));
+    if ( dict->familyname!=-1 ) {
+	free(info->familyname);
+	info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info));
+    }
+    if ( dict->fullname!=-1 ) {
+	free(info->fullname);
+	info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info));
+    }
+    if ( dict->weight!=-1 ) {
+	free(info->weight);
+	info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info));
+    }
+    if ( dict->version!=-1 ) {
+	free(info->version);
+	info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info));
+    }
+    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 = calloc(1,sizeof(struct psdict));
+	cffprivatefillup(info->private,dict);
+    }
+    if ( dict->ros_registry!=-1 ) {
+	info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info));
+	info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info));
+	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 = calloc(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));
+	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 deprecated 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 = calloc(j+1,sizeof(SplineFont *));
+    for ( j=0; subdicts[j]!=NULL; ++j )  {
+	info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info);
+	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 = calloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *));
+    /*encmap->encmax = encmap->enccount;*/
+    /*encmap->map = malloc(encmap->enccount*sizeof(int));*/
+    /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/
+
+    info->chars = calloc(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;
+	info->chars[i]->altuni = CIDSetAltUnis(map,cid);
+	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;
+	}
+	ff_progress_next();
+    }
+    /* 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" ));
+	info->bad_cff = true;
+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,info);
+    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,info, NULL);
+	/* String index is just the same as fontname index */
+    strings = readcfffontnames(ttf,&scnt,info);
+    readcffsubrs(ttf,&gsubs,info );
+    /* 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,info);
+    }
+    if ( dicts[which]->private_offset!=-1 )
+	readcffprivate(ttf,dicts[which],info);
+    if ( dicts[which]->charsetoff!=-1 )
+	readcffset(ttf,dicts[which],info);
+    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,info,dicts[which]);
+	fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET);
+	fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info);
+	for ( j=0; subdicts[j]!=NULL; ++j ) {
+	    if ( subdicts[j]->private_offset!=-1 )
+		readcffprivate(ttf,subdicts[j],info);
+	    if ( subdicts[j]->charsetoff!=-1 )
+		readcffset(ttf,subdicts[j],info);
+	}
+	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]);
+    }
+
+    if (fontnames[0] != NULL) {
+	free(fontnames[0]);
+	TopDictFree(dicts[0]);
+    }
+
+    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 int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) {
+    FontDict *fd;
+    FILE *tmp;
+    int i;
+    SplineChar *sc;
+
+    fseek(ttf,info->typ1_start,SEEK_SET);
+/* There appear to be about 20 bytes of garbage (well, I don't know what they */
+/*  mean, so they are garbage to me) before the start of the PostScript. But */
+/*  it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */
+/*  in the first few bytes of the file, and skip to there if found */
+    { char buffer[41];
+	fread(buffer,1,sizeof(buffer),ttf);
+	buffer[40] = '\0';
+	for ( i=39; i>=0; --i )
+	    if ( buffer[i]=='%' && buffer[i+1]=='!' )
+	break;
+	if ( i<0 )
+	    i = 0;
+	fseek(ttf,info->typ1_start+i,SEEK_SET);
+    }
+    
+    tmp = tmpfile();
+    for ( i=0; i<info->typ1_length; ++i )
+	putc(getc(ttf),tmp);
+    rewind(tmp);
+    fd = _ReadPSFont(tmp);
+    fclose(tmp);
+    if ( fd!=NULL ) {
+	SplineFont *sf = SplineFontFromPSFont(fd);
+	PSFontFree(fd);
+	info->emsize = (sf->ascent+sf->descent);
+	info->ascent = sf->ascent;
+	info->descent = sf->descent;
+	if ( sf->subfontcnt!=0 ) {
+	    info->subfontcnt = sf->subfontcnt;
+	    info->subfonts = sf->subfonts;
+	    info->cidregistry = copy(sf->cidregistry);
+	    info->ordering = copy(sf->ordering);
+	    info->supplement = sf->supplement;
+	    info->cidfontversion = sf->cidversion;
+	    sf->subfonts = NULL;
+	    sf->subfontcnt = 0;
+	} else {
+	    info->chars = sf->glyphs;
+	    info->glyph_cnt = sf->glyphcnt;
+	    for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL )
+		sc->parent = NULL;
+	    sf->glyphs = NULL;
+	    sf->glyphcnt = 0;
+	}
+	SplineFontFree(sf);
+return( true );
+    }
+return( false );
+}
+
+static void readttfwidths(FILE *ttf,struct ttfinfo *info) {
+    int i,j;
+    int lastwidth = info->emsize, lsb;
+    /* 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? */
+    /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */
+    /*  do move the outline */
+    int check_width_consistency = info->cff_start!=0 && info->glyph_start==0;
+    SplineChar *sc;
+    real trans[6];
+
+    memset(trans,0,sizeof(trans));
+    trans[0] = trans[3] = 1;
+
+    fseek(ttf,info->hmetrics_start,SEEK_SET);
+    for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) {
+	lastwidth = getushort(ttf);
+	lsb = (short) getushort(ttf);
+	if ( (sc = info->chars[i])!=NULL ) {	/* can happen in ttc files */
+	    if ( lastwidth>info->advanceWidthMax && info->hhea_start!=0 ) {
+		if ( !info->wdthcomplain || (info->openflags&of_fontlint)) {
+		    if ( info->fontname!=NULL && sc->name!=NULL )
+			LogError(_("In %s, the advance width (%d) for glyph %s is greater than the maximum (%d)\n"),
+				info->fontname, lastwidth, sc->name, info->advanceWidthMax );
+		    else
+			LogError(_("In GID %d the advance width (%d) is greater than the stated maximum (%d)\n"),
+				i, lastwidth, info->advanceWidthMax );
+		    if ( !(info->openflags&of_fontlint) )
+			LogError(_("  Subsequent errors will not be reported.\n") );
+		    info->wdthcomplain = true;
+		}
+	    }
+	    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 );
+		info->bad_metrics = true;
+		check_width_consistency = false;
+	    }
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+    if ( i==0 ) {
+	LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+	
+    for ( j=i; j<info->glyph_cnt; ++j ) {
+	if ( (sc = info->chars[j])!=NULL ) {	/* In a ttc file we may skip some */
+	    sc->width = lastwidth;
+	    sc->widthset = true;
+	    if ( info->apply_lsb ) {
+		lsb = (short) getushort(ttf);
+		if ( sc->lsidebearing!=lsb ) {
+		    trans[4] = lsb-sc->lsidebearing;
+		    SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
+		}
+	    }
+	}
+    }
+}
+
+static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) {
+    BDFFont *bdf;
+    int i, cnt;
+    double scaled_sum;
+
+    if ( info->bitmaps==NULL )
+return;
+    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;
+	}
+    }
+}
+
+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 ( i==0 ) {
+	LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") );
+	info->bad_metrics = true;
+    }
+
+    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;
+    }
+
+}
+
+static int modenc(int enc,int modtype) {
+return( enc );
+}
+
+static int badencoding(struct ttfinfo *info) {
+    if ( !info->bad_cmap ) {
+	LogError(_("Bad encoding information in 'cmap' table."));
+	info->bad_cmap = true;
+    }
+return( -1 );
+}
+
+static int umodenc(int enc,int modtype, struct ttfinfo *info) {
+    if ( modtype==-1 )
+return( -1 );
+    if ( modtype<=1 /* Unicode */ ) {
+	/* No conversion needed, already unicode */;
+    } 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 if ( enc<255 ) {
+	    /* This is erroneous as I understand SJIS */
+	    enc = badencoding(info);
+	} else if (enc >= 0xeaa5) {
+        /* Encoded value is outside SJIS range */
+        /* If this happens, it's likely that it's actually CP932 encoded */
+        /* Todo: Detect CP932 encoding earlier and apply that instead of SJIS */
+        enc = badencoding(info);
+	} 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;
+	    }
+	    if ( ch1<0x21 || ch2<0x21 || ch1>0x7e || ch2>0x7e )
+		enc = badencoding(info);
+	    else
+		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 = badencoding(info);
+    } 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 = badencoding(info);
+    } 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 = badencoding(info);
+    } else if ( modtype==6 /* Johab */ ) {
+	if ( enc>0x8400 )
+	    enc = unicode_from_johab[enc-0x8400];
+	else if ( enc>0x100 )
+	    enc = badencoding(info);
+    }
+    if ( enc==0 )
+	enc = -1;
+return( enc );
+}
+
+struct cmap_encs {
+    int platform;
+    int specific;
+    int offset;
+    int lang;
+    int format;
+    Encoding *enc;
+};
+
+static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, len, ret=false;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    cmap_enc->format = format = getushort(ttf);
+    if ( format<0 || (format&1) || format>12 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"),
+		cmap_enc->platform, cmap_enc->specific, format );
+	info->bad_cmap = true;
+	ret = true;
+    }
+
+    if ( format!=12 && format!=10 && format!=8 ) {
+	len = getushort(ttf);
+	cmap_enc->lang = getushort(ttf);
+    } else {
+	/* padding */ getushort(ttf);
+	len = getlong(ttf);
+	cmap_enc->lang = getlong(ttf);
+    }
+    if ( len==0 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"),
+		cmap_enc->platform, cmap_enc->specific );
+	info->bad_cmap = true;
+	ret = true;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) {
+    uint32 here = ftell(ttf);
+    int format, ret=true;
+
+    fseek(ttf,offset,SEEK_SET);
+
+    format = getushort(ttf);
+    if ( format!=14 ) {
+	LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"),
+		0, 5, format );
+	info->bad_cmap = true;
+	ret = false;
+    }
+    fseek(ttf,here,SEEK_SET);
+return( ret );
+}
+
+static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map,
+	struct ttfinfo *info,int justinuse) {
+    int sub_table_len, vs_cnt, i, j, rcnt, gid, cur_gid;
+    struct vs_data { int vs; uint32 def, non_def; } *vs_data;
+    SplineChar *sc;
+
+    fseek(ttf,vs_map,SEEK_SET);
+    /* We/ve already checked the format is 14 */ getushort(ttf);
+    sub_table_len = getlong(ttf);
+    vs_cnt = getlong(ttf);
+    vs_data = malloc(vs_cnt*sizeof(struct vs_data));
+    for ( i=0; i<vs_cnt; ++i ) {
+	vs_data[i].vs = get3byte(ttf);
+	vs_data[i].def = getlong(ttf);
+	vs_data[i].non_def = getlong(ttf);
+    }
+
+    for ( i=0; i<vs_cnt; ++i ) {
+	if ( vs_data[i].def!=0 && justinuse==git_normal ) {
+	    fseek(ttf,vs_map+vs_data[i].def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int start_uni = get3byte(ttf);
+		int cnt = getc(ttf);
+		int uni;
+		for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) {
+		    SplineChar *sc;
+		    struct altuni *altuni;
+		    for ( gid = 0; gid<info->glyph_cnt; ++gid ) {
+			if ( (sc = info->chars[gid])!=NULL ) {
+			    if ( sc->unicodeenc==uni )
+		    break;
+			    for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next )
+				if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 )
+			    break;
+			    if ( altuni!=NULL )
+		    break;
+			}
+		    }
+		    if ( gid==info->glyph_cnt ) {
+			LogError( _("No glyph with unicode U+%05x in font\n"),
+				uni );
+			info->bad_cmap = true;
+		    } else {
+			altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+	if ( vs_data[i].non_def!=0 ) {
+	    fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET);
+	    rcnt = getlong(ttf);
+	    for ( j=0; j<rcnt; ++j ) {
+		int uni = get3byte(ttf);
+		int curgid = getushort(ttf);
+		if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0)
+			info->inuse[curgid] = 1;
+		} else if ( justinuse==git_justinuse ) {
+		    if ( curgid<info->glyph_cnt && curgid>=0 &&
+			    (sc=info->chars[curgid])!=NULL && sc->name==NULL ) {
+			char buffer[32];
+			sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs );
+			sc->name = copy(buffer);
+		    }
+		} else {
+		    if ( curgid>=info->glyph_cnt || curgid<0 ||
+			    info->chars[curgid]==NULL ) {
+			LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"),
+				cur_gid );
+			info->bad_cmap = true;
+		    } else {
+			SplineChar *sc = info->chars[curgid];
+			struct altuni *altuni = chunkalloc(sizeof(struct altuni));
+			altuni->unienc = uni;
+			altuni->vs = vs_data[i].vs;
+			altuni->fid = 0;
+			altuni->next = sc->altuni;
+			sc->altuni = altuni;
+		    }
+		}
+	    }
+	}
+    }
+    free(vs_data);
+}
+
+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 int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) {
+    char buffer[500];
+    char **choices, *encname;
+    int i, ret;
+    static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"),
+	N_("Script|Arabic"), N_("Script|Hebrew"),  N_("Script|Greek"),
+/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */
+/* GT:  docs though */
+	N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"),
+/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL,
+/* 20*/	NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"),
+	NULL, NULL, NULL, N_("Script|Central European"),
+/* 30*/ NULL, NULL, NULL };
+
+    choices = malloc(enccnt*sizeof(char *));
+    for ( i=0; i<enccnt; ++i ) {
+	encname = NULL;
+	if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) {
+	    encname = macscripts[cmap_encs[i].specific];
+	    if ( encname!=NULL )
+		encname = S_(encname);
+	} else if ( cmap_encs[i].platform==0 ) {
+	    switch ( cmap_encs[i].specific ) {
+	      case 0:
+		encname = N_("Unicode 1.0");
+	      break;
+	      case 1:
+		encname = N_("Unicode 1.1");
+	      break;
+	      case 2:
+		encname = N_("ISO 10646:1993");
+	      break;
+	      case 3:
+		encname = N_("Unicode 2.0+, BMP only");
+	      break;
+	      case 4:
+		encname = N_("Unicode 2.0+, all planes");
+	      break;
+	    }
+	} else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 )
+	    encname = N_("\"Symbol\"");
+	if ( encname==NULL )
+	    encname = cmap_encs[i].enc->enc_name;
+
+	sprintf(buffer,"%d (%s) %d %s %s  %s",
+		cmap_encs[i].platform,
+		    cmap_encs[i].platform==0 ? _("Unicode") :
+		    cmap_encs[i].platform==1 ? _("Apple") :
+		    cmap_encs[i].platform==2 ? _("ISO (Deprecated)") :
+		    cmap_encs[i].platform==3 ? _("MicroSoft") :
+		    cmap_encs[i].platform==4 ? _("Custom") :
+		    cmap_encs[i].platform==7 ? _("FreeType internals") :
+					       _("Unknown"),
+		cmap_encs[i].specific,
+		encname,
+		cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "",
+		cmap_encs[i].format == 0 ? "Byte encoding table" :
+		cmap_encs[i].format == 2 ? "High-byte mapping through table" :
+		cmap_encs[i].format == 4 ? "Segment mapping to delta values" :
+		cmap_encs[i].format == 6 ? "Trimmed table mapping" :
+		cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" :
+		cmap_encs[i].format == 10 ? "Trimmed array" :
+		cmap_encs[i].format == 12 ? "Segmented coverage" :
+		    "Unknown format" );
+	choices[i] = copy(buffer);
+    }
+    ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def,
+	    _("Pick a CMap subtable"));
+    for ( i=0; i<enccnt; ++i )
+	free(choices[i]);
+    free(choices);
+return( ret );
+}
+
+/* 'cmap' table: readttfcmap */
+static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) {
+    int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc;
+    int nencs, version, usable_encs;
+    Encoding *enc = &custom;
+    const int32 *trans=NULL;
+    enum uni_interp interp = ui_none;
+    int platform, specific;
+    int offset, encoff=0;
+    int format, len;
+    uint32 vs_map=0;
+    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;
+    struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap;
+    extern int ask_user_for_cmap;
+
+    fseek(ttf,info->encoding_start,SEEK_SET);
+    version = getushort(ttf);
+    nencs = getushort(ttf);
+    if ( version!=0 && nencs==0 )
+	nencs = version;		/* Sometimes they are backwards */ /* Or was I just confused early on? */
+    cmap_encs = malloc(nencs*sizeof(struct cmap_encs));
+    for ( i=usable_encs=0; i<nencs; ++i ) {
+	cmap_encs[usable_encs].platform =  getushort(ttf);
+	cmap_encs[usable_encs].specific = getushort(ttf);
+	cmap_encs[usable_encs].offset = getlong(ttf);
+	if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) {
+	    /* This isn't a true encoding. */
+	    /* It's an optional set of encoding modifications (sort of) */
+	    /*  applied to a format 4/10 encoding (unicode BMP/Full) */
+	    if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) )
+		vs_map = info->encoding_start+cmap_encs[usable_encs].offset;
+    continue;
+	}
+	temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific);
+	if ( temp==NULL )	/* iconv doesn't support this. Some sun iconvs seem limited */
+	    temp = FindOrMakeEncoding("Custom");
+	cmap_encs[usable_encs].enc = temp;
+	if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset,
+		&cmap_encs[usable_encs],info))
+    continue;
+	++usable_encs;
+    }
+    if ( usable_encs==0 ) {
+	LogError( _("Could not find any valid encoding tables" ));
+	free(cmap_encs);
+return;
+    }
+    def = -1;
+    enc = &custom;
+    unicode_cmap = unicode4_cmap = -1;
+    for ( i=0; i<usable_encs; ++i ) {
+	temp = cmap_encs[i].enc;
+	platform = cmap_encs[i].platform;
+	specific = cmap_encs[i].specific;
+	offset = cmap_encs[i].offset;
+
+	if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */
+	    enc = temp;
+	    def = i;
+	    unicode4_cmap = i;
+	} 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;
+	    def = i;
+	} else if ( platform==3 && specific==0 && enc->is_custom ) {
+	    /* Only select symbol if we don't have something better */
+	    enc = temp;
+	    def = i;
+	    /* 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 ) {
+	    enc = temp;
+	    def = i;
+	} else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) &&
+		!enc->is_unicodefull &&
+		(prefer_cjk_encodings || !enc->is_unicodebmp) ) {
+	    enc = temp;
+	    def = i;
+	} 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;
+	    def = i;
+	}
+	if ( (platform==3 && specific==1) ||
+		(platform==0 && specific==3))
+	    unicode_cmap = i;
+    }
+
+    if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 )
+	i = def;
+
+    if ( i==-1 ) {
+	if ( justinuse==git_normal )
+	    LogError( _("Could not find a usable encoding table" ));
+	free(cmap_encs);
+return;
+    }
+
+    info->platform = cmap_encs[i].platform;
+    info->specific = cmap_encs[i].specific;
+
+    desired_cmaps[0] = cmap_encs[i]; dcnt = 1;
+    if ( unicode4_cmap!=-1 ) {
+	if ( i!=unicode4_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode4_cmap];
+	    ++dcnt;
+	}
+    } else if ( unicode_cmap!=-1 ) {
+	if ( i!=unicode_cmap ) {
+	    desired_cmaps[1] = cmap_encs[unicode_cmap];
+	    ++dcnt;
+	}
+    } else {
+	if ( i!=def && def!=-1 ) {
+	    desired_cmaps[1] = cmap_encs[def];
+	    ++dcnt;
+	}
+    }
+
+    map = NULL;
+    if ( justinuse==git_justinuse ) {
+	dcmap_cnt = usable_encs;
+	dcmap = cmap_encs;
+    } else {
+	dcmap_cnt = dcnt;
+	dcmap = desired_cmaps;
+    }
+    for ( dc=dcmap_cnt-1; dc>=0; --dc ) {
+	/* if justinuse then look at all cmaps and tick the glyphs they use */
+	/* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */
+	/* contains both the encoding and the source for unicode encodings */
+	/* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */
+	/*  and when dc==1 we are setting up the unicode code points */
+	int dounicode = (dc==dcmap_cnt-1);
+	enc = dcmap[dc].enc;
+	encoff = dcmap[dc].offset;
+
+	mod = 0;
+	if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 ))
+	    mod = dcmap[dc].specific;
+	else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25))
+	    mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3;		/* convert to ms specific */
+	if ( dc==0 && justinuse==git_normal ) {
+	    interp = interp_from_encoding(enc,ui_none);
+	    info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc);
+	    info->uni_interp = interp;
+	}
+
+	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 ( format==0 ) {
+	    if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) {
+		map->map = realloc(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;
+	    if ( trans==NULL && dcmap[dc].platform==1 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    for ( i=0; i<256 && i<len-6; ++i )
+		if ( justinuse==git_normal ) {
+		    if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) {
+			if ( map!=NULL )
+			    map->map[i] = table[i];
+			if ( dounicode && 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 ) {
+	    segCount = getushort(ttf)/2;
+	    /* searchRange = */ getushort(ttf);
+	    /* entrySelector = */ getushort(ttf);
+	    /* rangeShift = */ getushort(ttf);
+	    endchars = malloc(segCount*sizeof(uint16));
+	    used = calloc(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 = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		startchars[i] = getushort(ttf);
+	    delta = malloc(segCount*sizeof(uint16));
+	    for ( i=0; i<segCount; ++i )
+		delta[i] = getushort(ttf);
+	    rangeOffset = malloc(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 = malloc(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==git_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));
+			    info->bad_cmap = true;
+			} else {
+			    int uenc = umodenc(j,mod,info);
+			    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 );
+			            info->bad_cmap = true;
+			            badencwarned = true;
+				}
+			    } else {
+				if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 )
+				    info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc;
+			        if ( map!=NULL && 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==git_normal ) {
+				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, dcmap[dc].platform, dcmap[dc].specific );
+				info->bad_cmap = true;
+			    }
+			    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));
+				info->bad_cmap = true;
+			    } else if ( justinuse==git_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));
+				info->bad_cmap = true;
+			    } else {
+				int uenc = umodenc(j,mod,info);
+				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 );
+			                info->bad_cmap = true;
+					badencwarned = true;
+				    }
+				} else {
+				    if ( uenc!=-1 && dounicode ) used[uenc] = true;
+				    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+					info->chars[index]->unicodeenc = uenc;
+				    if ( map!=NULL && 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") );
+		    info->bad_cmap = true;
+		}
+	    }
+	    free(glyphs);
+	    free(rangeOffset);
+	    free(delta);
+	    free(startchars);
+	    free(endchars);
+	    free(used);
+	} else if ( format==6 ) {
+	    /* trimmed array 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;
+	    first = getushort(ttf);
+	    count = getushort(ttf);
+	    trans = enc->unicode;
+	    if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 )
+		trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1);
+	    if ( justinuse==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else {
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i;
+		    if ( map!=NULL && 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 = malloc((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 = malloc(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==git_justinuse )
+			    info->inuse[index] = 1;
+			else if ( info->chars[index]==NULL )
+			    /* Do Nothing */;
+			else {
+			    int lenc = modenc(i,mod);
+			    if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				info->chars[index]->unicodeenc = i;
+			    if ( map!=NULL && 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==git_justinuse )
+				info->inuse[index] = 1;
+			    else if ( info->chars[index]==NULL )
+				/* Do Nothing */;
+			    else {
+				if ( dounicode && info->chars[index]->unicodeenc==-1 )
+				    info->chars[index]->unicodeenc = umodenc(enc,mod,info);
+				if ( map!=NULL && 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==git_justinuse )
+		    for ( i=start; i<=end; ++i )
+			info->inuse[startglyph+i-start]= 1;
+		else
+		    for ( i=start; i<=end; ++i ) {
+			int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000;
+			sc = info->chars[startglyph+i-start];
+			if ( dounicode && sc->unicodeenc==-1 )
+			    sc->unicodeenc = uenc;
+			if ( map!=NULL && sc->unicodeenc < map->enccount )
+			    map->map[uenc] = 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==git_justinuse )
+		for ( i=0; i<count; ++i )
+		    info->inuse[getushort(ttf)]= 1;
+	    else
+		for ( i=0; i<count; ++i ) {
+		    int gid = getushort(ttf);
+		    if ( dounicode )
+			info->chars[gid]->unicodeenc = first+i;
+		    if ( map!=NULL && 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==git_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") );
+			    info->bad_cmap = true;
+		    break;
+			} else {
+			    if ( dounicode )
+				info->chars[startglyph+i-start]->unicodeenc = i;
+			    if ( map!=NULL && i < map->enccount )
+				map->map[i] = startglyph+i-start;
+			}
+		    }
+	    }
+	}
+    }
+    free(cmap_encs);
+    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;
+    info->vs_start = vs_map;
+    if ( vs_map!=0 )
+	ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse);
+    if ( justinuse==git_normal ) {
+	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, sel;
+
+    fseek(ttf,info->os2_start,SEEK_SET);
+    info->os2_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 */
+    info->pfminfo.unicoderanges[0] = getlong(ttf);
+    info->pfminfo.unicoderanges[1] = getlong(ttf);
+    info->pfminfo.unicoderanges[2] = getlong(ttf);
+    info->pfminfo.unicoderanges[3] = getlong(ttf);
+    info->pfminfo.hasunicoderanges = true;
+    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);
+    sel = getushort(ttf);
+    if ( info->os2_version>=4 ) {
+	info->use_typo_metrics = (sel&128)?1:0;
+	info->weight_width_slope_only = (sel&256)?1:0;
+    }
+    /* Clear the bits we don't support in stylemap and set it. */
+    info->pfminfo.stylemap = sel & 0x61;
+    /* 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;
+    }
+    info->pfminfo.os2_typolinegap = 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;
+    if ( info->os2_version>=1 ) {
+	info->pfminfo.codepages[0] = getlong(ttf);
+	info->pfminfo.codepages[1] = getlong(ttf);
+	info->pfminfo.hascodepages = true;
+	if ( info->os2_version>=2 ) {
+	info->pfminfo.os2_xheight = (short) getushort(ttf);
+	info->pfminfo.os2_capheight = (short) getushort(ttf);
+	}
+    }
+
+    if ( info->os2_version==0 ) {
+	LogError(_("Windows will reject fonts with an OS/2 version number of 0\n"));
+	info->bad_os2_version = true;
+    } else if ( info->os2_version==1 && info->cff_start!=0 ) {
+	LogError(_("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"));
+	info->bad_os2_version = true;
+    }
+}
+
+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;
+    int anynames = false;
+
+    ff_progress_change_line2(_("Reading Names"));
+
+    /* 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 = malloc(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);
+    /*
+     * Due to the legacy of two formats, there are two underlinePosition
+     * attributes in an OpenType CFF font, one being stored in the CFF table.
+     * FontForge due to its pfa heritage will only keep the PostScript/CFF
+     * underlinePosition in the SplineFont so we'll calculate that here if we
+     * are indeed working on a TTF.
+     * If we have a CFF font, cffinfofillup() has already read the appropriate
+     * data and so we don't rewind it (if info->uwidth is odd we are possibly
+     * introducing a rounding error).
+     */
+	if (info->cff_start==0) {
+	info->upos = (short) getushort(ttf);
+	info->uwidth = (short) getushort(ttf);
+	info->upos -= info->uwidth/2;		/* 'post' defn of this field is different from FontInfo defn and I didn't notice */
+	}
+	info->isFixedPitch = getlong(ttf);
+	/* mem1 = */ getlong(ttf);
+	/* mem2 = */ getlong(ttf);
+	/* mem3 = */ getlong(ttf);
+	/* mem4 = */ getlong(ttf);
+	if ( format==0x00020000 ) {
+	    gc = getushort(ttf);
+	    indexes = calloc(65536,sizeof(uint16));
+	    /* the index table is backwards from the way I want to use it */
+	    gcbig = 0;
+	    for ( i=0; i<gc; ++i ) {
+		val = getushort(ttf);
+		if ( val<0 )		/* Don't crash on EOF */
+	    break;
+		indexes[val] = 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]); /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    gcbig += 258;
+	    for ( i=258; i<gcbig; ++i ) {
+		char *nm;
+		len = getc(ttf);
+		if ( len<0 )		/* Don't crash on EOF */
+	    break;
+		nm = malloc(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; /* Too many fonts have badly named glyphs to deduce encoding from name */
+	    }
+	    free(indexes);
+	    anynames = true;
+	}
+    }
+
+    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;
+	}
+    }
+
+    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;
+	/* I used to check for glyphs with bad names (ie. names indicative of */
+	/*  another unicode code point than the one applied to the glyph) but */
+	/*  this proves too early for that check, as we don't have the altunis*/
+	/*  figured out yet. So I've moved that into its own routine later */
+	}
+	/* 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 ( anynames ) {
+		for ( j=0; j<info->glyph_cnt; ++j ) {
+		    if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) {
+			if ( strcmp(info->chars[j]->name,name)==0 ) {
+			    name = NULL;
+		break;
+			}
+		    }
+		}
+	    }
+	}
+	ff_progress_next();
+	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->vs_start!=0 )
+	ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames);
+    if ( i>=0 && info->gsub_start!=0 )
+	GuessNamesFromGSUB(ttf,info);
+    if ( i>=0 && info->math_start!=0 )
+	GuessNamesFromMATH(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->backmap[i]!=-1 )
+	    sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific,
+		    (int) info->map->backmap[i] );
+	else
+	    sprintf( buffer, "glyph%d", i );
+	info->chars[i]->name = copy(buffer);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+static void readttfgasp(FILE *ttf,struct ttfinfo *info) {
+    int i, cnt;
+
+    if ( info->gasp_start==0 )
+return;
+
+    fseek(ttf,info->gasp_start,SEEK_SET);
+    info->gasp_version = getushort(ttf);
+    if ( info->gasp_version!=0 && info->gasp_version!=1 )
+return;			/* We only support 'gasp' versions 0&1 (no other versions currently) */
+    info->gasp_cnt = cnt = getushort(ttf);
+    if ( cnt==0 )
+return;
+    info->gasp = malloc(cnt*sizeof(struct gasp));
+    for ( i=0; i<cnt; ++i ) {
+	info->gasp[i].ppem = getushort(ttf);
+	info->gasp[i].flags = getushort(ttf);
+    }
+}
+
+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 layer,int pnum,BasePoint *pos,
+	RefChar *bound) {
+    SplineSet *ss;
+    SplinePoint *sp;
+    int last=0, ret;
+    RefChar *refs;
+
+    for ( ss = sc->layers[layer].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+1;
+	    else if ( sp->ttfindex!=0xffff )
+		last = sp->ttfindex+1;
+	    if ( sp->next==NULL )
+	break;
+	    sp = sp->next->to;
+	    if ( sp==ss->first )
+	break;
+	}
+    }
+    for ( refs=sc->layers[layer].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,ly_fore,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;
+	if ( last>pnum ) {
+	    IError("Point match failure last=%d, pnum=%d", last, pnum );
+return( 0x800000 );
+	}
+    }
+return( last );		/* Count of number of points in the character */
+}
+
+static void ttfPointMatch(SplineChar *sc,RefChar *rf) {
+    BasePoint sofar, inref;
+
+    if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 ||
+	    ttfFindPointInSC(rf->sc,ly_fore,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,ly_fore);
+	    SCMakeDependent(chars[i],ref->sc);
+	    prev = ref;
+	}
+    }
+    chars[i]->ticked = false;
+return( true );
+}
+
+static void ttfFixupReferences(struct ttfinfo *info) {
+    int i;
+
+    ff_progress_change_line2(_("Fixing up References"));
+    for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL )
+	info->chars[i]->ticked = false;
+    for ( i=0; i<info->glyph_cnt; ++i ) {
+	ttfFixupRef(info->chars,i);
+	ff_progress_next();
+    }
+    ff_progress_next_stage();
+}
+
+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 = malloc(len);
+    fseek(ttf,start,SEEK_SET);
+    fread(tab->data,1,len,ttf);
+    tab->next = info->tabs;
+    info->tabs = tab;
+}
+
+static int LookupListHasFeature(OTLookup *otl,uint32 tag) {
+    FeatureScriptLangList *feat;
+
+    while ( otl!=NULL ) {
+	for ( feat = otl->features; feat!=NULL; feat=feat->next )
+	    if ( feat->featuretag == tag )
+return( true );
+	otl = otl->next;
+    }
+return( false );
+}
+
+static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) {
+    int i;
+
+    /* Determine file size to check table offset bounds */
+    fseek(ttf,0,SEEK_END);
+    info->ttfFileSize = ftell(ttf);
+    fseek(ttf,0,SEEK_SET);
+
+    ff_progress_change_stages(3);
+    if ( !readttfheader(ttf,info,filename,&info->chosenname)) {
+return( 0 );
+    }
+    /* TrueType doesn't need this but opentype dictionaries do */
+    locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
+    switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
+    readttfpreglyph(ttf,info);
+    ff_progress_change_total(info->glyph_cnt);
+
+    /* If font only contains bitmaps, then only read bitmaps */
+    if ( (info->glyphlocations_start==0 || info->glyph_length==0) &&
+	    info->cff_start==0 && info->typ1_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[2] = _("_Cancel");
+	buts[3] = NULL;
+	choice = ff_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"));
+	if ( choice==2 ) {
+          switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	} else if ( choice==0 )
+	    info->cff_start=0;
+	else
+	    info->glyph_start = info->glyphlocations_start = 0;
+    }
+    if ( info->onlystrikes ) {
+	info->chars = calloc(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) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else if ( info->typ1_start!=0 ) {
+	if ( !readtyp1glyphs(ttf,info) ) {
+	    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+	}
+    } else {
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+return( 0 );
+    }
+    if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 )
+	TTFLoadBitmaps(ttf,info,info->onlyonestrike);
+    else if ( info->onlystrikes )
+	ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename );
+    if ( info->onlystrikes && info->bitmaps==NULL ) {
+	free(info->chars);
+	switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+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 */
+    /* Oops. It is meaningful for cid fonts. It just seemed redundant to me */
+    /*  but that was my ignorance. Adobe complains that FF doesn't read it */
+    /* (We've already (probably) set the unicodeencs of the glyphs according */
+    /*  to the cidmap files, but we can override that here. Mmm. what about a*/
+    /*  glyph in cidmap but not in cmap???? */
+    if ( /*info->cidregistry==NULL &&*/ info->encoding_start!=0 )
+	readttfencodings(ttf,info,git_normal);
+    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);
+    }
+    if ( info->base_start!=0 )
+	readttfbase(ttf,info);
+    else if ( info->bsln_start!=0 )
+	readttfbsln(ttf,info);
+    if ( info->gasp_start!=0 )
+	readttfgasp(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);
+    /* Load the 'kern' table if the GPOS table either didn't exist or didn't */
+    /*  contain any kerning info */
+    if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n')))
+	readttfkerns(ttf,info);
+    if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d')))
+	readttfopbd(ttf,info);
+    if ( info->gsub_start!=0 )
+	readttfgpossub(ttf,info,false);
+    if ( info->morx_start!=0 || info->mort_start!=0 )
+	readttfmort(ttf,info);
+    if ( info->jstf_start!=0 )
+	readttfjstf(ttf,info);
+
+    if ( info->pfed_start!=0 )
+	pfed_read(ttf,info);
+    if ( info->tex_start!=0 )
+	tex_read(ttf,info);
+    if ( info->math_start!=0 )
+	otf_read_math(ttf,info);
+    switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
+    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 check_dups) {
+    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);
+	    if (check_dups)
+		AltUniAdd(sf->glyphs[gid],uni);
+	    else
+		AltUniAdd_DontCheckDups(sf->glyphs[gid],uni);
+	}
+    }
+}
+
+static void NameConsistancyCheck(SplineFont *sf,EncMap *map) {
+    /* Many fonts seem to have glyph names which mean something other than */
+    /*  what the encoding says of the glyph */
+    /* I used to ask about fixing the names up, but people didn't like that */
+    /*  so now I just produce warnings */
+    int gid, uni;
+    SplineChar *sc;
+
+    for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
+	if ( sc->name!=NULL &&
+		strcmp(sc->name,".null")!=0 &&
+		strcmp(sc->name,"nonmarkingreturn")!=0 &&
+		(uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 &&
+		sc->unicodeenc != uni ) {
+	    if ( uni>=0xe000 && uni<=0xf8ff )
+		/* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */;
+	    else if ( uni<0x20 )
+		/* Nor about control characters */;
+	    else if ( sc->unicodeenc==-1 ) {
+	    } else {
+		/* Ah, but suppose there's an altuni? */
+		struct altuni *alt;
+		for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next );
+		if ( alt==NULL )
+		{
+                   if ( strcmp(sc->name,"alefmaksurainitialarabic")==0 ||
+                        strcmp(sc->name,"alefmaksuramedialarabic")==0 )
+                   {
+                      LogError( _("The names 'alefmaksurainitialarabic' and 'alefmaksuramedialarabic' in the Adobe Glyph List disagree with Unicode.  The use of these glyph names is therefore discouraged.\n") );
+                   } else {
+		      LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"),
+			    sc->name,sc->unicodeenc, uni);
+		   }
+		}
+		else if ( alt->vs==0 ) {
+		    alt->unienc = sc->unicodeenc;
+		    sc->unicodeenc = uni;
+		}
+	    }
+	}
+    }
+}
+
+static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) {
+    int i;
+    RefChar *rf, *prev, *next;
+    SplineChar *sc;
+
+    sf->glyphs = info->chars;
+    sf->glyphcnt = sf->glyphmax = info->glyph_cnt;
+    for ( i=0; i<sf->glyphcnt; ++i )
+	if ( (sc = sf->glyphs[i])!=NULL ) {
+	    sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2;
+	    sc->parent = sf;
+	}
+
+    /* A CFF font could contain type1 charstrings, or a type2 font could use */
+    /*  the deprecated 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,false);
+    NameConsistancyCheck(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->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->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true;
+    for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) {
+	SplineChar *sc = sf->glyphs[i];
+	sc->orig_pos = i;
+	sc->parent = sf;
+	sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true;
+    }
+    sf->grid.order2 = true;
+    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,ly_fore);
+    }
+
+    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 = malloc(v->tuple_count*sizeof(SplineFont *));
+    mm->positions = malloc(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 = calloc(v->tuple_count,sizeof(real));	/* Doesn't apply */
+    mm->axismaps = calloc(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 = malloc(3*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(v->axes[i].paircount*sizeof(real));
+	    mm->axismaps[i].designs = malloc(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 = malloc(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 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 = realloc(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 = malloc(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;
+}
+
+void TTF_PSDupsDefault(SplineFont *sf) {
+    struct ttflangname *english;
+    char versionbuf[40];
+
+    /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/
+    /*  we copied some of them (copyright, family, fullname, etc) into equiv */
+    /*  postscript entries in the sf. If we then use FontInfo and change the */
+    /*  obvious postscript entries we are left with the old ttf entries. If */
+    /*  we generate a ttf file and then load it the old values pop up. */
+    /* Solution: Anything we can generate by default should be set to NULL */
+    for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next );
+    if ( english==NULL )
+return;
+    if ( english->names[ttf_family]!=NULL && sf->familyname!=NULL &&
+	    strcmp(english->names[ttf_family],sf->familyname)==0 ) {
+	free(english->names[ttf_family]);
+	english->names[ttf_family]=NULL;
+    }
+    if ( english->names[ttf_copyright]!=NULL && sf->copyright!=NULL &&
+	    strcmp(english->names[ttf_copyright],sf->copyright)==0 ) {
+	free(english->names[ttf_copyright]);
+	english->names[ttf_copyright]=NULL;
+    }
+    if ( english->names[ttf_fullname]!=NULL && sf->fullname!=NULL &&
+	    strcmp(english->names[ttf_fullname],sf->fullname)==0 ) {
+	free(english->names[ttf_fullname]);
+	english->names[ttf_fullname]=NULL;
+    }
+    if ( sf->subfontcnt!=0 || sf->version!=NULL ) {
+	if ( sf->subfontcnt!=0 )
+	    sprintf( versionbuf, "Version %f", sf->cidversion );
+	else
+	    sprintf(versionbuf,"Version %.20s ", sf->version);
+	if ( english->names[ttf_version]!=NULL &&
+		strcmp(english->names[ttf_version],versionbuf)==0 ) {
+	    free(english->names[ttf_version]);
+	    english->names[ttf_version]=NULL;
+	}
+    }
+    if ( english->names[ttf_subfamily]!=NULL &&
+	    strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) {
+	free(english->names[ttf_subfamily]);
+	english->names[ttf_subfamily]=NULL;
+    }
+
+    /* User should not be allowed any access to this one, not ever */
+    free(english->names[ttf_postscriptname]);
+    english->names[ttf_postscriptname]=NULL;
+}
+
+static void ASCIIcheck(char **str) {
+
+    if ( *str!=NULL && !AllAscii(*str)) {
+	char *temp = StripToASCII(*str);
+	free(*str);
+	*str = temp;
+    }
+}
+
+static SplineFont *SFFillFromTTF(struct ttfinfo *info) {
+    SplineFont *sf, *_sf;
+    int i,k;
+    BDFFont *bdf;
+    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->layers[ly_fore].order2 = info->to_order2;
+    sf->layers[ly_back].order2 = info->to_order2;
+    sf->grid.order2 = info->to_order2;
+    sf->comments = info->fontcomments;
+    sf->fontlog = info->fontlog;
+    sf->cvt_names = info->cvt_names;
+
+    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->feat_names = info->feat_names;
+
+    sf->gasp_cnt = info->gasp_cnt;
+    sf->gasp = info->gasp;
+    sf->MATH = info->math;
+
+    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;
+
+    sf->mark_set_cnt = info->mark_set_cnt;
+    sf->mark_sets = info->mark_sets;
+    sf->mark_set_names = info->mark_set_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;
+    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->os2_version = info->os2_version;
+    sf->sfntRevision = info->sfntRevision;
+    sf->use_typo_metrics = info->use_typo_metrics;
+    sf->weight_width_slope_only = info->weight_width_slope_only;
+    sf->head_optimized_for_cleartype = info->optimized_for_cleartype;
+    sf->gasp_version = info->gasp_version;
+    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->gpos_lookups = info->gpos_lookups;
+    sf->gsub_lookups = info->gsub_lookups;
+
+    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;
+    sf->grid = info->guidelines;
+    sf->horiz_base = info->horiz_base;
+    sf->vert_base = info->vert_base;
+    sf->justify = info->justify;
+    for ( bdf = info->bitmaps; bdf!=NULL; bdf = bdf->next ) {
+	bdf->sf = sf;
+    }
+    SFDefaultAscent(sf);
+    if ( info->layers!=NULL ) {
+	info->layers[ly_fore] = sf->layers[ly_fore];
+	sf->layers[ly_fore].name = NULL;
+	if ( info->layers[ly_back].name==NULL )
+	    info->layers[ly_back].name = sf->layers[ly_back].name;
+	else
+	    free( sf->layers[ly_back].name );
+	free( sf->layers );
+	sf->layers = info->layers;
+	sf->layer_cnt = info->layer_cnt;
+    }
+	
+
+    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]->hasvmetrics = sf->hasvmetrics;
+	}
+    }
+    ASCIIcheck(&sf->copyright);
+    ASCIIcheck(&sf->familyname);
+    ASCIIcheck(&sf->weight);
+    ASCIIcheck(&sf->version);
+    
+    TTF_PSDupsDefault(sf);
+
+    /* I thought the languages were supposed to be ordered, but it seems */
+    /*  that is not always the case. Order everything, just in case */
+    { int isgpos; OTLookup *otl;
+    for ( isgpos=0; isgpos<2; ++isgpos )
+	for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next )
+	    otl->features = FLOrder(otl->features);
+    }
+
+    if ( info->variations!=NULL )
+	MMFillFromVAR(sf,info);
+
+    if ( info->cff_length!=0 && !sf->layers[ly_fore].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 );
+    }
+    /* should not be default as it confuses users */
+    /* SFRelativeWinAsDs(sf); */
+    free(info->savetab);
+
+    if ( info->openflags & of_fontlint ) {
+	k=0;
+	do {
+	    _sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
+	    for ( i=0; i<sf->glyphcnt; ++i ) {
+		if ( (sc = _sf->glyphs[i])!=NULL ) {
+		    DBounds b;
+		    SplineCharQuickBounds(sc,&b);
+		    if ( b.minx==0 && b.maxx==0 )
+			/* Skip it, no points */;
+		    else if ( b.minx < info->fbb[0] || b.miny < info->fbb[1] ||
+			    b.maxx > info->fbb[2] || b.maxy > info->fbb[3] ) {
+			LogError(_("A point in %s is outside the font bounding box data.\n"), sc->name );
+			info->bad_cff = true;
+		    }
+		    if ( info->isFixedPitch && i>2 && sc->width!=info->advanceWidthMax )
+			LogError(_("The advance width of %s (%d) does not match the font's advanceWidthMax (%d) and this is a fixed pitch font\n"),
+				sc->name, sc->width, info->advanceWidthMax );
+		}
+	    }
+	    ++k;
+	} while ( k<sf->subfontcnt );
+    }
+
+    sf->loadvalidation_state =
+	    (info->bad_ps_fontname	?lvs_bad_ps_fontname:0) |
+	    (info->bad_glyph_data	?lvs_bad_glyph_table:0) |
+	    (info->bad_cff		?lvs_bad_cff_table:0) |
+	    (info->bad_metrics		?lvs_bad_metrics_table:0) |
+	    (info->bad_cmap		?lvs_bad_cmap_table:0) |
+	    (info->bad_embedded_bitmap	?lvs_bad_bitmaps_table:0) |
+	    (info->bad_gx		?lvs_bad_gx_table:0) |
+	    (info->bad_ot		?lvs_bad_ot_table:0) |
+	    (info->bad_os2_version	?lvs_bad_os2_version:0)|
+	    (info->bad_sfnt_header	?lvs_bad_sfnt_header:0);
+return( sf );
+}
+
+SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, 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.use_typo_metrics = false;
+    info.weight_width_slope_only = false;
+    info.openflags = openflags;
+    info.fd = fd;
+    ret = readttf(ttf,&info,filename);
+    if ( !ret )
+return( NULL );
+return( SFFillFromTTF(&info));
+}
+
+SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) {
+    FILE *ttf;
+    SplineFont *sf;
+    char *temp=filename, *pt, *lparen, *rparen;
+
+    pt = strrchr(filename,'/');
+    if ( pt==NULL ) pt = filename;
+    if ( (lparen = strrchr(pt,'('))!=NULL &&
+	    (rparen = strrchr(lparen,')'))!=NULL &&
+	    rparen[1]=='\0' ) {
+	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,openflags,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,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);
+	if (cnt != EOF && cnt >= 0 && cnt < 0xFFFF) {
+		offsets = malloc(cnt*sizeof(int32));
+		for ( i=0; i<cnt; ++i )
+		    offsets[i] = getlong(ttf);
+		ret = malloc((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 {
+		LogError(_("Invalid font count in TTC %s."), filename);
+	}
+    } else {
+	temp = TTFGetFontName(ttf,0,0);
+	if ( temp!=NULL ) {
+	    ret = malloc(2*sizeof(char *));
+	    ret[0] = temp;
+	    ret[1] = NULL;
+	}
+    }
+    fclose(ttf);
+return(ret);
+}
diff -pruN 1:20161005~dfsg-4/.pc/applied-patches 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/applied-patches
--- 1:20161005~dfsg-4/.pc/applied-patches	2017-09-21 06:45:19.587325073 +0000
+++ 1:20161005~dfsg-4+deb9u1ubuntu1/.pc/applied-patches	2017-09-21 06:45:20.755357480 +0000
@@ -1,3 +1,9 @@
+0001-Fix-out-of-bounds-read-in-getsid.patch
+0002-readcfftopdict-Prevent-stack-underflow-condition.patch
+0003-Fix-out-of-bounds-read-condition-and-buffer-overflow.patch
+0004-parsettf.c-Fix-buffer-overrun-condition.patch
+0005-parsettf.c-Fix-buffer-overflow-condition-when-readin.patch
+0006-parsettf.c-Fix-out-of-bounds-read-condition-on-buffe.patch
 2001_repackaging_fixup.patch
 2002_avoid_upstream_git_or_pkg.patch
 1001_reproducibe_buildtimestamps.patch


More information about the Pkg-fonts-bugs mailing list