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 <e32svr.h>
sl@0: #include "BidiTextImp.h"
sl@0: #include "BidiCopy.h"
sl@0: #include "BidiCompact.h"
sl@0: #include <bidi.h>
sl@0: #include <e32base.h>
sl@0: //#include <textbase.h>
sl@0: #include <gdi.h>
sl@0: #include <linebreak.h>
sl@0: 
sl@0: _LIT(KBidiPanicCategory,"Bidi");
sl@0: static const TInt KLineSeparator = 0x2028;
sl@0: static const TInt KParagraphSeparator = 0x2029;
sl@0: static const TInt KCodeCR = 0x000D;
sl@0: static const TInt KCodeLF = 0x000A;
sl@0: static const TInt KCodeEllipsis = 0x2026;
sl@0: 
sl@0: void DeleteTRunInfo(void* aRunInfo)
sl@0: 	{
sl@0: 	delete[] reinterpret_cast<TBidirectionalState::TRunInfo*>(aRunInfo);
sl@0: 	}
sl@0: 
sl@0: void BidiPanic(TInt aError)
sl@0: 	{
sl@0: 	User::Panic(KBidiPanicCategory, aError);
sl@0: 	}
sl@0: 
sl@0: 
sl@0: // One page-full of TRunInfos
sl@0: const TInt KMaxRunInfoArraySize = 4*1024 / sizeof(TBidirectionalState::TRunInfo);
sl@0: const TInt KBidiTlsHandle = 0x101F633D;
sl@0: 
sl@0: 
sl@0: /*
sl@0: * Ref-counted TLS for the shared run info array used by the SetText() method.
sl@0: */
sl@0: NONSHARABLE_CLASS(CBidiTextTls) : public CObject
sl@0: 	{
sl@0: public:	
sl@0: 	static CBidiTextTls* NewL();
sl@0: 	static CBidiTextTls* GetTls();
sl@0: 	~CBidiTextTls();
sl@0: 	inline TUint MaxArraySize();
sl@0: 	inline TBidirectionalState::TRunInfo* RunArray();
sl@0: 
sl@0: private:
sl@0: 	CBidiTextTls();
sl@0: 	void ConstructL(TUint aMaxArraySize);
sl@0: 
sl@0: private:
sl@0: 	TBidirectionalState::TRunInfo* iRunInfoArray;
sl@0: 	TUint iMaxArraySize;	
sl@0: 	};
sl@0: 
sl@0: 
sl@0: 
sl@0: CBidiTextTls::CBidiTextTls()
sl@0: :	iRunInfoArray(NULL),
sl@0:  	iMaxArraySize(0)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: 
sl@0: CBidiTextTls::~CBidiTextTls()
sl@0: 	{
sl@0: 	UserSvr::DllFreeTls(KBidiTlsHandle);
sl@0: 	
sl@0: 	if (iRunInfoArray)
sl@0: 		{
sl@0: 		delete [] iRunInfoArray;	
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: 
sl@0: TUint CBidiTextTls::MaxArraySize()
sl@0: 	{
sl@0: 	return iMaxArraySize;
sl@0: 	}
sl@0: 	
sl@0: 
sl@0: TBidirectionalState::TRunInfo* CBidiTextTls::RunArray()
sl@0: 	{
sl@0: 	return iRunInfoArray;
sl@0: 	}
sl@0: 	
sl@0: 	
sl@0: /**
sl@0:  * Helper function provided to simplify reading the TLS data and improve the
sl@0:  * readability of the code 
sl@0:  */
sl@0: CBidiTextTls* CBidiTextTls::GetTls()
sl@0: 	{
sl@0: 	return reinterpret_cast<CBidiTextTls*>(UserSvr::DllTls(KBidiTlsHandle));
sl@0: 	}
sl@0: 
sl@0: 
sl@0: CBidiTextTls* CBidiTextTls::NewL()
sl@0: 	{
sl@0: 	CBidiTextTls* self = new (ELeave) CBidiTextTls;
sl@0: 	CleanupClosePushL(*self);
sl@0: 	self->ConstructL(KMaxRunInfoArraySize);
sl@0: 	CleanupStack::Pop(self);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: 
sl@0: void CBidiTextTls::ConstructL(TUint aMaxArraySize)
sl@0: 	{
sl@0: 	iMaxArraySize = aMaxArraySize;
sl@0: 	iRunInfoArray = new (ELeave) TBidirectionalState::TRunInfo[aMaxArraySize];
sl@0: 	User::LeaveIfError(UserSvr::DllSetTls(KBidiTlsHandle, this));
sl@0: 	}
sl@0: 
sl@0: 
sl@0: EXPORT_C RRunInfoArray::RRunInfoArray() 
sl@0: :	iTls(NULL)
sl@0: 	{
sl@0: 	}
sl@0: 	
sl@0: 
sl@0: /** 
sl@0: Creates the run array if necessary and increases the reference count on it.
sl@0: RRunInfoArray::OpenL() must be called prior to calling TBidiText::SetText().
sl@0:  */
sl@0: EXPORT_C void RRunInfoArray::OpenL()
sl@0: 	{
sl@0: 	if(!iTls)
sl@0: 		{
sl@0: 		iTls = CBidiTextTls::GetTls();
sl@0: 		if(iTls)
sl@0: 			{
sl@0: 			iTls->Open();	// Increase ref count	
sl@0: 			}
sl@0: 		else	
sl@0: 			{
sl@0: 			iTls = CBidiTextTls::NewL();
sl@0: 			}
sl@0: 		}	
sl@0: 	}
sl@0: 	
sl@0: 	
sl@0: /**
sl@0: Decreases the reference count on the run array. The run array will be deleted
sl@0: if the reference count reaches zero. The client application must ensure that
sl@0: there is a matching call to Close() for every call to OpenL() or memory will
sl@0: be leaked.
sl@0:  */
sl@0: EXPORT_C void RRunInfoArray::Close()
sl@0: 	{
sl@0: 	if(iTls)
sl@0: 		{
sl@0: 		iTls->Close();	
sl@0: 		iTls = NULL;
sl@0: 		}
sl@0: 	}
sl@0: 	
sl@0: 
sl@0: /**
sl@0: @return Pointer to the run array buffer
sl@0: @internalComponent
sl@0:  */
sl@0: TBidirectionalState::TRunInfo* RRunInfoArray::RunArray() const
sl@0: 	{
sl@0: 	return iTls ? iTls->RunArray() : NULL;
sl@0: 	}
sl@0: 	
sl@0: 
sl@0: /**
sl@0: @return Number of bytes needed to hold the TBidiTextImp member variables, plus the 
sl@0: 		text data allocated off the end of the TBidiTextImp object.
sl@0: @internalComponent
sl@0: */
sl@0: TInt TBidiTextImp::RequiredBytes(TInt aLength, TInt aMaxLines, TInt aBdRunArraySize)		
sl@0: 	{
sl@0: 	// size of TBidiTextImp class
sl@0: 	TInt bytes = TBidiTextImp::AlignedSizeOf();
sl@0: 	// size of text for logical and visual orderings.
sl@0: 	// This includes aMaxLines - 1 line breaks with surrounding
sl@0: 	// zero-width joiners, and a truncation character (possibly
sl@0: 	// a surrogate pair) plus a zero-width joiner.
sl@0: 	bytes += sizeof(TText) * (aLength * 2 + aMaxLines * 3);
sl@0: 	// size of line length array
sl@0: 	bytes += sizeof(TInt16*) * aMaxLines;
sl@0: 	// alignment
sl@0: 	bytes = (bytes + 3) & 0xFFFFFFFC;
sl@0: 	// array of TRunInfoCompact
sl@0: 	bytes += sizeof(TRunInfoCompact) * aBdRunArraySize;
sl@0: 	
sl@0: 	return bytes;
sl@0: 	}
sl@0: 
sl@0: 	
sl@0: /**
sl@0: @return A TBidiTextImp object of sufficient size to hold the amount of text data specified
sl@0: 		by the the arguments.
sl@0: @param aLength The number of characters in the text.
sl@0: @param aMaxLines The maximum number of lines 
sl@0: @param aBdRunArraySize The size of the bidi run array. 
sl@0: @internalComponent
sl@0: */
sl@0: TBidiTextImp* TBidiTextImp::NewL(TInt aLength, TInt aMaxLines, TInt aBdRunArraySize)
sl@0: 	{
sl@0: 	const TInt bytes = RequiredBytes(aLength, aMaxLines, aBdRunArraySize);
sl@0: 	TInt8* mem = static_cast<TInt8*>(User::AllocL(bytes));
sl@0: 	TBidiTextImp* me = reinterpret_cast<TBidiTextImp*>(mem);
sl@0: 
sl@0: 	me->iTextLengthAndFlags = aLength;
sl@0: 	me->iVisualOrderedTextLength = -1;
sl@0: 	me->iWrappingWidth = 0xFFFF;
sl@0: 	me->iBidiRunArrayLength = aBdRunArraySize;
sl@0: 	me->iLines = static_cast<TUint8>(aMaxLines);
sl@0: 	me->iTruncationCharPlane = 0;
sl@0: 	me->iTruncationChar16 = KCodeEllipsis;
sl@0: 	me->SetAllocatedTextDataBytes(bytes - TBidiTextImp::AlignedSizeOf() - (sizeof(TRunInfoCompact) * aBdRunArraySize));
sl@0: 	return me;
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @return Position of logically-ordered text portion of the heap cell.
sl@0: @internalComponent
sl@0: */
sl@0: TText* TBidiTextImp::LogicalText()
sl@0: 	{
sl@0: 	return reinterpret_cast<TText*>(
sl@0: 		reinterpret_cast<TInt8*>(this)
sl@0: 		+ TBidiTextImp::AlignedSizeOf());
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @return Position of visually-ordered text portion of the heap cell.
sl@0: @internalComponent
sl@0: */
sl@0: TText* TBidiTextImp::VisualText()
sl@0: 	{
sl@0: 	TInt bytes = TBidiTextImp::AlignedSizeOf();
sl@0: 	bytes += sizeof(TText) * TextLength();
sl@0: 	return reinterpret_cast<TText*>(
sl@0: 		reinterpret_cast<TInt8*>(this) + bytes);
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: Returns a pointer to the array containing the width in pixels of each and every line.
sl@0: @return Position of the array of line widths portion of the heap cell.
sl@0: @internalComponent
sl@0: */
sl@0: TInt16* TBidiTextImp::LineWidthArray()
sl@0: 	{
sl@0: 	TInt bytes = TBidiTextImp::AlignedSizeOf();
sl@0: 	bytes += sizeof(TText) * (TextLength() * 2 + iLines + 2);
sl@0: 	return reinterpret_cast<TInt16*>(
sl@0: 		reinterpret_cast<TInt8*>(this) + bytes);
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @return Position of the array of runs portion of the heap cell.
sl@0: @internalComponent
sl@0: */
sl@0: TRunInfoCompact* TBidiTextImp::BidiRunArray()
sl@0: 	{
sl@0: 	TInt bytes = TBidiTextImp::AlignedSizeOf();
sl@0: 	bytes += sizeof(TText) * (TextLength() * 2 + iLines + 2);
sl@0: 	bytes += sizeof(TInt16*) * iLines;
sl@0: 	bytes = (bytes + 3) & 0xFFFFFFFC;
sl@0: 	return reinterpret_cast<TRunInfoCompact*>(
sl@0: 		reinterpret_cast<TInt8*>(this) + bytes);
sl@0: 	}
sl@0: 
sl@0: /** 
sl@0: Report if the current character is an explicit line break. Both
sl@0: aText[0] and aText[1] must be part of the string.
sl@0: @return Size of line break.
sl@0: @internalComponent 
sl@0: */
sl@0: TInt SizeLineBreak(const TText* aText, const TText* aTextEnd)
sl@0: 	{
sl@0: 	if (aText == aTextEnd )
sl@0: 		return 0;
sl@0: 		
sl@0: 	if (*aText == KLineSeparator || *aText == KParagraphSeparator
sl@0: 		|| *aText == KCodeLF)
sl@0: 		return 1;
sl@0: 	if (aText[0] == KCodeCR)
sl@0: 		{
sl@0: 		// first check for space before checking for LF
sl@0: 		if (aText+1 < aTextEnd )
sl@0: 			{
sl@0: 			return aText[1] == KCodeLF? 2 : 1;
sl@0: 			}
sl@0: 		else
sl@0: 			return 1;
sl@0: 		}
sl@0: 	return 0;
sl@0: 	}
sl@0: 
sl@0: /** 
sl@0: Find the next line break character.
sl@0: @internalComponent 
sl@0: */
sl@0: const TText* FindEndOfThisLine(const TText* aStart, const TText* aEnd)
sl@0: 	{
sl@0: 	while (aStart != aEnd && *aStart != KLineSeparator
sl@0: 		&& *aStart != KParagraphSeparator && *aStart != KCodeLF
sl@0: 		&& *aStart != KCodeCR)
sl@0: 		++aStart;
sl@0: 	return aStart;
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: Count number of lines in text.
sl@0: @internalComponent
sl@0: */
sl@0: TInt NumberOfLines(const TText* aStart, const TText* aEnd)
sl@0: 	{
sl@0: 	TInt num = 0;
sl@0: 	while (aStart != aEnd)
sl@0: 		{
sl@0: 		aStart = FindEndOfThisLine(aStart, aEnd);
sl@0: 		aStart += SizeLineBreak(aStart, aEnd);
sl@0: 		++num;
sl@0: 		}
sl@0: 	return num;
sl@0: 	}
sl@0: 
sl@0: /** Returns the directionality of a given language.
sl@0: @param aLanguage Language.
sl@0: @return The directionality of the given language. */
sl@0: EXPORT_C TBidiText::TDirectionality TBidiText::ScriptDirectionality(
sl@0: 	TLanguage aLanguage)
sl@0: 	{
sl@0: 	const TUint32 DirectionalityBitmap[] =
sl@0: 		{
sl@0: 		0,
sl@0: 		// Arabic, Farsi, Hebrew
sl@0: 		0x02040020,
sl@0: 		// Urdu
sl@0: 		0x40000000
sl@0: 		};
sl@0: 	TUint index = aLanguage;
sl@0: 	if (index < sizeof(DirectionalityBitmap) * 8)
sl@0: 		{
sl@0: 		index >>= 5;
sl@0: 		TInt bit = aLanguage & 31;
sl@0: 		return (DirectionalityBitmap[index] >> bit) & 1?
sl@0: 			ERightToLeft : ELeftToRight;
sl@0: 		}
sl@0: 	return ELeftToRight;
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /** Reports the implicit directionality of a piece of text.
sl@0: 
sl@0: @param aText The text to be examined. 
sl@0: @param aFound If non-null, returns ETrue if there were any strongly directional 
sl@0: characters and EFalse if there were none. If a piece of text is spread over 
sl@0: several descriptors, They need to be queried in sequence until one returns 
sl@0: ETrue in aFound. 
sl@0: @return The directionality implicit in aText. 131 */ 
sl@0: EXPORT_C TBidiText::TDirectionality TBidiText::TextDirectionality(
sl@0: 	const TDesC& aText, TBool* aFound)
sl@0: 	{
sl@0: 	return BidiCopy::ImplicitDirectionalityIsRightToLeft(
sl@0: 		aText.Ptr(), aText.Length(), aFound)?
sl@0: 		ERightToLeft : ELeftToRight;
sl@0: 	}
sl@0: 
sl@0: /** Creates a bidirectional text object with directionality determined by
sl@0: aDirectionality. Use this for text that has come from user input.
sl@0: 
sl@0: @param aText The text in logical order.
sl@0: @param aMaxLines
sl@0: 	The maximum number of lines that this text will need to be split into. Must
sl@0: 	be at least 1, but should not be too large, as each potential line takes an
sl@0: 	extra 8 bytes of memory.
sl@0: @param aDirectionality Direction to use.
sl@0: @return The newly constructed object.
sl@0:  */
sl@0: EXPORT_C TBidiText* TBidiText::NewL(const TDesC& aText, TInt aMaxLines,
sl@0: 	TBidiText::TDirectionality aDirectionality)
sl@0: 	{
sl@0: 	__ASSERT_ALWAYS(0 < aMaxLines, BidiPanic(EBidiPanic_InvalidMaxline));
sl@0: 	const TText* text = aText.Ptr();
sl@0: 	const TInt length = aText.Length();
sl@0: 	TInt linesInOriginalText = NumberOfLines(text, text + length);
sl@0: 	if (aMaxLines < linesInOriginalText)
sl@0: 		aMaxLines = linesInOriginalText;
sl@0: 
sl@0: 	const TInt arraySize = TBidirectionalState::GenerateBdRunArray(text, length, 0, 0);
sl@0: 	TBidirectionalState::TRunInfo* runInfoArray = new(ELeave) TBidirectionalState::TRunInfo[arraySize];
sl@0: 	TCleanupItem ci(DeleteTRunInfo, runInfoArray);
sl@0: 	CleanupStack::PushL(ci);
sl@0: 	TBidirectionalState::GenerateBdRunArray(text, length, runInfoArray, arraySize);
sl@0: 	TBidirectionalState state;
sl@0: 	state.ReorderLine(runInfoArray, arraySize, ETrue, ETrue, aDirectionality,
sl@0: 		TChar::EOtherNeutral, TChar::EOtherNeutral);
sl@0: 	TInt compactArraySize = TRunInfoCompact::Convert(0, aText, runInfoArray, arraySize);
sl@0: 
sl@0: 	// size of TBidiTextImp class
sl@0: 	TBidiTextImp* me = TBidiTextImp::NewL(length, aMaxLines, compactArraySize);
sl@0: 	me->SetRightToLeftDirectionality(aDirectionality != ELeftToRight);
sl@0: 		
sl@0: 	TRunInfoCompact::Convert(me->BidiRunArray(), aText, runInfoArray, arraySize);
sl@0: 	CleanupStack::PopAndDestroy(runInfoArray);
sl@0: 
sl@0: 	Mem::Copy(me->LogicalText(), text, length * sizeof(TText));
sl@0: 	return me;
sl@0: 	}
sl@0: 
sl@0: /** Creates a bidirectional text object with directionality determined by the text 
sl@0: itself. Use this for text that has been obtained from a resource file. 
sl@0: 
sl@0: @param aText The text in logical order.
sl@0: @param aMaxLines The maximum number of lines that this text will need to be 
sl@0: split into. Must be at least 1, but should not be too large, as each potential 
sl@0: line takes an extra 8 bytes of memory.
sl@0: @return The newly constructed object. */
sl@0: EXPORT_C TBidiText* TBidiText::NewL(const TDesC& aText, TInt aMaxLines)
sl@0: 	{
sl@0: 	return NewL(aText, aMaxLines, TextDirectionality(aText));
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /** Creates a bidirectional text object with enough room for up to aReservedMaxLength
sl@0: number of characters. The number of characters that will actually fit (when calling 
sl@0: SetText()) might be slightly less than aReservedMaxLength, as each change between a 
sl@0: left-to-right and a right-to-left sub-string (and the other way around) needs about
sl@0: two characters worth of memory.
sl@0: 
sl@0: @param aReservedMaxLength The maximum number of characters.
sl@0: @param aMaxLines The maximum number of lines that this text will need to be 
sl@0: split into. Must be at least 1, but should not be too large, as each potential 
sl@0: line takes an extra 8 bytes of memory.
sl@0: @return The newly constructed object. */
sl@0: EXPORT_C TBidiText* TBidiText::NewL(TInt aReservedMaxLength, TInt aMaxLines)
sl@0: 	{
sl@0: 	__ASSERT_ALWAYS(0 < aReservedMaxLength, BidiPanic(EBidiPanic_InvalidReservedMaxLength));
sl@0: 	__ASSERT_ALWAYS(0 < aMaxLines,          BidiPanic(EBidiPanic_InvalidMaxline));
sl@0: 	
sl@0: 	const TInt compactArraySize = 1;	// Always at least one needed
sl@0: 	
sl@0: 	TBidiTextImp* me = TBidiTextImp::NewL(aReservedMaxLength, aMaxLines, compactArraySize);
sl@0: 	me->SetTextLength(0);	// no text yet, just reserved memory
sl@0: 	return me;
sl@0: 	}
sl@0: 
sl@0: /** Sets the text of the bidirectional text object with directionality determined 
sl@0: by the text itself. Use this for text that has been obtained from a resource file. 
sl@0: 
sl@0: @param aText The text in logical order.
sl@0: @return The number of characters that didn't fit in the available buffer.
sl@0: */
sl@0: EXPORT_C TInt TBidiText::SetText(const TDesC& aText, RRunInfoArray& aRunInfoArray)
sl@0: 	{
sl@0: 	return SetText(aText, TextDirectionality(aText), aRunInfoArray);
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /** Sets the text of the bidirectional text with directionality determined by
sl@0: aDirectionality. Use this for text that has come from user input.
sl@0: 
sl@0: @param aText The text in logical order.
sl@0: @param aDirectionality Direction to use.
sl@0: @return The number of characters that didn't fit in the available buffer.
sl@0: @panic Bidi EBidiPanic_RunArrayNull The call to RRunInfoArray::OpenL() has not
sl@0: been made prior to this call to TBidiText::SetText()
sl@0: */
sl@0: EXPORT_C TInt TBidiText::SetText(const TDesC& aText,
sl@0: 								 TDirectionality aDirectionality, 
sl@0: 								 RRunInfoArray& aRunInfoArray)
sl@0: 	{
sl@0: 	TBidirectionalState::TRunInfo* const runArray = aRunInfoArray.RunArray();
sl@0: 	__ASSERT_ALWAYS(runArray, BidiPanic(EBidiPanic_RunArrayNull));
sl@0: 	
sl@0: 	TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	const TInt maxLines = me->iLines;	
sl@0: 	const TText* text = aText.Ptr();
sl@0: 	TInt length = aText.Length();
sl@0: 	
sl@0: 	TInt requiredArraySize = TBidirectionalState::GenerateBdRunArray(text, length, 0, 0);
sl@0: 	const TInt actualArraySize = aRunInfoArray.iTls->MaxArraySize();
sl@0: 		
sl@0: 	if (requiredArraySize > actualArraySize)
sl@0: 		{
sl@0: 		// Handle the case where we do not have enough space in the run array
sl@0: 		// to cope with the input text. The text will be truncated to ensure
sl@0: 		// we don't overrun the buffer and the number of excess characters 
sl@0: 		// returned as a negative number.
sl@0: 		requiredArraySize = actualArraySize;
sl@0: 		TBidirectionalState::GenerateBdRunArray(text, length, runArray, requiredArraySize);
sl@0: 		
sl@0: 		length = 0;
sl@0: 		for (TInt index = 0; index < requiredArraySize; index++)
sl@0: 			{
sl@0: 			length += runArray[index].iLength;
sl@0: 			}
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		TBidirectionalState::GenerateBdRunArray(text, length, runArray, requiredArraySize);
sl@0: 		}
sl@0: 	
sl@0: 	
sl@0: 
sl@0: 	TBidirectionalState state;
sl@0: 	state.ReorderLine(runArray, 
sl@0: 					  requiredArraySize, 
sl@0: 					  ETrue, 
sl@0: 					  ETrue, 
sl@0: 					  aDirectionality, 
sl@0: 					  TChar::EOtherNeutral, 
sl@0: 					  TChar::EOtherNeutral);
sl@0: 	const TInt compactArraySize = TRunInfoCompact::Convert(0, 
sl@0: 														   aText, 
sl@0: 														   runArray, 
sl@0: 														   requiredArraySize);
sl@0: 
sl@0: 	// Calculate number of bytes needed to keep text data
sl@0: 	TInt requiredBytes = sizeof(TText) * (length * 2 + maxLines * 3); // size of text for logical & visual orderings.
sl@0: 	requiredBytes += sizeof(TInt16*) * maxLines;					  // size of line length array
sl@0: 	requiredBytes = (requiredBytes + 3) & 0xFFFFFFFC;				  // alignment
sl@0: 	
sl@0: 	TInt textLength = length;
sl@0: 	const TInt excessData = Max(0, requiredBytes - me->AllocatedTextDataBytes());
sl@0: 	TInt excessChars = 0;
sl@0: 	if(excessData)
sl@0: 		{
sl@0: 		// Calculate how much text data that can be fitted into the available bytes, 
sl@0: 		// given the bytes needed for run array data
sl@0: 		excessChars = excessData / (sizeof(TText) * 2);
sl@0: 		textLength -= excessChars;
sl@0: 		}
sl@0: 	else if (aText.Length() > length)
sl@0: 		{
sl@0: 		excessChars = aText.Length() - length;
sl@0: 		}
sl@0: 	
sl@0: 	me->SetTextLength(textLength);
sl@0: 	me->SetRightToLeftDirectionality(aDirectionality != ELeftToRight);			
sl@0: 	me->iVisualOrderedTextLength = -1;
sl@0: 	me->iBidiRunArrayLength = static_cast<TUint16>(compactArraySize);
sl@0: 
sl@0: 	TRunInfoCompact::Convert(me->BidiRunArray(), aText, runArray, requiredArraySize);
sl@0: 	Mem::Copy(me->LogicalText(), text, textLength * sizeof(TText));
sl@0: 
sl@0: 	return excessChars;
sl@0: 	}
sl@0: 
sl@0: /** Sets the character that will be added at the end of the text if the whole text 
sl@0: cannot fit into the space specified. 
sl@0: 
sl@0: @param aTruncateWith The truncation char. */
sl@0: EXPORT_C void TBidiText::SetTruncationChar(TChar aTruncateWith)
sl@0: 	{
sl@0: 	TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	me->iTruncationCharPlane = static_cast<TUint8>(aTruncateWith >> 16);
sl@0: 	me->iTruncationChar16 = static_cast<TUint16>(aTruncateWith);
sl@0: 	}
sl@0: 
sl@0: TInt RemoveTrailingSpaces(const MLineBreaker* aBreaker,
sl@0: 	const TText* aInput, TInt aMinPos, TInt aEndPos)
sl@0: 	{
sl@0: 	// Ignore space characters at the end of the line.
sl@0: 	// Don't bother to ignore spaces made of surrogate pairs:
sl@0: 	// more processing than it's worth.
sl@0: 	TUint dummy1, dummy2;
sl@0: 	while (aEndPos != aMinPos && MLineBreaker::ESpLineBreakClass
sl@0: 		== aBreaker->LineBreakClass(aInput[aEndPos - 1], dummy1, dummy2))
sl@0: 		{
sl@0: 		--aEndPos;
sl@0: 		}
sl@0: 	return aEndPos;
sl@0: 	}
sl@0: 
sl@0: /** Prepares the visually-ordered text according to the wrapping width and font
sl@0: specified. Text cannot be drawn until this has been done.
sl@0: @param aWrappingWidth
sl@0: 	The maximum width of the text in pixels. Note that this distance should be
sl@0: 	slightly less than the available width to allow for characters such as "W"
sl@0: 	which can have side-bearings that leak into the margins.
sl@0: @param aFont The font that will provide the character metrics.
sl@0: @param aBreaker
sl@0: 	An object for breaking the lines. May be NULL for default behaviour.
sl@0: @param aMaxLines
sl@0: 	Number of lines to restrict wrapping to. The truncation character will be
sl@0: 	used if the text is too long for this number of lines. The number of lines
sl@0: 	wrapped to may not be greater than the figure passed to NewL.
sl@0: */
sl@0: EXPORT_C void TBidiText::WrapText(TInt aWrappingWidth, const CFont& aFont,
sl@0: 	const MLineBreaker* aBreaker, TInt aMaxLines)
sl@0: 	{
sl@0: 	TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	me->iWrappingWidth = aWrappingWidth;
sl@0: 	
sl@0: 	TInt16* lineWidths = me->LineWidthArray();
sl@0: 	TText* output = me->VisualText();
sl@0: 	
sl@0: 	TInt numLines = 0;
sl@0: 	DoWrapText(aWrappingWidth, aFont, aBreaker, aMaxLines, output, numLines, lineWidths);	
sl@0: 	me->iVisualOrderedTextLength = output - me->VisualText();
sl@0: 	}
sl@0: 
sl@0: /** Calculate the minimum size needed to draw the current text, given the specified
sl@0: wrapping width, font, and line gap. Calling this method will not rewrap the object's
sl@0: text.
sl@0: 
sl@0: @param aWrappingWidth
sl@0: 	The maximum width of the text in pixels. Note that this distance should be
sl@0: 	slightly less than the available width to allow for characters such as "W"
sl@0: 	which can have side-bearings that leak into the margins.
sl@0: @param aFont The font that will provide the character metrics.
sl@0: @param aLineGap The number of empty pixels between two lines of text. 
sl@0: 	Note that this is not the same as the baseline spacing, which is the font 
sl@0: 	height plus the line gap.
sl@0: @param aMaxLines
sl@0: 	Number of lines to restrict wrapping to. The truncation character will be
sl@0: 	used if the text is too long for this number of lines. The number of lines
sl@0: 	wrapped to may be greater than the figure passed to NewL, and that figure
sl@0: 	will be used if the number of lines is specified as -1. If 0 (zero) is specified
sl@0: 	no limit is applied.
sl@0: @param aBreaker
sl@0: 	An object for breaking the lines. May be NULL for default behaviour.
sl@0: */
sl@0: EXPORT_C TSize TBidiText::MinimumSize(TInt aWrappingWidth, const CFont& aFont, TInt aLineGap, 
sl@0: 									TInt aMaxLines, const MLineBreaker* aBreaker) const
sl@0: 	{
sl@0: 	__ASSERT_ALWAYS(0  <= aWrappingWidth, BidiPanic(EBidiPanic_InvalidWrappingWidth));
sl@0: 	__ASSERT_ALWAYS(0  <=  aLineGap,       BidiPanic(EBidiPanic_InvalidLineGap));
sl@0: 	__ASSERT_ALWAYS(-1 <= aMaxLines,      BidiPanic(EBidiPanic_InvalidMaxline));
sl@0: 
sl@0: 	TInt numLines = 0;
sl@0: 	TText* output = NULL;
sl@0: 	const TInt minWidth = DoWrapText(aWrappingWidth, 
sl@0: 									 aFont, 
sl@0: 									 aBreaker, 
sl@0: 									 (aMaxLines = 0 ? KMaxTInt : aMaxLines), 
sl@0: 									 output, 
sl@0: 									 numLines, 
sl@0: 									 NULL);
sl@0: 	const TInt minHeight = (aFont.FontMaxHeight() + aLineGap) * numLines - aLineGap; 
sl@0: 	return TSize(minWidth, minHeight);	
sl@0: 	}
sl@0: 
sl@0: 
sl@0: TInt TBidiText::DoWrapText(TInt aWrappingWidth, const CFont& aFont, const MLineBreaker* aBreaker, 
sl@0: 	TInt aMaxLines, TText*& aOutputText, TInt& aNumLines, TInt16* aLineWidthArray) const
sl@0: 	{
sl@0: 	MLineBreaker defaultBreaker;
sl@0: 	if (!aBreaker)
sl@0: 		aBreaker = &defaultBreaker;
sl@0: 	
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	if (me->iLines < aMaxLines)
sl@0: 		aMaxLines = me->iLines;
sl@0: 	
sl@0: 	const TRunInfoCompact* runArray = me->BidiRunArray();	
sl@0: 	const TRunInfoCompact* runArrayEnd = runArray + me->iBidiRunArrayLength;
sl@0: 	
sl@0: 	const TText* input = me->LogicalText();
sl@0: 	const TInt inputLength = me->TextLength();
sl@0: 	TPtrC textDes(input, inputLength);
sl@0: 	const TText* output = me->VisualText();
sl@0: 
sl@0: 	TRunInfoCompact::TReorderingContext context;
sl@0: 	context.iSource = input;
sl@0: 	context.iTruncation = 0xFFFF;
sl@0: 	context.iJoinsAtEnd = EFalse;
sl@0: 	
sl@0: 	TInt start = 0;
sl@0: 	CFont::TMeasureTextInput measureInput;
sl@0: 	measureInput.iMaxAdvance = aWrappingWidth;
sl@0: 	measureInput.iEndInputChar = FindEndOfThisLine(input, input + inputLength) - input;
sl@0: 	CFont::TMeasureTextOutput measureOutput;
sl@0: 	TBool truncated;
sl@0: 
sl@0: 	TInt widestLineWidth = 0;
sl@0: 	TBool bLastLine = EFalse;
sl@0: 	for (aNumLines = 0; aNumLines != aMaxLines && start < inputLength; ++aNumLines)
sl@0: 		{
sl@0: 		truncated=EFalse;
sl@0: 		context.iJoinsAtStart = context.iJoinsAtEnd;
sl@0: 		if(aNumLines != 0 && aOutputText)
sl@0: 			*(aOutputText++) = KLineSeparator;
sl@0: 		
sl@0: 		measureInput.iStartInputChar = start;
sl@0: 		TInt advance = aFont.MeasureText(textDes, &measureInput, &measureOutput);
sl@0: 		TInt breakPos = measureOutput.iChars;
sl@0: 		TInt endOfLine = breakPos;
sl@0: 		// truncationCharWidth is the width of any truncation character on this
sl@0: 		// line only.
sl@0: 		TInt truncationCharWidth = 0;
sl@0: 		if (endOfLine == measureInput.iEndInputChar)
sl@0: 			{
sl@0: 			//handle the dangling lines here
sl@0: 			TInt sizeLineBreak = SizeLineBreak(input + endOfLine, input + inputLength);
sl@0: 			if((measureInput.iEndInputChar < inputLength - sizeLineBreak) && (aNumLines == aMaxLines - 1))
sl@0: 				bLastLine = ETrue;
sl@0: 			}
sl@0: 		else if (aNumLines == aMaxLines - 1)
sl@0: 			{
sl@0: 			bLastLine = ETrue;
sl@0: 			}
sl@0: 		else 
sl@0: 			{ // Not last line, so find a legal line break.
sl@0: 			aBreaker->GetLineBreak(textDes, 
sl@0: 								   start + 1, 
sl@0: 								   measureOutput.iChars, 
sl@0: 								   EFalse, 
sl@0: 								   0, 
sl@0: 								   breakPos, 
sl@0: 								   endOfLine);
sl@0: 			}
sl@0: 
sl@0: 		if (bLastLine)
sl@0: 			{
sl@0: 			// Last line, so re-measure leaving enough room for
sl@0: 			// truncation character.
sl@0: 			context.iTruncation = me->TruncationChar();
sl@0: 			truncationCharWidth = aFont.CharWidthInPixels(context.iTruncation);
sl@0: 			measureInput.iMaxAdvance = aWrappingWidth - truncationCharWidth;
sl@0: 			advance = aFont.MeasureText(textDes, &measureInput, &measureOutput) + truncationCharWidth;
sl@0: 			breakPos = RemoveTrailingSpaces(aBreaker, input, start, measureOutput.iChars);
sl@0: 			truncated=ETrue;
sl@0: 			bLastLine = EFalse;
sl@0: 			}
sl@0: 
sl@0: 		// if the break position has changed, we need to remeasure
sl@0: 		if (breakPos != measureOutput.iChars)
sl@0: 			{
sl@0: 			const TInt oldEnd = measureInput.iEndInputChar;
sl@0: 			measureInput.iEndInputChar = breakPos;
sl@0: 			advance = aFont.MeasureText(textDes, &measureInput, &measureOutput) + truncationCharWidth;
sl@0: 			measureInput.iEndInputChar = oldEnd;
sl@0: 			truncated=ETrue;
sl@0: 			}
sl@0: 
sl@0: 		//width may be greater than advance
sl@0: 		advance = Max(advance,measureOutput.iBounds.Width());
sl@0: 
sl@0: 		if(widestLineWidth < advance)
sl@0: 			widestLineWidth = advance;
sl@0: 		
sl@0: 		if(aLineWidthArray)
sl@0: 			*(aLineWidthArray++) = static_cast<TInt16>(advance);
sl@0: 
sl@0: 		context.iStart = start;
sl@0: 		context.iEnd = breakPos;
sl@0: 		if (truncated)
sl@0: 			{
sl@0: 			context.iJoinsAtEnd = breakPos < inputLength?
sl@0: 				TRunInfoCompact::JoinBefore(input, breakPos) : EFalse;
sl@0: 			}
sl@0: 		else
sl@0: 			{
sl@0: 			context.iJoinsAtEnd = endOfLine < inputLength?
sl@0: 				TRunInfoCompact::JoinBefore(input, endOfLine) : EFalse;
sl@0: 			}
sl@0: 		if (aOutputText)
sl@0: 			{
sl@0: 			for (const TRunInfoCompact* p = runArray; p != runArrayEnd; ++p)
sl@0: 				aOutputText = p->Reorder(aOutputText, context);
sl@0: 			}
sl@0: 		// Set 'start' to the beginning of the next line...
sl@0: 		start = endOfLine;
sl@0: 		
sl@0: 		// ...move it past any line break...
sl@0: 		const TInt sizeOfLineBreak = SizeLineBreak(input + start, input + inputLength);
sl@0: 		if (sizeOfLineBreak != 0)
sl@0: 			{
sl@0: 			start += sizeOfLineBreak;
sl@0: 			// ...and find the end of this next line.
sl@0: 			const TText* endLine = FindEndOfThisLine(input + start, input + inputLength);
sl@0: 			measureInput.iEndInputChar = endLine - input;
sl@0: 			}
sl@0: 		}
sl@0: 		
sl@0: 	return widestLineWidth;	
sl@0: 	}
sl@0: 
sl@0: 
sl@0: /** Prepares the visually-ordered text according to the wrapping width and font 
sl@0: specified. Text cannot be drawn until this has been done. 
sl@0: 
sl@0: @param aWrappingWidth The maximum width of the text in pixels. Note that this 
sl@0: distance should be slightly less than the available width to allow for characters 
sl@0: such as "W" which can have side-bearings that leak into the margins.
sl@0: @param aFont The font that will provide the character metrics.
sl@0: @param aBreaker An object for breaking the lines. May be NULL for default behaviour. */
sl@0: EXPORT_C void TBidiText::WrapText(TInt aWrappingWidth, const CFont& aFont,
sl@0: 	const MLineBreaker* aBreaker)
sl@0: 	{
sl@0: 	WrapText(aWrappingWidth, aFont, aBreaker, KMaxTInt);
sl@0: 	}
sl@0: 
sl@0: /** Returns the original logically-ordered text supplied in the constructor. 
sl@0: @return The original logically-ordered text supplied in the constructor. */
sl@0: EXPORT_C TPtrC TBidiText::Text() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	const TText* text = me->LogicalText();
sl@0: 	return TPtrC(text, me->TextLength());
sl@0: 	}
sl@0: 
sl@0: /** Returns the text as prepared for display, provided that WrapText has been called. 
sl@0: If WrapText has not been called, a panic will result. 
sl@0: 
sl@0: @return The text as prepared for display */
sl@0: EXPORT_C TPtrC TBidiText::DisplayText() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	__ASSERT_ALWAYS(me->iVisualOrderedTextLength >= 0, BidiPanic(EBidiPanic_InvalidVisualOrderedTextLength));
sl@0: 	const TText* text = me->VisualText();
sl@0: 	return TPtrC(text, me->iVisualOrderedTextLength);
sl@0: 	}
sl@0: 
sl@0: /** Returns the wrapping width previously supplied to WrapText. 
sl@0: 
sl@0: @return The wrapping. */
sl@0: EXPORT_C TInt TBidiText::WrappingWidth() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	return me->iWrappingWidth;
sl@0: 	}
sl@0: 
sl@0: /** Returns the directionality of the text. 
sl@0: 
sl@0: @return The directionality. */
sl@0: EXPORT_C TBidiText::TDirectionality TBidiText::Directionality() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	return me->HasRightToLeftDirectionality() ? ERightToLeft : ELeftToRight;
sl@0: 	}
sl@0: 
sl@0: /** Returns the truncation character used. 
sl@0: 
sl@0: @return The truncation character. */
sl@0: EXPORT_C TChar TBidiText::TruncationChar() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	return me->TruncationChar();
sl@0: 	}
sl@0: 
sl@0: /** Reports the number of lines in the text to be drawn.
sl@0: 
sl@0: WrapText must have been called already.
sl@0: @return
sl@0: 	The number of lines in the text which would be drawn by DrawText.
sl@0: */
sl@0: EXPORT_C TInt TBidiText::NumberOfLinesInDisplayText() const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	if (me->iVisualOrderedTextLength <0)
sl@0: 		{
sl@0: 		return 0;
sl@0: 		}
sl@0: 	const TText* text = me->VisualText();
sl@0: 	const TText* textEnd = text + me->iVisualOrderedTextLength;
sl@0: 	return NumberOfLines(text, textEnd);
sl@0: 	}
sl@0: 
sl@0: /** Returns the text as prepared for display, provided that WrapText has been called. 
sl@0: If WrapText has not been called, a panic will result. 
sl@0: @param aLine Line number to retrieve.
sl@0: @param aWidth Returns the width in pixels of the line retrieved.
sl@0: @return The text as prepared for display. */
sl@0: EXPORT_C TPtrC TBidiText::LineOfDisplayText(TInt aLine, TInt& aWidthInPixels) const
sl@0: 	{
sl@0: 	const TBidiTextImp* me = TBidiTextImp::Imp(this);
sl@0: 	__ASSERT_ALWAYS(me->iVisualOrderedTextLength >= 0, BidiPanic(EBidiPanic_InvalidVisualOrderedTextLength));
sl@0: 	__ASSERT_ALWAYS(0 <= aLine && aLine < me->iLines, BidiPanic(EBidiPanic_InvalidLineNumber));
sl@0: 	aWidthInPixels = me->LineWidthArray()[aLine];
sl@0: 	const TText* text = me->VisualText();
sl@0: 	const TText* textEnd = text + me->iVisualOrderedTextLength;
sl@0: 	for (; aLine != 0; --aLine)
sl@0: 		{
sl@0: 		text = FindEndOfThisLine(text, textEnd);
sl@0: 		text += SizeLineBreak(text, textEnd);
sl@0: 		}
sl@0: 	const TText* endOfLine = FindEndOfThisLine(text, textEnd);
sl@0: 	return TPtrC(text, endOfLine - text);
sl@0: 	}
sl@0: 
sl@0: /** Draws all of the text. WrapText must have been called already. 
sl@0: 
sl@0: @param aGc The graphics context to draw the text to. The graphics context's 
sl@0: font is assumed to have been set to the same font that was passed to the previous 
sl@0: call to WrapText.
sl@0: @param aLeft The left extreme of the baseline. Note that this should not be 
sl@0: at the very edge of the available space, or characters such as "W" with left 
sl@0: side bearings may be truncated.
sl@0: @param aBaseLineSpacing The spacing between each line. If 0, only the first 
sl@0: line is drawn.
sl@0: @param aAlignment How to position the text horizontally. */
sl@0: EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft,
sl@0: 	TInt aBaseLineSpacing, CGraphicsContext::TTextAlign aAlignment) const
sl@0: 	{
sl@0: 	TPoint origin;
sl@0: 	origin.iY = aLeft.iY;
sl@0: 	TInt lines = aBaseLineSpacing == 0? 1 : NumberOfLinesInDisplayText();
sl@0: 	TInt wrappingWidth = WrappingWidth();
sl@0: 	for (TInt i = 0; i != lines; ++i)
sl@0: 		{
sl@0: 		TInt width;
sl@0: 		TPtrC textLine = LineOfDisplayText(i, width);
sl@0: 		origin.iX = aLeft.iX;
sl@0: 		if (aAlignment != CGraphicsContext::ELeft)
sl@0: 			{
sl@0: 			TInt excess = wrappingWidth - width;
sl@0: 			origin.iX += aAlignment != CGraphicsContext::ECenter?
sl@0: 				excess : excess >> 1;
sl@0: 			}
sl@0: 		aGc.DrawText(textLine, origin);
sl@0: 		origin.iY += aBaseLineSpacing;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: /** Draws all of the text. Alignment is taken from the directionality of the text. 
sl@0: WrapText must have been called already. 
sl@0: 
sl@0: @param aGc The graphics context to draw the text to. The graphics context's 
sl@0: font is assumed to have been set to the same font that was passed to the previous 
sl@0: call to WrapText.
sl@0: @param aLeft The left extreme of the baseline. Note that this should not be 
sl@0: at the very edge of the available space, or characters such as "W" with left 
sl@0: side bearings may be truncated.
sl@0: @param aBaseLineSpacing The spacing between each line. If 0, only the first 
sl@0: line is drawn. */
sl@0: EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft,
sl@0: 	TInt aBaseLineSpacing) const
sl@0: 	{
sl@0: 	DrawText(aGc, aLeft, aBaseLineSpacing,
sl@0: 		Directionality() == ELeftToRight?
sl@0: 		CGraphicsContext::ELeft : CGraphicsContext::ERight);
sl@0: 	}
sl@0: 
sl@0: /** Draws the first line of the text. WrapText must have been called already. Alignment 
sl@0: is taken from the directionality of the text. 
sl@0: 
sl@0: @param aGc The graphics context to draw the text to. The graphics context's 
sl@0: font is assumed to have been set to the same font that was passed to the previous 
sl@0: call to WrapText.
sl@0: @param aLeft The left extreme of the baseline. Note that this should not be 
sl@0: at the very edge of the available space, or characters such as "W" with left 
sl@0: side bearings may be truncated. */
sl@0: EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft) const
sl@0: 	{
sl@0: 	DrawText(aGc, aLeft, 0);
sl@0: 	}
sl@0: