/* type1lib.c

    Author - Amish S. Dave

This file contains the entry points of the Amiga shared library
It calls functions in type1interface.c

*/

/**
 * INCLUDES
 **/
#ifndef T1GST
#include "global.h"
#endif
#include <proto/exec.h>
#include <proto/utility.h>
#include <diskfont/glyph.h>
#include <diskfont/diskfonttag.h>
#include <diskfont/oterrors.h>	// TetiSoft
#include <clib/alib_protos.h>	// TetiSoft
#include <stdlib.h>
#include <string.h>

#define OT_Spec1_PFABFileName	(OT_Spec1  | OT_Indirect)
#define OT_Spec2_AFMFileName	(OT_Level1 | OT_Indirect | 0x102)
#define OT_Spec3_FontName	(OT_Level1 | 0x103)
#define OT_Spec4_ItalicAngle	(OT_Level1 | 0x104)	// TetiSoft
#define OT_Spec5_Weight		(OT_Level1 | 0x105)	// TetiSoft
#define OT_Spec6_OwnEncoding	(OT_Level1 | 0x106)	// TetiSoft
#define OT_Spec7_UnicodeArray	(OT_Level1 | 0x107)	// TetiSoft

static const char EngineName[] = "type1";

// TetiSoft: With math=standard we need to replace __except()
// (Only called by sqrt() with negative param which can't happen)
#ifdef _MATHEXCEPT
double __except(int type, const char *name, double arg1, double arg2, double retval)
{
#ifdef DEBUG
	kprintf("math exception type %ld from func %s\n", type, name);
#endif
	return retval;
}
#endif

// TetiSoft: With math=standard we need to replace _CXFERR()
#ifdef _MATHCXFERR
void __stdargs _CXFERR(int code)
{
#ifdef DEBUG	// TetiSoft
	char *errtype;

	switch (code)
	{
	case _FPEUND:
		errtype = "underflow";
		break;
	case _FPEOVF:
		errtype = "overflow";
		break;
	case _FPEZDV:
		errtype = "divide by zero";
		break;
	case _FPENAN:
		errtype = "not a number";
		break;
	case _FPECOM:
		errtype = "not comparable";
		break;
	default:
		errtype = "unknown";
	}
	kprintf("math error %ld (%s)\n", code, errtype);
#endif

// TetiSoft: global _FPERR is defined in _cxfpe.c in sc.lib,
// which also contains _CXFPE() which would import us _errno.
// To save space and to avoid non-allowed globals (libinit.o), we don't use this:
//	_FPERR = code;

}
#endif	// _MATHCXFERR

/**
 * DEFINES
 **/
#define REG(x) register __## x


/**
 * PROTOTYPES
 **/
struct T1GlyphEngine * __saveds __asm OpenEngine(REG(a6) struct Library *Type1Base);
void __saveds __asm CloseEngine(REG(a0) struct T1GlyphEngine *engine, REG(a6) struct Library *Type1Base);
ULONG __saveds __asm ObtainInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base);
ULONG __saveds __asm SetInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base);
ULONG __saveds __asm ReleaseInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base);

static void clearfontreq(struct FontRequest *fontreq_p, Bool ClearAll);	// TetiSoft: for FinalWriter compatibility
static int BuildWidthList(struct T1GlyphEngine *engine, ULONG glyph1, ULONG glyph2, struct MinList **ListPtrPtr, struct Library *Type1Base);	// TetiSoft
static void FreeWidthList(struct T1GlyphEngine *engine, struct MinList *ListPtr);	// TetiSoft



/**
 * OpenEngine
 **/
struct T1GlyphEngine * __saveds __asm OpenEngine(REG(a6) struct Library *Type1Base)
{
	struct T1GlyphEngine *ge;

	ge = AllocVec(sizeof(struct T1GlyphEngine), MEMF_PUBLIC|MEMF_CLEAR);
	if (!ge)
		return NULL;

	ge->GE.gle_Library = Type1Base;
	ge->GE.gle_Name = (char *)EngineName;

	clearfontreq(&ge->fontreq, TRUE);

	if (!OpenType1Engine(ge))
	{
		FreeVec(ge);
		return NULL;
	}

	ge->SpaceID = 1;

	ge->regions_currentworkarea = ge->regions_workedge;
	ge->regions_currentsize = MAXEDGE;

#ifdef PROCESSHINTS
	ge->ProcessHints = PROCESSHINTS;
#else
	ge->ProcessHints = TRUE;
#endif
#ifdef CONTINUITYVAL
	ge->Continuity = CONTINUITYVAL;
#else
	ge->Continuity = 2;
#endif

// TetiSoft: Added debugging (Use Sushi or a serial terminal)
#ifdef DEBUG
	ge->PathDebug = ge->LineDebug = ge->RegionDebug = ge->FontDebug = ge->HintDebug = ge->OffPageDebug = DEBUG;
#ifdef TRACECALLS
	ge->MustTraceCalls = TRACECALLS;
#endif
#ifdef LINEDEBUG
	ge->LineDebug = LINEDEBUG;
#endif
#ifdef REGIONDEBUG
	ge->RegionDebug = REGIONDEBUG;
#endif
#ifdef PATHDEBUG
	ge->PathDebug = PATHDEBUG;
#endif
#ifdef FONTDEBUG
	ge->FontDebug = FONTDEBUG;
#endif
#ifdef HINTDEBUG
	ge->HintDebug = HINTDEBUG;
#endif
#ifdef OFFPAGEDEBUG
	ge->OffPageDebug = OFFPAGEDEBUG;
#endif
#endif // DEBUG

	return ge;
}


/**
 * CloseEngine
 **/
void __saveds __asm CloseEngine(REG(a0) struct T1GlyphEngine *engine, REG(a6) struct Library *Type1Base)
{
#ifdef TAGDEBUG
	kprintf("CloseEngine at $%lx\n", engine);
#endif
	if(!engine)
		return;		// We should panic...

	CloseType1Engine(engine);

	if (engine->WidthListPool)
		DeletePool(engine->WidthListPool);	// TetiSoft
	T1_Done_AFM(engine->fontreq.AFM);		// TetiSoft

	FreeVec(engine);

	return;
}


/**
 * ObtainInfoA
 **/
ULONG __saveds __asm ObtainInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base)
{
// TetiSoft: Instead of only searching for the first appearance of known tags
// with GetTagData(), we now process the whole taglist with NextTagItem().
// So we are able to answer with OT_UnknownTag.
// Also added OTERR_BadData for NULL ptr tagvals.

	struct FontRequest *fontreq;
	struct TagItem *tstate;
	struct TagItem *tag;

	ULONG rc;		// TetiSoft

#ifdef TAGDEBUG
	kprintf("ObtainInfo Engine at $%lx\n", engine);
#endif
	if(!engine)
		return OTERR_Failure;	// We should panic...

	fontreq = &engine->fontreq;
// TetiSoft
	if (fontreq->FontFileName[0] == '\0')
	{
#ifdef TAGDEBUG
		kprintf("OTERR_NoFace\n");
#endif
		return OTERR_NoFace;
	}
	tstate = tags;
	while(tag = NextTagItem(&tstate))
	{
		switch(tag->ti_Tag)	
		{
		case OT_GlyphMap:
		{
			struct GlyphMap **gm_p;
			if (gm_p = (struct GlyphMap **)tag->ti_Data)
			{

// TetiSoft
				if (fontreq->GlyphCode == 0xffffffff)
				{
#ifdef TAGDEBUG
					kprintf("OTERR_NoGlyph\n");
#endif
					return OTERR_NoGlyph;
				}

				if (fontreq->FontDone == 0)
				{
					if ((rc = ReadType1Font(engine)) != OTERR_Success)
					{
#ifdef TAGDEBUG
						kprintf("ReadType1Font() failed rc %ld\n", rc);
#endif
						return rc;
					}
					fontreq->FontDone = 1;
					fontreq->transformchanged = FALSE;
				}

				if (fontreq->transformchanged == TRUE)
				{
					if ((rc = SetTransformType1Font(engine)) != OTERR_Success)
					{
#ifdef TAGDEBUG
						kprintf("SetTransformType1Font() failed rc %ld\n", rc);
#endif
						return rc;
					}
					fontreq->transformchanged = FALSE;
				}

				if ((rc = GetType1GlyphMap(engine, fontreq->GlyphCode, gm_p)) != OTERR_Success)
				{
#ifdef TAGDEBUG
					kprintf("GetType1GlyphMap() failed %ld\n", rc);
#endif
					return rc;
				}
			} else
				return OTERR_BadData;
			break;
		}
		case OT_WidthList:	// TetiSoft
			if (tag->ti_Data)
			{
				if ((fontreq->GlyphCode == 0xffffffff) || (fontreq->GlyphCode2 == 0xffffffff))
					return OTERR_NoGlyph;
				if (fontreq->GlyphCode > fontreq->GlyphCode2)
					return OTERR_BadGlyph;

				if (fontreq->FontDone == 0)
				{
					if ((rc = ReadType1Font(engine)) != OTERR_Success)
						return rc;
					fontreq->FontDone = 1;
					fontreq->transformchanged = FALSE;
				}

				if (fontreq->transformchanged == TRUE)
				{
					if ((rc = SetTransformType1Font(engine)) != OTERR_Success)	
						return rc;
					fontreq->transformchanged = FALSE;
				}

				if ((rc = BuildWidthList(engine, fontreq->GlyphCode, fontreq->GlyphCode2, (struct MinList **)tag->ti_Data, Type1Base)) != OTERR_Success)
					return rc;
			} else
				return OTERR_BadData;
			break;
		case OT_TextKernPair:	// TetiSoft
		case OT_DesignKernPair:	// TetiSoft
			if (tag->ti_Data)
			{
				ULONG *Kern_p = (ULONG *)tag->ti_Data;
				*Kern_p = 0;

				if ((fontreq->GlyphCode == 0xffffffff) || (fontreq->GlyphCode2 == 0xffffffff))
					return OTERR_NoGlyph;

				if (fontreq->FontDone == 0)
				{
					if ((rc = ReadType1Font(engine)) != OTERR_Success)
						return rc;
					fontreq->FontDone = 1;
					fontreq->transformchanged = FALSE;
				}

				if (!fontreq->IsFixed && fontreq->AFMFileName[0])
				{
					if (!fontreq->AFM)
					{
						char *AFMStart;
						int AFMLength;
						if (readfont(fontreq->AFMFileName, &AFMStart, &AFMLength) >= 0)
						{
							fontreq->AFM = T1_Read_AFM(engine, AFMStart, AFMStart + AFMLength);
							freefont(AFMStart);
						}
					}
					if (!fontreq->AFM)
						fontreq->AFMFileName[0] = '\0';	// Don't try again
					else
					{
						FT_Vector kern;
						T1_Get_Kerning(engine, fontreq->AFM, fontreq->GlyphCode, fontreq->GlyphCode2,
								(tag->ti_Tag == OT_DesignKernPair) ? 1 : 0, fontreq->PointHeight, &kern);
						if (kern.x > 0)
							*Kern_p = -((kern.x << 16) / 1000);
						else
							*Kern_p = (-kern.x << 16) / 1000;
					}
				}
			} else
				return OTERR_BadData;
			break;
		case OT_IsFixed:
		{
			ULONG *IsFixed_p;
			if (IsFixed_p = (ULONG *)tag->ti_Data)
			{
				if ((rc = MyQueryFontLib(engine, "isFixedPitch", (void *)IsFixed_p)) != 0)
					*IsFixed_p = FALSE;	// TetiSoft: optional, default: proportional
			} else
				return OTERR_BadData;
			break;
		}
		case OT_Family:
		{
			char **familyname_p;
			if (familyname_p = (char **)tag->ti_Data)
			{
				if ((rc = MyQueryFontLib(engine, "FamilyName", (void *)familyname_p)) != 0)
					*familyname_p = "";	// TetiSoft: optional, default: no name
			} else
				return OTERR_BadData;
			break;
		}
		case OT_Spec3_FontName:
		{
			char **fontname_p;
			if (fontname_p = (char **)tag->ti_Data)
			{
				if ((rc = MyQueryFontLib(engine, "FontName", (void *)fontname_p)) != 0)
					*fontname_p = "UnknownName";	// TetiSoft: optional, default: dummy
			} else
				return OTERR_BadData;
			break;
		}
		case OT_Spec4_ItalicAngle:	// TetiSoft
			if (tag->ti_Data)
			{
				double ItalicAngle;
				if ((rc = MyQueryFontLib(engine, "ItalicAngle", (void *)&ItalicAngle)) != 0)
					*(long *)tag->ti_Data = 0;	// TetiSoft: optional, default: Upright
				else
					*(long *)tag->ti_Data = (long) ItalicAngle;
			} else
				return OTERR_BadData;
			break;
		case OT_Spec5_Weight:	// TetiSoft
		{
			char **weightname_p;
			if (weightname_p = (char **)tag->ti_Data)
			{
				if ((rc = MyQueryFontLib(engine, "Weight", (void *)weightname_p)) != 0)
					*weightname_p = "UnknownWeight";	// TetiSoft: optional, default: dummy
			} else
				return OTERR_BadData;
			break;
		}
		case OT_Spec6_OwnEncoding:	// TetiSoft
		{
			double dummy;
			ULONG *HasEncPtr;
			if (HasEncPtr = (ULONG *)tag->ti_Data)
			{
				MyQueryFontLib(engine, "ItalicAngle", (void *)&dummy);	// force reading and parsing font
				*HasEncPtr = engine->Has_Builtin_Encoding;	// T1Manager has forced us already to call scanfont.c routines
			} else
				return OTERR_BadData;
			break;
		}
		case OT_Spec7_UnicodeArray:	// TetiSoft: Fill provided array of 65536 entries
		{				// with 0 (glyph not found) and 1 (glyph available).
			unsigned char *array = (unsigned char *)tag->ti_Data;
			psdict *dictP;
			int i, j, n, index;
			char buf[256];
			double dummy;
			MyQueryFontLib(engine, "ItalicAngle", (void *)&dummy);	// force reading and parsing font
			if (!array || !engine->FontP || !engine->FontP->CharStringsP)
				return OTERR_Failure;
			dictP = engine->FontP->CharStringsP;
			n = dictP[0].key.len;
			for (index = 0; index < 65536; index++)
				array[index] = 0;
			for (i = 1; i <= n; i++)
			{
				if (dictP[i].key.len)
				{
					strncpy(buf, dictP[i].key.data.valueP, dictP[i].key.len);
					buf[dictP[i].key.len] = '\0';
					/* if uni<CODE> name, extract index from name */
					if ((dictP[i].key.len >= 7) &&
					    (buf[0] == 'u')  &&
					    (buf[1] == 'n')  &&
					    (buf[2] == 'i')  &&
					    ishdigit(buf[3]) &&
					    ishdigit(buf[4]) &&
					    ishdigit(buf[5]) &&
					    ishdigit(buf[6]))
					{
//						buf[7] = '\0';
//						index = strtoul(&buf[3], NULL, 16);
						index = fourhexcharstoul(&buf[3]);
						array[index] = 1;
					}
					else
					{
						/* search glyph name in AGL, if found return index */
						for (j = 0; AGL[j].name; j++)
						{
							if (!strcmp(buf, AGL[j].name))
							{
								index = AGL[j].index;
								array[index] = 1;
								break;
							}
						}
					}
				}
			}
			break;
		}
		case OT_UnderLined:
			/* not implemented, fall through */
		case OT_StrikeThrough:
			/* not implemented, fall through */
#ifdef DEBUG
			kprintf("ObtainInfo: Unsupported Tag $%lx\n", tag->ti_Tag);
#endif
			return OTERR_UnknownTag;
			break;
		default:
#ifdef DEBUG
			kprintf("ObtainInfo: Unknown Tag $%lx value $%lx\n", tag->ti_Tag, tag->ti_Data);
#endif
			return OTERR_UnknownTag;
		}
	}
#ifdef TAGDEBUG
	kprintf("ObtainInfo Successful\n");
#endif
	return OTERR_Success;
}


#define sgn(a) (a<0?-1:a>0?1:0)

// TetiSoft: Now also works if the type to convert is an unsigned type...
//#define FP2DOUBLE(fp) (sgn(fp)*((double)((abs(fp) & 0xffff0000) >> 16) + (((double)(abs(fp) & 0x0000ffff)) / 65536)))
#define FP2DOUBLE(fp) (sgn((long)fp)*((double)((abs((long)fp) & 0xffff0000) >> 16) + (((double)(abs((long)fp) & 0x0000ffff)) / 65536)))

/**
 * SetInfoA
 **/
ULONG __saveds __asm SetInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base)
{
#if 0
	struct TagItem *otags;

	scantags(tags);

	if (otags = (struct TagItem *)GetTagData(OT_OTagList, NULL, tags))
		if (GetTagData(OT_OTagList, NULL, otags) == NULL)		/* Just in case... */
			scantags(otags);
	return OTERR_Success;
#endif

// TetiSoft: Instead of only searching for the first appearance of known tags
// with GetTagData(), we now process the whole taglist with NextTagItem().
// So we are able to answer with OT_UnknownTag.
// On the other side, it is no longer possible to specify e.g. size before fontname,
// but that is not allowed (see diskfont/diskfonttag.h).
// Added OTERR_BadData for NULL ptr tagvals.
// Added some .otag tag values to avoid OT_UnknownTag.

	struct FontRequest *fontreq;
	struct TagItem *tstate;
	struct TagItem *tag;

#ifdef TAGDEBUG
	kprintf("SetInfo Engine at $%lx\n", engine);
#endif
	if(!engine)
		return OTERR_Failure;	// We should panic...

	fontreq = &engine->fontreq;
	tstate = tags;
	while(tag = NextTagItem(&tstate))
	{
		switch(tag->ti_Tag)	
		{

// supported Tags
		case OT_OTagPath:
#ifdef TAGDEBUG
			kprintf("SetInfo OTagPath '%s'\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
//				clearfontreq(fontreq);
				clearfontreq(fontreq, FALSE);	// TetiSoft: for FinalWriter compatibility
//				stccpy(fontreq->OTagFileName, (char *)tag->ti_Data, sizeof(fontreq->OTagFileName));	// TetiSoft: was strcpy()
//				MyType1CloseFont(engine);
			} else
				return OTERR_BadData;
			break;

		case OT_Spec1_PFABFileName:		/* file name of the pfa/pfb file */
#ifdef TAGDEBUG
			kprintf("SetInfo PFABFilename '%s'\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
//				if (strncmp(fontreq->FontFileName, (char *)tag->ti_Data, sizeof(fontreq->FontFileName)) != 0)
//				{
					stccpy(fontreq->FontFileName, (char *)tag->ti_Data, sizeof(fontreq->FontFileName));	// TetiSoft: was strcpy()
//					fontreq->FontDone = 0;
//				}
			} else
				return OTERR_BadData;
			break;

		case OT_Spec2_AFMFileName:	/* file name of the AFM file to be parsed for kerning data */
#ifdef TAGDEBUG
			kprintf("SetInfo AFMFileName '%s'\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
				if (strncmp(fontreq->AFMFileName, (char *)tag->ti_Data, sizeof(fontreq->AFMFileName)) != 0)
				{
					stccpy(fontreq->AFMFileName, (char *)tag->ti_Data, sizeof(fontreq->AFMFileName));	// TetiSoft: was strcpy()
					T1_Done_AFM(fontreq->AFM);
					fontreq->AFM = NULL;
				}
			} else
				return OTERR_BadData;
			break;

		case OT_PointHeight:	/* For now, assume fractional part is zero */
#ifdef TAGDEBUG
			kprintf("SetInfo PointHeight $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
				ULONG PointHeight = tag->ti_Data >> 16;
				if(fontreq->PointHeight != PointHeight)
				{
					fontreq->PointHeight = PointHeight;
					fontreq->transformchanged = TRUE;
				}
			} else
				return OTERR_BadData;
			break;

		case OT_PointSize:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo PointSize $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
				ULONG PointHeight = tag->ti_Data / 16;
#ifdef DEBUG
				kprintf("SetInfo: OLD Tag OT_PointSize ($%lx -> %ld pts)\n", tag->ti_Data, tag->ti_Data / 16);
#endif
				if(fontreq->PointHeight != PointHeight)
				{
					fontreq->PointHeight = PointHeight;
					fontreq->transformchanged = TRUE;
				}
			} else
				return OTERR_BadData;
			break;

		case OT_RotateSin:
		{
			double RotateSin = FP2DOUBLE(tag->ti_Data);
#ifdef TAGDEBUG
			kprintf("SetInfo RotateSin $%lx ->$%lx $%lx\n", tag->ti_Data, RotateSin);
#endif
			if (fontreq->RotateSin != RotateSin)
			{
				fontreq->RotateSin = RotateSin;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_RotateCos:
		{
			double RotateCos = FP2DOUBLE(tag->ti_Data);
#ifdef TAGDEBUG
			kprintf("SetInfo RotateCos $%lx ->$%lx $%lx\n", tag->ti_Data, RotateCos);
#endif
			if (fontreq->RotateCos != RotateCos)
			{
				fontreq->RotateCos = RotateCos;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_ShearSin:
		{
			double ShearSin = FP2DOUBLE(tag->ti_Data);
#ifdef TAGDEBUG
			kprintf("SetInfo ShearSin $%lx ->$%lx $%lx\n", tag->ti_Data, ShearSin);
#endif
			if (fontreq->ShearSin != ShearSin)
			{
				fontreq->ShearSin = ShearSin;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_ShearCos:
		{
			double ShearCos = FP2DOUBLE(tag->ti_Data);
#ifdef TAGDEBUG
			kprintf("SetInfo ShearCos $%lx ->$%lx $%lx\n", tag->ti_Data, ShearCos);
#endif
			if (fontreq->ShearCos != ShearCos)
			{
				fontreq->ShearCos = ShearCos;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_DeviceDPI:
		{
			ULONG DeviceXDPI = tag->ti_Data >> 16;
			ULONG DeviceYDPI = tag->ti_Data & 0xffff;
#ifdef TAGDEBUG
			kprintf("SetInfo DeviceDPI X %ld Y %ld\n", DeviceXDPI, DeviceYDPI);
#endif
			if (DeviceXDPI && DeviceYDPI)
			{
				if ((fontreq->DeviceXDPI != DeviceXDPI) || (fontreq->DeviceYDPI != DeviceYDPI))
				{
					fontreq->DeviceXDPI = DeviceXDPI;
					fontreq->DeviceYDPI = DeviceYDPI;
					fontreq->transformchanged = TRUE;
				}
			} else
				return OTERR_BadData;
			break;
		}

		case OT_GlyphCode:
#ifdef TAGDEBUG
			kprintf("SetInfo GlyphCode %ld\n", tag->ti_Data);
#endif
			fontreq->GlyphCode = tag->ti_Data;
			break;

		case OT_GlyphCode2:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo GlyphCode2 %ld\n", tag->ti_Data);
#endif
			fontreq->GlyphCode2 = tag->ti_Data;
			break;

		case OT_GlyphWidth:	// TetiSoft
		{
			LONG GlyphWidth = tag->ti_Data;
#ifdef TAGDEBUG
			kprintf("SetInfo GlyphWidth %ld\n", tag->ti_Data);
#endif
			if(fontreq->GlyphWidth != GlyphWidth)
			{
				fontreq->GlyphWidth = GlyphWidth;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_OTagList:
#ifdef TAGDEBUG
			kprintf("SetInfo OTagList at $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data) {
				ULONG rc =SetInfoA(engine, (struct TagItem *)tag->ti_Data, Type1Base);
				// TetiSoft: Don't fail on unknown tags in .otag files
				if ((rc != OTERR_Success) && (rc != OTERR_UnknownTag))
					return rc;
			} else
				return OTERR_BadData;
			break;

		case OT_SymbolSet:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo SymbolSet $%lx\n", tag->ti_Data);
#endif
			if (fontreq->SymbolSet != tag->ti_Data)
			{
				fontreq->SymbolSet = tag->ti_Data;
				T1_Done_AFM(fontreq->AFM);
				fontreq->AFM = NULL;
			}
			break;

// unsupported Tags
		case OT_SetFactor:
		{
			ULONG SetFactor = tag->ti_Data;
#ifdef TAGDEBUG
			kprintf("SetInfo SetFactor $%lx\n", tag->ti_Data);
#endif
#ifdef SETFACTOR						// TetiSoft: Not implemented
			ULONG PointSet;
			if (SetFactor == 0x00010000)
				PointSet = 0;
			else
				PointSet = FP2DOUBLE(SetFactor) * fontreq->PointHeight;
			if ((fontreq->SetFactor != SetFactor) || (fontreq->PointSet != PointSet))
			{
				fontreq->SetFactor = SetFactor;
				fontreq->PointSet = PointSet;
				fontreq->transformchanged = TRUE;
			}
#else
			if (SetFactor != 0x00010000)
			{
#ifdef DEBUG
				kprintf("SetInfo: Tag OT_SetFactor (%ld.%ld) not supported\n", SetFactor >> 16, SetFactor & 0xffff);
#endif
				return OTERR_BadData;
			}
#endif
			break;
		}

		case OT_DotSize:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo DotSize $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data)
			{
				if (tag->ti_Data != 0x00640064)	// Dot size 100% of resolution ?
				{
#ifdef DEBUG
					kprintf("SetInfo: Tag OT_DotSize (X %ld%%, Y %ld%%) not supported\n", tag->ti_Data >> 16, tag->ti_Data & 0xffff);
#endif
					return OTERR_BadData;
				}
			} else
				return OTERR_BadData;
			break;

		case OT_EmboldenX:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo EmboldenX $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data != 0)	// Embolden by 0% of em?
			{
#ifdef DEBUG
				kprintf("SetInfo: Tag OT_EmboldenX (%ld.%ld%% em) not supported\n", tag->ti_Data >> 16, tag->ti_Data & 0xffff);
#endif
				return OTERR_BadData;
			}
			break;

		case OT_EmboldenY:	// TetiSoft
#ifdef TAGDEBUG
			kprintf("SetInfo EmboldenY $%lx\n", tag->ti_Data);
#endif
			if (tag->ti_Data != 0)	// Embolden by 0% of em?
			{
#ifdef DEBUG
				kprintf("SetInfo: Tag OT_EmboldenY (%ld.%ld%% em) not supported\n", tag->ti_Data >> 16, tag->ti_Data & 0xffff);
#endif
				return OTERR_BadData;
			}
			break;

// Tags from .otag file
		case OT_FileIdent:	// TetiSoft
			break;		// Just skip the .otag file identifier

		case OT_Engine:		// TetiSoft
			if (tag->ti_Data)
			{
				if(strcmp((char *)tag->ti_Data, EngineName))
				{
#ifdef DEBUG
					kprintf("SetInfo: .otag is designed for engine \"%s\"\n", tag->ti_Data);
#endif
					return OTERR_Failure;
				}
			} else
				return OTERR_BadData;
			break;

		case OT_IsFixed:	// TetiSoft
		{
			ULONG IsFixed = tag->ti_Data;
			if (fontreq->IsFixed != IsFixed)
			{
				fontreq->IsFixed = IsFixed;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_SpaceWidth:	// TetiSoft
		{
			LONG SpaceWidth = tag->ti_Data;
			if (fontreq->SpaceWidth != SpaceWidth)
			{
				fontreq->SpaceWidth = SpaceWidth;
				fontreq->transformchanged = TRUE;
			}
			break;
		}

		case OT_Family:		// TetiSoft: Just ignore the family name in the .otag file
			break;		// and use that from the type1 file instead

		case OT_AvailSizes:	// TetiSoft: Just ignore the list of bitmap sizes in the .otag file
			break;		// since diskfont.library can handle the bitmap fonts without us

		case OT_SpecCount:	// TetiSoft
			break;		// Just ignore the parameter counter

		case OT_BName:		// TetiSoft: Just ignore the names of bold, italic, and bold italic
		case OT_IName:		// typeface variants. May be useful for diskfont.library,
		case OT_BIName:		// but it doesn't use it currently?
			break;

		case OT_YSizeFactor:	// TetiSoft: Just ignore the rest, this is information
		case OT_SerifFlag:	// that is generated by Type1Manager for diskfont.library
		case OT_StemWeight:	// and applications.
		case OT_SlantStyle:
		case OT_HorizStyle:
		case OT_SpaceFactor:
		case OT_InhibitAlgoStyle:
		case OT_Spec6_OwnEncoding:
			break;

		default:
#ifdef DEBUG
			kprintf("SetInfo: Unknown Tag $%lx\n", tag->ti_Tag);
#endif
			return OTERR_UnknownTag;
		}
	}

// TetiSoft: Compute FixedWidth, FixedWidthPixels and GlyphWidthPixels
	if(fontreq->transformchanged)
	{
		if(fontreq->IsFixed)
		{
			fontreq->FixedWidth = (((fontreq->SpaceWidth * 72) / 250) << 16) / 2540;	// * 7.56
			fontreq->FixedWidthPixels = (fontreq->SpaceWidth / 2540.0) * (fontreq->PointHeight / 250.0) * fontreq->DeviceXDPI;	// spacewidth in pixels (X dots)
		}
		if(fontreq->GlyphWidth)
		{
			fontreq->GlyphWidthPixels = ((fontreq->GlyphWidth >> 16) + ((fontreq->GlyphWidth & 0xffff) / 65536.0)) * fontreq->PointHeight ;
		}
	}

#ifdef TAGDEBUG
	kprintf("SetInfo Successful\n");
#endif
	return OTERR_Success;
}

/**
 * ReleaseInfoA
 **/
ULONG __saveds __asm ReleaseInfoA(REG(a0) struct T1GlyphEngine *engine, REG(a1) struct TagItem *tags, REG(a6) struct Library *Type1Base)
{
// TetiSoft: Instead of only returning OTERR_Success, we now process the whole taglist with NextTagItem().
// So we are able to free WidthLists and GlyphMaps.

	struct TagItem *tstate;
	struct TagItem *tag;
	struct GlyphMap *gm;

	if(!engine)
		return OTERR_Failure;	// We should panic...

	tstate = tags;
	while(tag = NextTagItem(&tstate))
	{
		if ((tag->ti_Tag == OT_GlyphMap) && (gm = (struct GlyphMap*)tag->ti_Data))
		{
			if (gm->glm_BitMap)
				xfree(engine, gm->glm_BitMap);
			xfree(engine, gm);
		} else if (tag->ti_Tag == OT_WidthList)
			FreeWidthList(engine, (struct MinList *) tag->ti_Data);
	}
	return OTERR_Success;
}

static void clearfontreq(struct FontRequest *fontreq_p, Bool ClearAll)	// TetiSoft: for FinalWriter compatibility
{
	fontreq_p->FontDone = 0;
//	fontreq_p->GlyphCode = 0;
	fontreq_p->GlyphCode = 0xffffffff;	// TetiSoft
	fontreq_p->GlyphCode2 = 0xffffffff;	// TetiSoft
	fontreq_p->DeviceXDPI = 0;
	fontreq_p->DeviceYDPI = 0;
	fontreq_p->PointHeight = 0;
#ifdef SETFACTOR				// TetiSoft: Not implemented
	fontreq_p->SetFactor = 0;
	fontreq_p->PointSet = 0;
#endif
	fontreq_p->GlyphWidth = 0;		// TetiSoft
	fontreq_p->GlyphWidthPixels = 0;	// TetiSoft
	fontreq_p->RotateSin = 0.0;
	fontreq_p->RotateCos = 1.0;
	fontreq_p->ShearSin = 0.0;
	fontreq_p->ShearCos = 1.0;

// TetiSoft: for FinalWriter compatibility.
// include:diskfont/diskfonttag.h says OT_OTagPath must be specified before OT_OTagList,
// but FinalWriter does it the other way around...
	if(ClearAll)
	{
		fontreq_p->FontFileName[0] = '\0';
		fontreq_p->AFMFileName[0] = '\0';
		T1_Done_AFM(fontreq_p->AFM);		// TetiSoft
		fontreq_p->AFM = NULL;			// TetiSoft
		fontreq_p->SymbolSet = UNICODE;		// TetiSoft
		fontreq_p->IsFixed = FALSE;		// TetiSoft
		fontreq_p->FixedWidth = 0;		// TetiSoft
		fontreq_p->FixedWidthPixels = 0;	// TetiSoft
		fontreq_p->SpaceWidth = 0;		// TetiSoft
	}

//	fontreq_p->OTagFileName[0] = '\0';
}

static int BuildWidthList(struct T1GlyphEngine *engine, ULONG glyph1, ULONG glyph2, struct MinList **ListPtrPtr, struct Library *Type1Base)	// TetiSoft
{
	struct MinList *ListPtr;
	struct GlyphWidthEntry *gwe;
	struct GlyphMap *gm;
	struct TagItem tags[2];

	if(!engine->WidthListPool)
		engine->WidthListPool = CreatePool(MEMF_PUBLIC, sizeof(struct GlyphWidthEntry), sizeof(struct GlyphWidthEntry));
	if(!engine->WidthListPool)
		return OTERR_NoMemory;
	ListPtr = (struct MinList *)AllocPooled(engine->WidthListPool, sizeof(struct MinList));
	if(!ListPtr)
		return OTERR_NoMemory;
	NewList((struct List *)ListPtr);
	tags[0].ti_Tag = OT_GlyphMap;
	tags[1].ti_Tag = TAG_DONE;
	tags[1].ti_Data = 0;
	while(glyph1 <= glyph2)
	{
		if (GetType1GlyphMap(engine, glyph1, &gm) == OTERR_Success)
		{
			gwe = (struct GlyphWidthEntry *)AllocPooled(engine->WidthListPool, sizeof(struct GlyphWidthEntry));
			if(!gwe)
			{
				FreeWidthList(engine, ListPtr);
				return OTERR_NoMemory;
			}
			gwe->gwe_Code = glyph1;
			gwe->gwe_Width = gm->glm_Width;
			AddTail((struct List *)ListPtr, (struct Node *)gwe);
			tags[0].ti_Data = (ULONG)gm;
			ReleaseInfoA(engine, tags, Type1Base);
		}
		glyph1++;
	}
	*ListPtrPtr = ListPtr;
	return OTERR_Success;
}

static void FreeWidthList(struct T1GlyphEngine *engine, struct MinList *ListPtr)	// TetiSoft
{
	struct MinNode *current, *next;

	if(engine->WidthListPool && ListPtr)
	{
		current = ListPtr->mlh_Head;
		while (next = current->mln_Succ)
		{
			FreePooled(engine->WidthListPool, current, sizeof(struct GlyphWidthEntry));
			current = next;
		}
		FreePooled(engine->WidthListPool, ListPtr, sizeof(struct MinList));
	}
}
