/*--------------------------------------------------------------------*//*:Ignore this sentence.
Copyright (C) 1999, 2001 SIL International. All rights reserved.

Distributable under the terms of either the Common Public License or the
GNU Lesser General Public License, as specified in the LICENSING.txt file.

File: GrSlotState.cpp
Responsibility: Sharon Correll
Last reviewed: Not yet.

Description:
    GrSlotState class implementation.
-------------------------------------------------------------------------------*//*:End Ignore*/

//:>********************************************************************************************
//:>	Include files
//:>********************************************************************************************
#include "Main.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif
#undef THIS_FILE
DEFINE_THIS_FILE

//:>********************************************************************************************
//:>	Forward declarations
//:>********************************************************************************************

//:>********************************************************************************************
//:>	Local Constants and static variables
//:>********************************************************************************************

namespace gr
{

//:>********************************************************************************************
//:>	Methods
//:>********************************************************************************************

/*----------------------------------------------------------------------------------------------
	Initialize slots.
----------------------------------------------------------------------------------------------*/
//	standard for pass 0 slots
void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng,
	GrFeatureValues fval, int ipass, int ichwSegOffset, int nUnicode)
{
	Assert(ipass == 0);
	m_chwGlyphID = chw;
	m_chwActual = kInvalidGlyph;
	m_xysGlyphWidth = kNegInfFloat;
	m_bStyleIndex = byte(fval.m_nStyleIndex);
	u_intslot nullSlot;
	nullSlot.pslot = NULL;
	std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot);
	std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot);
	std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot);
	u_intslot * pFeatBuf = PFeatureBuf();
	for (size_t i = 0; i < m_cnFeat; i++)
		pFeatBuf[i].nValue = fval.m_rgnFValues[i];
	m_ipassFsmCol = -1;
	m_colFsm = -1;

	m_ipassModified = ipass;
	m_ichwSegOffset = ichwSegOffset;
	m_nUnicode = nUnicode;
	m_vpslotAssoc.clear();
	pgreng->InitSlot(this, nUnicode);

	switch (nUnicode)
	{
	case knLRM:	m_spsl = kspslLRM; break;
	case knRLM:	m_spsl = kspslRLM; break;
	case knLRO:	m_spsl = kspslLRO; break;
	case knRLO:	m_spsl = kspslRLO; break;
	case knLRE:	m_spsl = kspslLRE; break;
	case knRLE:	m_spsl = kspslRLE; break;
	case knPDF:	m_spsl = kspslPDF; break;
	default:
		Assert(m_spsl == kspslNone);
		m_spsl = kspslNone;
		break;
	}
}

//	line-break slots
void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng,
	GrSlotState * pslotFeat, int ipass, int ichwSegOffset)
{
	m_chwGlyphID = chw;
	m_chwActual = kInvalidGlyph;
	m_xysGlyphWidth = kNegInfFloat;
	u_intslot nullSlot;
	nullSlot.pslot = NULL;
	std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot);
	std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot);
	std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot);
	CopyFeaturesFrom(pslotFeat);
	m_ipassModified = ipass;
	m_ichwSegOffset = ichwSegOffset;
	m_nUnicode = -1;
	m_vpslotAssoc.clear();
	pgreng->InitSlot(this);
	// Caller is responsible for setting m_spsl.
	m_ipassFsmCol = -1;
	m_colFsm = -1;
}

//	for inserting new slots after pass 0 (under-pos and unicode are irrelevant)
void GrSlotState::Initialize(gid16 chw, GrEngine * pgreng,
	GrSlotState * pslotFeat, int ipass)
{
	m_chwGlyphID = chw;
	m_chwActual = kInvalidGlyph;
	m_xysGlyphWidth = kNegInfFloat;
	u_intslot nullSlot;
	nullSlot.pslot = NULL;
	std::fill_n(PUserDefnBuf(), m_cnUserDefn, nullSlot);
	std::fill_n(PCompRefBuf(), m_cnCompPerLig, nullSlot);
	std::fill_n(PSlatiBuf(), m_cnCompPerLig, nullSlot);
	CopyFeaturesFrom(pslotFeat);
	m_ipassModified = ipass;
	m_ichwSegOffset = kInvalid;
	m_nUnicode = kInvalid;
	m_vpslotAssoc.clear();
	pgreng->InitSlot(this);
	m_spsl = kspslNone;
	m_ipassFsmCol = -1;
	m_colFsm = -1;
}

/*----------------------------------------------------------------------------------------------
	The slot has been modified by the given pass and therefore is in a new state;
	make a new SlotState initialized from the old one.
----------------------------------------------------------------------------------------------*/
void GrSlotState::InitializeFrom(GrSlotState * pslotOld, int ipass)
{
	CopyFrom(pslotOld, false);

	m_ipassModified = ipass;
	m_pslotPrevState = pslotOld;
	m_ichwSegOffset = kInvalid;
	m_vpslotAssoc.clear();
	m_vpslotAssoc.push_back(pslotOld);

	m_dircProc = pslotOld->m_dircProc;
	m_fDirProcessed = pslotOld->m_fDirProcessed;

	// Since we're going on to a new pass, no point in copying these:
	m_ipassFsmCol = -1;	
	m_colFsm = -1;

	////FixAttachmentTree(pslotOld);
}

/*----------------------------------------------------------------------------------------------
	Copy the features and style information from the given slot.
----------------------------------------------------------------------------------------------*/
void GrSlotState::CopyFeaturesFrom(GrSlotState * pslotSrc)
{
	m_bStyleIndex = pslotSrc->m_bStyleIndex;
	Assert(m_cnFeat == pslotSrc->m_cnFeat);
	std::copy(pslotSrc->PFeatureBuf(), pslotSrc->PFeatureBuf() + m_cnFeat, PFeatureBuf());
}

/*----------------------------------------------------------------------------------------------
	Copy the basic information.
	Warning: the functions below will break if GrSlotState and subclasses are given virtual
	methods. In that case, we will need to copy from the address of the first variable in
	GrSlotAbstract.
----------------------------------------------------------------------------------------------*/
void GrSlotState::CopyFrom(GrSlotState * pslot, bool fCopyEverything)
{
	GrSlotAbstract::CopyAbstractFrom(pslot);
	std::copy(pslot->m_prgnVarLenBuf, pslot->m_prgnVarLenBuf + CExtraSpace(), m_prgnVarLenBuf);

	if (fCopyEverything)
	{
		Assert(false);
		m_ipassModified = pslot->m_ipassModified;
		m_pslotPrevState = pslot->m_pslotPrevState;
		m_ichwSegOffset = pslot->m_ichwSegOffset;
		m_colFsm = pslot->m_colFsm;
		m_ipassFsmCol = pslot->m_ipassFsmCol;
		// TODO: copy m_vpslotAssocs.
		m_fNeutralAssocs = pslot->m_fNeutralAssocs;
	}

	m_dislotRootFixed = pslot->m_dislotRootFixed;

	m_vdislotAttLeaves.resize(pslot->m_vdislotAttLeaves.size());
	for (size_t i = 0; i < pslot->m_vdislotAttLeaves.size(); i++)
		m_vdislotAttLeaves[i] = pslot->m_vdislotAttLeaves[i];

	m_islotPosPass = pslot->m_islotPosPass;
	m_nUnicode = pslot->m_nUnicode;
	m_dircProc = pslot->m_dircProc;
	m_fDirProcessed = pslot->m_fDirProcessed;
	m_cnUserDefn = pslot->m_cnUserDefn;
	m_cnFeat = pslot->m_cnFeat;
	m_bStyleIndex = pslot->m_bStyleIndex;
	m_mAdvanceX = pslot->m_mAdvanceX;
	m_mAdvanceY = pslot->m_mAdvanceY;
	m_mShiftX = pslot->m_mShiftX;
	m_mShiftY = pslot->m_mShiftY;
	m_srAttachTo = pslot->m_srAttachTo;
	m_nAttachLevel = pslot->m_nAttachLevel;
	m_mAttachAtX = pslot->m_mAttachAtX;
	m_mAttachAtY = pslot->m_mAttachAtY;
	m_mAttachAtXOffset = pslot->m_mAttachAtXOffset;
	m_mAttachAtYOffset = pslot->m_mAttachAtYOffset;
	m_mAttachWithX = pslot->m_mAttachWithX;
	m_mAttachWithY = pslot->m_mAttachWithY;
	m_mAttachWithXOffset = pslot->m_mAttachWithXOffset;
	m_mAttachWithYOffset = pslot->m_mAttachWithYOffset;
	m_nAttachAtGpoint = pslot->m_nAttachAtGpoint;
	m_nAttachWithGpoint = pslot->m_nAttachWithGpoint;
	m_fAttachMod = pslot->m_fAttachMod;
	m_fShiftMod = pslot->m_fShiftMod;
	m_fIgnoreAdvance = pslot->m_fIgnoreAdvance;

	m_nCompositeLevel = kNegInfinity;	// uncalculated, so don't need to copy the positions??
}

void GrSlotAbstract::CopyAbstractFrom(GrSlotState * pslot)
{
	u_intslot * pnBufSave = m_prgnVarLenBuf;
	*this = *pslot;
	m_prgnVarLenBuf = pnBufSave;
	Assert(m_prgnVarLenBuf);
}

/*----------------------------------------------------------------------------------------------
	Initialize the output slot from the one use within the passes, with the basic information.
	Warning: this function will break if GrSlotState and subclasses are given virtual
	methods. In that case, we will need to copy from the address of the first variable in
	GrSlotAbstract.
----------------------------------------------------------------------------------------------*/
void GrSlotOutput::InitializeOutputFrom(GrSlotState * pslot)
{
	CopyAbstractFrom(pslot);

	// Copy just the component information, which is of length (m_cnCompPerLig * 2)
	//std::copy(pslot->PCompRefBuf(),
	//	pslot->PCompRefBuf() + (m_cnCompPerLig * 2),
	//	this->PCompRefBufSlout());
}

/*----------------------------------------------------------------------------------------------
	Return the ID of the actual glyph that will be used for output and metrics. This is the
	same for most glyphs, but will be different for pseudo-glyphs.
----------------------------------------------------------------------------------------------*/
gid16 GrSlotAbstract::ActualGlyphForOutput(GrTableManager * ptman)
{
	if (m_chwActual == kInvalidGlyph)
		m_chwActual = ptman->ActualGlyphForOutput(m_chwGlyphID);
	return m_chwActual;
}
/*----------------------------------------------------------------------------------------------
	We are replacing the old slot with the recipient. Replace the pointers in any attachment
	root or attached leaf slots.
	OBSOLETE
----------------------------------------------------------------------------------------------*/
void GrSlotState::FixAttachmentTree(GrSlotState * pslotOld)
{
#if 0
	pslotOld->m_vpslotAttLeaves.CopyTo(m_vpslotAttLeaves);
	for (int islot = 0; islot < m_vpslotAttLeaves.Size(); islot++)
	{
		Assert(m_vpslotAttLeaves[islot]->m_pslotAttRoot == pslotOld);
		m_vpslotAttLeaves[islot]->m_pslotAttRoot = this;
	}

	m_pslotAttRoot = pslotOld->m_pslotAttRoot;
	if (m_pslotAttRoot)
	{
		for (int islot = 0; islot < m_pslotAttRoot->m_vpslotAttLeaves.Size(); islot++)
		{
			if (m_pslotAttRoot->m_vpslotAttLeaves[islot] == pslotOld)
			{
				m_pslotAttRoot->m_vpslotAttLeaves.Delete(islot);
				m_pslotAttRoot->m_vpslotAttLeaves.Push(this);
				return;
			}
		}
		Assert(false);	// didn't find old slot in the list
	}
#endif // 0
}

/*----------------------------------------------------------------------------------------------
	Make sure all the values are cached that are needed to be copied to the output slots
----------------------------------------------------------------------------------------------*/
void GrSlotState::EnsureCacheForOutput(GrTableManager * ptman)
{
	// Make sure the actual glyph ID is set.
	gid16 gidActual = ActualGlyphForOutput(ptman);

	// Make sure the glyph metrics are stored.
	Font * pfont = ptman->State()->GetFont();
	if (IsLineBreak(ptman->LBGlyphID()))
	{
		GetGlyphMetric(pfont, kgmetAscent, 0);
		GetGlyphMetric(pfont, kgmetDescent, 0);
		m_xysGlyphX = 0;
		m_xysGlyphY = 0;
		m_xysGlyphHeight = 0;
		m_xysGlyphWidth = 0;
		m_xysAdvX = 0;
		m_xysAdvY = 0;
		m_bIsSpace = true;
	}
	else
	{
		//IsSpace(ptman); // cache this flag--doing bb-top below will do it

		GetGlyphMetric(pfont, kgmetAscent, gidActual);
		GetGlyphMetric(pfont, kgmetDescent, gidActual);
		GetGlyphMetric(pfont, kgmetBbTop, gidActual);
		// call above will also cache all the values below
		//GetGlyphMetric(pfont, kgmetBbBottom, gidActual);
		//GetGlyphMetric(pfont, kgmetBbLeft, gidActual);
		//GetGlyphMetric(pfont, kgmetBbRight, gidActual);
		//GetGlyphMetric(pfont, kgmetAdvWidth, gidActual);
		//GetGlyphMetric(pfont, kgmetAdvHeight, gidActual);
	}
}

/*----------------------------------------------------------------------------------------------
	Set the associations for the slot.
----------------------------------------------------------------------------------------------*/
void GrSlotState::Associate(GrSlotState * pslot)
{
	m_vpslotAssoc.clear();
	m_vpslotAssoc.push_back(pslot);
}

void GrSlotState::Associate(GrSlotState * pslotBefore, GrSlotState * pslotAfter)
{
	m_vpslotAssoc.clear();
	m_vpslotAssoc.push_back(pslotBefore);
	m_vpslotAssoc.push_back(pslotAfter);
}

void GrSlotState::Associate(std::vector<GrSlotState*> & vpslot)
{
	m_vpslotAssoc.clear();
	///vpslot.CopyTo(m_vpslotAssoc);  -- bug in CopyTo, so we do it ourselves:
	for (size_t islot = 0; islot < vpslot.size(); ++islot)
	{
		m_vpslotAssoc.push_back(vpslot[islot]);
	}

	//	Set its character styles and features from the associated slot.
	if (vpslot.size() > 0) // && !m_pslotPrevState
	{
		std::copy(m_vpslotAssoc[0]->PFeatureBuf(),
				  m_vpslotAssoc[0]->PFeatureBuf() + m_cnFeat, PFeatureBuf());
	}
}

/*----------------------------------------------------------------------------------------------
	Clear the associations for the slot.
----------------------------------------------------------------------------------------------*/
void GrSlotState::ClearAssocs()
{
	m_vpslotAssoc.clear();
}

/*----------------------------------------------------------------------------------------------
	Return a list of (ie, add into the vector) all the underlying associations, relative
	to the official beginning of the segment.
----------------------------------------------------------------------------------------------*/
void GrSlotState::AllAssocs(std::vector<int> & vichw)
{
	if (PassModified() == 0)
	{
		Assert(m_ichwSegOffset != kInvalid);
		vichw.push_back(m_ichwSegOffset);
	}
	else
	{
		for (size_t i = 0; i < m_vpslotAssoc.size(); ++i)
			m_vpslotAssoc[i]->AllAssocs(vichw);
	}
}

/*----------------------------------------------------------------------------------------------
	Return the underlying position of the before-association, relative to the official
	beginning of the segment. May be -1, if this slot maps to a character in the previous
	segment, or >= the length of the segment, if it maps to a character in the following
	segment.
----------------------------------------------------------------------------------------------*/
int GrSlotState::BeforeAssoc()
{
	GrSlotState * pslot = this;
	while (pslot->PassModified() > 0)
	{
		pslot = pslot->RawBeforeAssocSlot();
		if (pslot == NULL)
		{
			return kPosInfinity;
		}
	}
	Assert(pslot->m_ichwSegOffset != kInvalid);
	return pslot->m_ichwSegOffset;
}

/*----------------------------------------------------------------------------------------------
	Return the underlying position of the after-association, relative to the official
	beginning of the segment. May be -1, if this slot maps to a character in the previous
	segment, or >= the length of the segment, if it maps to a character in the following
	segment.
----------------------------------------------------------------------------------------------*/
int GrSlotState::AfterAssoc()
{
	GrSlotState * pslot = this;
	while (pslot->PassModified() > 0)
	{
		pslot = pslot->RawAfterAssocSlot();
		if (pslot == NULL)
		{
			return kNegInfinity;
		}
	}
	Assert(pslot->m_ichwSegOffset != kInvalid);
	return pslot->m_ichwSegOffset;
}

/*----------------------------------------------------------------------------------------------
	It is possible to get into a state where we are associated with an invalid state.
	For instance, slot C may be associated with slots B1 and B2, but slot B1 is not associated
	with any earlier slot, in which case slot C should remove the association with B1 and just
	be associated with B2.
----------------------------------------------------------------------------------------------*/
void GrSlotState::CleanUpAssocs()
{
	for (size_t i = 0; i < m_vpslotAssoc.size(); i++)
		m_vpslotAssoc[i]->CleanUpAssocs();

	GrSlotState * pslot;

	pslot = RawBeforeAssocSlot();
	while (pslot && BeforeAssoc() == kPosInfinity)
	{
		//	The before association is bogus--delete it.
		m_vpslotAssoc.erase(m_vpslotAssoc.begin());
		pslot = RawBeforeAssocSlot();
	}

	pslot = RawAfterAssocSlot();
	while (pslot && AfterAssoc() == kNegInfinity)
	{
		//	The after association is bogus--delete it.
		m_vpslotAssoc.pop_back();
		pslot = RawAfterAssocSlot();
	}
}

/*----------------------------------------------------------------------------------------------
	Fill in the array of the given output slot with the underlying positions of the
	ligature components, relative to the official beginning of the segment.
	Positions may be < 0, if this slot maps to a character in the previous segment,
	or >= the length of the segment, if it maps to a character in the following segment.
----------------------------------------------------------------------------------------------*/
void GrSlotState::SetComponentRefsFor(GrSlotOutput * pslout, int slatiArg)
{
	if (PassModified() > 0)
	{
		GrSlotState * pslot;
		int slati;
		if (HasComponents())
		{
			for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++)
			{
				pslot = CompRefSlot(iComponent);
				slati = Slati(iComponent);
				if (pslot)
				{
					Assert(slati != -1);
					Assert(PassModified() >= pslot->PassModified());
					pslot->SetComponentRefsFor(pslout, slati);
				}
			}
		}
		else
		{
			// Follow the chain back through the passes.
			for (int islot = 0; islot < AssocsSize(); islot++)
			{
				pslot = m_vpslotAssoc[islot];
				if (pslot)
				{
					Assert(PassModified() >= pslot->PassModified());
					// Passing slati here is definitely needed for our Arabic font: for instance when
					// you type something like "mla", so the l is transformed into a temporary glyph
					// before creating the ligature. However, this seems to have broken something,
					// which I will probably find eventually.  :-(
					pslot->SetComponentRefsFor(pslout, slatiArg);
					//pslot->SetComponentRefsFor(pslout, -1);
				}
			}
		}
	}
	else
	{
		Assert(m_ichwSegOffset != kInvalid);
		pslout->AddComponentReference(m_ichwSegOffset, slatiArg);
	}
}

/*----------------------------------------------------------------------------------------------
	Fill in the vector with the underlying positions of the
	ligature components, relative to the official beginning of the segment.
	Positions may be < 0, if this slot maps to a character in the previous segment,
	or >= the length of the segment, if it maps to a character in the following segment.
	ENHANCE: merge with method above.
----------------------------------------------------------------------------------------------*/
void GrSlotState::AllComponentRefs(std::vector<int> & vichw, std::vector<int> & vicomp,
	int iComponent)
{
	if (PassModified() > 0)
	{
		GrSlotState * pslot;
		if (HasComponents())
		{
			for (int iComponent = 0; iComponent < m_cnCompPerLig; iComponent++)
			{
				pslot = CompRefSlot(iComponent);
				if (pslot)
				{
					Assert(PassModified() >= pslot->PassModified());
					pslot->AllComponentRefs(vichw, vicomp, iComponent);
				}
			}
		}
		else
		{
			for (int islot = 0; islot < AssocsSize(); islot++)
			{
				m_vpslotAssoc[islot]->AllComponentRefs(vichw, vicomp, iComponent);
			}
		}
	}
	else
	{
		Assert(m_ichwSegOffset != kInvalid);
		vichw.push_back(m_ichwSegOffset);
		vicomp.push_back(iComponent);
	}
}

/*----------------------------------------------------------------------------------------------
	Get the value of the component.???.ref attribute for the slot.
	Note that 'i' is the local index for the component as defined for this glyph.
----------------------------------------------------------------------------------------------*/
GrSlotState * GrSlotState::CompRefSlot(int i)
{
	Assert(i < m_cnCompPerLig);
	if (i < m_cnCompPerLig)
		return CompRef(i);
	else
		return NULL;
}

/*----------------------------------------------------------------------------------------------
	Set the value of the component.???.ref attribute for the slot.
----------------------------------------------------------------------------------------------*/
void GrSlotState::SetCompRefSlot(GrTableManager * ptman, int slati, GrSlotState * pslotComp)
{
	//	Convert the global identifer for the component to the index for this glyph.
	int icomp = ptman->ComponentIndexForGlyph(GlyphID(), slati);
	Assert(icomp != -1);
	if (icomp == -1)
		//	Component not defined for this glyph--ignore.
		return;
	Assert(icomp < m_cnCompPerLig);

	if (m_fHasComponents == false)
	{
		//	None have been set yet; initialize them.
		for (int iLoop = 0; iLoop < m_cnCompPerLig; iLoop++)
		{
			SetCompRef(iLoop, NULL);
			SetSlati(iLoop, -1);
		}
	}

	m_fHasComponents = true;
	int iLoop;
	for (iLoop = 0; iLoop < m_cnCompPerLig; iLoop++)
	{
		if (Slati(iLoop) == slati)
			break;
		if (Slati(iLoop) == -1)
			break;
	}
	//if (icomp < m_cnCompPerLig)
	if (iLoop < m_cnCompPerLig)
	{
		//SetCompRef(icomp, pslotComp);
		SetCompRef(iLoop, pslotComp);
		SetSlati(iLoop, slati);
	}
}

/*----------------------------------------------------------------------------------------------
	Return the underlying position for the slot, relative to the official beginning of the
	segment.
----------------------------------------------------------------------------------------------*/
int GrSlotState::SegOffset()
{
	if (m_ichwSegOffset == kInvalid)
	{
		Assert(m_pslotPrevState);
		Assert(m_ipassModified > 0);
		return m_pslotPrevState->SegOffset();
	}
	else
	{
		Assert(m_pslotPrevState == NULL);
		Assert(m_ipassModified == 0);
		return m_ichwSegOffset;
	}
}

/*----------------------------------------------------------------------------------------------
	Return the value of the glyph attribute (in design units, if this is a measurement).
----------------------------------------------------------------------------------------------*/
int GrSlotState::GlyphAttrValueEmUnits(GrTableManager * ptman, int nAttrID)
{
	return ptman->GlyphAttrValue(m_chwGlyphID, nAttrID);
}

/*----------------------------------------------------------------------------------------------
	Return the value of the glyph metric, in design coordinates (ie, based on the font's
	em-square).
----------------------------------------------------------------------------------------------*/
int GrSlotState::GlyphMetricEmUnits(GrTableManager * ptman, int nMetricID)
{
	int mValue;
	if (ptman->State()->GetFont())
	{
		//	Get the actual metric, possibly adjusted for hinting, then convert back to
		//	design units.
		float xysValue = GlyphMetricLogUnits(ptman, nMetricID);
		mValue = ptman->LogToEmUnits(xysValue);
	}
	else
		//	Ask the font directly.
		////mValue = ???
		mValue = 0;

	return mValue;
}

/*----------------------------------------------------------------------------------------------
	Return the value of the glyph attribute, converted to source device coordinates.
	The attribute is assumed to be one whose value is a measurement.
----------------------------------------------------------------------------------------------*/
float GrSlotState::GlyphAttrValueLogUnits(GrTableManager * ptman, int nAttrID)
{
	int mValue = GlyphAttrValueEmUnits(ptman, nAttrID);
	float xysRet = ptman->EmToLogUnits(mValue);
	return xysRet;
}

/*----------------------------------------------------------------------------------------------
	Return the value of the glyph metric, in the source device's logical units.
----------------------------------------------------------------------------------------------*/
float GrSlotState::GlyphMetricLogUnits(GrTableManager * ptman, int nMetricID)
{
#ifdef OLD_TEST_STUFF
	if (ptman->GlyphTable() == NULL)
		return 0;
#endif // OLD_TEST_STUFF

#ifdef _DEBUG
	if (IsLineBreak(ptman->LBGlyphID()))
	{
		Warn("Getting metrics of line-break slot");
	}
#endif // _DEBUG
	if (IsLineBreak(ptman->LBGlyphID()))
	{
		return 0;
	}

	return GetGlyphMetric(ptman->State()->GetFont(), nMetricID,
		ActualGlyphForOutput(ptman));
}


float GrSlotOutput::GlyphMetricLogUnits(Font * pfont, int nMetricID)
{
	Assert(m_chwActual != kInvalidGlyph);
	if (m_chwActual == kInvalidGlyph)
		return 0;

	return GetGlyphMetric(pfont, nMetricID, m_chwActual);
}

//float GrSlotOutput::GlyphMetricLogUnits(int gmet) -- obsolete: no longer caching these values
//{
//	// When the font is not passed as an argument, the values better be cached!
//
//	switch (gmet)
//	{ // There may be off by one errors below, depending on what width and height mean
//	case kgmetLsb:
//		return m_xysGlyphX;
//	case kgmetRsb:
//		return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth);
//	case kgmetBbTop:
//		return m_xysGlyphY;
//	case kgmetBbBottom:
//		return (m_xysGlyphY - m_xysGlyphHeight);
//	case kgmetBbLeft:
//		return m_xysGlyphX;
//	case kgmetBbRight:
//		return (m_xysGlyphX + m_xysGlyphWidth);
//	case kgmetBbHeight:
//		return m_xysGlyphHeight;
//	case kgmetBbWidth:
//		return m_xysGlyphWidth;
//	case kgmetAdvWidth:
//		return m_xysAdvX;
//	case kgmetAdvHeight:
//		return m_xysAdvY;
//	default:
//		Warn("GetGlyphMetric was asked for an illegal metric.");
//	};
//
//	return 0;		
//}

float GrSlotState::GetGlyphMetric(Font * pfont, int nMetricID, gid16 chwGlyphID)
{
	GlyphMetric gmet = GlyphMetric(nMetricID);
	switch (gmet)
	{
	case kgmetAscent:
	case kgmetDescent:
		if (m_xysFontAscent == kNegInfFloat)
		{
			m_xysFontAscent = GrSlotAbstract::GetGlyphMetric(pfont, kgmetAscent, chwGlyphID);
			m_xysFontDescent = GrSlotAbstract::GetGlyphMetric(pfont, kgmetDescent, chwGlyphID);
		}
		break;
	default:
		if (m_xysGlyphWidth == kNegInfFloat)
		{
			GetGlyphMetricAux(pfont, chwGlyphID, m_xysGlyphX, m_xysGlyphY, m_xysGlyphWidth,
				m_xysGlyphHeight, m_xysAdvX, m_xysAdvY, m_bIsSpace);
		}
		break;
	}

	switch (gmet)
	{ // There may be off-by-one errors below, depending on what width and height mean
	case kgmetAscent:
		return m_xysFontAscent;
	case kgmetDescent:
		return m_xysFontDescent;
	case kgmetLsb:
		return m_xysGlyphX;
	case kgmetRsb:
		return (m_xysAdvX - m_xysGlyphX - m_xysGlyphWidth);
	case kgmetBbTop:
		return m_xysGlyphY;
	case kgmetBbBottom:
		return (m_xysGlyphY - m_xysGlyphHeight);
	case kgmetBbLeft:
		return m_xysGlyphX;
	case kgmetBbRight:
		return (m_xysGlyphX + m_xysGlyphWidth);
	case kgmetBbHeight:
		return m_xysGlyphHeight;
	case kgmetBbWidth:
		return m_xysGlyphWidth;
	case kgmetAdvWidth:
		return m_xysAdvX;
	case kgmetAdvHeight:
		return m_xysAdvY;
	default:
		Warn("GetGlyphMetric was asked for an illegal metric.");
	};

	return 0;
}

float GrSlotAbstract::GetGlyphMetric(Font * pfont, int nMetricID, gid16 chwGlyphID)
{
	GlyphMetric gmet = GlyphMetric(nMetricID);

	float yAscent, yDescent;

	if (kgmetAscent == gmet)
	{
		//if (m_xysFontAscent != -1)
		//	return m_xysFontAscent;
		pfont->getFontMetrics(&yAscent);
		//m_xysFontAscent = xysRet;
		//if (pfont)
		//	m_xysFontAscent = yAscent;
		return yAscent;
	}

	if (kgmetDescent == gmet)
	{
		//if (m_xysFontDescent != -1)
		//	return m_xysFontDescent;
		pfont->getFontMetrics(NULL, &yDescent);
		//m_xysFontDescent = xysRet;
		//if (pfont)
		//	m_xysFontDescent = yDescent;
		return yDescent;
	}

	float xysGlyphX, xysGlyphY, xysGlyphWidth, xysGlyphHeight, xysAdvX, xysAdvY;
	GetGlyphMetricAux(pfont, chwGlyphID, xysGlyphX, xysGlyphY, xysGlyphWidth,
		xysGlyphHeight, xysAdvX, xysAdvY, m_bIsSpace);

	switch (gmet)
	{ // There may be off-by-one errors below, depending on what width and height mean
	case kgmetLsb:
		return xysGlyphX;
	case kgmetRsb:
		return (xysAdvX - xysGlyphX - xysGlyphWidth);
	case kgmetBbTop:
		return xysGlyphY;
	case kgmetBbBottom:
		return (xysGlyphY - xysGlyphHeight);
	case kgmetBbLeft:
		return xysGlyphX;
	case kgmetBbRight:
		return (xysGlyphX + xysGlyphWidth);
	case kgmetBbHeight:
		return xysGlyphHeight;
	case kgmetBbWidth:
		return xysGlyphWidth;
	case kgmetAdvWidth:
		return xysAdvX;
	case kgmetAdvHeight:
		return xysAdvY;
	default:
		Warn("GetGlyphMetric was asked for an illegal metric.");
	};

	return 0;		
}

void GrSlotAbstract::GetGlyphMetricAux(Font * pfont, gid16 chwGlyphID,
	float & xysGlyphX, float & xysGlyphY,
	float & xysGlyphWidth, float & xysGlyphHeight, float & xysAdvX, float & xysAdvY, sdata8 & bIsSpace)
{
	gr::Point ptAdvances;
	gr::Rect rectBb;
	pfont->getGlyphMetrics(chwGlyphID, rectBb, ptAdvances);

	xysGlyphX = rectBb.left;
	xysGlyphY = rectBb.top;
	xysGlyphWidth = (rectBb.right - rectBb.left);
	xysGlyphHeight = (rectBb.top - rectBb.bottom);
	xysAdvX = ptAdvances.x;
	xysAdvY = ptAdvances.y;

	bIsSpace = (0 == xysGlyphX && 0 == xysGlyphY); // should agree with test done in IsSpace() below

	if (bIsSpace == 1)
	{
		// White space glyph - only case where nGlyphX == nGlyphY == 0
		// nGlyphWidth & nGlyphHeight are always set to 16 for unknown reasons, so correct.
		xysGlyphWidth = xysGlyphHeight = 0; 
	}
}

/*----------------------------------------------------------------------------------------------
	Test the glyph id to see if it is a white space glyph.
----------------------------------------------------------------------------------------------*/
int GrSlotState::IsSpace(GrTableManager * ptman)
{
	gid16 gidActual = ActualGlyphForOutput(ptman);

	////if (m_xysGlyphWidth == -1)
	////{
	////	res = ptman->State()->GraphicsObject()->GetGlyphMetrics(gidActual,
	////		&m_xysGlyphWidth, &m_xysGlyphHeight,
	////		&m_xysGlyphX, &m_xysGlyphY, &m_xysAdvX, &m_xysAdvY);
	////	if (ResultFailed(res)) 
	////	{
	////		WARN(res);
	////		return 2; // will test as true but can be distinguished by separate value
	////	}

	////	gr::Point ptAdvances;
	////	gr::Rect rectBb;
	////	ptman->State()->Font()->getGlyphMetrics(gidActual, rectBb, ptAdvances);
	////}

	if (m_bIsSpace == -1)
		GetGlyphMetric(ptman->State()->GetFont(), kgmetBbLeft, gidActual);
	// One call is enough to cache the information.
	//GetGlyphMetric(ptman->State()->Font(), kgmetBbBottom, gidActual);

	// should agree with test done in GetGlyphMetric() above
	//////m_bIsSpace = (0 == m_xysGlyphX && 0 == m_xysGlyphY);

	Assert(m_bIsSpace == 0 || m_bIsSpace == 1);

	return m_bIsSpace;
}

bool GrSlotOutput::IsSpace()
{
	Assert(m_bIsSpace == 0 || m_bIsSpace == 1);
	return m_bIsSpace;
}

/*----------------------------------------------------------------------------------------------
	If any of the attach attributes have been modified, fix things up. Set up the attachment
	tree, and set any needed default positions. Zap the cached positions of any following
	glyphs in the stream.

	@param psstrm			- the stream in which the modifications were made
	@param islotThis		- the index of the attached (leaf) slot in the stream;
								-1 if we don't know
----------------------------------------------------------------------------------------------*/
void GrSlotState::HandleModifiedPosition(GrTableManager * ptman,
	GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis)
{
	if (!m_fAttachMod && !m_fShiftMod)
		return;

	if (islotThis == -1)
	{
		for (int islot = 0; islot < psstrmOut->WritePos(); islot++)
		{
			if (psstrmOut->SlotAt(islot) == this)
			{
				islotThis = islot;
				break;
			}
		}
		Assert(islotThis > -1);
	}

	if (m_fAttachMod)
	{
		//	Attachments.

		//	Note that it doesn't make sense to attach a slot to itself, so if the value of
		//	the attach.to attribute is 0, that means clear the attachment.
		GrSlotState * pslotNewRoot = AttachRoot(psstrmOut);

		AttachToRoot(ptman, psstrmOut, pslotNewRoot);

		if (pslotNewRoot)
		{
			//	If this is an attachment with no positions, attach the two glyphs side by side
			//	in the appropriate order.
			bool fRtl = ((pslotNewRoot->PostBidiDirLevel(ptman) % 2) != 0);
			if ((fRtl && m_srAttachTo < 0) || (!fRtl && m_srAttachTo > 0))
			{
				//	The root is on the right, the leaf is on the left.
				if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet)
					m_mAttachAtX = 0;
				if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet)
					m_mAttachWithX = short(AdvanceX(ptman));
			}
			else
			{
				//	The root is on the left, the leaf is on the right.
				if (m_mAttachAtX == kNotYetSet && m_nAttachAtGpoint == kNotYetSet)
					m_mAttachAtX = short(pslotNewRoot->AdvanceX(ptman));
				if (m_mAttachWithX == kNotYetSet && m_nAttachWithGpoint == kNotYetSet)
					m_mAttachWithX = 0;
			}
		}
	}
	else
	{
		// Shifting, or changing the advance width.
		Assert(m_fShiftMod);
		EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis);
		ZapMetricsAndPositionDownToBase(psstrmOut);
		ZapMetricsOfLeaves(psstrmOut);
	}

	if (psstrmOut->m_ipass == ptman->NumberOfPasses() - 1)
		ptman->InitPosCache();	// cached position is most likely no longer valid

	//	Invalidate the positions of this and any following glyphs.
	for (int islot = islotThis + 1; islot < psstrmOut->WritePos(); islot++)
		psstrmOut->SlotAt(islot)->ZapPosition();

	m_fAttachMod = false;
	m_fShiftMod = false;
}

/*----------------------------------------------------------------------------------------------
	The shift attribute of a slot has been modified. Make sure any slots that are part of the
	same attachment cluster are local to this stream. The reason for this is so that the
	position calculations stay consistent within the stream.
----------------------------------------------------------------------------------------------*/
void GrSlotState::EnsureLocalAttachmentTree(GrTableManager * ptman,
	GrSlotStream * psstrmIn, GrSlotStream * psstrmOut, int islotThis)
{
	if (m_dislotRootFixed)
	{
		GrSlotState * pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed);
		psstrmOut->EnsureLocalCopy(ptman, pslotRoot, psstrmIn);
		pslotRoot = SlotAtOffset(psstrmOut, m_dislotRootFixed); // get the new one!
		pslotRoot->EnsureLocalAttachmentTree(ptman, psstrmIn, psstrmOut, islotThis + m_dislotRootFixed);
	}
	for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++)
	{
		GrSlotState * pslotLeaf = SlotAtOffset(psstrmOut, m_vdislotAttLeaves[islot]);
		psstrmOut->EnsureLocalCopy(ptman, pslotLeaf, psstrmIn);
	}
}

/*----------------------------------------------------------------------------------------------
	The recipient slot is being attached to the argument slot.
	NOTE: the caller is responsible to zap the cached positions of following glyphs
	in the stream.
----------------------------------------------------------------------------------------------*/
void GrSlotState::AttachToRoot(GrTableManager * ptman, GrSlotStream * psstrm,
	GrSlotState * pslotNewRoot)
{
	GrSlotState * pslotOldRoot = (m_dislotRootFixed == 0) ?
		NULL :
		SlotAtOffset(psstrm, m_dislotRootFixed);

	if (pslotOldRoot)
	{
		if (pslotOldRoot != pslotNewRoot)
			pslotOldRoot->RemoveLeaf(m_dislotRootFixed);
		pslotOldRoot->ZapMetricsAndPositionDownToBase(psstrm);
		pslotOldRoot->ZapMetricsOfLeaves(psstrm);
	}

	ZapCompositeMetrics();
	
	if (pslotNewRoot && pslotNewRoot != pslotOldRoot)
	{
		pslotNewRoot->AddLeaf(m_srAttachTo);
		pslotNewRoot->ZapMetricsAndPositionDownToBase(psstrm);
		pslotNewRoot->ZapMetricsOfLeaves(psstrm);
	}

	m_dislotRootFixed = m_srAttachTo;
}

/*----------------------------------------------------------------------------------------------
	Return the absolute position of the glyph, relative to the start of the segment,
	in font design units.
----------------------------------------------------------------------------------------------*/
void GrSlotState::Position(GrTableManager * ptman,
	GrSlotStream * psstrmOut, int * pmXPos, int * pmYPos)
{
	Assert(!m_fAttachMod);	// the attachment tree should be set up
	Assert(!m_fShiftMod);

	float xsWidth, xsVisWidth;
	if (m_xsPositionX == kNegInfFloat || m_ysPositionY == kNegInfFloat)
		ptman->CalcPositionsUpTo(psstrmOut->m_ipass, this, &xsWidth, &xsVisWidth);

	*pmXPos = ptman->LogToEmUnits(m_xsPositionX);
	*pmYPos = ptman->LogToEmUnits(m_ysPositionY);
}

/*----------------------------------------------------------------------------------------------
	Recalculate the metrics for this slot, which has attachments on it (or possibly
	did in the past).
----------------------------------------------------------------------------------------------*/
void GrSlotState::AdjustRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm)
{
	Assert(m_dislotRootFixed == m_srAttachTo);
	GrSlotState * pslotRoot = AttachRoot(psstrm);
	CalcRootMetrics(ptman, psstrm, kPosInfinity);
	if (pslotRoot)
		pslotRoot->AdjustRootMetrics(ptman, psstrm);
}

/*----------------------------------------------------------------------------------------------
	Calculate the composite metrics for this slot.

	@param psstrm			- stream for which we are calculating it
	@param nLevel			- attachment level we are asking for; kPosInifinity means all levels
	@param fThorough		- true: do a thorough recalculation; false: don't recalculate
								metrics for leaves (are they assumed to be accurate???)
								--currently not used
----------------------------------------------------------------------------------------------*/
void GrSlotState::CalcCompositeMetrics(GrTableManager * ptman, GrSlotStream * psstrm,
	int nLevel, bool fThorough)
{
	if (m_nCompositeLevel == nLevel)
		return;

	if (fThorough)
	{
		Assert(m_dislotRootFixed == m_srAttachTo);
		GrSlotState * pslotRoot = AttachRoot(psstrm);

		InitMetrics(ptman, pslotRoot);

		for (size_t islot = 0; islot < m_vdislotAttLeaves.size(); islot++)
		{
			GrSlotState * pslotLeaf = SlotAtOffset(psstrm, m_vdislotAttLeaves[islot]);
			if (pslotLeaf->AttachLevel() <= nLevel)
				pslotLeaf->CalcCompositeMetrics(ptman, psstrm, nLevel, fThorough);
			else
				//	this slot will be ignored in the composite metrics
				pslotLeaf->ZapRootMetrics();
		}
		CalcRootMetrics(ptman, psstrm, nLevel);

		m_nCompositeLevel = nLevel;
	}
	else
	{
		Assert(false);	// for now

		//	Don't bother with the leaves.
		InitRootMetrics(ptman);
	}
}

/*----------------------------------------------------------------------------------------------
	Calculate the metrics for this node and all its leaf nodes.
----------------------------------------------------------------------------------------------*/
void GrSlotState::CalcRootMetrics(GrTableManager * ptman, GrSlotStream * psstrm, int nLevel)
{
	for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++)
	{
		GrSlotState * pslot = SlotAtOffset(psstrm, m_vdislotAttLeaves[idislot]);
		if (pslot->AttachLevel() > nLevel)
			continue;

		m_xsClusterXOffset = min(m_xsClusterXOffset, pslot->m_xsClusterXOffset);
		if (!pslot->m_fIgnoreAdvance)
		{
			m_xsClusterAdv = max(
				m_xsClusterAdv,
				pslot->m_xsClusterAdv + m_xsRootShiftX);
		}
		m_xsClusterBbLeft = min(m_xsClusterBbLeft, pslot->m_xsClusterBbLeft);
		m_xsClusterBbRight = max(m_xsClusterBbRight, pslot->m_xsClusterBbRight);
		m_ysClusterBbTop = max(m_ysClusterBbTop, pslot->m_ysClusterBbTop);
		m_ysClusterBbBottom = min(m_ysClusterBbBottom, pslot->m_ysClusterBbBottom);
	}
}

/*----------------------------------------------------------------------------------------------
	Reset the cluster metrics of this slot.
----------------------------------------------------------------------------------------------*/
void GrSlotState::InitMetrics(GrTableManager * ptman, GrSlotState * pslotRoot)
{
	InitLeafMetrics(ptman, pslotRoot);
 	InitRootMetrics(ptman);
}

/*----------------------------------------------------------------------------------------------
	Initialize the variables that store the offsets of just this node (ignoring any of its
	leaves) relative to the cluster base.
----------------------------------------------------------------------------------------------*/
void GrSlotState::InitLeafMetrics(GrTableManager * ptman, GrSlotState * pslotRoot)
{
	float xsShiftX = ptman->EmToLogUnits(ShiftX());
	float ysShiftY = ptman->EmToLogUnits(ShiftY());

	if (ptman->RightToLeft())
		xsShiftX *= -1;

	if (IsBase())
	{
		//	The x-value below has to be zero because the shift is incorporated into
		//	m_xsRootShiftX. (m_ysRootShiftY, on the other hand, isn't used anywhere.)
		m_xsOffsetX = 0;
		m_ysOffsetY = ysShiftY;

		m_xsRootShiftX = xsShiftX;
		m_ysRootShiftY = ysShiftY;
		Assert(!IsLineBreak(ptman->LBGlyphID())
			|| (m_xsRootShiftX == 0 && m_ysRootShiftY == 0));
		return;
	}

	Assert(!IsLineBreak(ptman->LBGlyphID()));

	//	Hint-adjusted logical coordinates equivalent to attach.at and attach.with attributes.
	//	If attach.at or attach.with attributes were unset, the defaults for a side-by-side
	//	attachment should have been supplied in HandleModifiedPosition().
	float xsAttAtX, ysAttAtY, xsAttWithX, ysAttWithY;
	AttachLogUnits(ptman, pslotRoot, &xsAttAtX, &ysAttAtY, &xsAttWithX, &ysAttWithY);

	m_xsOffsetX = xsAttAtX - xsAttWithX;
	m_xsOffsetX += pslotRoot->m_xsOffsetX;
	m_xsOffsetX += xsShiftX;

	m_ysOffsetY = ysAttAtY - ysAttWithY;
	m_ysOffsetY += pslotRoot->m_ysOffsetY;
	m_ysOffsetY += ysShiftY;

	// Cumulative effect of shifts on this and all base nodes:
	m_xsRootShiftX = pslotRoot->m_xsRootShiftX + xsShiftX;
	m_ysRootShiftY = pslotRoot->m_ysRootShiftY + ysShiftY;
}

/*----------------------------------------------------------------------------------------------
	Initialize the variables that store the offsets of this node taking into account its
	leaves; these are relative to the cluster base.
----------------------------------------------------------------------------------------------*/
void GrSlotState::InitRootMetrics(GrTableManager * ptman)
{
	if (IsLineBreak(ptman->LBGlyphID()))
	{
		m_fIgnoreAdvance = true;
		m_xsClusterXOffset = 0;
		m_xsClusterAdv = 0;
		m_xsClusterBbLeft = 0;
		m_xsClusterBbRight = 0;
		m_ysClusterBbTop = 0;
		m_ysClusterBbBottom = 0;
		return;
	}

	float xsAdvanceX = ptman->EmToLogUnits(AdvanceX(ptman));

	//	If the glyph's advance width is zero, then we NEVER want it to have any affect,
	//	even if the glyph is attached way out to the right of its base's advance.
	m_fIgnoreAdvance = (xsAdvanceX == 0);

	float xsBbLeft = GlyphMetricLogUnits(ptman, kgmetBbLeft);
	float xsBbRight = GlyphMetricLogUnits(ptman, kgmetBbRight);
	float ysBbTop = GlyphMetricLogUnits(ptman, kgmetBbTop);
	float ysBbBottom = GlyphMetricLogUnits(ptman, kgmetBbBottom);

	//	Any shifts should be ignored for the sake of calculating actual position or width,
	//	hence we subtract the cumulative effect of the shifts.
	m_xsClusterXOffset = m_xsOffsetX - m_xsRootShiftX;
	m_xsClusterAdv = m_xsOffsetX + xsAdvanceX - m_xsRootShiftX;

	m_xsClusterBbLeft = m_xsOffsetX + xsBbLeft;
	m_xsClusterBbRight = m_xsOffsetX + xsBbRight;
	m_ysClusterBbTop = m_ysOffsetY + ysBbTop;
	m_ysClusterBbBottom = m_ysOffsetY + ysBbBottom;
}

/*----------------------------------------------------------------------------------------------
	X-offset of a single glyph relative to the previous advance position.
----------------------------------------------------------------------------------------------*/
float GrSlotState::GlyphXOffset(GrSlotStream * psstrm, float fakeItalicRatio)
{
	float xsRet = Base(psstrm)->ClusterRootOffset() + m_xsOffsetX;

	// fake an italic slant
	xsRet += m_ysOffsetY * fakeItalicRatio;

	return xsRet;
}

/*----------------------------------------------------------------------------------------------
	Y-offsets of a single glyph relative to the previous advance position.
----------------------------------------------------------------------------------------------*/
float GrSlotState::GlyphYOffset(GrSlotStream * psstrm)
{
	return m_ysOffsetY;
}

/*----------------------------------------------------------------------------------------------
	Return the offset of the last leaf of the cluster, relative to this slot, which is
	the base. Return kNegInfinity if there are not enough slots in the stream to tell.
----------------------------------------------------------------------------------------------*/
int GrSlotState::LastLeafOffset(GrSlotStream * psstrm)
{
	int islotRet = 0;
	for (size_t idislot = 0; idislot < m_vdislotAttLeaves.size(); idislot++)
	{
		int dislot = m_vdislotAttLeaves[idislot];
		Assert(dislot != 0);
		if (!psstrm->HasSlotAtPosPassIndex(PosPassIndex() + dislot))
			return kNegInfinity;
		GrSlotState * pslotLeaf = SlotAtOffset(psstrm, dislot);
		int islotTmp = pslotLeaf->LastLeafOffset(psstrm);
		if (islotTmp == kNegInfinity)
			return kNegInfinity;
		islotRet = max(islotRet, (dislot + islotTmp));
	}
	return islotRet;
}

/*----------------------------------------------------------------------------------------------
	Return the attach positions in the device context's logical units,
	adjusted for hinting if possible.

	Note that m_nAttachAt/WithGpoint = 0 is always an invalid value (resulting from a
	glyph attribute that was defined in terms of x/y coordinates that didn't map to an
	actual on-curve point). Therefore the x/y coordinates should be used to do the
	attachment. In the case where we actually want point #0 on the curve, 
	m_nAttachAt/WithGpoint will have the special value 'kGpointZero' (the glyph attribute
	is defined this way).
----------------------------------------------------------------------------------------------*/
void GrSlotState::AttachLogUnits(GrTableManager * ptman, GrSlotState * pslotRoot,
	float * pxsAttAtX, float * pysAttAtY,
	float * pxsAttWithX, float * pysAttWithY)
{
	if (m_nAttachAtGpoint == kNotYetSet || m_nAttachAtGpoint == 0)
	{
		//	Use x- and y-coordinates; no adjustment for hinting is done.
		int mX = m_mAttachAtX + m_mAttachAtXOffset;
		int mY = m_mAttachAtY + m_mAttachAtYOffset;

		*pxsAttAtX = ptman->EmToLogUnits(mX);
		*pysAttAtY = ptman->EmToLogUnits(mY);
	}
	else
	{
		//	Look up the actual on-curve point.
		int nGpoint = m_nAttachAtGpoint;
		if (nGpoint == kGpointZero)
			nGpoint = 0;
		bool fImpl = ptman->GPointToXY(pslotRoot->GlyphID(), nGpoint, pxsAttAtX, pysAttAtY);

		// Debuggers:
		//int mXTmpAt = m_mAttachAtX + m_mAttachAtXOffset;
		//int mYTmpAt = m_mAttachAtY + m_mAttachAtYOffset;
		//int xsAttAtXTmp = ptman->EmToLogUnits(mXTmpAt);
		//int ysAttAtYTmp = ptman->EmToLogUnits(mYTmpAt);
		//fImpl = false;

		if (!fImpl)
		{
			//	Fall back to using x- and y-coordinates; no adjustment for hinting.
			int mX = m_mAttachAtX + m_mAttachAtXOffset;
			int mY = m_mAttachAtY + m_mAttachAtYOffset;
			*pxsAttAtX = ptman->EmToLogUnits(mX);
			*pysAttAtY = ptman->EmToLogUnits(mY);
		}
		else
		{
			//	Adjust by offsets.
			*pxsAttAtX += ptman->EmToLogUnits(m_mAttachAtXOffset);
			*pysAttAtY += ptman->EmToLogUnits(m_mAttachAtYOffset);
		}
	}

	if (m_nAttachWithGpoint == kNotYetSet || m_nAttachWithGpoint == 0)
	{
		//	Use x- and y-coordinates; no adjustment for hinting is done.
		int mX = m_mAttachWithX + m_mAttachWithXOffset;
		int mY = m_mAttachWithY + m_mAttachWithYOffset;

		*pxsAttWithX = ptman->EmToLogUnits(mX);
		*pysAttWithY = ptman->EmToLogUnits(mY);
	}
	else
	{
		//	Look up the actual on-curve point.
		int nGpoint = m_nAttachWithGpoint;
		if (nGpoint == kGpointZero)
			nGpoint = 0;
		bool fImpl = ptman->GPointToXY(m_chwGlyphID, nGpoint, pxsAttWithX, pysAttWithY);

		// Debuggers:
		//int mXTmpWith = m_mAttachWithX + m_mAttachWithXOffset;
		//int mYTmpWith = m_mAttachWithY + m_mAttachWithYOffset;
		//int xsAttWithXTmp = ptman->EmToLogUnits(mXTmpWith);
		//int ysAttWithYTmp = ptman->EmToLogUnits(mYTmpWith);
		//fImpl = false;

		if (!fImpl)
		{
			//	Fall back to using x- and y-coordinates; no adjustment for hinting.
			int mX = m_mAttachWithX + m_mAttachWithXOffset;
			int mY = m_mAttachWithY + m_mAttachWithYOffset;
			*pxsAttWithX = ptman->EmToLogUnits(mX);
			*pysAttWithY = ptman->EmToLogUnits(mY);
		}
		else
		{
            //	Adjust by offsets.
			*pxsAttWithX += ptman->EmToLogUnits(m_mAttachWithXOffset);
			*pysAttWithY += ptman->EmToLogUnits(m_mAttachWithYOffset);
		}
	}
}

/*----------------------------------------------------------------------------------------------
	Return the slot that is 'dislot' slots away from this slot in the given stream.
	Only valid for streams that are the input to or output of positioning passes.
----------------------------------------------------------------------------------------------*/
GrSlotState * GrSlotState::SlotAtOffset(GrSlotStream * psstrm, int dislot)
{
	Assert(psstrm->m_fUsedByPosPass);
	return psstrm->SlotAtPosPassIndex(PosPassIndex() + dislot);
}

/*----------------------------------------------------------------------------------------------
	If the direction level has not been calculated at this point, assume it is the
	top direction. This should only happen if there was no bidi pass to set it.
	This method should only be called by the positioning passes; it assumes any bidi
	pass has been run.
----------------------------------------------------------------------------------------------*/
int GrSlotState::PostBidiDirLevel(GrTableManager * ptman)
{
	if (m_nDirLevel == -1)
	{
		Assert(!ptman->HasBidiPass());
		return ptman->TopDirectionLevel();
	}
	return m_nDirLevel;
}

/*----------------------------------------------------------------------------------------------
	Return true if this glyph represents a LRM code.
----------------------------------------------------------------------------------------------*/
bool GrSlotState::IsLrm()
{
	if (PassModified() == 0)
		return (m_nUnicode == knLRM);
	return m_pslotPrevState->IsLrm();
}

/*----------------------------------------------------------------------------------------------
	Return true if this glyph represents a RLM code.
----------------------------------------------------------------------------------------------*/
bool GrSlotState::IsRlm()
{
	if (PassModified() == 0)
		return (m_nUnicode == knRLM);
	return m_pslotPrevState->IsRlm();
}


/*----------------------------------------------------------------------------------------------
	Used by GlyphInfo
----------------------------------------------------------------------------------------------*/
//float GrSlotOutput::AdvanceX(Segment * pseg)
//{
//	return pseg->EmToLogUnits(m_mAdvanceX);
//}
//float GrSlotOutput::AdvanceY(Segment * pseg)
//{
//	return pseg->EmToLogUnits(m_mAdvanceY);
//}
float GrSlotOutput::MaxStretch(Segment * pseg, int level)
{
	Assert(level == 0);
	return (level == 0) ? pseg->EmToLogUnits(m_mJStretch0) : 0;
}
float GrSlotOutput::MaxShrink(Segment * pseg, int level)
{
	Assert(level == 0);
	return (level == 0) ? pseg->EmToLogUnits(m_mJShrink0) : 0;
}
float GrSlotOutput::StretchStep(Segment * pseg, int level)
{
	Assert(level == 0);
	return (level == 0) ? pseg->EmToLogUnits(m_mJStep0) : 0;
}
int GrSlotOutput::JustWeight(int level)
{
	Assert(level == 0);
	return (level == 0) ? m_nJWeight0 : 0;
}
float GrSlotOutput::JustWidth(Segment * pseg, int level)
{
	Assert(level == 0);
	return (level == 0) ? pseg->EmToLogUnits(m_mJWidth0) : 0;
}
float GrSlotOutput::MeasureSolLogUnits(Segment * pseg)
{
	return pseg->EmToLogUnits(m_mMeasureSol);
}
float GrSlotOutput::MeasureEolLogUnits(Segment * pseg)
{
	return pseg->EmToLogUnits(m_mMeasureEol);
}

/*----------------------------------------------------------------------------------------------
	Make a copy of the GrSlotOutput. This is used in making an identical copy of the segment.
	Warning: this function will break if GrSlotState and subclasses are given virtual
	methods. In that case, we will need to copy from the address of the first variable in
	GrSlotAbstract.
----------------------------------------------------------------------------------------------*/
void GrSlotOutput::ExactCopyFrom(GrSlotOutput * pslout, u_intslot * pnVarLenBuf, int cnExtraPerSlot)
{
	// The chunk of the object from GrSlotAbstract can be copied exactly,
	// except for the variable-length buffer.
	*this = *pslout;
	m_prgnVarLenBuf = pnVarLenBuf;
	std::copy(pslout->m_prgnVarLenBuf, pslout->m_prgnVarLenBuf + cnExtraPerSlot,
				m_prgnVarLenBuf);

	// Now copy the stuff specific to GrSlotOutput.
	m_ichwBeforeAssoc = pslout->m_ichwBeforeAssoc;
	m_ichwAfterAssoc = pslout->m_ichwAfterAssoc;
	m_cComponents = pslout->m_cComponents;

    m_isloutClusterBase = pslout->m_isloutClusterBase;
	m_disloutCluster = pslout->m_disloutCluster;
	m_xsClusterXOffset = pslout->m_xsClusterXOffset;
	m_xsClusterAdvance = pslout->m_xsClusterAdvance;
	m_igbb = pslout->m_igbb;
	m_xsAdvanceX = pslout->m_xsAdvanceX;
//	m_ysAdvanceY = pslout->m_ysAdvanceY;
//	m_rectBB = pslout->m_rectBB;
}

/*----------------------------------------------------------------------------------------------
	Shift the glyph to the opposite end of the segment. This is needed for white-space-only
	segments whose direction is being changed.
----------------------------------------------------------------------------------------------*/
void GrSlotOutput::ShiftForDirDepthChange(float dxsSegWidth)
{
	float dxsShift = dxsSegWidth - m_xsAdvanceX - (2 * m_xsPositionX);
	int tmp; tmp = (int) dxsShift;
	m_xsPositionX += dxsShift;
//	m_rectBB.left += dxsShift;
//	m_rectBB.right += dxsShift;
}

/*----------------------------------------------------------------------------------------------
	Return the indices of all the glyphs attached to this cluster, in logical order.
	Return an empty vector if this glyph is not the base glyph or if there are no
	attached glyphs.
	This method return glyph indices, not slot indices.
----------------------------------------------------------------------------------------------*/
void GrSlotOutput::ClusterMembers(Segment * pseg, int isloutThis, std::vector<int> & visloutRet)
{
	if (m_isloutClusterBase == -1 || m_isloutClusterBase == isloutThis)	// yes, a base
		pseg->ClusterMembersForGlyph(isloutThis, m_disloutCluster, visloutRet);
	else
	{
		Assert(visloutRet.size() == 0);
	}
}

} // namespace gr
