sl@0: // Copyright (c) 2002-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include "BidiCompact.h" sl@0: #include "BidiCopy.h" sl@0: #include sl@0: sl@0: static const TInt KZeroWidthJoiner = 0x200D; sl@0: // This gets round the compiler warning about converting sl@0: // EFRightToLeft to unsigned long. sl@0: inline TUint FRightToLeft() { return static_cast(TRunInfoCompact::EFRightToLeft); } sl@0: sl@0: /** sl@0: Constructs a run description without considering optimisations based sl@0: on the text itself. sl@0: @param aStart Index of the start of the run. sl@0: @param aLength Length of the run. sl@0: @param aReverse ETrue if the run is right-to-left. sl@0: @internalTechnology sl@0: */ sl@0: TRunInfoCompact::TRunInfoCompact(TInt aStart, TInt aLength, sl@0: TBool aReverse) sl@0: : iStart(aStart), iLengthAndType(aLength) sl@0: { sl@0: if (aReverse) sl@0: iLengthAndType |= FRightToLeft(); sl@0: } sl@0: sl@0: /** sl@0: Constructs a run description. sl@0: sl@0: @param aStart Index of the start of the run. sl@0: @param aLength Length of the run. sl@0: @param aReverse ETrue if the run is right-to-left. sl@0: @param aText The text that this run refers to (starting at index 0, not sl@0: the start of the run). This is required only to determine if optimisations sl@0: to the re-ordering are possible. sl@0: @internalTechnology sl@0: */ sl@0: TRunInfoCompact::TRunInfoCompact(TInt aStart, TInt aLength, sl@0: TBool aReverse, const TText* aText) sl@0: : iStart(aStart), iLengthAndType(aLength) sl@0: { sl@0: ASSERT(0 <= aLength); sl@0: ASSERT(aLength < 0x10000000); sl@0: ASSERT(0 <= aStart); sl@0: if (!aReverse) sl@0: return; sl@0: iLengthAndType |= FRightToLeft(); sl@0: TUint32 flags = EFNoPairsNoCombiners | EFNoMirroredCharacters; sl@0: aText += aStart; sl@0: sl@0: for (const TText* end = aText + aLength; aText < end && flags; ++aText) sl@0: { sl@0: TInt code = *aText; sl@0: if ((code & 0xF800) == 0xD800) sl@0: { sl@0: flags &= ~EFNoPairsNoCombiners; sl@0: if ((code & 0xFC00) == 0xDC00 sl@0: && aText + 1 < end sl@0: && (aText[1] & 0xFC00) == 0xD800) sl@0: { sl@0: code = (aText[1] << 10) + (code & 0x3FF) sl@0: + (0x10000 - 0xD800*0x400); sl@0: ++aText; sl@0: } sl@0: } sl@0: TChar c = code; sl@0: if (c.GetCombiningClass() != 0) sl@0: flags &= ~EFNoPairsNoCombiners; sl@0: if (BidiCopy::Mirror(code) != code) sl@0: flags &= ~EFNoMirroredCharacters; sl@0: } sl@0: iLengthAndType |= flags; sl@0: } sl@0: sl@0: /** sl@0: Attempts to extend a run. sl@0: sl@0: @param aToBeAdded The run to be merged. sl@0: @return ETrue if extension succeeded, EFalse if not. sl@0: @internalTechnology sl@0: */ sl@0: TBool TRunInfoCompact::AddRun(const TRunInfoCompact& aToBeAdded) sl@0: { sl@0: TInt length = Length(); sl@0: if (length == 0) sl@0: { sl@0: *this = aToBeAdded; sl@0: return ETrue; sl@0: } sl@0: sl@0: // Are both runs in the same direction? sl@0: if ((iLengthAndType ^ aToBeAdded.iLengthAndType) & FRightToLeft()) sl@0: return EFalse; sl@0: sl@0: TBool rightToLeft = TypeFlags() & EFRightToLeft; sl@0: TInt end = rightToLeft? sl@0: Start() - Length() : Start() + Length(); sl@0: sl@0: if (end != aToBeAdded.Start()) sl@0: return EFalse; sl@0: sl@0: length += aToBeAdded.Length(); sl@0: sl@0: iLengthAndType = length | (TypeFlags() & aToBeAdded.TypeFlags()); sl@0: sl@0: if (rightToLeft) sl@0: iStart -= aToBeAdded.Length(); sl@0: sl@0: return ETrue; sl@0: } sl@0: sl@0: /** sl@0: Reorders text described by this run according to aContext. Allow 6 extra sl@0: bytes for a truncation. sl@0: @param aDestination Where to write this run of visually-ordered text to. sl@0: @param aContext The source of the text to be ordered. sl@0: @return The first byte not written to: in other words, what aDestination sl@0: should be updated to. sl@0: @internalTechnology sl@0: */ sl@0: TText* TRunInfoCompact::Reorder(TText* aDestination, sl@0: const TRunInfoCompact::TReorderingContext& aContext) const sl@0: { sl@0: TInt start = Start(); sl@0: if (aContext.iEnd < start) sl@0: // does not overlap sl@0: return aDestination; sl@0: TInt end = Start() + Length(); sl@0: if (end <= aContext.iStart) sl@0: // does not overlap sl@0: return aDestination; sl@0: TBool startJoins = EFalse; sl@0: if (start <= aContext.iStart) sl@0: { sl@0: start = aContext.iStart; sl@0: startJoins = aContext.iJoinsAtStart; sl@0: } sl@0: TBool truncated = EFalse; sl@0: TBool endJoins = EFalse; sl@0: if (aContext.iEnd <= end) sl@0: { sl@0: if (aContext.iEnd < end sl@0: && aContext.iTruncation != 0xFFFF) sl@0: truncated = ETrue; sl@0: end = aContext.iEnd; sl@0: endJoins = aContext.iJoinsAtEnd; sl@0: } sl@0: TInt length = end - start; sl@0: if (length == 0 && !truncated) sl@0: return aDestination; sl@0: ASSERT(0 <= length); sl@0: const TText* source = aContext.iSource + start; sl@0: if (TypeFlags() & FRightToLeft()) sl@0: { sl@0: // Right-to-left sl@0: if (truncated) sl@0: aDestination = BidiCopy::OutputTChar(aDestination, aContext.iTruncation); sl@0: if (endJoins) sl@0: *(aDestination++) = KZeroWidthJoiner; sl@0: if (TypeFlags() & EFNoPairsNoCombiners) sl@0: { sl@0: // Simple sl@0: aDestination = TypeFlags() & EFNoMirroredCharacters? sl@0: BidiCopy::CopyBackwards(aDestination, source, length) sl@0: : BidiCopy::CopyBackwardsWithMirroring(aDestination, source, length); sl@0: } sl@0: else sl@0: // Respect groups sl@0: aDestination = BidiCopy::CopyGroupsBackwards(aDestination, source, length); sl@0: if (startJoins) sl@0: *aDestination++ = KZeroWidthJoiner; sl@0: return aDestination; sl@0: } sl@0: // Left-to-right sl@0: if (startJoins) sl@0: *aDestination++ = KZeroWidthJoiner; sl@0: Mem::Copy(aDestination, source, length * sizeof(TText)); sl@0: aDestination += length; sl@0: if (endJoins) sl@0: *aDestination++ = KZeroWidthJoiner; sl@0: if (truncated) sl@0: aDestination = BidiCopy::OutputTChar(aDestination, aContext.iTruncation); sl@0: return aDestination; sl@0: } sl@0: sl@0: /** sl@0: Converts an array of aArraySize TBidirectionalState::TRunInfos into a sl@0: compact form. sl@0: sl@0: @param aBuffer Memory to output to, or null just to find out how large the output sl@0: array will need to be. sl@0: @param aText The text that aRunArray refers to. sl@0: @param aRunArray The array to be converted. sl@0: @param aArraySize The length of aRunArray. sl@0: @return The length of the output array. sl@0: @internalTechnology sl@0: */ sl@0: TInt TRunInfoCompact::Convert(TRunInfoCompact* aBuffer, const TDesC& aText, sl@0: const TBidirectionalState::TRunInfo* aRunArray, TInt aArraySize) sl@0: { sl@0: const TText* text = aText.Ptr(); sl@0: TInt outputSize = 0; sl@0: sl@0: TRunInfoCompact currentRun; sl@0: while (aArraySize) sl@0: { sl@0: TRunInfoCompact newRun(aRunArray->iStart, aRunArray->iLength, sl@0: aRunArray->iDirection, text); sl@0: --aArraySize; sl@0: if (!currentRun.AddRun(newRun)) sl@0: { sl@0: if (aBuffer) sl@0: *aBuffer++ = currentRun; sl@0: ++outputSize; sl@0: currentRun = newRun; sl@0: } sl@0: ++aRunArray; //point to next run sl@0: } sl@0: if (0 < currentRun.Length()) sl@0: { sl@0: if (aBuffer) sl@0: *aBuffer++ = currentRun; sl@0: ++outputSize; sl@0: } sl@0: sl@0: return outputSize; sl@0: } sl@0: sl@0: /** sl@0: Utility tells whether a character will form a join with the previous sl@0: base character. sl@0: sl@0: @param aText The text. sl@0: @param aIndex The index into aText of the character to test. sl@0: @return ETrue if there is a join before the character. sl@0: */ sl@0: TBool TRunInfoCompact::JoinBefore(const TText* aText, TInt aIndex) sl@0: { sl@0: TInt charUnderTest = aText[aIndex]; sl@0: if (!CFont::CharactersJoin(charUnderTest, KZeroWidthJoiner)) sl@0: // Character does not join with anything, so we sl@0: // will not do any more work. sl@0: return EFalse; sl@0: while (aIndex != 0) sl@0: { sl@0: --aIndex; sl@0: TInt c = aText[aIndex]; sl@0: // If it is an Arabic point, we will skip it. sl@0: if (0x64B <= c && c < 0x671 sl@0: && !(0x656 <= c && c < 0x670)) sl@0: continue; sl@0: return CFont::CharactersJoin(charUnderTest, c); sl@0: } sl@0: return EFalse; sl@0: }