#include "lsmem.h"
#include <limits.h>

#include "lstxtjst.h"

#include "lstxtwrd.h"
#include "lstxtcmp.h"
#include "lstxtglf.h"
#include "lstxtscl.h"
#include "lstxtmap.h"
#include "lsdnset.h"
#include "lsdntext.h"
#include "locchnk.h"
#include "posichnk.h"
#include "objdim.h"
#include "lstxtffi.h"
#include "txtils.h"
#include "txtln.h"
#include "txtobj.h"

#define min(a,b)     ((a) > (b) ? (b) : (a))
#define max(a,b)     ((a) < (b) ? (b) : (a))

static void GetFirstPosAfterStartSpaces(const LSGRCHNK* plsgrchnk, long itxtobjLast, long iwchLim,
				long* pitxtobjAfterStartSpaces, long* piwchAfterStartSpaces, BOOL* pfFirstOnLineAfter);
static LSERR HandleSimpleTextWysi(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long durToDistribute,
			 long dupAvailable, LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fExactSync,
			 BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail);
static LSERR HandleSimpleTextPres(LSKJUST lskjust, const LSGRCHNK* plsgrchnk,
					 long dupAvailable, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
					 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
					 long* pdupText, long* pdupTail);
static LSERR HandleGeneralSpacesExactSync(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long durToDistribute,
			 long dupAvailable, LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail);
static LSERR HandleGeneralSpacesPres(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long dupAvailable,
					 LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
					 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
					 long* pdupText, long* pdupTail);
static LSERR HandleTablesBased(LSKJUST lskjust, const LSGRCHNK* plsgrchnk,
			 long durToDistribute, long dupAvailable, LSTFLOW lstflow,
			 long itxtobjAfterStartSpaces, long iwchAfterStartSpaces, BOOL fFirstOnLineAfter,
			 long itxtobjLast, long iwchLast, long cNonText, BOOL fLastObjectIsText,
			 BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail, long* pdupExtNonText);
static LSERR HandleFullGlyphsExactSync(const LSGRCHNK* plsgrchnk,
			 long durToDistribute, long dupAvailable, LSTFLOW lstflow,
			 long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail);
static LSERR HandleFullGlyphsPres(const LSGRCHNK* plsgrchnk, long dupAvailable,
			 LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail);

/* A D J U S T  T E X T */
/*----------------------------------------------------------------------------
    %%Function: AdjustText
    %%Contact: sergeyge

	The top-level text handler function of
	the PrepLineForDisplay time---calculation of the presentation widths

	It calculates justification area (from first non-space to last non-space),
	checks for the type of justification and WYSIWYG algorythm 
	and redirects the program flow accordingly.
----------------------------------------------------------------------------*/
LSERR AdjustText(LSKJUST lskjust, long durColumnMax, long durTotal, long dupAvailable,
		const LSGRCHNK* plsgrchnk, PCPOSICHNK pposichnkBeforeTrailing, LSTFLOW lstflow,
		BOOL fCompress, DWORD cNonText,	BOOL fSuppressWiggle, BOOL fExactSync,
		BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
		long* pdupText, long* pdupTail,long* pdupExtNonTextObjects,	DWORD* pcExtNonTextObjects)
{
	PILSOBJ pilsobj;
	long itxtobjAfterStartSpaces;
	long itxtobjLast;
	PTXTOBJ ptxtobjLast;
	long iwchAfterStartSpaces;
	long iwchLast;
	long clsgrchnk;
	long durToDistribute;
	BOOL fFirstOnLineAfter;
	BOOL fLastObjectIsText;
	LSDCP dcp;

	*pdupText = 0;
	*pdupTail = 0;
	*pdupExtNonTextObjects = 0;
	*pcExtNonTextObjects = 0;

	clsgrchnk = (long)plsgrchnk->clsgrchnk;

	if (clsgrchnk == 0)
		{
		Assert(cNonText > 0);
		if (lskjust == lskjFullScaled || lskjust == lskjFullInterLetterAligned)
			{
			*pcExtNonTextObjects = cNonText - 1;
			*pdupExtNonTextObjects = dupAvailable;
			}
		return lserrNone;
		}


	pilsobj = ((PTXTOBJ)plsgrchnk->plschnk[0].pdobj)->plnobj->pilsobj;
	Assert (pilsobj->fDisplay);

	if (pilsobj->fPresEqualRef)
		{
		fExactSync = fFalse;
		fSuppressWiggle = fFalse;
		}


	itxtobjLast = pposichnkBeforeTrailing->ichnk;
	dcp = pposichnkBeforeTrailing->dcp;

	Assert(itxtobjLast >= 0);
	Assert(itxtobjLast < clsgrchnk || (itxtobjLast == clsgrchnk && dcp == 0));

	if (dcp == 0 && itxtobjLast > 0)
		{
		itxtobjLast--;
		dcp = plsgrchnk->plschnk[itxtobjLast].dcp;
		}

	ptxtobjLast = (PTXTOBJ)plsgrchnk->plschnk[itxtobjLast].pdobj;

	if (ptxtobjLast->iwchLim > ptxtobjLast->iwchFirst)
		iwchLast = ptxtobjLast->iwchFirst + dcp - 1;
	else
		iwchLast = ptxtobjLast->iwchLim - 1;

	/* In the case of AutoHyphenation, dcp reported by manager is not equal to the real number of characters---
		it should be fixed.	Notice that in the case of "delete before" hyphenation type,
		situation is totally wrong because deleted character was replaced by space and collected by manager  as trailing space.
	*/
	if (ptxtobjLast == ptxtobjLast->plnobj->pdobjHyphen)
		{
		iwchLast = ptxtobjLast->iwchLim - 1;
		}


	Assert(iwchLast >= ptxtobjLast->iwchFirst - 1);
	Assert(iwchLast <= ptxtobjLast->iwchLim - 1);
	
	GetFirstPosAfterStartSpaces(plsgrchnk, itxtobjLast, iwchLast + 1,
								&itxtobjAfterStartSpaces, &iwchAfterStartSpaces, &fFirstOnLineAfter);

	durToDistribute = durColumnMax - durTotal;

	if (!pilsobj->fNotSimpleText)
		{
		if (durToDistribute < 0)
			fSuppressWiggle = fFalse;

		if (fExactSync || fSuppressWiggle)
			{
			return HandleSimpleTextWysi(lskjust, plsgrchnk, durToDistribute, dupAvailable, lstflow, 
				itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast, fExactSync,
				fForcedBreak, fSuppressTrailingSpaces,
				pdupText, pdupTail);
			}
//		else if (fSupressWiggle) /* add later */
		else
			{
			return HandleSimpleTextPres(lskjust, plsgrchnk, dupAvailable, 
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail);
			}
		}
	else
		{
		long itxtobjFirstInLastTextChunk;
		for(itxtobjFirstInLastTextChunk = clsgrchnk; itxtobjFirstInLastTextChunk > 0 &&
			!(plsgrchnk->pcont[itxtobjFirstInLastTextChunk - 1] & fcontNonTextAfter); itxtobjFirstInLastTextChunk--);

		fLastObjectIsText = fTrue;
		if (itxtobjLast < itxtobjFirstInLastTextChunk || 
			itxtobjLast == itxtobjFirstInLastTextChunk && iwchLast < ((PTXTOBJ)plsgrchnk->plschnk[itxtobjFirstInLastTextChunk].pdobj)->iwchFirst )
			{
/* REVIEW sergeyge: check this logic */
			if (cNonText > 0)
				cNonText--;
			fLastObjectIsText = fFalse;
			}

		*pcExtNonTextObjects = cNonText;

		if (fCompress || lskjust == lskjFullInterLetterAligned || lskjust == lskjFullScaled || pilsobj->fSnapGrid)
			{
			return HandleTablesBased(lskjust, plsgrchnk, durToDistribute, dupAvailable, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, fFirstOnLineAfter, 
							itxtobjLast, iwchLast, cNonText, fLastObjectIsText,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail, pdupExtNonTextObjects);
			}
		else if (lskjust == lskjFullGlyphs)
			{
			if (fExactSync || fSuppressWiggle)
				{
				return HandleFullGlyphsExactSync(plsgrchnk, durToDistribute, dupAvailable, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail);
				}
			else
				{
				return HandleFullGlyphsPres(plsgrchnk, dupAvailable, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail);
				}
			}
		else 
			{
			if (plsgrchnk->clsgrchnk == 0)
				return lserrNone;

			Assert(fCompress == fFalse);
			Assert(lskjust == lskjNone || lskjust == lskjFullInterWord);
			if (fExactSync || fSuppressWiggle)
				{
				return HandleGeneralSpacesExactSync(lskjust, plsgrchnk, durToDistribute, dupAvailable, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail);
				}
			else
				{
				return HandleGeneralSpacesPres(lskjust, plsgrchnk, dupAvailable, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							fForcedBreak, fSuppressTrailingSpaces,
							pdupText, pdupTail);
				}
			}

		}

}


/* C A N  C O M P R E S S  T E X T */
/*----------------------------------------------------------------------------
    %%Function: CanCompressText
    %%Contact: sergeyge

	Procedure checks if there is enough compression opportunities on the line
	to squeeze in needed amount (durToCompress).
	Trailing spaces are already subtracted by the manager.
	This procedure takes care of the hanging punctuation
	and possible changes if this break opportunity will be realized

	At the end it helps Word to solve backward compatibility issues
----------------------------------------------------------------------------*/
LSERR CanCompressText(const LSGRCHNK* plsgrchnk, PCPOSICHNK pposichnkBeforeTrailing, LSTFLOW lstflow,
					long durToCompress,	BOOL* pfCanCompress, BOOL* pfActualCompress, long* pdurNonSufficient)
{
	LSERR lserr;
	PILSOBJ pilsobj;
	long clschnk;
	long itxtobjFirstInLastTextChunk; /* if GroupChunk ends with foreign object it is equal to cchnk */
	long itxtobjLast;
	PTXTOBJ ptxtobjLast;
	long iwchLast;
	long iwchLastTemp;
	long iwchAfterStartSpaces;
	long itxtobjAfterStartSpaces;
	long durCompressTotal;
	BOOL fHangingPunct;
	BOOL fChangeBackLastChar;
	long ibrkinf;
	BREAKINFO* pbrkinf = NULL;
	BOOL fFirstOnLineAfter;

	long durCompLastRight;
	long durCompLastLeft;
	long durChangeComp;
	BOOL fCancelHangingPunct;
	MWCLS mwclsLast;
	LSCP cpLim;
	LSCP cpLastAdjustable;
	LSDCP dcp;
	
	*pfCanCompress = fFalse;
	*pfActualCompress = fTrue;

	clschnk = (long)plsgrchnk->clsgrchnk;

	if (clschnk == 0)
		{
		*pfCanCompress = (durToCompress <=0 );
		*pfActualCompress = fFalse;
		return lserrNone;
		}

	Assert(clschnk > 0);

	pilsobj = ((PTXTOBJ)plsgrchnk->plschnk[clschnk-1].pdobj)->plnobj->pilsobj;

	itxtobjLast = pposichnkBeforeTrailing->ichnk;
	dcp = pposichnkBeforeTrailing->dcp;

	Assert(itxtobjLast >= 0);
	Assert(itxtobjLast < clschnk || (itxtobjLast == clschnk && dcp == 0));

	if (dcp == 0 && itxtobjLast > 0)
		{
		itxtobjLast--;
		dcp = plsgrchnk->plschnk[itxtobjLast].dcp;
		}

	ptxtobjLast = (PTXTOBJ)plsgrchnk->plschnk[itxtobjLast].pdobj;

	if (ptxtobjLast->iwchLim > ptxtobjLast->iwchFirst)
		iwchLast = ptxtobjLast->iwchFirst + dcp - 1;
	else
		iwchLast = ptxtobjLast->iwchLim - 1;

	Assert(iwchLast <= ptxtobjLast->iwchLim - 1);
	Assert(iwchLast >= ptxtobjLast->iwchFirst - 1);

	GetFirstPosAfterStartSpaces(plsgrchnk, itxtobjLast, iwchLast + 1,
								&itxtobjAfterStartSpaces, &iwchAfterStartSpaces, &fFirstOnLineAfter);

	if (iwchAfterStartSpaces > iwchLast)
		{
		*pfCanCompress = (durToCompress <=0 );
		*pfActualCompress = fFalse;
		return lserrNone;
		}

	for(itxtobjFirstInLastTextChunk = clschnk; itxtobjFirstInLastTextChunk > 0 && !(plsgrchnk->pcont[itxtobjFirstInLastTextChunk - 1] & fcontNonTextAfter); itxtobjFirstInLastTextChunk--);

	fHangingPunct = fFalse;
	if ((pilsobj->grpf & fTxtHangingPunct) &&
		(itxtobjLast > itxtobjFirstInLastTextChunk ||
		 itxtobjLast == itxtobjFirstInLastTextChunk && iwchLast >= ((PTXTOBJ)plsgrchnk->plschnk[itxtobjFirstInLastTextChunk].pdobj)->iwchFirst) &&
		!(ptxtobjLast->txtf & txtfGlyphBased))
		{
		lserr = (*pilsobj->plscbk->pfnFHangingPunct)(pilsobj->pols, plsgrchnk->plschnk[itxtobjLast].plsrun,
				(BYTE)pilsobj->ptxtinf[iwchLast].mwcls, pilsobj->pwchOrig[iwchLast], &fHangingPunct);
		if (lserr != lserrNone) return lserr;
		}
	
	/* Compression information should be collected under assumption that all chars have correct widths;
		Correct width of HangingPunct should be subtructed as well
	 */

	iwchLastTemp = iwchLast;
	
	fChangeBackLastChar = fFalse;

	for (ibrkinf = 0; ibrkinf < (long)pilsobj->breakinfMac &&
		(pilsobj->pbreakinf[ibrkinf].pdobj != (PDOBJ)ptxtobjLast ||
		((long)pilsobj->pbreakinf[ibrkinf].dcp != iwchLast + 1 - ptxtobjLast->iwchFirst &&
		 ptxtobjLast->txtkind != txtkindNonReqHyphen && ptxtobjLast->txtkind != txtkindOptBreak));
																						ibrkinf++);
	if (ibrkinf < (long)pilsobj->breakinfMac)
		{
		pbrkinf = &pilsobj->pbreakinf[ibrkinf];
		Assert(pbrkinf->brkt != brktHyphen);

		if (pbrkinf->brkt == brktNormal && pbrkinf->u.normal.durFix != 0)
			{
			/* Now manager makes correct calculation */
//			durToCompress += pbrkinf->u.normal.durFix;
			Assert(pilsobj->pdurRight[iwchLast] == - pbrkinf->u.normal.durFix);
			pilsobj->pdur[iwchLast] += pbrkinf->u.normal.durFix;
			pilsobj->pdurRight[iwchLast] = 0;
			fChangeBackLastChar = fTrue;
			}
		else if (pbrkinf->brkt == brktNonReq)
			{
			Assert(iwchLast + 1 == ptxtobjLast->iwchLim);
			/* Now manager makes correct calculation */
//			durToCompress += pbrkinf->u.nonreq.ddurTotal;
			fHangingPunct = fFalse;				/* hanging punct does not make sence in this case */
			if (pbrkinf->u.nonreq.dwchYsr >= 1)
				{
				if (pbrkinf->u.nonreq.wchPrev != 0)
					{
					iwchLastTemp--;
					if (pbrkinf->u.nonreq.wchPrevPrev != 0)
						{
						iwchLastTemp--;
						}
					}
				}
			}
		}


	*pfActualCompress = (durToCompress > 0);

	if (fHangingPunct)
		{
		pilsobj->ptxtinf[iwchLast].fHangingPunct = fTrue;

		durToCompress -= pilsobj->pdur[iwchLast];
		iwchLastTemp--;
		}

	durCompressTotal = 0;

	if (!pilsobj->fSnapGrid)
		{
		lserr = FetchCompressInfo(plsgrchnk, fFirstOnLineAfter, lstflow, 
			itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLastTemp + 1,
			durToCompress, &durCompressTotal);

		if (lserr != lserrNone) return lserr;
		}

	/* Next piece is added to provide mechanism for the backword compatibility with Word */
	durCompLastRight = 0; 
	durCompLastLeft = 0; 

	if	(!(((PTXTOBJ)(plsgrchnk->plschnk[itxtobjLast].pdobj))->txtf & txtfGlyphBased) &&
									!pilsobj->fSnapGrid)
		{
		GetCompLastCharInfo(pilsobj, iwchLast, &mwclsLast, &durCompLastRight, &durCompLastLeft);

		/* First 3 lines of the following condition mean:
			Is Last Significant Character On The Line Text?
		*/
		if (itxtobjFirstInLastTextChunk < (long)clschnk &&
			(itxtobjLast > itxtobjFirstInLastTextChunk ||
				 itxtobjLast == itxtobjFirstInLastTextChunk && iwchLast >= ((PTXTOBJ)plsgrchnk->plschnk[itxtobjFirstInLastTextChunk].pdobj)->iwchFirst) &&
			(durCompLastRight > 0 || durCompLastLeft > 0 || fHangingPunct))
			{
			cpLim = plsgrchnk->plschnk[clschnk-1].cpFirst + plsgrchnk->plschnk[clschnk-1].dcp;

			cpLastAdjustable = plsgrchnk->plschnk[itxtobjLast].cpFirst + 
					iwchLast - ((PTXTOBJ)plsgrchnk->plschnk[itxtobjLast].pdobj)->iwchFirst;

			durChangeComp = 0;

			if (fHangingPunct)
				{
				lserr = (*pilsobj->plscbk->pfnFCancelHangingPunct)(pilsobj->pols, cpLim, cpLastAdjustable,
												pilsobj->pwchOrig[iwchLast], mwclsLast, &fCancelHangingPunct);
				if (lserr != lserrNone) return lserr;

				if (fCancelHangingPunct)
					{
					lserr = FetchCompressInfo(plsgrchnk, fFirstOnLineAfter, lstflow, 
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast + 1,
							LONG_MAX, &durCompressTotal);
					if (lserr != lserrNone) return lserr;

					durToCompress += pilsobj->pdur[iwchLast];
		
					GetCompLastCharInfo(pilsobj, iwchLast, &mwclsLast, &durCompLastRight, &durCompLastLeft);
		
					if ((durCompLastRight + durCompLastLeft) > 0)
						{
						lserr = (*pilsobj->plscbk->pfnModifyCompAtLastChar)(pilsobj->pols, cpLim, cpLastAdjustable,
														pilsobj->pwchOrig[iwchLast], mwclsLast, 
														durCompLastRight, durCompLastLeft, &durChangeComp);
						if (lserr != lserrNone) return lserr;
						}

					Assert(durChangeComp >= 0);
					Assert(durChangeComp == 0 || (durCompLastRight + durCompLastLeft) > 0);
					}
				}
			else
				{
				lserr = (*pilsobj->plscbk->pfnModifyCompAtLastChar)(pilsobj->pols, cpLim, cpLastAdjustable,
					pilsobj->pwchOrig[iwchLast], mwclsLast, durCompLastRight, durCompLastLeft, &durChangeComp);
				if (lserr != lserrNone) return lserr;
		
				Assert(durChangeComp >= 0);
				Assert(durChangeComp == 0 || (durCompLastRight + durCompLastLeft) > 0);
				}

			durCompressTotal -= durChangeComp;
			}
		/* End of the piece is added to provide mechanizm for the backword compatibility with Word */
		}
	/* Restore width changed before the call to FetchCompressInfo */
	if (fChangeBackLastChar)
		{
		pilsobj->pdur[iwchLast] -= pbrkinf->u.normal.durFix;
		pilsobj->pdurRight[iwchLast] = - pbrkinf->u.normal.durFix;
		}

	if (!pilsobj->fSnapGrid)
		*pfCanCompress = (durToCompress <= durCompressTotal);
	else
		*pfCanCompress = (fHangingPunct && durToCompress <= 0);

	*pdurNonSufficient = durToCompress - durCompressTotal;

	return lserrNone;
}


/* D I S T R I B U T E  I N  T E X T */
/*----------------------------------------------------------------------------
    %%Function: DistributeInText
    %%Contact: sergeyge

	Distributes given amount in text chunk equally
	between all participating characters
----------------------------------------------------------------------------*/
LSERR DistributeInText(const LSGRCHNK* plsgrchnk, LSTFLOW lstflow, DWORD cNonText,
									   long durToDistribute, long* pdurNonTextObjects)
{
	LSERR lserr;
	PILSOBJ pilsobj;
	DWORD clschnk;
	long* rgdur;
	long iFirst;
	long iLim;
	PTXTOBJ ptxtobj;
	long itxtobj;
	long i;
	long durTxtobj;
	OBJDIM objdim;

	Unreferenced(lstflow);
	clschnk = plsgrchnk->clsgrchnk;
	Assert(clschnk + cNonText > 0);
	
	if (clschnk == 0)
		{
		*pdurNonTextObjects = durToDistribute;
		return lserrNone;
		}

	pilsobj = ((PTXTOBJ)plsgrchnk->plschnk[0].pdobj)->plnobj->pilsobj;

/* REVIEW sergeyge:Very ugly but still better than anything else?
	Problem case is latin Rubi---NTI was not called and so additional arrays were not allocated

	Original solution---scaling everything down---is not an option becuse than we lose all
	left sided changes in the Rubi subline for Japanese case
*/
	pilsobj->fNotSimpleText = fTrue;

	if (pilsobj->pdurRight == NULL)
		{
		pilsobj->pdurRight = (*pilsobj->plscbk->pfnNewPtr)(pilsobj->pols, sizeof(long) * pilsobj->wchMax );
		Assert (pilsobj->pdurLeft == NULL);
		Assert (pilsobj->ptxtinf == NULL);
		pilsobj->pdurLeft = (*pilsobj->plscbk->pfnNewPtr)(pilsobj->pols, sizeof(long) * pilsobj->wchMax );
		pilsobj->ptxtinf = (*pilsobj->plscbk->pfnNewPtr)(pilsobj->pols, sizeof(TXTINF) * pilsobj->wchMax );
		if (pilsobj->pdurRight == NULL || pilsobj->pdurLeft == NULL || pilsobj->ptxtinf == NULL)
			{
			return lserrOutOfMemory;
			}
		memset(pilsobj->pdurRight, 0, sizeof(long) * pilsobj->wchMax );
		memset(pilsobj->pdurLeft, 0, sizeof(long) * pilsobj->wchMax );
		memset(pilsobj->ptxtinf, 0, sizeof(TXTINF) * pilsobj->wchMax);
		}


	ApplyDistribution(plsgrchnk, cNonText, durToDistribute, pdurNonTextObjects);

	for (itxtobj = 0; itxtobj < (long)clschnk; itxtobj++)
		{
		ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[itxtobj].pdobj;
		if (ptxtobj->txtf & txtfGlyphBased)
			{
			iFirst = ptxtobj->igindFirst;
			iLim = ptxtobj->igindLim;
			rgdur = pilsobj->pdurGind;
			}
		else
			{
			iFirst = ptxtobj->iwchFirst;
			iLim = ptxtobj->iwchLim;
			rgdur = pilsobj->pdur;
			}
		durTxtobj = 0;
		for (i = iFirst; i < iLim; i++)
			{
			durTxtobj += rgdur[i];
			}

		lserr = LsdnGetObjDim(pilsobj->plsc, ptxtobj->plsdnUpNode, &objdim);
		if (lserr != lserrNone) return lserr;

		objdim.dur = durTxtobj;

		lserr = LsdnResetObjDim(pilsobj->plsc, ptxtobj->plsdnUpNode, &objdim);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;
}

/* G E T  T R A I L  I N F O  T E X T */
/*----------------------------------------------------------------------------
    %%Function: GetTrailInfoText
    %%Contact: sergeyge

	Calculates number of spaces at the end of dobj (assuming that it ends at dcp)
	and	the width of the trailing area
----------------------------------------------------------------------------*/
void GetTrailInfoText(PDOBJ pdobj, LSDCP dcp, DWORD* pcNumOfTrailSpaces, long* pdurTrailing)
{
	PILSOBJ pilsobj;
	PTXTOBJ ptxtobj;
	long iwch;
	
	Assert(dcp > 0);
	ptxtobj = (PTXTOBJ)pdobj;
	pilsobj = ptxtobj->plnobj->pilsobj;


	*pcNumOfTrailSpaces = 0;
	*pdurTrailing = 0;


	if (ptxtobj->txtkind == txtkindEOL)
		{
		Assert(dcp == 1);
		*pcNumOfTrailSpaces = 1;
		*pdurTrailing = ptxtobj->plnobj->pilsobj->pdur[ptxtobj->iwchFirst];
		}
	else if (!(pilsobj->grpf & fTxtWrapAllSpaces))
		{
		if (ptxtobj->txtkind == txtkindRegular)
			{

			Assert(ptxtobj->iwchLim >= ptxtobj->iwchFirst + (long)dcp);

			if (!(ptxtobj->txtf & txtfGlyphBased))
				{
				for (iwch = ptxtobj->iwchFirst + dcp - 1;
					iwch >= ptxtobj->iwchFirst && pilsobj->pwchOrig[iwch] == pilsobj->wchSpace; iwch--)
					{
					(*pcNumOfTrailSpaces)++;
					*pdurTrailing += pilsobj->pdur[iwch];
					}
				}
			else
				{
				long igindFirst = 0;
				long iwchFirst = 0;
				long igindLast;
				long igind;

				Assert(FIwchLastInContext(pilsobj, ptxtobj->iwchFirst + dcp - 1));

				igindLast = IgindLastFromIwch(ptxtobj, ptxtobj->iwchFirst + dcp - 1);

				for (iwch = ptxtobj->iwchFirst + dcp - 1;
					iwch >= ptxtobj->iwchFirst && pilsobj->pwchOrig[iwch] == pilsobj->wchSpace; iwch--);
				if (iwch < ptxtobj->iwchFirst)
					{
					iwchFirst = ptxtobj->iwchFirst;
					igindFirst = ptxtobj->igindFirst;
					}
				else
					{
					iwchFirst = IwchLastFromIwch(ptxtobj, iwch) + 1;
					igindFirst = IgindLastFromIwch(ptxtobj, iwch) + 1;
					}

				*pcNumOfTrailSpaces = ptxtobj->iwchFirst + dcp - iwchFirst;

				Assert(igindLast < ptxtobj->igindLim);
				for (igind = igindFirst; igind <= igindLast; igind++)
					*pdurTrailing += pilsobj->pdurGind[igind];
				}
			}
		else if (ptxtobj->txtkind == txtkindSpecSpace)
			{
			*pcNumOfTrailSpaces = dcp;
			*pdurTrailing = 0;
			for (iwch = ptxtobj->iwchFirst + dcp - 1; iwch >= ptxtobj->iwchFirst; iwch--)
					*pdurTrailing += pilsobj->pdur[iwch];
			}
		}
		
}


/* F  S U S P E C T  D E V I C E  D I F F E R E N T */
/*----------------------------------------------------------------------------
    %%Function: FSuspectDeviceDifferent
    %%Contact: sergeyge

	Returns TRUE if Visi character or NonReqHyphen-like character might be present
	on the line, and therefore fast prep-for-displaying is impossible in the case
	when fPresEqualRef is TRUE
----------------------------------------------------------------------------*/
BOOL FSuspectDeviceDifferent(PLNOBJ plnobj)
{
	return (plnobj->pilsobj->fDifficultForAdjust);
}


/* F  Q U I C K  S C A L I N G */
/*----------------------------------------------------------------------------
    %%Function: FQuickScaling
    %%Contact: sergeyge

	Checks if fast scaling is possible in the case when fPresEqualRef is FALSE
----------------------------------------------------------------------------*/
BOOL FQuickScaling(PLNOBJ plnobj, BOOL fVertical, long durTotal)
{
	PILSOBJ pilsobj;
	long durMax;

	pilsobj = plnobj->pilsobj;

	durMax = pilsobj->durRightMaxX;
	if (fVertical)
		durMax = pilsobj->durRightMaxY;

	return (durTotal < durMax && !pilsobj->fDifficultForAdjust && plnobj->ptxtobjFirst == plnobj->ptxtobj);
}


#define UpFromUrFast(ur)	( ((ur) * MagicConstant + (1 << 20)) >> 21)


/* Q U I C K  A D J U S T  E X A C T */
/*----------------------------------------------------------------------------
    %%Function: AdjustText
    %%Contact: sergeyge

	Fast scaling: does not check for width restrictions and for Visi situations,
	assumes that there is only text on the line.
----------------------------------------------------------------------------*/
void QuickAdjustExact(PDOBJ* rgpdobj, DWORD cdobj,	DWORD cNumOfTrailSpaces, BOOL fVertical,
																	long* pdupText, long* pdupTrail)
{
	LSERR lserr;
	PILSOBJ pilsobj;
	PLNOBJ plnobj;
	PTXTOBJ ptxtobj;
	long* rgdur;
	long* rgdup;
	long durSum;
	long dupSum;
	long dupErrLast;
	long dupPrevChar;
	long MagicConstant;
	long dupIdeal;
	long dupReal;
	long dupErrNew;
	long dupAdjust;
	long wCarry;
	long iwchPrev;
	long iwch;
	long itxtobj;
	long dupTotal;

	Assert(cdobj > 0);

	plnobj = ((PTXTOBJ)rgpdobj[0])->plnobj;
	pilsobj = plnobj->pilsobj;

	Assert(!pilsobj->fDifficultForAdjust);

	rgdur = pilsobj->pdur;
	rgdup = plnobj->pdup;

	if (fVertical)
		MagicConstant = pilsobj->MagicConstantY;
	else
		MagicConstant = pilsobj->MagicConstantX;

	itxtobj = 0;

	durSum = 0;
	dupPrevChar = 0;
	/* Pretty dirty; we make sure that for the first iteration dupAdjust will be 0 */
	iwchPrev = ((PTXTOBJ)rgpdobj[0])->iwchFirst;
	dupErrLast = rgdup[iwchPrev] - UpFromUrFast(rgdur[iwchPrev]);
	dupSum = 0;

	for(itxtobj = 0; itxtobj < (long)cdobj; itxtobj++)
		{
		ptxtobj = (PTXTOBJ) rgpdobj[itxtobj];
		Assert(ptxtobj->txtkind != txtkindTab);
		Assert(!(ptxtobj->txtf & txtfGlyphBased));

		for(iwch = ptxtobj->iwchFirst; iwch < ptxtobj->iwchLim; iwch++)
			{
			durSum += rgdur[iwch];
			/* here David Bangs algorithm starts */
			dupIdeal = UpFromUrFast(durSum) - dupSum;
			Assert(dupIdeal >= 0);

			dupReal = rgdup[iwch];
			dupErrNew = dupReal - dupIdeal;
			dupAdjust = dupErrNew - dupErrLast;
			Assert(iwch > ((PTXTOBJ)rgpdobj[0])->iwchFirst || dupAdjust == 0);
			if (dupAdjust != 0)
				{
				wCarry = dupAdjust & 1;

			   	if (dupAdjust > 0)	
						{
			   		dupAdjust >>= 1;
					if (dupErrLast < -dupErrNew)
						dupAdjust += wCarry;
						dupAdjust = min(dupPrevChar /*-1*/, dupAdjust); 
					}
				else
					{
					dupAdjust >>= 1;
					if (dupErrNew < -dupErrLast)
						dupAdjust += wCarry;
					dupAdjust = max(/*1*/ - dupIdeal, dupAdjust); 
					}

				rgdup[iwchPrev] -= dupAdjust;
				dupIdeal += dupAdjust;
				}

			rgdup[iwch] = dupIdeal;
			dupSum += (dupIdeal - dupAdjust);
			dupErrLast = dupReal - dupIdeal;
			iwchPrev = iwch;
			dupPrevChar = dupIdeal;
			/* here David Bangs algorithm stops */
			}

		}


	*pdupText = 0;
	*pdupTrail = 0;
	for (itxtobj=0; itxtobj < (long)cdobj - 1; itxtobj++)
		{
		ptxtobj = (PTXTOBJ) rgpdobj[itxtobj];
		dupTotal = 0;

		for(iwch = ptxtobj->iwchFirst; iwch < ptxtobj->iwchLim; iwch++)
			dupTotal += rgdup[iwch];

		*pdupText += dupTotal;
		lserr = LsdnSetTextDup(plnobj->pilsobj->plsc, ptxtobj->plsdnUpNode, dupTotal);
		Assert(lserr == lserrNone);
		}
	
	Assert(itxtobj == (long)cdobj - 1);
	ptxtobj = (PTXTOBJ) rgpdobj[itxtobj];

	Assert(ptxtobj->txtkind == txtkindEOL && cNumOfTrailSpaces == 1||
				 ptxtobj->iwchLim - ptxtobj->iwchFirst > (long)cNumOfTrailSpaces);
	dupTotal = 0;

	for (iwch = ptxtobj->iwchLim - 1; iwch > ptxtobj->iwchLim - (long)cNumOfTrailSpaces - 1; iwch--)
		{
		dupTotal += rgdup[iwch];
		*pdupTrail += rgdup[iwch];
		}

	Assert(iwch == ptxtobj->iwchLim - (long)cNumOfTrailSpaces - 1);

	for (; iwch >= ptxtobj->iwchFirst; iwch--)
		{
		dupTotal += rgdup[iwch];
		}

	*pdupText += dupTotal;
	lserr = LsdnSetTextDup(plnobj->pilsobj->plsc, ptxtobj->plsdnUpNode, dupTotal);
	Assert(lserr == lserrNone);

	return;
}

/* Internal functions implementation */


/* G E T  F I R S T  P O S  A F T E R  S T A R T  S P A C E S */
/*----------------------------------------------------------------------------
    %%Function: GetFirstPosAfterStartSpaces
    %%Contact: sergeyge

	Reports index of the first char after leading spaces
----------------------------------------------------------------------------*/
static void GetFirstPosAfterStartSpaces(const LSGRCHNK* plsgrchnk, long itxtobjLast, long iwchLim,
				long* pitxtobjAfterStartSpaces, long* piwchAfterStartSpaces, BOOL* pfFirstOnLineAfter)
{
	PILSOBJ pilsobj;
	PLNOBJ plnobj;
	long iwch;
	BOOL fInStartSpace;
	long itxtobj;
	PTXTOBJ ptxtobj;
	long iwchLimInDobj;
	PLSCHNK rglschnk;

	Assert(plsgrchnk->clsgrchnk > 0);

	rglschnk = plsgrchnk->plschnk;
	plnobj = ((PTXTOBJ)rglschnk[0].pdobj)->plnobj;
	pilsobj = plnobj->pilsobj;
	itxtobj = 0;
	ptxtobj = (PTXTOBJ)rglschnk[0].pdobj;
	iwch =  0;

	*pitxtobjAfterStartSpaces = 0;
	*piwchAfterStartSpaces = ptxtobj->iwchFirst;
	*pfFirstOnLineAfter = !(plsgrchnk->pcont[0] & fcontNonTextBefore);

	fInStartSpace = *pfFirstOnLineAfter;

	while (fInStartSpace && itxtobj <= itxtobjLast)
		{
		ptxtobj = (PTXTOBJ)rglschnk[itxtobj].pdobj;

		iwchLimInDobj = iwchLim;
		if (itxtobj < itxtobjLast)
			iwchLimInDobj = ptxtobj->iwchLim;

		if (plsgrchnk->pcont[itxtobj] & fcontNonTextBefore)
			{
			*pfFirstOnLineAfter = fFalse;
			*pitxtobjAfterStartSpaces = itxtobj;
			*piwchAfterStartSpaces = ptxtobj->iwchFirst;
			fInStartSpace = fFalse;
			}

		else if (ptxtobj->txtkind == txtkindRegular)
			{
			for (iwch = ptxtobj->iwchFirst; iwch < iwchLimInDobj && 
								pilsobj->pwchOrig[iwch] == pilsobj->wchSpace; iwch++);

			if ((ptxtobj->txtf & txtfGlyphBased) && iwch < iwchLimInDobj)
				{
				for(; !FIwchFirstInContext(pilsobj, iwch); iwch--);
				Assert(iwch >= ptxtobj->iwchFirst);
				}

			if (iwch < iwchLimInDobj)
				{
				*pitxtobjAfterStartSpaces = itxtobj;
				*piwchAfterStartSpaces = iwch;
				fInStartSpace = fFalse;
				}
			}
	/* REVIEW: sergeyge---should something be changed in the following check? */
		else if (ptxtobj->txtkind != txtkindEOL 
//				&&	 ptxtobj->txtkind != txtkindSpecSpace
				 )
			{
			*pitxtobjAfterStartSpaces = itxtobj;
			*piwchAfterStartSpaces = ptxtobj->iwchFirst;
			fInStartSpace = fFalse;
			}

		itxtobj++;
		iwch = iwchLimInDobj;
		}
		
	if (fInStartSpace)
		{
		*pitxtobjAfterStartSpaces = itxtobj;
		*piwchAfterStartSpaces = iwchLim;
		}

	return;

}


/* H A N D L E  S I M P L E  T E X T  W Y S I */
/*----------------------------------------------------------------------------
    %%Function: HandleSimpleTextWysi
    %%Contact: sergeyge

	Implements Latin-like justification in spaces (if needed)
	on the reference device	and WYSIWYG algorithm for the exact positioning
	under assumption that there were no NominalToIdeal modifications
	(except for Latin kerning) on the line.

	Startegy:
	 Distribute in spaces if needed
	 Scale down width of spaces from the reference device to the presentation one
	 Apply WYSIWYG algorithm
----------------------------------------------------------------------------*/
static LSERR HandleSimpleTextWysi(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long durToDistribute,
			 long dupAvailable, LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fExactSync, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail)
{
	PTXTOBJ ptxtobj;
	BOOL fFullyJustified;

	fFullyJustified = fFalse;

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[0].pdobj;

		if (lskjust != lskjNone && durToDistribute > 0)
			{
			FullPositiveSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast,	ptxtobj->plnobj->pilsobj->pdur, NULL,
							durToDistribute, &fFullyJustified);
			ScaleSpaces(plsgrchnk, lstflow, itxtobjLast, iwchLast);
			}
		else if (!fForcedBreak && durToDistribute < 0)
			{
			fFullyJustified = fTrue;
			NegativeSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, ptxtobj->plnobj->pilsobj->pdur, NULL,
							-durToDistribute);
			ScaleSpaces(plsgrchnk, lstflow, itxtobjLast, iwchLast);
			}
		}

	Unreferenced(fExactSync);
/*	if (fExactSync)*/
		ApplyWysi(plsgrchnk, lstflow);
/*	else
		ApplyNonExactWysi(plsgrchnk, lstflow);
*/

	return FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFullyJustified, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);

}

/* H A N D L E  S I M P L E  T E X T  P R E S */
/*----------------------------------------------------------------------------
    %%Function: HandleSimpleTextPres
    %%Contact: sergeyge

	Implements Latin-like justification in spaces (if needed) 
	on the presentation device
	under assumption that there were no NominalToIdeal modifications
	(except for Latin kerning) on the line.

----------------------------------------------------------------------------*/
static LSERR HandleSimpleTextPres(LSKJUST lskjust, const LSGRCHNK* plsgrchnk,
					 long dupAvailable, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
					 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
					 long* pdupText, long* pdupTail)
{
	PTXTOBJ ptxtobj;
	BOOL fFullyJustified;
	long* rgdup;
	long itxtobj;
	long iwchLim;
	long iwch;
	long dupTotal;
	long dupToDistribute;

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[0].pdobj;

		rgdup = ptxtobj->plnobj->pdup;

		dupTotal = 0;

		/* REVIEW sergeyge: should we think about eliminating this loop for online view? */
		for (itxtobj=0; itxtobj <= itxtobjLast; itxtobj++)
			{
			ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[itxtobj].pdobj;
	
			iwchLim = iwchLast + 1;
			if (itxtobj < itxtobjLast)
				iwchLim = ptxtobj->iwchLim;

			for (iwch = ptxtobj->iwchFirst; iwch < iwchLim; iwch++)
				{
				dupTotal += rgdup[iwch];
				}
			}

		dupToDistribute = dupAvailable - dupTotal;

		if (lskjust != lskjNone && dupToDistribute > 0)
			{
			FullPositiveSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, rgdup, NULL,
							dupToDistribute, &fFullyJustified);
			}
		else if (!fForcedBreak && dupToDistribute < 0)
			{
			NegativeSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, rgdup, NULL,
							-dupToDistribute);
			}

		}

	return FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFalse, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);

}

/* H A N D L E  G E N E R A L  S P A C E S  E X A C T  S Y N C */
/*----------------------------------------------------------------------------
    %%Function: HandleGeneralSpacesExactSync
    %%Contact: sergeyge

	Implements Latin-like justification in spaces (if needed)
	on the reference device	and WYSIWYG algorithm for the exact positioning
	in the general case

	Startegy:
	 Distribute in spaces if needed
	 Scale down changes applied to characters during NTI and distribution
	 If glyphs were detected on the line,
      scale down changes apllied to glyphs during NTI
	  and adjust offsets
	 Apply WYSIWYG algorithm
	 If some characters were changed on the left side
	  prepare additional width array for the display time
----------------------------------------------------------------------------*/
static LSERR HandleGeneralSpacesExactSync(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long durToDistribute,
			 long dupAvailable, LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail)
{
	LSERR lserr;
	PLNOBJ plnobj;
	PILSOBJ pilsobj;
	PTXTOBJ ptxtobj;
	BOOL fFullyJustified = fFalse;
	BOOL fLeftSideAffected = fFalse;
	BOOL fGlyphDetected = fFalse;

	plnobj = ((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->plnobj;
	pilsobj = plnobj->pilsobj;

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[0].pdobj;

		if (lskjust != lskjNone && durToDistribute > 0)
			{
			FullPositiveSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast,	pilsobj->pdur, pilsobj->pdurGind,
							durToDistribute, &fFullyJustified);
			}
		else if (!fForcedBreak && durToDistribute < 0)
			{
			fFullyJustified = fTrue;
			NegativeSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, pilsobj->pdur, pilsobj->pdurGind,
							-durToDistribute);
			}
		}

	ScaleCharSides(plsgrchnk, lstflow, &fLeftSideAffected, &fGlyphDetected);

	if (fGlyphDetected)
		{
		ScaleGlyphSides(plsgrchnk, lstflow);
		UpdateGlyphOffsets(plsgrchnk);
		SetBeforeJustCopy(plsgrchnk);
		}

	ApplyWysi(plsgrchnk, lstflow);

	lserr = FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFullyJustified, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);
	if (lserr != lserrNone) return lserr;

	/* If pdupPen is already used, don't forget to copy pdup there---ScaleSides could change it */
	if (fLeftSideAffected || plnobj->pdup != plnobj->pdupPen)
		{
		lserr = FillDupPen(plsgrchnk, lstflow, itxtobjLast, iwchLast);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;

}

/* H A N D L E  G E N E R A L  S P A C E S  P R E S */
/*----------------------------------------------------------------------------
    %%Function: HandleGeneralSpacesPres
    %%Contact: sergeyge

	Implements Latin-like justification in spaces (if needed)
	directly on the presentation device in the general case

	Startegy:
	 Scale down changes applied to characters during NTI
	 If glyphs were detected on the line,
      scale down changes apllied to glyphs during NTI
	  and adjust glyph offsets
	 Distribute in spaces if needed
	 If glyphs were detected on the line,
	  adjust glyph offsets
	 If some characters were changed on the left side
	  prepare additional width array for the display time
----------------------------------------------------------------------------*/
static LSERR HandleGeneralSpacesPres(LSKJUST lskjust, const LSGRCHNK* plsgrchnk, long dupAvailable,
					 LSTFLOW lstflow, long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
					 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
					 long* pdupText, long* pdupTail)
{
	LSERR lserr;
	PLNOBJ plnobj;
	PTXTOBJ ptxtobj;
	PTXTOBJ ptxtobjLast;
	long* rgdup;
	BOOL fFullyJustified;
	long itxtobj;
	long iwchLastInDobj;
	long iFirst;
	long iLim;
	long i;
	long dupTotal;
	long dupToDistribute;
	BOOL fLeftSideAffected = fFalse;
	BOOL fGlyphDetected = fFalse;

	ptxtobjLast = (PTXTOBJ)plsgrchnk->plschnk[max(0, itxtobjLast)].pdobj;
	plnobj = ptxtobjLast->plnobj;

	ScaleCharSides(plsgrchnk, lstflow, &fLeftSideAffected, &fGlyphDetected);

	if (fGlyphDetected)
		{
		ScaleGlyphSides(plsgrchnk, lstflow);
		UpdateGlyphOffsets(plsgrchnk);
		SetBeforeJustCopy(plsgrchnk);
		}

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[0].pdobj;

		dupTotal = 0;
		for (itxtobj=0; itxtobj <= itxtobjLast; itxtobj++)
			{
			ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[itxtobj].pdobj;

			if (ptxtobj->txtf & txtfGlyphBased)
				{
				iFirst = ptxtobj->igindFirst;
				iwchLastInDobj = iwchLast;
				if (itxtobj < itxtobjLast)
					iwchLastInDobj = ptxtobj->iwchLim - 1;
				iLim = IgindLastFromIwch(ptxtobj, iwchLastInDobj) + 1;
				rgdup = plnobj->pdupGind;
				}
			else
				{
				iFirst = ptxtobj->iwchFirst;
				iLim = iwchLast + 1;
				if (itxtobj < itxtobjLast)
					iLim = ptxtobj->iwchLim;
				rgdup = plnobj->pdup;
				}
	
			for (i =iFirst; i < iLim; i++)
				{
				dupTotal += rgdup[i];
				}
			}

		dupToDistribute = dupAvailable - dupTotal;

		if (lskjust != lskjNone && dupToDistribute > 0)
			{
			FullPositiveSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, plnobj->pdup, plnobj->pdupGind,
							dupToDistribute, &fFullyJustified);
			}
		else if (!fForcedBreak && dupToDistribute < 0)
			{
			NegativeSpaceJustification(plsgrchnk, itxtobjAfterStartSpaces, iwchAfterStartSpaces, 
							itxtobjLast, iwchLast, plnobj->pdup, plnobj->pdupGind,
							-dupToDistribute);
			}

		if (fGlyphDetected)
			{
			UpdateGlyphOffsets(plsgrchnk);
			}
		}

	lserr = FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFalse, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);

	if (lserr != lserrNone) return lserr;

	/* If pdupPen is already used, don't forget to copy pdup there---ScaleSides could change it */
	if (fLeftSideAffected || plnobj->pdup != plnobj->pdupPen)
		{
		lserr = FillDupPen(plsgrchnk, lstflow, itxtobjLast, iwchLast);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;
}

/* H A N D L E  T A B L E  B A S E D */
/*----------------------------------------------------------------------------
    %%Function: HandleTableBased
    %%Contact: sergeyge

	Implements FE-like justification or compression
	on the reference device	and WYSIWYG algorithm for the exact positioning

	Startegy:
	 Apply needed type of justification or compression
	 Scale down changes applied to characters during NTI and justification
	 If glyphs were detected on the line,
      scale down changes apllied to glyphs during NTI
	  and adjust offsets
	 Apply WYSIWYG algorithm
	 If some characters were changed on the left side
	  prepare additional width array for the display time
----------------------------------------------------------------------------*/
static LSERR HandleTablesBased(LSKJUST lskjust, const LSGRCHNK* plsgrchnk,
			 long durToDistribute, long dupAvailable, LSTFLOW lstflow,
			 long itxtobjAfterStartSpaces, long iwchAfterStartSpaces, BOOL fFirstOnLineAfter,
			 long itxtobjLast, long iwchLast, long cNonText, BOOL fLastObjectIsText,
			 BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail, long* pdupExtNonText)
{
	LSERR lserr;
	PILSOBJ pilsobj = NULL;
	PLNOBJ plnobj;
	long durExtNonText = 0;
	DWORD clschnk;
	MWCLS mwclsLast;
	long durCompLastLeft = 0;
	long durCompLastRight = 0;
	long durHangingChar;
	long dupHangingChar = 0;
	BOOL fHangingUsed = fFalse;
	long durCompressTotal;
	long iwchLastTemp;
	BOOL fScaledExp;
	BOOL fFullyJustified = fFalse;
	BOOL fLeftSideAffected = fFalse;
	BOOL fGlyphDetected = fFalse;

	Assert(lskjust == lskjFullInterLetterAligned ||
		   lskjust == lskjFullScaled ||
		   lskjust == lskjNone);

	*pdupExtNonText = 0;
	clschnk = plsgrchnk->clsgrchnk;
	Assert(clschnk > 0);

	plnobj = ((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->plnobj;
	pilsobj = plnobj->pilsobj;

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{

		Assert(clschnk > 0);

		if (pilsobj->fSnapGrid)
			{
			if (durToDistribute < 0)
				{
				Assert(-durToDistribute <= pilsobj->pdur[iwchLast]);
				fHangingUsed = fTrue;
				}
			}
		else if (durToDistribute < 0)
			{
			fFullyJustified = fTrue;
			lserr = FetchCompressInfo(plsgrchnk, fFirstOnLineAfter, lstflow,
				itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast + 1,
				LONG_MAX, &durCompressTotal);
			if (lserr != lserrNone) return lserr;
			
			if (fLastObjectIsText && !(((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->txtf & txtfGlyphBased))
				GetCompLastCharInfo(pilsobj, iwchLast, &mwclsLast, &durCompLastRight, &durCompLastLeft);

			if (pilsobj->ptxtinf[iwchLast].fHangingPunct)
				{		
				Assert(lskjust == lskjNone || lskjust == lskjFullInterLetterAligned || lskjust == lskjFullScaled);
				Assert(fLastObjectIsText);
				if (durCompLastRight >= -durToDistribute)
					{

					Assert(durCompLastRight > 0);
					CompressLastCharRight(pilsobj, iwchLast, durCompLastRight);

					if (lskjust != lskjNone)
						{
						fScaledExp = (lskjust != lskjFullInterLetterAligned);
						lserr = ApplyExpand(plsgrchnk, lstflow, fScaledExp, 
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast, cNonText,
							durCompLastRight + durToDistribute, &durExtNonText, &fFullyJustified);
						if (lserr != lserrNone) return lserr;
						}
					}

				else if (durCompressTotal - durCompLastRight >= -durToDistribute)
					{
					lserr = ApplyCompress(plsgrchnk, lstflow, itxtobjAfterStartSpaces, iwchAfterStartSpaces,
						itxtobjLast, iwchLast, -durToDistribute);
					if (lserr != lserrNone) return lserr;
					}

				else if (durCompressTotal >= -durToDistribute)
					{
					if (durCompLastRight > 0)
						CompressLastCharRight(pilsobj, iwchLast, durCompLastRight);

					lserr = ApplyCompress(plsgrchnk, lstflow, itxtobjAfterStartSpaces, iwchAfterStartSpaces,
						itxtobjLast, iwchLast + 1, -durToDistribute - durCompLastRight);
					if (lserr != lserrNone) return lserr;
					}
				else
					{
					durHangingChar = pilsobj->pdur[iwchLast];
					/* Order of operations is important here because dur of the hanging
						punctuation gets chnaged in the next lines of code and
						durHangingChar is used in ApplyCompress/ApplyExpand calls below!!!
					*/
					if (durCompLastRight > 0)
						CompressLastCharRight(pilsobj, iwchLast, durCompLastRight);

					fHangingUsed = fTrue;

					if (durHangingChar + durToDistribute >= 0)
						{
						fScaledExp = (lskjust != lskjFullInterLetterAligned);
						lserr = ApplyExpand(plsgrchnk, lstflow, fScaledExp,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast,	iwchLast,
							cNonText, durHangingChar + durToDistribute, &durExtNonText, &fFullyJustified);
						if (lserr != lserrNone) return lserr;
						}
					else
						{
						lserr = ApplyCompress(plsgrchnk, lstflow,
							itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
							 -durToDistribute - durHangingChar);
						if (lserr != lserrNone) return lserr;
						}
					}
				}
			else
				{
				if (durCompLastRight >= -durToDistribute)
					{
					Assert(!(((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->txtf & txtfGlyphBased));
					CompressLastCharRight(pilsobj, iwchLast, -durToDistribute);
					}
				else
					{
					if (durCompLastRight > 0)
						{
						Assert(!(((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->txtf & txtfGlyphBased));
						CompressLastCharRight(pilsobj, iwchLast, durCompLastRight);
						}
					lserr = ApplyCompress(plsgrchnk, lstflow, itxtobjAfterStartSpaces, iwchAfterStartSpaces,
						itxtobjLast, iwchLast + 1, -durToDistribute - durCompLastRight);
					if (lserr != lserrNone) return lserr;
					}
				}

			}
		else 
			{
/*			Assert (durToDistribute >= 0 || iwchLast == iwchAfterStartSpaces);---Unfortunately it might be not true
			for the second line of Warichu, because durTotal for it is scaled up value of dup of the first line
*/
			if (lskjust != lskjNone && durToDistribute > 0)
				{
				Assert(lskjust == lskjFullScaled || lskjust == lskjFullInterLetterAligned);
				iwchLastTemp = iwchLast;
				if (!fLastObjectIsText)
					iwchLastTemp++;
				lserr = ApplyExpand(plsgrchnk, lstflow, lskjust == lskjFullScaled,
						itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLastTemp,
						cNonText, durToDistribute, &durExtNonText, &fFullyJustified);
				if (lserr != lserrNone) return lserr;
				}
			}

		}
	else if (cNonText != 0 && lskjust != lskjNone && durToDistribute > 0)
		{
		durExtNonText = durToDistribute;
		}

	ScaleExtNonText(pilsobj, lstflow, durExtNonText, pdupExtNonText);
	
	ScaleCharSides(plsgrchnk, lstflow, &fLeftSideAffected, &fGlyphDetected);

	if (fGlyphDetected)
		{
		ScaleGlyphSides(plsgrchnk, lstflow);
		UpdateGlyphOffsets(plsgrchnk);
		SetBeforeJustCopy(plsgrchnk);
		}

	ApplyWysi(plsgrchnk, lstflow);

	if (fHangingUsed)
		GetDupLastChar(plsgrchnk, iwchLast, &dupHangingChar);
		

	lserr = FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast,
									 dupAvailable + dupHangingChar - *pdupExtNonText,
									 fFullyJustified, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);
	if (lserr != lserrNone) return lserr;

	/* If pdupPen is already used, don't forget to copy pdup there---ScaleSides could change it */
	if (fLeftSideAffected || plnobj->pdup != plnobj->pdupPen)
		{
		lserr = FillDupPen(plsgrchnk, lstflow, itxtobjLast, iwchLast);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;
}

/* H A N D L E  F U L L  G L Y P H S  E X A C T  S Y N C */
/*----------------------------------------------------------------------------
    %%Function: HandleFullGlyphsExactSync
    %%Contact: sergeyge

	Implements glyph-based justification
	on the reference device	and WYSIWYG algorithm
	for the exact positioning

	Startegy:
	 Apply glyph-based justification if needed
	 Scale down changes applied to characters during NTI and justification
	 If glyphs were detected on the line,
      scale down changes apllied to glyphs during NTI
	  and adjust offsets
	 Apply WYSIWYG algorithm
	 If some characters were changed on the left side
	  prepare additional width array for the display time
----------------------------------------------------------------------------*/
static LSERR HandleFullGlyphsExactSync(const LSGRCHNK* plsgrchnk,
			 long durToDistribute, long dupAvailable, LSTFLOW lstflow,
			 long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail)
{
	LSERR lserr;
	PILSOBJ pilsobj;
	PLNOBJ plnobj;
	BOOL fFullyJustified = fFalse;
	BOOL fLeftSideAffected = fFalse;
	BOOL fGlyphDetected = fFalse;

	plnobj = ((PTXTOBJ) plsgrchnk->plschnk[0].pdobj)->plnobj;
	pilsobj = plnobj->pilsobj;

	ScaleGlyphSides(plsgrchnk, lstflow);
	UpdateGlyphOffsets(plsgrchnk);
	SetBeforeJustCopy(plsgrchnk);

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		lserr = ApplyGlyphExpand(plsgrchnk, lstflow, lsdevReference,
						itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
						durToDistribute, pilsobj->pdur, pilsobj->pdurGind, pilsobj->pdurRight, pilsobj->pduGright,
						&fFullyJustified);
		if (lserr != lserrNone) return lserr;
		}

	ScaleCharSides(plsgrchnk, lstflow, &fLeftSideAffected, &fGlyphDetected);

	if (fGlyphDetected)
		{
		ScaleGlyphSides(plsgrchnk, lstflow);
		UpdateGlyphOffsets(plsgrchnk);
		}

	ApplyWysi(plsgrchnk, lstflow);

	lserr = FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFullyJustified, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);
	if (lserr != lserrNone) return lserr;

	/* If pdupPen is already used, don't forget to copy pdup there---ScaleSides could change it */
	if (fLeftSideAffected || plnobj->pdup != plnobj->pdupPen)
		{
		lserr = FillDupPen(plsgrchnk, lstflow, itxtobjLast, iwchLast);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;
}

/* H A N D L E  F U L L  G L Y P H S  P R E S */
/*----------------------------------------------------------------------------
    %%Function: HandleFullGlyphsPres
    %%Contact: sergeyge

	Implements glyph-based justification
	directly on the presentation device

	Startegy:
	 Scale down changes applied to characters during NTI
	 If glyphs were detected on the line,
      scale down changes apllied to glyphs during NTI
	  and adjust offsets
	 Apply glyph-based justification if needed
	 If glyphs were detected on the line,
	  adjust offsets
	 If some characters were changed on the left side
	  prepare additional width array for the display time
----------------------------------------------------------------------------*/
static LSERR HandleFullGlyphsPres(const LSGRCHNK* plsgrchnk,
			 long dupAvailable, LSTFLOW lstflow,
			 long itxtobjAfterStartSpaces, long iwchAfterStartSpaces,
			 long itxtobjLast, long iwchLast, BOOL fForcedBreak, BOOL fSuppressTrailingSpaces,
			 long* pdupText, long* pdupTail)
{
	LSERR lserr;
	PILSOBJ pilsobj;
	PLNOBJ plnobj;
	PTXTOBJ ptxtobj;
	PTXTOBJ ptxtobjLast;
	long* rgdup;
	long itxtobj;
	long iwchLastInDobj;
	long iFirst;
	long iLim;
	long i;
	long dupTotal;
	long dupToDistribute;
	BOOL fFullyJustified = fFalse;
	BOOL fLeftSideAffected = fFalse;
	BOOL fGlyphDetected = fFalse;

	ptxtobjLast = (PTXTOBJ)plsgrchnk->plschnk[max(0, itxtobjLast)].pdobj;
	plnobj = ptxtobjLast->plnobj;
	pilsobj = plnobj->pilsobj;

	ScaleCharSides(plsgrchnk, lstflow, &fLeftSideAffected, &fGlyphDetected);
	if (fGlyphDetected)
		{
		ScaleGlyphSides(plsgrchnk, lstflow);
		UpdateGlyphOffsets(plsgrchnk);
		SetBeforeJustCopy(plsgrchnk);
		}

	if (itxtobjLast > itxtobjAfterStartSpaces || (itxtobjLast == itxtobjAfterStartSpaces && iwchLast >= iwchAfterStartSpaces))
		{
		rgdup = plnobj->pdup;

		dupTotal = 0;
		for (itxtobj=0; itxtobj <= itxtobjLast; itxtobj++)
			{
			ptxtobj = (PTXTOBJ)plsgrchnk->plschnk[itxtobj].pdobj;

			if (ptxtobj->txtf & txtfGlyphBased)
				{
				iFirst = ptxtobj->igindFirst;
				iwchLastInDobj = iwchLast;
				if (itxtobj < itxtobjLast)
					iwchLastInDobj = ptxtobj->iwchLim - 1;
				iLim = IgindLastFromIwch(ptxtobj, iwchLastInDobj) + 1;
				rgdup = plnobj->pdupGind;
				}
			else
				{
				iFirst = ptxtobj->iwchFirst;
				iLim = iwchLast + 1;
				if (itxtobj < itxtobjLast)
					iLim = ptxtobj->iwchLim;
				rgdup = plnobj->pdup;
				}
			

			for (i =iFirst; i < iLim; i++)
				{
				dupTotal += rgdup[i];
				}
			}

		dupToDistribute = dupAvailable - dupTotal;

		lserr = ApplyGlyphExpand(plsgrchnk, lstflow, lsdevPres,
				itxtobjAfterStartSpaces, iwchAfterStartSpaces, itxtobjLast, iwchLast,
				dupToDistribute, plnobj->pdup, plnobj->pdupGind, pilsobj->pdurRight, pilsobj->pduGright,
				&fFullyJustified);
		if (lserr != lserrNone) return lserr;

		if (fGlyphDetected)
			{
			UpdateGlyphOffsets(plsgrchnk);
			}

		}

	lserr = FinalAdjustmentOnPres(plsgrchnk, itxtobjLast, iwchLast, dupAvailable,
									 fFalse, fForcedBreak, fSuppressTrailingSpaces,
									 pdupText, pdupTail);

	if (lserr != lserrNone) return lserr;

	/* If pdupPen is already used, don't forget to copy pdup there---ScaleSides could change it */
	if (fLeftSideAffected || plnobj->pdup != plnobj->pdupPen)
		{
		lserr = FillDupPen(plsgrchnk, lstflow, itxtobjLast, iwchLast);
		if (lserr != lserrNone) return lserr;
		}

	return lserrNone;
}