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

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

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

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

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

/***************************************************************************/
/*                                                                         */
/* Apple documents these tables at                                         */
/*    http:// developer.apple.com/fonts/TTRefMan/RM06/Chap6[fgca]var.html  */
/* The documentation for fvar is inconsistant. At one point it says that   */
/*  countSizePairs should be 3, at another point 2. It should be 2.        */
/* The documentation for gvar is not intelligable, cvar refers you to gvar */
/*  and is thus also incomprehensible                                      */
/* The documentation for avar appears correct, but as Apple has no fonts   */
/*  with an 'avar' table, so it's hard to test.                            */
/*                                                                         */
/***************************************************************************/

/* Parse Apple's variation tables (font, glyph, cvt and axis) */
/* To be interesting a font must have an fvar and a gvar */
/* If it's hinted it better have a cvar */
/* It may have an avar if it wants to be complicated */

void VariationFree(struct ttfinfo *info) {
    int i,j;
    struct variations *variation = info->variations;

    if ( variation==NULL )
return;
    if ( variation->axes!=NULL ) {
	for ( i=0; i<variation->axis_count; ++i ) {
	    free(variation->axes[i].mapfrom);
	    free(variation->axes[i].mapto);
	}
	free(variation->axes);
    }
    if ( variation->instances!=NULL ) {
	for ( i=0; i<variation->instance_count; ++i ) {
	    free(variation->instances[i].coords);
	}
	free(variation->instances);
    }
    if ( variation->tuples!=NULL ) {
	for ( i=0; i<variation->tuple_count; ++i ) {
	    free(variation->tuples[i].coords);
	    if ( variation->tuples[i].chars!=NULL )
		for ( j=0; j<info->glyph_cnt; ++j )
		    SplineCharFree(variation->tuples[i].chars[j]);
	    free(variation->tuples[i].chars);
	    KernClassListFree(variation->tuples[i].khead);
	    KernClassListFree(variation->tuples[i].vkhead);
	}
	free(variation->tuples);
    }
    free(variation);
    info->variations = NULL;
}

static void parsefvar(struct ttfinfo *info, FILE *ttf) {
    int data_off, axis_count, instance_count, cnt;
    int i,j;

    fseek(ttf,info->fvar_start,SEEK_SET);
    if ( getlong(ttf)!=0x00010000 )	/* I only understand version 1 */
return;
    data_off = getushort(ttf);
    cnt = getushort(ttf);
    if ( cnt>2 )
	LogError( _("Hmm, this 'fvar' table has more count/size pairs than I expect\n") );
    else if ( cnt<2 ) {
	LogError( _("Hmm, this 'fvar' table has too few count/size pairs, I shan't parse it\n") );
return;
    }
    axis_count = getushort(ttf);
    if ( axis_count==0 || axis_count>4 ) {
	if ( axis_count==0 )
	    LogError( _("Hmm, this 'fvar' table has no axes, that doesn't make sense.\n") );
	else
	    LogError( _("Hmm, this 'fvar' table has more axes than FontForge can handle.\n") );
return;
    }
    if ( getushort(ttf)!=20 ) {
	LogError( _("Hmm, this 'fvar' table has an unexpected size for an axis, I shan't parse it\n") );
return;
    }
    instance_count = getushort(ttf);
    if ( getushort(ttf)!=4+4*axis_count ) {
	LogError( _("Hmm, this 'fvar' table has an unexpected size for an instance, I shan't parse it\n") );
return;
    }
    if ( data_off+axis_count*20+instance_count*(4+4*axis_count)> info->fvar_len ) {
	LogError( _("Hmm, this 'fvar' table is too short\n") );
return;
    }

    info->variations = gcalloc(1,sizeof(struct variations));
    info->variations->axis_count = axis_count;
    info->variations->instance_count = instance_count;
    info->variations->axes = gcalloc(axis_count,sizeof(struct taxis));
    info->variations->instances = gcalloc(instance_count,sizeof(struct tinstance));
    for ( i=0; i<instance_count; ++i )
	info->variations->instances[i].coords = galloc(axis_count*sizeof(real));

    fseek(ttf,info->fvar_start+data_off,SEEK_SET);
    for ( i=0; i<axis_count; ++i ) {
	struct taxis *a = &info->variations->axes[i];
	a->tag = getlong(ttf);
	a->min = getlong(ttf)/65536.0;
	a->def = getlong(ttf)/65536.0;
	a->max = getlong(ttf)/65536.0;
	/* flags = */ getushort(ttf);
	a->nameid = getushort(ttf);
    }
    for ( i=0; i<instance_count; ++i ) {
	struct tinstance *ti = &info->variations->instances[i];
	ti->nameid = getushort(ttf);
	/* flags = */ getushort(ttf);
	for ( j=0 ; j<axis_count; ++j )
	    ti->coords[j] = getlong(ttf)/65536.0;
    }
}

static void parseavar(struct ttfinfo *info, FILE *ttf) {
    int axis_count, pair_count;
    int i,j;

    if ( info->variations==NULL || info->avar_start==0 || info->avar_len==0 )
return;

    fseek(ttf,info->avar_start,SEEK_SET);
    if ( getlong(ttf)!=0x00010000 ) {	/* I only understand version 1 */
	VariationFree(info);
return;
    }
    axis_count = getlong(ttf);
    if ( axis_count!=info->variations->axis_count ) {
	LogError( _("Hmm, the axis count in the 'avar' table is different from that in the 'fvar' table.\n") );
	VariationFree(info);
return;
    }
    for ( i=0; i<axis_count; ++i ) {
	pair_count = getushort(ttf);
	if ( pair_count!=0 ) {
	    info->variations->axes[i].mapfrom = galloc(pair_count*sizeof(real));
	    info->variations->axes[i].mapto= galloc(pair_count*sizeof(real));
	    for ( j=0; j<pair_count; ++j ) {
		info->variations->axes[i].mapfrom[j] = getushort(ttf)/16384.0;
		info->variations->axes[i].mapto[j] = getushort(ttf)/16384.0;
	    }
	}
    }
    if ( ftell(ttf)-info->avar_start>info->avar_len) {
	LogError( _("Hmm, the the 'avar' table is too long.\n") );
	VariationFree(info);
return;
    }
}

static SplineChar **InfoCopyGlyphs(struct ttfinfo *info) {
    SplineChar **chars = galloc(info->glyph_cnt*sizeof(SplineChar *));
    int i;
    RefChar *r;

    for ( i=0; i<info->glyph_cnt; ++i ) {
	if ( info->chars[i]==NULL )
	    chars[i] = NULL;
	else {
	    chars[i] = SplineCharCopy(info->chars[i],NULL);
	    free(chars[i]->ttf_instrs); chars[i]->ttf_instrs = NULL;
	    chars[i]->ttf_instrs_len = 0;
	    PSTFree(chars[i]->possub); chars[i]->possub = NULL;
	    for ( r=chars[i]->layers[ly_fore].refs; r!=NULL; r=r->next )
		r->sc = NULL;
	    chars[i]->changed = false;
	    chars[i]->ticked = false;
	}
    }

    for ( i=0; i<info->glyph_cnt; ++i )
	ttfFixupRef(chars,i);
return( chars );
}

#define BAD_DELTA	0x10001
static int *readpackeddeltas(FILE *ttf,int n) {
    int *deltas;
    int runcnt, i, j;

    deltas = galloc(n*sizeof(int));

    i = 0;
    while ( i<n ) {
	runcnt = getc(ttf);
	if ( runcnt&0x80 ) {
	    /* runcnt zeros get added */
	    for ( j=0; j<=(runcnt&0x3f) && i<n ; ++j )
		deltas[i++] = 0;
	} else if ( runcnt&0x40 ) {
	    /* runcnt shorts from the stack */
	    for ( j=0 ; j<=(runcnt&0x3f) && i<n ; ++j )
		deltas[i++] = (int16) getushort(ttf);
	} else {
	    /* runcnt signed bytes from the stack */
	    for ( j=0; j<=(runcnt&0x3f) && i<n; ++j )
		deltas[i++] = (int8) getc(ttf);
	}
	if ( j<=(runcnt&0x3f) ) {
	    if ( n>0 )
		deltas[0] = BAD_DELTA;
	}
    }
return( deltas );
}

#define ALL_POINTS	0x10001
#define END_OF_POINTS	0x10000

static int *readpackedpoints(FILE *ttf) {
    int *points;
    int n, runcnt, i, j, first;

    n = getc(ttf);
    if ( n==EOF )
	n = 0;
    if ( n&0x80 )
	n = getc(ttf)|((n&0x7f)<<8);
    points = galloc((n+1)*sizeof(int));
    if ( n==0 )
	points[0] = ALL_POINTS;
    else {
	i = 0;
	while ( i<n ) {
	    runcnt = getc(ttf);
	    if ( runcnt&0x80 ) {
		runcnt = (runcnt&0x7f);
		points[i++] = first = getushort(ttf);
		/* first point not included in runcount */
		for ( j=0; j<runcnt && i<n; ++j )
		    points[i++] = (first += getushort(ttf));
	    } else {
		points[i++] = first = getc(ttf);
		for ( j=0; j<runcnt && i<n; ++j )
		    points[i++] = (first += getc(ttf));
	    }
	}
	points[n] = END_OF_POINTS;
    }
return( points );
}

static int TuplesMatch(struct variations *v, int vtest, int dbase) {
    /* variations for [0,1] make up part of design [1,1], but not the other */
    /* way round */
    int i;

    if ( dbase>=v->tuple_count )
return( false );

    if ( vtest==dbase )
return( true );
    for ( i=0; i<v->axis_count; ++i ) {
	if ( v->tuples[vtest].coords[i]==0 && v->tuples[dbase].coords[i]!=0 )
return( false );
	if ( v->tuples[dbase].coords[i]!=0 &&
		v->tuples[dbase].coords[i]!=v->tuples[vtest].coords[i] )
return( false );
    }
return( true );
}

static int PointCount(SplineChar *sc) {
    int i;
    RefChar *ref;
    SplineSet *ss;
    SplinePoint *sp;

    if ( sc->layers[ly_fore].refs!=NULL )
	for ( i=0, ref=sc->layers[ly_fore].refs; ref!=NULL; ++i, ref=ref->next );
    else {
	for ( i=0, ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
	    for ( sp=ss->first; sp!=NULL ; ) {
		if ( sp->ttfindex!=0xffff && sp->ttfindex!=0xfffe )
		    ++i;
		if ( sp->nextcpindex!=0xffff && sp->nextcpindex!=0xfffe )
		    ++i;
		if ( sp->next==NULL )
	    break;
		sp = sp->next->to;
		if ( sp==ss->first )
	    break;
	    }
	}
    }
return( i );
}

static void SCShiftAllBy(SplineChar *sc,int xd, int yd) {
    /* If they change the left/right side-bearing, I think that means everything */
    /*  should be shifted over */
    SplineSet *ss;
    SplinePoint *sp;
    RefChar *ref;

    if ( xd==0 && yd==0 )
return;

    for ( ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
	for ( sp=ss->first; sp!=NULL ; ) {
	    if ( sp->ttfindex!=0xffff && sp->ttfindex!=0xfffe ) {
		sp->me.x += xd;
		sp->me.y += yd;
	    }
	    if ( sp->nextcpindex!=0xffff && sp->nextcpindex!=0xfffe ) {
		sp->nextcp.x += xd;
		sp->nextcp.y += yd;
		if ( sp->next!=NULL )
		    sp->next->to->prevcp = sp->nextcp;
	    }
	    if ( sp->next==NULL )
	break;
	    sp = sp->next->to;
	    if ( sp == ss->first )
	break;
	}
    }
    for ( ref=sc->layers[ly_fore].refs; ref!=NULL; ref=ref->next ) {
	ref->transform[4] += xd;
	ref->transform[5] += yd;
	SCReinstanciateRefChar(sc,ref);
    }
}

static void VaryGlyph(SplineChar *sc,int *points, int *xdeltas, int *ydeltas,
	int pcnt) {
    /* A character contains either composites or contours */
    int i,j;
    RefChar *ref;
    SplineSet *ss;
    SplinePoint *sp;
    Spline *s, *first;

    if ( points[0]==ALL_POINTS ) {
	if ( sc->layers[ly_fore].refs!=NULL ) {
	    for ( i=0, ref=sc->layers[ly_fore].refs; ref!=NULL; ++i, ref=ref->next ) {
		if ( xdeltas[i]!=0 || ydeltas[i]!=0 ) {
		    ref->transform[4] += xdeltas[i];
		    ref->transform[5] += ydeltas[i];
		    SCReinstanciateRefChar(sc,ref);
		}
	    }
	} else {
	    for ( ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
		for ( sp=ss->first; sp!=NULL ; ) {
		    if ( sp->ttfindex!=0xffff && sp->ttfindex!=0xfffe ) {
			sp->me.x += xdeltas[sp->ttfindex];
			sp->me.y += ydeltas[sp->ttfindex];
		    }
		    if ( sp->nextcpindex!=0xffff && sp->nextcpindex!=0xfffe ) {
			sp->nextcp.x += xdeltas[sp->nextcpindex];
			sp->nextcp.y += ydeltas[sp->nextcpindex];
			if ( sp->next!=NULL )
			    sp->next->to->prevcp = sp->nextcp;
		    }
		    if ( sp->next==NULL )
		break;
		    sp = sp->next->to;
		    if ( sp == ss->first )
		break;
		}
	    }
	}
	SCShiftAllBy(sc,-xdeltas[pcnt-4],0);
	SCShiftAllBy(sc,0,-ydeltas[pcnt-2]);
	sc->width += xdeltas[pcnt-3];
	sc->vwidth += ydeltas[pcnt-1];
    } else {
	j = 0;
	if ( sc->layers[ly_fore].refs!=NULL ) {
	    for ( i=0, ref=sc->layers[ly_fore].refs; ref!=NULL; ++i, ref=ref->next ) {
		if ( points[j]==i ) {
		    if ( xdeltas[j]!=0 || ydeltas[j]!=0 ) {
			ref->transform[4] += xdeltas[j];
			ref->transform[5] += ydeltas[j];
			SCReinstanciateRefChar(sc,ref);
		    }
		    ++j;
		}
	    }
	} else {
	    for ( i=0, ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
		if ( ss->first->prev!=NULL && ss->first->prev->from->nextcpindex==points[j] ) {
		    ss->first->prevcp.x += xdeltas[j];
		    ss->first->prevcp.y += ydeltas[j];
		    ss->first->prev->from->nextcp = ss->first->prevcp;
		    ++j;
		}
		for ( sp=ss->first; sp!=NULL ; ++i ) {
		    if ( sp->ttfindex!=0xffff && sp->ttfindex!=0xfffe )
			++i;
		    if ( sp->ttfindex==points[j] ) {
			sp->me.x += xdeltas[j];
			sp->me.y += ydeltas[j++];
		    }
		    if ( sp->nextcpindex!=0xffff && sp->nextcpindex!=0xfffe )
			++i;
		    if ( sp->nextcpindex==points[j] ) {
			sp->nextcp.x += xdeltas[j];
			sp->nextcp.y += ydeltas[j++];
			if ( sp->next!=NULL )
			    sp->next->to->prevcp = sp->nextcp;
		    } else if ( sp->nonextcp ) {
			sp->nextcp = sp->me;
		    }
		    if ( sp->next==NULL )
		break;
		    sp = sp->next->to;
		    if ( sp == ss->first )
		break;
		}
		first = NULL;
	    }
	}
	if ( points[j]==i )
	    SCShiftAllBy(sc,-xdeltas[j++],0);
	if ( points[j]==i+1 )
	    sc->width += xdeltas[j++];
	if ( points[j]==i+2 )
	    SCShiftAllBy(sc,0,-ydeltas[j++]);
	if ( points[j]==i+3 )
	    sc->vwidth += ydeltas[j++];
    }
    if ( sc->layers[ly_fore].refs==NULL ) {
	for ( ss = sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
	    for ( sp=ss->first; sp!=NULL ; ++i ) {
		if ( sp->ttfindex==0xffff ) {
		    sp->me.x = ( sp->nextcp.x + sp->prevcp.x )/2;
		    sp->me.y = ( sp->nextcp.y + sp->prevcp.y )/2;
		}
		if ( sp->next==NULL )
	    break;
		sp = sp->next->to;
		if ( sp == ss->first )
	    break;
	    }

	    first = NULL;
	    for ( s=ss->first->next; s!=NULL && s!=first; s=s->to->next ) {
		SplineRefigure(s);
		if ( first==NULL ) first = s;
	    }
	}
    }
}

static void VaryGlyphs(struct ttfinfo *info,int tupleIndex,int gnum,
	int *points, FILE *ttf ) {
    /* one annoying thing about gvar, is that the variations do not describe */
    /*  designs. well variations for [0,1] describes that design, but the */
    /*  design for [1,1] includes the variations [0,1], [1,0], and [1,1] */
    int pcnt, tc;
    int *xdeltas, *ydeltas;
    struct variations *v = info->variations;

    if ( info->chars[gnum]==NULL )	/* Apple doesn't support ttc so this */
return;					/*  can't happen */
    if ( points==NULL ) {
	LogError( _("Mismatched local and shared tuple flags.\n") );
return;
    }

    if ( points[0]==ALL_POINTS )
	pcnt = PointCount(info->chars[gnum])+4;
    else {
	for ( pcnt=0; points[pcnt]!=END_OF_POINTS; ++pcnt );
    }
    xdeltas = readpackeddeltas(ttf,pcnt);
    ydeltas = readpackeddeltas(ttf,pcnt);
    if ( xdeltas[0]!=BAD_DELTA && ydeltas[0]!=BAD_DELTA )
	for ( tc = 0; tc<v->tuple_count; ++tc ) {
	    if ( TuplesMatch(v,tc,tupleIndex))
		VaryGlyph(v->tuples[tc].chars[gnum],points,xdeltas,ydeltas,pcnt);
    } else {
	static int warned = false;
	if ( !warned )
	    LogError( _("Incorrect number of deltas in glyph %d (%s)\n"), gnum,
		    info->chars[gnum]->name!=NULL?info->chars[gnum]->name:"<Nameless>" );
	warned = true;
    }
    free(xdeltas);
    free(ydeltas);
}

static void parsegvar(struct ttfinfo *info, FILE *ttf) {
    /* I'm only going to support a subset of the gvar. Only the global tuples */
    int axiscount, globaltc, gvarflags, gc, i,j,g;
    uint32 tupoff, dataoff, *gvars;
    struct variations *v = info->variations;
    int warned=false;

    fseek(ttf,info->gvar_start,SEEK_SET);
    if ( getlong(ttf)!=0x00010000 ) {	/* I only understand version 1 */
	VariationFree(info);
return;
    }
    axiscount = getushort(ttf);
    if ( axiscount!=info->variations->axis_count ) {
	LogError( _("Hmm, the axis count in the 'gvar' table is different from that in the 'fvar' table.\n") );
	VariationFree(info);
return;
    }
    globaltc = getushort(ttf);
    tupoff = getlong(ttf) + info->gvar_start;
    gc = getushort(ttf);
    gvarflags = getushort(ttf);
    dataoff = getlong(ttf) + info->gvar_start;
    if ( globaltc==0 || globaltc>AppleMmMax ) {
	if ( globaltc==0 )
	    LogError( _("Hmm, no global tuples specified in the 'gvar' table.\n") );
	else
	    LogError( _("Hmm, too many global tuples specified in the 'gvar' table.\n FontForge only supports %d\n"), AppleMmMax );
	VariationFree(info);
return;
    }
    if ( gc>info->glyph_cnt ) {
	LogError( _("Hmm, more glyph variation data specified than there are glyphs in font.\n") );
	VariationFree(info);
return;
    }

    gvars = galloc((gc+1)*sizeof(uint32));
    if ( gvarflags&1 ) {	/* 32 bit data */
	for ( i=0; i<=gc; ++i )
	    gvars[i] = getlong(ttf)+dataoff;
    } else {
	for ( i=0; i<=gc; ++i )
	    gvars[i] = getushort(ttf)*2 +dataoff;	/* Undocumented *2 */
    }

    v->tuple_count = globaltc;
    v->tuples = gcalloc(globaltc,sizeof(struct tuples));
    fseek(ttf,tupoff,SEEK_SET);
    for ( i=0; i<globaltc; ++i ) {
	v->tuples[i].coords = galloc(axiscount*sizeof(float));
	for ( j=0; j<axiscount; ++j )
	    v->tuples[i].coords[j] = ((short) getushort(ttf))/16384.0;
	v->tuples[i].chars = InfoCopyGlyphs(info);
    }

    for ( g=0; g<gc; ++g ) if ( gvars[g]!=gvars[g+1] ) {
	int tc;
	uint32 datoff;
	int *sharedpoints=NULL;
	fseek(ttf,gvars[g],SEEK_SET);
	tc = getushort(ttf);
	datoff = gvars[g]+getushort(ttf);
	if ( tc&0x8000 ) {
	    uint32 here = ftell(ttf);
	    fseek(ttf,datoff,SEEK_SET);
	    sharedpoints = readpackedpoints(ttf);
	    datoff = ftell(ttf);
	    fseek(ttf,here,SEEK_SET);
	}
	for ( i=0; i<(tc&0xfff); ++i ) {
	    int tupleDataSize, tupleIndex;
	    tupleDataSize = getushort(ttf);
	    tupleIndex = getushort(ttf);
	    if ( tupleIndex&0xc000 ) {
		if ( !warned )
		    LogError( _("Warning: Glyph %d contains either private or intermediate tuple data.\n FontForge supports neither.\n"),
			    g);
		warned = true;
		if ( tupleIndex&0x8000 )
		    fseek(ttf,2*axiscount,SEEK_CUR);
		if ( tupleIndex&0x4000 )
		    fseek(ttf,4*axiscount,SEEK_CUR);
	    } else {
		int *localpoints=NULL;
		uint32 here = ftell(ttf);
		fseek(ttf,datoff,SEEK_SET);
		if ( tupleIndex&0x2000 )
		    localpoints = readpackedpoints(ttf);
		VaryGlyphs(info,tupleIndex&0xfff,g,
			(tupleIndex&0x2000)?localpoints:sharedpoints,ttf);
		free(localpoints);
		fseek(ttf,here,SEEK_SET);
	    }
	    datoff += tupleDataSize;
	}
	free(sharedpoints);
    }
    free(gvars);
}

static void AlterEntry(struct ttf_table *cvt, int i, int delta ) {
    int val = memushort(cvt->data,cvt->len,2*i);
    memputshort(cvt->data,2*i,val+delta);
}

static void VaryCvt(struct tuples *tuple,int *points, int *deltas,
	int pcnt, struct ttf_table *orig_cvt) {
    struct ttf_table *cvt;
    int i;

    if ( (cvt = tuple->cvt)==NULL ) {
	cvt = tuple->cvt = chunkalloc(sizeof(struct ttf_table));
	cvt->tag = orig_cvt->tag;
	cvt->len = cvt->maxlen = orig_cvt->len;
	cvt->data = galloc(cvt->len);
	memcpy(cvt->data,orig_cvt->data,cvt->len);
    }
    if ( points[0]==ALL_POINTS ) {
	for ( i=0; i<pcnt; ++i )
	    AlterEntry(cvt,i,deltas[i]);
    } else {
	for ( i=0; i<pcnt; ++i )
	    AlterEntry(cvt,points[i],deltas[i]);
    }
}

static void VaryCvts(struct ttfinfo *info,int tupleIndex, int *points, FILE *ttf,
    struct ttf_table *origcvt ) {
    /* one annoying thing about gvar, is that the variations do not describe */
    /*  designs. well variations for [0,1] describes that design, but the */
    /*  design for [1,1] includes the variations [0,1], [1,0], and [1,1] */
    /* And same is true of cvar */
    int pcnt, tc;
    int *deltas;
    struct variations *v = info->variations;

    if ( points[0]==ALL_POINTS )
	pcnt = origcvt->len/sizeof(uint16);
    else {
	for ( pcnt=0; points[pcnt]!=END_OF_POINTS; ++pcnt );
    }
    deltas = readpackeddeltas(ttf,pcnt);
    if ( deltas[0]!=BAD_DELTA )
	for ( tc = 0; tc<v->tuple_count; ++tc ) {
	    if ( TuplesMatch(v,tc,tupleIndex))
		VaryCvt(&v->tuples[tc],points,deltas,pcnt,origcvt);
    } else {
	static int warned = false;
	if ( !warned )
	    LogError( _("Incorrect number of deltas in cvt\n") );
	warned = true;
    }
    free(deltas);
}

static void parsecvar(struct ttfinfo *info, FILE *ttf) {
    struct ttf_table *cvt;
    int tuplecount;
    uint32 offset;
    int *sharedpoints=NULL;
    int i;
    int warned = false;

    for ( cvt = info->tabs; cvt!=NULL && cvt->tag!=CHR('c','v','t',' '); cvt=cvt->next );
    if ( cvt==NULL )
return;

    fseek(ttf,info->cvar_start,SEEK_SET);
    if ( getlong(ttf)!=0x00010000 ) {	/* I only understand version 1 */
	/* I think I can live without cvt variations... */
	/* So I shan't free the structure */
return;
    }

    tuplecount = getushort(ttf);
    offset = info->cvar_start+getushort(ttf);
    /* The documentation implies there are flags packed into the tuplecount */
    /*  but John Jenkins tells me that shared points don't apply to cvar */
    /*  Might as well parse it just in case */
    if ( tuplecount&0x8000 ) {
	uint32 here = ftell(ttf);
	fseek(ttf,offset,SEEK_SET);
	sharedpoints = readpackedpoints(ttf);
	offset = ftell(ttf);
	fseek(ttf,here,SEEK_SET);
    }
    for ( i=0; i<(tuplecount&0xfff); ++i ) {
	int tupleDataSize, tupleIndex;
	tupleDataSize = getushort(ttf);
	tupleIndex = getushort(ttf);
	/* there is no provision here for a global tuple coordinate section */
	/*  so John says there are no tuple indeces. Just embedded tuples */
	if ( tupleIndex&0x4000 ) {
	    if ( !warned )
		LogError( _("Warning: 'cvar' contains intermediate tuple data.\n FontForge doesn't support this.\n") );
	    warned = true;
	    if ( tupleIndex&0x8000 )
		fseek(ttf,2*info->variations->axis_count,SEEK_CUR);
	    if ( tupleIndex&0x4000 )
		fseek(ttf,4*info->variations->axis_count,SEEK_CUR);
	} else {
	    int *localpoints=NULL;
	    uint32 here;
	    int j,k,ti;
	    ti = tupleIndex&0xfff;
	    if ( tupleIndex&0x8000 ) {
		real *coords = galloc(info->variations->axis_count*sizeof(real));
		for ( j=0; j<info->variations->axis_count; ++j )
		    coords[j] = ((int16) getushort(ttf))/16384.0;
		for ( k=0 ; k<info->variations->tuple_count; ++k ) {
		    for ( j=0; j<info->variations->axis_count; ++j )
			if ( coords[j]!=info->variations->tuples[k].coords[j] )
		    break;
		    if ( j==info->variations->axis_count )
		break;
		}
		ti = -1;
		if ( k!=info->variations->tuple_count )
		    ti = k;
	    }
	    if ( ti!=-1 ) {
		here = ftell(ttf);
		fseek(ttf,offset,SEEK_SET);
		if ( tupleIndex&0x2000 )
		    localpoints = readpackedpoints(ttf);
		VaryCvts(info,ti,(tupleIndex&0x2000)?localpoints:sharedpoints,ttf,cvt);
		free(localpoints);
		fseek(ttf,here,SEEK_SET);
	    }
	}
	offset += tupleDataSize;
    }
    free(sharedpoints);
}

void readttfvariations(struct ttfinfo *info, FILE *ttf) {
    if ( info->gvar_start==0 || info->gvar_len==0 || info->fvar_start==0 || info->fvar_len==0 )
return;

#if defined(FONTFORGE_CONFIG_GDRAW)
    gwwv_progress_change_line2(_("Processing Variations"));
    if ( !no_windowing_ui )
	GDrawProcessPendingEvents(NULL);
#elif defined(FONTFORGE_CONFIG_GTK)
    gwwv_progress_change_line2(_("Processing Variations"));
    /* !!! force an expose */
#endif
    parsefvar(info,ttf);
    if ( info->variations!=NULL && info->avar_start!=0 )
	parseavar(info,ttf);
    if ( info->variations!=NULL )
	parsegvar(info,ttf);
    if ( info->variations!=NULL && info->cvar_start!=0 && info->cvt_start!=0 )
	parsecvar(info,ttf);
}
