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