add patch
[framework/osp/uifw.git] / src / graphics / FGrp_FontBidiUtil.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0/
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /*
19  * @file        FGrp_FontBidiUtil.cpp
20  * @brief       This is the cpp file for the bidi utilities.
21  *
22  */
23
24 #include <string.h>
25 #include <new>
26 #include <unique_ptr.h>
27 #include <glib.h>
28
29 #include "FGrp_FontBidiUtil.h"
30
31
32 namespace
33 {
34
35 struct BidiParagraph
36 {
37         wchar_t* pString;
38         FriBidiCharType* pCharTypes;
39         FriBidiLevel* pEmbeddingLevels;
40         FriBidiParType direction;
41         size_t bufferLength;
42
43         BidiParagraph(size_t length)
44                 : pString(new (std::nothrow) wchar_t[length + 1])
45                 , pCharTypes(new (std::nothrow) FriBidiCharType[length])
46                 , pEmbeddingLevels(new (std::nothrow) FriBidiLevel[length])
47                 , direction(FRIBIDI_PAR_LTR)//FRIBIDI_PAR_ON)
48                 , bufferLength(length)
49         {
50                 if (pString == NULL || pCharTypes == NULL || pEmbeddingLevels == NULL)
51                 {
52                         delete[] pString;
53                         delete[] pEmbeddingLevels;
54                         delete[] pCharTypes;
55
56                         pString = NULL;
57                         pEmbeddingLevels = NULL;
58                         pCharTypes = NULL;
59                         bufferLength = 0;
60                 }
61         }
62
63         ~BidiParagraph(void)
64         {
65                 delete[] pCharTypes;
66                 delete[] pEmbeddingLevels;
67                 delete[] pString;
68         }
69
70         bool IsValid(void)
71         {
72                 return (pString != NULL && pCharTypes != NULL && pEmbeddingLevels != NULL);
73         }
74 };
75
76 #define IS_DIRECTION_RTL(x) (((x->direction == FRIBIDI_PAR_RTL) || (x->direction == FRIBIDI_PAR_WRTL)) ? true : false)
77
78 ////////////////////////////////////////////////////////////////////////////////
79
80 bool
81 _IsRtl(const wchar_t* pString, int length)
82 {
83         if (pString)
84         {
85                 for ( ; *pString && length-- > 0 ; ++pString)
86                 {
87                         FriBidiCharType type = fribidi_get_bidi_type((FriBidiChar) *pString);
88
89                         GUnicodeScript script = g_unichar_get_script(*pString);
90
91                         if (FRIBIDI_IS_LETTER(type) && FRIBIDI_IS_RTL(type))
92                         {
93                                 return true;
94                         }
95                         else if (script == G_UNICODE_SCRIPT_DEVANAGARI || script == G_UNICODE_SCRIPT_BENGALI)
96                         {
97                                 return true;
98                         }
99                 }
100         }
101
102         return false;
103 }
104
105 size_t
106 _GetStringLength(const wchar_t* pString)
107 {
108         if (pString)
109         {
110                 const wchar_t* pEnd = pString;
111
112                 while (*pEnd++)
113                         ;
114
115                 return (pEnd - pString - 1);
116         }
117         else
118         {
119                 return 0;
120         }
121 }
122
123 template <typename DstCode, typename SrcCode>
124 void
125 _ConvertCharacterCode(DstCode* pDst, const SrcCode* pSrc, int length)
126 {
127         if (pDst && pSrc)
128         {
129                 while (*pSrc && length-- > 0)
130                 {
131                         *pDst++ = *pSrc++;
132                 }
133
134                 *pDst = 0;
135         }
136 }
137
138 ////////////////
139
140 BidiParagraph*
141 _GetBidiParagraphProperty(const wchar_t* pText, size_t len, int* pSegmentIndex, Tizen::Graphics::_TextBidiProperty::BidiHint hint)
142 {
143         #define CHECK_IF(cond) if (!(cond)) return NULL
144
145         // precondition check
146         {
147                 CHECK_IF((pText) && _IsRtl(pText, len));
148
149                 size_t textLength = _GetStringLength(pText);
150                 len = (len < textLength) ? len : textLength;
151         }
152
153         const FriBidiChar* pTestString = NULL;
154         std::unique_ptr<FriBidiChar[]> AutoDeleteString;
155         std::unique_ptr<BidiParagraph> bidiProperty(new (std::nothrow) BidiParagraph(len));
156
157         // out of memory
158         CHECK_IF(bidiProperty.get() && bidiProperty->IsValid());
159
160         // in case of different size of each wide character
161         if (sizeof(wchar_t) == sizeof(FriBidiChar))
162         {
163                 pTestString = (const FriBidiChar *) pText;
164         }
165         else
166         {
167                 AutoDeleteString.reset(new (std::nothrow) FriBidiChar[len + 1]);
168
169                 CHECK_IF(AutoDeleteString.get());
170
171                 _ConvertCharacterCode<FriBidiChar, wchar_t>(AutoDeleteString.get(), pText, len);
172
173                 pTestString = AutoDeleteString.get();
174         }
175
176         /*
177          * 0x110 FRIBIDI_TYPE_LTR;
178          * 0x111 FRIBIDI_TYPE_RTL;
179          * 0x113 FRIBIDI_TYPE_AL;
180          * 0x220 FRIBIDI_TYPE_EN;
181          */
182
183         fribidi_get_bidi_types(pTestString, len, bidiProperty->pCharTypes);
184
185         if (pSegmentIndex)
186         {
187                 size_t pos = 0;
188                 FriBidiLevel baseLevel = 0;
189
190                 for (int* itr = pSegmentIndex ; *itr > 0 ; itr++)
191                 {
192                         FriBidiParType direction = FRIBIDI_PAR_ON;
193                         CHECK_IF(fribidi_get_par_embedding_levels(bidiProperty->pCharTypes + pos, *itr - pos, &direction, bidiProperty->pEmbeddingLevels + pos));
194
195                         // Only on the first run
196                         if (itr == pSegmentIndex)
197                         {
198                                 bidiProperty->direction = direction;
199                                 // adjust baseLevel to be 1 for rtl paragraphs, and 0 for ltr paragraphs.
200                                 baseLevel = IS_DIRECTION_RTL(bidiProperty) ? 1 : 0;
201                         }
202
203                         // We want those chars at the override points to be on the base
204                         // level and we also remove -2 cause we later increment them,
205                         // just for simpler code paths
206                         bidiProperty->pEmbeddingLevels[*itr] = baseLevel - 2;
207                         pos = *itr + 1;
208                 }
209
210                 {
211                         FriBidiParType direction = FRIBIDI_PAR_ON;
212                         CHECK_IF(fribidi_get_par_embedding_levels(bidiProperty->pCharTypes + pos, len - pos, &direction, bidiProperty->pEmbeddingLevels + pos));
213                 }
214
215                 // Increment all levels by 2 to emulate embedding.
216                 {
217                         FriBidiLevel* pEmbeddingLevel = bidiProperty->pEmbeddingLevels;
218                         FriBidiLevel* pEmbeddingLevelEnd = pEmbeddingLevel + len;
219
220                         for ( ; pEmbeddingLevel < pEmbeddingLevelEnd ; ++pEmbeddingLevel)
221                         {
222                                 *pEmbeddingLevel += 2;
223                         }
224                 }
225         }
226         else
227         {
228                 FriBidiParType direction = FRIBIDI_PAR_ON;
229
230                 if ((Tizen::Graphics::_TextBidiUtil::IsTextBidiBaseLtr()))
231                 {
232                         switch (hint)
233                         {
234                         case Tizen::Graphics::_TextBidiProperty::BIDI_HINT_LTR:
235                                 direction = FRIBIDI_PAR_LTR;
236                                 break;
237                         case Tizen::Graphics::_TextBidiProperty::BIDI_HINT_RTL:
238                                 direction = FRIBIDI_PAR_RTL;
239                                 break;
240                         default:
241                                 for (size_t i = 0;  i < len; i++)
242                                 {
243                                         if (FRIBIDI_IS_STRONG(bidiProperty->pCharTypes[i]))
244                                         {
245                                                 if (FRIBIDI_IS_RTL(bidiProperty->pCharTypes[i]))
246                                                 {
247                                                         direction = FRIBIDI_PAR_RTL;
248                                                 }
249                                                 else
250                                                 {
251                                                         direction = FRIBIDI_PAR_LTR;
252                                                 }
253
254                                                 break;
255                                         }
256                                 }
257                                 break;
258                         }
259                 }
260                 else
261                 {
262                         switch (hint)
263                         {
264                         case Tizen::Graphics::_TextBidiProperty::BIDI_HINT_LTR:
265                                 direction = FRIBIDI_PAR_RTL;
266                                 break;
267                         case Tizen::Graphics::_TextBidiProperty::BIDI_HINT_RTL:
268                                 direction = FRIBIDI_PAR_LTR;
269                                 break;
270                         default:
271                                 for (size_t i = 0;  i < len; i++)
272                                 {
273                                         if (FRIBIDI_IS_STRONG(bidiProperty->pCharTypes[i]))
274                                         {
275                                                 if (FRIBIDI_IS_RTL(bidiProperty->pCharTypes[i]))
276                                                 {
277                                                         direction = FRIBIDI_PAR_LTR;
278                                                 }
279                                                 else
280                                                 {
281                                                         direction = FRIBIDI_PAR_RTL;
282                                                 }
283
284                                                 break;
285                                         }
286                                 }
287                                 break;
288                         }
289                 }
290
291                 bidiProperty->direction = direction;
292
293                 CHECK_IF(fribidi_get_par_embedding_levels(bidiProperty->pCharTypes, len, &bidiProperty->direction, bidiProperty->pEmbeddingLevels));
294         }
295
296         return bidiProperty.release();
297
298         #undef CHECK_IF
299 }
300
301 const wchar_t*
302 _GetBidiShapeString(BidiParagraph* pBidiProps)
303 {
304         #define CHECK_IF(cond) if (!(cond)) return 0
305
306         FriBidiChar* pTestString = NULL;
307         std::unique_ptr<FriBidiChar[]> autoDeleteString;
308
309         const size_t textLength = pBidiProps->bufferLength;
310
311         if (pBidiProps->pString)
312         {
313                 if (sizeof(wchar_t) == sizeof(FriBidiChar))
314                 {
315                         pTestString = (FriBidiChar *) pBidiProps->pString;
316                 }
317                 else
318                 {
319                         autoDeleteString.reset(new (std::nothrow) FriBidiChar[textLength + 1]);
320
321                         CHECK_IF(autoDeleteString.get());
322
323                         _ConvertCharacterCode<FriBidiChar, wchar_t>(autoDeleteString.get(), pBidiProps->pString, pBidiProps->bufferLength);
324
325                         pTestString = autoDeleteString.get();
326                 }
327
328                 std::unique_ptr<FriBidiJoiningType[]> pJoinTypes(new (std::nothrow) FriBidiJoiningType[textLength]);
329
330                 if (!pJoinTypes.get())
331                 {
332                         return pBidiProps->pString;
333                 }
334
335                 fribidi_get_joining_types(pTestString, textLength, pJoinTypes.get());
336
337                 fribidi_join_arabic(pBidiProps->pCharTypes, textLength, pBidiProps->pEmbeddingLevels, pJoinTypes.get());
338
339                 fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, pBidiProps->pEmbeddingLevels, textLength, pJoinTypes.get(), pTestString);
340
341                 if (sizeof(wchar_t) == sizeof(FriBidiChar))
342                 {
343                         pBidiProps->pString = (wchar_t *) pTestString;
344                 }
345                 else
346                 {
347                         _ConvertCharacterCode<wchar_t, FriBidiChar>(pBidiProps->pString, pTestString, textLength);
348                 }
349         }
350
351         return pBidiProps->pString;
352
353         #undef CHECK_IF
354 }
355
356 } // namespace
357
358 namespace
359 {
360 /*
361  * Reorders ustr according to the bidi props.
362  *
363  * @param pText the string to reorder. - Null is ok, will just populate the map.
364  * @param start the start of the line
365  * @param len the length of the line
366  * @param bidiProperty the paragraph props to reorder according to
367  * @param pOutVToL The visual to logical map to populate - if NULL it won't populate it.
368  */
369 bool
370 _ReorderBidiLine(wchar_t* pText, size_t start, size_t len, const BidiParagraph& bidiProperty, FriBidiStrIndex** pOutVToL)
371 {
372         #define CHECK_IF(cond) if (!(cond)) return false
373
374         FriBidiChar* pTestString = NULL;
375         std::unique_ptr<FriBidiChar[]> AutoDeleteString;
376         std::unique_ptr<FriBidiStrIndex[]> vToL;
377
378         const size_t textLength = len;
379
380         if (pText)
381         {
382                 if (sizeof(wchar_t) == sizeof(FriBidiChar))
383                 {
384                         pTestString = (FriBidiChar *) pText;
385                 }
386                 else
387                 {
388                         AutoDeleteString.reset(new (std::nothrow) FriBidiChar[textLength + 1]);
389
390                         CHECK_IF(AutoDeleteString.get());
391
392                         _ConvertCharacterCode<FriBidiChar, wchar_t>(AutoDeleteString.get(), pText, textLength);
393
394                         pTestString = AutoDeleteString.get();
395                 }
396         }
397
398         if (pOutVToL)
399         {
400                 vToL.reset(new (std::nothrow) FriBidiStrIndex[textLength]);
401
402                 CHECK_IF(vToL.get());
403
404                 // init the array for fribidi
405                 {
406                         FriBidiStrIndex* pRefIndex = vToL.get();
407
408                         for (size_t i = 0 ; i < textLength ; i++)
409                         {
410                                 *pRefIndex++ = i;
411                         }
412                 }
413         }
414
415         {
416                 std::unique_ptr<FriBidiLevel[]> embeddingLevel(new (std::nothrow) FriBidiLevel[start + textLength]);
417                 FriBidiLevel* pRefEmbeddingLevel = embeddingLevel.get();
418
419                 CHECK_IF(pRefEmbeddingLevel);
420
421                 memcpy(pRefEmbeddingLevel, bidiProperty.pEmbeddingLevels, (start + textLength) * sizeof(FriBidiLevel));
422
423                 if (vToL.get())
424                 {
425                         // We pass vToL.get() - start, because fribidi assumes start is the offset from the start of vToL.get() as well, not just the props.
426                         CHECK_IF(fribidi_reorder_line(FRIBIDI_FLAGS_DEFAULT, bidiProperty.pCharTypes, textLength, start, bidiProperty.direction, pRefEmbeddingLevel, pTestString, vToL.get() - start));
427                 }
428                 else
429                 {
430                         CHECK_IF(fribidi_reorder_line(FRIBIDI_FLAGS_DEFAULT, bidiProperty.pCharTypes, textLength, start, bidiProperty.direction, pRefEmbeddingLevel, pTestString, NULL));
431                 }
432         }
433
434         if (pText)
435         {
436                 if (sizeof(wchar_t) != sizeof(FriBidiChar))
437                 {
438                         _ConvertCharacterCode<wchar_t, FriBidiChar>(pText, AutoDeleteString.get(), textLength);
439                 }
440         }
441
442         if (pOutVToL)
443         {
444                 *pOutVToL = vToL.release();
445         }
446
447         return true;
448
449         #undef CHECK_IF
450 }
451
452 }
453
454 ////////////////////////////////////////////////////////////////////////////////
455
456 namespace Tizen { namespace Graphics
457 {
458
459 _TextBidiProperty::_TextBidiProperty(const wchar_t* pText, int length, BidiHint hint)
460         : pEventText(0)
461         , pCharType(0)
462         , pEmbeddingLevels(0)
463         , eventChar(0)
464         , baseDirection(FRIBIDI_PAR_ON)
465         , isRtl(false)
466         , length(0)
467         , pReserved(0)
468 {
469         BidiParagraph* pBidiProperty = _GetBidiParagraphProperty(pText, length, NULL, hint);
470
471         if (pBidiProperty)
472         {
473 #if 0
474                 this->pEventText = NULL;
475 #else
476                 memcpy(pBidiProperty->pString, pText, length * sizeof(wchar_t));
477                 pBidiProperty->pString[length] = 0;
478
479                 this->pEventText = _GetBidiShapeString(pBidiProperty);
480 #endif
481                 this->pCharType = pBidiProperty->pCharTypes;
482                 this->pEmbeddingLevels = pBidiProperty->pEmbeddingLevels;
483                 this->eventChar = (this->pEventText) ? *this->pEventText : 0;
484                 this->baseDirection = pBidiProperty->direction;
485                 this->isRtl = (this->pCharType) ? ((*this->pCharType & FRIBIDI_MASK_RTL) != 0) : false;
486                 this->length = length;
487
488                 this->pReserved = static_cast<void*>(pBidiProperty);
489         }
490         else
491         {
492                 this->pEventText = pText;
493                 this->eventChar = (this->pEventText) ? *this->pEventText : 0;
494                 this->length = length;
495         }
496 }
497
498 _TextBidiProperty::_TextBidiProperty(const _TextBidiProperty& refBidiProperty, int offset, int length)
499         : pEventText(0)
500         , pCharType(0)
501         , pEmbeddingLevels(0)
502         , eventChar(0)
503         , baseDirection(FRIBIDI_PAR_ON)
504         , isRtl(false)
505         , length(0)
506         , pReserved(0)
507 {
508         const wchar_t* pBegin = refBidiProperty.pEventText + offset;
509         const wchar_t* pEnd = pBegin + length;
510
511         pBegin = (pBegin > refBidiProperty.pEventText) ? pBegin : refBidiProperty.pEventText;
512         pEnd = (refBidiProperty.pEventText + refBidiProperty.length < pEnd) ? refBidiProperty.pEventText + refBidiProperty.length : pEnd;
513
514         if (pBegin <= pEnd)
515         {
516                 offset = pBegin - refBidiProperty.pEventText;
517                 length = pEnd - pBegin;
518
519                 this->pEventText = refBidiProperty.pEventText + offset;
520                 this->pCharType = refBidiProperty.pCharType + offset;
521                 this->pEmbeddingLevels = refBidiProperty.pEmbeddingLevels + offset;
522                 this->eventChar = (this->pEventText) ? *this->pEventText : 0;
523                 this->baseDirection = refBidiProperty.baseDirection;
524                 this->isRtl = (this->pEmbeddingLevels) ? ((*this->pEmbeddingLevels & 0x01) != 0) : false;
525                 this->length = length;
526
527                 // temporary
528                 if (this->pCharType)
529                 {
530                         const FriBidiCharType* pBegin = this->pCharType;
531                         const FriBidiCharType* pEnd = pBegin + length;
532
533                         while (pBegin < pEnd)
534                         {
535                                 if (*pBegin & FRIBIDI_MASK_LETTER)
536                                 {
537                                         this->eventChar = *(this->pEventText + (pBegin - this->pCharType));
538                                         break;
539                                 }
540
541                                 ++pBegin;
542                         }
543                 }
544         }
545 }
546
547 _TextBidiProperty::~_TextBidiProperty(void)
548 {
549         if (pReserved)
550         {
551                 delete static_cast<BidiParagraph*>(pReserved);
552         }
553 }
554
555 bool
556 _TextBidiProperty::HasComplexText(void) const
557 {
558         return ((pReserved != 0) || (!_TextBidiUtil::IsTextBidiBaseLtr()));
559 }
560
561 _TextBidiPropertyWithReorder::_TextBidiPropertyWithReorder(const wchar_t* pText, int length, BidiHint hint)
562         : _TextBidiProperty(pText, length, hint)
563         , pBidiIndex(0)
564         , __pReorderedText(0)
565 {
566         BidiParagraph* pBidiProperty = static_cast<BidiParagraph*>(this->pReserved);
567
568         if (pBidiProperty)
569         {
570                 FriBidiStrIndex* pTempIndex = 0;
571
572                 if (_ReorderBidiLine(NULL, 0, length, *pBidiProperty, &pTempIndex))
573                 {
574                         pBidiIndex = pTempIndex;
575                 }
576         }
577 }
578
579 _TextBidiPropertyWithReorder::_TextBidiPropertyWithReorder(const _TextBidiProperty& refBidiProperty, int offset, int length)
580         : _TextBidiProperty(refBidiProperty, offset, length)
581         , pBidiIndex(0)
582         , __pReorderedText(0)
583 {
584 }
585
586 const wchar_t*
587 _TextBidiPropertyWithReorder::GetReorderedText(void)
588 {
589         if ((__pReorderedText == NULL) && (pEventText != NULL) && (pBidiIndex != NULL))
590         {
591                 __pReorderedText = new (std::nothrow) wchar_t[length + 1];
592
593                 if (__pReorderedText)
594                 {
595                         wchar_t* pTempText = __pReorderedText;
596                         const wchar_t* pTempTextEnd = pTempText + length;
597                         const FriBidiStrIndex* pTempBidiIndex = pBidiIndex;
598
599                         while (pTempText < pTempTextEnd)
600                         {
601                                 *pTempText++ = pEventText[*pTempBidiIndex++];
602                         }
603
604                         *pTempText = 0;
605                 }
606
607                 return __pReorderedText;
608         }
609         else
610         {
611                 return pEventText;
612         }
613 }
614
615 _TextBidiPropertyWithReorder::~_TextBidiPropertyWithReorder(void)
616 {
617         delete[] pBidiIndex;
618         delete[] __pReorderedText;
619 }
620
621 GUnicodeScript
622 _TextBidiUtil::GetUnicodeScript(const wchar_t unicode)
623 {
624         static const struct MatchingTable
625         {
626                 wchar_t min;
627                 wchar_t max;
628                 GUnicodeScript script;
629         } MATCHING_TABLE[] =
630         {
631                 { 0x0600, 0x06ff, G_UNICODE_SCRIPT_ARABIC }
632         };
633
634         static const MatchingTable* pTableEnd = &MATCHING_TABLE[0] + sizeof(MATCHING_TABLE) / sizeof(MATCHING_TABLE[0]);
635
636         const MatchingTable* pTable = &MATCHING_TABLE[0] - 1;
637
638         while (++pTable < pTableEnd)
639         {
640                 if ((pTable->min <= unicode) && (pTable->max >= unicode))
641                 {
642                         return pTable->script;
643                 }
644         }
645
646         return g_unichar_get_script(unicode);
647 }
648
649 bool
650 _TextBidiUtil::IsCollationElement(const wchar_t first, const wchar_t second)
651 {
652         static const struct MatchingTable
653         {
654                 wchar_t first;
655                 wchar_t second;
656         } MATCHING_TABLE[] =
657         {
658                 { 0x0644, 0x0622 }, // arabic letter
659                 { 0x0644, 0x0623 },
660                 { 0x0644, 0x0625 },
661                 { 0x0644, 0x0627 },
662                 { 0x092F, 0x093C }, // Devanagari letter
663                 { 0x0930, 0x093C },
664                 { 0x0933, 0x093C },
665                 { 0x0915, 0x093C },
666                 { 0x0916, 0x093C },
667                 { 0x0917, 0x093C },
668                 { 0x091C, 0x093C },
669                 { 0x0921, 0x093C },
670                 { 0x0922, 0x093C },
671                 { 0x092B, 0x093C },
672                 { 0x092F, 0x093C },
673                 { 0x09C7, 0x09BE }, // Bengali letter
674                 { 0x09C7, 0x09D7 },
675                 { 0x09A1, 0x09BC },
676                 { 0x09A2, 0x09BC },
677                 { 0x09AF, 0x09BC },
678                 { 0x0020, 0x064B }, // arabic sign
679                 { 0x0640, 0x064B },
680                 { 0x0020, 0x064C },
681                 { 0x0020, 0x064D },
682                 { 0x0020, 0x064E },
683                 { 0x0640, 0x064E },
684                 { 0x0020, 0x064F },
685                 { 0x0640, 0x064F },
686                 { 0x0020, 0x0650 },
687                 { 0x0640, 0x0650 },
688                 { 0x0020, 0x0651 },
689                 { 0x0640, 0x0651 },
690                 { 0x0020, 0x0652 },
691                 { 0x0640, 0x0652 },
692         };
693
694         static const MatchingTable* pTableEnd = &MATCHING_TABLE[0] + sizeof(MATCHING_TABLE) / sizeof(MATCHING_TABLE[0]);
695
696         const MatchingTable* pTable = &MATCHING_TABLE[0] - 1;
697
698         while (++pTable < pTableEnd)
699         {
700                 if ((pTable->first != first) || (pTable->second != second))
701                 {
702                         continue;
703                 }
704
705                 return true;
706         }
707
708         return false;
709 }
710
711 bool
712 _TextBidiUtil::GetReorderedIndexList(const _TextBidiPropertyWithReorder& refBidiProperty, const _Util::AccumList<_Util::Pair<int, int> >& inputList, _Util::AccumList<_Util::Pair<int, int> >& outList)
713 {
714         if ((refBidiProperty.pEventText != NULL) && (refBidiProperty.pBidiIndex != NULL))
715         {
716                 int length = refBidiProperty.length;
717
718                 int* pCoIndex = new int[length];
719
720                 int coIndex = 0;
721
722                 for (int i = 0; i < refBidiProperty.length; i++, coIndex++)
723                 {
724                         if ((i + 1 < length) && IsCollationElement(refBidiProperty.pEventText[i], refBidiProperty.pEventText[i + 1]))
725                         {
726                                 pCoIndex[i] = coIndex;
727                                 pCoIndex[++i] = (1 << 24) | coIndex;
728                         }
729                         else
730                         {
731                                 pCoIndex[i] = coIndex;
732                         }
733                 }
734
735                 typedef _Util::Pair<int, int> Gap;
736                 typedef _Util::AccumList<Gap> GapList;
737
738                 GapList::Iterator dstBegin = outList.Begin();
739
740                 int index = 0;
741
742                 for (GapList::ConstIterator src = inputList.Begin(); src != inputList.End(); ++src)
743                 {
744                         // for safty
745                         if (index >= length)
746                         {
747                                 break;
748                         }
749
750                         int coIx = pCoIndex[refBidiProperty.pBidiIndex[index++]];
751                         int jump = coIx >> 24;
752
753                         index += jump;
754
755                         coIx &= 0x00FFFFFF;
756
757                         *(dstBegin + coIx) = *src;
758                 }
759
760                 delete[] pCoIndex;
761
762                 return true;
763         }
764
765         return false;
766 }
767
768 namespace
769 {
770         bool _isLtrBase = true;
771 }
772
773 void
774 _TextBidiUtil::SetTextBidiBase(bool isLtr)
775 {
776         _isLtrBase = isLtr;
777 }
778
779 bool
780 _TextBidiUtil::IsTextBidiBaseLtr(void)
781 {
782         return _isLtrBase;
783 }
784
785 }} // Tizen::Graphics
786
787
788 ////////////////////////////////////////////////////////////////////////////////
789
790 #include <hb.h>
791 #include <hb-ft.h>
792 #include <hb-glib.h>
793
794 namespace Tizen { namespace Graphics
795 {
796
797 _FontGlyphList::_FontGlyphList(const _Util::String& reorderedText, void* pFace, int script)
798 {
799         FT_Face face = FT_Face(pFace);
800
801         hb_font_t* hSubFont = NULL;
802         hb_buffer_t* hBuffer = NULL;
803
804         {
805                 hb_font_t *hFont = hb_ft_font_create(face, NULL);
806                 hSubFont = hb_font_create_sub_font(hFont);
807                 hb_font_destroy (hFont);
808         }
809
810         hBuffer = hb_buffer_create();
811         hb_buffer_set_unicode_funcs(hBuffer, hb_unicode_funcs_get_default());
812
813         hb_buffer_set_script(hBuffer, hb_glib_script_to_script(GUnicodeScript(script)));
814
815         hb_buffer_set_direction(hBuffer, (_TextBidiUtil::IsTextBidiBaseLtr()) ? HB_DIRECTION_LTR : HB_DIRECTION_RTL);
816
817         int textLength = reorderedText.length;
818
819         if (sizeof(wchar_t) == sizeof(uint32_t))
820         {
821                 hb_buffer_add_utf32(hBuffer, (const uint32_t*)reorderedText.pStart, textLength, 0, textLength);
822         }
823         else
824         {
825                 uint32_t* pDst = new (std::nothrow) uint32_t[textLength + 1];
826
827                 if (pDst)
828                 {
829                         {
830                                 const wchar_t* pSrc = reorderedText.pStart;
831
832                                 uint32_t* pDstBegin = pDst;
833                                 uint32_t* pDstEnd = pDstBegin + textLength;
834
835                                 while (pDstBegin < pDstEnd)
836                                 {
837                                         *pDstBegin++ = *pSrc++;
838                                 }
839
840                                 *pDstBegin = 0;
841                         }
842
843                         hb_buffer_add_utf32(hBuffer, pDst, textLength, 0, textLength);
844
845                         delete[] pDst;
846                 }
847         }
848
849         hb_shape(hSubFont, hBuffer, NULL, 0);
850
851         this->__glyphCount = hb_buffer_get_length(hBuffer);
852
853         if (int(this->__glyphCount) == textLength)
854         {
855                 this->__pGlyphAuxInfo = new (std::nothrow) GlyphAuxInfo[this->__glyphCount];
856
857                 if (this->__pGlyphAuxInfo)
858                 {
859                         hb_glyph_position_t* pPosition = hb_buffer_get_glyph_positions(hBuffer, NULL);
860                         hb_glyph_info_t* pInfo = hb_buffer_get_glyph_infos(hBuffer, NULL);
861
862                         GlyphAuxInfo* pAuxInfo = &this->__pGlyphAuxInfo[0];
863                         GlyphAuxInfo* pAuxInfoEnd = pAuxInfo + this->__glyphCount;
864
865                         while (pAuxInfo < pAuxInfoEnd)
866                         {
867                                 pAuxInfo->isAvailable = true;
868                                 pAuxInfo->glyphIndex = pInfo->codepoint;
869                                 pAuxInfo->xOffset.Reset(pPosition->x_offset);
870                                 pAuxInfo->yOffset.Reset(pPosition->y_offset);
871                                 pAuxInfo->xAdvance.Reset(pPosition->x_advance);
872                                 pAuxInfo->yAdvance.Reset(pPosition->y_advance);
873
874                                 ++pInfo;
875                                 ++pPosition;
876                                 ++pAuxInfo;
877                         }
878                 }
879         }
880         else if (int(this->__glyphCount) < textLength)
881         {
882                 int tempCount = this->__glyphCount;
883
884                 this->__glyphCount = textLength;
885
886                 this->__pGlyphAuxInfo = new (std::nothrow) GlyphAuxInfo[textLength];
887
888                 if (this->__pGlyphAuxInfo)
889                 {
890                         memset(static_cast<void*>(this->__pGlyphAuxInfo), 0, sizeof(GlyphAuxInfo) * this->__glyphCount);
891
892                         hb_glyph_position_t* pPosition = hb_buffer_get_glyph_positions(hBuffer, NULL);
893                         hb_glyph_info_t* pInfo = hb_buffer_get_glyph_infos(hBuffer, NULL);
894
895                         GlyphAuxInfo* pAuxInfo = &this->__pGlyphAuxInfo[0];
896                         GlyphAuxInfo* pAuxInfoEnd = pAuxInfo + textLength;
897
898                         int textIndex = 0;
899
900                         while (pAuxInfo < pAuxInfoEnd && textIndex < tempCount)
901                         {
902                                 pAuxInfo->isAvailable = true;
903                                 pAuxInfo->glyphIndex = pInfo->codepoint;
904                                 pAuxInfo->xOffset.Reset(pPosition->x_offset);
905                                 pAuxInfo->yOffset.Reset(pPosition->y_offset);
906                                 pAuxInfo->xAdvance.Reset(pPosition->x_advance);
907                                 pAuxInfo->yAdvance.Reset(pPosition->y_advance);
908
909                                 bool isCollationElement = false;
910
911                                 if (script == G_UNICODE_SCRIPT_ARABIC)
912                                 {
913                                         isCollationElement = ((textIndex + 1 < textLength) && _TextBidiUtil::IsCollationElement(*(reorderedText.pStart + textIndex + 1), *(reorderedText.pStart + textIndex)));
914                                 }
915                                 else
916                                 {
917                                         isCollationElement = ((textIndex + 1 < textLength) && _TextBidiUtil::IsCollationElement(*(reorderedText.pStart + textIndex), *(reorderedText.pStart + textIndex + 1)));
918                                 }
919
920                                 if (isCollationElement)
921                                 {
922                                         ++pAuxInfo;
923                                         ++textIndex;
924                                         ++tempCount;
925                                         pAuxInfo->isAvailable = false;
926                                 }
927
928                                 ++pInfo;
929                                 ++pPosition;
930                                 ++pAuxInfo;
931                                 ++textIndex;
932                         }
933                 }
934         }
935         else if (int(this->__glyphCount) > textLength)
936         {
937                 this->__pGlyphAuxInfo = new (std::nothrow) GlyphAuxInfo[this->__glyphCount];
938
939                 if (this->__pGlyphAuxInfo)
940                 {
941                         memset(static_cast<void*>(this->__pGlyphAuxInfo), 0, sizeof(GlyphAuxInfo) * this->__glyphCount);
942
943                         hb_glyph_position_t* pPosition = hb_buffer_get_glyph_positions(hBuffer, NULL);
944                         hb_glyph_info_t* pInfo = hb_buffer_get_glyph_infos(hBuffer, NULL);
945
946                         GlyphAuxInfo* pAuxInfo = &this->__pGlyphAuxInfo[0];
947                         GlyphAuxInfo* pAuxInfoEnd = pAuxInfo + this->__glyphCount;
948
949                         int textIndex = 0;
950
951                         while (pAuxInfo < pAuxInfoEnd)
952                         {
953                                 pAuxInfo->isAvailable = true;
954                                 pAuxInfo->glyphIndex = pInfo->codepoint;
955                                 pAuxInfo->xOffset.Reset(pPosition->x_offset);
956                                 pAuxInfo->yOffset.Reset(pPosition->y_offset);
957                                 pAuxInfo->xAdvance.Reset(pPosition->x_advance);
958                                 pAuxInfo->yAdvance.Reset(pPosition->y_advance);
959
960                                 bool isCollationElement = false;
961
962                                 if (script == G_UNICODE_SCRIPT_ARABIC)
963                                 {
964                                         isCollationElement = ((textIndex + 1 < textLength) && _TextBidiUtil::IsCollationElement(*(reorderedText.pStart + textIndex + 1), *(reorderedText.pStart + textIndex)));
965                                 }
966                                 else
967                                 {
968                                         isCollationElement = ((textIndex + 1 < textLength) && _TextBidiUtil::IsCollationElement(*(reorderedText.pStart + textIndex), *(reorderedText.pStart + textIndex + 1)));
969                                 }
970
971                                 if (isCollationElement)
972                                 {
973                                         ++pAuxInfo;
974                                         ++textIndex;
975                                         pAuxInfo->isAvailable = false;
976                                 }
977
978                                 ++pInfo;
979                                 ++pPosition;
980                                 ++pAuxInfo;
981                                 ++textIndex;
982                         }
983                 }
984         }
985
986         hb_buffer_destroy(hBuffer);
987         hb_font_destroy(hSubFont);
988 }
989
990 _FontGlyphList::~_FontGlyphList(void)
991 {
992         delete[] __pGlyphAuxInfo;
993 }
994
995 unsigned int _FontGlyphList::GetCount(void) const
996 {
997         return __glyphCount;
998 }
999
1000 const _FontGlyphList::GlyphAuxInfo& _FontGlyphList::GetGlyphAuxInfo(unsigned int index) const
1001 {
1002         static const GlyphAuxInfo nullInfo =
1003         {
1004                 false, 0, _Util::FixedPoint26_6(0), _Util::FixedPoint26_6(0), _Util::FixedPoint26_6(0), _Util::FixedPoint26_6(0)
1005         };
1006
1007         return (index < __glyphCount) ? __pGlyphAuxInfo[index] : nullInfo;
1008 }
1009
1010 }} // Tizen::Graphics