diff -r 000000000000 -r bde4ae8d615e os/textandloc/fontservices/textbase/sgdi/GlyphSel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/textandloc/fontservices/textbase/sgdi/GlyphSel.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,593 @@ +// Copyright (c) 2003-2010 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: +// + +/** + @file + @internalComponent +*/ + + +//#include +#include +#include "GlyphSel.h" +#include "TextBasePanic.h" + + +static const TText16 KLatinGlyph_SoftHyphen = 0x00AD; + + +// +// +// TUtf32Iterator Class definition +// +// + + +TUint TUtf32Iterator::UTF16ToTChar(const TText16* a) +/** + This routine takes an encoded UTF16 byte array and decodes the + first character at the start of the array and returns it as a TChar. + If the char is "not a char" character 0xFFFF results. +@param a + UTF16 byte array to be decoded. +@param aPr + Position pointer 'a' derived from, incremented if surragote pairs decoded. +@return + The character value in UTF32 format or 0xFFFF it not a character. +*/ + { + // Is next char a surrogate? + if (0xD800 == (a[0] & 0xF800)) + { + // Is it a high surrogate in the range D800..DBFF? + if (0xD800 == (a[0] & 0xFC00)) + { + // Its a high surrogate, is the next char a low surrogate? + if (0xDC00 == (a[1] & 0xFC00)) + { + // It's a low surrogate + return ((a[0] - 0xd7f7) << 10) + a[1]; + } + else + return 0xFFFF; + } + else + return 0xFFFF; + } + else + return a[0]; + } + + +TUtf32Iterator::TUtf32Iterator(const TText16* aStart, const TText16* aEnd, TInt aStartingIndex) +/** + Construct iterator given UTF16 encoded byte array. +@param aStart + Start address of the array. +@param aEnd + Address of the byte just beyond the end of the array. +@param aStartingIndex + Optional UTF16 offset into the array to initialise the current position to. +@panic ETextBasePanic_InvalidInputParam + Raised when array start if passed the array end. +*/ +: iStart(aStart), iCurrent(aStart+aStartingIndex), iEnd(aEnd), iChar(0xffff) + { + TEXTBASE_ASSERT_DEBUG(iStart < iEnd, ETextBasePanic_InvalidInputParam); + + if (iCurrent > iEnd) + iCurrent = iEnd; + else if (iCurrent < iStart) + iCurrent = iStart; + else + { + // Sanatise array end checking for an unpaired surrogate value + // so that UTF16ToTChar() does not read off the end of the array. + if (0xD800 == (iEnd[-1] & 0xFC00)) + { + if (iCurrent == iEnd-1) + ++iCurrent; + else + --iEnd; + } + + // Setup initial position UTF32 character value + iChar = UTF16ToTChar(iCurrent); + } + } + + +TChar TUtf32Iterator::Next() +/** +Moves the iterator forward to the next valid UTF32 character value. +@return TChar The next character in the text towards the end. +@panic ETextBasePanic_OutOfText +Raised when there is no next position to move to. +*/ + { + TEXTBASE_ASSERT_DEBUG(iCurrent < iEnd, ETextBasePanic_OutOfText); + + iCurrent += (iChar > 0xffff) ? 2 : 1; + if (iCurrent < iEnd) + iChar = UTF16ToTChar(iCurrent); + else + iChar = 0xFFFF; + return iChar; + } + + +TChar TUtf32Iterator::Prev() +/** +Moves the iterator backwards to the next valid UTF32 character value. +@return TChar The prev character in the text towards the start. +@panic ETextBasePanic_OutOfText Raised when there is no next position to move to. +*/ + { + TEXTBASE_ASSERT_DEBUG(iCurrent >= iStart, ETextBasePanic_OutOfText); + + --iCurrent; + if (iCurrent >= iStart) + iChar = UTF16ToTChar(iCurrent); + else + iChar = 0xFFFF; + return iChar; + } + + +void TUtf32Iterator::SetPos(TInt aPos) +/** + Moves the iterator to the position specified by array start+offset. +@param aPos + UTF16 offset into the array to set the current position to. +@panic ETextBasePanic_OutOfText + Raised when there is no next position to move to. +*/ + { + TEXTBASE_ASSERT_DEBUG(iStart+aPos <= iEnd, ETextBasePanic_OutOfText); + TEXTBASE_ASSERT_DEBUG(iStart+aPos >= iStart, ETextBasePanic_OutOfText); + + iCurrent = iStart+aPos; + iChar = UTF16ToTChar(iCurrent); + } + + +TUint TUtf32Iterator::Get(TInt offset) +/** + Returns the UTF32 char value at the offset specified. 0xFFFF may be returned + for unpaired surrogate and noncharacters. Does not change the current + position. +@param offset + UTF16 offset from current iterator position to get UTF32 char form. +@return TChar + UTF32 char value found at the iterator+offset, or 0xFFFF in error. +@panic ETextBasePanic_OutOfText + Raised when offset found to be outside the bounds of the original text array. +*/ + { + TEXTBASE_ASSERT_DEBUG(iCurrent+offset >= iStart, ETextBasePanic_OutOfText); + TEXTBASE_ASSERT_DEBUG(iCurrent+offset < iEnd, ETextBasePanic_OutOfText); + + return UTF16ToTChar(iCurrent+offset); + } + + +TChar TUtf32Iterator::GetThenNext() +/** + Return the UTF32 value at the current position. +@return TChar + UTF32 value currently pointed to by iterator. +@panic ETextBasePanic_EndOfText + Raised when current iterator position is not valid. +*/ + { + TEXTBASE_ASSERT_DEBUG(iCurrent < iEnd, ETextBasePanic_OutOfText); + + TChar current(iChar); + iCurrent += (iChar > 0xffff) ? 2 : 1; + if (iCurrent < iEnd) + iChar = UTF16ToTChar(iCurrent); + else + iChar = 0xFFFF; + return current; + } + + +TChar TUtf32Iterator::GetThenPrev() +/** + Return the UTF32 value at the current position. +@return TChar + UTF32 value currently pointed to by iterator. +@panic ETextBasePanic_EndOfText + Raised when current iterator position is not valid. +*/ + { + TEXTBASE_ASSERT_DEBUG(iCurrent >= iStart, ETextBasePanic_OutOfText); + + TChar current(iChar); + --iCurrent; + if (iCurrent >= iStart) + iChar = UTF16ToTChar(iCurrent); + else + iChar = 0xFFFF; + return current; + } + + +TInt TUtf32Iterator::LengthToStart() const +/** + Returns the number of TText16 codes between the start point and its + current position. +@return TInt + Number of TText16 characters between array start and current iterator + position. +*/ + { + return iCurrent-iStart; + } + + +TInt TUtf32Iterator::LengthToEnd() const +/** + Returns the number of remaining TText16 codes still ahead of the + iterator. +@return TInt + Number of TText16 characters between array current iterator position + and the end of the array. +*/ + { + return iEnd - iCurrent; + } + +const TText16* TUtf32Iterator::CurrentPosition() const + { + return iCurrent; + } + +void TUtf32Iterator::SetCurrentPosition(const TText16* a) + { + iCurrent = a; + } + +// +// +// TGlyphSelectionState Class definition +// +// + + +/** + The Unicode Combining Class values recognised by the + GlyphSelUtils::CombineLastGlyphToBase method. +@internalComponent +*/ +enum TCombiningClass + { + EArabicFathatan = 27, + EArabicDammatan = 28, + EArabicKasratan = 29, + EArabicFatha = 30, + EArabicDamma = 31, + EArabicKasra = 32, + EArabicShadda = 33, + EArabicSukun = 34, + ECombineBelowLeftAttached = 200, + ECombineBelowAttached = 202, + ECombineBelowRightAttached = 204, + ECombineLeftAttached = 208, + ECombineRightAttached = 210, + ECombineAboveLeftAttached = 212, + ECombineAboveAttached = 214, + ECombineAboveRightAttached = 216, + ECombineBelowLeft = 218, + ECombineBelow = 220, + ECombineBelowRight = 222, + ECombineLeft = 224, + ECombineRight = 226, + ECombineAboveLeft = 228, + ECombineAbove = 230, + ECombineAboveRight = 232 + }; + + +/** + This method is called to attach (by adjusing its bounding box) the current end + glyph in the output array of iParam to the base glyph bounding box based on + the Unicode combining class of the character. +@param aGss + The general input/output glyph selection data for the routine. +@param aGss.iOutput + Input: Glyph cluster with last glyph an actual combining character. Output: + Bounding box of last glyph adjusted according to char combining class. +@param aFirstDiacritic + Which character in the output array to treat as the first diacritic of the + cluster. Usually 1, but can be more if the base class is a ligature. +*/ +void TGlyphSelectionState::CombineLastGlyphToBase(const TRect& aBase, TInt aFirstDiacritic) + { + // Get the bounds of all the base characters. + TRect base = aBase; + int last = iParam.iOutputGlyphs-1; + for (int i = aFirstDiacritic; i < last; i++) + base.BoundingRect(iParam.iOutput[i].iBounds); + + // Calculate the attachment points. + TRect& r = iParam.iOutput[last].iBounds; + int w = r.Width(); + int h = r.Height(); + int t = r.iTl.iY; + int l = r.iTl.iX; + int left = base.iTl.iX; + int center = base.iTl.iX + (base.Width() - w) / 2; + int right = base.iBr.iX - w; + int below = base.iBr.iY; + int above = base.iTl.iY - h; + int left_of = left - w; + int right_of = right + w; + int xGap = 1; + int yGap = iFont->HeightInPixels()/10; + + // Select attachment based on combining class. + switch (iCombCls) + { + case ECombineBelowLeftAttached: + t = below; + l = left; + break; + case ECombineBelowAttached: + t = below; + l = center; + break; + case ECombineBelowRightAttached: + t = below; + l = right; + break; + case ECombineLeftAttached: + l = left_of; + break; + case ECombineRightAttached: + l = right_of; + break; + case ECombineAboveLeftAttached: + t = above; + l = left; + break; + case ECombineAboveAttached: + t = above; + l = center; + break; + case ECombineAboveRightAttached: + t = above; + l = right; + break; + case ECombineBelowLeft: + t = below + yGap; + l = left; + break; + case ECombineBelow: + case EArabicKasratan: + case EArabicKasra: + t = below + yGap; + l = center; + break; + case ECombineBelowRight: + t = below + yGap; + l = right; + break; + case ECombineLeft: + l = left_of - xGap; + break; + case ECombineRight: + l = right_of + xGap; + break; + case ECombineAboveLeft: + t = above - yGap; + l = left; + break; + case ECombineAbove: + case EArabicFathatan: + case EArabicDammatan: + case EArabicFatha: + case EArabicDamma: + case EArabicShadda: + case EArabicSukun: + t = above - yGap; + l = center; + break; + case ECombineAboveRight: + t = above - yGap; + l = right; + break; + default: + l = center; + break; + } + + // Adjust the bounding box of the last glyph to fix position + // based on the characters combining class. For speed, do directly. + // r.SetRect(l,t,l + w,t + h); + r.iTl.iX = l; + r.iTl.iY = t; + r.iBr.iX = l+w; + r.iBr.iY = t+h; + } + + +TBool TGlyphSelectionState::AppendGlyphToCluster(TUint aCode) +/** + This common method is used by glyph selector classes to add a glyph to + the end of the aGss.iParam output field filling in all the glyph info + needed. +@param aCode + The Unicode character for which a glyph should be appended. +@param aGss + The general input/output glyph selection data for the routine. +@return TBool + ETrue when successful, EFalse when failure occurs e..g no char data, overflow +*/ + { + // Setup reference to next free glyph record we need to update. + TEXTBASE_ASSERT_DEBUG(iParam.iOutputGlyphs < CFont::TPositionParam::EMaxOutputGlyphs, + ETextBasePanic_InvalidInputParam); + + CFont::TPositionParam::TOutput* output = iParam.iOutput+iParam.iOutputGlyphs; + + // Retrieve the glyph details from the Font. Essential to proceed, abort + // if not available. + TOpenFontCharMetrics metrics; + if (iFont->GetCharacterData(aCode, metrics, output->iBitmap, + output->iBitmapSize) == CFont::ENoCharacterData) + return EFalse; + + // Set code point of glyph in output record. + output->iCode = aCode; + + // Set the glyph's bounds in the output record and record pen advancement. + if (iParam.iDirection == CFont::EVertical) + { + metrics.GetVertBounds(output->iBounds); + iAdvance.iHeight = Max(iAdvance.iHeight, metrics.VertAdvance()); + } + else + { + metrics.GetHorizBounds(output->iBounds); + iAdvance.iWidth = Max(iAdvance.iWidth, metrics.HorizAdvance()); + } + + // Next adjust the glyph's bounding box to offset it from the pen + // position (origin of drawing). For speed increment attributes directly. + // output->iBounds.Move(aGss.iParam.iPen); + output->iBounds.iTl.iX += iParam.iPen.iX; + output->iBounds.iBr.iX += iParam.iPen.iX; + output->iBounds.iTl.iY += iParam.iPen.iY; + output->iBounds.iBr.iY += iParam.iPen.iY; + + // Before we exit with success, increment the glyph array counter. + // for the new glyph we've added here. + iParam.iOutputGlyphs++; + return ETrue; + } + + +// +// +// GlyphSelector_SoftHyphen Class definition +// +// + +TBool GlyphSelector_SoftHyphen::Process(TGlyphSelectionState& aGss, RShapeInfo&) +/** +@see GlyphSelUtils + See this class for the method description. +*/ + { + aGss.iText.Next(); + if (!aGss.iText.AtEnd()) + { + // Here we skip & don't output hyphen since its not at the end a line. + aGss.iPen = TGlyphSelectionState::EPenAdvance_No; + } + else + { + // If we reach here we must output hyphen. + if (!aGss.AppendGlyphToCluster(KLatinGlyph_SoftHyphen)) + return EFalse; + + aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes; + } + + // Logic to determine if we are now at the end of the glyph cluster. + // Default logic, based on whether a combining mark follows or not. + aGss.iClusterState = + (!aGss.iText.AtEnd() && + ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ? + TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete; + + return ETrue; + } + + +// +// +// GlyphSelector_Default Class definition +// +// + + +TBool GlyphSelector_Default::Process(TGlyphSelectionState& aGss, RShapeInfo&) +/** +@see GlyphSelUtils + See this class for the method description. +*/ + { + + // In this method we always output the glyph. + if (!aGss.AppendGlyphToCluster(aGss.iText.GetThenNext())) + return EFalse; + + // Adjust glyph's bounds further to position this character if it is a + // combining mark + if (aGss.IsCombiningClass()) + { + aGss.iPen = TGlyphSelectionState::EPenAdvance_No; + + TRect baseBounds(aGss.iParam.iOutput[0].iBounds); + // Get first character in this glyph cluster. In this default process function, the iCode should + // be Unicode Point Code. + TChar startChar = TChar(aGss.iParam.iOutput[0].iCode); + // Character index in the output array to treat as the first diacritic of the + // cluster. It will be used as first character for combine to. usually 1, but when + // the cluster starts with a combining mark, it should be set to 0. + TInt indexOfFirstCombining = 1; + TInt startCharCat = startChar.GetCategory() & 0xF0; + + // if the first character in this cluster is a combining mark or a graphically empty character, + // (such as a space Character0x0020), a fake bound, formed from the Ascent of the font, will be + // used for combining + if ((startCharCat == TChar::EMarkGroup) || baseBounds.Size() == TSize(0,0)) + { + // Determine the height of the combining glyph. + TInt glyphHeight = 0; + if (aGss.iParam.iOutputGlyphs == 1) + { + glyphHeight = aGss.iParam.iOutput[0].iBitmapSize.iHeight; + } + else + { + glyphHeight = aGss.iParam.iOutput[1].iBitmapSize.iHeight; + } + // Adjust Y values to a ficticious but reasonable range for it to combine to using the glyph height to adjust correctly below the font ascent. + baseBounds.iTl.iY = aGss.iParam.iPen.iY - aGss.iFont->AscentInPixels() + glyphHeight; //modest ascender + baseBounds.iBr.iY = aGss.iParam.iPen.iY; //No descender + } + + if (startCharCat == TChar::EMarkGroup) + indexOfFirstCombining = 0; + + aGss.CombineLastGlyphToBase(baseBounds, indexOfFirstCombining); + aGss.iGlyphPostCombine = TGlyphSelectionState::EGPostCombine_Yes; + } + else + aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes; + + // Logic to determine if we are now at the end of the glyph cluster. + // Default logic, based on whether a combining mark follows or not. + aGss.iClusterState = + (!aGss.iText.AtEnd() && + ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ? + TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete; + + return ETrue; + } +