--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/tagma/TMGLYPH.CPP Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,549 @@
+/*
+* Copyright (c) 1999-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:
+* Text caching for TAGMA: cacheing of text and formats extracted from a text source,
+*
+*/
+
+
+#include "TMSTD.H"
+#include <bidi.h>
+
+
+inline TBool IsSurrogate(TText a) { return 0xD800 == (a & 0xF800); }
+inline TBool IsHighSurrogate(TText a) { return 0xD800 == (a & 0xFC00); }
+inline TBool IsLowSurrogate(TText a) { return 0xDC00 == (a & 0xFC00); }
+inline TChar JoinSurrogates(TText aHigh, TText aLow)
+ {
+ return ((aHigh - 0xd7f7) << 10) + aLow;
+ }
+
+inline TText16 GetHighSurrogate(TUint aChar)
+ {
+ return STATIC_CAST(TText16, 0xD7C0 + (aChar >> 10));
+ }
+
+inline TText16 GetLowSurrogate(TUint aChar)
+ {
+ return STATIC_CAST(TText16, 0xDC00 | (aChar & 0x3FF));
+ }
+
+TUint RTmTextCache::Char(TInt aPos)
+ {
+ if (aPos < 0 || aPos > iDocumentLength)
+ return 0xFFFF;
+ if (aPos == iDocumentLength)
+ return CEditableText::EParagraphDelimiter;
+ if (aPos < iTextStart || aPos >= iTextStart + iTextLength)
+ {
+ TPtrC p;
+ GetText(aPos,KMaxTInt,p);
+ }
+ if ( IsHighSurrogate( iText[aPos-iTextStart] ) )
+ {
+ if (iTextStart+iTextLength-aPos > 1)
+ {
+ TText high = iText[aPos-iTextStart];
+ TText low = iText[aPos-iTextStart+1];
+ if ( IsLowSurrogate( low ) )
+ return JoinSurrogates( high, low );
+ else
+ // If the corrupt surrogate, it is treated as what it is mapped
+ return iSource.Map( high );
+ }
+ else
+ {
+ // If the corrupt surrogate, it is treated as what it is mapped
+ return iSource.Map( iText[aPos-iTextStart] );
+ }
+ }
+ else
+ {
+ return iText[aPos - iTextStart];
+ }
+ }
+
+/**
+Measure the width of some text, substituting glyphs where necessary.
+
+@param aStart Start position of text in document
+@param aEnd (Exclusive) end position of text in document
+@param aRightToLeft True if the text is right-to-left
+@param aMaxAdvance Measurement will stop if this advance is exceeded
+@param aOutput Output form text measurement function
+@param aExtraChar
+ Fetch this much context beyond aEnd. This helps when the text is to be
+ truncated, and aEnd is not certain to be at a cluster boundary.
+@return Advance width. Side-bearings are not included. */
+TInt RTmTextCache::AdvanceWidthL(TInt aStart, TInt aEnd, TBool aRightToLeft,
+ TInt aMaxAdvance, CFbsFont::TMeasureTextOutput* aOutput, TInt aExtraChar)
+ {
+ // Get the displayed text, up to the copy fit width if necessary.
+ TInt length = aEnd - aStart;
+ TText buffer[KMaxTextChunkSize + 2 + 1];
+ if (length > KMaxTextChunkSize)
+ TmPanic(ETextWidthBufferOverflow);
+
+ CTmTextFontCache *font = NULL;
+
+ // Get the text in logical order. If a custom interface to draw (in later stages)
+ // the text in context exists, then measure the text with context.
+ TInt error;
+ if (Source().GetExtendedInterface(KTmCustomExtensionUid))
+ {
+ error = GetDisplayedText(aStart, aEnd + aExtraChar,
+ aRightToLeft ? ELogicalRightToLeft : ELeftToRight,
+ buffer,iContextCharPerChunk, 0, &font);
+ }
+ else
+ {
+ error = GetDisplayedText(aStart, aEnd + aExtraChar,
+ aRightToLeft ? ELogicalRightToLeft : ELeftToRight,
+ buffer,0, 0, &font);
+ }
+
+ // Don't push font onto cleanup stack because this method only leaves below.
+ // If there was an error in GetDisplayedText, font will have been closed
+ if (error)
+ User::Leave(error);
+
+ CFont::TMeasureTextInput input;
+ input.iFlags = aRightToLeft?
+ CFont::TMeasureTextInput::EFVisualOrderRightToLeft
+ : CFont::TMeasureTextInput::EFVisualOrder;
+
+ // Create a new buffer which will have the context as well
+ TPtrC inputText(buffer, length + 2 + aExtraChar);
+ input.iStartInputChar = 1;
+ input.iEndInputChar = input.iStartInputChar + length;
+ input.iMaxBounds = aMaxAdvance;
+ input.iFlags |= CFont::TMeasureTextInput::EFIncludePenPositionInBoundsCheck;
+ CFbsFont::TMeasureTextOutput output;
+ CFbsFont::TMeasureTextOutput* pOutput = aOutput? aOutput : &output;
+ // Measure this text in the context supplied. Any punctuations in this chunk
+ // will now be measured based on this context.
+ TInt width = font->Font().MeasureText(inputText, &input, pOutput);
+ // Do not report initial Zero-Width Joiner or 0xFFFF
+ if (0 != pOutput->iChars)
+ --pOutput->iChars;
+
+ // Find the new context at the end of this chunk. This will be used to pass on to the next chunk.
+ // If the context hasn't changed, pass on the previous context.
+ TUint last;
+ TInt textLength = input.iStartInputChar + pOutput->iChars;
+ TChar::TBdCategory cat = TChar::ELeftToRight;
+ while (textLength != 0)
+ {
+ TUint charSize = 1;
+ last = *(inputText.Ptr() + textLength);
+ if ( IsSurrogate( last ) && textLength > 1 )
+ {
+ TText low = *(inputText.Ptr() + textLength);
+ TText high = *(inputText.Ptr() + textLength - 1);
+ if ( IsLowSurrogate( low ) && IsHighSurrogate( high ) )
+ {
+ charSize = 2;
+ last = JoinSurrogates( high, low );
+ }
+ else
+ {
+ // If the corrupt surrogate, it is treated as what it is mapped
+ last = iSource.Map( last );
+ }
+ }
+
+ cat = TChar(last).GetBdCategory();
+ if (last != 65535 && textLength !=0 &&
+ (cat == TChar::ELeftToRight ||
+ cat == TChar::ERightToLeft ||
+ cat == TChar::ERightToLeftArabic))
+ {
+ iContextCharPerChunk = last;
+ break;
+ }
+ textLength -= charSize;
+ }
+
+ // Check if this chunk is a punctuation chunk. If it is not, the context in the byte code for this chunk
+ // will be NULL. Otherwise it will just be the previous context.
+ if (cat == TChar::ELeftToRight ||
+ cat == TChar::ERightToLeft ||
+ cat == TChar::ERightToLeftArabic ||
+ cat == TChar::EWhitespace)
+ {
+ iContextCharInByteCode = NULL;
+ }
+ else
+ iContextCharInByteCode = iContextCharPerChunk;
+
+ font->Close();
+
+ return width;
+ }
+
+/** Measure the width of some text, substituting glyphs where necessary.
+@param aStart Start position of text in document
+@param aEnd (Exclusive) end position of text in document
+@param aRightToLeft True if the text is right-to-left
+@return width including side-bearings */
+TInt RTmTextCache::TotalWidthL(TInt aStart, TInt aEnd,
+ TBool aRightToLeft)
+ {
+ CFbsFont::TMeasureTextOutput output;
+ TInt width = AdvanceWidthL(aStart, aEnd, aRightToLeft, KMaxTInt, &output);
+ // Add on any protruding side-bearings
+ if (width < output.iBounds.iBr.iX)
+ width = output.iBounds.iBr.iX;
+ if (output.iBounds.iTl.iX < 0)
+ width -= output.iBounds.iTl.iX;
+ return width;
+ }
+
+
+/*
+Get source text starting at aPos and not more than (aMaxEndChar - aPos) characters.
+If aFormat is non-null get the format.
+@param aFont If aFont is not null, on return contains a pointer to an opened CTmTextFontCache*
+ The caller must call Close on aFont when finished with the font.
+@return Failure condition from getting the font, if any.
+*/
+TInt RTmTextCache::GetText(TInt aPos,TInt aMaxEndChar,TPtrC& aText,TTmCharFormat* aFormat,CTmTextFontCache** aFont)
+ {
+ TInt error = KErrNone;
+ if (aPos < iTextStart || aPos >= iTextStart + iTextLength)
+ {
+ iTextStart = aPos;
+ iTextBuffer.Zero();
+ TPtrC p;
+ TTmCharFormat new_format;
+ iSource.GetText(iTextStart,p,new_format);
+ if (!(new_format.iFontSpec == iFormat.iFontSpec))
+ {
+ ReleaseFont();
+ }
+ iFormat = new_format;
+ iText = p.Ptr();
+ iTextLength = p.Length();
+ if (iTextLength == 0)
+ TmPanic(EZeroLengthTextSupplied);
+ }
+ int offset = aPos - iTextStart;
+ aText.Set(iText + offset,iTextLength - offset);
+ if (aText.Length() > aMaxEndChar - aPos)
+ {
+ const TText* p = aText.Ptr();
+ aText.Set(p,aMaxEndChar - aPos);
+ }
+ if (aFormat)
+ *aFormat = iFormat;
+ if (aFont)
+ {
+ if (iFont == NULL)
+ {
+ TFontSpec fs;
+ iFormat.iFontSpec.GetTFontSpec(fs);
+ CFont *font = NULL;
+ error = iDevice.GetNearestFontInTwips(font,fs);
+ if (font != NULL)
+ {
+ iFont = CTmTextFontCache::New(iDevice, *font);
+ if (iFont == NULL)
+ {
+ iDevice.ReleaseFont(font);
+ error = KErrNoMemory;
+ }
+ }
+ }
+ if (iFont && error == KErrNone)
+ {
+ *aFont = iFont;
+ iFont->Open();
+ }
+ }
+ return error;
+ }
+
+/** Finds the extent of the run of characters that have the same format as
+aFormat following aPos, up to aMaxEndChar.
+@internalComponent */
+TInt FollowOnTextEnd(TInt aPos, TInt aMaxEndChar,
+ const TTmCharFormat& aFormat, MTmSource& aSource, TBool forcedExtend = EFalse)
+ {
+ TTmCharFormat format;
+ TPtrC text;
+ while (aPos < aMaxEndChar)
+ {
+ aSource.GetText(aPos, text, format);
+ // The forcedExtend flag is used for only one case that to prevent single ZWJ being
+ // returned by RTmTextCache::GetTextL. Please see RTmTextCache::GetTextL.
+ if (format != aFormat && !forcedExtend)
+ return aPos;
+ TInt length = text.Length();
+ if (length == 0)
+ return aPos;
+ if (text[0] == CEditableText::EPictureCharacter)
+ {
+ return aPos;
+ }
+ aPos += length;
+ }
+ return aMaxEndChar;
+ }
+
+/** Copys the specified text from the source into the buffer.
+@internalComponent */
+void AppendTextToBuffer(TDes& aBuffer,
+ TInt aStart, TInt aEnd, MTmSource& aSource)
+ {
+ TTmCharFormat dummy;
+ while (aStart != aEnd)
+ {
+ TPtrC text;
+ aSource.GetText(aStart, text, dummy);
+ TInt textEnd = aStart + text.Length();
+ __ASSERT_ALWAYS(aStart < textEnd, TmPanic(EZeroLengthTextSupplied));
+ if (aEnd < textEnd)
+ {
+ // This run is overlong, so just append all that will fit
+ aBuffer.Append(&text[0], aEnd - aStart);
+ return;
+ }
+ aBuffer.Append(text);
+ aStart = textEnd;
+ }
+ }
+
+/** Same as GetText but will join text together if it has the same format.
+@param
+ aText the text returned. Is valid until the next call of GetText, GetTextL
+ or Close.
+@param aFont If aFont is not null, on return contains a pointer to an opened CTmTextFontCache*
+ The caller must call Close on aFont when finished with the font.
+@internalComponent */
+TInt RTmTextCache::GetTextL(TInt aPos, TInt aMaxEndChar, TPtrC& aText,
+ TTmCharFormat* aFormat, CTmTextFontCache** aFont)
+ {
+ TInt bufferEnd = iTextBufferStart + iTextBuffer.Length();
+ if ( iTextBufferStart <= aPos && aPos < bufferEnd
+ && (aMaxEndChar < bufferEnd || iTextBufferEndsInFormatChange) )
+ {
+ TInt textEnd = Min(bufferEnd, aMaxEndChar);
+ aText.Set(&iTextBuffer[aPos - iTextBufferStart], textEnd - aPos);
+ if (aFormat)
+ *aFormat = iFormat;
+ if (aFont)
+ {
+ *aFont = iFont;
+ iFont->Open();
+ }
+
+ return KErrNone;
+ }
+
+ TInt error = GetText(aPos, aMaxEndChar, aText, aFormat, aFont);
+ TBool forcedExtend = EFalse;
+ const TText* ch = aText.Ptr();
+ // When the text is just a ZWJ, the following text should be forced extended, because the ZWJ should
+ // never be returned as a single text chunk.
+ if (aText.Length() == 1 && *ch == KZeroWidthJoiner)
+ forcedExtend = ETrue;
+ if (error == KErrNone)
+ {
+ CleanupClosePushL(**aFont);
+ TInt endOfAText = aPos + aText.Length();
+
+ TInt end = FollowOnTextEnd(endOfAText, aMaxEndChar, iFormat, iSource, forcedExtend);
+ if (end != endOfAText)
+ {
+ iTextBuffer.Zero();
+ if (iTextBuffer.MaxLength() < end - aPos)
+ iTextBuffer.ReAllocL(end - aPos);
+
+ iTextBuffer.Append(aText);
+ AppendTextToBuffer(iTextBuffer, endOfAText, end, iSource);
+ iTextBufferStart = aPos;
+ iTextBufferEndsInFormatChange = end < aMaxEndChar;
+ aText.Set(iTextBuffer);
+ }
+ CleanupStack::Pop(*aFont);
+ }
+ return error;
+ }
+
+TBool RTmTextCache::IsArabicPoint(TInt aChar)
+ {
+ return 0x64B <= aChar && aChar < 0x671
+ && !(0x656 <= aChar && aChar < 0x670);
+ }
+
+TUint SupplementaryCharMap(MTmSource *aSource, TText aHigh, TText aLow)
+ {
+ TChar c(aHigh);
+ if ( IsHighSurrogate( aHigh ) && IsLowSurrogate( aLow ))
+ c = JoinSurrogates( aHigh, aLow );
+ return aSource->Map(c);
+ }
+
+/**
+Gets all the displayed text in the range aStart...aEnd and puts it into a
+buffer that must be at least aEnd - aStart + 2 characters in length. If
+aFormat is non-null gets the format of the first section of text. If aFont
+is non-null gets the font for the first section of text. If aDirectionality
+is EVisualRightToLeft reverses the text and mirrors appropriate characters.
+Adds a zero-width joiner to the start and/or end of the text returned if
+these are necessary for the correct contextual glyph choice. Adds a
+0xFFFF to each end if this is not required.
+@param aFont If aFont is not null, on return contains a pointer to an opened CTmTextFontCache*
+ The caller must call Close on aFont when finished with the font.
+@return Failure condition on getting font, if any.
+@internalComponent
+*/
+TInt RTmTextCache::GetDisplayedText(TInt aStart, TInt aEnd,
+ TDisplayedTextDirectionality aDirectionality, TText* aBuffer,TUint aContextChar,
+ TTmCharFormat* aFormat, CTmTextFontCache** aFont)
+ {
+ __ASSERT_DEBUG(aStart <= aEnd, TmPanic(EBadArg));
+ if (aContextChar == 0)
+ aBuffer[0] = 0xFFFF;
+ else
+ aBuffer[0] = aContextChar;
+ if (aEnd == aStart)
+ return KErrGeneral;
+ TPtrC text;
+ TText* output = aBuffer;
+ TTmCharFormat format;
+
+ // might the (logically) first character join with the previous one?
+ GetText(aStart, aStart + 1, text, &format);
+ if (aFormat)
+ *aFormat = format;
+ TInt c = iSource.Map(text[0]);
+ if (IsHighSurrogate(text[0]))
+ {
+ GetText(aStart, aStart + 2, text, &format);
+ if (aFormat)
+ *aFormat = format;
+ if (text.Length() >= 2)
+ {
+ if (IsLowSurrogate(text[1]))
+ {
+ c = SupplementaryCharMap(&iSource, text[0], text[1]);
+ }
+ else
+ {
+ c = iSource.Map(text[0]);
+ }
+ }
+ }
+
+ if (0 < aStart && CFont::CharactersJoin(
+ aDirectionality == ELeftToRight? KZeroWidthJoiner : c,
+ aDirectionality == ELeftToRight? c : KZeroWidthJoiner))
+ {
+ TInt prev = aStart - 1;
+ TTmCharFormat prevFormat;
+ GetText(prev, aStart, text, &prevFormat);
+ TInt prevChar = iSource.Map(text[0]);
+ while (0 < prev && IsArabicPoint(prevChar))
+ {
+ GetText(--prev, aStart, text, &prevFormat);
+ prevChar = iSource.Map(text[0]);
+ }
+ if (CFont::CharactersJoin(
+ aDirectionality == ELeftToRight? prevChar : c,
+ aDirectionality == ELeftToRight? c : prevChar)
+ && format.iFontSpec == prevFormat.iFontSpec)
+ // Characters join at the beginning.
+ *output = KZeroWidthJoiner;
+ }
+
+ output = aBuffer + 1;
+ TInt error = KErrNone;
+ while (aStart < aEnd)
+ {
+ TInt currentError = GetText(aStart, aEnd, text, &format, aFont);
+ // do not have to push aFont onto clean-up stack because this is a non-leaving method
+ if (aFont)
+ error = currentError;
+ aFont = NULL;
+ TInt length = text.Length();
+ aStart += length;
+ const TText* input = text.Ptr();
+ const TText* end = input + length;
+ while (input < end)
+ {
+ if (IsHighSurrogate(*input))
+ {
+ if (input+1 < end && IsLowSurrogate(input[1]))
+ {
+ c = SupplementaryCharMap(&iSource, input[0], input[1]);
+ input++;
+ input++;
+ *output++ = GetHighSurrogate( c );
+ *output++ = GetLowSurrogate( c );
+ }
+ else
+ {
+ c = iSource.Map(input[0]);
+ input++;
+ *output++ = static_cast<TText>(c);
+ }
+ }
+ else if (IsLowSurrogate(*input))
+ {
+ c = iSource.Map(input[0]);
+ input++;
+ *output++ = static_cast<TText>(c);
+ }
+ else
+ {
+ c = iSource.Map(*input++);
+ *output++ = static_cast<TText>(c);
+ }
+ }
+ }
+
+ *output = 0xFFFF;
+ // might the (logically) last character join with the next one?
+ if (aStart < iSource.DocumentLength())
+ {
+ const TText* prev = output;
+ while (--prev != aBuffer && IsArabicPoint(*prev)) {}
+ if (CFont::CharactersJoin(
+ aDirectionality == ELeftToRight? *prev : KZeroWidthJoiner,
+ aDirectionality == ELeftToRight? KZeroWidthJoiner : *prev))
+ {
+ TTmCharFormat nextFormat;
+ GetText(aStart, aStart + 1, text, &nextFormat);
+ TInt nextChar = iSource.Map(text[0]);
+ if (CFont::CharactersJoin(
+ aDirectionality == ELeftToRight? *prev : nextChar,
+ aDirectionality == ELeftToRight? nextChar : *prev)
+ && format.iFontSpec == nextFormat.iFontSpec)
+ // Characters join at the end.
+ *output = KZeroWidthJoiner;
+ }
+ }
+
+ if (aDirectionality == EVisualRightToLeft)
+ TBidirectionalState::ReverseGroups(aBuffer, output + 1 - aBuffer);
+
+ return error;
+ }
+
+void RTmTextCache::SetContextChar(TUint aContextChar)
+ {
+ iContextCharPerChunk = aContextChar;
+ }
+