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