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