/*	Willows Software, Inc. - Feb 1997	*/
/*	"@(#)Access.c	1.1 :/users/sccs/src/win/mac/s.Access.c 2/6/97 17:41:01" */

#include <Memory.h>
#include <ToolUtils.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <stdio.h>
#include <FixMath.h>
#include <Resources.h>

#include "sfnt.h"
#include "Access.h"
#include "access library.h"

long	PostFontError(long error);

/********************* Private functions *****************/

long PostFontError(long error)
{
#ifdef	DEBUG
	if (error)
		DebugStr("\pPostFontError()");
#endif	//	DEBUG
	return( fNoError );
}

static short GetFontState(Handle sfnt)
{
	short state;

	LoadResource(sfnt);
	state = HGetState(sfnt);
	HNoPurge(sfnt);
	return state;
}

static void SetFontState(Handle sfnt, short state)
{
	HSetState(sfnt, state);
}

/*	Macro and glue routine to make handle growing easier.
*/
#define SHS(h, s)	NiceSetHandleSize((Handle)h, s)

static short NiceSetHandleSize(Handle h, long size)
{
	SetHandleSize(h, size);
	return MemError();
}

/*	Some private calls to return pointers to various sfnt pieces.
*/
static void* GetSfntTablePtr(Handle sfnt, fontTableTag tag)
{
	FontTableInfo info;

	if (GetSfntTableInfo(sfnt, tag, &info))
		return 0;
	return *sfnt + info.offset;
}

static void* GetSfntGlyphPtr(Handle sfnt, long glyphIndex, long* length)
{
	void* loc;
	char* glyphStart;
	sfnt_FontHeader* head;

	if (!((head = GetSfntTablePtr(sfnt, tag_FontHeader)) &&
		(loc = GetSfntTablePtr(sfnt, tag_IndexToLoc)) &&
		(glyphStart = GetSfntTablePtr(sfnt, tag_GlyphData))))
		return 0;

	if (head->indexToLocFormat == SHORT_INDEX_TO_LOC_FORMAT)
	{	unsigned short* offset = (unsigned short*)loc + glyphIndex;
		*length = (long)(offset[1] - *offset) << 1;
		return glyphStart + ((long)*offset << 1);
	}
	else
	{	long* offset = (long*)loc + glyphIndex;
		*length = offset[1] - *offset;
		return glyphStart + *offset;
	}
}

/********************** Public Font Tool Calls *******************/

long CountSfntTables(Handle sfnt)
{
	short state = GetFontState(sfnt);
	short tables = ((sfnt_OffsetTable*)*sfnt)->numOffsets;
	SetFontState(sfnt, state);
	return tables;
}

fontTableTag GetSfntTag(Handle sfnt, long index)
{
	fontTableTag tag = 0;
	short state = GetFontState(sfnt);
	if (index < ((sfnt_OffsetTable*)*sfnt)->numOffsets)
		tag = ((sfnt_OffsetTable*)*sfnt)->table[index].tableTag;
	SetFontState(sfnt, state);
	return tag;
}

FontError GetSfntTableInfo(Handle sfnt, fontTableTag tag, FontTableInfo* fTable)
{
	short state = GetFontState(sfnt);
	register sfnt_OffsetTable* dir = (sfnt_OffsetTable*)*sfnt;
	register sfnt_DirectoryEntry* table = dir->table;
	register short count = dir->numOffsets;
	FontError error = fNoError;

	for (; --count >= 0; table++)
		if (table->tableTag == tag)
		{	fTable->offset = table->offset;
			fTable->length = table->length;
			fTable->checkSum = table->checkSum;
			break;
		}
	if (count < 0)
		error = PostFontError(fTableNotFound);
	SetFontState(sfnt, state);
	return error;
}

FontError GetSfntTable(Handle sfnt, fontTableTag tag, Handle userCopy)
{
	FontTableInfo info;
	FontError error;

	if (error = GetSfntTableInfo(sfnt, tag, &info))
		return error;

	SetHandleSize(userCopy, info.length);
	if (MemError())
		return PostFontError(fMemoryError);
	
	{	short state = GetFontState(sfnt);
		BlockMove(*sfnt + info.offset, *userCopy, info.length);
		SetFontState(sfnt, state);
	}
	return fNoError;
}

FontError GetSfntNameString(Handle sfnt, long nameID, Str255 name)
{
	register sfnt_NamingTable* nameT;
	register sfnt_NameRecord* nameR;
	register short count;
	short state = GetFontState(sfnt);

	if (!(nameT = GetSfntTablePtr(sfnt, tag_NamingTable)))
	{	SetFontState(sfnt, state);
		return fTableNotFound;
	}

	nameR = (sfnt_NameRecord*)(nameT + 1);
	count = nameT->count;

	/*	This section can be changed to get font names in other languages
	*/
	for (; --count >= 0; nameR++)
		if (nameR->platformID == plat_Macintosh &&
			nameR->specificID == smRoman     &&
			nameR->languageID == lang_English   &&
			nameR->nameID == nameID)
		{
			name[0] = nameR->length <= 255 ? nameR->length : 255;
			BlockMove((Ptr)nameT + nameT->stringOffset + nameR->offset, (Ptr)&name[1], name[0]);
			SetFontState(sfnt, state);
			return fNoError;
		}

	name[0] = 0;
	SetFontState(sfnt, state);
	return fNameNotFound;
}

/*	Returns the glyph index for the given character code, or
 *	an error code.
 */
long GetCharGlyphIndex(Handle sfnt, unsigned short charCode)
{
	sfnt_char2IndexDirectory* table;
	short i;
	long mapOffset = 0;
	long glyphIndex = 0;		/* missing character glyph */
	short state = GetFontState(sfnt);

	if (!(table = GetSfntTablePtr(sfnt, tag_CharToIndexMap)))
	{	SetFontState(sfnt, state);
		return PostFontError(fTableNotFound);
	}

	{	register sfnt_platformEntry* plat = table->platform;
	
		/* You can change this section to look for other scripts
		*/
		for ( i = table->numTables - 1; i >= 0; --i )
		{	if ( plat->platformID == plat_Macintosh && plat->specificID == smRoman)
			{	mapOffset = plat->offset;
				break;
			}
			++plat;
		}
	}
	
	if (mapOffset)
	{	sfnt_mappingTable* mapping = (sfnt_mappingTable*)((char*)table + mapOffset);
		
		switch ( mapping->format ) {
		case 0:
			{	Byte* glyphs = (Byte*)(mapping + 1);
				glyphIndex = glyphs[charCode];
			}
			break;
		case 6:
			{	short* glyphs = (short*)(mapping + 1);
				short first = *glyphs++;
				short count = *glyphs++;
				charCode -= first;
				if (charCode < count)
					glyphIndex = glyphs[charCode];
			}
			break;
		default:
			glyphIndex = PostFontError(fUnimplemented);
		}
	}
	else
		glyphIndex = PostFontError(fCMapNotFound);

	SetFontState(sfnt, state);
	return glyphIndex;
}

long CountSfntGlyphs(Handle sfnt)
{
	sfnt_maxProfileTable* profile;
	short glyphs;

	if (!(profile = GetSfntTablePtr(sfnt, tag_MaxProfile)))
		return PostFontError(fTableNotFound);

	{	short state = GetFontState(sfnt);
		glyphs = profile->numGlyphs;
		SetFontState(sfnt, state);
	}
	return glyphs;
}

#define GetUnsignedByte( p ) ((Byte)(*p++))

FontError GetGlyphOutline(Handle sfnt, long glyphIndex, GlyphOutline* gOutline, Matrix xform)
{
	short upem, state, sideBearing, adjustToLsb;
	short* glyph;
	sfnt_FontHeader* head;
	sfnt_HorizontalHeader* hhea;
	sfnt_HorizontalMetrics* hori;
	long length;
	FontError error = fNoError;

	state = GetFontState( sfnt );
	HLock( sfnt );

	if (!((head = GetSfntTablePtr(sfnt, tag_FontHeader)) &&
		(hhea = GetSfntTablePtr(sfnt, tag_HoriHeader)) &&
		(hori = GetSfntTablePtr(sfnt, tag_HorizontalMetrics))))
	{	error = fTableNotFound;
		goto EXIT;
	}

	upem = head->unitsPerEm;

	{	long longMetrics = hhea->numberLongMetrics;
		if ( glyphIndex < longMetrics )
		{	gOutline->advance.x = FixRatio( hori[glyphIndex].advance, upem );
			sideBearing = hori[glyphIndex].sideBearing;
		}
		else
		{	short *lsb = (short *)&hori[longMetrics]; /* first entry after[AW,LSB] array */
			
			gOutline->advance.x = FixRatio( hori[longMetrics-1].advance, upem );
			sideBearing = hori[glyphIndex - longMetrics].sideBearing;
		}
		gOutline->advance.y = 0;
	}

	gOutline->origin.x = gOutline->origin.y = 0;

	if (!(glyph = GetSfntGlyphPtr(sfnt, glyphIndex, &length)))
	{	error = fGlyphNotFound;
		goto EXIT;
	}

	if (length == 0)
	{	gOutline->contourCount = gOutline->pointCount = 0;
		goto EXIT;
	}

	gOutline->contourCount = *glyph++;
	adjustToLsb = *glyph - sideBearing;		/* xmin - lsb */
	glyph += 4;						/* skip bounds rect */

	if (gOutline->contourCount == 0)
		gOutline->pointCount = 0;
	else if (gOutline->contourCount == -1)
	{	short flags, index, newMatrix;

		gOutline->contourCount = gOutline->pointCount = 0;
		SHS(gOutline->endPoints, 0);
		SHS(gOutline->onCurve, 0);
		SHS(gOutline->x, 0);
		SHS(gOutline->y, 0);
		do
		{	Matrix compXform;
			short arg1, arg2;
			
			flags = *glyph++;
			index = *glyph++;
			newMatrix = false;

			if ( flags & ARG_1_AND_2_ARE_WORDS )
			{	arg1 = *glyph++;
				arg2 = *glyph++;
			}
			else
			{	char* byteP = (char*)glyph;
				if ( flags & ARGS_ARE_XY_VALUES )
				{	/* offsets are signed */
					arg1 = *byteP++;
					arg2 = *byteP;
				}
				else
				{	/* anchor points are unsigned */
					arg1 = (unsigned char)*byteP++;
					arg2 = (unsigned char)*byteP;
				}
				++glyph;
			}
#if IMPLEMENT_SCALED_COMPONENTS
			if ( flags & (WE_HAVE_A_SCALE | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO) )
			{	Matrix subXform;
				MakeIdentityMatrix(subXform);
				if ( flags & WE_HAVE_A_TWO_BY_TWO )
				{	compXform[0][0] = (Fixed)*glyph++ << 2;
					compXform[0][1] = (Fixed)*glyph++ << 2;
					compXform[1][0] = (Fixed)*glyph++ << 2;
					compXform[1][1] = (Fixed)*glyph++ << 2;
				}
				else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE )
				{	compXform[0][0] = (Fixed)*glyph++ << 2;
					compXform[1][1] = (Fixed)*glyph++ << 2;
				}
				else
					compXform[0][0] = compXform[1][1] = (Fixed)*glyph++ << 2;
				PostMulMatrix(compXform, xform);
				newMatrix = true;
			}
#endif			
			{	GlyphOutline out;
				InitGlyphOutline(&out);
				GetGlyphOutline(sfnt, index, &out, newMatrix ? compXform : xform);
				{	Fixed dx, dy;
					if (flags & ARGS_ARE_XY_VALUES)
					{	dx = FixRatio(arg1, upem);
						dy = -FixRatio(arg2, upem);
					}
					else
					{	dx = (*gOutline->x)[arg1] - (*out.x)[arg2];
						dy = (*gOutline->y)[arg1] - (*out.y)[arg2];
					}
					MoveGlyphOutline(&out, dx, dy);
				}
				AppendGlyphOutline(gOutline, &out);
				KillGlyphOutline(&out);
			}
		} while (flags & MORE_COMPONENTS);
	}
	else if (gOutline->contourCount > 0)
	{	/*	Load in the end points.
		 */
		{	long size = gOutline->contourCount * sizeof(short);

			if (SHS(gOutline->endPoints, size))
			{	error = fMemoryError;
				goto EXIT;
			}
			BlockMove( (Ptr)glyph, (Ptr)*gOutline->endPoints, size );
			glyph += gOutline->contourCount;
		}

		gOutline->pointCount = (*gOutline->endPoints)[gOutline->contourCount - 1] + 1;
		if (SHS(gOutline->onCurve, gOutline->pointCount * sizeof(char)))
		{	error = fMemoryError;
			goto EXIT;
		}
		if (SHS(gOutline->x, gOutline->pointCount * sizeof(Fixed)))
		{	error = fMemoryError;
			goto EXIT;
		}
		if (SHS(gOutline->y, gOutline->pointCount * sizeof(Fixed)))
		{	error = fMemoryError;
			goto EXIT;
		}

		/*	Skip the word for instruction count + the instructions.
		 *	Then load in the onCurve bytes.
		 */
		{	Byte* p = (Byte*)glyph + sizeof(short) + *glyph;
			Byte* onCurve = *gOutline->onCurve;
			Byte* stop = onCurve + gOutline->pointCount;
			Byte flag;

			while (onCurve < stop)
			{	*onCurve++ = flag = GetUnsignedByte( p );
				if ( flag & REPEAT_FLAGS ) {
					short count = GetUnsignedByte( p );
					for (--count; count >= 0; --count)
						*onCurve++ = flag;
				}
			}
			/*	Lets do X
			*/
			{	short coord = adjustToLsb;
				Fixed* x = *gOutline->x;

				onCurve = *gOutline->onCurve;
				while (onCurve < stop)
				{	if ( (flag = *onCurve++) & XSHORT ) {
						if ( flag & SHORT_X_IS_POS )
							coord += GetUnsignedByte( p );
						else
							coord -= GetUnsignedByte( p );
					}
					else if ( !(flag & NEXT_X_IS_ZERO) )
					{	coord += (short)(*p++) << 8;
						coord += (Byte)*p++;
					}
					*x++ = FixRatio( coord, upem );
				}
			}
			/*	Lets do Y
			*/
			{	short coord = 0;
				Fixed* y = *gOutline->y;

				onCurve = *gOutline->onCurve;
				while (onCurve < stop)
				{	if ( (flag = *onCurve) & YSHORT ) {
						if ( flag & SHORT_Y_IS_POS )
							coord += GetUnsignedByte( p );
						else
							coord -= GetUnsignedByte( p );
					}
					else if ( !(flag & NEXT_Y_IS_ZERO) )
					{	coord += (short)(*p++) << 8;
						coord += (Byte)*p++;
					}
					*y++ = -FixRatio( coord, upem );
					
					/*	Filter off the extra bits
					*/
					*onCurve++ = flag & ONCURVE;
				}
			}
		}
	}
	else
		error = fUnimplemented;
EXIT:
	SetFontState( sfnt, state );

	return PostFontError(error);
}

