--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fontservices/textbase/sgdi/BIDI.CPP Wed Aug 18 11:34:25 2010 +0300
@@ -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);
+ }