Merge remote-tracking branch 'origin/tizen' into new_text
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / multi-language-support-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/multi-language-support-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/singleton-service.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
24 #include <dali-toolkit/public-api/text/logical-model.h>
25 #include <dali-toolkit/public-api/text/font-run.h>
26 #include <dali-toolkit/public-api/text/script.h>
27 #include <dali-toolkit/public-api/text/script-run.h>
28 #include <dali/integration-api/debug.h>
29
30 // EXTERNAL INCLUDES
31 #include <memory.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Text
40 {
41
42 namespace Internal
43 {
44
45 /**
46  * @brief Retrieves the font Id from the font run for a given character's @p index.
47  *
48  * If the character's index exceeds the current font run it increases the iterator to get the next one.
49  *
50  * @param[in] index The character's index.
51  * @param[in,out] fontRunIt Iterator to the current font run.
52  * @param[in] fontRunEndIt Iterator to one after the last font run.
53  *
54  * @return The font id.
55  */
56 FontId GetFontId( Length index,
57                   Vector<FontRun>::ConstIterator& fontRunIt,
58                   const Vector<FontRun>::ConstIterator& fontRunEndIt )
59 {
60   FontId fontId = 0u;
61
62   if( fontRunIt != fontRunEndIt )
63   {
64     const FontRun& fontRun = *fontRunIt;
65
66     if( ( index >= fontRun.characterRun.characterIndex ) &&
67         ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) )
68     {
69       fontId = fontRun.fontId;
70     }
71
72     if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
73     {
74       // All the characters of the current run have been traversed. Get the next one for the next iteration.
75       ++fontRunIt;
76     }
77   }
78
79   return fontId;
80 }
81
82 /**
83  * @brief Retrieves the script Id from the script run for a given character's @p index.
84  *
85  * If the character's index exceeds the current script run it increases the iterator to get the next one.
86  *
87  * @param[in] index The character's index.
88  * @param[in,out] scriptRunIt Iterator to the current font run.
89  * @param[in] scriptRunEndIt Iterator to one after the last script run.
90  *
91  * @return The script.
92  */
93 Script GetScript( Length index,
94                   Vector<ScriptRun>::ConstIterator& scriptRunIt,
95                   const Vector<ScriptRun>::ConstIterator& scriptRunEndIt )
96 {
97   Script script = TextAbstraction::UNKNOWN;
98
99   if( scriptRunIt != scriptRunEndIt )
100   {
101     const ScriptRun& scriptRun = *scriptRunIt;
102
103     if( ( index >= scriptRun.characterRun.characterIndex ) &&
104         ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) )
105     {
106       script = scriptRun.script;
107     }
108
109     if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
110     {
111       // All the characters of the current run have been traversed. Get the next one for the next iteration.
112       ++scriptRunIt;
113     }
114   }
115
116   return script;
117 }
118
119 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
120 {
121   for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
122          endIt = mValidFonts.End();
123        it != endIt;
124        ++it )
125   {
126     if( fontId == *it )
127     {
128       return true;
129     }
130   }
131
132   return false;
133 }
134
135 MultilanguageSupport::MultilanguageSupport()
136 : mDefaultFontPerScriptCache(),
137   mValidFontsPerScriptCache()
138 {
139   // Initializes the default font cache to zero (invalid font).
140   // Reserves space to cache the default fonts and access them with the script as an index.
141   mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u );
142
143   // Initializes the valid fonts cache to NULL (no valid fonts).
144   // Reserves space to cache the valid fonts and access them with the script as an index.
145   mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
146 }
147
148 MultilanguageSupport::~MultilanguageSupport()
149 {
150   // Destroy the valid fonts per script cache.
151
152   for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
153          endIt = mValidFontsPerScriptCache.End();
154        it != endIt;
155        ++it )
156   {
157     delete *it;
158   }
159 }
160
161 Text::MultilanguageSupport MultilanguageSupport::Get()
162 {
163   Text::MultilanguageSupport multilanguageSupportHandle;
164
165   SingletonService service( SingletonService::Get() );
166   if( service )
167   {
168     // Check whether the singleton is already created
169     Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
170     if( handle )
171     {
172       // If so, downcast the handle
173       MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
174       multilanguageSupportHandle = Text::MultilanguageSupport( impl );
175     }
176     else // create and register the object
177     {
178       multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
179       service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
180     }
181   }
182
183   return multilanguageSupportHandle;
184 }
185
186 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
187                                        Vector<ScriptRun>& scripts )
188 {
189   const Length numberOfCharacters = text.Count();
190
191   if( 0u == numberOfCharacters )
192   {
193     // Nothing to do if there are no characters.
194     return;
195   }
196
197   // Traverse all characters and set the scripts.
198
199   // Stores the current script run.
200   ScriptRun currentScriptRun;
201   currentScriptRun.characterRun.characterIndex = 0u;
202   currentScriptRun.characterRun.numberOfCharacters = 0u;
203   currentScriptRun.script = TextAbstraction::UNKNOWN;
204
205   // Reserve some space to reduce the number of reallocations.
206   scripts.Reserve( numberOfCharacters << 2u );
207
208   for( Length index = 0u; index < numberOfCharacters; ++index )
209   {
210     const Character character = *( text.Begin() + index );
211
212     Script script = GetCharacterScript( character );
213
214     if( TextAbstraction::UNKNOWN == script )
215     {
216       if( IsZeroWidthNonJoiner( character ) ||
217           IsZeroWidthJoiner( character ) ||
218           IsZeroWidthSpace( character ) ||
219           IsLeftToRightMark( character ) ||
220           IsRightToLeftMark( character ) ||
221           IsThinSpace( character ) )
222       {
223         // Keep previous script if the character is a zero width joiner or a zero width non joiner.
224         script = currentScriptRun.script;
225       }
226       else
227       {
228         script = TextAbstraction::LATIN;
229         DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" );
230       }
231     }
232
233     if( script != currentScriptRun.script )
234     {
235       // Current run needs to be stored and a new one initialized.
236
237       if( 0u != currentScriptRun.characterRun.numberOfCharacters )
238       {
239         // Store the script run.
240         scripts.PushBack( currentScriptRun );
241       }
242
243       // Initialize the new one.
244       currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
245       currentScriptRun.characterRun.numberOfCharacters = 0u;
246       currentScriptRun.script = script;
247     }
248
249     // Add one more character to the run.
250     ++currentScriptRun.characterRun.numberOfCharacters;
251   }
252
253   if( 0u != currentScriptRun.characterRun.numberOfCharacters )
254   {
255     // Store the last run.
256     scripts.PushBack( currentScriptRun );
257   }
258 }
259
260 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
261                                           const Vector<ScriptRun>& scripts,
262                                           Vector<FontRun>& fonts )
263 {
264   const Length numberOfCharacters = text.Count();
265
266   if( 0u == numberOfCharacters )
267   {
268     // Nothing to do if there are no characters.
269     return;
270   }
271
272   // Copy the fonts set by application developers.
273   const Length numberOfFontRuns = fonts.Count();
274   const Vector<FontRun> definedFonts = fonts;
275   fonts.Clear();
276
277   // Traverse the characters and validate/set the fonts.
278
279   // Get the caches.
280   FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
281   ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
282
283   // Stores the validated font runs.
284   fonts.Reserve( numberOfFontRuns );
285
286   // Initializes a validated font run.
287   FontRun currentFontRun;
288   currentFontRun.characterRun.characterIndex = 0u;
289   currentFontRun.characterRun.numberOfCharacters = 0u;
290   currentFontRun.fontId = 0u;
291   currentFontRun.isDefault = false;
292
293   // Get the font client.
294   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
295
296   // Iterators of the font and script runs.
297   Vector<FontRun>::ConstIterator fontRunIt = definedFonts.Begin();
298   Vector<FontRun>::ConstIterator fontRunEndIt = definedFonts.End();
299   Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
300   Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
301
302   for( Length index = 0u; index < numberOfCharacters; ++index )
303   {
304     // Get the character.
305     const Character character = *( text.Begin() + index );
306
307     // Get the font for the character.
308     FontId fontId = GetFontId( index,
309                                fontRunIt,
310                                fontRunEndIt );
311
312     // Get the script for the character.
313     Script script = GetScript( index,
314                                scriptRunIt,
315                                scriptRunEndIt );
316
317     if( TextAbstraction::UNKNOWN == script )
318     {
319       DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
320       script = TextAbstraction::LATIN;
321     }
322
323     // Whether the font being validated is a default one not set by the user.
324     const bool isDefault = ( 0u == fontId );
325
326     // The default font point size.
327     PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
328
329     if( !isDefault )
330     {
331       // Validate if the font set by the user supports the character.
332
333       // Check first in the caches.
334
335       // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
336       if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
337       {
338         // Check in the valid fonts cache.
339         ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
340         if( NULL != validateFontsPerScript )
341         {
342           if( !validateFontsPerScript->FindValidFont( fontId ) )
343           {
344             // Use the font client to validate the font.
345             const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
346
347             if( 0u == glyphIndex )
348             {
349               // Get the point size of the current font. It will be used to get a default font id.
350               pointSize = fontClient.GetPointSize( fontId );
351
352               // The font is not valid. Set to zero and a default one will be set.
353               fontId = 0u;
354             }
355             else
356             {
357               // Add the font to the valid font cache.
358               validateFontsPerScript->mValidFonts.PushBack( fontId );
359             }
360           }
361         }
362         else
363         {
364           // Use the font client to validate the font.
365           const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
366
367           if( 0u == glyphIndex )
368           {
369             // Get the point size of the current font. It will be used to get a default font id.
370             pointSize = fontClient.GetPointSize( fontId );
371
372             // The font is not valid. Set to zero and a default one will be set.
373             fontId = 0u;
374           }
375           else
376           {
377             // Add the font to the valid font cache.
378             validateFontsPerScript = new ValidateFontsPerScript();
379             *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
380
381             validateFontsPerScript->mValidFonts.PushBack( fontId );
382           }
383         }
384       }
385     }
386
387     // The font has not been validated. Find a default one.
388     if( 0u == fontId )
389     {
390       // The character has no font assigned. Get a default one from the cache
391       fontId = *( defaultFontPerScriptCacheBuffer + script );
392
393       // If the cache has not a default font, get one from the font client.
394       if( 0u == fontId )
395       {
396         // Find a default font.
397         fontId = fontClient.FindDefaultFont( character, pointSize );
398
399         // Cache the font.
400         *( defaultFontPerScriptCacheBuffer + script ) = fontId;
401       }
402     }
403
404     // The font is now validated.
405
406     if( ( fontId != currentFontRun.fontId ) ||
407         ( isDefault != currentFontRun.isDefault ) )
408     {
409       // Current run needs to be stored and a new one initialized.
410
411       if( 0u != currentFontRun.characterRun.numberOfCharacters )
412       {
413         // Store the font run.
414         fonts.PushBack( currentFontRun );
415       }
416
417       // Initialize the new one.
418       currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
419       currentFontRun.characterRun.numberOfCharacters = 0u;
420       currentFontRun.fontId = fontId;
421       currentFontRun.isDefault = isDefault;
422     }
423
424     // Add one more character to the run.
425     ++currentFontRun.characterRun.numberOfCharacters;
426   }
427
428   if( 0u != currentFontRun.characterRun.numberOfCharacters )
429   {
430     // Store the last run.
431     fonts.PushBack( currentFontRun );
432   }
433 }
434
435 } // namespace Internal
436
437 } // namespace Text
438
439 } // namespace Toolkit
440
441 } // namespace Dali