/* glyf.c -- Load and print Glyf outline data
 * Copyright (C) 1996 Li-Da Lho, All right reserved
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "ttf.h"
#include "ttfutil.h"

#ifdef MEMCHECK
#include <dmalloc.h>
#endif

/* 	$Id: glyf.c,v 1.1.1.1 1998/06/05 07:47:52 robert Exp $	 */

#ifndef lint
static char vcid[] = "$Id: glyf.c,v 1.1.1.1 1998/06/05 07:47:52 robert Exp $";
#endif /* lint */

static void ttfLoadSimpleGlyph(FILE *fp,GLYFPtr glyf,ULONG offset);
static void ttfPrintSimpleGlyph(FILE *fp,GLYFPtr glyf);
static void ttfLoadCompositeGlyph(FILE *fp,GLYFPtr glyf,ULONG offset);
static void ttfPrintCompositeGlyph(FILE*fp,GLYFPtr glyf);
static void ttfFreeCompositeGlyph(GLYFPtr glyf);
double fix2dbl(F2Dot14 fixed);

/* Naming convention about GLYF and Glyph:
 * High level functions which are exported should have their name
 * contain GLYF, on the other hand, low level function (or structures)
 * which is not intended to be exported should have their name 
 * contain Glyph
 */
void ttfInitGLYF(TTFontPtr font)
{
    ULONG tag  = 'g' | 'l' << 8 | 'y' << 16 | 'f' << 24;
    TableDirPtr ptd;

    if ((ptd = ttfLookUpTableDir(tag, font)) != NULL)
	{
	    font->glyphOffset = ptd->offset;
	}
}
/* offset: where the specified glyph really starts 
 * callers should compute this themself form the loca tables */
void ttfLoadGLYF(FILE *fp, GLYFPtr glyf, ULONG offset)
{
    if (fseek(fp, offset, SEEK_SET) != 0)
	ttfError("Fseek Failed in ttfLoadGLYF\n");

    glyf->numberOfContours = ttfGetSHORT(fp);
    glyf->xMin = ttfGetFWord(fp);
    glyf->yMin = ttfGetFWord(fp);
    glyf->xMax = ttfGetFWord(fp);
    glyf->yMax = ttfGetFWord(fp);

    offset += sizeof(USHORT) + 4*sizeof(FWord);

    if (glyf->numberOfContours >= 0)
	ttfLoadSimpleGlyph(fp, glyf, offset);
    else 
	ttfLoadCompositeGlyph(fp, glyf, offset); 
}
void ttfPrintGLYF(FILE *fp, GLYFPtr glyf)
{
    fprintf(fp,"\t numberOfContours:\t %d  %s\n", glyf->numberOfContours,
	    glyf->numberOfContours == -1 ? "(Composite)": "");
    fprintf(fp,"\t xMin:\t\t\t %d\n", glyf->xMin);
    fprintf(fp,"\t yMin:\t\t\t %d\n", glyf->yMin);
    fprintf(fp,"\t xMax:\t\t\t %d\n", glyf->xMax);
    fprintf(fp,"\t yMax:\t\t\t %d\n\n", glyf->yMax);
    
    if (glyf->numberOfContours >= 0)
	ttfPrintSimpleGlyph(fp, glyf);
    else
	ttfPrintCompositeGlyph(fp, glyf);
}
void ttfFreeGLYF(GLYFPtr glyf)
{
    if (glyf->numberOfContours < 0)
	ttfFreeCompositeGlyph(glyf);
}
static void ttfLoadSimpleGlyph(FILE *fp, GLYFPtr glyf, ULONG offset)
{
    SHORT nCnts = glyf->numberOfContours;
    USHORT i,nIns,nPts;

    if (fseek(fp, offset, SEEK_SET) != 0)
	ttfError("Fseek Failed in ttfLoadSimpleGlyph\n"); 
    
    if (fread(glyf->endPtsOfContours, sizeof(USHORT), nCnts, fp) != nCnts)
	ttfError("Error when getting endPtsOfContours\n");
#ifndef WORDS_BIGENDIAN
    TwoByteSwap((unsigned char *) glyf->endPtsOfContours,
		nCnts*sizeof(USHORT));
#endif

    if (nCnts != 0)
	nPts = (glyf->endPtsOfContours)[nCnts-1]+1;
    else
	/* there seems to be something wrong with my interpretation of 
	 * NOGLYPH chars, doing this way in case there is a char that has 
	 * zero contour */
	nPts = 0;

    /* how to deal with zero instruction glyf properly ?? */
    glyf->instructionLength = nIns = ttfGetUSHORT(fp);
    if (nIns != 0)
	{
	    if (fread(glyf->instructions, sizeof(BYTE), nIns, fp) != nIns)
		ttfError("Error when getting instructions\n");
	}

    for (i=0;i<nPts;i++)
	{
	    BYTE j,c;
	    if (((glyf->flags)[i] = c =  ttfGetBYTE(fp)) & FLAGS_REPEAT)
		{ /* if this flag should be repeated */
		    j = ttfGetBYTE(fp); /* j times */
		    while (j--) 
			{
			    i++;
			    (glyf->flags)[i] = c;
			    
			}
		}
	}

    for (i=0;i<nPts;i++)
	{
	    BYTE flag;
	    flag = (glyf->flags)[i];
	    
	    if (flag & FLAGS_X_SHORT_VECTOR)
		{ /* if the coordinate is a BYTE */
		    if (flag & FLAGS_X_SAME)
			(glyf->xCoordinates)[i] = (SHORT) ttfGetBYTE(fp);
		    else
			(glyf->xCoordinates)[i] = (SHORT) -ttfGetBYTE(fp);
		}
	    else
		{ /* the coordiante is a SHORT */
		    if (flag & FLAGS_X_SAME)
			(glyf->xCoordinates)[i] = 0;
		    else
			(glyf->xCoordinates)[i] = ttfGetSHORT(fp);
		}
	}
    for (i=0;i<nPts;i++)
	{
	    BYTE flag;
	    flag = (glyf->flags)[i];
	    
	    if (flag & FLAGS_Y_SHORT_VECTOR)
		{ /* if the coordinate is a BYTE */
		    if (flag & FLAGS_Y_SAME)
			(glyf->yCoordinates)[i] = (SHORT) ttfGetBYTE(fp);
		    else
			(glyf->yCoordinates)[i] = (SHORT) -ttfGetBYTE(fp);
		}
	    else
		{ /* the coordiante is a SHORT */
		    if (flag & FLAGS_Y_SAME)
			(glyf->yCoordinates)[i] = 0;
		    else
			(glyf->yCoordinates)[i] = ttfGetSHORT(fp);
		}
	}
}
static void ttfPrintSimpleGlyph(FILE *fp, GLYFPtr glyf)
{
    USHORT i, nPts, nCnts;
    SHORT x=0, y=0;

    nPts = (glyf->endPtsOfContours)[glyf->numberOfContours-1]+1;
    nCnts = glyf->numberOfContours;

    fprintf(fp,"\t EndPoints\n");
    fprintf(fp,"\t ---------\n");
    for (i=0;i<nCnts;i++)
	fprintf(fp,"\t  %d: %2d\n",i,(glyf->endPtsOfContours)[i]);
    fprintf(fp,"\n");

    fprintf(fp,"\t Length of Instructions: %2d\n\n",glyf->instructionLength);
    ttfPrintInstructions(fp,glyf->instructions);
    
    fprintf(fp,"\t Flags\n");
    fprintf(fp,"\t -----\n");
    for (i=0;i<nPts;i++)
	{
	    BYTE flag;
	    char buf[80];

	    flag =  (glyf->flags)[i];
	    
	    if (flag & FLAGS_Y_SAME)
		sprintf(buf,"YDual  ");
	    else
		sprintf(buf,"       ");
	    if (flag & FLAGS_X_SAME)
		strcat(buf,"XDual   ");
	    else
		strcat(buf,"        ");
	    if (flag & FLAGS_REPEAT)
		strcat(buf,"Repeat  ");
	    else 
		strcat(buf,"        ");
	    if (flag & FLAGS_Y_SHORT_VECTOR)
		strcat(buf,"Y-Short ");
	    else
		strcat(buf,"        ");
	    if (flag & FLAGS_X_SHORT_VECTOR)
		strcat(buf,"X-Short ");
	    else
		strcat(buf,"        ");
	    if (flag & FLAGS_ON_CURVE)
		strcat(buf,"On \n");
	    else
		strcat(buf,"Off\n");
	    		    
	    fprintf(fp,"\t %2d: %s",i,buf);
	}
    fprintf(fp,"\n");
    fprintf(fp,"\t Coordinates\n");
    fprintf(fp,"\t -----------\n");
    for (i=0;i<nPts;i++)
	{
	    x += (glyf->xCoordinates)[i];
	    y += (glyf->yCoordinates)[i];
	    fprintf(fp,"\t %2d Rel ( %6d, %6d) -> Abs ( %6d, %6d)\n", i,
		    (glyf->xCoordinates)[i], (glyf->yCoordinates)[i], x, y);
	}
    fprintf(fp,"\n");
}

static void ttfLoadCompositeGlyph(FILE *fp, GLYFPtr glyf, ULONG offset)
{
    USHORT nIns,flags;   
    Component *cur;

    if (fseek(fp, offset, SEEK_SET) != 0)
	ttfError("Fseek Failed in ttfLoadCompositeGlyph\n"); 
    
    if((glyf->comp = cur = (Component *) calloc(1, sizeof(Component))) == NULL)
	ttfError("Out Of Memory\n");
    cur->previous = NULL; /* beginning of a linked list */

    do {
   	cur->flags = flags = ttfGetUSHORT(fp);
	cur->glyphIndex = ttfGetUSHORT(fp);
	if (flags & ARG_1_AND_2_ARE_WORDS)
	    {
		(cur->data).args[0] = ttfGetSHORT(fp);
		(cur->data).args[1] = ttfGetSHORT(fp);
	    }
	else
	    (cur->data).args[0] = ttfGetUSHORT(fp);

	if (flags & WE_HAVE_A_SCALE)
	    {
		(cur->data).transform.scale = ttfGetF2Dot14(fp);
	    }
	else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
	    {
		(cur->data).transform.vector.xscale = ttfGetF2Dot14(fp);
		(cur->data).transform.vector.yscale = ttfGetF2Dot14(fp);
	    }
	else if (flags & WE_HAVE_A_TWO_BY_TWO)
	    {
		(cur->data).transform.tensor.xscale  = ttfGetF2Dot14(fp);
	        (cur->data).transform.tensor.scale01 = ttfGetF2Dot14(fp);
		(cur->data).transform.tensor.scale10 = ttfGetF2Dot14(fp);
		(cur->data).transform.tensor.yscale  = ttfGetF2Dot14(fp);
	    }	    
	/* allocate next component */
	if ((cur->next = (Component *) calloc(1, sizeof(Component))) == NULL)
	    ttfError("Out Of Memory\n");

	cur->next->previous = cur;
	cur = cur->next; /* move to next component */
    } while (flags & MORE_COMPONENT);
    cur->next = NULL;  /* end of the linked list */

    if (flags & WE_HAVE_INSTRUCTIONS)
	{
	    glyf->instructionLength = nIns = ttfGetUSHORT(fp);
	    if (fread(glyf->instructions, sizeof(BYTE), nIns, fp) != nIns)
		ttfError("Error when loading instructions\n");
	}
    else
	{
	    glyf->instructionLength = 0;
	}
}
static void ttfPrintCompositeGlyph(FILE *fp, GLYFPtr glyf)
{
    int i = 0;
    char buf[80];
    USHORT flags;
    Component *cur;

    cur = glyf->comp;
    
    do {
	flags = cur->flags;
	fprintf(fp, "\t %d: Flags:\t 0x%x\n", i, flags);
	fprintf(fp, "\t    Glyf Index:\t %d\n", cur->glyphIndex);
	if (flags & ARGS_ARE_XY_VALUES)
	    {
		if (flags & ARG_1_AND_2_ARE_WORDS)
		    {
			fprintf(fp, "\t    X WOffset:\t %d\n", (cur->data).args[0]);
			fprintf(fp, "\t    Y WOffset:\t %d\n", (cur->data).args[1]);
		    }
		else
		    {
			fprintf(fp, "\t    X BOffset:\t %d\n", 
				(signed char) ((cur->data).args[0] >> 8 & 0xff));
			fprintf(fp, "\t    Y BOffset:\t %d\n",
				(signed char) ((cur->data).args[0] & 0xff));
		    }
	    }
	else
	    {
		/* what the hell are the "patch points" ?? */ 
	    }

	if (flags & WE_HAVE_A_SCALE)
	    {
		fprintf(fp, "\t    X,Y Scale:\t %f\n",
			fix2dbl((cur->data).transform.scale));
	    }
	else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
	    {
		fprintf(fp, "\t    X Scale:\t %f\n", 
			fix2dbl((cur->data).transform.vector.xscale)); 
		fprintf(fp, "\t    Y Scale:\t %f\n", 
			fix2dbl((cur->data).transform.vector.yscale)); 
	    }
	else if (flags & WE_HAVE_A_TWO_BY_TWO)
	    {
		fprintf(fp, "\t    X Scale:\t %f\n", 
			fix2dbl((cur->data).transform.tensor.xscale)); 
		fprintf(fp, "\t    X,Y Scale:\t %f\n", 
			fix2dbl((cur->data).transform.tensor.scale01)); 
		fprintf(fp, "\t    Y,X Scale:\t %f\n", 
			fix2dbl((cur->data).transform.tensor.scale10)); 
		fprintf(fp, "\t    Y Scale:\t %f\n", 
			fix2dbl((cur->data).transform.tensor.yscale)); 
	    }	    

	if (flags & ROUND_XY_TO_GRID)
	    sprintf(buf, "Round X,Y to Grid   ");
	else
	    sprintf(buf, "                    ");

	if (flags & NO_OVERLAP)
	    strcat(buf, "NO Overlap   ");
	else
	    strcat(buf, "             ");

	if (flags & USE_MY_METRICS)
	    strcat(buf, "Use My Metrics   ");
	else
	    strcat(buf, "                 ");

	fprintf(fp, "\t    Others:\t %s\n\n", buf);

	i++;
	cur = cur->next;
    } while (cur->next != NULL);
    
    fprintf(fp, "\n");

    if (flags & WE_HAVE_INSTRUCTIONS)
	{
	    fprintf(fp,"\t Length of Instructions: %2d\n\n",
		    glyf->instructionLength);
	    ttfPrintInstructions(fp, glyf->instructions);
	}
}
static void ttfFreeCompositeGlyph(GLYFPtr glyf)
{
    Component *cur,*next;
    
    cur = glyf->comp;

    do {
	next = cur->next;
	free(cur);
	cur = next;
    } while (cur != NULL);
}

/* what I want:
 * outter procedures should not have any ideas about where the glyph starts,
 *
 * it just provide the TTFont structure and the character code or the index
 * of that glyph. Procedures below should do:
 * 1. look up where the glyph data is.
 * 2. provided some glyph cache mechanism for the reason that 
 *    a. it is not necessary to load all glyph into memory, especially for 
 *    eastern languages.
 *    b. if a glyph data has been loaded previously, it is not necessary to 
 *    load it again.
 *    c. malloc and free are SLOW !!
 */
/* Load a glyph by glyph index */
GLYFPtr ttfLoadGlyphIndex(TTFont *font, USHORT idx)
{
    ULONG pos;
    GLYFPtr glyf;
    
    /* compute the actual place where the glyph stored */
    pos = font->glyphOffset + ttfLookUpGlyfLOCA(font->loca, idx);
    glyf = ttfLoadGlyphCached(font, pos);

    return glyf;
}
/* Load a glyph by character code in the current encoding scheme */
GLYFPtr ttfLoadGlyphCode(TTFont *font,USHORT cc)
{
    USHORT index;
    
    index = ttfLookUpCMAP(font->encoding,cc);
    return ttfLoadGlyphIndex(font,index);
}

double fix2dbl(F2Dot14 fixed)
{
    double mantissa, fraction;

    mantissa = (double) (fixed >> 14);

    fraction = (double) (double)(fixed & 0x3fff) / 16384.0;

    return mantissa+fraction;
}
