fontservices/textbase/sgdi/BIDI.CPP
changeset 45 662fa7de7023
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fontservices/textbase/sgdi/BIDI.CPP	Mon Jul 12 14:38:26 2010 +0800
@@ -0,0 +1,1163 @@
+// Copyright (c) 2000-2010 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Bidirectional text reordering; based on the Unicode Bidirectional Reordering Algorithm.
+// 
+//
+
+#include <bidi.h>
+#include "BidiCopy.h"
+#include <s32std.h>
+
+const TInt KBidirectionalStateOverrideStreamValueNone = 0;
+const TInt KBidirectionalStateOverrideStreamValueLeftToRight = 1;
+const TInt KBidirectionalStateOverrideStreamValueRightToLeft = 2;
+
+inline TBool IsSupplementary(TUint aChar)
+/**
+@param aChar The 32-bit code point value of a Unicode character.
+
+@return True, if aChar is supplementary character; false, otherwise.
+*/
+	{
+	return (aChar > 0xFFFF);
+	}
+
+inline TBool IsHighSurrogate(TText16 aInt16)
+/**
+@return True, if aText16 is high surrogate; false, otherwise.
+*/
+	{
+	return (aInt16 & 0xFC00) == 0xD800;
+	}
+
+inline TBool IsLowSurrogate(TText16 aInt16)
+/**
+@return True, if aText16 is low surrogate; false, otherwise.
+*/
+	{
+	return (aInt16 & 0xFC00) == 0xDC00;
+	}
+
+inline TUint JoinSurrogate(TText16 aHighSurrogate, TText16 aLowSurrogate)
+/**
+Combine a high surrogate and a low surrogate into a supplementary character.
+
+@return The 32-bit code point value of the generated Unicode supplementary
+        character.
+*/
+	{
+	return ((aHighSurrogate - 0xD7F7) << 10) + aLowSurrogate;
+	}
+
+TBool TextDefaultsToRightToLeft(const TDesC& aText, TBool* aFound);
+
+TBidirectionalState::TCategory TBidirectionalState::CharToBdCat(TChar::TBdCategory aCat)
+	{
+	return static_cast<TBidirectionalState::TCategory>(
+		1 << static_cast<TInt>(aCat));
+	}
+
+TBidirectionalState::TCategory TBidirectionalState::UintToBdCat(TUint aCat)
+	{
+	return static_cast<TBidirectionalState::TCategory>(1 << aCat);
+	}
+
+void TBidirectionalState::TReorderContext::SetNextCategory(
+	TChar::TBdCategory aCat)
+	{
+	iNextCategory = CharToBdCat(aCat);
+	}
+
+void TBidirectionalState::TReorderContext::SetNextStrongCategory(
+	TChar::TBdCategory aCat)
+	{
+	iNextStrongCategory = CharToBdCat(aCat);
+	}
+
+
+EXPORT_C void TBidirectionalState::ReverseGroups(TText* aStart,TInt aLength)
+/** A utility to reverse text apart from combining characters, which remains after 
+their base characters. This is what is needed when drawing right-to-left text.
+
+@param aStart Start position of text to be reversed.
+@param aLength Length of text to be reversed. */
+	{
+	BidiCopy::ReverseCodes(aStart, aLength);
+	BidiCopy::DeleteUnreversedSurrogates(aStart, aLength);
+	BidiCopy::SubstituteMirrorImages(aStart, aLength);
+	BidiCopy::CorrectGroups(aStart, aLength);
+	BidiCopy::CorrectSurrogatePairs(aStart, aLength);
+	}
+
+
+// A local helper function. Get the next character from a buffer. This
+// function won't check buffer length.
+//
+// @param aText The text buffer to read character from.
+// @param aCharacterIndex Count of characters to skip in aText.
+// @return The character.
+TUint GetOneCharacter(const TText16 *aText, TInt aCharacterIndex)
+	{
+	const TText16 *p = aText;
+	TUint c = 0xFFFF;
+	while (aCharacterIndex >= 0)
+		{
+		c = *p++;
+		ASSERT(!IsLowSurrogate(c));
+		if (IsHighSurrogate(c))
+			{
+			ASSERT(IsLowSurrogate(*p));
+			c = JoinSurrogate(c, *p++);
+			}
+		--aCharacterIndex;
+		}
+	return c;
+	}
+
+
+TInt TBidirectionalState::GenerateBdRunArray(const TText* aText, TInt aLength,
+	TBidirectionalState::TRunInfo* aRun, TInt aMaxRuns)
+/** Analyse the input text for runs of characters that share the same
+bidirectional class. Categories TChar::EEuropeanNumberSeparator and
+TChar::ECommonNumberSeparator are kept as singletons due to a limitation in
+the reordering logic.
+@param aText The text to be analysed.
+@param aLength The length of the text to be analysed.
+@param aRun	Output buffer for the runs after analysis. May be null if there 
+is to be no output.
+@param aMaxRuns The size of the aRun array. No more than this number of runs 
+will be	output.
+@return The number of runs that are required for the full results of the
+analysis.
+@internalTechnology */	
+    {
+	if (aLength == 0)
+		{
+		if (aRun && 0 < aMaxRuns)
+			{
+			aRun[0].iCategory = TChar::EOtherNeutral;
+			aRun[0].iStart = 0;
+			aRun[0].iLength = 0;
+			}
+		return 1;
+		}
+	int runs = 0;
+	int run_start = 0;
+	int run_end = 1;
+	const TText* p = aText;
+	const TText* q = p + aLength;
+	
+	// get the character pointed by 'p', then move 'p' to next character, and adjust 'run_end' if need
+	TChar pc = ::GetOneCharacter(p, 0);
+	TChar::TBdCategory cur_cat = pc.GetBdCategory();
+	++p;
+	if (IsSupplementary(pc))
+		{
+		++p;
+		run_end = 2;	// run_end points to "end" of current character
+		}
+	
+	while (p < q)
+		{
+		// get the character pointed by 'p'
+		pc = ::GetOneCharacter(p, 0);
+		
+		TChar::TBdCategory new_cat = pc.GetBdCategory();
+		if (new_cat != cur_cat)
+			{
+			if (new_cat == TChar::ENonSpacingMark &&
+				cur_cat != TChar::ELeftToRightEmbedding &&
+				cur_cat != TChar::ELeftToRightOverride &&
+				cur_cat != TChar::ERightToLeftEmbedding &&
+				cur_cat != TChar::ERightToLeftOverride &&
+				cur_cat != TChar::EPopDirectionalFormat)
+				new_cat = cur_cat;
+			else if (p < q - 1 &&
+					 (new_cat == TChar::EWhitespace ||
+					  new_cat == TChar::EEuropeanNumberSeparator ||
+					  new_cat == TChar::ECommonNumberSeparator))
+				{
+				TChar nextChar = ::GetOneCharacter(p, 1);
+				TChar::TBdCategory next_cat = nextChar.GetBdCategory();
+				if (new_cat == TChar::EWhitespace)
+					{
+					if ((cur_cat == TChar::ELeftToRight ||
+						 cur_cat == TChar::ERightToLeft ||
+						 cur_cat == TChar::ERightToLeftArabic) && cur_cat == next_cat)
+						new_cat = cur_cat;
+					}
+				else if (cur_cat == TChar::EEuropeanNumber && next_cat == TChar::EEuropeanNumber)
+					new_cat = TChar::EEuropeanNumber;
+				}
+			}
+
+		if (new_cat != cur_cat ||
+			cur_cat == TChar::EEuropeanNumberSeparator ||
+			cur_cat == TChar::ECommonNumberSeparator)
+			{
+			if (aRun && runs < aMaxRuns)
+				{
+				aRun[runs].iCategory = cur_cat;
+				aRun[runs].iStart = run_start;
+				aRun[runs].iLength = run_end - run_start;
+				}
+			
+			runs++;
+			run_start = run_end;
+			}
+
+		p++;
+		run_end++;
+
+		// adjust 'p' and 'run_end'
+		if (IsSupplementary(pc))
+			{
+			p++;
+			run_end++;
+			}
+
+		cur_cat = new_cat;
+		}
+
+	if (aRun && runs < aMaxRuns)
+		{
+		aRun[runs].iCategory = cur_cat;
+		aRun[runs].iStart = run_start;
+		aRun[runs].iLength = run_end - run_start;
+		}
+
+	return runs + 1;
+	}
+
+
+EXPORT_C TInt TBidirectionalState::ReorderText(const TText* aText,TInt aLength,TBool aParRightToLeft,
+											   TText*& aNewText)
+/** Reorders text according to the Unicode Bidirectional Reordering algorithm.
+
+Reorders the input text from logical order (which may be bidirectional) to 
+display order (strictly left to right).
+
+@param aText The input text in logical order.
+@param aLength The length of the input text.
+@param aParRightToLeft ETrue if the default directionality of the text to be 
+re-ordered is right-to-left.
+@param aNewText Returns the re-ordered text. If the text did not need re-ordering, 
+or if there was an error, aText will be returned. Otherwise, ownership of 
+a newly allocated buffer will be returned to the caller. This buffer must 
+be deleted with delete[] (or CleanupArrayDeletePushL()) and not delete (or 
+CleanupStack::PushL()).
+@return A system-wide error value if there has been an error; KErrNone if there 
+has not. */
+	{
+	aNewText = const_cast<TText*>(aText);
+	if (aLength < 2)
+		return KErrNone;
+
+	int error = KErrNone;
+	TBidirectionalState::TRunInfo run_info;
+	run_info.iDirection = 0;
+	run_info.iIndex = 0;
+	run_info.iStart = 0;
+	run_info.iLength = aLength;
+	TBidirectionalState::TRunInfo* run_info_array = &run_info;
+	TBidirectionalState::TRunInfo* allocated_run_info_array = 0;
+	int runs = GenerateBdRunArray(aText, aLength, run_info_array, 1);
+	if (runs > 1)
+		{
+		allocated_run_info_array = new TBidirectionalState::TRunInfo[runs];
+		if (allocated_run_info_array)
+			{
+			run_info_array = allocated_run_info_array;
+			GenerateBdRunArray(aText, aLength, run_info_array, runs);
+			}
+		else
+			{
+			// the run cannot be allocated: stick with our single l-to-r run
+			error = KErrNoMemory;
+			runs = 1;
+			}
+		}
+	if (error == KErrNone)
+		{
+		TBidirectionalState state;
+		state.ReorderLine(run_info_array, runs, ETrue, ETrue, aParRightToLeft,
+			TChar::EOtherNeutral, TChar::EOtherNeutral);
+		}
+
+	// If there was only one run and it's left-to-right, we've finished.
+	if (!allocated_run_info_array && run_info.iDirection == 0)
+		return error;
+
+	// Reorder the text into a new buffer.
+	TText* buffer = new TText[aLength];
+	if (!buffer)
+		{
+		delete [] allocated_run_info_array;
+		return KErrNoMemory;
+		}
+	const TBidirectionalState::TRunInfo* r = run_info_array;
+	TText* dest = buffer;
+	for (int i = 0; i < runs; i++, r++)
+		{
+		const TText* source = &aText[r->iStart];
+		int length = r->iLength;
+		Mem::Copy(dest,source,length * sizeof(TText));
+		if (r->iDirection)
+			ReverseGroups(dest,length);
+		dest += length;
+		}
+
+	delete [] allocated_run_info_array;
+	aNewText = buffer;
+	return KErrNone;
+	}
+
+
+EXPORT_C TBidirectionalState::TBidirectionalState()
+/** Standard constructor. */
+	{
+	Reset();
+	}
+
+
+/** Reorders a line of text and updates the bidirectional state for the next line.
+
+@param aRunInfo An array of objects representing runs of characters with the 
+same bidirectional category. Any number of characters can be combined into 
+a run if they have the same category, except for the categories TChar::EEuropeanNumberSeparator 
+and TChar::ECommonNumberSeparator, which should be put into single-character 
+runs because the reordering logic depends on this.
+@param aRuns Number of 'run info' objects.
+@param aParStart Tells the function whether the line is the first line of a 
+paragraph.
+@param aParEnd Tells the function whether the line is the last line of a paragraph.
+@param aParRightToLeft ETrue if the default directionality of the text to be 
+re-ordered is right-to-left.
+@param aNextCategory The category of the character immediately after the end 
+of the line. This is ignored if aParEnd is ETrue.
+@param aNextStrongCategory The category of the first strong character (one 
+of the categories ELeftToRight, ELeftToRightEmbedding, ELeftToRightOverride, 
+ERightToLeft, ERightToLeftArabic, ERightToLeftEmbedding or ERightToLeftOverride) 
+after the end of the line. This is ignored if aParEnd is ETrue.
+@param aVisualEndIsAmbiguous EFalse if the logical end of this line is at the
+visual end and the logical beginning of the next line is at the visual beginning.
+*/
+EXPORT_C void TBidirectionalState::ReorderLine(TRunInfo* aRunInfo, TInt aRuns,
+	TBool aParStart, TBool aParEnd, TBool aParRightToLeft,
+	TChar::TBdCategory aNextCategory, TChar::TBdCategory aNextStrongCategory,
+	TBool& aVisualEndIsAmbiguous)
+	{
+	ReorderLine(aRunInfo, aRuns, aParStart, aParEnd, aParRightToLeft,
+		aNextCategory, aNextStrongCategory);
+	if (iStackLevel  != 0)
+		{
+		aVisualEndIsAmbiguous = ETrue;
+		return;
+		}
+	TCategory nextCat = CharToBdCat(aNextCategory);
+	TCategory nextStrong = CharToBdCat(aNextStrongCategory);
+	const TUint KAllStrongLeftToRight =
+		ELeftToRight | ELeftToRightEmbedding | ELeftToRightOverride;
+	const TUint KAllStrongRightToLeft =
+		ERightToLeft | ERightToLeftArabic | ERightToLeftEmbedding | ERightToLeftOverride;
+	if (aParRightToLeft)
+		{
+		// Ambiguous if any of the surrounding categories are strongly left-to-right
+		aVisualEndIsAmbiguous =
+			(iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
+			& KAllStrongLeftToRight;
+		}
+	else
+		{
+		// Ambiguous if any of the surrounding categories are strongly right-to-left
+		aVisualEndIsAmbiguous =
+			(iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
+			& KAllStrongRightToLeft;
+		}
+	}
+/** Reorders a line of text and updates the bidirectional state for the next line.
+
+@param aRunInfo An array of objects representing runs of characters with the 
+same bidirectional category. Any number of characters can be combined into 
+a run if they have the same category, except for the categories TChar::EEuropeanNumberSeparator 
+and TChar::ECommonNumberSeparator, which should be put into single-character 
+runs because the reordering logic depends on this.
+@param aRuns Number of 'run info' objects.
+@param aParStart Tells the function whether the line is the first line of a 
+paragraph.
+@param aParEnd Tells the function whether the line is the last line of a paragraph.
+@param aParRightToLeft ETrue if the default directionality of the text to be 
+re-ordered is right-to-left.
+@param aNextCategory The category of the character immediately after the end 
+of the line. This is ignored if aParEnd is ETrue.
+@param aNextStrongCategory The category of the first strong character (one 
+of the categories ELeftToRight, ELeftToRightEmbedding, ELeftToRightOverride, 
+ERightToLeft, ERightToLeftArabic, ERightToLeftEmbedding or ERightToLeftOverride) 
+after the end of the line. This is ignored if aParEnd is ETrue. */
+EXPORT_C void TBidirectionalState::ReorderLine(TRunInfo* aRunInfo, TInt aRuns,
+	TBool aParStart, TBool aParEnd, TBool aParRightToLeft,
+	TChar::TBdCategory aNextCategory, TChar::TBdCategory aNextStrongCategory)
+	{
+	// Reset if this is a new paragraph.
+	if (aParStart)
+		{
+		Reset();
+		iPreviousCategory = EOtherNeutral;
+		if (aParRightToLeft)
+			{
+			iStack[0].iEmbeddingLevel = 1;
+			iPreviousStrongCategory = ERightToLeft;
+			}
+		}
+
+	// Initialise the context object.
+	TReorderContext context;
+	context.iRunInfo = aRunInfo;
+	context.iRuns = aRuns;
+	context.iLastStrongCategory = iPreviousStrongCategory;
+	if (aParEnd)
+		context.iNextCategory = context.iNextStrongCategory = EOtherNeutral;
+	else
+		{
+		context.iNextCategory = CharToBdCat(aNextCategory);
+		context.iNextStrongCategory = CharToBdCat(aNextStrongCategory);
+		}
+
+	// Initialise output data and find out what categories are present so that unnecessary steps can be skipped.
+	context.iCategories = iPreviousCategory | context.iNextCategory | context.iNextStrongCategory;
+	for (TInt i = 0; i != aRuns; ++i)
+		{
+		aRunInfo[i].iEmbeddingLevel = iStack[0].iEmbeddingLevel;
+		aRunInfo[i].iDirection = 0;
+		aRunInfo[i].iIndex = i;
+		aRunInfo[i].iCategory = UintToBdCat(aRunInfo[i].iCategory);
+		context.iCategories |= aRunInfo[i].iCategory;
+		}
+
+	// Do nothing if no right-to-left material is present.
+	if (aRuns == 0 ||
+		(iStackLevel == 0 && iStack[0].iEmbeddingLevel == 0 &&
+		 !(context.iCategories & (ERightToLeftGroup | EBdControlsGroup))))
+		return;
+
+	// Perform the bidirectional algorithm.
+	if ((context.iCategories & EBdControlsGroup) ||
+		State().iOverrideState != ENoOverrideState)
+		HandleBdControls(context);
+	ResolveWeakTypesW1W2W3(context);
+	ResolveWeakTypesW4W5W6(context);
+	ResolveWeakTypesW7(context);
+	if (context.iCategories & EOtherNeutral)
+		ResolveNeutralTypes(context);
+	ResolveImplicitLevels(context);
+	PrepareForNextLine(context);
+	ReorderRuns(context);
+	}
+
+
+void TBidirectionalState::PrepareForNextLine(const TReorderContext& aContext)
+/**
+Fold context information back into TBidirectionalState.
+@internalComponent
+*/	
+   {
+	if (aContext.iRuns != 0)
+		{
+		iPreviousCategory = static_cast<TCategory>(
+			aContext.iRunInfo[aContext.iRuns - 1].iCategory);
+		iPreviousStrongCategory = aContext.iLastStrongCategory;
+		}
+	}
+
+
+void TBidirectionalState::HandleBdControls(TReorderContext& aContext)
+/**
+Handle LRO, RLO, LRE, RLE and PDF. After this phase, these categories will no
+longer be found.
+
+This corresponds to Unicode(3.2) Bidirectional Algorithm phases X1-X7.
+Phase X8 is not required as the run is assumed to be all in one paragraph.
+Phases X9-X10 are implicit in other functions.
+
+@internalComponent
+*/	
+   {
+	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
+	for (TInt i = 0; i != aContext.iRuns; ++i)
+		{
+		TRunInfo* r = aContext.iRunInfo + i;
+		TCategory nextCatInLine = i < aContext.iRuns - 1?
+			(TCategory)(r[1].iCategory) : ENoCategory;
+
+		TBool was_pdf = FALSE;
+		if (r->iCategory & EBdControlsGroup)
+			{
+			if (r->iCategory == EPopDirectionalFormat)
+				{
+				if (iStackLevel > 0)
+					{
+					was_pdf = TRUE;
+					r->iEmbeddingLevel = State().iEmbeddingLevel;
+					if (nextCatInLine == State().iStartCategory)
+						// Ignore POP-PUSH pair with nothing between.
+						// This is surely wrong? Perhaps it is a hack to
+						// help other parts of the algorithm. Must investigate.
+						// TPB.
+						r->iCategory = r[1].iCategory = EBoundaryNeutral;
+					else
+						{
+						r->iCategory = Pop();
+						}
+					}
+				else
+					r->iCategory = EBoundaryNeutral;
+				}
+			else
+				{
+				// Category is LRE, RLE, LRO or RLO.
+				if (nextCatInLine == EPopDirectionalFormat)
+					// Ignore PUSH-POP pair with nothing between.
+					r->iCategory = r[1].iCategory = EBoundaryNeutral;
+				else
+					r->iCategory = Push(static_cast<TCategory>(r->iCategory));
+				}
+			}
+
+		if (!was_pdf)
+			{
+			switch (State().iOverrideState)
+				{
+				case ELeftToRightOverrideState:
+					r->iCategory = ELeftToRight;
+					break;
+				case ERightToLeftOverrideState:
+					r->iCategory = ERightToLeft;
+					break;
+				default:
+					break;
+				}
+			r->iEmbeddingLevel = State().iEmbeddingLevel;
+			}
+		if (r->iCategory & EStrongGroup)
+			aContext.iLastStrongCategory = static_cast<TCategory>(r->iCategory);
+		aContext.iCategories |= r->iCategory;
+		}
+	}
+
+
+void TBidirectionalState::ResolveWeakTypesW1W2W3(TReorderContext& aContext)
+/**
+Unicode(3.2) Bidirectional Algorithm phases W1, W2 and W3.
+@internalComponent
+*/	
+    {
+	if (!(aContext.iCategories
+		& (ENonSpacingMark | ERightToLeftArabic | EEuropeanNumber)))
+		return;
+
+	TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
+	TCategory prev_cat = iPreviousCategory;
+	TBool european_to_arabic_number = iPreviousStrongCategory == ERightToLeftArabic;
+
+	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
+	for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
+		{
+		switch (r->iCategory)
+			{
+			case ENonSpacingMark:					// non-spacing marks change to the previous category
+				r->iCategory = prev_cat;
+				break;
+			case ELeftToRight:
+				european_to_arabic_number = EFalse;
+				break;
+			case ERightToLeft:
+				european_to_arabic_number = EFalse;
+				break;
+			case ERightToLeftArabic:				// Arabic letters change to R
+				european_to_arabic_number = ETrue;
+				r->iCategory = ERightToLeft;
+				break;
+			case EEuropeanNumber:				    // European numbers change to Arabic if last strong category was R
+				if (european_to_arabic_number)
+					r->iCategory = EArabicNumber;
+				break;
+			default:
+				break;
+			}
+		aContext.iCategories |= r->iCategory;
+		prev_cat = static_cast<TCategory>(r->iCategory);
+		}
+	}
+/**
+This phase removes categories NSM, AL, ES, ET, CS, BS, S, WS and BN, leaving
+only L, R, EN, AN and ON.
+@internalComponent
+*/
+void TBidirectionalState::ResolveWeakTypesW4W5W6(TReorderContext& aContext)
+	{
+	int i;
+	TRunInfo* r;
+	TCategory prev_cat = iPreviousCategory;
+	TCategory next_cat = EOtherNeutral;
+
+	// Phase P0b.
+	prev_cat = iPreviousCategory;
+	if (aContext.iCategories & EBoundaryNeutral)
+		{
+		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
+			 i < aContext.iRuns;
+			 i++, aContext.iCategories |= r->iCategory, r++)
+			{
+			if (r->iCategory == EBoundaryNeutral)		// runs of boundary neutrals change to EN, ET or AN if adjacent to
+				{										// one of these, otherwise to ON
+				int end = i + 1;
+				while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EBoundaryNeutral)
+					end++;
+				next_cat = end < aContext.iRuns ? (TCategory)(aContext.iRunInfo[end].iCategory) : aContext.iNextCategory;
+				TCategory c = EOtherNeutral;
+				if (prev_cat == EEuropeanNumber || next_cat == EEuropeanNumber)
+					c = EEuropeanNumber;
+				else if (prev_cat == EEuropeanNumberTerminator || next_cat == EEuropeanNumberTerminator)
+					c = EEuropeanNumberTerminator;
+				else if (prev_cat == EArabicNumber || next_cat == EArabicNumber)
+					c = EArabicNumber;
+				for (int j = i; j < end; j++)
+					aContext.iRunInfo[j].iCategory = c;
+				i = end - 1;
+				r = &aContext.iRunInfo[i];
+				}
+			prev_cat = (TCategory)r->iCategory;
+			}
+		}
+
+	// Phase P1.
+	prev_cat = iPreviousCategory;
+	if (aContext.iCategories & (EEuropeanNumberSeparator | ECommonNumberSeparator))
+		{
+		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
+			 i < aContext.iRuns;
+			 i++, aContext.iCategories |= r->iCategory, r++)
+			{
+			next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
+			switch (r->iCategory)
+				{
+				case EEuropeanNumberSeparator:			// European separators change to EN if between two ENs, else to ON
+					if (prev_cat == EEuropeanNumber && next_cat == EEuropeanNumber)
+						r->iCategory = EEuropeanNumber;
+					else
+						r->iCategory = EOtherNeutral;
+					break;
+				case ECommonNumberSeparator:			// CSs change to EN or AN if between two of the same, else to ON
+					if (prev_cat == EEuropeanNumber && next_cat == EEuropeanNumber)
+						r->iCategory = EEuropeanNumber;
+					else if (prev_cat == EArabicNumber && next_cat == EArabicNumber)
+						r->iCategory = EArabicNumber;
+					else
+						r->iCategory = EOtherNeutral;
+					break;
+				default:
+					break;
+				}
+			prev_cat = (TCategory)r->iCategory;
+			}
+		}
+
+	/*
+	Phase L1: tabs, whitespace before tabs, and trailing whitespace, is set to the base embedding level.
+	We ought to do this just before the final reordering, but the whitespace and segment separator
+	categories have disappeared by then so we use the sentinel value 255 which tells
+	ResolveImplicitLevels what to do.
+	*/
+	TBool demote_whitespace = TRUE;
+	for (i = aContext.iRuns - 1, r = &aContext.iRunInfo[i]; i >= 0; i--, r--)
+		{
+		switch (r->iCategory)
+			{
+			case EWhitespace:
+				break;
+			case ESegmentSeparator:
+				demote_whitespace = TRUE;
+				break;
+			default:
+				demote_whitespace = FALSE;
+				break;
+			}
+		if (demote_whitespace)
+			r->iEmbeddingLevel = 255;
+		}
+
+	// Phases P2 and P3.
+	prev_cat = iPreviousCategory;
+	if (aContext.iCategories & (EEuropeanNumberTerminator | ESegmentSeparator | EWhitespace))
+		{
+		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
+			 i < aContext.iRuns;
+			 i++, aContext.iCategories |= r->iCategory, r++)
+			{
+			next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
+			switch (r->iCategory)
+				{
+				case EEuropeanNumberTerminator:			// runs of ETs change to ENs if next to an EN, else to ON
+					{
+					int end = i + 1;
+					while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EEuropeanNumberTerminator)
+						end++;
+					next_cat = end < aContext.iRuns ? (TCategory)(aContext.iRunInfo[end].iCategory) : aContext.iNextCategory;
+					TCategory c = EOtherNeutral;
+					if (prev_cat == EEuropeanNumber || next_cat == EEuropeanNumber)
+						c = EEuropeanNumber;
+					for (int j = i; j < end; j++)
+						aContext.iRunInfo[j].iCategory = c;
+					i = end - 1;
+					r = &aContext.iRunInfo[i];
+					}
+					break;
+				case ESegmentSeparator:					// S and WS change to ON
+				case EWhitespace:
+					r->iCategory = EOtherNeutral;
+					break;
+				default:
+					break;
+				}
+			prev_cat = (TCategory)r->iCategory;
+			}
+		}
+	}
+
+void TBidirectionalState::ResolveWeakTypesW7(TReorderContext& aContext)
+	{
+	if (!(aContext.iCategories & EEuropeanNumber))
+		return;
+
+	TCategory prev_strong_cat = iPreviousStrongCategory;
+
+	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
+	TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
+	for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
+		{
+		switch (r->iCategory)
+			{
+			case ELeftToRight:
+				prev_strong_cat = ELeftToRight;
+				break;
+			case ERightToLeft:
+				prev_strong_cat = ERightToLeft;
+				break;
+			case EEuropeanNumber: 
+				if (prev_strong_cat == ELeftToRight)
+					r->iCategory = ELeftToRight;
+				break;
+			default:
+				break;
+			}
+		aContext.iCategories |= r->iCategory;
+		}
+	}
+
+
+
+void TBidirectionalState::DeneutralizeRuns(TRunInfo* aStart, TRunInfo* aEnd,
+	TCategory aStartCategory, TCategory aEndCategory)
+/**
+Turn all ON (Other Neutral) into non-neutrals according to the rules N1 and N2.
+@param aStart The start of the run array to be altered.
+@param aEnd One past the end of the run array to be altered.
+@param aStartCategory
+	The last non-neutral before the run, must be ELeftToRight or ERightToLeft.
+@param aEndCategory
+	The first non-neutral after the run, must be ELeftToRight or ERightToLeft.
+@internalComponent
+*/	{
+	// if sandwiched by the same category, neutrals become that.
+	if (aStartCategory == aEndCategory)
+		{
+		for (; aStart != aEnd; ++aStart)
+			aStart->iCategory = aStartCategory;
+		return;
+		}
+	// otherwise look at the embedding level in each case
+	for (; aStart != aEnd; ++aStart)
+		{
+		aStart->iCategory = aStart->iEmbeddingLevel & 1?
+			ERightToLeft : ELeftToRight;
+		}
+	}
+
+
+void TBidirectionalState::ResolveNeutralTypes(TReorderContext& aContext)
+	/**
+This phase removes the ON (Other Neutral) category, leaving only L, R, EN, and
+AN; no need to update aContext.iCategories.
+@internalComponent
+*/
+    {
+	// Really we should find if any number intervenes, but this would require
+	// a BC break.
+	TCategory prevNonNeutral = iPreviousStrongCategory;
+	if (prevNonNeutral & ELeftToRightGroup)
+		prevNonNeutral = ELeftToRight;
+	else if (prevNonNeutral & ERightToLeftGroup)
+		prevNonNeutral = ERightToLeft;
+	TRunInfo *prevNonNeutralRun = aContext.iRunInfo;	// one past the last non-neutral found
+	TRunInfo *endOfRuns = aContext.iRunInfo + aContext.iRuns;
+
+	// All neutrals have now been changed to ON; change them to L or R depending on context.
+	for (TRunInfo *p = aContext.iRunInfo; p != endOfRuns; ++p)
+		{
+		TCategory nonNeutral = EOtherNeutral;
+		switch (p->iCategory)
+			{
+			case ELeftToRight:
+				nonNeutral = ELeftToRight;
+				break;
+			case ERightToLeft:
+				nonNeutral = ERightToLeft;
+				break;
+			case EArabicNumber:
+			case EEuropeanNumber: 
+				nonNeutral = ERightToLeft;
+				break;
+			default:
+				break;
+			}
+		if (nonNeutral != EOtherNeutral)
+			{
+			if (p != prevNonNeutralRun)
+				DeneutralizeRuns(prevNonNeutralRun, p,
+					prevNonNeutral, nonNeutral);
+			prevNonNeutral = nonNeutral;
+			prevNonNeutralRun = p + 1;
+			}
+		}
+	DeneutralizeRuns(prevNonNeutralRun, endOfRuns, prevNonNeutral,
+		aContext.iNextStrongCategory);
+	}
+
+
+void TBidirectionalState::ResolveImplicitLevels(TReorderContext& aContext)
+/**
+Phases I1 and I2.
+@internalComponent
+*/	{
+	int i;
+	TRunInfo* r;
+	for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
+		{
+		if (r->iEmbeddingLevel == 255) // sentinel indicating this is a tab or segment-final whitespace
+			r->iEmbeddingLevel = iStack[0].iEmbeddingLevel;
+		else switch (r->iCategory)
+			{
+			case ELeftToRight:
+				if (r->iEmbeddingLevel & 1)
+					r->iEmbeddingLevel++;
+				break;
+			case ERightToLeft:
+				if (!(r->iEmbeddingLevel & 1))
+					r->iEmbeddingLevel++;
+				break;
+			case EEuropeanNumber: case EArabicNumber:
+				if (r->iEmbeddingLevel & 1)
+					r->iEmbeddingLevel++;
+				else
+					r->iEmbeddingLevel += 2;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+
+void TBidirectionalState::ReorderRuns(TReorderContext& aContext)
+/**
+Phase L2.
+@internalComponent
+*/	{
+	// Find the highest level and lowest odd level.
+	int i;
+	TRunInfo* r;
+	int highest = 0;
+	int lowest_odd = EMaxLevel;
+	int level = 0;
+	for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
+		{
+		level = r->iEmbeddingLevel;
+		if (level > highest)
+			highest = level;
+		if ((level & 1) && level < lowest_odd)
+			lowest_odd = level;
+		}
+
+	// From the highest level to the lowest odd level, reverse any run at that level or higher.
+	for (level = highest; level >= lowest_odd; level--)
+		{
+		int run_start = 0;
+		r = aContext.iRunInfo;
+		while (run_start < aContext.iRuns)
+			{
+			while (run_start < aContext.iRuns && r->iEmbeddingLevel < level)
+				{
+				run_start++;
+				r++;
+				}
+			int run_end = run_start;
+			while (run_end < aContext.iRuns && r->iEmbeddingLevel >= level)
+				{
+				r->iDirection = !r->iDirection;
+				run_end++;
+				r++;
+				}
+			TRunInfo* p = &aContext.iRunInfo[run_start];
+			TRunInfo* q = &aContext.iRunInfo[run_end - 1];
+			while (p < q)
+				{
+				TRunInfo temp = *p;
+				*p = *q;
+				*q = temp;
+				p++;
+				q--;
+				}
+			run_start = run_end;
+			}
+		}
+	}
+
+
+TBidirectionalState::TCategory TBidirectionalState::Push(TCategory aStartCategory)
+/** @internalComponent */
+	{
+	TInt rightToLeftFlag = (static_cast<TInt>(aStartCategory)
+		& ERightToLeftGroup)? 1 : 0;
+	TInt oldLevel = State().iEmbeddingLevel;
+	TInt newLevel = oldLevel + 1;
+	// And add an extra one if the bottom bit is not correct.
+	newLevel += (newLevel & 1) ^ rightToLeftFlag;
+
+	if (EMaxExplicitLevel < newLevel)
+		{
+		if (oldLevel == 60)
+			++iPushesBeyond60;
+		else
+			++iPushesBeyond61;
+		return EBoundaryNeutral;
+		}
+
+	++iStackLevel;
+	TStackItem& state = iStack[iStackLevel];
+	state.iEmbeddingLevel = static_cast<TUint8>(newLevel);
+	state.iOverrideState = static_cast<TOverrideState>(aStartCategory
+		& (ELeftToRightOverride | ERightToLeftOverride));
+	state.iStartCategory = aStartCategory;
+
+	return rightToLeftFlag? ERightToLeft : ELeftToRight;
+	}
+
+
+TBidirectionalState::TCategory TBidirectionalState::Pop()
+/** @internalComponent */	
+   {
+	__ASSERT_DEBUG(0 < iStackLevel, User::Invariant());
+	TInt level = State().iEmbeddingLevel;
+	if (level < 60)
+		--iStackLevel;
+	else if (iPushesBeyond61 != 0)
+		--iPushesBeyond61;
+	else if (level == 61)
+		--iStackLevel;
+	else if (iPushesBeyond60)
+		--iPushesBeyond60;
+	else
+		--iStackLevel;
+	return (level & 1)? ERightToLeft : ELeftToRight;
+	}
+
+
+EXPORT_C void TBidirectionalState::Reset()
+/** Sets the object to its default 'start of paragraph' state. */
+	{
+	iStackLevel = 0;
+	iPushesBeyond60 = 0;
+	iPushesBeyond61 = 0;
+	iStack[0].iEmbeddingLevel = 0;
+	iStack[0].iOverrideState = ENoOverrideState;
+	iStack[0].iStartCategory = EOtherNeutral;
+	iPreviousCategory = ELeftToRight;
+	iPreviousStrongCategory = ELeftToRight;
+	}
+
+
+EXPORT_C TBool TBidirectionalState::IsDefault() const
+/** Returns Gets the default 'start of paragraph' state.
+
+@return ETrue if the object is in its default 'start of paragraph' state. */
+	{
+	return iStackLevel == 0 &&
+		   iStack[0].iEmbeddingLevel == 0 &&
+		   iStack[0].iOverrideState == ENoOverrideState &&
+		   iStack[0].iStartCategory == EOtherNeutral &&
+		   iPreviousCategory == ELeftToRight &&
+		   iPreviousStrongCategory == ELeftToRight;
+	}
+
+
+EXPORT_C TBool TBidirectionalState::operator==(const TBidirectionalState& aState) const
+/** Return ETrue if two bidirectional states are identical.
+
+@param aState A bidirectional state.
+@return ETrue if two bidirectional states are identical. */
+	{
+	if (iPreviousCategory != aState.iPreviousCategory ||
+		iPreviousStrongCategory != aState.iPreviousStrongCategory ||
+		iStackLevel != aState.iStackLevel)
+		return FALSE;
+	const TStackItem* p = iStack;
+	const TStackItem* q = aState.iStack;
+	for (int i = 0; i <= iStackLevel; i++, p++, q++)
+		{
+		if (p->iStartCategory != q->iStartCategory ||
+			p->iOverrideState != q->iOverrideState ||
+			p->iEmbeddingLevel != q->iEmbeddingLevel)
+			return FALSE;
+		}
+	return TRUE;
+	}
+
+
+TInt TBidirectionalState::CatToNumber(TInt aCat)
+/**
+Finds the highest bit set in the input. Used to convert
+TBidirectionalState::TCategory into TChar::TBdCategory.
+@param aCat a TBidirectionalState::TCategory.
+@return The equivalent TChar::TBdCategory.
+@internalComponent
+*/	{
+	TInt shifts = 0;
+	TInt bits = 32;
+	TInt mask = ~0L;
+	while (bits != 0)
+		{
+		bits >>= 1;
+		mask <<= bits;
+		if ((aCat & mask) == 0)
+			{
+			aCat <<= bits;
+			shifts += bits;
+			}
+		}
+	return 31 - shifts;
+	}
+
+
+EXPORT_C void TBidirectionalState::ExternalizeL(RWriteStream& aDest)
+/** Serializes a bidirectional state to an output stream.
+
+@param aDest An output stream. */
+	{
+	//+ put the prev cat, prev strong cat and stack levels in one number?
+	// Write the previous category and previous strong category.
+	aDest.WriteInt8L(CatToNumber(iPreviousCategory));
+	aDest.WriteInt8L(CatToNumber(iPreviousStrongCategory));
+
+	// Write the number of stack levels
+	aDest.WriteInt8L(iStackLevel);
+
+	/*
+	Write each stack level as a single number: 5 bits for the start category, 2 for the override state,
+	6 for the embedding level.
+	*/
+	for (int i = 0; i <= iStackLevel; i++)
+		{
+		TInt x = CatToNumber(iStack[i].iStartCategory);
+		if (iStack[i].iOverrideState == ELeftToRightOverrideState)
+			{
+			x |= (KBidirectionalStateOverrideStreamValueLeftToRight << 5);	
+			}
+        else if (iStack[i].iOverrideState == ERightToLeftOverrideState)
+        	{
+        	x |= (KBidirectionalStateOverrideStreamValueRightToLeft << 5); 	
+        	}
+       	x |= ((TInt)iStack[i].iEmbeddingLevel << 7);
+		aDest.WriteInt16L(x);
+		}
+
+	TInt level = State().iEmbeddingLevel;
+	if (60 <= level)
+		{
+		aDest.WriteInt8L(iPushesBeyond60);
+		aDest.WriteInt8L(iPushesBeyond61);
+		}
+	}
+
+
+EXPORT_C void TBidirectionalState::InternalizeL(RReadStream& aSource)
+/** Reads a bidirectional state from an input stream, translating it from its serialized 
+form.
+
+@param aSource A source stream. */
+	{
+	// Read the previous category and the previous strong category.
+	TInt x = aSource.ReadInt8L();
+	iPreviousCategory = (TCategory)(1 << x);
+	x = aSource.ReadInt8L();
+	iPreviousStrongCategory = (TCategory)(1 << x);
+
+	// Read the number of stack levels.
+	iStackLevel = aSource.ReadInt8L();
+
+	// Read the stack levels.
+	for (int i = 0; i <= iStackLevel; i++)
+		{
+		x = aSource.ReadInt16L();
+		iStack[i].iStartCategory = (TCategory)(1 << (x & 0x1F));
+		switch ((x >> 5) & 3)
+        	{
+    	case KBidirectionalStateOverrideStreamValueLeftToRight: 
+    		iStack[i].iOverrideState = ELeftToRightOverrideState;
+    		break;
+    	case KBidirectionalStateOverrideStreamValueRightToLeft:
+    		iStack[i].iOverrideState = ERightToLeftOverrideState; 
+    		break;
+    	case KBidirectionalStateOverrideStreamValueNone:
+    	default: iStack[i].iOverrideState = ENoOverrideState; break;
+        	};
+		iStack[i].iEmbeddingLevel = (TUint8)(x >> 7);
+		}
+
+	TInt level = State().iEmbeddingLevel;
+	if (60 <= level)
+		{
+		iPushesBeyond60 = aSource.ReadInt8L();
+		iPushesBeyond61 = aSource.ReadInt8L();
+		}
+	else
+		{
+		iPushesBeyond60 = 0;
+		iPushesBeyond61 = 0;
+		}
+	}
+
+
+TBidirectionalState::TBidirectionalState(TChar::TBdCategory aPrevCat,
+	TChar::TBdCategory aPrevStrongCat,
+	TBool aParRightToLeft)
+	/**
+Constructor suitable for test code.
+@internalComponent
+*/
+    {
+	Reset();
+	iPreviousCategory = CharToBdCat(aPrevCat);
+	iPreviousStrongCategory = CharToBdCat(aPrevStrongCat);
+	iStack[0].iEmbeddingLevel = (TUint8) (aParRightToLeft? 1 : 0);
+	}