1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/graphicsdeviceinterface/gdi/sgdi/GlyphSel.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,593 @@
1.4 +// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +//
1.18 +
1.19 +/**
1.20 + @file
1.21 + @internalComponent
1.22 +*/
1.23 +
1.24 +
1.25 +#include <gdi.h>
1.26 +#include <openfont.h>
1.27 +#include "GlyphSel.h"
1.28 +#include "GDIPANIC.h"
1.29 +
1.30 +
1.31 +static const TText16 KLatinGlyph_SoftHyphen = 0x00AD;
1.32 +
1.33 +
1.34 +//
1.35 +//
1.36 +// TUtf32Iterator Class definition
1.37 +//
1.38 +//
1.39 +
1.40 +
1.41 +TUint TUtf32Iterator::UTF16ToTChar(const TText16* a)
1.42 +/**
1.43 + This routine takes an encoded UTF16 byte array and decodes the
1.44 + first character at the start of the array and returns it as a TChar.
1.45 + If the char is "not a char" character 0xFFFF results.
1.46 +@param a
1.47 + UTF16 byte array to be decoded.
1.48 +@param aPr
1.49 + Position pointer 'a' derived from, incremented if surragote pairs decoded.
1.50 +@return
1.51 + The character value in UTF32 format or 0xFFFF it not a character.
1.52 +*/
1.53 + {
1.54 + // Is next char a surrogate?
1.55 + if (0xD800 == (a[0] & 0xF800))
1.56 + {
1.57 + // Is it a high surrogate in the range D800..DBFF?
1.58 + if (0xD800 == (a[0] & 0xFC00))
1.59 + {
1.60 + // Its a high surrogate, is the next char a low surrogate?
1.61 + if (0xDC00 == (a[1] & 0xFC00))
1.62 + {
1.63 + // It's a low surrogate
1.64 + return ((a[0] - 0xd7f7) << 10) + a[1];
1.65 + }
1.66 + else
1.67 + return 0xFFFF;
1.68 + }
1.69 + else
1.70 + return 0xFFFF;
1.71 + }
1.72 + else
1.73 + return a[0];
1.74 + }
1.75 +
1.76 +
1.77 +TUtf32Iterator::TUtf32Iterator(const TText16* aStart, const TText16* aEnd, TInt aStartingIndex)
1.78 +/**
1.79 + Construct iterator given UTF16 encoded byte array.
1.80 +@param aStart
1.81 + Start address of the array.
1.82 +@param aEnd
1.83 + Address of the byte just beyond the end of the array.
1.84 +@param aStartingIndex
1.85 + Optional UTF16 offset into the array to initialise the current position to.
1.86 +@panic EGdiPanic_InvalidInputParam
1.87 + Raised when array start if passed the array end.
1.88 +*/
1.89 +: iStart(aStart), iCurrent(aStart+aStartingIndex), iEnd(aEnd), iChar(0xffff)
1.90 + {
1.91 + GDI_ASSERT_DEBUG(iStart < iEnd, EGdiPanic_InvalidInputParam);
1.92 +
1.93 + if (iCurrent > iEnd)
1.94 + iCurrent = iEnd;
1.95 + else if (iCurrent < iStart)
1.96 + iCurrent = iStart;
1.97 + else
1.98 + {
1.99 + // Sanatise array end checking for an unpaired surrogate value
1.100 + // so that UTF16ToTChar() does not read off the end of the array.
1.101 + if (0xD800 == (iEnd[-1] & 0xFC00))
1.102 + {
1.103 + if (iCurrent == iEnd-1)
1.104 + ++iCurrent;
1.105 + else
1.106 + --iEnd;
1.107 + }
1.108 +
1.109 + // Setup initial position UTF32 character value
1.110 + iChar = UTF16ToTChar(iCurrent);
1.111 + }
1.112 + }
1.113 +
1.114 +
1.115 +TChar TUtf32Iterator::Next()
1.116 +/**
1.117 +Moves the iterator forward to the next valid UTF32 character value.
1.118 +@return TChar The next character in the text towards the end.
1.119 +@panic EGdiPanic_OutOfText
1.120 +Raised when there is no next position to move to.
1.121 +*/
1.122 + {
1.123 + GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
1.124 +
1.125 + iCurrent += (iChar > 0xffff) ? 2 : 1;
1.126 + if (iCurrent < iEnd)
1.127 + iChar = UTF16ToTChar(iCurrent);
1.128 + else
1.129 + iChar = 0xFFFF;
1.130 + return iChar;
1.131 + }
1.132 +
1.133 +
1.134 +TChar TUtf32Iterator::Prev()
1.135 +/**
1.136 +Moves the iterator backwards to the next valid UTF32 character value.
1.137 +@return TChar The prev character in the text towards the start.
1.138 +@panic EGdiPanic_OutOfText Raised when there is no next position to move to.
1.139 +*/
1.140 + {
1.141 + GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
1.142 +
1.143 + --iCurrent;
1.144 + if (iCurrent >= iStart)
1.145 + iChar = UTF16ToTChar(iCurrent);
1.146 + else
1.147 + iChar = 0xFFFF;
1.148 + return iChar;
1.149 + }
1.150 +
1.151 +
1.152 +void TUtf32Iterator::SetPos(TInt aPos)
1.153 +/**
1.154 + Moves the iterator to the position specified by array start+offset.
1.155 +@param aPos
1.156 + UTF16 offset into the array to set the current position to.
1.157 +@panic EGdiPanic_OutOfText
1.158 + Raised when there is no next position to move to.
1.159 +*/
1.160 + {
1.161 + GDI_ASSERT_DEBUG(iStart+aPos <= iEnd, EGdiPanic_OutOfText);
1.162 + GDI_ASSERT_DEBUG(iStart+aPos >= iStart, EGdiPanic_OutOfText);
1.163 +
1.164 + iCurrent = iStart+aPos;
1.165 + iChar = UTF16ToTChar(iCurrent);
1.166 + }
1.167 +
1.168 +
1.169 +TUint TUtf32Iterator::Get(TInt offset)
1.170 +/**
1.171 + Returns the UTF32 char value at the offset specified. 0xFFFF may be returned
1.172 + for unpaired surrogate and noncharacters. Does not change the current
1.173 + position.
1.174 +@param offset
1.175 + UTF16 offset from current iterator position to get UTF32 char form.
1.176 +@return TChar
1.177 + UTF32 char value found at the iterator+offset, or 0xFFFF in error.
1.178 +@panic EGdiPanic_OutOfText
1.179 + Raised when offset found to be outside the bounds of the original text array.
1.180 +*/
1.181 + {
1.182 + GDI_ASSERT_DEBUG(iCurrent+offset >= iStart, EGdiPanic_OutOfText);
1.183 + GDI_ASSERT_DEBUG(iCurrent+offset < iEnd, EGdiPanic_OutOfText);
1.184 +
1.185 + return UTF16ToTChar(iCurrent+offset);
1.186 + }
1.187 +
1.188 +
1.189 +TChar TUtf32Iterator::GetThenNext()
1.190 +/**
1.191 + Return the UTF32 value at the current position.
1.192 +@return TChar
1.193 + UTF32 value currently pointed to by iterator.
1.194 +@panic EGdiPanic_EndOfText
1.195 + Raised when current iterator position is not valid.
1.196 +*/
1.197 + {
1.198 + GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
1.199 +
1.200 + TChar current(iChar);
1.201 + iCurrent += (iChar > 0xffff) ? 2 : 1;
1.202 + if (iCurrent < iEnd)
1.203 + iChar = UTF16ToTChar(iCurrent);
1.204 + else
1.205 + iChar = 0xFFFF;
1.206 + return current;
1.207 + }
1.208 +
1.209 +
1.210 +TChar TUtf32Iterator::GetThenPrev()
1.211 +/**
1.212 + Return the UTF32 value at the current position.
1.213 +@return TChar
1.214 + UTF32 value currently pointed to by iterator.
1.215 +@panic EGdiPanic_EndOfText
1.216 + Raised when current iterator position is not valid.
1.217 +*/
1.218 + {
1.219 + GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
1.220 +
1.221 + TChar current(iChar);
1.222 + --iCurrent;
1.223 + if (iCurrent >= iStart)
1.224 + iChar = UTF16ToTChar(iCurrent);
1.225 + else
1.226 + iChar = 0xFFFF;
1.227 + return current;
1.228 + }
1.229 +
1.230 +
1.231 +TInt TUtf32Iterator::LengthToStart() const
1.232 +/**
1.233 + Returns the number of TText16 codes between the start point and its
1.234 + current position.
1.235 +@return TInt
1.236 + Number of TText16 characters between array start and current iterator
1.237 + position.
1.238 +*/
1.239 + {
1.240 + return iCurrent-iStart;
1.241 + }
1.242 +
1.243 +
1.244 +TInt TUtf32Iterator::LengthToEnd() const
1.245 +/**
1.246 + Returns the number of remaining TText16 codes still ahead of the
1.247 + iterator.
1.248 +@return TInt
1.249 + Number of TText16 characters between array current iterator position
1.250 + and the end of the array.
1.251 +*/
1.252 + {
1.253 + return iEnd - iCurrent;
1.254 + }
1.255 +
1.256 +const TText16* TUtf32Iterator::CurrentPosition() const
1.257 + {
1.258 + return iCurrent;
1.259 + }
1.260 +
1.261 +void TUtf32Iterator::SetCurrentPosition(const TText16* a)
1.262 + {
1.263 + iCurrent = a;
1.264 + }
1.265 +
1.266 +//
1.267 +//
1.268 +// TGlyphSelectionState Class definition
1.269 +//
1.270 +//
1.271 +
1.272 +
1.273 +/**
1.274 + The Unicode Combining Class values recognised by the
1.275 + GlyphSelUtils::CombineLastGlyphToBase method.
1.276 +@internalComponent
1.277 +*/
1.278 +enum TCombiningClass
1.279 + {
1.280 + EArabicFathatan = 27,
1.281 + EArabicDammatan = 28,
1.282 + EArabicKasratan = 29,
1.283 + EArabicFatha = 30,
1.284 + EArabicDamma = 31,
1.285 + EArabicKasra = 32,
1.286 + EArabicShadda = 33,
1.287 + EArabicSukun = 34,
1.288 + ECombineBelowLeftAttached = 200,
1.289 + ECombineBelowAttached = 202,
1.290 + ECombineBelowRightAttached = 204,
1.291 + ECombineLeftAttached = 208,
1.292 + ECombineRightAttached = 210,
1.293 + ECombineAboveLeftAttached = 212,
1.294 + ECombineAboveAttached = 214,
1.295 + ECombineAboveRightAttached = 216,
1.296 + ECombineBelowLeft = 218,
1.297 + ECombineBelow = 220,
1.298 + ECombineBelowRight = 222,
1.299 + ECombineLeft = 224,
1.300 + ECombineRight = 226,
1.301 + ECombineAboveLeft = 228,
1.302 + ECombineAbove = 230,
1.303 + ECombineAboveRight = 232
1.304 + };
1.305 +
1.306 +
1.307 +/**
1.308 + This method is called to attach (by adjusing its bounding box) the current end
1.309 + glyph in the output array of iParam to the base glyph bounding box based on
1.310 + the Unicode combining class of the character.
1.311 +@param aGss
1.312 + The general input/output glyph selection data for the routine.
1.313 +@param aGss.iOutput
1.314 + Input: Glyph cluster with last glyph an actual combining character. Output:
1.315 + Bounding box of last glyph adjusted according to char combining class.
1.316 +@param aFirstDiacritic
1.317 + Which character in the output array to treat as the first diacritic of the
1.318 + cluster. Usually 1, but can be more if the base class is a ligature.
1.319 +*/
1.320 +void TGlyphSelectionState::CombineLastGlyphToBase(const TRect& aBase, TInt aFirstDiacritic)
1.321 + {
1.322 + // Get the bounds of all the base characters.
1.323 + TRect base = aBase;
1.324 + int last = iParam.iOutputGlyphs-1;
1.325 + for (int i = aFirstDiacritic; i < last; i++)
1.326 + base.BoundingRect(iParam.iOutput[i].iBounds);
1.327 +
1.328 + // Calculate the attachment points.
1.329 + TRect& r = iParam.iOutput[last].iBounds;
1.330 + int w = r.Width();
1.331 + int h = r.Height();
1.332 + int t = r.iTl.iY;
1.333 + int l = r.iTl.iX;
1.334 + int left = base.iTl.iX;
1.335 + int center = base.iTl.iX + (base.Width() - w) / 2;
1.336 + int right = base.iBr.iX - w;
1.337 + int below = base.iBr.iY;
1.338 + int above = base.iTl.iY - h;
1.339 + int left_of = left - w;
1.340 + int right_of = right + w;
1.341 + int xGap = 1;
1.342 + int yGap = iFont->HeightInPixels()/10;
1.343 +
1.344 + // Select attachment based on combining class.
1.345 + switch (iCombCls)
1.346 + {
1.347 + case ECombineBelowLeftAttached:
1.348 + t = below;
1.349 + l = left;
1.350 + break;
1.351 + case ECombineBelowAttached:
1.352 + t = below;
1.353 + l = center;
1.354 + break;
1.355 + case ECombineBelowRightAttached:
1.356 + t = below;
1.357 + l = right;
1.358 + break;
1.359 + case ECombineLeftAttached:
1.360 + l = left_of;
1.361 + break;
1.362 + case ECombineRightAttached:
1.363 + l = right_of;
1.364 + break;
1.365 + case ECombineAboveLeftAttached:
1.366 + t = above;
1.367 + l = left;
1.368 + break;
1.369 + case ECombineAboveAttached:
1.370 + t = above;
1.371 + l = center;
1.372 + break;
1.373 + case ECombineAboveRightAttached:
1.374 + t = above;
1.375 + l = right;
1.376 + break;
1.377 + case ECombineBelowLeft:
1.378 + t = below + yGap;
1.379 + l = left;
1.380 + break;
1.381 + case ECombineBelow:
1.382 + case EArabicKasratan:
1.383 + case EArabicKasra:
1.384 + t = below + yGap;
1.385 + l = center;
1.386 + break;
1.387 + case ECombineBelowRight:
1.388 + t = below + yGap;
1.389 + l = right;
1.390 + break;
1.391 + case ECombineLeft:
1.392 + l = left_of - xGap;
1.393 + break;
1.394 + case ECombineRight:
1.395 + l = right_of + xGap;
1.396 + break;
1.397 + case ECombineAboveLeft:
1.398 + t = above - yGap;
1.399 + l = left;
1.400 + break;
1.401 + case ECombineAbove:
1.402 + case EArabicFathatan:
1.403 + case EArabicDammatan:
1.404 + case EArabicFatha:
1.405 + case EArabicDamma:
1.406 + case EArabicShadda:
1.407 + case EArabicSukun:
1.408 + t = above - yGap;
1.409 + l = center;
1.410 + break;
1.411 + case ECombineAboveRight:
1.412 + t = above - yGap;
1.413 + l = right;
1.414 + break;
1.415 + default:
1.416 + l = center;
1.417 + break;
1.418 + }
1.419 +
1.420 + // Adjust the bounding box of the last glyph to fix position
1.421 + // based on the characters combining class. For speed, do directly.
1.422 + // r.SetRect(l,t,l + w,t + h);
1.423 + r.iTl.iX = l;
1.424 + r.iTl.iY = t;
1.425 + r.iBr.iX = l+w;
1.426 + r.iBr.iY = t+h;
1.427 + }
1.428 +
1.429 +
1.430 +TBool TGlyphSelectionState::AppendGlyphToCluster(TUint aCode)
1.431 +/**
1.432 + This common method is used by glyph selector classes to add a glyph to
1.433 + the end of the aGss.iParam output field filling in all the glyph info
1.434 + needed.
1.435 +@param aCode
1.436 + The Unicode character for which a glyph should be appended.
1.437 +@param aGss
1.438 + The general input/output glyph selection data for the routine.
1.439 +@return TBool
1.440 + ETrue when successful, EFalse when failure occurs e..g no char data, overflow
1.441 +*/
1.442 + {
1.443 + // Setup reference to next free glyph record we need to update.
1.444 + GDI_ASSERT_DEBUG(iParam.iOutputGlyphs < CFont::TPositionParam::EMaxOutputGlyphs,
1.445 + EGdiPanic_InvalidInputParam);
1.446 +
1.447 + CFont::TPositionParam::TOutput* output = iParam.iOutput+iParam.iOutputGlyphs;
1.448 +
1.449 + // Retrieve the glyph details from the Font. Essential to proceed, abort
1.450 + // if not available.
1.451 + TOpenFontCharMetrics metrics;
1.452 + if (iFont->GetCharacterData(aCode, metrics, output->iBitmap,
1.453 + output->iBitmapSize) == CFont::ENoCharacterData)
1.454 + return EFalse;
1.455 +
1.456 + // Set code point of glyph in output record.
1.457 + output->iCode = aCode;
1.458 +
1.459 + // Set the glyph's bounds in the output record and record pen advancement.
1.460 + if (iParam.iDirection == CFont::EVertical)
1.461 + {
1.462 + metrics.GetVertBounds(output->iBounds);
1.463 + iAdvance.iHeight = Max(iAdvance.iHeight, metrics.VertAdvance());
1.464 + }
1.465 + else
1.466 + {
1.467 + metrics.GetHorizBounds(output->iBounds);
1.468 + iAdvance.iWidth = Max(iAdvance.iWidth, metrics.HorizAdvance());
1.469 + }
1.470 +
1.471 + // Next adjust the glyph's bounding box to offset it from the pen
1.472 + // position (origin of drawing). For speed increment attributes directly.
1.473 + // output->iBounds.Move(aGss.iParam.iPen);
1.474 + output->iBounds.iTl.iX += iParam.iPen.iX;
1.475 + output->iBounds.iBr.iX += iParam.iPen.iX;
1.476 + output->iBounds.iTl.iY += iParam.iPen.iY;
1.477 + output->iBounds.iBr.iY += iParam.iPen.iY;
1.478 +
1.479 + // Before we exit with success, increment the glyph array counter.
1.480 + // for the new glyph we've added here.
1.481 + iParam.iOutputGlyphs++;
1.482 + return ETrue;
1.483 + }
1.484 +
1.485 +
1.486 +//
1.487 +//
1.488 +// GlyphSelector_SoftHyphen Class definition
1.489 +//
1.490 +//
1.491 +
1.492 +TBool GlyphSelector_SoftHyphen::Process(TGlyphSelectionState& aGss, RShapeInfo&)
1.493 +/**
1.494 +@see GlyphSelUtils
1.495 + See this class for the method description.
1.496 +*/
1.497 + {
1.498 + aGss.iText.Next();
1.499 + if (!aGss.iText.AtEnd())
1.500 + {
1.501 + // Here we skip & don't output hyphen since its not at the end a line.
1.502 + aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
1.503 + }
1.504 + else
1.505 + {
1.506 + // If we reach here we must output hyphen.
1.507 + if (!aGss.AppendGlyphToCluster(KLatinGlyph_SoftHyphen))
1.508 + return EFalse;
1.509 +
1.510 + aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
1.511 + }
1.512 +
1.513 + // Logic to determine if we are now at the end of the glyph cluster.
1.514 + // Default logic, based on whether a combining mark follows or not.
1.515 + aGss.iClusterState =
1.516 + (!aGss.iText.AtEnd() &&
1.517 + ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
1.518 + TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
1.519 +
1.520 + return ETrue;
1.521 + }
1.522 +
1.523 +
1.524 +//
1.525 +//
1.526 +// GlyphSelector_Default Class definition
1.527 +//
1.528 +//
1.529 +
1.530 +
1.531 +TBool GlyphSelector_Default::Process(TGlyphSelectionState& aGss, RShapeInfo&)
1.532 +/**
1.533 +@see GlyphSelUtils
1.534 + See this class for the method description.
1.535 +*/
1.536 + {
1.537 +
1.538 + // In this method we always output the glyph.
1.539 + if (!aGss.AppendGlyphToCluster(aGss.iText.GetThenNext()))
1.540 + return EFalse;
1.541 +
1.542 + // Adjust glyph's bounds further to position this character if it is a
1.543 + // combining mark
1.544 + if (aGss.IsCombiningClass())
1.545 + {
1.546 + aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
1.547 +
1.548 + TRect baseBounds(aGss.iParam.iOutput[0].iBounds);
1.549 + // Get first character in this glyph cluster. In this default process function, the iCode should
1.550 + // be Unicode Point Code.
1.551 + TChar startChar = TChar(aGss.iParam.iOutput[0].iCode);
1.552 + // Character index in the output array to treat as the first diacritic of the
1.553 + // cluster. It will be used as first character for combine to. usually 1, but when
1.554 + // the cluster starts with a combining mark, it should be set to 0.
1.555 + TInt indexOfFirstCombining = 1;
1.556 + TInt startCharCat = startChar.GetCategory() & 0xF0;
1.557 +
1.558 + // if the first character in this cluster is a combining mark or a graphically empty character,
1.559 + // (such as a space Character0x0020), a fake bound, formed from the Ascent of the font, will be
1.560 + // used for combining
1.561 + if ((startCharCat == TChar::EMarkGroup) || baseBounds.Size() == TSize(0,0))
1.562 + {
1.563 + // Determine the height of the combining glyph.
1.564 + TInt glyphHeight = 0;
1.565 + if (aGss.iParam.iOutputGlyphs == 1)
1.566 + {
1.567 + glyphHeight = aGss.iParam.iOutput[0].iBitmapSize.iHeight;
1.568 + }
1.569 + else
1.570 + {
1.571 + glyphHeight = aGss.iParam.iOutput[1].iBitmapSize.iHeight;
1.572 + }
1.573 + // 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.
1.574 + baseBounds.iTl.iY = aGss.iParam.iPen.iY - aGss.iFont->AscentInPixels() + glyphHeight; //modest ascender
1.575 + baseBounds.iBr.iY = aGss.iParam.iPen.iY; //No descender
1.576 + }
1.577 +
1.578 + if (startCharCat == TChar::EMarkGroup)
1.579 + indexOfFirstCombining = 0;
1.580 +
1.581 + aGss.CombineLastGlyphToBase(baseBounds, indexOfFirstCombining);
1.582 + aGss.iGlyphPostCombine = TGlyphSelectionState::EGPostCombine_Yes;
1.583 + }
1.584 + else
1.585 + aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
1.586 +
1.587 + // Logic to determine if we are now at the end of the glyph cluster.
1.588 + // Default logic, based on whether a combining mark follows or not.
1.589 + aGss.iClusterState =
1.590 + (!aGss.iText.AtEnd() &&
1.591 + ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
1.592 + TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
1.593 +
1.594 + return ETrue;
1.595 + }
1.596 +