/* 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-config.h>

#include "splinesave.h"

#include "autohint.h"
#include "dumppfa.h"
#include "fontforge.h"
#include "fvfonts.h"
#include "gwidget.h"
#include "parsepfa.h"
#include "psfont.h"
#include "splinefont.h"
#include "splineorder2.h"
#include "splinesaveafm.h"
#include "splineutil.h"
#include "splineutil2.h"
#include "ustring.h"
#include "utype.h"

#include <math.h>
#include <stdio.h>
#include <string.h>

float GenerateHintWidthEqualityTolerance = 0.0;
int autohint_before_generate = 1;

/* Let's talk about references. */
/* If we are doing Type1 output, then the obvious way of doing them is seac */
/*  but that's so limitting. It only works for exactly two characters both */
/*  of which are in Adobe's Standard Enc. Only translations allowed. Only */
/*  one reference may be translated and the width of the char must match */
/*  that of the non-translated reference */
/*   The first extension we can make is to allow a single character reference */
/*  by making the other character be a space */
/*   But if we want to do more than that we must use subrs. If we have two */
/*  refs in subrs then we can do translations by preceding the subr calls by */
/*  appropriate rmovetos. Actually the specs say that only one rmoveto should */
/*  precede a path, so that means we can't allow the subroutines to position */
/*  themselves, they must just assume that they are called with the current */
/*  position correct for the first point. But then we need to know where the */
/*  first point should be placed, so we allocate a BasePoint to hold that info*/
/*  and store it into the "keys" array (which the subrs don't use). Similarly */
/*  we need to know where the subr will leave us, so we actually allocate 2 */
/*  BasePoints, one containing the start point, one the end point */
/*   But that's still not good enough, hints are defined in such a way that */
/*  they are not relocatable. So our subrs can't include any hint definitions*/
/*  (or if they do then that subr can't be translated at all). So hints must */
/*  be set outside the subrs, and the subrs can't be for chars that need hint */
/*  substitution. Unless... The subr will never be relocated. */
/*   So we generate two types of reference subrs, one containing no hints, the*/
/*  other containing all the hints, stems and flexes. The first type may be */
/*  translated, the second cannot */
/* Type2 doesn't allow any seacs */
/*  So everything must go in subrs. We have a slightly different problem here:*/
/*  hintmasks need to know exactly how many stem hints there are in the char */
/*  so we can't include any hintmask operators inside a subr (unless we */
/*  guarantee that all invocations of that subr are done with the same number */
/*  of hints in the character). This again means that no char with hint subs- */
/*  titutions may be put in a subr. UNLESS all the other references in a */
/*  referring character contain no hints */

/* That's very complex. And it doesn't do a very good job. */
/* Instead let's take all strings bounded by either moveto or hintmask operators */
/*  store these as potential subroutines. So a glyph becomes a sequence of    */
/*  potential subroutine calls preceded by the glyph header (width, hint decl,*/
/*  counter declarations, etc.) and intersperced by hintmask/moveto operators */
/* Each time we get a potential subr we hash it and see if we've used that    */
/*  string before. If we have then we merge the two. Otherwise it's a new one.*/
/* Then at the end we see what strings get used often enough to go into subrs */
/*  we create the subrs array from that.				      */
/* Then each glyph. We insert the preamble. We check of the potential subroutine */
/*  became a real subroutine. If so we call it, else we insert the data inline*/
/*  Do the same for the next hintmask/moveto and potential subroutine...      */

/* Then, on top of that I tried generating some full glyph subroutines, and   */
/*  to my surprise, it just made things worse.                                */

struct potentialsubrs {
    uint8_t *data;		/* the charstring of the subr */
    int len;			/* the length of the charstring */
    int idx;			/* initially index into psubrs array */
    				/*  then index into subrs array or -1 if none */
    int cnt;			/* the usage count */
    int fd;			/* Which sub font is it in */
				/* -1 => used in more than one */
    int next;
    int full_glyph_index;	/* Into the glyphbits array */
				/* for full references */
    BasePoint *startstop;	/* Again for full references */
};

struct bits {
    uint8_t *data;
    int dlen;
    int psub_index;
};

struct glyphbits {
    SplineChar *sc;
    int fd;			/* Which subfont is it in */
    int bcnt;
    struct bits *bits;
    uint8_t wasseac;
};

#define HSH_SIZE	511
/* In type2 charstrings we divide every character into bits where a bit is */
/* bounded by a hintmask/moveto. Each of these is a potential subroutine and */
/* is stored here */
typedef struct glyphinfo {
    struct potentialsubrs *psubrs;
    int pcnt, pmax;
    int hashed[HSH_SIZE];
    struct glyphbits *gb, *active;
    SplineFont *sf;
    int layer;
    int glyphcnt;
    int subfontcnt;
    int bcnt, bmax;
    struct bits *bits;		/* For current glyph */
    const int *bygid;
    int justbroken;
    int instance_count;
} GlyphInfo;

struct mhlist {
    uint8_t mask[HntMax/8];
    int subr;
    struct mhlist *next;
};

struct hintdb {
    uint8_t mask[HntMax/8];
    int cnt;				/* number of hints */
    struct mhlist *sublist;
    struct pschars *subrs;
    /*SplineChar *sc;*/
    SplineChar **scs;
    int instance_count;
    unsigned int iscjk: 1;		/* If cjk then don't do stem3 hints */
					/* Will be done with counters instead */
	/* actually, most of the time we can't use stem3s, only if those three*/
	/* stems are always active and there are no other stems !(h/v)hasoverlap*/
    unsigned int noconflicts: 1;
    unsigned int startset: 1;
    unsigned int skiphm: 1;		/* Set when coming back to the start point of a contour. hintmask should be set the first time, not the second */
    unsigned int donefirsthm: 1;
    int cursub;				/* Current subr number */
    DBasePoint current;
    GlyphInfo *gi;
};

static void GIContentsFree(GlyphInfo *gi,SplineChar *dummynotdef) {
    int i,j;

    if ( gi->glyphcnt>0 && gi->gb[0].sc == dummynotdef ) {
	if ( dummynotdef->layers!=NULL ) {
	    SplinePointListsFree(dummynotdef->layers[gi->layer].splines);
	    dummynotdef->layers[gi->layer].splines = NULL;
	}
	StemInfosFree(dummynotdef->hstem);
	StemInfosFree(dummynotdef->vstem);
	dummynotdef->vstem = dummynotdef->hstem = NULL;
	free(dummynotdef->layers);
	dummynotdef->layers = NULL;
    }

    for ( i=0; i<gi->pcnt; ++i ) {
	free(gi->psubrs[i].data);
	free(gi->psubrs[i].startstop);
	gi->psubrs[i].data = NULL;
	gi->psubrs[i].startstop = NULL;
    }
    for ( i=0; i<gi->glyphcnt; ++i ) {
	for ( j=0; j<gi->gb[i].bcnt; ++j )
	    free(gi->gb[i].bits[j].data);
	free(gi->gb[i].bits);
	gi->gb[i].bits = NULL;
	gi->gb[i].bcnt = 0;
    }

    gi->pcnt = 0;
    gi->bcnt = 0;
    gi->justbroken = 0;
}

static void GIFree(GlyphInfo *gi,SplineChar *dummynotdef) {

    GIContentsFree(gi,dummynotdef);

    free(gi->gb);
    free(gi->psubrs);
    free(gi->bits);
}

static void StartNextSubroutine(GrowBuf *gb,struct hintdb *hdb) {
    GlyphInfo *gi;

    if ( hdb==NULL )
return;
    gi = hdb->gi;
    if ( gi==NULL )
return;
    /* Store everything in the grow buf into the data/dlen of the next bit */
    if ( gi->bcnt==-1 ) gi->bcnt = 0;
    if ( gi->bcnt>=gi->bmax )
	gi->bits = realloc(gi->bits,(gi->bmax+=20)*sizeof(struct bits));
    gi->bits[gi->bcnt].dlen = gb->pt-gb->base;
    gi->bits[gi->bcnt].data = malloc(gi->bits[gi->bcnt].dlen);
    gi->bits[gi->bcnt].psub_index = -1;
    memcpy(gi->bits[gi->bcnt].data,gb->base,gi->bits[gi->bcnt].dlen);
    gb->pt = gb->base;
    gi->justbroken = false;
}

static int hashfunc(uint8_t *data, int len) {
    uint8_t *end = data+len;
    unsigned int hash = 0, r;

    while ( data<end ) {
	r = (hash>>30)&3;
	hash <<= 2;
	hash = (hash|r)&0xffffffff;
	hash ^= *data++;
    }
return( hash%HSH_SIZE );
}

static void BreakSubroutine(GrowBuf *gb,struct hintdb *hdb) {
    GlyphInfo *gi;
    struct potentialsubrs *ps;
    int hash;
    int pi;

    if ( hdb==NULL )
return;
    gi = hdb->gi;
    if ( gi==NULL )
return;
    /* The stuff before the first moveto in a glyph (the header that sets */
    /*  the width, sets up the hints, counters, etc.) can't go into a subr */
    if ( gi->bcnt==-1 ) {
	gi->bcnt=0;
	gi->justbroken = true;
return;
    } else if ( gi->justbroken )
return;
    /* Otherwise stuff everything in the growbuffer into a subr */
    hash = hashfunc(gb->base,gb->pt-gb->base);
    ps = NULL;
    for ( pi=gi->hashed[hash]; pi!=-1; pi=gi->psubrs[pi].next ) {
	ps = &gi->psubrs[pi];
	if ( ps->len==gb->pt-gb->base && memcmp(ps->data,gb->base,gb->pt-gb->base)==0 )
    break;
    }
    if ( pi==-1 ) {
	if ( gi->pcnt>=gi->pmax )
	    gi->psubrs = realloc(gi->psubrs,(gi->pmax+=gi->glyphcnt)*sizeof(struct potentialsubrs));
	ps = &gi->psubrs[gi->pcnt];
	memset(ps,0,sizeof(*ps));	/* set cnt to 0 */
	ps->idx = gi->pcnt++;
	ps->len = gb->pt-gb->base;
	ps->data = malloc(ps->len);
	memcpy(ps->data,gb->base,ps->len);
	ps->next = gi->hashed[hash];
	gi->hashed[hash] = ps->idx;
	ps->fd = gi->active->fd;
	ps->full_glyph_index = -1;
    }
    if ( ps->fd!=gi->active->fd )
	ps->fd = -1;			/* used in multiple cid sub-fonts */
    gi->bits[gi->bcnt].psub_index = ps->idx;
    ++ps->cnt;
    gb->pt = gb->base;
    ++gi->bcnt;
    gi->justbroken = true;
}

static void MoveSubrsToChar(GlyphInfo *gi) {
    struct glyphbits *active;

    if ( gi==NULL )
return;
    active = gi->active;
    active->bcnt = gi->bcnt;
    active->bits = malloc(active->bcnt*sizeof(struct bits));
    memcpy(active->bits,gi->bits,active->bcnt*sizeof(struct bits));
    gi->bcnt = 0;
}

static int NumberHints(SplineChar **scs, int instance_count) {
    int i,j, cnt=-1;
    StemInfo *s;

    for ( j=0; j<instance_count; ++j ) {
	for ( s=scs[j]->hstem, i=0; s!=NULL; s=s->next ) {
	    if ( i<HntMax )
		s->hintnumber = i++;
	    else
		s->hintnumber = -1;
	}
	for ( s=scs[j]->vstem; s!=NULL; s=s->next ) {
	    if ( i<HntMax )
		s->hintnumber = i++;
	    else
		s->hintnumber = -1;
	}
	if ( cnt==-1 )
	    cnt = i;
	else if ( cnt!=i )
	    IError("MM font with different hint counts");
    }
return( cnt );
}

void RefCharsFreeRef(RefChar *ref) {
    RefChar *rnext;

    while ( ref!=NULL ) {
	rnext = ref->next;
	/* don't free the splines */
	free(ref->layers);
	chunkfree(ref,sizeof(RefChar));
	ref = rnext;
    }
}

static void MarkTranslationRefs(SplineFont *sf,int layer) {
    int i;
    SplineChar *sc;
    RefChar *r;

    for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
	for ( r = sc->layers[layer].refs; r!=NULL; r=r->next )
	    r->justtranslated = (r->transform[0]==1 && r->transform[3]==1 &&
		    r->transform[1]==0 && r->transform[2]==0);
    }
}

/* ************************************************************************** */
/* ********************** Type1 PostScript CharStrings ********************** */
/* ************************************************************************** */

static bigreal myround( bigreal pos, int round ) {
    if ( round )
return( rint( pos ));
    else
return( rint( pos*1024. )/1024. );
}

static void AddNumber(GrowBuf *gb, real pos, int round) {
    int dodiv = 0;
    int val;
    unsigned char *str;

    if ( gb->pt+8>=gb->end )
	GrowBuffer(gb);

    if ( !round && pos!=floor(pos) ) {
	{
	    if ( rint(pos*64)/64 == pos ) {
		pos *= 64;
		dodiv = 64;
	    } else {
		pos *= 1024;
		dodiv = 1024;
	    }
	}
    }
    pos = rint(pos);
    if ( dodiv>0 && floor(pos)/dodiv == floor(pos/dodiv) ) {
	pos = rint(pos/dodiv);
	dodiv = 0;
    }
    val = pos;
    str = gb->pt;

    if ( pos>=-107 && pos<=107 )
	*str++ = val+139;
    else if ( pos>=108 && pos<=1131 ) {
	val -= 108;
	*str++ = (val>>8)+247;
	*str++ = val&0xff;
    } else if ( pos>=-1131 && pos<=-108 ) {
	val = -val;
	val -= 108;
	*str++ = (val>>8)+251;
	*str++ = val&0xff;
    } else {
	*str++ = '\377';
	*str++ = (val>>24)&0xff;
	*str++ = (val>>16)&0xff;
	*str++ = (val>>8)&0xff;
	*str++ = val&0xff;
    }
    if ( dodiv ) {
	if ( dodiv<107 )
	    *str++ = dodiv+139;
	else {
	    dodiv -= 108;
	    *str++ = (dodiv>>8)+247;
	    *str++ = dodiv&0xff;
	}
	*str++ = 12;		/* div (byte1) */
	*str++ = 12;		/* div (byte2) */
    }
    gb->pt = str;
}

/* When doing a multiple master font we have multiple instances of the same data */
/*  which must all be added, and then a call made to the appropriate blend routine */
/* This is complicated because all the data may not fit on the stack so we */
/*  may need to make multiple calls */
static void AddData(GrowBuf *gb, bigreal data[MmMax][6], int instances, int num_coords,
	int round) {
    int allsame = true, alls[6];
    int i,j, chunk,min,max,subr;

    for ( j=0; j<num_coords; ++j ) {
	alls[j] = true;
	for ( i=1; i<instances; ++i ) {
	    if ( data[i][j]!=data[0][j] ) {
		alls[j] = false;
		allsame = false;
	    break;
	    }
	}
    }

    if ( allsame ) {		/* No need for blending */
				/*  Probably a normal font, but possible in an mm */
	for ( j=0; j<num_coords; ++j )
	    AddNumber(gb,data[0][j],round);
return;
    }

    chunk = 22/instances;
    if ( chunk == 5 ) chunk = 4;	/* No subroutine for 5 items */
    min = 0;
    while ( min<num_coords ) {
	while ( min<num_coords && alls[min] ) {
	    AddNumber(gb,data[0][min],round);
	    ++min;
	}
	max = min+chunk;
	if ( max>num_coords ) max = num_coords;
	while ( max-1>min && alls[max-1] )
	    --max;
	if ( max-min==5 ) max=min+4;
	if ( min<max ) {
	    for ( j=min; j<max; ++j )
		AddNumber(gb,data[0][j],round);
	    for ( j=min; j<max; ++j )
		for ( i=1; i<instances; ++i )
		    AddNumber(gb,data[i][j]-data[0][j],round);
	    subr = (j-min) + 4;
	    if ( j-min==6 ) subr = 9;
	    AddNumber(gb,subr,round);
	    if ( gb->pt+1>=gb->end )
		GrowBuffer(gb);
	    *gb->pt++ = 10;			/* callsubr */
	    min = j;
	}
    }
}

int CvtPsStem3(GrowBuf *gb, SplineChar *scs[MmMax], int instance_count,
	int ishstem, int round) {
    StemInfo *h1, *h2, *h3;
    StemInfo _h1, _h2, _h3;
    bigreal data[MmMax][6];
    int i;
    real off;

    for ( i=0; i<instance_count; ++i ) {
	if ( (ishstem && scs[i]->hconflicts) || (!ishstem && scs[i]->vconflicts))
return( false );
	h1 = ishstem ? scs[i]->hstem : scs[i]->vstem;
	if ( h1==NULL || (h2 = h1->next)==NULL || (h3=h2->next)==NULL )
return( false );
	if ( h3->next!=NULL )
return( false );
	off = ishstem ? 0 : scs[i]->lsidebearing;
	if ( h1->width<0 ) {
	    _h1 = *h1;
	    _h1.start += _h1.width;
	    _h1.width = -_h1.width;
	    h1 = &_h1;
	}
	if ( h2->width<0 ) {
	    _h2 = *h2;
	    _h2.start += _h2.width;
	    _h2.width = -_h2.width;
	    h2 = &_h2;
	}
	if ( h3->width<0 ) {
	    _h3 = *h3;
	    _h3.start += _h3.width;
	    _h3.width = -_h3.width;
	    h3 = &_h3;
	}

	if ( h1->start>h2->start ) {
	    StemInfo *ht = h1; h1 = h2; h2 = ht;
	}
	if ( h1->start>h3->start ) {
	    StemInfo *ht = h1; h1 = h3; h3 = ht;
	}
	if ( h2->start>h3->start ) {
	    StemInfo *ht = h2; h2 = h3; h3 = ht;
	}
	if ( h1->width != h3->width )
return( false );
	if ( (h2->start+h2->width/2) - (h1->start+h1->width/2) !=
		(h3->start+h3->width/2) - (h2->start+h2->width/2) )
return( false );
	data[i][0] = h1->start-off;
	data[i][1] = h1->width;
	data[i][2] = h2->start-off;
	data[i][3] = h2->width;
	data[i][4] = h3->start-off;
	data[i][5] = h3->width;
    }
    if ( gb==NULL )
return( true );
    AddData(gb,data,instance_count,6,round);
    if ( gb->pt+3>=gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 12;
    *(gb->pt)++ = ishstem?2:1;				/* h/v stem3 */
return( true );
}

static void CvtPsHints(GrowBuf *gb, SplineChar *scs[MmMax], int instance_count,
	int ishstem, int round, int iscjk, real *offsets ) {
    StemInfo *hs[MmMax];
    bigreal data[MmMax][6];
    int i;
    real off;

    for ( i=0; i<instance_count; ++i )
	hs[i] = ishstem ? scs[i]->hstem : scs[i]->vstem;

    if ( hs[0]!=NULL && hs[0]->next!=NULL && hs[0]->next->next!=NULL &&
	    hs[0]->next->next->next==NULL )
	if ( !iscjk && CvtPsStem3(gb, scs, instance_count, ishstem, round))
return;

    while ( hs[0]!=NULL ) {
	for ( i=0; i<instance_count; ++i ) {
	    off = offsets!=NULL ? offsets[i] :
		    ishstem ? 0 : scs[i]->lsidebearing;
	    if ( hs[i]->ghost ) {
		data[i][0] = hs[i]->start-off+hs[i]->width;
		data[i][1] = -hs[i]->width;
	    } else {
		data[i][0] = hs[i]->start-off;
		data[i][1] = hs[i]->width;
	    }
	}
	AddData(gb,data,instance_count,2,round);
	if ( gb->pt+1>=gb->end )
	    GrowBuffer(gb);
	*(gb->pt)++ = ishstem?1:3;			/* h/v stem */
	for ( i=0; i<instance_count; ++i )
	    hs[i] = hs[i]->next;
    }
}

static void CvtPsMasked(GrowBuf *gb,SplineChar *scs[MmMax], int instance_count,
	int ishstem, int round, uint8_t mask[12] ) {
    StemInfo *hs[MmMax];
    bigreal data[MmMax][6], off;
    int i;

    for ( i=0; i<instance_count; ++i )
	hs[i] = ishstem ? scs[i]->hstem : scs[i]->vstem;

    while ( hs[0]!=NULL ) {
	if ( hs[0]->hintnumber!=-1 &&
		(mask[hs[0]->hintnumber>>3]&(0x80>>(hs[0]->hintnumber&7))) ) {
	    for ( i=0; i<instance_count; ++i ) {
		off = ishstem ? 0 : scs[i]->lsidebearing;
		if ( hs[i]->ghost ) {
		    data[i][0] = hs[i]->start-off+hs[i]->width;
		    data[i][1] = -hs[i]->width;
		} else {
		    data[i][0] = hs[i]->start-off;
		    data[i][1] = hs[i]->width;
		}
	    }
	    AddData(gb,data,instance_count,2,round);
	    if ( gb->pt+1>=gb->end )
		GrowBuffer(gb);
	    *(gb->pt)++ = ishstem?1:3;			/* h/v stem */
	}
	for ( i=0; i<instance_count; ++i )
	    hs[i] = hs[i]->next;
    }
}

static int FigureCounters(StemInfo *stems,real *hints,int base,real offset,
	int countermask_cnt, HintMask *counters) {
    StemInfo *h;
    int pos = base+1, subbase, cnt=0;
    real last = offset;
    int i;

    for ( i=0; i<countermask_cnt; ++i ) {
	subbase = pos;
	for ( h=stems; h!=NULL ; h=h->next ) {
	    if ( h->hintnumber!=-1 && (counters[i][h->hintnumber>>3]&(0x80>>(h->hintnumber&7))) ) {
		hints[pos++] = h->start-last;
		hints[pos++] = h->width;
		last = h->start+h->width;
	    }
	}
	if ( pos!=subbase ) {
	    hints[pos-2] += hints[pos-1];
	    hints[pos-1] = -hints[pos-1];		/* Mark end of group */
	    last = offset;				/* Each new group starts at 0 or lbearing */
	    ++cnt;
	}
    }
    hints[base] = cnt;
return( pos );
}

static void CounterHints1(GrowBuf *gb, SplineChar *sc, int round) {
    real hints[HntMax*2+2];		/* At most 96 hints, no hint used more than once */
    int pos, i, j;

    if ( sc->countermask_cnt==0 )
return;

    pos = FigureCounters(sc->hstem,hints,0,0,sc->countermask_cnt,
	    sc->countermasks);
    /* Adobe's docs (T1_Supp.pdf, section 2.4) say these should be offset from*/
    /*  the left side bearing. The example (T1_Supp.pdf, 2.6) shows them offset*/
    /*  from 0. I've no idea which is correct, so I'll follow the words, think-*/
    /*  that the lbearing might have been set to 0 even though it shouldn't */
    /*  have been. */
    pos = FigureCounters(sc->vstem,hints,pos,sc->lsidebearing,sc->countermask_cnt,
	    sc->countermasks);
    if ( pos==2 )	/* => no counters, one byte to say 0 h counters, one byte for 0 v counters */
return;
    for ( i=pos; i>22; i-=22 ) {
	for ( j=i-22; j<i; ++j )
	    AddNumber(gb,hints[j],round);
	AddNumber(gb,22,round);
	AddNumber(gb,12,round);
	if ( gb->pt+2>=gb->end )
	    GrowBuffer(gb);
	*(gb->pt)++ = 12;
	*(gb->pt)++ = 16;		/* CallOtherSubr */
    }
    for ( j=0; j<i; ++j )
	AddNumber(gb,hints[j],round);
    AddNumber(gb,i,round);
    AddNumber(gb,13,round);
    if ( gb->pt+2>=gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 12;
    *(gb->pt)++ = 16;		/* CallOtherSubr */
}

static void SubrsCheck(struct pschars *subrs) {
	/* <subr number presumed to be on stack> 1 3 callother pop callsubr */

    if ( subrs->next>=subrs->cnt ) {
	subrs->cnt += 100;
	subrs->values = realloc(subrs->values,subrs->cnt*sizeof(uint8_t *));
	subrs->lens = realloc(subrs->lens,subrs->cnt*sizeof(int));
	if ( subrs->keys!=NULL ) {
	    int i;
	    subrs->keys = realloc(subrs->keys,subrs->cnt*sizeof(char *));
	    for ( i=subrs->cnt-100; i<subrs->cnt; ++i )
		subrs->keys[i] = NULL;
	}
    }
}

/* Does the hintmask we need already exist in a subroutine? if so return that */
/*  subr. Else build a new subr with the hints we need. Note we can only use */
/*  *stem3 commands if there are no conflicts in that coordinate, it isn't cjk*/
/*  and all the other conditions are met */
static int FindOrBuildHintSubr(struct hintdb *hdb, uint8_t mask[12], int round) {
    struct mhlist *mh;
    GrowBuf gb;

    for ( mh=hdb->sublist; mh!=NULL; mh=mh->next ) {
	if ( memcmp(mask,mh->mask,sizeof(uint8_t)*12)==0 )
return( mh->subr );
    }
    SubrsCheck(hdb->subrs);

    memset(&gb,0,sizeof(gb));
    if ( !hdb->scs[0]->hconflicts )
	CvtPsHints(&gb,hdb->scs,hdb->instance_count,true,round,hdb->iscjk,NULL);
    else
	CvtPsMasked(&gb,hdb->scs,hdb->instance_count,true,round,mask);
    if ( !hdb->scs[0]->vconflicts )
	CvtPsHints(&gb,hdb->scs,hdb->instance_count,false,round,hdb->iscjk,NULL);
    else
	CvtPsMasked(&gb,hdb->scs,hdb->instance_count,false,round,mask);
    if ( gb.pt+1 >= gb.end )
	GrowBuffer(&gb);
    *gb.pt++ = 11;			/* return */

    /* Replace an old subroutine */
    if ( mh!=NULL ) {
	free( hdb->subrs->values[mh->subr]);
	hdb->subrs->values[mh->subr] = (uint8_t *) copyn((char *) gb.base,gb.pt-gb.base);
	hdb->subrs->lens[mh->subr] = gb.pt-gb.base;
	memcpy(mh->mask,mask,sizeof(mh->mask));
    } else {
	hdb->subrs->values[hdb->subrs->next] = (uint8_t *) copyn((char *) gb.base,gb.pt-gb.base);
	hdb->subrs->lens[hdb->subrs->next] = gb.pt-gb.base;

	mh = calloc(1,sizeof(struct mhlist));
	memcpy(mh->mask,mask,sizeof(mh->mask));
	mh->subr = hdb->subrs->next++;
	mh->next = hdb->sublist;
	hdb->sublist = mh;
    }
    free(gb.base);

return( mh->subr );
}

static void CallTransformedHintSubr(GrowBuf *gb,struct hintdb *hdb,
	SplineChar *scs[MmMax], RefChar *refs[MmMax], BasePoint trans[MmMax],
	int instance_count, int round) {
    HintMask hm;
    int s;

    if ( HintMaskFromTransformedRef(refs[0],&trans[0],scs[0],&hm)==NULL )
return;
    s = FindOrBuildHintSubr(hdb, hm, round);
    AddNumber(gb,s,round);
    AddNumber(gb,4,round);		/* subr 4 is (my) magic subr that does the hint subs call */
    if ( gb->pt+1 >= gb->end )
	GrowBuffer(gb);
    *gb->pt++ = 10;			/* callsubr */
}

static void HintSetup(GrowBuf *gb,struct hintdb *hdb, SplinePoint *to,
	int round, int break_subr ) {
    int s;
    int i;

    if ( to->hintmask==NULL || hdb->noconflicts )
return;
    if ( hdb->scs[0]->hstem==NULL && hdb->scs[0]->vstem==NULL )		/* Hints are turned off. Hint mask still remains though */
return;
    for ( i=0; i<HntMax/8; ++i )
	if ( to->hintmask[i]!=0 )
    break;
    if ( i==HntMax/8 )		/* Empty mask */
return;

    s = FindOrBuildHintSubr(hdb,*to->hintmask,round);
    memcpy(hdb->mask,*to->hintmask,sizeof(HintMask));
    if ( hdb->cursub == s ) {			/* If we were able to redefine */
return;						/* the subroutine currently */
    }						/* active then we are done */

    if ( break_subr )
	BreakSubroutine(gb,hdb);

    AddNumber(gb,s,round);
    AddNumber(gb,4,round);		/* subr 4 is (my) magic subr that does the hint subs call */
    if ( gb->pt+1 >= gb->end )
	GrowBuffer(gb);
    *gb->pt++ = 10;			/* callsubr */
    hdb->cursub = s;
    if ( break_subr )
	StartNextSubroutine(gb,hdb);
}

static void _moveto(GrowBuf *gb,DBasePoint *current,BasePoint *to,int instance_count,
	int line, int round, struct hintdb *hdb) {
    BasePoint temp[MmMax];
    int i, samex, samey;
    bigreal data[MmMax][6];

    if ( gb->pt+18 >= gb->end )
	GrowBuffer(gb);

    for ( i=0; i<instance_count; ++i ) {
	temp[i].x = myround(to[i].x,round);
	temp[i].y = myround(to[i].y,round);
    }
    to = temp;
    samex = samey = true;
    for ( i=0; i<instance_count; ++i ) {
	if ( current[i].x!=to[i].x ) samex = false;
	if ( current[i].y!=to[i].y ) samey = false;
    }
    if ( samex ) {
	for ( i=0; i<instance_count; ++i )
	    data[i][0] = to[i].y-current[i].y;
	AddData(gb,data,instance_count,1,round);
	*(gb->pt)++ = line ? 7 : 4;		/* v move/line to */
	for ( i=0; i<instance_count; ++i )
	    current[i].y += data[i][0];
    } else if ( samey ) {
	for ( i=0; i<instance_count; ++i )
	    data[i][0] = to[i].x-current[i].x;
	AddData(gb,data,instance_count,1,round);
	*(gb->pt)++ = line ? 6 : 22;		/* h move/line to */
	for ( i=0; i<instance_count; ++i )
	    current[i].x += data[i][0];
    } else {
	for ( i=0; i<instance_count; ++i ) {
	    data[i][0] = to[i].x-current[i].x;
	    data[i][1] = to[i].y-current[i].y;
	}
	AddData(gb,data,instance_count,2,round);
	*(gb->pt)++ = line ? 5 : 21;		/* r move/line to */
	for ( i=0; i<instance_count; ++i ) {
	    current[i].x += data[i][0];
	    current[i].y += data[i][1];
	}
    }
    if ( !line )
	StartNextSubroutine(gb,hdb);
}

static void moveto(GrowBuf *gb,DBasePoint *current,Spline *splines[MmMax],
	int instance_count, int line, int round, struct hintdb *hdb) {
    BasePoint to[MmMax];
    int i;

    if ( !line) BreakSubroutine(gb,hdb);
    if ( hdb!=NULL ) HintSetup(gb,hdb,splines[0]->to,round,line);
    for ( i=0; i<instance_count; ++i )
	to[i] = splines[i]->to->me;
    _moveto(gb,current,to,instance_count,line,round,hdb);
}

static void splmoveto(GrowBuf *gb,DBasePoint *current,SplineSet *spl[MmMax],
	int instance_count, int line, int round, struct hintdb *hdb) {
    BasePoint to[MmMax];
    int i;

    if ( !line) BreakSubroutine(gb,hdb);
    if ( hdb!=NULL ) HintSetup(gb,hdb,spl[0]->first,round,line);
    for ( i=0; i<instance_count; ++i )
	to[i] = spl[i]->first->me;
    _moveto(gb,current,to,instance_count,line,round,hdb);
}

static int NeverConflicts(RefChar *refs[MmMax], int instance_count) {
    int i;
    for ( i=0; i<instance_count; ++i )
	if ( refs[i]->sc->hconflicts || refs[i]->sc->vconflicts )
return( false );

return( true );
}

static int AllStationary(RefChar *refs[MmMax], BasePoint trans[MmMax], int instance_count) {
    int i;
    for ( i=0; i<instance_count; ++i )
	if ( !refs[i]->justtranslated ||
		refs[i]->transform[4]+trans[i].x!=0 || 
		refs[i]->transform[5]+trans[i].y!=0 )
return( false );

return( true );
}

static int AnyRefs(SplineChar *sc,int layer) {

return( sc->layers[layer].refs!=NULL );
}

static void refmoveto(GrowBuf *gb,DBasePoint *current,BasePoint rpos[MmMax],
	int instance_count, int line, int round, struct hintdb *hdb, RefChar *refs[MmMax]) {
    BasePoint to[MmMax];
    int i;

    if ( !line) BreakSubroutine(gb,hdb);
    for ( i=0; i<instance_count; ++i ) {
	to[i] = rpos[i];
	if ( refs!=NULL ) {
	    to[i].x += refs[i]->transform[4];
	    to[i].y += refs[i]->transform[5];
	}
    }
    _moveto(gb,current,to,instance_count,line,round,hdb);
}

static void curveto(GrowBuf *gb,DBasePoint *current,Spline *splines[MmMax],int instance_count,
	int round, struct hintdb *hdb) {
    BasePoint temp1[MmMax], temp2[MmMax], temp3[MmMax], *c0[MmMax], *c1[MmMax], *s1[MmMax];
    bigreal data[MmMax][6];
    int i, op, opcnt;
    int vh, hv;

    if ( hdb!=NULL ) HintSetup(gb,hdb,splines[0]->to,round,true);

    if ( gb->pt+50 >= gb->end )
	GrowBuffer(gb);

    vh = hv = true;
    for ( i=0; i<instance_count; ++i ) {
	c0[i] = &splines[i]->from->nextcp;
	c1[i] = &splines[i]->to->prevcp;
	s1[i] = &splines[i]->to->me;
	temp1[i].x = myround(c0[i]->x,round);
	temp1[i].y = myround(c0[i]->y,round);
	c0[i] = &temp1[i];
	temp2[i].x = myround(c1[i]->x,round);
	temp2[i].y = myround(c1[i]->y,round);
	c1[i] = &temp2[i];
	temp3[i].x = myround(s1[i]->x,round);
	temp3[i].y = myround(s1[i]->y,round);
	s1[i] = &temp3[i];
	if ( current[i].x != c0[i]->x || c1[i]->y!=s1[i]->y ) vh = false;
	if ( current[i].y != c0[i]->y || c1[i]->x!=s1[i]->x ) hv = false;
    }
    if ( vh ) {
	for ( i=0; i<instance_count; ++i ) {
	    data[i][0] = c0[i]->y-current[i].y;
	    data[i][1] = c1[i]->x-c0[i]->x;
	    data[i][2] = c1[i]->y-c0[i]->y;
	    data[i][3] = s1[i]->x-c1[i]->x;
	}
	op = 30;		/* vhcurveto */
	opcnt = 4;
	for ( i=0; i<instance_count; ++i ) {
	    current[i].x += data[i][1]+data[i][3];
	    current[i].y += data[i][0]+data[i][2];
	}
    } else if ( hv ) {
	for ( i=0; i<instance_count; ++i ) {
	    data[i][0] = c0[i]->x-current[i].x;
	    data[i][1] = c1[i]->x-c0[i]->x;
	    data[i][2] = c1[i]->y-c0[i]->y;
	    data[i][3] = s1[i]->y-c1[i]->y;
	}
	op = 31;		/* hvcurveto */
	opcnt = 4;
	for ( i=0; i<instance_count; ++i ) {
	    current[i].x += data[i][0]+data[i][1];
	    current[i].y += data[i][2]+data[i][3];
	}
    } else {
	for ( i=0; i<instance_count; ++i ) {
	    data[i][0] = c0[i]->x-current[i].x;
	    data[i][1] = c0[i]->y-current[i].y;
	    data[i][2] = c1[i]->x-c0[i]->x;
	    data[i][3] = c1[i]->y-c0[i]->y;
	    data[i][4] = s1[i]->x-c1[i]->x;
	    data[i][5] = s1[i]->y-c1[i]->y;
	}
	op = 8;		/* rrcurveto */
	opcnt=6;
	for ( i=0; i<instance_count; ++i ) {
	    current[i].x += data[i][0]+data[i][2]+data[i][4];
	    current[i].y += data[i][1]+data[i][3]+data[i][5];
	}
    }
    AddData(gb,data,instance_count,opcnt,false);
    if ( gb->pt+1 >= gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = op;
}

static int SplinesAreFlexible(Spline *splines[MmMax], int instance_count) {
    int i, x=false, y=false;

    for ( i=0; i<instance_count; ++i ) {
	if ( !splines[i]->to->flexx && !splines[i]->to->flexy )
return( false );
	if (( x && splines[i]->to->flexy ) || ( y && splines[i]->to->flexx ))
return( false );
	x = splines[i]->to->flexx;
	y = splines[i]->to->flexy;
    }
return( true );
}

static void flexto(GrowBuf *gb,DBasePoint current[MmMax],Spline *pspline[MmMax],
	int instance_count,int round, struct hintdb *hdb) {
    BasePoint *c0, *c1, *mid, *end=NULL;
    Spline *nspline;
    BasePoint offsets[MmMax][8];
    int i,j;
    BasePoint temp1, temp2, temp3, temp;
    bigreal data[MmMax][6];

    for ( j=0; j<instance_count; ++j ) {
	c0 = &pspline[j]->from->nextcp;
	c1 = &pspline[j]->to->prevcp;
	mid = &pspline[j]->to->me;

	temp1.x = myround(c0->x,round);
	temp1.y = myround(c0->y,round);
	c0 = &temp1;
	temp2.x = myround(c1->x,round);
	temp2.y = myround(c1->y,round);
	c1 = &temp2;
	temp.x = myround(mid->x,round);
	temp.y = myround(mid->y,round);
	mid = &temp;
/* reference point is same level as current point */
	if ( current[j].y==pspline[j]->to->next->to->me.y ) {
	    offsets[j][0].x = mid->x-current[j].x;	offsets[j][0].y = 0;
	    offsets[j][1].x = c0->x-mid->x;		offsets[j][1].y = c0->y-current[j].y;
	} else {
	    offsets[j][0].x = 0;			offsets[j][0].y = mid->y-current[j].y;
	    offsets[j][1].x = c0->x-current[j].x;	offsets[j][1].y = c0->y-mid->y;
	}
	offsets[j][2].x = c1->x-c0->x;		offsets[j][2].y = c1->y-c0->y;
	offsets[j][3].x = mid->x-c1->x;		offsets[j][3].y = mid->y-c1->y;
	nspline = pspline[j]->to->next;
	c0 = &nspline->from->nextcp;
	c1 = &nspline->to->prevcp;
	end = &nspline->to->me;

	temp1.x = myround(c0->x,round);
	temp1.y = myround(c0->y,round);
	c0 = &temp1;
	temp2.x = myround(c1->x,round);
	temp2.y = myround(c1->y,round);
	c1 = &temp2;
	temp3.x = myround(end->x,round);
	temp3.y = myround(end->y,round);
	end = &temp3;

	offsets[j][4].x = c0->x-mid->x;		offsets[j][4].y = c0->y-mid->y;
	offsets[j][5].x = c1->x-c0->x;		offsets[j][5].y = c1->y-c0->y;
	offsets[j][6].x = end->x-c1->x;		offsets[j][6].y = end->y-c1->y;
	offsets[j][7].x = end->x;		offsets[j][7].y = end->y;
	current[j].x = end->x;
	current[j].y = end->y;
    }

    if ( hdb!=NULL )
	HintSetup(gb,hdb,pspline[0]->to->next->to,round,false);

    if ( gb->pt+2 >= gb->end )
	GrowBuffer(gb);

    *(gb->pt)++ = 1+139;		/* 1 */
    *(gb->pt)++ = 10;			/* callsubr */
    for ( i=0; i<7; ++i ) {
	if ( gb->pt+20 >= gb->end )
	    GrowBuffer(gb);
	for ( j=0; j<instance_count; ++j ) {
	    data[j][0] = offsets[j][i].x;
	    data[j][1] = offsets[j][i].y;
	}
	AddData(gb,data,instance_count,2,round);
	*(gb->pt)++ = 21;		/* rmoveto */
	*(gb->pt)++ = 2+139;		/* 2 */
	*(gb->pt)++ = 10;		/* callsubr */
    }
    if ( gb->pt+20 >= gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 50+139;		/* 50, .50 pixels */
    for ( j=0; j<instance_count; ++j ) {
	data[j][0] = offsets[j][7].x;
	data[j][1] = offsets[j][7].y;
    }
    AddData(gb,data,instance_count,2,round);
    *(gb->pt)++ = 0+139;		/* 0 */
    *(gb->pt)++ = 10;			/* callsubr */

    current->x = end->x;
    current->y = end->y;
}

static void _CvtPsSplineSet(GrowBuf *gb, SplinePointList *spl[MmMax], int instance_count,
	DBasePoint current[MmMax],
	int round, struct hintdb *hdb, int is_order2, int stroked ) {
    Spline *spline[MmMax], *first;
    SplinePointList temp[MmMax], *freeme=NULL;
    int i;

    if ( is_order2 ) {
	freeme = spl[0] = SplineSetsPSApprox(spl[0]);
	instance_count = 1;
    }
    while ( spl[0]!=NULL ) {
	first = NULL;
	for ( i=0; i<instance_count; ++i )
	    SplineSetReverse(spl[i]);
	/* For some reason fontographer reads its spline in in the reverse */
	/*  order from that I use. I'm not sure how they do that. The result */
	/*  being that what I call clockwise they call counter. Oh well. */
	/*  If I reverse the splinesets after reading them in, and then again*/
	/*  when saving them out, all should be well */
	if ( spl[0]->first->flexy || spl[0]->first->flexx ) {
	    /* can't handle a flex (mid) point as the first point. rotate the */
	    /* list by one, this is possible because only closed paths have */
	    /* points marked as flex, and because we can't have two flex mid- */
	    /* points in a row */
	    for ( i = 0; i<instance_count; ++i ) {
		temp[i] = *spl[i];
		temp[i].first = temp[i].last = spl[i]->first->next->to;
		spl[i] = &temp[i];
	    }
	    if ( spl[0]->first->flexy || spl[0]->first->flexx ) {
		/* well, well, well. We did have two flexes in a row */
		for ( i = 0; i<instance_count; ++i ) {
		    spl[i]->first->flexx = spl[i]->first->flexy = false;
		}
	    }
	}
	splmoveto(gb,current,spl,instance_count,false,round,hdb);
	for ( i=0; i<instance_count; ++i )
	    spline[i] = spl[i]->first->next;
	while ( spline[0]!=NULL && spline[0]!=first ) {
	    if ( first==NULL ) first = spline[0];
	    if ( SplinesAreFlexible(spline,instance_count) &&
		    (hdb->noconflicts || spline[0]->to->hintmask==NULL)) {
		flexto(gb,current,spline,instance_count,round,hdb);	/* does two adjacent splines */
		for ( i=0; i<instance_count; ++i )
		    spline[i] = spline[i]->to->next;
	    } else if ( spline[0]->knownlinear && spline[0]->to==spl[0]->first ) {
		/* We can finish this off with the closepath */
	break;
	    } else if ( spline[0]->knownlinear )
		moveto(gb,current,spline,instance_count,true,round,hdb);
	    else
		curveto(gb,current,spline,instance_count,round,hdb);
	    for ( i=0; i<instance_count; ++i )
		spline[i] = spline[i]->to->next;
	}
	if ( !stroked || spl[0]->first->prev!=NULL ) {
	    if ( gb->pt+1 >= gb->end )
		GrowBuffer(gb);
	    *(gb->pt)++ = 9;			/* closepath */
	}
	for ( i=0; i<instance_count; ++i ) {
	    SplineSetReverse(spl[i]);
	    /* Of course, I have to Reverse again to get back to my convention after*/
	    /*  saving */
	    spl[i] = spl[i]->next;
	}
    }
    SplinePointListsFree(freeme);
}

static int IsPSSeacable(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->justtranslated )
return( false );
    }
return( true );
}

static RefChar *RefFindAdobe(RefChar *r, RefChar *t,int layer) {
    *t = *r;
    while ( t->adobe_enc==-1 && t->sc->layers[layer].refs!=NULL &&
	    t->sc->layers[layer].refs->next==NULL &&
	    t->sc->layers[layer].splines==NULL &&
	    t->sc->layers[layer].refs->justtranslated ) {
	t->transform[4] += t->sc->layers[layer].refs->transform[4];
	t->transform[5] += t->sc->layers[layer].refs->transform[5];
	t->adobe_enc = t->sc->layers[layer].refs->adobe_enc;
	t->orig_pos = t->sc->layers[layer].refs->orig_pos;
	t->sc = t->sc->layers[layer].refs->sc;
    }
return( t );
}

static int IsSeacable(GrowBuf *gb, SplineChar *scs[MmMax],
	int instance_count, int round,int layer) {
    /* can be at most two chars in a seac (actually must be exactly 2, but */
    /*  I'll put in a space if there's only one (and if splace is blank) */
    RefChar *r1, *r2, *rt, *refs;
    RefChar space, t1, t2;
    DBounds b;
    int i, j, swap;
    bigreal data[MmMax][6];

    for ( j=0 ; j<instance_count; ++j )
	if ( !IsPSSeacable(scs[j],layer))
return( false );

    refs = scs[0]->layers[layer].refs;
    if ( refs==NULL )
return( false );

    r1 = refs;
    if ((r2 = r1->next)==NULL ) {
	RefChar *refs = r1->sc->layers[layer].refs;
	if ( refs!=NULL && refs->next!=NULL && refs->next->next==NULL &&
		r1->sc->layers[layer].splines==NULL &&
		refs->adobe_enc!=-1 && refs->next->adobe_enc!=-1 ) {
	    r2 = refs->next;
	    r1 = refs;
	}
    }
    if ( r2==NULL ) {
	r2 = &space;
	memset(r2,'\0',sizeof(space));
	space.adobe_enc = ' ';
	space.transform[0] = space.transform[3] = 1.0;
	for ( i=0; i<scs[0]->parent->glyphcnt; ++i )
	    if ( scs[0]->parent->glyphs[i]!=NULL &&
		    strcmp(scs[0]->parent->glyphs[i]->name,"space")==0 )
	break;
	if ( i==scs[0]->parent->glyphcnt )
	    r2 = NULL;			/* No space???? */
	else {
	    space.sc = scs[0]->parent->glyphs[i];
	    if ( space.sc->layers[layer].splines!=NULL || space.sc->layers[layer].refs!=NULL )
		r2 = NULL;
	}
    } else if ( r2->next!=NULL )
	r2 = NULL;

    /* check for something like "AcyrillicBreve" which has a ref to Acyril */
    /*  (which doesn't have an adobe enc) which in turn has a ref to A (which */
    /*  does) */
    if ( r2!=NULL ) {
	if ( r1->adobe_enc==-1 )
	    r1 = RefFindAdobe(r1,&t1,layer);
	if ( r2->adobe_enc==-1 )
	    r2 = RefFindAdobe(r2,&t2,layer);
    }

/* CID fonts have no encodings. So we can't use seac to reference characters */
/*  in them. The other requirements are just those of seac */
    if ( r2==NULL ||
	    r1->adobe_enc==-1 ||
	    r2->adobe_enc==-1 ||
	    ((r1->transform[4]!=0 || r1->transform[5]!=0 || r1->sc->width!=scs[0]->width ) &&
		 (r2->transform[4]!=0 || r2->transform[5]!=0 || r2->sc->width!=scs[0]->width)) )
return( false );

    swap = false;
    if ( r1->transform[4]!=0 || r1->transform[5]!=0 ) {
	rt = r1; r1 = r2; r2 = rt;
	swap = !swap;
    }

    SplineCharFindBounds(r1->sc,&b);
    r1->sc->lsidebearing = myround(b.minx,round);
    SplineCharFindBounds(r2->sc,&b);
    r2->sc->lsidebearing = myround(b.minx,round);

    if ( (r1->sc->width!=scs[0]->width || r1->sc->lsidebearing!=scs[0]->lsidebearing) &&
	 r2->sc->width==scs[0]->width && r2->sc->lsidebearing==scs[0]->lsidebearing &&
	    r2->transform[4]==0 && r2->transform[5]==0 ) {
	rt = r1; r1 = r2; r2 = rt;
	swap = !swap;
    }
    if ( r1->sc->width!=scs[0]->width || r1->sc->lsidebearing!=scs[0]->lsidebearing ||
	 r1->transform[4]!=0 || r1->transform[5]!=0 )
return( false );

    for ( j=0; j<instance_count; ++j ) {
	SplineChar *r2sc = scs[j]->parent->glyphs[r2->sc->orig_pos];
	RefChar *r3, t3;

	SplineCharFindBounds(r2sc,&b);
	if ( scs[j]->layers[layer].refs!=NULL && scs[j]->layers[layer].refs->next==NULL )
	    r3 = r2;		/* Space, not offset */
	else if ( swap )
	    r3 = RefFindAdobe(scs[j]->layers[layer].refs,&t3,layer);
	else
	    r3 = RefFindAdobe(scs[j]->layers[layer].refs->next,&t3,layer);

	b.minx = myround(b.minx,round);
	data[j][0] = b.minx;
	data[j][1] = r3->transform[4] + b.minx-scs[j]->lsidebearing;
	data[j][2] = r3->transform[5];
    }
    AddData(gb,data,instance_count,3,round);
    AddNumber(gb,r1->adobe_enc,round);
    AddNumber(gb,r2->adobe_enc,round);
    if ( gb->pt+2>gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 12;
    *(gb->pt)++ = 6;			/* seac 12,6 */

return( true );
}

static int _SCNeedsSubsPts(SplineChar *sc,int layer) {
    RefChar *ref;

    if ( sc->hstem==NULL && sc->vstem==NULL )
return( false );

    if ( sc->layers[layer].splines!=NULL )
return( sc->layers[layer].splines->first->hintmask==NULL );

    for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next )
	if ( ref->layers[0].splines!=NULL )
return( ref->layers[0].splines->first->hintmask==NULL );

return( false );		/* It's empty. that's easy. */
}

static int SCNeedsSubsPts(SplineChar *sc,enum fontformat format,int layer) {
    if ( (format!=ff_mma && format!=ff_mmb) || sc->parent->mm==NULL ) {
	if ( !sc->hconflicts && !sc->vconflicts )
return( false );		/* No conflicts, no swap-over points needed */
return( _SCNeedsSubsPts(sc,layer));
    } else {
	MMSet *mm = sc->parent->mm;
	int i;
	for ( i=0; i<mm->instance_count; ++i ) if ( sc->orig_pos<mm->instances[i]->glyphcnt ) {
	    if ( _SCNeedsSubsPts(mm->instances[i]->glyphs[sc->orig_pos],layer) )
return( true );
	}
return( false );
    }
}

static void ExpandRef1(GrowBuf *gb, SplineChar *scs[MmMax], int instance_count,
	struct hintdb *hdb, RefChar *r[MmMax], BasePoint trans[MmMax],
	DBasePoint current[MmMax],
	struct pschars *subrs, int round, int iscjk, int layer) {
    BasePoint *bpt;
    BasePoint rtrans[MmMax], rpos[MmMax];
    int i;

    for ( i=0; i<instance_count; ++i ) {
	rtrans[i].x = r[i]->transform[4]+trans[i].x;
	rtrans[i].y = r[i]->transform[5]+trans[i].y;
	if ( round ) {
	    rtrans[i].x = rint(rtrans[i].x);
	    rtrans[i].y = rint(rtrans[i].y);
	}
    }

    BreakSubroutine(gb,hdb);
    if ( r[0]->sc == scs[0] ) {
	/* Hints for self */
	if ( hdb->cnt>0 && !hdb->noconflicts && NeverConflicts(r,instance_count)) {
	    CvtPsHints(gb,scs,instance_count,true,round,iscjk,NULL);
	    CvtPsHints(gb,scs,instance_count,false,round,iscjk,NULL);
	}
    } else {
	/* Hints for a real reference */
	if ( !NeverConflicts(r,instance_count) || r[0]->sc->layers[layer].anyflexes || AnyRefs(r[0]->sc,layer) )
	    /* Hints already done */;
	else if ( hdb->noconflicts )
	    /* Hints already done */;
	else if ( r[0]->sc->hstem!=NULL || r[0]->sc->vstem!=NULL )
	    CallTransformedHintSubr(gb,hdb,scs,r,trans,instance_count,round);
    }

    if ( hdb!=NULL && hdb->gi!=NULL )
	bpt = hdb->gi->psubrs[r[0]->sc->ttf_glyph].startstop;
    else
	bpt = (BasePoint *) (subrs->keys[r[0]->sc->ttf_glyph]);
    for ( i=0; i<instance_count; ++i ) {
	rpos[i].x = bpt[2*i].x + rtrans[i].x;
	rpos[i].y = bpt[2*i].y + rtrans[i].y;
    }
    refmoveto(gb,current,rpos, instance_count,false,round,hdb,NULL);
    hdb->startset = true;

    if ( hdb!=NULL && hdb->gi!=NULL ) {
	GlyphInfo *gi = hdb->gi;
	StartNextSubroutine(gb,hdb);
	gi->bits[gi->bcnt].psub_index = r[0]->sc->ttf_glyph;
	++gi->bcnt;
	gi->justbroken = true;
    } else {
	AddNumber(gb,r[0]->sc->ttf_glyph,round);
	if ( gb->pt+1>=gb->end )
	    GrowBuffer(gb);
	*gb->pt++ = 10;
    }

    for ( i=0; i<instance_count; ++i ) {
	current[i].x = bpt[2*i+1].x + rtrans[i].x;
	current[i].y = bpt[2*i+1].y + rtrans[i].y;
    }
}

static void RSC2PS1(GrowBuf *gb, SplineChar *base[MmMax],SplineChar *rsc[MmMax],
	struct hintdb *hdb, BasePoint *trans, struct pschars *subrs,
	DBasePoint current[MmMax], int flags, int iscjk,
	int instance_count, int layer ) {
    BasePoint subtrans[MmMax];
    SplineChar *rscs[MmMax];
    int round = (flags&ps_flag_round)? true : false;
    RefChar *refs[MmMax];
    SplineSet *spls[MmMax], *freeme[MmMax];
    int i;
    int wasntconflicted = hdb->noconflicts;

    for ( i=0; i<instance_count; ++i ) {
	spls[i] = rsc[i]->layers[layer].splines;
	if ( base[0]!=rsc[0] )
	    spls[i] = freeme[i] = SPLCopyTranslatedHintMasks(spls[i],base[i],rsc[i],&trans[i]);
    }
    _CvtPsSplineSet(gb,spls,instance_count,current,round,hdb,
	    base[0]->layers[layer].order2,base[0]->parent->strokedfont);
    if ( base[0]!=rsc[0] )
	for ( i=0; i<instance_count; ++i )
	    SplinePointListsFree(freeme[i]);

    for ( i=0; i<instance_count; ++i )
	refs[i] = rsc[i]->layers[layer].refs;
    while ( refs[0]!=NULL ) {
	for ( i=0; i<instance_count; ++i )
	    spls[i] = refs[i]->layers[0].splines;
	if ( !refs[0]->justtranslated ) {
	    for ( i=0; i<instance_count; ++i )
		spls[i] = freeme[i] = SPLCopyTransformedHintMasks(refs[i],base[i],&trans[i],layer);
	    if ( NeverConflicts(refs,instance_count) && !hdb->noconflicts &&
		    refs[0]->transform[1]==0 && refs[0]->transform[2]==0 )
		CallTransformedHintSubr(gb,hdb,base,refs,trans,instance_count,round);
	    _CvtPsSplineSet(gb,spls,instance_count,current,round,hdb,
		    base[0]->layers[layer].order2,base[0]->parent->strokedfont);
	    for ( i=0; i<instance_count; ++i )
		SplinePointListsFree(freeme[i]);
	} else if ( refs[0]->sc->ttf_glyph!=0x7fff &&
		((flags&ps_flag_nohints) ||
		 !refs[0]->sc->layers[layer].anyflexes ||
		 (refs[0]->transform[4]+trans[0].x==0 && refs[0]->transform[5]+trans[0].y==0)) &&
		((flags&ps_flag_nohints) ||
		 NeverConflicts(refs,instance_count) ||
		 AllStationary(refs,trans,instance_count)) ) {
	    ExpandRef1(gb,base,instance_count,hdb,refs,trans,
		    current,subrs,round,iscjk,layer);
	} else {
	    for ( i=0; i<instance_count; ++i ) {
		subtrans[i].x = trans[i].x + refs[i]->transform[4];
		subtrans[i].y = trans[i].y + refs[i]->transform[5];
		rscs[i] = refs[i]->sc;
	    }
	    if ( !hdb->noconflicts && NeverConflicts(refs,instance_count)) {
		CallTransformedHintSubr(gb,hdb,base,refs,trans,instance_count,round);
		hdb->noconflicts = true;
	    }
	    RSC2PS1(gb,base,rscs,hdb,subtrans,subrs,current,flags,iscjk,
		    instance_count,layer);
	    hdb->noconflicts = wasntconflicted;
	}
	for ( i=0; i<instance_count; ++i )
	    refs[i] = refs[i]->next;
    }
}

static unsigned char *SplineChar2PS(SplineChar *sc,int *len,int round,int iscjk,
	struct pschars *subrs,int flags,enum fontformat format,
	GlyphInfo *gi) {
    DBounds b;
    GrowBuf gb;
    DBasePoint current[MmMax];
    unsigned char *ret;
    struct hintdb hintdb, *hdb=NULL;
    StemInfo *oldh[MmMax], *oldv[MmMax];
    int hc[MmMax], vc[MmMax];
    BasePoint trans[MmMax];
    int instance_count, i;
    SplineChar *scs[MmMax];
    bigreal data[MmMax][6];
    MMSet *mm = sc->parent->mm;
    HintMask *hm[MmMax];
    int fixuphm = false;

    if ( !(flags&ps_flag_nohints) && SCNeedsSubsPts(sc,format,gi->layer))
	SCFigureHintMasks(sc,gi->layer);

    if ( (format==ff_mma || format==ff_mmb) && mm!=NULL ) {
	instance_count = mm->instance_count;
	if ( instance_count>16 )
	    instance_count = 16;
	for ( i=0; i<instance_count; ++i )
	    scs[i] = mm->instances[i]->glyphs[sc->orig_pos];
    } else {
	instance_count = 1;
	scs[0] = sc;
	mm = NULL;
    }

    if ( flags&ps_flag_nohints ) {
	for ( i=0; i<instance_count; ++i ) {
	    oldh[i] = scs[i]->hstem; oldv[i] = scs[i]->vstem;
	    hc[i] = scs[i]->hconflicts; vc[i] = scs[i]->vconflicts;
	    scs[i]->hstem = NULL; scs[i]->vstem = NULL;
	    scs[i]->hconflicts = false; scs[i]->vconflicts = false;
	}
    } else {
	for ( i=0; i<instance_count; ++i )
	    if ( scs[i]->vconflicts || scs[i]->hconflicts )
	break;
	if ( scs[0]->layers[gi->layer].splines!=NULL && i==instance_count ) {	/* No conflicts */
	    fixuphm = true;
	    for ( i=0; i<instance_count; ++i ) {
		hm[i] = scs[i]->layers[gi->layer].splines->first->hintmask;
		scs[i]->layers[gi->layer].splines->first->hintmask = NULL;
	    }
	}
    }

    memset(&gb,'\0',sizeof(gb));
    memset(current,'\0',sizeof(current));
    for ( i=0; i<instance_count; ++i ) {
	SplineCharFindBounds(scs[i],&b);
	scs[i]->lsidebearing = current[i].x = myround(b.minx,round);
	data[i][0] = current[i].x;
	data[i][1] = scs[i]->width;
    }
    AddData(&gb,data,instance_count,2,round);
    *gb.pt++ = 13;				/* hsbw, lbearing & width */

    memset(&hintdb,0,sizeof(hintdb));
    hintdb.subrs = subrs; hintdb.iscjk = iscjk&~0x100; hintdb.scs = scs;
    hintdb.instance_count = instance_count;
    hintdb.cnt = NumberHints(scs,instance_count);
    hintdb.noconflicts = true;
    hintdb.gi = gi;
    for ( i=0; i<instance_count; ++i )
	if ( scs[i]->hconflicts || scs[i]->vconflicts )
	    hintdb.noconflicts = false;
    hdb = &hintdb;
    if ( gi!=NULL )
	gi->bcnt = -1;

    /* If this char is being placed in a subroutine, then we don't want to */
    /*  use seac because somebody is going to call that subroutine and */
    /*  add another reference to it later. CID keyed fonts also can't use */
    /*  seac (they have no encoding so it doesn't work), that's what iscjk&0x100 */
    /*  tests for */
    if ( scs[0]->ttf_glyph==0x7fff && !(iscjk&0x100) && !(flags&ps_flag_noseac) &&
	    IsSeacable(&gb,scs,instance_count,round,gi->layer)) {
	if ( gi )
	    gi->active->wasseac = true;
	/* in MM fonts, all should share the same refs, so all should be */
	/* seac-able if one is */
    } else {
	iscjk &= ~0x100;
	if ( iscjk && instance_count==1 )
	    CounterHints1(&gb,sc,round);	/* Must come immediately after hsbw */
	if ( hintdb.noconflicts ) {
	    CvtPsHints(&gb,scs,instance_count,true,round,iscjk,NULL);
	    CvtPsHints(&gb,scs,instance_count,false,round,iscjk,NULL);
	}
	memset(&trans,0,sizeof(trans));
	RSC2PS1(&gb,scs,scs,hdb,trans,subrs,current,flags,iscjk,
		instance_count,gi->layer);
    }
    if ( gi->bcnt==-1 ) {	/* If it's whitespace */
	gi->bcnt = 0;
	StartNextSubroutine(&gb,hdb);
    }
    BreakSubroutine(&gb,hdb);
    MoveSubrsToChar(gi);
    ret = NULL;

    if ( hdb!=NULL ) {
	struct mhlist *mh, *mhnext;
	for ( mh=hdb->sublist; mh!=NULL; mh=mhnext ) {
	    mhnext = mh->next;
	    free(mh);
	}
    }
    free(gb.base);
    if ( flags&ps_flag_nohints ) {
	for ( i=0; i<instance_count; ++i ) {
	    scs[i]->hstem = oldh[i]; scs[i]->vstem = oldv[i];
	    scs[i]->hconflicts = hc[i]; scs[i]->vconflicts = vc[i];
	}
    } else if ( fixuphm ) {
	for ( i=0; i<instance_count; ++i )
	    scs[i]->layers[gi->layer].splines->first->hintmask = hm[i];
    }
return( ret );
}

#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
static int AlwaysSeacable(SplineChar *sc,int flags) {
    struct splinecharlist *d;
    RefChar *r;

    if ( sc->parent->cidmaster!=NULL )	/* Can't use seac in CID fonts, no encoding */
return( false );
    if ( flags&ps_flag_noseac )
return( false );

    for ( d=sc->dependents; d!=NULL; d = d->next ) {
	if ( d->sc->layers[layer].splines!=NULL )	/* I won't deal with things with both splines and refs. */
    continue;				/*  skip it */
	for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next ) {
	    if ( !r->justtranslated )
	break;				/* Can't deal with it either way */
	}
	if ( r!=NULL )		/* Bad transform matrix */
    continue;			/* Can't handle either way, skip */

	for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next ) {
	    if ( r->adobe_enc==-1 )
return( false );			/* not seacable, but could go in subr */
	}
	r = d->sc->layers[layer].refs;
	if ( r->next!=NULL && r->next->next!=NULL )
return( false );		/* seac only takes 2 glyphs */
	if ( r->next!=NULL &&
		((r->transform[4]!=0 || r->transform[5]!=0 || r->sc->width!=d->sc->width) &&
		 (r->next->transform[4]!=0 || r->next->transform[5]!=0 || r->next->sc->width!=d->sc->width)))
return( false );		/* seac only allows one to be translated, and the untranslated one must have the right width */
	if ( r->next==NULL &&
		(r->transform[4]!=0 || r->transform[5]!=0 || r->sc->width!=d->sc->width))
return( false );
    }
    /* Either always can be represented by seac, or sometimes by neither */
return( true );
}

/* normally we can't put a character with hint conflicts into a subroutine */
/*  (because when we would have to invoke the hints within the subr and */
/*   hints are expressed as absolute positions, so if the char has been */
/*   translated we can't do the hints right). BUT if the character is not */
/*  translated, and if it has the right lbearing, then the hints in the */
/*  ref will match those in the character and we can use a subroutine for */
/*  both */
/* If at least one ref fits our requirements then return true */
/* The same reasoning applies to flex hints. There are absolute expressions */
/*  in them too. */
static int SpecialCaseConflicts(SplineChar *sc) {
    struct splinecharlist *d;
    RefChar *r;
    DBounds sb, db;

    SplineCharFindBounds(sc,&sb);
    for ( d=sc->dependents; d!=NULL; d = d->next ) {
	SplineCharFindBounds(d->sc,&db);
	if ( db.minx != sb.minx )
    continue;
	for ( r=d->sc->layers[layer].refs; r!=NULL; r=r->next )
	    if ( r->sc == sc && r->justtranslated &&
		    r->transform[4]==0 && r->transform[5]==0 )
return( true );
    }
return( false );
}

static BasePoint *FigureStartStop(SplineChar *sc, GlyphInfo *gi ) {
    int m, didfirst;
    SplineChar *msc;
    SplineSet *spl;
    RefChar *r;
    BasePoint *startstop;

    /* We need to know the location of the first point on the */
    /*  first path (need to rmoveto it, and the location of the */
    /*  last point on the last path (will need to move from it */
    /*  for the next component) */

    startstop = calloc(2*gi->instance_count,sizeof(BasePoint));
    for ( m=0; m<gi->instance_count; ++m ) {
	if ( gi->instance_count==1 || sc->parent->mm==NULL )
	    msc = sc;
	else
	    msc = sc->parent->mm->instances[m]->glyphs[sc->orig_pos];
	didfirst = false;
	spl = msc->layers[layer].splines;
	if ( spl!=NULL ) {
	    startstop[0] = spl->first->me;
	    didfirst = true;
	    while ( spl!=NULL ) {
		/* Closepath does NOT set the current point */
		/* Remember we reverse PostScript */
		if ( spl->last==spl->first && spl->first->next!=NULL &&
			spl->first->next->knownlinear )
		    startstop[1] = spl->first->next->to->me;
		else
		    startstop[1] = spl->last->me;
		spl = spl->next;
	    }
	}
	for ( r=msc->layers[layer].refs; r!=NULL; r=r->next ) {
	    spl = r->layers[0].splines;
	    if ( spl!=NULL ) {
		if ( !didfirst )
		    startstop[0] = spl->first->me;
		didfirst = true;
	    }
	    while ( spl!=NULL ) {
		/* Closepath does NOT set the current point */
		/* Remember we reverse PostScript */
		if ( spl->last==spl->first && spl->first->next!=NULL &&
			spl->first->next->knownlinear )
		    startstop[1] = spl->first->next->to->me;
		else
		    startstop[1] = spl->last->me;
		spl = spl->next;
	    }
	}
    }
return( startstop );
}
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */

/* Mark those glyphs which can live totally in subrs */
static void SplineFont2FullSubrs1(int flags,GlyphInfo *gi) {
    int i;
    SplineChar *sc;
#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
    int anydone, cc;
    struct potentialsubrs *ps;
    SplineSet *spl;
    RefChar *r;
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */

    if ( !autohint_before_generate && !(flags&ps_flag_nohints))
	SplineFontAutoHintRefs(gi->sf,gi->layer);
    for ( i=0; i<gi->glyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL )
	sc->ttf_glyph = 0x7fff;

#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
    anydone = true;
    while ( anydone ) {
	anydone = false;
	for ( i=0; i<gi->glyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL ) {
	    if ( !SCWorthOutputting(sc) || sc->ttf_glyph!=0x7fff )
	continue;
	    /* if the glyph is a single contour with no hintmasks then */
	    /*  our single contour code will find it. If we do it here too */
	    /*  we'll get a subr which points to another subr. Very dull and */
	    /*  a waste of space */
	    cc = 0;
	    for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next )
		++cc;
	    for ( r= sc->layers[layer].refs; r!=NULL && cc<2 ; r=r->next ) {
		for ( spl=r->layers[0].splines; spl!=NULL; spl=spl->next )
		    ++cc;
	    }
	    if ( cc<2 )
	continue;
	    /* Put the */
	    /*  character into a subr if it is referenced by other characters */
	    if ( (sc->dependents!=NULL &&
		     ((!sc->hconflicts && !sc->vconflicts && !sc->layers[gi->layer].anyflexes) ||
			 SpecialCaseConflicts(sc)) &&
		     !AlwaysSeacable(sc,flags))) {
		RefChar *r;

		for ( r=sc->layers[layer].refs; r!=NULL; r=r->next )
		    if ( r->sc->ttf_glyph==0x7fff )
		break;
		if ( r!=NULL )	/* Contains a reference to something which is */
	continue;		/* not in a sub itself. Skip it for now, we'll*/
				/* come back to it next pass when perhaps the */
			        /* reference will be nicely ensconced itself */
		if ( gi->pcnt>=gi->pmax )
		    gi->psubrs = realloc(gi->psubrs,(gi->pmax+=gi->glyphcnt)*sizeof(struct potentialsubrs));
		ps = &gi->psubrs[gi->pcnt];
		memset(ps,0,sizeof(*ps));	/* set cnt to 0 */
		ps->idx = gi->pcnt++;
		ps->full_glyph_index = i;
		sc->ttf_glyph = gi->pcnt-1;
		ps->startstop = FigureStartStop(sc,gi);
		anydone = true;
	    }
	}
    }
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */
}

typedef enum font_pitch { pitch_unknown, pitch_variable, pitch_fixed, pitch_dual } FontPitch;

static FontPitch SFComputePitch(SplineFont *sf, int *p_width) {
    FontPitch pitch = pitch_unknown;
    int width = -1;

    for ( int i=0; i<sf->glyphcnt; ++i) {
	/* Only trust the width of notdef if it's got some content */
	/* (at least as far as fixed pitch determination goes) */
    	if ( SCWorthOutputting(sf->glyphs[i]) &&
	     strcmp(sf->glyphs[i]->name,".null")!=0 &&
	     strcmp(sf->glyphs[i]->name,"nonmarkingreturn")!=0 &&
	     (strcmp(sf->glyphs[i]->name,".notdef")!=0 || sf->glyphs[i]->layers[ly_fore].splines!=NULL))
	{
	    int16_t this_width = sf->glyphs[i]->width;
	    if (this_width == 0) {
		/* Zero-width glyphs, such as control characters or diacritical marks
		   don't affect the state. */
		continue;
	    }

	    if (pitch == pitch_unknown) {
		width = this_width;
		pitch = pitch_fixed;
	    } else if (pitch == pitch_fixed) {
	    	if (this_width == 2*width) {
		    pitch = pitch_dual;
		} else if (2*this_width == width) {
		    width = this_width;
		    pitch = pitch_dual;
		} else if (this_width != width){
		    width = -1;
		    pitch = pitch_variable;
		    break; /* no further check necessary */
		}
	    } else if (pitch == pitch_dual && (this_width != width && this_width != 2*width)) {
		width = -1;
		pitch = pitch_variable;
		break; /* no further check necessary */
	    }
	}
    }

    if (p_width) *p_width = width;
    return pitch;
}

bool SFIsFixedWidth(SplineFont *sf) {
    FontPitch pitch = SFComputePitch(sf, NULL);
    return (pitch == pitch_fixed || pitch == pitch_dual);
}

/* Return value:
	-2: unknown pitch
	-1: variable or dual pitch
	width>0: fixed pitch with given character width
*/
int SFOneWidth(SplineFont *_sf) {
    int width, total_width = -2;
    int k = 0;
    SplineFont *sf;
    FontPitch pitch = pitch_unknown;

    if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster;
    do {
	sf = _sf->subfonts==NULL? _sf : _sf->subfonts[k];
	pitch = SFComputePitch(sf, &width);
	if (pitch == pitch_unknown) {
	    continue;
	}

	if (pitch == pitch_dual || pitch == pitch_variable ||
	    (pitch == pitch_fixed && total_width != -2 && width != total_width)) {
	    total_width = -1;
	    break;
	}
	total_width = width;
	++k;
    } while ( k<_sf->subfontcnt );
    return total_width;
}

int SFOneHeight(SplineFont *sf) {
    int width, i;

    if ( !sf->hasvmetrics )
return( sf->ascent+sf->descent );

    width = -2;
    for ( i=0; i<sf->glyphcnt; ++i ) if ( SCWorthOutputting(sf->glyphs[i]) &&
	    (strcmp(sf->glyphs[i]->name,".notdef")!=0 || sf->glyphs[i]->layers[ly_fore].splines!=NULL)) {
	/* Only trust the width of notdef if it's got some content */
	/* (at least as far as fixed pitch determination goes) */
	if ( width==-2 ) width = sf->glyphs[i]->vwidth;
	else if ( width!=sf->glyphs[i]->vwidth ) {
	    width = -1;
    break;
	}
    }
return(width);
}

int SFIsCJK(SplineFont *sf,EncMap *map) {
    char *val;

    if ( (val = PSDictHasEntry(sf->private,"LanguageGroup"))!=NULL )
return( strtol(val,NULL,10));

    if ( map->enc->is_japanese || map->enc->is_korean ||
	    map->enc->is_tradchinese || map->enc->is_simplechinese )
return( true );
    if ( (map->enc->is_unicodebmp || map->enc->is_unicodefull) &&
	    sf->glyphcnt>0x3000 &&
	    SCWorthOutputting(sf->glyphs[0x3000]) &&
	    !SCWorthOutputting(sf->glyphs['A']) )
return( true );
    if ( map->enc==&custom ) {
	/* If it's in a CID font and it doesn't contain alphabetics, then */
	/*  it's assumed to be CJK */
	if ( sf->cidmaster!=NULL )
return( !SCWorthOutputting(SFGetChar(sf,'A',NULL)) &&
	!SCWorthOutputting(SFGetChar(sf,0x391,NULL)) &&		/* Alpha */
	!SCWorthOutputting(SFGetChar(sf,0x410,NULL)) &&		/* Cyrillic A */
	!SCWorthOutputting(SFGetChar(sf,-1,"uni0041.hw")) );	/* Halfwidth A, non standard name, from my cidmap */
    }

return( false );
}

static void SetupType1Subrs(struct pschars *subrs,GlyphInfo *gi) {
    int scnt, call_size;
    int i;

    scnt = subrs->next;
    call_size = gi->pcnt+scnt<1131 ? 3 : 6;
    for ( i=0; i<gi->pcnt; ++i ) {
	/* A subroutine call takes somewhere between 2 and 6 bytes itself. */
	/*  and we must add a return statement to the end. We don't want to */
	/*  make things bigger */
	if ( gi->psubrs[i].full_glyph_index!=-1 )
	    gi->psubrs[i].idx = scnt++;
	else if ( gi->psubrs[i].cnt*gi->psubrs[i].len>(gi->psubrs[i].cnt*call_size)+gi->psubrs[i].len+1 )
	    gi->psubrs[i].idx = scnt++;
	else
	    gi->psubrs[i].idx = -1;
    }

    subrs->cnt = scnt;
    subrs->next = scnt;
    subrs->lens = realloc(subrs->lens,scnt*sizeof(int));
    subrs->values = realloc(subrs->values,scnt*sizeof(unsigned char *));

    for ( i=0; i<gi->pcnt; ++i ) {
	scnt = gi->psubrs[i].idx;
	if ( scnt==-1 || gi->psubrs[i].full_glyph_index != -1 )
    continue;
	subrs->lens[scnt] = gi->psubrs[i].len+1;
	subrs->values[scnt] = malloc(subrs->lens[scnt]);
	memcpy(subrs->values[scnt],gi->psubrs[i].data,gi->psubrs[i].len);
	subrs->values[scnt][gi->psubrs[i].len] = 11;	/* Add a return to end of subr */
    }
}

static void SetupType1Chrs(struct pschars *chrs,struct pschars *subrs,GlyphInfo *gi, int iscid) {
    int i,k,j;

    /* If a glyph lives entirely in a subroutine then we need to create both */
    /*  the subroutine entry, and the char entry which calls the subr. */
    /* The subroutine entry will be everything EXCEPT the glyph header */
    /* the char entry will be the glyph header and a subroutine call */
    /* If the glyph does not go into a subr then everything goes into the char */
    for ( i=0; i<gi->glyphcnt; ++i ) {
	int len=0;
	struct glyphbits *gb = &gi->gb[i];
	if ( gb->sc==NULL )
    continue;
	if ( !iscid )
	    chrs->keys[i] = copy(gb->sc->name);
	for ( k=0; k<2; ++k ) if ( k!=0 || gb->sc->ttf_glyph!=0x7fff ) {
	    uint8_t *vals;
	    for ( j=0; j<gb->bcnt; ++j ) {
		if ( k!=0 || j!=0 )
		    len += gb->bits[j].dlen;
		if ( k==1 && gb->sc->ttf_glyph!=0x7fff ) {
		    int si = gi->psubrs[ gb->sc->ttf_glyph ].idx;
		    len += 1 + (si<=107?1:si<=1131?2:5);
	    break;
		}
		if ( gi->psubrs[ gb->bits[j].psub_index ].idx==-1 )
		    len += gi->psubrs[ gb->bits[j].psub_index ].len;
		else {
		    int si = gi->psubrs[ gb->bits[j].psub_index ].idx;
		    len += 1 + (si<=107?1:si<=1131?2:5);
		    /* Space for a subr call & the sub number to call */
		}
	    }
	    if ( k==0 ) {
		int si = gi->psubrs[ gb->sc->ttf_glyph ].idx;
		subrs->lens[si] = len+1;
		vals = subrs->values[si] = malloc(len+2);
	    } else {
		/* Don't need or want and endchar if we are using seac */
		chrs->lens[i] = len + !gb->wasseac;
		vals = chrs->values[i] = malloc(len+2); /* space for endchar and a final NUL (which is really meaningless, but makes me feel better) */
	    }

	    len = 0;
	    for ( j=0; j<gb->bcnt; ++j ) {
		int si;
		if ( k!=0 || j!=0 ) {
		    memcpy(vals+len,gb->bits[j].data,gb->bits[j].dlen);
		    len += gb->bits[j].dlen;
		}
		si = -1;
		if ( k==1 && gb->sc->ttf_glyph!=0x7fff )
		    si = gi->psubrs[ gb->sc->ttf_glyph ].idx;
		else if ( gi->psubrs[ gb->bits[j].psub_index ].idx==-1 ) {
		    memcpy(vals+len,gi->psubrs[ gb->bits[j].psub_index ].data,
			    gi->psubrs[ gb->bits[j].psub_index ].len);
		    len += gi->psubrs[ gb->bits[j].psub_index ].len;
		} else
		    si = gi->psubrs[ gb->bits[j].psub_index ].idx;
		if ( si!=-1 ) {
		    /* space for the number (subroutine index) */
		    if ( si<=107 )
			vals[len++] = si+139;
		    else if ( si>0 && si<=1131 ) {
			si-=108;
			vals[len++] = (si>>8)+247;
			vals[len++] = si&0xff;
		    } else {
			vals[len++] = '\377';
			vals[len++] = (si>>24)&0xff;
			vals[len++] = (si>>16)&0xff;
			vals[len++] = (si>>8)&0xff;
			vals[len++] = si&0xff;
		    }
		    /* space for the subroutine operator */
		    vals[len++] = 10;
		}
		if ( k==1 && gb->sc->ttf_glyph!=0x7fff )
	    break;
	    }
	    if ( k==0 ) {
		vals[len++] = 11;	/* return */
		vals[len] = '\0';
	    } else if ( gb->wasseac ) {
		/* Don't want an endchar */
		vals[len] = '\0';
	    } else {
		vals[len++] = 14;	/* endchar */
		vals[len] = '\0';
	    }
	}
    }
}

struct pschars *SplineFont2ChrsSubrs(SplineFont *sf, int iscjk,
	struct pschars *subrs,int flags, enum fontformat format, int layer) {
    struct pschars *chrs = calloc(1,sizeof(struct pschars));
    int i, cnt, instance_count;
    int fixed;
    int notdef_pos;
    MMSet *mm = sf->mm;
    int round = (flags&ps_flag_round)? true : false;
    GlyphInfo gi;
    SplineChar dummynotdef, *sc;

    if ( (format==ff_mma || format==ff_mmb) && mm!=NULL ) {
	instance_count = mm->instance_count;
	sf = mm->instances[0];
	fixed = 0;
	for ( i=0; i<instance_count; ++i ) {
	    MarkTranslationRefs(mm->instances[i],layer);
	    fixed = SFOneWidth(mm->instances[i]);
	    if ( fixed==-1 )
	break;
	}
    } else {
	MarkTranslationRefs(sf,layer);
	fixed = SFOneWidth(sf);
	instance_count = 1;
    }

    notdef_pos = SFFindNotdef(sf,fixed);
    cnt = 0;
    for ( i=0; i<sf->glyphcnt; ++i )
#if HANYANG
	if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->compositionunit )
	    /* Don't count it */;
	else
#endif
	if ( SCWorthOutputting(sf->glyphs[i]) &&
		( i==notdef_pos || strcmp(sf->glyphs[i]->name,".notdef")!=0))
	    ++cnt;
/* only honor the width on .notdef in non-fixed pitch fonts (or ones where there is an actual outline in notdef) */
    if ( notdef_pos==-1 )
	++cnt;		/* one notdef entry */

    memset(&gi,0,sizeof(gi));
    memset(&gi.hashed,-1,sizeof(gi.hashed));
    gi.instance_count = 1;
    gi.sf = sf;
    gi.layer = layer;
    gi.glyphcnt = cnt;
    gi.gb = calloc(cnt,sizeof(struct glyphbits));
    gi.pmax = 3*cnt;
    gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs));
    gi.instance_count = instance_count;

    if ( notdef_pos==-1 ) {
	memset(&dummynotdef,0,sizeof(dummynotdef));
	dummynotdef.name = ".notdef";
	dummynotdef.parent = sf;
	dummynotdef.layer_cnt = sf->layer_cnt;
	dummynotdef.layers = calloc(sf->layer_cnt,sizeof(Layer));
	dummynotdef.width = SFOneWidth(sf);
	if ( dummynotdef.width==-1 )
	    dummynotdef.width = (sf->ascent+sf->descent)/2;
	gi.gb[0].sc = &dummynotdef;
    } else
	gi.gb[0].sc = sf->glyphs[notdef_pos];
    cnt = 1;
    for ( i=0 ; i<sf->glyphcnt; ++i ) {
#if HANYANG
	if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->compositionunit )
	    /* don't output it, should be in a subroutine */;
	else
#endif
	if ( SCWorthOutputting(sf->glyphs[i]) &&
		strcmp(sf->glyphs[i]->name,".notdef")!=0)	/* We've already added .notdef */
	    gi.gb[cnt++].sc = sf->glyphs[i];
    }

    SplineFont2FullSubrs1(flags,&gi);

    for ( i=0; i<cnt; ++i ) {
	if ( (sc = gi.gb[i].sc)==NULL )
    continue;
	gi.active = &gi.gb[i];
	SplineChar2PS(sc,NULL, round,iscjk,subrs,flags,format,&gi);
	if ( !ff_progress_next()) {
	    PSCharsFree(chrs);
	    GIFree(&gi,&dummynotdef);
return( NULL );
	}
    }

    SetupType1Subrs(subrs,&gi);

    chrs->cnt = cnt;
    chrs->keys = malloc(cnt*sizeof(char *));
    chrs->lens = malloc(cnt*sizeof(int));
    chrs->values = malloc(cnt*sizeof(unsigned char *));

    SetupType1Chrs(chrs,subrs,&gi,false);

    GIFree(&gi,&dummynotdef);

    chrs->next = cnt;
    if ( chrs->next>chrs->cnt )
	IError("Character estimate failed, about to die..." );
return( chrs );
}

struct pschars *CID2ChrsSubrs(SplineFont *cidmaster,struct cidbytes *cidbytes,int flags,int layer) {
    struct pschars *chrs = calloc(1,sizeof(struct pschars));
    int i, cnt, cid;
    SplineFont *sf = NULL;
    struct fddata *fd;
    int round = (flags&ps_flag_round)? true : false;
    /* I don't support mm cid files. I don't think adobe does either */
    GlyphInfo gi;
    int notdef_subfont;
    SplineChar dummynotdef, *sc;

    cnt = 0; notdef_subfont = -1;
    for ( i=0; i<cidmaster->subfontcnt; ++i ) {
	if ( cnt<cidmaster->subfonts[i]->glyphcnt )
	    cnt = cidmaster->subfonts[i]->glyphcnt;
	if ( cidmaster->subfonts[i]->glyphcnt>0 &&
		SCWorthOutputting(cidmaster->subfonts[i]->glyphs[0]) )
	    notdef_subfont = i;
    }
    cidbytes->cidcnt = cnt;

    if ( notdef_subfont==-1 ) {
	memset(&dummynotdef,0,sizeof(dummynotdef));
	dummynotdef.name = ".notdef";
	dummynotdef.parent = cidmaster->subfonts[0];
	dummynotdef.layer_cnt = layer+1;
	dummynotdef.layers = calloc(layer+1,sizeof(Layer));
	dummynotdef.width = SFOneWidth(dummynotdef.parent);
	if ( dummynotdef.width==-1 )
	    dummynotdef.width = (dummynotdef.parent->ascent+dummynotdef.parent->descent);
    }

    memset(&gi,0,sizeof(gi));
    gi.instance_count = 1;
    gi.glyphcnt = cnt;
    gi.gb = malloc(cnt*sizeof(struct glyphbits));
    gi.pmax = 3*cnt;
    gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs));
    gi.layer = layer;

    chrs->cnt = cnt;
    chrs->lens = calloc(cnt,sizeof(int));
    chrs->values = calloc(cnt,sizeof(unsigned char *));
    cidbytes->fdind = malloc(cnt*sizeof(unsigned char *));
    memset(cidbytes->fdind,-1,cnt*sizeof(unsigned char *));

    /* In a type1 CID-keyed font we must handle subroutines subfont by subfont*/
    /*  as there are no global subrs */
    for ( i=0; i<cidmaster->subfontcnt; ++i ) {
	gi.sf = sf = cidmaster->subfonts[i];
	MarkTranslationRefs(sf,layer);
	fd = &cidbytes->fds[i];
	memset(&gi.hashed,-1,sizeof(gi.hashed));
	gi.instance_count = 1;
	gi.glyphcnt = sf->glyphcnt;
	memset(gi.gb,0,sf->glyphcnt*sizeof(struct glyphbits));
	for ( cid=0; cid<cnt && cid<sf->glyphcnt; ++cid ) {
	    if ( cid==0 && notdef_subfont==-1 && i==cidmaster->subfontcnt-1 )
		gi.gb[0].sc = &dummynotdef;
	    else if ( SCWorthOutputting(sf->glyphs[cid]) &&
		    ((i==notdef_subfont && cid==0 ) ||
		     strcmp(sf->glyphs[cid]->name,".notdef")!=0))	/* We've already added .notdef */
		gi.gb[cid].sc = sf->glyphs[cid];
	    if ( gi.gb[cid].sc!=NULL )
		cidbytes->fdind[cid] = i;
	}
	SplineFont2FullSubrs1(flags,&gi);

	for ( cid=0; cid<cnt && cid<sf->glyphcnt; ++cid ) {
	    if ( (sc = gi.gb[cid].sc)==NULL )
	continue;
	    gi.active = &gi.gb[cid];
	    SplineChar2PS(sc,NULL, round,fd->iscjk|0x100,fd->subrs,
		    flags,ff_cid,&gi);
	    if ( !ff_progress_next()) {
		PSCharsFree(chrs);
		GIFree(&gi,&dummynotdef);
return( NULL );
	    }	
	}

	SetupType1Subrs(fd->subrs,&gi);
	SetupType1Chrs(chrs,fd->subrs,&gi,true);
	GIContentsFree(&gi,&dummynotdef);
    }
    GIFree(&gi,&dummynotdef);
    chrs->next = cnt;
return( chrs );
}

/* ************************************************************************** */
/* ********************** Type2 PostScript CharStrings ********************** */
/* ************************************************************************** */

static real myround2(real pos, int round) {
    if ( round )
return( rint(pos));

return( rint(65536*pos)/65536 );
}

static void AddNumber2(GrowBuf *gb, real pos, int round) {
    int val, factor;
    unsigned char *str;

    if ( gb->pt+5>=gb->end )
	GrowBuffer(gb);

    pos = rint(65536*pos)/65536;
    if ( round )
	pos = rint(pos);

    str = gb->pt;
    if ( pos>32767.99 || pos<-32768 ) {
	/* same logic for big ints and reals */
	if ( pos>0x3fffffff || pos<-0x40000000 ) {
	    LogError( _("Number out of range: %g in type2 output (must be [-65536,65535])"),
		    pos );
	    if ( pos>0 ) pos = 0x3fffffff; else pos = -0x40000000;
	}
	for ( factor=2; factor<32768; factor<<=2 )
	    if ( pos/factor<32767.99 && pos/factor>-32768 )
	break;
	AddNumber2(gb,pos/factor,false);
	AddNumber2(gb,factor,false);
	if ( gb->pt+2>=gb->end )
	    GrowBuffer(gb);
	*(gb->pt++) = 0x0c;		/* Multiply operator */
	*(gb->pt++) = 0x18;
    } else if ( pos!=floor(pos )) {
	val = pos*65536;
	*str++ = '\377';
	*str++ = (val>>24)&0xff;
	*str++ = (val>>16)&0xff;
	*str++ = (val>>8)&0xff;
	*str++ = val&0xff;
    } else {
	val = rint(pos);
	if ( pos>=-107 && pos<=107 )
	    *str++ = val+139;
	else if ( pos>=108 && pos<=1131 ) {
	    val -= 108;
	    *str++ = (val>>8)+247;
	    *str++ = val&0xff;
	} else if ( pos>=-1131 && pos<=-108 ) {
	    val = -val;
	    val -= 108;
	    *str++ = (val>>8)+251;
	    *str++ = val&0xff;
	} else {
	    *str++ = 28;
	    *str++ = (val>>8)&0xff;
	    *str++ = val&0xff;
	}
    }
    gb->pt = str;
}

static void AddMask2(GrowBuf *gb,uint8_t mask[12],int cnt, int oper) {
    int i;

    if ( gb->pt+1+((cnt+7)>>3)>=gb->end )
	GrowBuffer(gb);
    *gb->pt++ = oper;					/* hintmask,cntrmask */
    for ( i=0; i< ((cnt+7)>>3); ++i )
	*gb->pt++ = mask[i];
}

static void CounterHints2(GrowBuf *gb, SplineChar *sc, int hcnt) {
    int i;

    for ( i=0; i<sc->countermask_cnt; ++i )
	AddMask2(gb,sc->countermasks[i],hcnt,20);	/* cntrmask */
}

static int HintSetup2(GrowBuf *gb,struct hintdb *hdb, SplinePoint *to, int break_subr ) {

    /* We might get a point with a hintmask in a glyph with no conflicts */
    /* (ie. the initial point when we return to it at the end of the splineset*/
    /* in that case hdb->cnt will be 0 and we should ignore it */
    /* components in subroutines depend on not having any hintmasks */
    if ( to->hintmask==NULL || hdb->cnt==0 || hdb->noconflicts || hdb->skiphm )
return( false );

    if ( memcmp(hdb->mask,*to->hintmask,(hdb->cnt+7)/8)==0 )
return( false );

    if ( break_subr )
	BreakSubroutine(gb,hdb);

    AddMask2(gb,*to->hintmask,hdb->cnt,19);		/* hintmask */
    memcpy(hdb->mask,*to->hintmask,sizeof(HintMask));
    hdb->donefirsthm = true;
    if ( break_subr )
	StartNextSubroutine(gb,hdb);
return( true );
}

static void moveto2(GrowBuf *gb,struct hintdb *hdb,SplinePoint *to, int round) {
    BasePoint temp, *tom;

    if ( gb->pt+18 >= gb->end )
	GrowBuffer(gb);

    BreakSubroutine(gb,hdb);
    HintSetup2(gb,hdb,to,false);
    tom = &to->me;
    if ( round ) {
	temp.x = rint(tom->x);
	temp.y = rint(tom->y);
	tom = &temp;
    }
    if ( hdb->current.x==tom->x ) {
	AddNumber2(gb,tom->y-hdb->current.y,round);
	*(gb->pt)++ = 4;		/* v move to */
    } else if ( hdb->current.y==tom->y ) {
	AddNumber2(gb,tom->x-hdb->current.x,round);
	*(gb->pt)++ = 22;		/* h move to */
    } else {
	AddNumber2(gb,tom->x-hdb->current.x,round);
	AddNumber2(gb,tom->y-hdb->current.y,round);
	*(gb->pt)++ = 21;		/* r move to */
    }
    hdb->current.x = rint(32768*tom->x)/32768;
    hdb->current.y = rint(32768*tom->y)/32768;
    StartNextSubroutine(gb,hdb);
}

static Spline *lineto2(GrowBuf *gb,struct hintdb *hdb,Spline *spline, Spline *done, int round) {
    int cnt, hv, hvcnt;
    Spline *test, *lastgood, *lasthvgood;
    BasePoint temp1, temp2, *tom, *fromm;
    int donehm;

    lastgood = NULL;
    for ( test=spline, cnt=0; test->knownlinear && cnt<15; ) {
	++cnt;
	lastgood = test;
	test = test->to->next;
	/* it will be smaller to use a closepath operator so ignore the */
	/*  ultimate spline */
	if ( test==done || test==NULL || test->to->next==done )
    break;
    }

    HintSetup2(gb,hdb,spline->to,true);

    hv = -1; hvcnt=1; lasthvgood = NULL;
    if ( spline->from->me.x==spline->to->me.x )
	hv = 1;		/* Vertical */
    else if ( spline->from->me.y==spline->to->me.y )
	hv = 0;		/* Horizontal */
    donehm = true;
    if ( hv!=-1 ) {
	lasthvgood = spline; hvcnt = 1;
	if ( cnt!=1 ) {
	    for ( test=spline->to->next; test!=NULL ; test = test->to->next ) {
		fromm = &test->from->me;
		if ( round ) {
		    temp2.x = rint(fromm->x);
		    temp2.y = rint(fromm->y);
		    fromm = &temp2;
		}
		tom = &test->to->me;
		if ( round ) {
		    temp1.x = rint(tom->x);
		    temp1.y = rint(tom->y);
		    tom = &temp1;
		}
		if ( hv==1 && tom->y==fromm->y )
		    hv = 0;
		else if ( hv==0 && tom->x==fromm->x )
		    hv = 1;
		else
	    break;
		lasthvgood = test;
		++hvcnt;
		if ( test==lastgood )
	    break;
	    }
	}
	donehm = true;
	if ( hvcnt==cnt || hvcnt>=2 ) {
	    /* It's more efficient to do some h/v linetos */
	    for ( test=spline; ; test = test->to->next ) {
		if ( !donehm && test->to->hintmask!=NULL )
	    break;
		donehm = false;
		fromm = &test->from->me;
		if ( round ) {
		    temp2.x = rint(fromm->x);
		    temp2.y = rint(fromm->y);
		    fromm = &temp2;
		}
		tom = &test->to->me;
		if ( round ) {
		    temp1.x = rint(tom->x);
		    temp1.y = rint(tom->y);
		    tom = &temp1;
		}
		if ( fromm->x==tom->x )
		    AddNumber2(gb,tom->y-fromm->y,round);
		else
		    AddNumber2(gb,tom->x-fromm->x,round);
		hdb->current.x = rint(32768*tom->x)/32768;
		hdb->current.y = rint(32768*tom->y)/32768;
		if ( test==lasthvgood ) {
		    test = test->to->next;
	    break;
		}
	    }
	    if ( gb->pt+1 >= gb->end )
		GrowBuffer(gb);
	    *(gb->pt)++ = spline->from->me.x==spline->to->me.x? 7 : 6;
return( test );
	}
    }

    for ( test=spline; test!=NULL; test = test->to->next ) {
	if ( !donehm && test->to->hintmask!=NULL )
    break;
	donehm = false;
	fromm = &test->from->me;
	if ( round ) {
	    temp2.x = rint(fromm->x);
	    temp2.y = rint(fromm->y);
	    fromm = &temp2;
	}
	tom = &test->to->me;
	if ( round ) {
	    temp1.x = rint(tom->x);
	    temp1.y = rint(tom->y);
	    tom = &temp1;
	}
	AddNumber2(gb,tom->x-fromm->x,round);
	AddNumber2(gb,tom->y-fromm->y,round);
	hdb->current.x = rint(32768*tom->x)/32768;
	hdb->current.y = rint(32768*tom->y)/32768;
	if ( test==lastgood ) {
	    test = test->to->next;
    break;
	}
    }
    if ( gb->pt+1 >= gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 5;		/* r line to */
return( test );
}

static Spline *curveto2(GrowBuf *gb,struct hintdb *hdb,Spline *spline, Spline *done, int round) {
    int cnt=0, hv;
    Spline *first;
    DBasePoint start;
    int donehm;

    HintSetup2(gb,hdb,spline->to,true);

    hv = -1;
    if ( hdb->current.x==myround2(spline->from->nextcp.x,round) &&
	    myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) )
	hv = 1;
    else if ( hdb->current.y==myround2(spline->from->nextcp.y,round) &&
	    myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) )
	hv = 0;
    donehm = true;
    if ( hv!=-1 ) {
	first = spline; start = hdb->current;
	while (
		(hv==1 && hdb->current.x==myround2(spline->from->nextcp.x,round) &&
			myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) ) ||
		(hv==0 && hdb->current.y==myround2(spline->from->nextcp.y,round) &&
			myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) ) ) {
	    if ( !donehm && spline->to->hintmask!=NULL )
	break;
	    donehm = false;
	    if ( hv==1 ) {
		AddNumber2(gb,myround2(spline->from->nextcp.y,round)-hdb->current.y,round);
		AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round);
		AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round);
		AddNumber2(gb,myround2(spline->to->me.x,round)-myround2(spline->to->prevcp.x,round),round);
		hv = 0;
	    } else {
		AddNumber2(gb,myround2(spline->from->nextcp.x,round)-hdb->current.x,round);
		AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round);
		AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round);
		AddNumber2(gb,myround2(spline->to->me.y,round)-myround2(spline->to->prevcp.y,round),round);
		hv = 1;
	    }
	    hdb->current.x = myround2(spline->to->me.x,round);
	    hdb->current.y = myround2(spline->to->me.y,round);
	    ++cnt;
	    spline = spline->to->next;
	    if ( spline==done || spline==NULL || cnt>9 || spline->knownlinear )
	break;
	}
	if ( gb->pt+1 >= gb->end )
	    GrowBuffer(gb);
	*(gb->pt)++ = ( start.x==myround2(first->from->nextcp.x,round) && myround2(first->to->prevcp.y,round)==myround2(first->to->me.y,round) )?
		30:31;		/* vhcurveto:hvcurveto */
return( spline );
    }
    while ( cnt<6 ) {
	if ( !donehm && spline->to->hintmask!=NULL )
    break;
	donehm = false;
	hv = -1;
	if ( hdb->current.x==myround2(spline->from->nextcp.x,round) &&
		myround2(spline->to->prevcp.y,round)==myround2(spline->to->me.y,round) &&
		spline->to->next!=NULL &&
		myround2(spline->to->me.y,round)==myround2(spline->to->nextcp.y,round) &&
		myround2(spline->to->next->to->prevcp.x,round)==myround2(spline->to->next->to->me.x,round) )
    break;
	else if ( hdb->current.y==myround2(spline->from->nextcp.y,round) &&
		myround2(spline->to->prevcp.x,round)==myround2(spline->to->me.x,round) &&
		spline->to->next!=NULL &&
		myround2(spline->to->me.x,round)==myround2(spline->to->nextcp.x,round) &&
		myround2(spline->to->next->to->prevcp.y,round)==myround2(spline->to->next->to->me.y,round) )
    break;
	AddNumber2(gb,myround2(spline->from->nextcp.x,round)-hdb->current.x,round);
	AddNumber2(gb,myround2(spline->from->nextcp.y,round)-hdb->current.y,round);
	AddNumber2(gb,myround2(spline->to->prevcp.x,round)-myround2(spline->from->nextcp.x,round),round);
	AddNumber2(gb,myround2(spline->to->prevcp.y,round)-myround2(spline->from->nextcp.y,round),round);
	AddNumber2(gb,myround2(spline->to->me.x,round)-myround2(spline->to->prevcp.x,round),round);
	AddNumber2(gb,myround2(spline->to->me.y,round)-myround2(spline->to->prevcp.y,round),round);
	hdb->current.x = myround2(spline->to->me.x,round);
	hdb->current.y = myround2(spline->to->me.y,round);
	++cnt;
	spline = spline->to->next;
	if ( spline==done || spline==NULL || spline->knownlinear )
    break;
    }
    if ( gb->pt+1 >= gb->end )
	GrowBuffer(gb);
    *(gb->pt)++ = 8;		/* rrcurveto */
return( spline );
}

static void flexto2(GrowBuf *gb,struct hintdb *hdb,Spline *pspline,int round) {
    BasePoint *c0, *c1, *mid, *end, *nc0, *nc1;
    Spline *nspline;

    c0 = &pspline->from->nextcp;
    c1 = &pspline->to->prevcp;
    mid = &pspline->to->me;
    nspline = pspline->to->next;
    nc0 = &nspline->from->nextcp;
    nc1 = &nspline->to->prevcp;
    end = &nspline->to->me;

    HintSetup2(gb,hdb,nspline->to,true);

    if ( myround2(c0->y,round)==hdb->current.y && myround2(nc1->y,round)==hdb->current.y &&
	    myround2(end->y,round)==hdb->current.y &&
	    myround2(c1->y,round)==myround2(mid->y,round) && myround2(nc0->y,round)==myround2(mid->y,round) ) {
	if ( gb->pt+7*6+2 >= gb->end )
	    GrowBuffer(gb);
	AddNumber2(gb,myround2(c0->x,round)-hdb->current.x,round);
	AddNumber2(gb,myround2(c1->x,round)-myround2(c0->x,round),round);
	AddNumber2(gb,myround2(c1->y,round)-myround2(c0->y,round),round);
	AddNumber2(gb,myround2(mid->x,round)-myround2(c1->x,round),round);
	AddNumber2(gb,myround2(nc0->x,round)-myround2(mid->x,round),round);
	AddNumber2(gb,myround2(nc1->x,round)-myround2(nc0->x,round),round);
	AddNumber2(gb,myround2(end->x,round)-myround2(nc1->x,round),round);
	*gb->pt++ = 12; *gb->pt++ = 34;		/* hflex */
    } else {
	if ( gb->pt+11*6+2 >= gb->end )
	    GrowBuffer(gb);
	AddNumber2(gb,myround2(c0->x,round)-hdb->current.x,round);
	AddNumber2(gb,myround2(c0->y,round)-hdb->current.y,round);
	AddNumber2(gb,myround2(c1->x,round)-myround2(c0->x,round),round);
	AddNumber2(gb,myround2(c1->y,round)-myround2(c0->y,round),round);
	AddNumber2(gb,myround2(mid->x,round)-myround2(c1->x,round),round);
	AddNumber2(gb,myround2(mid->y,round)-myround2(c1->y,round),round);
	AddNumber2(gb,myround2(nc0->x,round)-myround2(mid->x,round),round);
	AddNumber2(gb,myround2(nc0->y,round)-myround2(mid->y,round),round);
	AddNumber2(gb,myround2(nc1->x,round)-myround2(nc0->x,round),round);
	AddNumber2(gb,myround2(nc1->y,round)-myround2(nc0->y,round),round);
	if ( hdb->current.y==myround2(end->y,round) )
	    AddNumber2(gb,myround2(end->x,round)-myround2(nc1->x,round),round);
	else
	    AddNumber2(gb,myround2(end->y,round)-myround2(nc1->y,round),round);
	*gb->pt++ = 12; *gb->pt++ = 37;		/* flex1 */
    }

    hdb->current.x = rint(32768*end->x)/32768;
    hdb->current.y = rint(32768*end->y)/32768;
}

static void CvtPsSplineSet2(GrowBuf *gb, SplinePointList *spl,
	struct hintdb *hdb, int is_order2,int round ) {
    Spline *spline, *first;
    SplinePointList temp, *freeme = NULL;
    int unhinted = true;

    if ( is_order2 )
	freeme = spl = SplineSetsPSApprox(spl);

    for ( ; spl!=NULL; spl = spl->next ) {
	first = NULL;
	SplineSetReverse(spl);
	/* PostScript and TrueType store their splines in in reverse */
	/*  orientations. Annoying. Oh well. I shall adopt TrueType and */
	/*  If I reverse the PS splinesets after reading them in, and then */
	/*  again when saving them out, all should be well */
	if ( spl->first->flexy || spl->first->flexx ) {
	    /* can't handle a flex (mid) point as the first point. rotate the */
	    /* list by one, this is possible because only closed paths have */
	    /* points marked as flex, and because we can't have two flex mid- */
	    /* points in a row */
	    if ( spl->first->hintmask==NULL || spl->first->next->to->hintmask!=NULL ) {
		/* But we can't rotate it if we expect it to provide us with */
		/*  a hintmask.                 			     */
		temp = *spl;
		temp.first = temp.last = spl->first->next->to;
		spl = &temp;
	    }
	    if ( spl->first->flexy || spl->first->flexx ) {
		/* If we couldn't rotate, or if we rotated to something that */
		/*  also is flexible, then just turn off flex. That's safe   */
		spl->first->flexx = spl->first->flexy = false;
	    }
	}
	if ( unhinted && hdb->cnt>0 && spl->first->hintmask!=NULL ) {
	    hdb->mask[0] = ~(*spl->first->hintmask)[0];	/* Make it different */
	    unhinted = false;
	}
	moveto2(gb,hdb,spl->first,round);
	for ( spline = spl->first->next; spline!=NULL && spline!=first; ) {
	    if ( first==NULL ) first = spline;
	    else if ( first->from==spline->to )
		hdb->skiphm = true;
	    if ( spline->to->flexx || spline->to->flexy ) {
		flexto2(gb,hdb,spline,round);	/* does two adjacent splines */
		spline = spline->to->next->to->next;
	    } else if ( spline->knownlinear && spline->to == spl->first )
		/* In Type2 we don't even need a closepath to finish this off */
		/*  (which is good, because there isn't a close path) */
	break;
	    else if ( spline->knownlinear )
		spline = lineto2(gb,hdb,spline,first,round);
	    else
		spline = curveto2(gb,hdb,spline,first,round);
	}
	hdb->skiphm = false;
	/* No closepath oper in type2 fonts, it's implied */
	SplineSetReverse(spl);
	/* Of course, I have to Reverse again to get back to my convention after*/
	/*  saving */
    }
    SplinePointListsFree(freeme);
}

void debug_printHintInstance( HintInstance* hi, int hin, char* msg )
{
    printf("___ hint instance %d %s\n", hin, msg );
    if( !hi )
	return;
    
    printf("hi.begin      %f\n", hi->begin );
    printf("hi.end        %f\n", hi->end );
    printf("hi.closed     %d\n", hi->closed );
    printf("hi.cnum       %d\n", hi->counternumber );
    printf("hi.next       %p\n", hi->next );
    if( hi->next )
	debug_printHintInstance( hi->next, hin+1, msg );
}


void debug_printHint( StemInfo *h, char* msg )
{
    printf("==============================\n");
    printf("debug_printHint(%p)... %s\n", h, msg );
    if( h )
    {
	printf("start         %f\n", h->start );
	printf("width         %f\n", h->width );
	printf("hinttype      %d\n", h->hinttype );
	printf("ghost         %d\n", h->ghost );
	printf("haspointleft  %d\n", h->haspointleft );
	printf("haspointright %d\n", h->haspointright );
	printf("hasconflicts  %d\n", h->hasconflicts );
	printf("used          %d\n", h->used );
	printf("tobeused      %d\n", h->tobeused );
	printf("active        %d\n", h->active );
	printf("enddone       %d\n", h->enddone );
	printf("startdone     %d\n", h->startdone );
	printf("reordered     %d\n", h->reordered );
	printf("pendingpt     %d\n", h->pendingpt );
	printf("linearedges   %d\n", h->linearedges );
	printf("hintnumber    %d\n", h->hintnumber );
	if( h->where )
	    debug_printHintInstance( h->where, 1, "" );
    }
    printf("==============================\n");    
}

bool equalWithTolerence( real a, real b, real tolerence )
{
//    printf("equalWithTolerence(1) a:%f b:%f tol:%f\n", a, b, tolerence );
//    printf("equalWithTolerence(2) a:%lf b:%lf tol:%lf\n", a, b, tolerence );
    
    if( tolerence == 0.0 )
	return a == b;

    return(    (b - tolerence < a)
	    && (b + tolerence > a ));
}

static void DumpHints(GrowBuf *gb,StemInfo *h,int oper,int midoper,int round) {
    real last = 0, cur;
    int cnt;

    if ( h==NULL )
return;
    cnt = 0;
    while ( h && h->hintnumber!=-1 ) {
	/* Type2 hints do not support negative widths except in the case of */
	/*  ghost (now called edge) hints */
	if ( cnt>24-2 ) {	/* stack max = 48 numbers, => 24 hints, leave a bit of slop for the width */
	    if ( gb->pt+1>=gb->end )
		GrowBuffer(gb);
	    *gb->pt++ = midoper;
	    cnt = 0;
	}
	cur = myround2(h->start,round) + myround2(h->width,round);
	if ( h->width<0 ) {
	    AddNumber2(gb,cur-last,round);
	    AddNumber2(gb,-myround2(h->width,round),round);
	    cur -= myround2(h->width,round);
	} else if ( h->ghost ) {
	    if ( equalWithTolerence( h->width, 20, GenerateHintWidthEqualityTolerance )) {
		AddNumber2(gb,myround2(h->start,round)-last+20,round);
		AddNumber2(gb,-20,round);
		cur = myround2(h->start,round);
	    } else {
		AddNumber2(gb,myround2(h->start+21,round)-last,round);
		AddNumber2(gb,-21,round);
		cur = myround2(h->start+21,round)-21;
	    }
	} else {
	    AddNumber2(gb,myround2(h->start,round)-last,round);
	    AddNumber2(gb,myround2(h->width,round),round);
	}
	last = cur;
	h = h->next;
	++cnt;
    }
    if ( oper!=-1 ) {
	if ( gb->pt+1>=gb->end )
	    GrowBuffer(gb);
	*gb->pt++ = oper;
    }
}

static void DumpRefsHints(GrowBuf *gb, struct hintdb *hdb,RefChar *cur,StemInfo *h,StemInfo *v,
	BasePoint *trans, int round,int layer) {
    uint8_t masks[12];
    int cnt, sets=0;
    StemInfo *rs;

    /* trans has already been rounded (whole char is translated by an integral amount) */

    /* If we have a subroutine containing conflicts, then its hints will match*/
    /*  ours exactly, and we can use its hintmasks directly */
    if (( cur->sc->hconflicts || cur->sc->vconflicts ) &&
	    cur->sc->layers[layer].splines!=NULL &&
	    cur->sc->layers[layer].splines->first->hintmask!=NULL ) {
	AddMask2(gb,*cur->sc->layers[layer].splines->first->hintmask,hdb->cnt,19);		/* hintmask */
	hdb->donefirsthm = true;
	memcpy(hdb->mask,*cur->sc->layers[layer].splines->first->hintmask,sizeof(HintMask));
return;
    }

    if ( h==NULL && v==NULL )
	IError("hintmask invoked when there are no hints");
    memset(masks,'\0',sizeof(masks));
    cnt = 0;
    while ( h!=NULL && h->hintnumber>=0 ) {
	/* Horizontal stems are defined by vertical bounds */
	real pos = (round ? rint(h->start) : h->start) - trans->y;
	for ( rs = cur->sc->hstem; rs!=NULL; rs=rs->next ) {
	    real rpos = round ? rint(rs->start) : rs->start;
	    if ( rpos==pos && (round ? (rint(rs->width)==rint(h->width)) : (rs->width==h->width)) ) {
		masks[h->hintnumber>>3] |= 0x80>>(h->hintnumber&7);
		++sets;
	break;
	    } else if ( rpos>pos )
	break;
	}
	h = h->next; ++cnt;
    }
    while ( v!=NULL && v->hintnumber>=0 ) {
	real pos = (round ? rint(v->start) : v->start) - trans->x;
	for ( rs = cur->sc->vstem; rs!=NULL; rs=rs->next ) {
	    real rpos = round ? rint(rs->start) : rs->start;
	    if ( rpos==pos && (round ? (rint(rs->width)==rint(v->width)) : (rs->width==v->width)) ) {
		masks[v->hintnumber>>3] |= 0x80>>(v->hintnumber&7);
		++sets;
	break;
	    } else if ( rpos>pos )
	break;
	}
	v = v->next; ++cnt;
    }
    BreakSubroutine(gb,hdb);
    hdb->donefirsthm = true;
    /* if ( sets!=0 ) */	/* First ref will need a hintmask even if it has no hints (if there are conflicts) */
	AddMask2(gb,masks,cnt,19);		/* hintmask */
}

static void DummyHintmask(GrowBuf *gb,struct hintdb *hdb) {
    HintMask hm;

    memset(hm,0,sizeof(hm));
    if ( hdb->cnt!=0 ) {
	BreakSubroutine(gb,hdb);
	hdb->donefirsthm = true;
	AddMask2(gb,hm,hdb->cnt,19);		/* hintmask */
    }
}

static void SetTransformedHintMask(GrowBuf *gb,struct hintdb *hdb,
	SplineChar *sc, RefChar *ref, BasePoint *trans, int round) {
    HintMask hm;

    if ( HintMaskFromTransformedRef(ref,trans,sc,&hm)!=NULL ) {
	BreakSubroutine(gb,hdb);
	hdb->donefirsthm = true;
	AddMask2(gb,hm,hdb->cnt,19);		/* hintmask */
    } else if ( !hdb->donefirsthm )
	DummyHintmask(gb,hdb);
}

static void ExpandRef2(GrowBuf *gb, SplineChar *sc, struct hintdb *hdb,
	RefChar *r, BasePoint *trans,
	struct pschars *subrs, int round,int layer) {
    BasePoint *bpt;
    BasePoint temp, rtrans;
    GlyphInfo *gi;
    /* The only refs I deal with here have no hint conflicts within them */
    
    rtrans.x = r->transform[4]+trans->x;
    rtrans.y = r->transform[5]+trans->y;
    if ( round ) {
	rtrans.x = rint(rtrans.x);
	rtrans.y = rint(rtrans.y);
    }

    BreakSubroutine(gb,hdb);
    if ( hdb->cnt>0 && !hdb->noconflicts )
	DumpRefsHints(gb,hdb,r,sc->hstem,sc->vstem,&rtrans,round,layer);

    /* Translate from end of last character to where this one should */
    /*  start (we must have one moveto operator to start off, none */
    /*  in the subr) */
    bpt = hdb->gi->psubrs[r->sc->lsidebearing].startstop;
    temp.x = bpt[0].x+rtrans.x;
    temp.y = bpt[0].y+rtrans.y;
    if ( hdb->current.x!=temp.x )
	AddNumber2(gb,temp.x-hdb->current.x,round);
    if ( hdb->current.y!=temp.y || hdb->current.x==temp.x )
	AddNumber2(gb,temp.y-hdb->current.y,round);
    if ( gb->pt+1>=gb->end )
	GrowBuffer(gb);
    *gb->pt++ = hdb->current.x==temp.x?4:	/* vmoveto */
		hdb->current.y==temp.y?22:	/* hmoveto */
		21;				/* rmoveto */
    if ( r->sc->lsidebearing==0x7fff )
	IError("Attempt to reference an unreferenceable glyph %s", r->sc->name );

    gi = hdb->gi;
    StartNextSubroutine(gb,hdb);
    gi->bits[gi->bcnt].psub_index = r->sc->lsidebearing;
    ++gi->bcnt;
    gi->justbroken = true;
    hdb->current.x = bpt[1].x+rtrans.x;
    hdb->current.y = bpt[1].y+rtrans.y;
}

static void RSC2PS2(GrowBuf *gb, SplineChar *base,SplineChar *rsc,
	struct hintdb *hdb, BasePoint *trans, struct pschars *subrs,
	int flags, int layer ) {
    BasePoint subtrans;
    int stationary = trans->x==0 && trans->y==0;
    RefChar *r, *unsafe=NULL;
    int allwithouthints=true;
    int round = (flags&ps_flag_round)? true : false;
    StemInfo *oldh, *oldv;
    int hc, vc;
    SplineSet *freeme, *temp;
    int wasntconflicted = hdb->noconflicts;

    if ( flags&ps_flag_nohints ) {
	oldh = rsc->hstem; oldv = rsc->vstem;
	hc = rsc->hconflicts; vc = rsc->vconflicts;
	rsc->hstem = NULL; rsc->vstem = NULL;
	rsc->hconflicts = false; rsc->vconflicts = false;
    } else {
	for ( r=rsc->layers[layer].refs; r!=NULL; r=r->next ) {
	    /* Ensure hintmask on refs are set correctly */
	    if (SCNeedsSubsPts(r->sc, ff_otf, layer))
	        SCFigureHintMasks(r->sc, layer);

	    if ( !r->justtranslated )
	continue;
	    if ( r->sc->hconflicts || r->sc->vconflicts ) {
		unsafe = r;
	    } else if ( r->sc->hstem!=NULL || r->sc->vstem!=NULL )
		allwithouthints = false;
	}
	if ( !stationary )
	    allwithouthints = false;
	if ( allwithouthints && unsafe!=NULL && hdb->cnt!=NumberHints(&unsafe->sc,1)) 
	    allwithouthints = false;		/* There are other hints elsewhere in the base glyph */
    }

    if ( unsafe && allwithouthints ) {
	if ( unsafe->sc->lsidebearing!=0x7fff ) {
	    ExpandRef2(gb,base,hdb,unsafe,trans,subrs,round,layer);
	} else if ( unsafe->transform[4]==0 && unsafe->transform[5]==0 )
	    RSC2PS2(gb,base,unsafe->sc,hdb,trans,subrs,flags,layer);
	else
	    unsafe = NULL;
    } else
	unsafe = NULL;

    /* What is the hintmask state here? It should not matter */
    freeme = NULL; temp = rsc->layers[layer].splines;
    if ( base!=rsc )
	temp = freeme = SPLCopyTranslatedHintMasks(temp,base,rsc,trans);
    CvtPsSplineSet2(gb,temp,hdb,rsc->layers[layer].order2,round);
    SplinePointListsFree(freeme);

    for ( r = rsc->layers[layer].refs; r!=NULL; r = r->next ) if ( r!=unsafe ) {
	if ( !r->justtranslated ) {
	    if ( !r->sc->hconflicts && !r->sc->vconflicts && !hdb->noconflicts &&
		    r->transform[1]==0 && r->transform[2]==0 &&
		    r->transform[0]>0 && r->transform[3]>0 )
		SetTransformedHintMask(gb,hdb,base,r,trans,round);
	    if ( !hdb->donefirsthm )
		DummyHintmask(gb,hdb);
	    temp = SPLCopyTransformedHintMasks(r,base,trans,layer);
	    CvtPsSplineSet2(gb,temp,hdb,rsc->layers[layer].order2,round);
	    SplinePointListsFree(temp);
	} else if ( r->sc->lsidebearing!=0x7fff &&
		((flags&ps_flag_nohints) ||
		 (!r->sc->hconflicts && !r->sc->vconflicts)) ) {
	    ExpandRef2(gb,base,hdb,r,trans,subrs,round,layer);
	} else {
	    subtrans.x = trans->x + r->transform[4];
	    subtrans.y = trans->y + r->transform[5];
	    if ( !hdb->noconflicts && !r->sc->hconflicts && !r->sc->vconflicts) {
		SetTransformedHintMask(gb,hdb,base,r,trans,round);
		hdb->noconflicts = true;
	    }
	    RSC2PS2(gb,base,r->sc,hdb,&subtrans,subrs,flags,layer);
	    hdb->noconflicts = wasntconflicted;
	}
    }

    if ( flags&ps_flag_nohints ) {
	rsc->hstem = oldh; rsc->vstem = oldv;
	rsc->hconflicts = hc; rsc->vconflicts = vc;
    }
}

static unsigned char *SplineChar2PS2(SplineChar *sc,int *len, int nomwid,
	int defwid, struct pschars *subrs, int flags,
	GlyphInfo *gi) {
    GrowBuf gb;
    unsigned char *ret;
    struct hintdb hdb;
    StemInfo *oldh, *oldv;
    int hc, vc;
    SplineChar *scs[MmMax];
    int round = (flags&ps_flag_round)? true : false;
    HintMask *hm = NULL;
    BasePoint trans;

    if ( autohint_before_generate && sc->changedsincelasthinted &&
	    !sc->manualhints && !(flags&ps_flag_nohints))
	SplineCharAutoHint(sc,gi->layer,NULL);
    if ( !(flags&ps_flag_nohints) && SCNeedsSubsPts(sc,ff_otf,gi->layer))
	SCFigureHintMasks(sc,gi->layer);

    if ( flags&ps_flag_nohints ) {
	oldh = sc->hstem; oldv = sc->vstem;
	hc = sc->hconflicts; vc = sc->vconflicts;
	sc->hstem = NULL; sc->vstem = NULL;
	sc->hconflicts = false; sc->vconflicts = false;
    } else if ( sc->layers[gi->layer].splines!=NULL && !sc->vconflicts &&
	    !sc->hconflicts ) {
	hm = sc->layers[gi->layer].splines->first->hintmask;
	sc->layers[gi->layer].splines->first->hintmask = NULL;
    }

    memset(&gb,'\0',sizeof(gb));

    GrowBuffer(&gb);

    /* store the width on the stack */
    if ( sc->width==defwid )
	/* Don't need to do anything for the width */;
    else
	AddNumber2(&gb,sc->width-nomwid,round);

    memset(&trans,'\0',sizeof(trans));
    memset(&hdb,'\0',sizeof(hdb));
    hdb.scs = scs;
    hdb.gi = gi;
    if ( gi!=NULL )
	gi->bcnt = -1;
    scs[0] = sc;
    hdb.noconflicts = !sc->hconflicts && !sc->vconflicts;
    hdb.cnt = NumberHints(hdb.scs,1);
    DumpHints(&gb,sc->hstem,sc->hconflicts || sc->vconflicts?18:1,
			    sc->hconflicts || sc->vconflicts?18:1,round);
    DumpHints(&gb,sc->vstem,sc->hconflicts || sc->vconflicts?-1:3,
			    sc->hconflicts || sc->vconflicts?23:3,round);
    CounterHints2(&gb, sc, hdb.cnt );
    RSC2PS2(&gb,sc,sc,&hdb,&trans,subrs,flags,gi->layer);

    if ( gi->bcnt==-1 ) {	/* If it's whitespace */
	gi->bcnt = 0;
	StartNextSubroutine(&gb,&hdb);
    }
    BreakSubroutine(&gb,&hdb);
    MoveSubrsToChar(gi);
    ret = NULL;

    free(gb.base);
    if ( flags&ps_flag_nohints ) {
	sc->hstem = oldh; sc->vstem = oldv;
	sc->hconflicts = hc; sc->vconflicts = vc;
    } else if ( hm!=NULL )
	sc->layers[gi->layer].splines->first->hintmask = hm;
return( ret );
}

static SplinePoint *LineTo(SplinePoint *last, int x, int y) {
    SplinePoint *sp = SplinePointCreate(x,y);
    SplineMake3(last,sp);
return( sp );
}

static void Type2NotDefSplines(SplineFont *sf,SplineChar *sc,int layer) {
    /* I'd always assumed that Type2 notdefs would look like type1 notdefs */
    /*  but they don't, they look like truetype notdefs. And Ralf Stubner */
    /*  points out that the spec says they should. So make a box here */
    int stem, ymax;
    SplineSet *inner, *ss;
    StemInfo *h, *hints;

    stem = (sf->ascent+sf->descent)/20;
    ymax = 2*sf->ascent/3;

    ss = chunkalloc(sizeof(SplineSet));
    ss->first = ss->last = SplinePointCreate(stem,0);
    ss->last = LineTo(ss->last,stem,ymax);
    ss->last = LineTo(ss->last,sc->width-stem,ymax);
    ss->last = LineTo(ss->last,sc->width-stem,0);
    SplineMake3(ss->last,ss->first);
    ss->last = ss->first;

    ss->next = inner = chunkalloc(sizeof(SplineSet));
    inner->first = inner->last = SplinePointCreate(2*stem,stem);
    inner->last = LineTo(inner->last,sc->width-2*stem,stem);
    inner->last = LineTo(inner->last,sc->width-2*stem,ymax-stem);
    inner->last = LineTo(inner->last,2*stem,ymax-stem);
    SplineMake3(inner->last,inner->first);
    inner->last = inner->first;

    sc->layers[layer].splines = ss;

    hints = chunkalloc(sizeof(StemInfo));
    hints->start = stem;
    hints->width = stem;
    hints->next = h = chunkalloc(sizeof(StemInfo));
    h->start = sc->width-2*stem;
    h->width = stem;
    sc->vstem = hints;

    hints = chunkalloc(sizeof(StemInfo));
    hints->start = 0;
    hints->width = stem;
    hints->next = h = chunkalloc(sizeof(StemInfo));
    h->start = ymax-stem;
    h->width = stem;
    sc->hstem = hints;
}

#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
/* This char has hint conflicts. Check to see if we can put it into a subr */
/*  in spite of that. If there is at least one dependent character which: */
/*	refers to us without translating us */
/*	and all its other refs contain no hints at all */
static int Type2SpecialCase(SplineChar *sc) {
    struct splinecharlist *d;
    RefChar *r;

    for ( d=sc->dependents; d!=NULL; d=d->next ) {
	for ( r=d->sc->layers[layer].refs; r!=NULL; r = r->next ) {
	    if ( autohint_before_generate && r->sc!=NULL &&
		    r->sc->changedsincelasthinted && !r->sc->manualhints )
		SplineCharAutoHint(r->sc,NULL);
	    if ( r->transform[0]!=1 || r->transform[1]!=0 ||
		    r->transform[2]!=0 || r->transform[3]!=1 )
	break;
	    if ( r->sc!=sc && (r->sc->hstem!=NULL || r->sc->vstem!=NULL))
	break;
	    if ( r->sc==sc && (r->transform[4]!=0 || r->transform[5]!=0))
	break;
	}
	if ( r==NULL )
return( true );
    }
return( false );
}
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */

/* Mark those glyphs which can live totally in subrs */
static void SplineFont2FullSubrs2(int flags,GlyphInfo *gi) {
    int i;
    SplineChar *sc;
#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
    int cc;
    RefChar *r;
    struct potentialsubrs *ps;
    SplineSet *spl;
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */

    if ( !autohint_before_generate && !(flags&ps_flag_nohints))
	SplineFontAutoHintRefs(gi->sf,gi->layer);

    for ( i=0; i<gi->glyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL )
	sc->lsidebearing = 0x7fff;

/* This code allows us to put whole glyphs into subroutines */
/* I found slight improvements in space on some fonts, and large increases */
/*  in others. So I'm disabling it for now */
#ifdef FONTFORGE_CONFIG_PS_REFS_GET_SUBRS
    /* We don't allow refs to refs. It's too complex */
    for ( i=0; i<gi->glyphcnt; ++i ) if ( (sc=gi->gb[i].sc)!=NULL ) {
	if ( SCWorthOutputting(sc) &&
	    (( sc->layers[layer].refs==NULL && sc->dependents!=NULL &&
		    ( (!sc->hconflicts && !sc->vconflicts) ||
			Type2SpecialCase(sc)) )  )) {
	    /* if the glyph is a single contour with no hintmasks then */
	    /*  our single contour code will find it. If we do it here too */
	    /*  we'll get a subr which points to another subr. Very dull and */
	    /*  a waste of space */
	    cc = 0;
	    for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next )
		++cc;
	    for ( r= sc->layers[layer].refs; r!=NULL && cc<2 ; r=r->next ) {
		for ( spl=r->layers[0].splines; spl!=NULL; spl=spl->next )
		    ++cc;
	    }
	    if ( cc<2 )
    continue;
	    /* Put the */
	    /*  character into a subr if it is referenced by other characters */
	    if ( gi->pcnt>=gi->pmax )
		gi->psubrs = realloc(gi->psubrs,(gi->pmax+=gi->glyphcnt)*sizeof(struct potentialsubrs));
	    ps = &gi->psubrs[gi->pcnt];
	    memset(ps,0,sizeof(*ps));	/* set cnt to 0 */
	    ps->idx = gi->pcnt++;
	    ps->full_glyph_index = i;
	    sc->lsidebearing = gi->pcnt-1;
	    ps->startstop = FigureStartStop(sc,gi);
	}
    }
#endif	/* FONTFORGE_CONFIG_PS_REFS_GET_SUBRS */
}

struct pschars *SplineFont2ChrsSubrs2(SplineFont *sf, int nomwid, int defwid,
	const int *bygid, int cnt, int flags, struct pschars **_subrs, int layer) {
    struct pschars *subrs, *chrs;
    int i,j,k,scnt;
    SplineChar *sc;
    GlyphInfo gi;
    SplineChar dummynotdef;

    if ( !autohint_before_generate && !(flags&ps_flag_nohints))
	SplineFontAutoHintRefs(sf,layer);

    memset(&gi,0,sizeof(gi));
    memset(&gi.hashed,-1,sizeof(gi.hashed));
    gi.instance_count = 1;
    gi.sf = sf;
    gi.layer = layer;
    gi.glyphcnt = cnt;
    gi.bygid = bygid;
    gi.gb = calloc(cnt,sizeof(struct glyphbits));
    gi.pmax = 3*cnt;
    gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs));
    for ( i=0; i<cnt; ++i ) {
	int gid = bygid[i];
	if ( i==0 && gid==-1 ) {
	    sc = &dummynotdef;
	    memset(sc,0,sizeof(dummynotdef));
	    dummynotdef.name = ".notdef";
	    dummynotdef.parent = sf;
	    dummynotdef.layer_cnt = sf->layer_cnt;
	    dummynotdef.layers = calloc(sf->layer_cnt,sizeof(Layer));
	    dummynotdef.width = SFOneWidth(sf);
	    if ( dummynotdef.width==-1 )
		dummynotdef.width = (sf->ascent+sf->descent)/2;
	    Type2NotDefSplines(sf,&dummynotdef,layer);
	} else if ( gid!=-1 )
	    sc = sf->glyphs[gid];
	else
    continue;
	gi.gb[i].sc = sc;
	if ( autohint_before_generate && sc!=NULL &&
		sc->changedsincelasthinted && !sc->manualhints &&
		!(flags&ps_flag_nohints))
	    SplineCharAutoHint(sc,layer,NULL);
	sc->lsidebearing = 0x7fff;
    }
    MarkTranslationRefs(sf,layer);
    SplineFont2FullSubrs2(flags,&gi);

    for ( i=0; i<cnt; ++i ) {
	if ( (sc = gi.gb[i].sc)==NULL )
    continue;
	gi.active = &gi.gb[i];
	SplineChar2PS2(sc,NULL,nomwid,defwid,NULL,flags,&gi);
	ff_progress_next();
    }

    for ( i=scnt=0; i<gi.pcnt; ++i ) {
	/* A subroutine call takes somewhere between 2 and 4 bytes itself. */
	/*  and we must add a return statement to the end. We don't want to */
	/*  make things bigger */
	/* if we have more than 65535 subrs a subr call can take 9 bytes */
	if ( gi.psubrs[i].full_glyph_index!=-1 )
	    gi.psubrs[i].idx = scnt++;
	else if ( gi.psubrs[i].cnt*gi.psubrs[i].len>(gi.psubrs[i].cnt*4)+gi.psubrs[i].len+1 )
	    gi.psubrs[i].idx = scnt++;
	else
	    gi.psubrs[i].idx = -1;
    }
    subrs = calloc(1,sizeof(struct pschars));
    subrs->cnt = scnt;
    subrs->next = scnt;
    subrs->lens = malloc(scnt*sizeof(int));
    subrs->values = malloc(scnt*sizeof(unsigned char *));
    subrs->bias = scnt<1240 ? 107 :
		  scnt<33900 ? 1131 : 32768;
    for ( i=0; i<gi.pcnt; ++i ) {
	if ( gi.psubrs[i].idx != -1 ) {
	    scnt = gi.psubrs[i].idx;
	    subrs->lens[scnt] = gi.psubrs[i].len+1;
	    subrs->values[scnt] = malloc(subrs->lens[scnt]);
	    memcpy(subrs->values[scnt],gi.psubrs[i].data,gi.psubrs[i].len);
	    subrs->values[scnt][gi.psubrs[i].len] = 11;	/* Add a return to end of subr */
	}
    }

    chrs = calloc(1,sizeof(struct pschars));
    chrs->cnt = cnt;
    chrs->next = cnt;
    chrs->lens = malloc(cnt*sizeof(int));
    chrs->values = malloc(cnt*sizeof(unsigned char *));
    chrs->keys = malloc(cnt*sizeof(char *));
    for ( i=0; i<cnt; ++i ) {
	int len=0;
	uint8_t *vals;
	struct glyphbits *gb = &gi.gb[i];
	if ( gb->sc==NULL )
    continue;
	chrs->keys[i] = copy(gb->sc->name);
	for ( k=0; k<2; ++k ) if ( k!=0 || gb->sc->lsidebearing!=0x7fff ) {
	    for ( j=0; j<gb->bcnt; ++j ) {
		if ( k!=0 || j!=0 )
		    len += gb->bits[j].dlen;
		if ( k==1 && gb->sc->lsidebearing!=0x7fff ) {
		    int si = gi.psubrs[ gb->sc->lsidebearing ].idx;
		    len += 1 + (si<=107 && si>=-107?1:si<=1131 && si>=-1131?2:si>=-32768 && si<32767?3:8);
	    break;
		}
		if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 )
		    len += gi.psubrs[ gb->bits[j].psub_index ].len;
		else {
		    int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias;
		    /* space for the number (subroutine index) */
		    if ( si>=-107 && si<=107 )
			++len;
		    else if ( si>=-1131 && si<=1131 )
			len += 2;
		    else if ( si>=-32768 && si<=32767 )
			len += 3;
		    else
			len += 8;
		    /* space for the subroutine operator */
		    ++len;
		}
	    }
	    if ( k==0 ) {
		int si = gi.psubrs[ gb->sc->lsidebearing ].idx;
		subrs->lens[si] = len+1;
		vals = subrs->values[si] = malloc(len+2);
	    } else {
		chrs->lens[i] = len+1;
		vals = chrs->values[i] = malloc(len+2); /* space for endchar and a final NUL (which is really meaningless, but makes me feel better) */
	    }

	    len = 0;
	    for ( j=0; j<gb->bcnt; ++j ) {
		int si;
		if ( k!=0 || j!=0 ) {
		    memcpy(vals+len,gb->bits[j].data,gb->bits[j].dlen);
		    len += gb->bits[j].dlen;
		}
		si = 0x80000000;
		if ( k==1 && gb->sc->lsidebearing!=0x7fff )
		    si = gi.psubrs[ gb->sc->lsidebearing ].idx - subrs->bias;
		else if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) {
		    memcpy(vals+len,gi.psubrs[ gb->bits[j].psub_index ].data,
			    gi.psubrs[ gb->bits[j].psub_index ].len);
		    len += gi.psubrs[ gb->bits[j].psub_index ].len;
		} else
		    si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias;
		if ( si!=0x80000000 ) {
		    /* space for the number (subroutine index) */
		    if ( si>=-107 && si<=107 )
			vals[len++] = si+139;
		    else if ( si>0 && si<=1131 ) {
			si-=108;
			vals[len++] = (si>>8)+247;
			vals[len++] = si&0xff;
		    } else if ( si>=-1131 && si<0 ) {
			si=(-si)-108;
			vals[len++] = (si>>8)+251;
			vals[len++] = si&0xff;
		    } else if ( si>=-32768 && si<=32767 ) {
			vals[len++] = 28;
			vals[len++] = (si>>8)&0xff;
			vals[len++] = si&0xff;
		    } else {
			/* store as fixed point, then multiply by 64. Takes 8 bytes */
			si *= (65536/64);
			vals[len++] = '\377';
			vals[len++] = (si>>24)&0xff;
			vals[len++] = (si>>16)&0xff;
			vals[len++] = (si>>8)&0xff;
			vals[len++] = si&0xff;
			vals[len++] = 64 + 139;
			vals[len++] = 0xc; vals[len++] = 0x18;	/* Multiply */
		    }
    
		    /* space for the subroutine operator */
		    vals[len++] = 10;
		}
		if ( k==1 && gb->sc->lsidebearing!=0x7fff )
	    break;
	    }
	    if ( k==0 ) {
		vals[len++] = 11;	/* return */
		vals[len] = '\0';
	    } else {
		vals[len++] = 14;	/* endchar */
		vals[len] = '\0';
	    }
	}
    }
    
    GIFree(&gi,&dummynotdef);
    *_subrs = subrs;
return( chrs );
}

struct pschars *CID2ChrsSubrs2(SplineFont *cidmaster,struct fd2data *fds,
	int flags, struct pschars **_glbls, int layer) {
    struct pschars *chrs, *glbls;
    int i, j, cnt, cid, max, fd;
    int *scnts;
    SplineChar *sc;
    SplineFont *sf = NULL;
    /* In a cid-keyed font, cid 0 is defined to be .notdef so there are no */
    /*  special worries. If it is defined we use it. If it is not defined */
    /*  we add it. */
    GlyphInfo gi;
    SplineChar dummynotdef;

    max = 0;
    for ( i=0; i<cidmaster->subfontcnt; ++i ) {
	if ( max<cidmaster->subfonts[i]->glyphcnt )
	    max = cidmaster->subfonts[i]->glyphcnt;
	MarkTranslationRefs(cidmaster->subfonts[i],layer);
    }
    cnt = 1;			/* for .notdef */
    for ( cid = 1; cid<max; ++cid ) {
	for ( i=0; i<cidmaster->subfontcnt; ++i ) {
	    sf = cidmaster->subfonts[i];
	    if ( cid<sf->glyphcnt && (sc=sf->glyphs[cid])!=NULL ) {
		sc->ttf_glyph = -1;
		sc->lsidebearing = 0x7fff;
		if ( SCWorthOutputting(sc))
		    ++cnt;
	break;
	    }
	}
    }

    memset(&gi,0,sizeof(gi));
    memset(&gi.hashed,-1,sizeof(gi.hashed));
    gi.instance_count = 1;
    gi.sf = sf;
    gi.glyphcnt = cnt;
    gi.bygid = NULL;
    gi.gb = calloc(cnt,sizeof(struct glyphbits));
    gi.pmax = 3*cnt;
    gi.psubrs = malloc(gi.pmax*sizeof(struct potentialsubrs));
    gi.layer = layer;

    for ( cid = cnt = 0; cid<max; ++cid ) {
	sf = NULL;
	for ( i=0; i<cidmaster->subfontcnt; ++i ) {
	    sf = cidmaster->subfonts[i];
	    if ( cid<sf->glyphcnt && SCWorthOutputting(sf->glyphs[cid]) )
	break;
	}
	if ( cid!=0 && i==cidmaster->subfontcnt ) {
	    sc=NULL;
	} else if ( i==cidmaster->subfontcnt ) {
	    /* They didn't define CID 0 */
	    sc = &dummynotdef;
	    /* Place it in the final subfont (which is what sf points to) */
	    memset(sc,0,sizeof(dummynotdef));
	    dummynotdef.name = ".notdef";
	    dummynotdef.parent = sf;
	    dummynotdef.layer_cnt = layer+1;
	    dummynotdef.layers = calloc(layer+1,sizeof(Layer));
	    dummynotdef.width = SFOneWidth(sf);
	    if ( dummynotdef.width==-1 )
		dummynotdef.width = (sf->ascent+sf->descent);
	    Type2NotDefSplines(sf,&dummynotdef,layer);
	    gi.gb[cnt].sc = sc;
	    gi.gb[cnt].fd = i = cidmaster->subfontcnt-1;
	} else {
	    gi.gb[cnt].sc = sc = sf->glyphs[cid];
	    gi.gb[cnt].fd = i;
	}
	if ( sc!=NULL ) {
	    sc->lsidebearing = 0x7fff;
	    gi.active = &gi.gb[cnt];
	    sc->ttf_glyph = cnt++;
	    SplineChar2PS2(sc,NULL,fds[i].nomwid,fds[i].defwid,NULL,flags,&gi);
	}
	ff_progress_next();
    }

    scnts = calloc( cidmaster->subfontcnt+1,sizeof(int));
    for ( i=0; i<gi.pcnt; ++i ) {
	gi.psubrs[i].idx = -1;
	if ( gi.psubrs[i].cnt*gi.psubrs[i].len>(gi.psubrs[i].cnt*4)+gi.psubrs[i].len+1 )
	    gi.psubrs[i].idx = scnts[gi.psubrs[i].fd+1]++;
    }

    glbls = calloc(1,sizeof(struct pschars));
    glbls->cnt = scnts[0];
    glbls->next = scnts[0];
    glbls->lens = malloc(scnts[0]*sizeof(int));
    glbls->values = malloc(scnts[0]*sizeof(unsigned char *));
    glbls->bias = scnts[0]<1240 ? 107 :
		  scnts[0]<33900 ? 1131 : 32768;
    for ( fd=0; fd<cidmaster->subfontcnt; ++fd ) {
	fds[fd].subrs = calloc(1,sizeof(struct pschars));
	fds[fd].subrs->cnt = scnts[fd+1];
	fds[fd].subrs->next = scnts[fd+1];
	fds[fd].subrs->lens = malloc(scnts[fd+1]*sizeof(int));
	fds[fd].subrs->values = malloc(scnts[fd+1]*sizeof(unsigned char *));
	fds[fd].subrs->bias = scnts[fd+1]<1240 ? 107 :
			      scnts[fd+1]<33900 ? 1131 : 32768;
    }
    free( scnts);

    for ( i=0; i<gi.pcnt; ++i ) {
	if ( gi.psubrs[i].idx != -1 ) {
	    struct pschars *subrs = gi.psubrs[i].fd==-1 ? glbls : fds[gi.psubrs[i].fd].subrs;
	    int scnt = gi.psubrs[i].idx;
	    subrs->lens[scnt] = gi.psubrs[i].len+1;
	    subrs->values[scnt] = malloc(subrs->lens[scnt]);
	    memcpy(subrs->values[scnt],gi.psubrs[i].data,gi.psubrs[i].len);
	    subrs->values[scnt][gi.psubrs[i].len] = 11;	/* Add a return to end of subr */
	}
    }


    chrs = calloc(1,sizeof(struct pschars));
    chrs->cnt = cnt;
    chrs->next = cnt;
    chrs->lens = malloc(cnt*sizeof(int));
    chrs->values = malloc(cnt*sizeof(unsigned char *));
    chrs->keys = malloc(cnt*sizeof(char *));
    for ( i=0; i<cnt; ++i ) {
	int len=0;
	struct glyphbits *gb = &gi.gb[i];
	chrs->keys[i] = copy(gb->sc->name);
	for ( j=0; j<gb->bcnt; ++j ) {
	    len += gb->bits[j].dlen;
	    if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 )
		len += gi.psubrs[ gb->bits[j].psub_index ].len;
	    else {
		struct pschars *subrs = gi.psubrs[gb->bits[j].psub_index].fd==-1 ? glbls : fds[gi.psubrs[gb->bits[j].psub_index].fd].subrs;
		int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias;
		/* space for the number (subroutine index) */
		if ( si>=-107 && si<=107 )
		    ++len;
		else if ( si>=-1131 && si<=1131 )
		    len += 2;
		else if ( si>=-32768 && si<=32767 )
		    len += 3;
		else
		    len += 8;
		/* space for the subroutine operator */
		++len;
	    }
	}
	chrs->lens[i] = len+1;
	chrs->values[i] = malloc(len+2); /* space for endchar and a final NUL (which is really meaningless, but makes me feel better) */

	len = 0;
	for ( j=0; j<gb->bcnt; ++j ) {
	    memcpy(chrs->values[i]+len,gb->bits[j].data,gb->bits[j].dlen);
	    len += gb->bits[j].dlen;
	    if ( gi.psubrs[ gb->bits[j].psub_index ].idx==-1 ) {
		memcpy(chrs->values[i]+len,gi.psubrs[ gb->bits[j].psub_index ].data,
			gi.psubrs[ gb->bits[j].psub_index ].len);
		len += gi.psubrs[ gb->bits[j].psub_index ].len;
	    } else {
		struct pschars *subrs = gi.psubrs[gb->bits[j].psub_index].fd==-1 ? glbls : fds[gi.psubrs[gb->bits[j].psub_index].fd].subrs;
		int si = gi.psubrs[ gb->bits[j].psub_index ].idx - subrs->bias;
		/* space for the number (subroutine index) */
		if ( si>=-107 && si<=107 )
		    chrs->values[i][len++] = si+139;
		else if ( si>0 && si<=1131 ) {
		    si-=108;
		    chrs->values[i][len++] = (si>>8)+247;
		    chrs->values[i][len++] = si&0xff;
		} else if ( si>=-1131 && si<0 ) {
		    si=(-si)-108;
		    chrs->values[i][len++] = (si>>8)+251;
		    chrs->values[i][len++] = si&0xff;
		} else if ( si>=-32768 && si<=32767 ) {
		    chrs->values[i][len++] = 28;
		    chrs->values[i][len++] = (si>>8)&0xff;
		    chrs->values[i][len++] = si&0xff;
		} else {
		    /* store as fixed point, then multiply by 64. Takes 8 bytes */
		    si *= (65536/64);
		    chrs->values[i][len++] = '\377';
		    chrs->values[i][len++] = (si>>24)&0xff;
		    chrs->values[i][len++] = (si>>16)&0xff;
		    chrs->values[i][len++] = (si>>8)&0xff;
		    chrs->values[i][len++] = si&0xff;
		    chrs->values[i][len++] = 64 + 139;
		    chrs->values[i][len++] = 0xc; chrs->values[i][len++] = 0x18;	/* Multiply */
		}
		/* space for the subroutine operator */
		if ( gi.psubrs[ gb->bits[j].psub_index ].fd==-1 ) {
		    chrs->values[i][len++] = 29;
		} else
		    chrs->values[i][len++] = 10;
	    }
	}
	chrs->values[i][len++] = 14;	/* endchar */
	chrs->values[i][len] = '\0';
    }
    GIFree(&gi,&dummynotdef);
    *_glbls = glbls;
return( chrs );
}

