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