
#ifndef T1MANAGER_GST
#include "t1manager.h"
#endif

#ifdef PARMDEBUG
extern void DebugBltTemplate(UWORD *SrcTemplate, WORD SrcX, WORD SrcMod, struct RastPort *rp, WORD DstX, WORD DstY, WORD SizeX, WORD SizeY, char ch);
#endif

/*************************************************************/
/* Library Bases */
/*************************************************************/

/* Opened automatically by SAS/C */
//extern struct Library *UtilityBase;
//extern struct Library *DOSBase;
//extern struct Library *GfxBase;

/* Opened "manually" */
struct Library *Type1Base;


/*************************************************************/
/* Global Variables */
/*************************************************************/
/* Font Install stuff */
static unsigned char fontfiledata[] = {0x0f, 0x03, 0x00, 0x00};
extern char *OTagFileBuf;	// TetiSoft


/*************************************************************/
/* T1_InstallFont routines */
/*************************************************************/
/**
 * OpenEngine() the type1.library.
 *
 * Load the font
 *
 * Determine the name to use for the .otag file
 * --enhancement: create Amiga font filename from the Font's internal name,
 *  appending a ".otag" to get otag filename.
 *
 * Obtain from the font engine the information that will be needed
 * to create the .otag file.
 *
 * Create the .otag file. The .otag file, will contain the full path to
 * the type1 font file.
 * --enhancement: get the path information from the file requester,
 *  so it may refer to assign's, rather than devices, allowing the user
 *  to, e.g. move the psfonts: directories to another hard drive w/o
 *  requiring re-installation of all fonts
 *
 * On success, should return an error code, so that listview will be
 * updated.
 **/
BOOL T1_InstallFont(char *filename, char *destdrawer, struct OTagValues *Presets, BOOL Update)
{
	struct TagItem tags[10], minOTags[3], *OTags;
	int result;
	long ItalicAngle;		// TetiSoft
	ULONG StemWeight;		// TetiSoft
	ULONG IsFixed;			// TetiSoft
	ULONG SlantStyle;		// TetiSoft
	FIXED SpaceWidth = 2540;	// TetiSoft
	ULONG YSizeFactor = 0x00010001;	// TetiSoft
	ULONG InhibitAlgoStyle;		// TetiSoft
	ULONG SymbolSet;		// TetiSoft
	ULONG OwnEncoding;		// TetiSoft
	char *family;
	char *internalname;
	char *WeightName;		// TetiSoft
	char *enginename = "type1";
	char fullfilename[MAXFONTPATH];
	BPTR lock;
	ULONG otagsize;
	struct MinList *otaglist;
	char otagfilename[100];
//	char dirfilename[100];		// TetiSoft
	char fontfilename[100];
	char familyname[100];
	BPTR otagfile;
	BPTR fontfile;
	BPTR newdirlock;
	void *otagbuf;
	char buf[100];
	char AFMFileName[MAXFONTPATH];

	if (!Type1Base)
		return FALSE;

	/**
	 * Get the full filename of the file to install
	 **/
	if ((lock = Lock(filename, ACCESS_READ)) == NULL)
		return FALSE;			/* failure in Lock() */

	if (!(NameFromLock(lock, fullfilename, sizeof(fullfilename))))
	{
		UnLock(lock);
		return FALSE;			/* failure in NameFromLock() */
	}

	UnLock(lock);

// TetiSoft: try to find the AFM file automatically during install
	stccpy(AFMFileName, Presets->AFMFileName, sizeof(AFMFileName));
	if (!Update && (AFMFileName[0] == '\0'))
	{
		char *s;
		stccpy(AFMFileName, fullfilename, sizeof(AFMFileName));
		s = FilePart(AFMFileName);
		if (s && strrchr(s, '.'))
			*strrchr(s, '.') = '\0';
		strcat(AFMFileName, ".afm");
		if ((lock = Lock(AFMFileName, ACCESS_READ)) == NULL)
			AFMFileName[0] = '\0';
		else
			UnLock(lock);
	}

	/**
	 * Now we open the font with type1.library
	 **/
	if (!engine && ((engine = OpenEngine()) == NULL))
	{
		MyErrorMsg(LString(MSG_ERROR_OPEN_ENGINE));
		return FALSE;
	}
/**
 * Tell the font engine what file we will need information from
 * This involves creating a temporary simple taglist containing the
 * necessary specification information (font's full filename),
 * and passing OT_OTagPath and OT_OTagList to SetInfoA().
 *
 * NOTE: for OT_OTagPath, we pass an empty string, since the otag
 * file hasn't been created yet.  We pass the in-memory, temporary,
 * minOTags with OT_OtagList.
 **/
	minOTags[0].ti_Tag = OT_SpecCount;
	minOTags[0].ti_Data = (ULONG) 1;

	minOTags[1].ti_Tag = OT_Spec1_PFABFileName;
	minOTags[1].ti_Data = (ULONG) fullfilename;

	minOTags[2].ti_Tag = TAG_DONE;
	minOTags[2].ti_Data = (ULONG) 0;

	tags[0].ti_Tag = OT_OTagPath;
	tags[0].ti_Data = (ULONG) "";

	tags[1].ti_Tag = OT_OTagList;
	tags[1].ti_Data = (ULONG) minOTags;

	tags[2].ti_Tag = TAG_DONE;
	tags[2].ti_Data = (ULONG) 0;
	if (result = SetInfoA(engine, tags))
	{
//		CloseEngine(engine);
		return FALSE;	/* failure in SetInfoA() */
	}

/**
 * Obtain some information from the font engine about the font.
 **/
	if (Presets->IsFixed == 0xffff)
		tags[0].ti_Tag = OT_IsFixed;
	else
	{
		tags[0].ti_Tag = TAG_IGNORE;
		IsFixed = Presets->IsFixed;
	}
	tags[0].ti_Data = (ULONG) &IsFixed;

	family = Presets->FamilyName;
	if (Presets->FamilyName[0] == '\0')
		tags[1].ti_Tag = OT_Family;
	else
		tags[1].ti_Tag = TAG_IGNORE;
	tags[1].ti_Data = (ULONG) &family;

	if (!Update)
		tags[2].ti_Tag = OT_Spec3_FontName;
	else
		tags[2].ti_Tag = TAG_IGNORE;
	tags[2].ti_Data = (ULONG) &internalname;

// TetiSoft: SlantStyle
	if (Presets->SlantStyle == 0xffff)
		tags[3].ti_Tag = OT_Spec4_ItalicAngle;
	else
		tags[3].ti_Tag = TAG_IGNORE;
	tags[3].ti_Data = (ULONG) &ItalicAngle;

// TetiSoft: StemWeight
	if (Presets->StemWeight == 0xffff)
		tags[4].ti_Tag = OT_Spec5_Weight;
	else
		tags[4].ti_Tag = TAG_IGNORE;
	tags[4].ti_Data = (ULONG) &WeightName;

	tags[5].ti_Tag = TAG_DONE;
	tags[5].ti_Data = (ULONG) 0;

	if (result = ObtainInfoA(engine, tags))
	{
		MyErrorMsg2(LString(MSG_ERROR_INSTALL), result, fullfilename);
//		CloseEngine(engine);
		return FALSE;	/* failure in ObtainInfoA() */
	}

	if (family)
		strcpy(familyname, family);
	else
		familyname[0] = '\0';

	if (!Update)
	{
		int afShortage, afSize;
		struct AvailFontsHeader *afh;
		int i, fontexists, num = 0;

		/* Strip the font's internal name of spaces, dashes and underscores. */

		/* Or maybe not. JK */
		strncpy(buf,internalname,99);

/*		dest = buf;
			
		for (src = internalname; (src != NULL) && (*src != '\0'); src++)
			if ((*src != ' ') && (*src != '-') && (*src != '_'))
				*dest++ = *src;
		*dest = '\0';
*/

/* TetiSoft: We must ensure that the name fits in a DiskFontHeader structure */
		buf[MAXFONTNAME - 1 - (strlen(".font") + 1)] = '\0';

/* TetiSoft: If .otag file already exists, don't overwrite */
		strcpy(otagfilename, destdrawer);
		AddPart(otagfilename, buf, sizeof(otagfilename));
		strcat(otagfilename, ".otag");
		newdirlock = Lock(otagfilename,ACCESS_READ);
		if (newdirlock)
		{
			UnLock(newdirlock);
			MyErrorMsg1(LString(MSG_ERROR_OTAG_EXISTS), otagfilename);
//			CloseEngine(engine);
			return FALSE;
		}

		if(!QuickInstall)
		{
/* TetiSoft: We must ensure that the name is not already used, and append a number if necessary */
			afSize = 1024;
			do
			{
				afh = (struct AvailFontsHeader *)AllocMem(afSize, 0);
				if (afh)
				{
					afShortage = AvailFonts((STRPTR)afh, afSize, AFF_MEMORY|AFF_DISK);
					if (afShortage)
					{
						FreeMem(afh, afSize);
						afSize += afShortage;
					}
				} else {
					MyErrorMsg(LString(MSG_ERROR_NOMEM_AVAILFONTS));
					return FALSE;
				}
			} while (afShortage);
			strcpy(fontfilename, buf);
			strcat(fontfilename, ".font");
			do {
				for (i = 0, fontexists = FALSE; i < afh->afh_NumEntries; i++)
				{
					struct AvailFonts *af = (struct AvailFonts *)(&afh[1]);
					if (!stricmp(af[i].af_Attr.ta_Name, fontfilename))
					{
						fontexists = TRUE;
						break;
					}
				}
				if (fontexists)
				{
					char numbuf[20];
					int numpos;

					sprintf(numbuf, "%ld", num++);
					numpos = MAXFONTNAME - (strlen(".font") + strlen(numbuf) + 2);
					if (numpos > strlen(buf))
						numpos = strlen(buf);
					strcpy(&buf[numpos], numbuf);
					strcpy(fontfilename, buf);
					strcat(fontfilename, ".font");
				}
			} while (fontexists);
			FreeMem(afh, afSize);
		}
		strcpy(otagfilename, destdrawer);
		AddPart(otagfilename, buf, sizeof(otagfilename));
		strcat(otagfilename, ".otag");

		strcpy(fontfilename, destdrawer);
		AddPart(fontfilename, buf, sizeof(fontfilename));
		strcat(fontfilename, ".font");
	} else
		stccpy(otagfilename, Presets->OTagFileName, sizeof(otagfilename));

// TetiSoft: InhibitAlgoStyle
	if (Presets->InhibitAlgoStyle == 0xffff)
// OT_EmboldenX, OT_EmboldenY, OT_UnderLined, OT_SetFactor not supported yet
		InhibitAlgoStyle = FSF_UNDERLINED|FSF_BOLD|FSF_EXTENDED;
	else
		InhibitAlgoStyle = Presets->InhibitAlgoStyle;

// TetiSoft: SlantStyle
	if (Presets->SlantStyle == 0xffff)
	{
		if (ItalicAngle < 0)
			SlantStyle = OTS_Italic;
		else if (ItalicAngle > 0)
			SlantStyle = OTS_LeftItalic;
		else
			SlantStyle = OTS_Upright;
	}
	else
		SlantStyle = Presets->SlantStyle;

// TetiSoft: StemWeight
	if (Presets->StemWeight == 0xffff)
	{
		if (!WeightName)
			StemWeight = OTS_Book;
		else if (astcsma(WeightName, "#?Ultra#?Thin#?"))
			StemWeight = OTS_UltraThin;
		else if (astcsma(WeightName, "#?Extra#?Thin#?"))
			StemWeight = OTS_ExtraThin;
		else if (astcsma(WeightName, "#?Thin#?"))
			StemWeight = OTS_Thin;
		else if (astcsma(WeightName, "#?Extra#?Light#?"))
			StemWeight = OTS_ExtraLight;
		else if (astcsma(WeightName, "#?Demi#?Light#?"))
			StemWeight = OTS_DemiLight;
		else if (astcsma(WeightName, "#?Semi#?Light#?"))
			StemWeight = OTS_SemiLight;
		else if (astcsma(WeightName, "#?Light#?"))
			StemWeight = OTS_Light;
		else if (astcsma(WeightName, "#?Book#?"))
			StemWeight = OTS_Book;
		else if (astcsma(WeightName, "#?Normal#?"))
			StemWeight = OTS_Medium;
		else if (astcsma(WeightName, "#?Medium#?"))
			StemWeight = OTS_Medium;
		else if (astcsma(WeightName, "#?Semi#?Bold#?"))
			StemWeight = OTS_SemiBold;
		else if (astcsma(WeightName, "#?Demi#?Bold#?"))
			StemWeight = OTS_DemiBold;
		else if (astcsma(WeightName, "#?Extra#?Bold#?"))
			StemWeight = OTS_ExtraBold;
		else if (astcsma(WeightName, "#?Bold#?"))
			StemWeight = OTS_Bold;
		else if (astcsma(WeightName, "#?Extra#?Black#?"))
			StemWeight = OTS_ExtraBlack;
		else if (astcsma(WeightName, "#?Ultra#?Black#?"))
			StemWeight = OTS_UltraBlack;
		else if (astcsma(WeightName, "#?Black#?"))
			StemWeight = OTS_Black;
		else
			StemWeight = OTS_Medium;
	}
	else
		StemWeight = Presets->StemWeight;

//	CloseEngine(engine);	// Strings famiy, internalname, WeightName no longer needed

// TetiSoft: OwnEncoding & SymbolSet
	OwnEncoding = Presets->OwnEncoding;
	SymbolSet = Presets->SymbolSet;
	if (!Update || !SymbolSet || (OwnEncoding == 0xffff)) {
		OwnEncoding = HasOwnEncoding(fullfilename);
		if (OwnEncoding)
			SymbolSet = BUILTINENCODING;
		else
			SymbolSet = ISOLATIN1ENCODING;
	}

// TetiSoft: SpaceWidth & YSizeFactor
	if ((Presets->SpaceWidth < 10) ||
	    ((Presets->YSizeFactor & 0xffff0000) == 0) ||
	    ((Presets->YSizeFactor & 0x0000ffff) == 0))
	{
		if (!CalcSpaceWidthYSizeFactor(NULL, fullfilename, IsFixed, SymbolSet, &SpaceWidth, &YSizeFactor))
			return FALSE;
	}

	if (Presets->SpaceWidth >= 10)
		SpaceWidth = Presets->SpaceWidth;
	if ((Presets->YSizeFactor & 0xffff0000) && (Presets->YSizeFactor & 0x0000ffff))
		YSizeFactor = Presets->YSizeFactor;


/**
 * Create the otags file in memory
 *
 * This is currently a multi-step procedure:
 *	Create a linked list containing the tag information and a size field
 *	that contains the size of OT_Indirect data
 *
 *	Allocate memory for the total size of the otag file.
 *
 *	Insert the linked list and OT_Indirect information into the memory.
 *	(while correcting the OT_Indirect ti_Data pointers)
 *
 *	Put the total size in the OT_FileIdent data field.
 *
 **/
	if ( (otaglist = NewOTagList()) &&
	     (AddOTag(otaglist, OT_FileIdent, 0, 0)) &&
	     (AddOTag(otaglist, OT_Engine, (ULONG) enginename, strlen(enginename) + 1)) &&
	     ((familyname[0] == '\0') || AddOTag(otaglist, OT_Family, (ULONG) familyname, strlen(familyname) + 1)) &&
	     ((Presets->BoldName[0] == '\0') || AddOTag(otaglist, OT_BName, (ULONG)Presets->BoldName, strlen(Presets->BoldName) + 1)) &&
	     ((Presets->ItalicName[0] == '\0') || AddOTag(otaglist, OT_IName, (ULONG)Presets->ItalicName, strlen(Presets->ItalicName) + 1)) &&
	     ((Presets->BoldItalicName[0] == '\0') || AddOTag(otaglist, OT_BIName, (ULONG)Presets->BoldItalicName, strlen(Presets->BoldItalicName) + 1)) &&
	     (AddOTag(otaglist, OT_SymbolSet, SymbolSet, 0)) &&			// TetiSoft
	     (AddOTag(otaglist, OT_YSizeFactor, YSizeFactor, 0)) &&		// TetiSoft
	     (AddOTag(otaglist, OT_SpaceWidth, SpaceWidth, 0)) &&		// TetiSoft
	     (AddOTag(otaglist, OT_IsFixed, IsFixed, 0)) &&
	     (AddOTag(otaglist, OT_SerifFlag, Presets->SerifFlag, 0)) &&	// TetiSoft
	     (AddOTag(otaglist, OT_StemWeight, StemWeight, 0)) &&		// TetiSoft
	     (AddOTag(otaglist, OT_SlantStyle, SlantStyle, 0)) &&		// TetiSoft
	     (AddOTag(otaglist, OT_HorizStyle, Presets->HorizStyle, 0)) &&	// TetiSoft
	     ((Presets->SpaceFactor == 0) || AddOTag(otaglist, OT_SpaceFactor, Presets->SpaceFactor, 0)) &&	// TetiSoft
	     (AddOTag(otaglist, OT_InhibitAlgoStyle, InhibitAlgoStyle, 0)) &&	// TetiSoft
	     (AddOTag(otaglist, OT_AvailSizes, (ULONG) &Presets->AvailSizes, (Presets->AvailSizes.Count + 1) * sizeof(UWORD))) &&
	     (AddOTag(otaglist, OT_SpecCount, (AFMFileName[0] == '\0') ? 2L : 3L, 0)) &&
	     (AddOTag(otaglist, OT_Spec1_PFABFileName, (ULONG) fullfilename, strlen(fullfilename) + 1)) &&
	     ((AFMFileName[0] == '\0') || AddOTag(otaglist, OT_Spec2_AFMFileName, (ULONG)AFMFileName, strlen(AFMFileName) + 1)) &&
	     (AddOTag(otaglist, OT_Spec6_OwnEncoding, OwnEncoding, 0)) &&	// TetiSoft
	     (AddOTag(otaglist, TAG_DONE, 0, 0)) )
	{
		otagsize = SizeOTagList(otaglist);
		if (otagbuf = (void *)AllocMem(otagsize, MEMF_CLEAR))
		{
			DumpOTagList(otaglist, otagbuf);
			FreeOTagList(otaglist);
			OTags = (struct TagItem *) otagbuf;
			OTags[0].ti_Data = (ULONG) otagsize;
		} else {
			FreeOTagList(otaglist);
			return FALSE;		/* failure allocating otagbuf */
		}
	} else {
		/* Note: FreeOTagList is safe to call with a NULL argument */
		FreeOTagList(otaglist);
		return FALSE;			/* failure allocating otaglist */
	}


/**
 * Write the file to the previously determined otagfilename
 **/
	if (otagfile = Open(otagfilename, MODE_NEWFILE))
	{
		if (Write(otagfile, otagbuf, otagsize) != otagsize)
		{
			MyErrorMsg1(LString(MSG_ERROR_WRITE), otagfilename);
			Close(otagfile);
			FreeMem(otagbuf, otagsize);
			DeleteFile(otagfilename);
			return FALSE;
		}
		Close(otagfile);
		FreeMem(otagbuf, otagsize);
	} else {
		MyErrorMsg1(LString(MSG_ERROR_CREATE_FILE), otagfilename);
		FreeMem(otagbuf, otagsize);
		return FALSE;			/* failure creating otag file */
	}

// Create the .font file:
	if (!Update)
	{
		if (fontfile = Open(fontfilename, MODE_NEWFILE))
		{
			if (Write(fontfile, fontfiledata, sizeof(fontfiledata)) != sizeof(fontfiledata))
			{
				MyErrorMsg1(LString(MSG_ERROR_WRITE), fontfilename);
				Close(fontfile);
				DeleteFile(fontfilename);
				DeleteFile(otagfilename);
				return FALSE;
			}
			Close(fontfile);
		} else {
			MyErrorMsg1(LString(MSG_ERROR_CREATE_FILE), fontfilename);
			DeleteFile(otagfilename);
			return FALSE;			/* failure creating font file */
		}
	}
	return TRUE;
}

// TetiSoft: Get array of available Unicode glyphs
BOOL GetUnicodeArray(char *Type1File)
{
	int i, j, cnt, max, rc;
	static char CurrentType1File[MAXFONTPATH];

	if (!Type1Base)
		return FALSE;
	if (UnicodeArray && !strcmp(Type1File, CurrentType1File))
		return TRUE;
	if (UnicodeArray)
		FreeVec(UnicodeArray);
	CurrentType1File[0] = '\0';
	UnicodeArray = AllocVec(65536, 0);
	if (!UnicodeArray)
	{
		MyErrorMsg(LString(MSG_ERROR_NOMEM));
		return FALSE;
	}
	if (!engine && ((engine = OpenEngine()) == NULL))
	{
		MyErrorMsg(LString(MSG_ERROR_OPEN_ENGINE));
		FreeVec(UnicodeArray);
		UnicodeArray = NULL;
		return FALSE;
	}
	if (((rc = SetInfo(engine, OT_SpecCount, 1, OT_Spec1_PFABFileName, Type1File, TAG_DONE, 0)) != OTERR_Success) ||
	    ((rc = ObtainInfo(engine, OT_Spec7_UnicodeArray, UnicodeArray, TAG_DONE, 0)) != OTERR_Success))
	{
		MyErrorMsg2(LString(MSG_ERROR_GET_UNICODE), rc, Type1File);
//		CloseEngine(engine);
		FreeVec(UnicodeArray);
		UnicodeArray = NULL;
		return FALSE;
	}
//	CloseEngine(engine);
	strcpy(CurrentType1File, Type1File);
	UnicodePages[0] = 1;
	Pages[0] = PageNumbers[0];
	for (i = 1, cnt = 1; i < 256; i++)
	{
		UnicodePages[i] = 0;
		for (j = i * 256, max = (i + 1) * 256; j < max; j++)
		{
			if (UnicodeArray[j])
			{
				UnicodePages[i] = 1;
				Pages[cnt++] = PageNumbers[i];
				break;
			}
		}
	}
	Pages[cnt] = NULL;
	return TRUE;
}

// TetiSoft: OwnEncoding & SymbolSet
BOOL HasOwnEncoding(char *Type1File)
{
	ULONG OwnEncoding = FALSE;
	int rc;

	if (!Type1Base)
		return FALSE;

	if (!engine && ((engine = OpenEngine()) == NULL))
	{
		MyErrorMsg(LString(MSG_ERROR_OPEN_ENGINE));
		return FALSE;
	}

	if (((rc = SetInfo(engine, OT_SpecCount, 1, OT_Spec1_PFABFileName, Type1File, TAG_DONE, 0)) != OTERR_Success) ||
	    ((rc = ObtainInfo(engine, OT_Spec6_OwnEncoding, &OwnEncoding, TAG_DONE, 0)) != OTERR_Success))
		MyErrorMsg2(LString(MSG_ERROR_GET_ENCODING), rc, Type1File);
//	CloseEngine(engine);
	return (BOOL)OwnEncoding;
}

BOOL CalcSpaceWidthYSizeFactor(Object *win, char *Type1File, ULONG IsFixed, ULONG SymbolSet, FIXED *SpaceWidthPtr, ULONG *YSizeFactorPtr)	// TetiSoft
{
	int rc;
	FIXED SpaceWidth = 0;
	FIXED WidthOfSpaceGlyph = 0;
	FIXED maxWidth = 0;
	ULONG YSizeFactor = 0x00010001;
	struct GlyphMap *glyph;
	int unicode, max;

	if (!Type1Base)
		return FALSE;

	if (!engine && ((engine = OpenEngine()) == NULL))
	{
		MyErrorMsg(LString(MSG_ERROR_OPEN_ENGINE));
		return FALSE;
	}

	if (win)
		set (win, MUIA_Window_Sleep, TRUE);

	if ((rc = SetInfo(engine, OT_SpecCount, 1,
				  OT_Spec1_PFABFileName, Type1File,
				  OT_IsFixed, FALSE,	// Otherwise we would always get glm_Width of 0 since SpaceWidth is not set...
				  OT_SymbolSet, SymbolSet,
				  OT_DeviceDPI, (72 << 16) | 72,
				  OT_PointHeight, 100 << 16,
				  TAG_DONE, 0
			 )) == OTERR_Success)
	{
		ULONG gcode;
		int Ascend, Descend;
		int maxAscend = 0, maxDescend = 0;
		if ((SymbolSet == UNICODE) && GetUnicodeArray(Type1File))
			unicode = TRUE;
		else
			unicode = FALSE;
		if (unicode)
			max = 65535;
		else
			max = 255;
		for(gcode = 0; gcode <= max; gcode++)
		{
			if (unicode && !UnicodeArray[gcode])
				continue;
			if (((rc = SetInfo(engine, OT_GlyphCode, gcode, TAG_DONE, 0)) == OTERR_Success) &&
			    ((rc = ObtainInfo(engine, OT_GlyphMap, &glyph, TAG_DONE, 0)) == OTERR_Success))
			{
				if (gcode == ' ')
					WidthOfSpaceGlyph = glyph->glm_Width;
				if (glyph->glm_Width > maxWidth)
					maxWidth = glyph->glm_Width;
				Ascend = glyph->glm_Y0 - glyph->glm_BlackTop;
				if (Ascend > maxAscend)
					maxAscend = Ascend;
				Descend = glyph->glm_BlackTop + glyph->glm_BlackHeight - glyph->glm_Y0;
				if (Descend > maxDescend)
					maxDescend = Descend;
				ReleaseInfo(engine, OT_GlyphMap, glyph, TAG_DONE, 0);
			}
		}
		SpaceWidth = ((((IsFixed ? maxWidth : WidthOfSpaceGlyph) * 2540) >> 16) * 250) / 72;
		YSizeFactor = (100 << 16) | (maxAscend + maxDescend) ;
	}
	if (win)
		set (win, MUIA_Window_Sleep, FALSE);
//	CloseEngine(engine);
	if (!SpaceWidth || ((YSizeFactor & 0xffff) < 10))
	{
		MyErrorMsg2(LString(MSG_ERROR_BUILD_SPACEWIDTH), rc, Type1File);
		return FALSE;
	}
	if (SpaceWidthPtr)
		*SpaceWidthPtr = SpaceWidth;
	if (YSizeFactorPtr)
		*YSizeFactorPtr = YSizeFactor;
	return TRUE;
}

struct MinList *NewOTagList(void)
{
	struct MinList *otaglist;

	if (otaglist = (struct MinList *)AllocMem(sizeof(struct MinList), MEMF_CLEAR))
		NewList((struct List *)otaglist);

	return otaglist;
}



int AddOTag(struct MinList *list, ULONG tag, ULONG data, ULONG size)
{
	struct otagnode *node;

	if (!(node = AllocMem(sizeof(struct otagnode), MEMF_CLEAR)))
		return 0;

	node->ti_Tag = tag;
	node->ti_Data = data;
	node->size = size;
	AddTail( (struct List *)list, (struct Node *)node );

	return 1;
}


ULONG SizeOTagList(struct MinList *list)
{
	struct otagnode *node;
	ULONG size = 0;

	for (node = (struct otagnode *)(list->mlh_Head); node->MyNode.mln_Succ; node = (struct otagnode *)node->MyNode.mln_Succ)
		size += 2 * sizeof(ULONG) + node->size;

	return size;
}


/**
 * Create an otag file in memory
 * first pass through the list, and copy the ti_Tag and ti_Data values
 * Then pass again, and copy the OT_Indirect information over, modifying
 * the corresponding memory ti_Data pointers accordingly.
 **/
void DumpOTagList(struct MinList *list, UBYTE *buf)
{
	struct otagnode *node;
	ULONG *cur;
	UBYTE *ptr;

	cur = (ULONG *)buf;
	for (node = (struct otagnode *)(list->mlh_Head); node->MyNode.mln_Succ; node = (struct otagnode *)node->MyNode.mln_Succ)
	{
		*(cur++) = node->ti_Tag;
		cur++;	/* don't bother setting ti_Data here */
	}

	ptr = (UBYTE *)cur;
	cur = (ULONG *)buf;
	for (node = (struct otagnode *)(list->mlh_Head); node->MyNode.mln_Succ; node = (struct otagnode *)node->MyNode.mln_Succ)
	{
		cur++;		/* Advance to ti_Data field */

		if (node->size > 0)		/* Store indirect data */
		{
			*(cur++) = (ULONG) (ptr - buf);
			CopyMem((void *)node->ti_Data, ptr, node->size);
			ptr += node->size;
		}
		else				/* Copy the ti_Data */
		{
			*(cur++) = (ULONG) node->ti_Data;
		}
	}

	return;
}


void FreeOTagList(struct MinList *list)
{
	struct otagnode *work_node, *next_node;

	if (list == NULL)
		return;

	work_node = (struct otagnode *)(list->mlh_Head);
	while (next_node = (struct otagnode *)(work_node->MyNode.mln_Succ))
	{
		FreeMem(work_node, sizeof(struct otagnode));
		work_node = next_node;
	}
	FreeMem(list, sizeof(struct MinList));
}


/*************************************************************/
/* DoAllAssigns */
/*************************************************************/
 /*
  * This routine accepts a path string that may include a device name.  From
  * that string, this routine locks the object named in the path and calls
  * the function passback_func() on the lock.  DoAllAssigns() should work on
  * paths with assigns and multiassigns, as well as a filesystem-based device
  * (i.e., df0:, work:, ram:, etc.)
  */
BOOL DoAllAssigns(char *dos_path, BOOL (*passback_func) (BPTR lock, void (*passfunc) (char *filename, int filesize)), void (*passfunc) (char *filename, int filesize))
{
	struct DevProc *dp = NULL;
	struct MsgPort *old_fsport;
	BPTR lock, old_curdir;
	char *rest_of_path;
	LONG err;

	while (dp = GetDeviceProc(dos_path, dp)) {
		/*
		 * I need to cut the device name from the front of dos_path
		 * so I can give that substring to Lock().
		 */
		rest_of_path = strchr(dos_path, ':');

		if (rest_of_path == NULL)

			/*
			 * there was no device name to cut off, use the whole
			 * string.
			 */
			rest_of_path = dos_path;
		else
			/* increment string pointer to just past the colon. */
			rest_of_path++;

		old_fsport = SetFileSysTask(dp->dvp_Port);	/* in case dp->dvp_Lock
								 * is NULL */

		/*
		 * Lock() locks relative to the current directory and falls
		 * back to the root of the current file system if
		 * dp->dvp_Lock is NULL
		 */
		old_curdir = CurrentDir(dp->dvp_Lock);

		lock = Lock(rest_of_path, SHARED_LOCK);

		/*
		 * reset the process' default filesystem port and current dir
		 * to their initial values for clean up later
		 */
		SetFileSysTask(old_fsport);
		CurrentDir(old_curdir);

		if (lock) {
			if (!(*passback_func) (lock, passfunc)) {
				UnLock(lock);
				FreeDeviceProc(dp);
				return FALSE;
			}

			UnLock(lock);
		}
	}

	err = IoErr();

	if ((err == ERROR_NO_MORE_ENTRIES) || (err == 0))

		/*
		 * at present, a bug in DOS prevents this case, so
		 * DoAllAssigns() always returns FALSE
		 */
		return TRUE;
	else
		return FALSE;
}


/*************************************************************/
/* Add parts of fonts: assign to an array of string pointers */
/* input: lock == directory lock */
/*************************************************************/
BOOL AddFontdir(BPTR lock, void (*passfunc) (char *filename, int filesize))
{
	extern char *fontdirparts[];
	extern int sizefontdirparts;
	extern int numfontdirparts;
	char fontdir[120];

	if (NameFromLock(lock, fontdir, sizeof(fontdir)))
	{
		if ((numfontdirparts + 1) < sizefontdirparts)
		{
			if (fontdirparts[numfontdirparts] = (char *) AllocVec(1 + strlen(fontdir), 0l))
			{
				strcpy(fontdirparts[numfontdirparts], fontdir);
				numfontdirparts++;
				fontdirparts[numfontdirparts] = NULL;
				return TRUE;
			}
		}
	}

	return FALSE;
}


/*************************************************************/
/* Free the strings in fontdirparts */
/*************************************************************/
void FreeFontdirparts(void)
{
	extern char *fontdirparts[];
	int i;

	if (fontdirparts != NULL)
	{
		for (i=0; fontdirparts[i] != NULL; i++)
			FreeVec(fontdirparts[i]);
	}
}


/*************************************************************/
/* Add installed Type1 fonts in dir to listview */
/* input: lock == directory lock */
/*************************************************************/
BOOL ScanDir(BPTR lock, void (*passfunc) (char *filename, int filesize))
{
	struct ExAllControl *myeac;
	struct ExAllData *myead;
	APTR buffer;
	BOOL done;
	struct FileInfoBlock *myfib;
	BPTR old_curdir;

	if (myfib = AllocDosObject(DOS_FIB, NULL))
	{
		if (Examine(lock, myfib) == DOSTRUE)
		{
			if (myfib->fib_DirEntryType > 0)
			{
				if (buffer = AllocVec(EXALLBUFSIZE, MEMF_PUBLIC))
				{
					if (myeac = AllocDosObject(DOS_EXALLCONTROL, NULL))
					{
						old_curdir = CurrentDir(lock);
						myeac->eac_LastKey = 0L;
						do
						{
							done = ExAll(lock, buffer, EXALLBUFSIZE - 200, ED_SIZE, myeac);

/* Added by TetiSoft, no Enforcer hits in empty directories */
							if (myeac->eac_Entries == 0)
								continue;

							myead = (struct ExAllData *)buffer;
							while (myead)
							{
								(*passfunc)(myead->ed_Name, myead->ed_Size);
								myead = myead->ed_Next;
							}
						} while (done != 0);
						CurrentDir(old_curdir);
						FreeDosObject(DOS_EXALLCONTROL, myeac);
						FreeVec(buffer);
						FreeDosObject(DOS_FIB, myfib);
						return TRUE;
					}
					FreeVec(buffer);
				}
			}
		}
		FreeDosObject(DOS_FIB, myfib);
	}
	return FALSE;
}


void DeleteBitmaps(char *filename, int filesize)
{
	char *ch;

	if (filename)
	{
		for (ch = filename; *ch != '\0'; ch++)
			if ((*ch < '0') || (*ch > '9'))
				break;

		/* Filename is numeric */
		if ((ch != filename) && (*ch == '\0'))

// TetiSoft: report errors
//			DeleteFile(filename);
			if (!DeleteFile(filename))
				MyErrorMsg1(LString(MSG_ERROR_DELETE_BITMAP_FILE), filename);

	}
}


void CheckFile(char *filename, int filesize)
{
	BPTR filehandle;
	char *ch, *filebuf;

	/* Check for .otag files */
	ch = filename;
	while (*ch != '\0') ch++;
	ch -= 5;

/* Added by TetiSoft: Don't read before a short string */
	if (ch <= filename)
		return;

/* TetiSoft: We also use ".ota", ".ot" and ".o" (from old T1Manager) which the filesystem stripped */
/*	if (strcmp(ch, ".otag") == 0) */
	if ((strcmp(ch, ".otag") == 0)||
	    ((strlen(filename)==30)&&((!strcmp(filename+26,".ota"))||(!strcmp(filename+27,".ot"))||(!strcmp(filename+28,".o"))))
	   )

	{
		if (filehandle = Open(filename, MODE_OLDFILE))
		{
			if (filebuf = (char *)AllocVec(filesize, 0L))
			{

// TetiSoft: must be checked
//				Read(filehandle, filebuf, filesize);
				if (Read(filehandle, filebuf, filesize) == filesize)

					if (IsOtagType1(filebuf))
						AddFontToListview(filename, filebuf);
				FreeVec(filebuf);
			}
			Close(filehandle);
		}
	}
}


/*************************************************************/
/* IsOtagType1 */
/*************************************************************/
/*
 * This routine determines whether the otag data in buf is a
 * type1.library otag file.
 */
BOOL IsOtagType1(char *buf)
{
	char *enginename;
	BOOL retvalue = FALSE;

	if (*((LONG *)(buf + 8)) == OT_Engine)
	{
		enginename = buf + *((long *)(buf + 12));
		if (strcmp(enginename, "type1") == 0)
			retvalue = TRUE;
	}

	return retvalue;
}


BOOL ReadOTagFile(char *filename)
{
	struct FileInfoBlock __aligned fib;
	BPTR OTFile;
	struct TagItem *OTags;
	BOOL result;
	int i;

	if (OTagFileBuf)
	{
		FreeVec(OTagFileBuf);
		OTagFileBuf = NULL;
	}
/**
 * Read the otag file into memory, and verify that it is a type1 font otag file
 **/
	result = FALSE;
	if (OTFile = Open(filename, MODE_OLDFILE))
	{
		if (ExamineFH(OTFile, &fib))
		{
			if (OTagFileBuf = (char *)AllocVec(fib.fib_Size, 0L))
			{
				if (Read(OTFile, OTagFileBuf, fib.fib_Size) == fib.fib_Size)
				{
					if (IsOtagType1(OTagFileBuf))
						result = TRUE;
					else
						MyErrorMsg(LString(MSG_ERROR_WRONG_ENGINE));
				} else
					MyErrorMsg1(LString(MSG_ERROR_READ_OTAG_FILE), filename);
			}
		}
		Close(OTFile);
	} else
		MyErrorMsg1(LString(MSG_ERROR_OPEN_OTAG_FILE), filename);
	if (!result)
	{
		if (OTagFileBuf)
		{
			FreeVec(OTagFileBuf);
			OTagFileBuf = NULL;
		}
		return FALSE;
	}
/**
 * Patch indirect pointers in the otag list
 **/
	OTags = (struct TagItem *)OTagFileBuf;
	for (i=0; i < OTags[0].ti_Data/sizeof(struct TagItem); i++)
	{
		if (OTags[i].ti_Tag == TAG_DONE)
			break;
		if (OTags[i].ti_Tag & OT_Indirect)
			OTags[i].ti_Data += (ULONG) OTags;
	}
	return TRUE;
}


/*************************************************************/
/* RenderPreview */
/*************************************************************/
//BOOL RenderPreview(char *filename, ULONG pointheight, struct RastPort *rp, LONG leftedge, LONG topedge, LONG width, LONG height, char *previewstring)
BOOL RenderPreview(char *filename, ULONG pointheight, ULONG lineheight, int fixedwidth, struct RastPort *rp, LONG leftedge, LONG topedge, LONG width, LONG height, unsigned char *previewstring, unsigned char *previewstring2, struct TagItem *AddOTags)
{
	struct TagItem tags[5];
	unsigned char *ch;
	int x, y, dx, dy, baseline;
	BOOL result;
	struct GlyphMap *glyph;
	PLANEPTR Template/* = 0*/;			// TetiSoft
	ULONG emwidth = pointheight /* * xdpi / 72 */;	// TetiSoft
	FIXED kern = 0;					// TetiSoft
	UWORD newwidth;					// TetiSoft
	int num_chars_obtained = 0;
	int linenum;

	if (!Type1Base)
		return FALSE;

	if (!AddOTags && !ReadOTagFile(filename))
		return FALSE;

	if (!engine && ((engine = OpenEngine()) == NULL))
	{
		MyErrorMsg(LString(MSG_ERROR_OPEN_ENGINE));
		return FALSE;
	}

/**
 * Now we open the font with type1.library
 **/
	tags[0].ti_Tag = OT_OTagPath;
	tags[0].ti_Data = (ULONG) filename;

	tags[1].ti_Tag = OT_OTagList;
	tags[1].ti_Data = (ULONG) OTagFileBuf;

	tags[2].ti_Tag = OT_PointHeight;
	tags[2].ti_Data = (ULONG) pointheight << 16;

	tags[3].ti_Tag = OT_DeviceDPI;
	tags[3].ti_Data = (ULONG) (72 << 16) | 72;

	if (AddOTags)	// TetiSoft
	{
		tags[4].ti_Tag = TAG_MORE;
		tags[4].ti_Data = (ULONG) AddOTags;
	} else {
		tags[4].ti_Tag = TAG_DONE;
		tags[4].ti_Data = (ULONG) 0;
	}

	result = SetInfoA(engine, tags);

	if (result == OTERR_Success)
	{
		tags[2].ti_Tag = TAG_DONE;
		tags[2].ti_Data = (ULONG) 0;

//		if (Template = AllocRaster(width, height))
		if (Template = AllocRaster(width, lineheight))	// TetiSoft: This should be exactly big enough
		{
			ULONG drawmodestorage = GetDrMd(rp);

// TetiSoft: One run without blitting just to check the baseline position,
// since we don't have a tf_Baseline from diskfont.library :-(

			int Ascend, Descend, maxAscend = 0, maxDescend = 0;
			for (linenum = 0; linenum <= 1; linenum++)
			{
				if (linenum == 0)
					ch = previewstring;
				else
					ch = previewstring2;
				x = leftedge;
				for ( ; *ch != '\0'; ch++)
				{
					tags[0].ti_Tag = OT_GlyphCode;
					tags[0].ti_Data = (ULONG) *ch;
					tags[1].ti_Tag = OT_GlyphCode2;
					tags[1].ti_Data = (ULONG) (ch[1] ? ch[1] : ' ');
					(void) SetInfoA(engine, tags);
					tags[0].ti_Tag = OT_GlyphMap;
					tags[0].ti_Data = (ULONG) &glyph;
					tags[1].ti_Tag = OT_TextKernPair;
					tags[1].ti_Data = (ULONG) &kern;
					result = ObtainInfoA(engine, tags);
					if (result == OTERR_Success)
					{
						dx = x - glyph->glm_X0 + glyph->glm_BlackLeft;
						if ((dx + glyph->glm_BlackWidth - leftedge) > width)
							break;
						newwidth = ((glyph->glm_Width - kern) * emwidth) / 65536;
						x += newwidth;

						Ascend = glyph->glm_Y0 - glyph->glm_BlackTop;
						Descend = glyph->glm_BlackTop + glyph->glm_BlackHeight - glyph->glm_Y0;
						if (Ascend > maxAscend)
							maxAscend = Ascend;
						if (Descend > maxDescend)
							maxDescend = Descend;
						ReleaseInfo(engine, OT_GlyphMap, glyph, TAG_DONE, 0);
					}
				}
			}
			baseline = (lineheight + maxAscend - maxDescend) / 2;
			SetDrMd(rp, COMPLEMENT);

			for (linenum = 0; linenum <= 1; linenum++)
			{
				if (linenum == 0)
					ch = previewstring;
				else
					ch = previewstring2;
				x = leftedge;
				y = topedge + baseline + linenum * lineheight;

				for ( ; *ch != '\0'; ch++)
				{
					tags[0].ti_Tag = OT_GlyphCode;
					tags[0].ti_Data = (ULONG) *ch;
					tags[1].ti_Tag = OT_GlyphCode2;
					tags[1].ti_Data = (ULONG) (ch[1] ? ch[1] : ' ');
					(void) SetInfoA(engine, tags);

					tags[0].ti_Tag = OT_GlyphMap;
					tags[0].ti_Data = (ULONG) &glyph;
					tags[1].ti_Tag = OT_TextKernPair;
					tags[1].ti_Data = (ULONG) &kern;
					result = ObtainInfoA(engine, tags);

					if (result == OTERR_Success)
					{
						num_chars_obtained++;

						dx = x - glyph->glm_X0 + glyph->glm_BlackLeft;
						dy = y - glyph->glm_Y0 + glyph->glm_BlackTop;

						if ((dx + glyph->glm_BlackWidth - leftedge) > width)
							break;

// TetiSoft: We MUST test the y direction, or we would write beyond our Template memory
						if (((baseline - glyph->glm_Y0 + glyph->glm_BlackTop) >= 0) &&
						    ((baseline - glyph->glm_Y0 + glyph->glm_BlackTop + glyph->glm_BlackHeight) <= lineheight))
						{
							int i;
							for (i = (glyph->glm_BMRows * glyph->glm_BMModulo/4) - 1; i >= 0; i--)
								((ULONG *) Template)[i] = ((ULONG *) glyph->glm_BitMap)[i];

#ifdef PARMDEBUG
							if (*ch == 'f') DebugBltTemplate( (UWORD *)(((char *) Template) + (glyph->glm_BlackTop * glyph->glm_BMModulo) + (glyph->glm_BlackLeft >> 3)),
									glyph->glm_BlackLeft & 0xf,
									glyph->glm_BMModulo,
									rp,
									dx,
									dy,
									glyph->glm_BlackWidth,
									glyph->glm_BlackHeight, *ch);
#endif

							if ((glyph->glm_BlackWidth != 0) || (glyph->glm_BlackHeight != 0))
							{
								if (fixedwidth && ((glyph->glm_BlackLeft + glyph->glm_BlackWidth - glyph->glm_X0) > fixedwidth))
								{
									RectFill(rp, x, y - baseline, x + (fixedwidth - 1), (y - baseline) + (lineheight -1));
									WaitBlit();
								}
								BltTemplate( ((char *) Template) + (glyph->glm_BlackTop * glyph->glm_BMModulo) + (glyph->glm_BlackLeft >> 3),
									glyph->glm_BlackLeft & 0xf,
									glyph->glm_BMModulo,
									rp,
									dx,
									dy,
									glyph->glm_BlackWidth,
									glyph->glm_BlackHeight);
								WaitBlit();
							}
						} else {
							RectFill(rp, x, y - baseline, dx + (glyph->glm_BlackWidth - 1), (y - baseline) + (lineheight -1));
							WaitBlit();
						}
						newwidth = ((glyph->glm_Width - kern) * emwidth) / 65536;
						x += newwidth;
						ReleaseInfo(engine, OT_GlyphMap, glyph, TAG_DONE, 0);
					}
//else{
//void __stdargs kprintf(char *, ...);
//kprintf("ObtainInfo rc %ld\n", result);
//}
				}
			}

			if (num_chars_obtained == 0)
				MyErrorMsg(LString(MSG_ERROR_NO_GLYPHS));
			WaitBlit();
			FreeRaster(Template, width, lineheight);
			SetDrMd(rp, drawmodestorage);
		}
	}
	else
		MyErrorMsg1(LString(MSG_ERROR_TYPE1_RC), result);

//	CloseEngine(engine);
	if (num_chars_obtained > 0)
		return TRUE;
	else
		return FALSE;
}
