/*
 *	@doc INTERNAL
 *
 *	@module	OLS.CPP -- COls LineServices object class
 *	
 *	Authors:
 *		Murray Sargent: initial coding up to nonLS RichEdit functionality
 *			(with lots of help from RickSa's ols code)
 *		Keith Curtis and Worachai Chaoweeraprasit: complex script support,
 *			etc.
 *
 *	@todo
 *		1) Fix table selection
 *		2) What are we to do with RTL tables? Word has a very different model
 *		3) What we should give for LSCHP.dcpMaxContext
 *
 *	Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved.
 */

#include "_common.h"
#include "_edit.h"
#include "_font.h"
#include "_render.h"
#include "_osdc.h"
#include "_dfreeze.h"
#include "_tomfmt.h"
#include "_ols.h"
#include "_clasfyc.h"
#include "_uspi.h"
#include "_txtbrk.h"
#include "lskysr.h"

#ifdef LINESERVICES

ASSERTDATA

// Guess at the number of characters on the line
const int cchLineHint = 66;

#define OBJID_OLE			0
#define OBJID_REVERSE		1
#define	OBJID_COUNT			2

const WCHAR wchObjectEnd = 0x9F;
const WCHAR rgchObjectEnd[]	= {wchObjectEnd};

#define	MAX_OBJ_DEPTH		3

extern const LSCBK lscbk;

// Kinsoku break pair information
extern const INT g_cKinsokuCategories;

CLineServices *g_plsc = NULL;		// LineServices Context
COls*		   g_pols = NULL;		// COls ptr

const LSBRK rglsbrkDefault[] =
{
	0,0,	// Always prohibited
	0,1,	// OK across blanks
	1,1		// Always allowed
};

// prototypes
void 	EmitBrace(COls* pols, PLSCHP pchp, BOOL* pfHid, DWORD* pcch, PLSRUN* pprun, LPCWSTR* plpwch, int id, LPCWSTR str);
void	DupShapeState(PLSRUN prun, LONG cch);


// public inline functions
//

// Emitting fake brace to LS
inline void EmitBrace(
	COls*		pols,
	PLSCHP		pchp,
	BOOL*		pfHid,
	DWORD*		pcch,
	PLSRUN*		pprun,
	LPCWSTR*	plpwch,
	int			id,
	LPCWSTR		str)
{
	ZeroMemory(pchp, sizeof(*pchp));								
	pchp->idObj = (WORD)id;												
	*pfHid 		= 0;												
	*pcch 		= 1;												
	*pprun 		= pols->GetPlsrun(0, pols->_pme->GetCF(), FALSE);
	*plpwch		= str;												
}

// Duplicate shaping state to each runs in the chain
// note: this macro used only by GetGlyph and GetGlyphPosition
inline void DupShapeState(
	PLSRUN		prun,
	LONG		cch)
{
	PLSRUN	pnext = prun->_pNext;										
	LONG	cpEnd = prun->_cp + cch;									
	while (pnext && pnext->_cp < cpEnd)
	{
		CopyMemory(&pnext->_a, &prun->_a, sizeof(SCRIPT_ANALYSIS));
		pnext->SetFallback(prun->IsFallback());
		prun = pnext;
		pnext = prun->_pNext;
	}																
	Assert(!pnext && prun->_cp < cpEnd);
}




LONG COls::GetCpLsFromCpRe(
	LONG cpRe)
{
	if (_rgcp.Count() == 0)
		return cpRe;

	LONG *pcp = _rgcp.Elem(0);

	for(LONG cpLs = cpRe; cpLs >= *pcp; pcp++)
		 cpLs++;

	return cpLs;
}

LONG COls::GetCpReFromCpLs(
	LONG cpLs
	)
{
	if (_rgcp.Count() == 0)
		return cpLs;

	LONG *pcp = _rgcp.Elem(0);

	for(int dcp = 0; cpLs > *pcp; pcp++)
		dcp--;

	return cpLs + dcp;
}

#ifdef DEBUG
//#define DEBUG_BRACE
#endif
// return TRUE if braces added
BOOL COls::AddBraceCp(long cpLs)
{
	if (_rgcp.Count() == 0)
	{
		long *pcp = _rgcp.Insert(0, 1);
		*pcp = tomForward;
	}

	long *pcp = _rgcp.Elem(0);
	long iel = 0;

	while (cpLs > pcp[iel])
		iel++;

	if (cpLs < pcp[iel])
	{
		pcp = _rgcp.Insert(iel, 1);
		*pcp = cpLs;
		return TRUE;
	}
	return FALSE;
}

// return number of braces before cp
//
LONG COls::BracesBeforeCp(LONG cpLs)
{
	LONG 	iel, cbr = 0;
	LONG*	pcp;

	if (!cpLs || (iel = _rgcp.Count()) < 2)
		return 0;

	iel -= 2;		// exclude the last tomForward one and make a count an index
	cpLs--;			// start with the cp preceding given cp

	pcp = _rgcp.Elem(0);

	while (iel > -1 && pcp[iel] > cpLs)		// search the first one
		iel--;

	while (iel > -1 && pcp[iel] == cpLs)	// continue counting
	{
		iel--;
		cpLs--;
		cbr++;
	}
	return cbr;
}

/*
 * 	SetRun(plsrun)
 *
 *	@func
 *		Do whatever is needed to initialize the measurer (pme) to the lsrun
 *		givin by plsrun and return whether the run is for autonumbering.
 *
 *	@rdesc
 *		TRUE if plsrun refers to an autonumbering run
 */
BOOL COls::SetRun(PLSRUN plsrun)
{
	LONG cp = plsrun->_cp;
	_pme->SetCp(cp & 0x7FFFFFFF);
	return plsrun->IsBullet();
}

/*
 * 	IsSelected()
 *
 *	@mfunc
 *	return whether or not the run should be drawn as selected.
 *
 */
CLsrun::IsSelected(void)
{
	if (!_fSelected)
		return FALSE;
	CRenderer *pre = g_pols->GetRenderer();
	Assert(pre->IsRenderer());
	return pre->_fRenderSelection ? TRUE : FALSE;
}

/*
 * 	CreatePlsrun (void)
 *
 *	@func
 *	Creates a PLSRUN. Is a little tricky because we allocate them in
 *	chunks.
 *
 *	@rdesc
 */
const int cplsrunAlloc = 8;
PLSRUN COls::CreatePlsrun()
{
	CLsrunChunk *plsrunChunk = 0;
	
	//First, find a chunk to use
	int cchunk = _rglsrunChunk.Count();
	for (int ichunk = 0; cchunk && ichunk < cchunk; ichunk++)
	{
		plsrunChunk = _rglsrunChunk.Elem(ichunk);
		if (plsrunChunk->_cel < cplsrunAlloc)
			break;
	}	

	if (!cchunk || ichunk == cchunk || plsrunChunk->_cel == cplsrunAlloc)
	{
		CLsrun *rglsrun = new CLsrun[cplsrunAlloc];
		if (rglsrun)
		{
			plsrunChunk = _rglsrunChunk.Add(1, 0);
			if (!plsrunChunk)
			{
				delete[] rglsrun;
				return 0;
			}
			plsrunChunk->_prglsrun = rglsrun;
		}
		else
			return 0;
	}
	return &plsrunChunk->_prglsrun[plsrunChunk->_cel++];
}

/*
 * 	GetPlsrun(cp, pCF, fAutoNumber)
 *
 *	@func
 *		Return plsrun for info in run. The structure contains the starting cp
 * 		of the run and the script analysis if Uniscribe is activated. The
 *		analysis information is needed by subsequent callbacks - GetGlyphs and
 *		GetGlyphPositions to be passed to Uniscribe in order to shape and
 *		position glyphs correctly for complex scripts.
 *
 *	@rdesc
 *		plsrun corresponding to info in arguments
 */
PLSRUN COls::GetPlsrun(
	LONG 		cp,
	const CCharFormat *pCF,
	BOOL 		fAutoNumber)
{
	if(fAutoNumber)
		cp |= CP_BULLET;

	CLsrun *plsrun = CreatePlsrun();

	if (plsrun)
	{
		ZeroMemory(plsrun, sizeof(CLsrun));

		plsrun->_pCF = pCF;
		plsrun->_cp = fAutoNumber ? _cp | CP_BULLET : cp;

		LONG 	cpSelMin, cpSelMost;
		_pme->GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);

		plsrun->SetSelected(!plsrun->IsBullet() && cp >= cpSelMin && cp < cpSelMost);

		if (pCF->_wScript && !_pme->GetPasswordChar())
		{
			CUniscribe*	pusp = _pme->Getusp();
			Assert(pusp);
			const SCRIPT_PROPERTIES* psp = pusp->GeteProp(pCF->_wScript);

			plsrun->_a.eScript = (pCF->_wScript < SCRIPT_MAX_COUNT) ? pCF->_wScript : 0;
			plsrun->_a.fRTL  = !psp->fNumeric && (IsBiDiCharSet(pCF->_bCharSet) || IsBiDiCharSet(psp->bCharSet));
			plsrun->_a.fLogicalOrder = TRUE;
		}
	}
	return plsrun;
}

/*
 *	COls::~COls()
 *
 *	@mfunc
 *		Destructor
 */
COls::~COls()
{
	for (int ichunk = 0, cchunk = _rglsrunChunk.Count(); ichunk < cchunk; ichunk++)
		delete []_rglsrunChunk.Elem(ichunk)->_prglsrun;
	DestroyLine(NULL);
	if (g_plsc)
		LsDestroyContext(g_plsc);
}


/*
 *	COls::Init(pme)
 *
 *	@mfunc
 *		Initialize this LineServices object
 *
 *	@rdesc
 *		HRESULT = (success) ? NOERROR : E_FAIL
 */
HRESULT COls::Init(
	CMeasurer *pme)
{
	_pme = pme;

	if(g_plsc)
		return NOERROR;

	// Build LS context to create
	LSCONTEXTINFO lsctxinf;

	// Setup object handlers
	LSIMETHODS vlsctxinf[OBJID_COUNT];
	vlsctxinf[OBJID_OLE] = vlsimethodsOle;
	if(LsGetReverseLsimethods(&vlsctxinf[OBJID_REVERSE]) != lserrNone)
		return E_FAIL;

	lsctxinf.cInstalledHandlers = OBJID_COUNT;
    lsctxinf.pInstalledHandlers = &vlsctxinf[0];

	// Set default and all other characters to 0xFFFF
    memset(&lsctxinf.lstxtcfg, 0xFF, sizeof(lsctxinf.lstxtcfg));

	lsctxinf.fDontReleaseRuns = TRUE;
	lsctxinf.lstxtcfg.cEstimatedCharsPerLine = cchLineHint;

	// Set the characters we handle
	lsctxinf.lstxtcfg.wchNull			= 0;
	lsctxinf.lstxtcfg.wchSpace			= ' ';
	lsctxinf.lstxtcfg.wchHyphen			= '-';
	lsctxinf.lstxtcfg.wchTab			= '\t';
	lsctxinf.lstxtcfg.wchEndLineInPara	= '\v';
	lsctxinf.lstxtcfg.wchEndPara1		= '\r';
	lsctxinf.lstxtcfg.wchEndPara2		= '\n';

	lsctxinf.lstxtcfg.wchVisiAltEndPara	=
	lsctxinf.lstxtcfg.wchVisiEndPara	=
	lsctxinf.lstxtcfg.wchVisiEndLineInPara = ' ';
	
	lsctxinf.lstxtcfg.wchNonReqHyphen = SOFTHYPHEN;

	// Auto number escape character
	lsctxinf.lstxtcfg.wchEscAnmRun = wchObjectEnd;

    lsctxinf.pols = this;
    lsctxinf.lscbk = lscbk;

	if(LsCreateContext(&lsctxinf, &g_plsc) != lserrNone)
		return E_FAIL;

	//REVIEW (keithcu) Quill seems to have a more mature kinsoku
	//table. For example, we don't allow breaking across space between
	//a word and the ending punctuation. French people want this behavior.
	BYTE  rgbrkpairsKinsoku[cKinsokuCategories][cKinsokuCategories];
	BYTE *prgbrkpairsKinsoku = &rgbrkpairsKinsoku[0][0];
	for(LONG i = 0; i < cKinsokuCategories; i++)
	{
		for(LONG j = 0; j < cKinsokuCategories; j++)
		{
			LONG iBreak = 2*CanBreak(i, j);
			// If don't break, allow break across blanks unless first
			// char is open brace or second char is close brace
			if (!iBreak &&				
				GetKinsokuClass(i) != brkclsOpen &&
				GetKinsokuClass(j) != brkclsOpen)
			{
				iBreak = 1;
			}
			*prgbrkpairsKinsoku++ = iBreak;
		}
	}
	if(g_plsc->SetBreaking(ARRAY_SIZE(rglsbrkDefault), rglsbrkDefault,
					 cKinsokuCategories, &rgbrkpairsKinsoku[0][0]) != lserrNone)
	{
		return E_FAIL;
	}

	return NOERROR;
}

/*
 * 	COls::MeasureLine(xWidth, pliTarget)
 *
 *	@mfunc
 *		Wrapper for LsCreateLine
 *
 *	@rdesc
 *		TRUE if success, FALSE if failed
 */
const int dxpMaxWidth = 0x000FFFFF;
BOOL COls::MeasureLine(
    LONG	xWidth,			//@parm Width of line in device units
	CLine *	pliTarget)		//@parm Returns target-device line metrics (optional)
{
	LONG	xWidthActual = xWidth;
	CMeasurer *pme = _pme;
	const CParaFormat *pPF = pme->Get_pPF();
	const CDisplay *pdp = pme->_pdp;
	
	LONG cp = pme->GetCp();
#ifdef DEBUG
	LONG cchText = pme->GetTextLength();	// For DEBUG...
	AssertSz(cp < cchText || !pme->IsRich() && cp == cchText, "COls::Measure: trying to measure past EOD");
#endif
	DestroyLine(NULL);

	_cp = cp;
	_pdp = pdp;
	_fCheckFit = FALSE;
	pme->SetUseTargetDevice(FALSE);

	LSDEVRES lsdevres;
	lsdevres.dyrInch = pme->_dyrInch;
	lsdevres.dxrInch = pme->_dxrInch;
	lsdevres.dypInch = pme->_dypInch;
	lsdevres.dxpInch = pme->_dxpInch;

	g_plsc->SetDoc(TRUE, lsdevres.dyrInch == lsdevres.dypInch &&
					lsdevres.dxrInch == lsdevres.dxpInch, &lsdevres);

	if(xWidth == -1)
	{
		if (pdp->GetMaxWidth())
			xWidth = xWidthActual = pme->LXtoDX(pdp->GetMaxWidth());
		else
			xWidth = xWidthActual = max(0, pdp->GetMaxPixelWidth() - dxCaret);
	}

	if(!pdp->GetWordWrap())
	{
		xWidth = dxpMaxWidth;
		BOOL fNearJust = pPF->_bAlignment == PFA_LEFT && !pPF->IsRtlPara() ||
					     pPF->_bAlignment == PFA_RIGHT && pPF->IsRtlPara();
		if (!fNearJust)
			_fCheckFit = TRUE;
	}


	DWORD cBreakRecOut;
	LSLINFO	 lslinfo;
	BREAKREC rgBreak[MAX_OBJ_DEPTH];
	_xWidth = xWidth;

	LSERR lserr = g_plsc->CreateLine(cp, pme->DXtoLX(xWidth), NULL, 0, MAX_OBJ_DEPTH, rgBreak,
						 &cBreakRecOut, &lslinfo, &_plsline);

	if (_fCheckFit)
	{
		long upJunk, upStartTrailing;
		LsQueryLineDup(_plsline, &upJunk, &upJunk, &upJunk, &upStartTrailing, &upJunk);

		if (pme->_pPF->InTable())
			{
			// We play games in case of tables, so we should change width to get proper alignment.
			//
			// LS formats from negative left indent of -_dxOffset, but we'll actually display
			// from	the +_dxOffset. We will also lie to LS (or is is truth) about position of
			// the last tab. As a result of this, LS thinks the line by _dxOffset shorter than
			// it really is.
			xWidthActual -= pme->LXtoDX(pme->_pPF->_dxOffset);
			}

		if (upStartTrailing < xWidthActual)
		{
			_xWidth = xWidth = xWidthActual;
			_fCheckFit = FALSE;
			DestroyLine(NULL);
			lserr = g_plsc->CreateLine(cp, pme->DXtoLX(xWidth), NULL, 0, MAX_OBJ_DEPTH, rgBreak,
									   &cBreakRecOut, &lslinfo, &_plsline);
		}
	}

	//Line Services doesn't put the autonumbering dimensions into the line,
	//so we have to do it ourselves.
	lslinfo.dvpAscent = max(lslinfo.dvpAscent, lslinfo.dvpAscentAutoNumber);
	lslinfo.dvpDescent = max(lslinfo.dvpDescent, lslinfo.dvpDescentAutoNumber);

	pme->SetUseTargetDevice(FALSE);

	lslinfo.cpLim = GetCpReFromCpLs(lslinfo.cpLim);

	if (lserr != lserrNone)
	{
		AssertSz(lserr == lserrOutOfMemory, "Line format failed for invalid reason");
		pme->GetPed()->GetCallMgr()->SetOutOfMemory();
		return FALSE;
	}

	//REVIEW (keithcu) Doing this hit-testing during measurement is slow--is it
	//worth it to cache this data?
	if(!pme->IsRenderer())
	{
		long upJunk, upStart, upStartTrailing, upLimLine;

		// Save some LineServices results in the measurer's CLine
		pme->_li._cch = lslinfo.cpLim - cp;
		AssertSz(pme->_li._cch > 0,	"no cps on line");

		// Query line services for line width and indent.
		LsQueryLineDup(_plsline, &upJunk, &upJunk, &upStart, &upStartTrailing, &upLimLine);
		long dupWidth = upStartTrailing - upStart;

		pme->_li._xLeft = upStart;
		pme->_li._xWidth = dupWidth;

		if(pme->IsRich())
		{
			pme->_li._yHeight  = lslinfo.dvpAscent + lslinfo.dvpDescent;
			pme->_li._yDescent = lslinfo.dvpDescent;
		}
		else
			pme->CheckLineHeight();				// Use default heights

		pme->_li._cchEOP = 0;

		pme->SetCp(lslinfo.cpLim);
		if(pme->_rpTX.IsAfterEOP())				// Line ends with an EOP
		{										// Store cch of EOP (1 or 2)
			pme->_rpTX.BackupCpCRLF(FALSE);
			UINT ch = pme->GetChar();
			if(ch == CR || pme->GetPed()->fUseCRLF() && ch == LF)
				pme->_li._bFlags |= fliHasEOP;
			pme->_li._cchEOP = pme->_rpTX.AdvanceCpCRLF(FALSE);
		}
		if (lslinfo.cpLim > pme->GetTextLength() &&
			(!pme->IsRich() || pme->IsHidden()))
		{
			Assert(lslinfo.cpLim == pme->GetTextLength() + 1);
			pme->_li._cch--;
		}
		else
			pme->AdjustLineHeight();
	}

	//Setup pliTarget if caller requests it
	//FUTURE (KeithCu) If people want target information, then the display
	//information is the same, except that OnFormatRange has a bug.
	if (pliTarget)
	{
		CLine liSave = pme->_li;
		pme->_li._yHeight = max(lslinfo.dvrAscent, lslinfo.dvrAscentAutoNumber) +
							max(lslinfo.dvrDescent, lslinfo.dvrDescentAutoNumber);
		pme->_li._yDescent = lslinfo.dvrDescent;
		pme->SetUseTargetDevice(TRUE);
		pme->AdjustLineHeight();
		pme->SetUseTargetDevice(FALSE);
		*pliTarget = pme->_li;
		pme->_li = liSave;
	}
	return TRUE;
}

/*
 * 	COls::RenderLine()
 *
 *	@mfunc
 *		Wrapper for LsDisplayLine
 *
 *	@rdesc
 *		TRUE if success, FALSE if failed
 */
BOOL COls::RenderLine(
	CLine &	li)				//@parm Line to render
{
	TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::RenderLine");

	LONG		cp = _pme->GetCp();
	CRenderer	*pre = GetRenderer();
	LONG		xAdj = 0, yAdj = 0;
	Assert(pre->_fRenderer);

	pre->NewLine(li);
	if(li._fCollapsed)				// Line is collapsed in Outline mode
	{
		pre->Advance(li._cch);		// Bypass line
		return TRUE;				// Let dispml continue with next line
	}
	pre->SetNumber(li._bNumber);

	CreateOrGetLine();
	if(!_plsline)
		return FALSE;

	pre->SetCp(cp);						// Back to beginning of line
	Assert(pre->_fTarget == FALSE);
	pre->Check_pccs(FALSE);
	pre->SetClipRect();

	HDC hdcSave = NULL;
	if(li._cch > 0 && pre->fUseOffScreenDC() && (li._bFlags & fliUseOffScreenDC))
	{
		// Set up an off-screen DC if we can. Note that if this fails,
		// we just use the regular DC which won't look as nice but
		// will at least display something readable.
		hdcSave = pre->SetUpOffScreenDC(xAdj, yAdj);

		// Is this a uniform text being rendered off screen?
		if(li._bFlags & fliOffScreenOnce)
		{
			// Yes - turn off special rendering since line has been rendered
			li._bFlags &= ~(fliOffScreenOnce | fliUseOffScreenDC);
		}
	}
	POINT pt = pre->GetCurPoint();			// Must follow offscreen setup
	RECT  rc = pre->GetClipRect();			//  since _ptCur, _rc change
	LONG  x = 0;
	if(pre->_pPF->InTable())
	{
		pt.x += 2*pre->LXtoDX(pre->_pPF->_dxOffset);
		x = pt.x + pre->_li._xLeft;
	}

	pre->_li._xLeft = 0;
	pre->RenderStartLine();

	pt.x += pre->XFromU(0);

	pt.y += li._yHeight - li._yDescent;		// Move to baseline	for LS
	LSERR lserr = LsDisplayLine(_plsline, &pt, pre->GetPdp()->IsMain() ? ETO_CLIPPED : 0, &rc);

	AssertSz(lserr == lserrNone, "COls::RenderLine: error in rendering line");

	pre->EndRenderLine(hdcSave, xAdj, yAdj, x);
	pre->SetCp(cp + li._cch);

	return lserr == lserrNone;
}

/*
 * 	COls::CreateOrGetLine()
 *
 *	@mfunc
 *		If _plsline is nonNull and _cp equals _pme->GetCp(), return.  Else
 *		create line with caching so that _plsline and _cp are correct for
 *		current line
 */
void COls::CreateOrGetLine()
{
	if(_plsline && _pme->GetCp() == _cp && _pme->_pdp == _pdp)
		return;

	MeasureLine(-1, NULL);		// Define new _plsline
}

/*
 * 	COls::MeasureText(cch, taMode, pdx, pdy)
 *
 *	@mfunc
 *		Gets x offset to cp given by CMeasurer _pme + cch chars along with
 *		display dimensions.
 *
 *	@rdesc
 *		xwidth measured
 */
LONG COls::MeasureText(
	LONG	 cch,			//(IN): Max cch to measure
	UINT	 taMode,		//(IN): requested coordinate
	CDispDim *pdispdim)		//(OUT): display dimensions
{
	CMeasurer *	pme = _pme;				
	LONG		cp = pme->GetCp() + cch;	// Enter with me at start of line
	POINT		pt;							// Point at cp in client coords
	BOOL		fAtLogicalRightEdge = FALSE;

	CreateOrGetLine();
	if(!_plsline)
		return 0;
	Assert(pme->_fTarget == FALSE);

	// Query point from cp
	DWORD		cActualDepth;
	LSQSUBINFO	lsqSubInfo[MAX_OBJ_DEPTH];
	LSTEXTCELL	lsTextCell;

    memset(&lsTextCell, 0, sizeof(lsTextCell));
	
	LsQueryLineCpPpoint(_plsline, GetCpLsFromCpRe(cp), MAX_OBJ_DEPTH, &lsqSubInfo[0],
									  &cActualDepth, &lsTextCell);

	pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;
	pdispdim->dx = lsTextCell.dupCell;

	LSTFLOW	 lstflowLine = lsqSubInfo[0].lstflowSubline;

	POINT ptStart = {pme->XFromU(0), pme->_li._yHeight - pme->_li._yDescent};
	POINTUV ptuv = lsTextCell.pointUvStartCell;

	if(taMode & (TA_STARTOFLINE | TA_ENDOFLINE) && cActualDepth > 1)
	{
		ptuv = lsqSubInfo[0].pointUvStartRun;
		if(taMode & TA_ENDOFLINE)
			ptuv.u += lsqSubInfo[0].dupRun;
	}

	//If they ask for position inside ligature or at lim of line, give right edge of cell
	else if (cp > GetCpReFromCpLs(lsTextCell.cpStartCell))
	{
		fAtLogicalRightEdge = TRUE;
		if (lstflowLine != pdispdim->lstflow)
			ptuv.u -= lsTextCell.dupCell;
		else
			ptuv.u += lsTextCell.dupCell;
	}

	LsPointXYFromPointUV(&ptStart, lstflowLine, &ptuv, &pt);

	if(pme->_pPF->InTable())
		pt.x += 2*pme->LXtoDX(pme->_pPF->_dxOffset);

	if (pdispdim->lstflow == lstflowWS && !(taMode & (TA_LOGICAL | TA_STARTOFLINE)))
	{
		if (fAtLogicalRightEdge)
		{
			if ((taMode & TA_RIGHT) == TA_RIGHT)
				pt.x += pdispdim->dx;
			else if (taMode & TA_CENTER)
				pt.x += pdispdim->dx / 2;
			return pt.x;
		}
		else
			pt.x -= pdispdim->dx;
	}

	LONG dx = 0;

	if(taMode & TA_CENTER && !fAtLogicalRightEdge)
		dx = pdispdim->dx;
	if((taMode & TA_CENTER) == TA_CENTER)
		dx >>= 1;

	if (pdispdim->lstflow == lstflowWS && (taMode & TA_LOGICAL))
		dx = -dx;
	return max(0, pt.x + dx);
}

/*
 * 	COls::CchFromXpos(pt, &dx)
 *
 *	@mfunc
 *		Moves _pme to pt.x. Calls LsQueryLinePointPcp()
 */
void COls::CchFromXpos(
	POINT pt,			//@parm Point to find cch for in line
	CDispDim *pdispdim,	//@parm dimensions of object
	LONG	*pcpActual) //@parm CP point
{
	TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::CchFromXpos");

	if(_pme->_pPF->InTable())
		pt.x -= 2*_pme->LXtoDX(_pme->_pPF->_dxOffset);

	// Make point relative to LS coordinate system - (0,0) in LS is at the
	// baseline of the line.
	POINTUV ptuv = {_pme->UFromX(pt.x), -pt.y + _pme->_li._yHeight - _pme->_li._yDescent};

	LONG cpStart = _pme->GetCp();

	CreateOrGetLine();
	if(!_plsline)
		return;
	Assert(_pme->_fTarget == FALSE);

	DWORD		cActualDepth;
	LSQSUBINFO	lsqSubInfo[MAX_OBJ_DEPTH];
	LSTEXTCELL	lsTextCell;

	memset(&lsTextCell, 0, sizeof(lsTextCell));

	LsQueryLinePointPcp(_plsline, &ptuv, MAX_OBJ_DEPTH,	&lsqSubInfo[0], &cActualDepth, &lsTextCell);

	if (cActualDepth == 0) //If we got back empty textcell, let's just query cp explicitly to get information
	{
		LsQueryLineCpPpoint(_plsline, cpStart, MAX_OBJ_DEPTH, &lsqSubInfo[0], &cActualDepth, &lsTextCell);
		Assert(cActualDepth != 0);
	}

	pdispdim->dx = lsTextCell.dupCell;
	pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;

    LONG cp = *pcpActual = GetCpReFromCpLs(lsTextCell.cpStartCell);

	POINTUV ptuvCell;
	//Convert the hit-test point from u,v of line to u,v of cell
	LsPointUV2FromPointUV1(lsqSubInfo[0].lstflowSubline, &lsTextCell.pointUvStartCell, &ptuv,
		lsqSubInfo[cActualDepth - 1].lstflowSubline, &ptuvCell);

	if(ptuvCell.u > lsTextCell.dupCell/2)
		cp += lsTextCell.cpEndCell - lsTextCell.cpStartCell + 1;

	if (_pme->GetPed()->_pbrk)
	{
		// If text breaker is up, verify cluster before placing the caret
		CTxtBreaker* pbrk = _pme->GetPed()->_pbrk;
		LONG		 cpEnd = _pme->GetPed()->GetTextLength();
		while (cp < cpEnd && !pbrk->CanBreakCp(BRK_CLUSTER, cp))
			cp++;
	}

	_pme->_li._cch = cp - _cp;
	_pme->SetCp(cp);
}

/*
 * 	COls::DestroyLine(pdp)
 *
 *	@mfunc
 *		Destroys any line data structures.
 */
void COls::DestroyLine(CDisplay *pdp)
{
	CLock lock;
	if (pdp && pdp != _pdp)
		return;

	if(_plsline)
	{
		g_plsc->DestroyLine(_plsline);
		_plsline = NULL;
	}
	if (_rgcp.Count())
		_rgcp.Clear(AF_KEEPMEM);

	int cchunk = _rglsrunChunk.Count();
	for (int ichunk = 0; ichunk < cchunk; ichunk++)
		_rglsrunChunk.Elem(ichunk)->_cel = 0;
}

/*
 * 	LimitChunk(pch, &cchChunk, f10Mode)
 *
 *	@func
 *		Return object ID at *pch and shorten cchChunk to 1 if object isn't
 *		text and to the count of text chars up to a nontext object if one
 *		occurs within cchChunk and within the current paragraph.
 *
 *	@rdesc
 *		Object ID at *pch
 */
DWORD LimitChunk(const WCHAR *pch, LONG &cchChunk, BOOL f10Mode)
{
	for(LONG i = 0; i < cchChunk && *pch != CR; i++, pch++)
	{
		switch(*pch)
		{
		case WCH_EMBEDDING:
			if(i == 0)
			{
				cchChunk = 1;
				return OBJID_OLE;		// Entered at an OLE object
			}
			cchChunk = i;				// Will break before
		break;

		case EURO:
			if (i == 0)
			{
				for(; i < cchChunk && *pch == EURO; i++)
					pch++;
			}
			cchChunk = i;
		break;

		case FF:
			if(f10Mode)					// RE 1.0 treats FFs as
				continue;				//  ordinary characters

			cchChunk = i;				// Will break before
		break;
		}
	}

	return idObjTextChp;
}

/*
 * 	SetLsChp(dwObjId, pme, plsChp)
 *
 *	@func
 *		Helper function that initializes an LS chp from RE CCharFormat
 *
 *	@rdesc
 *		TRUE iff IsHidden()
 */
BOOL COls::SetLsChp(
	DWORD		dwObjId,	//(IN): Object id
	PLSRUN		plsrun,		//(IN): Current Run
	PLSCHP		plsChp)		//(OUT): LS chp
{
	ZeroMemory(plsChp, sizeof(*plsChp));
	plsChp->idObj = (WORD)dwObjId;

	//If we do FE or Latin kerning, we need to set dcpMaxContext to 2

	if (_pme->GetPed()->IsComplexScript() && plsrun->_a.eScript && !plsrun->IsBullet())
	{
		CUniscribe*		pusp = _pme->Getusp();
		Assert (pusp);
		const SCRIPT_PROPERTIES *psp = pusp->GeteProp(plsrun->_a.eScript);

		if (psp->fComplex || plsrun->_a.fRTL ||
			psp->fNumeric && W32->GetDigitSubstitutionMode() != DIGITS_NOTIMPL)
		{
			// 1. Complex script
			// 2. RTL (internal direction) run (handle mirror glyph i.e.'?')
			// 3. Numeric run and substitution mode is either Native or Context

			plsChp->fGlyphBased	= TRUE;
		}
	}

	DWORD dwEffects = plsrun->_pCF->_dwEffects;

	if(dwEffects & (CFE_UNDERLINE | CFE_LINK | CFE_REVISED))
		plsChp->fUnderline = TRUE;

	if(dwEffects & CFE_STRIKEOUT && !plsrun->IsBullet())
		plsChp->fStrike = TRUE;

	if (plsrun->_pCF->_yOffset || dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT))
	{
		_pme->SetUseTargetDevice(FALSE);
		CCcs *pccs = _pme->Check_pccs(plsrun->IsBullet());
		LONG yOffset, yAdjust;
		pccs->GetOffset(plsrun->_pCF, _pme->_dypInch, &yOffset, &yAdjust);

		plsChp->dvpPos += yOffset + yAdjust;
	}

	//If its an OLE object, but the Object doesn't exist yet, then hide it
	if (dwObjId == OBJID_OLE)
	{
		COleObject * pobj = _pme->GetPed()->GetObjectMgr()->GetObjectFromCp(_pme->GetCp());
		if (!pobj)
			return TRUE;
	}
	return dwEffects & CFE_HIDDEN;
}

/*
 *	FetchAnmRun(pols, cp, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
 *
 *	@func
 *		 LineServices fetch bullets/numbering callback
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI COls::FetchAnmRun(
	LSCP	 cp,		//@parm [IN]: RE cp
	LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
	DWORD *	 pcchRun, 	//@parm [OUT]: Count of characters in run
	BOOL *	 pfHidden, 	//@parm [OUT]: fHidden run?
	PLSCHP	 plsChp, 	//@parm [OUT]: Char properties of run
	PLSRUN * pplsrun)	//@parm [OUT]: Abstract representation of run properties
{
	if (cp == cpFirstAnm && _pme->Get_pPF()->IsRtlPara())
	{
		ZeroMemory(plsChp, sizeof(*plsChp));
		plsChp->idObj = OBJID_REVERSE;
		*pfHidden	= 0; *pcchRun = 1;
		*pplsrun	= GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
		*plpwchRun	= &_szAnm[0];
		return lserrNone;
	}

	*plpwchRun = &_szAnm[cp - cpFirstAnm];
	*pcchRun = _cchAnm - (cp - cpFirstAnm);	
	*pplsrun  = GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
	SetLsChp(idObjTextChp, *pplsrun, plsChp);
	*pfHidden = FALSE;

	if (!_pme->GetNumber())
		plsChp->fUnderline = FALSE;

	return lserrNone;	
}



/*
 *	OlsFetchRun(pols, cp, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
 *
 *	@func
 *		 LineServices fetch-run callback
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFetchRun(
	POLS	 pols,		//@parm [IN]: COls *
	LSCP	 cpLs,		//@parm [IN]: LS cp
	LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
	DWORD *	 pcchRun, 	//@parm [OUT]: Count of characters in run
	BOOL *	 pfHidden, 	//@parm [OUT]: Hidden run?
	PLSCHP	 plsChp, 	//@parm [OUT]: Char properties of run
	PLSRUN * pplsrun)	//@parm [OUT]: Abstract representation of run properties
{
	if(cpLs < 0)
		return pols->FetchAnmRun(cpLs, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun);

	CMeasurer 	*pme = pols->GetMeasurer();
	CTxtEdit 	*ped = pme->GetPed();
	BOOL		fStart = FALSE;
	BOOL		fFetchBraces = ped->IsBiDi() && g_pusp && g_pusp->IsValid() &&
							!ped->_fItemizePending && ped->GetAdjustedTextLength();
	WCHAR		chPassword = pme->GetPasswordChar();
	LONG		cpAccelerator = ped->GetCpAccelerator();
	BOOL		fAccelerator = FALSE;
	BOOL		f10Mode = ped->Get10Mode();

	if (cpLs == pols->_cp)
	{
		//If we are formatting (or re-formatting) the line, cleanup
		if (pols->_rgcp.Count())
			pols->_rgcp.Clear(AF_KEEPMEM);
		pols->_cEmit = 0;
	}
	long cpRe = pols->GetCpReFromCpLs(cpLs);


	pme->SetCp(cpRe);		// start fetching at given cp


	if (fFetchBraces && pme->_rpCF.IsValid())
	{
		// consider emitting braces only at the run boundary or start of a fetched line
		//
		if (cpRe == pols->_cp || !pme->GetIchRunCF() || !pme->GetCchLeftRunCF())
		{
			SHORT	cBrClose, cBrOpen;
			BYTE	bBaseLevel = pme->IsParaRTL() ? 1 : 0;
			BYTE	bLevel, bLevelPrev;
	
			bLevelPrev = bLevel = bBaseLevel;			// assume base level
	
			if (cpRe < ped->GetTextLength())
			{
				CBiDiLevel	level;

				bLevel = pme->_rpCF.GetLevel(&level);	// got level of current run
				fStart = level._fStart;
			}
	
			if (cpRe > pols->_cp && pme->Advance(-1))
			{
				if (pme->_rpPF.SameLevel(bBaseLevel))	// preceding run may be hidden
					bLevelPrev = pme->_rpCF.GetLevel();	// got level of preceding run
				pme->Advance(1);						// resume position
			}
	
			cBrOpen = cBrClose = bLevel - bLevelPrev;
	
			if (fStart)									// start embedding at the current run
				cBrClose = bBaseLevel - bLevelPrev;		// this means we must close all braces of preceding run
	
			cBrClose = max(0, -cBrClose);

			if (cBrClose > 0 && pols->BracesBeforeCp(cpLs) < cBrClose)
			{
				// emit close braces

				if (pols->_cEmit > 0)
				{
					EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
					if (pols->AddBraceCp(cpLs))
						pols->_cEmit--;
#ifdef DEBUG_BRACE
					Tracef(TRCSEVNONE, "CLOSE(%d) cpLs %d: emitted %d", cBrClose, cpLs, pols->_cEmit);
#endif
					return lserrNone;
				}
				else
				{
					// We assert. You can click "Ignore All" with no hang.
					AssertSz(FALSE, "Prevent emitting close brace (no open counterpart)");
				}
			}
	
			if (fStart)									// start embedding at the current run
				cBrOpen = bLevel - bBaseLevel;			// we begin openning braces
	
			if (cBrOpen > 0 && pols->BracesBeforeCp(cpLs) < cBrOpen + cBrClose)
			{
				// emit open braces
	
				EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, OBJID_REVERSE, L" ");
				if (pols->AddBraceCp(cpLs))
					pols->_cEmit++;
#ifdef DEBUG_BRACE
				Tracef(TRCSEVNONE, "OPEN(%d) cpLs %d: emitted %d", cBrOpen, cpLs, pols->_cEmit);
#endif
				return lserrNone;
			}
		}
	}

	// Done fetching braces.
	// Begin getting real data...


#ifdef DEBUG_BRACE
	Tracef(TRCSEVNONE, "cpLs %d: emitted %d", cpLs, pols->_cEmit);
#endif

	// Initialized chunk to count of characters in format run
	LONG	cchChunk = pme->GetCchLeftRunCF();
	DWORD	dwObjId	 = idObjTextChp;

	if(!pme->IsHidden())							// Run isn't hidden
	{
		LONG cch;

		*plpwchRun = pme->GetPch(cch);		// Get text in run
		cchChunk = min(cchChunk, cch);		// Maybe less than cchChunk
		if (!pme->GetPdp()->IsMetafile())
			cchChunk = min(cchChunk, cchLineHint);

		if (chPassword)
		{
			cchChunk = min(cchChunk, int(sizeof(pols->_rgchTemp) / sizeof(WCHAR)));
			memcpy(pols->_rgchTemp, *plpwchRun, cchChunk * sizeof(WCHAR));

			for (int i = 0; i < cchChunk; i++)
			{
				if (!IsASCIIEOP((*plpwchRun)[i]))
					pols->_rgchTemp[i] = chPassword;
				else
					pols->_rgchTemp[i] = (*plpwchRun)[i];
			}
			*plpwchRun = pols->_rgchTemp;
		}

		if(cpAccelerator != -1)
		{
			LONG cpCur = pme->GetCp();		// Get current cp

			// Does accelerator character fall in this chunk?
			if (cpCur < cpAccelerator &&
				cpCur + cchChunk > cpAccelerator)
			{
				// Yes. Reduce chunk to char just before accelerator
				cchChunk = cpAccelerator - cpCur;
			}
			// Is this character the accelerator?
			else if(cpCur == cpAccelerator)
			{							// Set chunk size to 1 since only
				cchChunk = 1;			//  want to output underlined char
				fAccelerator = TRUE;	// Tell downstream routines that
										//  we're handling accelerator
			}
		}
		if(pme->GetCF()->_dwEffects & CFE_ALLCAPS)
		{
			cchChunk = min(cchChunk, int(sizeof(pols->_rgchTemp) / sizeof(WCHAR)));
			memcpy(pols->_rgchTemp, *plpwchRun, cchChunk * sizeof(WCHAR));
			CharUpperBuff(pols->_rgchTemp, cchChunk);
			*plpwchRun = pols->_rgchTemp;
		}

		//Line Services handles page breaks in a weird way, so lets just convert to a CR.
		if (*plpwchRun && *(*plpwchRun) == FF && !f10Mode)
		{
			pols->_szAnm[0] = CR;
			*plpwchRun = pols->_szAnm;
			cchChunk = 1;
		}

		AssertSz(cpRe < ped->GetTextLength() || !ped->IsRich(),	"0-length run at end of control");
		AssertSz(cch || !ped->IsRich(),	"0-length run at end of control");

		// Set run size appropriately for any objects that are in run
		dwObjId = LimitChunk(*plpwchRun, cchChunk, f10Mode);

		// Get regular highlighted positions
		LONG cpSelMin, cpSelMost;
		ped->GetSelRangeForRender(&cpSelMin, &cpSelMost);

		if(cpSelMin != cpSelMost)
		{
			if(cpRe >= cpSelMin)
			{
				if(cpRe < cpSelMost)
				{
					// Current text falls inside selection
					cch = cpSelMost - cpRe;
					cchChunk = min(cchChunk, cch);
				}
			}
			else if(cpRe + cchChunk >= cpSelMin)
			{
				// cp < cpSelMin - run starts outside of selection.
				// Limit text to start of selection.
				cchChunk = cpSelMin - cpRe;
			}
		}
	}

	*pplsrun = pols->GetPlsrun(cpRe, pme->GetCF(), FALSE);
	*pfHidden = pols->SetLsChp(dwObjId, *pplsrun, plsChp);

	if (fAccelerator)
		plsChp->fUnderline = TRUE;

	if(!cchChunk)							// Happens in plain-text controls
	{										//  and if hidden text to end of story
		if (!ped->IsRich() && pols->_cEmit > 0)
		{
			EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
			TRACEWARNSZ("(plain)Auto-emit a close brace to make balance");
			if (pols->AddBraceCp(cpLs))
				pols->_cEmit--;
			return lserrNone;
		}
		cchChunk = 1;
		*plpwchRun = szCR;
		*pfHidden = FALSE;
		//Paragraph marks should not have any script state associated with them,
		//even if the pCF that point to does.
		ZeroMemory(&(*pplsrun)->_a, sizeof((*pplsrun)->_a));
	}
	*pcchRun = cchChunk;

	return lserrNone;
}

/*
 *	OlsGetAutoNumberInfo (pols, plskalAnm, pwchAdd, plschp, pplsrun,
 *						  pfWord95Model, pduaSpaceAnm, pduaWidthAnm)
 *	@func
 *		LineServices fetch autonumbering info callback. Return info needed
 *		by LS for auto numbering. Get the chp/run for last char from auto
 *		number run. Always say we are Word95 model Anm and get rest of info
 *		from paragraph properties.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetAutoNumberInfo(
	POLS	 pols,			//(IN): Client context
	LSKALIGN *plskalAnm,	//(OUT):Justification
	PLSCHP   plschpAnm,
	PLSRUN   *pplsrunAnm,
	WCHAR *	 pwchAdd,		//(OUT):char to add (Nil is treated as none)
	PLSCHP	 plsChp,		//(OUT):chp for bridge character
	PLSRUN * pplsrun,		//(OUT):Run for bridge character
	BOOL *	 pfWord95Model,	//(OUT):Type of autonumber run
	long *	 pduaSpaceAnm,	//(OUT):Relevant iff fWord95Model
	long *	 pduaWidthAnm)	//(OUT):Relevant iff fWord95Model
{
	CMeasurer *pme = pols->GetMeasurer();
	const CParaFormat *pPF = pme->Get_pPF();

	*pplsrunAnm = *pplsrun = pols->GetPlsrun(pme->GetCp(), &pols->_CFBullet, TRUE);
	pols->SetLsChp(idObjTextChp, *pplsrun, plsChp);

	if (!pme->GetNumber())
		plsChp->fUnderline = FALSE;

	*plschpAnm		= *plsChp;
	*pwchAdd		= '\t';
	*pfWord95Model	= TRUE;
	*pduaSpaceAnm	= 0;
	*pduaWidthAnm	= max(pPF->_dxOffset, pPF->_wNumberingTab);
	*plskalAnm		= (LSKALIGN)(lskalLeft + (pPF->_wNumberingStyle & 3));

	return lserrNone;
}

/*
 *	OlsGetNumericSeparators (pols, cp, plspap)
 *
 *	@func
 *		Get numeric separators needed, e.g., for decimal tabs
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetNumericSeparators(
	POLS	pols,			//(IN): pols
	PLSRUN	plsrun,			//(IN): Run (cp here)
	WCHAR *	pwchDecimal,	//(OUT): Decimal separator for this run
	WCHAR *	pwchThousands)	//(OUT): Thousands separator for this run
{
	LCID	lcid = plsrun->_pCF->_lcid;
	WCHAR	ch = TEXT('.');

	// This may need to be virtualized for Win95/CE...
	::GetLocaleInfo(lcid, LOCALE_SDECIMAL, &ch, 1);
	*pwchDecimal = ch;
	ch = TEXT(',');
	::GetLocaleInfo(lcid, LOCALE_STHOUSAND, &ch, 1);
	*pwchThousands = ch;

	return lserrNone;
}

							
/*
 *	OlsFetchPap (pols, cp, plspap)
 *
 *	@func
 *		Fetch paragraph properties
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFetchPap(
	POLS	pols,		//(IN): pols
	LSCP	cpLs, 		//(IN):  an arbitrary cp value inside paragraph
	PLSPAP	plspap)		//(OUT): Paragraph properties.
{
	CMeasurer *pme = pols->GetMeasurer();
	pme->SetCp(pols->_cp);

	const CParaFormat *pPF = pme->Get_pPF();
	CTxtEdit *		   ped = pme->GetPed();

	// Default all results to 0
	ZeroMemory(plspap, sizeof(*plspap));

	//LS doesn't really care where the paragraph starts
	plspap->cpFirst = pols->_cp;

	if(plspap->cpFirst && !pme->fFirstInPara())	// Not first in para: say para
		plspap->cpFirst--;						//  starts one char earlier

	plspap->cpFirstContent = plspap->cpFirst;

	if (pPF->IsRtlPara() && !pPF->InTable())
		plspap->lstflow = lstflowWS;

	// Alignment
	plspap->lskal = (LSKALIGN) g_rgREtoTOMAlign[pPF->_bAlignment];

	if (pPF->_bAlignment == PFA_FULL_INTERWORD)
	{
		plspap->lskal = lskalLeft;
		plspap->lskj = lskjFullInterWord;
	}

	// Kind of EOP
	plspap->lskeop = ped->fUseCRLF() ? lskeopEndPara12 : lskeopEndPara1;

	if (pPF->IsRtlPara())
	{	//For Line Services, left means near and right means far.
		if (plspap->lskal == lskalLeft)
			plspap->lskal = lskalRight;
		else if (plspap->lskal == lskalRight)
			plspap->lskal = lskalLeft;
	}

	if (pols->_fCheckFit)
		plspap->lskal = lskalLeft;

	// Line breaking
	if (pPF->_bAlignment > PFA_FULL_INTERWORD || !ped->fUseSimpleLineBreak() ||
		!pme->GetPdp()->GetWordWrap())		// No word wrap
	{
		plspap->grpf |= fFmiApplyBreakingRules;
	}

	LONG dx = pPF->_dxRightIndent;

	plspap->uaRightBreak   = dx;
	plspap->uaRightJustify = dx;
	if(ped->IsInOutlineView())
	{
		plspap->uaLeft	   = lDefaultTab/2 * (pPF->_bOutlineLevel + 1);
		plspap->duaIndent  = 0;
	}
	else
	{
		plspap->uaLeft	   = pPF->_dxStartIndent + pPF->_dxOffset;
		plspap->duaIndent  = -pPF->_dxOffset;
	}

	if(!pPF->InTable() && plspap->uaLeft < 0)
		plspap->uaLeft = 0;

	// Is this a bulleted paragraph? - ignore bullets in a password
	if(pPF->_wNumbering && pme->fFirstInPara() && !pme->GetPasswordChar() &&
	   !pPF->IsNumberSuppressed())
	{
		CCcs *pccs = pme->GetCcsBullet(&pols->_CFBullet);
		if (pccs)
			pccs->Release();

		plspap->grpf |= fFmiAnm;
		WCHAR *pchAnm = pols->_szAnm;
		pols->_cchAnm = 0;

		if (pPF->IsRtlPara()) //open character
			*pchAnm++ = ' ';
		
		//FUTURE (KeithCu) we turn off Indic digits if there is any Hebrew,
		//which should be refined to do a better job with worldwide documents.
		pols->_cchAnm += pPF->NumToStr(pchAnm, pme->GetNumber(),
			(pme->GetPed()->GetCharFlags() & fHEBREW) ? 0 : fIndicDigits);
		pchAnm += pols->_cchAnm;
		
		if (pPF->IsRtlPara()) 	  //End character for reverser
		{
			*pchAnm++ = wchObjectEnd;
			pols->_cchAnm += 2;	  //alloc space for open and close
		}
		*pchAnm++ = ' ';		  //Ensure a little extra space
		*pchAnm++ = wchObjectEnd; //End character for Anm
		pols->_cchAnm += 2;
	}

	return lserrNone;
}

/*
 *	OlsFetchTabs(pols, LSCP cp, PLSTABS plstabs, BOOL *pfHangingTab,
 *				 long *pduaHangingTab, WCHAR *pwchHangingTabLeader)
 *	@func
 *		Fetch tabs
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFetchTabs(
	POLS	pols,					//(IN):	(COls *)
	LSCP	cp,						//(IN): Arbitrary cp value inside para
	PLSTABS plstabs,				//(OUT): Tabs array
	BOOL *	pfHangingTab,			//(OUT): There is hanging tab
	long *	pduaHangingTab,			//(OUT): dua of hanging tab
	WCHAR *	pwchHangingTabLeader)	//(OUT): Leader of hanging tab
{
	CMeasurer *pme = pols->GetMeasurer();

	const CParaFormat *pPF = pme->Get_pPF();
	const char rgchTabLeader[] = {0, '.', '-', '_', '_', '='};

	LONG		cTabCount = pPF->_bTabCount;
	LONG		i, iActual;
	LSTBD *		prgTab	  = pols->_rgTab;
	const LONG *prgxTabs  = pPF->GetTabs();

	Assert(cTabCount <= MAX_TAB_STOPS && (prgxTabs || !cTabCount));

	plstabs->duaIncrementalTab = pme->GetPed()->GetDefaultTab();

	*pwchHangingTabLeader = 0;
	*pduaHangingTab = pPF->_dxStartIndent + pPF->_dxOffset;
	*pfHangingTab = (!(pPF->InTable()) && pPF->_dxOffset > 0);

	for(i = 0, iActual = 0; i < cTabCount; i++)
	{
		LONG tbAlign, tbLeader;
		pPF->GetTab(i, &prgTab[iActual].ua, &tbAlign, &tbLeader, prgxTabs);

		pme->SetUseTargetDevice(FALSE);
		if (pme->LXtoDX(prgTab[iActual].ua) > pols->_xWidth)
			break;

		if(pPF->InTable())
		{
			tbAlign = lsktLeft;				// Don't have alignment and
			tbLeader = 0;			 		//	leader yet
		}
		if(tbAlign <= tomAlignDecimal)		// Don't include tomAlignBar
		{
			prgTab[iActual].lskt = (lsktab) tbAlign;
			prgTab[iActual].wchTabLeader = rgchTabLeader[tbLeader];
			iActual++;
		}
	}

	plstabs->pTab = prgTab;
	plstabs->iTabUserDefMac = iActual;
	return lserrNone;
}

/*
 *	OlsCheckParaBoundaries (pols, cpOld, cpNew, pfChanged)
 *
 *	@func
 *		Determine if para formatting between para containing cpOld and
 *		that containing cpNew are incompatible and shouldn't be formatted
 *		on the same line when connected by hidden text.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsCheckParaBoundaries(
	POLS	pols,		//(IN): Interface object
	LONG	cpOld,		//(IN): cp in one paragraph
	LONG	cpNew,		//(IN): cp in another paragraph
	BOOL *	pfChanged)	//(OUT): "Dangerous" change between para properties
{

	// It's easier (and safer) to allow LS decide which para properties to take.
	// Else we have to close objects (BiDi, for instance) before hidden EOP.

	*pfChanged = fFalse;			// they're always compatible

	return lserrNone;
}
		
/*
 *	OlsGetRunCharWidths (pols, plrun, deviceID, lpwchRun, cwchRun, du,
 *						 kTFlow, prgDu, pduRun, plimDu)
 *	@func
 *		Get run character widths
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetRunCharWidths(
	POLS	  pols,			//(IN): Interface object
	PLSRUN	  plsrun,		//(IN): Run (cp here)
	enum lsdevice deviceID, //(IN): Preview, reference, or absolute
	LPCWSTR	  lpwchRun,		//(IN): Run of characters
	DWORD	  cwchRun, 		//(IN): Count of characters in run
	long	  du, 			//(IN): Available space for characters
	LSTFLOW	  kTFlow,		//(IN): Text direction and orientation
	int *	  prgDu,		//(OUT): Widths of characters
	long *	  pduRun,		//(OUT): Sum of widths in rgDx[0] to rgDu[limDx-1]
	long *	  plimDu)		//(OUT): Number of widths fetched
{
	CMeasurer *pme = pols->GetMeasurer();
	BOOL fBullet = pols->SetRun(plsrun);
	DWORD i = 0;
	LONG  xWidth, xAdjust, duCalc = 0;
	BOOL  fGlyphRun = FALSE;
	pme->SetUseTargetDevice(deviceID == lsdevReference);
	CCcs *pccs = pme->Check_pccs(fBullet);
	if(!pccs)
		return lserrOutOfMemory;

	if (pme->GetPed()->IsComplexScript() &&
		plsrun->_a.eScript && !plsrun->IsBullet())
	{
		const SCRIPT_PROPERTIES *psp = pme->Getusp()->GeteProp(plsrun->_a.eScript);
		if (psp->fComplex)
			fGlyphRun = TRUE;
	}

	xAdjust = pme->LXtoDX(plsrun->_pCF->_sSpacing);
	for(;i < cwchRun; i++, lpwchRun++)
	{
		if (!fGlyphRun)
		{
			if (IsZerowidthCharacter(*lpwchRun))
				xWidth = 0;
			else
			{
				pccs->Include(*lpwchRun, xWidth);
				xWidth =  max(xWidth + xAdjust, 1);
			}
		}
		else
		{
			xWidth = 0;
			if (!IsDiacriticOrKashida(*lpwchRun, 0))
				xWidth = pccs->_xAveCharWidth;
		}

		duCalc += xWidth;				// Keep running total of width
		*prgDu++ = xWidth;				// Store width in output array
		if(xWidth + duCalc > du)		// Width exceeds width available
		{
			i++;						// Count this char as processed
			break;
		}
	}
	*plimDu = i;						// Store total chars processed
	*pduRun = duCalc;					// Store output total width
	return lserrNone;
}

/*
 *	OlsGetRunTextMetrics (pols, plsrun, deviceID, kTFlow, plsTxMet)
 *
 *	@func
 *		Get run text metrics
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetRunTextMetrics(
	POLS	  pols,			//(IN): interface object
	PLSRUN	  plsrun,		//(IN): run (cp here)
	enum lsdevice deviceID, //(IN): presentation or reference
	LSTFLOW	  kTFlow,		//(IN): text direction and orientation
	PLSTXM	  plsTxMet)		//(OUT): Text metrics
{
	CMeasurer *pme = pols->GetMeasurer();
	BOOL fBullet = pols->SetRun(plsrun);

	// Make sure right font is set for run
	pme->SetUseTargetDevice(deviceID == lsdevReference);
	CCcs *pccs = pme->Check_pccs(fBullet);
	if(!pccs)
		return lserrOutOfMemory;

	LONG yFEAdjust = pccs->AdjustFEHeight(pme->fAdjustFELineHt());

	// Cache descent to save a few indirections
	LONG yDescent = pccs->_yDescent + yFEAdjust;

	// Fill in metric structure
	plsTxMet->dvAscent			= pccs->_yHeight + (yFEAdjust << 1) - yDescent;
    plsTxMet->dvDescent			= yDescent;
    plsTxMet->dvMultiLineHeight = plsTxMet->dvAscent + yDescent;
    plsTxMet->fMonospaced		= pccs->_fFixPitchFont;

	if (plsrun->_pCF->_yOffset)
	{
		LONG yOffset, yAdjust;
		pccs->GetOffset(plsrun->_pCF, deviceID == lsdevReference ? pme->GetDyrInch() :
					    pme->GetDypInch(), &yOffset, &yAdjust);

		if (yOffset < 0)
			plsTxMet->dvDescent -= yOffset;
		else
			plsTxMet->dvAscent += yOffset;
		
	}

	return lserrNone;
}

/*
 *	OlsGetRunUnderlineInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
 *
 *	@func
 *		Get run underline info
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetRunUnderlineInfo(
	POLS	  pols,			//(IN): Interface object
	PLSRUN	  plsrun,		//(IN): Run (cp here)
	PCHEIGHTS pcheights,	//(IN): Height of line
	LSTFLOW	  kTFlow,		//(IN): Text direction and orientation
	PLSULINFO plsUlInfo)	//(OUT): Underline information
{
	CMeasurer *pme = pols->GetMeasurer();
	BOOL	   fBullet = pols->SetRun(plsrun);

	// Initialize output buffer
	ZeroMemory(plsUlInfo, sizeof(*plsUlInfo));
	//REVIEW KeithCu

	// Make sure right font is set for run
	CCcs *pccs = pme->Check_pccs(fBullet);
	if(!pccs)
		return lserrOutOfMemory;

	long dvpUlOffset = pccs->_dyULOffset;

	plsUlInfo->cNumberOfLines = 1;

	// Set underline type
	if (plsrun->_pCF->_dwEffects & CFE_LINK)
		plsUlInfo->kulbase = CFU_UNDERLINE;
	else if (plsrun->_pCF->_dwEffects & (CFE_UNDERLINE | CFE_REVISED))
		plsUlInfo->kulbase	= plsrun->_pCF->_bUnderlineType;
	else
	{
		Assert(pme->GetPed()->GetCpAccelerator() == plsrun->_cp);
		plsUlInfo->kulbase = CFU_UNDERLINE;
	}

	LONG yDescent = pccs->_yDescent + pccs->AdjustFEHeight(pme->fAdjustFELineHt());

	// Some fonts report invalid offset so we fix it up here
	//BUGBUG: subscripts with Line Services don't display.
	if(dvpUlOffset >= yDescent)
		dvpUlOffset = yDescent - 1;

	plsUlInfo->dvpFirstUnderlineOffset = dvpUlOffset;
	plsUlInfo->dvpFirstUnderlineSize   = pccs->_dyULWidth;

	return lserrNone;
}

/*
 *	OlsGetRunStrikethroughInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
 *
 *	@func
 *		Get run strikethrough info
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetRunStrikethroughInfo(
	POLS	  pols,			//(IN): interface object
	PLSRUN	  plsrun,		//(IN): run
	PCHEIGHTS pcheights,	//(IN): height of line
	LSTFLOW	  kTFlow,		//(IN): text direction and orientation
	PLSSTINFO plsStInfo)	//(OUT): Strikethrough information
{
	CMeasurer *pme = pols->GetMeasurer();
	BOOL fBullet = pols->SetRun(plsrun);

	AssertSz(plsrun->_pCF->_dwEffects & CFE_STRIKEOUT, "no strikeout");

	// Make sure right font is set for run
	CCcs *pccs = pme->Check_pccs(fBullet);
	if(!pccs)
		return lserrOutOfMemory;

	// Default number of lines
	plsStInfo->cNumberOfLines = 1;
	plsStInfo->dvpLowerStrikethroughOffset = -pccs->_dySOOffset;
	plsStInfo->dvpLowerStrikethroughSize   = pccs->_dySOWidth;

	return lserrNone;
}


/*	OlsDrawUnderline (pols, plsrun, kUlbase, pptStart, dupUL, dvpUL,
 *					  kTFlow, kDisp, prcClip)
 *	@func
 *		Draw underline
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsDrawUnderline(
	POLS		pols,		//(IN): interface object
	PLSRUN		plsrun,		//(IN): run (cp) to use for underlining
	UINT		kUlbase,	//(IN): underline kind
	const POINT *pptStart,	//(IN): starting position (top left)
	DWORD		dupUL,		//(IN): underline width
	DWORD		dvpUL,		//(IN): underline thickness
	LSTFLOW		lstflow,	//(IN): text direction and orientation
	UINT		kDisp,		//(IN): display mode - opaque, transparent
	const RECT *prcClip)	//(IN): clipping rectangle
{
	CRenderer *pre = pols->GetRenderer();
	Assert(pre->IsRenderer());

	pols->SetRun(plsrun);
	pre->Check_pccs();

	pre->SetSelected(plsrun->IsSelected());
	pre->SetFontAndColor(plsrun->_pCF);

	pre->SetupUnderline(kUlbase);
	pre->RenderUnderline(lstflow == lstflowWS ? pptStart->x - dupUL - 1:
						 pptStart->x, pptStart->y, dupUL, dvpUL);

	return lserrNone;
}

/*
 *	OlsDrawStrikethrough (pols, plsrun, kStbase, pptStart, dupSt, dvpSt,
 *						  kTFlow, kDisp, prcClip)
 *	@func
 *		Draw strikethrough
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsDrawStrikethrough(
	POLS		pols,		//(IN): Interface object
	PLSRUN		plsrun,		//(IN): run (cp) for strikethrough
	UINT		kStbase,	//(IN): strikethrough kind
	const POINT *pptStart,	//(IN): starting position (top left)
	DWORD		dupSt,		//(IN): strikethrough width
	DWORD		dvpSt,		//(IN): strikethrough thickness
	LSTFLOW		lstflow,	//(IN): text direction and orientation
	UINT		kDisp,		//(IN): display mode - opaque, transparent
	const RECT *prcClip)	//(IN): clipping rectangle
{
	CRenderer *pre = pols->GetRenderer();
	Assert(pre->IsRenderer());

	pols->SetRun(plsrun);
	pre->SetSelected(plsrun->IsSelected());

	pre->RenderStrikeOut(lstflow == lstflowWS ? pptStart->x - dupSt - 1:
						 pptStart->x, pptStart->y, dupSt, dvpSt);

	return lserrNone;
}


/*
 *	OlsFInterruptUnderline(pols, plsrunFirst, cpLastFirst, plsrunSecond,
 *						   cpStartSecond, pfInterruptUnderline)
 *	@func
 *		Says whether client wants to interrupt drawing of underline
 *		between the first and second runs
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFInterruptUnderline(
	POLS   pols,				//(IN): Client context
	PLSRUN plsrunFirst,			//(IN): Run pointer for previous run
	LSCP   cpLastFirst,			//(IN): cp of last character of previous run
	PLSRUN plsrunSecond,		//(IN): Run pointer for current run
	LSCP   cpStartSecond,		//(IN): cp of first character of current run
	BOOL * pfInterruptUnderline)//(OUT): Interrupt underline between runs?
{
	CRenderer *pre = pols->GetRenderer();
	Assert(pre->IsRenderer());

	pre->SetSelected(FALSE); //Selection is handled below

	COLORREF cr = pre->GetTextColor(plsrunFirst->_pCF);

	// Interrupt underline if run text colors differ
	*pfInterruptUnderline = cr != pre->GetTextColor(plsrunSecond->_pCF) ||
							plsrunFirst->IsSelected() != plsrunSecond->IsSelected();
	return lserrNone;
}

/*
 *	OlsDrawTextRun (pols, plsrun, kStbase, pptStart, dupSt, dvpSt,
 *						  kTFlow, kDisp, prcClip)
 *	@func
 *		Draw text run
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsDrawTextRun(
	POLS		pols,			//(IN): Interface object
	PLSRUN		plsrun,			//(IN): Run (cp) to use for text
	BOOL		fStrikeoutOkay, //(IN): TRUE <==> allow strikeout
	BOOL		fUnderlineOkay, //(IN): TRUE <==> allow underlining
	const POINT *ppt, 			//(IN): Starting position
	LPCWSTR		pwchRun, 		//(IN): Run of characters
	const int *	rgDupRun, 		//(IN): Character widths
	DWORD		cwchRun, 		//(IN): Count of chars in run
	LSTFLOW		lstflow,		//(IN): Text direction and orientation
	UINT		kDisp,			//(IN): Display mode - opaque, transparent
	const POINT *pptRun,		//(IN): Starting point of run
	PCHEIGHTS	pheightsPres, 	//(IN): Presentation heights for run
	long		dupRun, 		//(IN): Presentation width for run
	long		dupUlLimRun,	//(IN): Underlining limit
	const RECT *prcClip)		//(IN): Clipping rectangle
{
	CRenderer  *pre = pols->GetRenderer();
	RECT		rc = *prcClip;
	Assert(pre->IsRenderer());

	// Set up drawing point and options
	BOOL fBullet = pols->SetRun(plsrun);
	CCcs *pccs = pre->Check_pccs(fBullet);
	if(!pccs)
		return lserrOutOfMemory;

	// y needs to be moved from baseline to top of character
	POINT pt = {ppt->x, ppt->y - (pccs->_yHeight - pccs->_yDescent)};

	if (lstflow == lstflowWS)
		pt.x -= dupRun - 1;

	pre->SetSelected(plsrun->IsSelected());
	pre->SetFontAndColor(plsrun->_pCF);

	if(!fBullet && pre->fBackgroundColor())
	{
		kDisp = ETO_OPAQUE | ETO_CLIPPED;

		POINT ptCur = pre->GetCurPoint();
		ptCur.x = pt.x;
		pre->SetCurPoint(ptCur);
		pre->SetClipLeftRight(dupRun);
		rc = pre->GetClipRect();
	}
	else if (cwchRun == 1 && pwchRun[0] == ' ') //Don't waste time drawing a space.
		return lserrNone;						//(helps grid perf test a lot)

	pre->RenderExtTextOut(pt.x, pt.y, kDisp, &rc, pwchRun, cwchRun, rgDupRun);

	return lserrNone;
}

/*
 *	GetBreakingClasses (pols, plsrun, ch, pbrkclsBefore, pbrkclsAfter)
 *
 *	@func
 *		Line services calls this callback for each run, to obtain the
 *		breaking classes (line breaking behaviors) for each character
 *
 *	    For Quill and RichEdit, the breaking class of a character is
 *		independent of whether it occurs Before or After a break opportunity.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetBreakingClasses(
	POLS	pols,			//(IN): Interface object
	PLSRUN	plsrun,			//(IN): Run (cp) to use for text
	LSCP	cpLs,				//(IN): cp of the character
	WCHAR	ch, 			//(IN): Char to return breaking classes for
	BRKCLS *pbrkclsBefore,	//(OUT): Breaking class if ch is lead char in pair
	BRKCLS *pbrkclsAfter)	//(OUT): Breaking class if ch is trail char in pair
{
	long 		cpRe = pols->GetCpReFromCpLs(cpLs);
	CMeasurer 	*pme = pols->GetMeasurer();
	CTxtBreaker *pbrk = pme->GetPed()->_pbrk;

	// Get line breaking class and report it twice
	*pbrkclsBefore = *pbrkclsAfter = (pbrk && pbrk->CanBreakCp(BRK_WORD, cpRe)) ?
									brkclsOpen :
									GetKinsokuClass(ch);
	return lserrNone;
}

/*
 *	OlsFTruncateBefore (pols, cpCur, wchCur, durCur, cpPrev, wchPrev,
 *						durPrev, durCut, pfTruncateBefore)
 *	@func
 *		Line services support function. This should always return
 *		FALSE for best performance
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFTruncateBefore(
	POLS	pols,				// (IN): Client context
	PLSRUN  plsrunCur,			// (IN): PLSRUN of cp
	LSCP	cpCur,				// (IN): cp of truncation char
	WCHAR	wchCur,				// (IN): Truncation character
	long	durCur,				// (IN): Width of truncation char
	PLSRUN	plsrunPrev,			// (IN): PLSRUN of cpPrev
	LSCP	cpPrev,				// (IN): cp of truncation char
	WCHAR	wchPrev,			// (IN): Truncation character
	long	durPrev,			// (IN): Width of truncation character
	long	durCut,				// (IN): Width from RM until end of current char
	BOOL *	pfTruncateBefore)	// (OUT): Truncation point is before this char
{
	*pfTruncateBefore = FALSE;
	return lserrNone;
}

/*
 *	OlsCanBreakBeforeChar (pols, brkcls, pcond)
 *
 *	@func
 *		Line services calls this callback for a break candidate following an
 *		inline object, to determine whether breaks are prevented, possible or
 *		mandatory
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsCanBreakBeforeChar(
	POLS	pols,		//(IN): Client context
	BRKCLS	brkcls,		//(IN): Breaking class
	BRKCOND *pcond) 	//(OUT): Corresponding break condition
{
	switch (brkcls)
	{
	default:
		*pcond = brkcondCan;
		break;

	case brkclsClose:
	case brkclsNoStartIdeo:
	case brkclsExclaInterr:
	case brkclsGlueA:
		*pcond = brkcondNever;
		break;

	case brkclsIdeographic:
	case brkclsSpaceN:
	case brkclsSlash:
		*pcond = brkcondPlease;	
		break;
	};
	return lserrNone;
}

/*
 *	OlsCanBreakAfterChar (pols, brkcls, pcond)
 *
 *	@func
 *		Line services calls this callback for a break candidate preceding an
 *		inline object, to determine whether breaks are prevented, possible or
 *		mandatory
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsCanBreakAfterChar(
	POLS	pols,		//(IN): Client context
	BRKCLS	brkcls,		//(IN): Breaking class
	BRKCOND *pcond) 	//(OUT): Corresponding break condition
{
	switch (brkcls)
	{
	default:
		*pcond = brkcondCan;
		break;

	case brkclsOpen:
	case brkclsGlueA:
		*pcond = brkcondNever;
		break;

	case brkclsIdeographic:
	case brkclsSpaceN:
	case brkclsSlash:
		*pcond = brkcondPlease;	
		break;
	};
	return lserrNone;
}

/*
 *	OlsFInterruptShaping (pols, kTFlow, plsrunFirst, plsrunSecond, pfInterruptShaping)
 *
 *	@func
 *		Line services calls this callback to find out if you
 *		would like to ligate across these two runs.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFInterruptShaping(
	POLS	pols,					//(IN): Client context
	LSTFLOW	kTFlow,					//(IN): Text direction and orientation
	PLSRUN	plsrunFirst,			//(IN): Run #1
	PLSRUN	plsrunSecond,			//(IN): Run #2
	BOOL	*pfInterruptShaping)	//(OUT): Shape across these 2 runs?
{
	*pfInterruptShaping = FALSE;

	const CCharFormat* pCFFirst = plsrunFirst->_pCF;
	const CCharFormat* pCFSecond = plsrunSecond->_pCF;

	Assert (plsrunFirst->_a.eScript && plsrunSecond->_a.eScript);

	const DWORD dwMask = CFE_BOLD | CFE_ITALIC | CFM_SUBSCRIPT;

	if (pCFFirst == pCFSecond ||
		(plsrunFirst->_a.eScript == plsrunSecond->_a.eScript &&
		!((pCFFirst->_dwEffects ^ pCFSecond->_dwEffects) & dwMask) &&
		pCFFirst->_iFont == pCFSecond->_iFont &&
		pCFFirst->_yOffset == pCFSecond->_yOffset &&
		pCFFirst->_yHeight == pCFSecond->_yHeight))
	{
		// establish link
		plsrunFirst->_pNext = plsrunSecond;
		return lserrNone;
	}

	*pfInterruptShaping = TRUE;
	return lserrNone;
}


// LS calls this callback to shape the codepoint string to a glyph indices string
// for handling glyph based script such as Arabic, Hebrew and Thai.
//
LSERR OlsGetGlyphs(
	POLS 		pols,
	PLSRUN	 	plsrun,
	LPCWSTR		pwch,
	DWORD		cch,
	LSTFLOW		kTFlow,
	PGMAP		pgmap,				// OUT: array of logical cluster information
	PGINDEX*	ppgi, 				// OUT: array of output glyph indices
	PGPROP*		ppgprop, 			// OUT: array of glyph's properties
	DWORD*		pcgi)				// OUT: number of glyph generated
{
	pols->SetRun(plsrun);

	CMeasurer*		pme = pols->GetMeasurer();
	CUniscribe* 	pusp = pme->Getusp();
	Assert (pusp);

	WORD*			pwgi;
	SCRIPT_VISATTR *psva;
	int				cgi;

	pme->SetGlyphing(TRUE);

	// Glyphing doesn't care about the target device but always
	// using target device reduces creation of Cccs in general.
	pme->SetUseTargetDevice(TRUE);

	AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");

	// Digit substitution
	pusp->SubstituteDigitShaper(plsrun, pme);

	if (!(cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva)))
	{
		const SCRIPT_ANALYSIS	saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};

		// Current font cant shape given string.
		// Try SCRIPT_UNDEF so it generates invalid glyphs
		if (!(cgi = (DWORD)pusp->ShapeString(plsrun, (SCRIPT_ANALYSIS*)&saUndef, pme, pwch, (int)cch, pwgi, pgmap, psva)))
		{
			// For whatever reason we still fails.
			// Abandon glyph processing.
			plsrun->_a.fNoGlyphIndex = TRUE;
			cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva);
		}
	}

	*pcgi = cgi;

	DupShapeState(plsrun, cch);

	*ppgi = (PGINDEX)pwgi;
	*ppgprop = (PGPROP)psva;
	pme->SetGlyphing(FALSE);
	return lserrNone;
}

// LS calls this callback to find out glyph positioning for complex scripts
//
LSERR OlsGetGlyphPositions(
	POLS		pols,
	PLSRUN		plsrun,
	LSDEVICE	deviceID,
	LPWSTR		pwch,
	PCGMAP		pgmap,
	DWORD		cch,
	PCGINDEX	pgi,
	PCGPROP		pgprop,
	DWORD		cgi,
	LSTFLOW		kTFlow,
	int*		pgdx,				// OUT: array of glyph advanced width
	PGOFFSET	pgduv)				// OUT: array of offset between glyphs
{
	pols->SetRun(plsrun);

	CMeasurer*		pme = pols->GetMeasurer();
	CUniscribe* 	pusp = pme->Getusp();
	Assert (pusp);
	Assert(pgduv);
	pme->SetGlyphing(TRUE);

	// zero out before passing to shaping engine
	ZeroMemory ((void*)pgduv, cgi*sizeof(GOFFSET));

	pme->SetUseTargetDevice(deviceID == lsdevReference);

	AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");

	if (!pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
	{
		SCRIPT_ANALYSIS	saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};

		if (!pusp->PlaceString(plsrun, &saUndef, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
		{
			plsrun->_a.fNoGlyphIndex = TRUE;
			pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL);
		}
	}

	DupShapeState(plsrun, cch);

	pme->SetGlyphing(FALSE);
	return lserrNone;
}

LSERR OlsDrawGlyphs(
	POLS			pols,
	PLSRUN			plsrun,
	BOOL			fStrikeOut,
	BOOL			fUnderline,
	PCGINDEX		pcgi,
	const int*		pgdx,			// array of glyph width
	const int*		pgdxo,			// array of original glyph width (before justification)
	PGOFFSET		pgduv,			// array of glyph offset
	PGPROP			pgprop,			// array of glyph's properties
	PCEXPTYPE		pgxtype,		// array of expansion type
	DWORD			cgi,
	LSTFLOW			kTFlow,
	UINT			kDisp,
	const POINT*	pptRun,
	PCHEIGHTS		pHeight,
	long			dupRun,
	long			dupLimUnderline,
	const RECT*		prectClip)
{
	BOOL			fBullet = pols->SetRun(plsrun);
	CRenderer*		pre = pols->GetRenderer();
	CUniscribe* 	pusp = pre->Getusp();
	Assert(pusp && pre->IsRenderer());
	pre->SetGlyphing(TRUE);

	RECT			rc = *prectClip;
	CCcs* 			pccs = pre->Check_pccs(fBullet);

	if (!pccs)
		return lserrOutOfMemory;

	// Apply fallback font if we need to
	if (!fBullet)
		pccs = pre->ApplyFontCache(plsrun->IsFallback());

	pre->SetSelected(plsrun->IsSelected());
	pre->SetFontAndColor(plsrun->_pCF);

	// y needs to be moved from baseline to top of character
	POINT 			pt = {pptRun->x, pptRun->y - (pccs->_yHeight - pccs->_yDescent)};

	if (kTFlow == lstflowWS)
		pt.x -= dupRun - 1;	
	
	if(!fBullet && pre->fBackgroundColor())
	{
		kDisp = ETO_OPAQUE | ETO_CLIPPED;

		POINT ptCur = pre->GetCurPoint();
		ptCur.x = pt.x;
		pre->SetCurPoint(ptCur);
		pre->SetClipLeftRight(dupRun);
		rc = pre->GetClipRect();
	}

	if (rc.left == rc.right)
		goto Exit;

	if (pre->GetPdp()->IsMetafile() && !IsEnhancedMetafileDC(pre->GetDC()))
	{
		// -WMF metafile handling-
		//
		//     If the rendering device is WMF metafile. We metafile the codepoint array
		// instead of glyph indices. This requires that the target OS must know how to
		// playback complex script text (shaping, Bidi algorithm, etc.).
		//     Metafiling glyph indices only works for EMF since the WMF's META_EXTTEXTOUT
		// record stores the input string as an array of byte but a glyph index is 16-bit
		// word element.
		//     WMF also must NOT be used to record ExtTextOutW call otherwise the Unicode
		// string will be converted to mutlibyte text using system codepage. Anything
		// outside the codepage then becomes '?'.
		//     We have the workaround for such case in REExtTextOut to make sure we only
		// metafile ExtTextOutA to WMF. (wchao)
		//
	
		LONG			cch;
		const WCHAR*	pwch = pre->GetPch(cch);
		PINT			piDx;
	
		cch = min(cch, pre->GetCchLeftRunCF());
		cch = min(cch, pre->GetLine()._cch - plsrun->_cp + pols->_cp);

		// make sure that we record ETO with proper reading order.
		kDisp |= plsrun->_a.fRTL ? ETO_RTLREADING : 0;

		if (pusp->PlaceMetafileString(plsrun, pre, pwch, (int)cch, &piDx))
		{
			pre->RenderExtTextOut(pt.x, pt.y, kDisp, &rc, pwch, cch, piDx);
			goto Exit;
		}

		TRACEERRORSZ("Recording metafile failed!");

		// Fall through... with unexpected error

		// Else, metafile glyph indices for EMF...
	}

	//This is duplicated from RenderExtTextOut but the params are different so simplest solution
	//was to copy code.
	if(pre->_fDisabled)
	{
		if(pre->_crForeDisabled != pre->_crShadowDisabled)
		{
			// The shadow should be offset by a hairline point, namely
			// 3/4 of a point.  Calculate how big this is in device units,
			// but make sure it is at least 1 pixel.
			DWORD offset = MulDiv(3, pre->_dypInch, 4*72);
			offset = max(offset, 1);

			// Draw shadow
			pre->SetTextColor(pre->_crShadowDisabled);
					
			ScriptTextOut(pre->GetDC(), &pccs->_sc, pt.x + offset, pt.y + offset, kDisp, &rc, &plsrun->_a,
				NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);

			// Now set drawing mode to transparent
			kDisp &= ~ETO_OPAQUE;
		}
		pre->SetTextColor(pre->_crForeDisabled);
	}

	ScriptTextOut(pre->GetDC(), &pccs->_sc, pt.x, pt.y, kDisp, &rc, &plsrun->_a,
				NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);

Exit:
	if (!fBullet)
		pre->ApplyFontCache(0);		// reset font fallback if any

	pre->SetGlyphing(FALSE);
	return lserrNone;
}


/*
 *	OlsResetRunContents (pols, brkcls, pcond)
 *
 *	@func
 *		Line Services calls this routine when a ligature
 *		extends across run boundaries.
 *
 *		We don't have to do anything special here if we are
 *		careful about how we use our PLSRUNs.
 *	@rdesc
 *		LSERR
 */
 LSERR WINAPI OlsResetRunContents(
 	POLS 	pols,		//(IN): Client context
 	PLSRUN 	plsrun,		//(IN): Run being combined
 	LSCP 	cpFirstOld, //(IN): cp of the first run being combined
 	LSDCP 	dcpOld,		//(IN):	dcp of the first run being combined
 	LSCP 	cpFirstNew, //(IN): new cp of the run
 	LSDCP 	dcpNew)		//(IN): new dcp of the run
{
	return lserrNone;
}

/*
 *	OlsCheckForDigit (pols, cp, plspap)
 *
 *	@func
 *		Get numeric separators needed, e.g., for decimal tabs
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsCheckForDigit(
	POLS	pols,		//(IN): pols
	PLSRUN	plsrun,		//(IN): Run (cp here)
	WCHAR	wch,		//(IN): Character to check
	BOOL *	pfIsDigit)	//(OUT): This character is digit
{
	WORD	wType;

	// We could get the run LCID to use for the first parm in the following
	// call, but the digit property should be independent of LCID.
	W32->GetStringTypeEx(0, CT_CTYPE1, &wch, 1, &wType);
	*pfIsDigit = (wType & C1_DIGIT) != 0;

	return lserrNone;
}

/*
 *	OlsGetBreakThroughTab(pols, uaRightMargin, uaTabPos, puaRightMarginNew)
 *
 *	@func
 *		Just follow word95 behavior.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetBreakThroughTab(
	POLS	pols,				//(IN): client context
	long	uaRightMargin,		//(IN): right margin for breaking
	long	uaTabPos,			//(IN): breakthrough tab position
	long *	puaRightMarginNew)	//(OUT): new right margin
{
	*puaRightMarginNew = 20 * 1440;
	return lserrNone;
}

/*
 *	OlsFGetLastLineJustification(pols, lskj, endr, pfJustifyLastLine)
 *
 *	@func
 *		Just say no to justify last line.	
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsFGetLastLineJustification(
	POLS	pols,				//(IN): client context
	LSKJUST lskj,				//(IN): kind of justification
	LSKALIGN lskal,				//(IN): kind of alignment
	ENDRES	endr,				//(IN): result of formatting
	BOOL	*pfJustifyLastLine,	//(OUT): should last line be fully justified
	LSKALIGN *plskalLine)		//(OUT): kind of alignment for this line
{
	*pfJustifyLastLine = FALSE;
	*plskalLine = lskal;
	return lserrNone;
}

/*
 *	OlsGetHyphenInfo(pols, plsrun, pkysr, pwchYsr)
 *
 *	@func
 *		We don't support fancy YSR types, tell LS so.	
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsGetHyphenInfo(
	POLS	pols,				//(IN): client context
	PLSRUN	plsrun,				//(IN)
	DWORD*	pkysr,				//(OUT): Ysr type - see "lskysr.h"
	WCHAR*	pwchYsr)			//(OUT): Character code of YSR
{
	*pkysr = kysrNil;
	*pwchYsr = 0;	
	return lserrNone;
}

/*
 *	OlsReleaseRun (pols, plsrun)
 *
 *	@func
 *		We do nothing because the run is in an array and is
 *		released automatically.
 *
 *	@rdesc
 *		LSERR
 */
LSERR WINAPI OlsReleaseRun(
	POLS	pols,	//(IN): interface object
	PLSRUN	plsrun)	//(IN): run (cp) to use for underlining
{
	return lserrNone;
}

/*
 * 	OlsNewPtr(pols, cBytes)
 *
 *	@func
 *		Memory allocator.
 */
void* WINAPI OlsNewPtr(
	POLS	pols,		//@parm Not used
	DWORD	cBytes)		//@parm Count of bytes to alloc
{
	return PvAlloc(cBytes, 0);
}

/*
 * 	OlsDisposePtr(pols, pv)
 *
 *	@func
 *		Memory deallocator.
 */
void WINAPI OlsDisposePtr(
	POLS	pols,		//@parm Not used
	void *	pv)			//@parm [in]: ptr to free
{
	FreePv(pv);
}

/*
 * 	OlsDisposePtr(pols, pv, cBytes)
 *
 *	@func
 *		Memory reallocator.
 */
void* WINAPI OlsReallocPtr(
	POLS	pols,		//@parm Not used
	void *	pv, 		//@parm [in/out]: ptr to realloc
	DWORD	cBytes)		//@parm Count of bytes to realloc
{
	return PvReAlloc(pv, cBytes);
}
const REVERSEINIT reverseinit =
{
	REVERSE_VERSION,
	wchObjectEnd
};

LSERR WINAPI OlsGetObjectHandlerInfo(POLS pols, DWORD idObj, void* pObjectInfo)
{
	switch (idObj)
	{
	case OBJID_REVERSE:
		memcpy(pObjectInfo, (void *)&reverseinit, sizeof(REVERSEINIT));
		break;
	default:
		AssertSz(0, "Undefined Object handler. Add missing case.");
	}
	return lserrNone;
}

#ifdef DEBUG
/* Debugging APIs */
void WINAPI OlsAssertFailed(
	char *sz,
	char *szFile,
	int	  iLine)
{
	AssertSzFn(sz, szFile, iLine);
}
#endif


extern const LSCBK lscbk =
{
	OlsNewPtr,					// pfnNewPtr
	OlsDisposePtr,				// pfnDisposePtr
	OlsReallocPtr,				// pfnReallocPtr
	OlsFetchRun,				// pfnFetchRun
	OlsGetAutoNumberInfo,		// pfnGetAutoNumberInfo
	OlsGetNumericSeparators,	// pfnGetNumericSeparators
	OlsCheckForDigit,			// pfnCheckForDigit
	OlsFetchPap,				// pfnFetchPap
	OlsFetchTabs,				// pfnFetchTabs
	OlsGetBreakThroughTab,		// pfnGetBreakThroughTab
	OlsFGetLastLineJustification,// pfnFGetLastLineJustification
	OlsCheckParaBoundaries,		// pfnCheckParaBoundaries
	OlsGetRunCharWidths,		// pfnGetRunCharWidths
	0,							// pfnCheckRunKernability
	0,							// pfnGetRunCharKerning
	OlsGetRunTextMetrics,		// pfnGetRunTextMetrics
	OlsGetRunUnderlineInfo,		// pfnGetRunUnderlineInfo
	OlsGetRunStrikethroughInfo,	// pfnGetRunStrikethroughInfo
	0,							// pfnGetBorderInfo
	OlsReleaseRun,				// pfnReleaseRun
	0,							// pfnHyphenate
	OlsGetHyphenInfo,			// pfnGetHyphenInfo
	OlsDrawUnderline,			// pfnDrawUnderline
	OlsDrawStrikethrough,		// pfnDrawStrikethrough
	0,							// pfnDrawBorder
	0,							// pfnDrawUnderlineAsText //REVIEW (keithcu) Need to implement this??
	OlsFInterruptUnderline,		// pfnFInterruptUnderline
	0,							// pfnFInterruptShade
	0,							// pfnFInterruptBorder
	0,							// pfnShadeRectangle
	OlsDrawTextRun,				// pfnDrawTextRun
	0,							// pfnDrawSplatLine
	OlsFInterruptShaping,		// pfnFInterruptShaping
	OlsGetGlyphs,				// pfnGetGlyphs
	OlsGetGlyphPositions,		// pfnGetGlyphPositions
	OlsResetRunContents,		// pfnResetRunContents
	OlsDrawGlyphs,				// pfnDrawGlyphs
	0,							// pfnGetGlyphExpansionInfo
	0,							// pfnGetGlyphExpansionInkInfo
	0,							// pfnGetEms
	0,							// pfnPunctStartLine
	0,							// pfnModWidthOnRun
	0,							// pfnModWidthSpace
	0,							// pfnCompOnRun
	0,							// pfnCompWidthSpace
	0,							// pfnExpOnRun
	0,							// pfnExpWidthSpace
	0,							// pfnGetModWidthClasses
	OlsGetBreakingClasses,		// pfnGetBreakingClasses
	OlsFTruncateBefore,			// pfnFTruncateBefore
	OlsCanBreakBeforeChar,		// pfnCanBreakBeforeChar
	OlsCanBreakAfterChar,		// pfnCanBreakAfterChar
	0,							// pfnFHangingPunct
	0,							// pfnGetSnapGrid
	0,							// pfnDrawEffects
	0,							// pfnFCancelHangingPunct
	0,							// pfnModifyCompAtLastChar
	0,							// pfnEnumText
	0,							// pfnEnumTab
	0,							// pfnEnumPen
	OlsGetObjectHandlerInfo,	// pfnGetObjectHandlerInfo
#ifdef DEBUG
	OlsAssertFailed				// pfnAssertFailed
#else
	0							// pfnAssertFailed
#endif
};

#endif // LINESERVICES