graphicsdeviceinterface/gdi/sgdi/GlyphSel.cpp
changeset 0 5d03bc08d59c
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 /**
       
    17  @file
       
    18  @internalComponent
       
    19 */
       
    20 
       
    21 
       
    22 #include <gdi.h>
       
    23 #include <openfont.h>
       
    24 #include "GlyphSel.h"
       
    25 #include "GDIPANIC.h"
       
    26 
       
    27 
       
    28 static const TText16 KLatinGlyph_SoftHyphen = 0x00AD;
       
    29 
       
    30 
       
    31 // 
       
    32 //
       
    33 // TUtf32Iterator Class definition
       
    34 //
       
    35 //
       
    36 
       
    37 
       
    38 TUint TUtf32Iterator::UTF16ToTChar(const TText16* a)
       
    39 /**
       
    40  This routine takes an encoded UTF16 byte array and decodes the
       
    41  first character at the start of the array and returns it as a TChar.
       
    42  If the char is "not a char" character 0xFFFF results.
       
    43 @param a 
       
    44  UTF16 byte array to be decoded.
       
    45 @param aPr
       
    46  Position pointer 'a' derived from, incremented if surragote pairs decoded.
       
    47 @return
       
    48  The character value in UTF32 format or 0xFFFF it not a character.
       
    49 */
       
    50 	{
       
    51 	// Is next char a surrogate?
       
    52 	if (0xD800 == (a[0] & 0xF800)) 
       
    53 		{
       
    54 		// Is it a high surrogate in the range D800..DBFF?
       
    55 		if (0xD800 == (a[0] & 0xFC00)) 
       
    56 			{
       
    57 			// Its a high surrogate, is the next char a low surrogate?
       
    58 			if (0xDC00 == (a[1] & 0xFC00))
       
    59 				{
       
    60 				// It's a low surrogate
       
    61 				return ((a[0] - 0xd7f7) << 10) + a[1];
       
    62 				}
       
    63 			else
       
    64 				return 0xFFFF;
       
    65 			}
       
    66 		else
       
    67 			return 0xFFFF;
       
    68 		}
       
    69 	else
       
    70 		return a[0];
       
    71 	}
       
    72 
       
    73 
       
    74 TUtf32Iterator::TUtf32Iterator(const TText16* aStart, const TText16* aEnd, TInt aStartingIndex)
       
    75 /**
       
    76  Construct iterator given UTF16 encoded byte array.
       
    77 @param aStart
       
    78  Start address of the array.
       
    79 @param aEnd
       
    80  Address of the byte just beyond the end of the array.
       
    81 @param aStartingIndex
       
    82  Optional UTF16 offset into the array to initialise the current position to.
       
    83 @panic EGdiPanic_InvalidInputParam
       
    84  Raised when array start if passed the array end.
       
    85 */
       
    86 : iStart(aStart), iCurrent(aStart+aStartingIndex), iEnd(aEnd), iChar(0xffff)
       
    87 	{
       
    88 	GDI_ASSERT_DEBUG(iStart < iEnd, EGdiPanic_InvalidInputParam);
       
    89 	
       
    90 	if (iCurrent > iEnd) 
       
    91 	    iCurrent = iEnd;
       
    92 	else if (iCurrent < iStart)
       
    93 	    iCurrent = iStart;
       
    94 	else
       
    95 		{
       
    96 		// Sanatise array end checking for an unpaired surrogate value
       
    97 		// so that UTF16ToTChar() does not read off the end of the array.
       
    98         if (0xD800 == (iEnd[-1] & 0xFC00))
       
    99 			{
       
   100 			if (iCurrent == iEnd-1)
       
   101 				++iCurrent;
       
   102 			else
       
   103 				--iEnd;
       
   104 			}
       
   105 
       
   106 		// Setup initial position UTF32 character value 
       
   107 		iChar = UTF16ToTChar(iCurrent);
       
   108 		}
       
   109 	}
       
   110 
       
   111 
       
   112 TChar TUtf32Iterator::Next()
       
   113 /**
       
   114 Moves the iterator forward to the next valid UTF32 character value.
       
   115 @return TChar The next character in the text towards the end.
       
   116 @panic EGdiPanic_OutOfText
       
   117 Raised when there is no next position to move to.
       
   118 */
       
   119 	{
       
   120 	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
       
   121 
       
   122     iCurrent += (iChar > 0xffff) ? 2 : 1;
       
   123     if (iCurrent < iEnd)
       
   124 	    iChar = UTF16ToTChar(iCurrent);
       
   125 	else
       
   126 	    iChar = 0xFFFF;  
       
   127 	return iChar;
       
   128 	}
       
   129 
       
   130 
       
   131 TChar TUtf32Iterator::Prev()
       
   132 /**
       
   133 Moves the iterator backwards to the next valid UTF32 character value.
       
   134 @return TChar The prev character in the text towards the start.
       
   135 @panic EGdiPanic_OutOfText Raised when there is no next position to move to.
       
   136 */
       
   137 	{
       
   138 	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
       
   139 
       
   140     --iCurrent;
       
   141     if (iCurrent >= iStart)
       
   142 	    iChar = UTF16ToTChar(iCurrent);
       
   143 	else
       
   144 	    iChar = 0xFFFF;
       
   145 	return iChar; 
       
   146 	}
       
   147 
       
   148 
       
   149 void TUtf32Iterator::SetPos(TInt aPos)
       
   150 /**
       
   151  Moves the iterator to the position specified by array start+offset.
       
   152 @param aPos
       
   153   UTF16 offset into the array to set the current position to.
       
   154 @panic EGdiPanic_OutOfText
       
   155  Raised when there is no next position to move to.
       
   156 */
       
   157 	{
       
   158 	GDI_ASSERT_DEBUG(iStart+aPos <= iEnd, EGdiPanic_OutOfText);
       
   159 	GDI_ASSERT_DEBUG(iStart+aPos >= iStart, EGdiPanic_OutOfText);
       
   160 
       
   161 	iCurrent = iStart+aPos;
       
   162 	iChar = UTF16ToTChar(iCurrent);
       
   163 	}
       
   164 
       
   165 
       
   166 TUint TUtf32Iterator::Get(TInt offset)
       
   167 /**
       
   168  Returns the UTF32 char value at the offset specified. 0xFFFF may be returned
       
   169  for unpaired surrogate and noncharacters. Does not change the current 
       
   170  position.
       
   171 @param offset
       
   172  UTF16 offset from current iterator position to get UTF32 char form. 
       
   173 @return TChar
       
   174  UTF32 char value found at the iterator+offset, or 0xFFFF in error.
       
   175 @panic EGdiPanic_OutOfText
       
   176  Raised when offset found to be outside the bounds of the original text array.
       
   177 */
       
   178 	{
       
   179 	GDI_ASSERT_DEBUG(iCurrent+offset >= iStart, EGdiPanic_OutOfText);
       
   180 	GDI_ASSERT_DEBUG(iCurrent+offset < iEnd, EGdiPanic_OutOfText);
       
   181 	
       
   182 	return UTF16ToTChar(iCurrent+offset);
       
   183 	}
       
   184 
       
   185 
       
   186 TChar TUtf32Iterator::GetThenNext()
       
   187 /**
       
   188  Return the UTF32 value at the current position.
       
   189 @return TChar
       
   190  UTF32 value currently pointed to by iterator.
       
   191 @panic EGdiPanic_EndOfText
       
   192  Raised when current iterator position is not valid.
       
   193 */
       
   194 	{
       
   195 	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
       
   196 
       
   197 	TChar current(iChar);
       
   198     iCurrent += (iChar > 0xffff) ? 2 : 1;
       
   199     if (iCurrent < iEnd)
       
   200 	    iChar = UTF16ToTChar(iCurrent);
       
   201 	else
       
   202 	    iChar = 0xFFFF;  
       
   203 	return current;
       
   204 	}
       
   205 
       
   206 
       
   207 TChar TUtf32Iterator::GetThenPrev()
       
   208 /**
       
   209  Return the UTF32 value at the current position.
       
   210 @return TChar
       
   211  UTF32 value currently pointed to by iterator.
       
   212 @panic EGdiPanic_EndOfText
       
   213  Raised when current iterator position is not valid.
       
   214 */
       
   215 	{
       
   216 	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
       
   217 
       
   218 	TChar current(iChar);
       
   219     --iCurrent;
       
   220     if (iCurrent >= iStart)
       
   221 	    iChar = UTF16ToTChar(iCurrent);
       
   222 	else
       
   223 	    iChar = 0xFFFF;
       
   224 	return current;
       
   225 	}
       
   226 	
       
   227 	
       
   228 TInt TUtf32Iterator::LengthToStart() const
       
   229 /**
       
   230  Returns the number of TText16 codes between the start point and its
       
   231  current position.
       
   232 @return TInt
       
   233  Number of TText16 characters between array start and current iterator
       
   234  position.
       
   235 */
       
   236 	{
       
   237 	return iCurrent-iStart;
       
   238 	}
       
   239 
       
   240 
       
   241 TInt TUtf32Iterator::LengthToEnd() const
       
   242 /**
       
   243  Returns the number of remaining TText16 codes still ahead of the
       
   244  iterator.
       
   245 @return TInt
       
   246  Number of TText16 characters between array current iterator position
       
   247  and the end of the array.
       
   248 */
       
   249 	{
       
   250 	return iEnd - iCurrent;
       
   251 	}
       
   252 
       
   253 const TText16* TUtf32Iterator::CurrentPosition() const
       
   254 	{
       
   255 	return iCurrent;
       
   256 	}
       
   257 
       
   258 void TUtf32Iterator::SetCurrentPosition(const TText16* a)
       
   259 	{
       
   260 	iCurrent = a;
       
   261 	}
       
   262 
       
   263 // 
       
   264 //
       
   265 // TGlyphSelectionState Class definition
       
   266 //
       
   267 //
       
   268 
       
   269 
       
   270 /** 
       
   271  The Unicode Combining Class values recognised by the
       
   272  GlyphSelUtils::CombineLastGlyphToBase method.
       
   273 @internalComponent
       
   274 */
       
   275 enum TCombiningClass
       
   276 	{
       
   277 	EArabicFathatan = 27,
       
   278 	EArabicDammatan = 28,
       
   279 	EArabicKasratan = 29,
       
   280 	EArabicFatha = 30,
       
   281 	EArabicDamma = 31,
       
   282 	EArabicKasra = 32,
       
   283 	EArabicShadda = 33,
       
   284 	EArabicSukun = 34,
       
   285 	ECombineBelowLeftAttached = 200,
       
   286 	ECombineBelowAttached = 202,
       
   287 	ECombineBelowRightAttached = 204,
       
   288 	ECombineLeftAttached = 208,
       
   289 	ECombineRightAttached = 210,
       
   290 	ECombineAboveLeftAttached = 212,
       
   291 	ECombineAboveAttached = 214,
       
   292 	ECombineAboveRightAttached = 216,
       
   293 	ECombineBelowLeft = 218,
       
   294 	ECombineBelow = 220,
       
   295 	ECombineBelowRight = 222,
       
   296 	ECombineLeft = 224,
       
   297 	ECombineRight = 226,
       
   298 	ECombineAboveLeft = 228,
       
   299 	ECombineAbove = 230,
       
   300 	ECombineAboveRight = 232
       
   301 	};
       
   302 
       
   303 
       
   304 /**
       
   305  This method is called to attach (by adjusing its bounding box) the current end
       
   306  glyph in the output array of iParam to the base glyph bounding box based on
       
   307  the Unicode combining class of the character.
       
   308 @param aGss
       
   309  The general input/output glyph selection data for the routine.
       
   310 @param aGss.iOutput
       
   311  Input: Glyph cluster with last glyph an actual combining character. Output:
       
   312  Bounding box of last glyph adjusted according to char combining class.
       
   313 @param aFirstDiacritic
       
   314  Which character in the output array to treat as the first diacritic of the
       
   315  cluster. Usually 1, but can be more if the base class is a ligature.
       
   316 */
       
   317 void TGlyphSelectionState::CombineLastGlyphToBase(const TRect& aBase, TInt aFirstDiacritic)
       
   318 	{
       
   319 	// Get the bounds of all the base characters.
       
   320 	TRect base = aBase;
       
   321 	int last = iParam.iOutputGlyphs-1;
       
   322 	for (int i = aFirstDiacritic; i < last; i++)
       
   323 		base.BoundingRect(iParam.iOutput[i].iBounds);
       
   324 
       
   325 	// Calculate the attachment points.
       
   326 	TRect& r = iParam.iOutput[last].iBounds;
       
   327 	int w = r.Width();
       
   328 	int h = r.Height();
       
   329 	int t = r.iTl.iY;
       
   330 	int l = r.iTl.iX;
       
   331 	int left = base.iTl.iX;
       
   332 	int center = base.iTl.iX + (base.Width() - w) / 2;
       
   333 	int right = base.iBr.iX - w;
       
   334 	int below = base.iBr.iY;
       
   335 	int above = base.iTl.iY - h;
       
   336 	int left_of = left - w;
       
   337 	int right_of = right + w;
       
   338 	int xGap = 1;
       
   339 	int yGap = iFont->HeightInPixels()/10;
       
   340 	
       
   341 	// Select attachment based on combining class.
       
   342 	switch (iCombCls)
       
   343 		{
       
   344 		case ECombineBelowLeftAttached:
       
   345 			t = below;
       
   346 			l = left;
       
   347 			break;
       
   348 		case ECombineBelowAttached:
       
   349 			t = below;
       
   350 			l = center;
       
   351 			break;
       
   352 		case ECombineBelowRightAttached:
       
   353 			t = below;
       
   354 			l = right;
       
   355 			break;
       
   356 		case ECombineLeftAttached:
       
   357 			l = left_of;
       
   358 			break;
       
   359 		case ECombineRightAttached:
       
   360 			l = right_of;
       
   361 			break;
       
   362 		case ECombineAboveLeftAttached:
       
   363 			t = above;
       
   364 			l = left;
       
   365 			break;
       
   366 		case ECombineAboveAttached:
       
   367 			t = above;
       
   368 			l = center;
       
   369 			break;
       
   370 		case ECombineAboveRightAttached:
       
   371 			t = above;
       
   372 			l = right;
       
   373 			break;
       
   374 		case ECombineBelowLeft:
       
   375 			t = below + yGap;
       
   376 			l = left;
       
   377 			break;
       
   378 		case ECombineBelow:
       
   379 		case EArabicKasratan:
       
   380 		case EArabicKasra:
       
   381 			t = below + yGap;
       
   382 			l = center;
       
   383 			break;
       
   384 		case ECombineBelowRight:
       
   385 			t = below + yGap;
       
   386 			l = right;
       
   387 			break;
       
   388 		case ECombineLeft:
       
   389 			l = left_of - xGap;
       
   390 			break;
       
   391 		case ECombineRight:
       
   392 			l = right_of + xGap;
       
   393 			break;
       
   394 		case ECombineAboveLeft:
       
   395 			t = above - yGap;
       
   396 			l = left;
       
   397 			break;
       
   398 		case ECombineAbove:
       
   399 		case EArabicFathatan:
       
   400 		case EArabicDammatan:
       
   401 		case EArabicFatha:
       
   402 		case EArabicDamma:
       
   403 		case EArabicShadda:
       
   404 		case EArabicSukun:
       
   405 			t = above - yGap;
       
   406 			l = center;
       
   407 			break;
       
   408 		case ECombineAboveRight:
       
   409 			t = above - yGap;
       
   410 			l = right;
       
   411 			break;
       
   412 		default:
       
   413 			l = center;
       
   414 			break;
       
   415 		}
       
   416 
       
   417 	// Adjust the bounding box of the last glyph to fix position
       
   418 	// based on the characters combining class. For speed, do directly.
       
   419 	// r.SetRect(l,t,l + w,t + h);
       
   420 	r.iTl.iX = l;
       
   421 	r.iTl.iY = t;
       
   422 	r.iBr.iX = l+w;
       
   423 	r.iBr.iY = t+h;
       
   424 	}
       
   425 
       
   426 
       
   427 TBool TGlyphSelectionState::AppendGlyphToCluster(TUint aCode)
       
   428 /**
       
   429  This common method is used by glyph selector classes to add a glyph to
       
   430  the end of the aGss.iParam output field filling in all the glyph info 
       
   431  needed.
       
   432 @param aCode
       
   433  The Unicode character for which a glyph should be appended.
       
   434 @param aGss
       
   435  The general input/output glyph selection data for the routine. 
       
   436 @return TBool
       
   437  ETrue when successful, EFalse when failure occurs e..g no char data, overflow
       
   438 */
       
   439 	{
       
   440 	// Setup reference to next free glyph record we need to update.
       
   441 	GDI_ASSERT_DEBUG(iParam.iOutputGlyphs < CFont::TPositionParam::EMaxOutputGlyphs, 
       
   442 		EGdiPanic_InvalidInputParam);
       
   443 	   
       
   444 	CFont::TPositionParam::TOutput* output = iParam.iOutput+iParam.iOutputGlyphs;
       
   445 
       
   446 	// Retrieve the glyph details from the Font. Essential to proceed, abort
       
   447 	// if not available.
       
   448 	TOpenFontCharMetrics metrics;
       
   449 	if (iFont->GetCharacterData(aCode, metrics, output->iBitmap, 
       
   450 		output->iBitmapSize) == CFont::ENoCharacterData)
       
   451 		return EFalse;
       
   452 
       
   453 	// Set code point of glyph in output record.
       
   454 	output->iCode = aCode;
       
   455 	
       
   456 	// Set the glyph's bounds in the output record and record pen advancement.
       
   457 	if (iParam.iDirection == CFont::EVertical)
       
   458 		{
       
   459 		metrics.GetVertBounds(output->iBounds);
       
   460 		iAdvance.iHeight = Max(iAdvance.iHeight, metrics.VertAdvance());
       
   461 		}
       
   462 	else
       
   463 		{
       
   464 		metrics.GetHorizBounds(output->iBounds);
       
   465 		iAdvance.iWidth = Max(iAdvance.iWidth, metrics.HorizAdvance());
       
   466 		}
       
   467 
       
   468 	// Next adjust the glyph's bounding box to offset it from the pen
       
   469 	// position (origin of drawing). For speed increment attributes directly.
       
   470 	// output->iBounds.Move(aGss.iParam.iPen);
       
   471 	output->iBounds.iTl.iX += iParam.iPen.iX;
       
   472 	output->iBounds.iBr.iX += iParam.iPen.iX;
       
   473 	output->iBounds.iTl.iY += iParam.iPen.iY;
       
   474 	output->iBounds.iBr.iY += iParam.iPen.iY;
       
   475 	
       
   476 	// Before we exit with success, increment the glyph array counter.
       
   477 	// for the new glyph we've added here.
       
   478 	iParam.iOutputGlyphs++;
       
   479 	return ETrue;
       
   480 	}
       
   481 
       
   482 
       
   483 // 
       
   484 //
       
   485 // GlyphSelector_SoftHyphen Class definition
       
   486 //
       
   487 //
       
   488 
       
   489 TBool GlyphSelector_SoftHyphen::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
       
   490 /**
       
   491 @see GlyphSelUtils 
       
   492  See this class for the method description.
       
   493 */
       
   494 	{
       
   495  	aGss.iText.Next();
       
   496 	if (!aGss.iText.AtEnd())
       
   497 		{
       
   498 		// Here we skip & don't output hyphen since its not at the end a line.
       
   499 		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
       
   500 		}
       
   501 	else
       
   502 		{
       
   503 		// If we reach here we must output hyphen.
       
   504 		if (!aGss.AppendGlyphToCluster(KLatinGlyph_SoftHyphen))
       
   505 			return EFalse;
       
   506 		
       
   507 		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
       
   508 		}
       
   509 
       
   510 	// Logic to determine if we are now at the end of the glyph cluster.
       
   511 	// Default logic, based on whether a combining mark follows or not.
       
   512 	aGss.iClusterState = 
       
   513 		(!aGss.iText.AtEnd() &&
       
   514 			((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
       
   515 			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
       
   516 
       
   517 	return ETrue;
       
   518 	}
       
   519 
       
   520 
       
   521 // 
       
   522 //
       
   523 // GlyphSelector_Default Class definition
       
   524 //
       
   525 //
       
   526 
       
   527 
       
   528 TBool GlyphSelector_Default::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
       
   529 /**
       
   530 @see GlyphSelUtils 
       
   531  See this class for the method description.
       
   532 */
       
   533 	{
       
   534 	
       
   535 	// In this method we always output the glyph.
       
   536 	if (!aGss.AppendGlyphToCluster(aGss.iText.GetThenNext()))
       
   537 		return EFalse;
       
   538 
       
   539 	// Adjust glyph's bounds further to position this character if it is a
       
   540 	// combining mark
       
   541 	if (aGss.IsCombiningClass())
       
   542 		{
       
   543 		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
       
   544 		
       
   545 		TRect baseBounds(aGss.iParam.iOutput[0].iBounds);
       
   546 		// Get first character in this glyph cluster. In this default process function, the iCode should 
       
   547 		// be Unicode Point Code.  
       
   548 		TChar startChar = TChar(aGss.iParam.iOutput[0].iCode);
       
   549 		// Character index in the output array to treat as the first diacritic of the
       
   550 		// cluster. It will be used as first character for combine to. usually 1, but when
       
   551 		// the cluster starts with a combining mark, it should be set to 0.  
       
   552 		TInt indexOfFirstCombining = 1; 
       
   553 		TInt startCharCat = startChar.GetCategory() & 0xF0;
       
   554 		
       
   555 		// if the first character in this cluster is a combining mark or a graphically empty character, 
       
   556 		// (such as a space Character0x0020), a fake bound, formed from the Ascent of the font, will be 
       
   557 		// used for combining
       
   558 		if ((startCharCat == TChar::EMarkGroup) || baseBounds.Size() == TSize(0,0)) 
       
   559 			{
       
   560 			// Determine the height of the combining glyph.
       
   561 			TInt glyphHeight = 0;
       
   562 			if (aGss.iParam.iOutputGlyphs == 1)
       
   563 				{
       
   564 				glyphHeight = aGss.iParam.iOutput[0].iBitmapSize.iHeight;
       
   565 				}
       
   566 			else
       
   567 				{
       
   568 				glyphHeight = aGss.iParam.iOutput[1].iBitmapSize.iHeight;
       
   569 				}
       
   570 			// 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.
       
   571 			baseBounds.iTl.iY = aGss.iParam.iPen.iY - aGss.iFont->AscentInPixels() + glyphHeight; //modest ascender
       
   572 			baseBounds.iBr.iY = aGss.iParam.iPen.iY; //No descender
       
   573 			}
       
   574 		
       
   575 		if (startCharCat == TChar::EMarkGroup)
       
   576 			indexOfFirstCombining = 0;
       
   577 								
       
   578 		aGss.CombineLastGlyphToBase(baseBounds, indexOfFirstCombining);
       
   579 		aGss.iGlyphPostCombine = TGlyphSelectionState::EGPostCombine_Yes;
       
   580 		}	
       
   581 	else
       
   582 		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
       
   583 
       
   584 	// Logic to determine if we are now at the end of the glyph cluster.
       
   585 	// Default logic, based on whether a combining mark follows or not.
       
   586 	aGss.iClusterState = 
       
   587 		(!aGss.iText.AtEnd() &&
       
   588 		 ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
       
   589 			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
       
   590 
       
   591 	return ETrue;
       
   592 	}
       
   593