os/textandloc/fontservices/textbase/sgdi/BidiText.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/textandloc/fontservices/textbase/sgdi/BidiText.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,962 @@
     1.4 +// Copyright (c) 2002-2010 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +#include <e32svr.h>
    1.20 +#include "BidiTextImp.h"
    1.21 +#include "BidiCopy.h"
    1.22 +#include "BidiCompact.h"
    1.23 +#include <bidi.h>
    1.24 +#include <e32base.h>
    1.25 +//#include <textbase.h>
    1.26 +#include <gdi.h>
    1.27 +#include <linebreak.h>
    1.28 +
    1.29 +_LIT(KBidiPanicCategory,"Bidi");
    1.30 +static const TInt KLineSeparator = 0x2028;
    1.31 +static const TInt KParagraphSeparator = 0x2029;
    1.32 +static const TInt KCodeCR = 0x000D;
    1.33 +static const TInt KCodeLF = 0x000A;
    1.34 +static const TInt KCodeEllipsis = 0x2026;
    1.35 +
    1.36 +void DeleteTRunInfo(void* aRunInfo)
    1.37 +	{
    1.38 +	delete[] reinterpret_cast<TBidirectionalState::TRunInfo*>(aRunInfo);
    1.39 +	}
    1.40 +
    1.41 +void BidiPanic(TInt aError)
    1.42 +	{
    1.43 +	User::Panic(KBidiPanicCategory, aError);
    1.44 +	}
    1.45 +
    1.46 +
    1.47 +// One page-full of TRunInfos
    1.48 +const TInt KMaxRunInfoArraySize = 4*1024 / sizeof(TBidirectionalState::TRunInfo);
    1.49 +const TInt KBidiTlsHandle = 0x101F633D;
    1.50 +
    1.51 +
    1.52 +/*
    1.53 +* Ref-counted TLS for the shared run info array used by the SetText() method.
    1.54 +*/
    1.55 +NONSHARABLE_CLASS(CBidiTextTls) : public CObject
    1.56 +	{
    1.57 +public:	
    1.58 +	static CBidiTextTls* NewL();
    1.59 +	static CBidiTextTls* GetTls();
    1.60 +	~CBidiTextTls();
    1.61 +	inline TUint MaxArraySize();
    1.62 +	inline TBidirectionalState::TRunInfo* RunArray();
    1.63 +
    1.64 +private:
    1.65 +	CBidiTextTls();
    1.66 +	void ConstructL(TUint aMaxArraySize);
    1.67 +
    1.68 +private:
    1.69 +	TBidirectionalState::TRunInfo* iRunInfoArray;
    1.70 +	TUint iMaxArraySize;	
    1.71 +	};
    1.72 +
    1.73 +
    1.74 +
    1.75 +CBidiTextTls::CBidiTextTls()
    1.76 +:	iRunInfoArray(NULL),
    1.77 + 	iMaxArraySize(0)
    1.78 +	{
    1.79 +	}
    1.80 +
    1.81 +
    1.82 +CBidiTextTls::~CBidiTextTls()
    1.83 +	{
    1.84 +	UserSvr::DllFreeTls(KBidiTlsHandle);
    1.85 +	
    1.86 +	if (iRunInfoArray)
    1.87 +		{
    1.88 +		delete [] iRunInfoArray;	
    1.89 +		}
    1.90 +	}
    1.91 +
    1.92 +
    1.93 +TUint CBidiTextTls::MaxArraySize()
    1.94 +	{
    1.95 +	return iMaxArraySize;
    1.96 +	}
    1.97 +	
    1.98 +
    1.99 +TBidirectionalState::TRunInfo* CBidiTextTls::RunArray()
   1.100 +	{
   1.101 +	return iRunInfoArray;
   1.102 +	}
   1.103 +	
   1.104 +	
   1.105 +/**
   1.106 + * Helper function provided to simplify reading the TLS data and improve the
   1.107 + * readability of the code 
   1.108 + */
   1.109 +CBidiTextTls* CBidiTextTls::GetTls()
   1.110 +	{
   1.111 +	return reinterpret_cast<CBidiTextTls*>(UserSvr::DllTls(KBidiTlsHandle));
   1.112 +	}
   1.113 +
   1.114 +
   1.115 +CBidiTextTls* CBidiTextTls::NewL()
   1.116 +	{
   1.117 +	CBidiTextTls* self = new (ELeave) CBidiTextTls;
   1.118 +	CleanupClosePushL(*self);
   1.119 +	self->ConstructL(KMaxRunInfoArraySize);
   1.120 +	CleanupStack::Pop(self);
   1.121 +	return self;
   1.122 +	}
   1.123 +
   1.124 +
   1.125 +void CBidiTextTls::ConstructL(TUint aMaxArraySize)
   1.126 +	{
   1.127 +	iMaxArraySize = aMaxArraySize;
   1.128 +	iRunInfoArray = new (ELeave) TBidirectionalState::TRunInfo[aMaxArraySize];
   1.129 +	User::LeaveIfError(UserSvr::DllSetTls(KBidiTlsHandle, this));
   1.130 +	}
   1.131 +
   1.132 +
   1.133 +EXPORT_C RRunInfoArray::RRunInfoArray() 
   1.134 +:	iTls(NULL)
   1.135 +	{
   1.136 +	}
   1.137 +	
   1.138 +
   1.139 +/** 
   1.140 +Creates the run array if necessary and increases the reference count on it.
   1.141 +RRunInfoArray::OpenL() must be called prior to calling TBidiText::SetText().
   1.142 + */
   1.143 +EXPORT_C void RRunInfoArray::OpenL()
   1.144 +	{
   1.145 +	if(!iTls)
   1.146 +		{
   1.147 +		iTls = CBidiTextTls::GetTls();
   1.148 +		if(iTls)
   1.149 +			{
   1.150 +			iTls->Open();	// Increase ref count	
   1.151 +			}
   1.152 +		else	
   1.153 +			{
   1.154 +			iTls = CBidiTextTls::NewL();
   1.155 +			}
   1.156 +		}	
   1.157 +	}
   1.158 +	
   1.159 +	
   1.160 +/**
   1.161 +Decreases the reference count on the run array. The run array will be deleted
   1.162 +if the reference count reaches zero. The client application must ensure that
   1.163 +there is a matching call to Close() for every call to OpenL() or memory will
   1.164 +be leaked.
   1.165 + */
   1.166 +EXPORT_C void RRunInfoArray::Close()
   1.167 +	{
   1.168 +	if(iTls)
   1.169 +		{
   1.170 +		iTls->Close();	
   1.171 +		iTls = NULL;
   1.172 +		}
   1.173 +	}
   1.174 +	
   1.175 +
   1.176 +/**
   1.177 +@return Pointer to the run array buffer
   1.178 +@internalComponent
   1.179 + */
   1.180 +TBidirectionalState::TRunInfo* RRunInfoArray::RunArray() const
   1.181 +	{
   1.182 +	return iTls ? iTls->RunArray() : NULL;
   1.183 +	}
   1.184 +	
   1.185 +
   1.186 +/**
   1.187 +@return Number of bytes needed to hold the TBidiTextImp member variables, plus the 
   1.188 +		text data allocated off the end of the TBidiTextImp object.
   1.189 +@internalComponent
   1.190 +*/
   1.191 +TInt TBidiTextImp::RequiredBytes(TInt aLength, TInt aMaxLines, TInt aBdRunArraySize)		
   1.192 +	{
   1.193 +	// size of TBidiTextImp class
   1.194 +	TInt bytes = TBidiTextImp::AlignedSizeOf();
   1.195 +	// size of text for logical and visual orderings.
   1.196 +	// This includes aMaxLines - 1 line breaks with surrounding
   1.197 +	// zero-width joiners, and a truncation character (possibly
   1.198 +	// a surrogate pair) plus a zero-width joiner.
   1.199 +	bytes += sizeof(TText) * (aLength * 2 + aMaxLines * 3);
   1.200 +	// size of line length array
   1.201 +	bytes += sizeof(TInt16*) * aMaxLines;
   1.202 +	// alignment
   1.203 +	bytes = (bytes + 3) & 0xFFFFFFFC;
   1.204 +	// array of TRunInfoCompact
   1.205 +	bytes += sizeof(TRunInfoCompact) * aBdRunArraySize;
   1.206 +	
   1.207 +	return bytes;
   1.208 +	}
   1.209 +
   1.210 +	
   1.211 +/**
   1.212 +@return A TBidiTextImp object of sufficient size to hold the amount of text data specified
   1.213 +		by the the arguments.
   1.214 +@param aLength The number of characters in the text.
   1.215 +@param aMaxLines The maximum number of lines 
   1.216 +@param aBdRunArraySize The size of the bidi run array. 
   1.217 +@internalComponent
   1.218 +*/
   1.219 +TBidiTextImp* TBidiTextImp::NewL(TInt aLength, TInt aMaxLines, TInt aBdRunArraySize)
   1.220 +	{
   1.221 +	const TInt bytes = RequiredBytes(aLength, aMaxLines, aBdRunArraySize);
   1.222 +	TInt8* mem = static_cast<TInt8*>(User::AllocL(bytes));
   1.223 +	TBidiTextImp* me = reinterpret_cast<TBidiTextImp*>(mem);
   1.224 +
   1.225 +	me->iTextLengthAndFlags = aLength;
   1.226 +	me->iVisualOrderedTextLength = -1;
   1.227 +	me->iWrappingWidth = 0xFFFF;
   1.228 +	me->iBidiRunArrayLength = aBdRunArraySize;
   1.229 +	me->iLines = static_cast<TUint8>(aMaxLines);
   1.230 +	me->iTruncationCharPlane = 0;
   1.231 +	me->iTruncationChar16 = KCodeEllipsis;
   1.232 +	me->SetAllocatedTextDataBytes(bytes - TBidiTextImp::AlignedSizeOf() - (sizeof(TRunInfoCompact) * aBdRunArraySize));
   1.233 +	return me;
   1.234 +	}
   1.235 +
   1.236 +/**
   1.237 +@return Position of logically-ordered text portion of the heap cell.
   1.238 +@internalComponent
   1.239 +*/
   1.240 +TText* TBidiTextImp::LogicalText()
   1.241 +	{
   1.242 +	return reinterpret_cast<TText*>(
   1.243 +		reinterpret_cast<TInt8*>(this)
   1.244 +		+ TBidiTextImp::AlignedSizeOf());
   1.245 +	}
   1.246 +
   1.247 +/**
   1.248 +@return Position of visually-ordered text portion of the heap cell.
   1.249 +@internalComponent
   1.250 +*/
   1.251 +TText* TBidiTextImp::VisualText()
   1.252 +	{
   1.253 +	TInt bytes = TBidiTextImp::AlignedSizeOf();
   1.254 +	bytes += sizeof(TText) * TextLength();
   1.255 +	return reinterpret_cast<TText*>(
   1.256 +		reinterpret_cast<TInt8*>(this) + bytes);
   1.257 +	}
   1.258 +
   1.259 +/**
   1.260 +Returns a pointer to the array containing the width in pixels of each and every line.
   1.261 +@return Position of the array of line widths portion of the heap cell.
   1.262 +@internalComponent
   1.263 +*/
   1.264 +TInt16* TBidiTextImp::LineWidthArray()
   1.265 +	{
   1.266 +	TInt bytes = TBidiTextImp::AlignedSizeOf();
   1.267 +	bytes += sizeof(TText) * (TextLength() * 2 + iLines + 2);
   1.268 +	return reinterpret_cast<TInt16*>(
   1.269 +		reinterpret_cast<TInt8*>(this) + bytes);
   1.270 +	}
   1.271 +
   1.272 +/**
   1.273 +@return Position of the array of runs portion of the heap cell.
   1.274 +@internalComponent
   1.275 +*/
   1.276 +TRunInfoCompact* TBidiTextImp::BidiRunArray()
   1.277 +	{
   1.278 +	TInt bytes = TBidiTextImp::AlignedSizeOf();
   1.279 +	bytes += sizeof(TText) * (TextLength() * 2 + iLines + 2);
   1.280 +	bytes += sizeof(TInt16*) * iLines;
   1.281 +	bytes = (bytes + 3) & 0xFFFFFFFC;
   1.282 +	return reinterpret_cast<TRunInfoCompact*>(
   1.283 +		reinterpret_cast<TInt8*>(this) + bytes);
   1.284 +	}
   1.285 +
   1.286 +/** 
   1.287 +Report if the current character is an explicit line break. Both
   1.288 +aText[0] and aText[1] must be part of the string.
   1.289 +@return Size of line break.
   1.290 +@internalComponent 
   1.291 +*/
   1.292 +TInt SizeLineBreak(const TText* aText, const TText* aTextEnd)
   1.293 +	{
   1.294 +	if (aText == aTextEnd )
   1.295 +		return 0;
   1.296 +		
   1.297 +	if (*aText == KLineSeparator || *aText == KParagraphSeparator
   1.298 +		|| *aText == KCodeLF)
   1.299 +		return 1;
   1.300 +	if (aText[0] == KCodeCR)
   1.301 +		{
   1.302 +		// first check for space before checking for LF
   1.303 +		if (aText+1 < aTextEnd )
   1.304 +			{
   1.305 +			return aText[1] == KCodeLF? 2 : 1;
   1.306 +			}
   1.307 +		else
   1.308 +			return 1;
   1.309 +		}
   1.310 +	return 0;
   1.311 +	}
   1.312 +
   1.313 +/** 
   1.314 +Find the next line break character.
   1.315 +@internalComponent 
   1.316 +*/
   1.317 +const TText* FindEndOfThisLine(const TText* aStart, const TText* aEnd)
   1.318 +	{
   1.319 +	while (aStart != aEnd && *aStart != KLineSeparator
   1.320 +		&& *aStart != KParagraphSeparator && *aStart != KCodeLF
   1.321 +		&& *aStart != KCodeCR)
   1.322 +		++aStart;
   1.323 +	return aStart;
   1.324 +	}
   1.325 +
   1.326 +/**
   1.327 +Count number of lines in text.
   1.328 +@internalComponent
   1.329 +*/
   1.330 +TInt NumberOfLines(const TText* aStart, const TText* aEnd)
   1.331 +	{
   1.332 +	TInt num = 0;
   1.333 +	while (aStart != aEnd)
   1.334 +		{
   1.335 +		aStart = FindEndOfThisLine(aStart, aEnd);
   1.336 +		aStart += SizeLineBreak(aStart, aEnd);
   1.337 +		++num;
   1.338 +		}
   1.339 +	return num;
   1.340 +	}
   1.341 +
   1.342 +/** Returns the directionality of a given language.
   1.343 +@param aLanguage Language.
   1.344 +@return The directionality of the given language. */
   1.345 +EXPORT_C TBidiText::TDirectionality TBidiText::ScriptDirectionality(
   1.346 +	TLanguage aLanguage)
   1.347 +	{
   1.348 +	const TUint32 DirectionalityBitmap[] =
   1.349 +		{
   1.350 +		0,
   1.351 +		// Arabic, Farsi, Hebrew
   1.352 +		0x02040020,
   1.353 +		// Urdu
   1.354 +		0x40000000
   1.355 +		};
   1.356 +	TUint index = aLanguage;
   1.357 +	if (index < sizeof(DirectionalityBitmap) * 8)
   1.358 +		{
   1.359 +		index >>= 5;
   1.360 +		TInt bit = aLanguage & 31;
   1.361 +		return (DirectionalityBitmap[index] >> bit) & 1?
   1.362 +			ERightToLeft : ELeftToRight;
   1.363 +		}
   1.364 +	return ELeftToRight;
   1.365 +	}
   1.366 +
   1.367 +
   1.368 +/** Reports the implicit directionality of a piece of text.
   1.369 +
   1.370 +@param aText The text to be examined. 
   1.371 +@param aFound If non-null, returns ETrue if there were any strongly directional 
   1.372 +characters and EFalse if there were none. If a piece of text is spread over 
   1.373 +several descriptors, They need to be queried in sequence until one returns 
   1.374 +ETrue in aFound. 
   1.375 +@return The directionality implicit in aText. 131 */ 
   1.376 +EXPORT_C TBidiText::TDirectionality TBidiText::TextDirectionality(
   1.377 +	const TDesC& aText, TBool* aFound)
   1.378 +	{
   1.379 +	return BidiCopy::ImplicitDirectionalityIsRightToLeft(
   1.380 +		aText.Ptr(), aText.Length(), aFound)?
   1.381 +		ERightToLeft : ELeftToRight;
   1.382 +	}
   1.383 +
   1.384 +/** Creates a bidirectional text object with directionality determined by
   1.385 +aDirectionality. Use this for text that has come from user input.
   1.386 +
   1.387 +@param aText The text in logical order.
   1.388 +@param aMaxLines
   1.389 +	The maximum number of lines that this text will need to be split into. Must
   1.390 +	be at least 1, but should not be too large, as each potential line takes an
   1.391 +	extra 8 bytes of memory.
   1.392 +@param aDirectionality Direction to use.
   1.393 +@return The newly constructed object.
   1.394 + */
   1.395 +EXPORT_C TBidiText* TBidiText::NewL(const TDesC& aText, TInt aMaxLines,
   1.396 +	TBidiText::TDirectionality aDirectionality)
   1.397 +	{
   1.398 +	__ASSERT_ALWAYS(0 < aMaxLines, BidiPanic(EBidiPanic_InvalidMaxline));
   1.399 +	const TText* text = aText.Ptr();
   1.400 +	const TInt length = aText.Length();
   1.401 +	TInt linesInOriginalText = NumberOfLines(text, text + length);
   1.402 +	if (aMaxLines < linesInOriginalText)
   1.403 +		aMaxLines = linesInOriginalText;
   1.404 +
   1.405 +	const TInt arraySize = TBidirectionalState::GenerateBdRunArray(text, length, 0, 0);
   1.406 +	TBidirectionalState::TRunInfo* runInfoArray = new(ELeave) TBidirectionalState::TRunInfo[arraySize];
   1.407 +	TCleanupItem ci(DeleteTRunInfo, runInfoArray);
   1.408 +	CleanupStack::PushL(ci);
   1.409 +	TBidirectionalState::GenerateBdRunArray(text, length, runInfoArray, arraySize);
   1.410 +	TBidirectionalState state;
   1.411 +	state.ReorderLine(runInfoArray, arraySize, ETrue, ETrue, aDirectionality,
   1.412 +		TChar::EOtherNeutral, TChar::EOtherNeutral);
   1.413 +	TInt compactArraySize = TRunInfoCompact::Convert(0, aText, runInfoArray, arraySize);
   1.414 +
   1.415 +	// size of TBidiTextImp class
   1.416 +	TBidiTextImp* me = TBidiTextImp::NewL(length, aMaxLines, compactArraySize);
   1.417 +	me->SetRightToLeftDirectionality(aDirectionality != ELeftToRight);
   1.418 +		
   1.419 +	TRunInfoCompact::Convert(me->BidiRunArray(), aText, runInfoArray, arraySize);
   1.420 +	CleanupStack::PopAndDestroy(runInfoArray);
   1.421 +
   1.422 +	Mem::Copy(me->LogicalText(), text, length * sizeof(TText));
   1.423 +	return me;
   1.424 +	}
   1.425 +
   1.426 +/** Creates a bidirectional text object with directionality determined by the text 
   1.427 +itself. Use this for text that has been obtained from a resource file. 
   1.428 +
   1.429 +@param aText The text in logical order.
   1.430 +@param aMaxLines The maximum number of lines that this text will need to be 
   1.431 +split into. Must be at least 1, but should not be too large, as each potential 
   1.432 +line takes an extra 8 bytes of memory.
   1.433 +@return The newly constructed object. */
   1.434 +EXPORT_C TBidiText* TBidiText::NewL(const TDesC& aText, TInt aMaxLines)
   1.435 +	{
   1.436 +	return NewL(aText, aMaxLines, TextDirectionality(aText));
   1.437 +	}
   1.438 +
   1.439 +
   1.440 +/** Creates a bidirectional text object with enough room for up to aReservedMaxLength
   1.441 +number of characters. The number of characters that will actually fit (when calling 
   1.442 +SetText()) might be slightly less than aReservedMaxLength, as each change between a 
   1.443 +left-to-right and a right-to-left sub-string (and the other way around) needs about
   1.444 +two characters worth of memory.
   1.445 +
   1.446 +@param aReservedMaxLength The maximum number of characters.
   1.447 +@param aMaxLines The maximum number of lines that this text will need to be 
   1.448 +split into. Must be at least 1, but should not be too large, as each potential 
   1.449 +line takes an extra 8 bytes of memory.
   1.450 +@return The newly constructed object. */
   1.451 +EXPORT_C TBidiText* TBidiText::NewL(TInt aReservedMaxLength, TInt aMaxLines)
   1.452 +	{
   1.453 +	__ASSERT_ALWAYS(0 < aReservedMaxLength, BidiPanic(EBidiPanic_InvalidReservedMaxLength));
   1.454 +	__ASSERT_ALWAYS(0 < aMaxLines,          BidiPanic(EBidiPanic_InvalidMaxline));
   1.455 +	
   1.456 +	const TInt compactArraySize = 1;	// Always at least one needed
   1.457 +	
   1.458 +	TBidiTextImp* me = TBidiTextImp::NewL(aReservedMaxLength, aMaxLines, compactArraySize);
   1.459 +	me->SetTextLength(0);	// no text yet, just reserved memory
   1.460 +	return me;
   1.461 +	}
   1.462 +
   1.463 +/** Sets the text of the bidirectional text object with directionality determined 
   1.464 +by the text itself. Use this for text that has been obtained from a resource file. 
   1.465 +
   1.466 +@param aText The text in logical order.
   1.467 +@return The number of characters that didn't fit in the available buffer.
   1.468 +*/
   1.469 +EXPORT_C TInt TBidiText::SetText(const TDesC& aText, RRunInfoArray& aRunInfoArray)
   1.470 +	{
   1.471 +	return SetText(aText, TextDirectionality(aText), aRunInfoArray);
   1.472 +	}
   1.473 +
   1.474 +
   1.475 +/** Sets the text of the bidirectional text with directionality determined by
   1.476 +aDirectionality. Use this for text that has come from user input.
   1.477 +
   1.478 +@param aText The text in logical order.
   1.479 +@param aDirectionality Direction to use.
   1.480 +@return The number of characters that didn't fit in the available buffer.
   1.481 +@panic Bidi EBidiPanic_RunArrayNull The call to RRunInfoArray::OpenL() has not
   1.482 +been made prior to this call to TBidiText::SetText()
   1.483 +*/
   1.484 +EXPORT_C TInt TBidiText::SetText(const TDesC& aText,
   1.485 +								 TDirectionality aDirectionality, 
   1.486 +								 RRunInfoArray& aRunInfoArray)
   1.487 +	{
   1.488 +	TBidirectionalState::TRunInfo* const runArray = aRunInfoArray.RunArray();
   1.489 +	__ASSERT_ALWAYS(runArray, BidiPanic(EBidiPanic_RunArrayNull));
   1.490 +	
   1.491 +	TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.492 +	const TInt maxLines = me->iLines;	
   1.493 +	const TText* text = aText.Ptr();
   1.494 +	TInt length = aText.Length();
   1.495 +	
   1.496 +	TInt requiredArraySize = TBidirectionalState::GenerateBdRunArray(text, length, 0, 0);
   1.497 +	const TInt actualArraySize = aRunInfoArray.iTls->MaxArraySize();
   1.498 +		
   1.499 +	if (requiredArraySize > actualArraySize)
   1.500 +		{
   1.501 +		// Handle the case where we do not have enough space in the run array
   1.502 +		// to cope with the input text. The text will be truncated to ensure
   1.503 +		// we don't overrun the buffer and the number of excess characters 
   1.504 +		// returned as a negative number.
   1.505 +		requiredArraySize = actualArraySize;
   1.506 +		TBidirectionalState::GenerateBdRunArray(text, length, runArray, requiredArraySize);
   1.507 +		
   1.508 +		length = 0;
   1.509 +		for (TInt index = 0; index < requiredArraySize; index++)
   1.510 +			{
   1.511 +			length += runArray[index].iLength;
   1.512 +			}
   1.513 +		}
   1.514 +	else
   1.515 +		{
   1.516 +		TBidirectionalState::GenerateBdRunArray(text, length, runArray, requiredArraySize);
   1.517 +		}
   1.518 +	
   1.519 +	
   1.520 +
   1.521 +	TBidirectionalState state;
   1.522 +	state.ReorderLine(runArray, 
   1.523 +					  requiredArraySize, 
   1.524 +					  ETrue, 
   1.525 +					  ETrue, 
   1.526 +					  aDirectionality, 
   1.527 +					  TChar::EOtherNeutral, 
   1.528 +					  TChar::EOtherNeutral);
   1.529 +	const TInt compactArraySize = TRunInfoCompact::Convert(0, 
   1.530 +														   aText, 
   1.531 +														   runArray, 
   1.532 +														   requiredArraySize);
   1.533 +
   1.534 +	// Calculate number of bytes needed to keep text data
   1.535 +	TInt requiredBytes = sizeof(TText) * (length * 2 + maxLines * 3); // size of text for logical & visual orderings.
   1.536 +	requiredBytes += sizeof(TInt16*) * maxLines;					  // size of line length array
   1.537 +	requiredBytes = (requiredBytes + 3) & 0xFFFFFFFC;				  // alignment
   1.538 +	
   1.539 +	TInt textLength = length;
   1.540 +	const TInt excessData = Max(0, requiredBytes - me->AllocatedTextDataBytes());
   1.541 +	TInt excessChars = 0;
   1.542 +	if(excessData)
   1.543 +		{
   1.544 +		// Calculate how much text data that can be fitted into the available bytes, 
   1.545 +		// given the bytes needed for run array data
   1.546 +		excessChars = excessData / (sizeof(TText) * 2);
   1.547 +		textLength -= excessChars;
   1.548 +		}
   1.549 +	else if (aText.Length() > length)
   1.550 +		{
   1.551 +		excessChars = aText.Length() - length;
   1.552 +		}
   1.553 +	
   1.554 +	me->SetTextLength(textLength);
   1.555 +	me->SetRightToLeftDirectionality(aDirectionality != ELeftToRight);			
   1.556 +	me->iVisualOrderedTextLength = -1;
   1.557 +	me->iBidiRunArrayLength = static_cast<TUint16>(compactArraySize);
   1.558 +
   1.559 +	TRunInfoCompact::Convert(me->BidiRunArray(), aText, runArray, requiredArraySize);
   1.560 +	Mem::Copy(me->LogicalText(), text, textLength * sizeof(TText));
   1.561 +
   1.562 +	return excessChars;
   1.563 +	}
   1.564 +
   1.565 +/** Sets the character that will be added at the end of the text if the whole text 
   1.566 +cannot fit into the space specified. 
   1.567 +
   1.568 +@param aTruncateWith The truncation char. */
   1.569 +EXPORT_C void TBidiText::SetTruncationChar(TChar aTruncateWith)
   1.570 +	{
   1.571 +	TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.572 +	me->iTruncationCharPlane = static_cast<TUint8>(aTruncateWith >> 16);
   1.573 +	me->iTruncationChar16 = static_cast<TUint16>(aTruncateWith);
   1.574 +	}
   1.575 +
   1.576 +TInt RemoveTrailingSpaces(const MLineBreaker* aBreaker,
   1.577 +	const TText* aInput, TInt aMinPos, TInt aEndPos)
   1.578 +	{
   1.579 +	// Ignore space characters at the end of the line.
   1.580 +	// Don't bother to ignore spaces made of surrogate pairs:
   1.581 +	// more processing than it's worth.
   1.582 +	TUint dummy1, dummy2;
   1.583 +	while (aEndPos != aMinPos && MLineBreaker::ESpLineBreakClass
   1.584 +		== aBreaker->LineBreakClass(aInput[aEndPos - 1], dummy1, dummy2))
   1.585 +		{
   1.586 +		--aEndPos;
   1.587 +		}
   1.588 +	return aEndPos;
   1.589 +	}
   1.590 +
   1.591 +/** Prepares the visually-ordered text according to the wrapping width and font
   1.592 +specified. Text cannot be drawn until this has been done.
   1.593 +@param aWrappingWidth
   1.594 +	The maximum width of the text in pixels. Note that this distance should be
   1.595 +	slightly less than the available width to allow for characters such as "W"
   1.596 +	which can have side-bearings that leak into the margins.
   1.597 +@param aFont The font that will provide the character metrics.
   1.598 +@param aBreaker
   1.599 +	An object for breaking the lines. May be NULL for default behaviour.
   1.600 +@param aMaxLines
   1.601 +	Number of lines to restrict wrapping to. The truncation character will be
   1.602 +	used if the text is too long for this number of lines. The number of lines
   1.603 +	wrapped to may not be greater than the figure passed to NewL.
   1.604 +*/
   1.605 +EXPORT_C void TBidiText::WrapText(TInt aWrappingWidth, const CFont& aFont,
   1.606 +	const MLineBreaker* aBreaker, TInt aMaxLines)
   1.607 +	{
   1.608 +	TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.609 +	me->iWrappingWidth = aWrappingWidth;
   1.610 +	
   1.611 +	TInt16* lineWidths = me->LineWidthArray();
   1.612 +	TText* output = me->VisualText();
   1.613 +	
   1.614 +	TInt numLines = 0;
   1.615 +	DoWrapText(aWrappingWidth, aFont, aBreaker, aMaxLines, output, numLines, lineWidths);	
   1.616 +	me->iVisualOrderedTextLength = output - me->VisualText();
   1.617 +	}
   1.618 +
   1.619 +/** Calculate the minimum size needed to draw the current text, given the specified
   1.620 +wrapping width, font, and line gap. Calling this method will not rewrap the object's
   1.621 +text.
   1.622 +
   1.623 +@param aWrappingWidth
   1.624 +	The maximum width of the text in pixels. Note that this distance should be
   1.625 +	slightly less than the available width to allow for characters such as "W"
   1.626 +	which can have side-bearings that leak into the margins.
   1.627 +@param aFont The font that will provide the character metrics.
   1.628 +@param aLineGap The number of empty pixels between two lines of text. 
   1.629 +	Note that this is not the same as the baseline spacing, which is the font 
   1.630 +	height plus the line gap.
   1.631 +@param aMaxLines
   1.632 +	Number of lines to restrict wrapping to. The truncation character will be
   1.633 +	used if the text is too long for this number of lines. The number of lines
   1.634 +	wrapped to may be greater than the figure passed to NewL, and that figure
   1.635 +	will be used if the number of lines is specified as -1. If 0 (zero) is specified
   1.636 +	no limit is applied.
   1.637 +@param aBreaker
   1.638 +	An object for breaking the lines. May be NULL for default behaviour.
   1.639 +*/
   1.640 +EXPORT_C TSize TBidiText::MinimumSize(TInt aWrappingWidth, const CFont& aFont, TInt aLineGap, 
   1.641 +									TInt aMaxLines, const MLineBreaker* aBreaker) const
   1.642 +	{
   1.643 +	__ASSERT_ALWAYS(0  <= aWrappingWidth, BidiPanic(EBidiPanic_InvalidWrappingWidth));
   1.644 +	__ASSERT_ALWAYS(0  <=  aLineGap,       BidiPanic(EBidiPanic_InvalidLineGap));
   1.645 +	__ASSERT_ALWAYS(-1 <= aMaxLines,      BidiPanic(EBidiPanic_InvalidMaxline));
   1.646 +
   1.647 +	TInt numLines = 0;
   1.648 +	TText* output = NULL;
   1.649 +	const TInt minWidth = DoWrapText(aWrappingWidth, 
   1.650 +									 aFont, 
   1.651 +									 aBreaker, 
   1.652 +									 (aMaxLines = 0 ? KMaxTInt : aMaxLines), 
   1.653 +									 output, 
   1.654 +									 numLines, 
   1.655 +									 NULL);
   1.656 +	const TInt minHeight = (aFont.FontMaxHeight() + aLineGap) * numLines - aLineGap; 
   1.657 +	return TSize(minWidth, minHeight);	
   1.658 +	}
   1.659 +
   1.660 +
   1.661 +TInt TBidiText::DoWrapText(TInt aWrappingWidth, const CFont& aFont, const MLineBreaker* aBreaker, 
   1.662 +	TInt aMaxLines, TText*& aOutputText, TInt& aNumLines, TInt16* aLineWidthArray) const
   1.663 +	{
   1.664 +	MLineBreaker defaultBreaker;
   1.665 +	if (!aBreaker)
   1.666 +		aBreaker = &defaultBreaker;
   1.667 +	
   1.668 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.669 +	if (me->iLines < aMaxLines)
   1.670 +		aMaxLines = me->iLines;
   1.671 +	
   1.672 +	const TRunInfoCompact* runArray = me->BidiRunArray();	
   1.673 +	const TRunInfoCompact* runArrayEnd = runArray + me->iBidiRunArrayLength;
   1.674 +	
   1.675 +	const TText* input = me->LogicalText();
   1.676 +	const TInt inputLength = me->TextLength();
   1.677 +	TPtrC textDes(input, inputLength);
   1.678 +	const TText* output = me->VisualText();
   1.679 +
   1.680 +	TRunInfoCompact::TReorderingContext context;
   1.681 +	context.iSource = input;
   1.682 +	context.iTruncation = 0xFFFF;
   1.683 +	context.iJoinsAtEnd = EFalse;
   1.684 +	
   1.685 +	TInt start = 0;
   1.686 +	CFont::TMeasureTextInput measureInput;
   1.687 +	measureInput.iMaxAdvance = aWrappingWidth;
   1.688 +	measureInput.iEndInputChar = FindEndOfThisLine(input, input + inputLength) - input;
   1.689 +	CFont::TMeasureTextOutput measureOutput;
   1.690 +	TBool truncated;
   1.691 +
   1.692 +	TInt widestLineWidth = 0;
   1.693 +	TBool bLastLine = EFalse;
   1.694 +	for (aNumLines = 0; aNumLines != aMaxLines && start < inputLength; ++aNumLines)
   1.695 +		{
   1.696 +		truncated=EFalse;
   1.697 +		context.iJoinsAtStart = context.iJoinsAtEnd;
   1.698 +		if(aNumLines != 0 && aOutputText)
   1.699 +			*(aOutputText++) = KLineSeparator;
   1.700 +		
   1.701 +		measureInput.iStartInputChar = start;
   1.702 +		TInt advance = aFont.MeasureText(textDes, &measureInput, &measureOutput);
   1.703 +		TInt breakPos = measureOutput.iChars;
   1.704 +		TInt endOfLine = breakPos;
   1.705 +		// truncationCharWidth is the width of any truncation character on this
   1.706 +		// line only.
   1.707 +		TInt truncationCharWidth = 0;
   1.708 +		if (endOfLine == measureInput.iEndInputChar)
   1.709 +			{
   1.710 +			//handle the dangling lines here
   1.711 +			TInt sizeLineBreak = SizeLineBreak(input + endOfLine, input + inputLength);
   1.712 +			if((measureInput.iEndInputChar < inputLength - sizeLineBreak) && (aNumLines == aMaxLines - 1))
   1.713 +				bLastLine = ETrue;
   1.714 +			}
   1.715 +		else if (aNumLines == aMaxLines - 1)
   1.716 +			{
   1.717 +			bLastLine = ETrue;
   1.718 +			}
   1.719 +		else 
   1.720 +			{ // Not last line, so find a legal line break.
   1.721 +			aBreaker->GetLineBreak(textDes, 
   1.722 +								   start + 1, 
   1.723 +								   measureOutput.iChars, 
   1.724 +								   EFalse, 
   1.725 +								   0, 
   1.726 +								   breakPos, 
   1.727 +								   endOfLine);
   1.728 +			}
   1.729 +
   1.730 +		if (bLastLine)
   1.731 +			{
   1.732 +			// Last line, so re-measure leaving enough room for
   1.733 +			// truncation character.
   1.734 +			context.iTruncation = me->TruncationChar();
   1.735 +			truncationCharWidth = aFont.CharWidthInPixels(context.iTruncation);
   1.736 +			measureInput.iMaxAdvance = aWrappingWidth - truncationCharWidth;
   1.737 +			advance = aFont.MeasureText(textDes, &measureInput, &measureOutput) + truncationCharWidth;
   1.738 +			breakPos = RemoveTrailingSpaces(aBreaker, input, start, measureOutput.iChars);
   1.739 +			truncated=ETrue;
   1.740 +			bLastLine = EFalse;
   1.741 +			}
   1.742 +
   1.743 +		// if the break position has changed, we need to remeasure
   1.744 +		if (breakPos != measureOutput.iChars)
   1.745 +			{
   1.746 +			const TInt oldEnd = measureInput.iEndInputChar;
   1.747 +			measureInput.iEndInputChar = breakPos;
   1.748 +			advance = aFont.MeasureText(textDes, &measureInput, &measureOutput) + truncationCharWidth;
   1.749 +			measureInput.iEndInputChar = oldEnd;
   1.750 +			truncated=ETrue;
   1.751 +			}
   1.752 +
   1.753 +		//width may be greater than advance
   1.754 +		advance = Max(advance,measureOutput.iBounds.Width());
   1.755 +
   1.756 +		if(widestLineWidth < advance)
   1.757 +			widestLineWidth = advance;
   1.758 +		
   1.759 +		if(aLineWidthArray)
   1.760 +			*(aLineWidthArray++) = static_cast<TInt16>(advance);
   1.761 +
   1.762 +		context.iStart = start;
   1.763 +		context.iEnd = breakPos;
   1.764 +		if (truncated)
   1.765 +			{
   1.766 +			context.iJoinsAtEnd = breakPos < inputLength?
   1.767 +				TRunInfoCompact::JoinBefore(input, breakPos) : EFalse;
   1.768 +			}
   1.769 +		else
   1.770 +			{
   1.771 +			context.iJoinsAtEnd = endOfLine < inputLength?
   1.772 +				TRunInfoCompact::JoinBefore(input, endOfLine) : EFalse;
   1.773 +			}
   1.774 +		if (aOutputText)
   1.775 +			{
   1.776 +			for (const TRunInfoCompact* p = runArray; p != runArrayEnd; ++p)
   1.777 +				aOutputText = p->Reorder(aOutputText, context);
   1.778 +			}
   1.779 +		// Set 'start' to the beginning of the next line...
   1.780 +		start = endOfLine;
   1.781 +		
   1.782 +		// ...move it past any line break...
   1.783 +		const TInt sizeOfLineBreak = SizeLineBreak(input + start, input + inputLength);
   1.784 +		if (sizeOfLineBreak != 0)
   1.785 +			{
   1.786 +			start += sizeOfLineBreak;
   1.787 +			// ...and find the end of this next line.
   1.788 +			const TText* endLine = FindEndOfThisLine(input + start, input + inputLength);
   1.789 +			measureInput.iEndInputChar = endLine - input;
   1.790 +			}
   1.791 +		}
   1.792 +		
   1.793 +	return widestLineWidth;	
   1.794 +	}
   1.795 +
   1.796 +
   1.797 +/** Prepares the visually-ordered text according to the wrapping width and font 
   1.798 +specified. Text cannot be drawn until this has been done. 
   1.799 +
   1.800 +@param aWrappingWidth The maximum width of the text in pixels. Note that this 
   1.801 +distance should be slightly less than the available width to allow for characters 
   1.802 +such as "W" which can have side-bearings that leak into the margins.
   1.803 +@param aFont The font that will provide the character metrics.
   1.804 +@param aBreaker An object for breaking the lines. May be NULL for default behaviour. */
   1.805 +EXPORT_C void TBidiText::WrapText(TInt aWrappingWidth, const CFont& aFont,
   1.806 +	const MLineBreaker* aBreaker)
   1.807 +	{
   1.808 +	WrapText(aWrappingWidth, aFont, aBreaker, KMaxTInt);
   1.809 +	}
   1.810 +
   1.811 +/** Returns the original logically-ordered text supplied in the constructor. 
   1.812 +@return The original logically-ordered text supplied in the constructor. */
   1.813 +EXPORT_C TPtrC TBidiText::Text() const
   1.814 +	{
   1.815 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.816 +	const TText* text = me->LogicalText();
   1.817 +	return TPtrC(text, me->TextLength());
   1.818 +	}
   1.819 +
   1.820 +/** Returns the text as prepared for display, provided that WrapText has been called. 
   1.821 +If WrapText has not been called, a panic will result. 
   1.822 +
   1.823 +@return The text as prepared for display */
   1.824 +EXPORT_C TPtrC TBidiText::DisplayText() const
   1.825 +	{
   1.826 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.827 +	__ASSERT_ALWAYS(me->iVisualOrderedTextLength >= 0, BidiPanic(EBidiPanic_InvalidVisualOrderedTextLength));
   1.828 +	const TText* text = me->VisualText();
   1.829 +	return TPtrC(text, me->iVisualOrderedTextLength);
   1.830 +	}
   1.831 +
   1.832 +/** Returns the wrapping width previously supplied to WrapText. 
   1.833 +
   1.834 +@return The wrapping. */
   1.835 +EXPORT_C TInt TBidiText::WrappingWidth() const
   1.836 +	{
   1.837 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.838 +	return me->iWrappingWidth;
   1.839 +	}
   1.840 +
   1.841 +/** Returns the directionality of the text. 
   1.842 +
   1.843 +@return The directionality. */
   1.844 +EXPORT_C TBidiText::TDirectionality TBidiText::Directionality() const
   1.845 +	{
   1.846 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.847 +	return me->HasRightToLeftDirectionality() ? ERightToLeft : ELeftToRight;
   1.848 +	}
   1.849 +
   1.850 +/** Returns the truncation character used. 
   1.851 +
   1.852 +@return The truncation character. */
   1.853 +EXPORT_C TChar TBidiText::TruncationChar() const
   1.854 +	{
   1.855 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.856 +	return me->TruncationChar();
   1.857 +	}
   1.858 +
   1.859 +/** Reports the number of lines in the text to be drawn.
   1.860 +
   1.861 +WrapText must have been called already.
   1.862 +@return
   1.863 +	The number of lines in the text which would be drawn by DrawText.
   1.864 +*/
   1.865 +EXPORT_C TInt TBidiText::NumberOfLinesInDisplayText() const
   1.866 +	{
   1.867 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.868 +	if (me->iVisualOrderedTextLength <0)
   1.869 +		{
   1.870 +		return 0;
   1.871 +		}
   1.872 +	const TText* text = me->VisualText();
   1.873 +	const TText* textEnd = text + me->iVisualOrderedTextLength;
   1.874 +	return NumberOfLines(text, textEnd);
   1.875 +	}
   1.876 +
   1.877 +/** Returns the text as prepared for display, provided that WrapText has been called. 
   1.878 +If WrapText has not been called, a panic will result. 
   1.879 +@param aLine Line number to retrieve.
   1.880 +@param aWidth Returns the width in pixels of the line retrieved.
   1.881 +@return The text as prepared for display. */
   1.882 +EXPORT_C TPtrC TBidiText::LineOfDisplayText(TInt aLine, TInt& aWidthInPixels) const
   1.883 +	{
   1.884 +	const TBidiTextImp* me = TBidiTextImp::Imp(this);
   1.885 +	__ASSERT_ALWAYS(me->iVisualOrderedTextLength >= 0, BidiPanic(EBidiPanic_InvalidVisualOrderedTextLength));
   1.886 +	__ASSERT_ALWAYS(0 <= aLine && aLine < me->iLines, BidiPanic(EBidiPanic_InvalidLineNumber));
   1.887 +	aWidthInPixels = me->LineWidthArray()[aLine];
   1.888 +	const TText* text = me->VisualText();
   1.889 +	const TText* textEnd = text + me->iVisualOrderedTextLength;
   1.890 +	for (; aLine != 0; --aLine)
   1.891 +		{
   1.892 +		text = FindEndOfThisLine(text, textEnd);
   1.893 +		text += SizeLineBreak(text, textEnd);
   1.894 +		}
   1.895 +	const TText* endOfLine = FindEndOfThisLine(text, textEnd);
   1.896 +	return TPtrC(text, endOfLine - text);
   1.897 +	}
   1.898 +
   1.899 +/** Draws all of the text. WrapText must have been called already. 
   1.900 +
   1.901 +@param aGc The graphics context to draw the text to. The graphics context's 
   1.902 +font is assumed to have been set to the same font that was passed to the previous 
   1.903 +call to WrapText.
   1.904 +@param aLeft The left extreme of the baseline. Note that this should not be 
   1.905 +at the very edge of the available space, or characters such as "W" with left 
   1.906 +side bearings may be truncated.
   1.907 +@param aBaseLineSpacing The spacing between each line. If 0, only the first 
   1.908 +line is drawn.
   1.909 +@param aAlignment How to position the text horizontally. */
   1.910 +EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft,
   1.911 +	TInt aBaseLineSpacing, CGraphicsContext::TTextAlign aAlignment) const
   1.912 +	{
   1.913 +	TPoint origin;
   1.914 +	origin.iY = aLeft.iY;
   1.915 +	TInt lines = aBaseLineSpacing == 0? 1 : NumberOfLinesInDisplayText();
   1.916 +	TInt wrappingWidth = WrappingWidth();
   1.917 +	for (TInt i = 0; i != lines; ++i)
   1.918 +		{
   1.919 +		TInt width;
   1.920 +		TPtrC textLine = LineOfDisplayText(i, width);
   1.921 +		origin.iX = aLeft.iX;
   1.922 +		if (aAlignment != CGraphicsContext::ELeft)
   1.923 +			{
   1.924 +			TInt excess = wrappingWidth - width;
   1.925 +			origin.iX += aAlignment != CGraphicsContext::ECenter?
   1.926 +				excess : excess >> 1;
   1.927 +			}
   1.928 +		aGc.DrawText(textLine, origin);
   1.929 +		origin.iY += aBaseLineSpacing;
   1.930 +		}
   1.931 +	}
   1.932 +
   1.933 +/** Draws all of the text. Alignment is taken from the directionality of the text. 
   1.934 +WrapText must have been called already. 
   1.935 +
   1.936 +@param aGc The graphics context to draw the text to. The graphics context's 
   1.937 +font is assumed to have been set to the same font that was passed to the previous 
   1.938 +call to WrapText.
   1.939 +@param aLeft The left extreme of the baseline. Note that this should not be 
   1.940 +at the very edge of the available space, or characters such as "W" with left 
   1.941 +side bearings may be truncated.
   1.942 +@param aBaseLineSpacing The spacing between each line. If 0, only the first 
   1.943 +line is drawn. */
   1.944 +EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft,
   1.945 +	TInt aBaseLineSpacing) const
   1.946 +	{
   1.947 +	DrawText(aGc, aLeft, aBaseLineSpacing,
   1.948 +		Directionality() == ELeftToRight?
   1.949 +		CGraphicsContext::ELeft : CGraphicsContext::ERight);
   1.950 +	}
   1.951 +
   1.952 +/** Draws the first line of the text. WrapText must have been called already. Alignment 
   1.953 +is taken from the directionality of the text. 
   1.954 +
   1.955 +@param aGc The graphics context to draw the text to. The graphics context's 
   1.956 +font is assumed to have been set to the same font that was passed to the previous 
   1.957 +call to WrapText.
   1.958 +@param aLeft The left extreme of the baseline. Note that this should not be 
   1.959 +at the very edge of the available space, or characters such as "W" with left 
   1.960 +side bearings may be truncated. */
   1.961 +EXPORT_C void TBidiText::DrawText(CGraphicsContext& aGc, const TPoint& aLeft) const
   1.962 +	{
   1.963 +	DrawText(aGc, aLeft, 0);
   1.964 +	}
   1.965 +