/* $Id: parsettfvar.c 4532 2015-12-22 13:18:53Z mskala $ */
/* Copyright (C) 2000-2012  George Williams
 * Copyright (C) 2015  Matthew Skala
 *
 * 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 "fontanvil.h"
#include <chardata.h>
#include <utype.h>
#include <ustring.h>
#include <math.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,AFILE *ttf) {
   int data_off, axis_count, instance_count, cnt;
   int i, j;

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

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

   afseek(ttf, info->fvar_start+data_off, SEEK_SET);
   for (i=0; i<axis_count; ++i) {
      struct taxis *a=&info->variations->axes[i];

      a->tag=aget_int32_be(ttf);
      a->min=aget_int32_be(ttf)/65536.0;
      a->def=aget_int32_be(ttf)/65536.0;
      a->max=aget_int32_be(ttf)/65536.0;
      /* flags=*/ aget_uint16_be(ttf);
      a->nameid=aget_uint16_be(ttf);
   }
   for (i=0; i<instance_count; ++i) {
      struct tinstance *ti=&info->variations->instances[i];

      ti->nameid=aget_uint16_be(ttf);
      /* flags=*/ aget_uint16_be(ttf);
      for (j=0; j<axis_count; ++j)
	 ti->coords[j]=aget_int32_be(ttf)/65536.0;
   }
}

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

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

   afseek(ttf, info->avar_start, SEEK_SET);
   if (aget_int32_be(ttf) != 0x00010000) {	/* I only understand version 1 */
      VariationFree(info);
      return;
   }
   axis_count=aget_int32_be(ttf);
   if (axis_count != info->variations->axis_count) {
      ErrorMsg(2,"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=aget_uint16_be(ttf);
      if (pair_count != 0) {
	 info->variations->axes[i].mapfrom =
	    malloc(pair_count * sizeof(double));
	 info->variations->axes[i].mapto=malloc(pair_count * sizeof(double));
	 for (j=0; j<pair_count; ++j) {
	    info->variations->axes[i].mapfrom[j]=aget_uint16_be(ttf)/16384.0;
	    info->variations->axes[i].mapto[j]=aget_uint16_be(ttf)/16384.0;
	 }
      }
   }
   if (aftell(ttf)-info->avar_start>info->avar_len) {
      ErrorMsg(2,"Hmm, the 'avar' table is too long.\n");
      VariationFree(info);
      return;
   }
}

static SplineChar **InfoCopyGlyphs(struct ttfinfo *info) {
   SplineChar **chars=malloc(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, 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(AFILE *ttf,int n) {
   int *deltas;
   int runcnt, i, j;

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

   i=0;
   while (i<n) {
      runcnt=agetc(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_t) aget_uint16_be(ttf);
      } else {
	 /* runcnt signed bytes from the stack */
	 for (j=0; j <= (runcnt&0x3f) && i<n; ++j)
	    deltas[i++]=(int8_t) agetc(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(AFILE *ttf) {
   int *points;
   int n, runcnt, i, j, first;

   n=agetc(ttf);
   if (n==EOF)
      n=0;
   if (n&0x80)
      n=agetc(ttf)|((n&0x7f) << 8);
   points=malloc((n+1)*sizeof(int));
   if (n==0)
      points[0]=ALL_POINTS;
   else {
      i=0;
      while (i<n) {
	 runcnt=agetc(ttf);
	 if (runcnt&0x80) {
	    runcnt=(runcnt&0x7f);
	    points[i++]=first=aget_uint16_be(ttf);
	    /* first point not included in runcount */
	    for (j=0; j<runcnt && i<n; ++j)
	       points[i++]=(first += aget_uint16_be(ttf));
	 } else {
	    points[i++]=first=agetc(ttf);
	    for (j=0; j<runcnt && i<n; ++j)
	       points[i++]=(first += agetc(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;
      SCReinstantiateRefChar(sc, ref, ly_fore);
   }
}

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];
	       SCReinstantiateRefChar(sc, ref, ly_fore);
	    }
	 }
      } 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];
		  SCReinstantiateRefChar(sc, ref, ly_fore);
	       }
	       ++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, AFILE *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) {
      ErrorMsg(2,"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)
	 ErrorMsg(2,"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,AFILE *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_t tupoff, dataoff, *gvars;
   struct variations *v=info->variations;
   int warned=false;

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

   gvars=malloc((gc+1)*sizeof(uint32_t));
   if (gvarflags&1) {		/* 32 bit data */
      for (i=0; i <= gc; ++i)
	 gvars[i]=aget_int32_be(ttf)+dataoff;
   } else {
      for (i=0; i <= gc; ++i)
	 gvars[i]=aget_uint16_be(ttf)*2+dataoff;	/* Undocumented *2 */
   }

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

   for (g=0; g<gc; ++g)
      if (gvars[g] != gvars[g+1]) {
	 int tc;
	 uint32_t datoff;
	 int *sharedpoints=NULL;

	 afseek(ttf, gvars[g], SEEK_SET);
	 tc=aget_uint16_be(ttf);
	 datoff=gvars[g]+aget_uint16_be(ttf);
	 if (tc&0x8000) {
	    uint32_t here=aftell(ttf);

	    afseek(ttf, datoff, SEEK_SET);
	    sharedpoints=readpackedpoints(ttf);
	    datoff=aftell(ttf);
	    afseek(ttf, here, SEEK_SET);
	 }
	 for (i=0; i<(tc&0xfff); ++i) {
	    int tupleDataSize, tupleIndex;

	    tupleDataSize=aget_uint16_be(ttf);
	    tupleIndex=aget_uint16_be(ttf);
	    if (tupleIndex&0xc000) {
	       if (!warned)
		  ErrorMsg(2,"Warning: Glyph %d contains either private or intermediate tuple data.\n FontAnvil supports neither.\n",
			   g);
	       warned=true;
	       if (tupleIndex&0x8000)
		  afseek(ttf, 2*axiscount, SEEK_CUR);
	       if (tupleIndex&0x4000)
		  afseek(ttf, 4*axiscount, SEEK_CUR);
	    } else {
	       int *localpoints=NULL;
	       uint32_t here=aftell(ttf);

	       afseek(ttf, datoff, SEEK_SET);
	       if (tupleIndex&0x2000)
		  localpoints=readpackedpoints(ttf);
	       VaryGlyphs(info, tupleIndex&0xfff, g,
			  (tupleIndex&0x2000)?localpoints:sharedpoints,
			  ttf);
	       free(localpoints);
	       afseek(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=malloc(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,
		     AFILE *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_t);
   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)
	 ErrorMsg(2,"Incorrect number of deltas in cvt\n");
      warned=true;
   }
   free(deltas);
}

static void parsecvar(struct ttfinfo *info,AFILE *ttf) {
   struct ttf_table *cvt;
   int tuplecount;
   uint32_t 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;

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

   tuplecount=aget_uint16_be(ttf);
   offset=info->cvar_start+aget_uint16_be(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_t here=aftell(ttf);

      afseek(ttf, offset, SEEK_SET);
      sharedpoints=readpackedpoints(ttf);
      offset=aftell(ttf);
      afseek(ttf, here, SEEK_SET);
   }
   for (i=0; i<(tuplecount&0xfff); ++i) {
      int tupleDataSize, tupleIndex;

      tupleDataSize=aget_uint16_be(ttf);
      tupleIndex=aget_uint16_be(ttf);
      /* there is no provision here for a global tuple coordinate section */
      /*  so John says there are no tuple indices. Just embedded tuples */
      if (tupleIndex&0x4000) {
	 if (!warned)
	    ErrorMsg(2,"Warning: 'cvar' contains intermediate tuple data.\n FontAnvil doesn't support this.\n");
	 warned=true;
	 if (tupleIndex&0x8000)
	    afseek(ttf, 2*info->variations->axis_count, SEEK_CUR);
	 if (tupleIndex&0x4000)
	    afseek(ttf, 4*info->variations->axis_count, SEEK_CUR);
      } else {
	 int *localpoints=NULL;
	 uint32_t here;
	 int j, k, ti;

	 ti=tupleIndex&0xfff;
	 if (tupleIndex&0x8000) {
	    double *coords =
	       malloc(info->variations->axis_count * sizeof(double));
	    for (j=0; j<info->variations->axis_count; ++j)
	       coords[j]=((int16_t) aget_uint16_be(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=aftell(ttf);
	    afseek(ttf, offset, SEEK_SET);
	    if (tupleIndex&0x2000)
	       localpoints=readpackedpoints(ttf);
	    VaryCvts(info, ti,
		     (tupleIndex&0x2000)?localpoints:sharedpoints, ttf,
		     cvt);
	    free(localpoints);
	    afseek(ttf, here, SEEK_SET);
	 }
      }
      offset += tupleDataSize;
   }
   free(sharedpoints);
}

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

   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);
}
