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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // Bidirectional text reordering; based on the Unicode Bidirectional Reordering Algorithm.
22 const TInt KBidirectionalStateOverrideStreamValueNone = 0;
23 const TInt KBidirectionalStateOverrideStreamValueLeftToRight = 1;
24 const TInt KBidirectionalStateOverrideStreamValueRightToLeft = 2;
26 inline TBool IsSupplementary(TUint aChar)
28 @param aChar The 32-bit code point value of a Unicode character.
30 @return True, if aChar is supplementary character; false, otherwise.
33 return (aChar > 0xFFFF);
36 inline TBool IsHighSurrogate(TText16 aInt16)
38 @return True, if aText16 is high surrogate; false, otherwise.
41 return (aInt16 & 0xFC00) == 0xD800;
44 inline TBool IsLowSurrogate(TText16 aInt16)
46 @return True, if aText16 is low surrogate; false, otherwise.
49 return (aInt16 & 0xFC00) == 0xDC00;
52 inline TUint JoinSurrogate(TText16 aHighSurrogate, TText16 aLowSurrogate)
54 Combine a high surrogate and a low surrogate into a supplementary character.
56 @return The 32-bit code point value of the generated Unicode supplementary
60 return ((aHighSurrogate - 0xD7F7) << 10) + aLowSurrogate;
63 TBool TextDefaultsToRightToLeft(const TDesC& aText, TBool* aFound);
65 TBidirectionalState::TCategory TBidirectionalState::CharToBdCat(TChar::TBdCategory aCat)
67 return static_cast<TBidirectionalState::TCategory>(
68 1 << static_cast<TInt>(aCat));
71 TBidirectionalState::TCategory TBidirectionalState::UintToBdCat(TUint aCat)
73 return static_cast<TBidirectionalState::TCategory>(1 << aCat);
76 void TBidirectionalState::TReorderContext::SetNextCategory(
77 TChar::TBdCategory aCat)
79 iNextCategory = CharToBdCat(aCat);
82 void TBidirectionalState::TReorderContext::SetNextStrongCategory(
83 TChar::TBdCategory aCat)
85 iNextStrongCategory = CharToBdCat(aCat);
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.
93 @param aStart Start position of text to be reversed.
94 @param aLength Length of text to be reversed. */
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);
104 // A local helper function. Get the next character from a buffer. This
105 // function won't check buffer length.
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)
112 const TText16 *p = aText;
114 while (aCharacterIndex >= 0)
117 ASSERT(!IsLowSurrogate(c));
118 if (IsHighSurrogate(c))
120 ASSERT(IsLowSurrogate(*p));
121 c = JoinSurrogate(c, *p++);
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
139 @param aMaxRuns The size of the aRun array. No more than this number of runs
141 @return The number of runs that are required for the full results of the
143 @internalTechnology */
147 if (aRun && 0 < aMaxRuns)
149 aRun[0].iCategory = TChar::EOtherNeutral;
158 const TText* p = aText;
159 const TText* q = p + aLength;
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();
165 if (IsSupplementary(pc))
168 run_end = 2; // run_end points to "end" of current character
173 // get the character pointed by 'p'
174 pc = ::GetOneCharacter(p, 0);
176 TChar::TBdCategory new_cat = pc.GetBdCategory();
177 if (new_cat != cur_cat)
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)
186 else if (p < q - 1 &&
187 (new_cat == TChar::EWhitespace ||
188 new_cat == TChar::EEuropeanNumberSeparator ||
189 new_cat == TChar::ECommonNumberSeparator))
191 TChar nextChar = ::GetOneCharacter(p, 1);
192 TChar::TBdCategory next_cat = nextChar.GetBdCategory();
193 if (new_cat == TChar::EWhitespace)
195 if ((cur_cat == TChar::ELeftToRight ||
196 cur_cat == TChar::ERightToLeft ||
197 cur_cat == TChar::ERightToLeftArabic) && cur_cat == next_cat)
200 else if (cur_cat == TChar::EEuropeanNumber && next_cat == TChar::EEuropeanNumber)
201 new_cat = TChar::EEuropeanNumber;
205 if (new_cat != cur_cat ||
206 cur_cat == TChar::EEuropeanNumberSeparator ||
207 cur_cat == TChar::ECommonNumberSeparator)
209 if (aRun && runs < aMaxRuns)
211 aRun[runs].iCategory = cur_cat;
212 aRun[runs].iStart = run_start;
213 aRun[runs].iLength = run_end - run_start;
223 // adjust 'p' and 'run_end'
224 if (IsSupplementary(pc))
233 if (aRun && runs < aMaxRuns)
235 aRun[runs].iCategory = cur_cat;
236 aRun[runs].iStart = run_start;
237 aRun[runs].iLength = run_end - run_start;
244 EXPORT_C TInt TBidirectionalState::ReorderText(const TText* aText,TInt aLength,TBool aParRightToLeft,
246 /** Reorders text according to the Unicode Bidirectional Reordering algorithm.
248 Reorders the input text from logical order (which may be bidirectional) to
249 display order (strictly left to right).
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
263 aNewText = const_cast<TText*>(aText);
267 int error = KErrNone;
268 TBidirectionalState::TRunInfo run_info;
269 run_info.iDirection = 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);
278 allocated_run_info_array = new TBidirectionalState::TRunInfo[runs];
279 if (allocated_run_info_array)
281 run_info_array = allocated_run_info_array;
282 GenerateBdRunArray(aText, aLength, run_info_array, runs);
286 // the run cannot be allocated: stick with our single l-to-r run
287 error = KErrNoMemory;
291 if (error == KErrNone)
293 TBidirectionalState state;
294 state.ReorderLine(run_info_array, runs, ETrue, ETrue, aParRightToLeft,
295 TChar::EOtherNeutral, TChar::EOtherNeutral);
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)
302 // Reorder the text into a new buffer.
303 TText* buffer = new TText[aLength];
306 delete [] allocated_run_info_array;
309 const TBidirectionalState::TRunInfo* r = run_info_array;
310 TText* dest = buffer;
311 for (int i = 0; i < runs; i++, r++)
313 const TText* source = &aText[r->iStart];
314 int length = r->iLength;
315 Mem::Copy(dest,source,length * sizeof(TText));
317 ReverseGroups(dest,length);
321 delete [] allocated_run_info_array;
327 EXPORT_C TBidirectionalState::TBidirectionalState()
328 /** Standard constructor. */
334 /** Reorders a line of text and updates the bidirectional state for the next line.
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
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.
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)
361 ReorderLine(aRunInfo, aRuns, aParStart, aParEnd, aParRightToLeft,
362 aNextCategory, aNextStrongCategory);
363 if (iStackLevel != 0)
365 aVisualEndIsAmbiguous = ETrue;
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;
376 // Ambiguous if any of the surrounding categories are strongly left-to-right
377 aVisualEndIsAmbiguous =
378 (iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
379 & KAllStrongLeftToRight;
383 // Ambiguous if any of the surrounding categories are strongly right-to-left
384 aVisualEndIsAmbiguous =
385 (iPreviousStrongCategory | iPreviousCategory | nextCat | nextStrong)
386 & KAllStrongRightToLeft;
389 /** Reorders a line of text and updates the bidirectional state for the next line.
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
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)
412 // Reset if this is a new paragraph.
416 iPreviousCategory = EOtherNeutral;
419 iStack[0].iEmbeddingLevel = 1;
420 iPreviousStrongCategory = ERightToLeft;
424 // Initialise the context object.
425 TReorderContext context;
426 context.iRunInfo = aRunInfo;
427 context.iRuns = aRuns;
428 context.iLastStrongCategory = iPreviousStrongCategory;
430 context.iNextCategory = context.iNextStrongCategory = EOtherNeutral;
433 context.iNextCategory = CharToBdCat(aNextCategory);
434 context.iNextStrongCategory = CharToBdCat(aNextStrongCategory);
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)
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;
448 // Do nothing if no right-to-left material is present.
450 (iStackLevel == 0 && iStack[0].iEmbeddingLevel == 0 &&
451 !(context.iCategories & (ERightToLeftGroup | EBdControlsGroup))))
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);
469 void TBidirectionalState::PrepareForNextLine(const TReorderContext& aContext)
471 Fold context information back into TBidirectionalState.
475 if (aContext.iRuns != 0)
477 iPreviousCategory = static_cast<TCategory>(
478 aContext.iRunInfo[aContext.iRuns - 1].iCategory);
479 iPreviousStrongCategory = aContext.iLastStrongCategory;
484 void TBidirectionalState::HandleBdControls(TReorderContext& aContext)
486 Handle LRO, RLO, LRE, RLE and PDF. After this phase, these categories will no
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.
496 aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
497 for (TInt i = 0; i != aContext.iRuns; ++i)
499 TRunInfo* r = aContext.iRunInfo + i;
500 TCategory nextCatInLine = i < aContext.iRuns - 1?
501 (TCategory)(r[1].iCategory) : ENoCategory;
503 TBool was_pdf = FALSE;
504 if (r->iCategory & EBdControlsGroup)
506 if (r->iCategory == EPopDirectionalFormat)
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.
517 r->iCategory = r[1].iCategory = EBoundaryNeutral;
520 r->iCategory = Pop();
524 r->iCategory = EBoundaryNeutral;
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;
533 r->iCategory = Push(static_cast<TCategory>(r->iCategory));
539 switch (State().iOverrideState)
541 case ELeftToRightOverrideState:
542 r->iCategory = ELeftToRight;
544 case ERightToLeftOverrideState:
545 r->iCategory = ERightToLeft;
550 r->iEmbeddingLevel = State().iEmbeddingLevel;
552 if (r->iCategory & EStrongGroup)
553 aContext.iLastStrongCategory = static_cast<TCategory>(r->iCategory);
554 aContext.iCategories |= r->iCategory;
559 void TBidirectionalState::ResolveWeakTypesW1W2W3(TReorderContext& aContext)
561 Unicode(3.2) Bidirectional Algorithm phases W1, W2 and W3.
565 if (!(aContext.iCategories
566 & (ENonSpacingMark | ERightToLeftArabic | EEuropeanNumber)))
569 TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
570 TCategory prev_cat = iPreviousCategory;
571 TBool european_to_arabic_number = iPreviousStrongCategory == ERightToLeftArabic;
573 aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
574 for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
576 switch (r->iCategory)
578 case ENonSpacingMark: // non-spacing marks change to the previous category
579 r->iCategory = prev_cat;
582 european_to_arabic_number = EFalse;
585 european_to_arabic_number = EFalse;
587 case ERightToLeftArabic: // Arabic letters change to R
588 european_to_arabic_number = ETrue;
589 r->iCategory = ERightToLeft;
591 case EEuropeanNumber: // European numbers change to Arabic if last strong category was R
592 if (european_to_arabic_number)
593 r->iCategory = EArabicNumber;
598 aContext.iCategories |= r->iCategory;
599 prev_cat = static_cast<TCategory>(r->iCategory);
603 This phase removes categories NSM, AL, ES, ET, CS, BS, S, WS and BN, leaving
604 only L, R, EN, AN and ON.
607 void TBidirectionalState::ResolveWeakTypesW4W5W6(TReorderContext& aContext)
611 TCategory prev_cat = iPreviousCategory;
612 TCategory next_cat = EOtherNeutral;
615 prev_cat = iPreviousCategory;
616 if (aContext.iCategories & EBoundaryNeutral)
618 for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
620 i++, aContext.iCategories |= r->iCategory, r++)
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
625 while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EBoundaryNeutral)
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)
631 else if (prev_cat == EEuropeanNumberTerminator || next_cat == EEuropeanNumberTerminator)
632 c = EEuropeanNumberTerminator;
633 else if (prev_cat == EArabicNumber || next_cat == EArabicNumber)
635 for (int j = i; j < end; j++)
636 aContext.iRunInfo[j].iCategory = c;
638 r = &aContext.iRunInfo[i];
640 prev_cat = (TCategory)r->iCategory;
645 prev_cat = iPreviousCategory;
646 if (aContext.iCategories & (EEuropeanNumberSeparator | ECommonNumberSeparator))
648 for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
650 i++, aContext.iCategories |= r->iCategory, r++)
652 next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
653 switch (r->iCategory)
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;
659 r->iCategory = EOtherNeutral;
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;
667 r->iCategory = EOtherNeutral;
672 prev_cat = (TCategory)r->iCategory;
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.
682 TBool demote_whitespace = TRUE;
683 for (i = aContext.iRuns - 1, r = &aContext.iRunInfo[i]; i >= 0; i--, r--)
685 switch (r->iCategory)
689 case ESegmentSeparator:
690 demote_whitespace = TRUE;
693 demote_whitespace = FALSE;
696 if (demote_whitespace)
697 r->iEmbeddingLevel = 255;
701 prev_cat = iPreviousCategory;
702 if (aContext.iCategories & (EEuropeanNumberTerminator | ESegmentSeparator | EWhitespace))
704 for (i = 0, aContext.iCategories = iPreviousCategory | aContext.iNextCategory, r = aContext.iRunInfo;
706 i++, aContext.iCategories |= r->iCategory, r++)
708 next_cat = i < aContext.iRuns - 1 ? (TCategory)(r[1].iCategory) : aContext.iNextCategory;
709 switch (r->iCategory)
711 case EEuropeanNumberTerminator: // runs of ETs change to ENs if next to an EN, else to ON
714 while (end < aContext.iRuns && aContext.iRunInfo[end].iCategory == EEuropeanNumberTerminator)
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)
720 for (int j = i; j < end; j++)
721 aContext.iRunInfo[j].iCategory = c;
723 r = &aContext.iRunInfo[i];
726 case ESegmentSeparator: // S and WS change to ON
728 r->iCategory = EOtherNeutral;
733 prev_cat = (TCategory)r->iCategory;
738 void TBidirectionalState::ResolveWeakTypesW7(TReorderContext& aContext)
740 if (!(aContext.iCategories & EEuropeanNumber))
743 TCategory prev_strong_cat = iPreviousStrongCategory;
745 aContext.iCategories = iPreviousCategory | aContext.iNextCategory;
746 TRunInfo* endOfRuns = aContext.iRunInfo + aContext.iRuns;
747 for (TRunInfo* r = aContext.iRunInfo; r != endOfRuns; r++)
749 switch (r->iCategory)
752 prev_strong_cat = ELeftToRight;
755 prev_strong_cat = ERightToLeft;
757 case EEuropeanNumber:
758 if (prev_strong_cat == ELeftToRight)
759 r->iCategory = ELeftToRight;
764 aContext.iCategories |= r->iCategory;
770 void TBidirectionalState::DeneutralizeRuns(TRunInfo* aStart, TRunInfo* aEnd,
771 TCategory aStartCategory, TCategory aEndCategory)
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.
779 The first non-neutral after the run, must be ELeftToRight or ERightToLeft.
782 // if sandwiched by the same category, neutrals become that.
783 if (aStartCategory == aEndCategory)
785 for (; aStart != aEnd; ++aStart)
786 aStart->iCategory = aStartCategory;
789 // otherwise look at the embedding level in each case
790 for (; aStart != aEnd; ++aStart)
792 aStart->iCategory = aStart->iEmbeddingLevel & 1?
793 ERightToLeft : ELeftToRight;
798 void TBidirectionalState::ResolveNeutralTypes(TReorderContext& aContext)
800 This phase removes the ON (Other Neutral) category, leaving only L, R, EN, and
801 AN; no need to update aContext.iCategories.
805 // Really we should find if any number intervenes, but this would require
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;
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)
818 TCategory nonNeutral = EOtherNeutral;
819 switch (p->iCategory)
822 nonNeutral = ELeftToRight;
825 nonNeutral = ERightToLeft;
828 case EEuropeanNumber:
829 nonNeutral = ERightToLeft;
834 if (nonNeutral != EOtherNeutral)
836 if (p != prevNonNeutralRun)
837 DeneutralizeRuns(prevNonNeutralRun, p,
838 prevNonNeutral, nonNeutral);
839 prevNonNeutral = nonNeutral;
840 prevNonNeutralRun = p + 1;
843 DeneutralizeRuns(prevNonNeutralRun, endOfRuns, prevNonNeutral,
844 aContext.iNextStrongCategory);
848 void TBidirectionalState::ResolveImplicitLevels(TReorderContext& aContext)
855 for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
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)
862 if (r->iEmbeddingLevel & 1)
863 r->iEmbeddingLevel++;
866 if (!(r->iEmbeddingLevel & 1))
867 r->iEmbeddingLevel++;
869 case EEuropeanNumber: case EArabicNumber:
870 if (r->iEmbeddingLevel & 1)
871 r->iEmbeddingLevel++;
873 r->iEmbeddingLevel += 2;
882 void TBidirectionalState::ReorderRuns(TReorderContext& aContext)
887 // Find the highest level and lowest odd level.
891 int lowest_odd = EMaxLevel;
893 for (i = 0, r = aContext.iRunInfo; i < aContext.iRuns; i++, r++)
895 level = r->iEmbeddingLevel;
898 if ((level & 1) && level < lowest_odd)
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--)
906 r = aContext.iRunInfo;
907 while (run_start < aContext.iRuns)
909 while (run_start < aContext.iRuns && r->iEmbeddingLevel < level)
914 int run_end = run_start;
915 while (run_end < aContext.iRuns && r->iEmbeddingLevel >= level)
917 r->iDirection = !r->iDirection;
921 TRunInfo* p = &aContext.iRunInfo[run_start];
922 TRunInfo* q = &aContext.iRunInfo[run_end - 1];
937 TBidirectionalState::TCategory TBidirectionalState::Push(TCategory aStartCategory)
938 /** @internalComponent */
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;
947 if (EMaxExplicitLevel < newLevel)
953 return EBoundaryNeutral;
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;
963 return rightToLeftFlag? ERightToLeft : ELeftToRight;
967 TBidirectionalState::TCategory TBidirectionalState::Pop()
968 /** @internalComponent */
970 __ASSERT_DEBUG(0 < iStackLevel, User::Invariant());
971 TInt level = State().iEmbeddingLevel;
974 else if (iPushesBeyond61 != 0)
976 else if (level == 61)
978 else if (iPushesBeyond60)
982 return (level & 1)? ERightToLeft : ELeftToRight;
986 EXPORT_C void TBidirectionalState::Reset()
987 /** Sets the object to its default 'start of paragraph' state. */
992 iStack[0].iEmbeddingLevel = 0;
993 iStack[0].iOverrideState = ENoOverrideState;
994 iStack[0].iStartCategory = EOtherNeutral;
995 iPreviousCategory = ELeftToRight;
996 iPreviousStrongCategory = ELeftToRight;
1000 EXPORT_C TBool TBidirectionalState::IsDefault() const
1001 /** Returns Gets the default 'start of paragraph' state.
1003 @return ETrue if the object is in its default 'start of paragraph' state. */
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;
1014 EXPORT_C TBool TBidirectionalState::operator==(const TBidirectionalState& aState) const
1015 /** Return ETrue if two bidirectional states are identical.
1017 @param aState A bidirectional state.
1018 @return ETrue if two bidirectional states are identical. */
1020 if (iPreviousCategory != aState.iPreviousCategory ||
1021 iPreviousStrongCategory != aState.iPreviousStrongCategory ||
1022 iStackLevel != aState.iStackLevel)
1024 const TStackItem* p = iStack;
1025 const TStackItem* q = aState.iStack;
1026 for (int i = 0; i <= iStackLevel; i++, p++, q++)
1028 if (p->iStartCategory != q->iStartCategory ||
1029 p->iOverrideState != q->iOverrideState ||
1030 p->iEmbeddingLevel != q->iEmbeddingLevel)
1037 TInt TBidirectionalState::CatToNumber(TInt aCat)
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.
1052 if ((aCat & mask) == 0)
1062 EXPORT_C void TBidirectionalState::ExternalizeL(RWriteStream& aDest)
1063 /** Serializes a bidirectional state to an output stream.
1065 @param aDest An output stream. */
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));
1072 // Write the number of stack levels
1073 aDest.WriteInt8L(iStackLevel);
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.
1079 for (int i = 0; i <= iStackLevel; i++)
1081 TInt x = CatToNumber(iStack[i].iStartCategory);
1082 if (iStack[i].iOverrideState == ELeftToRightOverrideState)
1084 x |= (KBidirectionalStateOverrideStreamValueLeftToRight << 5);
1086 else if (iStack[i].iOverrideState == ERightToLeftOverrideState)
1088 x |= (KBidirectionalStateOverrideStreamValueRightToLeft << 5);
1090 x |= ((TInt)iStack[i].iEmbeddingLevel << 7);
1091 aDest.WriteInt16L(x);
1094 TInt level = State().iEmbeddingLevel;
1097 aDest.WriteInt8L(iPushesBeyond60);
1098 aDest.WriteInt8L(iPushesBeyond61);
1103 EXPORT_C void TBidirectionalState::InternalizeL(RReadStream& aSource)
1104 /** Reads a bidirectional state from an input stream, translating it from its serialized
1107 @param aSource A source stream. */
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);
1115 // Read the number of stack levels.
1116 iStackLevel = aSource.ReadInt8L();
1118 // Read the stack levels.
1119 for (int i = 0; i <= iStackLevel; i++)
1121 x = aSource.ReadInt16L();
1122 iStack[i].iStartCategory = (TCategory)(1 << (x & 0x1F));
1123 switch ((x >> 5) & 3)
1125 case KBidirectionalStateOverrideStreamValueLeftToRight:
1126 iStack[i].iOverrideState = ELeftToRightOverrideState;
1128 case KBidirectionalStateOverrideStreamValueRightToLeft:
1129 iStack[i].iOverrideState = ERightToLeftOverrideState;
1131 case KBidirectionalStateOverrideStreamValueNone:
1132 default: iStack[i].iOverrideState = ENoOverrideState; break;
1134 iStack[i].iEmbeddingLevel = (TUint8)(x >> 7);
1137 TInt level = State().iEmbeddingLevel;
1140 iPushesBeyond60 = aSource.ReadInt8L();
1141 iPushesBeyond61 = aSource.ReadInt8L();
1145 iPushesBeyond60 = 0;
1146 iPushesBeyond61 = 0;
1151 TBidirectionalState::TBidirectionalState(TChar::TBdCategory aPrevCat,
1152 TChar::TBdCategory aPrevStrongCat,
1153 TBool aParRightToLeft)
1155 Constructor suitable for test code.
1160 iPreviousCategory = CharToBdCat(aPrevCat);
1161 iPreviousStrongCategory = CharToBdCat(aPrevStrongCat);
1162 iStack[0].iEmbeddingLevel = (TUint8) (aParRightToLeft? 1 : 0);