os/graphics/graphicsdeviceinterface/gdi/sgdi/BIDI.CPP
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // Bidirectional text reordering; based on the Unicode Bidirectional Reordering Algorithm.
    15 // 
    16 //
    17 
    18 #include <bidi.h>
    19 #include "BidiCopy.h"
    20 #include <s32std.h>
    21 
    22 const TInt KBidirectionalStateOverrideStreamValueNone = 0;
    23 const TInt KBidirectionalStateOverrideStreamValueLeftToRight = 1;
    24 const TInt KBidirectionalStateOverrideStreamValueRightToLeft = 2;
    25 
    26 inline TBool IsSupplementary(TUint aChar)
    27 /**
    28 @param aChar The 32-bit code point value of a Unicode character.
    29 
    30 @return True, if aChar is supplementary character; false, otherwise.
    31 */
    32 	{
    33 	return (aChar > 0xFFFF);
    34 	}
    35 
    36 inline TBool IsHighSurrogate(TText16 aInt16)
    37 /**
    38 @return True, if aText16 is high surrogate; false, otherwise.
    39 */
    40 	{
    41 	return (aInt16 & 0xFC00) == 0xD800;
    42 	}
    43 
    44 inline TBool IsLowSurrogate(TText16 aInt16)
    45 /**
    46 @return True, if aText16 is low surrogate; false, otherwise.
    47 */
    48 	{
    49 	return (aInt16 & 0xFC00) == 0xDC00;
    50 	}
    51 
    52 inline TUint JoinSurrogate(TText16 aHighSurrogate, TText16 aLowSurrogate)
    53 /**
    54 Combine a high surrogate and a low surrogate into a supplementary character.
    55 
    56 @return The 32-bit code point value of the generated Unicode supplementary
    57         character.
    58 */
    59 	{
    60 	return ((aHighSurrogate - 0xD7F7) << 10) + aLowSurrogate;
    61 	}
    62 
    63 TBool TextDefaultsToRightToLeft(const TDesC& aText, TBool* aFound);
    64 
    65 TBidirectionalState::TCategory TBidirectionalState::CharToBdCat(TChar::TBdCategory aCat)
    66 	{
    67 	return static_cast<TBidirectionalState::TCategory>(
    68 		1 << static_cast<TInt>(aCat));
    69 	}
    70 
    71 TBidirectionalState::TCategory TBidirectionalState::UintToBdCat(TUint aCat)
    72 	{
    73 	return static_cast<TBidirectionalState::TCategory>(1 << aCat);
    74 	}
    75 
    76 void TBidirectionalState::TReorderContext::SetNextCategory(
    77 	TChar::TBdCategory aCat)
    78 	{
    79 	iNextCategory = CharToBdCat(aCat);
    80 	}
    81 
    82 void TBidirectionalState::TReorderContext::SetNextStrongCategory(
    83 	TChar::TBdCategory aCat)
    84 	{
    85 	iNextStrongCategory = CharToBdCat(aCat);
    86 	}
    87 
    88 
    89 EXPORT_C void TBidirectionalState::ReverseGroups(TText* aStart,TInt aLength)
    90 /** A utility to reverse text apart from combining characters, which remains after 
    91 their base characters. This is what is needed when drawing right-to-left text.
    92 
    93 @param aStart Start position of text to be reversed.
    94 @param aLength Length of text to be reversed. */
    95 	{
    96 	BidiCopy::ReverseCodes(aStart, aLength);
    97 	BidiCopy::DeleteUnreversedSurrogates(aStart, aLength);
    98 	BidiCopy::SubstituteMirrorImages(aStart, aLength);
    99 	BidiCopy::CorrectGroups(aStart, aLength);
   100 	BidiCopy::CorrectSurrogatePairs(aStart, aLength);
   101 	}
   102 
   103 
   104 // A local helper function. Get the next character from a buffer. This
   105 // function won't check buffer length.
   106 //
   107 // @param aText The text buffer to read character from.
   108 // @param aCharacterIndex Count of characters to skip in aText.
   109 // @return The character.
   110 TUint GetOneCharacter(const TText16 *aText, TInt aCharacterIndex)
   111 	{
   112 	const TText16 *p = aText;
   113 	TUint c = 0xFFFF;
   114 	while (aCharacterIndex >= 0)
   115 		{
   116 		c = *p++;
   117 		ASSERT(!IsLowSurrogate(c));
   118 		if (IsHighSurrogate(c))
   119 			{
   120 			ASSERT(IsLowSurrogate(*p));
   121 			c = JoinSurrogate(c, *p++);
   122 			}
   123 		--aCharacterIndex;
   124 		}
   125 	return c;
   126 	}
   127 
   128 
   129 TInt TBidirectionalState::GenerateBdRunArray(const TText* aText, TInt aLength,
   130 	TBidirectionalState::TRunInfo* aRun, TInt aMaxRuns)
   131 /** Analyse the input text for runs of characters that share the same
   132 bidirectional class. Categories TChar::EEuropeanNumberSeparator and
   133 TChar::ECommonNumberSeparator are kept as singletons due to a limitation in
   134 the reordering logic.
   135 @param aText The text to be analysed.
   136 @param aLength The length of the text to be analysed.
   137 @param aRun	Output buffer for the runs after analysis. May be null if there 
   138 is to be no output.
   139 @param aMaxRuns The size of the aRun array. No more than this number of runs 
   140 will be	output.
   141 @return The number of runs that are required for the full results of the
   142 analysis.
   143 @internalTechnology */	
   144     {
   145 	if (aLength == 0)
   146 		{
   147 		if (aRun && 0 < aMaxRuns)
   148 			{
   149 			aRun[0].iCategory = TChar::EOtherNeutral;
   150 			aRun[0].iStart = 0;
   151 			aRun[0].iLength = 0;
   152 			}
   153 		return 1;
   154 		}
   155 	int runs = 0;
   156 	int run_start = 0;
   157 	int run_end = 1;
   158 	const TText* p = aText;
   159 	const TText* q = p + aLength;
   160 	
   161 	// get the character pointed by 'p', then move 'p' to next character, and adjust 'run_end' if need
   162 	TChar pc = ::GetOneCharacter(p, 0);
   163 	TChar::TBdCategory cur_cat = pc.GetBdCategory();
   164 	++p;
   165 	if (IsSupplementary(pc))
   166 		{
   167 		++p;
   168 		run_end = 2;	// run_end points to "end" of current character
   169 		}
   170 	
   171 	while (p < q)
   172 		{
   173 		// get the character pointed by 'p'
   174 		pc = ::GetOneCharacter(p, 0);
   175 		
   176 		TChar::TBdCategory new_cat = pc.GetBdCategory();
   177 		if (new_cat != cur_cat)
   178 			{
   179 			if (new_cat == TChar::ENonSpacingMark &&
   180 				cur_cat != TChar::ELeftToRightEmbedding &&
   181 				cur_cat != TChar::ELeftToRightOverride &&
   182 				cur_cat != TChar::ERightToLeftEmbedding &&
   183 				cur_cat != TChar::ERightToLeftOverride &&
   184 				cur_cat != TChar::EPopDirectionalFormat)
   185 				new_cat = cur_cat;
   186 			else if (p < q - 1 &&
   187 					 (new_cat == TChar::EWhitespace ||
   188 					  new_cat == TChar::EEuropeanNumberSeparator ||
   189 					  new_cat == TChar::ECommonNumberSeparator))
   190 				{
   191 				TChar nextChar = ::GetOneCharacter(p, 1);
   192 				TChar::TBdCategory next_cat = nextChar.GetBdCategory();
   193 				if (new_cat == TChar::EWhitespace)
   194 					{
   195 					if ((cur_cat == TChar::ELeftToRight ||
   196 						 cur_cat == TChar::ERightToLeft ||
   197 						 cur_cat == TChar::ERightToLeftArabic) && cur_cat == next_cat)
   198 						new_cat = cur_cat;
   199 					}
   200 				else if (cur_cat == TChar::EEuropeanNumber && next_cat == TChar::EEuropeanNumber)
   201 					new_cat = TChar::EEuropeanNumber;
   202 				}
   203 			}
   204 
   205 		if (new_cat != cur_cat ||
   206 			cur_cat == TChar::EEuropeanNumberSeparator ||
   207 			cur_cat == TChar::ECommonNumberSeparator)
   208 			{
   209 			if (aRun && runs < aMaxRuns)
   210 				{
   211 				aRun[runs].iCategory = cur_cat;
   212 				aRun[runs].iStart = run_start;
   213 				aRun[runs].iLength = run_end - run_start;
   214 				}
   215 			
   216 			runs++;
   217 			run_start = run_end;
   218 			}
   219 
   220 		p++;
   221 		run_end++;
   222 
   223 		// adjust 'p' and 'run_end'
   224 		if (IsSupplementary(pc))
   225 			{
   226 			p++;
   227 			run_end++;
   228 			}
   229 
   230 		cur_cat = new_cat;
   231 		}
   232 
   233 	if (aRun && runs < aMaxRuns)
   234 		{
   235 		aRun[runs].iCategory = cur_cat;
   236 		aRun[runs].iStart = run_start;
   237 		aRun[runs].iLength = run_end - run_start;
   238 		}
   239 
   240 	return runs + 1;
   241 	}
   242 
   243 
   244 EXPORT_C TInt TBidirectionalState::ReorderText(const TText* aText,TInt aLength,TBool aParRightToLeft,
   245 											   TText*& aNewText)
   246 /** Reorders text according to the Unicode Bidirectional Reordering algorithm.
   247 
   248 Reorders the input text from logical order (which may be bidirectional) to 
   249 display order (strictly left to right).
   250 
   251 @param aText The input text in logical order.
   252 @param aLength The length of the input text.
   253 @param aParRightToLeft ETrue if the default directionality of the text to be 
   254 re-ordered is right-to-left.
   255 @param aNewText Returns the re-ordered text. If the text did not need re-ordering, 
   256 or if there was an error, aText will be returned. Otherwise, ownership of 
   257 a newly allocated buffer will be returned to the caller. This buffer must 
   258 be deleted with delete[] (or CleanupArrayDeletePushL()) and not delete (or 
   259 CleanupStack::PushL()).
   260 @return A system-wide error value if there has been an error; KErrNone if there 
   261 has not. */
   262 	{
   263 	aNewText = const_cast<TText*>(aText);
   264 	if (aLength < 2)
   265 		return KErrNone;
   266 
   267 	int error = KErrNone;
   268 	TBidirectionalState::TRunInfo run_info;
   269 	run_info.iDirection = 0;
   270 	run_info.iIndex = 0;
   271 	run_info.iStart = 0;
   272 	run_info.iLength = aLength;
   273 	TBidirectionalState::TRunInfo* run_info_array = &run_info;
   274 	TBidirectionalState::TRunInfo* allocated_run_info_array = 0;
   275 	int runs = GenerateBdRunArray(aText, aLength, run_info_array, 1);
   276 	if (runs > 1)
   277 		{
   278 		allocated_run_info_array = new TBidirectionalState::TRunInfo[runs];
   279 		if (allocated_run_info_array)
   280 			{
   281 			run_info_array = allocated_run_info_array;
   282 			GenerateBdRunArray(aText, aLength, run_info_array, runs);
   283 			}
   284 		else
   285 			{
   286 			// the run cannot be allocated: stick with our single l-to-r run
   287 			error = KErrNoMemory;
   288 			runs = 1;
   289 			}
   290 		}
   291 	if (error == KErrNone)
   292 		{
   293 		TBidirectionalState state;
   294 		state.ReorderLine(run_info_array, runs, ETrue, ETrue, aParRightToLeft,
   295 			TChar::EOtherNeutral, TChar::EOtherNeutral);
   296 		}
   297 
   298 	// If there was only one run and it's left-to-right, we've finished.
   299 	if (!allocated_run_info_array && run_info.iDirection == 0)
   300 		return error;
   301 
   302 	// Reorder the text into a new buffer.
   303 	TText* buffer = new TText[aLength];
   304 	if (!buffer)
   305 		{
   306 		delete [] allocated_run_info_array;
   307 		return KErrNoMemory;
   308 		}
   309 	const TBidirectionalState::TRunInfo* r = run_info_array;
   310 	TText* dest = buffer;
   311 	for (int i = 0; i < runs; i++, r++)
   312 		{
   313 		const TText* source = &aText[r->iStart];
   314 		int length = r->iLength;
   315 		Mem::Copy(dest,source,length * sizeof(TText));
   316 		if (r->iDirection)
   317 			ReverseGroups(dest,length);
   318 		dest += length;
   319 		}
   320 
   321 	delete [] allocated_run_info_array;
   322 	aNewText = buffer;
   323 	return KErrNone;
   324 	}
   325 
   326 
   327 EXPORT_C TBidirectionalState::TBidirectionalState()
   328 /** Standard constructor. */
   329 	{
   330 	Reset();
   331 	}
   332 
   333 
   334 /** Reorders a line of text and updates the bidirectional state for the next line.
   335 
   336 @param aRunInfo An array of objects representing runs of characters with the 
   337 same bidirectional category. Any number of characters can be combined into 
   338 a run if they have the same category, except for the categories TChar::EEuropeanNumberSeparator 
   339 and TChar::ECommonNumberSeparator, which should be put into single-character 
   340 runs because the reordering logic depends on this.
   341 @param aRuns Number of 'run info' objects.
   342 @param aParStart Tells the function whether the line is the first line of a 
   343 paragraph.
   344 @param aParEnd Tells the function whether the line is the last line of a paragraph.
   345 @param aParRightToLeft ETrue if the default directionality of the text to be 
   346 re-ordered is right-to-left.
   347 @param aNextCategory The category of the character immediately after the end 
   348 of the line. This is ignored if aParEnd is ETrue.
   349 @param aNextStrongCategory The category of the first strong character (one 
   350 of the categories ELeftToRight, ELeftToRightEmbedding, ELeftToRightOverride, 
   351 ERightToLeft, ERightToLeftArabic, ERightToLeftEmbedding or ERightToLeftOverride) 
   352 after the end of the line. This is ignored if aParEnd is ETrue.
   353 @param aVisualEndIsAmbiguous EFalse if the logical end of this line is at the
   354 visual end and the logical beginning of the next line is at the visual beginning.
   355 */
   356 EXPORT_C void TBidirectionalState::ReorderLine(TRunInfo* aRunInfo, TInt aRuns,
   357 	TBool aParStart, TBool aParEnd, TBool aParRightToLeft,
   358 	TChar::TBdCategory aNextCategory, TChar::TBdCategory aNextStrongCategory,
   359 	TBool& aVisualEndIsAmbiguous)
   360 	{
   361 	ReorderLine(aRunInfo, aRuns, aParStart, aParEnd, aParRightToLeft,
   362 		aNextCategory, aNextStrongCategory);
   363 	if (iStackLevel  != 0)
   364 		{
   365 		aVisualEndIsAmbiguous = ETrue;
   366 		return;
   367 		}
   368 	TCategory nextCat = CharToBdCat(aNextCategory);
   369 	TCategory nextStrong = CharToBdCat(aNextStrongCategory);
   370 	const TUint KAllStrongLeftToRight =
   371 		ELeftToRight | ELeftToRightEmbedding | ELeftToRightOverride;
   372 	const TUint KAllStrongRightToLeft =
   373 		ERightToLeft | ERightToLeftArabic | ERightToLeftEmbedding | ERightToLeftOverride;
   374 	if (aParRightToLeft)
   375 		{
   376 		// Ambiguous if any of the surrounding categories are strongly left-to-right
   377 		aVisualEndIsAmbiguous =
   378 			(iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
   379 			& KAllStrongLeftToRight;
   380 		}
   381 	else
   382 		{
   383 		// Ambiguous if any of the surrounding categories are strongly right-to-left
   384 		aVisualEndIsAmbiguous =
   385 			(iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
   386 			& KAllStrongRightToLeft;
   387 		}
   388 	}
   389 /** Reorders a line of text and updates the bidirectional state for the next line.
   390 
   391 @param aRunInfo An array of objects representing runs of characters with the 
   392 same bidirectional category. Any number of characters can be combined into 
   393 a run if they have the same category, except for the categories TChar::EEuropeanNumberSeparator 
   394 and TChar::ECommonNumberSeparator, which should be put into single-character 
   395 runs because the reordering logic depends on this.
   396 @param aRuns Number of 'run info' objects.
   397 @param aParStart Tells the function whether the line is the first line of a 
   398 paragraph.
   399 @param aParEnd Tells the function whether the line is the last line of a paragraph.
   400 @param aParRightToLeft ETrue if the default directionality of the text to be 
   401 re-ordered is right-to-left.
   402 @param aNextCategory The category of the character immediately after the end 
   403 of the line. This is ignored if aParEnd is ETrue.
   404 @param aNextStrongCategory The category of the first strong character (one 
   405 of the categories ELeftToRight, ELeftToRightEmbedding, ELeftToRightOverride, 
   406 ERightToLeft, ERightToLeftArabic, ERightToLeftEmbedding or ERightToLeftOverride) 
   407 after the end of the line. This is ignored if aParEnd is ETrue. */
   408 EXPORT_C void TBidirectionalState::ReorderLine(TRunInfo* aRunInfo, TInt aRuns,
   409 	TBool aParStart, TBool aParEnd, TBool aParRightToLeft,
   410 	TChar::TBdCategory aNextCategory, TChar::TBdCategory aNextStrongCategory)
   411 	{
   412 	// Reset if this is a new paragraph.
   413 	if (aParStart)
   414 		{
   415 		Reset();
   416 		iPreviousCategory = EOtherNeutral;
   417 		if (aParRightToLeft)
   418 			{
   419 			iStack[0].iEmbeddingLevel = 1;
   420 			iPreviousStrongCategory = ERightToLeft;
   421 			}
   422 		}
   423 
   424 	// Initialise the context object.
   425 	TReorderContext context;
   426 	context.iRunInfo = aRunInfo;
   427 	context.iRuns = aRuns;
   428 	context.iLastStrongCategory = iPreviousStrongCategory;
   429 	if (aParEnd)
   430 		context.iNextCategory = context.iNextStrongCategory = EOtherNeutral;
   431 	else
   432 		{
   433 		context.iNextCategory = CharToBdCat(aNextCategory);
   434 		context.iNextStrongCategory = CharToBdCat(aNextStrongCategory);
   435 		}
   436 
   437 	// Initialise output data and find out what categories are present so that unnecessary steps can be skipped.
   438 	context.iCategories = iPreviousCategory | context.iNextCategory | context.iNextStrongCategory;
   439 	for (TInt i = 0; i != aRuns; ++i)
   440 		{
   441 		aRunInfo[i].iEmbeddingLevel = iStack[0].iEmbeddingLevel;
   442 		aRunInfo[i].iDirection = 0;
   443 		aRunInfo[i].iIndex = i;
   444 		aRunInfo[i].iCategory = UintToBdCat(aRunInfo[i].iCategory);
   445 		context.iCategories |= aRunInfo[i].iCategory;
   446 		}
   447 
   448 	// Do nothing if no right-to-left material is present.
   449 	if (aRuns == 0 ||
   450 		(iStackLevel == 0 && iStack[0].iEmbeddingLevel == 0 &&
   451 		 !(context.iCategories & (ERightToLeftGroup | EBdControlsGroup))))
   452 		return;
   453 
   454 	// Perform the bidirectional algorithm.
   455 	if ((context.iCategories & EBdControlsGroup) ||
   456 		State().iOverrideState != ENoOverrideState)
   457 		HandleBdControls(context);
   458 	ResolveWeakTypesW1W2W3(context);
   459 	ResolveWeakTypesW4W5W6(context);
   460 	ResolveWeakTypesW7(context);
   461 	if (context.iCategories & EOtherNeutral)
   462 		ResolveNeutralTypes(context);
   463 	ResolveImplicitLevels(context);
   464 	PrepareForNextLine(context);
   465 	ReorderRuns(context);
   466 	}
   467 
   468 
   469 void TBidirectionalState::PrepareForNextLine(const TReorderContext& aContext)
   470 /**
   471 Fold context information back into TBidirectionalState.
   472 @internalComponent
   473 */	
   474    {
   475 	if (aContext.iRuns != 0)
   476 		{
   477 		iPreviousCategory = static_cast<TCategory>(
   478 			aContext.iRunInfo[aContext.iRuns - 1].iCategory);
   479 		iPreviousStrongCategory = aContext.iLastStrongCategory;
   480 		}
   481 	}
   482 
   483 
   484 void TBidirectionalState::HandleBdControls(TReorderContext& aContext)
   485 /**
   486 Handle LRO, RLO, LRE, RLE and PDF. After this phase, these categories will no
   487 longer be found.
   488 
   489 This corresponds to Unicode(3.2) Bidirectional Algorithm phases X1-X7.
   490 Phase X8 is not required as the run is assumed to be all in one paragraph.
   491 Phases X9-X10 are implicit in other functions.
   492 
   493 @internalComponent
   494 */	
   495    {
   496 	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
   497 	for (TInt i = 0; i != aContext.iRuns; ++i)
   498 		{
   499 		TRunInfo* r = aContext.iRunInfo + i;
   500 		TCategory nextCatInLine = i < aContext.iRuns - 1?
   501 			(TCategory)(r[1].iCategory) : ENoCategory;
   502 
   503 		TBool was_pdf = FALSE;
   504 		if (r->iCategory & EBdControlsGroup)
   505 			{
   506 			if (r->iCategory == EPopDirectionalFormat)
   507 				{
   508 				if (iStackLevel > 0)
   509 					{
   510 					was_pdf = TRUE;
   511 					r->iEmbeddingLevel = State().iEmbeddingLevel;
   512 					if (nextCatInLine == State().iStartCategory)
   513 						// Ignore POP-PUSH pair with nothing between.
   514 						// This is surely wrong? Perhaps it is a hack to
   515 						// help other parts of the algorithm. Must investigate.
   516 						// TPB.
   517 						r->iCategory = r[1].iCategory = EBoundaryNeutral;
   518 					else
   519 						{
   520 						r->iCategory = Pop();
   521 						}
   522 					}
   523 				else
   524 					r->iCategory = EBoundaryNeutral;
   525 				}
   526 			else
   527 				{
   528 				// Category is LRE, RLE, LRO or RLO.
   529 				if (nextCatInLine == EPopDirectionalFormat)
   530 					// Ignore PUSH-POP pair with nothing between.
   531 					r->iCategory = r[1].iCategory = EBoundaryNeutral;
   532 				else
   533 					r->iCategory = Push(static_cast<TCategory>(r->iCategory));
   534 				}
   535 			}
   536 
   537 		if (!was_pdf)
   538 			{
   539 			switch (State().iOverrideState)
   540 				{
   541 				case ELeftToRightOverrideState:
   542 					r->iCategory = ELeftToRight;
   543 					break;
   544 				case ERightToLeftOverrideState:
   545 					r->iCategory = ERightToLeft;
   546 					break;
   547 				default:
   548 					break;
   549 				}
   550 			r->iEmbeddingLevel = State().iEmbeddingLevel;
   551 			}
   552 		if (r->iCategory & EStrongGroup)
   553 			aContext.iLastStrongCategory = static_cast<TCategory>(r->iCategory);
   554 		aContext.iCategories |= r->iCategory;
   555 		}
   556 	}
   557 
   558 
   559 void TBidirectionalState::ResolveWeakTypesW1W2W3(TReorderContext& aContext)
   560 /**
   561 Unicode(3.2) Bidirectional Algorithm phases W1, W2 and W3.
   562 @internalComponent
   563 */	
   564     {
   565 	if (!(aContext.iCategories
   566 		& (ENonSpacingMark | ERightToLeftArabic | EEuropeanNumber)))
   567 		return;
   568 
   569 	TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
   570 	TCategory prev_cat = iPreviousCategory;
   571 	TBool european_to_arabic_number = iPreviousStrongCategory == ERightToLeftArabic;
   572 
   573 	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
   574 	for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
   575 		{
   576 		switch (r->iCategory)
   577 			{
   578 			case ENonSpacingMark:					// non-spacing marks change to the previous category
   579 				r->iCategory = prev_cat;
   580 				break;
   581 			case ELeftToRight:
   582 				european_to_arabic_number = EFalse;
   583 				break;
   584 			case ERightToLeft:
   585 				european_to_arabic_number = EFalse;
   586 				break;
   587 			case ERightToLeftArabic:				// Arabic letters change to R
   588 				european_to_arabic_number = ETrue;
   589 				r->iCategory = ERightToLeft;
   590 				break;
   591 			case EEuropeanNumber:				    // European numbers change to Arabic if last strong category was R
   592 				if (european_to_arabic_number)
   593 					r->iCategory = EArabicNumber;
   594 				break;
   595 			default:
   596 				break;
   597 			}
   598 		aContext.iCategories |= r->iCategory;
   599 		prev_cat = static_cast<TCategory>(r->iCategory);
   600 		}
   601 	}
   602 /**
   603 This phase removes categories NSM, AL, ES, ET, CS, BS, S, WS and BN, leaving
   604 only L, R, EN, AN and ON.
   605 @internalComponent
   606 */
   607 void TBidirectionalState::ResolveWeakTypesW4W5W6(TReorderContext& aContext)
   608 	{
   609 	int i;
   610 	TRunInfo* r;
   611 	TCategory prev_cat = iPreviousCategory;
   612 	TCategory next_cat = EOtherNeutral;
   613 
   614 	// Phase P0b.
   615 	prev_cat = iPreviousCategory;
   616 	if (aContext.iCategories & EBoundaryNeutral)
   617 		{
   618 		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
   619 			 i < aContext.iRuns;
   620 			 i++, aContext.iCategories |= r->iCategory, r++)
   621 			{
   622 			if (r->iCategory == EBoundaryNeutral)		// runs of boundary neutrals change to EN, ET or AN if adjacent to
   623 				{										// one of these, otherwise to ON
   624 				int end = i + 1;
   625 				while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EBoundaryNeutral)
   626 					end++;
   627 				next_cat = end < aContext.iRuns ? (TCategory)(aContext.iRunInfo[end].iCategory) : aContext.iNextCategory;
   628 				TCategory c = EOtherNeutral;
   629 				if (prev_cat == EEuropeanNumber || next_cat == EEuropeanNumber)
   630 					c = EEuropeanNumber;
   631 				else if (prev_cat == EEuropeanNumberTerminator || next_cat == EEuropeanNumberTerminator)
   632 					c = EEuropeanNumberTerminator;
   633 				else if (prev_cat == EArabicNumber || next_cat == EArabicNumber)
   634 					c = EArabicNumber;
   635 				for (int j = i; j < end; j++)
   636 					aContext.iRunInfo[j].iCategory = c;
   637 				i = end - 1;
   638 				r = &aContext.iRunInfo[i];
   639 				}
   640 			prev_cat = (TCategory)r->iCategory;
   641 			}
   642 		}
   643 
   644 	// Phase P1.
   645 	prev_cat = iPreviousCategory;
   646 	if (aContext.iCategories & (EEuropeanNumberSeparator | ECommonNumberSeparator))
   647 		{
   648 		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
   649 			 i < aContext.iRuns;
   650 			 i++, aContext.iCategories |= r->iCategory, r++)
   651 			{
   652 			next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
   653 			switch (r->iCategory)
   654 				{
   655 				case EEuropeanNumberSeparator:			// European separators change to EN if between two ENs, else to ON
   656 					if (prev_cat == EEuropeanNumber && next_cat == EEuropeanNumber)
   657 						r->iCategory = EEuropeanNumber;
   658 					else
   659 						r->iCategory = EOtherNeutral;
   660 					break;
   661 				case ECommonNumberSeparator:			// CSs change to EN or AN if between two of the same, else to ON
   662 					if (prev_cat == EEuropeanNumber && next_cat == EEuropeanNumber)
   663 						r->iCategory = EEuropeanNumber;
   664 					else if (prev_cat == EArabicNumber && next_cat == EArabicNumber)
   665 						r->iCategory = EArabicNumber;
   666 					else
   667 						r->iCategory = EOtherNeutral;
   668 					break;
   669 				default:
   670 					break;
   671 				}
   672 			prev_cat = (TCategory)r->iCategory;
   673 			}
   674 		}
   675 
   676 	/*
   677 	Phase L1: tabs, whitespace before tabs, and trailing whitespace, is set to the base embedding level.
   678 	We ought to do this just before the final reordering, but the whitespace and segment separator
   679 	categories have disappeared by then so we use the sentinel value 255 which tells
   680 	ResolveImplicitLevels what to do.
   681 	*/
   682 	TBool demote_whitespace = TRUE;
   683 	for (i = aContext.iRuns - 1, r = &aContext.iRunInfo[i]; i >= 0; i--, r--)
   684 		{
   685 		switch (r->iCategory)
   686 			{
   687 			case EWhitespace:
   688 				break;
   689 			case ESegmentSeparator:
   690 				demote_whitespace = TRUE;
   691 				break;
   692 			default:
   693 				demote_whitespace = FALSE;
   694 				break;
   695 			}
   696 		if (demote_whitespace)
   697 			r->iEmbeddingLevel = 255;
   698 		}
   699 
   700 	// Phases P2 and P3.
   701 	prev_cat = iPreviousCategory;
   702 	if (aContext.iCategories & (EEuropeanNumberTerminator | ESegmentSeparator | EWhitespace))
   703 		{
   704 		for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
   705 			 i < aContext.iRuns;
   706 			 i++, aContext.iCategories |= r->iCategory, r++)
   707 			{
   708 			next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
   709 			switch (r->iCategory)
   710 				{
   711 				case EEuropeanNumberTerminator:			// runs of ETs change to ENs if next to an EN, else to ON
   712 					{
   713 					int end = i + 1;
   714 					while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EEuropeanNumberTerminator)
   715 						end++;
   716 					next_cat = end < aContext.iRuns ? (TCategory)(aContext.iRunInfo[end].iCategory) : aContext.iNextCategory;
   717 					TCategory c = EOtherNeutral;
   718 					if (prev_cat == EEuropeanNumber || next_cat == EEuropeanNumber)
   719 						c = EEuropeanNumber;
   720 					for (int j = i; j < end; j++)
   721 						aContext.iRunInfo[j].iCategory = c;
   722 					i = end - 1;
   723 					r = &aContext.iRunInfo[i];
   724 					}
   725 					break;
   726 				case ESegmentSeparator:					// S and WS change to ON
   727 				case EWhitespace:
   728 					r->iCategory = EOtherNeutral;
   729 					break;
   730 				default:
   731 					break;
   732 				}
   733 			prev_cat = (TCategory)r->iCategory;
   734 			}
   735 		}
   736 	}
   737 
   738 void TBidirectionalState::ResolveWeakTypesW7(TReorderContext& aContext)
   739 	{
   740 	if (!(aContext.iCategories & EEuropeanNumber))
   741 		return;
   742 
   743 	TCategory prev_strong_cat = iPreviousStrongCategory;
   744 
   745 	aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
   746 	TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
   747 	for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
   748 		{
   749 		switch (r->iCategory)
   750 			{
   751 			case ELeftToRight:
   752 				prev_strong_cat = ELeftToRight;
   753 				break;
   754 			case ERightToLeft:
   755 				prev_strong_cat = ERightToLeft;
   756 				break;
   757 			case EEuropeanNumber: 
   758 				if (prev_strong_cat == ELeftToRight)
   759 					r->iCategory = ELeftToRight;
   760 				break;
   761 			default:
   762 				break;
   763 			}
   764 		aContext.iCategories |= r->iCategory;
   765 		}
   766 	}
   767 
   768 
   769 
   770 void TBidirectionalState::DeneutralizeRuns(TRunInfo* aStart, TRunInfo* aEnd,
   771 	TCategory aStartCategory, TCategory aEndCategory)
   772 /**
   773 Turn all ON (Other Neutral) into non-neutrals according to the rules N1 and N2.
   774 @param aStart The start of the run array to be altered.
   775 @param aEnd One past the end of the run array to be altered.
   776 @param aStartCategory
   777 	The last non-neutral before the run, must be ELeftToRight or ERightToLeft.
   778 @param aEndCategory
   779 	The first non-neutral after the run, must be ELeftToRight or ERightToLeft.
   780 @internalComponent
   781 */	{
   782 	// if sandwiched by the same category, neutrals become that.
   783 	if (aStartCategory == aEndCategory)
   784 		{
   785 		for (; aStart != aEnd; ++aStart)
   786 			aStart->iCategory = aStartCategory;
   787 		return;
   788 		}
   789 	// otherwise look at the embedding level in each case
   790 	for (; aStart != aEnd; ++aStart)
   791 		{
   792 		aStart->iCategory = aStart->iEmbeddingLevel & 1?
   793 			ERightToLeft : ELeftToRight;
   794 		}
   795 	}
   796 
   797 
   798 void TBidirectionalState::ResolveNeutralTypes(TReorderContext& aContext)
   799 	/**
   800 This phase removes the ON (Other Neutral) category, leaving only L, R, EN, and
   801 AN; no need to update aContext.iCategories.
   802 @internalComponent
   803 */
   804     {
   805 	// Really we should find if any number intervenes, but this would require
   806 	// a BC break.
   807 	TCategory prevNonNeutral = iPreviousStrongCategory;
   808 	if (prevNonNeutral & ELeftToRightGroup)
   809 		prevNonNeutral = ELeftToRight;
   810 	else if (prevNonNeutral & ERightToLeftGroup)
   811 		prevNonNeutral = ERightToLeft;
   812 	TRunInfo *prevNonNeutralRun = aContext.iRunInfo;	// one past the last non-neutral found
   813 	TRunInfo *endOfRuns = aContext.iRunInfo + aContext.iRuns;
   814 
   815 	// All neutrals have now been changed to ON; change them to L or R depending on context.
   816 	for (TRunInfo *p = aContext.iRunInfo; p != endOfRuns; ++p)
   817 		{
   818 		TCategory nonNeutral = EOtherNeutral;
   819 		switch (p->iCategory)
   820 			{
   821 			case ELeftToRight:
   822 				nonNeutral = ELeftToRight;
   823 				break;
   824 			case ERightToLeft:
   825 				nonNeutral = ERightToLeft;
   826 				break;
   827 			case EArabicNumber:
   828 			case EEuropeanNumber: 
   829 				nonNeutral = ERightToLeft;
   830 				break;
   831 			default:
   832 				break;
   833 			}
   834 		if (nonNeutral != EOtherNeutral)
   835 			{
   836 			if (p != prevNonNeutralRun)
   837 				DeneutralizeRuns(prevNonNeutralRun, p,
   838 					prevNonNeutral, nonNeutral);
   839 			prevNonNeutral = nonNeutral;
   840 			prevNonNeutralRun = p + 1;
   841 			}
   842 		}
   843 	DeneutralizeRuns(prevNonNeutralRun, endOfRuns, prevNonNeutral,
   844 		aContext.iNextStrongCategory);
   845 	}
   846 
   847 
   848 void TBidirectionalState::ResolveImplicitLevels(TReorderContext& aContext)
   849 /**
   850 Phases I1 and I2.
   851 @internalComponent
   852 */	{
   853 	int i;
   854 	TRunInfo* r;
   855 	for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
   856 		{
   857 		if (r->iEmbeddingLevel == 255) // sentinel indicating this is a tab or segment-final whitespace
   858 			r->iEmbeddingLevel = iStack[0].iEmbeddingLevel;
   859 		else switch (r->iCategory)
   860 			{
   861 			case ELeftToRight:
   862 				if (r->iEmbeddingLevel & 1)
   863 					r->iEmbeddingLevel++;
   864 				break;
   865 			case ERightToLeft:
   866 				if (!(r->iEmbeddingLevel & 1))
   867 					r->iEmbeddingLevel++;
   868 				break;
   869 			case EEuropeanNumber: case EArabicNumber:
   870 				if (r->iEmbeddingLevel & 1)
   871 					r->iEmbeddingLevel++;
   872 				else
   873 					r->iEmbeddingLevel += 2;
   874 				break;
   875 			default:
   876 				break;
   877 			}
   878 		}
   879 	}
   880 
   881 
   882 void TBidirectionalState::ReorderRuns(TReorderContext& aContext)
   883 /**
   884 Phase L2.
   885 @internalComponent
   886 */	{
   887 	// Find the highest level and lowest odd level.
   888 	int i;
   889 	TRunInfo* r;
   890 	int highest = 0;
   891 	int lowest_odd = EMaxLevel;
   892 	int level = 0;
   893 	for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
   894 		{
   895 		level = r->iEmbeddingLevel;
   896 		if (level > highest)
   897 			highest = level;
   898 		if ((level & 1) && level < lowest_odd)
   899 			lowest_odd = level;
   900 		}
   901 
   902 	// From the highest level to the lowest odd level, reverse any run at that level or higher.
   903 	for (level = highest; level >= lowest_odd; level--)
   904 		{
   905 		int run_start = 0;
   906 		r = aContext.iRunInfo;
   907 		while (run_start < aContext.iRuns)
   908 			{
   909 			while (run_start < aContext.iRuns && r->iEmbeddingLevel < level)
   910 				{
   911 				run_start++;
   912 				r++;
   913 				}
   914 			int run_end = run_start;
   915 			while (run_end < aContext.iRuns && r->iEmbeddingLevel >= level)
   916 				{
   917 				r->iDirection = !r->iDirection;
   918 				run_end++;
   919 				r++;
   920 				}
   921 			TRunInfo* p = &aContext.iRunInfo[run_start];
   922 			TRunInfo* q = &aContext.iRunInfo[run_end - 1];
   923 			while (p < q)
   924 				{
   925 				TRunInfo temp = *p;
   926 				*p = *q;
   927 				*q = temp;
   928 				p++;
   929 				q--;
   930 				}
   931 			run_start = run_end;
   932 			}
   933 		}
   934 	}
   935 
   936 
   937 TBidirectionalState::TCategory TBidirectionalState::Push(TCategory aStartCategory)
   938 /** @internalComponent */
   939 	{
   940 	TInt rightToLeftFlag = (static_cast<TInt>(aStartCategory)
   941 		& ERightToLeftGroup)? 1 : 0;
   942 	TInt oldLevel = State().iEmbeddingLevel;
   943 	TInt newLevel = oldLevel + 1;
   944 	// And add an extra one if the bottom bit is not correct.
   945 	newLevel += (newLevel & 1) ^ rightToLeftFlag;
   946 
   947 	if (EMaxExplicitLevel < newLevel)
   948 		{
   949 		if (oldLevel == 60)
   950 			++iPushesBeyond60;
   951 		else
   952 			++iPushesBeyond61;
   953 		return EBoundaryNeutral;
   954 		}
   955 
   956 	++iStackLevel;
   957 	TStackItem& state = iStack[iStackLevel];
   958 	state.iEmbeddingLevel = static_cast<TUint8>(newLevel);
   959 	state.iOverrideState = static_cast<TOverrideState>(aStartCategory
   960 		& (ELeftToRightOverride | ERightToLeftOverride));
   961 	state.iStartCategory = aStartCategory;
   962 
   963 	return rightToLeftFlag? ERightToLeft : ELeftToRight;
   964 	}
   965 
   966 
   967 TBidirectionalState::TCategory TBidirectionalState::Pop()
   968 /** @internalComponent */	
   969    {
   970 	__ASSERT_DEBUG(0 < iStackLevel, User::Invariant());
   971 	TInt level = State().iEmbeddingLevel;
   972 	if (level < 60)
   973 		--iStackLevel;
   974 	else if (iPushesBeyond61 != 0)
   975 		--iPushesBeyond61;
   976 	else if (level == 61)
   977 		--iStackLevel;
   978 	else if (iPushesBeyond60)
   979 		--iPushesBeyond60;
   980 	else
   981 		--iStackLevel;
   982 	return (level & 1)? ERightToLeft : ELeftToRight;
   983 	}
   984 
   985 
   986 EXPORT_C void TBidirectionalState::Reset()
   987 /** Sets the object to its default 'start of paragraph' state. */
   988 	{
   989 	iStackLevel = 0;
   990 	iPushesBeyond60 = 0;
   991 	iPushesBeyond61 = 0;
   992 	iStack[0].iEmbeddingLevel = 0;
   993 	iStack[0].iOverrideState = ENoOverrideState;
   994 	iStack[0].iStartCategory = EOtherNeutral;
   995 	iPreviousCategory = ELeftToRight;
   996 	iPreviousStrongCategory = ELeftToRight;
   997 	}
   998 
   999 
  1000 EXPORT_C TBool TBidirectionalState::IsDefault() const
  1001 /** Returns Gets the default 'start of paragraph' state.
  1002 
  1003 @return ETrue if the object is in its default 'start of paragraph' state. */
  1004 	{
  1005 	return iStackLevel == 0 &&
  1006 		   iStack[0].iEmbeddingLevel == 0 &&
  1007 		   iStack[0].iOverrideState == ENoOverrideState &&
  1008 		   iStack[0].iStartCategory == EOtherNeutral &&
  1009 		   iPreviousCategory == ELeftToRight &&
  1010 		   iPreviousStrongCategory == ELeftToRight;
  1011 	}
  1012 
  1013 
  1014 EXPORT_C TBool TBidirectionalState::operator==(const TBidirectionalState& aState) const
  1015 /** Return ETrue if two bidirectional states are identical.
  1016 
  1017 @param aState A bidirectional state.
  1018 @return ETrue if two bidirectional states are identical. */
  1019 	{
  1020 	if (iPreviousCategory != aState.iPreviousCategory ||
  1021 		iPreviousStrongCategory != aState.iPreviousStrongCategory ||
  1022 		iStackLevel != aState.iStackLevel)
  1023 		return FALSE;
  1024 	const TStackItem* p = iStack;
  1025 	const TStackItem* q = aState.iStack;
  1026 	for (int i = 0; i <= iStackLevel; i++, p++, q++)
  1027 		{
  1028 		if (p->iStartCategory != q->iStartCategory ||
  1029 			p->iOverrideState != q->iOverrideState ||
  1030 			p->iEmbeddingLevel != q->iEmbeddingLevel)
  1031 			return FALSE;
  1032 		}
  1033 	return TRUE;
  1034 	}
  1035 
  1036 
  1037 TInt TBidirectionalState::CatToNumber(TInt aCat)
  1038 /**
  1039 Finds the highest bit set in the input. Used to convert
  1040 TBidirectionalState::TCategory into TChar::TBdCategory.
  1041 @param aCat a TBidirectionalState::TCategory.
  1042 @return The equivalent TChar::TBdCategory.
  1043 @internalComponent
  1044 */	{
  1045 	TInt shifts = 0;
  1046 	TInt bits = 32;
  1047 	TInt mask = ~0L;
  1048 	while (bits != 0)
  1049 		{
  1050 		bits >>= 1;
  1051 		mask <<= bits;
  1052 		if ((aCat & mask) == 0)
  1053 			{
  1054 			aCat <<= bits;
  1055 			shifts += bits;
  1056 			}
  1057 		}
  1058 	return 31 - shifts;
  1059 	}
  1060 
  1061 
  1062 EXPORT_C void TBidirectionalState::ExternalizeL(RWriteStream& aDest)
  1063 /** Serializes a bidirectional state to an output stream.
  1064 
  1065 @param aDest An output stream. */
  1066 	{
  1067 	//+ put the prev cat, prev strong cat and stack levels in one number?
  1068 	// Write the previous category and previous strong category.
  1069 	aDest.WriteInt8L(CatToNumber(iPreviousCategory));
  1070 	aDest.WriteInt8L(CatToNumber(iPreviousStrongCategory));
  1071 
  1072 	// Write the number of stack levels
  1073 	aDest.WriteInt8L(iStackLevel);
  1074 
  1075 	/*
  1076 	Write each stack level as a single number: 5 bits for the start category, 2 for the override state,
  1077 	6 for the embedding level.
  1078 	*/
  1079 	for (int i = 0; i <= iStackLevel; i++)
  1080 		{
  1081 		TInt x = CatToNumber(iStack[i].iStartCategory);
  1082 		if (iStack[i].iOverrideState == ELeftToRightOverrideState)
  1083 			{
  1084 			x |= (KBidirectionalStateOverrideStreamValueLeftToRight << 5);	
  1085 			}
  1086         else if (iStack[i].iOverrideState == ERightToLeftOverrideState)
  1087         	{
  1088         	x |= (KBidirectionalStateOverrideStreamValueRightToLeft << 5); 	
  1089         	}
  1090        	x |= ((TInt)iStack[i].iEmbeddingLevel << 7);
  1091 		aDest.WriteInt16L(x);
  1092 		}
  1093 
  1094 	TInt level = State().iEmbeddingLevel;
  1095 	if (60 <= level)
  1096 		{
  1097 		aDest.WriteInt8L(iPushesBeyond60);
  1098 		aDest.WriteInt8L(iPushesBeyond61);
  1099 		}
  1100 	}
  1101 
  1102 
  1103 EXPORT_C void TBidirectionalState::InternalizeL(RReadStream& aSource)
  1104 /** Reads a bidirectional state from an input stream, translating it from its serialized 
  1105 form.
  1106 
  1107 @param aSource A source stream. */
  1108 	{
  1109 	// Read the previous category and the previous strong category.
  1110 	TInt x = aSource.ReadInt8L();
  1111 	iPreviousCategory = (TCategory)(1 << x);
  1112 	x = aSource.ReadInt8L();
  1113 	iPreviousStrongCategory = (TCategory)(1 << x);
  1114 
  1115 	// Read the number of stack levels.
  1116 	iStackLevel = aSource.ReadInt8L();
  1117 
  1118 	// Read the stack levels.
  1119 	for (int i = 0; i <= iStackLevel; i++)
  1120 		{
  1121 		x = aSource.ReadInt16L();
  1122 		iStack[i].iStartCategory = (TCategory)(1 << (x & 0x1F));
  1123 		switch ((x >> 5) & 3)
  1124         	{
  1125     	case KBidirectionalStateOverrideStreamValueLeftToRight: 
  1126     		iStack[i].iOverrideState = ELeftToRightOverrideState;
  1127     		break;
  1128     	case KBidirectionalStateOverrideStreamValueRightToLeft:
  1129     		iStack[i].iOverrideState = ERightToLeftOverrideState; 
  1130     		break;
  1131     	case KBidirectionalStateOverrideStreamValueNone:
  1132     	default: iStack[i].iOverrideState = ENoOverrideState; break;
  1133         	};
  1134 		iStack[i].iEmbeddingLevel = (TUint8)(x >> 7);
  1135 		}
  1136 
  1137 	TInt level = State().iEmbeddingLevel;
  1138 	if (60 <= level)
  1139 		{
  1140 		iPushesBeyond60 = aSource.ReadInt8L();
  1141 		iPushesBeyond61 = aSource.ReadInt8L();
  1142 		}
  1143 	else
  1144 		{
  1145 		iPushesBeyond60 = 0;
  1146 		iPushesBeyond61 = 0;
  1147 		}
  1148 	}
  1149 
  1150 
  1151 TBidirectionalState::TBidirectionalState(TChar::TBdCategory aPrevCat,
  1152 	TChar::TBdCategory aPrevStrongCat,
  1153 	TBool aParRightToLeft)
  1154 	/**
  1155 Constructor suitable for test code.
  1156 @internalComponent
  1157 */
  1158     {
  1159 	Reset();
  1160 	iPreviousCategory = CharToBdCat(aPrevCat);
  1161 	iPreviousStrongCategory = CharToBdCat(aPrevStrongCat);
  1162 	iStack[0].iEmbeddingLevel = (TUint8) (aParRightToLeft? 1 : 0);
  1163 	}