|
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 |